@claudexor/orchestrator 3.0.3 → 3.1.0

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.
Files changed (65) hide show
  1. package/LICENSE +1 -1
  2. package/dist/attemptFinalize.d.ts +162 -0
  3. package/dist/attemptFinalize.d.ts.map +1 -0
  4. package/dist/attemptFinalize.js +368 -0
  5. package/dist/attemptFinalize.js.map +1 -0
  6. package/dist/attemptTelemetry.d.ts +88 -7
  7. package/dist/attemptTelemetry.d.ts.map +1 -1
  8. package/dist/attemptTelemetry.js +276 -14
  9. package/dist/attemptTelemetry.js.map +1 -1
  10. package/dist/budgetFailure.d.ts +70 -0
  11. package/dist/budgetFailure.d.ts.map +1 -0
  12. package/dist/budgetFailure.js +114 -0
  13. package/dist/budgetFailure.js.map +1 -0
  14. package/dist/candidateEvidence.d.ts +55 -1
  15. package/dist/candidateEvidence.d.ts.map +1 -1
  16. package/dist/candidateEvidence.js +72 -0
  17. package/dist/candidateEvidence.js.map +1 -1
  18. package/dist/continuation.d.ts +77 -0
  19. package/dist/continuation.d.ts.map +1 -0
  20. package/dist/continuation.js +92 -0
  21. package/dist/continuation.js.map +1 -0
  22. package/dist/deepScanReducer.d.ts +148 -0
  23. package/dist/deepScanReducer.d.ts.map +1 -0
  24. package/dist/deepScanReducer.js +352 -0
  25. package/dist/deepScanReducer.js.map +1 -0
  26. package/dist/harnessFailure.d.ts +27 -0
  27. package/dist/harnessFailure.d.ts.map +1 -0
  28. package/dist/harnessFailure.js +61 -0
  29. package/dist/harnessFailure.js.map +1 -0
  30. package/dist/orchestrator.d.ts +97 -10
  31. package/dist/orchestrator.d.ts.map +1 -1
  32. package/dist/orchestrator.js +1213 -295
  33. package/dist/orchestrator.js.map +1 -1
  34. package/dist/planQuestions.d.ts.map +1 -1
  35. package/dist/planQuestions.js +105 -50
  36. package/dist/planQuestions.js.map +1 -1
  37. package/dist/planRun.d.ts +15 -7
  38. package/dist/planRun.d.ts.map +1 -1
  39. package/dist/planRun.js +88 -42
  40. package/dist/planRun.js.map +1 -1
  41. package/dist/planTerminal.d.ts +36 -0
  42. package/dist/planTerminal.d.ts.map +1 -0
  43. package/dist/planTerminal.js +63 -0
  44. package/dist/planTerminal.js.map +1 -0
  45. package/dist/runSupport.d.ts +50 -2
  46. package/dist/runSupport.d.ts.map +1 -1
  47. package/dist/runSupport.js +60 -6
  48. package/dist/runSupport.js.map +1 -1
  49. package/dist/runTelemetryWriter.d.ts +6 -1
  50. package/dist/runTelemetryWriter.d.ts.map +1 -1
  51. package/dist/runTelemetryWriter.js +2 -0
  52. package/dist/runTelemetryWriter.js.map +1 -1
  53. package/dist/runTerminals.d.ts +15 -4
  54. package/dist/runTerminals.d.ts.map +1 -1
  55. package/dist/runTerminals.js +16 -1
  56. package/dist/runTerminals.js.map +1 -1
  57. package/dist/structuredOutput.d.ts +27 -0
  58. package/dist/structuredOutput.d.ts.map +1 -1
  59. package/dist/structuredOutput.js +65 -7
  60. package/dist/structuredOutput.js.map +1 -1
  61. package/dist/transientClassify.d.ts +59 -0
  62. package/dist/transientClassify.d.ts.map +1 -0
  63. package/dist/transientClassify.js +109 -0
  64. package/dist/transientClassify.js.map +1 -0
  65. package/package.json +17 -17
@@ -5,7 +5,9 @@
5
5
  * no regex over prose, and a tool error is "recovered" only when the SAME
6
6
  * tool later succeeds against the SAME target.
7
7
  */
