harnery 0.34.1 → 0.36.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 +30 -5
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +124 -94
- package/dist/commands/agents.d.ts +37 -6
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +466 -371
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +10 -10
- package/dist/commands/browse-session.d.ts.map +1 -1
- package/dist/commands/browse-session.js +12 -0
- package/dist/commands/browse.d.ts +1 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +31 -6
- package/dist/commands/conversations.d.ts +10 -0
- package/dist/commands/conversations.d.ts.map +1 -0
- package/dist/commands/conversations.js +201 -0
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +2 -2
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +5 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +13 -2
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +51 -7
- package/dist/commands/events.d.ts.map +1 -1
- package/dist/commands/events.js +18 -1
- package/dist/commands/fetch.js +6 -5
- package/dist/commands/files.d.ts +22 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/files.js +113 -0
- package/dist/commands/governor.js +14 -12
- package/dist/commands/inbox.d.ts +5 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +57 -0
- package/dist/commands/init.d.ts +4 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +63 -13
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +347 -1
- package/dist/commands/logs.d.ts +4 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +169 -0
- package/dist/commands/messages.d.ts +5 -0
- package/dist/commands/messages.d.ts.map +1 -0
- package/dist/commands/messages.js +45 -0
- package/dist/commands/outline.d.ts +1 -1
- package/dist/commands/outline.d.ts.map +1 -1
- package/dist/commands/outline.js +2 -2
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/read.d.ts +1 -1
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +2 -2
- package/dist/commands/semantic.d.ts +4 -0
- package/dist/commands/semantic.d.ts.map +1 -0
- package/dist/commands/semantic.js +199 -0
- package/dist/commands/storage.d.ts +4 -0
- package/dist/commands/storage.d.ts.map +1 -0
- package/dist/commands/storage.js +238 -0
- package/dist/commands/tunnel.d.ts +5 -1
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +44 -31
- package/dist/commands/web.d.ts +3 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +84 -7
- package/dist/commands/work.js +3 -3
- package/dist/commands/workflow.js +13 -13
- package/dist/core/adapters/profiles.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts +3 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +12 -0
- package/dist/core/agents/claim-path.d.ts +11 -0
- package/dist/core/agents/claim-path.d.ts.map +1 -0
- package/dist/core/agents/claim-path.js +22 -0
- package/dist/core/agents/cli.js +209 -39
- package/dist/core/agents/coord-client.d.ts +29 -3
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +88 -9
- package/dist/core/agents/live-authority-v3.d.ts +47 -1
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +245 -13
- package/dist/core/agents/live-lifecycle-v3.js +9 -4
- package/dist/core/agents/live-observation-v3.d.ts.map +1 -1
- package/dist/core/agents/live-observation-v3.js +8 -3
- package/dist/core/agents/render/prompt-context.d.ts +9 -3
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +36 -17
- package/dist/core/agents/rules/stop-hook.d.ts +2 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +115 -22
- package/dist/core/agents/rules/verdict.d.ts +1 -0
- package/dist/core/agents/rules/verdict.d.ts.map +1 -1
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +4 -0
- package/dist/core/agents/session-finalization-state-v3.js +1 -1
- package/dist/core/agents/session-finalizer-v3.js +5 -1
- package/dist/core/agents/session-name-display.d.ts +1 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +8 -1
- package/dist/core/agents/state/activity-log.d.ts +3 -0
- package/dist/core/agents/state/activity-log.d.ts.map +1 -1
- package/dist/core/agents/state/activity-log.js +6 -3
- package/dist/core/agents/state/heartbeat-reader.d.ts +46 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-reader.js +25 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts +9 -48
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +34 -34
- package/dist/core/agents/state/live-coordination-view.d.ts +6 -8
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +16 -67
- package/dist/core/agents/state/live-coordination-writer.d.ts +12 -0
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -0
- package/dist/core/agents/state/live-coordination-writer.js +92 -0
- package/dist/core/agents/state/names.d.ts +9 -1
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +190 -182
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +23 -5
- package/dist/core/artifacts/constants.d.ts +5 -0
- package/dist/core/artifacts/constants.d.ts.map +1 -0
- package/dist/core/artifacts/constants.js +4 -0
- package/dist/core/artifacts/index.d.ts +24 -3
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +96 -8
- package/dist/core/config.d.ts +62 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +98 -4
- package/dist/core/conversations/archive.d.ts +49 -0
- package/dist/core/conversations/archive.d.ts.map +1 -0
- package/dist/core/conversations/archive.js +136 -0
- package/dist/core/conversations/catalog.d.ts +10 -0
- package/dist/core/conversations/catalog.d.ts.map +1 -0
- package/dist/core/conversations/catalog.js +72 -0
- package/dist/core/conversations/context-pack.d.ts +6 -0
- package/dist/core/conversations/context-pack.d.ts.map +1 -0
- package/dist/core/conversations/context-pack.js +60 -0
- package/dist/core/conversations/contract.d.ts +155 -0
- package/dist/core/conversations/contract.d.ts.map +1 -0
- package/dist/core/conversations/contract.js +1 -0
- package/dist/core/conversations/evaluation.d.ts +16 -0
- package/dist/core/conversations/evaluation.d.ts.map +1 -0
- package/dist/core/conversations/evaluation.js +9 -0
- package/dist/core/conversations/index.d.ts +10 -0
- package/dist/core/conversations/index.d.ts.map +1 -0
- package/dist/core/conversations/index.js +9 -0
- package/dist/core/conversations/lifecycle.d.ts +28 -0
- package/dist/core/conversations/lifecycle.d.ts.map +1 -0
- package/dist/core/conversations/lifecycle.js +29 -0
- package/dist/core/conversations/normalize.d.ts +14 -0
- package/dist/core/conversations/normalize.d.ts.map +1 -0
- package/dist/core/conversations/normalize.js +82 -0
- package/dist/core/conversations/query.d.ts +8 -0
- package/dist/core/conversations/query.d.ts.map +1 -0
- package/dist/core/conversations/query.js +280 -0
- package/dist/core/conversations/search-index.d.ts +9 -0
- package/dist/core/conversations/search-index.d.ts.map +1 -0
- package/dist/core/conversations/search-index.js +29 -0
- package/dist/core/events/legacy-storage/canary.d.ts +18 -0
- package/dist/core/events/legacy-storage/canary.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/canary.js +150 -0
- package/dist/core/events/legacy-storage/fence.d.ts +15 -0
- package/dist/core/events/legacy-storage/fence.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/fence.js +115 -0
- package/dist/core/events/legacy-storage/index.d.ts +6 -0
- package/dist/core/events/legacy-storage/index.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/index.js +5 -0
- package/dist/core/events/legacy-storage/inventory.d.ts +10 -0
- package/dist/core/events/legacy-storage/inventory.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/inventory.js +40 -0
- package/dist/core/events/legacy-storage/manifest.d.ts +23 -0
- package/dist/core/events/legacy-storage/manifest.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/manifest.js +108 -0
- package/dist/core/events/legacy-storage/reader.d.ts +15 -0
- package/dist/core/events/legacy-storage/reader.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/reader.js +154 -0
- package/dist/core/events/v3/authority-outbox.d.ts +7 -2
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +16 -3
- package/dist/core/events/v3/base-contract.d.ts +851 -16
- package/dist/core/events/v3/base-contract.d.ts.map +1 -1
- package/dist/core/events/v3/base-contract.js +50 -7
- package/dist/core/events/v3/bootstrap.d.ts +39 -0
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +167 -3
- package/dist/core/events/v3/capabilities.d.ts +11 -0
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +26 -3
- package/dist/core/events/v3/control-witness.d.ts +44 -0
- package/dist/core/events/v3/control-witness.d.ts.map +1 -0
- package/dist/core/events/v3/control-witness.js +233 -0
- package/dist/core/events/v3/control-writer.d.ts +4 -0
- package/dist/core/events/v3/control-writer.d.ts.map +1 -0
- package/dist/core/events/v3/control-writer.js +16 -0
- package/dist/core/events/v3/control.d.ts +8 -2
- package/dist/core/events/v3/control.d.ts.map +1 -1
- package/dist/core/events/v3/control.js +46 -18
- package/dist/core/events/v3/coordination-transaction-recovery.d.ts +42 -0
- package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -0
- package/dist/core/events/v3/coordination-transaction-recovery.js +342 -0
- package/dist/core/events/v3/coordination-view.d.ts.map +1 -1
- package/dist/core/events/v3/coordination-view.js +16 -1
- package/dist/core/events/v3/generated.d.ts +1 -1
- package/dist/core/events/v3/generated.js +1 -1
- package/dist/core/events/v3/index.d.ts +8 -5
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +7 -4
- package/dist/core/events/v3/latency.d.ts +31 -1
- package/dist/core/events/v3/latency.d.ts.map +1 -1
- package/dist/core/events/v3/latency.js +127 -3
- package/dist/core/events/v3/live-route-observer.d.ts +31 -0
- package/dist/core/events/v3/live-route-observer.d.ts.map +1 -0
- package/dist/core/events/v3/live-route-observer.js +58 -0
- package/dist/core/events/v3/live-routing.d.ts +42 -20
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +153 -41
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +10 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +11 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +90 -13
- package/dist/core/events/v3/producers/diagnostic-summaries.d.ts +129 -0
- package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -0
- package/dist/core/events/v3/producers/diagnostic-summaries.js +499 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +17 -1
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +83 -2
- package/dist/core/events/v3/producers/intake.d.ts +13 -0
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +36 -2
- package/dist/core/events/v3/producers/producer-state-observer.d.ts +8 -0
- package/dist/core/events/v3/producers/producer-state-observer.d.ts.map +1 -0
- package/dist/core/events/v3/producers/producer-state-observer.js +45 -0
- package/dist/core/events/v3/producers/recorder.d.ts +117 -1
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +992 -27
- package/dist/core/events/v3/reader.d.ts +51 -0
- package/dist/core/events/v3/reader.d.ts.map +1 -1
- package/dist/core/events/v3/reader.js +181 -1
- package/dist/core/events/v3/rotation-config.d.ts +9 -0
- package/dist/core/events/v3/rotation-config.d.ts.map +1 -0
- package/dist/core/events/v3/rotation-config.js +91 -0
- package/dist/core/events/v3/runtime-build.d.ts +11 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -0
- package/dist/core/events/v3/runtime-build.js +39 -0
- package/dist/core/events/v3/runtime-identity.d.ts +5 -0
- package/dist/core/events/v3/runtime-identity.d.ts.map +1 -0
- package/dist/core/events/v3/runtime-identity.js +18 -0
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +52 -0
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -0
- package/dist/core/events/v3/runtime-telemetry-capabilities.js +54 -0
- package/dist/core/events/v3/support-storage/classify.d.ts +46 -0
- package/dist/core/events/v3/support-storage/classify.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/classify.js +149 -0
- package/dist/core/events/v3/support-storage/eligibility.d.ts +29 -0
- package/dist/core/events/v3/support-storage/eligibility.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/eligibility.js +35 -0
- package/dist/core/events/v3/support-storage/index.d.ts +10 -0
- package/dist/core/events/v3/support-storage/index.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/index.js +9 -0
- package/dist/core/events/v3/support-storage/inventory.d.ts +30 -0
- package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/inventory.js +94 -0
- package/dist/core/events/v3/support-storage/logical-authority.d.ts +14 -0
- package/dist/core/events/v3/support-storage/logical-authority.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/logical-authority.js +104 -0
- package/dist/core/events/v3/support-storage/maintenance.d.ts +98 -0
- package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/maintenance.js +352 -0
- package/dist/core/events/v3/support-storage/pack-contract.d.ts +59 -0
- package/dist/core/events/v3/support-storage/pack-contract.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/pack-contract.js +249 -0
- package/dist/core/events/v3/support-storage/pack-reader.d.ts +34 -0
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/pack-reader.js +258 -0
- package/dist/core/events/v3/support-storage/pack-writer.d.ts +30 -0
- package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/pack-writer.js +268 -0
- package/dist/core/events/v3/support-storage/recovery.d.ts +15 -0
- package/dist/core/events/v3/support-storage/recovery.d.ts.map +1 -0
- package/dist/core/events/v3/support-storage/recovery.js +22 -0
- package/dist/core/events/v3/turn-telemetry.d.ts +21 -6
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
- package/dist/core/events/v3/turn-telemetry.js +32 -2
- package/dist/core/events/v3/writer.d.ts +13 -1
- package/dist/core/events/v3/writer.d.ts.map +1 -1
- package/dist/core/events/v3/writer.js +126 -12
- package/dist/core/governor/plan-read.d.ts.map +1 -1
- package/dist/core/governor/plan-read.js +5 -21
- package/dist/core/governor/planning.js +6 -8
- package/dist/core/governor/service-read.d.ts +12 -0
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +266 -2
- package/dist/core/governor/service.d.ts +1 -0
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +53 -18
- package/dist/core/guard/evidence-v3.d.ts.map +1 -1
- package/dist/core/guard/evidence-v3.js +2 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +1 -0
- package/dist/core/hooks/adapter/output.d.ts +4 -2
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +13 -5
- package/dist/core/hooks/adapter/parse.d.ts +3 -0
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +14 -0
- package/dist/core/hooks/adapter/runtime-telemetry.d.ts +142 -0
- package/dist/core/hooks/adapter/runtime-telemetry.d.ts.map +1 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +869 -0
- package/dist/core/hooks/cli.js +318 -61
- package/dist/core/hooks/effects/image-capture.d.ts +1 -1
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +4 -2
- package/dist/core/hooks/remediation-cap.d.ts +39 -0
- package/dist/core/hooks/remediation-cap.d.ts.map +1 -0
- package/dist/core/hooks/remediation-cap.js +63 -0
- package/dist/core/hooks/resolve/coord-root.d.ts +22 -9
- package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
- package/dist/core/hooks/resolve/coord-root.js +92 -11
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +5 -0
- package/dist/core/hooks/resolve/transcript.d.ts +33 -9
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +33 -10
- package/dist/core/inbox/contract.d.ts +64 -0
- package/dist/core/inbox/contract.d.ts.map +1 -0
- package/dist/core/inbox/contract.js +13 -0
- package/dist/core/inbox/index.d.ts +5 -0
- package/dist/core/inbox/index.d.ts.map +1 -0
- package/dist/core/inbox/index.js +4 -0
- package/dist/core/inbox/lifecycle.d.ts +22 -0
- package/dist/core/inbox/lifecycle.d.ts.map +1 -0
- package/dist/core/inbox/lifecycle.js +41 -0
- package/dist/core/inbox/service.d.ts +36 -0
- package/dist/core/inbox/service.d.ts.map +1 -0
- package/dist/core/inbox/service.js +210 -0
- package/dist/core/inbox/watcher.d.ts +7 -0
- package/dist/core/inbox/watcher.d.ts.map +1 -0
- package/dist/core/inbox/watcher.js +26 -0
- package/dist/core/presence/codec-digest.d.ts.map +1 -1
- package/dist/core/presence/codec-digest.js +14 -3
- package/dist/core/presence/relay-client.d.ts +1 -0
- package/dist/core/presence/relay-client.d.ts.map +1 -1
- package/dist/core/presence/relay-client.js +52 -8
- package/dist/core/semantic/adapters.d.ts +37 -0
- package/dist/core/semantic/adapters.d.ts.map +1 -0
- package/dist/core/semantic/adapters.js +360 -0
- package/dist/core/semantic/contract.d.ts +1072 -0
- package/dist/core/semantic/contract.d.ts.map +1 -0
- package/dist/core/semantic/contract.js +331 -0
- package/dist/core/semantic/evidence.d.ts +11 -0
- package/dist/core/semantic/evidence.d.ts.map +1 -0
- package/dist/core/semantic/evidence.js +262 -0
- package/dist/core/semantic/index.d.ts +14 -0
- package/dist/core/semantic/index.d.ts.map +1 -0
- package/dist/core/semantic/index.js +13 -0
- package/dist/core/semantic/once.d.ts +38 -0
- package/dist/core/semantic/once.d.ts.map +1 -0
- package/dist/core/semantic/once.js +364 -0
- package/dist/core/semantic/prompt.d.ts +12 -0
- package/dist/core/semantic/prompt.d.ts.map +1 -0
- package/dist/core/semantic/prompt.js +83 -0
- package/dist/core/semantic/review.d.ts +144 -0
- package/dist/core/semantic/review.d.ts.map +1 -0
- package/dist/core/semantic/review.js +544 -0
- package/dist/core/semantic/routes.d.ts +10 -0
- package/dist/core/semantic/routes.d.ts.map +1 -0
- package/dist/core/semantic/routes.js +21 -0
- package/dist/core/semantic/scheduler.d.ts +27 -0
- package/dist/core/semantic/scheduler.d.ts.map +1 -0
- package/dist/core/semantic/scheduler.js +91 -0
- package/dist/core/semantic/service-status.d.ts +51 -0
- package/dist/core/semantic/service-status.d.ts.map +1 -0
- package/dist/core/semantic/service-status.js +140 -0
- package/dist/core/semantic/service.d.ts +52 -0
- package/dist/core/semantic/service.d.ts.map +1 -0
- package/dist/core/semantic/service.js +559 -0
- package/dist/core/semantic/soak.d.ts +75 -0
- package/dist/core/semantic/soak.d.ts.map +1 -0
- package/dist/core/semantic/soak.js +476 -0
- package/dist/core/semantic/storage.d.ts +81 -0
- package/dist/core/semantic/storage.d.ts.map +1 -0
- package/dist/core/semantic/storage.js +180 -0
- package/dist/core/semantic/usage.d.ts +43 -0
- package/dist/core/semantic/usage.d.ts.map +1 -0
- package/dist/core/semantic/usage.js +193 -0
- package/dist/core/semantic/validate.d.ts +18 -0
- package/dist/core/semantic/validate.d.ts.map +1 -0
- package/dist/core/semantic/validate.js +207 -0
- package/dist/core/storage/buffer.d.ts +28 -0
- package/dist/core/storage/buffer.d.ts.map +1 -0
- package/dist/core/storage/buffer.js +101 -0
- package/dist/core/storage/builtins.d.ts +3 -0
- package/dist/core/storage/builtins.d.ts.map +1 -0
- package/dist/core/storage/builtins.js +683 -0
- package/dist/core/storage/catalog.d.ts +19 -0
- package/dist/core/storage/catalog.d.ts.map +1 -0
- package/dist/core/storage/catalog.js +365 -0
- package/dist/core/storage/config.d.ts +16 -0
- package/dist/core/storage/config.d.ts.map +1 -0
- package/dist/core/storage/config.js +311 -0
- package/dist/core/storage/contract.d.ts +275 -0
- package/dist/core/storage/contract.d.ts.map +1 -0
- package/dist/core/storage/contract.js +3 -0
- package/dist/core/storage/durable-history.d.ts +36 -0
- package/dist/core/storage/durable-history.d.ts.map +1 -0
- package/dist/core/storage/durable-history.js +606 -0
- package/dist/core/storage/health.d.ts +4 -0
- package/dist/core/storage/health.d.ts.map +1 -0
- package/dist/core/storage/health.js +65 -0
- package/dist/core/storage/index.d.ts +16 -0
- package/dist/core/storage/index.d.ts.map +1 -0
- package/dist/core/storage/index.js +15 -0
- package/dist/core/storage/inventory.d.ts +25 -0
- package/dist/core/storage/inventory.d.ts.map +1 -0
- package/dist/core/storage/inventory.js +666 -0
- package/dist/core/storage/jsonl.d.ts +52 -0
- package/dist/core/storage/jsonl.d.ts.map +1 -0
- package/dist/core/storage/jsonl.js +156 -0
- package/dist/core/storage/log-retention.d.ts +52 -0
- package/dist/core/storage/log-retention.d.ts.map +1 -0
- package/dist/core/storage/log-retention.js +419 -0
- package/dist/core/storage/logger.d.ts +82 -0
- package/dist/core/storage/logger.d.ts.map +1 -0
- package/dist/core/storage/logger.js +411 -0
- package/dist/core/storage/maintenance-providers.d.ts +26 -0
- package/dist/core/storage/maintenance-providers.d.ts.map +1 -0
- package/dist/core/storage/maintenance-providers.js +141 -0
- package/dist/core/storage/maintenance.d.ts +125 -0
- package/dist/core/storage/maintenance.d.ts.map +1 -0
- package/dist/core/storage/maintenance.js +609 -0
- package/dist/core/storage/metrics.d.ts +60 -0
- package/dist/core/storage/metrics.d.ts.map +1 -0
- package/dist/core/storage/metrics.js +134 -0
- package/dist/core/storage/policy.d.ts +7 -0
- package/dist/core/storage/policy.d.ts.map +1 -0
- package/dist/core/storage/policy.js +110 -0
- package/dist/core/storage/process-log.d.ts +38 -0
- package/dist/core/storage/process-log.d.ts.map +1 -0
- package/dist/core/storage/process-log.js +165 -0
- package/dist/core/storage/query.d.ts +34 -0
- package/dist/core/storage/query.d.ts.map +1 -0
- package/dist/core/storage/query.js +295 -0
- package/dist/core/storage/receipts.d.ts +23 -0
- package/dist/core/storage/receipts.d.ts.map +1 -0
- package/dist/core/storage/receipts.js +117 -0
- package/dist/core/storage/segments.d.ts +84 -0
- package/dist/core/storage/segments.d.ts.map +1 -0
- package/dist/core/storage/segments.js +542 -0
- package/dist/core/storage/sink-graph.d.ts +65 -0
- package/dist/core/storage/sink-graph.d.ts.map +1 -0
- package/dist/core/storage/sink-graph.js +213 -0
- package/dist/core/storage/storm-control.d.ts +23 -0
- package/dist/core/storage/storm-control.d.ts.map +1 -0
- package/dist/core/storage/storm-control.js +59 -0
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +10 -14
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lazy-commands.d.ts +26 -0
- package/dist/lazy-commands.d.ts.map +1 -0
- package/dist/lazy-commands.js +210 -0
- package/dist/lib/browser/client.d.ts +11 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +13 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/standalone-html.d.ts +25 -0
- package/dist/lib/browser/standalone-html.d.ts.map +1 -0
- package/dist/lib/browser/standalone-html.js +284 -0
- package/dist/lib/docs-lint.d.ts +2 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +45 -80
- package/dist/lib/docs-metadata-audit.d.ts +3 -0
- package/dist/lib/docs-metadata-audit.d.ts.map +1 -1
- package/dist/lib/docs-metadata-audit.js +13 -23
- package/dist/lib/docs-metadata-sync.d.ts +1 -0
- package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
- package/dist/lib/docs-metadata-sync.js +28 -10
- package/dist/lib/docs-repository-view.d.ts +22 -0
- package/dist/lib/docs-repository-view.d.ts.map +1 -0
- package/dist/lib/docs-repository-view.js +185 -0
- package/dist/lib/http/client.d.ts +11 -5
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +22 -4
- package/dist/lib/http/index.d.ts +1 -1
- package/dist/lib/http/index.d.ts.map +1 -1
- package/dist/lib/http/index.js +1 -1
- package/dist/lib/indexer-exclusions.d.ts +27 -0
- package/dist/lib/indexer-exclusions.d.ts.map +1 -0
- package/dist/lib/indexer-exclusions.js +176 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +1 -0
- package/dist/lib/instructions/templates.d.ts +2 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +149 -4
- package/dist/lib/local-file-url.d.ts +9 -0
- package/dist/lib/local-file-url.d.ts.map +1 -0
- package/dist/lib/local-file-url.js +20 -0
- package/dist/lib/web-performance.d.ts +118 -0
- package/dist/lib/web-performance.d.ts.map +1 -0
- package/dist/lib/web-performance.js +192 -0
- package/package.json +34 -2
- package/schemas/config.schema.json +58 -0
- package/schemas/event-v3.schema.json +3073 -581
- package/src/commander.ts +462 -93
- package/src/commands/agents.ts +617 -355
- package/src/commands/backup.ts +18 -14
- package/src/commands/browse-session.ts +12 -0
- package/src/commands/browse.ts +31 -9
- package/src/commands/conversations.ts +279 -0
- package/src/commands/decision.ts +8 -2
- package/src/commands/deinit.ts +6 -0
- package/src/commands/docs.ts +25 -6
- package/src/commands/doctor.ts +75 -9
- package/src/commands/events.ts +21 -1
- package/src/commands/fetch.ts +7 -6
- package/src/commands/files.ts +143 -0
- package/src/commands/governor.ts +13 -12
- package/src/commands/inbox.ts +61 -0
- package/src/commands/init.ts +78 -16
- package/src/commands/ledger-v3.ts +421 -1
- package/src/commands/logs.ts +224 -0
- package/src/commands/messages.ts +61 -0
- package/src/commands/outline.ts +6 -2
- package/src/commands/presence.ts +7 -2
- package/src/commands/read.ts +6 -2
- package/src/commands/semantic.ts +244 -0
- package/src/commands/storage.ts +310 -0
- package/src/commands/tunnel.ts +66 -52
- package/src/commands/web.ts +92 -7
- package/src/commands/work.ts +3 -3
- package/src/commands/workflow.ts +13 -13
- package/src/core/agents/canonical-emit.ts +15 -0
- package/src/core/agents/claim-path.ts +23 -0
- package/src/core/agents/cli.ts +259 -53
- package/src/core/agents/coord-client.ts +92 -8
- package/src/core/agents/live-authority-v3.ts +342 -14
- package/src/core/agents/live-lifecycle-v3.ts +14 -4
- package/src/core/agents/live-observation-v3.ts +13 -3
- package/src/core/agents/render/prompt-context.ts +40 -16
- package/src/core/agents/rules/stop-hook.ts +129 -25
- package/src/core/agents/rules/verdict.ts +1 -0
- package/src/core/agents/session-events.ts +4 -0
- package/src/core/agents/session-finalization-state-v3.ts +1 -1
- package/src/core/agents/session-finalizer-v3.ts +4 -1
- package/src/core/agents/session-name-display.ts +9 -1
- package/src/core/agents/state/activity-log.ts +7 -2
- package/src/core/agents/state/heartbeat-reader.ts +69 -0
- package/src/core/agents/state/heartbeat-writer.ts +47 -84
- package/src/core/agents/state/live-coordination-view.ts +17 -81
- package/src/core/agents/state/live-coordination-writer.ts +141 -0
- package/src/core/agents/state/names.ts +190 -182
- package/src/core/agents/state/stale-sweep.ts +25 -5
- package/src/core/artifacts/constants.ts +4 -0
- package/src/core/artifacts/index.ts +113 -8
- package/src/core/config.ts +126 -5
- package/src/core/conversations/archive.ts +237 -0
- package/src/core/conversations/catalog.ts +79 -0
- package/src/core/conversations/context-pack.ts +72 -0
- package/src/core/conversations/contract.ts +178 -0
- package/src/core/conversations/evaluation.ts +30 -0
- package/src/core/conversations/index.ts +9 -0
- package/src/core/conversations/lifecycle.ts +63 -0
- package/src/core/conversations/normalize.ts +105 -0
- package/src/core/conversations/query.ts +318 -0
- package/src/core/conversations/search-index.ts +48 -0
- package/src/core/events/legacy-storage/canary.ts +177 -0
- package/src/core/events/legacy-storage/fence.ts +129 -0
- package/src/core/events/legacy-storage/index.ts +5 -0
- package/src/core/events/legacy-storage/inventory.ts +51 -0
- package/src/core/events/legacy-storage/manifest.ts +136 -0
- package/src/core/events/legacy-storage/reader.ts +178 -0
- package/src/core/events/v3/authority-outbox.ts +22 -1
- package/src/core/events/v3/base-contract.ts +56 -7
- package/src/core/events/v3/bootstrap.ts +195 -2
- package/src/core/events/v3/capabilities.ts +37 -3
- package/src/core/events/v3/control-witness.ts +339 -0
- package/src/core/events/v3/control-writer.ts +16 -0
- package/src/core/events/v3/control.ts +54 -18
- package/src/core/events/v3/coordination-transaction-recovery.ts +477 -0
- package/src/core/events/v3/coordination-view.ts +17 -1
- package/src/core/events/v3/generated.ts +1 -1
- package/src/core/events/v3/index.ts +26 -2
- package/src/core/events/v3/latency.ts +213 -2
- package/src/core/events/v3/live-route-observer.ts +80 -0
- package/src/core/events/v3/live-routing.ts +182 -45
- package/src/core/events/v3/producers/command-recorder.ts +10 -1
- package/src/core/events/v3/producers/coordination-recorder.ts +112 -15
- package/src/core/events/v3/producers/diagnostic-summaries.ts +593 -0
- package/src/core/events/v3/producers/hook-base.ts +116 -2
- package/src/core/events/v3/producers/intake.ts +48 -2
- package/src/core/events/v3/producers/producer-state-observer.ts +52 -0
- package/src/core/events/v3/producers/recorder.ts +1403 -30
- package/src/core/events/v3/reader.ts +243 -2
- package/src/core/events/v3/rotation-config.ts +98 -0
- package/src/core/events/v3/runtime-build.ts +41 -0
- package/src/core/events/v3/runtime-identity.ts +16 -0
- package/src/core/events/v3/runtime-telemetry-capabilities.ts +143 -0
- package/src/core/events/v3/support-storage/classify.ts +209 -0
- package/src/core/events/v3/support-storage/eligibility.ts +58 -0
- package/src/core/events/v3/support-storage/index.ts +9 -0
- package/src/core/events/v3/support-storage/inventory.ts +135 -0
- package/src/core/events/v3/support-storage/logical-authority.ts +117 -0
- package/src/core/events/v3/support-storage/maintenance.ts +501 -0
- package/src/core/events/v3/support-storage/pack-contract.ts +337 -0
- package/src/core/events/v3/support-storage/pack-reader.ts +346 -0
- package/src/core/events/v3/support-storage/pack-writer.ts +352 -0
- package/src/core/events/v3/support-storage/recovery.ts +43 -0
- package/src/core/events/v3/turn-telemetry.ts +66 -5
- package/src/core/events/v3/writer.ts +168 -16
- package/src/core/governor/plan-read.ts +5 -18
- package/src/core/governor/planning.ts +5 -8
- package/src/core/governor/service-read.ts +320 -2
- package/src/core/governor/service.ts +49 -22
- package/src/core/guard/evidence-v3.ts +1 -0
- package/src/core/hooks/adapter/events.ts +1 -0
- package/src/core/hooks/adapter/output.ts +12 -5
- package/src/core/hooks/adapter/parse.ts +16 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +1200 -0
- package/src/core/hooks/cli.ts +342 -64
- package/src/core/hooks/effects/image-capture.ts +4 -1
- package/src/core/hooks/remediation-cap.ts +83 -0
- package/src/core/hooks/resolve/coord-root.ts +91 -11
- package/src/core/hooks/resolve/owner.ts +4 -0
- package/src/core/hooks/resolve/transcript.ts +55 -11
- package/src/core/inbox/contract.ts +81 -0
- package/src/core/inbox/index.ts +4 -0
- package/src/core/inbox/lifecycle.ts +104 -0
- package/src/core/inbox/service.ts +305 -0
- package/src/core/inbox/watcher.ts +36 -0
- package/src/core/presence/codec-digest.ts +15 -9
- package/src/core/presence/relay-client.ts +55 -7
- package/src/core/semantic/adapters.ts +452 -0
- package/src/core/semantic/contract.ts +430 -0
- package/src/core/semantic/evidence.ts +336 -0
- package/src/core/semantic/index.ts +201 -0
- package/src/core/semantic/once.ts +496 -0
- package/src/core/semantic/prompt.ts +96 -0
- package/src/core/semantic/review.ts +803 -0
- package/src/core/semantic/routes.ts +30 -0
- package/src/core/semantic/scheduler.ts +151 -0
- package/src/core/semantic/service-status.ts +212 -0
- package/src/core/semantic/service.ts +681 -0
- package/src/core/semantic/soak.ts +638 -0
- package/src/core/semantic/storage.ts +300 -0
- package/src/core/semantic/usage.ts +272 -0
- package/src/core/semantic/validate.ts +243 -0
- package/src/core/storage/buffer.ts +124 -0
- package/src/core/storage/builtins.ts +916 -0
- package/src/core/storage/catalog.ts +467 -0
- package/src/core/storage/config.ts +423 -0
- package/src/core/storage/contract.ts +342 -0
- package/src/core/storage/durable-history.ts +738 -0
- package/src/core/storage/health.ts +76 -0
- package/src/core/storage/index.ts +15 -0
- package/src/core/storage/inventory.ts +859 -0
- package/src/core/storage/jsonl.ts +213 -0
- package/src/core/storage/log-retention.ts +555 -0
- package/src/core/storage/logger.ts +546 -0
- package/src/core/storage/maintenance-providers.ts +200 -0
- package/src/core/storage/maintenance.ts +929 -0
- package/src/core/storage/metrics.ts +199 -0
- package/src/core/storage/policy.ts +141 -0
- package/src/core/storage/process-log.ts +203 -0
- package/src/core/storage/query.ts +360 -0
- package/src/core/storage/receipts.ts +162 -0
- package/src/core/storage/segments.ts +721 -0
- package/src/core/storage/sink-graph.ts +343 -0
- package/src/core/storage/storm-control.ts +91 -0
- package/src/core/work/state.ts +12 -21
- package/src/core/workflow/live-session-v3.ts +2 -4
- package/src/index.ts +2 -0
- package/src/lazy-commands.ts +294 -0
- package/src/lib/browser/client.ts +21 -0
- package/src/lib/browser/index.ts +4 -0
- package/src/lib/browser/standalone-html.ts +312 -0
- package/src/lib/docs-lint.ts +58 -77
- package/src/lib/docs-metadata-audit.ts +26 -21
- package/src/lib/docs-metadata-sync.ts +32 -9
- package/src/lib/docs-repository-view.ts +220 -0
- package/src/lib/http/client.ts +44 -8
- package/src/lib/http/index.ts +6 -1
- package/src/lib/indexer-exclusions.ts +173 -0
- package/src/lib/instructions/apply.ts +1 -0
- package/src/lib/instructions/templates.ts +154 -4
- package/src/lib/local-file-url.ts +31 -0
- package/src/lib/web-performance.ts +335 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
appendFileSync,
|
|
4
|
+
chmodSync,
|
|
5
|
+
closeSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
fsyncSync,
|
|
8
|
+
lstatSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
openSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
rmdirSync,
|
|
15
|
+
statSync,
|
|
16
|
+
unlinkSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { hostname } from "node:os";
|
|
20
|
+
import { join, resolve } from "node:path";
|
|
21
|
+
import { coordEnv } from "../../../../lib/env.ts";
|
|
22
|
+
import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
|
|
23
|
+
import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
|
|
24
|
+
import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../writer.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Producer-side bound on duplicate diagnostic file creation.
|
|
28
|
+
*
|
|
29
|
+
* A producer fault can emit the same diagnostic thousands of times per day,
|
|
30
|
+
* and every emission used to become one loose file in the diagnostics spool.
|
|
31
|
+
* This module keeps the first bounded loose exemplars per
|
|
32
|
+
* (category, reason, instance) key and UTC day unchanged, then coalesces
|
|
33
|
+
* further identical emissions into one deterministic summary file per key and
|
|
34
|
+
* window, preserving the exact logical count, first and last times,
|
|
35
|
+
* represented bytes, hourly rate buckets, and bounded exemplar digests.
|
|
36
|
+
*
|
|
37
|
+
* Below the bound the gate creates no state at all: admitted loose filenames
|
|
38
|
+
* carry the key digest as a suffix, and the gate counts a key's emissions for
|
|
39
|
+
* the window from those filenames alone. A summary file and its short-lived
|
|
40
|
+
* per-key lease exist only for a key that actually crossed the bound, so a
|
|
41
|
+
* healthy spool gains zero files and zero directories from this mitigation.
|
|
42
|
+
*
|
|
43
|
+
* The mitigation is fail-open: any count, lease, read, or publish failure
|
|
44
|
+
* admits the loose diagnostic exactly as before and appends to a lock-free,
|
|
45
|
+
* size-capped emergency log. It never deletes, moves, or rewrites an existing
|
|
46
|
+
* loose diagnostic, and it never calls back into the diagnostic writer.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
export const DIAGNOSTIC_SUMMARY_FORMAT = "harnery-v3-diagnostic-summary" as const;
|
|
50
|
+
export const DIAGNOSTIC_SUMMARY_VERSION = 1 as const;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Loose exemplars admitted per (category, reason, instance) key per UTC day
|
|
54
|
+
* before identical emissions coalesce into the key's summary. Frozen from a
|
|
55
|
+
* whole-history measurement: healthy categories peak well under this value
|
|
56
|
+
* per day across ALL keys combined, while a producer fault exceeds it within
|
|
57
|
+
* minutes. The bound is enforced from filename counts without cross-process
|
|
58
|
+
* locking, so simultaneous writers straddling the crossing can admit a few
|
|
59
|
+
* extra exemplars; the bound is a flood stop, not an exact quota.
|
|
60
|
+
*/
|
|
61
|
+
export const DIAGNOSTIC_LOOSE_EXEMPLAR_LIMIT = 32;
|
|
62
|
+
|
|
63
|
+
/** Bounded exemplar digests retained inside one summary. */
|
|
64
|
+
const SUMMARY_EXEMPLAR_LIMIT = 4;
|
|
65
|
+
/**
|
|
66
|
+
* Hourly rate buckets retained inside one summary. One summary covers one UTC
|
|
67
|
+
* day, so at most 24 buckets are reachable; the margin absorbs clock skew.
|
|
68
|
+
* Consumer windows wider than a day (doctor reads 48h) span multiple
|
|
69
|
+
* day-scoped summary files, not more buckets in one file.
|
|
70
|
+
*/
|
|
71
|
+
const SUMMARY_HOUR_BUCKET_LIMIT = 26;
|
|
72
|
+
/** Refuse to publish a summary larger than this; falls open to loose writes. */
|
|
73
|
+
const SUMMARY_MAX_BYTES = 128 * 1024;
|
|
74
|
+
/** Coalesce updates fsync only every Nth increment; a crash between durable
|
|
75
|
+
* points can lose at most this many recent increments from the summary. */
|
|
76
|
+
const SUMMARY_DURABLE_EVERY = 32;
|
|
77
|
+
/** Stop appending to the emergency fail-open log past this size. */
|
|
78
|
+
const MITIGATION_HEALTH_MAX_BYTES = 256 * 1024;
|
|
79
|
+
export const DIAGNOSTIC_MITIGATION_HEALTH_FILE = "mitigation-health.ndjson";
|
|
80
|
+
const LEASE_STALE_AFTER_MS = 5_000;
|
|
81
|
+
const LEASE_RETRIES = 4;
|
|
82
|
+
const LEASE_RETRY_MS = 10;
|
|
83
|
+
const METADATA_KEY_LIMIT = 16;
|
|
84
|
+
const METADATA_VALUE_LIMIT = 256;
|
|
85
|
+
/** Extracts the 15-digit epoch-ms segment of a loose diagnostic order key. */
|
|
86
|
+
const LOOSE_ORDER_KEY_TIMESTAMP = /-(\d{15})-\d{20}-/;
|
|
87
|
+
|
|
88
|
+
const sleepCell = new Int32Array(new SharedArrayBuffer(4));
|
|
89
|
+
|
|
90
|
+
export interface DiagnosticContentFingerprint {
|
|
91
|
+
bytes: number;
|
|
92
|
+
sha256: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface DiagnosticGateInputV3 {
|
|
96
|
+
/** Already sanitized by the diagnostic writer. */
|
|
97
|
+
category: string;
|
|
98
|
+
reason?: string;
|
|
99
|
+
instanceId?: string;
|
|
100
|
+
fingerprint: DiagnosticContentFingerprint;
|
|
101
|
+
/** Approved scalar metadata only; stored once per summary. */
|
|
102
|
+
metadata: Record<string, string | number | boolean | null>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type DiagnosticGateDecisionV3 =
|
|
106
|
+
| {
|
|
107
|
+
admit: true;
|
|
108
|
+
/**
|
|
109
|
+
* Appended to the loose filename before `.json` so future gate calls
|
|
110
|
+
* can count the key's admitted exemplars from the directory listing.
|
|
111
|
+
* Empty when the gate is disabled.
|
|
112
|
+
*/
|
|
113
|
+
loose_name_suffix: string;
|
|
114
|
+
}
|
|
115
|
+
| {
|
|
116
|
+
admit: false;
|
|
117
|
+
/** The summary file the emission was durably coalesced into. */
|
|
118
|
+
summary_path: string;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export interface DiagnosticSummaryV3 {
|
|
122
|
+
format: typeof DIAGNOSTIC_SUMMARY_FORMAT;
|
|
123
|
+
format_version: typeof DIAGNOSTIC_SUMMARY_VERSION;
|
|
124
|
+
category: string;
|
|
125
|
+
reason: string;
|
|
126
|
+
instance_id: string;
|
|
127
|
+
/** UTC day this summary covers, as YYYY-MM-DD. */
|
|
128
|
+
window: string;
|
|
129
|
+
/** Loose exemplar files observed for the key when the bound was crossed. */
|
|
130
|
+
loose_count: number;
|
|
131
|
+
/** Identical emissions coalesced here instead of becoming loose files. */
|
|
132
|
+
summarized_count: number;
|
|
133
|
+
first_summarized_at: string | null;
|
|
134
|
+
last_summarized_at: string | null;
|
|
135
|
+
/** Sum of content-fingerprint bytes across summarized emissions. */
|
|
136
|
+
represented_bytes: number;
|
|
137
|
+
/** Digests of the first summarized emissions, bounded. */
|
|
138
|
+
exemplars: DiagnosticContentFingerprint[];
|
|
139
|
+
/** Approved scalar metadata from the first summarized emission. */
|
|
140
|
+
metadata: Record<string, string | number | boolean | null>;
|
|
141
|
+
/** Summarized-only counts per UTC hour ("YYYY-MM-DDTHH"), pruned. */
|
|
142
|
+
recent_hours: Record<string, number>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Aggregate of the append-only emergency fail-open log. */
|
|
146
|
+
export interface DiagnosticMitigationHealthV3 {
|
|
147
|
+
fail_open_count: number;
|
|
148
|
+
last_fail_open_at: string | null;
|
|
149
|
+
by_stage: Record<string, number>;
|
|
150
|
+
/** True when the log hit its size cap and later fail-opens were dropped. */
|
|
151
|
+
truncated: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function diagnosticSummariesRootV3(coordRoot: string): string {
|
|
155
|
+
return join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "diagnostic-summaries");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function looseDiagnosticsRootV3(coordRoot: string): string {
|
|
159
|
+
return join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "diagnostics");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function diagnosticSummariesEnabledV3(): boolean {
|
|
163
|
+
return coordEnv("V3_DIAGNOSTIC_SUMMARIES") !== "0";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function sanitizeComponent(value: string | undefined, fallback: string): string {
|
|
167
|
+
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
168
|
+
const safe = value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 128);
|
|
169
|
+
return safe.length > 0 ? safe : fallback;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function keyDigest(category: string, reason: string, instanceId: string): string {
|
|
173
|
+
return createHash("sha256")
|
|
174
|
+
.update(`${category}\n${reason}\n${instanceId}`)
|
|
175
|
+
.digest("hex")
|
|
176
|
+
.slice(0, 16);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function utcDay(nowMs: number): string {
|
|
180
|
+
return new Date(nowMs).toISOString().slice(0, 10);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function utcHour(nowMs: number): string {
|
|
184
|
+
return new Date(nowMs).toISOString().slice(0, 13);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function summaryPath(root: string, category: string, window: string, digest: string): string {
|
|
188
|
+
return join(root, `${category}-${window.replaceAll("-", "")}-${digest}.json`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function ensureOwnerOnlyDirectory(path: string): void {
|
|
192
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
193
|
+
chmodSync(path, 0o700);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function publishOwnerOnlyJson(path: string, value: unknown, durable: boolean): void {
|
|
197
|
+
const contents = `${JSON.stringify(value)}\n`;
|
|
198
|
+
if (Buffer.byteLength(contents, "utf8") > SUMMARY_MAX_BYTES) {
|
|
199
|
+
throw new Error("diagnostic summary exceeds its size bound");
|
|
200
|
+
}
|
|
201
|
+
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
202
|
+
let fd: number | undefined;
|
|
203
|
+
try {
|
|
204
|
+
fd = openSync(temporary, "wx", 0o600);
|
|
205
|
+
writeFileSync(fd, contents, "utf8");
|
|
206
|
+
if (durable) fsyncSync(fd);
|
|
207
|
+
closeSync(fd);
|
|
208
|
+
fd = undefined;
|
|
209
|
+
renameSync(temporary, path);
|
|
210
|
+
if (durable) fsyncParentDirectory(path);
|
|
211
|
+
} finally {
|
|
212
|
+
if (fd !== undefined) closeSync(fd);
|
|
213
|
+
if (existsSync(temporary)) unlinkSync(temporary);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function pidIsAlive(pid: number): boolean {
|
|
218
|
+
if (!Number.isSafeInteger(pid) || pid < 1) return false;
|
|
219
|
+
try {
|
|
220
|
+
process.kill(pid, 0);
|
|
221
|
+
return true;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
return (error as NodeJS.ErrnoException).code !== "ESRCH";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function withKeyLease<T>(root: string, digest: string, operation: () => T): T {
|
|
228
|
+
const leasesDir = join(root, "leases");
|
|
229
|
+
ensureOwnerOnlyDirectory(leasesDir);
|
|
230
|
+
const leaseDir = join(leasesDir, digest);
|
|
231
|
+
const authority = createHash("sha256").update(resolve(root)).digest("hex");
|
|
232
|
+
let lease: ReturnType<typeof acquireNoClobberLease> | undefined;
|
|
233
|
+
for (let attempt = 0; attempt < LEASE_RETRIES; attempt += 1) {
|
|
234
|
+
try {
|
|
235
|
+
lease = acquireNoClobberLease({
|
|
236
|
+
path: leaseDir,
|
|
237
|
+
scope: "event-v3-diagnostic-summary",
|
|
238
|
+
authoritySha256: authority,
|
|
239
|
+
staleAfterMs: LEASE_STALE_AFTER_MS,
|
|
240
|
+
validateStaleOwner: (owner) => owner.host === hostname() && !pidIsAlive(owner.pid),
|
|
241
|
+
});
|
|
242
|
+
break;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (attempt === LEASE_RETRIES - 1) throw error;
|
|
245
|
+
Atomics.wait(sleepCell, 0, 0, LEASE_RETRY_MS);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!lease) throw new Error("diagnostic summary lease is busy");
|
|
249
|
+
try {
|
|
250
|
+
return operation();
|
|
251
|
+
} finally {
|
|
252
|
+
// A durably published result must survive release-time faults (an epoch
|
|
253
|
+
// rotation can remove the lease state mid-operation); stale takeover
|
|
254
|
+
// recovers any residue. The empty lease directory is removed so per-key
|
|
255
|
+
// lease state never outlives the contention that needed it.
|
|
256
|
+
try {
|
|
257
|
+
lease.release();
|
|
258
|
+
} catch {
|
|
259
|
+
// Swallowed: the operation's outcome already stands.
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
rmdirSync(leaseDir);
|
|
263
|
+
} catch {
|
|
264
|
+
// A concurrent acquirer repopulated it, or release left residue.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Count the key's admitted loose exemplars for a UTC-day window from their
|
|
271
|
+
* digest-suffixed filenames. Files written before this mitigation carry no
|
|
272
|
+
* suffix and are not counted, so a pre-existing flood does not consume the
|
|
273
|
+
* window's exemplar budget.
|
|
274
|
+
*/
|
|
275
|
+
function countLooseExemplars(coordRoot: string, digest: string, window: string): number {
|
|
276
|
+
const directory = looseDiagnosticsRootV3(coordRoot);
|
|
277
|
+
if (!existsSync(directory)) return 0;
|
|
278
|
+
const windowStartMs = Date.parse(`${window}T00:00:00.000Z`);
|
|
279
|
+
const windowEndMs = windowStartMs + 24 * 60 * 60 * 1000;
|
|
280
|
+
const suffix = `-${digest}.json`;
|
|
281
|
+
let count = 0;
|
|
282
|
+
for (const name of readdirSync(directory)) {
|
|
283
|
+
if (!name.endsWith(suffix)) continue;
|
|
284
|
+
const epochMs = Number(LOOSE_ORDER_KEY_TIMESTAMP.exec(name)?.[1]);
|
|
285
|
+
if (Number.isFinite(epochMs) && epochMs >= windowStartMs && epochMs < windowEndMs) {
|
|
286
|
+
count += 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return count;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function boundedMetadata(
|
|
293
|
+
metadata: Record<string, string | number | boolean | null>,
|
|
294
|
+
): Record<string, string | number | boolean | null> {
|
|
295
|
+
const bounded: Record<string, string | number | boolean | null> = {};
|
|
296
|
+
let kept = 0;
|
|
297
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
298
|
+
if (kept >= METADATA_KEY_LIMIT) break;
|
|
299
|
+
if (typeof value === "string" && value.length > METADATA_VALUE_LIMIT) continue;
|
|
300
|
+
bounded[key] = value;
|
|
301
|
+
kept += 1;
|
|
302
|
+
}
|
|
303
|
+
return bounded;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function pruneHourBuckets(buckets: Record<string, number>, nowMs: number): Record<string, number> {
|
|
307
|
+
const keys = Object.keys(buckets).sort();
|
|
308
|
+
const horizon = utcHour(nowMs - SUMMARY_HOUR_BUCKET_LIMIT * 60 * 60 * 1000);
|
|
309
|
+
const pruned: Record<string, number> = {};
|
|
310
|
+
for (const key of keys) {
|
|
311
|
+
if (key >= horizon) pruned[key] = buckets[key] as number;
|
|
312
|
+
}
|
|
313
|
+
// A pathological clock can still grow the map; hard-cap by newest keys.
|
|
314
|
+
const capped = Object.keys(pruned).sort().slice(-SUMMARY_HOUR_BUCKET_LIMIT);
|
|
315
|
+
const result: Record<string, number> = {};
|
|
316
|
+
for (const key of capped) result[key] = pruned[key] as number;
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function freshSummary(
|
|
321
|
+
category: string,
|
|
322
|
+
reason: string,
|
|
323
|
+
instanceId: string,
|
|
324
|
+
window: string,
|
|
325
|
+
): DiagnosticSummaryV3 {
|
|
326
|
+
return {
|
|
327
|
+
format: DIAGNOSTIC_SUMMARY_FORMAT,
|
|
328
|
+
format_version: DIAGNOSTIC_SUMMARY_VERSION,
|
|
329
|
+
category,
|
|
330
|
+
reason,
|
|
331
|
+
instance_id: instanceId,
|
|
332
|
+
window,
|
|
333
|
+
loose_count: 0,
|
|
334
|
+
summarized_count: 0,
|
|
335
|
+
first_summarized_at: null,
|
|
336
|
+
last_summarized_at: null,
|
|
337
|
+
represented_bytes: 0,
|
|
338
|
+
exemplars: [],
|
|
339
|
+
metadata: {},
|
|
340
|
+
recent_hours: {},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function readSummaryFile(path: string): DiagnosticSummaryV3 | undefined {
|
|
345
|
+
let parsed: unknown;
|
|
346
|
+
try {
|
|
347
|
+
const metadata = lstatSync(path);
|
|
348
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) return undefined;
|
|
349
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
350
|
+
} catch {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
354
|
+
const summary = parsed as DiagnosticSummaryV3;
|
|
355
|
+
if (
|
|
356
|
+
summary.format !== DIAGNOSTIC_SUMMARY_FORMAT ||
|
|
357
|
+
summary.format_version !== DIAGNOSTIC_SUMMARY_VERSION ||
|
|
358
|
+
typeof summary.category !== "string" ||
|
|
359
|
+
typeof summary.reason !== "string" ||
|
|
360
|
+
typeof summary.instance_id !== "string" ||
|
|
361
|
+
!/^\d{4}-\d{2}-\d{2}$/.test(summary.window) ||
|
|
362
|
+
!Number.isSafeInteger(summary.loose_count) ||
|
|
363
|
+
summary.loose_count < 0 ||
|
|
364
|
+
!Number.isSafeInteger(summary.summarized_count) ||
|
|
365
|
+
summary.summarized_count < 0 ||
|
|
366
|
+
!Number.isSafeInteger(summary.represented_bytes) ||
|
|
367
|
+
summary.represented_bytes < 0 ||
|
|
368
|
+
!Array.isArray(summary.exemplars) ||
|
|
369
|
+
!summary.recent_hours ||
|
|
370
|
+
typeof summary.recent_hours !== "object" ||
|
|
371
|
+
Array.isArray(summary.recent_hours)
|
|
372
|
+
) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
// Drop malformed rate buckets so a corrupt value can neither poison later
|
|
376
|
+
// increments nor silently diverge windowed counts from the total.
|
|
377
|
+
const hours: Record<string, number> = {};
|
|
378
|
+
for (const [key, value] of Object.entries(summary.recent_hours)) {
|
|
379
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}$/.test(key) && Number.isSafeInteger(value) && value >= 0) {
|
|
380
|
+
hours[key] = value;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
summary.recent_hours = hours;
|
|
384
|
+
return summary;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Append one line to the emergency fail-open log. Lock-free by design: an
|
|
389
|
+
* O_APPEND write of one small line is atomic, so concurrent fail-opens never
|
|
390
|
+
* serialize on shared state. Best-effort, size-capped, and nonrecursive: a
|
|
391
|
+
* failure here is swallowed, never diagnosed, and never blocks the loose
|
|
392
|
+
* fallback write.
|
|
393
|
+
*/
|
|
394
|
+
function recordFailOpen(root: string, stage: string, nowMs: number): void {
|
|
395
|
+
try {
|
|
396
|
+
ensureOwnerOnlyDirectory(root);
|
|
397
|
+
const path = join(root, DIAGNOSTIC_MITIGATION_HEALTH_FILE);
|
|
398
|
+
try {
|
|
399
|
+
if (statSync(path).size >= MITIGATION_HEALTH_MAX_BYTES) return;
|
|
400
|
+
} catch {
|
|
401
|
+
// Missing log starts fresh.
|
|
402
|
+
}
|
|
403
|
+
const line = JSON.stringify({ at: new Date(nowMs).toISOString(), stage, pid: process.pid });
|
|
404
|
+
appendFileSync(path, `${line}\n`, { mode: 0o600 });
|
|
405
|
+
} catch {
|
|
406
|
+
// The emergency log may undercount when storage is unavailable; it is a
|
|
407
|
+
// signal, not accounting, and must never throw into the producer path.
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function readMitigationHealth(path: string): DiagnosticMitigationHealthV3 | null {
|
|
412
|
+
let contents: string;
|
|
413
|
+
let size = 0;
|
|
414
|
+
try {
|
|
415
|
+
const metadata = lstatSync(path);
|
|
416
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) return null;
|
|
417
|
+
size = metadata.size;
|
|
418
|
+
contents = readFileSync(path, "utf8");
|
|
419
|
+
} catch {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
const health: DiagnosticMitigationHealthV3 = {
|
|
423
|
+
fail_open_count: 0,
|
|
424
|
+
last_fail_open_at: null,
|
|
425
|
+
by_stage: {},
|
|
426
|
+
truncated: size >= MITIGATION_HEALTH_MAX_BYTES,
|
|
427
|
+
};
|
|
428
|
+
for (const line of contents.split("\n")) {
|
|
429
|
+
if (line.trim().length === 0) continue;
|
|
430
|
+
// Every non-empty line was one fail-open, even a torn one.
|
|
431
|
+
health.fail_open_count += 1;
|
|
432
|
+
try {
|
|
433
|
+
const row = JSON.parse(line) as { at?: unknown; stage?: unknown };
|
|
434
|
+
if (typeof row.at === "string") health.last_fail_open_at = row.at;
|
|
435
|
+
if (typeof row.stage === "string" && row.stage.length <= 32) {
|
|
436
|
+
if (Object.keys(health.by_stage).length < 16 || health.by_stage[row.stage] !== undefined) {
|
|
437
|
+
health.by_stage[row.stage] = (health.by_stage[row.stage] ?? 0) + 1;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
} catch {
|
|
441
|
+
// A torn tail line still counts above.
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return health;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Decide whether one diagnostic emission may become a loose file.
|
|
449
|
+
*
|
|
450
|
+
* Below the per-key bound (counted from digest-suffixed loose filenames) the
|
|
451
|
+
* emission is admitted with the suffix to append, and no summary or lease
|
|
452
|
+
* state is created. At or past the bound the emission is durably coalesced
|
|
453
|
+
* into the key's summary under a short per-key lease and `admit: false` is
|
|
454
|
+
* returned with the summary path. Any mitigation failure, and the disabled
|
|
455
|
+
* gate, admit the loose write exactly as before.
|
|
456
|
+
*/
|
|
457
|
+
export function gateProducerDiagnosticV3(
|
|
458
|
+
coordRoot: string,
|
|
459
|
+
input: DiagnosticGateInputV3,
|
|
460
|
+
nowMs = Date.now(),
|
|
461
|
+
): DiagnosticGateDecisionV3 {
|
|
462
|
+
if (!diagnosticSummariesEnabledV3()) return { admit: true, loose_name_suffix: "" };
|
|
463
|
+
const at = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
464
|
+
const category = sanitizeComponent(input.category, "unknown");
|
|
465
|
+
const reason = sanitizeComponent(input.reason, "none");
|
|
466
|
+
const instanceId = sanitizeComponent(input.instanceId, "unknown");
|
|
467
|
+
const window = utcDay(at);
|
|
468
|
+
const digest = keyDigest(category, reason, instanceId);
|
|
469
|
+
const admitLoose: DiagnosticGateDecisionV3 = { admit: true, loose_name_suffix: `-${digest}` };
|
|
470
|
+
const root = diagnosticSummariesRootV3(coordRoot);
|
|
471
|
+
let stage = "peek";
|
|
472
|
+
try {
|
|
473
|
+
const path = summaryPath(root, category, window, digest);
|
|
474
|
+
if (!readSummaryFile(path)) {
|
|
475
|
+
stage = "count";
|
|
476
|
+
if (countLooseExemplars(coordRoot, digest, window) < DIAGNOSTIC_LOOSE_EXEMPLAR_LIMIT) {
|
|
477
|
+
return admitLoose;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
stage = "coalesce";
|
|
481
|
+
ensureOwnerOnlyDirectory(root);
|
|
482
|
+
return withKeyLease(root, digest, () => {
|
|
483
|
+
const existing = readSummaryFile(path);
|
|
484
|
+
const state = existing ?? freshSummary(category, reason, instanceId, window);
|
|
485
|
+
if (!existing) state.loose_count = countLooseExemplars(coordRoot, digest, window);
|
|
486
|
+
const atIso = new Date(at).toISOString();
|
|
487
|
+
state.summarized_count += 1;
|
|
488
|
+
state.first_summarized_at ??= atIso;
|
|
489
|
+
state.last_summarized_at = atIso;
|
|
490
|
+
if (Number.isSafeInteger(input.fingerprint?.bytes) && input.fingerprint.bytes >= 0) {
|
|
491
|
+
state.represented_bytes += input.fingerprint.bytes;
|
|
492
|
+
}
|
|
493
|
+
if (state.exemplars.length < SUMMARY_EXEMPLAR_LIMIT && input.fingerprint) {
|
|
494
|
+
state.exemplars.push({
|
|
495
|
+
bytes: input.fingerprint.bytes,
|
|
496
|
+
sha256: input.fingerprint.sha256,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
if (Object.keys(state.metadata).length === 0) {
|
|
500
|
+
state.metadata = boundedMetadata(input.metadata ?? {});
|
|
501
|
+
}
|
|
502
|
+
const hour = utcHour(at);
|
|
503
|
+
state.recent_hours[hour] = (state.recent_hours[hour] ?? 0) + 1;
|
|
504
|
+
state.recent_hours = pruneHourBuckets(state.recent_hours, at);
|
|
505
|
+
const durable = !existing || state.summarized_count % SUMMARY_DURABLE_EVERY === 0;
|
|
506
|
+
publishOwnerOnlyJson(path, state, durable);
|
|
507
|
+
return { admit: false, summary_path: path };
|
|
508
|
+
});
|
|
509
|
+
} catch {
|
|
510
|
+
recordFailOpen(root, stage, at);
|
|
511
|
+
return admitLoose;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export interface DiagnosticSummaryListingV3 {
|
|
516
|
+
summaries: DiagnosticSummaryV3[];
|
|
517
|
+
/** Physical summary files, including any that failed validation. */
|
|
518
|
+
file_count: number;
|
|
519
|
+
unreadable_count: number;
|
|
520
|
+
mitigation_health: DiagnosticMitigationHealthV3 | null;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Read every summary in the active epoch's `diagnostic-summaries/` subtree.
|
|
525
|
+
* Skips lease state, temp files, and the mitigation health log (which is
|
|
526
|
+
* aggregated into `mitigation_health` instead). Never throws; a missing
|
|
527
|
+
* subtree returns an empty listing.
|
|
528
|
+
*/
|
|
529
|
+
export function listDiagnosticSummariesV3(coordRoot: string): DiagnosticSummaryListingV3 {
|
|
530
|
+
const root = diagnosticSummariesRootV3(coordRoot);
|
|
531
|
+
const listing: DiagnosticSummaryListingV3 = {
|
|
532
|
+
summaries: [],
|
|
533
|
+
file_count: 0,
|
|
534
|
+
unreadable_count: 0,
|
|
535
|
+
mitigation_health: null,
|
|
536
|
+
};
|
|
537
|
+
let names: string[];
|
|
538
|
+
try {
|
|
539
|
+
const metadata = lstatSync(root);
|
|
540
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) return listing;
|
|
541
|
+
names = readdirSync(root);
|
|
542
|
+
} catch {
|
|
543
|
+
return listing;
|
|
544
|
+
}
|
|
545
|
+
for (const name of names.sort()) {
|
|
546
|
+
if (name === "leases" || name.includes(".tmp-")) continue;
|
|
547
|
+
if (name.startsWith("mitigation-health")) {
|
|
548
|
+
if (name === DIAGNOSTIC_MITIGATION_HEALTH_FILE) {
|
|
549
|
+
listing.mitigation_health = readMitigationHealth(join(root, name));
|
|
550
|
+
}
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (!name.endsWith(".json")) continue;
|
|
554
|
+
listing.file_count += 1;
|
|
555
|
+
const summary = readSummaryFile(join(root, name));
|
|
556
|
+
if (summary) listing.summaries.push(summary);
|
|
557
|
+
else listing.unreadable_count += 1;
|
|
558
|
+
}
|
|
559
|
+
return listing;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Sum summarized-only occurrences inside a lookback window, from hour
|
|
564
|
+
* buckets. Hour-granular and conservative: a bucket is counted only when its
|
|
565
|
+
* whole hour lies inside `[sinceMs, nowMs]`, so a boundary bucket is excluded
|
|
566
|
+
* (up to 59 minutes undercounted) rather than fully included (up to 59
|
|
567
|
+
* minutes overcounted), and a future-dated bucket from clock skew never
|
|
568
|
+
* inflates a window.
|
|
569
|
+
*/
|
|
570
|
+
export function countSummarizedSinceV3(
|
|
571
|
+
summaries: readonly DiagnosticSummaryV3[],
|
|
572
|
+
sinceMs: number,
|
|
573
|
+
filter?: (summary: DiagnosticSummaryV3) => boolean,
|
|
574
|
+
nowMs = Date.now(),
|
|
575
|
+
): number {
|
|
576
|
+
let count = 0;
|
|
577
|
+
for (const summary of summaries) {
|
|
578
|
+
if (filter && !filter(summary)) continue;
|
|
579
|
+
for (const [hour, value] of Object.entries(summary.recent_hours)) {
|
|
580
|
+
const hourStartMs = Date.parse(`${hour}:00:00.000Z`);
|
|
581
|
+
if (
|
|
582
|
+
Number.isFinite(hourStartMs) &&
|
|
583
|
+
hourStartMs >= sinceMs &&
|
|
584
|
+
hourStartMs <= nowMs &&
|
|
585
|
+
Number.isSafeInteger(value) &&
|
|
586
|
+
value > 0
|
|
587
|
+
) {
|
|
588
|
+
count += value;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return count;
|
|
593
|
+
}
|