harnery 0.40.0 → 0.42.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 +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +34 -20
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse-ai.d.ts.map +1 -1
- package/dist/commands/browse-ai.js +4 -0
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +388 -45
- package/dist/commands/checkpoint.js +8 -12
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- 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/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +2 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +58 -3
- package/dist/commands/fetch.d.ts.map +1 -1
- package/dist/commands/fetch.js +10 -0
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +21 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +271 -109
- 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/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- 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 +29 -3
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +53 -1
- package/dist/core/adapters/registry.d.ts.map +1 -1
- package/dist/core/adapters/registry.js +33 -0
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- 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 +25 -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/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- 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 +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +61 -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 +11 -3
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +526 -52
- package/dist/core/config.d.ts +73 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +142 -3
- 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 +5 -4
- 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 +125 -101
- package/dist/core/events/v3/projection.js +9 -1
- 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 +46 -1
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +36 -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/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +52 -8
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +133 -12
- package/dist/core/hooks/cli.js +676 -531
- 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/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- 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/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -1
- package/dist/core/storage/atomic-json.js +7 -3
- package/dist/core/storage/builtins.js +1 -0
- 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/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- 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/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- 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 +135 -0
- package/dist/core/workflow/transcript.d.ts.map +1 -1
- package/dist/core/workflow/transcript.js +3 -2
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- 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/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -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/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- 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 +3 -2
- 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/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- 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/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- 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/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- 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/instructions/templates.js +8 -8
- 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/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- 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 +34 -10
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- 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 +16 -4
- package/schemas/config.schema.json +104 -0
- package/schemas/event-v3.schema.json +164 -0
- package/src/commander.ts +26 -0
- package/src/commands/agents.ts +47 -18
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse-ai.ts +8 -0
- package/src/commands/browse.ts +570 -48
- package/src/commands/checkpoint.ts +7 -9
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/deinit.ts +9 -4
- package/src/commands/disk.ts +114 -0
- package/src/commands/docs.ts +2 -0
- package/src/commands/doctor.ts +63 -3
- package/src/commands/fetch.ts +14 -0
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +334 -122
- package/src/commands/instructions.ts +8 -4
- package/src/commands/ledger-v3.ts +2 -1
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- 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 +63 -1
- package/src/core/adapters/registry.ts +37 -0
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/canonical-emit.ts +2 -5
- package/src/core/agents/cli.ts +26 -17
- package/src/core/agents/coord-client.ts +9 -3
- package/src/core/agents/render/session-context.ts +1 -0
- 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 +74 -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 +12 -3
- package/src/core/artifacts/index.ts +701 -47
- package/src/core/config.ts +206 -4
- package/src/core/context/index.ts +2 -1
- package/src/core/conversations/archive.ts +4 -3
- package/src/core/diagnostics/bundle.ts +5 -3
- 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 +139 -114
- package/src/core/events/v3/projection.ts +11 -1
- 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 +72 -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/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +198 -16
- package/src/core/hooks/cli.ts +776 -539
- 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/run-markers.ts +132 -0
- 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/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/atomic-json.ts +7 -3
- package/src/core/storage/builtins.ts +1 -0
- 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/engine.ts +38 -1
- package/src/core/workflow/live-session-v3.ts +2 -3
- package/src/core/workflow/run-state.ts +3 -2
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +153 -0
- package/src/core/workflow/transcript.ts +3 -2
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- 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/batch-steps.ts +79 -0
- package/src/lib/browser/client.ts +147 -9
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/identity.ts +64 -0
- package/src/lib/browser/index.ts +16 -1
- package/src/lib/browser/launch-args.ts +53 -1
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/user-agent.ts +130 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/docs-metadata-managed.ts +21 -0
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +2 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/pace/index.ts +14 -0
- package/src/lib/pace/pace.ts +370 -0
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/forward-headers.ts +16 -0
- package/src/lib/tunnel/gate.ts +39 -10
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
- package/src/lib/webview/index.ts +13 -0
- package/src/lib/webview/webview.ts +457 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide, per launch, whether a workflow worker runs without its vendor sandbox
|
|
3
|
+
* (ADR 0192).
|
|
4
|
+
*
|
|
5
|
+
* A worker gets full access only when three independent things agree:
|
|
6
|
+
*
|
|
7
|
+
* 1. The host project enabled it. Harnery ships it off.
|
|
8
|
+
* 2. The session that launched the run can itself be shown to have full
|
|
9
|
+
* access. A sandboxed parent never hands more access to a child than it
|
|
10
|
+
* holds, and a parent whose access cannot be established counts as
|
|
11
|
+
* sandboxed.
|
|
12
|
+
* 3. The worker's model and effort meet a floor the host configured for that
|
|
13
|
+
* adapter.
|
|
14
|
+
*
|
|
15
|
+
* Every other case keeps the ordinary sandboxed projection, byte for byte. The
|
|
16
|
+
* decision is recorded on every launch, so a run states what each worker was
|
|
17
|
+
* allowed to do and why.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
closeSync,
|
|
22
|
+
existsSync,
|
|
23
|
+
openSync,
|
|
24
|
+
readdirSync,
|
|
25
|
+
readFileSync,
|
|
26
|
+
readSync,
|
|
27
|
+
statSync,
|
|
28
|
+
} from "node:fs";
|
|
29
|
+
import { homedir } from "node:os";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { builtinAdapterProfile } from "../adapters/profiles.ts";
|
|
32
|
+
import type { ParentAccessEvidence, SpawnFilesystemPolicy, WorkerAccessDecision } from "./types.ts";
|
|
33
|
+
|
|
34
|
+
/** One model an adapter's worker may run unsandboxed on. `minEffort` is on the
|
|
35
|
+
* adapter's declared effort scale; it is required for an adapter that has one
|
|
36
|
+
* and refused for an adapter that does not (whose effort lives in the model id). */
|
|
37
|
+
export interface WorkerFullAccessFloor {
|
|
38
|
+
model: string;
|
|
39
|
+
minEffort?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface WorkerFullAccessPolicy {
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
floors: Readonly<Record<string, readonly WorkerFullAccessFloor[]>>;
|
|
45
|
+
/** Set when the configured value could not be validated. An invalid policy
|
|
46
|
+
* is disabled as a whole rather than partially honored. */
|
|
47
|
+
invalid?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const DISABLED_WORKER_FULL_ACCESS: WorkerFullAccessPolicy = Object.freeze({
|
|
51
|
+
enabled: false,
|
|
52
|
+
floors: Object.freeze({}),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
56
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function invalid(message: string): WorkerFullAccessPolicy {
|
|
60
|
+
return { enabled: false, floors: {}, invalid: message };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate the raw `workflow.workerFullAccess` config value.
|
|
65
|
+
*
|
|
66
|
+
* Absent means disabled. Anything malformed disables the whole policy and says
|
|
67
|
+
* why, because honoring the valid half of a half-valid trust grant would hand
|
|
68
|
+
* out access the host never cleanly stated.
|
|
69
|
+
*/
|
|
70
|
+
export function parseWorkerFullAccessPolicy(raw: unknown): WorkerFullAccessPolicy {
|
|
71
|
+
if (raw === undefined || raw === null) return DISABLED_WORKER_FULL_ACCESS;
|
|
72
|
+
if (!isPlainObject(raw)) return invalid("workflow.workerFullAccess must be an object");
|
|
73
|
+
for (const key of Object.keys(raw)) {
|
|
74
|
+
if (key !== "enabled" && key !== "floors") {
|
|
75
|
+
return invalid(`workflow.workerFullAccess has unknown key ${JSON.stringify(key)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (raw.enabled !== undefined && typeof raw.enabled !== "boolean") {
|
|
79
|
+
return invalid("workflow.workerFullAccess.enabled must be a boolean");
|
|
80
|
+
}
|
|
81
|
+
const floorsRaw = raw.floors ?? {};
|
|
82
|
+
if (!isPlainObject(floorsRaw))
|
|
83
|
+
return invalid("workflow.workerFullAccess.floors must be an object");
|
|
84
|
+
const floors: Record<string, WorkerFullAccessFloor[]> = {};
|
|
85
|
+
for (const [adapter, entries] of Object.entries(floorsRaw)) {
|
|
86
|
+
const profile = builtinAdapterProfile(adapter);
|
|
87
|
+
if (!profile) {
|
|
88
|
+
return invalid(
|
|
89
|
+
`workflow.workerFullAccess.floors names unknown adapter ${JSON.stringify(adapter)}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(entries)) {
|
|
93
|
+
return invalid(`workflow.workerFullAccess.floors.${adapter} must be an array`);
|
|
94
|
+
}
|
|
95
|
+
const parsed: WorkerFullAccessFloor[] = [];
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
if (!isPlainObject(entry)) {
|
|
98
|
+
return invalid(`workflow.workerFullAccess.floors.${adapter} entries must be objects`);
|
|
99
|
+
}
|
|
100
|
+
for (const key of Object.keys(entry)) {
|
|
101
|
+
if (key !== "model" && key !== "minEffort") {
|
|
102
|
+
return invalid(
|
|
103
|
+
`workflow.workerFullAccess.floors.${adapter} entry has unknown key ${JSON.stringify(key)}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const model = typeof entry.model === "string" ? entry.model.trim() : "";
|
|
108
|
+
if (!model) {
|
|
109
|
+
return invalid(`workflow.workerFullAccess.floors.${adapter} entry needs a model id`);
|
|
110
|
+
}
|
|
111
|
+
const minEffort = entry.minEffort;
|
|
112
|
+
if (profile.effortValues.length > 0) {
|
|
113
|
+
if (typeof minEffort !== "string" || !profile.effortValues.includes(minEffort)) {
|
|
114
|
+
return invalid(
|
|
115
|
+
`workflow.workerFullAccess.floors.${adapter} entry for ${model} needs minEffort, one of: ${profile.effortValues.join(", ")}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
parsed.push({ model, minEffort });
|
|
119
|
+
} else {
|
|
120
|
+
if (minEffort !== undefined) {
|
|
121
|
+
return invalid(
|
|
122
|
+
`workflow.workerFullAccess.floors.${adapter} has no effort scale; name the exact model id instead of minEffort`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
parsed.push({ model });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
floors[adapter] = parsed;
|
|
129
|
+
}
|
|
130
|
+
return { enabled: raw.enabled === true, floors };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface WorkerAccessInput {
|
|
134
|
+
adapter: string;
|
|
135
|
+
model?: string;
|
|
136
|
+
effort?: string;
|
|
137
|
+
policy: WorkerFullAccessPolicy;
|
|
138
|
+
/** The run's filesystem policy, if the host requested one. */
|
|
139
|
+
filesystemPolicy?: SpawnFilesystemPolicy;
|
|
140
|
+
/** Called at most when every cheaper condition already allows full access. */
|
|
141
|
+
parent: () => ParentAccessEvidence;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const sandboxed = (
|
|
145
|
+
reason: WorkerAccessDecision["reason"],
|
|
146
|
+
detail: string,
|
|
147
|
+
): WorkerAccessDecision => ({
|
|
148
|
+
mode: "sandboxed",
|
|
149
|
+
reason,
|
|
150
|
+
detail,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The access decision for one launch. Conditions are checked from the host's
|
|
155
|
+
* own configuration outward, so the recorded reason names the first thing that
|
|
156
|
+
* kept the sandbox in place.
|
|
157
|
+
*/
|
|
158
|
+
export function decideWorkerAccess(input: WorkerAccessInput): WorkerAccessDecision {
|
|
159
|
+
const { adapter, policy } = input;
|
|
160
|
+
if (policy.invalid) {
|
|
161
|
+
return sandboxed("config_invalid", `worker full access is off: ${policy.invalid}`);
|
|
162
|
+
}
|
|
163
|
+
if (!policy.enabled) {
|
|
164
|
+
return sandboxed("disabled", "the host has not enabled worker full access");
|
|
165
|
+
}
|
|
166
|
+
if (!builtinAdapterProfile(adapter)?.fullAccess) {
|
|
167
|
+
return sandboxed("adapter_unrepresentable", `${adapter} declares no full-access mode`);
|
|
168
|
+
}
|
|
169
|
+
if (input.filesystemPolicy?.mode === "read-only") {
|
|
170
|
+
// A read-only policy is a deliberate constraint on this run's children, not
|
|
171
|
+
// a sandbox limitation to route around.
|
|
172
|
+
return sandboxed("policy_read_only", "the run requested a read-only filesystem policy");
|
|
173
|
+
}
|
|
174
|
+
const floors = policy.floors[adapter] ?? [];
|
|
175
|
+
if (floors.length === 0) {
|
|
176
|
+
return sandboxed("model_not_listed", `no full-access floor is configured for ${adapter}`);
|
|
177
|
+
}
|
|
178
|
+
if (!input.model) {
|
|
179
|
+
return sandboxed(
|
|
180
|
+
"model_unset",
|
|
181
|
+
"the worker names no model, so the adapter default cannot be checked against a floor",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const matching = floors.filter((floor) => floor.model === input.model);
|
|
185
|
+
if (matching.length === 0) {
|
|
186
|
+
return sandboxed(
|
|
187
|
+
"model_not_listed",
|
|
188
|
+
`${input.model} is not on the ${adapter} full-access list`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const scale = builtinAdapterProfile(adapter)?.effortValues ?? [];
|
|
192
|
+
if (scale.length > 0) {
|
|
193
|
+
if (!input.effort) {
|
|
194
|
+
return sandboxed(
|
|
195
|
+
"effort_unset",
|
|
196
|
+
`the worker names no effort, so ${input.model} cannot be checked against its floor`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const rank = scale.indexOf(input.effort);
|
|
200
|
+
const floor = matching.find(
|
|
201
|
+
(entry) => entry.minEffort !== undefined && rank >= scale.indexOf(entry.minEffort),
|
|
202
|
+
);
|
|
203
|
+
if (rank < 0 || !floor) {
|
|
204
|
+
const needed = matching.map((entry) => entry.minEffort).join(" or ");
|
|
205
|
+
return sandboxed(
|
|
206
|
+
"effort_below_floor",
|
|
207
|
+
`${input.model} at effort ${input.effort} is below the ${needed} floor`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const parent = input.parent();
|
|
212
|
+
if (parent.state === "sandboxed") {
|
|
213
|
+
return sandboxed("parent_sandboxed", `the launching session is sandboxed: ${parent.detail}`);
|
|
214
|
+
}
|
|
215
|
+
if (parent.state !== "full-access") {
|
|
216
|
+
return sandboxed(
|
|
217
|
+
"parent_unknown",
|
|
218
|
+
`the launching session's access could not be established: ${parent.detail}`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const effortPart = input.effort ? ` at effort ${input.effort}` : "";
|
|
222
|
+
return {
|
|
223
|
+
mode: "full-access",
|
|
224
|
+
reason: "qualified",
|
|
225
|
+
detail: `${input.model}${effortPart} meets the ${adapter} floor and ${parent.detail}`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Parent access detection
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
export interface ParentAccessDeps {
|
|
234
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
235
|
+
platform: NodeJS.Platform;
|
|
236
|
+
homeDir: string;
|
|
237
|
+
/** Contents of /proc/self/status, or null when unreadable. */
|
|
238
|
+
procStatus: () => string | null;
|
|
239
|
+
/** Windows user profile roots visible from a WSL guest. */
|
|
240
|
+
windowsUserRoots: () => string[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function defaultDeps(): ParentAccessDeps {
|
|
244
|
+
return {
|
|
245
|
+
env: process.env,
|
|
246
|
+
platform: process.platform,
|
|
247
|
+
homeDir: homedir(),
|
|
248
|
+
procStatus: () => {
|
|
249
|
+
try {
|
|
250
|
+
return readFileSync("/proc/self/status", "utf8");
|
|
251
|
+
} catch {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
windowsUserRoots: () => {
|
|
256
|
+
try {
|
|
257
|
+
return readdirSync("/mnt/c/Users").map((name) => join("/mnt/c/Users", name));
|
|
258
|
+
} catch {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const nonEmpty = (value: string | undefined): string | undefined => {
|
|
266
|
+
const trimmed = value?.trim();
|
|
267
|
+
return trimmed ? trimmed : undefined;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
type Verdict = { state: ParentAccessEvidence["state"]; detail: string };
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Establish whether the session that launched this process runs without a
|
|
274
|
+
* sandbox. Only positive evidence counts.
|
|
275
|
+
*
|
|
276
|
+
* Every adapter session marker in the environment has to prove full access on
|
|
277
|
+
* its own. A nested launch (one agent CLI inside another) inherits both
|
|
278
|
+
* markers, and the outer session's sandbox would still bind the inner one, so
|
|
279
|
+
* one sandboxed or unprovable marker is enough to keep workers sandboxed.
|
|
280
|
+
*
|
|
281
|
+
* - Process confinement is checked first. On Linux, the Codex and Claude Code
|
|
282
|
+
* sandboxes both set `no_new_privs`, and Codex also exports
|
|
283
|
+
* `CODEX_SANDBOX*` variables. Either one means this process is confined.
|
|
284
|
+
* - Codex: the thread's rollout file must show the latest turn ran under the
|
|
285
|
+
* `danger-full-access` sandbox policy.
|
|
286
|
+
* - Claude Code: the session transcript's latest permission mode must be
|
|
287
|
+
* `bypassPermissions`. Claude Code's Bash sandbox is covered by the Linux
|
|
288
|
+
* confinement check; on other platforms it cannot be ruled out, so a Claude
|
|
289
|
+
* Code parent there is unknown.
|
|
290
|
+
* - Cursor and OpenCode record no readable access state, so they are unknown.
|
|
291
|
+
*/
|
|
292
|
+
export function detectParentAccess(deps: ParentAccessDeps = defaultDeps()): ParentAccessEvidence {
|
|
293
|
+
const env = deps.env;
|
|
294
|
+
const markers: Array<{ adapter: string; id: string }> = [];
|
|
295
|
+
const claudeId = nonEmpty(env.CLAUDE_CODE_SESSION_ID);
|
|
296
|
+
if (claudeId) markers.push({ adapter: "claude-code", id: claudeId });
|
|
297
|
+
const codexId = nonEmpty(env.CODEX_THREAD_ID);
|
|
298
|
+
if (codexId) markers.push({ adapter: "codex", id: codexId });
|
|
299
|
+
const cursorId = nonEmpty(env.CURSOR_SESSION_ID) ?? nonEmpty(env.CURSOR_CONVERSATION_ID);
|
|
300
|
+
if (cursorId) markers.push({ adapter: "cursor", id: cursorId });
|
|
301
|
+
const openCodeId = nonEmpty(env.OPENCODE_SESSION_ID);
|
|
302
|
+
if (openCodeId) markers.push({ adapter: "opencode", id: openCodeId });
|
|
303
|
+
const adapters = markers.map((marker) => marker.adapter);
|
|
304
|
+
const result = (verdict: Verdict): ParentAccessEvidence => ({ ...verdict, adapters });
|
|
305
|
+
|
|
306
|
+
if (markers.length === 0) {
|
|
307
|
+
return result({ state: "unknown", detail: "no adapter session marker is present" });
|
|
308
|
+
}
|
|
309
|
+
const sandboxVars = Object.keys(env).filter(
|
|
310
|
+
(key) => key.startsWith("CODEX_SANDBOX") && nonEmpty(env[key]),
|
|
311
|
+
);
|
|
312
|
+
if (sandboxVars.length > 0) {
|
|
313
|
+
return result({ state: "sandboxed", detail: `${sandboxVars.join(", ")} is set` });
|
|
314
|
+
}
|
|
315
|
+
let confinement: Verdict;
|
|
316
|
+
if (deps.platform === "linux") {
|
|
317
|
+
const status = deps.procStatus();
|
|
318
|
+
const match = status?.match(/^NoNewPrivs:\s*(\d)/m);
|
|
319
|
+
if (!match) {
|
|
320
|
+
return result({ state: "unknown", detail: "process confinement could not be read" });
|
|
321
|
+
}
|
|
322
|
+
if (match[1] !== "0") {
|
|
323
|
+
return result({ state: "sandboxed", detail: "this process runs with no_new_privs set" });
|
|
324
|
+
}
|
|
325
|
+
confinement = { state: "full-access", detail: "no_new_privs is clear" };
|
|
326
|
+
} else {
|
|
327
|
+
confinement = { state: "unknown", detail: `no confinement probe on ${deps.platform}` };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const verdicts: Verdict[] = [];
|
|
331
|
+
for (const marker of markers) {
|
|
332
|
+
if (marker.adapter === "codex") {
|
|
333
|
+
verdicts.push(codexParentAccess(marker.id, deps));
|
|
334
|
+
} else if (marker.adapter === "claude-code") {
|
|
335
|
+
if (confinement.state !== "full-access") {
|
|
336
|
+
verdicts.push({
|
|
337
|
+
state: "unknown",
|
|
338
|
+
detail: `Claude Code's Bash sandbox cannot be ruled out (${confinement.detail})`,
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
verdicts.push(claudeParentAccess(marker.id, deps));
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
verdicts.push({
|
|
345
|
+
state: "unknown",
|
|
346
|
+
detail: `${marker.adapter} records no readable access state`,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const sandboxedVerdict = verdicts.find((verdict) => verdict.state === "sandboxed");
|
|
351
|
+
if (sandboxedVerdict) return result(sandboxedVerdict);
|
|
352
|
+
const unknownVerdict = verdicts.find((verdict) => verdict.state === "unknown");
|
|
353
|
+
if (unknownVerdict) return result(unknownVerdict);
|
|
354
|
+
const details = verdicts.map((verdict) => verdict.detail);
|
|
355
|
+
if (confinement.state === "full-access") details.push(confinement.detail);
|
|
356
|
+
return result({ state: "full-access", detail: details.join("; ") });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** The last line of `path` containing `needle`, reading backwards so a large
|
|
360
|
+
* session log is not loaded whole. Null when absent or past `maxBytes`. */
|
|
361
|
+
export function lastLineContaining(
|
|
362
|
+
path: string,
|
|
363
|
+
needle: string,
|
|
364
|
+
maxBytes = 256 * 1024 * 1024,
|
|
365
|
+
): string | null {
|
|
366
|
+
let fd: number;
|
|
367
|
+
try {
|
|
368
|
+
fd = openSync(path, "r");
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
const size = statSync(path).size;
|
|
374
|
+
const chunkSize = 1024 * 1024;
|
|
375
|
+
const needleBytes = Buffer.from(needle);
|
|
376
|
+
let position = size;
|
|
377
|
+
let tail = Buffer.alloc(0);
|
|
378
|
+
while (position > 0 && size - position < maxBytes) {
|
|
379
|
+
const length = Math.min(chunkSize, position);
|
|
380
|
+
position -= length;
|
|
381
|
+
const chunk = Buffer.alloc(length);
|
|
382
|
+
readSync(fd, chunk, 0, length, position);
|
|
383
|
+
tail = Buffer.concat([chunk, tail]);
|
|
384
|
+
const hit = tail.lastIndexOf(needleBytes);
|
|
385
|
+
if (hit < 0) continue;
|
|
386
|
+
const start = tail.lastIndexOf(0x0a, hit);
|
|
387
|
+
if (start < 0 && position > 0) continue; // the line starts in an unread chunk
|
|
388
|
+
const end = tail.indexOf(0x0a, hit);
|
|
389
|
+
return tail.subarray(start + 1, end < 0 ? tail.length : end).toString("utf8");
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
} finally {
|
|
395
|
+
closeSync(fd);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Candidate UTC dates (YYYY/MM/DD) for a UUIDv7 thread id: its creation day
|
|
400
|
+
* and the days either side, since rollout folders use the creator's local date. */
|
|
401
|
+
function rolloutDays(threadId: string): string[] | null {
|
|
402
|
+
const hex = threadId.replace(/-/g, "");
|
|
403
|
+
if (!/^[0-9a-f]{32}$/i.test(hex) || hex[12] !== "7") return null;
|
|
404
|
+
const ms = Number.parseInt(hex.slice(0, 12), 16);
|
|
405
|
+
const days: string[] = [];
|
|
406
|
+
for (const offset of [0, -1, 1]) {
|
|
407
|
+
const date = new Date(ms + offset * 86_400_000);
|
|
408
|
+
const y = date.getUTCFullYear();
|
|
409
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
410
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
411
|
+
days.push(`${y}/${m}/${d}`);
|
|
412
|
+
}
|
|
413
|
+
return days;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function codexHomes(deps: ParentAccessDeps): string[] {
|
|
417
|
+
const homes: string[] = [];
|
|
418
|
+
const explicit = nonEmpty(deps.env.CODEX_HOME);
|
|
419
|
+
if (explicit) homes.push(explicit);
|
|
420
|
+
homes.push(join(deps.homeDir, ".codex"));
|
|
421
|
+
// A Windows Codex session driving a WSL checkout keeps its rollouts in the
|
|
422
|
+
// Windows profile, which the guest sees under /mnt/c/Users.
|
|
423
|
+
if (deps.platform === "linux" && nonEmpty(deps.env.WSL_DISTRO_NAME)) {
|
|
424
|
+
for (const root of deps.windowsUserRoots()) homes.push(join(root, ".codex"));
|
|
425
|
+
}
|
|
426
|
+
return [...new Set(homes)];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function findCodexRollout(threadId: string, deps: ParentAccessDeps): string | null {
|
|
430
|
+
const days = rolloutDays(threadId);
|
|
431
|
+
if (!days) return null;
|
|
432
|
+
const suffix = `-${threadId}.jsonl`;
|
|
433
|
+
for (const home of codexHomes(deps)) {
|
|
434
|
+
for (const day of days) {
|
|
435
|
+
const dir = join(home, "sessions", day);
|
|
436
|
+
let names: string[];
|
|
437
|
+
try {
|
|
438
|
+
names = readdirSync(dir);
|
|
439
|
+
} catch {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
const name = names.find((entry) => entry.startsWith("rollout-") && entry.endsWith(suffix));
|
|
443
|
+
if (name) return join(dir, name);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function codexParentAccess(threadId: string, deps: ParentAccessDeps): Verdict {
|
|
450
|
+
const rollout = findCodexRollout(threadId, deps);
|
|
451
|
+
if (!rollout) {
|
|
452
|
+
return { state: "unknown", detail: `no Codex rollout was found for thread ${threadId}` };
|
|
453
|
+
}
|
|
454
|
+
const line = lastLineContaining(rollout, '"type":"turn_context"');
|
|
455
|
+
if (!line) return { state: "unknown", detail: "the Codex rollout records no turn context" };
|
|
456
|
+
let policyType: unknown;
|
|
457
|
+
try {
|
|
458
|
+
const record = JSON.parse(line) as {
|
|
459
|
+
type?: unknown;
|
|
460
|
+
payload?: { sandbox_policy?: { type?: unknown } };
|
|
461
|
+
};
|
|
462
|
+
if (record.type !== "turn_context") {
|
|
463
|
+
return { state: "unknown", detail: "the Codex rollout's latest turn context is unreadable" };
|
|
464
|
+
}
|
|
465
|
+
policyType = record.payload?.sandbox_policy?.type;
|
|
466
|
+
} catch {
|
|
467
|
+
return { state: "unknown", detail: "the Codex rollout's latest turn context is unreadable" };
|
|
468
|
+
}
|
|
469
|
+
if (typeof policyType !== "string") {
|
|
470
|
+
return { state: "unknown", detail: "the Codex turn context names no sandbox policy" };
|
|
471
|
+
}
|
|
472
|
+
if (policyType === "danger-full-access") {
|
|
473
|
+
return {
|
|
474
|
+
state: "full-access",
|
|
475
|
+
detail: "the Codex parent's latest turn ran danger-full-access",
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return { state: "sandboxed", detail: `the Codex parent's latest turn ran ${policyType}` };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function findClaudeTranscript(sessionId: string, deps: ParentAccessDeps): string | null {
|
|
482
|
+
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
483
|
+
const configDir = nonEmpty(deps.env.CLAUDE_CONFIG_DIR) ?? join(deps.homeDir, ".claude");
|
|
484
|
+
const projects = join(configDir, "projects");
|
|
485
|
+
let dirs: string[];
|
|
486
|
+
try {
|
|
487
|
+
dirs = readdirSync(projects);
|
|
488
|
+
} catch {
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
for (const dir of dirs) {
|
|
492
|
+
const candidate = join(projects, dir, `${sessionId}.jsonl`);
|
|
493
|
+
if (existsSync(candidate)) return candidate;
|
|
494
|
+
}
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function claudeParentAccess(sessionId: string, deps: ParentAccessDeps): Verdict {
|
|
499
|
+
const transcript = findClaudeTranscript(sessionId, deps);
|
|
500
|
+
if (!transcript) {
|
|
501
|
+
return {
|
|
502
|
+
state: "unknown",
|
|
503
|
+
detail: `no Claude Code transcript was found for session ${sessionId}`,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const line = lastLineContaining(transcript, '"permissionMode":"');
|
|
507
|
+
if (!line)
|
|
508
|
+
return { state: "unknown", detail: "the Claude Code transcript records no permission mode" };
|
|
509
|
+
let mode: unknown;
|
|
510
|
+
try {
|
|
511
|
+
mode = (JSON.parse(line) as { permissionMode?: unknown }).permissionMode;
|
|
512
|
+
} catch {
|
|
513
|
+
return {
|
|
514
|
+
state: "unknown",
|
|
515
|
+
detail: "the Claude Code transcript's latest permission mode is unreadable",
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
if (mode === "bypassPermissions") {
|
|
519
|
+
return { state: "full-access", detail: "the Claude Code parent runs bypassPermissions" };
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
state: "sandboxed",
|
|
523
|
+
detail: `the Claude Code parent runs ${typeof mode === "string" ? mode : "an unrecognized"} permission mode`,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from "node:fs";
|
|
17
17
|
import { hostname } from "node:os";
|
|
18
18
|
import { basename, dirname, join, resolve } from "node:path";
|
|
19
|
+
import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
|
|
19
20
|
|
|
20
21
|
const MAX_LEASE_BYTES = 8 * 1024;
|
|
21
22
|
const MAX_CONTENTION_OBSERVATION_ATTEMPTS = 8;
|
|
@@ -202,11 +203,11 @@ function validateInput(input: AcquireNoClobberLeaseInput): void {
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
function ensureLeaseDirectory(leaseDir: string, onCreate?: () => void): void {
|
|
205
|
-
mkdirSync(dirname(leaseDir), { recursive: true, mode:
|
|
206
|
+
mkdirSync(dirname(leaseDir), { recursive: true, mode: stateDirMode() });
|
|
206
207
|
let created = false;
|
|
207
208
|
onCreate?.();
|
|
208
209
|
try {
|
|
209
|
-
mkdirSync(leaseDir, { mode:
|
|
210
|
+
mkdirSync(leaseDir, { mode: stateDirMode() });
|
|
210
211
|
created = true;
|
|
211
212
|
} catch (error) {
|
|
212
213
|
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
@@ -226,7 +227,7 @@ function createRecoveryClaim(
|
|
|
226
227
|
observed: { owner: NoClobberLeaseOwner; bytes: string },
|
|
227
228
|
): void {
|
|
228
229
|
const temporary = join(leaseDir, `recovery-claim-${claimant.owner_id}`);
|
|
229
|
-
mkdirSync(temporary, { mode:
|
|
230
|
+
mkdirSync(temporary, { mode: stateDirMode() });
|
|
230
231
|
const claim: RecoveryClaim = {
|
|
231
232
|
schema_version: 1,
|
|
232
233
|
claimant,
|
|
@@ -385,7 +386,7 @@ function leaseHandle(
|
|
|
385
386
|
}
|
|
386
387
|
|
|
387
388
|
function writeExclusive(path: string, bytes: string): void {
|
|
388
|
-
const fd = openSync(path, "wx",
|
|
389
|
+
const fd = openSync(path, "wx", stateFileMode());
|
|
389
390
|
try {
|
|
390
391
|
writeFileSync(fd, bytes, "utf8");
|
|
391
392
|
fsyncSync(fd);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from "node:fs";
|
|
10
10
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
11
|
+
import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
|
|
11
12
|
import { WorkspaceAttestationError } from "./attestation-error.ts";
|
|
12
13
|
import {
|
|
13
14
|
git,
|
|
@@ -323,7 +324,7 @@ function claimedParentSegment(claim: WorkspaceClaim): string {
|
|
|
323
324
|
function hideWorkspaceParentFromGit(parent: string): void {
|
|
324
325
|
const ignoreFile = join(parent, ".gitignore");
|
|
325
326
|
if (existsSync(ignoreFile)) return;
|
|
326
|
-
writeFileSync(ignoreFile, "*\n", { mode:
|
|
327
|
+
writeFileSync(ignoreFile, "*\n", { mode: stateFileMode() });
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
function reconcileAllocation(
|
|
@@ -1908,7 +1909,7 @@ function capabilityDigestForClaim(claim: WorkspaceClaim): string {
|
|
|
1908
1909
|
|
|
1909
1910
|
function acquireRepositoryLease(coordRoot: string, claim: WorkspaceClaim): () => void {
|
|
1910
1911
|
const leaseDir = join(resolve(coordRoot), ".harnery", "workspaces", PROVIDER_ID, ".leases");
|
|
1911
|
-
mkdirSync(leaseDir, { recursive: true, mode:
|
|
1912
|
+
mkdirSync(leaseDir, { recursive: true, mode: stateDirMode() });
|
|
1912
1913
|
// Mutual exclusion keys on the Git common directory alone. `git worktree add`,
|
|
1913
1914
|
// `prune`, and the shared-`config` migration all write to the common directory's
|
|
1914
1915
|
// administrative area, and once worktrees and submodules are allowed, several
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
statSync,
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
12
|
+
import { stateDirMode } from "../../storage/modes.ts";
|
|
12
13
|
import type { FilesystemIdentity, ValidatedFilesystemPath } from "./types.ts";
|
|
13
14
|
|
|
14
15
|
const WINDOWS_AMBIGUOUS = /[<>:"|?*]|[ .]$/;
|
|
@@ -152,7 +153,7 @@ export function openContainedDirectory(
|
|
|
152
153
|
);
|
|
153
154
|
}
|
|
154
155
|
} else {
|
|
155
|
-
mkdirSync(candidate, { mode:
|
|
156
|
+
mkdirSync(candidate, { mode: stateDirMode() });
|
|
156
157
|
}
|
|
157
158
|
const childFd = openSync(
|
|
158
159
|
candidate,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
writeFileSync,
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
|
|
13
14
|
import {
|
|
14
15
|
canonicalJson,
|
|
15
16
|
fsyncParentDirectory,
|
|
@@ -192,7 +193,7 @@ export function appendWorkspaceEvent(
|
|
|
192
193
|
throw new Error("workspace provider event is invalid or too large");
|
|
193
194
|
}
|
|
194
195
|
const path = workspaceEventsPath(coordRoot, claim.provider_id, claim.binding_id);
|
|
195
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
196
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
196
197
|
const existed = existsSync(path);
|
|
197
198
|
const prior = readWorkspaceEvents(coordRoot, claim.provider_id, claim.binding_id);
|
|
198
199
|
const basis = {
|
|
@@ -213,14 +214,14 @@ export function appendWorkspaceEvent(
|
|
|
213
214
|
const line = `${canonicalJson(record)}\n`;
|
|
214
215
|
if (Buffer.byteLength(line) > WORKSPACE_EVENT_BYTES)
|
|
215
216
|
throw new Error("workspace provider event is too large");
|
|
216
|
-
const fd = openSync(path, "a",
|
|
217
|
+
const fd = openSync(path, "a", stateFileMode());
|
|
217
218
|
try {
|
|
218
219
|
writeFileSync(fd, line, "utf8");
|
|
219
220
|
fsyncSync(fd);
|
|
220
221
|
} finally {
|
|
221
222
|
closeSync(fd);
|
|
222
223
|
}
|
|
223
|
-
chmodSync(path,
|
|
224
|
+
chmodSync(path, stateFileMode());
|
|
224
225
|
if (!existed) fsyncParentDirectory(path);
|
|
225
226
|
return record;
|
|
226
227
|
}
|
|
@@ -295,7 +296,7 @@ export function appendCleanupAttempt(
|
|
|
295
296
|
input: Omit<WorkspaceCleanupAttempt, "seq" | "previous_sha256" | "record_sha256">,
|
|
296
297
|
): WorkspaceCleanupAttempt {
|
|
297
298
|
const path = join(workflowRunDir(coordRoot, runId), "cleanup", "attempts.jsonl");
|
|
298
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
299
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
299
300
|
const existed = existsSync(path);
|
|
300
301
|
const prior = readCleanupAttempts(coordRoot, runId);
|
|
301
302
|
const basis = {
|
|
@@ -311,14 +312,14 @@ export function appendCleanupAttempt(
|
|
|
311
312
|
if (Buffer.byteLength(line) > WORKSPACE_EVENT_BYTES) {
|
|
312
313
|
throw new Error("workspace cleanup attempt is too large");
|
|
313
314
|
}
|
|
314
|
-
const fd = openSync(path, "a",
|
|
315
|
+
const fd = openSync(path, "a", stateFileMode());
|
|
315
316
|
try {
|
|
316
317
|
writeFileSync(fd, line, "utf8");
|
|
317
318
|
fsyncSync(fd);
|
|
318
319
|
} finally {
|
|
319
320
|
closeSync(fd);
|
|
320
321
|
}
|
|
321
|
-
chmodSync(path,
|
|
322
|
+
chmodSync(path, stateFileMode());
|
|
322
323
|
if (!existed) fsyncParentDirectory(path);
|
|
323
324
|
return attempt;
|
|
324
325
|
}
|
|
@@ -329,7 +330,7 @@ export function appendIntegrationAttempt(
|
|
|
329
330
|
input: Omit<IntegrationApplyAttempt, "seq" | "previous_sha256" | "record_sha256">,
|
|
330
331
|
): IntegrationApplyAttempt {
|
|
331
332
|
const path = join(workflowRunDir(coordRoot, runId), "integration", "attempts.jsonl");
|
|
332
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
333
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
333
334
|
const existed = existsSync(path);
|
|
334
335
|
const prior = readIntegrationAttempts(coordRoot, runId);
|
|
335
336
|
const basis = {
|
|
@@ -372,14 +373,14 @@ function appendChainedRecord(
|
|
|
372
373
|
if (Buffer.byteLength(line) > WORKSPACE_EVENT_BYTES) {
|
|
373
374
|
throw new Error(`${label} is too large`);
|
|
374
375
|
}
|
|
375
|
-
const fd = openSync(path, "a",
|
|
376
|
+
const fd = openSync(path, "a", stateFileMode());
|
|
376
377
|
try {
|
|
377
378
|
writeFileSync(fd, line, "utf8");
|
|
378
379
|
fsyncSync(fd);
|
|
379
380
|
} finally {
|
|
380
381
|
closeSync(fd);
|
|
381
382
|
}
|
|
382
|
-
chmodSync(path,
|
|
383
|
+
chmodSync(path, stateFileMode());
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
function readChainedRecords<
|