harnery 0.34.1 → 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 +89 -175
- 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 +1 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +8 -1
- 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 +111 -36
- 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 +22 -10
- 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 +102 -167
- 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 +9 -1
- 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 +123 -39
- package/src/core/hooks/remediation-cap.ts +77 -0
- package/src/core/hooks/resolve/transcript.ts +37 -11
- 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
|
@@ -100,6 +100,24 @@ export function ObservationV3BaseSchema<T extends TSchema>(value: T) {
|
|
|
100
100
|
]);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
const RuntimeTelemetryCapabilityValueV3BaseSchema = StrictObject({
|
|
104
|
+
support: Type.Union([
|
|
105
|
+
Type.Literal("native"),
|
|
106
|
+
Type.Literal("derived"),
|
|
107
|
+
Type.Literal("conditional"),
|
|
108
|
+
]),
|
|
109
|
+
source: SafeToken,
|
|
110
|
+
completeness: Type.Union([
|
|
111
|
+
Type.Literal("exact"),
|
|
112
|
+
Type.Literal("lower_bound"),
|
|
113
|
+
Type.Literal("unknown"),
|
|
114
|
+
]),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const RuntimeTelemetryCapabilityObservationV3BaseSchema = ObservationV3BaseSchema(
|
|
118
|
+
RuntimeTelemetryCapabilityValueV3BaseSchema,
|
|
119
|
+
);
|
|
120
|
+
|
|
103
121
|
export const RuntimeAttestationV3BaseSchema = StrictObject({
|
|
104
122
|
attestation_id: AttestationId,
|
|
105
123
|
generation_id: GenerationId,
|
|
@@ -110,6 +128,27 @@ export const RuntimeAttestationV3BaseSchema = StrictObject({
|
|
|
110
128
|
StrictObject({ id: SafeToken, version: Type.Optional(SafeToken) }),
|
|
111
129
|
),
|
|
112
130
|
model: ObservationV3BaseSchema(StrictObject({ provider: SafeToken, id: SafeToken })),
|
|
131
|
+
/** Runtime tuning identity: the effort dial (and speed tier where the
|
|
132
|
+
* runtime reports one). `observed` with a missing `effort` never occurs —
|
|
133
|
+
* a runtime that proved the model has no dial attests `unsupported` with
|
|
134
|
+
* capability `effort_selection` instead. Values pass through verbatim
|
|
135
|
+
* (`low`…`max`, `standard`/`fast`); display labels are a renderer concern. */
|
|
136
|
+
tuning: ObservationV3BaseSchema(
|
|
137
|
+
StrictObject({
|
|
138
|
+
effort: Type.Optional(SafeToken),
|
|
139
|
+
speed: Type.Optional(SafeToken),
|
|
140
|
+
}),
|
|
141
|
+
),
|
|
142
|
+
/** Effective telemetry support proven for this runtime session. This is
|
|
143
|
+
* intentionally separate from the static capability profile digest: a
|
|
144
|
+
* missing or late local source changes evidence, not the gated profile. */
|
|
145
|
+
telemetry: StrictObject({
|
|
146
|
+
context_usage: RuntimeTelemetryCapabilityObservationV3BaseSchema,
|
|
147
|
+
wait_spans: RuntimeTelemetryCapabilityObservationV3BaseSchema,
|
|
148
|
+
wait_completeness: RuntimeTelemetryCapabilityObservationV3BaseSchema,
|
|
149
|
+
response_latency: RuntimeTelemetryCapabilityObservationV3BaseSchema,
|
|
150
|
+
inference_timing: RuntimeTelemetryCapabilityObservationV3BaseSchema,
|
|
151
|
+
}),
|
|
113
152
|
capability_profile: Type.String({ pattern: "^cap_[a-f0-9]{64}$" }),
|
|
114
153
|
declared_by_event_id: EventId,
|
|
115
154
|
});
|
|
@@ -14,6 +14,21 @@ export type AdapterWaitKindV3 =
|
|
|
14
14
|
| "rate_limit"
|
|
15
15
|
| "unknown";
|
|
16
16
|
|
|
17
|
+
export const ADAPTER_WAIT_KINDS_V3: readonly AdapterWaitKindV3[] = [
|
|
18
|
+
"permission",
|
|
19
|
+
"needs_input",
|
|
20
|
+
"decision",
|
|
21
|
+
"approval",
|
|
22
|
+
"scheduled",
|
|
23
|
+
"rate_limit",
|
|
24
|
+
"unknown",
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export interface WaitKindCapabilityV3 {
|
|
28
|
+
span_delivery: CapabilitySupportV3;
|
|
29
|
+
turn_completeness: CapabilitySupportV3;
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
export type BaseAdapterSignalV3 =
|
|
18
33
|
| "session_start"
|
|
19
34
|
| "session_end"
|
|
@@ -159,6 +174,25 @@ export function adapterTurnWaitCountSupportV3(_adapter: Adapter): CapabilitySupp
|
|
|
159
174
|
return "unsupported";
|
|
160
175
|
}
|
|
161
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Per-kind wait matrix for native adapter turns. Span delivery and independent
|
|
179
|
+
* completed-turn completeness are deliberately separate: observing a span
|
|
180
|
+
* never proves that an absent span means zero.
|
|
181
|
+
*/
|
|
182
|
+
export function adapterWaitCoverageMatrixV3(
|
|
183
|
+
adapter: Adapter,
|
|
184
|
+
): Record<AdapterWaitKindV3, WaitKindCapabilityV3> {
|
|
185
|
+
return Object.fromEntries(
|
|
186
|
+
ADAPTER_WAIT_KINDS_V3.map((kind) => [
|
|
187
|
+
kind,
|
|
188
|
+
{
|
|
189
|
+
span_delivery: adapterWaitKindSupportV3(adapter, kind),
|
|
190
|
+
turn_completeness: adapterTurnWaitCountSupportV3(adapter),
|
|
191
|
+
},
|
|
192
|
+
]),
|
|
193
|
+
) as Record<AdapterWaitKindV3, WaitKindCapabilityV3>;
|
|
194
|
+
}
|
|
195
|
+
|
|
162
196
|
/**
|
|
163
197
|
* Local Cursor sessions expose the generic tool channel. Cloud/private-worker
|
|
164
198
|
* sessions cannot guarantee whole-turn delivery because hooks may start only
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Generated by scripts/generate-event-v3-contract.ts. Do not edit. */
|
|
2
2
|
export const EVENT_V3_SCHEMA_DIGEST =
|
|
3
|
-
"sha256:
|
|
3
|
+
"sha256:297ed06021fd046c6f1a2061de4341c7765840bf4f0d9b597ad7c9d78b7a9ba6" as const;
|
|
4
4
|
export const EVENT_V3_GENERATED_CORE_TYPES = [
|
|
5
5
|
"ledger.genesis",
|
|
6
6
|
"ledger.activated",
|
|
@@ -34,12 +34,18 @@ export {
|
|
|
34
34
|
} from "./canonical.ts";
|
|
35
35
|
export {
|
|
36
36
|
ADAPTER_CAPABILITY_PROFILES_V3,
|
|
37
|
+
ADAPTER_WAIT_KINDS_V3,
|
|
37
38
|
type AdapterCapabilityProfileV3,
|
|
38
39
|
type AdapterSignalV3,
|
|
40
|
+
type AdapterWaitKindV3,
|
|
39
41
|
adapterCapabilityProfileDigestV3,
|
|
40
42
|
adapterCapabilityProfileV3,
|
|
41
43
|
adapterSignalSupportV3,
|
|
44
|
+
adapterWaitCoverageMatrixV3,
|
|
45
|
+
adapterWaitKindSupportV3,
|
|
42
46
|
type CapabilitySupportV3,
|
|
47
|
+
type CursorExecutionModeV3,
|
|
48
|
+
type WaitKindCapabilityV3,
|
|
43
49
|
} from "./capabilities.ts";
|
|
44
50
|
export {
|
|
45
51
|
type CapabilityDeliveryV3,
|
|
@@ -153,8 +159,12 @@ export {
|
|
|
153
159
|
type LatencyProjectionDiagnosticV3,
|
|
154
160
|
type LatencyProjectionV3,
|
|
155
161
|
projectLatencyV3,
|
|
162
|
+
type ResponseLatencyV3,
|
|
163
|
+
runtimeTelemetryEvidenceKeyV3,
|
|
156
164
|
type ToolLatencyV3,
|
|
157
165
|
type TurnLatencyV3,
|
|
166
|
+
type WaitKindCompletenessV3,
|
|
167
|
+
type WaitKindCoverageV3,
|
|
158
168
|
} from "./latency.ts";
|
|
159
169
|
export {
|
|
160
170
|
EVENT_V3_LIVE_RELATIVE_ROOT,
|
|
@@ -245,6 +255,7 @@ export {
|
|
|
245
255
|
} from "./projection.ts";
|
|
246
256
|
export {
|
|
247
257
|
digestEventV3AuthorityDirectoryV3,
|
|
258
|
+
eventV3ActiveWatchPath,
|
|
248
259
|
inspectInvalidActiveAuthorityV3,
|
|
249
260
|
type LedgerCursorV3,
|
|
250
261
|
type LedgerDiagnosticCodeV3,
|
|
@@ -277,6 +288,16 @@ export {
|
|
|
277
288
|
validateEventV3RecoveryIntent,
|
|
278
289
|
validateEventV3RecoveryReceipt,
|
|
279
290
|
} from "./recovery-record.ts";
|
|
291
|
+
export {
|
|
292
|
+
effectiveRuntimeTelemetryCapabilitiesV3,
|
|
293
|
+
type RuntimeContextCapabilityEvidenceV3,
|
|
294
|
+
type RuntimeTelemetryCapabilitiesV3,
|
|
295
|
+
type RuntimeTelemetryCapabilityEvidenceV3,
|
|
296
|
+
type RuntimeTelemetryCapabilityObservationV3,
|
|
297
|
+
type RuntimeTelemetryCapabilityValueV3,
|
|
298
|
+
type RuntimeTelemetryChannelV3,
|
|
299
|
+
type RuntimeTelemetryCompletenessV3,
|
|
300
|
+
} from "./runtime-telemetry-capabilities.ts";
|
|
280
301
|
export {
|
|
281
302
|
type CloseSpanV3Input,
|
|
282
303
|
captureSpanClockV3,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
|
|
2
|
+
import { ADAPTER_WAIT_KINDS_V3, type AdapterWaitKindV3 } from "./capabilities.ts";
|
|
1
3
|
import type { ReadLedgerV3Result } from "./reader.ts";
|
|
4
|
+
import type { RuntimeTelemetryCapabilitiesV3 } from "./runtime-telemetry-capabilities.ts";
|
|
2
5
|
|
|
3
|
-
export const EVENT_V3_LATENCY_PROJECTION_VERSION = "event-v3-latency-
|
|
6
|
+
export const EVENT_V3_LATENCY_PROJECTION_VERSION = "event-v3-latency-v3" as const;
|
|
4
7
|
|
|
5
8
|
export type LatencyMetricV3 =
|
|
6
9
|
| { state: "observed"; value_ms: number }
|
|
@@ -25,6 +28,30 @@ export interface ContextCoverageV3 {
|
|
|
25
28
|
reason: string | null;
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
export interface ResponseLatencyV3 {
|
|
32
|
+
/** Turn start to first tool request, or terminal response for an attested no-tool turn. */
|
|
33
|
+
agent_action_ms: LatencyMetricV3;
|
|
34
|
+
/** Turn start to first tool request; unknown with `no_tool_request` for no-tool turns. */
|
|
35
|
+
first_tool_request_ms: LatencyMetricV3;
|
|
36
|
+
/** Last complete tool terminal to the turn terminal. */
|
|
37
|
+
post_tool_response_ms: LatencyMetricV3;
|
|
38
|
+
/** Turn start to terminal response, only when an attested turn used no tools. */
|
|
39
|
+
no_tool_terminal_response_ms: LatencyMetricV3;
|
|
40
|
+
method: "canonical_event_timestamps_v1";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type WaitKindCompletenessV3 = "complete" | "lower_bound" | "unknown" | "unsupported";
|
|
44
|
+
|
|
45
|
+
export interface WaitKindCoverageV3 {
|
|
46
|
+
kind: AdapterWaitKindV3;
|
|
47
|
+
started_count: number;
|
|
48
|
+
ended_count: number;
|
|
49
|
+
open_count: number;
|
|
50
|
+
duration_ms: LatencyMetricV3;
|
|
51
|
+
completeness: WaitKindCompletenessV3;
|
|
52
|
+
reason: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
28
55
|
export interface TurnLatencyV3 {
|
|
29
56
|
generation_id: string;
|
|
30
57
|
turn_id: string;
|
|
@@ -39,6 +66,7 @@ export interface TurnLatencyV3 {
|
|
|
39
66
|
wait_ms: LatencyMetricV3;
|
|
40
67
|
occupied_ms: LatencyMetricV3;
|
|
41
68
|
inference_ms: LatencyMetricV3;
|
|
69
|
+
response_latency: ResponseLatencyV3;
|
|
42
70
|
harness_ms: LatencyMetricV3;
|
|
43
71
|
slowest_hook: string | null;
|
|
44
72
|
slowest_hook_ms: number | null;
|
|
@@ -46,6 +74,10 @@ export interface TurnLatencyV3 {
|
|
|
46
74
|
over_attributed_ms: number;
|
|
47
75
|
context_percent: number | null;
|
|
48
76
|
context_coverage: ContextCoverageV3;
|
|
77
|
+
telemetry_capabilities: RuntimeTelemetryCapabilitiesV3 | null;
|
|
78
|
+
/** Equal keys are required before a report compares telemetry-backed values. */
|
|
79
|
+
telemetry_evidence_key: `sha256:${string}` | null;
|
|
80
|
+
wait_coverage_by_kind: Record<AdapterWaitKindV3, WaitKindCoverageV3>;
|
|
49
81
|
span_counts: { tool: number; command: number; wait: number };
|
|
50
82
|
tool_breakdown: ToolLatencyV3[];
|
|
51
83
|
}
|
|
@@ -72,6 +104,7 @@ interface EventShape {
|
|
|
72
104
|
event_id: string;
|
|
73
105
|
event_type: string;
|
|
74
106
|
scope: { generation_id?: string; turn_id?: string };
|
|
107
|
+
time: { observed_at?: string };
|
|
75
108
|
payload: Record<string, unknown>;
|
|
76
109
|
}
|
|
77
110
|
|
|
@@ -92,6 +125,7 @@ export function projectLatencyV3(read: ReadLedgerV3Result): LatencyProjectionV3
|
|
|
92
125
|
const events = read.events.map(({ event }) => event as unknown as EventShape);
|
|
93
126
|
const diagnostics: LatencyProjectionDiagnosticV3[] = [];
|
|
94
127
|
const terminalByTurn = new Map<string, EventShape>();
|
|
128
|
+
const telemetryByGeneration = latestTelemetryByGeneration(events);
|
|
95
129
|
|
|
96
130
|
for (const event of events) {
|
|
97
131
|
if (event.event_type !== "turn.completed") continue;
|
|
@@ -104,7 +138,12 @@ export function projectLatencyV3(read: ReadLedgerV3Result): LatencyProjectionV3
|
|
|
104
138
|
const turns = [...terminalByTurn.values()].map((terminal) => {
|
|
105
139
|
const key = turnKey(terminal);
|
|
106
140
|
const turnEvents = events.filter((event) => turnKey(event) === key);
|
|
107
|
-
return projectTurn(
|
|
141
|
+
return projectTurn(
|
|
142
|
+
terminal,
|
|
143
|
+
turnEvents,
|
|
144
|
+
diagnostics,
|
|
145
|
+
telemetryByGeneration.get(terminal.scope.generation_id ?? "") ?? null,
|
|
146
|
+
);
|
|
108
147
|
});
|
|
109
148
|
turns.sort(
|
|
110
149
|
(left, right) =>
|
|
@@ -118,6 +157,7 @@ function projectTurn(
|
|
|
118
157
|
terminal: EventShape,
|
|
119
158
|
events: EventShape[],
|
|
120
159
|
diagnostics: LatencyProjectionDiagnosticV3[],
|
|
160
|
+
telemetryCapabilities: RuntimeTelemetryCapabilitiesV3 | null,
|
|
121
161
|
): TurnLatencyV3 {
|
|
122
162
|
const span = record(terminal.payload.span);
|
|
123
163
|
const wall = metricFromObservation(span.duration_ms, "turn_duration_unknown");
|
|
@@ -157,6 +197,10 @@ function projectTurn(
|
|
|
157
197
|
) {
|
|
158
198
|
waits.reasons.push("wait_terminal_count_mismatch");
|
|
159
199
|
}
|
|
200
|
+
const waitCoverageComplete =
|
|
201
|
+
expectedWaitCount !== undefined &&
|
|
202
|
+
expectedWaitCount === endedWaitIds.size &&
|
|
203
|
+
!waitStartEvents.some(({ payload }) => !endedWaitIds.has(string(record(payload).wait_id)));
|
|
160
204
|
|
|
161
205
|
const tool = metricFromIntervals(tools);
|
|
162
206
|
const command = metricFromIntervals(commands);
|
|
@@ -176,6 +220,7 @@ function projectTurn(
|
|
|
176
220
|
"api_time_ms",
|
|
177
221
|
"inference_timing_unknown",
|
|
178
222
|
);
|
|
223
|
+
const responseLatency = projectResponseLatency(terminal, events, wall, tools, expectedToolCount);
|
|
179
224
|
const harness = metricFromNestedObservation(
|
|
180
225
|
terminal.payload.harness,
|
|
181
226
|
"hook_time_ms",
|
|
@@ -217,6 +262,7 @@ function projectTurn(
|
|
|
217
262
|
wait_ms: wait,
|
|
218
263
|
occupied_ms: occupied,
|
|
219
264
|
inference_ms: inference,
|
|
265
|
+
response_latency: responseLatency,
|
|
220
266
|
harness_ms: harness,
|
|
221
267
|
slowest_hook: slowestHook.name,
|
|
222
268
|
slowest_hook_ms: slowestHook.duration_ms,
|
|
@@ -224,6 +270,18 @@ function projectTurn(
|
|
|
224
270
|
over_attributed_ms: overAttributed,
|
|
225
271
|
context_percent: context.percent,
|
|
226
272
|
context_coverage: context.coverage,
|
|
273
|
+
telemetry_capabilities: telemetryCapabilities,
|
|
274
|
+
telemetry_evidence_key: telemetryCapabilities
|
|
275
|
+
? runtimeTelemetryEvidenceKeyV3(telemetryCapabilities)
|
|
276
|
+
: null,
|
|
277
|
+
wait_coverage_by_kind: projectWaitCoverageByKind(
|
|
278
|
+
waitStartEvents,
|
|
279
|
+
waitEvents,
|
|
280
|
+
wallInterval,
|
|
281
|
+
waitCoverageComplete,
|
|
282
|
+
waitCount,
|
|
283
|
+
diagnostics,
|
|
284
|
+
),
|
|
227
285
|
span_counts: {
|
|
228
286
|
tool: toolEvents.length,
|
|
229
287
|
command: commandEvents.length,
|
|
@@ -233,6 +291,159 @@ function projectTurn(
|
|
|
233
291
|
};
|
|
234
292
|
}
|
|
235
293
|
|
|
294
|
+
/** Reports may compare a telemetry-backed channel only inside one evidence key. */
|
|
295
|
+
export function runtimeTelemetryEvidenceKeyV3(
|
|
296
|
+
telemetry: RuntimeTelemetryCapabilitiesV3,
|
|
297
|
+
): `sha256:${string}` {
|
|
298
|
+
return sha256V3(canonicalJsonV3(telemetry));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function latestTelemetryByGeneration(
|
|
302
|
+
events: EventShape[],
|
|
303
|
+
): Map<string, RuntimeTelemetryCapabilitiesV3> {
|
|
304
|
+
const result = new Map<string, RuntimeTelemetryCapabilitiesV3>();
|
|
305
|
+
for (const event of events) {
|
|
306
|
+
if (
|
|
307
|
+
event.event_type !== "session.started" &&
|
|
308
|
+
event.event_type !== "session.attestation_changed"
|
|
309
|
+
) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const attestation = record(event.payload.runtime_attestation);
|
|
313
|
+
const telemetry = record(attestation.telemetry);
|
|
314
|
+
if (Object.keys(telemetry).length === 0) continue;
|
|
315
|
+
result.set(event.scope.generation_id ?? "", telemetry as RuntimeTelemetryCapabilitiesV3);
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function projectResponseLatency(
|
|
321
|
+
terminal: EventShape,
|
|
322
|
+
events: EventShape[],
|
|
323
|
+
wall: LatencyMetricV3,
|
|
324
|
+
tools: IntervalSet,
|
|
325
|
+
expectedToolCount: number | undefined,
|
|
326
|
+
): ResponseLatencyV3 {
|
|
327
|
+
const turnStart = Date.parse(string(record(terminal.payload.span).opened_at));
|
|
328
|
+
const turnEnd =
|
|
329
|
+
Number.isFinite(turnStart) && wall.state === "observed" ? turnStart + wall.value_ms : undefined;
|
|
330
|
+
const requests = events
|
|
331
|
+
.filter(({ event_type }) => event_type === "tool.requested")
|
|
332
|
+
.map(({ time }) => Date.parse(string(time.observed_at)))
|
|
333
|
+
.filter(Number.isFinite)
|
|
334
|
+
.sort((left, right) => left - right);
|
|
335
|
+
const firstRequest = requests[0];
|
|
336
|
+
const firstTool =
|
|
337
|
+
Number.isFinite(turnStart) && firstRequest !== undefined
|
|
338
|
+
? observedLatency(firstRequest - turnStart)
|
|
339
|
+
: unknownLatency(expectedToolCount === 0 ? "no_tool_request" : "tool_request_unobserved");
|
|
340
|
+
const noToolTerminal =
|
|
341
|
+
expectedToolCount === 0
|
|
342
|
+
? wall
|
|
343
|
+
: unknownLatency(
|
|
344
|
+
expectedToolCount === undefined ? "tool_channel_unattested" : "turn_contains_tools",
|
|
345
|
+
);
|
|
346
|
+
const agentAction =
|
|
347
|
+
firstRequest !== undefined
|
|
348
|
+
? firstTool
|
|
349
|
+
: expectedToolCount === 0
|
|
350
|
+
? noToolTerminal
|
|
351
|
+
: unknownLatency(
|
|
352
|
+
expectedToolCount === undefined ? "tool_channel_unattested" : "tool_request_unobserved",
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
let postTool = unknownLatency(
|
|
356
|
+
expectedToolCount === 0
|
|
357
|
+
? "no_tool_completion"
|
|
358
|
+
: expectedToolCount === undefined
|
|
359
|
+
? "tool_channel_unattested"
|
|
360
|
+
: "final_tool_completion_unknown",
|
|
361
|
+
);
|
|
362
|
+
if (
|
|
363
|
+
turnEnd !== undefined &&
|
|
364
|
+
expectedToolCount !== undefined &&
|
|
365
|
+
expectedToolCount > 0 &&
|
|
366
|
+
tools.reasons.length === 0 &&
|
|
367
|
+
tools.intervals.length === expectedToolCount
|
|
368
|
+
) {
|
|
369
|
+
postTool = observedLatency(turnEnd - Math.max(...tools.intervals.map(({ end }) => end)));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
agent_action_ms: agentAction,
|
|
374
|
+
first_tool_request_ms: firstTool,
|
|
375
|
+
post_tool_response_ms: postTool,
|
|
376
|
+
no_tool_terminal_response_ms: noToolTerminal,
|
|
377
|
+
method: "canonical_event_timestamps_v1",
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function projectWaitCoverageByKind(
|
|
382
|
+
starts: EventShape[],
|
|
383
|
+
ends: EventShape[],
|
|
384
|
+
wall: Interval | undefined,
|
|
385
|
+
complete: boolean,
|
|
386
|
+
waitCount: Record<string, unknown>,
|
|
387
|
+
diagnostics: LatencyProjectionDiagnosticV3[],
|
|
388
|
+
): Record<AdapterWaitKindV3, WaitKindCoverageV3> {
|
|
389
|
+
const endedIds = new Set(ends.map(({ payload }) => string(record(payload).wait_id)));
|
|
390
|
+
const unsupported = waitCount.state === "unsupported";
|
|
391
|
+
return Object.fromEntries(
|
|
392
|
+
ADAPTER_WAIT_KINDS_V3.map((kind) => {
|
|
393
|
+
const kindStarts = starts.filter(({ payload }) => waitKind(payload) === kind);
|
|
394
|
+
const kindEnds = ends.filter(({ payload }) => waitKind(payload) === kind);
|
|
395
|
+
const openCount = kindStarts.filter(
|
|
396
|
+
({ payload }) => !endedIds.has(string(record(payload).wait_id)),
|
|
397
|
+
).length;
|
|
398
|
+
const set = intervalSet(kindEnds, wall, diagnostics);
|
|
399
|
+
if (!complete) set.reasons.push("wait_kind_completeness_unattested");
|
|
400
|
+
const completeness: WaitKindCompletenessV3 = complete
|
|
401
|
+
? "complete"
|
|
402
|
+
: unsupported
|
|
403
|
+
? kindStarts.length > 0 || kindEnds.length > 0
|
|
404
|
+
? "lower_bound"
|
|
405
|
+
: "unsupported"
|
|
406
|
+
: kindStarts.length > 0 || kindEnds.length > 0
|
|
407
|
+
? "lower_bound"
|
|
408
|
+
: "unknown";
|
|
409
|
+
const reason = complete
|
|
410
|
+
? null
|
|
411
|
+
: unsupported
|
|
412
|
+
? "turn_wait_count_unsupported"
|
|
413
|
+
: "turn_wait_count_unattested";
|
|
414
|
+
return [
|
|
415
|
+
kind,
|
|
416
|
+
{
|
|
417
|
+
kind,
|
|
418
|
+
started_count: kindStarts.length,
|
|
419
|
+
ended_count: kindEnds.length,
|
|
420
|
+
open_count: openCount,
|
|
421
|
+
duration_ms: metricFromIntervals(set),
|
|
422
|
+
completeness,
|
|
423
|
+
reason,
|
|
424
|
+
},
|
|
425
|
+
];
|
|
426
|
+
}),
|
|
427
|
+
) as Record<AdapterWaitKindV3, WaitKindCoverageV3>;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function waitKind(payload: Record<string, unknown>): AdapterWaitKindV3 {
|
|
431
|
+
const value = string(record(payload).kind);
|
|
432
|
+
return ADAPTER_WAIT_KINDS_V3.includes(value as AdapterWaitKindV3)
|
|
433
|
+
? (value as AdapterWaitKindV3)
|
|
434
|
+
: "unknown";
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function observedLatency(value: number): LatencyMetricV3 {
|
|
438
|
+
return Number.isFinite(value) && value >= 0
|
|
439
|
+
? { state: "observed", value_ms: Math.floor(value) }
|
|
440
|
+
: unknownLatency("event_time_order_invalid");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function unknownLatency(reason: string): LatencyMetricV3 {
|
|
444
|
+
return { state: "unknown", known_ms: 0, reasons: [reason] };
|
|
445
|
+
}
|
|
446
|
+
|
|
236
447
|
function groupByTool(events: EventShape[]): Map<string, EventShape[]> {
|
|
237
448
|
const groups = new Map<string, EventShape[]>();
|
|
238
449
|
for (const event of events) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { Adapter } from "../../adapter.ts";
|
|
3
3
|
import type { ParsedPayload } from "../../hooks/adapter/parse.ts";
|
|
4
|
+
import type { EventV3 } from "./contract.ts";
|
|
4
5
|
import {
|
|
5
6
|
type EventV3WriteMode,
|
|
6
7
|
readEventV3ControlState,
|
|
@@ -84,6 +85,20 @@ export function hookSignalV3(eventName: string): HookSignalV3 | undefined {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Tool lifecycle hooks may stop after publishing their event to the durable
|
|
90
|
+
* ready WAL. The next non-tool hook drains the causal batch into the active
|
|
91
|
+
* ledger, avoiding one global append-lease cycle for every tool boundary.
|
|
92
|
+
*/
|
|
93
|
+
export function liveHookSignalDefersDrainV3(eventName: string, override?: string): boolean {
|
|
94
|
+
if (override === "1") return true;
|
|
95
|
+
if (override === "0") return false;
|
|
96
|
+
const signal = hookSignalV3(eventName);
|
|
97
|
+
return (
|
|
98
|
+
signal === "pre-tool-use" || signal === "post-tool-use" || signal === "post-tool-use-failure"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
export function recordLiveHookSignalV3(input: {
|
|
88
103
|
coordRoot: string;
|
|
89
104
|
route: Extract<LiveEventLedgerRouteV3, { state: "v3" }>;
|
|
@@ -104,11 +119,27 @@ export function recordLiveHookSignalV3(input: {
|
|
|
104
119
|
}): RecordHookSignalV3Result | { state: "ignored" } {
|
|
105
120
|
const signal = hookSignalV3(input.eventName);
|
|
106
121
|
if (!signal) return { state: "ignored" };
|
|
122
|
+
const nativeChild = input.payload?.subagent_id ?? input.payload?.agent_id;
|
|
123
|
+
const childOwnsSignal =
|
|
124
|
+
signal !== "sub-agent-start" &&
|
|
125
|
+
signal !== "sub-agent-stop" &&
|
|
126
|
+
nativeChild !== undefined &&
|
|
127
|
+
liveInstanceIdV3(input.instanceId) === liveInstanceIdV3(nativeChild);
|
|
128
|
+
const payload = input.payload
|
|
129
|
+
? childOwnsSignal
|
|
130
|
+
? {
|
|
131
|
+
...input.payload,
|
|
132
|
+
session_id: nativeChild,
|
|
133
|
+
conversation_id: undefined,
|
|
134
|
+
parent_session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
135
|
+
}
|
|
136
|
+
: input.payload
|
|
137
|
+
: { raw: {} };
|
|
107
138
|
return recordHookSignalV3({
|
|
108
139
|
coordRoot: input.coordRoot,
|
|
109
140
|
mode: input.route.mode,
|
|
110
141
|
signal,
|
|
111
|
-
payload
|
|
142
|
+
payload,
|
|
112
143
|
adapter: input.adapter,
|
|
113
144
|
instance_id: liveInstanceIdV3(input.instanceId),
|
|
114
145
|
run_id: input.run_id,
|
|
@@ -127,6 +158,94 @@ export function recordLiveHookSignalV3(input: {
|
|
|
127
158
|
});
|
|
128
159
|
}
|
|
129
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Open the child generation named by a native SubagentStart event.
|
|
163
|
+
*
|
|
164
|
+
* The parent producer owns the delegation span and mints its child generation
|
|
165
|
+
* id. This second crash-safe session-start gives that exact generation its own
|
|
166
|
+
* producer authority, so later child tool hooks can resolve away from the
|
|
167
|
+
* parent session and the disposable heartbeat projection has a real source.
|
|
168
|
+
*/
|
|
169
|
+
export function recordLiveDelegatedChildSessionV3(input: {
|
|
170
|
+
coordRoot: string;
|
|
171
|
+
route: Extract<LiveEventLedgerRouteV3, { state: "v3" }>;
|
|
172
|
+
parentEvent: Extract<EventV3, { event_type: "agent.started" }>;
|
|
173
|
+
payload: ParsedPayload;
|
|
174
|
+
adapter: Adapter;
|
|
175
|
+
bridge?: "codex-wsl";
|
|
176
|
+
monotonic_ns?: string;
|
|
177
|
+
}): RecordHookSignalV3Result | { state: "ignored" } {
|
|
178
|
+
const nativeChild = input.payload.subagent_id ?? input.payload.agent_id;
|
|
179
|
+
if (!nativeChild) return { state: "ignored" };
|
|
180
|
+
const parentGeneration = (input.parentEvent.links as { parent_generation_id?: `gen_${string}` })
|
|
181
|
+
.parent_generation_id;
|
|
182
|
+
if (!parentGeneration) return { state: "ignored" };
|
|
183
|
+
const nativeParent = input.payload.session_id ?? input.payload.conversation_id;
|
|
184
|
+
const childPayload: ParsedPayload = {
|
|
185
|
+
session_id: nativeChild,
|
|
186
|
+
agent_id: nativeChild,
|
|
187
|
+
parent_session_id: nativeParent,
|
|
188
|
+
source: "startup",
|
|
189
|
+
model: input.payload.model,
|
|
190
|
+
cwd: input.payload.cwd,
|
|
191
|
+
raw: {
|
|
192
|
+
session_id: nativeChild,
|
|
193
|
+
agent_id: nativeChild,
|
|
194
|
+
...(nativeParent ? { parent_session_id: nativeParent } : {}),
|
|
195
|
+
...(input.payload.raw.agent_type ? { agent_type: input.payload.raw.agent_type } : {}),
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
const started = recordHookSignalV3({
|
|
199
|
+
coordRoot: input.coordRoot,
|
|
200
|
+
mode: input.route.mode,
|
|
201
|
+
signal: "session-start",
|
|
202
|
+
payload: childPayload,
|
|
203
|
+
adapter: input.adapter,
|
|
204
|
+
instance_id: liveInstanceIdV3(nativeChild),
|
|
205
|
+
producer_id: LIVE_HOOK_V3_PRODUCER_ID,
|
|
206
|
+
build_id: input.route.build_id,
|
|
207
|
+
platform: livePlatformV3(),
|
|
208
|
+
...(input.bridge ? { bridge: input.bridge } : {}),
|
|
209
|
+
monotonic_ns: input.monotonic_ns,
|
|
210
|
+
hook_name: "sub-agent-start-child-session",
|
|
211
|
+
delegated_child: {
|
|
212
|
+
generation_id: input.parentEvent.payload.child_generation_id as `gen_${string}`,
|
|
213
|
+
parent_generation_id: parentGeneration,
|
|
214
|
+
delegation_id: input.parentEvent.payload.delegation_id as `del_${string}`,
|
|
215
|
+
caused_by_event_id: input.parentEvent.event_id as `evt_${string}`,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
if (started.state !== "recorded" && started.state !== "already_started") return started;
|
|
219
|
+
const childStartEventId = (
|
|
220
|
+
started.state === "recorded" ? started.event.event_id : started.event_id
|
|
221
|
+
) as `evt_${string}`;
|
|
222
|
+
recordHookSignalV3({
|
|
223
|
+
coordRoot: input.coordRoot,
|
|
224
|
+
mode: input.route.mode,
|
|
225
|
+
signal: "user-prompt-submit",
|
|
226
|
+
payload: {
|
|
227
|
+
...childPayload,
|
|
228
|
+
turn_id: `delegated:${nativeChild}`,
|
|
229
|
+
raw: { ...childPayload.raw, turn_id: `delegated:${nativeChild}` },
|
|
230
|
+
},
|
|
231
|
+
adapter: input.adapter,
|
|
232
|
+
instance_id: liveInstanceIdV3(nativeChild),
|
|
233
|
+
producer_id: LIVE_HOOK_V3_PRODUCER_ID,
|
|
234
|
+
build_id: input.route.build_id,
|
|
235
|
+
platform: livePlatformV3(),
|
|
236
|
+
...(input.bridge ? { bridge: input.bridge } : {}),
|
|
237
|
+
monotonic_ns: input.monotonic_ns,
|
|
238
|
+
hook_name: "sub-agent-start-child-turn",
|
|
239
|
+
delegated_child: {
|
|
240
|
+
generation_id: input.parentEvent.payload.child_generation_id as `gen_${string}`,
|
|
241
|
+
parent_generation_id: parentGeneration,
|
|
242
|
+
delegation_id: input.parentEvent.payload.delegation_id as `del_${string}`,
|
|
243
|
+
caused_by_event_id: childStartEventId,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
return started;
|
|
247
|
+
}
|
|
248
|
+
|
|
130
249
|
export function livePlatformV3(): "linux" | "windows" | "macos" | "unknown" {
|
|
131
250
|
if (process.platform === "linux") return "linux";
|
|
132
251
|
if (process.platform === "win32") return "windows";
|
|
@@ -141,6 +141,14 @@ export function recordCommandSignalV3(
|
|
|
141
141
|
if (state && !matchesHookState(state, hook, input, epochId)) {
|
|
142
142
|
throw new Error("V3 command producer state does not match the joined hook generation");
|
|
143
143
|
}
|
|
144
|
+
if (state && state.attestation_id !== hook.attestation_id) {
|
|
145
|
+
// A mid-generation re-attestation (session.attestation_changed) moves
|
|
146
|
+
// the hook's attestation id without opening a new generation or turn. A
|
|
147
|
+
// command span that straddles the change keeps its span, sequence, and
|
|
148
|
+
// dedupe state; later events ride the live attestation, the same
|
|
149
|
+
// in-place adoption the hook producer performs.
|
|
150
|
+
state.attestation_id = hook.attestation_id;
|
|
151
|
+
}
|
|
144
152
|
let recovered = false;
|
|
145
153
|
if (state?.pending) {
|
|
146
154
|
const pending = state.pending;
|
|
@@ -445,7 +453,8 @@ function matchesHookState(
|
|
|
445
453
|
state.session_id === hook.session_id &&
|
|
446
454
|
state.generation_id === hook.generation_id &&
|
|
447
455
|
state.turn_id === hook.current_turn_id &&
|
|
448
|
-
|
|
456
|
+
// attestation_id is deliberately absent: a re-attestation within the
|
|
457
|
+
// generation is a continuation, adopted by the caller, not a mismatch.
|
|
449
458
|
state.privacy_epoch_id === epochId
|
|
450
459
|
);
|
|
451
460
|
}
|