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,97 @@
|
|
|
1
|
+
const SENSITIVE_KEY =
|
|
2
|
+
/(?:authorization|cookie|credential|password|passwd|private[_-]?key|secret|session[_-]?token|access[_-]?token|refresh[_-]?token|api[_-]?key)/i;
|
|
3
|
+
const OMITTED_CONTENT_KEY =
|
|
4
|
+
/(?:^|[_-])(?:environment|env|prompt|prompts|tool[_-]?payload|tool[_-]?result|transcript|transcripts)(?:$|[_-])/i;
|
|
5
|
+
const HOME_PATH = /(?:[A-Za-z]:\\Users\\[^\\\s]+|\/home\/[^/\s]+|\/Users\/[^/\s]+)/g;
|
|
6
|
+
const BEARER_VALUE = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi;
|
|
7
|
+
const URL_CREDENTIALS = /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi;
|
|
8
|
+
const PRIVATE_KEY = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
|
|
9
|
+
const TOKENISH_VALUE = /\b(?:sk|pk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{16,}\b/g;
|
|
10
|
+
const AWS_ACCESS_KEY = /\bAKIA[A-Z0-9]{16,}\b/g;
|
|
11
|
+
const SENSITIVE_QUERY = /([?&](?:access_token|api_key|key|password|secret|token)=)[^&#\s]+/gi;
|
|
12
|
+
const SENSITIVE_ENV = /(\b[A-Z0-9_]*(?:API_KEY|PASSWORD|SECRET|TOKEN)=)[^\s]+/g;
|
|
13
|
+
const SENSITIVE_ARGUMENT = /(\B--?(?:access-token|api-key|password|secret|token)\s+)[^\s]+/gi;
|
|
14
|
+
|
|
15
|
+
export interface SanitizationStats {
|
|
16
|
+
sanitized_value_count: number;
|
|
17
|
+
omitted_value_count: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function sanitizeDiagnosticValue(value: unknown): {
|
|
21
|
+
value: unknown;
|
|
22
|
+
stats: SanitizationStats;
|
|
23
|
+
} {
|
|
24
|
+
const stats: SanitizationStats = { sanitized_value_count: 0, omitted_value_count: 0 };
|
|
25
|
+
return { value: sanitize(value, stats, 0), stats };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sanitize(value: unknown, stats: SanitizationStats, depth: number): unknown {
|
|
29
|
+
if (depth > 24) {
|
|
30
|
+
stats.omitted_value_count += 1;
|
|
31
|
+
return "[OMITTED:depth-limit]";
|
|
32
|
+
}
|
|
33
|
+
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
34
|
+
if (typeof value === "string") return sanitizeString(value, stats);
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
const sliced = value.slice(0, 5_000);
|
|
37
|
+
const kept = sliced.map((item, index) => {
|
|
38
|
+
if (
|
|
39
|
+
typeof sliced[index - 1] === "string" &&
|
|
40
|
+
/^(?:--?)(?:access-token|api-key|password|secret|token)$/i.test(sliced[index - 1] as string)
|
|
41
|
+
) {
|
|
42
|
+
stats.sanitized_value_count += 1;
|
|
43
|
+
return "[REDACTED:argument]";
|
|
44
|
+
}
|
|
45
|
+
return sanitize(item, stats, depth + 1);
|
|
46
|
+
});
|
|
47
|
+
if (kept.length !== value.length) stats.omitted_value_count += value.length - kept.length;
|
|
48
|
+
return kept;
|
|
49
|
+
}
|
|
50
|
+
if (!value || typeof value !== "object") return String(value);
|
|
51
|
+
|
|
52
|
+
const result: Record<string, unknown> = {};
|
|
53
|
+
const entries = Object.entries(value);
|
|
54
|
+
const namedSensitiveField = entries.some(
|
|
55
|
+
([key, item]) => key === "name" && typeof item === "string" && SENSITIVE_KEY.test(item),
|
|
56
|
+
);
|
|
57
|
+
for (const [key, item] of entries) {
|
|
58
|
+
if (OMITTED_CONTENT_KEY.test(key)) {
|
|
59
|
+
result[key] = "[OMITTED:sensitive-content]";
|
|
60
|
+
stats.omitted_value_count += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key === "value" && namedSensitiveField) {
|
|
64
|
+
result[key] = "[REDACTED:sensitive-field]";
|
|
65
|
+
stats.sanitized_value_count += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (SENSITIVE_KEY.test(key)) {
|
|
69
|
+
result[key] = "[REDACTED:sensitive-field]";
|
|
70
|
+
stats.sanitized_value_count += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
result[key] = sanitize(item, stats, depth + 1);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function sanitizeString(value: string, stats: SanitizationStats): string {
|
|
79
|
+
let next = value;
|
|
80
|
+
const replacements: Array<[RegExp, string | ((...args: string[]) => string)]> = [
|
|
81
|
+
[PRIVATE_KEY, "[REDACTED:private-key]"],
|
|
82
|
+
[BEARER_VALUE, "Bearer [REDACTED:token]"],
|
|
83
|
+
[TOKENISH_VALUE, "[REDACTED:token]"],
|
|
84
|
+
[AWS_ACCESS_KEY, "[REDACTED:access-key]"],
|
|
85
|
+
[SENSITIVE_QUERY, "$1[REDACTED]"],
|
|
86
|
+
[SENSITIVE_ENV, "$1[REDACTED]"],
|
|
87
|
+
[SENSITIVE_ARGUMENT, "$1[REDACTED]"],
|
|
88
|
+
[URL_CREDENTIALS, (_match, scheme: string) => `${scheme}[REDACTED]@`],
|
|
89
|
+
[HOME_PATH, "[REDACTED:home]"],
|
|
90
|
+
];
|
|
91
|
+
for (const [pattern, replacement] of replacements) {
|
|
92
|
+
const replaced = next.replace(pattern, replacement as never);
|
|
93
|
+
if (replaced !== next) stats.sanitized_value_count += 1;
|
|
94
|
+
next = replaced;
|
|
95
|
+
}
|
|
96
|
+
return next;
|
|
97
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** Verified in-place compression for sealed, non-terminal legacy V1 shards. */
|
|
2
|
+
|
|
3
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
4
|
+
import { chmod, lstat, rename, rm } from "node:fs/promises";
|
|
5
|
+
import { basename } from "node:path";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
import { createGzip } from "node:zlib";
|
|
8
|
+
import { verifyLegacyV1HardFence } from "./fence.ts";
|
|
9
|
+
import { inventoryLegacyV1Segments } from "./inventory.ts";
|
|
10
|
+
import { streamLegacyV1Rows } from "./reader.ts";
|
|
11
|
+
|
|
12
|
+
export interface LegacyV1CompressionEntry {
|
|
13
|
+
filename: string;
|
|
14
|
+
path: string;
|
|
15
|
+
bytes_before: number;
|
|
16
|
+
bytes_after: number | null;
|
|
17
|
+
action: "keep" | "would-compress" | "compressed";
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function compressSealedLegacyV1Segments(
|
|
22
|
+
coordRoot: string,
|
|
23
|
+
opts: { yes?: boolean } = {},
|
|
24
|
+
): Promise<LegacyV1CompressionEntry[]> {
|
|
25
|
+
const fence = await verifyLegacyV1HardFence(coordRoot);
|
|
26
|
+
const terminal = basename(fence.terminal_archive_path);
|
|
27
|
+
const inventory = await inventoryLegacyV1Segments(coordRoot);
|
|
28
|
+
const rows = inventory.map<LegacyV1CompressionEntry>((entry) => {
|
|
29
|
+
if (entry.filename === terminal) {
|
|
30
|
+
return {
|
|
31
|
+
filename: entry.filename,
|
|
32
|
+
path: entry.path,
|
|
33
|
+
bytes_before: entry.bytes,
|
|
34
|
+
bytes_after: null,
|
|
35
|
+
action: "keep",
|
|
36
|
+
reason: "terminal shard is bound by the V1 hard-fence digest",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (entry.compressed) {
|
|
40
|
+
return {
|
|
41
|
+
filename: entry.filename,
|
|
42
|
+
path: entry.path,
|
|
43
|
+
bytes_before: entry.bytes,
|
|
44
|
+
bytes_after: entry.bytes,
|
|
45
|
+
action: "keep",
|
|
46
|
+
reason: "already compressed",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
filename: entry.filename,
|
|
51
|
+
path: entry.path,
|
|
52
|
+
bytes_before: entry.bytes,
|
|
53
|
+
bytes_after: null,
|
|
54
|
+
action: "would-compress",
|
|
55
|
+
reason: "sealed non-terminal V1 shard",
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
if (!opts.yes) return rows;
|
|
59
|
+
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
if (row.action !== "would-compress") continue;
|
|
62
|
+
const expected = inventory.find((entry) => entry.path === row.path)!;
|
|
63
|
+
const current = (await inventoryLegacyV1Segments(coordRoot)).find(
|
|
64
|
+
(entry) => entry.path === row.path,
|
|
65
|
+
);
|
|
66
|
+
if (
|
|
67
|
+
!current ||
|
|
68
|
+
current.bytes !== expected.bytes ||
|
|
69
|
+
current.digest !== expected.digest ||
|
|
70
|
+
current.compressed
|
|
71
|
+
) {
|
|
72
|
+
throw new Error(`legacy_v1_segment_changed_before_compression:${row.filename}`);
|
|
73
|
+
}
|
|
74
|
+
const sourceStat = await lstat(row.path);
|
|
75
|
+
const destination = `${row.path}.gz`;
|
|
76
|
+
const temporary = `${row.path}.partial-${process.pid}.gz`;
|
|
77
|
+
try {
|
|
78
|
+
try {
|
|
79
|
+
await lstat(destination);
|
|
80
|
+
throw new Error(`legacy_v1_compressed_destination_exists:${row.filename}`);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
83
|
+
}
|
|
84
|
+
await pipeline(
|
|
85
|
+
createReadStream(row.path),
|
|
86
|
+
createGzip({ level: 9 }),
|
|
87
|
+
createWriteStream(temporary, { flags: "wx", mode: 0o600 }),
|
|
88
|
+
);
|
|
89
|
+
await proveRowParity(row.path, temporary);
|
|
90
|
+
const unchanged = (await inventoryLegacyV1Segments(coordRoot)).find(
|
|
91
|
+
(entry) => entry.path === row.path,
|
|
92
|
+
);
|
|
93
|
+
if (
|
|
94
|
+
!unchanged ||
|
|
95
|
+
unchanged.bytes !== expected.bytes ||
|
|
96
|
+
unchanged.digest !== expected.digest
|
|
97
|
+
) {
|
|
98
|
+
throw new Error(`legacy_v1_segment_changed_during_compression:${row.filename}`);
|
|
99
|
+
}
|
|
100
|
+
await chmod(temporary, sourceStat.mode & 0o777);
|
|
101
|
+
await rename(temporary, destination);
|
|
102
|
+
await rm(row.path);
|
|
103
|
+
row.action = "compressed";
|
|
104
|
+
row.reason = "verified gzip replacement preserved every logical row";
|
|
105
|
+
row.bytes_after = (await lstat(destination)).size;
|
|
106
|
+
row.path = destination;
|
|
107
|
+
row.filename = basename(destination);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
await rm(temporary, { force: true });
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return rows;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function proveRowParity(loose: string, compressed: string): Promise<void> {
|
|
117
|
+
const left = streamLegacyV1Rows(loose)[Symbol.asyncIterator]();
|
|
118
|
+
const right = streamLegacyV1Rows(compressed)[Symbol.asyncIterator]();
|
|
119
|
+
while (true) {
|
|
120
|
+
const [looseRow, packedRow] = await Promise.all([left.next(), right.next()]);
|
|
121
|
+
if (looseRow.done || packedRow.done) {
|
|
122
|
+
if (looseRow.done !== packedRow.done)
|
|
123
|
+
throw new Error("legacy_v1_compression_row_count_mismatch");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!looseRow.value.bytes.equals(packedRow.value.bytes)) {
|
|
127
|
+
throw new Error(`legacy_v1_compression_row_mismatch:${looseRow.value.row_number}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/** Bounded retention for complete, closed Event Ledger V3 epochs. */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
writeFileSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
artifactAutoCleanIntervalHours,
|
|
15
|
+
type EventLedgerArchivePolicy,
|
|
16
|
+
eventLedgerArchivePolicy,
|
|
17
|
+
} from "../../config.ts";
|
|
18
|
+
|
|
19
|
+
export type EventV3ArchiveClassification =
|
|
20
|
+
| "retained"
|
|
21
|
+
| "expired"
|
|
22
|
+
| "over-budget"
|
|
23
|
+
| "protected-minimum"
|
|
24
|
+
| "unmanaged"
|
|
25
|
+
| "symlink"
|
|
26
|
+
| "unknown";
|
|
27
|
+
|
|
28
|
+
export interface EventV3ArchiveEntry {
|
|
29
|
+
name: string;
|
|
30
|
+
path: string;
|
|
31
|
+
relative_path: string;
|
|
32
|
+
classification: EventV3ArchiveClassification;
|
|
33
|
+
reason: string;
|
|
34
|
+
action: "keep" | "would-delete" | "deleted";
|
|
35
|
+
bytes: number | null;
|
|
36
|
+
last_modified_at: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface EventV3ArchiveAutoCleanResult {
|
|
40
|
+
ran: boolean;
|
|
41
|
+
reason: "swept" | "disabled" | "fresh" | "no-root";
|
|
42
|
+
deleted: number;
|
|
43
|
+
bytes: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ARCHIVE_PATTERN = /^epoch-[0-9]+(?:-[0-9]+)?$/;
|
|
47
|
+
const AUTO_CLEAN_STAMP = ".harnery/event-v3-archive-auto-clean.json";
|
|
48
|
+
|
|
49
|
+
export function eventV3ArchivesRoot(repoRoot: string): string {
|
|
50
|
+
return join(resolve(repoRoot), ".harnery", "ledgers", "v3-archives");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function inventoryEventV3Archives(
|
|
54
|
+
repoRoot: string,
|
|
55
|
+
opts: { now?: Date; policy?: EventLedgerArchivePolicy } = {},
|
|
56
|
+
): EventV3ArchiveEntry[] {
|
|
57
|
+
const root = eventV3ArchivesRoot(repoRoot);
|
|
58
|
+
if (!existsSync(root)) return [];
|
|
59
|
+
const now = opts.now ?? new Date();
|
|
60
|
+
const policy = opts.policy ?? eventLedgerArchivePolicy(repoRoot);
|
|
61
|
+
const rows = readdirSync(root)
|
|
62
|
+
.sort()
|
|
63
|
+
.map((name) => classifyArchive(repoRoot, join(root, name)));
|
|
64
|
+
const managed = rows.filter(
|
|
65
|
+
(row) => row.classification === "retained" && row.bytes !== null && row.last_modified_at,
|
|
66
|
+
);
|
|
67
|
+
const newestFirst = [...managed].sort((left, right) => right.name.localeCompare(left.name));
|
|
68
|
+
const protectedNames = new Set(newestFirst.slice(0, policy.keepMin).map((row) => row.name));
|
|
69
|
+
const cutoff = now.getTime() - policy.maxAgeDays * 24 * 60 * 60 * 1000;
|
|
70
|
+
|
|
71
|
+
for (const row of managed) {
|
|
72
|
+
if (protectedNames.has(row.name)) {
|
|
73
|
+
row.classification = "protected-minimum";
|
|
74
|
+
row.reason = `one of the newest ${policy.keepMin} complete epochs`;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (Date.parse(row.last_modified_at!) <= cutoff) {
|
|
78
|
+
row.classification = "expired";
|
|
79
|
+
row.reason = `closed epoch is older than ${policy.maxAgeDays} days`;
|
|
80
|
+
row.action = "would-delete";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let retainedBytes = managed.reduce((sum, row) => sum + (row.bytes ?? 0), 0);
|
|
85
|
+
retainedBytes -= managed.reduce(
|
|
86
|
+
(sum, row) => sum + (row.action === "would-delete" ? (row.bytes ?? 0) : 0),
|
|
87
|
+
0,
|
|
88
|
+
);
|
|
89
|
+
const budgetCandidates = managed
|
|
90
|
+
.filter((row) => row.classification === "retained")
|
|
91
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
92
|
+
for (const row of budgetCandidates) {
|
|
93
|
+
if (retainedBytes <= policy.maxBytes) break;
|
|
94
|
+
row.classification = "over-budget";
|
|
95
|
+
row.reason = `closed-epoch budget is ${policy.maxBytes} bytes; oldest epochs are removed first`;
|
|
96
|
+
row.action = "would-delete";
|
|
97
|
+
retainedBytes -= row.bytes ?? 0;
|
|
98
|
+
}
|
|
99
|
+
return rows;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function cleanEventV3Archives(
|
|
103
|
+
repoRoot: string,
|
|
104
|
+
opts: { yes?: boolean; now?: Date; policy?: EventLedgerArchivePolicy } = {},
|
|
105
|
+
): EventV3ArchiveEntry[] {
|
|
106
|
+
const rows = inventoryEventV3Archives(repoRoot, opts);
|
|
107
|
+
if (!opts.yes) return rows;
|
|
108
|
+
return rows.map((entry) => {
|
|
109
|
+
if (entry.action !== "would-delete") return entry;
|
|
110
|
+
const current = inventoryEventV3Archives(repoRoot, opts).find((row) => row.path === entry.path);
|
|
111
|
+
if (!current) {
|
|
112
|
+
return { ...entry, classification: "unknown", reason: "entry disappeared", action: "keep" };
|
|
113
|
+
}
|
|
114
|
+
if (
|
|
115
|
+
current.action !== "would-delete" ||
|
|
116
|
+
current.bytes !== entry.bytes ||
|
|
117
|
+
current.last_modified_at !== entry.last_modified_at
|
|
118
|
+
) {
|
|
119
|
+
return current;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const stat = lstatSync(current.path);
|
|
123
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || !ARCHIVE_PATTERN.test(current.name)) {
|
|
124
|
+
return {
|
|
125
|
+
...current,
|
|
126
|
+
classification: "unknown",
|
|
127
|
+
reason: "entry changed before deletion",
|
|
128
|
+
action: "keep",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
rmSync(current.path, { recursive: true, force: false });
|
|
132
|
+
return { ...current, action: "deleted" };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
...current,
|
|
136
|
+
classification: "unknown",
|
|
137
|
+
reason: `entry changed or could not be deleted: ${error instanceof Error ? error.message : String(error)}`,
|
|
138
|
+
action: "keep",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function autoCleanEventV3Archives(
|
|
145
|
+
repoRoot: string,
|
|
146
|
+
opts: { now?: Date } = {},
|
|
147
|
+
): EventV3ArchiveAutoCleanResult {
|
|
148
|
+
const now = opts.now ?? new Date();
|
|
149
|
+
const root = eventV3ArchivesRoot(repoRoot);
|
|
150
|
+
if (!existsSync(root)) return { ran: false, reason: "no-root", deleted: 0, bytes: 0 };
|
|
151
|
+
const policy = eventLedgerArchivePolicy(repoRoot);
|
|
152
|
+
if (!policy.autoClean) return { ran: false, reason: "disabled", deleted: 0, bytes: 0 };
|
|
153
|
+
const stampPath = join(resolve(repoRoot), AUTO_CLEAN_STAMP);
|
|
154
|
+
const intervalMs = artifactAutoCleanIntervalHours() * 60 * 60 * 1000;
|
|
155
|
+
try {
|
|
156
|
+
const stamp = JSON.parse(readFileSync(stampPath, "utf8")) as { last_run_at?: string };
|
|
157
|
+
const last = Date.parse(stamp.last_run_at ?? "");
|
|
158
|
+
if (Number.isFinite(last) && now.getTime() - last < intervalMs) {
|
|
159
|
+
return { ran: false, reason: "fresh", deleted: 0, bytes: 0 };
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Missing or unreadable stamp: sweep now.
|
|
163
|
+
}
|
|
164
|
+
mkdirSync(join(resolve(repoRoot), ".harnery"), { recursive: true });
|
|
165
|
+
writeFileSync(stampPath, `${JSON.stringify({ last_run_at: now.toISOString() }, null, 2)}\n`);
|
|
166
|
+
const rows = cleanEventV3Archives(repoRoot, { yes: true, now, policy });
|
|
167
|
+
const deletedRows = rows.filter((row) => row.action === "deleted");
|
|
168
|
+
const result = {
|
|
169
|
+
ran: true as const,
|
|
170
|
+
reason: "swept" as const,
|
|
171
|
+
deleted: deletedRows.length,
|
|
172
|
+
bytes: deletedRows.reduce((sum, row) => sum + (row.bytes ?? 0), 0),
|
|
173
|
+
};
|
|
174
|
+
writeFileSync(
|
|
175
|
+
stampPath,
|
|
176
|
+
`${JSON.stringify({ last_run_at: now.toISOString(), ...result }, null, 2)}\n`,
|
|
177
|
+
);
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function classifyArchive(repoRoot: string, path: string): EventV3ArchiveEntry {
|
|
182
|
+
const name = basename(path);
|
|
183
|
+
const base: EventV3ArchiveEntry = {
|
|
184
|
+
name,
|
|
185
|
+
path,
|
|
186
|
+
relative_path: relative(repoRoot, path),
|
|
187
|
+
classification: "retained",
|
|
188
|
+
reason: "closed epoch is inside the retention window",
|
|
189
|
+
action: "keep",
|
|
190
|
+
bytes: null,
|
|
191
|
+
last_modified_at: null,
|
|
192
|
+
};
|
|
193
|
+
try {
|
|
194
|
+
const stat = lstatSync(path);
|
|
195
|
+
if (stat.isSymbolicLink()) {
|
|
196
|
+
return {
|
|
197
|
+
...base,
|
|
198
|
+
classification: "symlink",
|
|
199
|
+
reason: "symlinks are never traversed or deleted",
|
|
200
|
+
bytes: stat.size,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (!stat.isDirectory() || !ARCHIVE_PATTERN.test(name)) {
|
|
204
|
+
return {
|
|
205
|
+
...base,
|
|
206
|
+
classification: "unmanaged",
|
|
207
|
+
reason: "entry is not a complete named epoch",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const measured = measureTree(path);
|
|
211
|
+
if (!measured) {
|
|
212
|
+
return {
|
|
213
|
+
...base,
|
|
214
|
+
classification: "unknown",
|
|
215
|
+
reason: "one or more epoch paths are unreadable",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
...base,
|
|
220
|
+
bytes: measured.bytes,
|
|
221
|
+
last_modified_at: new Date(measured.lastModifiedMs).toISOString(),
|
|
222
|
+
};
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
...base,
|
|
226
|
+
classification: "unknown",
|
|
227
|
+
reason: `cannot inspect archive: ${error instanceof Error ? error.message : String(error)}`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function measureTree(path: string): { bytes: number; lastModifiedMs: number } | null {
|
|
233
|
+
try {
|
|
234
|
+
const stat = lstatSync(path);
|
|
235
|
+
if (stat.isSymbolicLink() || stat.isFile()) {
|
|
236
|
+
return { bytes: stat.size, lastModifiedMs: stat.mtimeMs };
|
|
237
|
+
}
|
|
238
|
+
if (!stat.isDirectory()) return { bytes: 0, lastModifiedMs: stat.mtimeMs };
|
|
239
|
+
let bytes = stat.size;
|
|
240
|
+
let lastModifiedMs = stat.mtimeMs;
|
|
241
|
+
for (const child of readdirSync(path)) {
|
|
242
|
+
const measured = measureTree(join(path, child));
|
|
243
|
+
if (!measured) return null;
|
|
244
|
+
bytes += measured.bytes;
|
|
245
|
+
lastModifiedMs = Math.max(lastModifiedMs, measured.lastModifiedMs);
|
|
246
|
+
}
|
|
247
|
+
return { bytes, lastModifiedMs };
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { repairEventV3ControlPair } from "./control-writer.ts";
|
|
31
31
|
import { loadOrCreateFingerprintKeyStoreV3 } from "./fingerprint-keys.ts";
|
|
32
32
|
import { EVENT_V3_SCHEMA_DIGEST } from "./generated.ts";
|
|
33
|
+
import { reanchorArchivedHookProducersV3 } from "./producers/recorder.ts";
|
|
33
34
|
import { resolveEventLedgerRotateActiveBytesV3 } from "./rotation-config.ts";
|
|
34
35
|
import { currentHarneryRuntimeBuild, liveEventV3BuildId, livePlatformV3 } from "./runtime-build.ts";
|
|
35
36
|
import { drainReadyEventsV3 } from "./writer.ts";
|
|
@@ -210,6 +211,15 @@ export function rotateOversizedEventLedgerV3(
|
|
|
210
211
|
approvalRecordId: "harnery-runtime-v3-size-rotation",
|
|
211
212
|
forceNewEpoch: true,
|
|
212
213
|
});
|
|
214
|
+
if (initialized.archived_epoch) {
|
|
215
|
+
reanchorArchivedHookProducersV3({
|
|
216
|
+
coordRoot: root,
|
|
217
|
+
archivedEpoch: initialized.archived_epoch,
|
|
218
|
+
mode: initialized.control.state,
|
|
219
|
+
build_id: liveEventV3BuildId(currentHarneryRuntimeBuild()),
|
|
220
|
+
platform: livePlatformV3(),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
213
223
|
return {
|
|
214
224
|
state: "rotated",
|
|
215
225
|
active_bytes: activeBytes,
|
|
@@ -35,6 +35,7 @@ export type BaseAdapterSignalV3 =
|
|
|
35
35
|
| "prompt"
|
|
36
36
|
| "turn_id"
|
|
37
37
|
| "turn_completion"
|
|
38
|
+
| "assistant_reply_text"
|
|
38
39
|
| "tool_request"
|
|
39
40
|
| "tool_result"
|
|
40
41
|
| "tool_failure"
|
|
@@ -66,6 +67,7 @@ const SHARED_SIGNAL_SUPPORT = {
|
|
|
66
67
|
session_start: "native",
|
|
67
68
|
prompt: "native",
|
|
68
69
|
turn_completion: "native",
|
|
70
|
+
assistant_reply_text: "native",
|
|
69
71
|
tool_request: "native",
|
|
70
72
|
tool_result: "native",
|
|
71
73
|
tool_failure: "native",
|
|
@@ -52,8 +52,11 @@ export interface CoordinationViewV3 {
|
|
|
52
52
|
contract_major: 2;
|
|
53
53
|
reducer_build_id: typeof EVENT_V3_SAFETY_REDUCER_BUILD;
|
|
54
54
|
source_complete: boolean;
|
|
55
|
+
/** True when no unscoped authority diagnostic can affect every generation. */
|
|
55
56
|
authority_safe: boolean;
|
|
56
57
|
diagnostics: SafetyProjectionDiagnosticV3[];
|
|
58
|
+
global_diagnostics: SafetyProjectionDiagnosticV3[];
|
|
59
|
+
diagnostics_by_generation: Record<string, SafetyProjectionDiagnosticV3[]>;
|
|
57
60
|
instances: Record<string, CoordinationGenerationViewV3>;
|
|
58
61
|
terminal_generations: Record<string, CoordinationGenerationViewV3>;
|
|
59
62
|
delegations: SafetyProjectionV3["delegations"];
|
|
@@ -95,7 +98,9 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
95
98
|
const safety = reduceSafetyProjectionV3(read);
|
|
96
99
|
const starts = new Map<string, Extract<EventV3, { event_type: "session.started" }>>();
|
|
97
100
|
const eventGeneration = new Map<string, string>();
|
|
101
|
+
const eventTypes = new Map<string, EventV3["event_type"]>();
|
|
98
102
|
for (const { event } of read.events) {
|
|
103
|
+
eventTypes.set(event.event_id, event.event_type);
|
|
99
104
|
const generationId = "generation_id" in event.scope ? event.scope.generation_id : undefined;
|
|
100
105
|
if (generationId !== undefined) {
|
|
101
106
|
eventGeneration.set(event.event_id, generationId);
|
|
@@ -105,6 +110,15 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
const { globalDiagnostics, diagnosticsByGeneration } = partitionCoordinationDiagnosticsV3(
|
|
114
|
+
safety,
|
|
115
|
+
eventGeneration,
|
|
116
|
+
eventTypes,
|
|
117
|
+
);
|
|
118
|
+
const globalAuthoritySafe = !globalDiagnostics.some(
|
|
119
|
+
(diagnostic) => diagnostic.authority_blocking,
|
|
120
|
+
);
|
|
121
|
+
|
|
108
122
|
const instances: Record<string, CoordinationGenerationViewV3> = {};
|
|
109
123
|
const terminalGenerations: Record<string, CoordinationGenerationViewV3> = {};
|
|
110
124
|
const states = Object.values(safety.generations).sort((left, right) =>
|
|
@@ -113,14 +127,7 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
113
127
|
for (const state of states) {
|
|
114
128
|
const started = starts.get(state.generation_id);
|
|
115
129
|
if (!started) continue;
|
|
116
|
-
const diagnostics =
|
|
117
|
-
(diagnostic) =>
|
|
118
|
-
diagnostic.generation_id === state.generation_id ||
|
|
119
|
-
diagnostic.subject_instance_id === state.instance_id ||
|
|
120
|
-
(diagnostic.event_id !== undefined &&
|
|
121
|
-
eventGeneration.get(diagnostic.event_id) === state.generation_id) ||
|
|
122
|
-
diagnostic.code === "ledger_incomplete",
|
|
123
|
-
);
|
|
130
|
+
const diagnostics = diagnosticsByGeneration[state.generation_id] ?? [];
|
|
124
131
|
const delegation = Object.values(safety.delegations).find(
|
|
125
132
|
(candidate) => candidate.child_generation_id === state.generation_id,
|
|
126
133
|
);
|
|
@@ -173,8 +180,11 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
173
180
|
last_context_event_id: state.last_context_event_id,
|
|
174
181
|
provisional_termination: state.provisional_termination,
|
|
175
182
|
terminal: state.terminal,
|
|
176
|
-
evidence_complete:
|
|
177
|
-
authority_eligible:
|
|
183
|
+
evidence_complete: globalDiagnostics.length === 0 && diagnostics.length === 0,
|
|
184
|
+
authority_eligible:
|
|
185
|
+
globalAuthoritySafe &&
|
|
186
|
+
!diagnostics.some((diagnostic) => diagnostic.authority_blocking) &&
|
|
187
|
+
state.phase === "live",
|
|
178
188
|
};
|
|
179
189
|
if (state.phase === "terminal") {
|
|
180
190
|
terminalGenerations[state.generation_id] = view;
|
|
@@ -188,8 +198,10 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
188
198
|
contract_major: 2,
|
|
189
199
|
reducer_build_id: EVENT_V3_SAFETY_REDUCER_BUILD,
|
|
190
200
|
source_complete: read.complete,
|
|
191
|
-
authority_safe:
|
|
201
|
+
authority_safe: globalAuthoritySafe,
|
|
192
202
|
diagnostics: safety.diagnostics,
|
|
203
|
+
global_diagnostics: globalDiagnostics,
|
|
204
|
+
diagnostics_by_generation: diagnosticsByGeneration,
|
|
193
205
|
instances,
|
|
194
206
|
terminal_generations: terminalGenerations,
|
|
195
207
|
delegations: safety.delegations,
|
|
@@ -200,6 +212,83 @@ export function projectCoordinationViewV3(read: ReadLedgerV3Result): Coordinatio
|
|
|
200
212
|
return view;
|
|
201
213
|
}
|
|
202
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Partition reducer findings at the narrowest authority boundary that can be
|
|
217
|
+
* proven from canonical event attribution. A diagnostic stays global when it
|
|
218
|
+
* concerns shared claim/decision state, lacks a live generation witness, or
|
|
219
|
+
* came from the canonical reader. Reader failures deliberately remain global:
|
|
220
|
+
* an incomplete read cannot produce the append checkpoint that proves causal
|
|
221
|
+
* parents and attestations for later writes.
|
|
222
|
+
*/
|
|
223
|
+
function partitionCoordinationDiagnosticsV3(
|
|
224
|
+
safety: SafetyProjectionV3,
|
|
225
|
+
eventGeneration: ReadonlyMap<string, string>,
|
|
226
|
+
eventTypes: ReadonlyMap<string, EventV3["event_type"]>,
|
|
227
|
+
): {
|
|
228
|
+
globalDiagnostics: SafetyProjectionDiagnosticV3[];
|
|
229
|
+
diagnosticsByGeneration: Record<string, SafetyProjectionDiagnosticV3[]>;
|
|
230
|
+
} {
|
|
231
|
+
const globalDiagnostics: SafetyProjectionDiagnosticV3[] = [];
|
|
232
|
+
const diagnosticsByGeneration: Record<string, SafetyProjectionDiagnosticV3[]> = {};
|
|
233
|
+
for (const diagnostic of safety.diagnostics) {
|
|
234
|
+
const generationIds = diagnosticGenerationIdsV3(diagnostic, safety, eventGeneration);
|
|
235
|
+
if (
|
|
236
|
+
generationIds.length === 0 ||
|
|
237
|
+
diagnosticRequiresGlobalAuthorityV3(diagnostic, eventTypes.get(diagnostic.event_id ?? ""))
|
|
238
|
+
) {
|
|
239
|
+
globalDiagnostics.push(diagnostic);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
for (const generationId of generationIds) {
|
|
243
|
+
const generationDiagnostics = diagnosticsByGeneration[generationId] ?? [];
|
|
244
|
+
generationDiagnostics.push(diagnostic);
|
|
245
|
+
diagnosticsByGeneration[generationId] = generationDiagnostics;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { globalDiagnostics, diagnosticsByGeneration };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function diagnosticGenerationIdsV3(
|
|
252
|
+
diagnostic: SafetyProjectionDiagnosticV3,
|
|
253
|
+
safety: SafetyProjectionV3,
|
|
254
|
+
eventGeneration: ReadonlyMap<string, string>,
|
|
255
|
+
): string[] {
|
|
256
|
+
const exact = new Set<string>();
|
|
257
|
+
if (diagnostic.generation_id && safety.generations[diagnostic.generation_id]) {
|
|
258
|
+
exact.add(diagnostic.generation_id);
|
|
259
|
+
}
|
|
260
|
+
const eventGenerationId = diagnostic.event_id
|
|
261
|
+
? eventGeneration.get(diagnostic.event_id)
|
|
262
|
+
: undefined;
|
|
263
|
+
if (eventGenerationId && safety.generations[eventGenerationId]) exact.add(eventGenerationId);
|
|
264
|
+
if (exact.size > 0) return [...exact].sort();
|
|
265
|
+
|
|
266
|
+
const subjectGenerationId = diagnostic.subject_instance_id
|
|
267
|
+
? safety.current_generation_by_instance[diagnostic.subject_instance_id]
|
|
268
|
+
: undefined;
|
|
269
|
+
return subjectGenerationId && safety.generations[subjectGenerationId]
|
|
270
|
+
? [subjectGenerationId]
|
|
271
|
+
: [];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function diagnosticRequiresGlobalAuthorityV3(
|
|
275
|
+
diagnostic: SafetyProjectionDiagnosticV3,
|
|
276
|
+
eventType: EventV3["event_type"] | undefined,
|
|
277
|
+
): boolean {
|
|
278
|
+
if (diagnostic.code === "ledger_incomplete") return true;
|
|
279
|
+
if (diagnostic.code === "decision_prior_mismatch") return true;
|
|
280
|
+
if (
|
|
281
|
+
diagnostic.code === "claim_acquire_conflict" ||
|
|
282
|
+
diagnostic.code === "claim_release_without_acquire"
|
|
283
|
+
) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
return (
|
|
287
|
+
diagnostic.code === "authority_attribution_unverified" &&
|
|
288
|
+
(eventType === "decision.state_changed" || eventType === "coord.claim_changed")
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
203
292
|
/** Refuse authority decisions while still allowing callers to render diagnostics. */
|
|
204
293
|
export function requireAuthoritySafeCoordinationViewV3(
|
|
205
294
|
view: CoordinationViewV3,
|