harnery 0.34.0 → 0.35.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.
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +2 -0
- package/dist/commands/agents.d.ts +15 -4
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +91 -162
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +26 -2
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +2 -2
- package/dist/commands/events.d.ts.map +1 -1
- package/dist/commands/events.js +18 -1
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/semantic.d.ts +4 -0
- package/dist/commands/semantic.d.ts.map +1 -0
- package/dist/commands/semantic.js +147 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +17 -0
- package/dist/core/agents/canonical-emit.d.ts +3 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +12 -0
- package/dist/core/agents/claim-path.d.ts +11 -0
- package/dist/core/agents/claim-path.d.ts.map +1 -0
- package/dist/core/agents/claim-path.js +22 -0
- package/dist/core/agents/coord-client.d.ts +29 -3
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +88 -9
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +1 -3
- package/dist/core/agents/live-lifecycle-v3.js +3 -3
- package/dist/core/agents/live-observation-v3.d.ts.map +1 -1
- package/dist/core/agents/live-observation-v3.js +2 -2
- package/dist/core/agents/render/prompt-context.js +12 -10
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +23 -11
- package/dist/core/agents/session-name-display.d.ts +2 -1
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +10 -3
- package/dist/core/agents/state/heartbeat-reader.d.ts +46 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-reader.js +25 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts +3 -48
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +16 -34
- package/dist/core/agents/state/live-coordination-view.d.ts +1 -1
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +25 -1
- package/dist/core/agents/state/names.d.ts +9 -1
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +190 -182
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +23 -5
- package/dist/core/artifacts/constants.d.ts +5 -0
- package/dist/core/artifacts/constants.d.ts.map +1 -0
- package/dist/core/artifacts/constants.js +4 -0
- package/dist/core/artifacts/index.d.ts +3 -3
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +50 -7
- package/dist/core/events/v3/base-contract.d.ts +795 -0
- package/dist/core/events/v3/base-contract.d.ts.map +1 -1
- package/dist/core/events/v3/base-contract.js +33 -0
- package/dist/core/events/v3/capabilities.d.ts +11 -0
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +23 -0
- package/dist/core/events/v3/generated.d.ts +1 -1
- package/dist/core/events/v3/generated.js +1 -1
- package/dist/core/events/v3/index.d.ts +4 -3
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +4 -3
- package/dist/core/events/v3/latency.d.ts +31 -1
- package/dist/core/events/v3/latency.d.ts.map +1 -1
- package/dist/core/events/v3/latency.js +127 -3
- package/dist/core/events/v3/live-routing.d.ts +30 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +109 -1
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +10 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +14 -3
- package/dist/core/events/v3/producers/hook-base.d.ts +17 -1
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +82 -1
- package/dist/core/events/v3/producers/intake.d.ts +6 -0
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +11 -0
- package/dist/core/events/v3/producers/recorder.d.ts +62 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +654 -18
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +51 -0
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -0
- package/dist/core/events/v3/runtime-telemetry-capabilities.js +54 -0
- package/dist/core/events/v3/turn-telemetry.d.ts +9 -0
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.d.ts +3 -0
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +14 -0
- package/dist/core/hooks/adapter/runtime-telemetry.d.ts +119 -0
- package/dist/core/hooks/adapter/runtime-telemetry.d.ts.map +1 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +568 -0
- package/dist/core/hooks/cli.js +114 -38
- package/dist/core/hooks/remediation-cap.d.ts +34 -0
- package/dist/core/hooks/remediation-cap.d.ts.map +1 -0
- package/dist/core/hooks/remediation-cap.js +71 -0
- package/dist/core/hooks/resolve/transcript.d.ts +25 -9
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +35 -17
- package/dist/core/semantic/adapters.d.ts +34 -0
- package/dist/core/semantic/adapters.d.ts.map +1 -0
- package/dist/core/semantic/adapters.js +222 -0
- package/dist/core/semantic/contract.d.ts +588 -0
- package/dist/core/semantic/contract.d.ts.map +1 -0
- package/dist/core/semantic/contract.js +230 -0
- package/dist/core/semantic/evidence.d.ts +11 -0
- package/dist/core/semantic/evidence.d.ts.map +1 -0
- package/dist/core/semantic/evidence.js +262 -0
- package/dist/core/semantic/index.d.ts +10 -0
- package/dist/core/semantic/index.d.ts.map +1 -0
- package/dist/core/semantic/index.js +9 -0
- package/dist/core/semantic/once.d.ts +33 -0
- package/dist/core/semantic/once.d.ts.map +1 -0
- package/dist/core/semantic/once.js +293 -0
- package/dist/core/semantic/prompt.d.ts +12 -0
- package/dist/core/semantic/prompt.d.ts.map +1 -0
- package/dist/core/semantic/prompt.js +70 -0
- package/dist/core/semantic/scheduler.d.ts +16 -0
- package/dist/core/semantic/scheduler.d.ts.map +1 -0
- package/dist/core/semantic/scheduler.js +67 -0
- package/dist/core/semantic/service-status.d.ts +31 -0
- package/dist/core/semantic/service-status.d.ts.map +1 -0
- package/dist/core/semantic/service-status.js +84 -0
- package/dist/core/semantic/service.d.ts +51 -0
- package/dist/core/semantic/service.d.ts.map +1 -0
- package/dist/core/semantic/service.js +470 -0
- package/dist/core/semantic/storage.d.ts +71 -0
- package/dist/core/semantic/storage.d.ts.map +1 -0
- package/dist/core/semantic/storage.js +179 -0
- package/dist/core/semantic/validate.d.ts +16 -0
- package/dist/core/semantic/validate.d.ts.map +1 -0
- package/dist/core/semantic/validate.js +174 -0
- package/dist/lib/browser/client.d.ts +11 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +13 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/standalone-html.d.ts +25 -0
- package/dist/lib/browser/standalone-html.d.ts.map +1 -0
- package/dist/lib/browser/standalone-html.js +284 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +1 -0
- package/dist/lib/instructions/templates.d.ts +2 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +144 -4
- package/package.json +6 -1
- package/schemas/event-v3.schema.json +2758 -478
- package/src/commander.ts +2 -0
- package/src/commands/agents.ts +104 -152
- package/src/commands/browse.ts +25 -2
- package/src/commands/decision.ts +8 -2
- package/src/commands/events.ts +21 -1
- package/src/commands/presence.ts +7 -2
- package/src/commands/semantic.ts +179 -0
- package/src/commands/web.ts +15 -0
- package/src/core/agents/canonical-emit.ts +15 -0
- package/src/core/agents/claim-path.ts +23 -0
- package/src/core/agents/coord-client.ts +92 -8
- package/src/core/agents/live-authority-v3.ts +1 -4
- package/src/core/agents/live-lifecycle-v3.ts +8 -3
- package/src/core/agents/live-observation-v3.ts +7 -2
- package/src/core/agents/render/prompt-context.ts +13 -10
- package/src/core/agents/rules/stop-hook.ts +24 -11
- package/src/core/agents/session-name-display.ts +11 -3
- package/src/core/agents/state/heartbeat-reader.ts +69 -0
- package/src/core/agents/state/heartbeat-writer.ts +20 -84
- package/src/core/agents/state/live-coordination-view.ts +26 -1
- package/src/core/agents/state/names.ts +190 -182
- package/src/core/agents/state/stale-sweep.ts +25 -5
- package/src/core/artifacts/constants.ts +4 -0
- package/src/core/artifacts/index.ts +49 -7
- package/src/core/events/v3/base-contract.ts +39 -0
- package/src/core/events/v3/capabilities.ts +34 -0
- package/src/core/events/v3/generated.ts +1 -1
- package/src/core/events/v3/index.ts +21 -0
- package/src/core/events/v3/latency.ts +213 -2
- package/src/core/events/v3/live-routing.ts +120 -1
- package/src/core/events/v3/producers/command-recorder.ts +10 -1
- package/src/core/events/v3/producers/coordination-recorder.ts +17 -5
- package/src/core/events/v3/producers/hook-base.ts +115 -1
- package/src/core/events/v3/producers/intake.ts +18 -0
- package/src/core/events/v3/producers/recorder.ts +856 -21
- package/src/core/events/v3/runtime-telemetry-capabilities.ts +137 -0
- package/src/core/events/v3/turn-telemetry.ts +10 -0
- package/src/core/hooks/adapter/parse.ts +16 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +805 -0
- package/src/core/hooks/cli.ts +126 -41
- package/src/core/hooks/remediation-cap.ts +77 -0
- package/src/core/hooks/resolve/transcript.ts +49 -17
- package/src/core/semantic/adapters.ts +293 -0
- package/src/core/semantic/contract.ts +298 -0
- package/src/core/semantic/evidence.ts +336 -0
- package/src/core/semantic/index.ts +127 -0
- package/src/core/semantic/once.ts +410 -0
- package/src/core/semantic/prompt.ts +84 -0
- package/src/core/semantic/scheduler.ts +107 -0
- package/src/core/semantic/service-status.ts +118 -0
- package/src/core/semantic/service.ts +580 -0
- package/src/core/semantic/storage.ts +286 -0
- package/src/core/semantic/validate.ts +212 -0
- package/src/lib/browser/client.ts +21 -0
- package/src/lib/browser/index.ts +4 -0
- package/src/lib/browser/standalone-html.ts +312 -0
- package/src/lib/instructions/apply.ts +1 -0
- package/src/lib/instructions/templates.ts +149 -4
|
@@ -17,10 +17,18 @@ import { hostname } from "node:os";
|
|
|
17
17
|
import { basename, join, resolve } from "node:path";
|
|
18
18
|
import type { Adapter } from "../../../adapter.ts";
|
|
19
19
|
import { extractBashCommand, type ParsedPayload } from "../../../hooks/adapter/parse.ts";
|
|
20
|
+
import {
|
|
21
|
+
discoverCodexSessionTranscript,
|
|
22
|
+
type RuntimeContextTelemetry,
|
|
23
|
+
type RuntimeTelemetryOptions,
|
|
24
|
+
readRuntimeContextTelemetry,
|
|
25
|
+
readRuntimeTuning,
|
|
26
|
+
} from "../../../hooks/adapter/runtime-telemetry.ts";
|
|
20
27
|
import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
|
|
21
28
|
import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
|
|
29
|
+
import type { RuntimeAttestationV3Base } from "../base-contract.ts";
|
|
22
30
|
import { buildEventV3 } from "../builder.ts";
|
|
23
|
-
import { fingerprintV3, normalizeNativeIdV3 } from "../canonical.ts";
|
|
31
|
+
import { canonicalJsonV3, fingerprintV3, normalizeNativeIdV3 } from "../canonical.ts";
|
|
24
32
|
import {
|
|
25
33
|
type AdapterSignalV3,
|
|
26
34
|
adapterCapabilityProfileDigestV3,
|
|
@@ -33,8 +41,19 @@ import { markObservedClockRegressionV3 } from "../clock-order.ts";
|
|
|
33
41
|
import type { EventV3 } from "../contract.ts";
|
|
34
42
|
import { type EventV3WriteMode, readEventV3ControlState } from "../control.ts";
|
|
35
43
|
import { fingerprintContextV3 } from "../fingerprint-keys.ts";
|
|
36
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
attestationIdV3,
|
|
46
|
+
clockIdV3,
|
|
47
|
+
delegationIdV3,
|
|
48
|
+
eventIdV3,
|
|
49
|
+
generationIdV3,
|
|
50
|
+
spanIdV3,
|
|
51
|
+
} from "../ids.ts";
|
|
37
52
|
import { readLedgerV3 } from "../reader.ts";
|
|
53
|
+
import {
|
|
54
|
+
effectiveRuntimeTelemetryCapabilitiesV3,
|
|
55
|
+
type RuntimeTelemetryCapabilitiesV3,
|
|
56
|
+
} from "../runtime-telemetry-capabilities.ts";
|
|
38
57
|
import {
|
|
39
58
|
closeSpanStateV3,
|
|
40
59
|
type OpenSpanStateV3,
|
|
@@ -44,8 +63,10 @@ import {
|
|
|
44
63
|
} from "../span-state.ts";
|
|
45
64
|
import {
|
|
46
65
|
type ContextMeasurementV3,
|
|
66
|
+
type ContextTelemetryProvenanceV3,
|
|
47
67
|
extractTurnTelemetryV3,
|
|
48
68
|
type TelemetryObservationV3,
|
|
69
|
+
type TurnTelemetryV3,
|
|
49
70
|
} from "../turn-telemetry.ts";
|
|
50
71
|
import { assertEventV3, validateEventV3 } from "../validate.ts";
|
|
51
72
|
import {
|
|
@@ -74,6 +95,10 @@ const CLOSED_SPAN_TURN_RETENTION = 2;
|
|
|
74
95
|
const CLOSED_SPAN_MEMORY_CAP = 512;
|
|
75
96
|
const CLOSED_TURN_MEMORY_CAP = 16;
|
|
76
97
|
const SPAN_SOFT_WATERMARK = 128;
|
|
98
|
+
const PENDING_RUNTIME_CONTEXT_CAP = 4;
|
|
99
|
+
const RUNTIME_CONTEXT_RETRY_LIMIT = 2;
|
|
100
|
+
/** Finalization is off the interactive hook path; briefly cover Codex's delayed transcript flush. */
|
|
101
|
+
const APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS = [0, 250, 250] as const;
|
|
77
102
|
/**
|
|
78
103
|
* Adapters whose turn-boundary recovery and mid-flight onboarding are enabled.
|
|
79
104
|
* Kept in code, outside the digested capability profiles, so tuning recovery
|
|
@@ -117,6 +142,23 @@ interface PendingEventV3 {
|
|
|
117
142
|
event: EventV3;
|
|
118
143
|
}
|
|
119
144
|
|
|
145
|
+
interface TurnContextTargetV3 {
|
|
146
|
+
terminal_event_id: `evt_${string}`;
|
|
147
|
+
terminal_observed_at: string;
|
|
148
|
+
turn_id: `tid_${string}`;
|
|
149
|
+
run_id?: `run_${string}`;
|
|
150
|
+
workflow_id?: `wf_${string}`;
|
|
151
|
+
workflow_agent_id?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface PendingRuntimeContextV3 extends TurnContextTargetV3 {
|
|
155
|
+
native_session_id: string;
|
|
156
|
+
native_turn_id: string;
|
|
157
|
+
transcript_path?: string;
|
|
158
|
+
runtime_version?: string;
|
|
159
|
+
attempts: number;
|
|
160
|
+
}
|
|
161
|
+
|
|
120
162
|
interface DelegationStateV3 extends OpenSpanStateV3 {
|
|
121
163
|
source_id: `hid_${string}`;
|
|
122
164
|
delegation_id: `del_${string}`;
|
|
@@ -139,6 +181,8 @@ export interface HookProducerStateV3 {
|
|
|
139
181
|
clock_id: `clk_${string}`;
|
|
140
182
|
next_sequence: number;
|
|
141
183
|
current_turn_id?: `tid_${string}`;
|
|
184
|
+
/** Native turn identity retained owner-only for transcript attribution. */
|
|
185
|
+
current_native_turn_id?: string;
|
|
142
186
|
tool_call_count: number;
|
|
143
187
|
tool_call_count_turn_id?: `tid_${string}`;
|
|
144
188
|
last_event_id?: `evt_${string}`;
|
|
@@ -156,6 +200,25 @@ export interface HookProducerStateV3 {
|
|
|
156
200
|
turn_harness: TurnHarnessTimingV3;
|
|
157
201
|
turn_ordinal: number;
|
|
158
202
|
pending?: PendingEventV3;
|
|
203
|
+
pending_runtime_contexts?: PendingRuntimeContextV3[];
|
|
204
|
+
/** Verified adapter-native transcript path retained only in owner-only state. */
|
|
205
|
+
runtime_transcript_path?: string;
|
|
206
|
+
/** Tuning carried by the current attestation; {} means attested-none.
|
|
207
|
+
* Absent on pre-upgrade state files, which seed silently on first sight. */
|
|
208
|
+
last_attested_tuning?: { effort?: string; speed?: string };
|
|
209
|
+
/** Model observation carried forward into a refreshed attestation when the
|
|
210
|
+
* change-time transcript read does not pair a model with the new tuning. */
|
|
211
|
+
last_attested_model?: { provider: string; id: string };
|
|
212
|
+
/** Telemetry evidence carried forward verbatim: a tuning change does not
|
|
213
|
+
* alter what telemetry the runtime proved it can deliver. */
|
|
214
|
+
last_attested_telemetry?: RuntimeTelemetryCapabilitiesV3;
|
|
215
|
+
/** Full observations preserve missing and unsupported states when a
|
|
216
|
+
* different attestation channel changes. */
|
|
217
|
+
last_attested_model_observation?: RuntimeAttestationV3Base["model"];
|
|
218
|
+
last_attested_tuning_observation?: RuntimeAttestationV3Base["tuning"];
|
|
219
|
+
/** Turn already probed for Codex tuning while effort is unknown; bounds the
|
|
220
|
+
* rollout forward-scan to one attempt per turn. */
|
|
221
|
+
tuning_probe_turn_id?: `tid_${string}`;
|
|
159
222
|
}
|
|
160
223
|
|
|
161
224
|
export interface RecordHookSignalV3Input {
|
|
@@ -181,6 +244,14 @@ export interface RecordHookSignalV3Input {
|
|
|
181
244
|
stop_remediation?: boolean;
|
|
182
245
|
turn_ritual?: TurnRitualEvidenceV3;
|
|
183
246
|
session_start_derivation?: "approved_lifecycle_reopen";
|
|
247
|
+
delegated_child?: {
|
|
248
|
+
generation_id: `gen_${string}`;
|
|
249
|
+
parent_generation_id: `gen_${string}`;
|
|
250
|
+
delegation_id: `del_${string}`;
|
|
251
|
+
caused_by_event_id: `evt_${string}`;
|
|
252
|
+
};
|
|
253
|
+
/** Optional roots and read budgets for runtime telemetry adapters. */
|
|
254
|
+
runtimeTelemetryOptions?: RuntimeTelemetryOptions;
|
|
184
255
|
writerOptions?: WriteEventV3Options;
|
|
185
256
|
}
|
|
186
257
|
|
|
@@ -310,11 +381,21 @@ function hookSignalGate(
|
|
|
310
381
|
}
|
|
311
382
|
|
|
312
383
|
function sessionHashForSignal(
|
|
313
|
-
input: Pick<
|
|
384
|
+
input: Pick<
|
|
385
|
+
RecordHookSignalV3Input,
|
|
386
|
+
"coordRoot" | "payload" | "adapter" | "instance_id" | "signal"
|
|
387
|
+
>,
|
|
314
388
|
context: ReturnType<typeof fingerprintContextV3>,
|
|
315
389
|
): `hid_${string}` {
|
|
316
|
-
const
|
|
317
|
-
|
|
390
|
+
const nativeChild = input.payload.subagent_id ?? input.payload.agent_id;
|
|
391
|
+
const childOwnsSignal =
|
|
392
|
+
input.signal !== "sub-agent-start" &&
|
|
393
|
+
input.signal !== "sub-agent-stop" &&
|
|
394
|
+
nativeChild !== undefined &&
|
|
395
|
+
input.instance_id === asLiveInstanceId(nativeChild);
|
|
396
|
+
const nativeSession = childOwnsSignal
|
|
397
|
+
? nativeChild
|
|
398
|
+
: (input.payload.session_id ?? input.payload.conversation_id ?? input.payload.agent_id);
|
|
318
399
|
if (nativeSession !== undefined) {
|
|
319
400
|
return normalizeNativeIdV3(context, `${input.adapter}.session`, nativeSession);
|
|
320
401
|
}
|
|
@@ -360,6 +441,7 @@ function intakeRecord(input: RecordHookSignalV3Input): HookIntakeRecordV3 {
|
|
|
360
441
|
: {}),
|
|
361
442
|
...(input.stop_remediation !== undefined ? { stop_remediation: input.stop_remediation } : {}),
|
|
362
443
|
...(input.turn_ritual ? { turn_ritual: input.turn_ritual } : {}),
|
|
444
|
+
...(input.delegated_child ? { delegated_child: input.delegated_child } : {}),
|
|
363
445
|
};
|
|
364
446
|
}
|
|
365
447
|
|
|
@@ -386,6 +468,7 @@ function inputForIntakeRecord(
|
|
|
386
468
|
...(record.hook_duration_ms !== undefined ? { hook_duration_ms: record.hook_duration_ms } : {}),
|
|
387
469
|
...(record.stop_remediation !== undefined ? { stop_remediation: record.stop_remediation } : {}),
|
|
388
470
|
...(record.turn_ritual ? { turn_ritual: record.turn_ritual } : {}),
|
|
471
|
+
...(record.delegated_child ? { delegated_child: record.delegated_child } : {}),
|
|
389
472
|
};
|
|
390
473
|
}
|
|
391
474
|
|
|
@@ -669,6 +752,9 @@ function processHookSignalLocked(
|
|
|
669
752
|
(nativeTid !== undefined && state.current_turn_id === nativeTid));
|
|
670
753
|
|
|
671
754
|
recordTurnHarnessTiming(state, input, !duplicateOpenTurnStart);
|
|
755
|
+
reconcilePendingRuntimeContexts(input, state, path, rootId, fingerprintContext, {
|
|
756
|
+
finalAttempt: input.signal === "session-end",
|
|
757
|
+
});
|
|
672
758
|
|
|
673
759
|
closeResolvedWaits(input, state, path, rootId, fingerprintContext, nativeTid);
|
|
674
760
|
if (input.signal === "permission-request") {
|
|
@@ -736,6 +822,7 @@ function processHookSignalLocked(
|
|
|
736
822
|
boot_id: state.boot_id,
|
|
737
823
|
clock: eventClock,
|
|
738
824
|
});
|
|
825
|
+
state.current_native_turn_id = input.payload.turn_id;
|
|
739
826
|
}
|
|
740
827
|
|
|
741
828
|
let sourceId = sourceIdForSignal(input, rootFingerprintContext);
|
|
@@ -933,7 +1020,7 @@ function processHookSignalLocked(
|
|
|
933
1020
|
|
|
934
1021
|
const turnTelemetry =
|
|
935
1022
|
input.signal === "stop" || input.signal === "stop-failure"
|
|
936
|
-
?
|
|
1023
|
+
? turnTelemetryForTerminal(input, state, eventClock.observed_at)
|
|
937
1024
|
: undefined;
|
|
938
1025
|
const cursorToolChannelSupport =
|
|
939
1026
|
input.adapter === "cursor"
|
|
@@ -950,6 +1037,9 @@ function processHookSignalLocked(
|
|
|
950
1037
|
const toolCallCountScopeMismatch =
|
|
951
1038
|
(input.signal === "stop" || input.signal === "stop-failure") &&
|
|
952
1039
|
(!state.current_turn_id || state.tool_call_count_turn_id !== state.current_turn_id);
|
|
1040
|
+
// Before the signal's own event: the tool call or turn that carries the
|
|
1041
|
+
// new effort already ran under it, so it must ride the new attestation.
|
|
1042
|
+
maybeAttestTuningChange(input, state, path, rootId);
|
|
953
1043
|
const event = normalizeHookEventV3(input.signal, eventInput.payload, {
|
|
954
1044
|
coordRoot: input.coordRoot,
|
|
955
1045
|
adapter: input.adapter,
|
|
@@ -973,6 +1063,7 @@ function processHookSignalLocked(
|
|
|
973
1063
|
platform: input.platform,
|
|
974
1064
|
bridge: input.bridge,
|
|
975
1065
|
capability_profile: state.capability_profile,
|
|
1066
|
+
cursor_mode: state.cursor_mode,
|
|
976
1067
|
fingerprintContext,
|
|
977
1068
|
turn_id: state.current_turn_id,
|
|
978
1069
|
span_id: openingSpan?.span_id ?? closingSpan?.span_id,
|
|
@@ -1010,6 +1101,48 @@ function processHookSignalLocked(
|
|
|
1010
1101
|
turn_ritual: input.turn_ritual,
|
|
1011
1102
|
});
|
|
1012
1103
|
if (!event) return { state: "ignored" };
|
|
1104
|
+
if (
|
|
1105
|
+
input.signal === "session-start" &&
|
|
1106
|
+
input.delegated_child &&
|
|
1107
|
+
event.event_type === "session.started"
|
|
1108
|
+
) {
|
|
1109
|
+
event.links = {
|
|
1110
|
+
caused_by: [input.delegated_child.caused_by_event_id],
|
|
1111
|
+
parent_generation_id: input.delegated_child.parent_generation_id,
|
|
1112
|
+
delegation_id: input.delegated_child.delegation_id,
|
|
1113
|
+
};
|
|
1114
|
+
event.provenance = {
|
|
1115
|
+
...event.provenance,
|
|
1116
|
+
source_event: `${input.adapter}.subagent-start.child-generation`,
|
|
1117
|
+
attestation: "derived",
|
|
1118
|
+
confidence: "high",
|
|
1119
|
+
attribution: {
|
|
1120
|
+
method: "native_payload",
|
|
1121
|
+
state: "verified",
|
|
1122
|
+
subject_instance_id: state.instance_id,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
assertEventV3(event);
|
|
1126
|
+
}
|
|
1127
|
+
if (
|
|
1128
|
+
input.signal === "user-prompt-submit" &&
|
|
1129
|
+
input.delegated_child &&
|
|
1130
|
+
event.event_type === "turn.started"
|
|
1131
|
+
) {
|
|
1132
|
+
event.links = { caused_by: [input.delegated_child.caused_by_event_id] };
|
|
1133
|
+
event.provenance = {
|
|
1134
|
+
...event.provenance,
|
|
1135
|
+
source_event: `${input.adapter}.subagent-start.child-turn`,
|
|
1136
|
+
attestation: "derived",
|
|
1137
|
+
confidence: "high",
|
|
1138
|
+
attribution: {
|
|
1139
|
+
method: "native_payload",
|
|
1140
|
+
state: "verified",
|
|
1141
|
+
subject_instance_id: state.instance_id,
|
|
1142
|
+
},
|
|
1143
|
+
};
|
|
1144
|
+
assertEventV3(event);
|
|
1145
|
+
}
|
|
1013
1146
|
if (
|
|
1014
1147
|
input.signal === "session-start" &&
|
|
1015
1148
|
input.session_start_derivation === "approved_lifecycle_reopen" &&
|
|
@@ -1051,7 +1184,18 @@ function processHookSignalLocked(
|
|
|
1051
1184
|
commitProgressObservation(input, state, path, rootId, event, progressKind);
|
|
1052
1185
|
}
|
|
1053
1186
|
if (turnTelemetry) {
|
|
1054
|
-
|
|
1187
|
+
const contextTarget = turnContextTarget(event);
|
|
1188
|
+
queuePendingRuntimeContext(input, state, contextTarget, turnTelemetry.context);
|
|
1189
|
+
commitTurnContextObservation(
|
|
1190
|
+
input,
|
|
1191
|
+
state,
|
|
1192
|
+
path,
|
|
1193
|
+
rootId,
|
|
1194
|
+
fingerprintContext,
|
|
1195
|
+
contextTarget,
|
|
1196
|
+
turnTelemetry.context,
|
|
1197
|
+
turnTelemetry.context_provenance,
|
|
1198
|
+
);
|
|
1055
1199
|
}
|
|
1056
1200
|
if (event.event_type === "session.ended") {
|
|
1057
1201
|
commitCapabilityDriftEvents(input, state, path, rootId);
|
|
@@ -1501,10 +1645,13 @@ function commitTurnContextObservation(
|
|
|
1501
1645
|
state: HookProducerStateV3,
|
|
1502
1646
|
path: string,
|
|
1503
1647
|
rootId: `root_${string}`,
|
|
1504
|
-
|
|
1648
|
+
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
1649
|
+
target: TurnContextTargetV3 | undefined,
|
|
1505
1650
|
measurement: TelemetryObservationV3<ContextMeasurementV3>,
|
|
1651
|
+
contextProvenance?: ContextTelemetryProvenanceV3,
|
|
1652
|
+
observedAt?: string,
|
|
1506
1653
|
): void {
|
|
1507
|
-
if (
|
|
1654
|
+
if (!target) return;
|
|
1508
1655
|
const event = buildEventV3("context.observed", {
|
|
1509
1656
|
producer: {
|
|
1510
1657
|
producer_id: input.producer_id,
|
|
@@ -1520,36 +1667,631 @@ function commitTurnContextObservation(
|
|
|
1520
1667
|
instance_id: state.instance_id,
|
|
1521
1668
|
session_id: state.session_id,
|
|
1522
1669
|
generation_id: state.generation_id,
|
|
1523
|
-
turn_id:
|
|
1524
|
-
...(
|
|
1525
|
-
...(
|
|
1526
|
-
...(
|
|
1670
|
+
turn_id: target.turn_id,
|
|
1671
|
+
...(target.run_id ? { run_id: target.run_id } : {}),
|
|
1672
|
+
...(target.workflow_id ? { workflow_id: target.workflow_id } : {}),
|
|
1673
|
+
...(target.workflow_agent_id ? { workflow_agent_id: target.workflow_agent_id } : {}),
|
|
1527
1674
|
},
|
|
1528
1675
|
attestation_id: state.attestation_id,
|
|
1529
|
-
links: { caused_by: [
|
|
1676
|
+
links: { caused_by: [target.terminal_event_id] },
|
|
1530
1677
|
provenance: {
|
|
1531
|
-
source_event:
|
|
1678
|
+
source_event: safeRole(
|
|
1679
|
+
[
|
|
1680
|
+
contextProvenance?.source_event ?? `${input.adapter}.turn-context`,
|
|
1681
|
+
contextProvenance?.runtime_version,
|
|
1682
|
+
]
|
|
1683
|
+
.filter(Boolean)
|
|
1684
|
+
.join("."),
|
|
1685
|
+
),
|
|
1686
|
+
attestation: contextProvenance?.attestation ?? "native",
|
|
1687
|
+
confidence: contextProvenance?.confidence ?? "exact",
|
|
1688
|
+
...(contextProvenance?.source_witness
|
|
1689
|
+
? {
|
|
1690
|
+
source_record_id: normalizeNativeIdV3(
|
|
1691
|
+
fingerprintContext,
|
|
1692
|
+
`${input.adapter}.runtime-context`,
|
|
1693
|
+
contextProvenance.source_witness,
|
|
1694
|
+
),
|
|
1695
|
+
}
|
|
1696
|
+
: {}),
|
|
1697
|
+
attribution: {
|
|
1698
|
+
method: "native_payload",
|
|
1699
|
+
state: "verified",
|
|
1700
|
+
subject_instance_id: state.instance_id,
|
|
1701
|
+
},
|
|
1702
|
+
},
|
|
1703
|
+
// The source sample may precede native task completion. The event itself
|
|
1704
|
+
// is committed after turn.completed, while measurement.measured_at keeps
|
|
1705
|
+
// the sample clock without creating a causal wall-clock regression.
|
|
1706
|
+
observed_at: observedAt ?? target.terminal_observed_at,
|
|
1707
|
+
monotonic_ns: orderedEventMonotonic(state, input.monotonic_ns),
|
|
1708
|
+
clock_id: state.clock_id,
|
|
1709
|
+
payload: {
|
|
1710
|
+
measurement,
|
|
1711
|
+
},
|
|
1712
|
+
}) as EventV3;
|
|
1713
|
+
commitEventLocked(input, state, path, event);
|
|
1714
|
+
maybeAttestContextCapabilityChange(input, state, path, rootId, measurement, contextProvenance);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
function maybeAttestContextCapabilityChange(
|
|
1718
|
+
input: RecordHookSignalV3Input,
|
|
1719
|
+
state: HookProducerStateV3,
|
|
1720
|
+
path: string,
|
|
1721
|
+
rootId: `root_${string}`,
|
|
1722
|
+
measurement: TelemetryObservationV3<ContextMeasurementV3>,
|
|
1723
|
+
provenance?: ContextTelemetryProvenanceV3,
|
|
1724
|
+
): void {
|
|
1725
|
+
const context =
|
|
1726
|
+
measurement.state === "observed"
|
|
1727
|
+
? {
|
|
1728
|
+
state: "observed" as const,
|
|
1729
|
+
source: provenance?.source_event ?? `${input.adapter}.turn_context`,
|
|
1730
|
+
attestation: provenance?.attestation ?? measurement.attestation,
|
|
1731
|
+
confidence: provenance?.confidence ?? measurement.confidence,
|
|
1732
|
+
}
|
|
1733
|
+
: measurement.state === "expected_but_missing"
|
|
1734
|
+
? { state: "partial" as const, reason: measurement.reason }
|
|
1735
|
+
: { state: "unsupported" as const };
|
|
1736
|
+
const telemetry = effectiveRuntimeTelemetryCapabilitiesV3({
|
|
1737
|
+
adapter: input.adapter,
|
|
1738
|
+
...(state.cursor_mode ? { cursor_mode: state.cursor_mode } : {}),
|
|
1739
|
+
context,
|
|
1740
|
+
canonical_turn_boundaries: true,
|
|
1741
|
+
});
|
|
1742
|
+
if (!state.last_attested_telemetry) return;
|
|
1743
|
+
if (canonicalJsonV3(telemetry) === canonicalJsonV3(state.last_attested_telemetry)) return;
|
|
1744
|
+
if (!state.last_attested_model_observation || !state.last_attested_tuning_observation) return;
|
|
1745
|
+
|
|
1746
|
+
commitRuntimeAttestationChange(input, state, path, rootId, {
|
|
1747
|
+
model: state.last_attested_model_observation,
|
|
1748
|
+
tuning: state.last_attested_tuning_observation,
|
|
1749
|
+
telemetry,
|
|
1750
|
+
reason: "runtime_telemetry_changed",
|
|
1751
|
+
source_event: provenance?.source_event ?? `${input.adapter}.turn_context`,
|
|
1752
|
+
attestation: provenance?.attestation ?? "derived",
|
|
1753
|
+
confidence: provenance?.confidence ?? (measurement.state === "observed" ? "exact" : "high"),
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
function turnContextTarget(turnTerminal: EventV3): TurnContextTargetV3 | undefined {
|
|
1758
|
+
if (turnTerminal.event_type !== "turn.completed") return undefined;
|
|
1759
|
+
const scope = turnTerminal.scope as {
|
|
1760
|
+
turn_id: `tid_${string}`;
|
|
1761
|
+
run_id?: `run_${string}`;
|
|
1762
|
+
workflow_id?: `wf_${string}`;
|
|
1763
|
+
workflow_agent_id?: string;
|
|
1764
|
+
};
|
|
1765
|
+
return {
|
|
1766
|
+
terminal_event_id: turnTerminal.event_id as `evt_${string}`,
|
|
1767
|
+
terminal_observed_at: turnTerminal.time.observed_at,
|
|
1768
|
+
turn_id: scope.turn_id,
|
|
1769
|
+
...(scope.run_id ? { run_id: scope.run_id } : {}),
|
|
1770
|
+
...(scope.workflow_id ? { workflow_id: scope.workflow_id } : {}),
|
|
1771
|
+
...(scope.workflow_agent_id ? { workflow_agent_id: scope.workflow_agent_id } : {}),
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
function queuePendingRuntimeContext(
|
|
1776
|
+
input: RecordHookSignalV3Input,
|
|
1777
|
+
state: HookProducerStateV3,
|
|
1778
|
+
target: TurnContextTargetV3 | undefined,
|
|
1779
|
+
measurement: TelemetryObservationV3<ContextMeasurementV3>,
|
|
1780
|
+
): void {
|
|
1781
|
+
if (
|
|
1782
|
+
input.adapter !== "codex" ||
|
|
1783
|
+
!target ||
|
|
1784
|
+
measurement.state !== "expected_but_missing" ||
|
|
1785
|
+
!retryableRuntimeContextReason(measurement.reason)
|
|
1786
|
+
) {
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
1790
|
+
const nativeTurnId = input.payload.turn_id;
|
|
1791
|
+
if (!nativeSessionId || !nativeTurnId) return;
|
|
1792
|
+
const pending: PendingRuntimeContextV3 = {
|
|
1793
|
+
...target,
|
|
1794
|
+
native_session_id: nativeSessionId,
|
|
1795
|
+
native_turn_id: nativeTurnId,
|
|
1796
|
+
...((input.payload.transcript_path ?? state.runtime_transcript_path)
|
|
1797
|
+
? { transcript_path: input.payload.transcript_path ?? state.runtime_transcript_path }
|
|
1798
|
+
: {}),
|
|
1799
|
+
...(input.adapterVersion ? { runtime_version: input.adapterVersion } : {}),
|
|
1800
|
+
attempts: 0,
|
|
1801
|
+
};
|
|
1802
|
+
state.pending_runtime_contexts = [
|
|
1803
|
+
...(state.pending_runtime_contexts ?? []).filter(
|
|
1804
|
+
(candidate) => candidate.turn_id !== target.turn_id,
|
|
1805
|
+
),
|
|
1806
|
+
pending,
|
|
1807
|
+
].slice(-PENDING_RUNTIME_CONTEXT_CAP);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function reconcilePendingRuntimeContexts(
|
|
1811
|
+
input: RecordHookSignalV3Input,
|
|
1812
|
+
state: HookProducerStateV3,
|
|
1813
|
+
path: string,
|
|
1814
|
+
rootId: `root_${string}`,
|
|
1815
|
+
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
1816
|
+
options: { deferRetryLimit?: boolean; finalAttempt?: boolean } = {},
|
|
1817
|
+
): void {
|
|
1818
|
+
if (state.adapter !== "codex" || !state.pending_runtime_contexts?.length) return;
|
|
1819
|
+
for (const pending of [...state.pending_runtime_contexts]) {
|
|
1820
|
+
const runtime = readRuntimeContextTelemetry(
|
|
1821
|
+
{
|
|
1822
|
+
adapter: "codex",
|
|
1823
|
+
session_id: pending.native_session_id,
|
|
1824
|
+
turn_id: pending.native_turn_id,
|
|
1825
|
+
transcript_path: pending.transcript_path,
|
|
1826
|
+
mode: "turn",
|
|
1827
|
+
},
|
|
1828
|
+
input.runtimeTelemetryOptions,
|
|
1829
|
+
);
|
|
1830
|
+
if (runtime.bytes_read > 0) recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1831
|
+
const queue = state.pending_runtime_contexts;
|
|
1832
|
+
if (!queue) break;
|
|
1833
|
+
const index = queue.findIndex((candidate) => candidate.turn_id === pending.turn_id);
|
|
1834
|
+
if (index < 0) continue;
|
|
1835
|
+
if (runtime.state === "observed") {
|
|
1836
|
+
queue.splice(index, 1);
|
|
1837
|
+
if (queue.length === 0) {
|
|
1838
|
+
state.pending_runtime_contexts = undefined;
|
|
1839
|
+
}
|
|
1840
|
+
commitTurnContextObservation(
|
|
1841
|
+
input,
|
|
1842
|
+
state,
|
|
1843
|
+
path,
|
|
1844
|
+
rootId,
|
|
1845
|
+
fingerprintContext,
|
|
1846
|
+
pending,
|
|
1847
|
+
runtimeContextObservation(runtime),
|
|
1848
|
+
{
|
|
1849
|
+
source_event: runtime.source_event,
|
|
1850
|
+
source_witness: runtime.source_witness,
|
|
1851
|
+
...(pending.runtime_version ? { runtime_version: pending.runtime_version } : {}),
|
|
1852
|
+
attestation: "derived",
|
|
1853
|
+
confidence: "exact",
|
|
1854
|
+
},
|
|
1855
|
+
signalClock(input).observed_at,
|
|
1856
|
+
);
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
const attempts = pending.attempts + 1;
|
|
1860
|
+
if (
|
|
1861
|
+
options.finalAttempt ||
|
|
1862
|
+
runtime.state === "unsupported" ||
|
|
1863
|
+
!retryableRuntimeContextReason(runtime.reason) ||
|
|
1864
|
+
(!options.deferRetryLimit && attempts >= RUNTIME_CONTEXT_RETRY_LIMIT)
|
|
1865
|
+
) {
|
|
1866
|
+
queue.splice(index, 1);
|
|
1867
|
+
} else {
|
|
1868
|
+
queue[index] = { ...pending, attempts };
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
if (state.pending_runtime_contexts?.length === 0) {
|
|
1872
|
+
state.pending_runtime_contexts = undefined;
|
|
1873
|
+
}
|
|
1874
|
+
publishProducerState(path, state);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
function reconcilePendingRuntimeContextsBeforeApprovedEnd(
|
|
1878
|
+
input: RecordApprovedSessionEndV3Input,
|
|
1879
|
+
state: HookProducerStateV3,
|
|
1880
|
+
path: string,
|
|
1881
|
+
rootId: `root_${string}`,
|
|
1882
|
+
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
1883
|
+
): void {
|
|
1884
|
+
if (state.adapter !== "codex" || !state.pending_runtime_contexts?.length) return;
|
|
1885
|
+
for (const [index, delayMs] of APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS.entries()) {
|
|
1886
|
+
if (delayMs > 0) sleepSync(delayMs);
|
|
1887
|
+
reconcilePendingRuntimeContexts(
|
|
1888
|
+
{
|
|
1889
|
+
coordRoot: input.coordRoot,
|
|
1890
|
+
mode: input.mode,
|
|
1891
|
+
signal: "session-end",
|
|
1892
|
+
payload: { raw: {} },
|
|
1893
|
+
adapter: state.adapter,
|
|
1894
|
+
instance_id: state.instance_id,
|
|
1895
|
+
producer_id: "prd_agent-finalizer",
|
|
1896
|
+
build_id: input.build_id,
|
|
1897
|
+
platform: input.platform,
|
|
1898
|
+
observed_at: new Date().toISOString(),
|
|
1899
|
+
writerOptions: input.writerOptions,
|
|
1900
|
+
},
|
|
1901
|
+
state,
|
|
1902
|
+
path,
|
|
1903
|
+
rootId,
|
|
1904
|
+
fingerprintContext,
|
|
1905
|
+
{
|
|
1906
|
+
deferRetryLimit: true,
|
|
1907
|
+
finalAttempt: index === APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS.length - 1,
|
|
1908
|
+
},
|
|
1909
|
+
);
|
|
1910
|
+
if (!state.pending_runtime_contexts?.length) return;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
function retryableRuntimeContextReason(reason: string | undefined): boolean {
|
|
1915
|
+
return (
|
|
1916
|
+
reason === "codex_transcript_turn_not_found" ||
|
|
1917
|
+
reason === "codex_transcript_turn_not_terminal" ||
|
|
1918
|
+
reason === "codex_transcript_token_count_missing"
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
function turnTelemetryForTerminal(
|
|
1923
|
+
input: RecordHookSignalV3Input,
|
|
1924
|
+
state: HookProducerStateV3,
|
|
1925
|
+
observedAt: string,
|
|
1926
|
+
): TurnTelemetryV3 {
|
|
1927
|
+
const native = extractTurnTelemetryV3(input.adapter, input.payload.raw, observedAt);
|
|
1928
|
+
if (native.context.state === "observed" || input.adapter === "cursor") return native;
|
|
1929
|
+
|
|
1930
|
+
const runtime = readRuntimeContextTelemetry(
|
|
1931
|
+
{
|
|
1932
|
+
adapter: input.adapter,
|
|
1933
|
+
session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
1934
|
+
turn_id: input.payload.turn_id ?? state.current_native_turn_id,
|
|
1935
|
+
transcript_path: runtimeTranscriptPath(input, state),
|
|
1936
|
+
mode: "turn",
|
|
1937
|
+
},
|
|
1938
|
+
input.runtimeTelemetryOptions,
|
|
1939
|
+
);
|
|
1940
|
+
if (runtime.bytes_read > 0) recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1941
|
+
if (runtime.state === "unsupported") return native;
|
|
1942
|
+
return {
|
|
1943
|
+
...native,
|
|
1944
|
+
context: runtimeContextObservation(runtime),
|
|
1945
|
+
context_provenance: {
|
|
1946
|
+
source_event:
|
|
1947
|
+
runtime.state === "observed" ? runtime.source_event : `${input.adapter}.runtime_context`,
|
|
1948
|
+
...(runtime.state === "observed" ? { source_witness: runtime.source_witness } : {}),
|
|
1949
|
+
...(input.adapterVersion ? { runtime_version: input.adapterVersion } : {}),
|
|
1950
|
+
attestation: "derived",
|
|
1951
|
+
confidence: runtime.state === "observed" ? "exact" : "high",
|
|
1952
|
+
},
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Resolve Codex's rollout once, retain it in owner-only producer state, and
|
|
1958
|
+
* verify the cached path against the native session before every reuse. Public
|
|
1959
|
+
* events receive only numeric telemetry and an opaque source witness.
|
|
1960
|
+
*/
|
|
1961
|
+
function runtimeTranscriptPath(
|
|
1962
|
+
input: RecordHookSignalV3Input,
|
|
1963
|
+
state: HookProducerStateV3,
|
|
1964
|
+
): string | undefined {
|
|
1965
|
+
if (input.adapter !== "codex") return input.payload.transcript_path;
|
|
1966
|
+
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
1967
|
+
if (!nativeSessionId) return input.payload.transcript_path;
|
|
1968
|
+
|
|
1969
|
+
const supplied = input.payload.transcript_path;
|
|
1970
|
+
if (supplied) {
|
|
1971
|
+
const verified = discoverCodexSessionTranscript(
|
|
1972
|
+
nativeSessionId,
|
|
1973
|
+
supplied,
|
|
1974
|
+
input.runtimeTelemetryOptions,
|
|
1975
|
+
);
|
|
1976
|
+
if (verified) state.runtime_transcript_path = verified;
|
|
1977
|
+
// Preserve a supplied mismatch so the typed reader reports it honestly.
|
|
1978
|
+
return verified ?? supplied;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
if (state.runtime_transcript_path) {
|
|
1982
|
+
const verified = discoverCodexSessionTranscript(
|
|
1983
|
+
nativeSessionId,
|
|
1984
|
+
state.runtime_transcript_path,
|
|
1985
|
+
input.runtimeTelemetryOptions,
|
|
1986
|
+
);
|
|
1987
|
+
if (verified) return verified;
|
|
1988
|
+
state.runtime_transcript_path = undefined;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
const discovered = discoverCodexSessionTranscript(
|
|
1992
|
+
nativeSessionId,
|
|
1993
|
+
undefined,
|
|
1994
|
+
input.runtimeTelemetryOptions,
|
|
1995
|
+
);
|
|
1996
|
+
if (discovered) state.runtime_transcript_path = discovered;
|
|
1997
|
+
return discovered;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
function runtimeContextObservation(
|
|
2001
|
+
runtime: RuntimeContextTelemetry,
|
|
2002
|
+
): TelemetryObservationV3<ContextMeasurementV3> {
|
|
2003
|
+
if (runtime.state === "observed") {
|
|
2004
|
+
return {
|
|
2005
|
+
state: "observed",
|
|
2006
|
+
value: {
|
|
2007
|
+
used_tokens: runtime.used_tokens,
|
|
2008
|
+
limit_tokens: runtime.limit_tokens,
|
|
2009
|
+
remaining_tokens: Math.max(0, runtime.limit_tokens - runtime.used_tokens),
|
|
2010
|
+
measured_at: runtime.measured_at,
|
|
2011
|
+
method: runtime.method,
|
|
2012
|
+
},
|
|
2013
|
+
attestation: "derived",
|
|
2014
|
+
confidence: "exact",
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
return {
|
|
2018
|
+
state: "expected_but_missing",
|
|
2019
|
+
capability: "context_usage",
|
|
2020
|
+
reason: runtime.reason,
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/** Seed the change-detection baseline from a committed session.started. */
|
|
2025
|
+
function seedAttestedTuning(state: HookProducerStateV3, event: { payload: unknown }): void {
|
|
2026
|
+
const attestation = (event.payload as { runtime_attestation?: RuntimeAttestationV3Base })
|
|
2027
|
+
.runtime_attestation;
|
|
2028
|
+
if (!attestation) return;
|
|
2029
|
+
const tuning = attestation.tuning;
|
|
2030
|
+
state.last_attested_tuning =
|
|
2031
|
+
tuning?.state === "observed"
|
|
2032
|
+
? {
|
|
2033
|
+
...(tuning.value?.effort ? { effort: tuning.value.effort } : {}),
|
|
2034
|
+
...(tuning.value?.speed ? { speed: tuning.value.speed } : {}),
|
|
2035
|
+
}
|
|
2036
|
+
: {};
|
|
2037
|
+
const model = attestation.model;
|
|
2038
|
+
if (model.state === "observed") {
|
|
2039
|
+
state.last_attested_model = { provider: model.value.provider, id: model.value.id };
|
|
2040
|
+
}
|
|
2041
|
+
state.last_attested_model_observation = model;
|
|
2042
|
+
state.last_attested_tuning_observation = tuning;
|
|
2043
|
+
state.last_attested_telemetry = attestation.telemetry as RuntimeTelemetryCapabilitiesV3;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* Emit `session.attestation_changed` when the observed tuning moves.
|
|
2048
|
+
*
|
|
2049
|
+
* Detection channels are deliberately cheap: CC stamps `effort.level` on
|
|
2050
|
+
* every tool hook and Stop (zero I/O), and Codex tuning is read from the
|
|
2051
|
+
* rollout only at turn terminals, where the context enrichment already pays
|
|
2052
|
+
* for a bounded tail read. A pre-upgrade state file (no baseline) seeds
|
|
2053
|
+
* silently so existing sessions do not fire a spurious change on their next
|
|
2054
|
+
* tool event; a fresh session's baseline comes from its session.started.
|
|
2055
|
+
*/
|
|
2056
|
+
function maybeAttestTuningChange(
|
|
2057
|
+
input: RecordHookSignalV3Input,
|
|
2058
|
+
state: HookProducerStateV3,
|
|
2059
|
+
path: string,
|
|
2060
|
+
rootId: `root_${string}`,
|
|
2061
|
+
): void {
|
|
2062
|
+
if (
|
|
2063
|
+
input.signal !== "pre-tool-use" &&
|
|
2064
|
+
input.signal !== "post-tool-use" &&
|
|
2065
|
+
input.signal !== "stop"
|
|
2066
|
+
) {
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
if (state.terminal) return;
|
|
2070
|
+
|
|
2071
|
+
let candidate: { effort?: string; speed?: string; model?: string } | undefined;
|
|
2072
|
+
let attestationSource: "native" | "derived" = "native";
|
|
2073
|
+
let sourceEvent = `${input.adapter}.effort-payload`;
|
|
2074
|
+
if (input.adapter === "claude-code" || input.adapter === "cursor") {
|
|
2075
|
+
if (!input.payload.effort) return;
|
|
2076
|
+
candidate = { effort: input.payload.effort };
|
|
2077
|
+
} else if (
|
|
2078
|
+
input.adapter === "codex" &&
|
|
2079
|
+
(input.signal === "stop" ||
|
|
2080
|
+
// Early probe: without it, a Codex card shows no effort until the first
|
|
2081
|
+
// turn terminal. One rollout read per turn while effort is unknown.
|
|
2082
|
+
(input.signal === "post-tool-use" &&
|
|
2083
|
+
!state.last_attested_tuning?.effort &&
|
|
2084
|
+
state.current_turn_id !== undefined &&
|
|
2085
|
+
state.tuning_probe_turn_id !== state.current_turn_id))
|
|
2086
|
+
) {
|
|
2087
|
+
if (input.signal === "post-tool-use") state.tuning_probe_turn_id = state.current_turn_id;
|
|
2088
|
+
const runtime = readRuntimeTuning(
|
|
2089
|
+
{
|
|
2090
|
+
adapter: "codex",
|
|
2091
|
+
session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
2092
|
+
transcript_path: runtimeTranscriptPath(input, state),
|
|
2093
|
+
},
|
|
2094
|
+
input.runtimeTelemetryOptions,
|
|
2095
|
+
);
|
|
2096
|
+
if (runtime.bytes_read > 0) recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
2097
|
+
if (runtime.state !== "observed") return;
|
|
2098
|
+
candidate = {
|
|
2099
|
+
...(runtime.effort ? { effort: runtime.effort } : {}),
|
|
2100
|
+
...(runtime.speed ? { speed: runtime.speed } : {}),
|
|
2101
|
+
...(runtime.model ? { model: runtime.model } : {}),
|
|
2102
|
+
};
|
|
2103
|
+
attestationSource = "derived";
|
|
2104
|
+
sourceEvent = runtime.source_event;
|
|
2105
|
+
} else {
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
const baseline = state.last_attested_tuning;
|
|
2110
|
+
const next = { effort: candidate.effort, speed: candidate.speed ?? baseline?.speed };
|
|
2111
|
+
if (baseline === undefined || state.last_attested_telemetry === undefined) {
|
|
2112
|
+
// Pre-upgrade state: adopt without emitting; there is no attested prior.
|
|
2113
|
+
state.last_attested_tuning = { ...(next.effort ? { effort: next.effort } : {}) };
|
|
2114
|
+
if (next.speed) state.last_attested_tuning.speed = next.speed;
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
if (baseline.effort === next.effort && (candidate.speed ?? baseline.speed) === baseline.speed) {
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// CC change detection rides the payload; the paired transcript row also
|
|
2122
|
+
// carries speed and the (possibly swapped) model, so refresh from it.
|
|
2123
|
+
if (input.adapter === "claude-code") {
|
|
2124
|
+
const runtime = readRuntimeTuning({
|
|
2125
|
+
adapter: "claude-code",
|
|
2126
|
+
transcript_path: input.payload.transcript_path,
|
|
2127
|
+
});
|
|
2128
|
+
if (runtime.bytes_read > 0) recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
2129
|
+
if (runtime.state === "observed" && runtime.effort === candidate.effort) {
|
|
2130
|
+
if (runtime.speed) candidate.speed = runtime.speed;
|
|
2131
|
+
if (runtime.model) candidate.model = runtime.model;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
const model: RuntimeAttestationV3Base["model"] = candidate.model
|
|
2136
|
+
? {
|
|
2137
|
+
state: "observed",
|
|
2138
|
+
value: { provider: modelProviderFor(input.adapter), id: safeRole(candidate.model) },
|
|
2139
|
+
attestation: "derived",
|
|
2140
|
+
confidence: "high",
|
|
2141
|
+
}
|
|
2142
|
+
: state.last_attested_model
|
|
2143
|
+
? {
|
|
2144
|
+
state: "observed",
|
|
2145
|
+
value: state.last_attested_model,
|
|
2146
|
+
attestation: "derived",
|
|
2147
|
+
confidence: "high",
|
|
2148
|
+
}
|
|
2149
|
+
: { state: "expected_but_missing", capability: "model_identity", reason: "not_reported" };
|
|
2150
|
+
const tuning: RuntimeAttestationV3Base["tuning"] =
|
|
2151
|
+
candidate.effort || candidate.speed
|
|
2152
|
+
? {
|
|
2153
|
+
state: "observed",
|
|
2154
|
+
value: {
|
|
2155
|
+
...(candidate.effort ? { effort: candidate.effort } : {}),
|
|
2156
|
+
...(candidate.speed ? { speed: candidate.speed } : {}),
|
|
2157
|
+
},
|
|
2158
|
+
attestation: attestationSource,
|
|
2159
|
+
confidence: "exact",
|
|
2160
|
+
}
|
|
2161
|
+
: { state: "unsupported", capability: "effort_selection" };
|
|
2162
|
+
commitRuntimeAttestationChange(input, state, path, rootId, {
|
|
2163
|
+
model,
|
|
2164
|
+
tuning,
|
|
2165
|
+
telemetry: state.last_attested_telemetry,
|
|
2166
|
+
reason: "runtime_tuning_changed",
|
|
2167
|
+
source_event: sourceEvent,
|
|
2168
|
+
attestation: attestationSource,
|
|
2169
|
+
confidence: attestationSource === "native" ? "exact" : "high",
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
interface RuntimeAttestationChangeV3 {
|
|
2174
|
+
model: RuntimeAttestationV3Base["model"];
|
|
2175
|
+
tuning: RuntimeAttestationV3Base["tuning"];
|
|
2176
|
+
telemetry: RuntimeTelemetryCapabilitiesV3;
|
|
2177
|
+
reason: string;
|
|
2178
|
+
source_event: string;
|
|
2179
|
+
attestation: "native" | "derived";
|
|
2180
|
+
confidence: "exact" | "high";
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
function commitRuntimeAttestationChange(
|
|
2184
|
+
input: RecordHookSignalV3Input,
|
|
2185
|
+
state: HookProducerStateV3,
|
|
2186
|
+
path: string,
|
|
2187
|
+
rootId: `root_${string}`,
|
|
2188
|
+
change: RuntimeAttestationChangeV3,
|
|
2189
|
+
): void {
|
|
2190
|
+
const priorAttestationId = state.attestation_id;
|
|
2191
|
+
const nextAttestationId = attestationIdV3();
|
|
2192
|
+
const eventId = eventIdV3();
|
|
2193
|
+
const runtimeAttestation: RuntimeAttestationV3Base = {
|
|
2194
|
+
attestation_id: nextAttestationId,
|
|
2195
|
+
generation_id: state.generation_id,
|
|
2196
|
+
adapter: {
|
|
2197
|
+
state: "observed",
|
|
2198
|
+
value: {
|
|
2199
|
+
id: input.adapter,
|
|
2200
|
+
...(input.adapterVersion ? { version: input.adapterVersion } : {}),
|
|
2201
|
+
},
|
|
2202
|
+
attestation: "native",
|
|
2203
|
+
confidence: "exact",
|
|
2204
|
+
},
|
|
2205
|
+
harness: {
|
|
2206
|
+
state: "observed",
|
|
2207
|
+
value: {
|
|
2208
|
+
id: input.adapter,
|
|
2209
|
+
...(input.harnessVersion ? { version: input.harnessVersion } : {}),
|
|
2210
|
+
},
|
|
1532
2211
|
attestation: "native",
|
|
1533
2212
|
confidence: "exact",
|
|
2213
|
+
},
|
|
2214
|
+
model: change.model,
|
|
2215
|
+
tuning: change.tuning,
|
|
2216
|
+
telemetry: change.telemetry,
|
|
2217
|
+
capability_profile: state.capability_profile,
|
|
2218
|
+
declared_by_event_id: eventId,
|
|
2219
|
+
};
|
|
2220
|
+
const event = buildEventV3("session.attestation_changed", {
|
|
2221
|
+
event_id: eventId,
|
|
2222
|
+
producer: {
|
|
2223
|
+
producer_id: input.producer_id,
|
|
2224
|
+
boot_id: state.boot_id,
|
|
2225
|
+
sequence: state.next_sequence,
|
|
2226
|
+
component: "agent-hook",
|
|
2227
|
+
build_id: input.build_id,
|
|
2228
|
+
platform: input.platform,
|
|
2229
|
+
...(input.bridge ? { bridge: input.bridge } : {}),
|
|
2230
|
+
},
|
|
2231
|
+
scope: {
|
|
2232
|
+
root_id: rootId,
|
|
2233
|
+
instance_id: state.instance_id,
|
|
2234
|
+
session_id: state.session_id,
|
|
2235
|
+
generation_id: state.generation_id,
|
|
2236
|
+
...(input.run_id ? { run_id: input.run_id } : {}),
|
|
2237
|
+
...(input.workflow_id ? { workflow_id: input.workflow_id } : {}),
|
|
2238
|
+
...(input.workflow_agent_id ? { workflow_agent_id: input.workflow_agent_id } : {}),
|
|
2239
|
+
},
|
|
2240
|
+
attestation_id: nextAttestationId,
|
|
2241
|
+
links: { caused_by: state.last_event_id ? [state.last_event_id] : [] },
|
|
2242
|
+
provenance: {
|
|
2243
|
+
source_event: safeRole(change.source_event),
|
|
2244
|
+
attestation: change.attestation,
|
|
2245
|
+
confidence: change.confidence,
|
|
1534
2246
|
attribution: {
|
|
1535
2247
|
method: "native_payload",
|
|
1536
2248
|
state: "verified",
|
|
1537
2249
|
subject_instance_id: state.instance_id,
|
|
1538
2250
|
},
|
|
1539
2251
|
},
|
|
1540
|
-
observed_at:
|
|
1541
|
-
measurement.state === "observed"
|
|
1542
|
-
? measurement.value.measured_at
|
|
1543
|
-
: turnTerminal.time.observed_at,
|
|
2252
|
+
observed_at: input.observed_at ?? new Date().toISOString(),
|
|
1544
2253
|
monotonic_ns: orderedEventMonotonic(state, input.monotonic_ns),
|
|
1545
2254
|
clock_id: state.clock_id,
|
|
1546
2255
|
payload: {
|
|
1547
|
-
|
|
2256
|
+
prior_attestation_id: priorAttestationId,
|
|
2257
|
+
runtime_attestation: runtimeAttestation,
|
|
2258
|
+
reason: change.reason,
|
|
1548
2259
|
},
|
|
1549
2260
|
}) as EventV3;
|
|
2261
|
+
// Mutate BEFORE the commit: commitEventLocked publishes the state file, so
|
|
2262
|
+
// post-commit mutations would be dropped by the next signal's fresh read.
|
|
2263
|
+
// A crash between publishes replays the pending event against a state that
|
|
2264
|
+
// already carries the new attestation id, which is the id the event declares.
|
|
2265
|
+
state.attestation_id = nextAttestationId;
|
|
2266
|
+
state.last_attested_model_observation = change.model;
|
|
2267
|
+
state.last_attested_tuning_observation = change.tuning;
|
|
2268
|
+
state.last_attested_telemetry = change.telemetry;
|
|
2269
|
+
state.last_attested_model =
|
|
2270
|
+
change.model.state === "observed" ? { ...change.model.value } : undefined;
|
|
2271
|
+
state.last_attested_tuning = change.tuning.state === "observed" ? { ...change.tuning.value } : {};
|
|
1550
2272
|
commitEventLocked(input, state, path, event);
|
|
1551
2273
|
}
|
|
1552
2274
|
|
|
2275
|
+
function modelProviderFor(adapter: Adapter): string {
|
|
2276
|
+
if (adapter === "claude-code") return "anthropic";
|
|
2277
|
+
if (adapter === "codex") return "openai";
|
|
2278
|
+
return "cursor";
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
function recordRuntimeTelemetryTiming(state: HookProducerStateV3, durationMs: number): void {
|
|
2282
|
+
const duration = Math.max(0, Math.ceil(durationMs));
|
|
2283
|
+
if (!Number.isSafeInteger(duration)) return;
|
|
2284
|
+
state.turn_harness = {
|
|
2285
|
+
hook_time_ms: state.turn_harness.hook_time_ms + duration,
|
|
2286
|
+
hook_count: state.turn_harness.hook_count + 1,
|
|
2287
|
+
slowest_hook:
|
|
2288
|
+
duration >= state.turn_harness.slowest_hook_ms
|
|
2289
|
+
? "runtime-context"
|
|
2290
|
+
: state.turn_harness.slowest_hook,
|
|
2291
|
+
slowest_hook_ms: Math.max(duration, state.turn_harness.slowest_hook_ms),
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
|
|
1553
2295
|
function commitCapabilityDriftEvents(
|
|
1554
2296
|
input: RecordHookSignalV3Input,
|
|
1555
2297
|
state: HookProducerStateV3,
|
|
@@ -1854,6 +2596,7 @@ export function recordApprovedSessionEndV3(
|
|
|
1854
2596
|
state.generation_id,
|
|
1855
2597
|
state.privacy_epoch_id,
|
|
1856
2598
|
);
|
|
2599
|
+
reconcilePendingRuntimeContextsBeforeApprovedEnd(input, state, record.path, rootId, context);
|
|
1857
2600
|
const expected = [
|
|
1858
2601
|
"session_started",
|
|
1859
2602
|
"turn_closed",
|
|
@@ -2181,6 +2924,26 @@ export function readHookProducerStateByInstanceV3(
|
|
|
2181
2924
|
return matches.length === 1 ? matches[0] : undefined;
|
|
2182
2925
|
}
|
|
2183
2926
|
|
|
2927
|
+
/**
|
|
2928
|
+
* Join a coordination actor to its hook producer.
|
|
2929
|
+
*
|
|
2930
|
+
* Native subagent commands can retain the parent's session id even after the
|
|
2931
|
+
* child has its own canonical generation. Prefer the native session join, but
|
|
2932
|
+
* when that points at a different instance accept only one exact live
|
|
2933
|
+
* instance match for the same adapter.
|
|
2934
|
+
*/
|
|
2935
|
+
export function readJoinableHookProducerStateV3(
|
|
2936
|
+
coordRoot: string,
|
|
2937
|
+
adapter: Adapter,
|
|
2938
|
+
nativeSessionId: string,
|
|
2939
|
+
instanceId: `inst_${string}`,
|
|
2940
|
+
): HookProducerStateV3 | undefined {
|
|
2941
|
+
const direct = readHookProducerStateV3(coordRoot, adapter, nativeSessionId);
|
|
2942
|
+
if (direct?.instance_id === instanceId && !direct.terminal) return direct;
|
|
2943
|
+
const byInstance = readHookProducerStateByInstanceV3(coordRoot, instanceId);
|
|
2944
|
+
return byInstance?.adapter === adapter && !byInstance.terminal ? byInstance : undefined;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2184
2947
|
function newProducerState(
|
|
2185
2948
|
input: RecordHookSignalV3Input,
|
|
2186
2949
|
sessionId: `sid_${string}`,
|
|
@@ -2194,7 +2957,7 @@ function newProducerState(
|
|
|
2194
2957
|
adapter: input.adapter,
|
|
2195
2958
|
instance_id: input.instance_id,
|
|
2196
2959
|
session_id: sessionId,
|
|
2197
|
-
generation_id: generationIdV3(),
|
|
2960
|
+
generation_id: input.delegated_child?.generation_id ?? generationIdV3(),
|
|
2198
2961
|
attestation_id: attestationIdV3(),
|
|
2199
2962
|
capability_profile: adapterCapabilityProfileDigestV3(input.adapter),
|
|
2200
2963
|
cursor_mode: input.adapter === "cursor" ? (input.payload.cursor_mode ?? "unknown") : undefined,
|
|
@@ -2234,6 +2997,7 @@ function applyCommittedEvent(state: HookProducerStateV3, event: EventV3): void {
|
|
|
2234
2997
|
if (event.event_type === "session.started") {
|
|
2235
2998
|
state.started_event_id = event.event_id as `evt_${string}`;
|
|
2236
2999
|
state.session_span.open_event_id = event.event_id as `evt_${string}`;
|
|
3000
|
+
seedAttestedTuning(state, event);
|
|
2237
3001
|
}
|
|
2238
3002
|
if (event.event_type === "turn.started") {
|
|
2239
3003
|
const nextTurnId = (event.scope as { turn_id: `tid_${string}` }).turn_id;
|
|
@@ -2321,6 +3085,7 @@ function applyCommittedEvent(state: HookProducerStateV3, event: EventV3): void {
|
|
|
2321
3085
|
}
|
|
2322
3086
|
}
|
|
2323
3087
|
state.current_turn_id = undefined;
|
|
3088
|
+
state.current_native_turn_id = undefined;
|
|
2324
3089
|
state.current_turn_span = undefined;
|
|
2325
3090
|
state.tool_call_count = 0;
|
|
2326
3091
|
state.tool_call_count_turn_id = undefined;
|
|
@@ -2511,6 +3276,14 @@ function safeRole(value: unknown): string {
|
|
|
2511
3276
|
return /^[a-zA-Z0-9]/.test(normalized) ? normalized : "agent";
|
|
2512
3277
|
}
|
|
2513
3278
|
|
|
3279
|
+
function asLiveInstanceId(nativeInstanceId: string): `inst_${string}` {
|
|
3280
|
+
if (/^inst_[a-zA-Z0-9._-]{1,128}$/.test(nativeInstanceId)) {
|
|
3281
|
+
return nativeInstanceId as `inst_${string}`;
|
|
3282
|
+
}
|
|
3283
|
+
if (/^[a-zA-Z0-9._-]{1,128}$/.test(nativeInstanceId)) return `inst_${nativeInstanceId}`;
|
|
3284
|
+
return `inst_${createHash("sha256").update(nativeInstanceId.normalize("NFC")).digest("hex")}`;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
2514
3287
|
function safeTokenOrUndefined(value: unknown): string | undefined {
|
|
2515
3288
|
if (typeof value !== "string") return undefined;
|
|
2516
3289
|
const normalized = value.normalize("NFC");
|
|
@@ -2597,6 +3370,12 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
2597
3370
|
state.waits ??= [];
|
|
2598
3371
|
state.closed_turn_ids ??= [];
|
|
2599
3372
|
state.turn_harness ??= emptyTurnHarnessTiming();
|
|
3373
|
+
if (
|
|
3374
|
+
Array.isArray(state.pending_runtime_contexts) &&
|
|
3375
|
+
state.pending_runtime_contexts.length === 0
|
|
3376
|
+
) {
|
|
3377
|
+
state.pending_runtime_contexts = undefined;
|
|
3378
|
+
}
|
|
2600
3379
|
if (state.adapter === "cursor") state.cursor_mode ??= "unknown";
|
|
2601
3380
|
const allowedKeys = new Set([
|
|
2602
3381
|
"adapter",
|
|
@@ -2607,6 +3386,7 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
2607
3386
|
"closed_spans",
|
|
2608
3387
|
"closed_turn_ids",
|
|
2609
3388
|
"current_turn_id",
|
|
3389
|
+
"current_native_turn_id",
|
|
2610
3390
|
"current_turn_span",
|
|
2611
3391
|
"cursor_mode",
|
|
2612
3392
|
"delegations",
|
|
@@ -2614,11 +3394,19 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
2614
3394
|
"format_version",
|
|
2615
3395
|
"generation_id",
|
|
2616
3396
|
"instance_id",
|
|
3397
|
+
"last_attested_model",
|
|
3398
|
+
"last_attested_model_observation",
|
|
3399
|
+
"last_attested_telemetry",
|
|
3400
|
+
"last_attested_tuning",
|
|
3401
|
+
"last_attested_tuning_observation",
|
|
3402
|
+
"tuning_probe_turn_id",
|
|
2617
3403
|
"last_event_id",
|
|
2618
3404
|
"last_monotonic_ns",
|
|
2619
3405
|
"last_observed_at",
|
|
2620
3406
|
"next_sequence",
|
|
2621
3407
|
"pending",
|
|
3408
|
+
"pending_runtime_contexts",
|
|
3409
|
+
"runtime_transcript_path",
|
|
2622
3410
|
"privacy_epoch_id",
|
|
2623
3411
|
"session_id",
|
|
2624
3412
|
"session_span",
|
|
@@ -2710,11 +3498,25 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
2710
3498
|
!Number.isSafeInteger(state.turn_ordinal) ||
|
|
2711
3499
|
state.turn_ordinal < 0 ||
|
|
2712
3500
|
(state.current_turn_id !== undefined && !/^tid_[a-f0-9]{64}$/.test(state.current_turn_id)) ||
|
|
3501
|
+
(state.current_native_turn_id !== undefined &&
|
|
3502
|
+
(typeof state.current_native_turn_id !== "string" ||
|
|
3503
|
+
state.current_native_turn_id.length === 0 ||
|
|
3504
|
+
state.current_native_turn_id.length > 512)) ||
|
|
2713
3505
|
(state.last_event_id !== undefined && !/^evt_[0-9a-f-]{36}$/.test(state.last_event_id)) ||
|
|
2714
3506
|
(state.last_monotonic_ns !== undefined && !/^\d+$/.test(state.last_monotonic_ns)) ||
|
|
2715
3507
|
(state.last_observed_at !== undefined &&
|
|
2716
3508
|
!Number.isFinite(Date.parse(state.last_observed_at))) ||
|
|
2717
3509
|
(state.started_event_id !== undefined && !/^evt_[0-9a-f-]{36}$/.test(state.started_event_id)) ||
|
|
3510
|
+
(state.pending_runtime_contexts !== undefined &&
|
|
3511
|
+
(!Array.isArray(state.pending_runtime_contexts) ||
|
|
3512
|
+
state.pending_runtime_contexts.length === 0 ||
|
|
3513
|
+
state.pending_runtime_contexts.length > PENDING_RUNTIME_CONTEXT_CAP ||
|
|
3514
|
+
state.pending_runtime_contexts.some((pending) => !validPendingRuntimeContext(pending)))) ||
|
|
3515
|
+
(state.runtime_transcript_path !== undefined &&
|
|
3516
|
+
(typeof state.runtime_transcript_path !== "string" ||
|
|
3517
|
+
state.runtime_transcript_path.length === 0 ||
|
|
3518
|
+
state.runtime_transcript_path.length > 4096 ||
|
|
3519
|
+
state.runtime_transcript_path.includes("\0"))) ||
|
|
2718
3520
|
(state.pending?.source_id !== undefined &&
|
|
2719
3521
|
!/^hid_[a-f0-9]{64}$/.test(state.pending.source_id)) ||
|
|
2720
3522
|
(state.pending && !validateEventV3(state.pending.event).ok)
|
|
@@ -2794,6 +3596,39 @@ function validTurnHarnessTiming(value: TurnHarnessTimingV3): boolean {
|
|
|
2794
3596
|
);
|
|
2795
3597
|
}
|
|
2796
3598
|
|
|
3599
|
+
function validPendingRuntimeContext(value: unknown): value is PendingRuntimeContextV3 {
|
|
3600
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3601
|
+
const pending = value as PendingRuntimeContextV3;
|
|
3602
|
+
return (
|
|
3603
|
+
typeof pending.terminal_event_id === "string" &&
|
|
3604
|
+
/^evt_[0-9a-f-]{36}$/.test(pending.terminal_event_id) &&
|
|
3605
|
+
typeof pending.terminal_observed_at === "string" &&
|
|
3606
|
+
Number.isFinite(Date.parse(pending.terminal_observed_at)) &&
|
|
3607
|
+
typeof pending.turn_id === "string" &&
|
|
3608
|
+
/^tid_[a-f0-9]{64}$/.test(pending.turn_id) &&
|
|
3609
|
+
(pending.run_id === undefined || /^run_[0-9a-f-]{36}$/.test(pending.run_id)) &&
|
|
3610
|
+
(pending.workflow_id === undefined || /^wf_[0-9a-f-]{36}$/.test(pending.workflow_id)) &&
|
|
3611
|
+
(pending.workflow_agent_id === undefined ||
|
|
3612
|
+
/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(pending.workflow_agent_id)) &&
|
|
3613
|
+
typeof pending.native_session_id === "string" &&
|
|
3614
|
+
pending.native_session_id.length > 0 &&
|
|
3615
|
+
pending.native_session_id.length <= 512 &&
|
|
3616
|
+
typeof pending.native_turn_id === "string" &&
|
|
3617
|
+
pending.native_turn_id.length > 0 &&
|
|
3618
|
+
pending.native_turn_id.length <= 512 &&
|
|
3619
|
+
(pending.transcript_path === undefined ||
|
|
3620
|
+
(typeof pending.transcript_path === "string" &&
|
|
3621
|
+
pending.transcript_path.length > 0 &&
|
|
3622
|
+
pending.transcript_path.length <= 4096)) &&
|
|
3623
|
+
(pending.runtime_version === undefined ||
|
|
3624
|
+
(typeof pending.runtime_version === "string" &&
|
|
3625
|
+
/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(pending.runtime_version))) &&
|
|
3626
|
+
Number.isSafeInteger(pending.attempts) &&
|
|
3627
|
+
pending.attempts >= 0 &&
|
|
3628
|
+
pending.attempts < RUNTIME_CONTEXT_RETRY_LIMIT
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
|
|
2797
3632
|
function pidIsAlive(pid: number): boolean {
|
|
2798
3633
|
if (!Number.isSafeInteger(pid) || pid < 1) return false;
|
|
2799
3634
|
try {
|