elanous 0.2.20 → 0.2.21
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/MObU8ujkMciAKr6ljAV05/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
- package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
- package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
- package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
- package/apps/pwa/out/_next/static/chunks/{1307-77fad80cf54477c6.js → 1307-c97eba9fb72a9a5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
- package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
- package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
- package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
- package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
- package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
- package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
- package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
- package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
- package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
- package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
- package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
- package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{6158.af2e93b7adff709b.js → 6158.566c890adc06a459.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
- package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
- package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
- package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
- package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
- package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
- package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
- package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
- package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
- package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
- package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
- package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d781be3e3a9cfd74.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-b0a80997903852d8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-2b1263165630d9c8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-7a852956c74015a3.js → page-ea998f7f5d156bea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-e2d6bed48ac20120.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-111cabe4d1fd69ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-544dd4bd4847eeb7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-3ee191319bf97d87.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-a449f1ed472b81c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-9045094d6d3b5cd0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-7a12f61be4a579d1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-f6055495e9ed7ea8.js → page-8cc707bd09a497d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-dc39ee7a36d26385.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-84ae759f76053672.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-3db58ddb61fe2be9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-2fe368efb6de3b6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-c26fa10aac6c21c4.js → page-8d04132dd0ac5e10.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-68793c6e10331d66.js → page-f23484de467c9ea3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-c69762987b751174.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-be1c4e8697f22360.js → page-d75f7cd65845ce98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-0fd895fab9b62c1b.js → page-a6eb73b8a4840f1c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-c6e4364d9de8f717.js → page-6c0b88068c143f15.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-7f8d7fcdf058a845.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-22163556cd38bc88.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
- package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
- package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
- package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/live-run/index.html +1 -0
- package/apps/pwa/out/live-run/index.txt +35 -0
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/drafts/recipes.yaml +4 -4
- package/graphs/implement-loop.yaml +277 -5
- package/package.json +1 -1
- package/scripts/coord-channel-watch.sh +31 -4
- package/scripts/docs-lint.ts +4 -1
- package/scripts/marketing/generate-ax-brief.ts +18 -1
- package/scripts/measure-nl-routing-corpus.ts +11 -3
- package/scripts/release-loop/gate-node.ts +5 -5
- package/scripts/release-loop/shard-plan.ts +50 -5
- package/scripts/rhythm/daily.ts +2 -1
- package/src/browser-cdp/client.ts +10 -5
- package/src/cli/ops-cli.ts +11 -0
- package/src/cli/ops-overview-cli.ts +50 -0
- package/src/cli/pod-cli.ts +1 -0
- package/src/cli/tasks-cli.ts +106 -0
- package/src/dashboard/index.ts +40 -4
- package/src/esc-abort-gate.ts +3 -1
- package/src/graph-runner/graph-cli.ts +57 -0
- package/src/graph-runner/graph-run-api.ts +13 -0
- package/src/graph-wizard/archetypes.ts +88 -0
- package/src/graph-wizard/generate.ts +545 -0
- package/src/graph-wizard/saved-steps.ts +120 -0
- package/src/graph-wizard/steps.ts +193 -0
- package/src/harness/harness-cli-command.ts +68 -12
- package/src/index.ts +7 -4
- package/src/intake-plane/digest.ts +18 -11
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/nexus/api/draft-metrics.ts +114 -0
- package/src/nexus/api/graph-wizard-api.ts +53 -0
- package/src/nexus/api/graphs-api.ts +50 -12
- package/src/nexus/api/harness-traversal.ts +205 -0
- package/src/nexus/api/http-server.ts +31 -0
- package/src/nexus/outbound/router.ts +28 -8
- package/src/onboarding/first-run.ts +3 -0
- package/src/ops/overview-collect.ts +345 -0
- package/src/ops/overview-seat-era.ts +90 -0
- package/src/ops/overview.ts +223 -0
- package/src/release-loop/gate-shards.ts +7 -2
- package/src/self-dev/ask-launch-flow.ts +4 -2
- package/src/self-dev/ask-launch-io.ts +3 -1
- package/src/self-dev/graph-journey-nodes.ts +157 -0
- package/src/self-dev/graph-journey-route.ts +105 -0
- package/src/self-dev/launch-preflight.ts +43 -3
- package/src/self-dev/release-path-guard.ts +7 -4
- package/src/self-implement/frozen-merges.ts +23 -0
- package/src/self-implement/goal-author.ts +14 -0
- package/src/self-implement/heal-intake.ts +3 -0
- package/src/self-implement/orchestrator.ts +16 -1
- package/src/self-implement/seams.ts +3 -0
- package/src/self-implement/self-implement-runtime.ts +12 -0
- package/src/session-runtime/index.ts +5 -3
- package/src/shell-env-bootstrap.ts +17 -3
- package/src/task-agent/card-evidence.ts +6 -0
- package/src/task-agent/parent-host.ts +315 -0
- package/src/task-agent/task-hand.ts +9 -2
- package/src/task-orchestrator/surfaces/pod-command-job.ts +41 -6
- package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
- package/src/tool-runtime/browser-runtime.ts +87 -3
- package/src/tool-runtime/index.ts +1 -0
- package/src/user-config.ts +20 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.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/2116-6429c188eb846ab8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +0 -25
- package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-c855c24de7db74fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f9ccb1b1adbea14d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +0 -1
- package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +0 -1
- /package/apps/pwa/out/_next/static/{Bam1YziK2iH9bN4K0utGk → MObU8ujkMciAKr6ljAV05}/_ssgManifest.js +0 -0
- /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
// `{id, method, params}` messages, receive `{id, result}` or
|
|
8
8
|
// `{id, error}`.
|
|
9
9
|
//
|
|
10
|
-
// Scope kept narrow:
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// describes. Full Puppeteer feature parity is explicitly out of
|
|
14
|
-
// scope.
|
|
10
|
+
// Scope kept narrow: navigation, capture, evaluation and CDP input.
|
|
11
|
+
// Approval of mutating actions belongs to the caller, not this transport.
|
|
12
|
+
// Full Puppeteer feature parity is explicitly out of scope.
|
|
15
13
|
|
|
16
14
|
import { CHROME_NO_KEYCHAIN_FLAGS } from './chrome-flags.js';
|
|
17
15
|
import { spawn, type ChildProcess } from 'node:child_process';
|
|
@@ -126,6 +124,8 @@ export interface CdpClient {
|
|
|
126
124
|
setScriptExecutionDisabled(value: boolean): Promise<void>;
|
|
127
125
|
/** Dispatch one primary-button click at viewport coordinates. */
|
|
128
126
|
click?(coordinates: { x: number; y: number }): Promise<void>;
|
|
127
|
+
/** Insert text at the focused element (approval is the caller's responsibility). */
|
|
128
|
+
input?(text: string): Promise<void>;
|
|
129
129
|
/** Dispose — closes Chrome + WebSocket. */
|
|
130
130
|
close(): Promise<void>;
|
|
131
131
|
readonly isAlive: boolean;
|
|
@@ -513,6 +513,11 @@ async function buildClientFromTransport(
|
|
|
513
513
|
await runOnPage('Input.dispatchMouseEvent', { type: 'mousePressed', ...params });
|
|
514
514
|
await runOnPage('Input.dispatchMouseEvent', { type: 'mouseReleased', ...params });
|
|
515
515
|
},
|
|
516
|
+
async input(text) {
|
|
517
|
+
// Newlines can submit a form even without an Enter key event.
|
|
518
|
+
if (/[\r\n]/.test(text)) throw new Error('CDP input refuses newlines');
|
|
519
|
+
await runOnPage('Input.insertText', { text });
|
|
520
|
+
},
|
|
516
521
|
async close() {
|
|
517
522
|
if (!alive) return;
|
|
518
523
|
alive = false;
|
package/src/cli/ops-cli.ts
CHANGED
|
@@ -213,6 +213,17 @@ opsCmd.command('status').description('현재 상태 종합(미션·태스크·
|
|
|
213
213
|
}
|
|
214
214
|
return o.allInstances ? runOpsFleet(o.json === true, o.includeTest === true) : runOps('snapshot', o);
|
|
215
215
|
});
|
|
216
|
+
// OPS-OVERVIEW-CLI (RFC-ops-overview-flow-health-cli-2026-10-08) — 판정 먼저 · 이유 사슬 · 절. 못 잼 = null(0 아님).
|
|
217
|
+
opsCmd.command('overview').description('흐름 한 장 — 저작→구현→착지가 흐르나(판정 먼저·이유 사슬) · 판·일꾼·기계·착지·루프 · 못 잼은 null · exit 0=흐름 정상/저하 3=그 밖 1=수집 실패')
|
|
218
|
+
.option('--json', '칸 꼴 그대로 ⊕ 머리 {verdict, reasons, nextActions, meta.elapsedMs}')
|
|
219
|
+
.option('--watch [seconds]', '주기 재조회(기본 30초)')
|
|
220
|
+
.option('--since <window>', '착지 창(<n>m|h|d · 기본 1h)', '1h')
|
|
221
|
+
.option('--owner <owner>', '일꾼 절 거르기 — seat:TC · TC · unassigned · all')
|
|
222
|
+
.option('--version <v>', '판(기본: 다음 컷)')
|
|
223
|
+
.action(async (o: { json?: boolean; watch?: string | boolean; since?: string; owner?: string; version?: string }) => {
|
|
224
|
+
const { runOpsOverview } = await import('./ops-overview-cli.js');
|
|
225
|
+
process.exitCode = await runOpsOverview(o);
|
|
226
|
+
});
|
|
216
227
|
opsCmd.command('health').description('이상 판정만(blocked·errored·stale)').option('--json')
|
|
217
228
|
.action((o: OpsOpts) => runOps('health', o));
|
|
218
229
|
opsCmd.command('timeline').description('상태 전이 최근순 통합')
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { collectOverview, parseSince } from '../ops/overview-collect.js';
|
|
3
|
+
import { computeVerdict, renderOverview, unmeasuredCells, verdictExitCode, VERDICT_LABEL } from '../ops/overview.js';
|
|
4
|
+
import { writeStdoutJson } from './stdout-json.js';
|
|
5
|
+
|
|
6
|
+
export interface OpsOverviewOptions { json?: boolean; watch?: string | boolean; since?: string; owner?: string; version?: string }
|
|
7
|
+
|
|
8
|
+
/** 한 번 모아서 내고 exit code 를 돌려준다(RFC §A4: flowing·degraded 0 · 그 밖 3 · 수집 실패 1). */
|
|
9
|
+
async function once(o: OpsOverviewOptions, previous: string | null): Promise<{ code: number; verdict: string | null }> {
|
|
10
|
+
const started = performance.now();
|
|
11
|
+
let since: { ms: number; label: string };
|
|
12
|
+
try { since = parseSince(o.since); } catch (error) { console.error(String((error as Error).message)); return { code: 1, verdict: null }; }
|
|
13
|
+
try {
|
|
14
|
+
const snapshot = await collectOverview({ sinceMs: since.ms, sinceLabel: since.label, ...(o.owner ? { owner: o.owner } : {}), ...(o.version ? { version: o.version } : {}) });
|
|
15
|
+
const result = computeVerdict(snapshot);
|
|
16
|
+
const elapsedMs = Math.round(performance.now() - started);
|
|
17
|
+
const unmeasured = unmeasuredCells(snapshot);
|
|
18
|
+
debug.log('ops.overview', 'computed', { verdict: result.verdict, elapsedMs, unmeasured });
|
|
19
|
+
debug.flush();
|
|
20
|
+
if (o.json) {
|
|
21
|
+
await writeStdoutJson(JSON.stringify({ verdict: result.verdict, verdictLabel: VERDICT_LABEL[result.verdict], reasons: result.reasons, nextActions: result.nextActions,
|
|
22
|
+
meta: { elapsedMs, source: 'local', unmeasured }, ...snapshot }, null, 2) + '\n');
|
|
23
|
+
} else {
|
|
24
|
+
const text = renderOverview(snapshot, result, { elapsedMs });
|
|
25
|
+
if (previous !== null && previous !== result.verdict) console.log(`🔔 판정 바뀜: ${previous} → ${result.verdict}`);
|
|
26
|
+
console.log(text);
|
|
27
|
+
}
|
|
28
|
+
return { code: verdictExitCode(result.verdict), verdict: result.verdict };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
debug.log('ops.overview', 'failed', { reason: String(error) });
|
|
31
|
+
debug.flush();
|
|
32
|
+
console.error(`ops overview 수집 실패: ${String(error)}`);
|
|
33
|
+
return { code: 1, verdict: null };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runOpsOverview(o: OpsOverviewOptions): Promise<number> {
|
|
38
|
+
// 독립 CLI 프로세스는 데몬 StoreSink 를 물려받지 않는다 — 등록 없이는 debug.log 가 logs.db 에 안 닿는다(관측 안 한 것).
|
|
39
|
+
try { await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('cli'); } catch { /* fail-open */ }
|
|
40
|
+
if (o.watch === undefined || o.watch === false) return (await once(o, null)).code;
|
|
41
|
+
const seconds = o.watch === true ? 30 : Number(o.watch);
|
|
42
|
+
if (!Number.isFinite(seconds) || seconds < 5) { console.error('--watch 는 5초 이상'); return 1; }
|
|
43
|
+
let previous: string | null = null;
|
|
44
|
+
for (;;) {
|
|
45
|
+
if (!o.json) process.stdout.write('\x1b[2J\x1b[H');
|
|
46
|
+
const r = await once(o, previous);
|
|
47
|
+
previous = r.verdict ?? previous;
|
|
48
|
+
await new Promise((res) => setTimeout(res, seconds * 1000));
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/cli/pod-cli.ts
CHANGED
|
@@ -151,6 +151,7 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
|
|
|
151
151
|
try {
|
|
152
152
|
const result = await run({
|
|
153
153
|
command: commandArgs,
|
|
154
|
+
returnLogs: true,
|
|
154
155
|
...(opts.pool ? { pool: opts.pool } : {}),
|
|
155
156
|
...(skills.length ? { skills } : {}),
|
|
156
157
|
...(opts.llm === 'grok' ? { llm: 'grok' as const } : {}),
|
package/src/cli/tasks-cli.ts
CHANGED
|
@@ -34,6 +34,10 @@ export interface TasksCliDeps {
|
|
|
34
34
|
releaseBoard?: () => BoardNode[];
|
|
35
35
|
/** `tasks show`·`advance` 의 런 근거 읽기(시험 주입) — 기본 = 런 원장(이 우주 ⊕ 연합) ⊕ 호스트 logs 의 Pod job-finished. */
|
|
36
36
|
cardRunEvidence?: CardRunEvidenceDeps;
|
|
37
|
+
/** HARNESS-PARENT-ON-MSB1 — 원격 부모 카드의 원장 거울(시험 주입). 기본 = ssh 로 `<prodRoot>/remote-parents/<host>/run-ledger`. */
|
|
38
|
+
remoteLedgerMirror?: (card: TaskCard) => void;
|
|
39
|
+
/** `tasks parents` 의 낡은 부모 보고 의존(시험 주입). */
|
|
40
|
+
staleParents?: import('../task-agent/parent-host.js').StaleParentDeps;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
/** `<조각id>=<값>` 반복 인자. */
|
|
@@ -97,6 +101,60 @@ export interface DefaultTaskLauncherDeps {
|
|
|
97
101
|
writeStop?: import('../self-implement/run-ledger.js').RunLedgerWriter;
|
|
98
102
|
/** 이미 stop 이 있나(시험 주입). 기본 = 이 우주 런 원장 ⊕ 이 프로세스 기록. */
|
|
99
103
|
hasStop?: (runId: string) => boolean;
|
|
104
|
+
/** HARNESS-PARENT-ON-MSB1 — 원격 부모 대상(시험 주입). undefined = 설정·env 에서 푼다 · null = 끔. */
|
|
105
|
+
parentHost?: import('../task-agent/parent-host.js').ParentHostConfig | null;
|
|
106
|
+
/** 원격 ssh(시험 주입). */
|
|
107
|
+
ssh?: import('../task-agent/parent-host.js').SshRunner;
|
|
108
|
+
/** HQ 발사 동결(holdLaunches) 여부(시험 주입). */
|
|
109
|
+
launchHoldActive?: () => boolean;
|
|
110
|
+
/** HQ 판(시험 주입). */
|
|
111
|
+
localVersion?: string;
|
|
112
|
+
/** 원격 → 로컬 되돌림 알림(시험 주입). 기본 = stderr 한 줄. */
|
|
113
|
+
notice?: (line: string) => void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* HARNESS-PARENT-ON-MSB1 — 켜져 있고 발사 전 점검이 통과하면 원격(ssh)에서 부모를 띄운다(PR 까지만 · 병합은 HQ).
|
|
118
|
+
* 막히면 null — 호출부가 종전 로컬 발사로 간다(관측: task-agent parent-host-fallback).
|
|
119
|
+
*/
|
|
120
|
+
async function tryRemoteParent(args: string[], cwd: string | undefined, context: TaskLaunchContext, deps: DefaultTaskLauncherDeps): Promise<TaskLaunchReceipt | null> {
|
|
121
|
+
const parentHostModule = await import('../task-agent/parent-host.js');
|
|
122
|
+
let target = deps.parentHost;
|
|
123
|
+
if (target === undefined) {
|
|
124
|
+
const { getUserConfig } = await import('../user-config.js');
|
|
125
|
+
let configured: Parameters<typeof parentHostModule.resolveParentHost>[0];
|
|
126
|
+
try { configured = getUserConfig().taskAgent?.parentHost; } catch { configured = undefined; }
|
|
127
|
+
target = parentHostModule.resolveParentHost(configured);
|
|
128
|
+
}
|
|
129
|
+
if (!target) return null;
|
|
130
|
+
const notice = deps.notice ?? ((line: string) => { try { process.stderr.write(`${line}\n`); } catch { /* ignore */ } });
|
|
131
|
+
const fallback = (gaps: string[], facts: Record<string, string> = {}): null => {
|
|
132
|
+
try { debug.log('task-agent', 'parent-host-fallback', { host: target!.host, runId: context.runId, gaps, facts }); } catch { /* fail-soft */ }
|
|
133
|
+
notice(`[parent-host] ${target!.host} 로 못 띄움 — 로컬 발사로 간다: ${gaps.join(' · ')}`);
|
|
134
|
+
return null;
|
|
135
|
+
};
|
|
136
|
+
if (cwd) return fallback(['project-target-is-local']);
|
|
137
|
+
let launchHoldActive = false;
|
|
138
|
+
try {
|
|
139
|
+
if (deps.launchHoldActive) launchHoldActive = deps.launchHoldActive();
|
|
140
|
+
else {
|
|
141
|
+
const { readLandingFreeze } = await import('../release-loop/landing-freeze.js');
|
|
142
|
+
const { prodInstanceRoot } = await import('../instance/resolve.js');
|
|
143
|
+
launchHoldActive = readLandingFreeze(prodInstanceRoot())?.holdLaunches === true;
|
|
144
|
+
}
|
|
145
|
+
} catch { launchHoldActive = true; /* 못 읽으면 막힌 것으로(fail-closed) */ }
|
|
146
|
+
const preflight = parentHostModule.preflightParentHost({ target, localVersion: deps.localVersion ?? parentHostModule.localElanousVersion(), launchHoldActive, ...(deps.ssh ? { ssh: deps.ssh } : {}) });
|
|
147
|
+
if (!preflight.ok) return fallback(preflight.gaps, preflight.facts);
|
|
148
|
+
try {
|
|
149
|
+
const launched = parentHostModule.launchRemoteParent(target, args, context.env, deps.ssh);
|
|
150
|
+
try { debug.log('task-agent', 'parent-host-launched', { host: target.host, runId: context.runId, pid: launched.pid, log: launched.log }); } catch { /* fail-soft */ }
|
|
151
|
+
return { runId: context.runId, parentHost: { host: target.host, ...(target.configDir ? { configDir: target.configDir } : {}), pid: launched.pid, log: launched.log } };
|
|
152
|
+
} catch (error) {
|
|
153
|
+
// «확실히 안 떴다»일 때만 로컬로 — 불확실(ssh 끊김 등)은 던져 카드를 launch-failed 로 남긴다(중복 발사 방지).
|
|
154
|
+
if (error instanceof parentHostModule.RemoteParentNotLaunchedError) return fallback([error.message]);
|
|
155
|
+
try { debug.log('task-agent', 'parent-host-launch-uncertain', { host: target.host, runId: context.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
100
158
|
}
|
|
101
159
|
|
|
102
160
|
/** stderr 파일의 첫 비어 있지 않은 줄(최대 4KB 만 읽는다 — 원 로그 꼬리를 싣지 않는다). */
|
|
@@ -112,6 +170,10 @@ function firstStderrLine(path: string): string {
|
|
|
112
170
|
}
|
|
113
171
|
|
|
114
172
|
export async function defaultTaskLauncher(args: string[], cwd?: string, context?: TaskLaunchContext, deps: DefaultTaskLauncherDeps = {}): Promise<void | TaskLaunchReceipt> {
|
|
173
|
+
if (context) {
|
|
174
|
+
const remote = await tryRemoteParent(args, cwd, context, deps);
|
|
175
|
+
if (remote) return remote;
|
|
176
|
+
}
|
|
115
177
|
const spawn = deps.spawn ?? ((await import('node:child_process')).spawn as unknown as DetachedSpawn);
|
|
116
178
|
const { getElanousConfigDirOverride } = await import('../elanous-config-dir.js');
|
|
117
179
|
const { prodInstanceRoot } = await import('../instance/resolve.js');
|
|
@@ -208,6 +270,21 @@ export function spawnDetachedConfirmed(spawnFn: DetachedSpawn, command: string,
|
|
|
208
270
|
});
|
|
209
271
|
}
|
|
210
272
|
|
|
273
|
+
/** HARNESS-PARENT-ON-MSB1 — 카드가 원격 부모면 그 런 원장(⊕ Pod 자식)을 HQ 거울로 당긴다. 실패는 관측만(«없음»으로 읽히지 않게). */
|
|
274
|
+
async function mirrorRemoteCardLedger(cardId: string, deps: TasksCliDeps): Promise<void> {
|
|
275
|
+
let card: TaskCard | undefined;
|
|
276
|
+
try { card = readTaskCard(cardId, deps.taskStatePath); } catch { return; }
|
|
277
|
+
if (!card?.parentHost || !card.runId) return;
|
|
278
|
+
try {
|
|
279
|
+
if (deps.remoteLedgerMirror) { deps.remoteLedgerMirror(card); return; }
|
|
280
|
+
const { mirrorRemoteRunLedgers } = await import('../task-agent/parent-host.js');
|
|
281
|
+
const { prodInstanceRoot } = await import('../instance/resolve.js');
|
|
282
|
+
mirrorRemoteRunLedgers(card.parentHost, card.runId, prodInstanceRoot());
|
|
283
|
+
} catch (error) {
|
|
284
|
+
try { debug.log('task-agent', 'remote-ledger-mirror-failed', { card: cardId, host: card.parentHost.host, runId: card.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
211
288
|
function moveLine(card: TaskCard): string {
|
|
212
289
|
const move = nextMoveFor(card);
|
|
213
290
|
return `${move.kind}${move.command ? ` — elanous ${move.command.map(shellQuote).join(' ')}` : ''} (${move.reason})`;
|
|
@@ -442,6 +519,32 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
442
519
|
}
|
|
443
520
|
});
|
|
444
521
|
|
|
522
|
+
tasks.command('parents')
|
|
523
|
+
.description('낡은 하니스 부모 보고(읽기 전용 · 죽이지 않는다) — 나이 ≥ N 시간이고 런 원장이 끝났거나(run-status) Pod 이 없는 `harness say|ask` 부모')
|
|
524
|
+
.option('--older-than <hours>', '나이 하한(시간)', '6')
|
|
525
|
+
.option('--no-pods', 'Pod 목록을 재지 않는다(원장 근거만)')
|
|
526
|
+
.option('--json', 'JSON 출력')
|
|
527
|
+
.action(async (opts: { olderThan: string; pods?: boolean; json?: boolean }) => {
|
|
528
|
+
const hours = Number(opts.olderThan);
|
|
529
|
+
if (!Number.isFinite(hours) || hours < 0) { out(`--older-than 는 0 이상 수다: ${opts.olderThan}`); process.exitCode = 1; return; }
|
|
530
|
+
const { staleHarnessParents } = await import('../task-agent/parent-host.js');
|
|
531
|
+
const injected = deps.staleParents ?? {};
|
|
532
|
+
const loadLedger = injected.loadLedger ?? ((runId: string) => loadRunLedgerSync(runId));
|
|
533
|
+
let podRuns = injected.podRuns;
|
|
534
|
+
let labelOf = injected.labelOf;
|
|
535
|
+
if (!podRuns && opts.pods !== false) {
|
|
536
|
+
const queue = await import('../harness/harness-queue.js');
|
|
537
|
+
podRuns = () => queue.readHarnessQueuePodRuns();
|
|
538
|
+
labelOf = queue.queueRunLabelValue;
|
|
539
|
+
}
|
|
540
|
+
const report = staleHarnessParents(hours, { ...injected, loadLedger, ...(podRuns ? { podRuns } : {}), ...(labelOf ? { labelOf } : {}) });
|
|
541
|
+
try { debug.log('task-agent', 'stale-parents', { olderThanHours: hours, scanned: report.scanned, stale: report.rows.length, podRunsMeasured: report.podRunsMeasured }); } catch { /* fail-soft */ }
|
|
542
|
+
if (opts.json) { out(JSON.stringify({ olderThanHours: hours, ...report })); return; }
|
|
543
|
+
out(`하니스 부모 ${report.scanned} · 나이 ≥ ${hours}h 중 낡음 후보 ${report.rows.length}${report.podRunsMeasured ? '' : ' · ⚠ Pod 목록 못 잼(원장 근거만)'} — 읽기 전용(죽이지 않는다)`);
|
|
544
|
+
out('pid\t나이h\t자리\t판\t런\t사유');
|
|
545
|
+
for (const row of report.rows) out([row.pid, row.ageHours, row.seat ?? '-', row.version ?? '-', row.runId ?? '-', row.reasons.join(',')].join('\t'));
|
|
546
|
+
});
|
|
547
|
+
|
|
445
548
|
tasks.command('board')
|
|
446
549
|
.description('보드 — 프로젝트 › 목표 › 이정표 › 과제. 과제 카드(project·goal·milestone)와 내부 팩(릴리스 체크리스트 = elanous › 판 › 칸)을 합쳐 읽는다(쓰지 않는다)')
|
|
447
550
|
.option('--project <id>', '이 프로젝트만')
|
|
@@ -569,6 +672,8 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
569
672
|
// 런 id 가 있으면 원장에서 골 id · PR 을 묶는다(찾았을 때만 적는다 · 실패는 그대로 보인다).
|
|
570
673
|
const bindable = card?.pieces ? card.pieces : card ? [card.id] : [];
|
|
571
674
|
for (const cardId of bindable) {
|
|
675
|
+
// HARNESS-PARENT-ON-MSB1: 원격 부모 런은 원장이 그 호스트에 있다 — HQ 거울로 먼저 당긴다(읽기 전용).
|
|
676
|
+
await mirrorRemoteCardLedger(cardId, deps);
|
|
572
677
|
try { await refreshCardRunBinding(cardId, deps.taskStatePath ?? taskStatePathDefault(), deps.cardRunEvidence ?? {}); } catch (error) {
|
|
573
678
|
// 근거 없음으로 보인다 — 원장 부재와 갈리게 실패는 남긴다.
|
|
574
679
|
try { debug.log('task-agent', 'card-bind-failed', { card: cardId, via: 'show', error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
@@ -608,6 +713,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
608
713
|
if (opts.json) out(JSON.stringify({ card, nextMove: nextMoveFor(card), salvageBranch: salvage.salvageBranch, ...(salvageUnread ? { salvageBranchUnread: true } : {}) }));
|
|
609
714
|
else {
|
|
610
715
|
for (const line of taskCardLines(card)) out(line);
|
|
716
|
+
if (card.parentHost) out(`부모 호스트: ${card.parentHost.host}${card.parentHost.pid ? ` (pid ${card.parentHost.pid})` : ''} · PR 까지만 — 병합은 HQ${card.parentHost.log ? ` · 로그 ${card.parentHost.host}:${card.parentHost.log}` : ''}`);
|
|
611
717
|
if (salvage.salvageBranch) out(`수확 가지: ${salvage.salvageBranch} (draft PR 대신)`);
|
|
612
718
|
else if (salvageUnread) out('수확 가지: 못 읽음(원장 읽기 실패)');
|
|
613
719
|
}
|
package/src/dashboard/index.ts
CHANGED
|
@@ -769,6 +769,15 @@ export function clearEscAbortToolState(toolState: EscAbortToolState): void {
|
|
|
769
769
|
toolState.activeToolNamesByCallId.clear();
|
|
770
770
|
}
|
|
771
771
|
|
|
772
|
+
export function activeEscHarnessSpace(
|
|
773
|
+
toolState: EscAbortToolState,
|
|
774
|
+
spacesByCallId: ReadonlyMap<string, string>,
|
|
775
|
+
): string | null {
|
|
776
|
+
const active = [...toolState.activeToolNamesByCallId].filter(([, name]) =>
|
|
777
|
+
(SELF_IMPLEMENT_TOOL_NAMES as readonly string[]).includes(name));
|
|
778
|
+
return active.length === 1 ? spacesByCallId.get(active[0]![0]) ?? null : null;
|
|
779
|
+
}
|
|
780
|
+
|
|
772
781
|
/** Reset per-turn ESC tool tracking before a new stream runtime begins. */
|
|
773
782
|
export function createDashboardTurnStreamRuntimeEscBoundary(toolState: EscAbortToolState): void {
|
|
774
783
|
clearEscAbortToolState(toolState);
|
|
@@ -1324,6 +1333,8 @@ import {
|
|
|
1324
1333
|
PULSE_HALF_PERIOD_MS,
|
|
1325
1334
|
} from '../agent-activity-hud.js';
|
|
1326
1335
|
import { DASHBOARD_CLARIFICATION_REQUEST, consumeStreamingTopModalEscape, createEscAbortGate, createHarnessEscHint, routeStreamingEscapeKey } from '../esc-abort-gate.js';
|
|
1336
|
+
import { enqueueSoftStop, readSoftStopRequestStatus } from '../harness/control-inbox.js';
|
|
1337
|
+
import { setSelfImplementSoftStopSpaceObserver } from '../self-implement/self-implement-runtime.js';
|
|
1327
1338
|
import { createViEditor, type ViEditorHandle } from '../vi-editor.js';
|
|
1328
1339
|
import { launchEditor, canLaunchEditor } from '../editor-launcher.js';
|
|
1329
1340
|
import { createSshPickerModal } from '../ssh/ssh-picker-modal.js';
|
|
@@ -6700,6 +6711,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6700
6711
|
* this function is invoked.
|
|
6701
6712
|
*/
|
|
6702
6713
|
const escAbortToolState = createEscAbortToolState();
|
|
6714
|
+
const escHarnessSpacesByCallId = new Map<string, string>();
|
|
6715
|
+
setSelfImplementSoftStopSpaceObserver((callId, spaceId) => {
|
|
6716
|
+
const toolName = escAbortToolState.activeToolNamesByCallId.get(callId);
|
|
6717
|
+
if (toolName && (SELF_IMPLEMENT_TOOL_NAMES as readonly string[]).includes(toolName)) {
|
|
6718
|
+
escHarnessSpacesByCallId.set(callId, spaceId);
|
|
6719
|
+
}
|
|
6720
|
+
});
|
|
6703
6721
|
let clarificationModalId: string | null = null;
|
|
6704
6722
|
|
|
6705
6723
|
const attachChatStreamingKeys = (abortCtrl: AbortController): () => void => {
|
|
@@ -6713,6 +6731,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6713
6731
|
// so the user can make an informed call.
|
|
6714
6732
|
let abortGateModalId: string | null = null;
|
|
6715
6733
|
const showHarnessEscHint = createHarnessEscHint((line) => pushChatLine(line));
|
|
6734
|
+
let stoppedHarnessSpace: string | null = null;
|
|
6735
|
+
const activeHarnessSpace = (): string | null => activeEscHarnessSpace(escAbortToolState, escHarnessSpacesByCallId);
|
|
6716
6736
|
const escGate = createEscAbortGate({
|
|
6717
6737
|
abortCtrl,
|
|
6718
6738
|
getRunningCount: () => getEscAbortRunningCount(escAbortToolState),
|
|
@@ -6726,14 +6746,25 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
6726
6746
|
requestRedraw: () => draw(),
|
|
6727
6747
|
getTheme: () => currentThemeTokens(),
|
|
6728
6748
|
getWaitingTargetNames: () => getEscAbortWaitingToolNames(escAbortToolState),
|
|
6749
|
+
shouldAbortImmediately: () => activeHarnessSpace() !== null,
|
|
6729
6750
|
onAbortPending: ({ targets }) => {
|
|
6730
|
-
|
|
6751
|
+
const spaceId = activeHarnessSpace();
|
|
6752
|
+
if (spaceId) {
|
|
6753
|
+
try {
|
|
6754
|
+
enqueueSoftStop(spaceId);
|
|
6755
|
+
if (readSoftStopRequestStatus(spaceId).status !== 'present') throw new Error('soft-stop marker not persisted');
|
|
6756
|
+
stoppedHarnessSpace = spaceId;
|
|
6757
|
+
pushChatLine(' ⏹ 멈췄다 — 해당 하니스 런에 중단 요청을 보냈습니다.');
|
|
6758
|
+
} catch {
|
|
6759
|
+
pushChatLine(' ⚠ 하니스 런 중단 요청을 보내지 못했습니다. /harness runs 에서 확인해 주세요.');
|
|
6760
|
+
}
|
|
6761
|
+
} else if (!showHarnessEscHint(targets)) {
|
|
6731
6762
|
pushChatLine(` ⏳ 중단 요청됨 — ${formatEscAbortWaitingTargets(targets)} 종료를 기다리는 중입니다.`);
|
|
6732
6763
|
}
|
|
6733
6764
|
chatScrollOffset = -1;
|
|
6734
6765
|
},
|
|
6735
6766
|
onAbortRepeat: ({ repeat, targets }) => {
|
|
6736
|
-
if (!showHarnessEscHint(targets)) {
|
|
6767
|
+
if (!stoppedHarnessSpace && !showHarnessEscHint(targets)) {
|
|
6737
6768
|
pushChatLine(` ⏳ ESC 재시도 ${repeat}회 — ${formatEscAbortWaitingTargets(targets)} 종료를 기다리는 중입니다.`);
|
|
6738
6769
|
}
|
|
6739
6770
|
chatScrollOffset = -1;
|
|
@@ -7251,7 +7282,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
7251
7282
|
return () => {
|
|
7252
7283
|
cleanup();
|
|
7253
7284
|
escGate.dispose();
|
|
7254
|
-
settleEscAbortPendingLines(chatLines, chatLinesStartIndex);
|
|
7285
|
+
if (!stoppedHarnessSpace) settleEscAbortPendingLines(chatLines, chatLinesStartIndex);
|
|
7255
7286
|
userScrolledDuringStream = false;
|
|
7256
7287
|
streamingInFlight = false;
|
|
7257
7288
|
};
|
|
@@ -15674,6 +15705,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15674
15705
|
pushChatLine,
|
|
15675
15706
|
draw,
|
|
15676
15707
|
signal: acpTurnRef.abortCtrl?.signal ?? new AbortController().signal,
|
|
15708
|
+
toolCallId: ctx?.callId,
|
|
15677
15709
|
// ⛔⭐ 폴백은 `''` 가 아니라 `undefined` 다 — 「안 넘김」과 「말했는데 어휘가 없음」은 «다른 값»이다.
|
|
15678
15710
|
// harnessMentionState(session-runtime 소비처)는 `undefined → 'absent'` · `'' → 'not-matched'` 로
|
|
15679
15711
|
// 가른다. `?? ''` 로 접으면 그 둘이 같은 값이 되고, 그것이 2026-08-06 에 「TUI 가 하니스에게
|
|
@@ -15695,9 +15727,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15695
15727
|
ptyDashboardOn:
|
|
15696
15728
|
uc.shell.allowDashboardPty === true && (acpPtyAvailable?.() ?? false),
|
|
15697
15729
|
getToolRuntime: (toolName) => runtime.getToolRuntime(toolName),
|
|
15698
|
-
dispatchToolRuntime: (toolName, input) =>
|
|
15730
|
+
dispatchToolRuntime: (toolName, input, toolCallId) =>
|
|
15699
15731
|
runtime.dispatchToolByName(toolName, input, {
|
|
15700
15732
|
surface: 'tui',
|
|
15733
|
+
toolCallId,
|
|
15701
15734
|
signal: acpTurnRef.abortCtrl?.signal,
|
|
15702
15735
|
// ⭐ Agent-family context. Without these two the sub-agent runs
|
|
15703
15736
|
// text-only (see getTools above). `agentDispatchTool` routes the
|
|
@@ -19171,6 +19204,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19171
19204
|
createTurnStreamRuntime: (initialAssistantStart) => {
|
|
19172
19205
|
latestTurnUsage = undefined;
|
|
19173
19206
|
createDashboardTurnStreamRuntimeEscBoundary(escAbortToolState);
|
|
19207
|
+
escHarnessSpacesByCallId.clear();
|
|
19174
19208
|
liveTypeaheadEchoes.length = 0;
|
|
19175
19209
|
const toolRendering = {
|
|
19176
19210
|
...getUserConfig().chat.rendering.tool,
|
|
@@ -19225,6 +19259,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19225
19259
|
const toolCall = call as DashboardTurnStreamCall;
|
|
19226
19260
|
turnStreamRuntime.onToolResult(toolCall);
|
|
19227
19261
|
settleEscAbortToolCall(escAbortToolState, toolCall);
|
|
19262
|
+
escHarnessSpacesByCallId.delete(toolCall.id);
|
|
19228
19263
|
},
|
|
19229
19264
|
};
|
|
19230
19265
|
},
|
|
@@ -19524,6 +19559,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
19524
19559
|
closeDashboardTui();
|
|
19525
19560
|
throw err;
|
|
19526
19561
|
} finally {
|
|
19562
|
+
setSelfImplementSoftStopSpaceObserver(null);
|
|
19527
19563
|
if (tuiSeatAskTimer) clearInterval(tuiSeatAskTimer);
|
|
19528
19564
|
}
|
|
19529
19565
|
}
|
package/src/esc-abort-gate.ts
CHANGED
|
@@ -55,6 +55,8 @@ export interface EscAbortGateDeps {
|
|
|
55
55
|
getTheme?: () => ThemeTokens | null | undefined;
|
|
56
56
|
/** Names shown/logged while a turn abort request is waiting for running work to unwind. */
|
|
57
57
|
getWaitingTargetNames?: () => readonly string[];
|
|
58
|
+
/** Only an identified foreground harness run may bypass the sub-agent confirmation. */
|
|
59
|
+
shouldAbortImmediately?: () => boolean;
|
|
58
60
|
/** Called before abortTurnOnly so the UI can paint a visible pending state immediately. */
|
|
59
61
|
onAbortPending?: (event: { running: number; repeat: number; targets: readonly string[] }) => void;
|
|
60
62
|
/** Called when another Escape arrives after the first abort request. */
|
|
@@ -211,7 +213,7 @@ export function createEscAbortGate(deps: EscAbortGateDeps): EscAbortGate {
|
|
|
211
213
|
return;
|
|
212
214
|
}
|
|
213
215
|
const running = deps.getRunningCount();
|
|
214
|
-
if (running <= 0) {
|
|
216
|
+
if (running <= 0 || deps.shouldAbortImmediately?.()) {
|
|
215
217
|
debug.log('esc.abort', 'abort-immediately', { running, repeat: Math.max(0, escapePresses - 1) });
|
|
216
218
|
abortPendingTurn(running);
|
|
217
219
|
return;
|
|
@@ -94,6 +94,63 @@ export function registerGraphCommands(program: Command, runsDeps: { root?: strin
|
|
|
94
94
|
process.exitCode = 1;
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
|
+
graph.command('wizard <prompt>')
|
|
98
|
+
.description('말 한 줄로 그래프 YAML 을 짓고 같은 검증기 ⊕ graph run --dry-run 으로 확인한다(저장하지 않는다) — --from 이면 그 그래프를 고친다')
|
|
99
|
+
.option('--kind <kind>', 'harness | workflow', 'harness')
|
|
100
|
+
.option('--from <file>', '고칠 그래프 YAML (편집 경로)')
|
|
101
|
+
.option('--out <file>', 'YAML 을 이 파일에 쓰고, 하니스면 같은 폴더에 recipes.yaml 도 쓴다(있으면 덮지 않는다)')
|
|
102
|
+
.option('--json', '결과를 JSON 으로')
|
|
103
|
+
.option('--run', '생성된 그래프의 dry-run 경로를 보여 준다(--dry-run 과 함께만)')
|
|
104
|
+
.option('--dry-run', '--run 과 함께: 명령을 실행하지 않고 경로만 걷는다')
|
|
105
|
+
.action(async (prompt: string, opts: { kind: string; from?: string; out?: string; json?: boolean; run?: boolean; dryRun?: boolean }) => {
|
|
106
|
+
try {
|
|
107
|
+
if (opts.kind !== 'harness' && opts.kind !== 'workflow') throw new Error('--kind must be harness or workflow');
|
|
108
|
+
if (opts.run && !opts.dryRun) throw new Error('--run is only supported with --dry-run (run the saved graph with `elanous graph run <file>`)');
|
|
109
|
+
if (opts.run && opts.kind !== 'harness') throw new Error('--run --dry-run walks harness graphs only');
|
|
110
|
+
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
111
|
+
const { generateGraphFromPrompt } = await import('../graph-wizard/generate.js');
|
|
112
|
+
const currentYaml = opts.from ? readFileSync(opts.from, 'utf8') : undefined;
|
|
113
|
+
const started = Date.now();
|
|
114
|
+
const result = await generateGraphFromPrompt({ prompt, kind: opts.kind, ...(currentYaml ? { currentYaml } : {}) });
|
|
115
|
+
const ms = Date.now() - started;
|
|
116
|
+
let recipesNote: string | undefined;
|
|
117
|
+
if (opts.out) {
|
|
118
|
+
const { writeFileSync } = await import('node:fs');
|
|
119
|
+
const { dirname } = await import('node:path');
|
|
120
|
+
writeFileSync(opts.out, result.yaml);
|
|
121
|
+
if (result.recipes) {
|
|
122
|
+
const recipesPath = join(dirname(resolve(opts.out)), 'recipes.yaml');
|
|
123
|
+
if (existsSync(recipesPath) && readFileSync(recipesPath, 'utf8') !== result.recipes) recipesNote = `recipes.yaml 이 이미 있어 덮지 않았다: ${recipesPath}`;
|
|
124
|
+
else { writeFileSync(recipesPath, result.recipes); recipesNote = `recipes ${recipesPath}`; }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (opts.json) {
|
|
128
|
+
await writeStdoutJson(JSON.stringify({ ...result, ms, ...(recipesNote ? { recipesNote } : {}) }) + '\n');
|
|
129
|
+
} else {
|
|
130
|
+
if (!opts.out) process.stdout.write(result.yaml);
|
|
131
|
+
console.log(`# ${result.ok ? 'ok' : 'invalid'} · id ${result.id} · base ${result.base ?? '-'} · attempts ${result.attempts} · ${(ms / 1000).toFixed(1)}s${opts.out ? ` · saved ${opts.out}` : ''}`);
|
|
132
|
+
console.log(`# ${result.summary}`);
|
|
133
|
+
if (recipesNote) console.log(`# ${recipesNote}`);
|
|
134
|
+
for (const issue of result.issues) console.log(`# issue: ${issue}`);
|
|
135
|
+
if (result.dryRun && (opts.run || !result.ok)) console.log(`# dry-run ${result.dryRun.status}: ${result.dryRun.path.join(' → ')}`);
|
|
136
|
+
}
|
|
137
|
+
if (!result.ok) process.exitCode = 1;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(`graph wizard: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
graph.command('step <step>')
|
|
144
|
+
.description('그래프 마법사 단계 하나를 실행한다(graph run 의 cmd 노드가 부른다 · 마지막 줄 JSON {outcome})')
|
|
145
|
+
.option('--arg <text>', '단계 인자')
|
|
146
|
+
.option('--retries <n>', '실패하면 이 횟수만큼 다시 시도', '0')
|
|
147
|
+
.action(async (step: string, opts: { arg?: string; retries: string }) => {
|
|
148
|
+
const { runWizardStepWithRetries } = await import('../graph-wizard/steps.js');
|
|
149
|
+
const result = runWizardStepWithRetries(step, opts.arg, Number.parseInt(opts.retries, 10) || 0);
|
|
150
|
+
if (result.error) console.error(result.error);
|
|
151
|
+
console.log(JSON.stringify({ outcome: result.outcome, tries: result.tries, ...(result.text ? { text: result.text.slice(0, 20_000) } : {}), ...(result.error ? { error: result.error } : {}) }));
|
|
152
|
+
if (result.outcome === 'fail') process.exitCode = 1;
|
|
153
|
+
});
|
|
97
154
|
graph.command('tick <file>')
|
|
98
155
|
.description('Advance one graph run, resuming a pending decision or starting only when requested')
|
|
99
156
|
.option('--start', 'Start a new run when the graph is idle')
|
|
@@ -13,6 +13,8 @@ import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
|
13
13
|
import { jsonResponse } from '../nexus/api/json-response.js';
|
|
14
14
|
import { defaultGraphsDir } from '../self-implement/graph-templates.js';
|
|
15
15
|
import { runGraph, type GraphRunState } from './runner.js';
|
|
16
|
+
import { readWizardSteps, wizardRecipesFor } from '../graph-wizard/saved-steps.js';
|
|
17
|
+
import { stringify as stringifyYaml } from 'yaml';
|
|
16
18
|
|
|
17
19
|
const GRAPH_ID = /^[a-z0-9-]+$/;
|
|
18
20
|
const RUN_ID = /^ed-[a-z0-9]+-[a-f0-9]{6}$/;
|
|
@@ -148,6 +150,17 @@ export function handleGraphRunStart(rawId: string, deps: GraphRunApiDeps = {}):
|
|
|
148
150
|
recipeText = readFileSync(recipesFile, 'utf8');
|
|
149
151
|
const parsed: unknown = parseYaml(recipeText);
|
|
150
152
|
allowed = new Set(parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? Object.keys(parsed) : []);
|
|
153
|
+
// GRAPH-WIZARD-SAVE-RECIPES — a wizard graph's own steps (saved as a validated sidecar) become recipes here,
|
|
154
|
+
// generated by the server from the step library; they take precedence for the nodes that carry them.
|
|
155
|
+
const steps = readWizardSteps(mineDirOf(deps), id, readFileSync(mine.file, 'utf8'));
|
|
156
|
+
const generated = steps ? wizardRecipesFor(readFileSync(mine.file, 'utf8'), steps) : {};
|
|
157
|
+
if (Object.keys(generated).length) {
|
|
158
|
+
const base = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed as Record<string, unknown> : {};
|
|
159
|
+
const merged = { ...base, ...generated };
|
|
160
|
+
recipeText = stringifyYaml(merged);
|
|
161
|
+
allowed = new Set(Object.keys(merged));
|
|
162
|
+
debug.log('graphs.run', 'wizard-recipes', { id, nodes: Object.keys(generated) });
|
|
163
|
+
}
|
|
151
164
|
} catch {
|
|
152
165
|
return jsonResponse({ error: 'recipes-unavailable' }, 503);
|
|
153
166
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 마법사 «원형» — 사람이 말로 자주 시키는 일의 뼈대 셋. graphs/** 의 운영 템플릿(개발·출시 루프)은
|
|
3
|
+
* «모아서 요약해 보내기» 같은 말과 낱말이 안 겹쳐 엉뚱한 기반(lecture-note)을 골랐다(10-08 라이브 실측).
|
|
4
|
+
* 원형은 기반 후보 «맨 앞»에 서고, 키워드가 겹치는 만큼 점수를 얻는다.
|
|
5
|
+
*/
|
|
6
|
+
export interface WizardArchetype { id: string; description: string; keywords: string[]; text: string }
|
|
7
|
+
|
|
8
|
+
export const WIZARD_ARCHETYPES: readonly WizardArchetype[] = [
|
|
9
|
+
{
|
|
10
|
+
id: 'wizard-collect-summarize-send',
|
|
11
|
+
description: '모아서 요약해 보내기 — 검색·수집 → 요약 → 텔레그램/알림 발송 (브리핑·다이제스트·동향)',
|
|
12
|
+
keywords: ['뉴스', '모아', '수집', '검색', '찾아', '요약', '정리', '보내', '알려', '텔레그램', '브리핑', '동향', '소식', 'news', 'digest', 'summary'],
|
|
13
|
+
text: `graph_id: wizard-collect-summarize-send
|
|
14
|
+
loop:
|
|
15
|
+
title: 모아서 요약해 보내기
|
|
16
|
+
description: 검색으로 모은 내용을 요약해 텔레그램으로 보낸다
|
|
17
|
+
trigger:
|
|
18
|
+
cron: "0 8 * * *"
|
|
19
|
+
version: 1
|
|
20
|
+
entry_node: collect
|
|
21
|
+
terminal_nodes: [done, failed]
|
|
22
|
+
nodes:
|
|
23
|
+
- { node_id: collect, kind: agent, recipe: 'cmd:collect', max_visits: 1 } # 수집 | web-search | 검색어
|
|
24
|
+
- { node_id: summarize, kind: agent, recipe: 'cmd:summarize', max_visits: 1 } # 요약 | summarize | 핵심 5줄 한국어
|
|
25
|
+
- { node_id: send, kind: agent, recipe: 'cmd:send', max_visits: 1 } # 텔레그램 발송 | telegram-send
|
|
26
|
+
- { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
|
|
27
|
+
- { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
|
|
28
|
+
edges:
|
|
29
|
+
- { from: collect, on: outcome, map: { ok: summarize, fail: failed } }
|
|
30
|
+
- { from: summarize, on: outcome, map: { ok: send, fail: failed } }
|
|
31
|
+
- { from: send, on: outcome, map: { ok: done, fail: failed } }
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'wizard-review-merge',
|
|
36
|
+
description: 'PR 리뷰 후 머지 — 리뷰 → must-fix 없으면 승인 뒤 머지 (있으면 멈추고 알림)',
|
|
37
|
+
keywords: ['pr', '리뷰', '머지', '병합', '풀리퀘', 'must', 'fix', 'review', 'merge'],
|
|
38
|
+
text: `graph_id: wizard-review-merge
|
|
39
|
+
loop:
|
|
40
|
+
title: PR 리뷰 후 머지
|
|
41
|
+
description: PR 을 리뷰하고 must-fix 가 없으면 머지한다
|
|
42
|
+
trigger:
|
|
43
|
+
events: [pr-opened]
|
|
44
|
+
version: 1
|
|
45
|
+
entry_node: review
|
|
46
|
+
terminal_nodes: [done, failed]
|
|
47
|
+
nodes:
|
|
48
|
+
- { node_id: review, kind: judge, recipe: 'cmd:review', max_visits: 1 } # PR 리뷰 | gh-pr-review
|
|
49
|
+
- { node_id: approve-merge, kind: hitl, recipe: 'approval:approve-merge', max_visits: 1 } # 머지 승인 | approval
|
|
50
|
+
- { node_id: merge, kind: git, recipe: 'cmd:merge', max_visits: 1 } # PR 머지 | gh-pr-merge
|
|
51
|
+
- { node_id: notify, kind: agent, recipe: 'cmd:notify', max_visits: 1 } # must-fix 알림 | notify-me | PR 에 must-fix 가 있어 머지하지 않았다
|
|
52
|
+
- { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
|
|
53
|
+
- { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
|
|
54
|
+
edges:
|
|
55
|
+
- { from: review, on: outcome, map: { ok: approve-merge, must-fix: notify, fail: failed } }
|
|
56
|
+
- { from: approve-merge, on: outcome, map: { ok: merge, fail: failed } }
|
|
57
|
+
- { from: merge, on: outcome, map: { ok: done, fail: failed } }
|
|
58
|
+
- { from: notify, on: outcome, map: { ok: done, fail: failed } }
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'wizard-research-approve-publish',
|
|
63
|
+
description: '조사해 정리하고 승인 뒤 게시 — 조사 → 표/초안 정리 → 사람 승인 → 게시',
|
|
64
|
+
keywords: ['조사', '가격', '경쟁', '표', '정리', '승인', '올려', '게시', '노션', '초안', '블로그', '보고서'],
|
|
65
|
+
text: `graph_id: wizard-research-approve-publish
|
|
66
|
+
loop:
|
|
67
|
+
title: 조사해 정리하고 승인 뒤 게시
|
|
68
|
+
description: 조사한 내용을 표로 정리하고 사람 승인 뒤 게시한다
|
|
69
|
+
trigger:
|
|
70
|
+
events: [manual]
|
|
71
|
+
version: 1
|
|
72
|
+
entry_node: research
|
|
73
|
+
terminal_nodes: [done, failed]
|
|
74
|
+
nodes:
|
|
75
|
+
- { node_id: research, kind: agent, recipe: 'cmd:research', max_visits: 1 } # 조사 | web-search | 조사 주제
|
|
76
|
+
- { node_id: tabulate, kind: agent, recipe: 'cmd:tabulate', max_visits: 1 } # 표로 정리 | llm | 결과를 마크다운 표로 정리
|
|
77
|
+
- { node_id: approve, kind: hitl, recipe: 'approval:approve', max_visits: 1 } # 게시 승인 | approval
|
|
78
|
+
- { node_id: publish, kind: agent, recipe: 'cmd:publish', max_visits: 1 } # 게시 | custom | 게시처에 올리기
|
|
79
|
+
- { node_id: done, kind: gate, recipe: none, max_visits: 1 } # 완료
|
|
80
|
+
- { node_id: failed, kind: gate, recipe: none, max_visits: 1 } # 실패
|
|
81
|
+
edges:
|
|
82
|
+
- { from: research, on: outcome, map: { ok: tabulate, fail: failed } }
|
|
83
|
+
- { from: tabulate, on: outcome, map: { ok: approve, fail: failed } }
|
|
84
|
+
- { from: approve, on: outcome, map: { ok: publish, fail: failed } }
|
|
85
|
+
- { from: publish, on: outcome, map: { ok: done, fail: failed } }
|
|
86
|
+
`,
|
|
87
|
+
},
|
|
88
|
+
];
|