harnery 0.40.0 → 0.41.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/README.md +1 -1
- package/dist/commander.js +1 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +25 -19
- package/dist/commands/browse-ai.d.ts.map +1 -1
- package/dist/commands/browse-ai.js +4 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +141 -10
- package/dist/commands/checkpoint.js +8 -12
- package/dist/commands/deinit.d.ts +1 -1
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +8 -4
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +2 -0
- package/dist/commands/doctor.js +10 -2
- package/dist/commands/fetch.d.ts.map +1 -1
- package/dist/commands/fetch.js +10 -0
- package/dist/commands/init.d.ts +19 -3
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +232 -104
- package/dist/commands/instructions.js +5 -4
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +2 -1
- package/dist/commands/webview.d.ts +4 -0
- package/dist/commands/webview.d.ts.map +1 -0
- package/dist/commands/webview.js +94 -0
- package/dist/core/adapter.d.ts +50 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +59 -1
- package/dist/core/adapters/attestation.d.ts.map +1 -1
- package/dist/core/adapters/attestation.js +3 -2
- package/dist/core/adapters/profiles.d.ts +20 -3
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +32 -1
- package/dist/core/adapters/registry.d.ts.map +1 -1
- package/dist/core/adapters/registry.js +33 -0
- package/dist/core/agents/canonical-emit.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -7
- package/dist/core/agents/cli.js +18 -16
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +7 -2
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +3 -9
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +2 -8
- package/dist/core/agents/rules/stop-hook.d.ts +2 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +1 -1
- package/dist/core/agents/session-events.d.ts +6 -2
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +105 -26
- package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalization-state-v3.js +2 -1
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalizer-v3.js +5 -4
- package/dist/core/agents/session-name-display.d.ts +28 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +43 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +21 -2
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +1 -0
- package/dist/core/agents/state/live-coordination-writer.js +3 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +2 -10
- package/dist/core/agents/turn-changes.d.ts +17 -0
- package/dist/core/agents/turn-changes.d.ts.map +1 -0
- package/dist/core/agents/turn-changes.js +76 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +2 -1
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +4 -3
- package/dist/core/config.d.ts +5 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +14 -0
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +2 -1
- package/dist/core/conversations/archive.d.ts.map +1 -1
- package/dist/core/conversations/archive.js +4 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +3 -2
- package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/canary.js +5 -4
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/compression.js +2 -1
- package/dist/core/events/v3/adapter-id.d.ts +10 -2
- package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
- package/dist/core/events/v3/adapter-id.js +16 -0
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +2 -1
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +5 -4
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +17 -0
- package/dist/core/events/v3/contract.d.ts +5 -0
- package/dist/core/events/v3/contract.d.ts.map +1 -1
- package/dist/core/events/v3/contract.js +7 -1
- package/dist/core/events/v3/control-witness.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.js +3 -2
- package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
- package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
- package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
- package/dist/core/events/v3/fingerprint-keys.js +7 -6
- package/dist/core/events/v3/generated.d.ts +1 -1
- package/dist/core/events/v3/generated.js +1 -1
- package/dist/core/events/v3/live-feed.d.ts.map +1 -1
- package/dist/core/events/v3/live-feed.js +5 -4
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +8 -7
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
- package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
- package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +10 -4
- package/dist/core/events/v3/producers/hook.d.ts +2 -0
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook.js +16 -1
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +16 -6
- package/dist/core/events/v3/producers/recorder.d.ts +2 -4
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +109 -101
- package/dist/core/events/v3/recovery.d.ts.map +1 -1
- package/dist/core/events/v3/recovery.js +4 -3
- package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/inventory.js +2 -1
- package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/maintenance.js +4 -3
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
- package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
- package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
- package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
- package/dist/core/events/v3/tool-line-changes.js +135 -0
- package/dist/core/events/v3/writer.d.ts.map +1 -1
- package/dist/core/events/v3/writer.js +8 -7
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +6 -5
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +12 -11
- package/dist/core/governor/state.d.ts.map +1 -1
- package/dist/core/governor/state.js +7 -6
- package/dist/core/guard/evaluator.js +1 -0
- package/dist/core/guard/storage.d.ts.map +1 -1
- package/dist/core/guard/storage.js +3 -2
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/index.js +20 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/types.js +1 -0
- package/dist/core/hooks/adapter/detect.d.ts +1 -1
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +2 -3
- package/dist/core/hooks/adapter/events.d.ts +29 -1
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +31 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
- package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
- package/dist/core/hooks/adapter/output.d.ts +7 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +8 -1
- package/dist/core/hooks/adapter/wiring.d.ts +15 -1
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +74 -11
- package/dist/core/hooks/cli.js +635 -529
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +2 -1
- package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
- package/dist/core/hooks/prompt-context/contract.js +1 -0
- package/dist/core/hooks/resolve/transcript.d.ts +1 -1
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +35 -4
- package/dist/core/hooks/session-name-presence.d.ts +3 -1
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +7 -2
- package/dist/core/hooks/shell-waiter.d.ts +2 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
- package/dist/core/hooks/shell-waiter.js +173 -0
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +5 -4
- package/dist/core/semantic/review.d.ts.map +1 -1
- package/dist/core/semantic/review.js +5 -4
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +15 -14
- package/dist/core/semantic/storage.d.ts.map +1 -1
- package/dist/core/semantic/storage.js +5 -4
- package/dist/core/storage/atomic-json.d.ts.map +1 -1
- package/dist/core/storage/atomic-json.js +7 -3
- package/dist/core/storage/durable-history.d.ts.map +1 -1
- package/dist/core/storage/durable-history.js +6 -5
- package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
- package/dist/core/storage/maintenance-providers.js +2 -1
- package/dist/core/storage/maintenance.d.ts.map +1 -1
- package/dist/core/storage/maintenance.js +7 -6
- package/dist/core/storage/metrics.d.ts.map +1 -1
- package/dist/core/storage/metrics.js +5 -1
- package/dist/core/storage/modes.d.ts +17 -0
- package/dist/core/storage/modes.d.ts.map +1 -0
- package/dist/core/storage/modes.js +47 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +3 -2
- package/dist/core/storage/receipts.d.ts.map +1 -1
- package/dist/core/storage/receipts.js +4 -3
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +7 -6
- package/dist/core/supervisor/hook-health.d.ts +2 -1
- package/dist/core/supervisor/hook-health.d.ts.map +1 -1
- package/dist/core/supervisor/hook-health.js +4 -3
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +7 -6
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +8 -7
- package/dist/core/workflow/adapters.d.ts +7 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -1
- package/dist/core/workflow/adapters.js +9 -0
- package/dist/core/workflow/approvals.d.ts.map +1 -1
- package/dist/core/workflow/approvals.js +6 -5
- package/dist/core/workflow/billing.d.ts.map +1 -1
- package/dist/core/workflow/billing.js +33 -4
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +4 -3
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +2 -3
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -2
- package/dist/core/workflow/spawn-opencode.d.ts +36 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
- package/dist/core/workflow/spawn-opencode.js +132 -0
- package/dist/core/workflow/transcript.d.ts.map +1 -1
- package/dist/core/workflow/transcript.js +3 -2
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +5 -4
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +3 -2
- package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/paths.js +2 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +10 -9
- package/dist/lazy-commands.js +8 -1
- package/dist/lib/agent-browser/client.d.ts +9 -0
- package/dist/lib/agent-browser/client.d.ts.map +1 -1
- package/dist/lib/agent-browser/client.js +13 -0
- package/dist/lib/browser/client.d.ts +35 -1
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +117 -8
- package/dist/lib/browser/identity.d.ts +25 -0
- package/dist/lib/browser/identity.d.ts.map +1 -0
- package/dist/lib/browser/identity.js +66 -0
- package/dist/lib/browser/index.d.ts +2 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -1
- package/dist/lib/browser/launch-args.d.ts +26 -0
- package/dist/lib/browser/launch-args.d.ts.map +1 -1
- package/dist/lib/browser/launch-args.js +41 -1
- package/dist/lib/browser/user-agent.d.ts +53 -0
- package/dist/lib/browser/user-agent.d.ts.map +1 -0
- package/dist/lib/browser/user-agent.js +79 -0
- package/dist/lib/docs-metadata-managed.d.ts +1 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
- package/dist/lib/docs-metadata-managed.js +17 -1
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -9
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +2 -0
- package/dist/lib/pace/index.d.ts +2 -0
- package/dist/lib/pace/index.d.ts.map +1 -0
- package/dist/lib/pace/index.js +1 -0
- package/dist/lib/pace/pace.d.ts +95 -0
- package/dist/lib/pace/pace.d.ts.map +1 -0
- package/dist/lib/pace/pace.js +313 -0
- package/dist/lib/tunnel/forward-headers.d.ts +11 -0
- package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
- package/dist/lib/tunnel/forward-headers.js +17 -0
- package/dist/lib/tunnel/gate.js +15 -10
- package/dist/lib/webview/index.d.ts +2 -0
- package/dist/lib/webview/index.d.ts.map +1 -0
- package/dist/lib/webview/index.js +1 -0
- package/dist/lib/webview/webview.d.ts +116 -0
- package/dist/lib/webview/webview.d.ts.map +1 -0
- package/dist/lib/webview/webview.js +289 -0
- package/opencode-plugin/package.json +19 -0
- package/opencode-plugin/src/index.ts +791 -0
- package/package.json +11 -4
- package/schemas/config.schema.json +13 -0
- package/schemas/event-v3.schema.json +164 -0
- package/src/commander.ts +7 -0
- package/src/commands/agents.ts +33 -18
- package/src/commands/browse-ai.ts +8 -0
- package/src/commands/browse.ts +189 -10
- package/src/commands/checkpoint.ts +7 -9
- package/src/commands/deinit.ts +9 -4
- package/src/commands/docs.ts +2 -0
- package/src/commands/doctor.ts +11 -2
- package/src/commands/fetch.ts +14 -0
- package/src/commands/init.ts +281 -119
- package/src/commands/instructions.ts +8 -4
- package/src/commands/ledger-v3.ts +2 -1
- package/src/commands/webview.ts +159 -0
- package/src/core/adapter.ts +102 -1
- package/src/core/adapters/attestation.ts +3 -2
- package/src/core/adapters/profiles.ts +42 -1
- package/src/core/adapters/registry.ts +37 -0
- package/src/core/agents/canonical-emit.ts +2 -5
- package/src/core/agents/cli.ts +18 -17
- package/src/core/agents/coord-client.ts +9 -3
- package/src/core/agents/rules/claim-conflict.ts +3 -8
- package/src/core/agents/rules/commit-conflict.ts +2 -7
- package/src/core/agents/rules/stop-hook.ts +3 -1
- package/src/core/agents/session-events.ts +120 -26
- package/src/core/agents/session-finalization-state-v3.ts +2 -1
- package/src/core/agents/session-finalizer-v3.ts +5 -4
- package/src/core/agents/session-name-display.ts +48 -0
- package/src/core/agents/state/heartbeat-reader.ts +7 -0
- package/src/core/agents/state/heartbeat-writer.ts +24 -2
- package/src/core/agents/state/live-coordination-view.ts +1 -0
- package/src/core/agents/state/live-coordination-writer.ts +3 -0
- package/src/core/agents/state/stale-sweep.ts +2 -9
- package/src/core/agents/turn-changes.ts +97 -0
- package/src/core/artifacts/delivery-card.ts +2 -1
- package/src/core/artifacts/index.ts +4 -3
- package/src/core/config.ts +23 -0
- package/src/core/context/index.ts +2 -1
- package/src/core/conversations/archive.ts +4 -3
- package/src/core/diagnostics/bundle.ts +3 -2
- package/src/core/events/legacy-storage/canary.ts +5 -4
- package/src/core/events/legacy-storage/compression.ts +2 -1
- package/src/core/events/v3/adapter-id.ts +29 -1
- package/src/core/events/v3/authority-outbox.ts +2 -1
- package/src/core/events/v3/bootstrap.ts +5 -4
- package/src/core/events/v3/capabilities.ts +17 -0
- package/src/core/events/v3/contract.ts +9 -1
- package/src/core/events/v3/control-witness.ts +3 -2
- package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
- package/src/core/events/v3/fingerprint-keys.ts +7 -6
- package/src/core/events/v3/generated.ts +1 -1
- package/src/core/events/v3/live-feed.ts +5 -4
- package/src/core/events/v3/producers/command-recorder.ts +8 -6
- package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
- package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
- package/src/core/events/v3/producers/hook-base.ts +11 -4
- package/src/core/events/v3/producers/hook.ts +19 -1
- package/src/core/events/v3/producers/intake.ts +18 -6
- package/src/core/events/v3/producers/recorder.ts +123 -114
- package/src/core/events/v3/recovery.ts +4 -3
- package/src/core/events/v3/support-storage/inventory.ts +2 -1
- package/src/core/events/v3/support-storage/maintenance.ts +8 -3
- package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
- package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
- package/src/core/events/v3/tool-line-changes.ts +139 -0
- package/src/core/events/v3/writer.ts +8 -7
- package/src/core/governor/planning.ts +6 -5
- package/src/core/governor/service.ts +12 -11
- package/src/core/governor/state.ts +7 -6
- package/src/core/guard/evaluator.ts +1 -0
- package/src/core/guard/storage.ts +3 -2
- package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
- package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
- package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
- package/src/core/hooks/adapter/behaviors/index.ts +31 -0
- package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
- package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
- package/src/core/hooks/adapter/behaviors/types.ts +110 -0
- package/src/core/hooks/adapter/detect.ts +2 -3
- package/src/core/hooks/adapter/events.ts +53 -1
- package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
- package/src/core/hooks/adapter/output.ts +8 -1
- package/src/core/hooks/adapter/wiring.ts +85 -11
- package/src/core/hooks/cli.ts +733 -537
- package/src/core/hooks/effects/image-capture.ts +2 -1
- package/src/core/hooks/prompt-context/contract.ts +1 -0
- package/src/core/hooks/resolve/transcript.ts +42 -5
- package/src/core/hooks/session-name-presence.ts +10 -3
- package/src/core/hooks/shell-waiter.ts +166 -0
- package/src/core/resources/service.ts +5 -4
- package/src/core/semantic/review.ts +5 -4
- package/src/core/semantic/service.ts +15 -14
- package/src/core/semantic/storage.ts +5 -4
- package/src/core/storage/atomic-json.ts +7 -3
- package/src/core/storage/durable-history.ts +6 -5
- package/src/core/storage/maintenance-providers.ts +2 -1
- package/src/core/storage/maintenance.ts +7 -6
- package/src/core/storage/metrics.ts +5 -1
- package/src/core/storage/modes.ts +53 -0
- package/src/core/storage/process-log.ts +3 -2
- package/src/core/storage/receipts.ts +4 -3
- package/src/core/storage/segments.ts +7 -6
- package/src/core/supervisor/hook-health.ts +4 -4
- package/src/core/supervisor/service.ts +7 -6
- package/src/core/work/state.ts +8 -7
- package/src/core/workflow/adapters.ts +15 -0
- package/src/core/workflow/approvals.ts +6 -5
- package/src/core/workflow/billing.ts +34 -4
- package/src/core/workflow/durable-record.ts +4 -3
- package/src/core/workflow/live-session-v3.ts +2 -3
- package/src/core/workflow/run-state.ts +3 -2
- package/src/core/workflow/spawn-opencode.ts +150 -0
- package/src/core/workflow/transcript.ts +3 -2
- package/src/core/workflow/workspaces/leases.ts +5 -4
- package/src/core/workflow/workspaces/local-git.ts +3 -2
- package/src/core/workflow/workspaces/paths.ts +2 -1
- package/src/core/workflow/workspaces/state.ts +10 -9
- package/src/lazy-commands.ts +11 -1
- package/src/lib/agent-browser/client.ts +18 -0
- package/src/lib/browser/client.ts +147 -9
- package/src/lib/browser/identity.ts +64 -0
- package/src/lib/browser/index.ts +15 -1
- package/src/lib/browser/launch-args.ts +53 -1
- package/src/lib/browser/user-agent.ts +130 -0
- package/src/lib/docs-metadata-managed.ts +21 -0
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +2 -0
- package/src/lib/pace/index.ts +14 -0
- package/src/lib/pace/pace.ts +370 -0
- package/src/lib/tunnel/forward-headers.ts +16 -0
- package/src/lib/tunnel/gate.ts +18 -10
- package/src/lib/webview/index.ts +13 -0
- package/src/lib/webview/webview.ts +457 -0
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "node:fs";
|
|
15
15
|
import { dirname, join, resolve } from "node:path";
|
|
16
16
|
import type { ParsedPayload } from "../../../hooks/adapter/parse.ts";
|
|
17
|
+
import { stateDirMode, stateFileMode, stateModeTooOpen } from "../../../storage/modes.ts";
|
|
17
18
|
import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
|
|
18
19
|
import { EVENT_ADAPTER_IDS_V3, type EventAdapterIdV3 } from "../adapter-id.ts";
|
|
19
20
|
import type { EventV3WriteMode } from "../control.ts";
|
|
@@ -152,14 +153,14 @@ function intakeOrderKey(): string {
|
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
function ensureOwnerOnlyDirectory(path: string): void {
|
|
155
|
-
mkdirSync(path, { recursive: true, mode:
|
|
156
|
-
chmodSync(path,
|
|
156
|
+
mkdirSync(path, { recursive: true, mode: stateDirMode() });
|
|
157
|
+
chmodSync(path, stateDirMode());
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
function writeOwnerOnlyFileDurably(path: string, contents: string): void {
|
|
160
161
|
let fd: number | undefined;
|
|
161
162
|
try {
|
|
162
|
-
fd = openSync(path, "wx",
|
|
163
|
+
fd = openSync(path, "wx", stateFileMode());
|
|
163
164
|
writeFileSync(fd, contents, "utf8");
|
|
164
165
|
fsyncSync(fd);
|
|
165
166
|
closeSync(fd);
|
|
@@ -240,7 +241,7 @@ function readIntakeRecord(path: string): HookIntakeRecordV3 | undefined {
|
|
|
240
241
|
try {
|
|
241
242
|
const metadata = lstatSync(path);
|
|
242
243
|
if (!metadata.isFile() || metadata.isSymbolicLink()) return undefined;
|
|
243
|
-
if ((metadata.mode
|
|
244
|
+
if (stateModeTooOpen(metadata.mode)) return undefined;
|
|
244
245
|
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
245
246
|
} catch {
|
|
246
247
|
return undefined;
|
|
@@ -289,14 +290,25 @@ function validDelegatedChild(value: HookIntakeRecordV3["delegated_child"]): bool
|
|
|
289
290
|
function validTurnRitual(value: TurnRitualEvidenceV3 | undefined): boolean {
|
|
290
291
|
if (value === undefined) return true;
|
|
291
292
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
292
|
-
const keys = Object.keys(value)
|
|
293
|
+
const keys = Object.keys(value)
|
|
294
|
+
.filter((key) => key !== "session_name_unavailable_reason")
|
|
295
|
+
.sort();
|
|
293
296
|
if (
|
|
294
297
|
keys.join(",") !==
|
|
295
298
|
"session_name_present,session_name_required,status_box_present,status_box_present_strict"
|
|
296
299
|
) {
|
|
297
300
|
return false;
|
|
298
301
|
}
|
|
299
|
-
return
|
|
302
|
+
return (
|
|
303
|
+
keys.every((key) => typeof value[key as keyof TurnRitualEvidenceV3] === "boolean") &&
|
|
304
|
+
(value.session_name_unavailable_reason === undefined ||
|
|
305
|
+
[
|
|
306
|
+
"missing_transcript",
|
|
307
|
+
"transcript_not_ready",
|
|
308
|
+
"transcript_parse_error",
|
|
309
|
+
"observation_error",
|
|
310
|
+
].includes(value.session_name_unavailable_reason))
|
|
311
|
+
);
|
|
300
312
|
}
|
|
301
313
|
|
|
302
314
|
/**
|
|
@@ -15,14 +15,16 @@ import {
|
|
|
15
15
|
} from "node:fs";
|
|
16
16
|
import { hostname } from "node:os";
|
|
17
17
|
import { basename, join, resolve } from "node:path";
|
|
18
|
+
import { normalizeAdapter } from "../../../adapter.ts";
|
|
19
|
+
import { adapterBehavior } from "../../../hooks/adapter/behaviors/index.ts";
|
|
18
20
|
import { extractBashCommand, type ParsedPayload } from "../../../hooks/adapter/parse.ts";
|
|
19
21
|
import {
|
|
20
|
-
discoverCodexSessionTranscript,
|
|
21
22
|
type RuntimeContextTelemetry,
|
|
22
23
|
type RuntimeTelemetryOptions,
|
|
23
24
|
readRuntimeContextTelemetry,
|
|
24
25
|
readRuntimeTuning,
|
|
25
26
|
} from "../../../hooks/adapter/runtime-telemetry.ts";
|
|
27
|
+
import { stateDirMode, stateFileMode, stateModeTooOpen } from "../../../storage/modes.ts";
|
|
26
28
|
import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
|
|
27
29
|
import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
|
|
28
30
|
import { EVENT_ADAPTER_IDS_V3, type EventAdapterIdV3 } from "../adapter-id.ts";
|
|
@@ -61,6 +63,7 @@ import {
|
|
|
61
63
|
recoverSpanUpperBoundV3,
|
|
62
64
|
type SpanClockV3,
|
|
63
65
|
} from "../span-state.ts";
|
|
66
|
+
import { type LineChanges, prepareToolLineChanges } from "../tool-line-changes.ts";
|
|
64
67
|
import {
|
|
65
68
|
type ContextMeasurementV3,
|
|
66
69
|
type ContextTelemetryProvenanceV3,
|
|
@@ -103,19 +106,9 @@ const ACTIVE_RUNTIME_CONTEXT_PROBE_INTERVAL_MS = 15_000;
|
|
|
103
106
|
const STOP_RUNTIME_CONTEXT_RETRY_DELAYS_MS = [75, 175] as const;
|
|
104
107
|
/** Finalization is off the interactive hook path; briefly cover Codex's delayed transcript flush. */
|
|
105
108
|
const APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS = [0, 250, 250] as const;
|
|
106
|
-
/**
|
|
107
|
-
* Adapters whose turn-boundary recovery and mid-flight onboarding are enabled.
|
|
108
|
-
* Kept in code, outside the digested capability profiles, so tuning recovery
|
|
109
|
-
* never changes an adapter capability digest (ADR 0078).
|
|
110
|
-
*/
|
|
111
|
-
const RECOVERY_ENABLED_ADAPTERS: ReadonlySet<string> = new Set([
|
|
112
|
-
"claude-code",
|
|
113
|
-
"codex",
|
|
114
|
-
"cursor",
|
|
115
|
-
"openclaw",
|
|
116
|
-
]);
|
|
117
109
|
|
|
118
110
|
interface SpanStateV3 extends OpenSpanStateV3 {
|
|
111
|
+
line_changes?: LineChanges;
|
|
119
112
|
source_id: `hid_${string}`;
|
|
120
113
|
semantic_key?: `hid_${string}`;
|
|
121
114
|
recovery_reason?: string;
|
|
@@ -467,11 +460,11 @@ function sessionHashForSignal(
|
|
|
467
460
|
return normalizeNativeIdV3(context, `${input.adapter}.session`, nativeSession);
|
|
468
461
|
}
|
|
469
462
|
|
|
470
|
-
//
|
|
471
|
-
// hooks may still have an exact coordination instance, so reuse its
|
|
472
|
-
// live producer authority instead of hashing the instance into a
|
|
473
|
-
// state path. Ambiguous or absent matches keep the fail-closed fallback.
|
|
474
|
-
if (input.adapter
|
|
463
|
+
// Some adapters carry native session identity only on their start signal.
|
|
464
|
+
// Later hooks may still have an exact coordination instance, so reuse its
|
|
465
|
+
// single live producer authority instead of hashing the instance into a
|
|
466
|
+
// second state path. Ambiguous or absent matches keep the fail-closed fallback.
|
|
467
|
+
if (adapterBehavior(input.adapter).sessionIdentityOnStartOnly) {
|
|
475
468
|
const matches = listHookProducerStateRecordsV3(input.coordRoot).filter(
|
|
476
469
|
({ state }) => state.adapter === input.adapter && state.instance_id === input.instance_id,
|
|
477
470
|
);
|
|
@@ -666,6 +659,7 @@ function processHookSignalLocked(
|
|
|
666
659
|
sessionHash: `hid_${string}`,
|
|
667
660
|
path: string,
|
|
668
661
|
): RecordHookSignalV3Result {
|
|
662
|
+
const behavior = adapterBehavior(input.adapter);
|
|
669
663
|
const rootId = control.genesis.event.scope.root_id as `root_${string}`;
|
|
670
664
|
const epochId = control.genesis.profile.privacy_key_epoch;
|
|
671
665
|
const boundaryEventId =
|
|
@@ -701,14 +695,15 @@ function processHookSignalLocked(
|
|
|
701
695
|
throw new Error("V3 producer state authority does not match the active boundary");
|
|
702
696
|
}
|
|
703
697
|
if (
|
|
704
|
-
state
|
|
698
|
+
state &&
|
|
699
|
+
behavior.tracksExecutionMode &&
|
|
705
700
|
input.payload.cursor_mode &&
|
|
706
701
|
input.payload.cursor_mode !== "unknown"
|
|
707
702
|
) {
|
|
708
703
|
if (!state.cursor_mode || state.cursor_mode === "unknown") {
|
|
709
704
|
state.cursor_mode = input.payload.cursor_mode;
|
|
710
705
|
} else if (state.cursor_mode !== input.payload.cursor_mode) {
|
|
711
|
-
throw new Error("
|
|
706
|
+
throw new Error("Execution mode changed within one V3 generation");
|
|
712
707
|
}
|
|
713
708
|
}
|
|
714
709
|
let recovered: RecordHookSignalV3Result | undefined;
|
|
@@ -756,7 +751,7 @@ function processHookSignalLocked(
|
|
|
756
751
|
input.payload.session_id ?? input.payload.conversation_id ?? input.payload.agent_id;
|
|
757
752
|
const onboardable =
|
|
758
753
|
!state &&
|
|
759
|
-
|
|
754
|
+
behavior.turnRecovery &&
|
|
760
755
|
input.signal !== "session-end" &&
|
|
761
756
|
nativeSessionIdentity !== undefined;
|
|
762
757
|
if (!onboardable) {
|
|
@@ -794,7 +789,7 @@ function processHookSignalLocked(
|
|
|
794
789
|
instance_id: input.instance_id,
|
|
795
790
|
signal: input.signal,
|
|
796
791
|
session_hash: sessionHash,
|
|
797
|
-
...(
|
|
792
|
+
...(behavior.midFlightDiagnosticContext?.(input.payload) ?? {}),
|
|
798
793
|
});
|
|
799
794
|
}
|
|
800
795
|
}
|
|
@@ -805,7 +800,7 @@ function processHookSignalLocked(
|
|
|
805
800
|
state.generation_id,
|
|
806
801
|
state.privacy_epoch_id,
|
|
807
802
|
);
|
|
808
|
-
const recoveryEnabled =
|
|
803
|
+
const recoveryEnabled = behavior.turnRecovery;
|
|
809
804
|
const nativeTid = input.payload.turn_id
|
|
810
805
|
? (normalizeNativeIdV3(
|
|
811
806
|
fingerprintContext,
|
|
@@ -814,7 +809,7 @@ function processHookSignalLocked(
|
|
|
814
809
|
).replace(/^hid_/, "tid_") as `tid_${string}`)
|
|
815
810
|
: undefined;
|
|
816
811
|
const cursorPromptWithinOpenTurn =
|
|
817
|
-
|
|
812
|
+
behavior.promptContinuesOpenTurn &&
|
|
818
813
|
input.signal === "user-prompt-submit" &&
|
|
819
814
|
state.current_turn_id !== undefined &&
|
|
820
815
|
state.current_turn_span !== undefined;
|
|
@@ -923,8 +918,32 @@ function processHookSignalLocked(
|
|
|
923
918
|
// their self-contained timing cannot drift by a millisecond.
|
|
924
919
|
const eventClock = signalClock(input);
|
|
925
920
|
if (input.signal === "after-agent-response") {
|
|
921
|
+
// Cursor Stop followups auto-submit without beforeSubmitPrompt, so they
|
|
922
|
+
// never open a turn. The first stop already closed the human turn.
|
|
923
|
+
// Later completed replies still fire afterAgentResponse; bind them to a
|
|
924
|
+
// derived remediation turn so Stop can consume the new ritual instead of
|
|
925
|
+
// looping on the frozen first terminal.
|
|
926
|
+
const ritualFromResponseEvent = behavior.completedResponseEvent === input.signal;
|
|
927
|
+
if (
|
|
928
|
+
ritualFromResponseEvent &&
|
|
929
|
+
input.turn_ritual &&
|
|
930
|
+
(!state.current_turn_id || !state.current_turn_span)
|
|
931
|
+
) {
|
|
932
|
+
commitRecoveredToolTurnStart(input, state, path, rootId, fingerprintContext, {
|
|
933
|
+
stop_remediation: true,
|
|
934
|
+
});
|
|
935
|
+
writeProducerDiagnosticV3(input.coordRoot, "cursor_response_ritual_recovered_turn", {
|
|
936
|
+
adapter: input.adapter,
|
|
937
|
+
instance_id: input.instance_id,
|
|
938
|
+
generation_id: state.generation_id,
|
|
939
|
+
signal: input.signal,
|
|
940
|
+
reason: "no_open_turn",
|
|
941
|
+
turn_id: state.current_turn_id,
|
|
942
|
+
status_box_present_strict: input.turn_ritual.status_box_present_strict,
|
|
943
|
+
});
|
|
944
|
+
}
|
|
926
945
|
if (
|
|
927
|
-
|
|
946
|
+
!ritualFromResponseEvent ||
|
|
928
947
|
!state.current_turn_id ||
|
|
929
948
|
!state.current_turn_span ||
|
|
930
949
|
!input.turn_ritual
|
|
@@ -1023,6 +1042,10 @@ function processHookSignalLocked(
|
|
|
1023
1042
|
};
|
|
1024
1043
|
state.spans.push(span);
|
|
1025
1044
|
}
|
|
1045
|
+
span.line_changes = prepareToolLineChanges(
|
|
1046
|
+
input.payload,
|
|
1047
|
+
input.payload.cwd ?? input.coordRoot,
|
|
1048
|
+
);
|
|
1026
1049
|
} else if (input.signal === "post-tool-use" || input.signal === "post-tool-use-failure") {
|
|
1027
1050
|
span = sourceId
|
|
1028
1051
|
? state.spans.find((candidate) => candidate.source_id === sourceId)
|
|
@@ -1174,13 +1197,12 @@ function processHookSignalLocked(
|
|
|
1174
1197
|
input.signal === "stop" || input.signal === "stop-failure"
|
|
1175
1198
|
? turnTelemetryForTerminal(input, state, eventClock.observed_at)
|
|
1176
1199
|
: undefined;
|
|
1177
|
-
const cursorToolChannelSupport =
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
: undefined;
|
|
1200
|
+
const cursorToolChannelSupport = behavior.tracksExecutionMode
|
|
1201
|
+
? cursorToolChannelSupportV3(state.cursor_mode ?? "unknown")
|
|
1202
|
+
: undefined;
|
|
1181
1203
|
const cursorToolChannelUnattested =
|
|
1182
1204
|
(input.signal === "stop" || input.signal === "stop-failure") &&
|
|
1183
|
-
|
|
1205
|
+
behavior.tracksExecutionMode &&
|
|
1184
1206
|
cursorToolChannelSupport !== "unsupported" &&
|
|
1185
1207
|
state.tool_call_count === 0;
|
|
1186
1208
|
if (input.signal === "sub-agent-start" && delegation && !delegation.open_event_id) {
|
|
@@ -1192,15 +1214,16 @@ function processHookSignalLocked(
|
|
|
1192
1214
|
// Before the signal's own event: the tool call or turn that carries the
|
|
1193
1215
|
// new effort already ran under it, so it must ride the new attestation.
|
|
1194
1216
|
maybeAttestTuningChange(input, state, path, rootId);
|
|
1217
|
+
const ritualArrivesSeparately = behavior.completedResponseEvent !== undefined;
|
|
1195
1218
|
const cursorResponseRitual =
|
|
1196
|
-
|
|
1219
|
+
ritualArrivesSeparately &&
|
|
1197
1220
|
(input.signal === "stop" || input.signal === "stop-failure") &&
|
|
1198
1221
|
state.current_turn_id &&
|
|
1199
1222
|
state.cursor_response_ritual?.turn_id === state.current_turn_id
|
|
1200
1223
|
? state.cursor_response_ritual
|
|
1201
1224
|
: undefined;
|
|
1202
1225
|
const terminalTurnRitual =
|
|
1203
|
-
|
|
1226
|
+
ritualArrivesSeparately && (input.signal === "stop" || input.signal === "stop-failure")
|
|
1204
1227
|
? cursorResponseRitual
|
|
1205
1228
|
? {
|
|
1206
1229
|
status_box_present: cursorResponseRitual.status_box_present,
|
|
@@ -1253,6 +1276,7 @@ function processHookSignalLocked(
|
|
|
1253
1276
|
span_id: openingSpan?.span_id ?? closingSpan?.span_id,
|
|
1254
1277
|
parent_span_id: openingSpan?.parent_span_id ?? closingSpan?.parent_span_id,
|
|
1255
1278
|
terminal_span: terminalSpan,
|
|
1279
|
+
tool_line_changes: span?.line_changes,
|
|
1256
1280
|
harness_timing: state.turn_harness,
|
|
1257
1281
|
turn_telemetry: turnTelemetry,
|
|
1258
1282
|
caused_by: [
|
|
@@ -1777,13 +1801,14 @@ function commitRecoveredTurnCompleted(
|
|
|
1777
1801
|
commitEventLocked(input, state, path, event);
|
|
1778
1802
|
}
|
|
1779
1803
|
|
|
1780
|
-
/** Open a derived turn for
|
|
1804
|
+
/** Open a derived turn for signals that arrive after their native turn closed. */
|
|
1781
1805
|
function commitRecoveredToolTurnStart(
|
|
1782
1806
|
input: RecordHookSignalV3Input,
|
|
1783
1807
|
state: HookProducerStateV3,
|
|
1784
1808
|
path: string,
|
|
1785
1809
|
rootId: `root_${string}`,
|
|
1786
1810
|
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
1811
|
+
options?: { stop_remediation?: boolean },
|
|
1787
1812
|
): void {
|
|
1788
1813
|
const clock = signalClock(input);
|
|
1789
1814
|
const span = openSpanStateV3({
|
|
@@ -1819,6 +1844,7 @@ function commitRecoveredToolTurnStart(
|
|
|
1819
1844
|
observed_at: clock.observed_at,
|
|
1820
1845
|
monotonic_ns: orderedEventMonotonic(state, input.monotonic_ns),
|
|
1821
1846
|
clock_id: state.clock_id,
|
|
1847
|
+
...(options?.stop_remediation ? { stop_remediation: true } : {}),
|
|
1822
1848
|
});
|
|
1823
1849
|
if (event?.event_type !== "turn.started") {
|
|
1824
1850
|
throw new Error("recovered tool turn start could not be normalized");
|
|
@@ -2105,7 +2131,8 @@ function maybeCommitActiveRuntimeContextObservation(
|
|
|
2105
2131
|
}
|
|
2106
2132
|
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
2107
2133
|
const nativeTurnId = input.payload.turn_id ?? state.current_native_turn_id;
|
|
2108
|
-
|
|
2134
|
+
const behavior = adapterBehavior(input.adapter);
|
|
2135
|
+
if (!nativeSessionId || (!nativeTurnId && !behavior.nativeTurnIdOptional)) return;
|
|
2109
2136
|
|
|
2110
2137
|
const now = Date.now();
|
|
2111
2138
|
const configuredInterval = input.runtimeTelemetryOptions?.activeContextProbeIntervalMs;
|
|
@@ -2148,23 +2175,22 @@ function maybeCommitActiveRuntimeContextObservation(
|
|
|
2148
2175
|
);
|
|
2149
2176
|
return;
|
|
2150
2177
|
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
if (input.adapter === "openclaw") {
|
|
2178
|
+
// Runtime readers exist only for workflow adapters; an event-only adapter
|
|
2179
|
+
// has no transcript to read.
|
|
2180
|
+
const runtimeAdapter = normalizeAdapter(input.adapter);
|
|
2181
|
+
if (!runtimeAdapter || !behavior.runtimeContextAvailable(state.cursor_mode)) {
|
|
2156
2182
|
publishProducerState(input.coordRoot, path, state);
|
|
2157
2183
|
return;
|
|
2158
2184
|
}
|
|
2159
2185
|
|
|
2160
2186
|
const transcriptPath = runtimeTranscriptPath(input, state);
|
|
2161
|
-
if (!transcriptPath &&
|
|
2187
|
+
if (!transcriptPath && !behavior.runtimeContextTranscriptOptional) {
|
|
2162
2188
|
publishProducerState(input.coordRoot, path, state);
|
|
2163
2189
|
return;
|
|
2164
2190
|
}
|
|
2165
2191
|
const runtime = readRuntimeContextTelemetry(
|
|
2166
2192
|
{
|
|
2167
|
-
adapter:
|
|
2193
|
+
adapter: runtimeAdapter,
|
|
2168
2194
|
session_id: nativeSessionId,
|
|
2169
2195
|
...(nativeTurnId ? { turn_id: nativeTurnId } : {}),
|
|
2170
2196
|
...(state.current_turn_span?.opened_at
|
|
@@ -2354,7 +2380,7 @@ function queuePendingRuntimeContext(
|
|
|
2354
2380
|
nativeTurnId: string | undefined,
|
|
2355
2381
|
): void {
|
|
2356
2382
|
if (
|
|
2357
|
-
input.adapter
|
|
2383
|
+
!adapterBehavior(input.adapter).runtimeContextRetry ||
|
|
2358
2384
|
!target ||
|
|
2359
2385
|
measurement.state !== "expected_but_missing" ||
|
|
2360
2386
|
!retryableRuntimeContextReason(measurement.reason)
|
|
@@ -2389,7 +2415,12 @@ function reconcilePendingRuntimeContexts(
|
|
|
2389
2415
|
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
2390
2416
|
options: { deferRetryLimit?: boolean; finalAttempt?: boolean } = {},
|
|
2391
2417
|
): void {
|
|
2392
|
-
if (
|
|
2418
|
+
if (
|
|
2419
|
+
!adapterBehavior(state.adapter).runtimeContextRetry ||
|
|
2420
|
+
!state.pending_runtime_contexts?.length
|
|
2421
|
+
) {
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2393
2424
|
for (const pending of [...state.pending_runtime_contexts]) {
|
|
2394
2425
|
const runtime = readRuntimeContextTelemetry(
|
|
2395
2426
|
{
|
|
@@ -2455,7 +2486,20 @@ function reconcilePendingRuntimeContextsBeforeApprovedEnd(
|
|
|
2455
2486
|
rootId: `root_${string}`,
|
|
2456
2487
|
fingerprintContext: ReturnType<typeof fingerprintContextV3>,
|
|
2457
2488
|
): void {
|
|
2458
|
-
if (
|
|
2489
|
+
if (
|
|
2490
|
+
!adapterBehavior(state.adapter).runtimeContextRetry ||
|
|
2491
|
+
!state.pending_runtime_contexts?.length
|
|
2492
|
+
) {
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
// Context reconciliation continues the hook boot's sequence, even when the
|
|
2496
|
+
// finalizer drives it. Recover that producer identity from its validated
|
|
2497
|
+
// session start instead of attaching the sequence to the finalizer's name.
|
|
2498
|
+
const ledger = readLedgerV3(input.coordRoot, { authority: input.mode });
|
|
2499
|
+
const start = ledger.events.find(({ event }) => event.event_id === state.started_event_id)?.event;
|
|
2500
|
+
if (!ledger.complete || !start || start.producer.boot_id !== state.boot_id) {
|
|
2501
|
+
throw new Error("event_v3_finalizer_context_producer_unavailable");
|
|
2502
|
+
}
|
|
2459
2503
|
for (const [index, delayMs] of APPROVED_END_RUNTIME_CONTEXT_RETRY_DELAYS_MS.entries()) {
|
|
2460
2504
|
if (delayMs > 0) sleepSync(delayMs);
|
|
2461
2505
|
reconcilePendingRuntimeContexts(
|
|
@@ -2466,7 +2510,7 @@ function reconcilePendingRuntimeContextsBeforeApprovedEnd(
|
|
|
2466
2510
|
payload: { raw: {} },
|
|
2467
2511
|
adapter: state.adapter,
|
|
2468
2512
|
instance_id: state.instance_id,
|
|
2469
|
-
producer_id:
|
|
2513
|
+
producer_id: start.producer.producer_id as `prd_${string}`,
|
|
2470
2514
|
build_id: input.build_id,
|
|
2471
2515
|
platform: input.platform,
|
|
2472
2516
|
observed_at: new Date().toISOString(),
|
|
@@ -2499,9 +2543,11 @@ function turnTelemetryForTerminal(
|
|
|
2499
2543
|
observedAt: string,
|
|
2500
2544
|
): TurnTelemetryV3 {
|
|
2501
2545
|
const native = extractTurnTelemetryV3(input.adapter, input.payload.raw, observedAt);
|
|
2546
|
+
const behavior = adapterBehavior(input.adapter);
|
|
2502
2547
|
if (native.context.state === "observed") return native;
|
|
2503
|
-
|
|
2504
|
-
if (
|
|
2548
|
+
const runtimeAdapter = normalizeAdapter(input.adapter);
|
|
2549
|
+
if (!runtimeAdapter || behavior.nativeTelemetryOnly) return native;
|
|
2550
|
+
if (!behavior.runtimeContextAvailable(state.cursor_mode)) {
|
|
2505
2551
|
return {
|
|
2506
2552
|
...native,
|
|
2507
2553
|
context: { state: "unsupported", capability: "context_usage" },
|
|
@@ -2509,7 +2555,7 @@ function turnTelemetryForTerminal(
|
|
|
2509
2555
|
}
|
|
2510
2556
|
|
|
2511
2557
|
const request = {
|
|
2512
|
-
adapter:
|
|
2558
|
+
adapter: runtimeAdapter,
|
|
2513
2559
|
session_id: input.payload.session_id ?? input.payload.conversation_id,
|
|
2514
2560
|
turn_id: input.payload.turn_id ?? state.current_native_turn_id,
|
|
2515
2561
|
transcript_path: runtimeTranscriptPath(input, state),
|
|
@@ -2519,7 +2565,7 @@ function turnTelemetryForTerminal(
|
|
|
2519
2565
|
let runtime = readRuntimeContextTelemetry(request, input.runtimeTelemetryOptions);
|
|
2520
2566
|
if (runtime.bytes_read > 0) recordRuntimeTelemetryTiming(state, runtime.io_duration_ms);
|
|
2521
2567
|
if (
|
|
2522
|
-
|
|
2568
|
+
behavior.runtimeContextRetry &&
|
|
2523
2569
|
runtime.state === "partial" &&
|
|
2524
2570
|
retryableRuntimeContextReason(runtime.reason)
|
|
2525
2571
|
) {
|
|
@@ -2554,24 +2600,21 @@ function runtimeTranscriptPath(
|
|
|
2554
2600
|
input: RecordHookSignalV3Input,
|
|
2555
2601
|
state: HookProducerStateV3,
|
|
2556
2602
|
): string | undefined {
|
|
2557
|
-
|
|
2603
|
+
const discover = adapterBehavior(input.adapter).discoverTranscript;
|
|
2604
|
+
if (!discover) return input.payload.transcript_path;
|
|
2558
2605
|
const nativeSessionId = input.payload.session_id ?? input.payload.conversation_id;
|
|
2559
2606
|
if (!nativeSessionId) return input.payload.transcript_path;
|
|
2560
2607
|
|
|
2561
2608
|
const supplied = input.payload.transcript_path;
|
|
2562
2609
|
if (supplied) {
|
|
2563
|
-
const verified =
|
|
2564
|
-
nativeSessionId,
|
|
2565
|
-
supplied,
|
|
2566
|
-
input.runtimeTelemetryOptions,
|
|
2567
|
-
);
|
|
2610
|
+
const verified = discover(nativeSessionId, supplied, input.runtimeTelemetryOptions);
|
|
2568
2611
|
if (verified) state.runtime_transcript_path = verified;
|
|
2569
2612
|
// Preserve a supplied mismatch so the typed reader reports it honestly.
|
|
2570
2613
|
return verified ?? supplied;
|
|
2571
2614
|
}
|
|
2572
2615
|
|
|
2573
2616
|
if (state.runtime_transcript_path) {
|
|
2574
|
-
const verified =
|
|
2617
|
+
const verified = discover(
|
|
2575
2618
|
nativeSessionId,
|
|
2576
2619
|
state.runtime_transcript_path,
|
|
2577
2620
|
input.runtimeTelemetryOptions,
|
|
@@ -2580,11 +2623,7 @@ function runtimeTranscriptPath(
|
|
|
2580
2623
|
state.runtime_transcript_path = undefined;
|
|
2581
2624
|
}
|
|
2582
2625
|
|
|
2583
|
-
const discovered =
|
|
2584
|
-
nativeSessionId,
|
|
2585
|
-
undefined,
|
|
2586
|
-
input.runtimeTelemetryOptions,
|
|
2587
|
-
);
|
|
2626
|
+
const discovered = discover(nativeSessionId, undefined, input.runtimeTelemetryOptions);
|
|
2588
2627
|
if (discovered) state.runtime_transcript_path = discovered;
|
|
2589
2628
|
return discovered;
|
|
2590
2629
|
}
|
|
@@ -2676,11 +2715,12 @@ function maybeAttestTuningChange(
|
|
|
2676
2715
|
let candidate: { effort?: string; speed?: string; model?: string } | undefined;
|
|
2677
2716
|
let attestationSource: "native" | "derived" = "native";
|
|
2678
2717
|
let sourceEvent = `${input.adapter}.effort-payload`;
|
|
2679
|
-
|
|
2718
|
+
const behavior = adapterBehavior(input.adapter);
|
|
2719
|
+
if (behavior.effortAttestation === "payload") {
|
|
2680
2720
|
if (!input.payload.effort) return;
|
|
2681
2721
|
candidate = { effort: input.payload.effort };
|
|
2682
2722
|
} else if (
|
|
2683
|
-
|
|
2723
|
+
behavior.effortAttestation === "rollout-probe" &&
|
|
2684
2724
|
(input.signal === "stop" ||
|
|
2685
2725
|
// Early probe: without it, a Codex card shows no effort until the first
|
|
2686
2726
|
// turn terminal. One rollout read per turn while effort is unknown.
|
|
@@ -2723,9 +2763,9 @@ function maybeAttestTuningChange(
|
|
|
2723
2763
|
return;
|
|
2724
2764
|
}
|
|
2725
2765
|
|
|
2726
|
-
//
|
|
2727
|
-
// carries speed and the (possibly swapped) model, so refresh from it.
|
|
2728
|
-
if (
|
|
2766
|
+
// Payload change detection rides the payload; the paired transcript row
|
|
2767
|
+
// also carries speed and the (possibly swapped) model, so refresh from it.
|
|
2768
|
+
if (behavior.transcriptTuningRefresh) {
|
|
2729
2769
|
const runtime = readRuntimeTuning({
|
|
2730
2770
|
adapter: "claude-code",
|
|
2731
2771
|
transcript_path: input.payload.transcript_path,
|
|
@@ -2878,9 +2918,7 @@ function commitRuntimeAttestationChange(
|
|
|
2878
2918
|
}
|
|
2879
2919
|
|
|
2880
2920
|
function modelProviderFor(adapter: EventAdapterIdV3): string {
|
|
2881
|
-
|
|
2882
|
-
if (adapter === "codex") return "openai";
|
|
2883
|
-
return adapter;
|
|
2921
|
+
return adapterBehavior(adapter).modelProvider;
|
|
2884
2922
|
}
|
|
2885
2923
|
|
|
2886
2924
|
function recordRuntimeTelemetryTiming(state: HookProducerStateV3, durationMs: number): void {
|
|
@@ -3125,47 +3163,13 @@ function buildMidFlightSessionStart(
|
|
|
3125
3163
|
*/
|
|
3126
3164
|
function isCursorPromptBootstrap(input: RecordHookSignalV3Input): boolean {
|
|
3127
3165
|
return (
|
|
3128
|
-
input.adapter
|
|
3166
|
+
adapterBehavior(input.adapter).promptMayPrecedeSessionStart &&
|
|
3129
3167
|
input.signal === "user-prompt-submit" &&
|
|
3130
3168
|
(input.payload.conversation_id !== undefined || input.payload.session_id !== undefined) &&
|
|
3131
3169
|
input.payload.turn_id !== undefined
|
|
3132
3170
|
);
|
|
3133
3171
|
}
|
|
3134
3172
|
|
|
3135
|
-
/** Privacy-safe environment and recovery provenance for Codex mid-flight
|
|
3136
|
-
* onboarding. WSLENV values are never recorded, only normalized variable
|
|
3137
|
-
* names, and the native session identifier itself remains fingerprinted. */
|
|
3138
|
-
export function codexMidFlightDiagnosticContext(
|
|
3139
|
-
payload: ParsedPayload,
|
|
3140
|
-
env: NodeJS.ProcessEnv = process.env,
|
|
3141
|
-
): Record<string, string | boolean> {
|
|
3142
|
-
const wslenv = env.WSLENV?.trim() ?? "";
|
|
3143
|
-
const wslenvNames = [
|
|
3144
|
-
...new Set(
|
|
3145
|
-
wslenv
|
|
3146
|
-
.split(":")
|
|
3147
|
-
.map((entry) => entry.split("/", 1)[0]?.trim() ?? "")
|
|
3148
|
-
.filter((name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name)),
|
|
3149
|
-
),
|
|
3150
|
-
]
|
|
3151
|
-
.sort()
|
|
3152
|
-
.join(":")
|
|
3153
|
-
.slice(0, 512);
|
|
3154
|
-
const identityRecoverySource = payload.session_id
|
|
3155
|
-
? "native_session_id"
|
|
3156
|
-
: payload.conversation_id
|
|
3157
|
-
? "native_conversation_id"
|
|
3158
|
-
: payload.agent_id
|
|
3159
|
-
? "native_agent_id"
|
|
3160
|
-
: "unavailable";
|
|
3161
|
-
return {
|
|
3162
|
-
thread_id_present: Boolean(env.CODEX_THREAD_ID?.trim()),
|
|
3163
|
-
wslenv_present: wslenv.length > 0,
|
|
3164
|
-
wslenv_names: wslenvNames,
|
|
3165
|
-
identity_recovery_source: identityRecoverySource,
|
|
3166
|
-
};
|
|
3167
|
-
}
|
|
3168
|
-
|
|
3169
3173
|
function suppressClosedSpanSignal(
|
|
3170
3174
|
input: RecordHookSignalV3Input,
|
|
3171
3175
|
sessionHash: `hid_${string}`,
|
|
@@ -3554,7 +3558,7 @@ export function reanchorArchivedHookProducersV3(
|
|
|
3554
3558
|
payload: {
|
|
3555
3559
|
raw: {},
|
|
3556
3560
|
...(previous.current_native_turn_id ? { turn_id: previous.current_native_turn_id } : {}),
|
|
3557
|
-
...(previous.adapter
|
|
3561
|
+
...(adapterBehavior(previous.adapter).tracksExecutionMode && previous.cursor_mode
|
|
3558
3562
|
? { cursor_mode: previous.cursor_mode }
|
|
3559
3563
|
: {}),
|
|
3560
3564
|
},
|
|
@@ -3745,7 +3749,10 @@ export function reconcilePendingRuntimeContextV3(
|
|
|
3745
3749
|
// holds everything this producer durably recorded.
|
|
3746
3750
|
return { state: "missing" };
|
|
3747
3751
|
}
|
|
3748
|
-
if (
|
|
3752
|
+
if (
|
|
3753
|
+
!adapterBehavior(state.adapter).runtimeContextRetry ||
|
|
3754
|
+
!state.pending_runtime_contexts?.length
|
|
3755
|
+
) {
|
|
3749
3756
|
return { state: "not_pending" };
|
|
3750
3757
|
}
|
|
3751
3758
|
const context = fingerprintContextV3(
|
|
@@ -3863,7 +3870,9 @@ function newProducerState(
|
|
|
3863
3870
|
generation_id: input.delegated_child?.generation_id ?? generationIdV3(),
|
|
3864
3871
|
attestation_id: attestationIdV3(),
|
|
3865
3872
|
capability_profile: adapterCapabilityProfileDigestV3(input.adapter),
|
|
3866
|
-
cursor_mode: input.adapter
|
|
3873
|
+
cursor_mode: adapterBehavior(input.adapter).tracksExecutionMode
|
|
3874
|
+
? (input.payload.cursor_mode ?? "unknown")
|
|
3875
|
+
: undefined,
|
|
3867
3876
|
privacy_epoch_id: epochId,
|
|
3868
3877
|
epoch_genesis_id: genesisId,
|
|
3869
3878
|
boot_id: bootId,
|
|
@@ -4048,7 +4057,7 @@ function cursorShellSemanticKey(
|
|
|
4048
4057
|
input: RecordHookSignalV3Input,
|
|
4049
4058
|
context: ReturnType<typeof fingerprintContextV3>,
|
|
4050
4059
|
): `hid_${string}` | undefined {
|
|
4051
|
-
if (input.adapter
|
|
4060
|
+
if (!adapterBehavior(input.adapter).shellOperationDedup) return undefined;
|
|
4052
4061
|
const command = extractBashCommand(input.payload.tool_name, input.payload.tool_input);
|
|
4053
4062
|
return command
|
|
4054
4063
|
? normalizeNativeIdV3(context, "cursor.shell-operation", command.normalize("NFC"))
|
|
@@ -4215,9 +4224,9 @@ function producerStatePath(
|
|
|
4215
4224
|
function acquireStateLease(coordRoot: string, statePath: string) {
|
|
4216
4225
|
const directory = join(statePath, "..");
|
|
4217
4226
|
const producerRoot = join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "private-producers");
|
|
4218
|
-
mkdirSync(directory, { recursive: true, mode:
|
|
4219
|
-
chmodSync(producerRoot,
|
|
4220
|
-
chmodSync(directory,
|
|
4227
|
+
mkdirSync(directory, { recursive: true, mode: stateDirMode() });
|
|
4228
|
+
chmodSync(producerRoot, stateDirMode());
|
|
4229
|
+
chmodSync(directory, stateDirMode());
|
|
4221
4230
|
const acquiredControl = readEventV3ControlState(coordRoot);
|
|
4222
4231
|
const acquiredGenesis =
|
|
4223
4232
|
acquiredControl.state === "candidate" || acquiredControl.state === "active"
|
|
@@ -4263,13 +4272,13 @@ function publishProducerState(coordRoot: string, path: string, state: HookProduc
|
|
|
4263
4272
|
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
4264
4273
|
let fd: number | undefined;
|
|
4265
4274
|
try {
|
|
4266
|
-
fd = openSync(temporary, "wx",
|
|
4275
|
+
fd = openSync(temporary, "wx", stateFileMode());
|
|
4267
4276
|
writeFileSync(fd, `${JSON.stringify(state)}\n`, "utf8");
|
|
4268
4277
|
fsyncSync(fd);
|
|
4269
4278
|
closeSync(fd);
|
|
4270
4279
|
fd = undefined;
|
|
4271
4280
|
renameSync(temporary, path);
|
|
4272
|
-
chmodSync(path,
|
|
4281
|
+
chmodSync(path, stateFileMode());
|
|
4273
4282
|
fsyncParentDirectory(path);
|
|
4274
4283
|
} finally {
|
|
4275
4284
|
if (fd !== undefined) closeSync(fd);
|
|
@@ -4282,7 +4291,7 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
4282
4291
|
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
4283
4292
|
throw new Error("V3 producer state path is unsafe");
|
|
4284
4293
|
}
|
|
4285
|
-
if ((metadata.mode
|
|
4294
|
+
if (stateModeTooOpen(metadata.mode)) throw new Error("V3 producer state is open to other users");
|
|
4286
4295
|
let parsed: unknown;
|
|
4287
4296
|
try {
|
|
4288
4297
|
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
@@ -4312,7 +4321,7 @@ function readProducerState(path: string): HookProducerStateV3 {
|
|
|
4312
4321
|
) {
|
|
4313
4322
|
state.pending_runtime_contexts = undefined;
|
|
4314
4323
|
}
|
|
4315
|
-
if (state.adapter
|
|
4324
|
+
if (adapterBehavior(state.adapter).tracksExecutionMode) state.cursor_mode ??= "unknown";
|
|
4316
4325
|
const allowedKeys = new Set([
|
|
4317
4326
|
"active_runtime_context_probe",
|
|
4318
4327
|
"adapter",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
writeFileSync,
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { basename, join, resolve } from "node:path";
|
|
14
|
+
import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
|
|
14
15
|
import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
|
|
15
16
|
import { initializeEventLedgerV3 } from "./bootstrap.ts";
|
|
16
17
|
import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
|
|
@@ -133,8 +134,8 @@ function recoveryIdFor(failure: EventV3RecoveryFailure): `rcv_${string}` {
|
|
|
133
134
|
function publishRecoveryIntent(root: string, value: EventV3RecoveryIntent): EventV3RecoveryIntent {
|
|
134
135
|
const intent = validateEventV3RecoveryIntent(value);
|
|
135
136
|
const records = eventV3RecoveryRecordsRoot(root);
|
|
136
|
-
mkdirSync(records, { recursive: true, mode:
|
|
137
|
-
chmodSync(records,
|
|
137
|
+
mkdirSync(records, { recursive: true, mode: stateDirMode() });
|
|
138
|
+
chmodSync(records, stateDirMode());
|
|
138
139
|
const path = recoveryIntentPath(records, intent.recovery_id);
|
|
139
140
|
const serialized = `${canonicalJsonV3(intent)}\n`;
|
|
140
141
|
if (existsSync(path)) {
|
|
@@ -256,7 +257,7 @@ function publishExclusive(path: string, serialized: string): void {
|
|
|
256
257
|
const temporary = `${path}.tmp.${process.pid}`;
|
|
257
258
|
let fd: number | undefined;
|
|
258
259
|
try {
|
|
259
|
-
fd = openSync(temporary, "wx",
|
|
260
|
+
fd = openSync(temporary, "wx", stateFileMode());
|
|
260
261
|
writeFileSync(fd, serialized, "utf8");
|
|
261
262
|
fsyncSync(fd);
|
|
262
263
|
closeSync(fd);
|