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,44 @@
|
|
|
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
|
+
import { type ReconcileSessionFinalizationOptionsV3, type ReconcileSessionFinalizationResultV3 } from "./session-finalizer-v3.js";
|
|
24
|
+
export interface ReconcileCoordinationResultV3 extends ReconcileSessionFinalizationResultV3 {
|
|
25
|
+
/** Heartbeat cache files deleted from `.harnery/active` by the sweep. */
|
|
26
|
+
swept_heartbeats: number;
|
|
27
|
+
/** Orphaned `.harnery/pid-map` entries deleted by the sweep. */
|
|
28
|
+
swept_pidmaps: number;
|
|
29
|
+
/** Dead-owner `.last-peer-hash.*` files deleted by the sweep. */
|
|
30
|
+
swept_peer_hashes: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sweep the disposable coordination cache, then reconcile session
|
|
34
|
+
* finalization, and report both sets of counts.
|
|
35
|
+
*
|
|
36
|
+
* The sweep owns its own safety contract (configured coordination freshness
|
|
37
|
+
* for valid rows, an additional stale-mtime gate for malformed or missing
|
|
38
|
+
* timestamps, and a durable audit record before any deletion, failing closed
|
|
39
|
+
* when neither can be written). Nothing here loosens it, and a sweep failure
|
|
40
|
+
* never blocks finalization: the cache is a projection that heals on the
|
|
41
|
+
* owning agent's next tool call, while the V3 ledger is authoritative.
|
|
42
|
+
*/
|
|
43
|
+
export declare function reconcileCoordinationV3(coordRoot: string, options?: ReconcileSessionFinalizationOptionsV3): ReconcileCoordinationResultV3;
|
|
44
|
+
//# sourceMappingURL=reconcile-coordination-v3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile-coordination-v3.d.ts","sourceRoot":"","sources":["../../../src/core/agents/reconcile-coordination-v3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,KAAK,qCAAqC,EAC1C,KAAK,oCAAoC,EAE1C,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,6BAA8B,SAAQ,oCAAoC;IACzF,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qCAA0C,GAClD,6BAA6B,CAuB/B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
import { reconcileSessionFinalizationV3, } from "./session-finalizer-v3.js";
|
|
24
|
+
import { staleSweep } from "./state/stale-sweep.js";
|
|
25
|
+
/**
|
|
26
|
+
* Sweep the disposable coordination cache, then reconcile session
|
|
27
|
+
* finalization, and report both sets of counts.
|
|
28
|
+
*
|
|
29
|
+
* The sweep owns its own safety contract (configured coordination freshness
|
|
30
|
+
* for valid rows, an additional stale-mtime gate for malformed or missing
|
|
31
|
+
* timestamps, and a durable audit record before any deletion, failing closed
|
|
32
|
+
* when neither can be written). Nothing here loosens it, and a sweep failure
|
|
33
|
+
* never blocks finalization: the cache is a projection that heals on the
|
|
34
|
+
* owning agent's next tool call, while the V3 ledger is authoritative.
|
|
35
|
+
*/
|
|
36
|
+
export function reconcileCoordinationV3(coordRoot, options = {}) {
|
|
37
|
+
let sweptHeartbeats = 0;
|
|
38
|
+
let sweptPidmaps = 0;
|
|
39
|
+
let sweptPeerHashes = 0;
|
|
40
|
+
const sweepDiagnostics = [];
|
|
41
|
+
try {
|
|
42
|
+
const sweep = staleSweep(coordRoot);
|
|
43
|
+
sweptHeartbeats = sweep.heartbeatsRemoved.length;
|
|
44
|
+
sweptPidmaps = sweep.pidmapsRemoved;
|
|
45
|
+
sweptPeerHashes = sweep.peerHashesRemoved;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
sweepDiagnostics.push(`stale_sweep_failed:${error instanceof Error ? error.message : String(error)}`);
|
|
49
|
+
}
|
|
50
|
+
const result = reconcileSessionFinalizationV3(coordRoot, options);
|
|
51
|
+
return {
|
|
52
|
+
...result,
|
|
53
|
+
diagnostics: [...sweepDiagnostics, ...result.diagnostics],
|
|
54
|
+
swept_heartbeats: sweptHeartbeats,
|
|
55
|
+
swept_pidmaps: sweptPidmaps,
|
|
56
|
+
swept_peer_hashes: sweptPeerHashes,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -49,6 +49,11 @@ export interface PromptContextOpts {
|
|
|
49
49
|
/** When true, append the non-deduplicated Codex status-footer reminder.
|
|
50
50
|
* The caller enables this only for human-facing Codex sessions. */
|
|
51
51
|
statusFooterNudge?: boolean;
|
|
52
|
+
/** Adapter id this prompt is being rendered for. Gates the mailbox drain:
|
|
53
|
+
* an adapter that cannot receive UserPromptSubmit context must not consume
|
|
54
|
+
* messages here, or they are removed from the queue and never shown. When
|
|
55
|
+
* omitted, delivery is attempted (the default route can receive). */
|
|
56
|
+
adapter?: string;
|
|
52
57
|
/** Adapter id whose Stop hook ENFORCES the end-of-turn ritual (task_set +
|
|
53
58
|
* status check). When set, append a fresh per-prompt reminder of that
|
|
54
59
|
* contract so the model satisfies it before ending the turn instead of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;
|
|
1
|
+
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA+CH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8EAC0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;kFAC8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;kFAC8E;IAC9E,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;yEAGqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;wBAIoB;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAqFnE"}
|
|
@@ -37,10 +37,12 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, w
|
|
|
37
37
|
import { join } from "node:path";
|
|
38
38
|
import { coordEnv } from "../../../lib/env.js";
|
|
39
39
|
import { endOfTurnStatusCommand, hostPromptReminder, resolveBinName } from "../../config.js";
|
|
40
|
+
import { canReceiveContext } from "../../hooks/adapter/output.js";
|
|
40
41
|
import { readRemoteMachines } from "../../presence/index.js";
|
|
42
|
+
import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
|
|
41
43
|
import { sessionNameDisplayBlock, sessionNameDisplayPending } from "../session-name-display.js";
|
|
42
44
|
import { readLiveCoordinationRow, readLiveCoordinationRows, } from "../state/live-coordination-view.js";
|
|
43
|
-
import { formatPendingCouncils } from "./session-context.js";
|
|
45
|
+
import { formatPendingCouncils, recordDeliveredMessages } from "./session-context.js";
|
|
44
46
|
/**
|
|
45
47
|
* Build the combined UserPromptSubmit additionalContext string. Returns "" when
|
|
46
48
|
* nothing has changed since the last call (so the caller can skip the JSON emit).
|
|
@@ -50,19 +52,39 @@ import { formatPendingCouncils } from "./session-context.js";
|
|
|
50
52
|
* councils are pending, matching the bash behavior).
|
|
51
53
|
*/
|
|
52
54
|
export function renderPromptContext(opts) {
|
|
53
|
-
const { coordRoot, instanceId, sessionId, agentName, sessionNameNudge, taskNudge, hostPromptReminder: promptReminderEnabled, statusFooterNudge, turnRitualNudge, } = opts;
|
|
55
|
+
const { coordRoot, instanceId, sessionId, agentName, sessionNameNudge, taskNudge, hostPromptReminder: promptReminderEnabled, statusFooterNudge, turnRitualNudge, adapter, } = opts;
|
|
54
56
|
const sections = [];
|
|
55
|
-
//
|
|
57
|
+
// The hook route does not pass --name, so resolve it from the heartbeat.
|
|
58
|
+
// Every name-addressed section below (messages, councils) is silently
|
|
59
|
+
// skipped without it.
|
|
60
|
+
const resolvedName = agentName ?? readLiveCoordinationRow(coordRoot, instanceId)?.name;
|
|
61
|
+
// 1. Peer messages. Drained first and never deduped: a message is delivered
|
|
62
|
+
// exactly once, and it is the one section here that carries new information
|
|
63
|
+
// from another agent rather than a reminder.
|
|
64
|
+
//
|
|
65
|
+
// Draining is gated on the adapter being able to receive this event's
|
|
66
|
+
// context. Cursor cannot, so draining there would empty the queue into output
|
|
67
|
+
// that is discarded; those sessions get their messages at SessionStart, which
|
|
68
|
+
// Cursor does deliver.
|
|
69
|
+
const canDeliverHere = !adapter || canReceiveContext(adapter, "UserPromptSubmit");
|
|
70
|
+
if (resolvedName && canDeliverHere) {
|
|
71
|
+
const delivered = drainMailbox(coordRoot, resolvedName);
|
|
72
|
+
if (delivered.length > 0) {
|
|
73
|
+
recordDeliveredMessages(instanceId, delivered);
|
|
74
|
+
sections.push(formatMailboxDelivery(coordRoot, delivered));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 2. Peer table with hash dedup.
|
|
56
78
|
const peerTable = computePeerTableIfChanged(coordRoot, instanceId, sessionId);
|
|
57
79
|
if (peerTable)
|
|
58
80
|
sections.push(peerTable);
|
|
59
|
-
//
|
|
60
|
-
if (
|
|
61
|
-
const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId,
|
|
81
|
+
// 3. Council pending with hash dedup.
|
|
82
|
+
if (resolvedName) {
|
|
83
|
+
const councilMsg = computeCouncilPendingIfChanged(coordRoot, instanceId, resolvedName);
|
|
62
84
|
if (councilMsg)
|
|
63
85
|
sections.push(councilMsg);
|
|
64
86
|
}
|
|
65
|
-
//
|
|
87
|
+
// 4. First-session naming for every adapter, plus unset/stale-task reminders
|
|
66
88
|
// for Cursor/Codex. One state machine avoids a second generic "task unset"
|
|
67
89
|
// reminder immediately after the first-session reminder has been deduped.
|
|
68
90
|
if (sessionNameNudge || taskNudge) {
|
|
@@ -73,7 +95,7 @@ export function renderPromptContext(opts) {
|
|
|
73
95
|
if (nudgeMsg)
|
|
74
96
|
sections.push(nudgeMsg);
|
|
75
97
|
}
|
|
76
|
-
//
|
|
98
|
+
// 5. A supported adapter gets the host reminder on every prompt. The text is
|
|
77
99
|
// intentionally not hash-deduped or persisted into runtime state: freshness
|
|
78
100
|
// is the feature, and the project config remains its only durable copy.
|
|
79
101
|
if (promptReminderEnabled) {
|
|
@@ -81,7 +103,7 @@ export function renderPromptContext(opts) {
|
|
|
81
103
|
if (reminder)
|
|
82
104
|
sections.push(reminder);
|
|
83
105
|
}
|
|
84
|
-
//
|
|
106
|
+
// 6. Codex gets this on every prompt. It is intentionally not hash-deduped:
|
|
85
107
|
// the reminder must be fresh in the model's context when it writes the reply.
|
|
86
108
|
// Missing it is harmless because Codex Stop enforcement remains observe-only.
|
|
87
109
|
if (statusFooterNudge) {
|
|
@@ -89,7 +111,7 @@ export function renderPromptContext(opts) {
|
|
|
89
111
|
if (statusMsg)
|
|
90
112
|
sections.push(statusMsg);
|
|
91
113
|
}
|
|
92
|
-
//
|
|
114
|
+
// 7. Adapters whose Stop hook ENFORCES the turn ritual get a fresh reminder
|
|
93
115
|
// on every prompt. Reminding before the reply is drafted is far cheaper than
|
|
94
116
|
// the bounce-and-retry the Stop hook otherwise forces: enforcement-only
|
|
95
117
|
// compliance was measured missing on a double-digit share of turns. Not
|
|
@@ -113,9 +135,8 @@ function renderTurnRitualReminder(coordRoot, selfInstanceId, adapter) {
|
|
|
113
135
|
return "";
|
|
114
136
|
const bin = resolveBinName(coordRoot);
|
|
115
137
|
const statusCommand = endOfTurnStatusCommand(coordRoot);
|
|
116
|
-
// Claude Code
|
|
117
|
-
|
|
118
|
-
const paste = adapter === "claude-code"
|
|
138
|
+
// Claude Code and Cursor can both enforce completed assistant reply text.
|
|
139
|
+
const paste = adapter === "claude-code" || adapter === "cursor"
|
|
119
140
|
? " and paste its output verbatim in a fenced code block at the end of your reply"
|
|
120
141
|
: "";
|
|
121
142
|
return (`Turn ritual (Stop-enforced): on any turn that uses tools, run \`${bin} agents set-task "<short focus>"\` at some point during the turn (required every turn, even when the focus is unchanged; pass an empty string when there is no meaningful focus), then make \`${statusCommand}\` your final tool call${paste}. ` +
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* string.
|
|
8
8
|
*/
|
|
9
9
|
import { statSync } from "node:fs";
|
|
10
|
+
import { type MailboxMessageV1 } from "../mailbox.js";
|
|
10
11
|
export interface RenderOpts {
|
|
11
12
|
coordRoot: string;
|
|
12
13
|
instanceId: string;
|
|
@@ -21,6 +22,13 @@ export interface RenderOpts {
|
|
|
21
22
|
* string (or "" if there's nothing to say).
|
|
22
23
|
*/
|
|
23
24
|
export declare function renderSessionContext(opts: RenderOpts): string;
|
|
25
|
+
/**
|
|
26
|
+
* Write delivered peer messages into the recipient's own journal, so they
|
|
27
|
+
* survive context compaction the way any other coordination breadcrumb does.
|
|
28
|
+
* A message the sender already journaled (the recipient was live when it was
|
|
29
|
+
* sent) is skipped to avoid a duplicate entry.
|
|
30
|
+
*/
|
|
31
|
+
export declare function recordDeliveredMessages(instanceId: string, messages: readonly MailboxMessageV1[]): void;
|
|
24
32
|
/**
|
|
25
33
|
* Returns a list of commit-guard wiring issues (empty when wired). Portable
|
|
26
34
|
* across host projects: it asserts the FUNCTIONAL property ("does this repo's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;AAM1E,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAoB3F,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;+BAC2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAwF7D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,IAAI,CAUN;AAkID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvD;AA0DD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAiDlF;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iBAAW,CAAC"}
|
|
@@ -11,7 +11,9 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { resolveBinName, resolveHooksSetupHint } from "../../config.js";
|
|
13
13
|
import { harneryVersion, loadAdapterWiring } from "../../hooks/adapter/wiring.js";
|
|
14
|
+
import { appendEntry } from "../../journal/index.js";
|
|
14
15
|
import { readRemoteMachines } from "../../presence/index.js";
|
|
16
|
+
import { drainMailbox, formatMailboxDelivery } from "../mailbox.js";
|
|
15
17
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.js";
|
|
16
18
|
import { readForkParent } from "../state/names.js";
|
|
17
19
|
/**
|
|
@@ -50,13 +52,23 @@ export function renderSessionContext(opts) {
|
|
|
50
52
|
if (isLinkedWorktree(coordRoot)) {
|
|
51
53
|
messages.push(`Running inside worktree ${process.cwd()}. The coord layer is scoped to this worktree only; use \`${resolveBinName(coordRoot)} worktree diff\` to check for conflicts against sibling worktrees.`);
|
|
52
54
|
}
|
|
53
|
-
// 3.
|
|
55
|
+
// 3. Peer messages queued for this name while no session held it. Draining
|
|
56
|
+
// here is what makes `agents ping` safe to use on a dormant agent: the sender
|
|
57
|
+
// never has to know whether the recipient is running.
|
|
58
|
+
if (agentName) {
|
|
59
|
+
const delivered = drainMailbox(coordRoot, agentName);
|
|
60
|
+
if (delivered.length > 0) {
|
|
61
|
+
recordDeliveredMessages(instanceId, delivered);
|
|
62
|
+
messages.push(formatMailboxDelivery(coordRoot, delivered));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 4. Council invites
|
|
54
66
|
if (agentName) {
|
|
55
67
|
const councilMsg = formatPendingCouncils(coordRoot, agentName);
|
|
56
68
|
if (councilMsg)
|
|
57
69
|
messages.push(councilMsg);
|
|
58
70
|
}
|
|
59
|
-
//
|
|
71
|
+
// 5. Commit-guard wiring check
|
|
60
72
|
const wiringIssues = checkWiring(coordRoot);
|
|
61
73
|
if (wiringIssues.length > 0) {
|
|
62
74
|
const hint = resolveHooksSetupHint(coordRoot);
|
|
@@ -66,7 +78,7 @@ export function renderSessionContext(opts) {
|
|
|
66
78
|
const wiringSummary = `Coordination hooks are NOT wired: the E-guard will not block conflicting commits, and post-commit claim pruning will not run. ${fix} Detected:\n${wiringIssues.map((i) => ` - ${i}`).join("\n")}`;
|
|
67
79
|
messages.push(wiringSummary);
|
|
68
80
|
}
|
|
69
|
-
//
|
|
81
|
+
// 6. Adapter-hook drift: a harnery upgrade changed the hook set, but this
|
|
70
82
|
// project's settings file hasn't been re-wired. Only fires for a adapter the
|
|
71
83
|
// project already opted into (≥1 hook wired), so it never nags a project that
|
|
72
84
|
// simply has a settings file. Remedy is always `<bin> init` (idempotent).
|
|
@@ -89,6 +101,25 @@ export function renderSessionContext(opts) {
|
|
|
89
101
|
}
|
|
90
102
|
return messages.join("\n\n");
|
|
91
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Write delivered peer messages into the recipient's own journal, so they
|
|
106
|
+
* survive context compaction the way any other coordination breadcrumb does.
|
|
107
|
+
* A message the sender already journaled (the recipient was live when it was
|
|
108
|
+
* sent) is skipped to avoid a duplicate entry.
|
|
109
|
+
*/
|
|
110
|
+
export function recordDeliveredMessages(instanceId, messages) {
|
|
111
|
+
for (const m of messages) {
|
|
112
|
+
if (m.journaled)
|
|
113
|
+
continue;
|
|
114
|
+
try {
|
|
115
|
+
appendEntry(instanceId, "handoff", `from agent-${m.from_name}: ${m.body}`);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// A journal write failure must never cost the delivery itself; the
|
|
119
|
+
// message is already in the rendered context the model reads.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
92
123
|
/** One-line-per-agent view of sessions on other machines (presence refs). */
|
|
93
124
|
function formatRemoteMachines(coordRoot) {
|
|
94
125
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAoBH,UAAU,QAAQ;IAChB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;wEACoE;IACpE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,mBAAmB,CAgIhG"}
|
|
@@ -24,6 +24,7 @@ import { appendFileSync, existsSync } from "node:fs";
|
|
|
24
24
|
import { basename, dirname, join } from "node:path";
|
|
25
25
|
import { coordFreshnessSeconds } from "../../config.js";
|
|
26
26
|
import { resolveOwner } from "../../hooks/resolve/owner.js";
|
|
27
|
+
import { recordLiveClaimChangeV3 } from "../live-authority-v3.js";
|
|
27
28
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.js";
|
|
28
29
|
import { instanceHasLivePid } from "../state/pidmap.js";
|
|
29
30
|
export function evaluateCommit(coordRoot, req) {
|
|
@@ -120,6 +121,14 @@ export function evaluateCommit(coordRoot, req) {
|
|
|
120
121
|
suppressed_self_attribution: true,
|
|
121
122
|
};
|
|
122
123
|
}
|
|
124
|
+
// A blocked commit proves this session has work in every staged path, even
|
|
125
|
+
// when the edit hook never acquired a claim (for example, a shell edit made
|
|
126
|
+
// just before another session claimed the same file). Keep those paths on
|
|
127
|
+
// the blocked session's durable claim set. The original holder's later
|
|
128
|
+
// commit will then see the overlap instead of silently sweeping this
|
|
129
|
+
// session's working-tree hunks. A successful later commit releases clean
|
|
130
|
+
// paths through the existing post-commit hook.
|
|
131
|
+
retainBlockedStagedPaths(coordRoot, me, mySession, req.staged_paths, peers);
|
|
123
132
|
return {
|
|
124
133
|
allow: false,
|
|
125
134
|
exit_code: 1,
|
|
@@ -132,6 +141,37 @@ export function evaluateCommit(coordRoot, req) {
|
|
|
132
141
|
" active agents:",
|
|
133
142
|
};
|
|
134
143
|
}
|
|
144
|
+
function retainBlockedStagedPaths(coordRoot, instanceId, sessionId, stagedPaths, peers) {
|
|
145
|
+
if (instanceId === "__unattributed__")
|
|
146
|
+
return;
|
|
147
|
+
const self = peers.find((peer) => peer.instance_id === instanceId);
|
|
148
|
+
if (!self)
|
|
149
|
+
return;
|
|
150
|
+
for (const path of stagedPaths) {
|
|
151
|
+
try {
|
|
152
|
+
recordLiveClaimChangeV3({
|
|
153
|
+
coordRoot,
|
|
154
|
+
owner: instanceId,
|
|
155
|
+
nativeSessionId: self.session_id ?? sessionId,
|
|
156
|
+
adapter: adapterFromPlatform(self.platform),
|
|
157
|
+
operation: "acquired",
|
|
158
|
+
path,
|
|
159
|
+
access: "write",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// The commit is already blocked. Coordination remains fail-open for
|
|
164
|
+
// authority-write failures so a damaged cache cannot brick Git.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function adapterFromPlatform(platform) {
|
|
169
|
+
if (platform === "cursor")
|
|
170
|
+
return "cursor";
|
|
171
|
+
if (platform === "codex")
|
|
172
|
+
return "codex";
|
|
173
|
+
return "claude-code";
|
|
174
|
+
}
|
|
135
175
|
function findOverlap(staged, files, coordRoot, stagedIsGitlink) {
|
|
136
176
|
for (const claimed of files) {
|
|
137
177
|
if (claimed === staged)
|
|
@@ -188,13 +228,22 @@ function isGitlinkInIndex(coordRoot, path) {
|
|
|
188
228
|
return result.stdout.trim().startsWith("160000 ");
|
|
189
229
|
}
|
|
190
230
|
function isHolderSelfAttributed(coordRoot, holderId, stagedSet, peers) {
|
|
231
|
+
const holder = peers.find((p) => p.instance_id === holderId);
|
|
232
|
+
// Windows-hosted Codex sessions cross a process boundary into WSL, so they
|
|
233
|
+
// deliberately have no usable PID anchor. Their canonical V3 session row is
|
|
234
|
+
// the foreign-identity evidence instead. Treating that row as a transient
|
|
235
|
+
// self identity would suppress the symmetric claim retained after a blocked
|
|
236
|
+
// commit and reopen the working-tree sweep this guard is meant to stop.
|
|
237
|
+
if (holder?.platform === "codex" && holder.session_id) {
|
|
238
|
+
debugGate(coordRoot, { holder: holderId, gate: "B", reason: "canonical_codex_session" });
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
191
241
|
// Gate B (cheaper): live foreign pid-map entry blocks self-attribution.
|
|
192
242
|
if (holderHasLiveForeignPid(coordRoot, holderId)) {
|
|
193
243
|
debugGate(coordRoot, { holder: holderId, gate: "B", reason: "live_pid_anchor" });
|
|
194
244
|
return false;
|
|
195
245
|
}
|
|
196
246
|
// Gate A: every held path is either in the staged set or already clean in HEAD.
|
|
197
|
-
const holder = peers.find((p) => p.instance_id === holderId);
|
|
198
247
|
if (!holder) {
|
|
199
248
|
debugGate(coordRoot, { holder: holderId, gate: "A", reason: "holder_not_found" });
|
|
200
249
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop-hook.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAM3D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAInE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa,CA6BvF;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,aAAa,
|
|
1
|
+
{"version":3,"file":"stop-hook.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAM3D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAInE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa,CA6BvF;AAED,oFAAoF;AACpF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,aAAa,CAiIf"}
|
|
@@ -91,17 +91,13 @@ export function evaluateStopHookV3Events(coordRoot, req, events) {
|
|
|
91
91
|
.map((event) => observedBoolean(event.payload.ritual?.status_box_present_strict))
|
|
92
92
|
.filter((value) => value !== undefined);
|
|
93
93
|
const currentStrictBox = claudeContinuation ? req.status_box_present_strict : undefined;
|
|
94
|
-
if (
|
|
94
|
+
if (latestBox === undefined && currentStrictBox === undefined) {
|
|
95
95
|
return evidenceUnavailable("turn.completed has no observed V3 assistant-text status-box result; Stop remains fail-open");
|
|
96
96
|
}
|
|
97
|
-
const ackPresent =
|
|
98
|
-
?
|
|
99
|
-
:
|
|
100
|
-
|
|
101
|
-
: latestBox === true;
|
|
102
|
-
const ackBlock = req.adapter === "cursor"
|
|
103
|
-
? () => rule13Block(coordRoot, req.session_id)
|
|
104
|
-
: () => rule23Block(coordRoot, req.session_id);
|
|
97
|
+
const ackPresent = claudeContinuation
|
|
98
|
+
? strictBoxObservations.includes(true) || currentStrictBox === true
|
|
99
|
+
: latestBox === true;
|
|
100
|
+
const ackBlock = () => rule23Block(coordRoot, req.session_id);
|
|
105
101
|
// Pure prose turns keep the human-visible acknowledgement but do not require
|
|
106
102
|
// task or final-status mutations. This preserves the pre-V3 policy exactly.
|
|
107
103
|
if (!usedTool) {
|
|
@@ -260,7 +256,7 @@ function sessionIdSuffix(sessionId) {
|
|
|
260
256
|
* The verdict keeps the first failure's rule id, so the `blocked_rule`
|
|
261
257
|
* telemetry and the adapter's `rule=` stderr contract are unchanged; the
|
|
262
258
|
* reason enumerates the rest so a single continuation can repair all of them.
|
|
263
|
-
*
|
|
259
|
+
* Duplicate rules are folded so one continuation repairs every missing signal.
|
|
264
260
|
*/
|
|
265
261
|
function mergeStopBlocks(blocks) {
|
|
266
262
|
let primary;
|
|
@@ -450,7 +450,10 @@ function observeCascades(coordRoot, route, records, events, lastByGeneration, no
|
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
if (event.event_type === "lifecycle.sweep_observed" &&
|
|
453
|
-
|
|
453
|
+
// stale-sweep.ts emits the observation as `stale_heartbeat`; the
|
|
454
|
+
// finalization request it produces is the one named `stale_sweep`.
|
|
455
|
+
// Matching the request name here made this branch unreachable.
|
|
456
|
+
event.payload.observation === "stale_heartbeat") {
|
|
454
457
|
const record = records.find(({ state }) => !state.terminal && state.instance_id === event.payload.subject_instance_id);
|
|
455
458
|
const last = record ? lastByGeneration.get(record.state.generation_id) : undefined;
|
|
456
459
|
if (!record?.state.last_event_id || !last)
|
|
@@ -7,9 +7,29 @@
|
|
|
7
7
|
export interface SessionNameDisplayState {
|
|
8
8
|
suggested_session_name?: string;
|
|
9
9
|
session_name_seen_for?: string;
|
|
10
|
+
session_name_display_requested_for?: string;
|
|
10
11
|
}
|
|
11
12
|
export declare const SESSION_NAME_DISPLAY_NOTE = "Next, before any prose or another tool call, send suggested_session_name verbatim by itself in a fenced code block.";
|
|
12
13
|
export declare function sessionNameDisplayPending(row: SessionNameDisplayState | null | undefined): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Every title whose exact display satisfies the current latch.
|
|
16
|
+
*
|
|
17
|
+
* The pending title always counts. The title the agent was last instructed to
|
|
18
|
+
* display also counts, because the harness — not the agent — is what changed
|
|
19
|
+
* the target after asking. Accepting it costs nothing the operator can see: a
|
|
20
|
+
* clean one-line block did open that reply. Refusing it left Cursor sessions
|
|
21
|
+
* permanently latched, since no Cursor surface can re-open a missed window.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sessionNameDisplayAcceptedNames(row: SessionNameDisplayState | null | undefined): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Match assistant text against any accepted title and report which one closed
|
|
26
|
+
* the latch, so a drifted display can be stamped against the pending title and
|
|
27
|
+
* logged as drift rather than silently discarded.
|
|
28
|
+
*/
|
|
29
|
+
export declare function matchSessionNameDisplay(row: SessionNameDisplayState | null | undefined, text: string | undefined, startsWithBlock?: (text: string, expectedName: string) => boolean): {
|
|
30
|
+
pending: string;
|
|
31
|
+
displayed: string;
|
|
32
|
+
} | null;
|
|
13
33
|
export declare function sessionNameDisplayBlock(name: string): string;
|
|
14
34
|
export declare function sessionNameDisplayInstruction(name: string): string;
|
|
15
35
|
export declare function sessionNameDisplayRecoveryInstruction(name: string, binName: string): string;
|
|
@@ -26,11 +46,6 @@ export declare function sessionNameDisplayRecoveryInstruction(name: string, binN
|
|
|
26
46
|
* activating the display ritual.
|
|
27
47
|
*/
|
|
28
48
|
export declare function toolResponseMintedSessionName(response: unknown, name: string): boolean;
|
|
29
|
-
/**
|
|
30
|
-
* Accept the exact unlabelled block Harnery requests. `text` and `plaintext`
|
|
31
|
-
* are tolerated for older prompts, but the code block may contain only the
|
|
32
|
-
* suggested name and must be the first non-whitespace user-facing content.
|
|
33
|
-
*/
|
|
34
49
|
export declare function assistantTextStartsWithSessionNameBlock(text: string, name: string): boolean;
|
|
35
50
|
/**
|
|
36
51
|
* The display latch must not block the two coordination commands that can
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kCAAkC,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED,eAAO,MAAM,yBAAyB,wHACiF,CAAC;AAExH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAIf;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,EAAE,CAKV;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,EAC/C,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,eAAe,GAAE,CACf,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB,OAAiD,GACrD;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO/C;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlE;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtF;AAqFD,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAI3F;AAUD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAmBT"}
|
|
@@ -11,6 +11,37 @@ export function sessionNameDisplayPending(row) {
|
|
|
11
11
|
return null;
|
|
12
12
|
return name;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Every title whose exact display satisfies the current latch.
|
|
16
|
+
*
|
|
17
|
+
* The pending title always counts. The title the agent was last instructed to
|
|
18
|
+
* display also counts, because the harness — not the agent — is what changed
|
|
19
|
+
* the target after asking. Accepting it costs nothing the operator can see: a
|
|
20
|
+
* clean one-line block did open that reply. Refusing it left Cursor sessions
|
|
21
|
+
* permanently latched, since no Cursor surface can re-open a missed window.
|
|
22
|
+
*/
|
|
23
|
+
export function sessionNameDisplayAcceptedNames(row) {
|
|
24
|
+
const pending = sessionNameDisplayPending(row);
|
|
25
|
+
if (!pending)
|
|
26
|
+
return [];
|
|
27
|
+
const requested = row?.session_name_display_requested_for;
|
|
28
|
+
return requested && requested !== pending ? [pending, requested] : [pending];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Match assistant text against any accepted title and report which one closed
|
|
32
|
+
* the latch, so a drifted display can be stamped against the pending title and
|
|
33
|
+
* logged as drift rather than silently discarded.
|
|
34
|
+
*/
|
|
35
|
+
export function matchSessionNameDisplay(row, text, startsWithBlock = assistantTextStartsWithSessionNameBlock) {
|
|
36
|
+
if (!text)
|
|
37
|
+
return null;
|
|
38
|
+
const accepted = sessionNameDisplayAcceptedNames(row);
|
|
39
|
+
const pending = accepted[0];
|
|
40
|
+
if (!pending)
|
|
41
|
+
return null;
|
|
42
|
+
const displayed = accepted.find((candidate) => startsWithBlock(text, candidate));
|
|
43
|
+
return displayed ? { pending, displayed } : null;
|
|
44
|
+
}
|
|
14
45
|
export function sessionNameDisplayBlock(name) {
|
|
15
46
|
return `\`\`\`\n${name}\n\`\`\``;
|
|
16
47
|
}
|
|
@@ -62,10 +93,16 @@ function responseContainsSessionNameMint(value, name, seen, depth) {
|
|
|
62
93
|
}
|
|
63
94
|
}
|
|
64
95
|
catch {
|
|
65
|
-
//
|
|
96
|
+
// Fall through to the textual check below.
|
|
66
97
|
}
|
|
67
98
|
}
|
|
68
|
-
|
|
99
|
+
// The mint command's stdout often reaches the transcript mangled: cut
|
|
100
|
+
// short by `cut`/`head -c`, or with a tail-truncated first line. Whole-JSON
|
|
101
|
+
// parsing then fails even though the name and the mint flag are both
|
|
102
|
+
// legible, and a session that did mint can never latch. Accept the text
|
|
103
|
+
// when the exact quoted name sits under its key AND a mint flag is true;
|
|
104
|
+
// ordinary status output carries the name without any flag.
|
|
105
|
+
return textCarriesSessionNameMint(value, name);
|
|
69
106
|
}
|
|
70
107
|
if (typeof value !== "object")
|
|
71
108
|
return false;
|
|
@@ -82,17 +119,40 @@ function responseContainsSessionNameMint(value, name, seen, depth) {
|
|
|
82
119
|
}
|
|
83
120
|
return Object.values(row).some((item) => responseContainsSessionNameMint(item, name, seen, depth + 1));
|
|
84
121
|
}
|
|
122
|
+
const MINT_FLAG_PATTERN = /"(?:first_of_session|name_reminted|session_name_retry)"\s*:\s*true\b/;
|
|
123
|
+
function textCarriesSessionNameMint(text, name) {
|
|
124
|
+
const quotedName = JSON.stringify(name);
|
|
125
|
+
const keyIndex = text.indexOf('"suggested_session_name"');
|
|
126
|
+
if (keyIndex < 0)
|
|
127
|
+
return false;
|
|
128
|
+
const afterKey = text.slice(keyIndex + '"suggested_session_name"'.length);
|
|
129
|
+
const valueMatch = /^\s*:\s*("(?:[^"\\]|\\.)*")/.exec(afterKey);
|
|
130
|
+
if (!valueMatch || valueMatch[1] !== quotedName)
|
|
131
|
+
return false;
|
|
132
|
+
return MINT_FLAG_PATTERN.test(text);
|
|
133
|
+
}
|
|
85
134
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
135
|
+
* A leading fenced block whose only line is the suggested name.
|
|
136
|
+
*
|
|
137
|
+
* What the contract protects is what the operator sees: the reply opens with a
|
|
138
|
+
* code block containing exactly the title, so it renders as one clean
|
|
139
|
+
* copy-pasteable line. The fence's info string changes none of that, so any
|
|
140
|
+
* single-word language tag passes. Restricting it to `text`/`plaintext` meant a
|
|
141
|
+
* model that wrote ```txt or ```markdown produced a display the operator could
|
|
142
|
+
* read perfectly while the latch refused to close — and since nothing else can
|
|
143
|
+
* close a Cursor latch, that mismatch stranded the whole session.
|
|
144
|
+
*
|
|
145
|
+
* Still exact in every load-bearing way: the block is the first user-facing
|
|
146
|
+
* content, holds one line, and that line equals the name. The backreference
|
|
147
|
+
* requires the closing fence to match the opening run, so a longer fence cannot
|
|
148
|
+
* be closed by a shorter one.
|
|
89
149
|
*/
|
|
150
|
+
const LEADING_SESSION_NAME_BLOCK = /^(`{3,})[ \t]*[A-Za-z0-9_.+#-]*[ \t]*\n([^\n]*)\n\1[ \t]*(?:\n|$)/;
|
|
90
151
|
export function assistantTextStartsWithSessionNameBlock(text, name) {
|
|
91
152
|
if (!text || !name)
|
|
92
153
|
return false;
|
|
93
154
|
const normalized = text.replace(/\r\n?/g, "\n").trimStart();
|
|
94
|
-
|
|
95
|
-
return match?.[1] === name;
|
|
155
|
+
return LEADING_SESSION_NAME_BLOCK.exec(normalized)?.[2] === name;
|
|
96
156
|
}
|
|
97
157
|
/**
|
|
98
158
|
* A trailing stream redirect to `/dev/null` or another descriptor. Agents append
|