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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import type { EmitContext } from "../commander.ts";
|
|
4
|
+
import { commandPaceGate } from "../lib/pace/index.ts";
|
|
5
|
+
import {
|
|
6
|
+
parseWebViewTypeStep,
|
|
7
|
+
parseWebViewViewport,
|
|
8
|
+
runWebView,
|
|
9
|
+
type WebViewBackend,
|
|
10
|
+
WebViewUnavailableError,
|
|
11
|
+
} from "../lib/webview/index.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `harn webview`: a lightweight headless page probe through Bun.WebView.
|
|
15
|
+
*
|
|
16
|
+
* Experimental and Bun-only (Bun 1.4+). It always launches a fresh browser with
|
|
17
|
+
* ephemeral storage unless `--profile` is given, and never attaches to a
|
|
18
|
+
* running user browser. Use `browse` for headed login, the shared cookie jar,
|
|
19
|
+
* HAR capture, full-page screenshots, and page QA; use `browse-ai` for
|
|
20
|
+
* accessibility-tree element references. On Node the command fails with a
|
|
21
|
+
* clear message, matching how `tunnel` handles its Bun-only worker.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface WebviewOpts {
|
|
25
|
+
userAgent?: string;
|
|
26
|
+
backend: string;
|
|
27
|
+
viewport: string;
|
|
28
|
+
profile?: string;
|
|
29
|
+
chromePath?: string;
|
|
30
|
+
browserArg: string[];
|
|
31
|
+
timeout: string;
|
|
32
|
+
waitFor?: string;
|
|
33
|
+
type: string[];
|
|
34
|
+
click: string[];
|
|
35
|
+
press: string[];
|
|
36
|
+
evaluate?: string;
|
|
37
|
+
settle: string;
|
|
38
|
+
screenshot?: string;
|
|
39
|
+
html?: boolean;
|
|
40
|
+
json?: boolean;
|
|
41
|
+
pace?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function registerWebviewCommand(program: Command, emit: EmitContext, binName: string): void {
|
|
45
|
+
program
|
|
46
|
+
.command("webview <url>")
|
|
47
|
+
.description(
|
|
48
|
+
"Lightweight headless page probe through Bun.WebView (experimental, Bun 1.4+). " +
|
|
49
|
+
`Fresh ephemeral browser per call; use ${binName} browse for login, cookies, HAR, and QA.`,
|
|
50
|
+
)
|
|
51
|
+
.option("--backend <engine>", "Browser engine: auto | chrome | webkit", "auto")
|
|
52
|
+
.option(
|
|
53
|
+
"--user-agent <ua>",
|
|
54
|
+
"Chrome user agent: shared store by default; auto refreshes, native disables",
|
|
55
|
+
)
|
|
56
|
+
.option("--viewport <WxH>", "Viewport in CSS pixels", "1280x800")
|
|
57
|
+
.option(
|
|
58
|
+
"--profile <dir>",
|
|
59
|
+
"Persist cookies and local storage in this directory (default is ephemeral)",
|
|
60
|
+
)
|
|
61
|
+
.option("--chrome-path <path>", "Chrome-family executable; implies the Chrome backend")
|
|
62
|
+
.option("--browser-arg <flag>", "Extra Chrome launch flag (repeatable)", collect, [])
|
|
63
|
+
.option("--timeout <ms>", "Navigation and selector timeout in milliseconds", "30000")
|
|
64
|
+
.option("--wait-for <css>", "Wait for a CSS selector after navigation")
|
|
65
|
+
.option(
|
|
66
|
+
"--type <css=>text>",
|
|
67
|
+
"Focus a selector and insert text (repeatable; runs before clicks)",
|
|
68
|
+
collect,
|
|
69
|
+
[],
|
|
70
|
+
)
|
|
71
|
+
.option("--click <css>", "Scroll to and click a CSS selector (repeatable)", collect, [])
|
|
72
|
+
.option(
|
|
73
|
+
"--press <key>",
|
|
74
|
+
"Press a key in the currently focused element (repeatable)",
|
|
75
|
+
collect,
|
|
76
|
+
[],
|
|
77
|
+
)
|
|
78
|
+
.option("--evaluate <expression>", "Evaluate a JavaScript expression after actions")
|
|
79
|
+
.option("--settle <ms>", "Extra delay after actions before capture", "0")
|
|
80
|
+
.option(
|
|
81
|
+
"--screenshot <path>",
|
|
82
|
+
"Save a viewport PNG (Bun.WebView does not provide full-page capture)",
|
|
83
|
+
)
|
|
84
|
+
.option("--html", "Print the final document HTML instead of body text")
|
|
85
|
+
.option("--json", "Print the full result envelope")
|
|
86
|
+
.option(
|
|
87
|
+
"--no-pace",
|
|
88
|
+
"Skip the human-pace gap (3 to 9 s) between page loads of the same site for this run. " +
|
|
89
|
+
"HARNERY_PACE=off disables it machine-wide; local and private hosts never wait.",
|
|
90
|
+
)
|
|
91
|
+
.action(async (url: string, opts: WebviewOpts) => {
|
|
92
|
+
try {
|
|
93
|
+
await runWebview(url, opts, emit);
|
|
94
|
+
} catch (err: unknown) {
|
|
95
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
96
|
+
emit.error({
|
|
97
|
+
code: err instanceof WebViewUnavailableError ? err.code : "webview_error",
|
|
98
|
+
message,
|
|
99
|
+
});
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function runWebview(url: string, opts: WebviewOpts, emit: EmitContext): Promise<void> {
|
|
106
|
+
if (!isBackend(opts.backend)) {
|
|
107
|
+
throw new Error(`Invalid backend "${opts.backend}". Expected auto, chrome, or webkit.`);
|
|
108
|
+
}
|
|
109
|
+
const backend = opts.backend === "auto" && opts.chromePath ? "chrome" : opts.backend;
|
|
110
|
+
const result = await runWebView(url, {
|
|
111
|
+
backend,
|
|
112
|
+
viewport: parseWebViewViewport(opts.viewport),
|
|
113
|
+
profileDir: opts.profile ? resolve(opts.profile) : undefined,
|
|
114
|
+
chromePath: opts.chromePath ? resolve(opts.chromePath) : undefined,
|
|
115
|
+
browserArgs: opts.browserArg,
|
|
116
|
+
timeoutMs: parseNonNegativeInteger(opts.timeout, "--timeout", false),
|
|
117
|
+
waitFor: opts.waitFor,
|
|
118
|
+
typeSteps: opts.type.map(parseWebViewTypeStep),
|
|
119
|
+
clicks: opts.click,
|
|
120
|
+
presses: opts.press,
|
|
121
|
+
evaluate: opts.evaluate,
|
|
122
|
+
settleMs: parseNonNegativeInteger(opts.settle, "--settle", true),
|
|
123
|
+
screenshotPath: opts.screenshot ? resolve(opts.screenshot) : undefined,
|
|
124
|
+
captureHtml: opts.html === true,
|
|
125
|
+
pace: commandPaceGate(opts.pace !== false, (message) => emit.log(message, "info")),
|
|
126
|
+
userAgent: opts.userAgent,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (opts.json) {
|
|
130
|
+
emit.data(result);
|
|
131
|
+
} else if (opts.html) {
|
|
132
|
+
emit.text(result.html?.endsWith("\n") ? result.html : `${result.html ?? ""}\n`);
|
|
133
|
+
} else if (opts.evaluate !== undefined) {
|
|
134
|
+
const value =
|
|
135
|
+
typeof result.evaluation === "string"
|
|
136
|
+
? result.evaluation
|
|
137
|
+
: JSON.stringify(result.evaluation, null, 2);
|
|
138
|
+
emit.text(`${value ?? "undefined"}\n`);
|
|
139
|
+
} else {
|
|
140
|
+
emit.text(result.text.endsWith("\n") ? result.text : `${result.text}\n`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function collect(value: string, previous: string[]): string[] {
|
|
145
|
+
return [...previous, value];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isBackend(value: string): value is WebViewBackend {
|
|
149
|
+
return value === "auto" || value === "chrome" || value === "webkit";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function parseNonNegativeInteger(value: string, flag: string, allowZero: boolean): number {
|
|
153
|
+
if (!/^\d+$/.test(value)) throw new Error(`${flag} expects an integer, got "${value}".`);
|
|
154
|
+
const parsed = Number.parseInt(value, 10);
|
|
155
|
+
if ((!allowZero && parsed < 1) || (allowZero && parsed < 0)) {
|
|
156
|
+
throw new Error(`${flag} must be ${allowZero ? "zero or greater" : "greater than zero"}.`);
|
|
157
|
+
}
|
|
158
|
+
return parsed;
|
|
159
|
+
}
|
package/src/core/adapter.ts
CHANGED
|
@@ -1,5 +1,106 @@
|
|
|
1
1
|
/** First-party harness adapters supported by Harnery's canonical V3 ledger. */
|
|
2
|
-
export type Adapter = "claude-code" | "cursor" | "codex";
|
|
2
|
+
export type Adapter = "claude-code" | "cursor" | "codex" | "opencode";
|
|
3
3
|
|
|
4
4
|
/** Canonical producer classes used by V3 provenance. */
|
|
5
5
|
export type EventSource = "agent-hooks" | "agent-coord" | "user" | "system";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Runtime census of the workflow adapters. Adding a member to `Adapter`
|
|
9
|
+
* without listing it here is a compile error (see `adaptersComplete`), so a
|
|
10
|
+
* new adapter can never slip past the shared normalizer.
|
|
11
|
+
*/
|
|
12
|
+
export const ADAPTERS = [
|
|
13
|
+
"claude-code",
|
|
14
|
+
"cursor",
|
|
15
|
+
"codex",
|
|
16
|
+
"opencode",
|
|
17
|
+
] as const satisfies readonly Adapter[];
|
|
18
|
+
|
|
19
|
+
type MissingAdapter = Exclude<Adapter, (typeof ADAPTERS)[number]>;
|
|
20
|
+
const adaptersComplete: [MissingAdapter] extends [never] ? true : never = true;
|
|
21
|
+
void adaptersComplete;
|
|
22
|
+
|
|
23
|
+
/** Every adapter falls back to this identity when a platform value carries no usable evidence. */
|
|
24
|
+
export const DEFAULT_ADAPTER: Adapter = "claude-code";
|
|
25
|
+
|
|
26
|
+
/** Why a platform value could not be normalized to a known adapter. */
|
|
27
|
+
export type AdapterFallbackReason = "missing" | "unknown";
|
|
28
|
+
|
|
29
|
+
export interface AdapterFallback {
|
|
30
|
+
/** The raw value that was normalized. */
|
|
31
|
+
value: unknown;
|
|
32
|
+
/** `missing` for null, undefined, or an empty string; `unknown` for anything else. */
|
|
33
|
+
reason: AdapterFallbackReason;
|
|
34
|
+
/** The adapter that was substituted. */
|
|
35
|
+
fallback: string;
|
|
36
|
+
/** Caller-supplied label naming the code path, for the diagnostic line. */
|
|
37
|
+
context?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AdapterFallbackOptions {
|
|
41
|
+
/** Names the caller in the diagnostic line, e.g. `"claim-conflict"`. */
|
|
42
|
+
context?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Receives every fallback. The default writes one stderr line per unknown
|
|
45
|
+
* value; a missing value is a pre-heartbeat state rather than a
|
|
46
|
+
* misattribution, so it is passed here but not printed.
|
|
47
|
+
*/
|
|
48
|
+
onFallback?: (fallback: AdapterFallback) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Generic membership normalizer shared by every adapter census. Returns the
|
|
53
|
+
* matching id or `null`; it never substitutes a default.
|
|
54
|
+
*/
|
|
55
|
+
export function normalizeAdapterId<T extends string>(
|
|
56
|
+
value: unknown,
|
|
57
|
+
census: readonly T[],
|
|
58
|
+
): T | null {
|
|
59
|
+
if (typeof value !== "string") return null;
|
|
60
|
+
return (census as readonly string[]).includes(value) ? (value as T) : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Normalize with an explicit, reported fallback. Every caller that must end up
|
|
65
|
+
* with *some* adapter routes through here so the substitution is visible.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveAdapterId<T extends string>(
|
|
68
|
+
value: unknown,
|
|
69
|
+
census: readonly T[],
|
|
70
|
+
fallback: T,
|
|
71
|
+
options: AdapterFallbackOptions = {},
|
|
72
|
+
): T {
|
|
73
|
+
const normalized = normalizeAdapterId(value, census);
|
|
74
|
+
if (normalized !== null) return normalized;
|
|
75
|
+
const reason: AdapterFallbackReason =
|
|
76
|
+
value === undefined || value === null || value === "" ? "missing" : "unknown";
|
|
77
|
+
(options.onFallback ?? reportAdapterFallback)({
|
|
78
|
+
value,
|
|
79
|
+
reason,
|
|
80
|
+
fallback,
|
|
81
|
+
context: options.context,
|
|
82
|
+
});
|
|
83
|
+
return fallback;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Default fallback sink: one stderr line for an unrecognized platform value. */
|
|
87
|
+
export function reportAdapterFallback(fallback: AdapterFallback): void {
|
|
88
|
+
if (fallback.reason !== "unknown") return;
|
|
89
|
+
const where = fallback.context ? ` (${fallback.context})` : "";
|
|
90
|
+
process.stderr.write(
|
|
91
|
+
`harnery: unknown adapter platform ${JSON.stringify(fallback.value)}${where}; treating it as ${fallback.fallback}\n`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Strict workflow-adapter normalizer: a known id or `null`. */
|
|
96
|
+
export function normalizeAdapter(value: unknown): Adapter | null {
|
|
97
|
+
return normalizeAdapterId(value, ADAPTERS);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Heartbeat `platform` (or any adapter-shaped string) to a workflow adapter,
|
|
102
|
+
* substituting `DEFAULT_ADAPTER` and reporting the substitution.
|
|
103
|
+
*/
|
|
104
|
+
export function adapterFromPlatform(value: unknown, options?: AdapterFallbackOptions): Adapter {
|
|
105
|
+
return resolveAdapterId(value, ADAPTERS, DEFAULT_ADAPTER, options);
|
|
106
|
+
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "node:fs";
|
|
23
23
|
import { resolve } from "node:path";
|
|
24
24
|
import { monorepoRoot } from "../agents/coord-client.ts";
|
|
25
|
+
import { stateDirMode, stateFileMode } from "../storage/modes.ts";
|
|
25
26
|
import { stableDigest } from "../workflow/durable-record.ts";
|
|
26
27
|
import type { AdapterId, AdapterProfile, CapabilitySupport } from "./types.ts";
|
|
27
28
|
|
|
@@ -107,12 +108,12 @@ export function writeAttestation(
|
|
|
107
108
|
opts: AttestationStoreOptions = {},
|
|
108
109
|
): string {
|
|
109
110
|
const path = attestationPath(record.adapter, opts);
|
|
110
|
-
mkdirSync(attestationsDir(opts), { recursive: true, mode:
|
|
111
|
+
mkdirSync(attestationsDir(opts), { recursive: true, mode: stateDirMode() });
|
|
111
112
|
const temporary = `${path}.tmp-${process.pid}`;
|
|
112
113
|
try {
|
|
113
114
|
writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\n`, {
|
|
114
115
|
encoding: "utf8",
|
|
115
|
-
mode:
|
|
116
|
+
mode: stateFileMode(),
|
|
116
117
|
});
|
|
117
118
|
renameSync(temporary, path);
|
|
118
119
|
} catch (error) {
|
|
@@ -106,7 +106,7 @@ export const BUILTIN_ADAPTER_PROFILES = {
|
|
|
106
106
|
authModel: "own-auth",
|
|
107
107
|
modelFamily: "multi",
|
|
108
108
|
effortValues: [],
|
|
109
|
-
verified: { date: "2026-
|
|
109
|
+
verified: { date: "2026-09-20", version: "2026.09.18-9a7762b" },
|
|
110
110
|
capabilities: capabilities({
|
|
111
111
|
effortSelection: unsupported(
|
|
112
112
|
"Cursor embeds effort in some parameterized model ids; Harnery does not rewrite model ids.",
|
|
@@ -114,6 +114,10 @@ export const BUILTIN_ADAPTER_PROFILES = {
|
|
|
114
114
|
maxTurns: unsupported("cursor-agent exposes no turn-ceiling flag."),
|
|
115
115
|
sessionId: supported("Read from the JSON result envelope."),
|
|
116
116
|
cost: unsupported("The JSON result envelope carries no cost."),
|
|
117
|
+
// Still unknown on 2026-09-20 against cursor-agent 2026.09.18: the live
|
|
118
|
+
// `cursor_composer_context_percent` reader serves IDE hook sessions from
|
|
119
|
+
// the composer `state.vscdb`, and headless `cursor-agent` children (this
|
|
120
|
+
// workflow adapter) leave no composer row and no usage in the JSON result.
|
|
117
121
|
contextTelemetry: unknown("No stable Cursor context-usage payload has been certified."),
|
|
118
122
|
preCompactionSignal: supported("Cursor preCompact is wired to a durable checkpoint."),
|
|
119
123
|
postCompactionSignal: unsupported(
|
|
@@ -121,6 +125,43 @@ export const BUILTIN_ADAPTER_PROFILES = {
|
|
|
121
125
|
),
|
|
122
126
|
}),
|
|
123
127
|
},
|
|
128
|
+
opencode: {
|
|
129
|
+
id: "opencode",
|
|
130
|
+
displayName: "OpenCode",
|
|
131
|
+
binary: "opencode",
|
|
132
|
+
installHint: "curl -fsSL https://opencode.ai/install | bash",
|
|
133
|
+
loginHint: "opencode auth login",
|
|
134
|
+
apiKeyEnv: "OPENCODE_API_KEY",
|
|
135
|
+
integrationMode: "cli-subprocess",
|
|
136
|
+
authModel: "own-auth",
|
|
137
|
+
modelFamily: "multi",
|
|
138
|
+
// Model reasoning is selected through the `provider/model#variant` id;
|
|
139
|
+
// OpenCode exposes no separate effort dial for Harnery to map.
|
|
140
|
+
effortValues: [],
|
|
141
|
+
verified: { date: "2026-09-20", version: "opencode v2.0.11" },
|
|
142
|
+
capabilities: capabilities({
|
|
143
|
+
effortSelection: unsupported(
|
|
144
|
+
"OpenCode selects reasoning through the model id variant; there is no separate effort flag.",
|
|
145
|
+
),
|
|
146
|
+
maxTurns: unsupported("`opencode run` exposes no turn-ceiling flag."),
|
|
147
|
+
sessionId: supported("Read from the `sessionID` on every `--format json` NDJSON line."),
|
|
148
|
+
cost: unsupported(
|
|
149
|
+
"`opencode run --format json` streams parts without a final cost; cost lives in `session export`.",
|
|
150
|
+
),
|
|
151
|
+
// Tool evidence is delivered on the event axis by the Harnery OpenCode
|
|
152
|
+
// plugin, not by the final-result workflow spawn, which retains no tool
|
|
153
|
+
// events (as with Codex and Cursor).
|
|
154
|
+
contextTelemetry: unknown(
|
|
155
|
+
"OpenCode stores context in SQLite; no context-window bridge is certified yet.",
|
|
156
|
+
),
|
|
157
|
+
preCompactionSignal: supported(
|
|
158
|
+
'OpenCode session.hook("compaction") is bridged to a durable checkpoint.',
|
|
159
|
+
),
|
|
160
|
+
postCompactionSignal: unsupported(
|
|
161
|
+
"No post-compact hook is wired; OpenCode post-compaction recovery is not yet certified.",
|
|
162
|
+
),
|
|
163
|
+
}),
|
|
164
|
+
},
|
|
124
165
|
} as const satisfies Record<string, AdapterProfile>;
|
|
125
166
|
|
|
126
167
|
export type BuiltinAdapterId = keyof typeof BUILTIN_ADAPTER_PROFILES;
|
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
cursorSpawner,
|
|
14
14
|
normalizeCursorResult,
|
|
15
15
|
} from "../workflow/spawn-cursor.ts";
|
|
16
|
+
import {
|
|
17
|
+
buildOpenCodeInvocation,
|
|
18
|
+
normalizeOpenCodeResult,
|
|
19
|
+
openCodeSpawner,
|
|
20
|
+
} from "../workflow/spawn-opencode.ts";
|
|
16
21
|
import {
|
|
17
22
|
BUILTIN_ADAPTER_IDS,
|
|
18
23
|
BUILTIN_ADAPTER_PROFILES,
|
|
@@ -85,6 +90,38 @@ const BUILTIN_ADAPTERS: Record<BuiltinAdapterId, Adapter> = {
|
|
|
85
90
|
},
|
|
86
91
|
},
|
|
87
92
|
},
|
|
93
|
+
opencode: {
|
|
94
|
+
profile: BUILTIN_ADAPTER_PROFILES.opencode,
|
|
95
|
+
spawn: openCodeSpawner,
|
|
96
|
+
buildInvocation: buildOpenCodeInvocation,
|
|
97
|
+
normalizeResult: normalizeOpenCodeResult,
|
|
98
|
+
fixture: {
|
|
99
|
+
// `opencode run --format json` streams NDJSON: a step-start line then one
|
|
100
|
+
// or more text parts, each carrying the same sessionID.
|
|
101
|
+
raw: {
|
|
102
|
+
stdout: [
|
|
103
|
+
JSON.stringify({
|
|
104
|
+
type: "step_start",
|
|
105
|
+
sessionID: "opencode-bench-session",
|
|
106
|
+
part: { type: "step-start" },
|
|
107
|
+
}),
|
|
108
|
+
JSON.stringify({
|
|
109
|
+
type: "text",
|
|
110
|
+
sessionID: "opencode-bench-session",
|
|
111
|
+
part: { type: "text", text: "HARNERY_BENCH_OK" },
|
|
112
|
+
}),
|
|
113
|
+
].join("\n"),
|
|
114
|
+
stderr: "",
|
|
115
|
+
exitCode: 0,
|
|
116
|
+
durationMs: 11,
|
|
117
|
+
},
|
|
118
|
+
expected: {
|
|
119
|
+
ok: true,
|
|
120
|
+
text: "HARNERY_BENCH_OK",
|
|
121
|
+
sessionId: "opencode-bench-session",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
88
125
|
};
|
|
89
126
|
|
|
90
127
|
/** Mutable registry for embedders, initialized empty unless adapters are
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { spawnSync } from "node:child_process";
|
|
14
|
-
import type
|
|
14
|
+
import { type EventAdapterIdV3, eventAdapterIdV3FromPlatform } from "../events/v3/adapter-id.ts";
|
|
15
15
|
import type { LiveCoordinationObservationV3 } from "../events/v3/live-observation.ts";
|
|
16
16
|
import { coordBinPath } from "./coord-bin.ts";
|
|
17
17
|
import { resolveCoordRoot } from "./coord-client.ts";
|
|
@@ -94,10 +94,7 @@ export function emitEventV3(input: EventV3EmitInput): boolean {
|
|
|
94
94
|
* Normalize the heartbeat's `platform` field to the canonical Adapter type.
|
|
95
95
|
*/
|
|
96
96
|
export function normalizeAdapter(platform: string | undefined): EventAdapterIdV3 {
|
|
97
|
-
|
|
98
|
-
if (platform === "codex") return "codex";
|
|
99
|
-
if (platform === "openclaw") return "openclaw";
|
|
100
|
-
return "claude-code";
|
|
97
|
+
return eventAdapterIdV3FromPlatform(platform, { context: "canonical-emit" });
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
/** Producer joins use the private native session ID, never its V3 fingerprint. */
|
package/src/core/agents/cli.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { appendFile, mkdir } from "node:fs/promises";
|
|
|
13
13
|
import { hostname } from "node:os";
|
|
14
14
|
import { dirname, join, resolve } from "node:path";
|
|
15
15
|
import { coordEnv } from "../../lib/env.ts";
|
|
16
|
+
import { normalizeAdapter, adapterFromPlatform as sharedAdapterFromPlatform } from "../adapter.ts";
|
|
17
|
+
import { resolveBinName } from "../config.ts";
|
|
16
18
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
|
|
17
19
|
import { acquireNoClobberLease } from "../workflow/workspaces/leases.ts";
|
|
18
20
|
|
|
@@ -166,12 +168,6 @@ async function handleProject(root: string, rest: string[]): Promise<number> {
|
|
|
166
168
|
return view.authority_safe ? 0 : 1;
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
|
|
170
|
-
if (platform === "cursor") return "cursor";
|
|
171
|
-
if (platform === "codex") return "codex";
|
|
172
|
-
return "claude-code";
|
|
173
|
-
}
|
|
174
|
-
|
|
175
171
|
const coordinationRetryCell = new Int32Array(new SharedArrayBuffer(4));
|
|
176
172
|
const COORDINATION_RETRY_ATTEMPTS = 240;
|
|
177
173
|
const COORDINATION_RETRY_DELAY_MS = 25;
|
|
@@ -281,7 +277,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
281
277
|
coordRoot: root,
|
|
282
278
|
owner,
|
|
283
279
|
nativeSessionId: before?.session_id ?? owner,
|
|
284
|
-
adapter:
|
|
280
|
+
adapter: sharedAdapterFromPlatform(before?.platform, { context: "agent-coord" }),
|
|
285
281
|
task,
|
|
286
282
|
});
|
|
287
283
|
break;
|
|
@@ -312,9 +308,14 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
312
308
|
hb = writer.setAssignedNameCache(root, owner, name, "session");
|
|
313
309
|
}
|
|
314
310
|
} catch (error) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
);
|
|
311
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
312
|
+
// A missing heartbeat here means the session's generation ended
|
|
313
|
+
// while its process lived on. Name the reopen command (ADR 0088);
|
|
314
|
+
// set-task is usually the first thing such a session runs.
|
|
315
|
+
const hint = detail.includes("heartbeat_missing:")
|
|
316
|
+
? `; run \`${resolveBinName(root)} agents lifecycle active\` to open a fresh generation for this continuing session`
|
|
317
|
+
: "";
|
|
318
|
+
process.stderr.write(`agent-coord set-task: V3 authority refused (${detail})${hint}\n`);
|
|
318
319
|
return 1;
|
|
319
320
|
}
|
|
320
321
|
if (!hb) {
|
|
@@ -347,7 +348,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
347
348
|
coordRoot: root,
|
|
348
349
|
owner,
|
|
349
350
|
nativeSessionId: before?.session_id ?? owner,
|
|
350
|
-
adapter:
|
|
351
|
+
adapter: sharedAdapterFromPlatform(before?.platform, { context: "agent-coord" }),
|
|
351
352
|
operation: "released",
|
|
352
353
|
path,
|
|
353
354
|
access: "write",
|
|
@@ -408,7 +409,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
408
409
|
coordRoot: root,
|
|
409
410
|
owner,
|
|
410
411
|
nativeSessionId: sessionId ?? owner,
|
|
411
|
-
adapter:
|
|
412
|
+
adapter: sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
|
|
412
413
|
});
|
|
413
414
|
}
|
|
414
415
|
const { repairLiveCoordinationHeartbeat } = await import(
|
|
@@ -418,7 +419,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
418
419
|
root,
|
|
419
420
|
owner,
|
|
420
421
|
sessionId ?? owner,
|
|
421
|
-
|
|
422
|
+
sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
|
|
422
423
|
model,
|
|
423
424
|
);
|
|
424
425
|
} catch (error) {
|
|
@@ -453,7 +454,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
453
454
|
root,
|
|
454
455
|
owner,
|
|
455
456
|
sessionId ?? owner,
|
|
456
|
-
|
|
457
|
+
sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
|
|
457
458
|
);
|
|
458
459
|
if (!heartbeat) {
|
|
459
460
|
throw new Error("authoritative coordination cache could not be materialized");
|
|
@@ -471,7 +472,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
471
472
|
root,
|
|
472
473
|
owner,
|
|
473
474
|
sessionId ?? owner,
|
|
474
|
-
|
|
475
|
+
sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
|
|
475
476
|
);
|
|
476
477
|
process.stdout.write(
|
|
477
478
|
`${JSON.stringify({
|
|
@@ -875,7 +876,7 @@ async function handleEmitEvent(root: string, rest: string[]): Promise<number> {
|
|
|
875
876
|
const eventType = args.type;
|
|
876
877
|
const instanceId = args.owner;
|
|
877
878
|
const sessionId = args.session;
|
|
878
|
-
const adapter = args.adapter
|
|
879
|
+
const adapter = normalizeAdapter(args.adapter) ?? undefined;
|
|
879
880
|
const dataJson = args["data-stdin"] === "true" ? await readStdin() : "";
|
|
880
881
|
|
|
881
882
|
if (!eventType || !instanceId || !sessionId || !adapter || !dataJson) {
|
|
@@ -1063,7 +1064,7 @@ async function handleGitHook(fallbackRoot: string, rest: string[]): Promise<numb
|
|
|
1063
1064
|
owner,
|
|
1064
1065
|
subject: hit.instance_id,
|
|
1065
1066
|
nativeSessionId: hit.session_id ?? owner,
|
|
1066
|
-
adapter:
|
|
1067
|
+
adapter: sharedAdapterFromPlatform(hit.platform, { context: "agent-coord" }),
|
|
1067
1068
|
operation: "released",
|
|
1068
1069
|
path,
|
|
1069
1070
|
access: "write",
|
|
@@ -527,6 +527,10 @@ const SESSION_ID_ENV_VARS = [
|
|
|
527
527
|
"CURSOR_CONVERSATION_ID",
|
|
528
528
|
"CODEX_SESSION_ID",
|
|
529
529
|
"CODEX_THREAD_ID",
|
|
530
|
+
// OpenCode exports no session id into the tool shell; the Harnery OpenCode
|
|
531
|
+
// plugin stamps this via shell.hook("create.before") so a coord CLI run as a
|
|
532
|
+
// Bash tool recovers its identity.
|
|
533
|
+
"OPENCODE_SESSION_ID",
|
|
530
534
|
] as const;
|
|
531
535
|
|
|
532
536
|
/** Read normalized candidates from the first non-empty adapter session-id env var. */
|
|
@@ -637,10 +641,12 @@ export function resolveSingleActiveOwner(root: string): string | null {
|
|
|
637
641
|
return live.size === 1 ? [...live][0]! : null;
|
|
638
642
|
}
|
|
639
643
|
|
|
640
|
-
function adapterCandidatesFromEnv(): Array<"claude-code" | "codex" | "cursor"> {
|
|
644
|
+
function adapterCandidatesFromEnv(): Array<"claude-code" | "codex" | "cursor" | "opencode"> {
|
|
641
645
|
const value = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim();
|
|
642
|
-
if (value === "claude-code" || value === "codex" || value === "cursor"
|
|
643
|
-
|
|
646
|
+
if (value === "claude-code" || value === "codex" || value === "cursor" || value === "opencode") {
|
|
647
|
+
return [value];
|
|
648
|
+
}
|
|
649
|
+
return ["claude-code", "codex", "cursor", "opencode"];
|
|
644
650
|
}
|
|
645
651
|
|
|
646
652
|
function nativeOwnerForV3Instance(root: string, instanceId: string): string {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { spawnSync } from "node:child_process";
|
|
18
18
|
import { existsSync } from "node:fs";
|
|
19
19
|
import { join } from "node:path";
|
|
20
|
+
import { adapterFromPlatform as sharedAdapterFromPlatform } from "../../adapter.ts";
|
|
20
21
|
import { recordLiveClaimChangeV3 } from "../live-authority-v3.ts";
|
|
21
22
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.ts";
|
|
22
23
|
|
|
@@ -248,7 +249,7 @@ function acquireClaimThroughLedger(coordRoot: string, req: ClaimRequest): boolea
|
|
|
248
249
|
coordRoot,
|
|
249
250
|
owner: req.instance_id,
|
|
250
251
|
nativeSessionId: req.session_id ?? owner?.session_id ?? req.instance_id,
|
|
251
|
-
adapter:
|
|
252
|
+
adapter: sharedAdapterFromPlatform(owner?.platform, { context: "claim-conflict" }),
|
|
252
253
|
operation: "acquired",
|
|
253
254
|
path: req.path,
|
|
254
255
|
access: "write",
|
|
@@ -272,7 +273,7 @@ function releaseClaimThroughLedger(
|
|
|
272
273
|
owner: actor.instance_id,
|
|
273
274
|
subject: subjectInstanceId,
|
|
274
275
|
nativeSessionId: actor.session_id ?? owner?.session_id ?? actor.instance_id,
|
|
275
|
-
adapter:
|
|
276
|
+
adapter: sharedAdapterFromPlatform(owner?.platform, { context: "claim-conflict" }),
|
|
276
277
|
operation: "released",
|
|
277
278
|
path: relPath,
|
|
278
279
|
access: "write",
|
|
@@ -283,12 +284,6 @@ function releaseClaimThroughLedger(
|
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
|
|
287
|
-
if (platform === "cursor") return "cursor";
|
|
288
|
-
if (platform === "codex") return "codex";
|
|
289
|
-
return "claude-code";
|
|
290
|
-
}
|
|
291
|
-
|
|
292
287
|
function authorityUnavailable(path: string, operation: string): VerdictResult {
|
|
293
288
|
return {
|
|
294
289
|
allow: false,
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { spawnSync } from "node:child_process";
|
|
24
24
|
import { appendFileSync, existsSync } from "node:fs";
|
|
25
25
|
import { basename, dirname, join } from "node:path";
|
|
26
|
+
import { adapterFromPlatform as sharedAdapterFromPlatform } from "../../adapter.ts";
|
|
26
27
|
import { coordFreshnessSeconds } from "../../config.ts";
|
|
27
28
|
import { resolveOwner } from "../../hooks/resolve/owner.ts";
|
|
28
29
|
import { recordLiveClaimChangeV3 } from "../live-authority-v3.ts";
|
|
@@ -232,7 +233,7 @@ function retainBlockedStagedPaths(
|
|
|
232
233
|
coordRoot,
|
|
233
234
|
owner: instanceId,
|
|
234
235
|
nativeSessionId: self.session_id ?? sessionId,
|
|
235
|
-
adapter:
|
|
236
|
+
adapter: sharedAdapterFromPlatform(self.platform, { context: "commit-conflict" }),
|
|
236
237
|
operation: "acquired",
|
|
237
238
|
path,
|
|
238
239
|
access: "write",
|
|
@@ -244,12 +245,6 @@ function retainBlockedStagedPaths(
|
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
|
|
248
|
-
if (platform === "cursor") return "cursor";
|
|
249
|
-
if (platform === "codex") return "codex";
|
|
250
|
-
return "claude-code";
|
|
251
|
-
}
|
|
252
|
-
|
|
253
248
|
function findOverlap(
|
|
254
249
|
staged: string,
|
|
255
250
|
files: readonly string[],
|
|
@@ -32,6 +32,8 @@ export interface StopHookRequest {
|
|
|
32
32
|
turn_window?: { start_ms: number; end_ms: number };
|
|
33
33
|
stop_hook_active?: boolean;
|
|
34
34
|
status_box_present_strict?: boolean;
|
|
35
|
+
/** Current observation overrides an older failed terminal during remediation. */
|
|
36
|
+
session_name_observation_unavailable?: boolean;
|
|
35
37
|
bypass?: boolean;
|
|
36
38
|
workflow_child?: boolean;
|
|
37
39
|
}
|
|
@@ -188,7 +190,7 @@ export function evaluateStopHookV3Events(
|
|
|
188
190
|
|
|
189
191
|
// Claude Code can prove session-name presentation from assistant-only text.
|
|
190
192
|
// Cursor cannot expose that text and Codex returned observe-only above.
|
|
191
|
-
if (req.adapter !== "cursor") {
|
|
193
|
+
if (req.adapter !== "cursor" && !req.session_name_observation_unavailable) {
|
|
192
194
|
const naming = turnTerminals
|
|
193
195
|
.map((event) => observedNaming(event.payload.ritual?.session_name))
|
|
194
196
|
.filter((value): value is { required: boolean; present: boolean } => value !== undefined);
|