harnery 0.36.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.d.ts +9 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +10 -1
- package/dist/commands/admission.d.ts +21 -0
- package/dist/commands/admission.d.ts.map +1 -0
- package/dist/commands/admission.js +565 -0
- package/dist/commands/agents.d.ts +7 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +148 -47
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +73 -2
- package/dist/commands/browse-ai.d.ts +2 -2
- package/dist/commands/browse-ai.d.ts.map +1 -1
- package/dist/commands/browse-ai.js +6 -4
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +360 -21
- package/dist/commands/diagnostics.d.ts +4 -0
- package/dist/commands/diagnostics.d.ts.map +1 -0
- package/dist/commands/diagnostics.js +229 -0
- package/dist/commands/fetch.js +1 -0
- package/dist/commands/files.d.ts +12 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -1
- package/dist/commands/governor.d.ts.map +1 -1
- package/dist/commands/governor.js +4 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +71 -2
- package/dist/commands/qa-record.d.ts +144 -0
- package/dist/commands/qa-record.d.ts.map +1 -0
- package/dist/commands/qa-record.js +0 -0
- package/dist/commands/qa-run.d.ts +8 -0
- package/dist/commands/qa-run.d.ts.map +1 -0
- package/dist/commands/qa-run.js +396 -0
- package/dist/commands/qa-status.d.ts +71 -0
- package/dist/commands/qa-status.d.ts.map +1 -0
- package/dist/commands/qa-status.js +490 -0
- package/dist/commands/qa-verify.d.ts +40 -0
- package/dist/commands/qa-verify.d.ts.map +1 -0
- package/dist/commands/qa-verify.js +180 -0
- package/dist/commands/resources.d.ts +4 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +29 -0
- package/dist/commands/review-pack.d.ts +4 -0
- package/dist/commands/review-pack.d.ts.map +1 -0
- package/dist/commands/review-pack.js +1001 -0
- package/dist/commands/supervisor.d.ts +4 -0
- package/dist/commands/supervisor.d.ts.map +1 -0
- package/dist/commands/supervisor.js +101 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +17 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +4 -0
- package/dist/core/agents/cli.js +7 -3
- package/dist/core/agents/health.d.ts +17 -0
- package/dist/core/agents/health.d.ts.map +1 -0
- package/dist/core/agents/health.js +109 -0
- package/dist/core/agents/mailbox.d.ts +92 -0
- package/dist/core/agents/mailbox.d.ts.map +1 -0
- package/dist/core/agents/mailbox.js +325 -0
- package/dist/core/agents/qa-signal.d.ts +111 -0
- package/dist/core/agents/qa-signal.d.ts.map +1 -0
- package/dist/core/agents/qa-signal.js +231 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
- package/dist/core/agents/reconcile-coordination-v3.js +58 -0
- package/dist/core/agents/render/prompt-context.d.ts +5 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +34 -13
- package/dist/core/agents/render/session-context.d.ts +8 -0
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +34 -3
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +50 -1
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +6 -10
- package/dist/core/agents/session-finalizer-v3.js +4 -1
- package/dist/core/agents/session-name-display.d.ts +20 -5
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +67 -7
- 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 +11 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +17 -0
- 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 +5 -0
- package/dist/core/artifacts/constants.d.ts +1 -1
- package/dist/core/artifacts/constants.js +1 -1
- package/dist/core/artifacts/index.d.ts +82 -6
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +410 -15
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +59 -0
- package/dist/core/diagnostics/advice.d.ts +9 -0
- package/dist/core/diagnostics/advice.d.ts.map +1 -0
- package/dist/core/diagnostics/advice.js +112 -0
- package/dist/core/diagnostics/bundle.d.ts +51 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -0
- package/dist/core/diagnostics/bundle.js +968 -0
- package/dist/core/diagnostics/comparison.d.ts +5 -0
- package/dist/core/diagnostics/comparison.d.ts.map +1 -0
- package/dist/core/diagnostics/comparison.js +444 -0
- package/dist/core/diagnostics/contract.d.ts +245 -0
- package/dist/core/diagnostics/contract.d.ts.map +1 -0
- package/dist/core/diagnostics/contract.js +18 -0
- package/dist/core/diagnostics/identity.d.ts +3 -0
- package/dist/core/diagnostics/identity.d.ts.map +1 -0
- package/dist/core/diagnostics/identity.js +10 -0
- package/dist/core/diagnostics/index.d.ts +8 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -0
- package/dist/core/diagnostics/index.js +7 -0
- package/dist/core/diagnostics/replay.d.ts +8 -0
- package/dist/core/diagnostics/replay.d.ts.map +1 -0
- package/dist/core/diagnostics/replay.js +213 -0
- package/dist/core/diagnostics/sanitize.d.ts +9 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
- package/dist/core/diagnostics/sanitize.js +84 -0
- package/dist/core/events/legacy-storage/compression.d.ts +13 -0
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/compression.js +107 -0
- package/dist/core/events/legacy-storage/index.d.ts +1 -0
- package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/index.js +1 -0
- package/dist/core/events/v3/archive-retention.d.ts +33 -0
- package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
- package/dist/core/events/v3/archive-retention.js +195 -0
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +10 -0
- package/dist/core/events/v3/capabilities.d.ts +1 -1
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +1 -0
- package/dist/core/events/v3/coordination-view.d.ts +3 -0
- package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
- package/dist/core/events/v3/coordination-view.js +69 -8
- package/dist/core/events/v3/index.d.ts +1 -0
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +1 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +13 -2
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +10 -2
- package/dist/core/events/v3/producers/recorder.d.ts +39 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +350 -17
- package/dist/core/hooks/adapter/output.d.ts +8 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +11 -1
- package/dist/core/hooks/cli.js +303 -219
- package/dist/core/hooks/health.d.ts +59 -0
- package/dist/core/hooks/health.d.ts.map +1 -0
- package/dist/core/hooks/health.js +84 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +10 -3
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +4 -1
- package/dist/core/qa-artifacts.d.ts +20 -0
- package/dist/core/qa-artifacts.d.ts.map +1 -0
- package/dist/core/qa-artifacts.js +110 -0
- package/dist/core/resources/contract.d.ts +96 -0
- package/dist/core/resources/contract.d.ts.map +1 -0
- package/dist/core/resources/contract.js +2 -0
- package/dist/core/resources/index.d.ts +6 -0
- package/dist/core/resources/index.d.ts.map +1 -0
- package/dist/core/resources/index.js +5 -0
- package/dist/core/resources/sampler.d.ts +34 -0
- package/dist/core/resources/sampler.d.ts.map +1 -0
- package/dist/core/resources/sampler.js +458 -0
- package/dist/core/resources/service-status.d.ts +3 -0
- package/dist/core/resources/service-status.d.ts.map +1 -0
- package/dist/core/resources/service-status.js +48 -0
- package/dist/core/resources/service.d.ts +26 -0
- package/dist/core/resources/service.d.ts.map +1 -0
- package/dist/core/resources/service.js +292 -0
- package/dist/core/resources/storage.d.ts +13 -0
- package/dist/core/resources/storage.d.ts.map +1 -0
- package/dist/core/resources/storage.js +30 -0
- package/dist/core/storage/atomic-json.d.ts +3 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -0
- package/dist/core/storage/atomic-json.js +15 -0
- package/dist/core/storage/builtins.d.ts.map +1 -1
- package/dist/core/storage/builtins.js +45 -6
- package/dist/core/storage/logger.d.ts +2 -0
- package/dist/core/storage/logger.d.ts.map +1 -1
- package/dist/core/storage/logger.js +2 -0
- package/dist/core/storage/query.d.ts +14 -0
- package/dist/core/storage/query.d.ts.map +1 -1
- package/dist/core/storage/query.js +51 -0
- package/dist/core/supervisor/activity.d.ts +4 -0
- package/dist/core/supervisor/activity.d.ts.map +1 -0
- package/dist/core/supervisor/activity.js +141 -0
- package/dist/core/supervisor/contract.d.ts +268 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -0
- package/dist/core/supervisor/contract.js +35 -0
- package/dist/core/supervisor/explanations.d.ts +4 -0
- package/dist/core/supervisor/explanations.d.ts.map +1 -0
- package/dist/core/supervisor/explanations.js +71 -0
- package/dist/core/supervisor/findings.d.ts +20 -0
- package/dist/core/supervisor/findings.d.ts.map +1 -0
- package/dist/core/supervisor/findings.js +399 -0
- package/dist/core/supervisor/history.d.ts +9 -0
- package/dist/core/supervisor/history.d.ts.map +1 -0
- package/dist/core/supervisor/history.js +46 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-alerts.js +70 -0
- package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
- package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-storage.js +24 -0
- package/dist/core/supervisor/hook-health.d.ts +72 -0
- package/dist/core/supervisor/hook-health.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health.js +203 -0
- package/dist/core/supervisor/hooks.d.ts +10 -0
- package/dist/core/supervisor/hooks.d.ts.map +1 -0
- package/dist/core/supervisor/hooks.js +28 -0
- package/dist/core/supervisor/index.d.ts +15 -0
- package/dist/core/supervisor/index.d.ts.map +1 -0
- package/dist/core/supervisor/index.js +14 -0
- package/dist/core/supervisor/log-feed.d.ts +9 -0
- package/dist/core/supervisor/log-feed.d.ts.map +1 -0
- package/dist/core/supervisor/log-feed.js +103 -0
- package/dist/core/supervisor/service.d.ts +29 -0
- package/dist/core/supervisor/service.d.ts.map +1 -0
- package/dist/core/supervisor/service.js +450 -0
- package/dist/core/supervisor/services.d.ts +11 -0
- package/dist/core/supervisor/services.d.ts.map +1 -0
- package/dist/core/supervisor/services.js +155 -0
- package/dist/core/supervisor/status.d.ts +3 -0
- package/dist/core/supervisor/status.d.ts.map +1 -0
- package/dist/core/supervisor/status.js +48 -0
- package/dist/core/supervisor/storage.d.ts +30 -0
- package/dist/core/supervisor/storage.d.ts.map +1 -0
- package/dist/core/supervisor/storage.js +73 -0
- package/dist/core/supervisor/timeline.d.ts +3 -0
- package/dist/core/supervisor/timeline.d.ts.map +1 -0
- package/dist/core/supervisor/timeline.js +87 -0
- package/dist/core/workflow/admission.d.ts +23 -0
- package/dist/core/workflow/admission.d.ts.map +1 -0
- package/dist/core/workflow/admission.js +136 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +77 -2
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +2 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +39 -1
- package/dist/core/workflow/run-state.d.ts +3 -2
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +8 -1
- package/dist/core/workflow/types.d.ts +33 -1
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +2 -1
- package/dist/lib/admission.d.ts +71 -0
- package/dist/lib/admission.d.ts.map +1 -0
- package/dist/lib/admission.js +264 -0
- package/dist/lib/agent-browser/client.d.ts +1 -1
- package/dist/lib/agent-browser/client.d.ts.map +1 -1
- package/dist/lib/agent-browser/client.js +1 -5
- package/dist/lib/browser/capture-fidelity.d.ts +39 -0
- package/dist/lib/browser/capture-fidelity.d.ts.map +1 -0
- package/dist/lib/browser/capture-fidelity.js +84 -0
- package/dist/lib/browser/client.d.ts +46 -1
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +168 -9
- package/dist/lib/browser/critique.d.ts +38 -1
- package/dist/lib/browser/critique.d.ts.map +1 -1
- package/dist/lib/browser/critique.js +34 -6
- package/dist/lib/browser/index.d.ts +5 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +4 -0
- package/dist/lib/browser/page-review-judge.d.ts +64 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -0
- package/dist/lib/browser/page-review-judge.js +270 -0
- package/dist/lib/browser/page-review-pack.d.ts +613 -0
- package/dist/lib/browser/page-review-pack.d.ts.map +1 -0
- package/dist/lib/browser/page-review-pack.js +1751 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +377 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
- package/dist/lib/browser/qa-run-contracts.js +361 -0
- package/dist/lib/browser/qa-run.d.ts +141 -0
- package/dist/lib/browser/qa-run.d.ts.map +1 -0
- package/dist/lib/browser/qa-run.js +1283 -0
- package/dist/lib/browser/request-diagnostics.d.ts +13 -0
- package/dist/lib/browser/request-diagnostics.d.ts.map +1 -0
- package/dist/lib/browser/request-diagnostics.js +18 -0
- package/dist/lib/browser/tiling.d.ts +19 -0
- package/dist/lib/browser/tiling.d.ts.map +1 -1
- package/dist/lib/browser/tiling.js +28 -0
- package/dist/lib/cookies/client.d.ts +9 -0
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +197 -44
- package/dist/lib/cookies/extra.d.ts +18 -0
- package/dist/lib/cookies/extra.d.ts.map +1 -0
- package/dist/lib/cookies/extra.js +14 -0
- package/dist/lib/cookies/index.d.ts +2 -1
- package/dist/lib/cookies/index.d.ts.map +1 -1
- package/dist/lib/cookies/index.js +2 -1
- package/dist/lib/coord-root-id.d.ts +9 -0
- package/dist/lib/coord-root-id.d.ts.map +1 -0
- package/dist/lib/coord-root-id.js +17 -0
- package/dist/lib/durable-job.d.ts +124 -0
- package/dist/lib/durable-job.d.ts.map +1 -0
- package/dist/lib/durable-job.js +296 -0
- package/dist/lib/http/client.d.ts +7 -1
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +2 -0
- package/dist/lib/instructions/apply.d.ts +3 -1
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +74 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +11 -2
- package/package.json +19 -2
- package/src/commander.ts +75 -1
- package/src/commands/admission.ts +699 -0
- package/src/commands/agents.ts +189 -50
- package/src/commands/artifacts.ts +132 -18
- package/src/commands/browse-ai.ts +10 -5
- package/src/commands/browse.ts +496 -20
- package/src/commands/diagnostics.ts +271 -0
- package/src/commands/fetch.ts +1 -0
- package/src/commands/files.ts +102 -2
- package/src/commands/governor.ts +8 -0
- package/src/commands/ledger-v3.ts +82 -0
- package/src/commands/qa-record.ts +682 -0
- package/src/commands/qa-run.ts +503 -0
- package/src/commands/qa-status.ts +608 -0
- package/src/commands/qa-verify.ts +238 -0
- package/src/commands/resources.ts +38 -0
- package/src/commands/review-pack.ts +1281 -0
- package/src/commands/supervisor.ts +141 -0
- package/src/commands/web.ts +19 -0
- package/src/commands/workflow.ts +8 -0
- package/src/core/agents/cli.ts +7 -3
- package/src/core/agents/health.ts +142 -0
- package/src/core/agents/mailbox.ts +373 -0
- package/src/core/agents/qa-signal.ts +261 -0
- package/src/core/agents/reconcile-coordination-v3.ts +77 -0
- package/src/core/agents/render/prompt-context.ts +42 -12
- package/src/core/agents/render/session-context.ts +37 -3
- package/src/core/agents/rules/commit-conflict.ts +58 -1
- package/src/core/agents/rules/stop-hook.ts +6 -12
- package/src/core/agents/session-finalizer-v3.ts +4 -1
- package/src/core/agents/session-name-display.ts +78 -7
- package/src/core/agents/state/heartbeat-reader.ts +7 -0
- package/src/core/agents/state/heartbeat-writer.ts +23 -0
- package/src/core/agents/state/live-coordination-view.ts +1 -0
- package/src/core/agents/state/live-coordination-writer.ts +5 -0
- package/src/core/artifacts/constants.ts +1 -1
- package/src/core/artifacts/index.ts +565 -30
- package/src/core/config.ts +134 -2
- package/src/core/diagnostics/advice.ts +147 -0
- package/src/core/diagnostics/bundle.ts +1250 -0
- package/src/core/diagnostics/comparison.ts +565 -0
- package/src/core/diagnostics/contract.ts +311 -0
- package/src/core/diagnostics/identity.ts +11 -0
- package/src/core/diagnostics/index.ts +7 -0
- package/src/core/diagnostics/replay.ts +314 -0
- package/src/core/diagnostics/sanitize.ts +97 -0
- package/src/core/events/legacy-storage/compression.ts +130 -0
- package/src/core/events/legacy-storage/index.ts +1 -0
- package/src/core/events/v3/archive-retention.ts +251 -0
- package/src/core/events/v3/bootstrap.ts +10 -0
- package/src/core/events/v3/capabilities.ts +2 -0
- package/src/core/events/v3/coordination-view.ts +100 -11
- package/src/core/events/v3/index.ts +9 -0
- package/src/core/events/v3/live-routing.ts +1 -0
- package/src/core/events/v3/producers/hook-base.ts +14 -2
- package/src/core/events/v3/producers/intake.ts +10 -2
- package/src/core/events/v3/producers/recorder.ts +420 -17
- package/src/core/hooks/adapter/output.ts +12 -1
- package/src/core/hooks/cli.ts +332 -247
- package/src/core/hooks/health.ts +146 -0
- package/src/core/hooks/resolve/transcript.ts +10 -3
- package/src/core/hooks/session-name-presence.ts +6 -1
- package/src/core/qa-artifacts.ts +126 -0
- package/src/core/resources/contract.ts +105 -0
- package/src/core/resources/index.ts +5 -0
- package/src/core/resources/sampler.ts +590 -0
- package/src/core/resources/service-status.ts +62 -0
- package/src/core/resources/service.ts +337 -0
- package/src/core/resources/storage.ts +44 -0
- package/src/core/storage/atomic-json.ts +15 -0
- package/src/core/storage/builtins.ts +62 -6
- package/src/core/storage/logger.ts +2 -0
- package/src/core/storage/query.ts +58 -0
- package/src/core/supervisor/activity.ts +193 -0
- package/src/core/supervisor/contract.ts +312 -0
- package/src/core/supervisor/explanations.ts +104 -0
- package/src/core/supervisor/findings.ts +788 -0
- package/src/core/supervisor/history.ts +61 -0
- package/src/core/supervisor/hook-health-alerts.ts +108 -0
- package/src/core/supervisor/hook-health-storage.ts +25 -0
- package/src/core/supervisor/hook-health.ts +301 -0
- package/src/core/supervisor/hooks.ts +35 -0
- package/src/core/supervisor/index.ts +14 -0
- package/src/core/supervisor/log-feed.ts +131 -0
- package/src/core/supervisor/service.ts +511 -0
- package/src/core/supervisor/services.ts +203 -0
- package/src/core/supervisor/status.ts +59 -0
- package/src/core/supervisor/storage.ts +126 -0
- package/src/core/supervisor/timeline.ts +133 -0
- package/src/core/workflow/admission.ts +215 -0
- package/src/core/workflow/engine.ts +93 -1
- package/src/core/workflow/index.ts +10 -0
- package/src/core/workflow/proof.ts +54 -1
- package/src/core/workflow/run-state.ts +13 -1
- package/src/core/workflow/types.ts +39 -1
- package/src/lib/admission.ts +347 -0
- package/src/lib/agent-browser/client.ts +2 -10
- package/src/lib/browser/capture-fidelity.ts +98 -0
- package/src/lib/browser/client.ts +212 -10
- package/src/lib/browser/critique.ts +62 -7
- package/src/lib/browser/index.ts +65 -0
- package/src/lib/browser/page-review-judge.ts +360 -0
- package/src/lib/browser/page-review-pack.ts +2384 -0
- package/src/lib/browser/qa-run-contracts.ts +743 -0
- package/src/lib/browser/qa-run.ts +1462 -0
- package/src/lib/browser/request-diagnostics.ts +27 -0
- package/src/lib/browser/tiling.ts +32 -0
- package/src/lib/cookies/client.ts +228 -42
- package/src/lib/cookies/extra.ts +28 -0
- package/src/lib/cookies/index.ts +2 -0
- package/src/lib/coord-root-id.ts +23 -0
- package/src/lib/durable-job.ts +407 -0
- package/src/lib/http/client.ts +13 -1
- package/src/lib/instructions/apply.ts +81 -2
- package/src/lib/instructions/templates.ts +11 -2
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical supervisor reconcile pass.
|
|
3
|
+
*
|
|
4
|
+
* Two things have to happen together, in this order:
|
|
5
|
+
*
|
|
6
|
+
* 1. `staleSweep` prunes the disposable coordination cache under
|
|
7
|
+
* `.harnery/active` (plus orphaned pid-map and `.last-peer-hash` files)
|
|
8
|
+
* and leaves a durable lifecycle observation for every deletion.
|
|
9
|
+
* 2. `reconcileSessionFinalizationV3` turns those observations, along with
|
|
10
|
+
* archive/idle/run/host signals, into session finalization requests.
|
|
11
|
+
*
|
|
12
|
+
* Running the sweep first is what makes a single pass self-contained: the
|
|
13
|
+
* `lifecycle.sweep_observed` rows it writes are visible to the finalizer in
|
|
14
|
+
* the same pass, so a stale session is both reaped and finalized without
|
|
15
|
+
* waiting for a later invocation.
|
|
16
|
+
*
|
|
17
|
+
* ADR 0077 already specified that `agents reconcile` and session start
|
|
18
|
+
* discover stale-sweep in the same pass. Both entry points had drifted to
|
|
19
|
+
* calling only the finalizer, so the sweep never ran outside its own
|
|
20
|
+
* subcommand. This module is the single composition both now share; adding a
|
|
21
|
+
* caller means calling this, not re-composing the two halves.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
type ReconcileSessionFinalizationOptionsV3,
|
|
26
|
+
type ReconcileSessionFinalizationResultV3,
|
|
27
|
+
reconcileSessionFinalizationV3,
|
|
28
|
+
} from "./session-finalizer-v3.ts";
|
|
29
|
+
import { staleSweep } from "./state/stale-sweep.ts";
|
|
30
|
+
|
|
31
|
+
export interface ReconcileCoordinationResultV3 extends ReconcileSessionFinalizationResultV3 {
|
|
32
|
+
/** Heartbeat cache files deleted from `.harnery/active` by the sweep. */
|
|
33
|
+
swept_heartbeats: number;
|
|
34
|
+
/** Orphaned `.harnery/pid-map` entries deleted by the sweep. */
|
|
35
|
+
swept_pidmaps: number;
|
|
36
|
+
/** Dead-owner `.last-peer-hash.*` files deleted by the sweep. */
|
|
37
|
+
swept_peer_hashes: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sweep the disposable coordination cache, then reconcile session
|
|
42
|
+
* finalization, and report both sets of counts.
|
|
43
|
+
*
|
|
44
|
+
* The sweep owns its own safety contract (configured coordination freshness
|
|
45
|
+
* for valid rows, an additional stale-mtime gate for malformed or missing
|
|
46
|
+
* timestamps, and a durable audit record before any deletion, failing closed
|
|
47
|
+
* when neither can be written). Nothing here loosens it, and a sweep failure
|
|
48
|
+
* never blocks finalization: the cache is a projection that heals on the
|
|
49
|
+
* owning agent's next tool call, while the V3 ledger is authoritative.
|
|
50
|
+
*/
|
|
51
|
+
export function reconcileCoordinationV3(
|
|
52
|
+
coordRoot: string,
|
|
53
|
+
options: ReconcileSessionFinalizationOptionsV3 = {},
|
|
54
|
+
): ReconcileCoordinationResultV3 {
|
|
55
|
+
let sweptHeartbeats = 0;
|
|
56
|
+
let sweptPidmaps = 0;
|
|
57
|
+
let sweptPeerHashes = 0;
|
|
58
|
+
const sweepDiagnostics: string[] = [];
|
|
59
|
+
try {
|
|
60
|
+
const sweep = staleSweep(coordRoot);
|
|
61
|
+
sweptHeartbeats = sweep.heartbeatsRemoved.length;
|
|
62
|
+
sweptPidmaps = sweep.pidmapsRemoved;
|
|
63
|
+
sweptPeerHashes = sweep.peerHashesRemoved;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
sweepDiagnostics.push(
|
|
66
|
+
`stale_sweep_failed:${error instanceof Error ? error.message : String(error)}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const result = reconcileSessionFinalizationV3(coordRoot, options);
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
diagnostics: [...sweepDiagnostics, ...result.diagnostics],
|
|
73
|
+
swept_heartbeats: sweptHeartbeats,
|
|
74
|
+
swept_pidmaps: sweptPidmaps,
|
|
75
|
+
swept_peer_hashes: sweptPeerHashes,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -46,15 +46,18 @@ import {
|
|
|
46
46
|
import { join } from "node:path";
|
|
47
47
|
|
|
48
48
|
import { coordEnv } from "../../../lib/env.ts";
|
|
49
|
+
import type { Adapter } from "../../adapter.ts";
|
|
49
50
|
import { endOfTurnStatusCommand, hostPromptReminder, resolveBinName } from "../../config.ts";
|
|
51
|
+
import { canReceiveContext } from "../../hooks/adapter/output.ts";
|
|
50
52
|
import { type RemoteMachine, readRemoteMachines } from "../../presence/index.ts";
|
|
53
|
+
import { drainMailbox, formatMailboxDelivery } from "../mailbox.ts";
|
|
51
54
|
import { sessionNameDisplayBlock, sessionNameDisplayPending } from "../session-name-display.ts";
|
|
52
55
|
import {
|
|
53
56
|
readLiveCoordinationRow,
|
|
54
57
|
readLiveCoordinationRows,
|
|
55
58
|
} from "../state/live-coordination-view.ts";
|
|
56
59
|
import type { AgentActivity, TaskState } from "../state/session-state.ts";
|
|
57
|
-
import { formatPendingCouncils } from "./session-context.ts";
|
|
60
|
+
import { formatPendingCouncils, recordDeliveredMessages } from "./session-context.ts";
|
|
58
61
|
|
|
59
62
|
interface HeartbeatRow {
|
|
60
63
|
instance_id?: string;
|
|
@@ -92,6 +95,11 @@ export interface PromptContextOpts {
|
|
|
92
95
|
/** When true, append the non-deduplicated Codex status-footer reminder.
|
|
93
96
|
* The caller enables this only for human-facing Codex sessions. */
|
|
94
97
|
statusFooterNudge?: boolean;
|
|
98
|
+
/** Adapter id this prompt is being rendered for. Gates the mailbox drain:
|
|
99
|
+
* an adapter that cannot receive UserPromptSubmit context must not consume
|
|
100
|
+
* messages here, or they are removed from the queue and never shown. When
|
|
101
|
+
* omitted, delivery is attempted (the default route can receive). */
|
|
102
|
+
adapter?: string;
|
|
95
103
|
/** Adapter id whose Stop hook ENFORCES the end-of-turn ritual (task_set +
|
|
96
104
|
* status check). When set, append a fresh per-prompt reminder of that
|
|
97
105
|
* contract so the model satisfies it before ending the turn instead of
|
|
@@ -119,20 +127,43 @@ export function renderPromptContext(opts: PromptContextOpts): string {
|
|
|
119
127
|
hostPromptReminder: promptReminderEnabled,
|
|
120
128
|
statusFooterNudge,
|
|
121
129
|
turnRitualNudge,
|
|
130
|
+
adapter,
|
|
122
131
|
} = opts;
|
|
123
132
|
const sections: string[] = [];
|
|
124
133
|
|
|
125
|
-
//
|
|
134
|
+
// The hook route does not pass --name, so resolve it from the heartbeat.
|
|
135
|
+
// Every name-addressed section below (messages, councils) is silently
|
|
136
|
+
// skipped without it.
|
|
137
|
+
const resolvedName = agentName ?? readLiveCoordinationRow(coordRoot, instanceId)?.name;
|
|
138
|
+
|
|
139
|
+
// 1. Peer messages. Drained first and never deduped: a message is delivered
|
|
140
|
+
// exactly once, and it is the one section here that carries new information
|
|
141
|
+
// from another agent rather than a reminder.
|
|
142
|
+
//
|
|
143
|
+
// Draining is gated on the adapter being able to receive this event's
|
|
144
|
+
// context. Cursor cannot, so draining there would empty the queue into output
|
|
145
|
+
// that is discarded; those sessions get their messages at SessionStart, which
|
|
146
|
+
// Cursor does deliver.
|
|
147
|
+
const canDeliverHere = !adapter || canReceiveContext(adapter as Adapter, "UserPromptSubmit");
|
|
148
|
+
if (resolvedName && canDeliverHere) {
|
|
149
|
+
const delivered = drainMailbox(coordRoot, resolvedName);
|
|
150
|
+
if (delivered.length > 0) {
|
|
151
|
+
recordDeliveredMessages(instanceId, delivered);
|
|
152
|
+
sections.push(formatMailboxDelivery(coordRoot, delivered));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 2. Peer table with hash dedup.
|
|
126
157
|
const peerTable = computePeerTableIfChanged(coordRoot, instanceId, sessionId);
|
|
127
158
|
if (peerTable) sections.push(peerTable);
|
|
128
159
|
|
|
129
|
-
//
|
|
130
|
-
if (
|
|
131
|
-
const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId,
|
|
160
|
+
// 3. Council pending with hash dedup.
|
|
161
|
+
if (resolvedName) {
|
|
162
|
+
const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId, resolvedName);
|
|
132
163
|
if (councilMsg) sections.push(councilMsg);
|
|
133
164
|
}
|
|
134
165
|
|
|
135
|
-
//
|
|
166
|
+
// 4. First-session naming for every adapter, plus unset/stale-task reminders
|
|
136
167
|
// for Cursor/Codex. One state machine avoids a second generic "task unset"
|
|
137
168
|
// reminder immediately after the first-session reminder has been deduped.
|
|
138
169
|
if (sessionNameNudge || taskNudge) {
|
|
@@ -143,7 +174,7 @@ export function renderPromptContext(opts: PromptContextOpts): string {
|
|
|
143
174
|
if (nudgeMsg) sections.push(nudgeMsg);
|
|
144
175
|
}
|
|
145
176
|
|
|
146
|
-
//
|
|
177
|
+
// 5. A supported adapter gets the host reminder on every prompt. The text is
|
|
147
178
|
// intentionally not hash-deduped or persisted into runtime state: freshness
|
|
148
179
|
// is the feature, and the project config remains its only durable copy.
|
|
149
180
|
if (promptReminderEnabled) {
|
|
@@ -151,7 +182,7 @@ export function renderPromptContext(opts: PromptContextOpts): string {
|
|
|
151
182
|
if (reminder) sections.push(reminder);
|
|
152
183
|
}
|
|
153
184
|
|
|
154
|
-
//
|
|
185
|
+
// 6. Codex gets this on every prompt. It is intentionally not hash-deduped:
|
|
155
186
|
// the reminder must be fresh in the model's context when it writes the reply.
|
|
156
187
|
// Missing it is harmless because Codex Stop enforcement remains observe-only.
|
|
157
188
|
if (statusFooterNudge) {
|
|
@@ -159,7 +190,7 @@ export function renderPromptContext(opts: PromptContextOpts): string {
|
|
|
159
190
|
if (statusMsg) sections.push(statusMsg);
|
|
160
191
|
}
|
|
161
192
|
|
|
162
|
-
//
|
|
193
|
+
// 7. Adapters whose Stop hook ENFORCES the turn ritual get a fresh reminder
|
|
163
194
|
// on every prompt. Reminding before the reply is drafted is far cheaper than
|
|
164
195
|
// the bounce-and-retry the Stop hook otherwise forces: enforcement-only
|
|
165
196
|
// compliance was measured missing on a double-digit share of turns. Not
|
|
@@ -188,10 +219,9 @@ function renderTurnRitualReminder(
|
|
|
188
219
|
|
|
189
220
|
const bin = resolveBinName(coordRoot);
|
|
190
221
|
const statusCommand = endOfTurnStatusCommand(coordRoot);
|
|
191
|
-
// Claude Code
|
|
192
|
-
// shows the box inline on its own, so it is never asked for a second copy.
|
|
222
|
+
// Claude Code and Cursor can both enforce completed assistant reply text.
|
|
193
223
|
const paste =
|
|
194
|
-
adapter === "claude-code"
|
|
224
|
+
adapter === "claude-code" || adapter === "cursor"
|
|
195
225
|
? " and paste its output verbatim in a fenced code block at the end of your reply"
|
|
196
226
|
: "";
|
|
197
227
|
return (
|
|
@@ -12,7 +12,9 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { resolveBinName, resolveHooksSetupHint } from "../../config.ts";
|
|
14
14
|
import { harneryVersion, loadAdapterWiring } from "../../hooks/adapter/wiring.ts";
|
|
15
|
+
import { appendEntry } from "../../journal/index.ts";
|
|
15
16
|
import { readRemoteMachines } from "../../presence/index.ts";
|
|
17
|
+
import { drainMailbox, formatMailboxDelivery, type MailboxMessageV1 } from "../mailbox.ts";
|
|
16
18
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.ts";
|
|
17
19
|
import { readForkParent } from "../state/names.ts";
|
|
18
20
|
import type { AgentActivity, TaskState } from "../state/session-state.ts";
|
|
@@ -82,13 +84,24 @@ export function renderSessionContext(opts: RenderOpts): string {
|
|
|
82
84
|
);
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
// 3.
|
|
87
|
+
// 3. Peer messages queued for this name while no session held it. Draining
|
|
88
|
+
// here is what makes `agents ping` safe to use on a dormant agent: the sender
|
|
89
|
+
// never has to know whether the recipient is running.
|
|
90
|
+
if (agentName) {
|
|
91
|
+
const delivered = drainMailbox(coordRoot, agentName);
|
|
92
|
+
if (delivered.length > 0) {
|
|
93
|
+
recordDeliveredMessages(instanceId, delivered);
|
|
94
|
+
messages.push(formatMailboxDelivery(coordRoot, delivered));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 4. Council invites
|
|
86
99
|
if (agentName) {
|
|
87
100
|
const councilMsg = formatPendingCouncils(coordRoot, agentName);
|
|
88
101
|
if (councilMsg) messages.push(councilMsg);
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
//
|
|
104
|
+
// 5. Commit-guard wiring check
|
|
92
105
|
const wiringIssues = checkWiring(coordRoot);
|
|
93
106
|
if (wiringIssues.length > 0) {
|
|
94
107
|
const hint = resolveHooksSetupHint(coordRoot);
|
|
@@ -99,7 +112,7 @@ export function renderSessionContext(opts: RenderOpts): string {
|
|
|
99
112
|
messages.push(wiringSummary);
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
//
|
|
115
|
+
// 6. Adapter-hook drift: a harnery upgrade changed the hook set, but this
|
|
103
116
|
// project's settings file hasn't been re-wired. Only fires for a adapter the
|
|
104
117
|
// project already opted into (≥1 hook wired), so it never nags a project that
|
|
105
118
|
// simply has a settings file. Remedy is always `<bin> init` (idempotent).
|
|
@@ -125,6 +138,27 @@ export function renderSessionContext(opts: RenderOpts): string {
|
|
|
125
138
|
return messages.join("\n\n");
|
|
126
139
|
}
|
|
127
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Write delivered peer messages into the recipient's own journal, so they
|
|
143
|
+
* survive context compaction the way any other coordination breadcrumb does.
|
|
144
|
+
* A message the sender already journaled (the recipient was live when it was
|
|
145
|
+
* sent) is skipped to avoid a duplicate entry.
|
|
146
|
+
*/
|
|
147
|
+
export function recordDeliveredMessages(
|
|
148
|
+
instanceId: string,
|
|
149
|
+
messages: readonly MailboxMessageV1[],
|
|
150
|
+
): void {
|
|
151
|
+
for (const m of messages) {
|
|
152
|
+
if (m.journaled) continue;
|
|
153
|
+
try {
|
|
154
|
+
appendEntry(instanceId, "handoff", `from agent-${m.from_name}: ${m.body}`);
|
|
155
|
+
} catch {
|
|
156
|
+
// A journal write failure must never cost the delivery itself; the
|
|
157
|
+
// message is already in the rendered context the model reads.
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
128
162
|
/** One-line-per-agent view of sessions on other machines (presence refs). */
|
|
129
163
|
function formatRemoteMachines(coordRoot: string): string {
|
|
130
164
|
try {
|
|
@@ -25,6 +25,7 @@ import { appendFileSync, existsSync } from "node:fs";
|
|
|
25
25
|
import { basename, dirname, join } from "node:path";
|
|
26
26
|
import { coordFreshnessSeconds } from "../../config.ts";
|
|
27
27
|
import { resolveOwner } from "../../hooks/resolve/owner.ts";
|
|
28
|
+
import { recordLiveClaimChangeV3 } from "../live-authority-v3.ts";
|
|
28
29
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.ts";
|
|
29
30
|
import { instanceHasLivePid } from "../state/pidmap.ts";
|
|
30
31
|
|
|
@@ -34,6 +35,7 @@ interface PeerHeartbeat {
|
|
|
34
35
|
name?: string;
|
|
35
36
|
files_touched?: string[];
|
|
36
37
|
last_heartbeat?: string;
|
|
38
|
+
platform?: string;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
interface Conflict {
|
|
@@ -190,6 +192,15 @@ export function evaluateCommit(coordRoot: string, req: CommitVerdictRequest): Co
|
|
|
190
192
|
};
|
|
191
193
|
}
|
|
192
194
|
|
|
195
|
+
// A blocked commit proves this session has work in every staged path, even
|
|
196
|
+
// when the edit hook never acquired a claim (for example, a shell edit made
|
|
197
|
+
// just before another session claimed the same file). Keep those paths on
|
|
198
|
+
// the blocked session's durable claim set. The original holder's later
|
|
199
|
+
// commit will then see the overlap instead of silently sweeping this
|
|
200
|
+
// session's working-tree hunks. A successful later commit releases clean
|
|
201
|
+
// paths through the existing post-commit hook.
|
|
202
|
+
retainBlockedStagedPaths(coordRoot, me, mySession, req.staged_paths, peers);
|
|
203
|
+
|
|
193
204
|
return {
|
|
194
205
|
allow: false,
|
|
195
206
|
exit_code: 1,
|
|
@@ -204,6 +215,41 @@ export function evaluateCommit(coordRoot: string, req: CommitVerdictRequest): Co
|
|
|
204
215
|
};
|
|
205
216
|
}
|
|
206
217
|
|
|
218
|
+
function retainBlockedStagedPaths(
|
|
219
|
+
coordRoot: string,
|
|
220
|
+
instanceId: string,
|
|
221
|
+
sessionId: string,
|
|
222
|
+
stagedPaths: readonly string[],
|
|
223
|
+
peers: readonly PeerHeartbeat[],
|
|
224
|
+
): void {
|
|
225
|
+
if (instanceId === "__unattributed__") return;
|
|
226
|
+
const self = peers.find((peer) => peer.instance_id === instanceId);
|
|
227
|
+
if (!self) return;
|
|
228
|
+
|
|
229
|
+
for (const path of stagedPaths) {
|
|
230
|
+
try {
|
|
231
|
+
recordLiveClaimChangeV3({
|
|
232
|
+
coordRoot,
|
|
233
|
+
owner: instanceId,
|
|
234
|
+
nativeSessionId: self.session_id ?? sessionId,
|
|
235
|
+
adapter: adapterFromPlatform(self.platform),
|
|
236
|
+
operation: "acquired",
|
|
237
|
+
path,
|
|
238
|
+
access: "write",
|
|
239
|
+
});
|
|
240
|
+
} catch {
|
|
241
|
+
// The commit is already blocked. Coordination remains fail-open for
|
|
242
|
+
// authority-write failures so a damaged cache cannot brick Git.
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
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
|
+
|
|
207
253
|
function findOverlap(
|
|
208
254
|
staged: string,
|
|
209
255
|
files: readonly string[],
|
|
@@ -269,6 +315,18 @@ function isHolderSelfAttributed(
|
|
|
269
315
|
stagedSet: Set<string>,
|
|
270
316
|
peers: readonly PeerHeartbeat[],
|
|
271
317
|
): boolean {
|
|
318
|
+
const holder = peers.find((p) => p.instance_id === holderId);
|
|
319
|
+
|
|
320
|
+
// Windows-hosted Codex sessions cross a process boundary into WSL, so they
|
|
321
|
+
// deliberately have no usable PID anchor. Their canonical V3 session row is
|
|
322
|
+
// the foreign-identity evidence instead. Treating that row as a transient
|
|
323
|
+
// self identity would suppress the symmetric claim retained after a blocked
|
|
324
|
+
// commit and reopen the working-tree sweep this guard is meant to stop.
|
|
325
|
+
if (holder?.platform === "codex" && holder.session_id) {
|
|
326
|
+
debugGate(coordRoot, { holder: holderId, gate: "B", reason: "canonical_codex_session" });
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
272
330
|
// Gate B (cheaper): live foreign pid-map entry blocks self-attribution.
|
|
273
331
|
if (holderHasLiveForeignPid(coordRoot, holderId)) {
|
|
274
332
|
debugGate(coordRoot, { holder: holderId, gate: "B", reason: "live_pid_anchor" });
|
|
@@ -276,7 +334,6 @@ function isHolderSelfAttributed(
|
|
|
276
334
|
}
|
|
277
335
|
|
|
278
336
|
// Gate A: every held path is either in the staged set or already clean in HEAD.
|
|
279
|
-
const holder = peers.find((p) => p.instance_id === holderId);
|
|
280
337
|
if (!holder) {
|
|
281
338
|
debugGate(coordRoot, { holder: holderId, gate: "A", reason: "holder_not_found" });
|
|
282
339
|
return false;
|
|
@@ -154,22 +154,16 @@ export function evaluateStopHookV3Events(
|
|
|
154
154
|
.map((event) => observedBoolean(event.payload.ritual?.status_box_present_strict))
|
|
155
155
|
.filter((value): value is boolean => value !== undefined);
|
|
156
156
|
const currentStrictBox = claudeContinuation ? req.status_box_present_strict : undefined;
|
|
157
|
-
if (
|
|
157
|
+
if (latestBox === undefined && currentStrictBox === undefined) {
|
|
158
158
|
return evidenceUnavailable(
|
|
159
159
|
"turn.completed has no observed V3 assistant-text status-box result; Stop remains fail-open",
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
const ackPresent =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
? strictBoxObservations.includes(true) || currentStrictBox === true
|
|
168
|
-
: latestBox === true;
|
|
169
|
-
const ackBlock =
|
|
170
|
-
req.adapter === "cursor"
|
|
171
|
-
? () => rule13Block(coordRoot, req.session_id)
|
|
172
|
-
: () => rule23Block(coordRoot, req.session_id);
|
|
163
|
+
const ackPresent = claudeContinuation
|
|
164
|
+
? strictBoxObservations.includes(true) || currentStrictBox === true
|
|
165
|
+
: latestBox === true;
|
|
166
|
+
const ackBlock = () => rule23Block(coordRoot, req.session_id);
|
|
173
167
|
|
|
174
168
|
// Pure prose turns keep the human-visible acknowledgement but do not require
|
|
175
169
|
// task or final-status mutations. This preserves the pre-V3 policy exactly.
|
|
@@ -354,7 +348,7 @@ function sessionIdSuffix(sessionId?: string): string {
|
|
|
354
348
|
* The verdict keeps the first failure's rule id, so the `blocked_rule`
|
|
355
349
|
* telemetry and the adapter's `rule=` stderr contract are unchanged; the
|
|
356
350
|
* reason enumerates the rest so a single continuation can repair all of them.
|
|
357
|
-
*
|
|
351
|
+
* Duplicate rules are folded so one continuation repairs every missing signal.
|
|
358
352
|
*/
|
|
359
353
|
function mergeStopBlocks(blocks: VerdictResult[]): VerdictResult | undefined {
|
|
360
354
|
let primary: VerdictResult | undefined;
|
|
@@ -584,7 +584,10 @@ function observeCascades(
|
|
|
584
584
|
}
|
|
585
585
|
if (
|
|
586
586
|
event.event_type === "lifecycle.sweep_observed" &&
|
|
587
|
-
|
|
587
|
+
// stale-sweep.ts emits the observation as `stale_heartbeat`; the
|
|
588
|
+
// finalization request it produces is the one named `stale_sweep`.
|
|
589
|
+
// Matching the request name here made this branch unreachable.
|
|
590
|
+
event.payload.observation === "stale_heartbeat"
|
|
588
591
|
) {
|
|
589
592
|
const record = records.find(
|
|
590
593
|
({ state }) => !state.terminal && state.instance_id === event.payload.subject_instance_id,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
export interface SessionNameDisplayState {
|
|
9
9
|
suggested_session_name?: string;
|
|
10
10
|
session_name_seen_for?: string;
|
|
11
|
+
session_name_display_requested_for?: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const SESSION_NAME_DISPLAY_NOTE =
|
|
@@ -21,6 +22,45 @@ export function sessionNameDisplayPending(
|
|
|
21
22
|
return name;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Every title whose exact display satisfies the current latch.
|
|
27
|
+
*
|
|
28
|
+
* The pending title always counts. The title the agent was last instructed to
|
|
29
|
+
* display also counts, because the harness — not the agent — is what changed
|
|
30
|
+
* the target after asking. Accepting it costs nothing the operator can see: a
|
|
31
|
+
* clean one-line block did open that reply. Refusing it left Cursor sessions
|
|
32
|
+
* permanently latched, since no Cursor surface can re-open a missed window.
|
|
33
|
+
*/
|
|
34
|
+
export function sessionNameDisplayAcceptedNames(
|
|
35
|
+
row: SessionNameDisplayState | null | undefined,
|
|
36
|
+
): string[] {
|
|
37
|
+
const pending = sessionNameDisplayPending(row);
|
|
38
|
+
if (!pending) return [];
|
|
39
|
+
const requested = row?.session_name_display_requested_for;
|
|
40
|
+
return requested && requested !== pending ? [pending, requested] : [pending];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Match assistant text against any accepted title and report which one closed
|
|
45
|
+
* the latch, so a drifted display can be stamped against the pending title and
|
|
46
|
+
* logged as drift rather than silently discarded.
|
|
47
|
+
*/
|
|
48
|
+
export function matchSessionNameDisplay(
|
|
49
|
+
row: SessionNameDisplayState | null | undefined,
|
|
50
|
+
text: string | undefined,
|
|
51
|
+
startsWithBlock: (
|
|
52
|
+
text: string,
|
|
53
|
+
expectedName: string,
|
|
54
|
+
) => boolean = assistantTextStartsWithSessionNameBlock,
|
|
55
|
+
): { pending: string; displayed: string } | null {
|
|
56
|
+
if (!text) return null;
|
|
57
|
+
const accepted = sessionNameDisplayAcceptedNames(row);
|
|
58
|
+
const pending = accepted[0];
|
|
59
|
+
if (!pending) return null;
|
|
60
|
+
const displayed = accepted.find((candidate) => startsWithBlock(text, candidate));
|
|
61
|
+
return displayed ? { pending, displayed } : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
24
64
|
export function sessionNameDisplayBlock(name: string): string {
|
|
25
65
|
return `\`\`\`\n${name}\n\`\`\``;
|
|
26
66
|
}
|
|
@@ -80,10 +120,16 @@ function responseContainsSessionNameMint(
|
|
|
80
120
|
return true;
|
|
81
121
|
}
|
|
82
122
|
} catch {
|
|
83
|
-
//
|
|
123
|
+
// Fall through to the textual check below.
|
|
84
124
|
}
|
|
85
125
|
}
|
|
86
|
-
|
|
126
|
+
// The mint command's stdout often reaches the transcript mangled: cut
|
|
127
|
+
// short by `cut`/`head -c`, or with a tail-truncated first line. Whole-JSON
|
|
128
|
+
// parsing then fails even though the name and the mint flag are both
|
|
129
|
+
// legible, and a session that did mint can never latch. Accept the text
|
|
130
|
+
// when the exact quoted name sits under its key AND a mint flag is true;
|
|
131
|
+
// ordinary status output carries the name without any flag.
|
|
132
|
+
return textCarriesSessionNameMint(value, name);
|
|
87
133
|
}
|
|
88
134
|
|
|
89
135
|
if (typeof value !== "object") return false;
|
|
@@ -107,16 +153,41 @@ function responseContainsSessionNameMint(
|
|
|
107
153
|
);
|
|
108
154
|
}
|
|
109
155
|
|
|
156
|
+
const MINT_FLAG_PATTERN = /"(?:first_of_session|name_reminted|session_name_retry)"\s*:\s*true\b/;
|
|
157
|
+
|
|
158
|
+
function textCarriesSessionNameMint(text: string, name: string): boolean {
|
|
159
|
+
const quotedName = JSON.stringify(name);
|
|
160
|
+
const keyIndex = text.indexOf('"suggested_session_name"');
|
|
161
|
+
if (keyIndex < 0) return false;
|
|
162
|
+
const afterKey = text.slice(keyIndex + '"suggested_session_name"'.length);
|
|
163
|
+
const valueMatch = /^\s*:\s*("(?:[^"\\]|\\.)*")/.exec(afterKey);
|
|
164
|
+
if (!valueMatch || valueMatch[1] !== quotedName) return false;
|
|
165
|
+
return MINT_FLAG_PATTERN.test(text);
|
|
166
|
+
}
|
|
167
|
+
|
|
110
168
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
169
|
+
* A leading fenced block whose only line is the suggested name.
|
|
170
|
+
*
|
|
171
|
+
* What the contract protects is what the operator sees: the reply opens with a
|
|
172
|
+
* code block containing exactly the title, so it renders as one clean
|
|
173
|
+
* copy-pasteable line. The fence's info string changes none of that, so any
|
|
174
|
+
* single-word language tag passes. Restricting it to `text`/`plaintext` meant a
|
|
175
|
+
* model that wrote ```txt or ```markdown produced a display the operator could
|
|
176
|
+
* read perfectly while the latch refused to close — and since nothing else can
|
|
177
|
+
* close a Cursor latch, that mismatch stranded the whole session.
|
|
178
|
+
*
|
|
179
|
+
* Still exact in every load-bearing way: the block is the first user-facing
|
|
180
|
+
* content, holds one line, and that line equals the name. The backreference
|
|
181
|
+
* requires the closing fence to match the opening run, so a longer fence cannot
|
|
182
|
+
* be closed by a shorter one.
|
|
114
183
|
*/
|
|
184
|
+
const LEADING_SESSION_NAME_BLOCK =
|
|
185
|
+
/^(`{3,})[ \t]*[A-Za-z0-9_.+#-]*[ \t]*\n([^\n]*)\n\1[ \t]*(?:\n|$)/;
|
|
186
|
+
|
|
115
187
|
export function assistantTextStartsWithSessionNameBlock(text: string, name: string): boolean {
|
|
116
188
|
if (!text || !name) return false;
|
|
117
189
|
const normalized = text.replace(/\r\n?/g, "\n").trimStart();
|
|
118
|
-
|
|
119
|
-
return match?.[1] === name;
|
|
190
|
+
return LEADING_SESSION_NAME_BLOCK.exec(normalized)?.[2] === name;
|
|
120
191
|
}
|
|
121
192
|
|
|
122
193
|
/**
|
|
@@ -33,6 +33,13 @@ export interface Heartbeat {
|
|
|
33
33
|
session_name_seen_at?: string;
|
|
34
34
|
/** The specific suggested name proven present by the sighting stamp. */
|
|
35
35
|
session_name_seen_for?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The suggested name the agent was last actually instructed to display.
|
|
38
|
+
* Usually identical to `suggested_session_name`; it differs only when the
|
|
39
|
+
* title changed after the instruction went out, and a display of it still
|
|
40
|
+
* counts so that drift cannot strand the latch.
|
|
41
|
+
*/
|
|
42
|
+
session_name_display_requested_for?: string;
|
|
36
43
|
last_status_at?: string;
|
|
37
44
|
current_turn_id?: string;
|
|
38
45
|
parent_instance_id?: string;
|
|
@@ -180,6 +180,29 @@ export function stampSessionNameSeen(
|
|
|
180
180
|
}));
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Remember which title the agent was actually asked to display.
|
|
185
|
+
*
|
|
186
|
+
* The gate can only ask for a display through the PostToolUse instruction, and
|
|
187
|
+
* the title it names can change afterwards (an assigned-name rewrite of an
|
|
188
|
+
* `Agent unknown - ...` suggestion, a lifecycle re-mint, a rebuilt cache that
|
|
189
|
+
* re-minted from the current task). Without this record the agent's correct
|
|
190
|
+
* display of the title it was handed matches nothing, the latch never closes,
|
|
191
|
+
* and on Cursor no later surface can repair it.
|
|
192
|
+
*/
|
|
193
|
+
export function stampSessionNameRequested(
|
|
194
|
+
coordRoot: string,
|
|
195
|
+
instanceId: string,
|
|
196
|
+
name: string,
|
|
197
|
+
): Heartbeat | null {
|
|
198
|
+
if (!name) return null;
|
|
199
|
+
return mutate(coordRoot, instanceId, (hb) =>
|
|
200
|
+
hb.session_name_display_requested_for === name
|
|
201
|
+
? hb
|
|
202
|
+
: { ...hb, session_name_display_requested_for: name },
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
183
206
|
export function releaseClaim(
|
|
184
207
|
coordRoot: string,
|
|
185
208
|
instanceId: string,
|
|
@@ -197,6 +197,7 @@ export function projectHeartbeatV3(
|
|
|
197
197
|
suggested_session_name: cache?.suggested_session_name,
|
|
198
198
|
session_name_seen_at: cache?.session_name_seen_at,
|
|
199
199
|
session_name_seen_for: cache?.session_name_seen_for,
|
|
200
|
+
session_name_display_requested_for: cache?.session_name_display_requested_for,
|
|
200
201
|
workflow_run_id: generation.run_id,
|
|
201
202
|
workflow_agent_id: generation.workflow_agent_id,
|
|
202
203
|
parent_instance_id: parent ? nativeInstanceIdV3(parent.instance_id) : undefined,
|
|
@@ -107,6 +107,11 @@ function materializeLiveCoordinationHeartbeat(
|
|
|
107
107
|
...(current.session_name_seen_at
|
|
108
108
|
? { session_name_seen_at: current.session_name_seen_at }
|
|
109
109
|
: {}),
|
|
110
|
+
...(current.session_name_display_requested_for
|
|
111
|
+
? {
|
|
112
|
+
session_name_display_requested_for: current.session_name_display_requested_for,
|
|
113
|
+
}
|
|
114
|
+
: {}),
|
|
110
115
|
}
|
|
111
116
|
: {};
|
|
112
117
|
const materialized: V3HeartbeatMaterialization = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Read-only artifact layout constants safe for browser-side server modules. */
|
|
2
2
|
export const ARTIFACTS_DIR = ".harnery/artifacts";
|
|
3
3
|
export const ARTIFACT_MANIFEST = ".harnery-artifact.json";
|
|
4
|
-
export const ARTIFACT_SCHEMA_VERSION =
|
|
4
|
+
export const ARTIFACT_SCHEMA_VERSION = 2 as const;
|