@vostride/agent-qa-analytics 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/index.cjs +5 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -82
- package/dist/index.d.ts +2 -82
- package/dist/index.js +5 -44
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.cts
CHANGED
|
@@ -12,33 +12,14 @@ import { RunEventEnvelope } from '@vostride/agent-qa-schema';
|
|
|
12
12
|
*/
|
|
13
13
|
declare const ANALYTICS_VOCABULARY_META_KEY = "analyticsVocabulary";
|
|
14
14
|
declare const ANALYTICS_KNOWN_ID_MARKER = "known-id";
|
|
15
|
-
/**
|
|
16
|
-
* The known-id fields' guards, as FUNCTIONS a host can call (398-10).
|
|
17
|
-
*
|
|
18
|
-
* Published beside the schemas because an emitter has to make the same decision the schema makes
|
|
19
|
-
* and has to make it BEFORE capture: nothing parses an event on the way to the transport, so a
|
|
20
|
-
* foreign string put on `run_id` is not refused, it is SENT — and `run_id` is a known-id codec
|
|
21
|
-
* precisely so that cannot happen. A host filtering ids for itself would need `@vostride/agent-qa-ids`,
|
|
22
|
-
* which the surface firewall does not admit; asking the package that owns the union is the route
|
|
23
|
-
* that exists. Anything that is not a canonical id becomes an absence, which every field allows.
|
|
24
|
-
*/
|
|
25
15
|
declare function analyticsRunId(value: unknown): string | undefined;
|
|
26
16
|
declare function analyticsTestId(value: unknown): string | undefined;
|
|
27
17
|
declare function analyticsSuiteId(value: unknown): string | undefined;
|
|
28
|
-
/**
|
|
29
|
-
* WHICH HOST EMITTED, and deliberately NOT the 397 D-04 surface roster.
|
|
30
|
-
*
|
|
31
|
-
* 2026-08-17. The roster answers "which invocation doors does the parity suite measure" and is
|
|
32
|
-
* pinned closed at `chat` and `mcp`. This list answers a different question — which process sent
|
|
33
|
-
* the event — and `cli` and `daemon` are real answers to it while being no kind of surface. Tying
|
|
34
|
-
* the two would make a roster change an analytics schema change for no reason, and would make this
|
|
35
|
-
* list wrong the moment a host emits that is not a door.
|
|
36
|
-
*/
|
|
37
18
|
declare const ANALYTICS_EMITTING_HOSTS: readonly ["cli", "chat", "mcp", "daemon"];
|
|
38
19
|
declare const ANALYTICS_RUNTIME_CONTEXTS: readonly ["user", "ci"];
|
|
39
20
|
declare const ANALYTICS_AGENT_PRODUCTS: readonly ["claude_code", "cursor", "gemini_cli", "augment", "goose", "opencode", "codex", "cline", "amp", "unknown"];
|
|
40
21
|
declare const ANALYTICS_TRIGGER_SOURCES: readonly ["cli", "chat", "mcp", "daemon", "unknown"];
|
|
41
|
-
declare const ANALYTICS_RUNNERS: readonly ["local", "
|
|
22
|
+
declare const ANALYTICS_RUNNERS: readonly ["local", "unknown"];
|
|
42
23
|
declare const ANALYTICS_PLATFORMS: readonly ["web", "android", "ios", "unknown"];
|
|
43
24
|
declare const ANALYTICS_RUN_STATUSES: readonly ["passed", "failed", "skipped", "cancelled", "timeout", "unknown"];
|
|
44
25
|
declare const ANALYTICS_SUITE_EXECUTION_MODES: readonly ["sequential", "parallel", "unknown"];
|
|
@@ -59,31 +40,8 @@ declare const ANALYTICS_CHAT_END_REASONS: readonly ["exit", "interrupt", "error"
|
|
|
59
40
|
* thing a second transcription does is survive a rename that the first one did not.
|
|
60
41
|
*/
|
|
61
42
|
declare const ANALYTICS_MCP_TOOL_NAMES: [string, ...string[]];
|
|
62
|
-
/**
|
|
63
|
-
* The membership, in the order D-18 states it.
|
|
64
|
-
*
|
|
65
|
-
* The names keep the `agent-qa.` prefix the v33 set used: the events land in a project shared with
|
|
66
|
-
* nothing else today, but a prefix costs nothing and un-prefixing later is a rename of history.
|
|
67
|
-
*/
|
|
68
43
|
declare const ANALYTICS_EVENT_NAMES: readonly ["agent-qa.test_run.completed", "agent-qa.suite_run.completed", "agent-qa.chat.session.completed", "agent-qa.chat.turn.completed", "agent-qa.daemon.job.completed", "agent-qa.mcp.server.lifecycle", "agent-qa.mcp.tool.invoked", "agent-qa.init.completed", "agent-qa.explain.answered", "agent-qa.explain.abstained"];
|
|
69
44
|
type AnalyticsEventName = (typeof ANALYTICS_EVENT_NAMES)[number];
|
|
70
|
-
/**
|
|
71
|
-
* The v33 names that are NOT members, listed rather than merely absent.
|
|
72
|
-
*
|
|
73
|
-
* 2026-08-17 (Phase 398 plan 03, D-18 · RESEARCH Open Question 2). Two different reasons:
|
|
74
|
-
*
|
|
75
|
-
* `analytics.initialized` and `analytics.test_event` were TRANSPORT SELF-TESTS — an event whose
|
|
76
|
-
* only content is "the thing that sends events works" is a diagnostic, and shipping it as product
|
|
77
|
-
* signal means every dashboard has to remember to exclude it. Dropped outright; the zero-network
|
|
78
|
-
* differential in plan 06 is the same claim, proven rather than reported.
|
|
79
|
-
*
|
|
80
|
-
* The three `dashboard.*` rows are dropped BECAUSE THE DASHBOARD IS BEING REBUILT. M5 re-adds
|
|
81
|
-
* what it needs against this port, which is the right moment to decide what a rebuilt dashboard
|
|
82
|
-
* is actually worth measuring — carrying the v1 answers forward would have pre-decided it.
|
|
83
|
-
*
|
|
84
|
-
* RETUNE: a name re-added here must be REMOVED from this list in the same change. The disjointness
|
|
85
|
-
* cell fires if it appears in both, so the two lists cannot quietly disagree.
|
|
86
|
-
*/
|
|
87
45
|
declare const RETIRED_V33_EVENT_NAMES: readonly ["agent-qa.analytics.initialized", "agent-qa.analytics.test_event", "agent-qa.dashboard.opened", "agent-qa.dashboard.live_mode.started", "agent-qa.dashboard.entity.created"];
|
|
88
46
|
declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
89
47
|
readonly 'agent-qa.test_run.completed': z.ZodObject<{
|
|
@@ -125,7 +83,6 @@ declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
|
125
83
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
126
84
|
unknown: "unknown";
|
|
127
85
|
local: "local";
|
|
128
|
-
browserstack: "browserstack";
|
|
129
86
|
}>>;
|
|
130
87
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
131
88
|
unknown: "unknown";
|
|
@@ -217,7 +174,6 @@ declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
|
217
174
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
218
175
|
unknown: "unknown";
|
|
219
176
|
local: "local";
|
|
220
|
-
browserstack: "browserstack";
|
|
221
177
|
}>>;
|
|
222
178
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
223
179
|
unknown: "unknown";
|
|
@@ -619,7 +575,6 @@ declare const ANALYTICS_EVENT_MEMBERS: readonly [z.ZodObject<{
|
|
|
619
575
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
620
576
|
unknown: "unknown";
|
|
621
577
|
local: "local";
|
|
622
|
-
browserstack: "browserstack";
|
|
623
578
|
}>>;
|
|
624
579
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
625
580
|
unknown: "unknown";
|
|
@@ -710,7 +665,6 @@ declare const ANALYTICS_EVENT_MEMBERS: readonly [z.ZodObject<{
|
|
|
710
665
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
711
666
|
unknown: "unknown";
|
|
712
667
|
local: "local";
|
|
713
|
-
browserstack: "browserstack";
|
|
714
668
|
}>>;
|
|
715
669
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
716
670
|
unknown: "unknown";
|
|
@@ -1103,7 +1057,6 @@ declare const AgentQaAnalyticsEventSchema: z.ZodDiscriminatedUnion<readonly [z.Z
|
|
|
1103
1057
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
1104
1058
|
unknown: "unknown";
|
|
1105
1059
|
local: "local";
|
|
1106
|
-
browserstack: "browserstack";
|
|
1107
1060
|
}>>;
|
|
1108
1061
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
1109
1062
|
unknown: "unknown";
|
|
@@ -1194,7 +1147,6 @@ declare const AgentQaAnalyticsEventSchema: z.ZodDiscriminatedUnion<readonly [z.Z
|
|
|
1194
1147
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
1195
1148
|
unknown: "unknown";
|
|
1196
1149
|
local: "local";
|
|
1197
|
-
browserstack: "browserstack";
|
|
1198
1150
|
}>>;
|
|
1199
1151
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
1200
1152
|
unknown: "unknown";
|
|
@@ -1606,20 +1558,9 @@ interface AnalyticsPort<TEvent extends AnalyticsEventEnvelope = AnalyticsEventEn
|
|
|
1606
1558
|
shutdown(): Promise<void>;
|
|
1607
1559
|
}
|
|
1608
1560
|
interface AnalyticsPortOptions {
|
|
1609
|
-
/** Absent when opted out or keyless: the port then drops every capture
|
|
1561
|
+
/** Absent when opted out or keyless: the port then drops every capture. */
|
|
1610
1562
|
readonly transport?: AnalyticsTransport;
|
|
1611
1563
|
readonly identity: AnalyticsIdentityResolver;
|
|
1612
|
-
/**
|
|
1613
|
-
* How long `shutdown` will wait for the transport before releasing its caller (T-398-35 / WR-02).
|
|
1614
|
-
*
|
|
1615
|
-
* A host calls `shutdown` on its way OUT — at a terminal exit, in a `finally`, on the error path.
|
|
1616
|
-
* Whatever it waits for is time a person spends looking at a terminal that has already finished
|
|
1617
|
-
* its work. posthog-node's own shutdown defaults to 30 000 ms and races retriable flushes against
|
|
1618
|
-
* it, so an unreachable network turns a completed run into half a minute of dead prompt.
|
|
1619
|
-
*
|
|
1620
|
-
* The default is deliberately small. This is the LAST thing a process does; delivering the final
|
|
1621
|
-
* batch is worth a couple of seconds and no more.
|
|
1622
|
-
*/
|
|
1623
1564
|
readonly shutdownTimeoutMs?: number;
|
|
1624
1565
|
}
|
|
1625
1566
|
declare function createAnalyticsPort<TEvent extends AnalyticsEventEnvelope = AnalyticsEventEnvelope>(options: AnalyticsPortOptions): AnalyticsPort<TEvent>;
|
|
@@ -1636,15 +1577,6 @@ interface AnalyticsMaskingContext {
|
|
|
1636
1577
|
*/
|
|
1637
1578
|
declare function maskEventForSend(payload: AnalyticsCapturePayload, context: AnalyticsMaskingContext | undefined): AnalyticsCapturePayload;
|
|
1638
1579
|
|
|
1639
|
-
/**
|
|
1640
|
-
* The subset of the PostHog client this transport uses.
|
|
1641
|
-
*
|
|
1642
|
-
* `flush` is REQUIRED, not optional, and that is the whole content of the fix (398-15, CR-01): the
|
|
1643
|
-
* interface previously declared only `capture`/`shutdown`, so the type system could not express the
|
|
1644
|
-
* correct per-boundary call and the transport reached for `shutdown` instead. An optional member
|
|
1645
|
-
* with a shutdown fallback would reinstate the same defect behind a spelling. posthog-node 5.33.2
|
|
1646
|
-
* exposes both. The contract being honoured is stated at `port.ts:49-51`.
|
|
1647
|
-
*/
|
|
1648
1580
|
interface PosthogAnalyticsClient {
|
|
1649
1581
|
capture(input: {
|
|
1650
1582
|
distinctId: string;
|
|
@@ -1658,14 +1590,6 @@ interface PosthogTransportOptions {
|
|
|
1658
1590
|
readonly projectKey: string;
|
|
1659
1591
|
readonly host: string;
|
|
1660
1592
|
readonly disableGeoip?: boolean;
|
|
1661
|
-
/**
|
|
1662
|
-
* posthog-node's own `fetch` option, passed straight through.
|
|
1663
|
-
*
|
|
1664
|
-
* 2026-08-17 (Phase 398 plan 03): this exists for plan 06's sandboxed-sink arm of the zero-network
|
|
1665
|
-
* differential. posthog-node 5.33.2 sends through `this.options.fetch ?? fetch`, so injecting here
|
|
1666
|
-
* observes the SHIPPED send path rather than a parallel one built for the test — which is the
|
|
1667
|
-
* difference between measuring the product and measuring the fixture.
|
|
1668
|
-
*/
|
|
1669
1593
|
readonly fetch?: PostHogOptions['fetch'];
|
|
1670
1594
|
/** The host's declared-secret mask, applied at the send seam. Absent means no masking. */
|
|
1671
1595
|
readonly maskingContext?: AnalyticsMaskingContext;
|
|
@@ -1688,10 +1612,6 @@ interface ComposeAnalyticsOptions {
|
|
|
1688
1612
|
readonly identity?: AnalyticsIdentityResolver;
|
|
1689
1613
|
readonly projectKey?: string;
|
|
1690
1614
|
readonly projectHost?: string;
|
|
1691
|
-
/**
|
|
1692
|
-
* The host's declared-secret mask, which the send seam applies (D-19, plan 06). It reaches only
|
|
1693
|
-
* the opted-IN arm: the deny arm has no transport, so there is no payload to mask.
|
|
1694
|
-
*/
|
|
1695
1615
|
readonly maskingContext?: AnalyticsMaskingContext;
|
|
1696
1616
|
/** Replaces the transport on the OPTED-IN arm only. Never consulted once the answer is deny. */
|
|
1697
1617
|
readonly transportOverride?: AnalyticsTransport;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,33 +12,14 @@ import { RunEventEnvelope } from '@vostride/agent-qa-schema';
|
|
|
12
12
|
*/
|
|
13
13
|
declare const ANALYTICS_VOCABULARY_META_KEY = "analyticsVocabulary";
|
|
14
14
|
declare const ANALYTICS_KNOWN_ID_MARKER = "known-id";
|
|
15
|
-
/**
|
|
16
|
-
* The known-id fields' guards, as FUNCTIONS a host can call (398-10).
|
|
17
|
-
*
|
|
18
|
-
* Published beside the schemas because an emitter has to make the same decision the schema makes
|
|
19
|
-
* and has to make it BEFORE capture: nothing parses an event on the way to the transport, so a
|
|
20
|
-
* foreign string put on `run_id` is not refused, it is SENT — and `run_id` is a known-id codec
|
|
21
|
-
* precisely so that cannot happen. A host filtering ids for itself would need `@vostride/agent-qa-ids`,
|
|
22
|
-
* which the surface firewall does not admit; asking the package that owns the union is the route
|
|
23
|
-
* that exists. Anything that is not a canonical id becomes an absence, which every field allows.
|
|
24
|
-
*/
|
|
25
15
|
declare function analyticsRunId(value: unknown): string | undefined;
|
|
26
16
|
declare function analyticsTestId(value: unknown): string | undefined;
|
|
27
17
|
declare function analyticsSuiteId(value: unknown): string | undefined;
|
|
28
|
-
/**
|
|
29
|
-
* WHICH HOST EMITTED, and deliberately NOT the 397 D-04 surface roster.
|
|
30
|
-
*
|
|
31
|
-
* 2026-08-17. The roster answers "which invocation doors does the parity suite measure" and is
|
|
32
|
-
* pinned closed at `chat` and `mcp`. This list answers a different question — which process sent
|
|
33
|
-
* the event — and `cli` and `daemon` are real answers to it while being no kind of surface. Tying
|
|
34
|
-
* the two would make a roster change an analytics schema change for no reason, and would make this
|
|
35
|
-
* list wrong the moment a host emits that is not a door.
|
|
36
|
-
*/
|
|
37
18
|
declare const ANALYTICS_EMITTING_HOSTS: readonly ["cli", "chat", "mcp", "daemon"];
|
|
38
19
|
declare const ANALYTICS_RUNTIME_CONTEXTS: readonly ["user", "ci"];
|
|
39
20
|
declare const ANALYTICS_AGENT_PRODUCTS: readonly ["claude_code", "cursor", "gemini_cli", "augment", "goose", "opencode", "codex", "cline", "amp", "unknown"];
|
|
40
21
|
declare const ANALYTICS_TRIGGER_SOURCES: readonly ["cli", "chat", "mcp", "daemon", "unknown"];
|
|
41
|
-
declare const ANALYTICS_RUNNERS: readonly ["local", "
|
|
22
|
+
declare const ANALYTICS_RUNNERS: readonly ["local", "unknown"];
|
|
42
23
|
declare const ANALYTICS_PLATFORMS: readonly ["web", "android", "ios", "unknown"];
|
|
43
24
|
declare const ANALYTICS_RUN_STATUSES: readonly ["passed", "failed", "skipped", "cancelled", "timeout", "unknown"];
|
|
44
25
|
declare const ANALYTICS_SUITE_EXECUTION_MODES: readonly ["sequential", "parallel", "unknown"];
|
|
@@ -59,31 +40,8 @@ declare const ANALYTICS_CHAT_END_REASONS: readonly ["exit", "interrupt", "error"
|
|
|
59
40
|
* thing a second transcription does is survive a rename that the first one did not.
|
|
60
41
|
*/
|
|
61
42
|
declare const ANALYTICS_MCP_TOOL_NAMES: [string, ...string[]];
|
|
62
|
-
/**
|
|
63
|
-
* The membership, in the order D-18 states it.
|
|
64
|
-
*
|
|
65
|
-
* The names keep the `agent-qa.` prefix the v33 set used: the events land in a project shared with
|
|
66
|
-
* nothing else today, but a prefix costs nothing and un-prefixing later is a rename of history.
|
|
67
|
-
*/
|
|
68
43
|
declare const ANALYTICS_EVENT_NAMES: readonly ["agent-qa.test_run.completed", "agent-qa.suite_run.completed", "agent-qa.chat.session.completed", "agent-qa.chat.turn.completed", "agent-qa.daemon.job.completed", "agent-qa.mcp.server.lifecycle", "agent-qa.mcp.tool.invoked", "agent-qa.init.completed", "agent-qa.explain.answered", "agent-qa.explain.abstained"];
|
|
69
44
|
type AnalyticsEventName = (typeof ANALYTICS_EVENT_NAMES)[number];
|
|
70
|
-
/**
|
|
71
|
-
* The v33 names that are NOT members, listed rather than merely absent.
|
|
72
|
-
*
|
|
73
|
-
* 2026-08-17 (Phase 398 plan 03, D-18 · RESEARCH Open Question 2). Two different reasons:
|
|
74
|
-
*
|
|
75
|
-
* `analytics.initialized` and `analytics.test_event` were TRANSPORT SELF-TESTS — an event whose
|
|
76
|
-
* only content is "the thing that sends events works" is a diagnostic, and shipping it as product
|
|
77
|
-
* signal means every dashboard has to remember to exclude it. Dropped outright; the zero-network
|
|
78
|
-
* differential in plan 06 is the same claim, proven rather than reported.
|
|
79
|
-
*
|
|
80
|
-
* The three `dashboard.*` rows are dropped BECAUSE THE DASHBOARD IS BEING REBUILT. M5 re-adds
|
|
81
|
-
* what it needs against this port, which is the right moment to decide what a rebuilt dashboard
|
|
82
|
-
* is actually worth measuring — carrying the v1 answers forward would have pre-decided it.
|
|
83
|
-
*
|
|
84
|
-
* RETUNE: a name re-added here must be REMOVED from this list in the same change. The disjointness
|
|
85
|
-
* cell fires if it appears in both, so the two lists cannot quietly disagree.
|
|
86
|
-
*/
|
|
87
45
|
declare const RETIRED_V33_EVENT_NAMES: readonly ["agent-qa.analytics.initialized", "agent-qa.analytics.test_event", "agent-qa.dashboard.opened", "agent-qa.dashboard.live_mode.started", "agent-qa.dashboard.entity.created"];
|
|
88
46
|
declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
89
47
|
readonly 'agent-qa.test_run.completed': z.ZodObject<{
|
|
@@ -125,7 +83,6 @@ declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
|
125
83
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
126
84
|
unknown: "unknown";
|
|
127
85
|
local: "local";
|
|
128
|
-
browserstack: "browserstack";
|
|
129
86
|
}>>;
|
|
130
87
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
131
88
|
unknown: "unknown";
|
|
@@ -217,7 +174,6 @@ declare const ANALYTICS_EVENT_SCHEMA_BY_NAME: {
|
|
|
217
174
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
218
175
|
unknown: "unknown";
|
|
219
176
|
local: "local";
|
|
220
|
-
browserstack: "browserstack";
|
|
221
177
|
}>>;
|
|
222
178
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
223
179
|
unknown: "unknown";
|
|
@@ -619,7 +575,6 @@ declare const ANALYTICS_EVENT_MEMBERS: readonly [z.ZodObject<{
|
|
|
619
575
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
620
576
|
unknown: "unknown";
|
|
621
577
|
local: "local";
|
|
622
|
-
browserstack: "browserstack";
|
|
623
578
|
}>>;
|
|
624
579
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
625
580
|
unknown: "unknown";
|
|
@@ -710,7 +665,6 @@ declare const ANALYTICS_EVENT_MEMBERS: readonly [z.ZodObject<{
|
|
|
710
665
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
711
666
|
unknown: "unknown";
|
|
712
667
|
local: "local";
|
|
713
|
-
browserstack: "browserstack";
|
|
714
668
|
}>>;
|
|
715
669
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
716
670
|
unknown: "unknown";
|
|
@@ -1103,7 +1057,6 @@ declare const AgentQaAnalyticsEventSchema: z.ZodDiscriminatedUnion<readonly [z.Z
|
|
|
1103
1057
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
1104
1058
|
unknown: "unknown";
|
|
1105
1059
|
local: "local";
|
|
1106
|
-
browserstack: "browserstack";
|
|
1107
1060
|
}>>;
|
|
1108
1061
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
1109
1062
|
unknown: "unknown";
|
|
@@ -1194,7 +1147,6 @@ declare const AgentQaAnalyticsEventSchema: z.ZodDiscriminatedUnion<readonly [z.Z
|
|
|
1194
1147
|
runner: z.ZodOptional<z.ZodEnum<{
|
|
1195
1148
|
unknown: "unknown";
|
|
1196
1149
|
local: "local";
|
|
1197
|
-
browserstack: "browserstack";
|
|
1198
1150
|
}>>;
|
|
1199
1151
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
1200
1152
|
unknown: "unknown";
|
|
@@ -1606,20 +1558,9 @@ interface AnalyticsPort<TEvent extends AnalyticsEventEnvelope = AnalyticsEventEn
|
|
|
1606
1558
|
shutdown(): Promise<void>;
|
|
1607
1559
|
}
|
|
1608
1560
|
interface AnalyticsPortOptions {
|
|
1609
|
-
/** Absent when opted out or keyless: the port then drops every capture
|
|
1561
|
+
/** Absent when opted out or keyless: the port then drops every capture. */
|
|
1610
1562
|
readonly transport?: AnalyticsTransport;
|
|
1611
1563
|
readonly identity: AnalyticsIdentityResolver;
|
|
1612
|
-
/**
|
|
1613
|
-
* How long `shutdown` will wait for the transport before releasing its caller (T-398-35 / WR-02).
|
|
1614
|
-
*
|
|
1615
|
-
* A host calls `shutdown` on its way OUT — at a terminal exit, in a `finally`, on the error path.
|
|
1616
|
-
* Whatever it waits for is time a person spends looking at a terminal that has already finished
|
|
1617
|
-
* its work. posthog-node's own shutdown defaults to 30 000 ms and races retriable flushes against
|
|
1618
|
-
* it, so an unreachable network turns a completed run into half a minute of dead prompt.
|
|
1619
|
-
*
|
|
1620
|
-
* The default is deliberately small. This is the LAST thing a process does; delivering the final
|
|
1621
|
-
* batch is worth a couple of seconds and no more.
|
|
1622
|
-
*/
|
|
1623
1564
|
readonly shutdownTimeoutMs?: number;
|
|
1624
1565
|
}
|
|
1625
1566
|
declare function createAnalyticsPort<TEvent extends AnalyticsEventEnvelope = AnalyticsEventEnvelope>(options: AnalyticsPortOptions): AnalyticsPort<TEvent>;
|
|
@@ -1636,15 +1577,6 @@ interface AnalyticsMaskingContext {
|
|
|
1636
1577
|
*/
|
|
1637
1578
|
declare function maskEventForSend(payload: AnalyticsCapturePayload, context: AnalyticsMaskingContext | undefined): AnalyticsCapturePayload;
|
|
1638
1579
|
|
|
1639
|
-
/**
|
|
1640
|
-
* The subset of the PostHog client this transport uses.
|
|
1641
|
-
*
|
|
1642
|
-
* `flush` is REQUIRED, not optional, and that is the whole content of the fix (398-15, CR-01): the
|
|
1643
|
-
* interface previously declared only `capture`/`shutdown`, so the type system could not express the
|
|
1644
|
-
* correct per-boundary call and the transport reached for `shutdown` instead. An optional member
|
|
1645
|
-
* with a shutdown fallback would reinstate the same defect behind a spelling. posthog-node 5.33.2
|
|
1646
|
-
* exposes both. The contract being honoured is stated at `port.ts:49-51`.
|
|
1647
|
-
*/
|
|
1648
1580
|
interface PosthogAnalyticsClient {
|
|
1649
1581
|
capture(input: {
|
|
1650
1582
|
distinctId: string;
|
|
@@ -1658,14 +1590,6 @@ interface PosthogTransportOptions {
|
|
|
1658
1590
|
readonly projectKey: string;
|
|
1659
1591
|
readonly host: string;
|
|
1660
1592
|
readonly disableGeoip?: boolean;
|
|
1661
|
-
/**
|
|
1662
|
-
* posthog-node's own `fetch` option, passed straight through.
|
|
1663
|
-
*
|
|
1664
|
-
* 2026-08-17 (Phase 398 plan 03): this exists for plan 06's sandboxed-sink arm of the zero-network
|
|
1665
|
-
* differential. posthog-node 5.33.2 sends through `this.options.fetch ?? fetch`, so injecting here
|
|
1666
|
-
* observes the SHIPPED send path rather than a parallel one built for the test — which is the
|
|
1667
|
-
* difference between measuring the product and measuring the fixture.
|
|
1668
|
-
*/
|
|
1669
1593
|
readonly fetch?: PostHogOptions['fetch'];
|
|
1670
1594
|
/** The host's declared-secret mask, applied at the send seam. Absent means no masking. */
|
|
1671
1595
|
readonly maskingContext?: AnalyticsMaskingContext;
|
|
@@ -1688,10 +1612,6 @@ interface ComposeAnalyticsOptions {
|
|
|
1688
1612
|
readonly identity?: AnalyticsIdentityResolver;
|
|
1689
1613
|
readonly projectKey?: string;
|
|
1690
1614
|
readonly projectHost?: string;
|
|
1691
|
-
/**
|
|
1692
|
-
* The host's declared-secret mask, which the send seam applies (D-19, plan 06). It reaches only
|
|
1693
|
-
* the opted-IN arm: the deny arm has no transport, so there is no payload to mask.
|
|
1694
|
-
*/
|
|
1695
1615
|
readonly maskingContext?: AnalyticsMaskingContext;
|
|
1696
1616
|
/** Replaces the transport on the OPTED-IN arm only. Never consulted once the answer is deny. */
|
|
1697
1617
|
readonly transportOverride?: AnalyticsTransport;
|
package/dist/index.js
CHANGED
|
@@ -12,21 +12,7 @@ var BestEffortAnalyticsPort = class {
|
|
|
12
12
|
this.identity = options.identity;
|
|
13
13
|
this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? DEFAULT_ANALYTICS_SHUTDOWN_TIMEOUT_MS;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
* There is NO try/catch here, and the absence is deliberate — measured, not assumed.
|
|
17
|
-
*
|
|
18
|
-
* 2026-08-17 (Phase 398 plan 03, mutation leg M6). This method was first written with the v1
|
|
19
|
-
* module's `try { ... } catch {}` wrapper around the body. Deleting that wrapper outright left
|
|
20
|
-
* every cell green, including the one named "never throws into the host", because there is no
|
|
21
|
-
* synchronous throw for it to catch: an `async` function body's throw — including one out of the
|
|
22
|
-
* identity resolver, the only caller-supplied code reachable from here — becomes a REJECTED
|
|
23
|
-
* PROMISE, never a synchronous exception. The wrapper read like the boundary and guarded nothing.
|
|
24
|
-
*
|
|
25
|
-
* The real boundary is the `.catch` below, attached in the same tick the promise is created so a
|
|
26
|
-
* rejection cannot become an unhandled rejection even if `flush()` is never called — which is the
|
|
27
|
-
* normal case for a host that emits on a path it does not await. Leg M8 removes that `.catch` and
|
|
28
|
-
* reds; that is the guard this method actually has.
|
|
29
|
-
*/
|
|
15
|
+
// An async body never throws synchronously; the `.catch` below, attached at once, is the guard.
|
|
30
16
|
capture(event) {
|
|
31
17
|
const transport = this.transport;
|
|
32
18
|
if (transport === void 0) return;
|
|
@@ -43,17 +29,7 @@ var BestEffortAnalyticsPort = class {
|
|
|
43
29
|
})
|
|
44
30
|
);
|
|
45
31
|
}
|
|
46
|
-
|
|
47
|
-
* EDGE ANLY-01/empty. A flush with nothing captured touches the transport at ALL — not "makes no
|
|
48
|
-
* network call", but makes no call. The distinction matters because `flush` on the PostHog
|
|
49
|
-
* transport is a send, so a host that flushes on every command return would otherwise poke the
|
|
50
|
-
* wire once per invocation whether or not anything happened. Zero captures in, zero calls out,
|
|
51
|
-
* and the promise still resolves.
|
|
52
|
-
*
|
|
53
|
-
* 2026-10-21 (447-07, D-20): flush awaits the local captures and STARTS the send without awaiting
|
|
54
|
-
* it, so a stalled network never holds a run's verdict or a command's return; `shutdown` waits
|
|
55
|
-
* for the started sends inside its bound.
|
|
56
|
-
*/
|
|
32
|
+
// Zero captures make no transport call; `flush` starts the send without awaiting it.
|
|
57
33
|
async flush() {
|
|
58
34
|
const transport = this.transport;
|
|
59
35
|
if (transport === void 0) return;
|
|
@@ -150,15 +126,7 @@ var PosthogTransport = class {
|
|
|
150
126
|
...options.fetch ? { fetch: options.fetch } : {}
|
|
151
127
|
});
|
|
152
128
|
}
|
|
153
|
-
|
|
154
|
-
* THE SEND CHOKE POINT. Masking is the first thing that happens here and it happens exactly once
|
|
155
|
-
* (a cell counts the call sites in shipped source and refuses any number but one).
|
|
156
|
-
*
|
|
157
|
-
* `flush`/`shutdown` are not second seams: everything they drain arrived through this method, so
|
|
158
|
-
* masking here ranges the whole queue. `is_internal` is added AFTER the mask because it is a
|
|
159
|
-
* boolean this file writes — there is no caller string in it to mask. It is an event property,
|
|
160
|
-
* sent only when true (447-07, D-13).
|
|
161
|
-
*/
|
|
129
|
+
// The one masking site; `is_internal` is added after the mask because this file writes it.
|
|
162
130
|
capture(payload) {
|
|
163
131
|
let masked;
|
|
164
132
|
try {
|
|
@@ -267,7 +235,7 @@ var ANALYTICS_AGENT_PRODUCTS = [
|
|
|
267
235
|
"unknown"
|
|
268
236
|
];
|
|
269
237
|
var ANALYTICS_TRIGGER_SOURCES = ["cli", "chat", "mcp", "daemon", "unknown"];
|
|
270
|
-
var ANALYTICS_RUNNERS = ["local", "
|
|
238
|
+
var ANALYTICS_RUNNERS = ["local", "unknown"];
|
|
271
239
|
var ANALYTICS_PLATFORMS = ["web", "android", "ios", "unknown"];
|
|
272
240
|
var ANALYTICS_RUN_STATUSES = [
|
|
273
241
|
"passed",
|
|
@@ -340,10 +308,6 @@ var RunCompletionShape = {
|
|
|
340
308
|
failure_category: z.enum(ANALYTICS_FAILURE_CATEGORIES).optional(),
|
|
341
309
|
duration_ms: NonNegativeNumber.optional(),
|
|
342
310
|
run_id: RunIdSchema.optional(),
|
|
343
|
-
// 2026-08-28 (405-13, D-21/DETL-06). The parent-link property was deleted here. It was declared
|
|
344
|
-
// with no producer anywhere in this package — 402-09 put one run row per run and moved member
|
|
345
|
-
// identity onto handles, and 405-07 then deleted the column it named — so it could only ever
|
|
346
|
-
// have travelled as `undefined`. Same disposition and same reason as the healing counter below.
|
|
347
311
|
step_count: NonNegativeInteger.optional(),
|
|
348
312
|
passed_step_count: NonNegativeInteger.optional(),
|
|
349
313
|
failed_step_count: NonNegativeInteger.optional(),
|
|
@@ -770,10 +734,7 @@ function createAnalyticsRunSubscriber(options) {
|
|
|
770
734
|
retry_count: record.retryCount,
|
|
771
735
|
is_flaky: record.retryCount !== void 0 ? status === "passed" && record.retryCount > 0 : void 0,
|
|
772
736
|
...steps,
|
|
773
|
-
//
|
|
774
|
-
// would drop three `undefined`s anyway, but building them unconditionally would mean writing
|
|
775
|
-
// `input_tokens: tokens?.input ?? 0` somewhere — and that `?? 0` is exactly the fabricated
|
|
776
|
-
// measurement this whole path refuses to make.
|
|
737
|
+
// Spread in only when a step is measured; a `?? 0` here would fabricate a measurement.
|
|
777
738
|
...record.steps.tokens === void 0 ? {} : {
|
|
778
739
|
input_tokens: record.steps.tokens.input,
|
|
779
740
|
output_tokens: record.steps.tokens.output,
|