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
|
@@ -3,17 +3,19 @@ import { chmodSync, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, open
|
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import { extractBashCommand } from "../../../hooks/adapter/parse.js";
|
|
6
|
+
import { discoverCodexSessionTranscript, readRuntimeContextTelemetry, readRuntimeTuning, } from "../../../hooks/adapter/runtime-telemetry.js";
|
|
6
7
|
import { fsyncParentDirectory } from "../../../workflow/durable-record.js";
|
|
7
8
|
import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.js";
|
|
8
9
|
import { buildEventV3 } from "../builder.js";
|
|
9
|
-
import { fingerprintV3, normalizeNativeIdV3 } from "../canonical.js";
|
|
10
|
+
import { canonicalJsonV3, fingerprintV3, normalizeNativeIdV3 } from "../canonical.js";
|
|
10
11
|
import { adapterCapabilityProfileDigestV3, adapterSignalSupportV3, cursorToolChannelSupportV3, } from "../capabilities.js";
|
|
11
12
|
import { capabilityDriftPayloadsV3 } from "../capability-drift.js";
|
|
12
13
|
import { markObservedClockRegressionV3 } from "../clock-order.js";
|
|
13
14
|
import { readEventV3ControlState } from "../control.js";
|
|
14
15
|
import { fingerprintContextV3 } from "../fingerprint-keys.js";
|
|
15
|
-
import { attestationIdV3, clockIdV3, delegationIdV3, generationIdV3, spanIdV3 } from "../ids.js";
|
|
16
|
+
import { attestationIdV3, clockIdV3, delegationIdV3, eventIdV3, generationIdV3, spanIdV3, } from "../ids.js";
|
|
16
17
|
import { readLedgerV3 } from "../reader.js";
|
|
18
|
+
import { effectiveRuntimeTelemetryCapabilitiesV3, } from "../runtime-telemetry-capabilities.js";
|
|
17
19
|
import { closeSpanStateV3, openSpanStateV3, recoverSpanUpperBoundV3, } from "../span-state.js";
|
|
18
20
|
import { extractTurnTelemetryV3, } from "../turn-telemetry.js";
|
|
19
21
|
import { assertEventV3, validateEventV3 } from "../validate.js";
|
|
@@ -27,6 +29,10 @@ const CLOSED_SPAN_TURN_RETENTION = 2;
|
|
|
27
29
|
const CLOSED_SPAN_MEMORY_CAP = 512;
|
|
28
30
|
const CLOSED_TURN_MEMORY_CAP = 16;
|
|
29
31
|
const SPAN_SOFT_WATERMARK = 128;
|
|
32
|
+
const PENDING_RUNTIME_CONTEXT_CAP = 4;
|
|
33
|
+
const RUNTIME_CONTEXT_RETRY_LIMIT = 2;
|
|
34
|
+
/** Finalization is off the interactive hook path; briefly cover Codex's delayed transcript flush. */
|
|
35
|
+
const APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS = [0, 250, 250];
|
|
30
36
|
/**
|
|
31
37
|
* Adapters whose turn-boundary recovery and mid-flight onboarding are enabled.
|
|
32
38
|
* Kept in code, outside the digested capability profiles, so tuning recovery
|
|
@@ -88,7 +94,14 @@ function hookSignalGate(control, adapter, signal) {
|
|
|
88
94
|
return undefined;
|
|
89
95
|
}
|
|
90
96
|
function sessionHashForSignal(input, context) {
|
|
91
|
-
const
|
|
97
|
+
const nativeChild = input.payload.subagent_id ?? input.payload.agent_id;
|
|
98
|
+
const childOwnsSignal = input.signal !== "sub-agent-start" &&
|
|
99
|
+
input.signal !== "sub-agent-stop" &&
|
|
100
|
+
nativeChild !== undefined &&
|
|
101
|
+
input.instance_id === asLiveInstanceId(nativeChild);
|
|
102
|
+
const nativeSession = childOwnsSignal
|
|
103
|
+
? nativeChild
|
|
104
|
+
: (input.payload.session_id ?? input.payload.conversation_id ?? input.payload.agent_id);
|
|
92
105
|
if (nativeSession !== undefined) {
|
|
93
106
|
return normalizeNativeIdV3(context, `${input.adapter}.session`, nativeSession);
|
|
94
107
|
}
|
|
@@ -130,6 +143,7 @@ function intakeRecord(input) {
|
|
|
130
143
|
: {}),
|
|
131
144
|
...(input.stop_remediation !== undefined ? { stop_remediation: input.stop_remediation } : {}),
|
|
132
145
|
...(input.turn_ritual ? { turn_ritual: input.turn_ritual } : {}),
|
|
146
|
+
...(input.delegated_child ? { delegated_child: input.delegated_child } : {}),
|
|
133
147
|
};
|
|
134
148
|
}
|
|
135
149
|
function inputForIntakeRecord(coordRoot, record) {
|
|
@@ -152,6 +166,7 @@ function inputForIntakeRecord(coordRoot, record) {
|
|
|
152
166
|
...(record.hook_duration_ms !== undefined ? { hook_duration_ms: record.hook_duration_ms } : {}),
|
|
153
167
|
...(record.stop_remediation !== undefined ? { stop_remediation: record.stop_remediation } : {}),
|
|
154
168
|
...(record.turn_ritual ? { turn_ritual: record.turn_ritual } : {}),
|
|
169
|
+
...(record.delegated_child ? { delegated_child: record.delegated_child } : {}),
|
|
155
170
|
};
|
|
156
171
|
}
|
|
157
172
|
/**
|
|
@@ -386,6 +401,9 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
386
401
|
(cursorPromptWithinOpenTurn ||
|
|
387
402
|
(nativeTid !== undefined && state.current_turn_id === nativeTid));
|
|
388
403
|
recordTurnHarnessTiming(state, input, !duplicateOpenTurnStart);
|
|
404
|
+
reconcilePendingRuntimeContexts(input, state, path, rootId, fingerprintContext, {
|
|
405
|
+
finalAttempt: input.signal === "session-end",
|
|
406
|
+
});
|
|
389
407
|
closeResolvedWaits(input, state, path, rootId, fingerprintContext, nativeTid);
|
|
390
408
|
if (input.signal === "permission-request") {
|
|
391
409
|
const waitId = waitIdForInput(input, fingerprintContext);
|
|
@@ -435,6 +453,7 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
435
453
|
boot_id: state.boot_id,
|
|
436
454
|
clock: eventClock,
|
|
437
455
|
});
|
|
456
|
+
state.current_native_turn_id = input.payload.turn_id;
|
|
438
457
|
}
|
|
439
458
|
let sourceId = sourceIdForSignal(input, rootFingerprintContext);
|
|
440
459
|
const semanticKey = cursorShellSemanticKey(input, rootFingerprintContext);
|
|
@@ -628,7 +647,7 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
628
647
|
})
|
|
629
648
|
: undefined;
|
|
630
649
|
const turnTelemetry = input.signal === "stop" || input.signal === "stop-failure"
|
|
631
|
-
?
|
|
650
|
+
? turnTelemetryForTerminal(input, state, eventClock.observed_at)
|
|
632
651
|
: undefined;
|
|
633
652
|
const cursorToolChannelSupport = input.adapter === "cursor"
|
|
634
653
|
? cursorToolChannelSupportV3(state.cursor_mode ?? "unknown")
|
|
@@ -642,6 +661,9 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
642
661
|
}
|
|
643
662
|
const toolCallCountScopeMismatch = (input.signal === "stop" || input.signal === "stop-failure") &&
|
|
644
663
|
(!state.current_turn_id || state.tool_call_count_turn_id !== state.current_turn_id);
|
|
664
|
+
// Before the signal's own event: the tool call or turn that carries the
|
|
665
|
+
// new effort already ran under it, so it must ride the new attestation.
|
|
666
|
+
maybeAttestTuningChange(input, state, path, rootId);
|
|
645
667
|
const event = normalizeHookEventV3(input.signal, eventInput.payload, {
|
|
646
668
|
coordRoot: input.coordRoot,
|
|
647
669
|
adapter: input.adapter,
|
|
@@ -665,6 +687,7 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
665
687
|
platform: input.platform,
|
|
666
688
|
bridge: input.bridge,
|
|
667
689
|
capability_profile: state.capability_profile,
|
|
690
|
+
cursor_mode: state.cursor_mode,
|
|
668
691
|
fingerprintContext,
|
|
669
692
|
turn_id: state.current_turn_id,
|
|
670
693
|
span_id: openingSpan?.span_id ?? closingSpan?.span_id,
|
|
@@ -702,6 +725,44 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
702
725
|
});
|
|
703
726
|
if (!event)
|
|
704
727
|
return { state: "ignored" };
|
|
728
|
+
if (input.signal === "session-start" &&
|
|
729
|
+
input.delegated_child &&
|
|
730
|
+
event.event_type === "session.started") {
|
|
731
|
+
event.links = {
|
|
732
|
+
caused_by: [input.delegated_child.caused_by_event_id],
|
|
733
|
+
parent_generation_id: input.delegated_child.parent_generation_id,
|
|
734
|
+
delegation_id: input.delegated_child.delegation_id,
|
|
735
|
+
};
|
|
736
|
+
event.provenance = {
|
|
737
|
+
...event.provenance,
|
|
738
|
+
source_event: `${input.adapter}.subagent-start.child-generation`,
|
|
739
|
+
attestation: "derived",
|
|
740
|
+
confidence: "high",
|
|
741
|
+
attribution: {
|
|
742
|
+
method: "native_payload",
|
|
743
|
+
state: "verified",
|
|
744
|
+
subject_instance_id: state.instance_id,
|
|
745
|
+
},
|
|
746
|
+
};
|
|
747
|
+
assertEventV3(event);
|
|
748
|
+
}
|
|
749
|
+
if (input.signal === "user-prompt-submit" &&
|
|
750
|
+
input.delegated_child &&
|
|
751
|
+
event.event_type === "turn.started") {
|
|
752
|
+
event.links = { caused_by: [input.delegated_child.caused_by_event_id] };
|
|
753
|
+
event.provenance = {
|
|
754
|
+
...event.provenance,
|
|
755
|
+
source_event: `${input.adapter}.subagent-start.child-turn`,
|
|
756
|
+
attestation: "derived",
|
|
757
|
+
confidence: "high",
|
|
758
|
+
attribution: {
|
|
759
|
+
method: "native_payload",
|
|
760
|
+
state: "verified",
|
|
761
|
+
subject_instance_id: state.instance_id,
|
|
762
|
+
},
|
|
763
|
+
};
|
|
764
|
+
assertEventV3(event);
|
|
765
|
+
}
|
|
705
766
|
if (input.signal === "session-start" &&
|
|
706
767
|
input.session_start_derivation === "approved_lifecycle_reopen" &&
|
|
707
768
|
event.event_type === "session.started") {
|
|
@@ -742,7 +803,9 @@ function processHookSignalLocked(control, input, sessionHash, path) {
|
|
|
742
803
|
commitProgressObservation(input, state, path, rootId, event, progressKind);
|
|
743
804
|
}
|
|
744
805
|
if (turnTelemetry) {
|
|
745
|
-
|
|
806
|
+
const contextTarget = turnContextTarget(event);
|
|
807
|
+
queuePendingRuntimeContext(input, state, contextTarget, turnTelemetry.context);
|
|
808
|
+
commitTurnContextObservation(input, state, path, rootId, fingerprintContext, contextTarget, turnTelemetry.context, turnTelemetry.context_provenance);
|
|
746
809
|
}
|
|
747
810
|
if (event.event_type === "session.ended") {
|
|
748
811
|
commitCapabilityDriftEvents(input, state, path, rootId);
|
|
@@ -1101,8 +1164,8 @@ function commitEventLocked(input, state, path, event, sourceId) {
|
|
|
1101
1164
|
publishProducerState(path, state);
|
|
1102
1165
|
return durability;
|
|
1103
1166
|
}
|
|
1104
|
-
function commitTurnContextObservation(input, state, path, rootId,
|
|
1105
|
-
if (
|
|
1167
|
+
function commitTurnContextObservation(input, state, path, rootId, fingerprintContext, target, measurement, contextProvenance, observedAt) {
|
|
1168
|
+
if (!target)
|
|
1106
1169
|
return;
|
|
1107
1170
|
const event = buildEventV3("context.observed", {
|
|
1108
1171
|
producer: {
|
|
@@ -1119,34 +1182,523 @@ function commitTurnContextObservation(input, state, path, rootId, turnTerminal,
|
|
|
1119
1182
|
instance_id: state.instance_id,
|
|
1120
1183
|
session_id: state.session_id,
|
|
1121
1184
|
generation_id: state.generation_id,
|
|
1122
|
-
turn_id:
|
|
1123
|
-
...(
|
|
1124
|
-
...(
|
|
1125
|
-
...(
|
|
1185
|
+
turn_id: target.turn_id,
|
|
1186
|
+
...(target.run_id ? { run_id: target.run_id } : {}),
|
|
1187
|
+
...(target.workflow_id ? { workflow_id: target.workflow_id } : {}),
|
|
1188
|
+
...(target.workflow_agent_id ? { workflow_agent_id: target.workflow_agent_id } : {}),
|
|
1126
1189
|
},
|
|
1127
1190
|
attestation_id: state.attestation_id,
|
|
1128
|
-
links: { caused_by: [
|
|
1191
|
+
links: { caused_by: [target.terminal_event_id] },
|
|
1129
1192
|
provenance: {
|
|
1130
|
-
source_event:
|
|
1193
|
+
source_event: safeRole([
|
|
1194
|
+
contextProvenance?.source_event ?? `${input.adapter}.turn-context`,
|
|
1195
|
+
contextProvenance?.runtime_version,
|
|
1196
|
+
]
|
|
1197
|
+
.filter(Boolean)
|
|
1198
|
+
.join(".")),
|
|
1199
|
+
attestation: contextProvenance?.attestation ?? "native",
|
|
1200
|
+
confidence: contextProvenance?.confidence ?? "exact",
|
|
1201
|
+
...(contextProvenance?.source_witness
|
|
1202
|
+
? {
|
|
1203
|
+
source_record_id: normalizeNativeIdV3(fingerprintContext, `${input.adapter}.runtime-context`, contextProvenance.source_witness),
|
|
1204
|
+
}
|
|
1205
|
+
: {}),
|
|
1206
|
+
attribution: {
|
|
1207
|
+
method: "native_payload",
|
|
1208
|
+
state: "verified",
|
|
1209
|
+
subject_instance_id: state.instance_id,
|
|
1210
|
+
},
|
|
1211
|
+
},
|
|
1212
|
+
// The source sample may precede native task completion. The event itself
|
|
1213
|
+
// is committed after turn.completed, while measurement.measured_at keeps
|
|
1214
|
+
// the sample clock without creating a causal wall-clock regression.
|
|
1215
|
+
observed_at: observedAt ?? target.terminal_observed_at,
|
|
1216
|
+
monotonic_ns: orderedEventMonotonic(state, input.monotonic_ns),
|
|
1217
|
+
clock_id: state.clock_id,
|
|
1218
|
+
payload: {
|
|
1219
|
+
measurement,
|
|
1220
|
+
},
|
|
1221
|
+
});
|
|
1222
|
+
commitEventLocked(input, state, path, event);
|
|
1223
|
+
maybeAttestContextCapabilityChange(input, state, path, rootId, measurement, contextProvenance);
|
|
1224
|
+
}
|
|
1225
|
+
function maybeAttestContextCapabilityChange(input, state, path, rootId, measurement, provenance) {
|
|
1226
|
+
const context = measurement.state === "observed"
|
|
1227
|
+
? {
|
|
1228
|
+
state: "observed",
|
|
1229
|
+
source: provenance?.source_event ?? `${input.adapter}.turn_context`,
|
|
1230
|
+
attestation: provenance?.attestation ?? measurement.attestation,
|
|
1231
|
+
confidence: provenance?.confidence ?? measurement.confidence,
|
|
1232
|
+
}
|
|
1233
|
+
: measurement.state === "expected_but_missing"
|
|
1234
|
+
? { state: "partial", reason: measurement.reason }
|
|
1235
|
+
: { state: "unsupported" };
|
|
1236
|
+
const telemetry = effectiveRuntimeTelemetryCapabilitiesV3({
|
|
1237
|
+
adapter: input.adapter,
|
|
1238
|
+
...(state.cursor_mode ? { cursor_mode: state.cursor_mode } : {}),
|
|
1239
|
+
context,
|
|
1240
|
+
canonical_turn_boundaries: true,
|
|
1241
|
+
});
|
|
1242
|
+
if (!state.last_attested_telemetry)
|
|
1243
|
+
return;
|
|
1244
|
+
if (canonicalJsonV3(telemetry) === canonicalJsonV3(state.last_attested_telemetry))
|
|
1245
|
+
return;
|
|
1246
|
+
if (!state.last_attested_model_observation || !state.last_attested_tuning_observation)
|
|
1247
|
+
return;
|
|
1248
|
+
commitRuntimeAttestationChange(input, state, path, rootId, {
|
|
1249
|
+
model: state.last_attested_model_observation,
|
|
1250
|
+
tuning: state.last_attested_tuning_observation,
|
|
1251
|
+
telemetry,
|
|
1252
|
+
reason: "runtime_telemetry_changed",
|
|
1253
|
+
source_event: provenance?.source_event ?? `${input.adapter}.turn_context`,
|
|
1254
|
+
attestation: provenance?.attestation ?? "derived",
|
|
1255
|
+
confidence: provenance?.confidence ?? (measurement.state === "observed" ? "exact" : "high"),
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
function turnContextTarget(turnTerminal) {
|
|
1259
|
+
if (turnTerminal.event_type !== "turn.completed")
|
|
1260
|
+
return undefined;
|
|
1261
|
+
const scope = turnTerminal.scope;
|
|
1262
|
+
return {
|
|
1263
|
+
terminal_event_id: turnTerminal.event_id,
|
|
1264
|
+
terminal_observed_at: turnTerminal.time.observed_at,
|
|
1265
|
+
turn_id: scope.turn_id,
|
|
1266
|
+
...(scope.run_id ? { run_id: scope.run_id } : {}),
|
|
1267
|
+
...(scope.workflow_id ? { workflow_id: scope.workflow_id } : {}),
|
|
1268
|
+
...(scope.workflow_agent_id ? { workflow_agent_id: scope.workflow_agent_id } : {}),
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function queuePendingRuntimeContext(input, state, target, measurement) {
|
|
1272
|
+
if (input.adapter !== "codex" ||
|
|
1273
|
+
!target ||
|
|
1274
|
+
measurement.state !== "expected_but_missing" ||
|
|
1275
|
+
!retryableRuntimeContextReason(measurement.reason)) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
1279
|
+
const nativeTurnId = input.payload.turn_id;
|
|
1280
|
+
if (!nativeSessionId || !nativeTurnId)
|
|
1281
|
+
return;
|
|
1282
|
+
const pending = {
|
|
1283
|
+
...target,
|
|
1284
|
+
native_session_id: nativeSessionId,
|
|
1285
|
+
native_turn_id: nativeTurnId,
|
|
1286
|
+
...((input.payload.transcript_path ?? state.runtime_transcript_path)
|
|
1287
|
+
? { transcript_path: input.payload.transcript_path ?? state.runtime_transcript_path }
|
|
1288
|
+
: {}),
|
|
1289
|
+
...(input.adapterVersion ? { runtime_version: input.adapterVersion } : {}),
|
|
1290
|
+
attempts: 0,
|
|
1291
|
+
};
|
|
1292
|
+
state.pending_runtime_contexts = [
|
|
1293
|
+
...(state.pending_runtime_contexts ?? []).filter((candidate) => candidate.turn_id !== target.turn_id),
|
|
1294
|
+
pending,
|
|
1295
|
+
].slice(-PENDING_RUNTIME_CONTEXT_CAP);
|
|
1296
|
+
}
|
|
1297
|
+
function reconcilePendingRuntimeContexts(input, state, path, rootId, fingerprintContext, options = {}) {
|
|
1298
|
+
if (state.adapter !== "codex" || !state.pending_runtime_contexts?.length)
|
|
1299
|
+
return;
|
|
1300
|
+
for (const pending of [...state.pending_runtime_contexts]) {
|
|
1301
|
+
const runtime = readRuntimeContextTelemetry({
|
|
1302
|
+
adapter: "codex",
|
|
1303
|
+
session_id: pending.native_session_id,
|
|
1304
|
+
turn_id: pending.native_turn_id,
|
|
1305
|
+
transcript_path: pending.transcript_path,
|
|
1306
|
+
mode: "turn",
|
|
1307
|
+
}, input.runtimeTelemetryOptions);
|
|
1308
|
+
if (runtime.bytes_read > 0)
|
|
1309
|
+
recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1310
|
+
const queue = state.pending_runtime_contexts;
|
|
1311
|
+
if (!queue)
|
|
1312
|
+
break;
|
|
1313
|
+
const index = queue.findIndex((candidate) => candidate.turn_id === pending.turn_id);
|
|
1314
|
+
if (index < 0)
|
|
1315
|
+
continue;
|
|
1316
|
+
if (runtime.state === "observed") {
|
|
1317
|
+
queue.splice(index, 1);
|
|
1318
|
+
if (queue.length === 0) {
|
|
1319
|
+
state.pending_runtime_contexts = undefined;
|
|
1320
|
+
}
|
|
1321
|
+
commitTurnContextObservation(input, state, path, rootId, fingerprintContext, pending, runtimeContextObservation(runtime), {
|
|
1322
|
+
source_event: runtime.source_event,
|
|
1323
|
+
source_witness: runtime.source_witness,
|
|
1324
|
+
...(pending.runtime_version ? { runtime_version: pending.runtime_version } : {}),
|
|
1325
|
+
attestation: "derived",
|
|
1326
|
+
confidence: "exact",
|
|
1327
|
+
}, signalClock(input).observed_at);
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
const attempts = pending.attempts + 1;
|
|
1331
|
+
if (options.finalAttempt ||
|
|
1332
|
+
runtime.state === "unsupported" ||
|
|
1333
|
+
!retryableRuntimeContextReason(runtime.reason) ||
|
|
1334
|
+
(!options.deferRetryLimit && attempts >= RUNTIME_CONTEXT_RETRY_LIMIT)) {
|
|
1335
|
+
queue.splice(index, 1);
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
queue[index] = { ...pending, attempts };
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (state.pending_runtime_contexts?.length === 0) {
|
|
1342
|
+
state.pending_runtime_contexts = undefined;
|
|
1343
|
+
}
|
|
1344
|
+
publishProducerState(path, state);
|
|
1345
|
+
}
|
|
1346
|
+
function reconcilePendingRuntimeContextsBeforeApprovedEnd(input, state, path, rootId, fingerprintContext) {
|
|
1347
|
+
if (state.adapter !== "codex" || !state.pending_runtime_contexts?.length)
|
|
1348
|
+
return;
|
|
1349
|
+
for (const [index, delayMs] of APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS.entries()) {
|
|
1350
|
+
if (delayMs > 0)
|
|
1351
|
+
sleepSync(delayMs);
|
|
1352
|
+
reconcilePendingRuntimeContexts({
|
|
1353
|
+
coordRoot: input.coordRoot,
|
|
1354
|
+
mode: input.mode,
|
|
1355
|
+
signal: "session-end",
|
|
1356
|
+
payload: { raw: {} },
|
|
1357
|
+
adapter: state.adapter,
|
|
1358
|
+
instance_id: state.instance_id,
|
|
1359
|
+
producer_id: "prd_agent-finalizer",
|
|
1360
|
+
build_id: input.build_id,
|
|
1361
|
+
platform: input.platform,
|
|
1362
|
+
observed_at: new Date().toISOString(),
|
|
1363
|
+
writerOptions: input.writerOptions,
|
|
1364
|
+
}, state, path, rootId, fingerprintContext, {
|
|
1365
|
+
deferRetryLimit: true,
|
|
1366
|
+
finalAttempt: index === APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS.length - 1,
|
|
1367
|
+
});
|
|
1368
|
+
if (!state.pending_runtime_contexts?.length)
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
function retryableRuntimeContextReason(reason) {
|
|
1373
|
+
return (reason === "codex_transcript_turn_not_found" ||
|
|
1374
|
+
reason === "codex_transcript_turn_not_terminal" ||
|
|
1375
|
+
reason === "codex_transcript_token_count_missing");
|
|
1376
|
+
}
|
|
1377
|
+
function turnTelemetryForTerminal(input, state, observedAt) {
|
|
1378
|
+
const native = extractTurnTelemetryV3(input.adapter, input.payload.raw, observedAt);
|
|
1379
|
+
if (native.context.state === "observed" || input.adapter === "cursor")
|
|
1380
|
+
return native;
|
|
1381
|
+
const runtime = readRuntimeContextTelemetry({
|
|
1382
|
+
adapter: input.adapter,
|
|
1383
|
+
session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
1384
|
+
turn_id: input.payload.turn_id ?? state.current_native_turn_id,
|
|
1385
|
+
transcript_path: runtimeTranscriptPath(input, state),
|
|
1386
|
+
mode: "turn",
|
|
1387
|
+
}, input.runtimeTelemetryOptions);
|
|
1388
|
+
if (runtime.bytes_read > 0)
|
|
1389
|
+
recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1390
|
+
if (runtime.state === "unsupported")
|
|
1391
|
+
return native;
|
|
1392
|
+
return {
|
|
1393
|
+
...native,
|
|
1394
|
+
context: runtimeContextObservation(runtime),
|
|
1395
|
+
context_provenance: {
|
|
1396
|
+
source_event: runtime.state === "observed" ? runtime.source_event : `${input.adapter}.runtime_context`,
|
|
1397
|
+
...(runtime.state === "observed" ? { source_witness: runtime.source_witness } : {}),
|
|
1398
|
+
...(input.adapterVersion ? { runtime_version: input.adapterVersion } : {}),
|
|
1399
|
+
attestation: "derived",
|
|
1400
|
+
confidence: runtime.state === "observed" ? "exact" : "high",
|
|
1401
|
+
},
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Resolve Codex's rollout once, retain it in owner-only producer state, and
|
|
1406
|
+
* verify the cached path against the native session before every reuse. Public
|
|
1407
|
+
* events receive only numeric telemetry and an opaque source witness.
|
|
1408
|
+
*/
|
|
1409
|
+
function runtimeTranscriptPath(input, state) {
|
|
1410
|
+
if (input.adapter !== "codex")
|
|
1411
|
+
return input.payload.transcript_path;
|
|
1412
|
+
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
1413
|
+
if (!nativeSessionId)
|
|
1414
|
+
return input.payload.transcript_path;
|
|
1415
|
+
const supplied = input.payload.transcript_path;
|
|
1416
|
+
if (supplied) {
|
|
1417
|
+
const verified = discoverCodexSessionTranscript(nativeSessionId, supplied, input.runtimeTelemetryOptions);
|
|
1418
|
+
if (verified)
|
|
1419
|
+
state.runtime_transcript_path = verified;
|
|
1420
|
+
// Preserve a supplied mismatch so the typed reader reports it honestly.
|
|
1421
|
+
return verified ?? supplied;
|
|
1422
|
+
}
|
|
1423
|
+
if (state.runtime_transcript_path) {
|
|
1424
|
+
const verified = discoverCodexSessionTranscript(nativeSessionId, state.runtime_transcript_path, input.runtimeTelemetryOptions);
|
|
1425
|
+
if (verified)
|
|
1426
|
+
return verified;
|
|
1427
|
+
state.runtime_transcript_path = undefined;
|
|
1428
|
+
}
|
|
1429
|
+
const discovered = discoverCodexSessionTranscript(nativeSessionId, undefined, input.runtimeTelemetryOptions);
|
|
1430
|
+
if (discovered)
|
|
1431
|
+
state.runtime_transcript_path = discovered;
|
|
1432
|
+
return discovered;
|
|
1433
|
+
}
|
|
1434
|
+
function runtimeContextObservation(runtime) {
|
|
1435
|
+
if (runtime.state === "observed") {
|
|
1436
|
+
return {
|
|
1437
|
+
state: "observed",
|
|
1438
|
+
value: {
|
|
1439
|
+
used_tokens: runtime.used_tokens,
|
|
1440
|
+
limit_tokens: runtime.limit_tokens,
|
|
1441
|
+
remaining_tokens: Math.max(0, runtime.limit_tokens - runtime.used_tokens),
|
|
1442
|
+
measured_at: runtime.measured_at,
|
|
1443
|
+
method: runtime.method,
|
|
1444
|
+
},
|
|
1445
|
+
attestation: "derived",
|
|
1446
|
+
confidence: "exact",
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
state: "expected_but_missing",
|
|
1451
|
+
capability: "context_usage",
|
|
1452
|
+
reason: runtime.reason,
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
/** Seed the change-detection baseline from a committed session.started. */
|
|
1456
|
+
function seedAttestedTuning(state, event) {
|
|
1457
|
+
const attestation = event.payload
|
|
1458
|
+
.runtime_attestation;
|
|
1459
|
+
if (!attestation)
|
|
1460
|
+
return;
|
|
1461
|
+
const tuning = attestation.tuning;
|
|
1462
|
+
state.last_attested_tuning =
|
|
1463
|
+
tuning?.state === "observed"
|
|
1464
|
+
? {
|
|
1465
|
+
...(tuning.value?.effort ? { effort: tuning.value.effort } : {}),
|
|
1466
|
+
...(tuning.value?.speed ? { speed: tuning.value.speed } : {}),
|
|
1467
|
+
}
|
|
1468
|
+
: {};
|
|
1469
|
+
const model = attestation.model;
|
|
1470
|
+
if (model.state === "observed") {
|
|
1471
|
+
state.last_attested_model = { provider: model.value.provider, id: model.value.id };
|
|
1472
|
+
}
|
|
1473
|
+
state.last_attested_model_observation = model;
|
|
1474
|
+
state.last_attested_tuning_observation = tuning;
|
|
1475
|
+
state.last_attested_telemetry = attestation.telemetry;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Emit `session.attestation_changed` when the observed tuning moves.
|
|
1479
|
+
*
|
|
1480
|
+
* Detection channels are deliberately cheap: CC stamps `effort.level` on
|
|
1481
|
+
* every tool hook and Stop (zero I/O), and Codex tuning is read from the
|
|
1482
|
+
* rollout only at turn terminals, where the context enrichment already pays
|
|
1483
|
+
* for a bounded tail read. A pre-upgrade state file (no baseline) seeds
|
|
1484
|
+
* silently so existing sessions do not fire a spurious change on their next
|
|
1485
|
+
* tool event; a fresh session's baseline comes from its session.started.
|
|
1486
|
+
*/
|
|
1487
|
+
function maybeAttestTuningChange(input, state, path, rootId) {
|
|
1488
|
+
if (input.signal !== "pre-tool-use" &&
|
|
1489
|
+
input.signal !== "post-tool-use" &&
|
|
1490
|
+
input.signal !== "stop") {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (state.terminal)
|
|
1494
|
+
return;
|
|
1495
|
+
let candidate;
|
|
1496
|
+
let attestationSource = "native";
|
|
1497
|
+
let sourceEvent = `${input.adapter}.effort-payload`;
|
|
1498
|
+
if (input.adapter === "claude-code" || input.adapter === "cursor") {
|
|
1499
|
+
if (!input.payload.effort)
|
|
1500
|
+
return;
|
|
1501
|
+
candidate = { effort: input.payload.effort };
|
|
1502
|
+
}
|
|
1503
|
+
else if (input.adapter === "codex" &&
|
|
1504
|
+
(input.signal === "stop" ||
|
|
1505
|
+
// Early probe: without it, a Codex card shows no effort until the first
|
|
1506
|
+
// turn terminal. One rollout read per turn while effort is unknown.
|
|
1507
|
+
(input.signal === "post-tool-use" &&
|
|
1508
|
+
!state.last_attested_tuning?.effort &&
|
|
1509
|
+
state.current_turn_id !== undefined &&
|
|
1510
|
+
state.tuning_probe_turn_id !== state.current_turn_id))) {
|
|
1511
|
+
if (input.signal === "post-tool-use")
|
|
1512
|
+
state.tuning_probe_turn_id = state.current_turn_id;
|
|
1513
|
+
const runtime = readRuntimeTuning({
|
|
1514
|
+
adapter: "codex",
|
|
1515
|
+
session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
1516
|
+
transcript_path: runtimeTranscriptPath(input, state),
|
|
1517
|
+
}, input.runtimeTelemetryOptions);
|
|
1518
|
+
if (runtime.bytes_read > 0)
|
|
1519
|
+
recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1520
|
+
if (runtime.state !== "observed")
|
|
1521
|
+
return;
|
|
1522
|
+
candidate = {
|
|
1523
|
+
...(runtime.effort ? { effort: runtime.effort } : {}),
|
|
1524
|
+
...(runtime.speed ? { speed: runtime.speed } : {}),
|
|
1525
|
+
...(runtime.model ? { model: runtime.model } : {}),
|
|
1526
|
+
};
|
|
1527
|
+
attestationSource = "derived";
|
|
1528
|
+
sourceEvent = runtime.source_event;
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const baseline = state.last_attested_tuning;
|
|
1534
|
+
const next = { effort: candidate.effort, speed: candidate.speed ?? baseline?.speed };
|
|
1535
|
+
if (baseline === undefined || state.last_attested_telemetry === undefined) {
|
|
1536
|
+
// Pre-upgrade state: adopt without emitting; there is no attested prior.
|
|
1537
|
+
state.last_attested_tuning = { ...(next.effort ? { effort: next.effort } : {}) };
|
|
1538
|
+
if (next.speed)
|
|
1539
|
+
state.last_attested_tuning.speed = next.speed;
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
if (baseline.effort === next.effort && (candidate.speed ?? baseline.speed) === baseline.speed) {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
// CC change detection rides the payload; the paired transcript row also
|
|
1546
|
+
// carries speed and the (possibly swapped) model, so refresh from it.
|
|
1547
|
+
if (input.adapter === "claude-code") {
|
|
1548
|
+
const runtime = readRuntimeTuning({
|
|
1549
|
+
adapter: "claude-code",
|
|
1550
|
+
transcript_path: input.payload.transcript_path,
|
|
1551
|
+
});
|
|
1552
|
+
if (runtime.bytes_read > 0)
|
|
1553
|
+
recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
1554
|
+
if (runtime.state === "observed" && runtime.effort === candidate.effort) {
|
|
1555
|
+
if (runtime.speed)
|
|
1556
|
+
candidate.speed = runtime.speed;
|
|
1557
|
+
if (runtime.model)
|
|
1558
|
+
candidate.model = runtime.model;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
const model = candidate.model
|
|
1562
|
+
? {
|
|
1563
|
+
state: "observed",
|
|
1564
|
+
value: { provider: modelProviderFor(input.adapter), id: safeRole(candidate.model) },
|
|
1565
|
+
attestation: "derived",
|
|
1566
|
+
confidence: "high",
|
|
1567
|
+
}
|
|
1568
|
+
: state.last_attested_model
|
|
1569
|
+
? {
|
|
1570
|
+
state: "observed",
|
|
1571
|
+
value: state.last_attested_model,
|
|
1572
|
+
attestation: "derived",
|
|
1573
|
+
confidence: "high",
|
|
1574
|
+
}
|
|
1575
|
+
: { state: "expected_but_missing", capability: "model_identity", reason: "not_reported" };
|
|
1576
|
+
const tuning = candidate.effort || candidate.speed
|
|
1577
|
+
? {
|
|
1578
|
+
state: "observed",
|
|
1579
|
+
value: {
|
|
1580
|
+
...(candidate.effort ? { effort: candidate.effort } : {}),
|
|
1581
|
+
...(candidate.speed ? { speed: candidate.speed } : {}),
|
|
1582
|
+
},
|
|
1583
|
+
attestation: attestationSource,
|
|
1584
|
+
confidence: "exact",
|
|
1585
|
+
}
|
|
1586
|
+
: { state: "unsupported", capability: "effort_selection" };
|
|
1587
|
+
commitRuntimeAttestationChange(input, state, path, rootId, {
|
|
1588
|
+
model,
|
|
1589
|
+
tuning,
|
|
1590
|
+
telemetry: state.last_attested_telemetry,
|
|
1591
|
+
reason: "runtime_tuning_changed",
|
|
1592
|
+
source_event: sourceEvent,
|
|
1593
|
+
attestation: attestationSource,
|
|
1594
|
+
confidence: attestationSource === "native" ? "exact" : "high",
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
function commitRuntimeAttestationChange(input, state, path, rootId, change) {
|
|
1598
|
+
const priorAttestationId = state.attestation_id;
|
|
1599
|
+
const nextAttestationId = attestationIdV3();
|
|
1600
|
+
const eventId = eventIdV3();
|
|
1601
|
+
const runtimeAttestation = {
|
|
1602
|
+
attestation_id: nextAttestationId,
|
|
1603
|
+
generation_id: state.generation_id,
|
|
1604
|
+
adapter: {
|
|
1605
|
+
state: "observed",
|
|
1606
|
+
value: {
|
|
1607
|
+
id: input.adapter,
|
|
1608
|
+
...(input.adapterVersion ? { version: input.adapterVersion } : {}),
|
|
1609
|
+
},
|
|
1610
|
+
attestation: "native",
|
|
1611
|
+
confidence: "exact",
|
|
1612
|
+
},
|
|
1613
|
+
harness: {
|
|
1614
|
+
state: "observed",
|
|
1615
|
+
value: {
|
|
1616
|
+
id: input.adapter,
|
|
1617
|
+
...(input.harnessVersion ? { version: input.harnessVersion } : {}),
|
|
1618
|
+
},
|
|
1131
1619
|
attestation: "native",
|
|
1132
1620
|
confidence: "exact",
|
|
1621
|
+
},
|
|
1622
|
+
model: change.model,
|
|
1623
|
+
tuning: change.tuning,
|
|
1624
|
+
telemetry: change.telemetry,
|
|
1625
|
+
capability_profile: state.capability_profile,
|
|
1626
|
+
declared_by_event_id: eventId,
|
|
1627
|
+
};
|
|
1628
|
+
const event = buildEventV3("session.attestation_changed", {
|
|
1629
|
+
event_id: eventId,
|
|
1630
|
+
producer: {
|
|
1631
|
+
producer_id: input.producer_id,
|
|
1632
|
+
boot_id: state.boot_id,
|
|
1633
|
+
sequence: state.next_sequence,
|
|
1634
|
+
component: "agent-hook",
|
|
1635
|
+
build_id: input.build_id,
|
|
1636
|
+
platform: input.platform,
|
|
1637
|
+
...(input.bridge ? { bridge: input.bridge } : {}),
|
|
1638
|
+
},
|
|
1639
|
+
scope: {
|
|
1640
|
+
root_id: rootId,
|
|
1641
|
+
instance_id: state.instance_id,
|
|
1642
|
+
session_id: state.session_id,
|
|
1643
|
+
generation_id: state.generation_id,
|
|
1644
|
+
...(input.run_id ? { run_id: input.run_id } : {}),
|
|
1645
|
+
...(input.workflow_id ? { workflow_id: input.workflow_id } : {}),
|
|
1646
|
+
...(input.workflow_agent_id ? { workflow_agent_id: input.workflow_agent_id } : {}),
|
|
1647
|
+
},
|
|
1648
|
+
attestation_id: nextAttestationId,
|
|
1649
|
+
links: { caused_by: state.last_event_id ? [state.last_event_id] : [] },
|
|
1650
|
+
provenance: {
|
|
1651
|
+
source_event: safeRole(change.source_event),
|
|
1652
|
+
attestation: change.attestation,
|
|
1653
|
+
confidence: change.confidence,
|
|
1133
1654
|
attribution: {
|
|
1134
1655
|
method: "native_payload",
|
|
1135
1656
|
state: "verified",
|
|
1136
1657
|
subject_instance_id: state.instance_id,
|
|
1137
1658
|
},
|
|
1138
1659
|
},
|
|
1139
|
-
observed_at:
|
|
1140
|
-
? measurement.value.measured_at
|
|
1141
|
-
: turnTerminal.time.observed_at,
|
|
1660
|
+
observed_at: input.observed_at ?? new Date().toISOString(),
|
|
1142
1661
|
monotonic_ns: orderedEventMonotonic(state, input.monotonic_ns),
|
|
1143
1662
|
clock_id: state.clock_id,
|
|
1144
1663
|
payload: {
|
|
1145
|
-
|
|
1664
|
+
prior_attestation_id: priorAttestationId,
|
|
1665
|
+
runtime_attestation: runtimeAttestation,
|
|
1666
|
+
reason: change.reason,
|
|
1146
1667
|
},
|
|
1147
1668
|
});
|
|
1669
|
+
// Mutate BEFORE the commit: commitEventLocked publishes the state file, so
|
|
1670
|
+
// post-commit mutations would be dropped by the next signal's fresh read.
|
|
1671
|
+
// A crash between publishes replays the pending event against a state that
|
|
1672
|
+
// already carries the new attestation id, which is the id the event declares.
|
|
1673
|
+
state.attestation_id = nextAttestationId;
|
|
1674
|
+
state.last_attested_model_observation = change.model;
|
|
1675
|
+
state.last_attested_tuning_observation = change.tuning;
|
|
1676
|
+
state.last_attested_telemetry = change.telemetry;
|
|
1677
|
+
state.last_attested_model =
|
|
1678
|
+
change.model.state === "observed" ? { ...change.model.value } : undefined;
|
|
1679
|
+
state.last_attested_tuning = change.tuning.state === "observed" ? { ...change.tuning.value } : {};
|
|
1148
1680
|
commitEventLocked(input, state, path, event);
|
|
1149
1681
|
}
|
|
1682
|
+
function modelProviderFor(adapter) {
|
|
1683
|
+
if (adapter === "claude-code")
|
|
1684
|
+
return "anthropic";
|
|
1685
|
+
if (adapter === "codex")
|
|
1686
|
+
return "openai";
|
|
1687
|
+
return "cursor";
|
|
1688
|
+
}
|
|
1689
|
+
function recordRuntimeTelemetryTiming(state, durationMs) {
|
|
1690
|
+
const duration = Math.max(0, Math.ceil(durationMs));
|
|
1691
|
+
if (!Number.isSafeInteger(duration))
|
|
1692
|
+
return;
|
|
1693
|
+
state.turn_harness = {
|
|
1694
|
+
hook_time_ms: state.turn_harness.hook_time_ms + duration,
|
|
1695
|
+
hook_count: state.turn_harness.hook_count + 1,
|
|
1696
|
+
slowest_hook: duration >= state.turn_harness.slowest_hook_ms
|
|
1697
|
+
? "runtime-context"
|
|
1698
|
+
: state.turn_harness.slowest_hook,
|
|
1699
|
+
slowest_hook_ms: Math.max(duration, state.turn_harness.slowest_hook_ms),
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1150
1702
|
function commitCapabilityDriftEvents(input, state, path, rootId) {
|
|
1151
1703
|
const read = readLedgerV3(input.coordRoot);
|
|
1152
1704
|
if (!read.complete)
|
|
@@ -1390,6 +1942,7 @@ export function recordApprovedSessionEndV3(input) {
|
|
|
1390
1942
|
}
|
|
1391
1943
|
const rootId = control.genesis.event.scope.root_id;
|
|
1392
1944
|
const context = fingerprintContextV3(input.coordRoot, rootId, state.generation_id, state.privacy_epoch_id);
|
|
1945
|
+
reconcilePendingRuntimeContextsBeforeApprovedEnd(input, state, record.path, rootId, context);
|
|
1393
1946
|
const expected = [
|
|
1394
1947
|
"session_started",
|
|
1395
1948
|
"turn_closed",
|
|
@@ -1645,6 +2198,21 @@ export function readHookProducerStateByInstanceV3(coordRoot, instanceId) {
|
|
|
1645
2198
|
}
|
|
1646
2199
|
return matches.length === 1 ? matches[0] : undefined;
|
|
1647
2200
|
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Join a coordination actor to its hook producer.
|
|
2203
|
+
*
|
|
2204
|
+
* Native subagent commands can retain the parent's session id even after the
|
|
2205
|
+
* child has its own canonical generation. Prefer the native session join, but
|
|
2206
|
+
* when that points at a different instance accept only one exact live
|
|
2207
|
+
* instance match for the same adapter.
|
|
2208
|
+
*/
|
|
2209
|
+
export function readJoinableHookProducerStateV3(coordRoot, adapter, nativeSessionId, instanceId) {
|
|
2210
|
+
const direct = readHookProducerStateV3(coordRoot, adapter, nativeSessionId);
|
|
2211
|
+
if (direct?.instance_id === instanceId && !direct.terminal)
|
|
2212
|
+
return direct;
|
|
2213
|
+
const byInstance = readHookProducerStateByInstanceV3(coordRoot, instanceId);
|
|
2214
|
+
return byInstance?.adapter === adapter && !byInstance.terminal ? byInstance : undefined;
|
|
2215
|
+
}
|
|
1648
2216
|
function newProducerState(input, sessionId, epochId, boundaryEventId) {
|
|
1649
2217
|
const bootId = `boot_${randomUUID()}`;
|
|
1650
2218
|
return {
|
|
@@ -1653,7 +2221,7 @@ function newProducerState(input, sessionId, epochId, boundaryEventId) {
|
|
|
1653
2221
|
adapter: input.adapter,
|
|
1654
2222
|
instance_id: input.instance_id,
|
|
1655
2223
|
session_id: sessionId,
|
|
1656
|
-
generation_id: generationIdV3(),
|
|
2224
|
+
generation_id: input.delegated_child?.generation_id ?? generationIdV3(),
|
|
1657
2225
|
attestation_id: attestationIdV3(),
|
|
1658
2226
|
capability_profile: adapterCapabilityProfileDigestV3(input.adapter),
|
|
1659
2227
|
cursor_mode: input.adapter === "cursor" ? (input.payload.cursor_mode ?? "unknown") : undefined,
|
|
@@ -1694,6 +2262,7 @@ function applyCommittedEvent(state, event) {
|
|
|
1694
2262
|
if (event.event_type === "session.started") {
|
|
1695
2263
|
state.started_event_id = event.event_id;
|
|
1696
2264
|
state.session_span.open_event_id = event.event_id;
|
|
2265
|
+
seedAttestedTuning(state, event);
|
|
1697
2266
|
}
|
|
1698
2267
|
if (event.event_type === "turn.started") {
|
|
1699
2268
|
const nextTurnId = event.scope.turn_id;
|
|
@@ -1773,6 +2342,7 @@ function applyCommittedEvent(state, event) {
|
|
|
1773
2342
|
}
|
|
1774
2343
|
}
|
|
1775
2344
|
state.current_turn_id = undefined;
|
|
2345
|
+
state.current_native_turn_id = undefined;
|
|
1776
2346
|
state.current_turn_span = undefined;
|
|
1777
2347
|
state.tool_call_count = 0;
|
|
1778
2348
|
state.tool_call_count_turn_id = undefined;
|
|
@@ -1918,6 +2488,14 @@ function safeRole(value) {
|
|
|
1918
2488
|
.slice(0, 128);
|
|
1919
2489
|
return /^[a-zA-Z0-9]/.test(normalized) ? normalized : "agent";
|
|
1920
2490
|
}
|
|
2491
|
+
function asLiveInstanceId(nativeInstanceId) {
|
|
2492
|
+
if (/^inst_[a-zA-Z0-9._-]{1,128}$/.test(nativeInstanceId)) {
|
|
2493
|
+
return nativeInstanceId;
|
|
2494
|
+
}
|
|
2495
|
+
if (/^[a-zA-Z0-9._-]{1,128}$/.test(nativeInstanceId))
|
|
2496
|
+
return `inst_${nativeInstanceId}`;
|
|
2497
|
+
return `inst_${createHash("sha256").update(nativeInstanceId.normalize("NFC")).digest("hex")}`;
|
|
2498
|
+
}
|
|
1921
2499
|
function safeTokenOrUndefined(value) {
|
|
1922
2500
|
if (typeof value !== "string")
|
|
1923
2501
|
return undefined;
|
|
@@ -1996,6 +2574,10 @@ function readProducerState(path) {
|
|
|
1996
2574
|
state.waits ??= [];
|
|
1997
2575
|
state.closed_turn_ids ??= [];
|
|
1998
2576
|
state.turn_harness ??= emptyTurnHarnessTiming();
|
|
2577
|
+
if (Array.isArray(state.pending_runtime_contexts) &&
|
|
2578
|
+
state.pending_runtime_contexts.length === 0) {
|
|
2579
|
+
state.pending_runtime_contexts = undefined;
|
|
2580
|
+
}
|
|
1999
2581
|
if (state.adapter === "cursor")
|
|
2000
2582
|
state.cursor_mode ??= "unknown";
|
|
2001
2583
|
const allowedKeys = new Set([
|
|
@@ -2007,6 +2589,7 @@ function readProducerState(path) {
|
|
|
2007
2589
|
"closed_spans",
|
|
2008
2590
|
"closed_turn_ids",
|
|
2009
2591
|
"current_turn_id",
|
|
2592
|
+
"current_native_turn_id",
|
|
2010
2593
|
"current_turn_span",
|
|
2011
2594
|
"cursor_mode",
|
|
2012
2595
|
"delegations",
|
|
@@ -2014,11 +2597,19 @@ function readProducerState(path) {
|
|
|
2014
2597
|
"format_version",
|
|
2015
2598
|
"generation_id",
|
|
2016
2599
|
"instance_id",
|
|
2600
|
+
"last_attested_model",
|
|
2601
|
+
"last_attested_model_observation",
|
|
2602
|
+
"last_attested_telemetry",
|
|
2603
|
+
"last_attested_tuning",
|
|
2604
|
+
"last_attested_tuning_observation",
|
|
2605
|
+
"tuning_probe_turn_id",
|
|
2017
2606
|
"last_event_id",
|
|
2018
2607
|
"last_monotonic_ns",
|
|
2019
2608
|
"last_observed_at",
|
|
2020
2609
|
"next_sequence",
|
|
2021
2610
|
"pending",
|
|
2611
|
+
"pending_runtime_contexts",
|
|
2612
|
+
"runtime_transcript_path",
|
|
2022
2613
|
"privacy_epoch_id",
|
|
2023
2614
|
"session_id",
|
|
2024
2615
|
"session_span",
|
|
@@ -2097,11 +2688,25 @@ function readProducerState(path) {
|
|
|
2097
2688
|
!Number.isSafeInteger(state.turn_ordinal) ||
|
|
2098
2689
|
state.turn_ordinal < 0 ||
|
|
2099
2690
|
(state.current_turn_id !== undefined && !/^tid_[a-f0-9]{64}$/.test(state.current_turn_id)) ||
|
|
2691
|
+
(state.current_native_turn_id !== undefined &&
|
|
2692
|
+
(typeof state.current_native_turn_id !== "string" ||
|
|
2693
|
+
state.current_native_turn_id.length === 0 ||
|
|
2694
|
+
state.current_native_turn_id.length > 512)) ||
|
|
2100
2695
|
(state.last_event_id !== undefined && !/^evt_[0-9a-f-]{36}$/.test(state.last_event_id)) ||
|
|
2101
2696
|
(state.last_monotonic_ns !== undefined && !/^\d+$/.test(state.last_monotonic_ns)) ||
|
|
2102
2697
|
(state.last_observed_at !== undefined &&
|
|
2103
2698
|
!Number.isFinite(Date.parse(state.last_observed_at))) ||
|
|
2104
2699
|
(state.started_event_id !== undefined && !/^evt_[0-9a-f-]{36}$/.test(state.started_event_id)) ||
|
|
2700
|
+
(state.pending_runtime_contexts !== undefined &&
|
|
2701
|
+
(!Array.isArray(state.pending_runtime_contexts) ||
|
|
2702
|
+
state.pending_runtime_contexts.length === 0 ||
|
|
2703
|
+
state.pending_runtime_contexts.length > PENDING_RUNTIME_CONTEXT_CAP ||
|
|
2704
|
+
state.pending_runtime_contexts.some((pending) => !validPendingRuntimeContext(pending)))) ||
|
|
2705
|
+
(state.runtime_transcript_path !== undefined &&
|
|
2706
|
+
(typeof state.runtime_transcript_path !== "string" ||
|
|
2707
|
+
state.runtime_transcript_path.length === 0 ||
|
|
2708
|
+
state.runtime_transcript_path.length > 4096 ||
|
|
2709
|
+
state.runtime_transcript_path.includes("\0"))) ||
|
|
2105
2710
|
(state.pending?.source_id !== undefined &&
|
|
2106
2711
|
!/^hid_[a-f0-9]{64}$/.test(state.pending.source_id)) ||
|
|
2107
2712
|
(state.pending && !validateEventV3(state.pending.event).ok)) {
|
|
@@ -2163,6 +2768,37 @@ function validTurnHarnessTiming(value) {
|
|
|
2163
2768
|
(value.slowest_hook === undefined ||
|
|
2164
2769
|
/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(value.slowest_hook)));
|
|
2165
2770
|
}
|
|
2771
|
+
function validPendingRuntimeContext(value) {
|
|
2772
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2773
|
+
return false;
|
|
2774
|
+
const pending = value;
|
|
2775
|
+
return (typeof pending.terminal_event_id === "string" &&
|
|
2776
|
+
/^evt_[0-9a-f-]{36}$/.test(pending.terminal_event_id) &&
|
|
2777
|
+
typeof pending.terminal_observed_at === "string" &&
|
|
2778
|
+
Number.isFinite(Date.parse(pending.terminal_observed_at)) &&
|
|
2779
|
+
typeof pending.turn_id === "string" &&
|
|
2780
|
+
/^tid_[a-f0-9]{64}$/.test(pending.turn_id) &&
|
|
2781
|
+
(pending.run_id === undefined || /^run_[0-9a-f-]{36}$/.test(pending.run_id)) &&
|
|
2782
|
+
(pending.workflow_id === undefined || /^wf_[0-9a-f-]{36}$/.test(pending.workflow_id)) &&
|
|
2783
|
+
(pending.workflow_agent_id === undefined ||
|
|
2784
|
+
/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(pending.workflow_agent_id)) &&
|
|
2785
|
+
typeof pending.native_session_id === "string" &&
|
|
2786
|
+
pending.native_session_id.length > 0 &&
|
|
2787
|
+
pending.native_session_id.length <= 512 &&
|
|
2788
|
+
typeof pending.native_turn_id === "string" &&
|
|
2789
|
+
pending.native_turn_id.length > 0 &&
|
|
2790
|
+
pending.native_turn_id.length <= 512 &&
|
|
2791
|
+
(pending.transcript_path === undefined ||
|
|
2792
|
+
(typeof pending.transcript_path === "string" &&
|
|
2793
|
+
pending.transcript_path.length > 0 &&
|
|
2794
|
+
pending.transcript_path.length <= 4096)) &&
|
|
2795
|
+
(pending.runtime_version === undefined ||
|
|
2796
|
+
(typeof pending.runtime_version === "string" &&
|
|
2797
|
+
/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,127}$/.test(pending.runtime_version))) &&
|
|
2798
|
+
Number.isSafeInteger(pending.attempts) &&
|
|
2799
|
+
pending.attempts >= 0 &&
|
|
2800
|
+
pending.attempts < RUNTIME_CONTEXT_RETRY_LIMIT);
|
|
2801
|
+
}
|
|
2166
2802
|
function pidIsAlive(pid) {
|
|
2167
2803
|
if (!Number.isSafeInteger(pid) || pid < 1)
|
|
2168
2804
|
return false;
|