8
- import type { AttemptTelemetryRecord, AuthSourceKind, ExternalContextPolicy, HarnessEvent, RequestRequirementResolution, TaskContract, ToolKind } from "@claudexor/schema";
8
+ import type { AttemptTelemetryRecord, AuthSourceKind, ExternalContextPolicy, HarnessEvent, RequestRequirementResolution, TaskContract, ToolKind, WorkState } from "@claudexor/schema";
9
+ import { type TransientFailureObservation } from "./transientClassify.js";
10
+ export { type TransientFailureObservation, classifyAdapterThrow } from "./transientClassify.js";
9
11
  export interface ToolErrorRecord {
10
12
  tool: string;
11
13
  kind: ToolKind;
@@ -23,6 +25,9 @@ export interface AttemptOutcomeState {
23
25
  webRequiredUnsatisfied: boolean;
24
26
  toolWarningsCount: number;
25
27
  status: AttemptOutcomeStatus;
28
+ /** D-16 model-attested work outcome (from the finalizer); absent on routes
29
+ * with no work_report transport. */
30
+ workState?: WorkState;
26
31
  }
27
32
  export interface WebEvidenceState {
28
33
  required: boolean;
@@ -31,10 +36,44 @@ export interface WebEvidenceState {
31
36
  attempted: boolean;
32
37
  satisfied: boolean;
33
38
  failed: boolean;
39
+ /** QA-042: retrieval strength — "verified" once any web result carried a
40
+ * typed successful retrieval, else "dispatched" once web activity completed
41
+ * with no typed outcome (codex), else "none". Never downgrades verified. */
42
+ verification: "verified" | "dispatched" | "none";
34
43
  tool: string | null;
35
44
  target: string | null;
36
45
  errorSummary: string | null;
37
46
  }
47
+ /**
48
+ * QA-040: runtime browser-MCP evidence for one attempt. `requested` is set when
49
+ * the engine armed the browser injection (a fixed `browser` server namespace).
50
+ * A browser tool call/result matched to that injected server flips attempted/
51
+ * satisfied/failed — so a successful browser navigation is recognized as
52
+ * trusted live-web activity even though adapters normalize browser calls as
53
+ * `kind:"mcp"`. Spoof-resistant: only the engine-injected server name matches.
54
+ */
55
+ export interface BrowserEvidenceState {
56
+ requested: boolean;
57
+ serverName: string | null;
58
+ attempted: boolean;
59
+ satisfied: boolean;
60
+ failed: boolean;
61
+ }
62
+ /**
63
+ * Delegation-belt runtime readiness for one attempt (QA-024). `requested` is
64
+ * set at attempt creation when a belt MCP server was injected into the spec;
65
+ * `ready`/`failed` are filled from the harness's `started` event (its
66
+ * `mcp_servers[<belt>].status`); `toolEvidence` flips when any `mcp__<belt>__*`
67
+ * tool actually runs. A requested belt that reports `failed` with no tool
68
+ * evidence is the false-success trap the outcome axis must catch.
69
+ */
70
+ export interface DelegationBeltState {
71
+ requested: boolean;
72
+ serverName: string | null;
73
+ ready: boolean;
74
+ failed: boolean;
75
+ toolEvidence: boolean;
76
+ }
38
77
  export interface AttemptTelemetry {
39
78
  requestRequirements: RequestRequirementResolution[];
40
79
  toolErrors: ToolErrorRecord[];
@@ -58,17 +97,32 @@ export interface AttemptTelemetry {
58
97
  /** Model hint the engine SENT this attempt (requested side; observedModel is
59
98
  * the disclosed side of the model x route truth). */
60
99
  requestedModel: string | null;
61
- /** Adapter-declared transient failures seen during this attempt. */
62
- transientFailures: {
63
- kind: NonNullable<HarnessEvent["transient"]>["kind"];
64
- retryDelayMs: number | null;
65
- }[];
100
+ /** Adapter-declared transient failures seen during this attempt, each
101
+ * classified into the GH #31 typed taxonomy the retry policy gates on. */
102
+ transientFailures: TransientFailureObservation[];
66
103
  /** TYPED vendor rate-limit signals seen during this attempt (W5.4): the
67
104
  * rotation predicate reads these, never prose or plain transients. */
68
105
  rateLimits: {
69
106
  retryDelayMs: number | null;
70
107
  resetsAt: string | null;
71
108
  }[];
109
+ /** Delegation-belt runtime readiness (QA-024); requested=false on non-delegate attempts. */
110
+ delegationBelt: DelegationBeltState;
111
+ /** Browser-MCP runtime evidence (QA-040); requested=false unless the browser
112
+ * injection was armed for this attempt. */
113
+ browser: BrowserEvidenceState;
114
+ /** D-16: a terminal `capacity_exhausted` context signal was observed this
115
+ * attempt (never a transient; consumed by the finalizer, not the retry loop). */
116
+ contextExhausted: boolean;
117
+ /** D-16d: the typed cause of the terminal capacity exhaustion (last-wins),
118
+ * or null when none observed. The continuation controller keys eligibility on
119
+ * `repeated_refill` (claude's rapid-refill breaker), never on `prompt_too_long`
120
+ * (an irreducible packet). */
121
+ contextExhaustedCause: NonNullable<HarnessEvent["context"]>["cause"] | null;
122
+ /** D-16c: the raw `{work_report}` payload a `side_tool` route surfaced on its
123
+ * final message (claude StructuredOutput tool), or null. The unwrap validates
124
+ * it while the markdown answer stays the deliverable. */
125
+ sideToolWorkReport: unknown;
72
126
  /** Contract/outcome truth for this attempt, produced by the orchestrator. */
73
127
  outcome: AttemptOutcomeState | null;
74
128
  /** Token usage summed across this attempt's usage events (money stays in the
@@ -89,7 +143,30 @@ export interface AttemptTelemetry {
89
143
  unknownUsd: number;
90
144
  };
91
145
  }
92
- export declare function createAttemptTelemetry(policy: ExternalContextPolicy, webRequired: boolean, effectiveMode?: ExternalContextPolicy, requestRequirements?: RequestRequirementResolution[], requestedModel?: string | null): AttemptTelemetry;
146
+ export declare function createAttemptTelemetry(policy: ExternalContextPolicy, webRequired: boolean, effectiveMode?: ExternalContextPolicy, requestRequirements?: RequestRequirementResolution[], requestedModel?: string | null,
147
+ /** The delegation-belt MCP server name injected into THIS attempt's spec, or
148
+ * null when no belt was injected (QA-024). Non-null marks the belt requested. */
149
+ beltServerName?: string | null,
150
+ /** The browser MCP server name armed for THIS attempt (fixed `browser`
151
+ * namespace), or null when the browser was not injected (QA-040). Non-null
152
+ * marks the browser requested/armed. */
153
+ browserServerName?: string | null): AttemptTelemetry;
154
+ /**
155
+ * The delegation belt was requested (--delegate injected it) but never became
156
+ * operational: the harness reported the server `failed` and no belt tool ever
157
+ * ran (QA-024). This is the false-success trap — the harness may have answered
158
+ * from its own native subagent with no Claudexor sub-run provenance. A belt
159
+ * that was ready-but-unused is NOT unavailable (docs leave the spawn decision to
160
+ * the harness); only a startup failure counts.
161
+ */
162
+ export declare function delegationBeltUnavailable(t: AttemptTelemetry): boolean;
163
+ /**
164
+ * QA-040: the browser was armed but never called while generic web evidence
165
+ * satisfied the run — a legitimately policy-satisfied run (e.g. web_search
166
+ * answered) that nonetheless did not exercise the requested browser. Disclosure
167
+ * only: it must NOT fail the run.
168
+ */
169
+ export declare function browserUnused(t: AttemptTelemetry): boolean;
93
170
  /**
94
171
  * Observe a normalized harness event into the attempt telemetry. Governance is
95
172
  * fully typed: only the `tool` ToolRef on tool_call/tool_result/file_change
@@ -104,6 +181,10 @@ export declare function setAttemptOutcome(t: AttemptTelemetry, opts: {
104
181
  gatesPassed: boolean | null;
105
182
  harnessErrored: boolean;
106
183
  webRequiredUnsatisfied: boolean;
184
+ /** D-16 work_state from the unified finalizer (INV-116): a needs_input/
185
+ * incomplete veto rides HERE without flipping `status` — the lifecycle
186
+ * stays succeeded-class; applyability and the CLI exit read the axis. */
187
+ workState?: WorkState;
107
188
  }): void;
108
189
  /** Bounded telemetry summary for events/artifacts (full detail lives in telemetry.yaml). */
109
190
  export declare function telemetrySummary(t: AttemptTelemetry): Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"attemptTelemetry.d.ts","sourceRoot":"","sources":["../src/attemptTelemetry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attemptTelemetry.d.ts","sourceRoot":"","sources":["../src/attemptTelemetry.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,14 @@
1
1
  import { redactSecrets } from "@claudexor/util";
2
- export function createAttemptTelemetry(policy, webRequired, effectiveMode = policy, requestRequirements = [], requestedModel = null) {
2
+ import { classifyCompletedCrash, classifyRateLimit, classifyStatusError, classifyTransientSignal, } from "./transientClassify.js";
3
+ export { classifyAdapterThrow } from "./transientClassify.js";
4
+ export function createAttemptTelemetry(policy, webRequired, effectiveMode = policy, requestRequirements = [], requestedModel = null,
5
+ /** The delegation-belt MCP server name injected into THIS attempt's spec, or
6
+ * null when no belt was injected (QA-024). Non-null marks the belt requested. */
7
+ beltServerName = null,
8
+ /** The browser MCP server name armed for THIS attempt (fixed `browser`
9
+ * namespace), or null when the browser was not injected (QA-040). Non-null
10
+ * marks the browser requested/armed. */
11
+ browserServerName = null) {
3
12
  return {
4
13
  requestRequirements,
5
14
  toolErrors: [],
@@ -12,6 +21,7 @@ export function createAttemptTelemetry(policy, webRequired, effectiveMode = poli
12
21
  attempted: false,
13
22
  satisfied: false,
14
23
  failed: false,
24
+ verification: "none",
15
25
  tool: null,
16
26
  target: null,
17
27
  errorSummary: null,
@@ -24,6 +34,23 @@ export function createAttemptTelemetry(policy, webRequired, effectiveMode = poli
24
34
  requestedModel,
25
35
  transientFailures: [],
26
36
  rateLimits: [],
37
+ delegationBelt: {
38
+ requested: beltServerName !== null,
39
+ serverName: beltServerName,
40
+ ready: false,
41
+ failed: false,
42
+ toolEvidence: false,
43
+ },
44
+ browser: {
45
+ requested: browserServerName !== null,
46
+ serverName: browserServerName,
47
+ attempted: false,
48
+ satisfied: false,
49
+ failed: false,
50
+ },
51
+ contextExhausted: false,
52
+ contextExhaustedCause: null,
53
+ sideToolWorkReport: null,
27
54
  outcome: null,
28
55
  usage: { inputTokens: null, outputTokens: null, cachedInputTokens: null },
29
56
  usageCost: { cashUsd: 0, valuationUsd: 0, unknownUsd: 0 },
@@ -34,6 +61,76 @@ export function createAttemptTelemetry(policy, webRequired, effectiveMode = poli
34
61
  function addToken(acc, value) {
35
62
  return value === undefined ? acc : (acc ?? 0) + value;
36
63
  }
64
+ /**
65
+ * Read the injected belt server's status out of the harness `started` frame's
66
+ * `mcp_servers` list (QA-024). The shape is the vendor's — claude emits
67
+ * `{ name, status }` entries — so we defensively narrow each entry and match by
68
+ * the injected belt server name. `status:"failed"` (or "error") is the startup
69
+ * failure the outcome axis must not let terminalize a silent success.
70
+ */
71
+ function observeBeltStartup(t, ev) {
72
+ const payload = ev.payload;
73
+ const servers = payload?.["mcp_servers"];
74
+ if (!Array.isArray(servers))
75
+ return;
76
+ for (const raw of servers) {
77
+ if (!raw || typeof raw !== "object")
78
+ continue;
79
+ const entry = raw;
80
+ if (entry.name !== t.delegationBelt.serverName)
81
+ continue;
82
+ const status = typeof entry.status === "string" ? entry.status.toLowerCase() : "";
83
+ if (status === "failed" || status === "error")
84
+ t.delegationBelt.failed = true;
85
+ else if (status === "connected" || status === "ready" || status === "ok") {
86
+ t.delegationBelt.ready = true;
87
+ }
88
+ return;
89
+ }
90
+ }
91
+ /**
92
+ * The delegation belt was requested (--delegate injected it) but never became
93
+ * operational: the harness reported the server `failed` and no belt tool ever
94
+ * ran (QA-024). This is the false-success trap — the harness may have answered
95
+ * from its own native subagent with no Claudexor sub-run provenance. A belt
96
+ * that was ready-but-unused is NOT unavailable (docs leave the spawn decision to
97
+ * the harness); only a startup failure counts.
98
+ */
99
+ export function delegationBeltUnavailable(t) {
100
+ return t.delegationBelt.requested && t.delegationBelt.failed && !t.delegationBelt.toolEvidence;
101
+ }
102
+ /**
103
+ * QA-040: does this tool ref belong to the engine-armed browser MCP? Adapters
104
+ * normalize browser calls as `kind:"mcp"` (codex `browser:browser_navigate`,
105
+ * claude `mcp__browser__browser_navigate`), so the ToolKind cannot express
106
+ * "browser". Match on the ENGINE-INJECTED server namespace only — a user MCP
107
+ * server cannot spoof trusted browser evidence because the browser is matched
108
+ * solely when the engine armed it under its fixed injected name.
109
+ */
110
+ function matchesBrowser(t, tool) {
111
+ const server = t.browser.serverName;
112
+ if (!t.browser.requested || !server)
113
+ return false;
114
+ return tool.name.startsWith(`mcp__${server}__`) || (tool.target ?? "").startsWith(`${server}:`);
115
+ }
116
+ /**
117
+ * QA-040: the browser was armed but never called while generic web evidence
118
+ * satisfied the run — a legitimately policy-satisfied run (e.g. web_search
119
+ * answered) that nonetheless did not exercise the requested browser. Disclosure
120
+ * only: it must NOT fail the run.
121
+ */
122
+ export function browserUnused(t) {
123
+ return t.browser.requested && !t.browser.attempted && t.web.satisfied;
124
+ }
125
+ /** QA-042: raise the web retrieval strength, never downgrading a proven
126
+ * `verified`. A typed success promotes to verified; any other satisfied web
127
+ * activity is dispatch-strength (completed, content not typed-proven). */
128
+ function bumpWebVerification(t, retrieval) {
129
+ if (retrieval === "verified")
130
+ t.web.verification = "verified";
131
+ else if (t.web.verification !== "verified")
132
+ t.web.verification = "dispatched";
133
+ }
37
134
  /**
38
135
  * Observe a normalized harness event into the attempt telemetry. Governance is
39
136
  * fully typed: only the `tool` ToolRef on tool_call/tool_result/file_change
@@ -41,6 +138,21 @@ function addToken(acc, value) {
41
138
  * matching or tool-name heuristics.
42
139
  */
43
140
  export function observeAttemptTelemetry(t, ev) {
141
+ // Delegation belt readiness (QA-024): the harness's `started` frame lists its
142
+ // MCP servers and each one's status. When the engine injected a belt, read
143
+ // THAT server's status as first-class readiness truth — never prose. A
144
+ // `failed` belt with no later tool evidence is the false-success trap.
145
+ if (ev.type === "started" && t.delegationBelt.requested) {
146
+ observeBeltStartup(t, ev);
147
+ }
148
+ // Belt tool evidence: any `mcp__<belt>__*` tool call/result proves the belt
149
+ // was actually reachable and used (a real Claudexor sub-run path), which
150
+ // distinguishes a used belt from one the harness silently substituted.
151
+ if (t.delegationBelt.requested && t.delegationBelt.serverName && ev.tool?.name) {
152
+ if (ev.tool.name.startsWith(`mcp__${t.delegationBelt.serverName}`)) {
153
+ t.delegationBelt.toolEvidence = true;
154
+ }
155
+ }
44
156
  // Route evidence: remember the model identity the stream itself disclosed.
45
157
  if (ev.observed_model && !t.observedModel)
46
158
  t.observedModel = ev.observed_model;
@@ -83,18 +195,38 @@ export function observeAttemptTelemetry(t, ev) {
83
195
  // produced the deliverable.
84
196
  if (ev.credential_profile_id)
85
197
  t.profileId = ev.credential_profile_id;
86
- if (ev.transient) {
87
- t.transientFailures.push({
88
- kind: ev.transient.kind,
89
- retryDelayMs: ev.transient.retry_delay_ms ?? null,
90
- });
91
- }
198
+ // #31: classify every disclosed transient into the typed taxonomy (see
199
+ // transientClassify.ts). An adapter `transient` and a `rate_limit` are
200
+ // retryable failures; the vendor's typed `status.error_category` surfaces only
201
+ // the deterministic FAILURE classes (auth/capability/config) so required-
202
+ // actions attach the right remediation. Rate limits ALSO stay in rateLimits
203
+ // for the W5.4 rotation predicate.
204
+ if (ev.transient)
205
+ t.transientFailures.push(classifyTransientSignal(ev.transient));
92
206
  if (ev.rate_limit) {
93
207
  t.rateLimits.push({
94
208
  retryDelayMs: ev.rate_limit.retry_delay_ms ?? null,
95
209
  resetsAt: ev.rate_limit.resets_at ?? null,
96
210
  });
211
+ t.transientFailures.push(classifyRateLimit(ev.rate_limit.retry_delay_ms ?? null));
97
212
  }
213
+ if (ev.status?.error_category) {
214
+ const obs = classifyStatusError(ev.status.error_category, ev.status.retry_delay_ms ?? null);
215
+ if (obs)
216
+ t.transientFailures.push(obs);
217
+ }
218
+ // D-16 context signal: a terminal capacity_exhausted marks the attempt for the
219
+ // finalizer's interrupted/context_capacity_exhausted mapping. Context signals
220
+ // NEVER enter the transient-retry loop (they are not transient failures).
221
+ if (ev.context?.kind === "capacity_exhausted") {
222
+ t.contextExhausted = true;
223
+ t.contextExhaustedCause = ev.context.cause ?? null;
224
+ }
225
+ // D-16c side_tool: capture the raw work_report the StructuredOutput tool
226
+ // surfaced on the final message (last-wins). The unwrap validates it.
227
+ const sideTool = ev.payload?.["work_report_side_tool"];
228
+ if (sideTool !== undefined)
229
+ t.sideToolWorkReport = sideTool;
98
230
  // Token usage: SUM across the attempt's usage events (single-event adapters
99
231
  // sum == last-wins; codex per-turn needs the sum). Money is the ledger's job.
100
232
  if (ev.type === "usage" && ev.usage) {
@@ -107,6 +239,11 @@ export function observeAttemptTelemetry(t, ev) {
107
239
  Number(ev.payload?.["dropped_unrecognized_events"] ?? 0);
108
240
  if (Number.isFinite(dropped) && dropped > 0)
109
241
  t.droppedEvents += dropped;
242
+ // #31 process crash: the run loop discloses a non-aborted signal kill, a
243
+ // non-zero exit, or a spawn failure as TYPED payload fields (never prose).
244
+ const crash = classifyCompletedCrash(ev.payload);
245
+ if (crash)
246
+ t.transientFailures.push(crash);
110
247
  return;
111
248
  }
112
249
  const tool = ev.tool;
@@ -118,6 +255,15 @@ export function observeAttemptTelemetry(t, ev) {
118
255
  t.web.tool = tool.name;
119
256
  t.web.target = tool.target ?? t.web.target;
120
257
  }
258
+ // QA-040: an armed browser MCP call is trusted live-web ACTIVITY even though
259
+ // its kind is "mcp" — mark the browser attempted and count it as web
260
+ // attempted (a successful result below satisfies the generic web gate).
261
+ if (matchesBrowser(t, tool)) {
262
+ t.browser.attempted = true;
263
+ t.web.attempted = true;
264
+ t.web.tool = t.web.tool ?? tool.name;
265
+ t.web.target = tool.target ?? t.web.target;
266
+ }
121
267
  return;
122
268
  }
123
269
  if (ev.type !== "tool_result")
@@ -133,6 +279,10 @@ export function observeAttemptTelemetry(t, ev) {
133
279
  t.web.tool = tool.name;
134
280
  t.web.target = tool.target ?? t.web.target;
135
281
  }
282
+ if (matchesBrowser(t, tool)) {
283
+ t.browser.attempted = true;
284
+ t.web.attempted = true;
285
+ }
136
286
  return;
137
287
  }
138
288
  if (tool.status === "error") {
@@ -151,6 +301,19 @@ export function observeAttemptTelemetry(t, ev) {
151
301
  t.web.target = tool.target ?? t.web.target;
152
302
  t.web.errorSummary = redactSecrets(tool.error_summary ?? "web tool result marked error").slice(0, 1000);
153
303
  }
304
+ // QA-040: a failed armed-browser call is browser activity that did not
305
+ // satisfy. It contributes web `attempted` (a call was made) and, since the
306
+ // browser is a live-egress channel, a web failure — but a later successful
307
+ // web/browser call still recovers the generic gate (the ok branch below).
308
+ if (matchesBrowser(t, tool)) {
309
+ t.browser.attempted = true;
310
+ t.browser.failed = true;
311
+ t.web.attempted = true;
312
+ t.web.failed = true;
313
+ t.web.errorSummary =
314
+ t.web.errorSummary ??
315
+ redactSecrets(tool.error_summary ?? "browser tool result marked error").slice(0, 1000);
316
+ }
154
317
  return;
155
318
  }
156
319
  // status === "ok": a later success of the SAME tool against the SAME target
@@ -191,6 +354,34 @@ export function observeAttemptTelemetry(t, ev) {
191
354
  : null;
192
355
  t.web.tool = tool.name;
193
356
  t.web.target = tool.target ?? t.web.target;
357
+ // QA-042: the retrieval STRENGTH. A typed `verified` retrieval (claude
358
+ // WebFetch content) proves content; codex `web_search`/`open_page` stamp
359
+ // `dispatched` (completed, no typed fetch outcome — a hidden 502 is
360
+ // indistinguishable), so the gate is satisfied at DISPATCH strength only.
361
+ // Absent stamp is treated as dispatch (never claim verified without proof).
362
+ bumpWebVerification(t, tool.web_retrieval);
363
+ }
364
+ // QA-040: a successful armed-browser call is trusted live-web evidence. It
365
+ // satisfies the generic web gate AND records browser runtime evidence — a
366
+ // real navigation/screenshot is a typed success, so it is `verified` strength
367
+ // (unlike a dispatch-only codex web_search). A user MCP server cannot reach
368
+ // here: matchesBrowser only accepts the engine-injected server namespace.
369
+ if (matchesBrowser(t, tool)) {
370
+ t.browser.attempted = true;
371
+ t.browser.satisfied = true;
372
+ t.web.attempted = true;
373
+ t.web.satisfied = true;
374
+ t.web.verification = "verified";
375
+ // The recovery loop above already marked a matching earlier browser error
376
+ // recovered; recompute the disclosed failure rollup from what remains.
377
+ t.browser.failed = t.toolErrors.some((e) => e.kind === "mcp" &&
378
+ matchesBrowser(t, { name: e.tool, target: e.target ?? undefined }) &&
379
+ !e.recovered);
380
+ t.web.failed = t.toolErrors.some((e) => e.kind === "web" && !e.recovered) || t.browser.failed;
381
+ if (!t.web.failed)
382
+ t.web.errorSummary = null;
383
+ t.web.tool = t.web.tool ?? tool.name;
384
+ t.web.target = tool.target ?? t.web.target;
194
385
  }
195
386
  }
196
387
  const TELEMETRY_TOOL_ERRORS_MAX = 20;
@@ -209,9 +400,18 @@ export function toolWarnings(t) {
209
400
  export function setAttemptOutcome(t, opts) {
210
401
  const warnings = toolWarnings(t).length;
211
402
  const contractFailed = !opts.deliverablePresent || opts.gatesPassed === false;
403
+ // QA-024: a requested belt that failed to start with no tool evidence is an
404
+ // explicitly-requested capability that never became operational — treated
405
+ // like an unsatisfied hard requirement (never a silent clean success). It
406
+ // rides the same axis order as web: it can only ELEVATE severity, never mask
407
+ // a harder failure. NOTE (D-16 seam): this producer maps belt-unavailable to
408
+ // `failed`; a future finalizer that prefers a softer disclosure would flip
409
+ // this to `success_with_warnings` — the typed telemetry fact
410
+ // (delegation_belt.*) is what a consumer reads either way.
411
+ const beltUnavailable = delegationBeltUnavailable(t);
212
412
  const status = opts.webRequiredUnsatisfied
213
413
  ? "blocked"
214
- : opts.harnessErrored || contractFailed
414
+ : opts.harnessErrored || contractFailed || beltUnavailable
215
415
  ? "failed"
216
416
  : warnings > 0
217
417
  ? "success_with_warnings"
@@ -223,6 +423,7 @@ export function setAttemptOutcome(t, opts) {
223
423
  webRequiredUnsatisfied: opts.webRequiredUnsatisfied,
224
424
  toolWarningsCount: warnings,
225
425
  status,
426
+ ...(opts.workState ? { workState: opts.workState } : {}),
226
427
  };
227
428
  }
228
429
  function webStatus(t) {
@@ -246,10 +447,22 @@ export function telemetrySummary(t) {
246
447
  attempted: t.web.attempted,
247
448
  satisfied: t.web.satisfied,
248
449
  status: webStatus(t),
450
+ verification: t.web.verification,
249
451
  tool: t.web.tool,
250
452
  target: t.web.target,
251
453
  error_summary: t.web.errorSummary,
252
454
  },
455
+ ...(t.browser.requested
456
+ ? {
457
+ browser_evidence: {
458
+ server_name: t.browser.serverName,
459
+ attempted: t.browser.attempted,
460
+ satisfied: t.browser.satisfied,
461
+ failed: t.browser.failed,
462
+ unused: browserUnused(t),
463
+ },
464
+ }
465
+ : {}),
253
466
  tool_errors_total: t.toolErrors.length,
254
467
  unrecovered_tool_errors: unrecovered.length,
255
468
  tool_errors: unrecovered
@@ -264,11 +477,25 @@ export function telemetrySummary(t) {
264
477
  }
265
478
  : {}),
266
479
  ...(t.outcome ? { outcome: t.outcome } : {}),
480
+ ...(t.delegationBelt.requested
481
+ ? {
482
+ delegation_belt: {
483
+ server_name: t.delegationBelt.serverName,
484
+ ready: t.delegationBelt.ready,
485
+ failed: t.delegationBelt.failed,
486
+ tool_evidence: t.delegationBelt.toolEvidence,
487
+ unavailable: delegationBeltUnavailable(t),
488
+ },
489
+ }
490
+ : {}),
267
491
  ...(t.transientFailures.length > 0
268
492
  ? {
269
- transient_failures: t.transientFailures
270
- .slice(-5)
271
- .map((e) => ({ kind: e.kind, retry_delay_ms: e.retryDelayMs })),
493
+ transient_failures: t.transientFailures.slice(-5).map((e) => ({
494
+ kind: e.kind,
495
+ category: e.category,
496
+ retryable: e.retryable,
497
+ retry_delay_ms: e.retryDelayMs,
498
+ })),
272
499
  }
273
500
  : {}),
274
501
  ...(t.droppedEvents > 0 ? { dropped_events: t.droppedEvents } : {}),
@@ -294,6 +521,7 @@ export function attemptTelemetryRecord(attemptId, harnessId, t) {
294
521
  attempted: t.web.attempted,
295
522
  satisfied: t.web.satisfied,
296
523
  status: webStatus(t),
524
+ verification: t.web.verification,
297
525
  tool: t.web.tool,
298
526
  target: t.web.target,
299
527
  error_summary: t.web.errorSummary,
@@ -310,17 +538,50 @@ export function attemptTelemetryRecord(attemptId, harnessId, t) {
310
538
  unrecovered_tool_errors: unrecoveredToolErrors(t).length,
311
539
  statusless_tool_results: t.statuslessResults,
312
540
  dropped_events: t.droppedEvents,
313
- transient_failures: t.transientFailures
314
- .slice(-TELEMETRY_TOOL_ERRORS_MAX)
315
- .map((e) => ({ kind: e.kind, retry_delay_ms: e.retryDelayMs })),
541
+ transient_failures: t.transientFailures.slice(-TELEMETRY_TOOL_ERRORS_MAX).map((e) => ({
542
+ kind: e.kind,
543
+ category: e.category,
544
+ retryable: e.retryable,
545
+ retry_delay_ms: e.retryDelayMs,
546
+ http_status: e.httpStatus,
547
+ signal: e.signal,
548
+ adapter_code: e.adapterCode,
549
+ })),
316
550
  outcome: {
317
551
  deliverable_present: t.outcome?.deliverablePresent ?? false,
318
552
  gates_passed: t.outcome?.gatesPassed ?? null,
319
553
  harness_errored: t.outcome?.harnessErrored ?? false,
320
554
  web_required_unsatisfied: t.outcome?.webRequiredUnsatisfied ?? false,
555
+ delegation_belt_unavailable: delegationBeltUnavailable(t),
321
556
  tool_warnings_count: t.outcome?.toolWarningsCount ?? warnings.length,
322
557
  status: t.outcome?.status ?? (warnings.length > 0 ? "success_with_warnings" : "success"),
558
+ ...(t.outcome?.workState ? { work_state: t.outcome.workState } : {}),
323
559
  },
560
+ // Only present when a belt was actually injected into this attempt (QA-024).
561
+ ...(t.delegationBelt.requested
562
+ ? {
563
+ delegation_belt: {
564
+ requested: t.delegationBelt.requested,
565
+ server_name: t.delegationBelt.serverName,
566
+ ready: t.delegationBelt.ready,
567
+ failed: t.delegationBelt.failed,
568
+ tool_evidence: t.delegationBelt.toolEvidence,
569
+ },
570
+ }
571
+ : {}),
572
+ // Only present when the browser was armed for this attempt (QA-040).
573
+ ...(t.browser.requested
574
+ ? {
575
+ browser: {
576
+ requested: t.browser.requested,
577
+ server_name: t.browser.serverName,
578
+ attempted: t.browser.attempted,
579
+ satisfied: t.browser.satisfied,
580
+ failed: t.browser.failed,
581
+ unused: browserUnused(t),
582
+ },
583
+ }
584
+ : {}),
324
585
  usage: {
325
586
  input_tokens: t.usage.inputTokens,
326
587
  output_tokens: t.usage.outputTokens,
@@ -360,6 +621,7 @@ export function aggregateRunWebEvidence(records, contract) {
360
621
  attempted: false,
361
622
  satisfied: false,
362
623
  status: contract.external_context.web_required ? "unverified" : "none",
624
+ verification: "none",
363
625
  tool: null,
364
626
  target: null,
365
627
  error_summary: null,