harnery 0.0.1 → 0.2.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/LICENSE +21 -0
- package/README.md +84 -2
- package/bin/agent-coord +42 -0
- package/bin/agent-hook +44 -0
- package/bin/harn +40 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/commander.d.ts +128 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +126 -0
- package/dist/commands/agents.d.ts +18 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +3946 -0
- package/dist/commands/backup.d.ts +22 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +262 -0
- package/dist/commands/browse-ai.d.ts +4 -0
- package/dist/commands/browse-ai.d.ts.map +1 -0
- package/dist/commands/browse-ai.js +156 -0
- package/dist/commands/browse.d.ts +4 -0
- package/dist/commands/browse.d.ts.map +1 -0
- package/dist/commands/browse.js +590 -0
- package/dist/commands/callers.d.ts +4 -0
- package/dist/commands/callers.d.ts.map +1 -0
- package/dist/commands/callers.js +276 -0
- package/dist/commands/completion.d.ts +17 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +158 -0
- package/dist/commands/config-get.d.ts +4 -0
- package/dist/commands/config-get.d.ts.map +1 -0
- package/dist/commands/config-get.js +131 -0
- package/dist/commands/context.d.ts +11 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +185 -0
- package/dist/commands/cookies.d.ts +4 -0
- package/dist/commands/cookies.d.ts.map +1 -0
- package/dist/commands/cookies.js +140 -0
- package/dist/commands/docs.d.ts +4 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +137 -0
- package/dist/commands/doctor.d.ts +25 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +200 -0
- package/dist/commands/edit-batch.d.ts +18 -0
- package/dist/commands/edit-batch.d.ts.map +1 -0
- package/dist/commands/edit-batch.js +172 -0
- package/dist/commands/eml.d.ts +4 -0
- package/dist/commands/eml.d.ts.map +1 -0
- package/dist/commands/eml.js +428 -0
- package/dist/commands/env.d.ts +4 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +201 -0
- package/dist/commands/fetch.d.ts +4 -0
- package/dist/commands/fetch.d.ts.map +1 -0
- package/dist/commands/fetch.js +99 -0
- package/dist/commands/file-history.d.ts +4 -0
- package/dist/commands/file-history.d.ts.map +1 -0
- package/dist/commands/file-history.js +152 -0
- package/dist/commands/grep.d.ts +4 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +317 -0
- package/dist/commands/init.d.ts +82 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +288 -0
- package/dist/commands/outline.d.ts +4 -0
- package/dist/commands/outline.d.ts.map +1 -0
- package/dist/commands/outline.js +494 -0
- package/dist/commands/presence.d.ts +12 -0
- package/dist/commands/presence.d.ts.map +1 -0
- package/dist/commands/presence.js +123 -0
- package/dist/commands/read.d.ts +7 -0
- package/dist/commands/read.d.ts.map +1 -0
- package/dist/commands/read.js +46 -0
- package/dist/commands/scratch.d.ts +4 -0
- package/dist/commands/scratch.d.ts.map +1 -0
- package/dist/commands/scratch.js +426 -0
- package/dist/commands/session.d.ts +4 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +162 -0
- package/dist/commands/sync.d.ts +24 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +275 -0
- package/dist/commands/toc.d.ts +5 -0
- package/dist/commands/toc.d.ts.map +1 -0
- package/dist/commands/toc.js +153 -0
- package/dist/commands/tokens.d.ts +4 -0
- package/dist/commands/tokens.d.ts.map +1 -0
- package/dist/commands/tokens.js +48 -0
- package/dist/commands/tunnel.d.ts +4 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +513 -0
- package/dist/commands/uninstall.d.ts +22 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +126 -0
- package/dist/commands/web.d.ts +4 -0
- package/dist/commands/web.d.ts.map +1 -0
- package/dist/commands/web.js +165 -0
- package/dist/core/agents/canonical-emit.d.ts +27 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -0
- package/dist/core/agents/canonical-emit.js +72 -0
- package/dist/core/agents/cli-emit.d.ts +27 -0
- package/dist/core/agents/cli-emit.d.ts.map +1 -0
- package/dist/core/agents/cli-emit.js +57 -0
- package/dist/core/agents/cli.d.ts +10 -0
- package/dist/core/agents/cli.d.ts.map +1 -0
- package/dist/core/agents/cli.js +757 -0
- package/dist/core/agents/codex-replay.d.ts +29 -0
- package/dist/core/agents/codex-replay.d.ts.map +1 -0
- package/dist/core/agents/codex-replay.js +138 -0
- package/dist/core/agents/coord-client.d.ts +98 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -0
- package/dist/core/agents/coord-client.js +212 -0
- package/dist/core/agents/events/consume.d.ts +59 -0
- package/dist/core/agents/events/consume.d.ts.map +1 -0
- package/dist/core/agents/events/consume.js +147 -0
- package/dist/core/agents/events/emit.d.ts +42 -0
- package/dist/core/agents/events/emit.d.ts.map +1 -0
- package/dist/core/agents/events/emit.js +70 -0
- package/dist/core/agents/events/ulid.d.ts +11 -0
- package/dist/core/agents/events/ulid.d.ts.map +1 -0
- package/dist/core/agents/events/ulid.js +47 -0
- package/dist/core/agents/index.d.ts +14 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +13 -0
- package/dist/core/agents/paths.d.ts +6 -0
- package/dist/core/agents/paths.d.ts.map +1 -0
- package/dist/core/agents/paths.js +17 -0
- package/dist/core/agents/render/prompt-context.d.ts +43 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
- package/dist/core/agents/render/prompt-context.js +335 -0
- package/dist/core/agents/render/session-context.d.ts +39 -0
- package/dist/core/agents/render/session-context.d.ts.map +1 -0
- package/dist/core/agents/render/session-context.js +283 -0
- package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
- package/dist/core/agents/rules/claim-conflict.js +244 -0
- package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
- package/dist/core/agents/rules/commit-conflict.js +244 -0
- package/dist/core/agents/rules/stop-hook.d.ts +44 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
- package/dist/core/agents/rules/stop-hook.js +161 -0
- package/dist/core/agents/session-events.d.ts +41 -0
- package/dist/core/agents/session-events.d.ts.map +1 -0
- package/dist/core/agents/session-events.js +205 -0
- package/dist/core/agents/state/activity-log.d.ts +18 -0
- package/dist/core/agents/state/activity-log.d.ts.map +1 -0
- package/dist/core/agents/state/activity-log.js +34 -0
- package/dist/core/agents/state/council.d.ts +39 -0
- package/dist/core/agents/state/council.d.ts.map +1 -0
- package/dist/core/agents/state/council.js +216 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-projector.js +436 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-writer.js +271 -0
- package/dist/core/agents/state/names.d.ts +35 -0
- package/dist/core/agents/state/names.d.ts.map +1 -0
- package/dist/core/agents/state/names.js +376 -0
- package/dist/core/agents/state/pidmap.d.ts +11 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -0
- package/dist/core/agents/state/pidmap.js +32 -0
- package/dist/core/agents/state/scratch.d.ts +27 -0
- package/dist/core/agents/state/scratch.d.ts.map +1 -0
- package/dist/core/agents/state/scratch.js +90 -0
- package/dist/core/agents/state/shell-mutation.d.ts +17 -0
- package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
- package/dist/core/agents/state/shell-mutation.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts +16 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
- package/dist/core/agents/state/stale-sweep.js +166 -0
- package/dist/core/config.d.ts +29 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +108 -0
- package/dist/core/hooks/cli.d.ts +21 -0
- package/dist/core/hooks/cli.d.ts.map +1 -0
- package/dist/core/hooks/cli.js +1123 -0
- package/dist/core/hooks/effects/image-capture.d.ts +43 -0
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
- package/dist/core/hooks/effects/image-capture.js +288 -0
- package/dist/core/hooks/effects/index.d.ts +64 -0
- package/dist/core/hooks/effects/index.d.ts.map +1 -0
- package/dist/core/hooks/effects/index.js +197 -0
- package/dist/core/hooks/events/emit.d.ts +31 -0
- package/dist/core/hooks/events/emit.d.ts.map +1 -0
- package/dist/core/hooks/events/emit.js +89 -0
- package/dist/core/hooks/events/schema.d.ts +235 -0
- package/dist/core/hooks/events/schema.d.ts.map +1 -0
- package/dist/core/hooks/events/schema.js +12 -0
- package/dist/core/hooks/events/ulid.d.ts +10 -0
- package/dist/core/hooks/events/ulid.d.ts.map +1 -0
- package/dist/core/hooks/events/ulid.js +47 -0
- package/dist/core/hooks/harness/detect.d.ts +9 -0
- package/dist/core/hooks/harness/detect.d.ts.map +1 -0
- package/dist/core/hooks/harness/detect.js +29 -0
- package/dist/core/hooks/harness/events.d.ts +45 -0
- package/dist/core/hooks/harness/events.d.ts.map +1 -0
- package/dist/core/hooks/harness/events.js +71 -0
- package/dist/core/hooks/harness/output.d.ts +46 -0
- package/dist/core/hooks/harness/output.d.ts.map +1 -0
- package/dist/core/hooks/harness/output.js +87 -0
- package/dist/core/hooks/harness/parse.d.ts +67 -0
- package/dist/core/hooks/harness/parse.d.ts.map +1 -0
- package/dist/core/hooks/harness/parse.js +132 -0
- package/dist/core/hooks/index.d.ts +8 -0
- package/dist/core/hooks/index.d.ts.map +1 -0
- package/dist/core/hooks/index.js +7 -0
- package/dist/core/hooks/resolve/anchor.d.ts +37 -0
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
- package/dist/core/hooks/resolve/anchor.js +48 -0
- package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
- package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
- package/dist/core/hooks/resolve/coord-root.js +27 -0
- package/dist/core/hooks/resolve/intent.d.ts +33 -0
- package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
- package/dist/core/hooks/resolve/intent.js +79 -0
- package/dist/core/hooks/resolve/owner.d.ts +42 -0
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
- package/dist/core/hooks/resolve/owner.js +140 -0
- package/dist/core/hooks/resolve/transcript.d.ts +26 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
- package/dist/core/hooks/resolve/transcript.js +73 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/lib/agent-browser/client.d.ts +99 -0
- package/dist/lib/agent-browser/client.d.ts.map +1 -0
- package/dist/lib/agent-browser/client.js +177 -0
- package/dist/lib/agent-browser/index.d.ts +2 -0
- package/dist/lib/agent-browser/index.d.ts.map +1 -0
- package/dist/lib/agent-browser/index.js +1 -0
- package/dist/lib/browser/client.d.ts +193 -0
- package/dist/lib/browser/client.d.ts.map +1 -0
- package/dist/lib/browser/client.js +325 -0
- package/dist/lib/browser/dev-overlay.d.ts +23 -0
- package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
- package/dist/lib/browser/dev-overlay.js +153 -0
- package/dist/lib/browser/index.d.ts +5 -0
- package/dist/lib/browser/index.d.ts.map +1 -0
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/browser/layout.d.ts +79 -0
- package/dist/lib/browser/layout.d.ts.map +1 -0
- package/dist/lib/browser/layout.js +220 -0
- package/dist/lib/browser/visibility.d.ts +86 -0
- package/dist/lib/browser/visibility.d.ts.map +1 -0
- package/dist/lib/browser/visibility.js +333 -0
- package/dist/lib/browser/visual-diff.d.ts +38 -0
- package/dist/lib/browser/visual-diff.d.ts.map +1 -0
- package/dist/lib/browser/visual-diff.js +107 -0
- package/dist/lib/completion/bash.d.ts +25 -0
- package/dist/lib/completion/bash.d.ts.map +1 -0
- package/dist/lib/completion/bash.js +284 -0
- package/dist/lib/completion/fish.d.ts +16 -0
- package/dist/lib/completion/fish.d.ts.map +1 -0
- package/dist/lib/completion/fish.js +118 -0
- package/dist/lib/completion/index.d.ts +5 -0
- package/dist/lib/completion/index.d.ts.map +1 -0
- package/dist/lib/completion/index.js +4 -0
- package/dist/lib/completion/walk.d.ts +68 -0
- package/dist/lib/completion/walk.d.ts.map +1 -0
- package/dist/lib/completion/walk.js +102 -0
- package/dist/lib/completion/zsh.d.ts +13 -0
- package/dist/lib/completion/zsh.d.ts.map +1 -0
- package/dist/lib/completion/zsh.js +249 -0
- package/dist/lib/context/index.d.ts +107 -0
- package/dist/lib/context/index.d.ts.map +1 -0
- package/dist/lib/context/index.js +275 -0
- package/dist/lib/cookies/client.d.ts +131 -0
- package/dist/lib/cookies/client.d.ts.map +1 -0
- package/dist/lib/cookies/client.js +239 -0
- package/dist/lib/cookies/index.d.ts +2 -0
- package/dist/lib/cookies/index.d.ts.map +1 -0
- package/dist/lib/cookies/index.js +1 -0
- package/dist/lib/council/index.d.ts +266 -0
- package/dist/lib/council/index.d.ts.map +1 -0
- package/dist/lib/council/index.js +674 -0
- package/dist/lib/docs-index.d.ts +28 -0
- package/dist/lib/docs-index.d.ts.map +1 -0
- package/dist/lib/docs-index.js +169 -0
- package/dist/lib/docs-lint.d.ts +26 -0
- package/dist/lib/docs-lint.d.ts.map +1 -0
- package/dist/lib/docs-lint.js +378 -0
- package/dist/lib/docs-sweep.d.ts +34 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -0
- package/dist/lib/docs-sweep.js +304 -0
- package/dist/lib/docs.d.ts +27 -0
- package/dist/lib/docs.d.ts.map +1 -0
- package/dist/lib/docs.js +142 -0
- package/dist/lib/env.d.ts +11 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +12 -0
- package/dist/lib/exec.d.ts +32 -0
- package/dist/lib/exec.d.ts.map +1 -0
- package/dist/lib/exec.js +54 -0
- package/dist/lib/format.d.ts +29 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +139 -0
- package/dist/lib/http/client.d.ts +56 -0
- package/dist/lib/http/client.d.ts.map +1 -0
- package/dist/lib/http/client.js +160 -0
- package/dist/lib/http/index.d.ts +2 -0
- package/dist/lib/http/index.d.ts.map +1 -0
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/identities/index.d.ts +77 -0
- package/dist/lib/identities/index.d.ts.map +1 -0
- package/dist/lib/identities/index.js +190 -0
- package/dist/lib/machine.d.ts +19 -0
- package/dist/lib/machine.d.ts.map +1 -0
- package/dist/lib/machine.js +61 -0
- package/dist/lib/presence.d.ts +48 -0
- package/dist/lib/presence.d.ts.map +1 -0
- package/dist/lib/presence.js +123 -0
- package/dist/lib/readability/client.d.ts +32 -0
- package/dist/lib/readability/client.d.ts.map +1 -0
- package/dist/lib/readability/client.js +119 -0
- package/dist/lib/readability/index.d.ts +2 -0
- package/dist/lib/readability/index.d.ts.map +1 -0
- package/dist/lib/readability/index.js +1 -0
- package/dist/lib/scratch/index.d.ts +74 -0
- package/dist/lib/scratch/index.d.ts.map +1 -0
- package/dist/lib/scratch/index.js +393 -0
- package/dist/lib/tunnel/gate.d.ts +12 -0
- package/dist/lib/tunnel/gate.d.ts.map +1 -0
- package/dist/lib/tunnel/gate.js +101 -0
- package/dist/lib/tunnel/state.d.ts +34 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -0
- package/dist/lib/tunnel/state.js +132 -0
- package/package.json +160 -8
- package/schemas/.gitkeep +0 -0
- package/schemas/config.schema.json +109 -0
- package/src/cli.ts +22 -0
- package/src/commander.ts +242 -0
- package/src/commands/.gitkeep +0 -0
- package/src/commands/agents.ts +4567 -0
- package/src/commands/backup.ts +305 -0
- package/src/commands/browse-ai.ts +198 -0
- package/src/commands/browse.ts +849 -0
- package/src/commands/callers.ts +363 -0
- package/src/commands/completion.ts +193 -0
- package/src/commands/config-get.ts +161 -0
- package/src/commands/context.ts +209 -0
- package/src/commands/cookies.ts +198 -0
- package/src/commands/docs.ts +174 -0
- package/src/commands/doctor.ts +231 -0
- package/src/commands/edit-batch.ts +233 -0
- package/src/commands/eml.ts +519 -0
- package/src/commands/env.ts +254 -0
- package/src/commands/fetch.ts +136 -0
- package/src/commands/file-history.ts +202 -0
- package/src/commands/grep.ts +371 -0
- package/src/commands/init.ts +335 -0
- package/src/commands/outline.ts +564 -0
- package/src/commands/presence.ts +152 -0
- package/src/commands/read.ts +64 -0
- package/src/commands/scratch.ts +445 -0
- package/src/commands/session.ts +187 -0
- package/src/commands/sync.ts +306 -0
- package/src/commands/toc.ts +218 -0
- package/src/commands/tokens.ts +79 -0
- package/src/commands/tunnel.ts +633 -0
- package/src/commands/uninstall.ts +144 -0
- package/src/commands/web.ts +193 -0
- package/src/core/agents/canonical-emit.ts +77 -0
- package/src/core/agents/cli-emit.ts +64 -0
- package/src/core/agents/cli.ts +838 -0
- package/src/core/agents/codex-replay.ts +163 -0
- package/src/core/agents/coord-client.ts +249 -0
- package/src/core/agents/events/consume.ts +196 -0
- package/src/core/agents/events/emit.ts +108 -0
- package/src/core/agents/events/ulid.ts +51 -0
- package/src/core/agents/index.ts +14 -0
- package/src/core/agents/paths.ts +16 -0
- package/src/core/agents/render/prompt-context.ts +401 -0
- package/src/core/agents/render/session-context.ts +341 -0
- package/src/core/agents/rules/claim-conflict.ts +282 -0
- package/src/core/agents/rules/commit-conflict.ts +303 -0
- package/src/core/agents/rules/stop-hook.ts +229 -0
- package/src/core/agents/session-events.ts +228 -0
- package/src/core/agents/state/activity-log.ts +33 -0
- package/src/core/agents/state/council.ts +265 -0
- package/src/core/agents/state/heartbeat-projector.ts +488 -0
- package/src/core/agents/state/heartbeat-writer.ts +333 -0
- package/src/core/agents/state/names.ts +399 -0
- package/src/core/agents/state/pidmap.ts +38 -0
- package/src/core/agents/state/scratch.ts +121 -0
- package/src/core/agents/state/shell-mutation.ts +44 -0
- package/src/core/agents/state/stale-sweep.ts +190 -0
- package/src/core/config.ts +111 -0
- package/src/core/hooks/cli.ts +1247 -0
- package/src/core/hooks/effects/image-capture.ts +330 -0
- package/src/core/hooks/effects/index.ts +210 -0
- package/src/core/hooks/events/emit.ts +120 -0
- package/src/core/hooks/events/schema.ts +430 -0
- package/src/core/hooks/events/ulid.ts +51 -0
- package/src/core/hooks/harness/detect.ts +30 -0
- package/src/core/hooks/harness/events.ts +102 -0
- package/src/core/hooks/harness/output.ts +100 -0
- package/src/core/hooks/harness/parse.ts +180 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/resolve/anchor.ts +51 -0
- package/src/core/hooks/resolve/coord-root.ts +25 -0
- package/src/core/hooks/resolve/intent.ts +89 -0
- package/src/core/hooks/resolve/owner.ts +140 -0
- package/src/core/hooks/resolve/transcript.ts +72 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/index.ts +15 -0
- package/src/lib/agent-browser/client.ts +239 -0
- package/src/lib/agent-browser/index.ts +1 -0
- package/src/lib/browser/client.ts +449 -0
- package/src/lib/browser/dev-overlay.ts +207 -0
- package/src/lib/browser/index.ts +24 -0
- package/src/lib/browser/layout.ts +288 -0
- package/src/lib/browser/visibility.ts +419 -0
- package/src/lib/browser/visual-diff.ts +150 -0
- package/src/lib/completion/bash.ts +291 -0
- package/src/lib/completion/fish.ts +134 -0
- package/src/lib/completion/index.ts +10 -0
- package/src/lib/completion/walk.ts +184 -0
- package/src/lib/completion/zsh.ts +262 -0
- package/src/lib/context/index.ts +386 -0
- package/src/lib/cookies/client.ts +301 -0
- package/src/lib/cookies/index.ts +13 -0
- package/src/lib/council/index.ts +803 -0
- package/src/lib/docs-index.ts +216 -0
- package/src/lib/docs-lint.ts +413 -0
- package/src/lib/docs-sweep.ts +348 -0
- package/src/lib/docs.ts +199 -0
- package/src/lib/env.ts +12 -0
- package/src/lib/exec.ts +74 -0
- package/src/lib/format.ts +147 -0
- package/src/lib/http/client.ts +211 -0
- package/src/lib/http/index.ts +1 -0
- package/src/lib/identities/index.ts +210 -0
- package/src/lib/machine.ts +61 -0
- package/src/lib/presence.ts +154 -0
- package/src/lib/readability/client.ts +156 -0
- package/src/lib/readability/index.ts +5 -0
- package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
- package/src/lib/scratch/index.ts +470 -0
- package/src/lib/tunnel/gate.ts +113 -0
- package/src/lib/tunnel/state.ts +167 -0
- package/src/web/.gitkeep +0 -0
- package/index.js +0 -1
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agent-coord` CLI entry point. Phase 1: every subcommand no-ops with a
|
|
3
|
+
* structured debug log. The one exception is `verdict`, which replies
|
|
4
|
+
* fail-open so adapters can already wire it up without affecting flow.
|
|
5
|
+
*
|
|
6
|
+
* Phase 2 replaces the no-op branches with real state projection + CLI
|
|
7
|
+
* handlers; Phase 4 flips the default so `harn agents …` shims through here.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { appendFileSync, existsSync } from "node:fs";
|
|
11
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { coordEnv } from "../../lib/env.ts";
|
|
14
|
+
|
|
15
|
+
function findCoordRoot(start: string): string | null {
|
|
16
|
+
// HARNERY_COORD_ROOT_OVERRIDE: the bash side's test-mode escape hatch. agent-coord
|
|
17
|
+
// honors the same env so sandboxed test runs don't get derailed when cwd
|
|
18
|
+
// doesn't contain a .harnery/ tree. Phase 8: dropped the `.harnery/` existence
|
|
19
|
+
// precondition so test fixtures that haven't bootstrapped the dir yet still
|
|
20
|
+
// resolve.
|
|
21
|
+
const override = coordEnv("COORD_ROOT_OVERRIDE");
|
|
22
|
+
if (override) return override;
|
|
23
|
+
let dir = resolve(start);
|
|
24
|
+
while (true) {
|
|
25
|
+
if (existsSync(join(dir, ".harnery"))) return dir;
|
|
26
|
+
const parent = dirname(dir);
|
|
27
|
+
if (parent === dir) return null;
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function readStdin(): Promise<string> {
|
|
33
|
+
if (process.stdin.isTTY) return "";
|
|
34
|
+
const chunks: Uint8Array[] = [];
|
|
35
|
+
for await (const chunk of process.stdin) {
|
|
36
|
+
chunks.push(chunk as Uint8Array);
|
|
37
|
+
}
|
|
38
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function logNoop(root: string, subcommand: string, argv: string[]): Promise<void> {
|
|
42
|
+
const logPath = join(root, ".harnery", "debug", "agent-coord.ndjson");
|
|
43
|
+
await mkdir(dirname(logPath), { recursive: true });
|
|
44
|
+
const entry = {
|
|
45
|
+
ts: new Date().toISOString(),
|
|
46
|
+
note: "called, no-op",
|
|
47
|
+
subcommand,
|
|
48
|
+
extra_argv: argv,
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
pid: process.pid,
|
|
51
|
+
ppid: process.ppid,
|
|
52
|
+
};
|
|
53
|
+
await appendFile(logPath, `${JSON.stringify(entry)}\n`, "utf8");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Verdict endpoint. Reads a JSON request from stdin, dispatches to the
|
|
58
|
+
* matching rule evaluator, writes a JSON verdict to stdout. Exit code is
|
|
59
|
+
* 0 regardless; the caller branches on the JSON's `exit_code` field so
|
|
60
|
+
* fail-open semantics survive a malformed-request bug here.
|
|
61
|
+
*
|
|
62
|
+
* The Stop and PreToolUse hooks route here.
|
|
63
|
+
*/
|
|
64
|
+
async function handleVerdict(root: string): Promise<number> {
|
|
65
|
+
const raw = await readStdin();
|
|
66
|
+
const logPath = join(root, ".harnery", "debug", "agent-coord-verdict.ndjson");
|
|
67
|
+
await mkdir(dirname(logPath), { recursive: true });
|
|
68
|
+
|
|
69
|
+
let parsed: { rule?: string } & Record<string, unknown> = {};
|
|
70
|
+
let parseErr: string | null = null;
|
|
71
|
+
try {
|
|
72
|
+
if (raw.trim().length > 0) {
|
|
73
|
+
parsed = JSON.parse(raw) as { rule?: string } & Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
parseErr = err instanceof Error ? err.message : String(err);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let verdict: {
|
|
80
|
+
allow: boolean;
|
|
81
|
+
exit_code: number;
|
|
82
|
+
rule: string;
|
|
83
|
+
reason?: string;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (parseErr) {
|
|
87
|
+
verdict = {
|
|
88
|
+
allow: true,
|
|
89
|
+
exit_code: 0,
|
|
90
|
+
rule: "verdict.bad_request",
|
|
91
|
+
reason: `invalid JSON: ${parseErr} (fail-open)`,
|
|
92
|
+
};
|
|
93
|
+
} else if (parsed.rule === "stop-hook") {
|
|
94
|
+
const { evaluateStopHook } = await import("./rules/stop-hook.ts");
|
|
95
|
+
verdict = evaluateStopHook(root, parsed as unknown as Parameters<typeof evaluateStopHook>[1]);
|
|
96
|
+
} else if (parsed.rule === "claim") {
|
|
97
|
+
const { evaluateClaim } = await import("./rules/claim-conflict.ts");
|
|
98
|
+
verdict = evaluateClaim(root, parsed as unknown as Parameters<typeof evaluateClaim>[1]);
|
|
99
|
+
} else if (parsed.rule === "commit") {
|
|
100
|
+
const { evaluateCommit } = await import("./rules/commit-conflict.ts");
|
|
101
|
+
const result = evaluateCommit(root, parsed as unknown as Parameters<typeof evaluateCommit>[1]);
|
|
102
|
+
// Map CommitVerdictResult → the standard verdict envelope, stash details
|
|
103
|
+
// on extra fields so the bash caller can pull conflicts + log_lines.
|
|
104
|
+
verdict = {
|
|
105
|
+
allow: result.allow,
|
|
106
|
+
exit_code: result.exit_code,
|
|
107
|
+
rule: result.rule,
|
|
108
|
+
reason: result.message,
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
110
|
+
} as typeof verdict;
|
|
111
|
+
(verdict as Record<string, unknown>).conflicts = result.conflicts;
|
|
112
|
+
(verdict as Record<string, unknown>).log_lines = result.log_lines;
|
|
113
|
+
(verdict as Record<string, unknown>).suppressed_self_attribution =
|
|
114
|
+
result.suppressed_self_attribution ?? false;
|
|
115
|
+
} else {
|
|
116
|
+
verdict = {
|
|
117
|
+
allow: true,
|
|
118
|
+
exit_code: 0,
|
|
119
|
+
rule: "verdict.unknown_rule",
|
|
120
|
+
reason: `no evaluator for rule=${parsed.rule ?? "<missing>"} (fail-open)`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await appendFile(
|
|
125
|
+
logPath,
|
|
126
|
+
`${JSON.stringify({
|
|
127
|
+
ts: new Date().toISOString(),
|
|
128
|
+
request_preview: raw.slice(0, 500),
|
|
129
|
+
verdict,
|
|
130
|
+
})}\n`,
|
|
131
|
+
"utf8",
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
process.stdout.write(`${JSON.stringify(verdict)}\n`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function handleProject(root: string, rest: string[]): Promise<number> {
|
|
139
|
+
const { consumeSince, writeCursor } = await import("./events/consume.ts");
|
|
140
|
+
const { projectHeartbeats } = await import("./state/heartbeat-projector.ts");
|
|
141
|
+
const replayAll = rest.includes("--replay-all");
|
|
142
|
+
const result = consumeSince(root, { replayAll });
|
|
143
|
+
const project = projectHeartbeats(root, result.events);
|
|
144
|
+
const report = {
|
|
145
|
+
events_consumed: result.events.length,
|
|
146
|
+
stream_bytes: result.streamBytes,
|
|
147
|
+
owners_projected: project.written.length,
|
|
148
|
+
owners: project.written,
|
|
149
|
+
cursor: result.lastEventId,
|
|
150
|
+
replayed_all: replayAll,
|
|
151
|
+
};
|
|
152
|
+
if (result.lastEventId) writeCursor(root, result.lastEventId);
|
|
153
|
+
if (rest.includes("--json")) {
|
|
154
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
155
|
+
} else {
|
|
156
|
+
process.stdout.write(
|
|
157
|
+
`projected ${report.events_consumed} events across ${report.owners_projected} owners\n cursor → ${report.cursor ?? "<none>"}\n${
|
|
158
|
+
report.owners.length
|
|
159
|
+
? ` owners: ${report.owners.map((o) => o.slice(0, 8)).join(", ")}\n`
|
|
160
|
+
: ""
|
|
161
|
+
}`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function handleStateAction(root: string, action: string, rest: string[]): Promise<number> {
|
|
168
|
+
const writer = await import("./state/heartbeat-writer.ts");
|
|
169
|
+
const [owner, ...args] = rest;
|
|
170
|
+
if (!owner) {
|
|
171
|
+
process.stderr.write(`agent-coord ${action}: missing <instance_id>\n`);
|
|
172
|
+
return 2;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
switch (action) {
|
|
176
|
+
case "set-task": {
|
|
177
|
+
const task = args.join(" ");
|
|
178
|
+
const hb = writer.setTask(root, owner, task);
|
|
179
|
+
if (!hb) {
|
|
180
|
+
process.stderr.write(
|
|
181
|
+
`agent-coord set-task: no heartbeat at .harnery/active/${owner}.json\n`,
|
|
182
|
+
);
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
process.stdout.write(
|
|
186
|
+
`${JSON.stringify({ instance_id: owner, task: hb.task ?? null, cleared: !task })}\n`,
|
|
187
|
+
);
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
case "stamp-status-call": {
|
|
191
|
+
const hb = writer.stampStatusCheck(root, owner);
|
|
192
|
+
if (!hb) return 1;
|
|
193
|
+
process.stdout.write(
|
|
194
|
+
`${JSON.stringify({ instance_id: owner, last_status_at: hb.last_status_at })}\n`,
|
|
195
|
+
);
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
case "set-turn-summary": {
|
|
199
|
+
const summary = args.join(" ");
|
|
200
|
+
const hb = writer.setTurnSummary(root, owner, summary);
|
|
201
|
+
if (!hb) return 1;
|
|
202
|
+
process.stdout.write(
|
|
203
|
+
`${JSON.stringify({ instance_id: owner, turn_summary: hb.turn_summary })}\n`,
|
|
204
|
+
);
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
case "release-claim": {
|
|
208
|
+
const path = args[0];
|
|
209
|
+
if (!path) {
|
|
210
|
+
process.stderr.write("agent-coord release-claim: missing <path>\n");
|
|
211
|
+
return 2;
|
|
212
|
+
}
|
|
213
|
+
const hb = writer.releaseClaim(root, owner, path);
|
|
214
|
+
if (!hb) return 1;
|
|
215
|
+
process.stdout.write(
|
|
216
|
+
`${JSON.stringify({ instance_id: owner, files_touched: hb.files_touched })}\n`,
|
|
217
|
+
);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
case "kill-heartbeat": {
|
|
221
|
+
const ok = writer.killHeartbeat(root, owner);
|
|
222
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, removed: ok })}\n`);
|
|
223
|
+
return ok ? 0 : 1;
|
|
224
|
+
}
|
|
225
|
+
case "heal-pidmap": {
|
|
226
|
+
const pidArg = args[0];
|
|
227
|
+
const pid = pidArg ? Number(pidArg) : process.ppid;
|
|
228
|
+
if (!Number.isFinite(pid)) {
|
|
229
|
+
process.stderr.write(`agent-coord heal-pidmap: invalid pid ${pidArg}\n`);
|
|
230
|
+
return 2;
|
|
231
|
+
}
|
|
232
|
+
writer.healPidmap(root, owner, pid);
|
|
233
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, pid })}\n`);
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
236
|
+
case "heal-heartbeat": {
|
|
237
|
+
// harness arrives as a `--harness=<h>` flag (not positional) so the live
|
|
238
|
+
// tool.pre_use heal and the manual `harn agents heal` path (which pass
|
|
239
|
+
// different positional counts) can both supply it without arg-order
|
|
240
|
+
// fragility. Positionals (sessionId, model) stay as-is once flags are
|
|
241
|
+
// filtered out.
|
|
242
|
+
const harness = args.find((a) => a.startsWith("--harness="))?.slice("--harness=".length);
|
|
243
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
244
|
+
const sessionId = positional[0];
|
|
245
|
+
const model = positional[1];
|
|
246
|
+
const hb = writer.healHeartbeat(root, owner, sessionId, model, harness);
|
|
247
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, recreated: !!hb })}\n`);
|
|
248
|
+
return hb ? 0 : 1;
|
|
249
|
+
}
|
|
250
|
+
case "stamp-tool-activity": {
|
|
251
|
+
const toolName = args[0] ?? "";
|
|
252
|
+
const target = args.slice(1).join(" ");
|
|
253
|
+
const hb = writer.stampToolActivity(root, owner, toolName, target);
|
|
254
|
+
if (!hb) return 1;
|
|
255
|
+
process.stdout.write(
|
|
256
|
+
`${JSON.stringify({
|
|
257
|
+
instance_id: owner,
|
|
258
|
+
last_tool: hb.last_tool,
|
|
259
|
+
last_tool_target: hb.last_tool_target,
|
|
260
|
+
})}\n`,
|
|
261
|
+
);
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
default:
|
|
265
|
+
process.stderr.write(`agent-coord: unknown state action ${action}\n`);
|
|
266
|
+
return 2;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function handleScratchAction(root: string, action: string, rest: string[]): Promise<number> {
|
|
271
|
+
const scratch = await import("./state/scratch.ts");
|
|
272
|
+
|
|
273
|
+
if (action === "append-scratch") {
|
|
274
|
+
const [owner, category, ...bodyParts] = rest;
|
|
275
|
+
const body = bodyParts.join(" ");
|
|
276
|
+
if (!owner || !category || !body) {
|
|
277
|
+
process.stderr.write("agent-coord append-scratch <instance_id> <category> <body>\n");
|
|
278
|
+
return 2;
|
|
279
|
+
}
|
|
280
|
+
const result = scratch.appendScratch(root, owner, category, body);
|
|
281
|
+
if (!result.ok) {
|
|
282
|
+
process.stderr.write(`agent-coord append-scratch: ${result.reason}\n`);
|
|
283
|
+
return 1;
|
|
284
|
+
}
|
|
285
|
+
process.stdout.write(
|
|
286
|
+
`${JSON.stringify({ instance_id: owner, category, path: result.path })}\n`,
|
|
287
|
+
);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (action === "edit-scratchpad") {
|
|
292
|
+
const [owner, newBodyFile, ...summaryParts] = rest;
|
|
293
|
+
const summary = summaryParts.join(" ");
|
|
294
|
+
if (!owner || !newBodyFile) {
|
|
295
|
+
process.stderr.write(
|
|
296
|
+
"agent-coord edit-scratchpad <instance_id> <new-body-file> [<summary>]\n",
|
|
297
|
+
);
|
|
298
|
+
return 2;
|
|
299
|
+
}
|
|
300
|
+
if (!existsSync(newBodyFile)) {
|
|
301
|
+
process.stderr.write(`agent-coord edit-scratchpad: file not found: ${newBodyFile}\n`);
|
|
302
|
+
return 2;
|
|
303
|
+
}
|
|
304
|
+
const { readFileSync } = await import("node:fs");
|
|
305
|
+
const newBody = readFileSync(newBodyFile, "utf8");
|
|
306
|
+
const result = scratch.editScratchpad(root, owner, newBody, summary);
|
|
307
|
+
if (!result.ok) {
|
|
308
|
+
process.stderr.write(`agent-coord edit-scratchpad: ${result.reason}\n`);
|
|
309
|
+
return 1;
|
|
310
|
+
}
|
|
311
|
+
process.stdout.write(
|
|
312
|
+
`${JSON.stringify({ instance_id: owner, path: result.path, archive_path: result.archivePath })}\n`,
|
|
313
|
+
);
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
process.stderr.write(`agent-coord: unknown scratch action ${action}\n`);
|
|
318
|
+
return 2;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function handleCouncilAction(root: string, action: string, rest: string[]): Promise<number> {
|
|
322
|
+
const council = await import("./state/council.ts");
|
|
323
|
+
|
|
324
|
+
const [councilId, ...args] = rest;
|
|
325
|
+
if (!councilId) {
|
|
326
|
+
process.stderr.write(`agent-coord ${action}: missing <council_id>\n`);
|
|
327
|
+
return 2;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
switch (action) {
|
|
331
|
+
case "council-advance": {
|
|
332
|
+
const force = args.includes("--force");
|
|
333
|
+
const result = council.advanceCouncil(root, councilId, { force });
|
|
334
|
+
if (!result.ok) {
|
|
335
|
+
process.stderr.write(`agent-coord council-advance: ${result.reason}\n`);
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
|
|
339
|
+
return 0;
|
|
340
|
+
}
|
|
341
|
+
case "council-close": {
|
|
342
|
+
const result = council.closeCouncil(root, councilId);
|
|
343
|
+
if (!result.ok) {
|
|
344
|
+
process.stderr.write(`agent-coord council-close: ${result.reason}\n`);
|
|
345
|
+
return 1;
|
|
346
|
+
}
|
|
347
|
+
process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
|
+
case "council-archive": {
|
|
351
|
+
const result = council.archiveCouncil(root, councilId);
|
|
352
|
+
if (!result.ok) {
|
|
353
|
+
process.stderr.write(`agent-coord council-archive: ${result.reason}\n`);
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
|
|
357
|
+
return 0;
|
|
358
|
+
}
|
|
359
|
+
case "council-unarchive": {
|
|
360
|
+
const result = council.unarchiveCouncil(root, councilId);
|
|
361
|
+
if (!result.ok) {
|
|
362
|
+
process.stderr.write(`agent-coord council-unarchive: ${result.reason}\n`);
|
|
363
|
+
return 1;
|
|
364
|
+
}
|
|
365
|
+
process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
case "council-delete": {
|
|
369
|
+
const result = council.deleteCouncil(root, councilId);
|
|
370
|
+
if (!result.ok) {
|
|
371
|
+
process.stderr.write(`agent-coord council-delete: ${result.reason}\n`);
|
|
372
|
+
return 1;
|
|
373
|
+
}
|
|
374
|
+
process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
case "council-set-steward": {
|
|
378
|
+
const steward = args[0] ?? "";
|
|
379
|
+
const stewardId = args[1] ?? "";
|
|
380
|
+
const result = council.setCouncilSteward(root, councilId, steward, stewardId);
|
|
381
|
+
if (!result.ok) {
|
|
382
|
+
process.stderr.write(`agent-coord council-set-steward: ${result.reason}\n`);
|
|
383
|
+
return 1;
|
|
384
|
+
}
|
|
385
|
+
process.stdout.write(
|
|
386
|
+
`${JSON.stringify({ council_id: councilId, steward: steward || null, ok: true })}\n`,
|
|
387
|
+
);
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
process.stderr.write(`agent-coord: unknown council action ${action}\n`);
|
|
393
|
+
return 2;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function handleAssignName(root: string, rest: string[]): Promise<number> {
|
|
397
|
+
const { assignName } = await import("./state/names.ts");
|
|
398
|
+
const [owner, kindArg] = rest;
|
|
399
|
+
if (!owner || !kindArg) {
|
|
400
|
+
process.stderr.write("agent-coord assign-name <instance_id> <session|subagent|transient>\n");
|
|
401
|
+
return 2;
|
|
402
|
+
}
|
|
403
|
+
if (kindArg !== "session" && kindArg !== "subagent" && kindArg !== "transient") {
|
|
404
|
+
process.stderr.write(`agent-coord assign-name: invalid kind ${kindArg}\n`);
|
|
405
|
+
return 2;
|
|
406
|
+
}
|
|
407
|
+
const name = assignName(root, owner, kindArg);
|
|
408
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, name, kind: kindArg })}\n`);
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function handlePostCommit(root: string): Promise<number> {
|
|
413
|
+
const raw = await readStdin();
|
|
414
|
+
let req: { owner?: string; prune?: string[] } = {};
|
|
415
|
+
try {
|
|
416
|
+
req = JSON.parse(raw);
|
|
417
|
+
} catch {
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
|
|
421
|
+
|
|
422
|
+
// Session-group-wide unclaim. `owner` is the parent's session_id which is
|
|
423
|
+
// also the group key (parent + subagents share session_id).
|
|
424
|
+
if (req.owner && Array.isArray(req.prune)) {
|
|
425
|
+
for (const path of req.prune) {
|
|
426
|
+
try {
|
|
427
|
+
groupUnclaim(root, req.owner, path);
|
|
428
|
+
} catch {
|
|
429
|
+
/* best-effort */
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function handlePostCheckout(root: string, _rest: string[]): Promise<number> {
|
|
437
|
+
const raw = await readStdin();
|
|
438
|
+
let req: { owner?: string; removed?: string[] } = {};
|
|
439
|
+
try {
|
|
440
|
+
req = JSON.parse(raw);
|
|
441
|
+
} catch {
|
|
442
|
+
return 0;
|
|
443
|
+
}
|
|
444
|
+
const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
|
|
445
|
+
if (req.owner && Array.isArray(req.removed)) {
|
|
446
|
+
for (const path of req.removed) {
|
|
447
|
+
try {
|
|
448
|
+
groupUnclaim(root, req.owner, path);
|
|
449
|
+
} catch {
|
|
450
|
+
/* best-effort */
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async function handleShellMutationPaths(root: string, rest: string[]): Promise<number> {
|
|
458
|
+
const { shellMutationPaths } = await import("./state/shell-mutation.ts");
|
|
459
|
+
// --cmd "<string>" form; falls back to stdin if --cmd not supplied
|
|
460
|
+
let cmd: string | undefined;
|
|
461
|
+
for (let i = 0; i < rest.length; i++) {
|
|
462
|
+
const a = rest[i]!;
|
|
463
|
+
if (a === "--cmd") {
|
|
464
|
+
cmd = rest[i + 1];
|
|
465
|
+
i++;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (cmd === undefined) cmd = await readStdin();
|
|
469
|
+
const paths = shellMutationPaths(cmd, root);
|
|
470
|
+
for (const p of paths) process.stdout.write(`${p}\n`);
|
|
471
|
+
return 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function handleShellMutationClaimLog(root: string, rest: string[]): Promise<number> {
|
|
475
|
+
// Parse + log in one spawn, avoids per-line process spawn from the bash loop.
|
|
476
|
+
// Usage:
|
|
477
|
+
// agent-coord shell-mutation-claim-log --cmd "<string>" --owner <id> --platform <p>
|
|
478
|
+
const args: Record<string, string> = {};
|
|
479
|
+
for (let i = 0; i < rest.length; i++) {
|
|
480
|
+
const a = rest[i]!;
|
|
481
|
+
if (a.startsWith("--")) {
|
|
482
|
+
const key = a.slice(2);
|
|
483
|
+
const val = rest[i + 1];
|
|
484
|
+
if (val === undefined || val.startsWith("--")) {
|
|
485
|
+
args[key] = "true";
|
|
486
|
+
} else {
|
|
487
|
+
args[key] = val;
|
|
488
|
+
i++;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const cmd = args.cmd ?? "";
|
|
493
|
+
if (!cmd) return 0;
|
|
494
|
+
const platform = args.platform ?? "unknown";
|
|
495
|
+
const owner = args.owner ?? null;
|
|
496
|
+
const { shellMutationPaths } = await import("./state/shell-mutation.ts");
|
|
497
|
+
const { emit } = await import("./events/emit.ts");
|
|
498
|
+
const { readHeartbeat } = await import("./state/heartbeat-writer.ts");
|
|
499
|
+
const paths = shellMutationPaths(cmd, root);
|
|
500
|
+
const truncated = cmd.length > 80 ? cmd.slice(0, 80) : cmd;
|
|
501
|
+
// Warn-only peer-shell-mutation signal. Formerly a SHELL_CLAIM_CANDIDATE line
|
|
502
|
+
// in a log file; now a canonical decision.warn so the
|
|
503
|
+
// signal survives in events.ndjson. (The blocking claim-conflict path is
|
|
504
|
+
// separate: claim.conflict / verdict, and unaffected.)
|
|
505
|
+
const harness = platform === "cursor" ? "cursor" : platform === "codex" ? "codex" : "claude-code";
|
|
506
|
+
const hb = owner ? readHeartbeat(root, owner) : null;
|
|
507
|
+
for (const p of paths) {
|
|
508
|
+
try {
|
|
509
|
+
emit(root, {
|
|
510
|
+
event_type: "decision.warn",
|
|
511
|
+
instance_id: owner ?? "unknown",
|
|
512
|
+
session_id: hb?.session_id ?? owner ?? "unknown",
|
|
513
|
+
harness,
|
|
514
|
+
data: {
|
|
515
|
+
rule: "shell_mutation_candidate",
|
|
516
|
+
reason: `path=${p} cmd=${truncated} platform=${platform}`,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
} catch {
|
|
520
|
+
/* telemetry only, never break the dispatcher */
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return 0;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function handleStaleSweep(root: string, _rest: string[]): Promise<number> {
|
|
527
|
+
const { staleSweep } = await import("./state/stale-sweep.ts");
|
|
528
|
+
const result = staleSweep(root);
|
|
529
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
530
|
+
return 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function handlePromptContext(root: string, rest: string[]): Promise<number> {
|
|
534
|
+
const args: Record<string, string> = {};
|
|
535
|
+
for (let i = 0; i < rest.length; i++) {
|
|
536
|
+
const a = rest[i]!;
|
|
537
|
+
if (a.startsWith("--")) {
|
|
538
|
+
const key = a.slice(2);
|
|
539
|
+
const val = rest[i + 1];
|
|
540
|
+
if (val === undefined || val.startsWith("--")) {
|
|
541
|
+
args[key] = "true";
|
|
542
|
+
} else {
|
|
543
|
+
args[key] = val;
|
|
544
|
+
i++;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const instanceId = args.instance;
|
|
549
|
+
const sessionId = args.session ?? instanceId;
|
|
550
|
+
const agentName = args.name;
|
|
551
|
+
const taskNudge = args["task-nudge"] === "true";
|
|
552
|
+
if (!instanceId) {
|
|
553
|
+
process.stderr.write(
|
|
554
|
+
"agent-coord prompt-context --instance <id> [--session <id>] [--name <agent-name>] [--task-nudge]\n",
|
|
555
|
+
);
|
|
556
|
+
return 2;
|
|
557
|
+
}
|
|
558
|
+
const { renderPromptContext } = await import("./render/prompt-context.ts");
|
|
559
|
+
const text = renderPromptContext({
|
|
560
|
+
coordRoot: root,
|
|
561
|
+
instanceId,
|
|
562
|
+
sessionId: sessionId!,
|
|
563
|
+
agentName,
|
|
564
|
+
taskNudge,
|
|
565
|
+
});
|
|
566
|
+
process.stdout.write(text);
|
|
567
|
+
return 0;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async function handleSessionContext(root: string, rest: string[]): Promise<number> {
|
|
571
|
+
const args: Record<string, string> = {};
|
|
572
|
+
for (let i = 0; i < rest.length; i++) {
|
|
573
|
+
const a = rest[i]!;
|
|
574
|
+
if (a.startsWith("--")) {
|
|
575
|
+
const key = a.slice(2);
|
|
576
|
+
const val = rest[i + 1];
|
|
577
|
+
if (val === undefined || val.startsWith("--")) {
|
|
578
|
+
args[key] = "true";
|
|
579
|
+
} else {
|
|
580
|
+
args[key] = val;
|
|
581
|
+
i++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const instanceId = args.instance;
|
|
586
|
+
const sessionId = args.session ?? instanceId;
|
|
587
|
+
const agentName = args.name;
|
|
588
|
+
const platformLabel = args["platform-label"];
|
|
589
|
+
if (!instanceId) {
|
|
590
|
+
process.stderr.write(
|
|
591
|
+
"agent-coord session-context --instance <id> [--session <id>] [--name <agent-name>] [--platform-label <label>]\n",
|
|
592
|
+
);
|
|
593
|
+
return 2;
|
|
594
|
+
}
|
|
595
|
+
const { renderSessionContext } = await import("./render/session-context.ts");
|
|
596
|
+
const text = renderSessionContext({
|
|
597
|
+
coordRoot: root,
|
|
598
|
+
instanceId,
|
|
599
|
+
sessionId: sessionId!,
|
|
600
|
+
agentName,
|
|
601
|
+
platformLabel,
|
|
602
|
+
});
|
|
603
|
+
process.stdout.write(text);
|
|
604
|
+
return 0;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function handleCodexReplay(root: string, rest: string[]): Promise<number> {
|
|
608
|
+
const args: Record<string, string> = {};
|
|
609
|
+
for (let i = 0; i < rest.length; i++) {
|
|
610
|
+
const a = rest[i]!;
|
|
611
|
+
if (a.startsWith("--")) {
|
|
612
|
+
const key = a.slice(2);
|
|
613
|
+
const val = rest[i + 1];
|
|
614
|
+
if (val === undefined || val.startsWith("--")) {
|
|
615
|
+
args[key] = "true";
|
|
616
|
+
} else {
|
|
617
|
+
args[key] = val;
|
|
618
|
+
i++;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const jsonlPath = args.jsonl;
|
|
623
|
+
const sessionId = args.session;
|
|
624
|
+
const instanceId = args.owner ?? sessionId;
|
|
625
|
+
const lastMsg = args["last-message"];
|
|
626
|
+
if (!jsonlPath || !sessionId) {
|
|
627
|
+
process.stderr.write(
|
|
628
|
+
"agent-coord codex-replay --jsonl <path> --session <id> [--owner <id>] [--last-message <text>]\n",
|
|
629
|
+
);
|
|
630
|
+
return 2;
|
|
631
|
+
}
|
|
632
|
+
const { replayCodexJsonl } = await import("./codex-replay.ts");
|
|
633
|
+
const result = replayCodexJsonl({
|
|
634
|
+
coordRoot: root,
|
|
635
|
+
jsonlPath,
|
|
636
|
+
sessionId,
|
|
637
|
+
instanceId: instanceId!,
|
|
638
|
+
lastAssistantMessage: lastMsg,
|
|
639
|
+
});
|
|
640
|
+
process.stdout.write(`${JSON.stringify({ session_id: sessionId, emitted: result.emitted })}\n`);
|
|
641
|
+
return 0;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async function handleResolveName(root: string, rest: string[]): Promise<number> {
|
|
645
|
+
const { resolveName } = await import("./state/names.ts");
|
|
646
|
+
const [owner, session] = rest;
|
|
647
|
+
if (!owner) {
|
|
648
|
+
process.stderr.write("agent-coord resolve-name <instance_id> [<session_id>]\n");
|
|
649
|
+
return 2;
|
|
650
|
+
}
|
|
651
|
+
const resolved = resolveName(root, owner, session);
|
|
652
|
+
if (!resolved) {
|
|
653
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, name: null, kind: null })}\n`);
|
|
654
|
+
return 0;
|
|
655
|
+
}
|
|
656
|
+
process.stdout.write(
|
|
657
|
+
`${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind })}\n`,
|
|
658
|
+
);
|
|
659
|
+
return 0;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async function handleEmitEvent(root: string, rest: string[]): Promise<number> {
|
|
663
|
+
const { emitAndProject } = await import("./cli-emit.ts");
|
|
664
|
+
const args: Record<string, string> = {};
|
|
665
|
+
for (let i = 0; i < rest.length; i++) {
|
|
666
|
+
const a = rest[i]!;
|
|
667
|
+
if (a.startsWith("--")) {
|
|
668
|
+
const key = a.slice(2);
|
|
669
|
+
const val = rest[i + 1];
|
|
670
|
+
if (val === undefined || val.startsWith("--")) {
|
|
671
|
+
args[key] = "true";
|
|
672
|
+
} else {
|
|
673
|
+
args[key] = val;
|
|
674
|
+
i++;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const eventType = args.type;
|
|
680
|
+
const instanceId = args.owner;
|
|
681
|
+
const sessionId = args.session;
|
|
682
|
+
const harness = args.harness as "claude-code" | "cursor" | "codex" | undefined;
|
|
683
|
+
const dataJson = args["data-json"] ?? "{}";
|
|
684
|
+
|
|
685
|
+
if (!eventType || !instanceId || !sessionId || !harness) {
|
|
686
|
+
process.stderr.write(
|
|
687
|
+
"agent-coord emit-event --type <T> --owner <id> --session <id> --harness <h> [--data-json '<json>']\n",
|
|
688
|
+
);
|
|
689
|
+
return 2;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
let data: Record<string, unknown>;
|
|
693
|
+
try {
|
|
694
|
+
const parsed = JSON.parse(dataJson) as unknown;
|
|
695
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
696
|
+
data = parsed as Record<string, unknown>;
|
|
697
|
+
} else {
|
|
698
|
+
process.stderr.write("agent-coord emit-event: --data-json must encode an object\n");
|
|
699
|
+
return 2;
|
|
700
|
+
}
|
|
701
|
+
} catch (err) {
|
|
702
|
+
process.stderr.write(
|
|
703
|
+
`agent-coord emit-event: invalid --data-json (${err instanceof Error ? err.message : String(err)})\n`,
|
|
704
|
+
);
|
|
705
|
+
return 2;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const result = emitAndProject(
|
|
709
|
+
{
|
|
710
|
+
event_type: eventType,
|
|
711
|
+
instance_id: instanceId,
|
|
712
|
+
session_id: sessionId,
|
|
713
|
+
harness,
|
|
714
|
+
turn_id: args["turn-id"],
|
|
715
|
+
parent_session_id: args["parent-session-id"],
|
|
716
|
+
parent_turn_id: args["parent-turn-id"],
|
|
717
|
+
data,
|
|
718
|
+
},
|
|
719
|
+
{ coordRoot: root },
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
if (!result) {
|
|
723
|
+
process.stderr.write("agent-coord emit-event: emission failed\n");
|
|
724
|
+
return 1;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
process.stdout.write(`${JSON.stringify(result.envelope)}\n`);
|
|
728
|
+
return 0;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
async function main(): Promise<number> {
|
|
732
|
+
const [subcommand, ...rest] = process.argv.slice(2);
|
|
733
|
+
const root = findCoordRoot(process.cwd());
|
|
734
|
+
if (!root) return 0;
|
|
735
|
+
|
|
736
|
+
if (subcommand === "verdict") {
|
|
737
|
+
return handleVerdict(root);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (subcommand === "project") {
|
|
741
|
+
return handleProject(root, rest);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (subcommand === "emit-event") {
|
|
745
|
+
return handleEmitEvent(root, rest);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (
|
|
749
|
+
subcommand === "set-task" ||
|
|
750
|
+
subcommand === "stamp-status-call" ||
|
|
751
|
+
subcommand === "set-turn-summary" ||
|
|
752
|
+
subcommand === "release-claim" ||
|
|
753
|
+
subcommand === "kill-heartbeat" ||
|
|
754
|
+
subcommand === "heal-pidmap" ||
|
|
755
|
+
subcommand === "heal-heartbeat" ||
|
|
756
|
+
subcommand === "stamp-tool-activity"
|
|
757
|
+
) {
|
|
758
|
+
return handleStateAction(root, subcommand, rest);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (subcommand === "append-scratch" || subcommand === "edit-scratchpad") {
|
|
762
|
+
return handleScratchAction(root, subcommand, rest);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (
|
|
766
|
+
subcommand === "council-advance" ||
|
|
767
|
+
subcommand === "council-close" ||
|
|
768
|
+
subcommand === "council-archive" ||
|
|
769
|
+
subcommand === "council-unarchive" ||
|
|
770
|
+
subcommand === "council-delete" ||
|
|
771
|
+
subcommand === "council-set-steward"
|
|
772
|
+
) {
|
|
773
|
+
return handleCouncilAction(root, subcommand, rest);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (subcommand === "assign-name") {
|
|
777
|
+
return handleAssignName(root, rest);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (subcommand === "resolve-name") {
|
|
781
|
+
return handleResolveName(root, rest);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (subcommand === "codex-replay") {
|
|
785
|
+
return handleCodexReplay(root, rest);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (subcommand === "stale-sweep") {
|
|
789
|
+
return handleStaleSweep(root, rest);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (subcommand === "session-context") {
|
|
793
|
+
return handleSessionContext(root, rest);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (subcommand === "prompt-context") {
|
|
797
|
+
return handlePromptContext(root, rest);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (subcommand === "shell-mutation-paths") {
|
|
801
|
+
return handleShellMutationPaths(root, rest);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (subcommand === "shell-mutation-claim-log") {
|
|
805
|
+
return handleShellMutationClaimLog(root, rest);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (subcommand === "post-commit") {
|
|
809
|
+
return handlePostCommit(root);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (subcommand === "post-checkout") {
|
|
813
|
+
return handlePostCheckout(root, rest);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
await logNoop(root, subcommand ?? "(none)", rest);
|
|
817
|
+
// Phase 1/2: silent on stdout for unknown subcommands, exit 0. Existing
|
|
818
|
+
// `harn agents …` callers keep working unchanged.
|
|
819
|
+
return 0;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
main()
|
|
823
|
+
.then((code) => process.exit(code))
|
|
824
|
+
.catch((err) => {
|
|
825
|
+
try {
|
|
826
|
+
const root = findCoordRoot(process.cwd());
|
|
827
|
+
if (root) {
|
|
828
|
+
const path = join(root, ".harnery", "debug", "agent-coord.errors.ndjson");
|
|
829
|
+
appendFileSync(
|
|
830
|
+
path,
|
|
831
|
+
`${JSON.stringify({ ts: new Date().toISOString(), error: String(err) })}\n`,
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
} catch {
|
|
835
|
+
/* swallow */
|
|
836
|
+
}
|
|
837
|
+
process.exit(0);
|
|
838
|
+
});
|