harnery 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +7 -1
- package/dist/commands/adapter.js +2 -2
- package/dist/commands/agents.d.ts +144 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +1408 -565
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +29 -16
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +234 -12
- package/dist/commands/checkpoint.d.ts.map +1 -1
- package/dist/commands/checkpoint.js +3 -2
- package/dist/commands/claude-desktop.d.ts +2 -2
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +2 -2
- package/dist/commands/context.js +1 -6
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +22 -38
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +91 -28
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +55 -7
- package/dist/commands/events.d.ts +5 -0
- package/dist/commands/events.d.ts.map +1 -0
- package/dist/commands/events.js +189 -0
- package/dist/commands/governor.d.ts.map +1 -1
- package/dist/commands/governor.js +13 -4
- package/dist/commands/init.d.ts +10 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +233 -51
- package/dist/commands/instructions.d.ts +4 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +100 -0
- package/dist/commands/journal.d.ts.map +1 -1
- package/dist/commands/journal.js +4 -13
- package/dist/commands/ledger-v3.d.ts +5 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -0
- package/dist/commands/ledger-v3.js +91 -0
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +15 -6
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/web.d.ts +6 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +67 -7
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +22 -5
- package/dist/core/adapter.d.ts +5 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +1 -0
- package/dist/core/adapters/attest.d.ts +5 -2
- package/dist/core/adapters/attest.d.ts.map +1 -1
- package/dist/core/adapters/attest.js +8 -3
- package/dist/core/adapters/profiles.d.ts +6 -6
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +4 -4
- package/dist/core/agents/canonical-emit.d.ts +17 -15
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +27 -25
- package/dist/core/agents/cli.js +212 -235
- package/dist/core/agents/codex-archive-v3.d.ts +14 -0
- package/dist/core/agents/codex-archive-v3.d.ts.map +1 -0
- package/dist/core/agents/codex-archive-v3.js +110 -0
- package/dist/core/agents/coord-client.d.ts +19 -70
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +65 -155
- package/dist/core/agents/finalization.d.ts +4 -1
- package/dist/core/agents/finalization.d.ts.map +1 -1
- package/dist/core/agents/finalization.js +89 -32
- package/dist/core/agents/index.d.ts +5 -5
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +5 -5
- package/dist/core/agents/live-authority-v3.d.ts +59 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -0
- package/dist/core/agents/live-authority-v3.js +233 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +20 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -0
- package/dist/core/agents/live-lifecycle-v3.js +91 -0
- package/dist/core/agents/live-observation-v3.d.ts +25 -0
- package/dist/core/agents/live-observation-v3.d.ts.map +1 -0
- package/dist/core/agents/live-observation-v3.js +157 -0
- package/dist/core/agents/render/prompt-context.d.ts +10 -4
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +61 -55
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +15 -33
- package/dist/core/agents/rules/claim-conflict.d.ts +3 -3
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +79 -127
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +3 -18
- package/dist/core/agents/rules/stop-hook.d.ts +9 -50
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +193 -236
- package/dist/core/agents/session-events.d.ts +5 -7
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +127 -95
- package/dist/core/agents/session-finalization-state-v3.d.ts +30 -0
- package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -0
- package/dist/core/agents/session-finalization-state-v3.js +40 -0
- package/dist/core/agents/session-finalizer-v3.d.ts +107 -0
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -0
- package/dist/core/agents/session-finalizer-v3.js +734 -0
- package/dist/core/agents/session-name-display.d.ts +40 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -0
- package/dist/core/agents/session-name-display.js +119 -0
- package/dist/core/agents/state/activity-log.d.ts +3 -3
- package/dist/core/agents/state/activity-log.d.ts.map +1 -1
- package/dist/core/agents/state/activity-log.js +7 -16
- package/dist/core/agents/state/heartbeat-writer.d.ts +29 -46
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +59 -199
- package/dist/core/agents/state/live-coordination-view.d.ts +31 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -0
- package/dist/core/agents/state/live-coordination-view.js +209 -0
- package/dist/core/agents/state/session-state.d.ts +45 -0
- package/dist/core/agents/state/session-state.d.ts.map +1 -0
- package/dist/core/agents/state/session-state.js +90 -0
- package/dist/core/agents/state/stale-sweep.d.ts +1 -1
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +26 -15
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +5 -7
- package/dist/core/config.d.ts +33 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +98 -2
- package/dist/core/context/index.d.ts +1 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +9 -9
- package/dist/core/events/span-parent.d.ts +15 -0
- package/dist/core/events/span-parent.d.ts.map +1 -0
- package/dist/core/events/span-parent.js +34 -0
- package/dist/core/events/v3/advance.d.ts +14 -0
- package/dist/core/events/v3/advance.d.ts.map +1 -0
- package/dist/core/events/v3/advance.js +134 -0
- package/dist/core/events/v3/authority-outbox.d.ts +78 -0
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -0
- package/dist/core/events/v3/authority-outbox.js +399 -0
- package/dist/core/events/v3/base-builder.d.ts +20 -0
- package/dist/core/events/v3/base-builder.d.ts.map +1 -0
- package/dist/core/events/v3/base-builder.js +29 -0
- package/dist/core/events/v3/base-contract.d.ts +7576 -0
- package/dist/core/events/v3/base-contract.d.ts.map +1 -0
- package/dist/core/events/v3/base-contract.js +647 -0
- package/dist/core/events/v3/bootstrap.d.ts +34 -0
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -0
- package/dist/core/events/v3/bootstrap.js +175 -0
- package/dist/core/events/v3/builder.d.ts +20 -0
- package/dist/core/events/v3/builder.d.ts.map +1 -0
- package/dist/core/events/v3/builder.js +28 -0
- package/dist/core/events/v3/canonical.d.ts +31 -0
- package/dist/core/events/v3/canonical.d.ts.map +1 -0
- package/dist/core/events/v3/canonical.js +100 -0
- package/dist/core/events/v3/capabilities.d.ts +31 -0
- package/dist/core/events/v3/capabilities.d.ts.map +1 -0
- package/dist/core/events/v3/capabilities.js +107 -0
- package/dist/core/events/v3/capability-drift.d.ts +22 -0
- package/dist/core/events/v3/capability-drift.d.ts.map +1 -0
- package/dist/core/events/v3/capability-drift.js +134 -0
- package/dist/core/events/v3/catalog.d.ts +38 -0
- package/dist/core/events/v3/catalog.d.ts.map +1 -0
- package/dist/core/events/v3/catalog.js +135 -0
- package/dist/core/events/v3/clock-order.d.ts +34 -0
- package/dist/core/events/v3/clock-order.d.ts.map +1 -0
- package/dist/core/events/v3/clock-order.js +35 -0
- package/dist/core/events/v3/contract.d.ts +171 -0
- package/dist/core/events/v3/contract.d.ts.map +1 -0
- package/dist/core/events/v3/contract.js +180 -0
- package/dist/core/events/v3/control.d.ts +111 -0
- package/dist/core/events/v3/control.d.ts.map +1 -0
- package/dist/core/events/v3/control.js +419 -0
- package/dist/core/events/v3/coordination-view.d.ts +69 -0
- package/dist/core/events/v3/coordination-view.d.ts.map +1 -0
- package/dist/core/events/v3/coordination-view.js +119 -0
- package/dist/core/events/v3/economics.d.ts +77 -0
- package/dist/core/events/v3/economics.d.ts.map +1 -0
- package/dist/core/events/v3/economics.js +274 -0
- package/dist/core/events/v3/finalization-view.d.ts +26 -0
- package/dist/core/events/v3/finalization-view.d.ts.map +1 -0
- package/dist/core/events/v3/finalization-view.js +37 -0
- package/dist/core/events/v3/fingerprint-keys.d.ts +25 -0
- package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -0
- package/dist/core/events/v3/fingerprint-keys.js +217 -0
- package/dist/core/events/v3/generated.d.ts +4 -0
- package/dist/core/events/v3/generated.d.ts.map +1 -0
- package/dist/core/events/v3/generated.js +47 -0
- package/dist/core/events/v3/ids.d.ts +9 -0
- package/dist/core/events/v3/ids.d.ts.map +1 -0
- package/dist/core/events/v3/ids.js +25 -0
- package/dist/core/events/v3/index.d.ts +35 -0
- package/dist/core/events/v3/index.d.ts.map +1 -0
- package/dist/core/events/v3/index.js +33 -0
- package/dist/core/events/v3/latency.d.ts +63 -0
- package/dist/core/events/v3/latency.d.ts.map +1 -0
- package/dist/core/events/v3/latency.js +412 -0
- package/dist/core/events/v3/live-feed.d.ts +45 -0
- package/dist/core/events/v3/live-feed.d.ts.map +1 -0
- package/dist/core/events/v3/live-feed.js +246 -0
- package/dist/core/events/v3/live-observation.d.ts +35 -0
- package/dist/core/events/v3/live-observation.d.ts.map +1 -0
- package/dist/core/events/v3/live-observation.js +1 -0
- package/dist/core/events/v3/live-routing.d.ts +57 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -0
- package/dist/core/events/v3/live-routing.js +107 -0
- package/dist/core/events/v3/producers/command-recorder.d.ts +67 -0
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/command-recorder.js +477 -0
- package/dist/core/events/v3/producers/command.d.ts +50 -0
- package/dist/core/events/v3/producers/command.d.ts.map +1 -0
- package/dist/core/events/v3/producers/command.js +135 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +53 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.js +376 -0
- package/dist/core/events/v3/producers/coordination.d.ts +90 -0
- package/dist/core/events/v3/producers/coordination.d.ts.map +1 -0
- package/dist/core/events/v3/producers/coordination.js +229 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +54 -0
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -0
- package/dist/core/events/v3/producers/hook-base.js +424 -0
- package/dist/core/events/v3/producers/hook.d.ts +29 -0
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -0
- package/dist/core/events/v3/producers/hook.js +71 -0
- package/dist/core/events/v3/producers/intake.d.ts +65 -0
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -0
- package/dist/core/events/v3/producers/intake.js +244 -0
- package/dist/core/events/v3/producers/recorder.d.ts +240 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/recorder.js +2176 -0
- package/dist/core/events/v3/projection.d.ts +101 -0
- package/dist/core/events/v3/projection.d.ts.map +1 -0
- package/dist/core/events/v3/projection.js +479 -0
- package/dist/core/events/v3/reader.d.ts +75 -0
- package/dist/core/events/v3/reader.d.ts.map +1 -0
- package/dist/core/events/v3/reader.js +778 -0
- package/dist/core/events/v3/recovery-record.d.ts +47 -0
- package/dist/core/events/v3/recovery-record.d.ts.map +1 -0
- package/dist/core/events/v3/recovery-record.js +167 -0
- package/dist/core/events/v3/recovery.d.ts +22 -0
- package/dist/core/events/v3/recovery.d.ts.map +1 -0
- package/dist/core/events/v3/recovery.js +227 -0
- package/dist/core/events/v3/span-state.d.ts +52 -0
- package/dist/core/events/v3/span-state.d.ts.map +1 -0
- package/dist/core/events/v3/span-state.js +142 -0
- package/dist/core/events/v3/targets.d.ts +23 -0
- package/dist/core/events/v3/targets.d.ts.map +1 -0
- package/dist/core/events/v3/targets.js +134 -0
- package/dist/core/events/v3/turn-telemetry.d.ts +41 -0
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -0
- package/dist/core/events/v3/turn-telemetry.js +168 -0
- package/dist/core/events/v3/validate.d.ts +9 -0
- package/dist/core/events/v3/validate.d.ts.map +1 -0
- package/dist/core/events/v3/validate.js +281 -0
- package/dist/core/events/v3/writer.d.ts +56 -0
- package/dist/core/events/v3/writer.d.ts.map +1 -0
- package/dist/core/events/v3/writer.js +335 -0
- package/dist/core/governor/index.d.ts +1 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +1 -1
- package/dist/core/governor/plan-api.d.ts +2 -1
- package/dist/core/governor/plan-api.d.ts.map +1 -1
- package/dist/core/governor/plan-api.js +6 -3
- package/dist/core/governor/read.d.ts +1 -1
- package/dist/core/governor/read.d.ts.map +1 -1
- package/dist/core/governor/read.js +1 -1
- package/dist/core/governor/state.d.ts +9 -0
- package/dist/core/governor/state.d.ts.map +1 -1
- package/dist/core/governor/state.js +40 -3
- package/dist/core/guard/config.d.ts +5 -0
- package/dist/core/guard/config.d.ts.map +1 -0
- package/dist/core/guard/config.js +157 -0
- package/dist/core/guard/coordinator.d.ts +14 -0
- package/dist/core/guard/coordinator.d.ts.map +1 -0
- package/dist/core/guard/coordinator.js +173 -0
- package/dist/core/guard/evaluator.d.ts +4 -0
- package/dist/core/guard/evaluator.d.ts.map +1 -0
- package/dist/core/guard/evaluator.js +242 -0
- package/dist/core/guard/evidence-v3.d.ts +15 -0
- package/dist/core/guard/evidence-v3.d.ts.map +1 -0
- package/dist/core/guard/evidence-v3.js +171 -0
- package/dist/core/guard/index.d.ts +10 -0
- package/dist/core/guard/index.d.ts.map +1 -0
- package/dist/core/guard/index.js +6 -0
- package/dist/core/guard/live-source-v3.d.ts +40 -0
- package/dist/core/guard/live-source-v3.d.ts.map +1 -0
- package/dist/core/guard/live-source-v3.js +128 -0
- package/dist/core/guard/storage.d.ts +27 -0
- package/dist/core/guard/storage.d.ts.map +1 -0
- package/dist/core/guard/storage.js +140 -0
- package/dist/core/guard/types.d.ts +119 -0
- package/dist/core/guard/types.d.ts.map +1 -0
- package/dist/core/guard/types.js +1 -0
- package/dist/core/hooks/adapter/detect.d.ts +18 -1
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +29 -0
- package/dist/core/hooks/adapter/events.d.ts +7 -4
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +13 -4
- package/dist/core/hooks/adapter/output.d.ts +2 -2
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.d.ts +7 -4
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +64 -24
- package/dist/core/hooks/adapter/wiring.d.ts +30 -4
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +89 -11
- package/dist/core/hooks/cli.d.ts +1 -1
- package/dist/core/hooks/cli.js +345 -400
- package/dist/core/hooks/codex-authorization.d.ts +30 -0
- package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
- package/dist/core/hooks/codex-authorization.js +199 -0
- package/dist/core/hooks/effects/image-capture.d.ts +20 -34
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +212 -221
- package/dist/core/hooks/effects/index.d.ts +1 -9
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +1 -24
- package/dist/core/hooks/events/input-hash.d.ts +7 -0
- package/dist/core/hooks/events/input-hash.d.ts.map +1 -0
- package/dist/core/hooks/events/input-hash.js +43 -0
- package/dist/core/hooks/index.d.ts +4 -3
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +2 -3
- package/dist/core/hooks/resolve/coord-root.d.ts +1 -1
- package/dist/core/hooks/resolve/coord-root.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +16 -15
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +51 -42
- package/dist/core/hooks/resolve/transcript.d.ts +33 -3
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +155 -14
- package/dist/core/hooks/session-name-presence.d.ts +9 -10
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +15 -15
- package/dist/core/hooks/unsafe-cross-shell.d.ts +1 -1
- package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -1
- package/dist/core/journal/index.d.ts +2 -2
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +9 -22
- package/dist/core/presence/blob.d.ts +8 -3
- package/dist/core/presence/blob.d.ts.map +1 -1
- package/dist/core/presence/blob.js +54 -41
- package/dist/core/presence/codec-digest.d.ts +29 -0
- package/dist/core/presence/codec-digest.d.ts.map +1 -0
- package/dist/core/presence/codec-digest.js +170 -0
- package/dist/core/presence/index.js +15 -1
- package/dist/core/presence/relay-client.d.ts +1 -1
- package/dist/core/presence/relay-client.d.ts.map +1 -1
- package/dist/core/presence/relay-client.js +5 -5
- package/dist/core/work/index.d.ts +1 -1
- package/dist/core/work/index.d.ts.map +1 -1
- package/dist/core/work/index.js +1 -1
- package/dist/core/work/read.d.ts +1 -1
- package/dist/core/work/read.d.ts.map +1 -1
- package/dist/core/work/read.js +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +6 -0
- package/dist/core/work/state.d.ts +9 -0
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +17 -3
- package/dist/core/workflow/engine.js +24 -25
- package/dist/core/workflow/live-session-v3.d.ts +19 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -0
- package/dist/core/workflow/live-session-v3.js +86 -0
- package/dist/core/workflow/scope-id.d.ts +3 -0
- package/dist/core/workflow/scope-id.d.ts.map +1 -0
- package/dist/core/workflow/scope-id.js +5 -0
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +53 -19
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/browser/client.d.ts +41 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +203 -0
- package/dist/lib/browser/critique.d.ts +28 -2
- package/dist/lib/browser/critique.d.ts.map +1 -1
- package/dist/lib/browser/critique.js +74 -18
- package/dist/lib/browser/geometry.d.ts +1 -1
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +130 -6
- package/dist/lib/browser/index.d.ts +3 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +3 -0
- package/dist/lib/browser/qa-plan.d.ts +174 -0
- package/dist/lib/browser/qa-plan.d.ts.map +1 -0
- package/dist/lib/browser/qa-plan.js +321 -0
- package/dist/lib/browser/qa-reuse.d.ts +99 -0
- package/dist/lib/browser/qa-reuse.d.ts.map +1 -0
- package/dist/lib/browser/qa-reuse.js +222 -0
- package/dist/lib/browser/qa-snapshot.d.ts +64 -0
- package/dist/lib/browser/qa-snapshot.d.ts.map +1 -0
- package/dist/lib/browser/qa-snapshot.js +171 -0
- package/dist/lib/browser/runts.d.ts.map +1 -1
- package/dist/lib/browser/runts.js +22 -1
- package/dist/lib/browser/tiling.d.ts +71 -0
- package/dist/lib/browser/tiling.d.ts.map +1 -0
- package/dist/lib/browser/tiling.js +118 -0
- package/dist/lib/context/index.d.ts +0 -3
- package/dist/lib/context/index.d.ts.map +1 -1
- package/dist/lib/context/index.js +20 -42
- package/dist/lib/council/index.d.ts +2 -2
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +9 -20
- package/dist/lib/docs-frontmatter.d.ts +1 -6
- package/dist/lib/docs-frontmatter.d.ts.map +1 -1
- package/dist/lib/docs-frontmatter.js +12 -78
- package/dist/lib/docs-index.d.ts +9 -0
- package/dist/lib/docs-index.d.ts.map +1 -1
- package/dist/lib/docs-index.js +32 -3
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +14 -30
- package/dist/lib/docs-metadata-audit.d.ts +19 -0
- package/dist/lib/docs-metadata-audit.d.ts.map +1 -0
- package/dist/lib/docs-metadata-audit.js +132 -0
- package/dist/lib/docs-metadata-managed.d.ts +23 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -0
- package/dist/lib/docs-metadata-managed.js +78 -0
- package/dist/lib/docs-metadata-sync.d.ts +20 -0
- package/dist/lib/docs-metadata-sync.d.ts.map +1 -0
- package/dist/lib/docs-metadata-sync.js +222 -0
- package/dist/lib/docs-metadata-v2-schema.d.ts +273 -0
- package/dist/lib/docs-metadata-v2-schema.d.ts.map +1 -0
- package/dist/lib/docs-metadata-v2-schema.js +134 -0
- package/dist/lib/docs-metadata-v2.d.ts +21 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -0
- package/dist/lib/docs-metadata-v2.js +424 -0
- package/dist/lib/docs-new.d.ts +11 -0
- package/dist/lib/docs-new.d.ts.map +1 -0
- package/dist/lib/docs-new.js +52 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -1
- package/dist/lib/docs-sweep.js +45 -29
- package/dist/lib/headless/index.d.ts +98 -0
- package/dist/lib/headless/index.d.ts.map +1 -0
- package/dist/lib/headless/index.js +211 -0
- package/dist/lib/identities/assume.d.ts +4 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +52 -68
- package/dist/lib/instructions/apply.d.ts +5 -4
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +32 -20
- package/dist/lib/instructions/bundle.d.ts +33 -0
- package/dist/lib/instructions/bundle.d.ts.map +1 -0
- package/dist/lib/instructions/bundle.js +149 -0
- package/dist/lib/instructions/splice.d.ts +0 -1
- package/dist/lib/instructions/splice.d.ts.map +1 -1
- package/dist/lib/instructions/splice.js +22 -6
- package/dist/lib/instructions/templates.d.ts +7 -5
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +79 -9
- package/package.json +27 -6
- package/schemas/config.schema.json +120 -2
- package/schemas/docs-metadata-v2.schema.json +441 -0
- package/schemas/event-v3.schema.json +24472 -0
- package/src/commander.ts +7 -1
- package/src/commands/adapter.ts +2 -2
- package/src/commands/agents.ts +1737 -570
- package/src/commands/artifacts.ts +7 -6
- package/src/commands/backup.ts +29 -21
- package/src/commands/browse.ts +349 -8
- package/src/commands/checkpoint.ts +4 -3
- package/src/commands/claude-desktop.ts +7 -5
- package/src/commands/context.ts +1 -6
- package/src/commands/decision.ts +27 -44
- package/src/commands/docs.ts +100 -33
- package/src/commands/doctor.ts +73 -8
- package/src/commands/events.ts +256 -0
- package/src/commands/governor.ts +18 -4
- package/src/commands/init.ts +264 -57
- package/src/commands/instructions.ts +129 -0
- package/src/commands/journal.ts +4 -13
- package/src/commands/ledger-v3.ts +111 -0
- package/src/commands/presence.ts +18 -11
- package/src/commands/sync.ts +1 -1
- package/src/commands/web.ts +67 -8
- package/src/commands/work.ts +28 -5
- package/src/core/adapter.ts +5 -0
- package/src/core/adapters/attest.ts +8 -3
- package/src/core/adapters/profiles.ts +4 -6
- package/src/core/agents/canonical-emit.ts +39 -28
- package/src/core/agents/cli.ts +242 -263
- package/src/core/agents/codex-archive-v3.ts +138 -0
- package/src/core/agents/coord-client.ts +75 -177
- package/src/core/agents/finalization.ts +120 -38
- package/src/core/agents/index.ts +5 -5
- package/src/core/agents/live-authority-v3.ts +375 -0
- package/src/core/agents/live-lifecycle-v3.ts +140 -0
- package/src/core/agents/live-observation-v3.ts +216 -0
- package/src/core/agents/render/prompt-context.ts +89 -55
- package/src/core/agents/render/session-context.ts +23 -32
- package/src/core/agents/rules/claim-conflict.ts +85 -126
- package/src/core/agents/rules/commit-conflict.ts +3 -14
- package/src/core/agents/rules/stop-hook.ts +239 -293
- package/src/core/agents/session-events.ts +137 -104
- package/src/core/agents/session-finalization-state-v3.ts +90 -0
- package/src/core/agents/session-finalizer-v3.ts +953 -0
- package/src/core/agents/session-name-display.ts +149 -0
- package/src/core/agents/state/activity-log.ts +6 -14
- package/src/core/agents/state/heartbeat-writer.ts +98 -242
- package/src/core/agents/state/live-coordination-view.ts +290 -0
- package/src/core/agents/state/session-state.ts +143 -0
- package/src/core/agents/state/stale-sweep.ts +40 -17
- package/src/core/artifacts/index.ts +4 -5
- package/src/core/config.ts +148 -3
- package/src/core/context/index.ts +9 -17
- package/src/core/events/span-parent.ts +46 -0
- package/src/core/events/v3/advance.ts +168 -0
- package/src/core/events/v3/authority-outbox.ts +548 -0
- package/src/core/events/v3/base-builder.ts +60 -0
- package/src/core/events/v3/base-contract.ts +882 -0
- package/src/core/events/v3/bootstrap.ts +226 -0
- package/src/core/events/v3/builder.ts +59 -0
- package/src/core/events/v3/canonical.ts +153 -0
- package/src/core/events/v3/capabilities.ts +171 -0
- package/src/core/events/v3/capability-drift.ts +177 -0
- package/src/core/events/v3/catalog.ts +202 -0
- package/src/core/events/v3/clock-order.ts +41 -0
- package/src/core/events/v3/contract.ts +365 -0
- package/src/core/events/v3/control.ts +576 -0
- package/src/core/events/v3/coordination-view.ts +193 -0
- package/src/core/events/v3/economics.ts +384 -0
- package/src/core/events/v3/finalization-view.ts +67 -0
- package/src/core/events/v3/fingerprint-keys.ts +260 -0
- package/src/core/events/v3/generated.ts +48 -0
- package/src/core/events/v3/ids.ts +33 -0
- package/src/core/events/v3/index.ts +322 -0
- package/src/core/events/v3/latency.ts +551 -0
- package/src/core/events/v3/live-feed.ts +319 -0
- package/src/core/events/v3/live-observation.ts +39 -0
- package/src/core/events/v3/live-routing.ts +135 -0
- package/src/core/events/v3/producers/command-recorder.ts +631 -0
- package/src/core/events/v3/producers/command.ts +201 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +538 -0
- package/src/core/events/v3/producers/coordination.ts +355 -0
- package/src/core/events/v3/producers/hook-base.ts +578 -0
- package/src/core/events/v3/producers/hook.ts +119 -0
- package/src/core/events/v3/producers/intake.ts +319 -0
- package/src/core/events/v3/producers/recorder.ts +2805 -0
- package/src/core/events/v3/projection.ts +653 -0
- package/src/core/events/v3/reader.ts +1127 -0
- package/src/core/events/v3/recovery-record.ts +244 -0
- package/src/core/events/v3/recovery.ts +271 -0
- package/src/core/events/v3/span-state.ts +187 -0
- package/src/core/events/v3/targets.ts +182 -0
- package/src/core/events/v3/turn-telemetry.ts +259 -0
- package/src/core/events/v3/validate.ts +361 -0
- package/src/core/events/v3/writer.ts +419 -0
- package/src/core/governor/index.ts +3 -0
- package/src/core/governor/plan-api.ts +10 -2
- package/src/core/governor/read.ts +3 -0
- package/src/core/governor/state.ts +52 -5
- package/src/core/guard/config.ts +206 -0
- package/src/core/guard/coordinator.ts +227 -0
- package/src/core/guard/evaluator.ts +343 -0
- package/src/core/guard/evidence-v3.ts +227 -0
- package/src/core/guard/index.ts +35 -0
- package/src/core/guard/live-source-v3.ts +204 -0
- package/src/core/guard/storage.ts +188 -0
- package/src/core/guard/types.ts +152 -0
- package/src/core/hooks/adapter/detect.ts +28 -1
- package/src/core/hooks/adapter/events.ts +15 -6
- package/src/core/hooks/adapter/output.ts +7 -2
- package/src/core/hooks/adapter/parse.ts +81 -36
- package/src/core/hooks/adapter/wiring.ts +119 -13
- package/src/core/hooks/cli.ts +398 -422
- package/src/core/hooks/codex-authorization.ts +253 -0
- package/src/core/hooks/effects/image-capture.ts +228 -238
- package/src/core/hooks/effects/index.ts +1 -36
- package/src/core/hooks/events/input-hash.ts +47 -0
- package/src/core/hooks/index.ts +4 -11
- package/src/core/hooks/resolve/coord-root.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +52 -45
- package/src/core/hooks/resolve/transcript.ts +165 -14
- package/src/core/hooks/session-name-presence.ts +14 -15
- package/src/core/hooks/unsafe-cross-shell.ts +1 -1
- package/src/core/journal/index.ts +15 -22
- package/src/core/presence/blob.ts +63 -38
- package/src/core/presence/codec-digest.ts +215 -0
- package/src/core/presence/index.ts +18 -1
- package/src/core/presence/relay-client.ts +5 -5
- package/src/core/work/index.ts +3 -0
- package/src/core/work/read.ts +3 -0
- package/src/core/work/runner.ts +6 -0
- package/src/core/work/state.ts +27 -3
- package/src/core/workflow/engine.ts +24 -24
- package/src/core/workflow/live-session-v3.ts +126 -0
- package/src/core/workflow/scope-id.ts +6 -0
- package/src/core/workflow/workspaces/leases.ts +56 -19
- package/src/index.ts +1 -1
- package/src/lib/browser/client.ts +230 -0
- package/src/lib/browser/critique.ts +104 -27
- package/src/lib/browser/geometry.ts +143 -14
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/browser/qa-plan.ts +501 -0
- package/src/lib/browser/qa-reuse.ts +306 -0
- package/src/lib/browser/qa-snapshot.ts +223 -0
- package/src/lib/browser/runts.ts +27 -2
- package/src/lib/browser/tiling.ts +164 -0
- package/src/lib/context/index.ts +22 -46
- package/src/lib/council/index.ts +9 -21
- package/src/lib/docs-frontmatter.ts +8 -79
- package/src/lib/docs-index.ts +33 -3
- package/src/lib/docs-lint.ts +15 -29
- package/src/lib/docs-metadata-audit.ts +166 -0
- package/src/lib/docs-metadata-managed.ts +83 -0
- package/src/lib/docs-metadata-sync.ts +262 -0
- package/src/lib/docs-metadata-v2-schema.ts +138 -0
- package/src/lib/docs-metadata-v2.ts +646 -0
- package/src/lib/docs-new.ts +63 -0
- package/src/lib/docs-sweep.ts +42 -28
- package/src/lib/headless/index.ts +287 -0
- package/src/lib/identities/assume.ts +67 -86
- package/src/lib/instructions/apply.ts +33 -20
- package/src/lib/instructions/bundle.ts +199 -0
- package/src/lib/instructions/splice.ts +21 -7
- package/src/lib/instructions/templates.ts +85 -13
- package/dist/core/agents/cli-emit.d.ts +0 -27
- package/dist/core/agents/cli-emit.d.ts.map +0 -1
- package/dist/core/agents/cli-emit.js +0 -57
- package/dist/core/agents/codex-replay.d.ts +0 -29
- package/dist/core/agents/codex-replay.d.ts.map +0 -1
- package/dist/core/agents/codex-replay.js +0 -138
- package/dist/core/agents/events/consume.d.ts +0 -82
- package/dist/core/agents/events/consume.d.ts.map +0 -1
- package/dist/core/agents/events/consume.js +0 -195
- package/dist/core/agents/events/emit.d.ts +0 -42
- package/dist/core/agents/events/emit.d.ts.map +0 -1
- package/dist/core/agents/events/emit.js +0 -75
- package/dist/core/agents/events/ulid.d.ts +0 -11
- package/dist/core/agents/events/ulid.d.ts.map +0 -1
- package/dist/core/agents/events/ulid.js +0 -47
- package/dist/core/agents/state/heartbeat-projector.d.ts +0 -65
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
- package/dist/core/agents/state/heartbeat-projector.js +0 -527
- package/dist/core/hooks/events/emit.d.ts +0 -31
- package/dist/core/hooks/events/emit.d.ts.map +0 -1
- package/dist/core/hooks/events/emit.js +0 -93
- package/dist/core/hooks/events/rotate.d.ts +0 -43
- package/dist/core/hooks/events/rotate.d.ts.map +0 -1
- package/dist/core/hooks/events/rotate.js +0 -142
- package/dist/core/hooks/events/schema.d.ts +0 -312
- package/dist/core/hooks/events/schema.d.ts.map +0 -1
- package/dist/core/hooks/events/schema.js +0 -12
- package/dist/core/hooks/events/ulid.d.ts +0 -10
- package/dist/core/hooks/events/ulid.d.ts.map +0 -1
- package/dist/core/hooks/events/ulid.js +0 -47
- package/dist/lib/docs-frontmatter-migrate.d.ts +0 -33
- package/dist/lib/docs-frontmatter-migrate.d.ts.map +0 -1
- package/dist/lib/docs-frontmatter-migrate.js +0 -364
- package/src/core/agents/cli-emit.ts +0 -64
- package/src/core/agents/codex-replay.ts +0 -163
- package/src/core/agents/events/consume.ts +0 -254
- package/src/core/agents/events/emit.ts +0 -115
- package/src/core/agents/events/ulid.ts +0 -51
- package/src/core/agents/state/heartbeat-projector.ts +0 -582
- package/src/core/hooks/events/emit.ts +0 -125
- package/src/core/hooks/events/rotate.ts +0 -151
- package/src/core/hooks/events/schema.ts +0 -538
- package/src/core/hooks/events/ulid.ts +0 -51
- package/src/lib/docs-frontmatter-migrate.ts +0 -427
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `agent-hook` tool handlers (see effects/index.ts for the family rationale).
|
|
2
|
+
* Privacy-safe image artifact capture for normalized tool hooks.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the canonical stream. The web image feed (`/images`) groups those events by
|
|
10
|
-
* hash and streams them live over the existing SSE infra.
|
|
11
|
-
*
|
|
12
|
-
* Why this lives at the hook layer: it's the single adapter-agnostic chokepoint
|
|
13
|
-
* that sees every tool call across Claude Code / Cursor / Codex with the full
|
|
14
|
-
* `tool_input` (file paths) and `tool_response`. No per-command code needed.
|
|
15
|
-
*
|
|
16
|
-
* Everything here is best-effort and MUST NOT throw; callers wrap it in
|
|
17
|
-
* try/catch + logError, matching every other effect.
|
|
4
|
+
* Image bytes are content-addressed under `.harnery/images/`. The canonical V3
|
|
5
|
+
* ledger receives an `artifact.observed` record after the source tool event is
|
|
6
|
+
* durable, so the web feed can recover provenance without retaining command,
|
|
7
|
+
* output, intent, or absolute paths.
|
|
18
8
|
*/
|
|
19
9
|
|
|
20
|
-
import { createHash } from "node:crypto";
|
|
10
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
21
11
|
import {
|
|
22
12
|
existsSync,
|
|
23
13
|
mkdirSync,
|
|
@@ -28,185 +18,139 @@ import {
|
|
|
28
18
|
statSync,
|
|
29
19
|
writeFileSync,
|
|
30
20
|
} from "node:fs";
|
|
31
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
21
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
22
|
+
import { buildEventV3 } from "../../events/v3/builder.ts";
|
|
23
|
+
import type { EventV3 } from "../../events/v3/contract.ts";
|
|
24
|
+
import { writeEventV3 } from "../../events/v3/writer.ts";
|
|
32
25
|
import type { ParsedPayload } from "../adapter/parse.ts";
|
|
33
|
-
import { emit } from "../events/emit.ts";
|
|
34
|
-
import type { Adapter } from "../events/schema.ts";
|
|
35
26
|
|
|
36
|
-
/** Raster + vector image extensions the feed accepts. PDF is intentionally
|
|
37
|
-
* excluded: book renders aren't an "image feed" and don't thumbnail inline. */
|
|
38
27
|
const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"]);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
const MEDIA_TYPES: Record<string, string> = {
|
|
29
|
+
png: "image/png",
|
|
30
|
+
jpg: "image/jpeg",
|
|
31
|
+
jpeg: "image/jpeg",
|
|
32
|
+
gif: "image/gif",
|
|
33
|
+
webp: "image/webp",
|
|
34
|
+
bmp: "image/bmp",
|
|
35
|
+
svg: "image/svg+xml",
|
|
36
|
+
};
|
|
42
37
|
const MAX_CAPTURE_BYTES = 25 * 1024 * 1024;
|
|
43
|
-
|
|
44
|
-
/** A produced image must have been written within this window of the command
|
|
45
|
-
* finishing, otherwise a path merely *mentioned* in output (an old baseline,
|
|
46
|
-
* a doc reference) would be captured. Viewed images skip this gate. */
|
|
47
38
|
const PRODUCED_MTIME_WINDOW_MS = 120_000;
|
|
39
|
+
const IMAGE_PATH_RE = /[\w./~@:+-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi;
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
eventType: "tool.pre_use" | "tool.post_use";
|
|
56
|
-
/** The built event data (tool_name, tool_input string, intent, tool_use_id). */
|
|
57
|
-
data: Record<string, unknown>;
|
|
58
|
-
/** The full parsed payload, used for the un-clamped command + tool_response. */
|
|
59
|
-
payload: ParsedPayload | null;
|
|
60
|
-
instanceId: string;
|
|
61
|
-
sessionId: string;
|
|
62
|
-
adapter: Adapter;
|
|
41
|
+
export interface CapturedImageArtifact {
|
|
42
|
+
hash: string;
|
|
43
|
+
ext: string;
|
|
44
|
+
bytes: number;
|
|
45
|
+
role: "viewed" | "produced";
|
|
46
|
+
workspace_path?: string;
|
|
63
47
|
}
|
|
64
48
|
|
|
65
49
|
interface Candidate {
|
|
66
|
-
path: string;
|
|
67
|
-
role: "
|
|
68
|
-
intent?: string;
|
|
69
|
-
commandHead?: string;
|
|
50
|
+
path: string;
|
|
51
|
+
role: CapturedImageArtifact["role"];
|
|
70
52
|
requireRecentMtime: boolean;
|
|
71
53
|
}
|
|
72
54
|
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
export function captureImages(coordRoot: string, ctx: CaptureContext): void {
|
|
78
|
-
const imagesDir = join(coordRoot, ".harnery", "images");
|
|
79
|
-
const cwd = resolveCwd(ctx.payload);
|
|
80
|
-
const toolName = String(ctx.data.tool_name ?? "");
|
|
81
|
-
|
|
82
|
-
const candidates =
|
|
83
|
-
ctx.eventType === "tool.pre_use"
|
|
84
|
-
? collectViewed(toolName, ctx.data, cwd)
|
|
85
|
-
: collectProduced(toolName, ctx.payload, cwd);
|
|
86
|
-
|
|
87
|
-
if (candidates.length === 0) return;
|
|
88
|
-
|
|
89
|
-
for (const cand of candidates) {
|
|
90
|
-
// Never re-capture our own blob store (would loop on Reads of the gallery).
|
|
91
|
-
if (cand.path.startsWith(`${imagesDir}/`)) continue;
|
|
92
|
-
const captured = captureOne(imagesDir, cand);
|
|
93
|
-
if (!captured) continue;
|
|
94
|
-
emit(coordRoot, {
|
|
95
|
-
event_type: "image.captured",
|
|
96
|
-
instance_id: ctx.instanceId,
|
|
97
|
-
session_id: ctx.sessionId,
|
|
98
|
-
adapter: ctx.adapter,
|
|
99
|
-
data: {
|
|
100
|
-
hash: captured.hash,
|
|
101
|
-
ext: captured.ext,
|
|
102
|
-
bytes: captured.bytes,
|
|
103
|
-
role: cand.role,
|
|
104
|
-
source_path: canonicalize(coordRoot, cand.path),
|
|
105
|
-
tool_name: toolName,
|
|
106
|
-
tool_use_id: ctx.data.tool_use_id as string | undefined,
|
|
107
|
-
...(cand.intent ? { intent: cand.intent } : {}),
|
|
108
|
-
...(cand.commandHead ? { command_head: cand.commandHead } : {}),
|
|
109
|
-
},
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
111
|
-
} as Parameters<typeof emit>[1]);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Read tool → the file_path it's about to show (the "agent saw this" signal). */
|
|
116
|
-
function collectViewed(toolName: string, data: Record<string, unknown>, cwd: string): Candidate[] {
|
|
117
|
-
if (toolName !== "Read") return [];
|
|
118
|
-
const input = parseToolInput(data.tool_input);
|
|
119
|
-
const filePath = input?.file_path;
|
|
120
|
-
if (typeof filePath !== "string" || !filePath) return [];
|
|
121
|
-
if (!hasImageExt(filePath)) return [];
|
|
122
|
-
return [
|
|
123
|
-
{
|
|
124
|
-
path: toAbsolute(filePath, cwd),
|
|
125
|
-
role: "viewed",
|
|
126
|
-
intent: typeof data.intent === "string" ? data.intent : undefined,
|
|
127
|
-
requireRecentMtime: false,
|
|
128
|
-
},
|
|
129
|
-
];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Bash tool → scan the command + its output for freshly-written image files. */
|
|
133
|
-
function collectProduced(
|
|
134
|
-
toolName: string,
|
|
55
|
+
/** Copy every qualifying image referenced by one tool hook into the blob store. */
|
|
56
|
+
export function captureImages(
|
|
57
|
+
coordRoot: string,
|
|
58
|
+
eventType: "tool.requested" | "tool.completed",
|
|
135
59
|
payload: ParsedPayload | null,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
60
|
+
): CapturedImageArtifact[] {
|
|
61
|
+
const images = join(coordRoot, ".harnery", "images");
|
|
62
|
+
const cwd = payload?.cwd ? resolve(payload.cwd) : process.cwd();
|
|
63
|
+
const toolName = payload?.tool_name ?? "";
|
|
64
|
+
const candidates =
|
|
65
|
+
eventType === "tool.requested"
|
|
66
|
+
? collectViewed(toolName, payload?.tool_input, cwd)
|
|
67
|
+
: collectProduced(toolName, payload, cwd);
|
|
68
|
+
const captured: CapturedImageArtifact[] = [];
|
|
143
69
|
const seen = new Set<string>();
|
|
144
|
-
const out: Candidate[] = [];
|
|
145
|
-
for (const text of [command, responseText]) {
|
|
146
|
-
if (!text) continue;
|
|
147
|
-
for (const m of text.matchAll(IMAGE_PATH_RE)) {
|
|
148
|
-
const raw = m[0];
|
|
149
|
-
const abs = toAbsolute(raw, cwd);
|
|
150
|
-
if (seen.has(abs)) continue;
|
|
151
|
-
seen.add(abs);
|
|
152
|
-
out.push({ path: abs, role: "produced", commandHead, requireRecentMtime: true });
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return out;
|
|
156
|
-
}
|
|
157
70
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
if (candidate.path.startsWith(`${images}${sep}`)) continue;
|
|
73
|
+
const blob = captureOne(images, candidate);
|
|
74
|
+
if (!blob) continue;
|
|
75
|
+
const dedupeKey = `${blob.hash}:${candidate.role}`;
|
|
76
|
+
if (seen.has(dedupeKey)) continue;
|
|
77
|
+
seen.add(dedupeKey);
|
|
78
|
+
const workspacePath = safeWorkspacePath(coordRoot, candidate.path);
|
|
79
|
+
captured.push({
|
|
80
|
+
...blob,
|
|
81
|
+
role: candidate.role,
|
|
82
|
+
...(workspacePath ? { workspace_path: workspacePath } : {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return captured;
|
|
162
86
|
}
|
|
163
87
|
|
|
164
88
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
89
|
+
* Emit content-addressed image references as canonical V3 artifact events.
|
|
90
|
+
* Each observation uses a one-event producer boot, which preserves independent
|
|
91
|
+
* producer sequence continuity while the ledger writer serializes appends.
|
|
168
92
|
*/
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (cand.requireRecentMtime && Date.now() - st.mtimeMs > PRODUCED_MTIME_WINDOW_MS) {
|
|
178
|
-
return null; // mentioned but not freshly produced by this command
|
|
93
|
+
export function recordImageArtifactsV3(
|
|
94
|
+
coordRoot: string,
|
|
95
|
+
sourceEvent: EventV3,
|
|
96
|
+
artifacts: readonly CapturedImageArtifact[],
|
|
97
|
+
): void {
|
|
98
|
+
if (!("generation_id" in sourceEvent.scope) || !sourceEvent.attestation_id) return;
|
|
99
|
+
for (const artifact of artifacts) {
|
|
100
|
+
writeEventV3(coordRoot, buildImageArtifactEventV3(sourceEvent, artifact));
|
|
179
101
|
}
|
|
180
|
-
|
|
181
|
-
if (!ext || !IMAGE_EXTS.has(ext)) return null;
|
|
102
|
+
}
|
|
182
103
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const hash = createHash("sha256").update(bytes).digest("hex");
|
|
190
|
-
const dest = join(imagesDir, `${hash}.${ext}`);
|
|
191
|
-
if (!existsSync(dest)) {
|
|
192
|
-
try {
|
|
193
|
-
mkdirSync(imagesDir, { recursive: true });
|
|
194
|
-
const tmp = `${dest}.tmp.${process.pid}`;
|
|
195
|
-
writeFileSync(tmp, bytes);
|
|
196
|
-
renameSync(tmp, dest);
|
|
197
|
-
} catch {
|
|
198
|
-
return null; // couldn't store, don't emit a dangling event
|
|
199
|
-
}
|
|
104
|
+
export function buildImageArtifactEventV3(
|
|
105
|
+
sourceEvent: EventV3,
|
|
106
|
+
artifact: CapturedImageArtifact,
|
|
107
|
+
): EventV3 {
|
|
108
|
+
if (!("generation_id" in sourceEvent.scope) || !sourceEvent.attestation_id) {
|
|
109
|
+
throw new Error("image artifacts require a generation-scoped source event");
|
|
200
110
|
}
|
|
201
|
-
return
|
|
111
|
+
return buildEventV3("artifact.observed", {
|
|
112
|
+
producer: {
|
|
113
|
+
producer_id: "prd_agent-hook-image-capture",
|
|
114
|
+
boot_id: `boot_${randomUUID()}`,
|
|
115
|
+
sequence: 1,
|
|
116
|
+
component: "agent-hook",
|
|
117
|
+
build_id: sourceEvent.producer.build_id,
|
|
118
|
+
platform: sourceEvent.producer.platform,
|
|
119
|
+
...(sourceEvent.producer.bridge ? { bridge: sourceEvent.producer.bridge } : {}),
|
|
120
|
+
},
|
|
121
|
+
scope: {
|
|
122
|
+
root_id: sourceEvent.scope.root_id,
|
|
123
|
+
instance_id: sourceEvent.scope.instance_id,
|
|
124
|
+
session_id: sourceEvent.scope.session_id,
|
|
125
|
+
generation_id: sourceEvent.scope.generation_id,
|
|
126
|
+
...(sourceEvent.scope.run_id ? { run_id: sourceEvent.scope.run_id } : {}),
|
|
127
|
+
...(sourceEvent.scope.workflow_id ? { workflow_id: sourceEvent.scope.workflow_id } : {}),
|
|
128
|
+
...(sourceEvent.scope.workflow_agent_id
|
|
129
|
+
? { workflow_agent_id: sourceEvent.scope.workflow_agent_id }
|
|
130
|
+
: {}),
|
|
131
|
+
},
|
|
132
|
+
attestation_id: sourceEvent.attestation_id as `att_${string}`,
|
|
133
|
+
links: { caused_by: [sourceEvent.event_id] },
|
|
134
|
+
provenance: {
|
|
135
|
+
...sourceEvent.provenance,
|
|
136
|
+
attestation: "derived",
|
|
137
|
+
confidence: "high",
|
|
138
|
+
},
|
|
139
|
+
payload: {
|
|
140
|
+
artifact: {
|
|
141
|
+
artifact_id: `art_${artifact.hash}`,
|
|
142
|
+
kind: "image",
|
|
143
|
+
media_type: MEDIA_TYPES[artifact.ext] ?? "image/png",
|
|
144
|
+
bytes: artifact.bytes,
|
|
145
|
+
retention_class: "bounded_local",
|
|
146
|
+
...(artifact.workspace_path ? { workspace_path: artifact.workspace_path } : {}),
|
|
147
|
+
},
|
|
148
|
+
operation: artifact.role === "viewed" ? "viewed" : "created",
|
|
149
|
+
},
|
|
150
|
+
});
|
|
202
151
|
}
|
|
203
152
|
|
|
204
|
-
/**
|
|
205
|
-
* Prune `.harnery/images/` past a size cap (default 2 GB) and an age cap
|
|
206
|
-
* (default 30 days), oldest-mtime-first. Fired on session.start next to
|
|
207
|
-
* journalJanitor. Pure-fs, fail-soft. Orphaned `image.captured` events whose
|
|
208
|
-
* blob was pruned render as an "expired" placeholder in the gallery.
|
|
209
|
-
*/
|
|
153
|
+
/** Prune the bounded blob store by age and total size, oldest first. */
|
|
210
154
|
export function imageJanitor(coordRoot: string): void {
|
|
211
155
|
try {
|
|
212
156
|
const dir = join(coordRoot, ".harnery", "images");
|
|
@@ -214,117 +158,163 @@ export function imageJanitor(coordRoot: string): void {
|
|
|
214
158
|
const maxBytes = envInt("HARNERY_IMAGES_MAX_BYTES", 2 * 1024 * 1024 * 1024);
|
|
215
159
|
const maxAgeMs = envInt("HARNERY_IMAGES_MAX_AGE_DAYS", 30) * 24 * 60 * 60 * 1000;
|
|
216
160
|
const now = Date.now();
|
|
161
|
+
const entries: Array<{ path: string; size: number; mtimeMs: number }> = [];
|
|
217
162
|
|
|
218
|
-
type Entry = { path: string; size: number; mtimeMs: number };
|
|
219
|
-
const entries: Entry[] = [];
|
|
220
163
|
for (const name of readdirSync(dir)) {
|
|
221
164
|
if (name.endsWith(".tmp") || name.includes(".tmp.")) {
|
|
222
|
-
// Orphaned temp from a crashed copy; sweep it.
|
|
223
165
|
rmSync(join(dir, name), { force: true });
|
|
224
166
|
continue;
|
|
225
167
|
}
|
|
226
168
|
const full = join(dir, name);
|
|
227
|
-
let st: ReturnType<typeof statSync>;
|
|
228
169
|
try {
|
|
229
|
-
|
|
170
|
+
const stat = statSync(full);
|
|
171
|
+
if (stat.isFile()) entries.push({ path: full, size: stat.size, mtimeMs: stat.mtimeMs });
|
|
230
172
|
} catch {
|
|
231
|
-
|
|
173
|
+
// A concurrent cleanup or write can make one entry disappear.
|
|
232
174
|
}
|
|
233
|
-
if (!st.isFile()) continue;
|
|
234
|
-
entries.push({ path: full, size: st.size, mtimeMs: st.mtimeMs });
|
|
235
175
|
}
|
|
236
176
|
|
|
237
|
-
// Age cap.
|
|
238
177
|
let total = 0;
|
|
239
|
-
const survivors:
|
|
240
|
-
for (const
|
|
241
|
-
if (now -
|
|
242
|
-
rmSync(
|
|
243
|
-
|
|
178
|
+
const survivors: typeof entries = [];
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
if (now - entry.mtimeMs > maxAgeMs) {
|
|
181
|
+
rmSync(entry.path, { force: true });
|
|
182
|
+
} else {
|
|
183
|
+
total += entry.size;
|
|
184
|
+
survivors.push(entry);
|
|
244
185
|
}
|
|
245
|
-
total += e.size;
|
|
246
|
-
survivors.push(e);
|
|
247
186
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (total <= maxBytes) break;
|
|
254
|
-
rmSync(e.path, { force: true });
|
|
255
|
-
total -= e.size;
|
|
256
|
-
}
|
|
187
|
+
survivors.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
188
|
+
for (const entry of survivors) {
|
|
189
|
+
if (total <= maxBytes) break;
|
|
190
|
+
rmSync(entry.path, { force: true });
|
|
191
|
+
total -= entry.size;
|
|
257
192
|
}
|
|
258
193
|
} catch {
|
|
259
|
-
// best-effort
|
|
194
|
+
// Hook effects are always best-effort.
|
|
260
195
|
}
|
|
261
196
|
}
|
|
262
197
|
|
|
263
|
-
|
|
198
|
+
function collectViewed(toolName: string, toolInput: unknown, cwd: string): Candidate[] {
|
|
199
|
+
const name = normalizedToolName(toolName);
|
|
200
|
+
if (name !== "read" && name !== "view_image" && name !== "image") return [];
|
|
201
|
+
const input = objectInput(toolInput);
|
|
202
|
+
const filePath = input?.file_path ?? input?.path ?? input?.image_path;
|
|
203
|
+
if (typeof filePath !== "string" || !hasImageExt(filePath)) return [];
|
|
204
|
+
return [{ path: toAbsolute(filePath, cwd), role: "viewed", requireRecentMtime: false }];
|
|
205
|
+
}
|
|
264
206
|
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
207
|
+
function collectProduced(
|
|
208
|
+
toolName: string,
|
|
209
|
+
payload: ParsedPayload | null,
|
|
210
|
+
cwd: string,
|
|
211
|
+
): Candidate[] {
|
|
212
|
+
const name = normalizedToolName(toolName);
|
|
213
|
+
const producerTools = new Set([
|
|
214
|
+
"bash",
|
|
215
|
+
"shell",
|
|
216
|
+
"exec_command",
|
|
217
|
+
"imagegen",
|
|
218
|
+
"image_gen",
|
|
219
|
+
"browse",
|
|
220
|
+
"browse_ai",
|
|
221
|
+
"screenshot",
|
|
222
|
+
]);
|
|
223
|
+
if (!producerTools.has(name)) return [];
|
|
224
|
+
const input = objectInput(payload?.tool_input);
|
|
225
|
+
const command = [input?.command, input?.cmd].find((value) => typeof value === "string") ?? "";
|
|
226
|
+
const response = stringify(payload?.tool_response).slice(0, 2_000_000);
|
|
227
|
+
const seen = new Set<string>();
|
|
228
|
+
const out: Candidate[] = [];
|
|
229
|
+
for (const text of [String(command), response]) {
|
|
230
|
+
for (const match of text.matchAll(IMAGE_PATH_RE)) {
|
|
231
|
+
const absolute = toAbsolute(match[0], cwd);
|
|
232
|
+
if (seen.has(absolute)) continue;
|
|
233
|
+
seen.add(absolute);
|
|
234
|
+
out.push({ path: absolute, role: "produced", requireRecentMtime: true });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return out;
|
|
270
238
|
}
|
|
271
239
|
|
|
272
|
-
function
|
|
273
|
-
|
|
240
|
+
function captureOne(
|
|
241
|
+
imagesDir: string,
|
|
242
|
+
candidate: Candidate,
|
|
243
|
+
): Omit<CapturedImageArtifact, "role" | "workspace_path"> | null {
|
|
274
244
|
try {
|
|
275
|
-
const
|
|
276
|
-
|
|
245
|
+
const stat = statSync(candidate.path);
|
|
246
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_CAPTURE_BYTES) return null;
|
|
247
|
+
if (candidate.requireRecentMtime && Date.now() - stat.mtimeMs > PRODUCED_MTIME_WINDOW_MS) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
const ext = extOf(candidate.path);
|
|
251
|
+
if (!IMAGE_EXTS.has(ext)) return null;
|
|
252
|
+
const bytes = readFileSync(candidate.path);
|
|
253
|
+
const hash = createHash("sha256").update(bytes).digest("hex");
|
|
254
|
+
const destination = join(imagesDir, `${hash}.${ext}`);
|
|
255
|
+
if (!existsSync(destination)) {
|
|
256
|
+
mkdirSync(imagesDir, { recursive: true });
|
|
257
|
+
const temporary = `${destination}.tmp.${process.pid}.${randomUUID().slice(0, 8)}`;
|
|
258
|
+
writeFileSync(temporary, bytes, { mode: 0o600 });
|
|
259
|
+
renameSync(temporary, destination);
|
|
260
|
+
}
|
|
261
|
+
return { hash, ext, bytes: stat.size };
|
|
277
262
|
} catch {
|
|
278
263
|
return null;
|
|
279
264
|
}
|
|
280
265
|
}
|
|
281
266
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const ti = payload?.raw?.tool_input as Record<string, unknown> | undefined;
|
|
285
|
-
const cmd = ti?.command;
|
|
286
|
-
return typeof cmd === "string" ? cmd : "";
|
|
267
|
+
function normalizedToolName(value: string): string {
|
|
268
|
+
return (value.split(/[.:/]/).pop() ?? value).toLowerCase().replaceAll("-", "_");
|
|
287
269
|
}
|
|
288
270
|
|
|
289
|
-
function
|
|
290
|
-
if (value
|
|
271
|
+
function objectInput(value: unknown): Record<string, unknown> | null {
|
|
272
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
273
|
+
return value as Record<string, unknown>;
|
|
274
|
+
}
|
|
275
|
+
if (typeof value !== "string") return null;
|
|
276
|
+
try {
|
|
277
|
+
const parsed = JSON.parse(value) as unknown;
|
|
278
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
279
|
+
? (parsed as Record<string, unknown>)
|
|
280
|
+
: null;
|
|
281
|
+
} catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function stringify(value: unknown): string {
|
|
291
287
|
if (typeof value === "string") return value;
|
|
292
288
|
try {
|
|
293
|
-
return JSON.stringify(value);
|
|
289
|
+
return JSON.stringify(value ?? "");
|
|
294
290
|
} catch {
|
|
295
291
|
return "";
|
|
296
292
|
}
|
|
297
293
|
}
|
|
298
294
|
|
|
299
|
-
function
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return typeof cwd === "string" && cwd ? cwd : process.cwd();
|
|
295
|
+
function toAbsolute(value: string, cwd: string): string {
|
|
296
|
+
if (value.startsWith("~/") && process.env.HOME) return resolve(process.env.HOME, value.slice(2));
|
|
297
|
+
return isAbsolute(value) ? resolve(value) : resolve(cwd, value);
|
|
303
298
|
}
|
|
304
299
|
|
|
305
|
-
function
|
|
306
|
-
const
|
|
307
|
-
|
|
300
|
+
function safeWorkspacePath(coordRoot: string, value: string): string | undefined {
|
|
301
|
+
const rel = relative(resolve(coordRoot), resolve(value));
|
|
302
|
+
if (!rel || isAbsolute(rel) || rel === ".." || rel.startsWith(`..${sep}`)) return undefined;
|
|
303
|
+
const normalized = rel.split(sep).join("/");
|
|
304
|
+
return normalized.length <= 240 && !normalized.startsWith("/") ? normalized : undefined;
|
|
308
305
|
}
|
|
309
306
|
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
const dot = clean.lastIndexOf(".");
|
|
313
|
-
if (dot < 0) return "";
|
|
314
|
-
return clean.slice(dot + 1).toLowerCase();
|
|
307
|
+
function hasImageExt(value: string): boolean {
|
|
308
|
+
return IMAGE_EXTS.has(extOf(value));
|
|
315
309
|
}
|
|
316
310
|
|
|
317
|
-
function
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (home) path = join(home, path.slice(2));
|
|
322
|
-
}
|
|
323
|
-
return isAbsolute(path) ? path : resolve(cwd, path);
|
|
311
|
+
function extOf(value: string): string {
|
|
312
|
+
const clean = value.split(/[?#]/)[0] ?? value;
|
|
313
|
+
const dot = clean.lastIndexOf(".");
|
|
314
|
+
return dot < 0 ? "" : clean.slice(dot + 1).toLowerCase();
|
|
324
315
|
}
|
|
325
316
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return p;
|
|
317
|
+
function envInt(name: string, fallback: number): number {
|
|
318
|
+
const parsed = Number(process.env[name]);
|
|
319
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
330
320
|
}
|
|
@@ -19,8 +19,7 @@ import { join } from "node:path";
|
|
|
19
19
|
import { applyDetection } from "../../../lib/presence.ts";
|
|
20
20
|
import { coordBinPath } from "../../agents/coord-bin.ts";
|
|
21
21
|
|
|
22
|
-
export
|
|
23
|
-
export { captureImages, imageJanitor } from "./image-capture.ts";
|
|
22
|
+
export { captureImages, imageJanitor, recordImageArtifactsV3 } from "./image-capture.ts";
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* Play a notification sound via the cross-platform utility
|
|
@@ -151,40 +150,6 @@ export function runSessionSyncExtension(repoRoot: string, force: boolean): void
|
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
/**
|
|
155
|
-
* Fire the turn-summary extension (Haiku auto-summary of the turn → heartbeat
|
|
156
|
-
* `turn_summary`). Detached + unref'd; it makes an Anthropic API call and must
|
|
157
|
-
* never block the Stop hook. Claude-Code-only. The
|
|
158
|
-
* script self-guards on ANTHROPIC_API_KEY / curl / jq / matching session.
|
|
159
|
-
*/
|
|
160
|
-
export function runTurnSummary(
|
|
161
|
-
repoRoot: string,
|
|
162
|
-
owner: string,
|
|
163
|
-
sessionId: string,
|
|
164
|
-
transcriptPath: string | undefined,
|
|
165
|
-
): void {
|
|
166
|
-
try {
|
|
167
|
-
if (!transcriptPath || !existsSync(transcriptPath)) return;
|
|
168
|
-
const script = join(
|
|
169
|
-
repoRoot,
|
|
170
|
-
"scripts",
|
|
171
|
-
"hooks",
|
|
172
|
-
"harness",
|
|
173
|
-
"claude-code",
|
|
174
|
-
"extensions",
|
|
175
|
-
"turn-summary.sh",
|
|
176
|
-
);
|
|
177
|
-
if (!existsSync(script)) return;
|
|
178
|
-
const child = spawn("bash", [script, owner, sessionId, transcriptPath], {
|
|
179
|
-
detached: true,
|
|
180
|
-
stdio: "ignore",
|
|
181
|
-
});
|
|
182
|
-
child.unref();
|
|
183
|
-
} catch {
|
|
184
|
-
// best-effort
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
153
|
/** Reset per-turn sound rate-limit counters at the start of a new turn. */
|
|
189
154
|
export function resetSoundCounters(sessionId: string): void {
|
|
190
155
|
try {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
/** Canonical JSON for hashing adapter input before any event-payload clamp. */
|
|
4
|
+
export function canonicalToolInput(value: unknown): string {
|
|
5
|
+
return JSON.stringify(canonicalize(value));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Exact-input digest used by the report-only run-quality evaluator. */
|
|
9
|
+
export function toolInputHash(toolName: string, input: unknown): string {
|
|
10
|
+
return createHash("sha256")
|
|
11
|
+
.update(toolName)
|
|
12
|
+
.update("\n")
|
|
13
|
+
.update(canonicalToolInput(input))
|
|
14
|
+
.digest("hex");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Hash a semantic target without persisting its path, URL, or query. */
|
|
18
|
+
export function toolTargetHash(toolName: string, input: unknown): string | undefined {
|
|
19
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return undefined;
|
|
20
|
+
const record = input as Record<string, unknown>;
|
|
21
|
+
const target =
|
|
22
|
+
record.file_path ??
|
|
23
|
+
record.path ??
|
|
24
|
+
record.target ??
|
|
25
|
+
record.url ??
|
|
26
|
+
record.notebook_path ??
|
|
27
|
+
record.pattern;
|
|
28
|
+
if (target === undefined) return undefined;
|
|
29
|
+
return createHash("sha256")
|
|
30
|
+
.update(toolName)
|
|
31
|
+
.update("\n")
|
|
32
|
+
.update(canonicalToolInput(target))
|
|
33
|
+
.digest("hex");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function canonicalize(value: unknown): unknown {
|
|
37
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
38
|
+
if (value && typeof value === "object") {
|
|
39
|
+
const record = value as Record<string, unknown>;
|
|
40
|
+
return Object.fromEntries(
|
|
41
|
+
Object.keys(record)
|
|
42
|
+
.sort()
|
|
43
|
+
.map((key) => [key, canonicalize(record[key])]),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|