Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ jobs:
env:
RELEASE_TAG: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }}

- name: Verify installed collectors and development-only bundles
run: |
npx nx build growth --skip-nx-cache
npx nx run telemetry:test-install-pack --skip-nx-cache
npx nx run telemetry:test-development-bundle --skip-nx-cache

# Trusted publishing is configured per-package on npm; no NPM_TOKEN needed.
# The OIDC token from id-token: write authenticates this workflow as a
# trusted publisher for each @threadplane/* package. Provenance attestations are
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ keys/
# Aborted fixture-record runs strand raw recordings under a committed path
**/e2e/fixtures/.staging/

# Shared install collector staged before each library is packaged.
libs/*/.install-collector/*
!libs/*/.install-collector/development-install.mjs
!libs/*/.install-collector/development-install.d.ts

# Growth research generated deployment artifacts
apps/growth-research/.dawn/
apps/growth-research/.deployment/
24 changes: 24 additions & 0 deletions apps/website/content/AGENTS.md.template
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ Supported Angular majors: 20, 21, and 22.
## Install
npm install @threadplane/chat @threadplane/langgraph @langchain/core @langchain/langgraph-sdk marked

The chat, LangGraph, AG-UI, and render packages include automatic install collection
for local and CI execution: package/environment details, a random installation ID,
configured Git name/full email, and repository provider/owner hints when available.
Set `DO_NOT_TRACK=1` or `TPLANE_TELEMETRY_DISABLED=1` before installation to disable
it. Package-manager script controls are respected. A usable install email can qualify
for a short founder welcome sequence after linked development-browser use, capped
at three emails with unsubscribe and reply stops. CI alone does not trigger it.
The package-local correlation token contains no email; copied/cached packages can
retain it, so it does not verify a person. No registration is required.
See https://threadplane.ai/privacy.


Supported runtime/browser collection is automatic only in Angular development mode.
Production builds, SSR, imports, unused construction, and automated browsers
reporting `navigator.webdriver` are inert. It sends closed
progress milestones with package/version and random browser-origin/session IDs, never
conversation content or private URLs. Adapter `telemetry: false` disables it; a custom
sink replaces the automatic destination, including nested chat JSON rendering. Use
`setDevelopmentCollectionEnabled(false)` from `@threadplane/telemetry/browser`, or set
browser localStorage `THREADPLANE_TELEMETRY_DISABLED=1` and reload. Development console
announcements need no click to acknowledge progress. Linked non-CI install and runtime
evidence can qualify the install email for the founder sequence described above.
Standalone render also accepts `provideRender({ telemetry: false })`.

## Key requirement
`injectAgent()` MUST be called within an Angular injection context (component constructor or field initializer). Calling it in ngOnInit or any async context throws "NG0203: inject() must be called from an injection context".

Expand Down
24 changes: 24 additions & 0 deletions apps/website/content/CLAUDE.md.template
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ Supported Angular majors: 20, 21, and 22.
## Install
npm install @threadplane/chat @threadplane/langgraph @langchain/core @langchain/langgraph-sdk marked

The chat, LangGraph, AG-UI, and render packages include automatic install collection
for local and CI execution: package/environment details, a random installation ID,
configured Git name/full email, and repository provider/owner hints when available.
Set `DO_NOT_TRACK=1` or `TPLANE_TELEMETRY_DISABLED=1` before installation to disable
it. Package-manager script controls are respected. A usable install email can qualify
for a short founder welcome sequence after linked development-browser use, capped
at three emails with unsubscribe and reply stops. CI alone does not trigger it.
The package-local correlation token contains no email; copied/cached packages can
retain it, so it does not verify a person. No registration is required.
See https://threadplane.ai/privacy.


Supported runtime/browser collection is automatic only in Angular development mode.
Production builds, SSR, imports, unused construction, and automated browsers
reporting `navigator.webdriver` are inert. It sends closed
progress milestones with package/version and random browser-origin/session IDs, never
conversation content or private URLs. Adapter `telemetry: false` disables it; a custom
sink replaces the automatic destination, including nested chat JSON rendering. Use
`setDevelopmentCollectionEnabled(false)` from `@threadplane/telemetry/browser`, or set
browser localStorage `THREADPLANE_TELEMETRY_DISABLED=1` and reload. Development console
announcements need no click to acknowledge progress. Linked non-CI install and runtime
evidence can qualify the install email for the founder sequence described above.
Standalone render also accepts `provideRender({ telemetry: false })`.

## Key requirement
`injectAgent()` MUST be called within an Angular injection context (component constructor or field initializer). Calling it in ngOnInit or any async context throws "NG0203: inject() must be called from an injection context".

Expand Down
4 changes: 2 additions & 2 deletions apps/website/content/docs/ag-ui/api/api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@
{
"name": "telemetry",
"type": "false | AgentRuntimeTelemetrySink",
"description": "Optional app-owned sink. Supply one to receive runtime lifecycle events.",
"description": "Omit to enable automatic development-only collection. Set `false` to disable.\nAn app-owned sink replaces the automatic destination and receives the\nexisting runtime lifecycle callbacks.",
"optional": true
},
{
Expand Down Expand Up @@ -540,7 +540,7 @@
{
"name": "telemetry",
"type": "false | AgentRuntimeTelemetrySink",
"description": "Optional app-owned sink. Supply one to receive runtime lifecycle events.",
"description": "Omit to enable automatic development-only collection. Set `false` to disable.\nAn app-owned sink replaces the automatic destination and receives the\nexisting runtime lifecycle callbacks.",
"optional": true
}
],
Expand Down
4 changes: 2 additions & 2 deletions apps/website/content/docs/langgraph/api/api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@
{
"name": "telemetry",
"type": "false | AgentRuntimeTelemetrySink",
"description": "Optional app-owned sink. Supply one to receive runtime lifecycle events.",
"description": "Omit to enable automatic development-only collection. Set `false` to disable.\nAn app-owned sink replaces the automatic destination and receives the\nexisting runtime lifecycle callbacks.",
"optional": true
},
{
Expand Down Expand Up @@ -1061,7 +1061,7 @@
{
"name": "telemetry",
"type": "false | AgentRuntimeTelemetrySink",
"description": "Optional app-owned sink. Supply one to receive runtime lifecycle events.",
"description": "Omit to enable automatic development-only collection. Set `false` to disable.\nAn app-owned sink replaces the automatic destination and receives the\nexisting runtime lifecycle callbacks.",
"optional": true
},
{
Expand Down
12 changes: 12 additions & 0 deletions apps/website/content/docs/render/api/api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@
"type": "InputSignal<StateStore | undefined>",
"description": "",
"optional": false
},
{
"name": "telemetry",
"type": "InputSignal<boolean | undefined>",
"description": "Disable automatic development collection for this render tree.",
"optional": false
}
],
"methods": [
Expand Down Expand Up @@ -276,6 +282,12 @@
"type": "StateStore",
"description": "Backing state store that `$bindState` paths read from and interactive elements write to.",
"optional": true
},
{
"name": "telemetry",
"type": "boolean",
"description": "Set false to disable automatic development browser collection for this render tree.",
"optional": true
}
],
"examples": []
Expand Down
24 changes: 24 additions & 0 deletions apps/website/public/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ Supported Angular majors: 20, 21, and 22.
## Install
npm install @threadplane/chat @threadplane/langgraph @langchain/core @langchain/langgraph-sdk marked

The chat, LangGraph, AG-UI, and render packages include automatic install collection
for local and CI execution: package/environment details, a random installation ID,
configured Git name/full email, and repository provider/owner hints when available.
Set `DO_NOT_TRACK=1` or `TPLANE_TELEMETRY_DISABLED=1` before installation to disable
it. Package-manager script controls are respected. A usable install email can qualify
for a short founder welcome sequence after linked development-browser use, capped
at three emails with unsubscribe and reply stops. CI alone does not trigger it.
The package-local correlation token contains no email; copied/cached packages can
retain it, so it does not verify a person. No registration is required.
See https://threadplane.ai/privacy.


Supported runtime/browser collection is automatic only in Angular development mode.
Production builds, SSR, imports, unused construction, and automated browsers
reporting `navigator.webdriver` are inert. It sends closed
progress milestones with package/version and random browser-origin/session IDs, never
conversation content or private URLs. Adapter `telemetry: false` disables it; a custom
sink replaces the automatic destination, including nested chat JSON rendering. Use
`setDevelopmentCollectionEnabled(false)` from `@threadplane/telemetry/browser`, or set
browser localStorage `THREADPLANE_TELEMETRY_DISABLED=1` and reload. Development console
announcements need no click to acknowledge progress. Linked non-CI install and runtime
evidence can qualify the install email for the founder sequence described above.
Standalone render also accepts `provideRender({ telemetry: false })`.

## Key requirement
`injectAgent()` MUST be called within an Angular injection context (component constructor or field initializer). Calling it in ngOnInit or any async context throws "NG0203: inject() must be called from an injection context".

Expand Down
24 changes: 24 additions & 0 deletions apps/website/public/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ Supported Angular majors: 20, 21, and 22.
## Install
npm install @threadplane/chat @threadplane/langgraph @langchain/core @langchain/langgraph-sdk marked

The chat, LangGraph, AG-UI, and render packages include automatic install collection
for local and CI execution: package/environment details, a random installation ID,
configured Git name/full email, and repository provider/owner hints when available.
Set `DO_NOT_TRACK=1` or `TPLANE_TELEMETRY_DISABLED=1` before installation to disable
it. Package-manager script controls are respected. A usable install email can qualify
for a short founder welcome sequence after linked development-browser use, capped
at three emails with unsubscribe and reply stops. CI alone does not trigger it.
The package-local correlation token contains no email; copied/cached packages can
retain it, so it does not verify a person. No registration is required.
See https://threadplane.ai/privacy.


Supported runtime/browser collection is automatic only in Angular development mode.
Production builds, SSR, imports, unused construction, and automated browsers
reporting `navigator.webdriver` are inert. It sends closed
progress milestones with package/version and random browser-origin/session IDs, never
conversation content or private URLs. Adapter `telemetry: false` disables it; a custom
sink replaces the automatic destination, including nested chat JSON rendering. Use
`setDevelopmentCollectionEnabled(false)` from `@threadplane/telemetry/browser`, or set
browser localStorage `THREADPLANE_TELEMETRY_DISABLED=1` and reload. Development console
announcements need no click to acknowledge progress. Linked non-CI install and runtime
evidence can qualify the install email for the founder sequence described above.
Standalone render also accepts `provideRender({ telemetry: false })`.

## Key requirement
`injectAgent()` MUST be called within an Angular injection context (component constructor or field initializer). Calling it in ngOnInit or any async context throws "NG0203: inject() must be called from an injection context".

Expand Down
3 changes: 3 additions & 0 deletions apps/website/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
SITE_ORIGIN,
} from '../lib/site-metadata';
import { getFormPolicy } from '../lib/growth/form-policy';
import { WebsiteSignals } from '../components/shared/WebsiteSignals';
import { websiteContentCatalog } from '../lib/growth/website-content';

const garamond = EB_Garamond({
subsets: ['latin'],
Expand Down Expand Up @@ -78,6 +80,7 @@ export default function RootLayout({
reason — do not mount its component builders individually.
*/}
<JsonLd data={rootJsonLd()} />
<WebsiteSignals catalog={websiteContentCatalog()} />
<Nav />
<div id="site-content">
<main>
Expand Down
18 changes: 16 additions & 2 deletions apps/website/src/app/privacy/page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import PrivacyPage, { metadata } from './page';
/**
* One canonical policy replaces the previous scattering of analytics promises.
* These assertions pin what it must say and, just as importantly, what it must
* never grow back into: a per-event catalog, an installation-behavior claim, or
* never grow back into: a per-event catalog or
* an absolute guarantee that a future change could quietly falsify.
*/
describe('privacy policy metadata', () => {
Expand All @@ -44,6 +44,21 @@ describe('privacy policy content', () => {
render(<PrivacyPage />);
return document.body.textContent ?? '';
};
it('discloses install identity collection and its disable control', () => {
const body = text();
expect(body).toMatch(/install/i);
expect(body).toMatch(/Git.*email/i);
expect(body).toContain('DO_NOT_TRACK');
expect(body).toMatch(/CI/);
});
it('explains development-only runtime collection and browser disable controls', () => {
const body = text();
expect(body).toMatch(/development builds/i);
expect(body).toMatch(/production builds.*do not/is);
expect(body).toContain('THREADPLANE_TELEMETRY_DISABLED');
expect(body).toMatch(/browser-origin identifier/i);
expect(body).toMatch(/custom telemetry sink/i);
});

it('names the information Threadplane collects', () => {
const body = text();
Expand Down Expand Up @@ -89,7 +104,6 @@ describe('privacy policy content', () => {
});

it.each([
['an installation behavior claim', /install/i],
['a never-collected list', /never collect|we will never|do not collect/i],
['an absolute guarantee', /\bguarantee/i],
['a per-event catalog', /event name|event catalog|property name/i],
Expand Down
65 changes: 53 additions & 12 deletions apps/website/src/app/privacy/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export default function PrivacyPage() {
<Section surface="canvas" ariaLabelledBy="privacy-heading">
<Container>
<div className="privacy-inner">
<Eyebrow tone="accent" className="privacy-eyebrow">Legal</Eyebrow>
<Eyebrow tone="accent" className="privacy-eyebrow">
Legal
</Eyebrow>
<h1 id="privacy-heading" className="privacy-h1">
Privacy
</h1>
Expand All @@ -51,18 +53,51 @@ export default function PrivacyPage() {
<p className="privacy-body">
We record how pages are used — pages viewed, referring source, and
broad technical details such as browser and approximate region — to
understand what people find useful. This is used in aggregate.
understand what people find useful. We also record content topics,
campaign and referral context, and interactions with setup commands.
A short-lived website session can connect this activity to a form
you submit, helping us understand your interest and respond in
context. We use both aggregate reporting and individual activity
history.
</p>

<h2 className="privacy-h2">Product analytics</h2>
<p className="privacy-body">
Threadplane software may report operational facts about how the
product is running so we can see which capabilities work in real
deployments. These reports describe activity, not content. We do not
want and do not seek the substance of what your application is
doing: prompts, messages, tool inputs and outputs, application
state, and source code are outside what this reporting is designed
to carry.
Installing Threadplane chat, LangGraph, AG-UI, or render packages
can automatically report package and environment details, a random
installation identifier, configured Git name and full email, and the
repository hosting provider and owner when available. This includes
CI installations, which are labeled as automation. Set
DO_NOT_TRACK=1 or TPLANE_TELEMETRY_DISABLED=1 before installation to
disable this collection. A usable install-captured email may receive
a short founder welcome sequence after the first linked development
browser use, with at most three emails and an unsubscribe option. CI
installation alone does not trigger this sequence.
</p>
<p className="privacy-body">
In Angular development builds, supported browser runtimes
automatically report package versions and progress such as a working
connection, completed interaction, restored conversation, or mounted
component. Production builds and server-side execution do not run
this automatic browser collection. A random browser-origin
identifier and a session with a 30-minute inactivity boundary
connect these reports; they do not identify a repository or verified
person. Storage failures fall back to memory. An opaque
package-local token can link development use to an installation; it
contains no email. Copied or cached installed packages can retain an
earlier token, so this link does not verify who is operating the
browser.
</p>
<p className="privacy-body">
Set the browser localStorage key THREADPLANE_TELEMETRY_DISABLED to
1, use the programmatic browser disable control, or configure
telemetry: false to disable automatic runtime collection. A custom
telemetry sink replaces the automatic destination for that runtime.
Development announcements may appear in the console; no click is
required to record progress. These reports exclude prompts,
messages, tool inputs and outputs, application state, source code,
private URLs, and credentials. They do not authorize marketing
email.
</p>

<h2 className="privacy-h2">How the information is used</h2>
Expand Down Expand Up @@ -95,8 +130,11 @@ export default function PrivacyPage() {
stops further marketing email. Replying to ask us to stop has the
same effect. To request deletion of the information we hold about
you, or to ask what that is, email{' '}
<a className="privacy-link" href={`mailto:${CONTACT_EMAIL}`}>{CONTACT_EMAIL}</a> and we will
act on it. Some records are kept where we are required to keep them.
<a className="privacy-link" href={`mailto:${CONTACT_EMAIL}`}>
{CONTACT_EMAIL}
</a>{' '}
and we will act on it. Some records are kept where we are required
to keep them.
</p>

<h2 className="privacy-h2">Security</h2>
Expand All @@ -121,7 +159,10 @@ export default function PrivacyPage() {
<h2 className="privacy-h2">Contact</h2>
<p className="privacy-body">
Questions about this page or about the information we hold:{' '}
<a className="privacy-link" href={`mailto:${CONTACT_EMAIL}`}>{CONTACT_EMAIL}</a>.
<a className="privacy-link" href={`mailto:${CONTACT_EMAIL}`}>
{CONTACT_EMAIL}
</a>
.
</p>
</div>
</Container>
Expand Down
Loading
Loading