elanous 0.2.17 → 0.2.19
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +25 -24
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +24 -23
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +28 -27
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +6 -3
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/lib/pch-regress-map.ts +23 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +436 -59
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +120 -0
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +8 -1
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +174 -0
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +45 -7
- package/scripts/release-loop/version-node.ts +145 -40
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/away/presence.ts +40 -0
- package/src/away/release-watch.ts +255 -0
- package/src/briefing/brief-items.ts +86 -17
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +85 -7
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +128 -19
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +403 -4
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +244 -40
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/graph-runner/runner.ts +25 -5
- package/src/harness/harness-cli-command.ts +12 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/harness-salvage-cli.ts +78 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/hq.ts +6 -1
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +185 -18
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -15
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +106 -4
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +450 -54
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/http-server.ts +16 -5
- package/src/nexus/api/loop-edges.ts +352 -23
- package/src/nexus/api/ops-api.ts +96 -28
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/nexus/index.ts +7 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +106 -6
- package/src/release-loop/feature-store.ts +64 -16
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +64 -20
- package/src/release-loop/rubric.ts +136 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +12 -6
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-cli.ts +36 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +179 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +61 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +101 -7
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +203 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +49 -5
- package/src/task-agent/live-moves.ts +176 -0
- package/src/task-agent/mission.ts +449 -0
- package/src/task-agent/shadow.ts +258 -0
- package/src/task-agent/task-hand.ts +337 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
- package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +88 -20
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +11 -0
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/telegram.ts +5 -20
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +196 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-aac7332576c4d6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3143b07a817d1da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-17ba086ef82d339b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-6e5c3a867557298b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4187666132fc98c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-09328d499162bae4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-29ff3a8f84f03b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-80ec7401bc83f511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-fc7d86467291ae38.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-043a9111e74f6b24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-23a4601ceb7f452e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-10d958605a77b3bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-12916918988ac97e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7f49fe645d5132f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-424659bacfb8cdd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d4fef9ccd7d406b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
package/src/cli/tasks-cli.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import type
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { InvalidArgumentError, Option, type Command } from 'commander';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
4
5
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
5
6
|
import { readNexusRuntime } from '../nexus/runtime.js';
|
|
7
|
+
import { checklistDevVersion } from '../release-loop/checklist.js';
|
|
8
|
+
import { cardsBoard, countBoardTasks, INTERNAL_PACK_PROJECT, mergeBoards, releasePackBoard, type BoardNode } from '../task-agent/board.js';
|
|
6
9
|
import type { Task } from '../task-orchestrator/types.js';
|
|
10
|
+
import { cardPrNumber, COMPLETION_KINDS, handTask, nextMoveFor, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote, TASK_CARD_PREFIX, TASK_SEATS, type CompletionKind, type TaskCard, type TaskLaunchContext, type TaskLaunchReceipt, type TaskLauncher, type TaskSeat } from '../task-agent/task-hand.js';
|
|
11
|
+
import { refreshCardRunBinding, type CardRunEvidenceDeps } from '../task-agent/card-evidence.js';
|
|
12
|
+
import { advanceMission, handMission, MissionAdvanceError, openMissionIds, recordPieceRef, type AdvanceMissionResult, type MissionDecompose, type PieceEvidenceReader } from '../task-agent/mission.js';
|
|
7
13
|
|
|
8
14
|
type TaskRow = Pick<Task, 'id' | 'title' | 'priority' | 'status' | 'createdAt' | 'approval' | 'generatedBy'>;
|
|
9
15
|
|
|
@@ -12,6 +18,157 @@ export interface TasksCliDeps {
|
|
|
12
18
|
bearerToken?: string;
|
|
13
19
|
fetch?: typeof fetch;
|
|
14
20
|
output?: (line: string) => void;
|
|
21
|
+
/** `task hand --live` 의 발사기(시험 주입). 기본은 운영 config-dir 로 `harness say` 를 떼어 띄운다. */
|
|
22
|
+
taskLauncher?: TaskLauncher;
|
|
23
|
+
/** 과제 카드 상태 파일(시험 주입). 기본 = `effectiveInstanceRoot()/task-agent-actions.json`. */
|
|
24
|
+
taskStatePath?: string;
|
|
25
|
+
/** logs.db 싱크 등록(시험 주입). 기본 = `registerStandaloneLogSink`. `hand`·`show` 가 부른다. */
|
|
26
|
+
registerSink?: (surface: string) => Promise<unknown>;
|
|
27
|
+
/** `task hand --mission` 의 분해기(시험 주입). 기본 = 나열형은 결정론 · 아니면 `decomposeSelfDevGoal`. */
|
|
28
|
+
missionDecompose?: MissionDecompose;
|
|
29
|
+
/** `task advance` 의 착지 근거 읽기(시험 주입). 기본 = `predecessorState`(gh · 런 원장). */
|
|
30
|
+
pieceEvidence?: PieceEvidenceReader;
|
|
31
|
+
/** `task board` 의 내부 팩 어댑터(시험 주입). 기본 = 현재 판(`checklistDevVersion()`)의 `releasePackBoard` — 읽기만. */
|
|
32
|
+
releaseBoard?: () => BoardNode[];
|
|
33
|
+
/** `tasks show`·`advance` 의 런 근거 읽기(시험 주입) — 기본 = 런 원장(이 우주 ⊕ 연합) ⊕ 호스트 logs 의 Pod job-finished. */
|
|
34
|
+
cardRunEvidence?: CardRunEvidenceDeps;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** `<조각id>=<값>` 반복 인자. */
|
|
38
|
+
function collectPair(value: string, previous: Array<[string, string]> = []): Array<[string, string]> {
|
|
39
|
+
const at = value.indexOf('=');
|
|
40
|
+
if (at <= 0 || at === value.length - 1) throw new InvalidArgumentError(`<조각id>=<값> 모양이 아니다: ${value}`);
|
|
41
|
+
return [...previous, [value.slice(0, at), value.slice(at + 1)]];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function advanceLines(result: AdvanceMissionResult): string[] {
|
|
45
|
+
// 제안이 끝난 미션은 다시 조회하지 않는다 — «착지 0»으로 보이지 않게 따로 적는다.
|
|
46
|
+
if (result.green === 'already-proposed' && Object.keys(result.evidence).length === 0) return [`${result.missionId}\tadvance\t${result.mode}\tgreen already-proposed — 다시 조회·넘기지 않는다`];
|
|
47
|
+
return [
|
|
48
|
+
`${result.missionId}\tadvance\t${result.mode}\t착지 ${result.landed.length} · 준비 ${result.ready.length} · 넘김 ${result.handed.length} · green ${result.green}`,
|
|
49
|
+
...Object.entries(result.evidence).map(([id, state]) => ` 근거: ${id}\t${state}${result.landed.includes(id) ? '\t착지' : ''}`),
|
|
50
|
+
...result.handed.map((piece) => ` 넘김: ${piece.pieceId}\t${piece.launched ? 'launched' : piece.error ? 'failed' : 'not launched'}${piece.error ? `\t${piece.error}` : ''}`),
|
|
51
|
+
...(result.green === 'proposed' ? [' green 제안을 미션 카드에 적었다 — 체크리스트는 바꾸지 않는다(OP/주인이 뒤집는다)'] : []),
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `tasks hand`·`show` 가 남기는 `task-agent` 관측의 logs.db surface 라벨. */
|
|
56
|
+
export const TASK_AGENT_SINK_SURFACE = 'task-agent';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 독립 CLI 프로세스는 데몬의 logs.db 싱크를 상속하지 않는다 — 등록하지 않으면 `debug.log('task-agent', …)` 가
|
|
60
|
+
* 파일 트레일에만 남고 `elanous logs --category task-agent` 에 0건으로 보인다(10-06 실측: 카드 3장 · 이벤트 0).
|
|
61
|
+
* fail-open — 등록 실패가 과제 넘기기를 막지 않는다.
|
|
62
|
+
*/
|
|
63
|
+
export async function registerTaskAgentSink(register?: (surface: string) => Promise<unknown>): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
const fn = register ?? (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink;
|
|
66
|
+
await fn(TASK_AGENT_SINK_SURFACE);
|
|
67
|
+
} catch { /* fail-open — 파일 트레일이 진실원 */ }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 기본 발사기 — 지금 도는 진입점으로 `--config-dir <운영 config>` 를 붙여 떼어 띄운다.
|
|
72
|
+
* config-dir 은 명시(`--config-dir`/`--test`)가 있으면 그것, 없으면 운영 루트(`prodInstanceRoot()`)다.
|
|
73
|
+
* (작업 트리에서 그냥 띄우면 test 우주로 가서 동결·몫·풀 상한 밖에서 돈다.)
|
|
74
|
+
*/
|
|
75
|
+
/** This package's CLI entry — never `process.argv[1]`, which is whatever script started this process
|
|
76
|
+
* (ORCH-LIVE-1008: the orchestrator tick runs `bun …/tick.ts delegate`, so the hand launched `tick.ts --config-dir …
|
|
77
|
+
* harness say …`, which exited 2 with stdio ignored — the card said `launched` and no run ever started). */
|
|
78
|
+
export const ELANOUS_CLI_ENTRY = resolve(import.meta.dir, '../../bin/elanous.mjs');
|
|
79
|
+
|
|
80
|
+
/** argv for a detached `elanous` child that runs `args` in `configDir`. */
|
|
81
|
+
export function taskLauncherArgv(configDir: string, args: readonly string[]): string[] {
|
|
82
|
+
return [ELANOUS_CLI_ENTRY, '--config-dir', configDir, ...args];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function defaultTaskLauncher(args: string[], cwd?: string, context?: TaskLaunchContext, deps: { spawn?: DetachedSpawn } = {}): Promise<void | TaskLaunchReceipt> {
|
|
86
|
+
const spawn = deps.spawn ?? ((await import('node:child_process')).spawn as unknown as DetachedSpawn);
|
|
87
|
+
const { getElanousConfigDirOverride } = await import('../elanous-config-dir.js');
|
|
88
|
+
const { prodInstanceRoot } = await import('../instance/resolve.js');
|
|
89
|
+
const configDir = getElanousConfigDirOverride() ?? prodInstanceRoot();
|
|
90
|
+
// TA-CARD-RUN-LINK: 런 id 를 자식 환경에 얹는다 — `harness say` 의 런(호스트 orchestrate)이 그 id 를 상속한다.
|
|
91
|
+
await spawnDetachedConfirmed(spawn, process.execPath, taskLauncherArgv(configDir, args), SPAWN_CONFIRM_TIMEOUT_MS, cwd, context?.env);
|
|
92
|
+
return context ? { runId: context.runId } : undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** `spawn` 의 필요한 면만(시험 주입). */
|
|
96
|
+
export type DetachedSpawn = (command: string, args: string[], options: { detached: true; stdio: 'ignore'; cwd?: string; env?: NodeJS.ProcessEnv }) => {
|
|
97
|
+
pid?: number;
|
|
98
|
+
once(event: 'spawn', listener: () => void): unknown;
|
|
99
|
+
once(event: 'error', listener: (error: Error) => void): unknown;
|
|
100
|
+
removeListener(event: string, listener: (...args: never[]) => void): unknown;
|
|
101
|
+
unref(): void;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** 발사 확인 대기 상한 — 'spawn'·'error' 어느 쪽도 이 안에 안 오면 pid 유무로 판정한다. */
|
|
105
|
+
export const SPAWN_CONFIRM_TIMEOUT_MS = 2_000;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 떼어 띄우고 «떴는지» 확인한다 — 'spawn' 이면 성공, 'error'(ENOENT·EACCES 등)면 던진다.
|
|
109
|
+
* 확인 없이 돌려주면 비동기 error 가 처리되지 않고 카드가 `launched` 로 남는다(사후 리뷰 must-fix).
|
|
110
|
+
*/
|
|
111
|
+
export function spawnDetachedConfirmed(spawnFn: DetachedSpawn, command: string, args: string[], timeoutMs = SPAWN_CONFIRM_TIMEOUT_MS, cwd?: string, env?: Record<string, string>): Promise<void> {
|
|
112
|
+
return new Promise<void>((resolve, reject) => {
|
|
113
|
+
let child: ReturnType<DetachedSpawn>;
|
|
114
|
+
try { child = spawnFn(command, args, { detached: true, stdio: 'ignore', ...(cwd ? { cwd } : {}), ...(env ? { env: { ...process.env, ...env } } : {}) }); } catch (error) { reject(error); return; }
|
|
115
|
+
let settled = false;
|
|
116
|
+
const finish = (error?: Error) => {
|
|
117
|
+
if (settled) return;
|
|
118
|
+
settled = true;
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
child.removeListener('spawn', onSpawn);
|
|
121
|
+
// error 리스너는 남겨 둔다 — 확인 뒤 늦게 오는 error 도 처리되지 않은 예외가 되지 않게.
|
|
122
|
+
if (error) { reject(error); return; }
|
|
123
|
+
child.unref();
|
|
124
|
+
resolve();
|
|
125
|
+
};
|
|
126
|
+
const onSpawn = () => finish();
|
|
127
|
+
const onError = (error: Error) => finish(error);
|
|
128
|
+
child.once('spawn', onSpawn);
|
|
129
|
+
child.once('error', onError);
|
|
130
|
+
const timer = setTimeout(() => finish(child.pid ? undefined : new Error(`발사 확인 시간 초과(${timeoutMs}ms) · pid 없음`)), timeoutMs);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function moveLine(card: TaskCard): string {
|
|
135
|
+
const move = nextMoveFor(card);
|
|
136
|
+
return `${move.kind}${move.command ? ` — elanous ${move.command.map(shellQuote).join(' ')}` : ''} (${move.reason})`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function headLine(text: string): string {
|
|
140
|
+
const first = text.split('\n')[0]!.trim();
|
|
141
|
+
return first.length > 100 ? `${first.slice(0, 100)}…` : first;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** 런 연결 한 줄 — 런 id 가 없으면 없다(옛 카드 · shadow). */
|
|
145
|
+
function runLinkLine(card: TaskCard): string[] {
|
|
146
|
+
if (!card.runId) return [];
|
|
147
|
+
const number = cardPrNumber(card);
|
|
148
|
+
const url = typeof card.pr === 'object' ? card.pr.url : undefined;
|
|
149
|
+
const pr = number !== undefined ? `#${number}${url ? ` ${url}` : ''}` : '-';
|
|
150
|
+
return [`런: ${card.runId} · 골: ${card.goalId ?? '-'} · PR: ${pr}${card.runChildId ? ` · 자식 런: ${card.runChildId}` : ''}`];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function taskCardLines(card: TaskCard): string[] {
|
|
154
|
+
return [
|
|
155
|
+
[card.id, card.status, card.seat ?? '-', card.checklistId ?? '-', card.createdAt, card.text].join('\t'),
|
|
156
|
+
...(card.mission ? [`미션: ${card.mission} · after: ${card.after?.length ? card.after.join(', ') : '-'}`] : []),
|
|
157
|
+
...runLinkLine(card),
|
|
158
|
+
`다음 수: ${moveLine(card)}`,
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** 미션 카드 — 머리 한 줄 ⊕ 조각 한 줄씩(상태·다음 수·선행) ⊕ 간선 한 줄씩. */
|
|
163
|
+
function missionLines(mission: TaskCard, pieces: TaskCard[]): string[] {
|
|
164
|
+
const edges = pieces.flatMap((piece) => (piece.after ?? []).map((before) => `간선: ${before} -> ${piece.id}`));
|
|
165
|
+
return [
|
|
166
|
+
[mission.id, 'mission', mission.seat ?? '-', mission.checklistId ?? '-', mission.createdAt, `조각 ${pieces.length} · 간선 ${edges.length} · 출처 ${mission.splitSource ?? '-'}`, headLine(mission.text)].join('\t'),
|
|
167
|
+
...pieces.map((piece) => [`조각: ${piece.id}`, piece.status, nextMoveFor(piece).kind, `after: ${piece.after?.length ? piece.after.join(',') : '-'}`, headLine(piece.text),
|
|
168
|
+
...(piece.runId ? [`런: ${piece.runId}${cardPrNumber(piece) !== undefined ? ` · PR #${cardPrNumber(piece)}` : ''}`] : [])].join('\t')),
|
|
169
|
+
...edges,
|
|
170
|
+
...(mission.greenProposal ? [`green 제안: ${mission.greenProposal.at} · 칸 ${mission.greenProposal.checklistId ?? '-'} · 근거 ${Object.entries(mission.greenProposal.evidence).map(([id, ref]) => `${id}=${ref}`).join(', ')}`] : []),
|
|
171
|
+
];
|
|
15
172
|
}
|
|
16
173
|
|
|
17
174
|
const PRIORITY_RANK: Record<Task['priority'], number> = { urgent: 0, high: 1, medium: 2, low: 3 };
|
|
@@ -89,7 +246,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
89
246
|
if (!auth) throw new NexusUnavailable();
|
|
90
247
|
return auth;
|
|
91
248
|
};
|
|
92
|
-
const tasks = program.command('tasks').alias('task').description('TOX 태스크 조회 및 승인');
|
|
249
|
+
const tasks = program.command('tasks').alias('task').description('TOX 태스크 조회 및 승인 · TASK-AGENT 과제 넘기기(hand)');
|
|
93
250
|
|
|
94
251
|
tasks.command('list')
|
|
95
252
|
.description('우선순위와 생성 시각 순으로 태스크 보기')
|
|
@@ -124,10 +281,252 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
124
281
|
} catch (error) { handleError(error, out); }
|
|
125
282
|
});
|
|
126
283
|
|
|
284
|
+
tasks.command('hand [text]')
|
|
285
|
+
.description('TASK-AGENT 에 과제 한 줄을 넘긴다 — 카드를 적고 첫 수(harness say --substrate pod --merge-by-host)를 고른다 · 기본 shadow(발사 0) · `--mission` 이면 조각 카드 ⊕ 선후로 쪼개 선행 없는 조각만 넘긴다')
|
|
286
|
+
.addOption(new Option('--seat <seat>', `과제 자리 (${TASK_SEATS.join('|')})`).choices([...TASK_SEATS]))
|
|
287
|
+
.option('--checklist <cellId>', '릴리스 체크리스트 칸 id')
|
|
288
|
+
.addOption(new Option('--completion <kind>', `종결 종류 (${COMPLETION_KINDS.join('|')} · 기본 code-pr = PR 병합)`).choices([...COMPLETION_KINDS]))
|
|
289
|
+
.option('--mission <text>', '미션 문면 — 조각(`- …`·`① …`·`1. …` ⊕ `after: 1`)으로 나열돼 있으면 결정론, 아니면 분해기로 쪼갠다')
|
|
290
|
+
.option('--project <id>', '프로젝트 id — --target 과 함께')
|
|
291
|
+
.option('--goal <id>', '보드 소속 목표 id (task board)')
|
|
292
|
+
.option('--milestone <id>', '보드 소속 이정표 id (task board)')
|
|
293
|
+
.option('--target <dir>', '프로젝트 대상 디렉터리(있어야 한다 · 상대 경로는 절대로 · git 이 아니면 --completion 이 code-pr 밖이어야 한다) — 발사기를 이 디렉터리에서 띄운다')
|
|
294
|
+
.option('--live', '실제로 발사한다(기본은 shadow — 명령만 출력)')
|
|
295
|
+
.option('--json', 'JSON 출력')
|
|
296
|
+
.action(async (text: string | undefined, opts: { seat?: TaskSeat; checklist?: string; completion?: CompletionKind; mission?: string; project?: string; target?: string; goal?: string; milestone?: string; live?: boolean; json?: boolean }) => {
|
|
297
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
298
|
+
if ((text === undefined) === (opts.mission === undefined)) {
|
|
299
|
+
out('과제 한 줄 «또는» --mission <문면> 중 하나만 준다');
|
|
300
|
+
process.exitCode = 1;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if ((opts.project === undefined) !== (opts.target === undefined)) {
|
|
304
|
+
out('--project <id> 와 --target <dir> 를 함께 줘야 한다');
|
|
305
|
+
process.exitCode = 1;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (opts.mission !== undefined && (opts.completion !== undefined || opts.project !== undefined || opts.goal !== undefined || opts.milestone !== undefined)) {
|
|
309
|
+
out('--completion/--project/--target/--goal/--milestone 은 과제 한 줄에만 준다 — --mission 조각은 종전처럼 code-pr 이다');
|
|
310
|
+
process.exitCode = 1;
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (opts.mission !== undefined) {
|
|
314
|
+
try {
|
|
315
|
+
const result = await handMission({
|
|
316
|
+
text: opts.mission,
|
|
317
|
+
...(opts.seat ? { seat: opts.seat } : {}),
|
|
318
|
+
...(opts.checklist ? { checklistId: opts.checklist } : {}),
|
|
319
|
+
live: opts.live === true,
|
|
320
|
+
...(deps.taskStatePath ? { statePath: deps.taskStatePath } : {}),
|
|
321
|
+
launcher: deps.taskLauncher ?? defaultTaskLauncher,
|
|
322
|
+
...(deps.missionDecompose ? { decompose: deps.missionDecompose } : {}),
|
|
323
|
+
});
|
|
324
|
+
if (opts.json) out(JSON.stringify(result));
|
|
325
|
+
else {
|
|
326
|
+
out(`${result.mission.id}\tmission\t${result.mode}\t조각 ${result.pieces.length} · 간선 ${result.edges.length} · 출처 ${result.source}`);
|
|
327
|
+
for (const piece of result.pieces) {
|
|
328
|
+
out([piece.card.id, piece.launched ? 'launched' : piece.error ? 'failed' : 'not launched', `${piece.move.kind} (${piece.move.reason})`, headLine(piece.card.text)].join('\t'));
|
|
329
|
+
if (piece.move.command) out(` elanous ${piece.move.command.map(shellQuote).join(' ')}`);
|
|
330
|
+
if (piece.error) out(` ${piece.error}`);
|
|
331
|
+
}
|
|
332
|
+
for (const [before, after] of result.edges) out(`간선: ${before} -> ${after}`);
|
|
333
|
+
if (result.mode === 'shadow') out('(shadow — 띄우지 않았다 · 띄우려면 --live)');
|
|
334
|
+
}
|
|
335
|
+
if (result.pieces.some((piece) => piece.error)) process.exitCode = 1;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
out(error instanceof Error ? error.message : '미션 넘기기 실패');
|
|
338
|
+
process.exitCode = 1;
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const result = await handTask({
|
|
344
|
+
text: text!,
|
|
345
|
+
...(opts.project !== undefined && opts.target !== undefined ? { project: { id: opts.project, target: opts.target } } : {}),
|
|
346
|
+
...(opts.goal !== undefined ? { goal: opts.goal } : {}),
|
|
347
|
+
...(opts.milestone !== undefined ? { milestone: opts.milestone } : {}),
|
|
348
|
+
...(opts.seat ? { seat: opts.seat } : {}),
|
|
349
|
+
...(opts.checklist ? { checklistId: opts.checklist } : {}),
|
|
350
|
+
...(opts.completion ? { completion: opts.completion } : {}),
|
|
351
|
+
live: opts.live === true,
|
|
352
|
+
...(deps.taskStatePath ? { statePath: deps.taskStatePath } : {}),
|
|
353
|
+
launcher: deps.taskLauncher ?? defaultTaskLauncher,
|
|
354
|
+
});
|
|
355
|
+
if (opts.json) out(JSON.stringify(result));
|
|
356
|
+
else {
|
|
357
|
+
out(`${result.card.id}\t${result.mode}\t${result.launched ? 'launched' : 'not launched'}`);
|
|
358
|
+
out(`elanous ${result.move.command!.map(shellQuote).join(' ')}`);
|
|
359
|
+
if (result.cwd) out(`(cwd: ${result.cwd})`);
|
|
360
|
+
if (!result.launched) out('(shadow — 띄우지 않았다 · 띄우려면 --live)');
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
out(error instanceof Error ? error.message : '과제 넘기기 실패');
|
|
364
|
+
process.exitCode = 1;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
tasks.command('board')
|
|
369
|
+
.description('보드 — 프로젝트 › 목표 › 이정표 › 과제. 과제 카드(project·goal·milestone)와 내부 팩(릴리스 체크리스트 = elanous › 판 › 칸)을 합쳐 읽는다(쓰지 않는다)')
|
|
370
|
+
.option('--project <id>', '이 프로젝트만')
|
|
371
|
+
.option('--json', 'JSON 출력')
|
|
372
|
+
.action(async (opts: { project?: string; json?: boolean }) => {
|
|
373
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
374
|
+
try {
|
|
375
|
+
// 내부 팩은 elanous 프로젝트만 낸다 — 다른 프로젝트로 거르면 릴리스 원장을 열지 않는다(그 원장의 오류가 카드 보드를 막지 않게).
|
|
376
|
+
const wantsPack = opts.project === undefined || opts.project === INTERNAL_PACK_PROJECT;
|
|
377
|
+
const board = mergeBoards(
|
|
378
|
+
cardsBoard(deps.taskStatePath ?? taskStatePathDefault()),
|
|
379
|
+
wantsPack ? (deps.releaseBoard ?? (() => releasePackBoard(checklistDevVersion())))() : [],
|
|
380
|
+
);
|
|
381
|
+
// 무소속(unassigned: true)은 어떤 --project 로도 고르지 않는다 — 실제 프로젝트 id 와 겹치지 않게.
|
|
382
|
+
const selected = opts.project !== undefined ? board.filter((project) => project.unassigned !== true && project.id === opts.project) : board;
|
|
383
|
+
try { debug.log('task-agent', 'board', { projects: selected.length, tasks: countBoardTasks(selected), project: opts.project ?? null }); } catch { /* fail-soft */ }
|
|
384
|
+
if (opts.json) out(JSON.stringify(selected));
|
|
385
|
+
else {
|
|
386
|
+
const show = (node: BoardNode, depth: number): void => {
|
|
387
|
+
out(`${' '.repeat(depth)}${node.level}\t${node.id}\t${node.status}\t${headLine(node.title)}`);
|
|
388
|
+
for (const next of node.children ?? []) show(next, depth + 1);
|
|
389
|
+
};
|
|
390
|
+
if (selected.length === 0) out(opts.project !== undefined ? `프로젝트 없음: ${opts.project}` : '보드가 비어 있다');
|
|
391
|
+
for (const project of selected) show(project, 0);
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
out(error instanceof Error ? error.message : '보드 읽기 실패');
|
|
395
|
+
process.exitCode = 1;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
tasks.command('advance [missionId]')
|
|
400
|
+
.description('TASK-AGENT 미션 ② — 조각의 PR 병합(근거: `--pr`/`--goal` 로 카드에 적은 PR 번호·골 id)으로 착지를 가르고 새로 준비된 조각만 한 번 넘긴다 · 모두 착지면 green «제안»만 적는다 · 기본 shadow(발사 0)')
|
|
401
|
+
.option('--all', 'green 제안이 아직 없는 미션 전부')
|
|
402
|
+
.option('--pr <pieceId=N>', '조각 카드에 그 런의 PR 번호를 적는다(반복 가능)', collectPair)
|
|
403
|
+
.option('--goal <pieceId=goalId>', '조각 카드에 골 id(16자)를 적는다 — 런 원장 pr-opened 로 PR 을 찾는다(반복 가능)', collectPair)
|
|
404
|
+
.option('--live', '준비된 조각을 실제로 발사한다(기본은 shadow)')
|
|
405
|
+
.option('--json', 'JSON 출력')
|
|
406
|
+
.action(async (missionId: string | undefined, opts: { all?: boolean; pr?: Array<[string, string]>; goal?: Array<[string, string]>; live?: boolean; json?: boolean }) => {
|
|
407
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
408
|
+
const started = Date.now();
|
|
409
|
+
if ((missionId === undefined) === (opts.all !== true)) {
|
|
410
|
+
const reason = '미션 id «또는» --all 중 하나만 준다';
|
|
411
|
+
try { debug.log('task-agent', 'mission-advance', { missionId: missionId ?? null, all: opts.all === true, mode: opts.live ? 'live' : 'shadow', stage: 'cli-args', error: reason, ms: Date.now() - started }); } catch { /* fail-soft */ }
|
|
412
|
+
out(reason);
|
|
413
|
+
process.exitCode = 1;
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const statePath = deps.taskStatePath;
|
|
417
|
+
try {
|
|
418
|
+
// 근거 인자는 운영자 입력이다 — 모양을 «먼저» 전부 검사해 하나라도 틀리면 아무것도 적지 않고 진행도 안 한다.
|
|
419
|
+
const refs: Array<[string, { pr: number } | { goalId: string }]> = [
|
|
420
|
+
...(opts.pr ?? []).map(([pieceId, value]): [string, { pr: number }] => {
|
|
421
|
+
if (!/^#?[1-9]\d*$/.test(value)) throw new Error(`PR 번호가 아니다: ${value}`);
|
|
422
|
+
return [pieceId, { pr: Number(value.replace(/^#/, '')) }];
|
|
423
|
+
}),
|
|
424
|
+
...(opts.goal ?? []).map(([pieceId, value]): [string, { goalId: string }] => {
|
|
425
|
+
if (!/^[a-f0-9]{16}$/.test(value)) throw new Error(`골 id 는 16자 hex 다: ${value}`);
|
|
426
|
+
return [pieceId, { goalId: value }];
|
|
427
|
+
}),
|
|
428
|
+
];
|
|
429
|
+
const twice = refs.map(([pieceId]) => pieceId).filter((pieceId, at, all) => all.indexOf(pieceId) !== at);
|
|
430
|
+
if (twice.length) throw new Error(`한 조각에 근거를 두 번 줬다(--pr·--goal 은 조각마다 하나): ${[...new Set(twice)].join(', ')}`);
|
|
431
|
+
// 기록 실패(없는 조각 · 제안된 미션)는 그 근거만 실패로 적고 계속 — `--all` 의 나머지 미션을 막지 않는다.
|
|
432
|
+
const refFailures: string[] = [];
|
|
433
|
+
for (const [pieceId, ref] of refs) {
|
|
434
|
+
try { recordPieceRef(pieceId, ref, statePath, missionId); } catch (error) {
|
|
435
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
436
|
+
if (missionId !== undefined) throw error;
|
|
437
|
+
refFailures.push(reason);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const ids = missionId !== undefined ? [missionId] : openMissionIds(statePath);
|
|
441
|
+
const results: AdvanceMissionResult[] = [];
|
|
442
|
+
const failures: Array<{ missionId: string; error: string }> = [];
|
|
443
|
+
for (const id of ids) {
|
|
444
|
+
try {
|
|
445
|
+
results.push(await advanceMission(id, {
|
|
446
|
+
live: opts.live === true,
|
|
447
|
+
...(statePath ? { statePath } : {}),
|
|
448
|
+
launcher: deps.taskLauncher ?? defaultTaskLauncher,
|
|
449
|
+
...(deps.pieceEvidence ? { readEvidence: deps.pieceEvidence } : {}),
|
|
450
|
+
...(deps.cardRunEvidence ? { runEvidence: deps.cardRunEvidence } : {}),
|
|
451
|
+
}));
|
|
452
|
+
} catch (error) {
|
|
453
|
+
// 미션 id 하나면 그대로 실패 · --all 이면 그 미션만 실패로 적고 다음 미션으로 간다(크론이 한 미션에 막히지 않게).
|
|
454
|
+
if (missionId !== undefined) throw error;
|
|
455
|
+
failures.push({ missionId: id, error: error instanceof Error ? error.message : String(error) });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (missionId === undefined) {
|
|
459
|
+
// --all 은 호출마다 요약 한 줄 — 대상 0·근거 기록 실패뿐인 호출도 관측에 남는다(미션별 줄은 advanceMission 이 남긴다).
|
|
460
|
+
try { debug.log('task-agent', 'mission-advance', { missionId: null, all: true, mode: opts.live ? 'live' : 'shadow', stage: 'all-summary', missions: ids.length, advanced: results.length, failures: failures.map((failure) => failure.missionId), refFailures: refFailures.length, ms: Date.now() - started }); } catch { /* fail-soft */ }
|
|
461
|
+
}
|
|
462
|
+
if (opts.json) out(JSON.stringify(missionId !== undefined ? results[0] : failures.length || refFailures.length ? { results, failures, refFailures } : results));
|
|
463
|
+
else {
|
|
464
|
+
if (results.length === 0 && failures.length === 0) out('넘길 미션이 없다 (green 제안 전 미션 0)');
|
|
465
|
+
for (const failure of failures) out(`${failure.missionId}\tadvance\tfailed\t${failure.error}`);
|
|
466
|
+
for (const reason of refFailures) out(`근거 기록 실패: ${reason}`);
|
|
467
|
+
for (const result of results) for (const line of advanceLines(result)) out(line);
|
|
468
|
+
if (!opts.live && results.some((result) => result.handed.length > 0)) out('(shadow — 띄우지 않았다 · 띄우려면 --live)');
|
|
469
|
+
}
|
|
470
|
+
if (failures.length || refFailures.length || results.some((result) => result.handed.some((piece) => piece.error))) process.exitCode = 1;
|
|
471
|
+
} catch (error) {
|
|
472
|
+
const reason = error instanceof Error ? error.message : '미션 진행 실패';
|
|
473
|
+
// advanceMission 에 닿기 전(근거 인자 검사·기록)에 멈춘 호출도 한 줄 — 관측이 «호출마다» 남는다.
|
|
474
|
+
// advanceMission 안에서 던진 것은 거기서 이미 남겼다(같은 오류를 두 번 적지 않는다).
|
|
475
|
+
if (!(error instanceof MissionAdvanceError)) {
|
|
476
|
+
try { debug.log('task-agent', 'mission-advance', { missionId: missionId ?? null, all: opts.all === true, mode: opts.live ? 'live' : 'shadow', stage: 'cli-args', error: reason, ms: Date.now() - started }); } catch { /* fail-soft */ }
|
|
477
|
+
}
|
|
478
|
+
out(reason);
|
|
479
|
+
process.exitCode = 1;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
127
483
|
tasks.command('show <id>')
|
|
128
|
-
.description(
|
|
484
|
+
.description(`태스크 상세 보기 (id 가 \`${TASK_CARD_PREFIX}\` 로 시작하면 TASK-AGENT 과제 카드 ⊕ 다음 수)`)
|
|
129
485
|
.option('--json', 'JSON 출력')
|
|
130
486
|
.action(async (id: string, opts: { json?: boolean }) => {
|
|
487
|
+
if (id.startsWith(TASK_CARD_PREFIX)) {
|
|
488
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
489
|
+
let card: TaskCard | undefined;
|
|
490
|
+
try {
|
|
491
|
+
card = readTaskCard(id, deps.taskStatePath);
|
|
492
|
+
// 런 id 가 있으면 원장에서 골 id · PR 을 묶는다(찾았을 때만 적는다 · 실패는 그대로 보인다).
|
|
493
|
+
const bindable = card?.pieces ? card.pieces : card ? [card.id] : [];
|
|
494
|
+
for (const cardId of bindable) {
|
|
495
|
+
try { await refreshCardRunBinding(cardId, deps.taskStatePath ?? taskStatePathDefault(), deps.cardRunEvidence ?? {}); } catch (error) {
|
|
496
|
+
// 근거 없음으로 보인다 — 원장 부재와 갈리게 실패는 남긴다.
|
|
497
|
+
try { debug.log('task-agent', 'card-bind-failed', { card: cardId, via: 'show', error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (card) card = readTaskCard(id, deps.taskStatePath);
|
|
501
|
+
} catch (error) {
|
|
502
|
+
out(error instanceof Error ? error.message : '과제 카드 읽기 실패');
|
|
503
|
+
process.exitCode = 1;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (!card) { out(`과제 카드 없음: ${id}`); process.exitCode = 1; return; }
|
|
507
|
+
if (card.pieces) {
|
|
508
|
+
let all: Record<string, TaskCard>;
|
|
509
|
+
try { all = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(deps.taskStatePath ?? taskStatePathDefault()).tasks ?? {}; } catch (error) {
|
|
510
|
+
out(error instanceof Error ? error.message : '과제 카드 읽기 실패');
|
|
511
|
+
process.exitCode = 1;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const pieces = card.pieces.map((pieceId) => all[pieceId]).filter((piece): piece is TaskCard => piece !== undefined);
|
|
515
|
+
const missingPieces = card.pieces.filter((pieceId) => !all[pieceId]);
|
|
516
|
+
if (opts.json) {
|
|
517
|
+
out(JSON.stringify({ card, nextMove: nextMoveFor(card), pieces: pieces.map((piece) => ({ card: piece, nextMove: nextMoveFor(piece) })), missingPieces }));
|
|
518
|
+
if (missingPieces.length) process.exitCode = 1;
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
for (const line of missionLines(card, pieces)) out(line);
|
|
522
|
+
for (const missing of missingPieces) { out(`조각 카드 없음: ${missing} — 선후 기록이 불완전하다`); process.exitCode = 1; }
|
|
523
|
+
}
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (opts.json) out(JSON.stringify({ card, nextMove: nextMoveFor(card) }));
|
|
527
|
+
else for (const line of taskCardLines(card)) out(line);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
131
530
|
try {
|
|
132
531
|
activeToken = undefined;
|
|
133
532
|
const auth = authenticate();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
1
|
+
import { closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
2
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { listMemories, memoryRoot, saveMemory, type MemoryEntry } from '../memory.js';
|
|
5
5
|
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
6
|
+
import { debug } from '../debug/log.js';
|
|
6
7
|
import { condenseContextWindowWithReport, readCondenseEvents, type CondenseFunnel, type CondenseSkipped, type MemoryDeps, type MemoryItem, type MemorySource, type MemorySummary } from './long-term-memory.js';
|
|
7
8
|
|
|
8
9
|
const summarize = async (source: MemorySource): Promise<MemorySummary> =>
|
|
@@ -51,6 +52,7 @@ export function renderCondenseMarkdown(report: Pick<CondenseReport, 'since' | 'u
|
|
|
51
52
|
|
|
52
53
|
type CondenseStore = {
|
|
53
54
|
list(): MemoryEntry[];
|
|
55
|
+
hasMemoryDirectory?(): boolean;
|
|
54
56
|
save(input: Parameters<typeof saveMemory>[0]): MemoryEntry;
|
|
55
57
|
raise(input: Parameters<DecisionLedger['raiseOnce']>[0], ref: string): string;
|
|
56
58
|
withLock<T>(action: () => T): T;
|
|
@@ -96,6 +98,7 @@ function defaultStore(root?: string): CondenseStore {
|
|
|
96
98
|
const ledger = new DecisionLedger(root ? { stateDir: root } : {});
|
|
97
99
|
return {
|
|
98
100
|
list: () => listMemories({}, memoryDir),
|
|
101
|
+
hasMemoryDirectory: () => existsSync(memoryDir),
|
|
99
102
|
save: input => saveMemory(input, memoryDir),
|
|
100
103
|
raise: (input, ref) => ledger.raiseOnce(input, ref).id,
|
|
101
104
|
withLock: action => withMemoryLock(memoryDir, action),
|
|
@@ -118,10 +121,22 @@ function newestActive(entries: readonly MemoryEntry[]): MemoryEntry | undefined
|
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
/** Apply candidate cards through the existing curated memory and decision ledger paths. */
|
|
121
|
-
export function applyCondensedCards(cards: readonly MemoryItem[], store: CondenseStore): NonNullable<CondenseReport['applied']> {
|
|
124
|
+
export function applyCondensedCards(cards: readonly MemoryItem[], store: CondenseStore, now: Date = new Date()): NonNullable<CondenseReport['applied']> {
|
|
122
125
|
const decisions: string[] = [];
|
|
123
126
|
let written = 0;
|
|
124
127
|
let retired = 0;
|
|
128
|
+
const cutoff = now.getTime() - 30 * 24 * 3_600_000;
|
|
129
|
+
const retireAged = () => {
|
|
130
|
+
let agedRetired = 0;
|
|
131
|
+
for (const old of store.list()) {
|
|
132
|
+
if (old.contextCard?.status !== 'active' || !(Date.parse(old.contextCard.updatedAt) < cutoff)) continue;
|
|
133
|
+
store.save({ type: old.type, name: old.name, description: old.description, body: old.body, id: old.id,
|
|
134
|
+
pinned: false, priority: old.priority, contextCard: { ...old.contextCard, status: 'retired' } });
|
|
135
|
+
agedRetired++;
|
|
136
|
+
}
|
|
137
|
+
retired += agedRetired;
|
|
138
|
+
debug.log('context.condense', 'aged-out', { retired: agedRetired });
|
|
139
|
+
};
|
|
125
140
|
for (const card of cards) {
|
|
126
141
|
if (card.conflict) {
|
|
127
142
|
const ref = `context-condense:${JSON.stringify([card.project, card.seat, card.topic, card.source, card.updatedAt])}`;
|
|
@@ -162,6 +177,7 @@ export function applyCondensedCards(cards: readonly MemoryItem[], store: Condens
|
|
|
162
177
|
};
|
|
163
178
|
store.withLock(applyTopic);
|
|
164
179
|
}
|
|
180
|
+
if (store.hasMemoryDirectory?.() !== false) store.withLock(retireAged);
|
|
165
181
|
return { written, retired, decisions };
|
|
166
182
|
}
|
|
167
183
|
|
|
@@ -184,6 +200,6 @@ export async function runContextCondense(options: {
|
|
|
184
200
|
const conflicts = cards.filter(card => card.conflict !== undefined).length;
|
|
185
201
|
const report: CondenseReport = { since, until, cards, conflicts, skipped, folded, droppedHarnessChild, funnel,
|
|
186
202
|
markdown: renderCondenseMarkdown({ since, until, cards, conflicts, skipped, droppedHarnessChild, funnel }) };
|
|
187
|
-
if (options.apply === true) report.applied = applyCondensedCards(cards, store ?? defaultStore(options.root));
|
|
203
|
+
if (options.apply === true) report.applied = applyCondensedCards(cards, store ?? defaultStore(options.root), now);
|
|
188
204
|
return report;
|
|
189
205
|
}
|
|
@@ -12,6 +12,16 @@ export interface ContextDayRow {
|
|
|
12
12
|
status: '들어옴' | '안 들어옴';
|
|
13
13
|
}
|
|
14
14
|
export interface ContextDayReport { since: string; until: string; rows: ContextDayRow[] }
|
|
15
|
+
export interface ContextDayTimelineEvent {
|
|
16
|
+
at: string;
|
|
17
|
+
source: ContextDaySource;
|
|
18
|
+
kind: ContextDayKind | 'other';
|
|
19
|
+
summary: string;
|
|
20
|
+
link: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface ContextDayTimelineReport { since: string; until: string; events: ContextDayTimelineEvent[] }
|
|
23
|
+
interface JournalEvent { surface: string; kind: string; ts: string; refs: string | null; summary: string | null }
|
|
24
|
+
type DayDeps = { now?: () => Date; dbPath?: string; seat?: ContextDaySource };
|
|
15
25
|
|
|
16
26
|
/** Reject malformed windows instead of silently changing the time range being judged. */
|
|
17
27
|
export function contextDayHours(since: string): number {
|
|
@@ -34,43 +44,86 @@ function kindFor(surface: string, kind: string): ContextDayKind | null {
|
|
|
34
44
|
return null;
|
|
35
45
|
}
|
|
36
46
|
|
|
47
|
+
function sourceFor(event: JournalEvent): { source: ContextDaySource; refs: { url?: unknown; source?: unknown } } | null {
|
|
48
|
+
let refs: { seat?: unknown; origin?: unknown; url?: unknown; source?: unknown };
|
|
49
|
+
try { refs = JSON.parse(event.refs ?? '{}') as typeof refs; } catch { return null; }
|
|
50
|
+
if (!refs || typeof refs !== 'object') return null;
|
|
51
|
+
const source = event.surface === 'context:external' ? refs.origin : refs.seat;
|
|
52
|
+
if (typeof source !== 'string' || !CONTEXT_DAY_SOURCES.some(item => item === source)) return null;
|
|
53
|
+
const isExternal = CONTEXT_DAY_SOURCES.indexOf(source as ContextDaySource) >= 4;
|
|
54
|
+
// Do not attribute an external hook to a seat (or a seat event to an external origin).
|
|
55
|
+
if ((event.surface === 'context:external') !== isExternal) return null;
|
|
56
|
+
return { source: source as ContextDaySource, refs };
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
/** Read only the context journal, not recallEvents (which mutates recall counts) or the writable DB opener. */
|
|
38
|
-
|
|
60
|
+
function readDay(since: string, deps: DayDeps): { since: string; until: string; events: JournalEvent[] } {
|
|
39
61
|
const hours = contextDayHours(since);
|
|
40
62
|
const now = (deps.now ?? (() => new Date()))();
|
|
41
63
|
const until = now.toISOString();
|
|
42
64
|
const from = new Date(now.getTime() - hours * 3_600_000).toISOString();
|
|
43
|
-
const rows: ContextDayRow[] = CONTEXT_DAY_SOURCES.map(source => ({
|
|
44
|
-
source, counts: { 'task-claimed': 0, done: 0, asked: 0, 'guidance-changed': 0 }, lastAt: null, status: '안 들어옴',
|
|
45
|
-
}));
|
|
46
|
-
const bySource = new Map(rows.map(row => [row.source, row]));
|
|
47
65
|
const path = deps.dbPath ?? surfaceEventsDbPath();
|
|
48
|
-
if (!existsSync(path)) return { since: from, until,
|
|
66
|
+
if (!existsSync(path)) return { since: from, until, events: [] };
|
|
49
67
|
const db = new Database(path, { readonly: true });
|
|
50
68
|
try {
|
|
51
|
-
const events = db.prepare(`SELECT surface, kind, ts, refs FROM events
|
|
69
|
+
const events = db.prepare(`SELECT surface, kind, ts, refs, summary FROM events
|
|
52
70
|
WHERE surface IN ('coord:channel', 'context:session', 'context:external')
|
|
53
|
-
AND direction='outbound' AND ts>=? AND ts<=? ORDER BY ts ASC`).all(from, until) as
|
|
54
|
-
|
|
55
|
-
}>;
|
|
56
|
-
for (const event of events) {
|
|
57
|
-
let refs: { seat?: unknown; origin?: unknown };
|
|
58
|
-
try { refs = JSON.parse(event.refs ?? '{}') as typeof refs; } catch { continue; }
|
|
59
|
-
const source = event.surface === 'context:external' ? refs.origin : refs.seat;
|
|
60
|
-
if (typeof source !== 'string') continue;
|
|
61
|
-
const row = bySource.get(source as ContextDaySource);
|
|
62
|
-
if (!row) continue;
|
|
63
|
-
// Do not attribute an external hook to a seat (or a seat event to an external origin).
|
|
64
|
-
if ((event.surface === 'context:external') !== (rows.indexOf(row) >= 4)) continue;
|
|
65
|
-
row.lastAt = event.ts;
|
|
66
|
-
row.status = '들어옴';
|
|
67
|
-
const kind = kindFor(event.surface, event.kind);
|
|
68
|
-
if (kind) row.counts[kind]++;
|
|
69
|
-
}
|
|
71
|
+
AND direction='outbound' AND ts>=? AND ts<=? ORDER BY ts ASC`).all(from, until) as JournalEvent[];
|
|
72
|
+
return { since: from, until, events };
|
|
70
73
|
} finally { db.close(); }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function contextDay(since = '24h', deps: { now?: () => Date; dbPath?: string } = {}): ContextDayReport {
|
|
77
|
+
const { since: from, until, events } = readDay(since, deps);
|
|
78
|
+
const rows: ContextDayRow[] = CONTEXT_DAY_SOURCES.map(source => ({
|
|
79
|
+
source, counts: { 'task-claimed': 0, done: 0, asked: 0, 'guidance-changed': 0 }, lastAt: null, status: '안 들어옴',
|
|
80
|
+
}));
|
|
81
|
+
const bySource = new Map(rows.map(row => [row.source, row]));
|
|
82
|
+
for (const event of events) {
|
|
83
|
+
const attribution = sourceFor(event);
|
|
84
|
+
if (!attribution) continue;
|
|
85
|
+
const row = bySource.get(attribution.source)!;
|
|
86
|
+
row.lastAt = event.ts;
|
|
87
|
+
row.status = '들어옴';
|
|
88
|
+
const kind = kindFor(event.surface, event.kind);
|
|
89
|
+
if (kind) row.counts[kind]++;
|
|
90
|
+
}
|
|
71
91
|
return { since: from, until, rows };
|
|
72
92
|
}
|
|
73
93
|
|
|
94
|
+
export function contextDayTimeline(since = '24h', deps: DayDeps = {}): ContextDayTimelineReport {
|
|
95
|
+
if (deps.seat !== undefined && !CONTEXT_DAY_SOURCES.some(source => source === deps.seat)) {
|
|
96
|
+
throw new Error(`unknown --seat: ${deps.seat}`);
|
|
97
|
+
}
|
|
98
|
+
const { since: from, until, events } = readDay(since, deps);
|
|
99
|
+
const timeline: ContextDayTimelineEvent[] = [];
|
|
100
|
+
for (const event of events) {
|
|
101
|
+
const attribution = sourceFor(event);
|
|
102
|
+
if (!attribution || (deps.seat && attribution.source !== deps.seat)) continue;
|
|
103
|
+
const { url, source } = attribution.refs;
|
|
104
|
+
const link = [url, source].find(value => typeof value === 'string' && /^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(value));
|
|
105
|
+
timeline.push({ at: event.ts, source: attribution.source, kind: kindFor(event.surface, event.kind) ?? 'other',
|
|
106
|
+
summary: event.summary ?? '', link: typeof link === 'string' ? link : null });
|
|
107
|
+
}
|
|
108
|
+
return { since: from, until, events: timeline };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const kstFormatter = new Intl.DateTimeFormat('en-CA', {
|
|
112
|
+
timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
|
|
113
|
+
});
|
|
114
|
+
function kst(at: string): string {
|
|
115
|
+
const parts = Object.fromEntries(kstFormatter.formatToParts(new Date(at)).map(part => [part.type, part.value]));
|
|
116
|
+
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute} KST`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function renderContextDayTimeline(report: ContextDayTimelineReport, dbPath = surfaceEventsDbPath()): string {
|
|
120
|
+
const lines = [`맥락 사건 ${kst(report.since)} ~ ${kst(report.until)}`];
|
|
121
|
+
if (!report.events.length) lines.push(`이 창에 사건 0건 — 원장 경로: ${dbPath}`);
|
|
122
|
+
else lines.push(...report.events.map(event => `${kst(event.at).slice(11)} · ${event.source} · ${event.kind} · ${event.summary.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, ' ')} · ${event.link ?? '—'}`));
|
|
123
|
+
lines.push(`${report.events.length}건 · 출처 ${new Set(report.events.map(event => event.source)).size}곳`);
|
|
124
|
+
return lines.join('\n');
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
export function renderContextDay(report: ContextDayReport): string {
|
|
75
128
|
return [
|
|
76
129
|
`맥락 이벤트 ${report.since} ~ ${report.until} (UTC)`,
|