elanous 0.2.20 → 0.2.22
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/aBi6EyMtLsrl7J8eH4kF7/_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-08b716476f1d50af.js +1 -0
- 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/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
- 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/3857-5985236d9307f676.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/5633-d6852385000738ef.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-5b9e8a45a5b0c823.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-aa1cc4ac90af4d40.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-55bcfbb0e8057f59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -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-c855c24de7db74fe.js → page-88459e0903da2803.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d85c43f46f58716b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-4e645600579ae952.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-87edbfd19117823b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-cbaf66b1e1804adf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-80ed511e02c81412.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-9f5610caaa06178c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-ec8f3575e339de74.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-2a19c5cd3b562580.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-b6a09cde2e07521a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-35bfec2e631d3eae.js → page-abd86cd832aaa011.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-f9ccb1b1adbea14d.js → page-8c3f168fc3f91939.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-786fb4504d0f2fa9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-7ec8e289b4c14df6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-8c8ac17d52be2a79.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-d359ab048d63a9d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-f151cc10ad026940.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-bf495a1e284f4e00.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-cf0a4b4abead71ea.js → page-9abcfac07e02a542.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-69cb93d1a2e57e0e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-72ec543928d67c3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.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/f2bd5257e6af88d8.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/catalog/catalog.yaml +1 -1
- package/graphs/drafts/recipes.yaml +6 -6
- package/graphs/implement-loop.yaml +277 -5
- package/graphs/test-diet/recipes.yaml +3 -0
- package/graphs/test-diet/test-diet.yaml +3 -1
- package/package.json +1 -1
- package/scripts/coord-channel-watch.sh +31 -4
- package/scripts/docs-lint.ts +4 -1
- package/scripts/f-session-monitor.ts +69 -0
- package/scripts/hq/standby-snapshot.ts +7 -3
- package/scripts/knowledge-compress.ts +11 -0
- package/scripts/marketing/generate-ax-brief.ts +18 -1
- package/scripts/measure-nl-routing-corpus.ts +11 -3
- package/scripts/nightly-docops.ts +90 -3
- package/scripts/release-loop/gate-node.ts +5 -5
- package/scripts/release-loop/shard-plan.ts +50 -5
- package/scripts/research-coo-admin-dates.ts +39 -0
- package/scripts/rhythm/daily.ts +2 -1
- package/scripts/skills/public-check.ts +7 -2
- package/scripts/smart-merge/resync-shadow.ts +5 -3
- package/scripts/test-diet/lib.ts +81 -1
- package/scripts/test-diet/node.ts +48 -2
- package/scripts/verify-coo-admin-evidence.ts +83 -0
- package/src/acp/client.ts +4 -2
- package/src/agent/run-context.ts +4 -0
- package/src/agent-env/profile.ts +25 -5
- package/src/agent-mission/driver.ts +26 -12
- package/src/agent-mission/supervisor-metrics.ts +17 -2
- package/src/agent-substrate/acp-reviewer.ts +12 -5
- package/src/agent-substrate/pr-reviewer.ts +11 -4
- package/src/agent-substrate/review-intent.ts +102 -3
- package/src/autopilot/build/llm-conflict-merge.ts +22 -8
- package/src/autopilot/pr-manager.ts +18 -8
- package/src/away/release-watch.ts +47 -8
- package/src/briefing/brief-items.ts +104 -11
- package/src/browser-cdp/client.ts +10 -5
- package/src/chat/index.ts +1 -0
- package/src/cli/brief-cli.ts +20 -2
- package/src/cli/config-classify.ts +83 -0
- package/src/cli/control-cli.ts +14 -0
- package/src/cli/decisions-cli.ts +26 -16
- package/src/cli/logs-model-input.ts +1 -1
- 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/pr-cli.ts +65 -8
- package/src/cli/release-cli.ts +21 -4
- package/src/cli/tasks-cli.ts +214 -1
- package/src/connectors/types.ts +6 -6
- package/src/context-bus/context-now-surfaces.ts +18 -0
- package/src/context-bus/coord-events.ts +32 -4
- package/src/context-bus/long-term-memory.ts +23 -6
- package/src/context-bus/nightly-condense.ts +169 -21
- package/src/control/control-status.ts +87 -0
- package/src/control-plane/light-member.ts +5 -3
- package/src/control-plane/member.ts +172 -7
- package/src/core-turn/run-goal-loop.ts +22 -1
- package/src/dashboard/index.ts +40 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
- package/src/decisions/decision-cards.ts +20 -1
- package/src/decisions/decision-ledger.ts +11 -8
- package/src/decisions/web-push-decision-cards.ts +19 -3
- package/src/discord-self-message.ts +2 -2
- package/src/domains/coo-admin-dates.evidence.json +876 -0
- package/src/domains/coo-admin-tool.ts +48 -14
- package/src/domains/ops-status.ts +53 -0
- package/src/domains/outbound-alert.ts +24 -1
- package/src/esc-abort-gate.ts +3 -1
- package/src/exec-requests/planner.ts +6 -0
- package/src/graph-kinds/registry.ts +6 -6
- package/src/graph-runner/graph-cli.ts +91 -2
- package/src/graph-runner/graph-run-api.ts +20 -0
- package/src/graph-runner/graph-tick.ts +73 -12
- package/src/graph-runner/runner.ts +43 -11
- package/src/graph-unify/wf-to-graph.ts +79 -0
- package/src/graph-wizard/archetypes.ts +88 -0
- package/src/graph-wizard/generate.ts +577 -0
- package/src/graph-wizard/saved-steps.ts +120 -0
- package/src/graph-wizard/steps.ts +206 -0
- package/src/harness/harness-cli-command.ts +186 -32
- package/src/harness/harness-queue.ts +58 -14
- package/src/harness/launch-advice.ts +23 -0
- package/src/index.ts +43 -11
- package/src/intake-plane/digest.ts +18 -11
- package/src/knowledge/conclusion-compress.ts +93 -0
- package/src/knowledge/know-find.ts +9 -2
- package/src/knowledge/query.ts +20 -0
- package/src/llm.ts +26 -16
- package/src/loops/neighbor-runtime.ts +88 -0
- package/src/loops/orchestrator/author-depth.ts +29 -11
- package/src/loops/orchestrator/overlap.ts +7 -1
- package/src/loops/orchestrator/resync-shadow.ts +1 -1
- package/src/loops/orchestrator/tick.ts +35 -2
- package/src/loops/orchestrator/traffic.ts +2 -0
- package/src/loops/registry.ts +5 -0
- package/src/market/publish.ts +17 -1
- package/src/maturity/feature-maturity.ts +4 -2
- package/src/mss/logging/redaction.ts +17 -0
- package/src/nexus/api/draft-metrics.ts +128 -0
- package/src/nexus/api/graph-peer-edit.ts +77 -0
- package/src/nexus/api/graph-wizard-api.ts +61 -0
- package/src/nexus/api/graphs-api.ts +193 -17
- package/src/nexus/api/harness-traversal.ts +205 -0
- package/src/nexus/api/http-server.ts +122 -6
- package/src/nexus/api/plugins-market.ts +24 -0
- package/src/nexus/index.ts +3 -2
- package/src/nexus/outbound/router.ts +28 -8
- package/src/oauth/claude-pty-maturity.ts +24 -0
- package/src/oauth/fallback-chain.ts +27 -14
- 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/plugins/maker/market-bundle.ts +9 -0
- package/src/plugins/maker/plugin-maker.ts +9 -3
- package/src/plugins/maker/wizard-generate.ts +73 -9
- package/src/policy/llm-policy.ts +11 -6
- package/src/release-loop/checklist-board.ts +27 -0
- package/src/release-loop/checklist.ts +38 -0
- package/src/release-loop/gate-shards.ts +7 -2
- package/src/seat-address/seat-address.ts +1 -0
- package/src/seat-loop/seat-loop.ts +174 -20
- package/src/seat-loop/seat-report.ts +29 -1
- package/src/self-dev/ask-launch-flow.ts +4 -2
- package/src/self-dev/ask-launch-io.ts +3 -1
- package/src/self-dev/dev-cli.ts +90 -8
- package/src/self-dev/dev-pipeline.ts +20 -1
- package/src/self-dev/draft-sweep.ts +45 -8
- 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/orchestrate.ts +3 -0
- package/src/self-dev/release-path-guard.ts +7 -4
- package/src/self-dev/review-provider-fallback.ts +83 -0
- package/src/self-dev/run-supervisor.ts +5 -1
- package/src/self-implement/frozen-merges.ts +23 -0
- package/src/self-implement/goal-author.ts +14 -0
- package/src/self-implement/harness-policy-gates.ts +7 -5
- package/src/self-implement/heal-intake.ts +3 -0
- package/src/self-implement/host-regate.ts +28 -14
- package/src/self-implement/l8-merge-queue.ts +80 -8
- package/src/self-implement/orchestrator.ts +84 -28
- package/src/self-implement/seams.ts +8 -3
- package/src/self-implement/self-implement-cli.ts +4 -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 +35 -6
- package/src/steward/triage.ts +25 -8
- package/src/storage/file-lock.ts +33 -4
- package/src/task-agent/card-evidence.ts +6 -0
- package/src/task-agent/completion-evidence.ts +31 -3
- package/src/task-agent/cover.ts +61 -0
- package/src/task-agent/live-moves.ts +126 -3
- package/src/task-agent/parent-host.ts +315 -0
- package/src/task-agent/shadow.ts +19 -4
- package/src/task-agent/task-hand.ts +11 -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/pod-pool.ts +138 -15
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
- package/src/telegram-commands.ts +8 -0
- package/src/telegram-project-command.ts +6 -0
- package/src/telegram.ts +2 -2
- package/src/tool-runtime/browser-runtime.ts +87 -3
- package/src/tool-runtime/index.ts +1 -0
- package/src/typecheck-ratchet.ts +2 -1
- package/src/user-config.ts +55 -8
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +24 -2
- package/src/workflow-runtime/triggers/registry.ts +27 -0
- package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
- package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.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/3857-995ea9f5ef33d71e.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/bots/page-7a852956c74015a3.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/market/page-9ca21f9ad3208a61.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/checklist/page-f6055495e9ed7ea8.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/setup/done/page-c26fa10aac6c21c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.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/tasks/page-be1c4e8697f22360.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/trace/page-0fd895fab9b62c1b.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/workflows/chat-ui/page-c6e4364d9de8f717.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 → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
- /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
|
@@ -4,10 +4,23 @@ import { getUserConfig } from '../user-config.js';
|
|
|
4
4
|
import { getSecretAsync } from '../nexus/config/secrets/index.js';
|
|
5
5
|
import { fetchLinearProjectIssues, type LinearProjectIssue } from '../connectors/linear.js';
|
|
6
6
|
import { formatElanousCard } from './elanous-card.js';
|
|
7
|
+
import recordedDateEvidence from './coo-admin-dates.evidence.json';
|
|
8
|
+
|
|
9
|
+
type CooDateEvidence = {
|
|
10
|
+
project: string;
|
|
11
|
+
verifiedIssues: Array<{
|
|
12
|
+
identifier: string;
|
|
13
|
+
title: string;
|
|
14
|
+
url: string;
|
|
15
|
+
officialDeadline: string | null;
|
|
16
|
+
preparationPeriod: string | { start: string } | null;
|
|
17
|
+
representativeActionDate: string | null;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
export const COO_ADMIN_SPEC: LLMToolSpec = {
|
|
9
22
|
name: 'coo_admin',
|
|
10
|
-
description: '읽기 전용 Linear COO 행정 조회 — «행정 뭐 남았어 · COO 할 일 · 마감 다가오는 행정» 질문에 사용. 외부 행정 프로젝트의 열린 일을
|
|
23
|
+
description: '읽기 전용 Linear COO 행정 조회 — «행정 뭐 남았어 · COO 할 일 · 마감 다가오는 행정» 질문에 사용. 외부 행정 프로젝트의 열린 일을 우선순위와 확인된 날짜 근거로 브리핑한다.',
|
|
11
24
|
parameters: { type: 'object', properties: {}, required: [] },
|
|
12
25
|
};
|
|
13
26
|
|
|
@@ -16,6 +29,7 @@ export interface CooAdminDeps {
|
|
|
16
29
|
fetch?: typeof fetch;
|
|
17
30
|
project?: string;
|
|
18
31
|
now?: Date;
|
|
32
|
+
dateEvidence?: CooDateEvidence;
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
/** Reads the configured Linear project without changing any Linear issue. */
|
|
@@ -32,27 +46,47 @@ export async function dispatchCooAdmin(_args: Record<string, unknown>, deps: Coo
|
|
|
32
46
|
const issues = await fetchLinearProjectIssues({ apiKey, project, fetch: deps.fetch ?? fetch });
|
|
33
47
|
const today = deps.now ?? new Date();
|
|
34
48
|
const todayUtc = Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
|
|
35
|
-
const daysUntil = (date: string) => Math.round((Date.parse(`${date
|
|
49
|
+
const daysUntil = (date: string) => Math.round((Date.parse(`${date}T00:00:00Z`) - todayUtc) / 86_400_000);
|
|
50
|
+
const dateEvidence = deps.dateEvidence ?? (recordedDateEvidence as CooDateEvidence);
|
|
51
|
+
const verifiedDate = (date: string | null | undefined): string | null => {
|
|
52
|
+
if (!date || !/^\d{4}-\d{2}-\d{2}$/.test(date)) return null;
|
|
53
|
+
const time = Date.parse(`${date}T00:00:00Z`);
|
|
54
|
+
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? date : null;
|
|
55
|
+
};
|
|
56
|
+
const evidenceFor = (item: LinearProjectIssue) => dateEvidence.project === project
|
|
57
|
+
? dateEvidence.verifiedIssues.find(row => row.identifier === item.identifier && row.url === item.url && row.title === item.title)
|
|
58
|
+
: undefined;
|
|
59
|
+
const deadlineFor = (item: LinearProjectIssue) => verifiedDate(evidenceFor(item)?.officialDeadline);
|
|
60
|
+
const rank = (item: LinearProjectIssue) => item.priority === 0 ? 5 : item.priority;
|
|
36
61
|
issues.sort((a, b) => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return
|
|
62
|
+
const aDue = deadlineFor(a);
|
|
63
|
+
const bDue = deadlineFor(b);
|
|
64
|
+
return rank(a) - rank(b) || (aDue === null ? (bDue === null ? 0 : 1)
|
|
65
|
+
: bDue === null ? -1 : daysUntil(aDue) - daysUntil(bDue));
|
|
40
66
|
});
|
|
41
67
|
count = issues.length;
|
|
42
68
|
const lines = issues.map((item: LinearProjectIssue) => {
|
|
43
|
-
const
|
|
69
|
+
const evidence = evidenceFor(item);
|
|
70
|
+
const due = deadlineFor(item);
|
|
71
|
+
const days = due ? daysUntil(due) : null;
|
|
44
72
|
if (days !== null && days < 0) overdue++;
|
|
45
|
-
const deadline =
|
|
46
|
-
|
|
73
|
+
const deadline = due ? `확인된 마감일 ${due} (${days !== null && days < 0 ? `지남 ${-days}일` : `D-${days}`})` : '기한 미확인 · 확인 예정일 미정';
|
|
74
|
+
const preparation = verifiedDate(typeof evidence?.preparationPeriod === 'string'
|
|
75
|
+
? evidence.preparationPeriod : evidence?.preparationPeriod?.start);
|
|
76
|
+
const representative = verifiedDate(evidence?.representativeActionDate);
|
|
77
|
+
const dates = [preparation && `준비 시작일 ${preparation}`, representative && `대표 손이 필요한 날 ${representative}`].filter(Boolean);
|
|
78
|
+
return `${deadline} · 우선순위 ${item.priority || '미지정'} · ${item.title} · ${item.state.name} · ${item.assignee?.name ?? '미배정'}${dates.length ? ` · ${dates.join(' · ')}` : ''}\n${item.url}`;
|
|
47
79
|
});
|
|
48
80
|
debug.log('coo.admin', 'read', { count, overdue, reason: 'ok' });
|
|
49
|
-
const scope = issues.truncated ? '최대 250건 중 조회한 항목만 마감 순으로 표시 (이후 항목은 포함되지 않음)\n' : '';
|
|
81
|
+
const scope = issues.truncated ? '최대 250건 중 조회한 항목만 우선순위·확인된 마감 순으로 표시 (이후 항목은 포함되지 않음)\n' : '';
|
|
50
82
|
const prose = lines.length ? `${scope}${lines.join('\n')}` : '남은 행정 0건';
|
|
51
|
-
const card = formatElanousCard({ kind: 'coo-admin', items: issues.map(item =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
83
|
+
const card = formatElanousCard({ kind: 'coo-admin', items: issues.map(item => {
|
|
84
|
+
const due = deadlineFor(item);
|
|
85
|
+
return {
|
|
86
|
+
title: item.title, due, daysLeft: due ? daysUntil(due) : null,
|
|
87
|
+
state: item.state.name, owner: item.assignee?.name ?? '미배정', url: item.url,
|
|
88
|
+
};
|
|
89
|
+
}) });
|
|
56
90
|
return `${prose}\n${card}`;
|
|
57
91
|
} catch (error) {
|
|
58
92
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -26,6 +26,7 @@ import { loadMandate, type TradeMandate } from './trade-mandate.js';
|
|
|
26
26
|
import { debug, redactSecretText } from '../debug/log.js';
|
|
27
27
|
import { LogStore, logsDbPath, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
28
28
|
import { detectRepeatedFailures, explainFailure } from './repeated-failure.js';
|
|
29
|
+
import { listLoops, loopRecentRuns, type LoopRun } from '../loops/registry.js';
|
|
29
30
|
|
|
30
31
|
/** 예약 레지스트리 조회 관측 — 실패가 0으로 접히지 않게 남기는 창구. */
|
|
31
32
|
export const OPS_STATUS_LOG_CATEGORY = 'ops.status';
|
|
@@ -34,6 +35,29 @@ export const OPS_SCHEDULES_LOOKUP_FAILED_EVENT = 'schedules.lookup-failed';
|
|
|
34
35
|
/** 예약 레지스트리 행 조회 seam — 미지정 시 실 레지스트리 reader. */
|
|
35
36
|
export type ListScheduleRows = () => ScheduleRow[];
|
|
36
37
|
|
|
38
|
+
/** Graph-loop health input — run history is newest first. */
|
|
39
|
+
export type GraphLoopRuns = () => Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
runs: Array<Pick<LoopRun, 'at' | 'status' | 'failedNodes'>>;
|
|
43
|
+
}>;
|
|
44
|
+
|
|
45
|
+
/** Bounded window per loop: a streak whose oldest read run is already >24h old is flagged; a longer
|
|
46
|
+
* all-failed history than this window can only under-report (never over-report) the streak length. */
|
|
47
|
+
export const GRAPH_LOOP_HEALTH_RUN_WINDOW = 50;
|
|
48
|
+
|
|
49
|
+
function defaultGraphLoopRuns(): ReturnType<GraphLoopRuns> {
|
|
50
|
+
const out: ReturnType<GraphLoopRuns> = [];
|
|
51
|
+
for (const loop of listLoops().filter(entry => entry.enabled)) {
|
|
52
|
+
// One unreadable run history must not hide the other loops.
|
|
53
|
+
try { out.push({ id: loop.id, enabled: loop.enabled, runs: loopRecentRuns(loop.id, {}, GRAPH_LOOP_HEALTH_RUN_WINDOW) }); }
|
|
54
|
+
catch (err) {
|
|
55
|
+
debug.log('ops.health', 'graph-loop-failure-skipped', { loop: loop.id, reason: redactSecretText(err instanceof Error ? err.message : String(err)) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
37
61
|
/** 조회 seam(테스트) — 미지정 시 실 스토어. */
|
|
38
62
|
export interface OpsStatusOpts {
|
|
39
63
|
now?: Date;
|
|
@@ -47,6 +71,8 @@ export interface OpsStatusOpts {
|
|
|
47
71
|
planDraftFor?: (missionId: string) => string;
|
|
48
72
|
/** Health-only log reader seam; the default reads the current instance's last ten minutes read-only. */
|
|
49
73
|
recentLogs?: () => LogStoreRow[];
|
|
74
|
+
/** Health-only graph-loop registry reader; injection avoids the real state directory. */
|
|
75
|
+
graphLoopRuns?: GraphLoopRuns;
|
|
50
76
|
}
|
|
51
77
|
|
|
52
78
|
export interface CountByStatus { [status: string]: number }
|
|
@@ -341,6 +367,33 @@ export function opsHealth(opts: OpsStatusOpts = {}): OpsHealthReport {
|
|
|
341
367
|
}
|
|
342
368
|
}
|
|
343
369
|
|
|
370
|
+
// Graph-run history is independent of ops_events loops and of log-store failures.
|
|
371
|
+
try {
|
|
372
|
+
const loops = (opts.graphLoopRuns ?? defaultGraphLoopRuns)();
|
|
373
|
+
const nowMs = (opts.now ?? new Date()).getTime();
|
|
374
|
+
let flagged = 0;
|
|
375
|
+
for (const loop of loops) {
|
|
376
|
+
if (!loop.enabled) continue;
|
|
377
|
+
const runs = [...loop.runs].sort((a, b) => Date.parse(b.at) - Date.parse(a.at));
|
|
378
|
+
const failures = [] as typeof runs;
|
|
379
|
+
for (const run of runs) {
|
|
380
|
+
if (run.status !== 'failed') break;
|
|
381
|
+
failures.push(run);
|
|
382
|
+
}
|
|
383
|
+
const first = failures.at(-1);
|
|
384
|
+
if (failures.length < 2 || !first || !Number.isFinite(Date.parse(first.at)) || nowMs - Date.parse(first.at) <= 24 * 3600_000) continue;
|
|
385
|
+
anomalies.push({
|
|
386
|
+
kind: 'repeated_failure', entity: `loop:${loop.id}`,
|
|
387
|
+
detail: `그래프 루프 ${loop.id} 연속 실패 ${failures.length}회 · ${first.at} 부터 · 마지막 실패 노드 ${failures[0]!.failedNodes.join(', ')}`,
|
|
388
|
+
since: first.at,
|
|
389
|
+
});
|
|
390
|
+
flagged++;
|
|
391
|
+
}
|
|
392
|
+
debug.log('ops.health', 'graph-loop-failure', { loops: loops.length, flagged });
|
|
393
|
+
} catch (err) {
|
|
394
|
+
debug.log('ops.health', 'graph-loop-failure-skipped', { reason: redactSecretText(err instanceof Error ? err.message : String(err)) });
|
|
395
|
+
}
|
|
396
|
+
|
|
344
397
|
// Independent log check: an unreadable log store must not hide task/loop/schedule anomalies.
|
|
345
398
|
try {
|
|
346
399
|
const recentLogs = opts.recentLogs ?? (() => {
|
|
@@ -17,6 +17,7 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
|
17
17
|
import { resolveChannelBotToken } from '../channel-bot-token.js';
|
|
18
18
|
import { explainReportRoute, isOperationalKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './telegram-kind-route.js';
|
|
19
19
|
import { debug } from '../debug/log.js';
|
|
20
|
+
import type { AddBriefItem } from '../briefing/brief-items.js';
|
|
20
21
|
import { registerLogStoreSink, setLogInstanceName } from '../mss/logging/log-store.js';
|
|
21
22
|
import type { LogSink } from '../mss/logging/sink.js';
|
|
22
23
|
import { resolveDaemonEndpoint } from '../nexus/daemon-endpoint.js';
|
|
@@ -633,6 +634,13 @@ export function deliver(text: string, kind = 'alert', onBot?: (bot: string) => v
|
|
|
633
634
|
return false;
|
|
634
635
|
}
|
|
635
636
|
|
|
637
|
+
type UndeliverableRecorder = (item: AddBriefItem) => void;
|
|
638
|
+
let undeliverableRecorder: UndeliverableRecorder | null = null;
|
|
639
|
+
/** Replace the ledger writer in tests; null restores the production ledger. */
|
|
640
|
+
export function setUndeliverableRecorder(recorder: UndeliverableRecorder | null): void {
|
|
641
|
+
undeliverableRecorder = recorder;
|
|
642
|
+
}
|
|
643
|
+
|
|
636
644
|
/** OB8b — both paths failed: say so loudly, with the universe this process resolved, instead of a silent false.
|
|
637
645
|
* The usual cause (10-01 · MK): an ad-hoc `bun -e` or a script from a source tree resolves a cwd-derived test
|
|
638
646
|
* universe, so it finds neither the production daemon nor the production bot token. */
|
|
@@ -647,7 +655,22 @@ function reportUndeliverable(kind: string, daemonPath: DaemonPathClass | 'not-fo
|
|
|
647
655
|
let route: ReturnType<typeof explainReportRoute> | null = null;
|
|
648
656
|
try { if (cfg) route = explainReportRoute(cfg, kind); } catch { /* the report still goes out */ }
|
|
649
657
|
ensureOutboundLogSink();
|
|
650
|
-
|
|
658
|
+
const reason = route?.reason ?? 'unknown';
|
|
659
|
+
try { debug.log('outbound.send', 'undeliverable', { kind: safeObservationLabel(kind), daemonPath, universe, root, route: reason }); } catch { /* fail-soft */ }
|
|
660
|
+
try {
|
|
661
|
+
const record = undeliverableRecorder ?? ((item: AddBriefItem) => {
|
|
662
|
+
const { BriefItemsLedger } = require('../briefing/brief-items.js') as typeof import('../briefing/brief-items.js');
|
|
663
|
+
new BriefItemsLedger().add(item);
|
|
664
|
+
});
|
|
665
|
+
record({
|
|
666
|
+
domain: '운영', priority: 'P1', source: 'outbound.undeliverable',
|
|
667
|
+
text: `미전달: kind ${kind} · 데몬 ${daemonPath} · 경로 ${reason} · 우주 ${universe}`,
|
|
668
|
+
evidence: 'elanous logs --category outbound.send --event undeliverable',
|
|
669
|
+
dedupeKey: `${kind} · ${reason}`, dedupeWithinMs: 60 * 60_000,
|
|
670
|
+
});
|
|
671
|
+
} catch (error) {
|
|
672
|
+
try { debug.log('outbound.send', 'undeliverable-record-failed', { kind: safeObservationLabel(kind), route: reason, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
673
|
+
}
|
|
651
674
|
const hint = universe === 'prod'
|
|
652
675
|
? '운영 데몬이 떠 있는지 확인: elanous nexus show'
|
|
653
676
|
: '운영으로 보내려면 설치본 elanous 로 실행하거나 ELANOUS_STATE_DIR=~/.elanous 와 --config-dir ~/.elanous 를 준다';
|
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;
|
|
@@ -7,6 +7,7 @@ import { defaultGraphsDir } from '../self-implement/graph-templates.js';
|
|
|
7
7
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
8
8
|
import type { ExecAttachment } from './store.js';
|
|
9
9
|
import { debug } from '../debug/log.js';
|
|
10
|
+
import { peerEditRunGate } from '../nexus/api/graph-peer-edit.js';
|
|
10
11
|
import { OUTPUT_KINDS, pickDefaultOutput, type OutputKind } from './default-outputs.js';
|
|
11
12
|
|
|
12
13
|
export interface InstalledGraph {
|
|
@@ -105,6 +106,11 @@ async function coreAndMineGraphs(coreDir: string, mineDir: string): Promise<Inst
|
|
|
105
106
|
const id = doc?.graph_id;
|
|
106
107
|
if (typeof id !== 'string' || !/^[a-z0-9-]+$/.test(id) || seen.has(id)) continue;
|
|
107
108
|
if (core && doc?.loop?.exec_request !== true) continue;
|
|
109
|
+
// W9c — a «mine» graph a peer changed is not runnable until the owner approves it (fail closed).
|
|
110
|
+
if (!core) {
|
|
111
|
+
const gate = peerEditRunGate(dir, id);
|
|
112
|
+
if (!gate.ok) { debug.log('exec-requests', 'skipped-peer-edit', { id, error: gate.error }); continue; }
|
|
113
|
+
}
|
|
108
114
|
const inputs = doc?.loop?.inputs;
|
|
109
115
|
const inputKeys = core && Array.isArray(inputs)
|
|
110
116
|
? inputs.filter((key): key is string => typeof key === 'string')
|
|
@@ -20,13 +20,13 @@ export interface NodeKindEntry {
|
|
|
20
20
|
run?: NodeKindRun;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const HARNESS_CORE_KINDS = ['agent', 'gate', 'git', 'judge', 'observe', 'hitl', 'subgraph'] as const;
|
|
23
|
+
export const HARNESS_CORE_KINDS = ['agent', 'gate', 'git', 'judge', 'observe', 'hitl', 'subgraph', 'prompt'] as const;
|
|
24
24
|
|
|
25
25
|
// Workflow validation, the palette, and executor variant labels read the same keys.
|
|
26
26
|
export const WORKFLOW_CORE_KINDS = ['prompt', 'bash', 'skill', 'cft', 'approval', 'if', 'switch', 'iteration', 'classify', 'extract', 'set', 'filter', 'template', 'http', 'showroom', 'task', 'scheduleTrigger', 'webhookTrigger', 'discordTrigger', 'telegramTrigger', 'manualTrigger', 'chatTrigger', 'subworkflow', 'knowledge'] as const;
|
|
27
27
|
|
|
28
28
|
const descriptions: Record<GraphKind, readonly string[]> = {
|
|
29
|
-
harness: ['Run an agent', 'Check an execution gate', 'Perform git operations', 'Judge an outcome', 'Observe a result', 'Request human input', 'Run a child graph'],
|
|
29
|
+
harness: ['Run an agent', 'Check an execution gate', 'Perform git operations', 'Judge an outcome', 'Observe a result', 'Request human input', 'Run a child graph', 'Run an LLM prompt'],
|
|
30
30
|
workflow: ['Run an LLM prompt', 'Run a shell command', 'Invoke a skill', 'Invoke a CFT method', 'Request approval', 'Branch on a condition', 'Select a case', 'Iterate items', 'Classify input', 'Extract structured data', 'Assign variables', 'Filter items', 'Render a template', 'Make an HTTP request', 'Run a showroom', 'Create a task', 'Start on a schedule', 'Start on a webhook', 'Start on Discord', 'Start on Telegram', 'Start manually', 'Start on chat', 'Call another workflow', 'Search the vault for context'],
|
|
31
31
|
};
|
|
32
32
|
|
|
@@ -93,21 +93,21 @@ export function getNodeKind(graph: GraphKind, kind: string): NodeKindEntry | und
|
|
|
93
93
|
|
|
94
94
|
function isNodeKindRun(run: NodeKindRun): boolean {
|
|
95
95
|
if (!run || typeof run !== 'object' || Array.isArray(run)) return false;
|
|
96
|
-
if ('bash' in run) return typeof run.bash === 'string' && run.bash.trim().length > 0 && !('http' in run) && !('skill' in run) && !('mcp' in run);
|
|
96
|
+
if ('bash' in run) return typeof run.bash === 'string' && run.bash.trim().length > 0 && !('prompt' in run) && !('http' in run) && !('skill' in run) && !('mcp' in run);
|
|
97
97
|
if ('http' in run) {
|
|
98
|
-
return !('skill' in run) && !('mcp' in run)
|
|
98
|
+
return !('prompt' in run) && !('skill' in run) && !('mcp' in run)
|
|
99
99
|
&& typeof run.http === 'object' && run.http !== null
|
|
100
100
|
&& typeof run.http.method === 'string' && run.http.method.trim().length > 0
|
|
101
101
|
&& typeof run.http.url === 'string' && run.http.url.trim().length > 0
|
|
102
102
|
&& (run.http.body === undefined || typeof run.http.body === 'string');
|
|
103
103
|
}
|
|
104
|
-
if ('skill' in run) return !('mcp' in run)
|
|
104
|
+
if ('skill' in run) return !('prompt' in run) && !('mcp' in run)
|
|
105
105
|
&& typeof run.skill === 'object' && run.skill !== null
|
|
106
106
|
&& typeof run.skill.name === 'string' && run.skill.name.trim().length > 0
|
|
107
107
|
&& (run.skill.prompt === undefined || typeof run.skill.prompt === 'string');
|
|
108
108
|
if ('mcp' in run) {
|
|
109
109
|
const spec = run.mcp;
|
|
110
|
-
return typeof spec === 'object' && spec !== null && !Array.isArray(spec)
|
|
110
|
+
return !('prompt' in run) && typeof spec === 'object' && spec !== null && !Array.isArray(spec)
|
|
111
111
|
&& typeof spec.server === 'string' && /^[a-z0-9_-]+$/.test(spec.server)
|
|
112
112
|
&& typeof spec.tool === 'string' && /^[a-z0-9_-]+$/.test(spec.tool)
|
|
113
113
|
&& (spec.args === undefined || (typeof spec.args === 'object' && spec.args !== null && !Array.isArray(spec.args)));
|
|
@@ -94,13 +94,72 @@ 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')
|
|
157
|
+
.option('--schedule', 'Subscribe graph YAML triggers.schedule to the workflow scheduler and tick on each fire')
|
|
100
158
|
.option('--input <json>', 'JSON object passed to a newly started run')
|
|
101
159
|
.option('--json', 'Print the tick result as JSON')
|
|
102
|
-
.action(async (file: string, opts: { start?: boolean; input?: string; json?: boolean }) => {
|
|
160
|
+
.action(async (file: string, opts: { start?: boolean; schedule?: boolean; input?: string; json?: boolean }) => {
|
|
103
161
|
try {
|
|
162
|
+
if (opts.schedule && (opts.start || opts.input !== undefined || opts.json)) throw new Error('--schedule cannot be combined with --start, --input or --json');
|
|
104
163
|
if (opts.input !== undefined && !opts.start) throw new Error('--input requires --start');
|
|
105
164
|
let input: Record<string, unknown> | undefined;
|
|
106
165
|
if (opts.input !== undefined) {
|
|
@@ -112,11 +171,41 @@ export function registerGraphCommands(program: Command, runsDeps: { root?: strin
|
|
|
112
171
|
}
|
|
113
172
|
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
114
173
|
const { graphTick } = await import('./graph-tick.js');
|
|
174
|
+
if (opts.schedule) {
|
|
175
|
+
const { createScheduleSource } = await import('../workflow-runtime/triggers/schedule-source.js');
|
|
176
|
+
const graphFile = resolve(file);
|
|
177
|
+
const source = createScheduleSource();
|
|
178
|
+
// The CLI tick callback is the graph counterpart of the workflow scheduler's onEmit.
|
|
179
|
+
const onScheduledTick = async () => {
|
|
180
|
+
try {
|
|
181
|
+
const { notifyGraphEvents } = await import('./graph-notify.js');
|
|
182
|
+
const result = await graphTick(graphFile, { startIfIdle: true, deps: { notify: async () => { await notifyGraphEvents(); } } });
|
|
183
|
+
if (result.status === 'refused') console.error(`graph tick: refused: ${result.reason}`);
|
|
184
|
+
else if (result.status === 'failed') console.error(`graph tick: ${result.runId}: failed`);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
debug.log('graph.runner', 'schedule-tick-failed', { file: graphFile, error: error instanceof Error ? error.message : String(error) });
|
|
187
|
+
console.error(`graph tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
source.subscribeGraph(graphFile, onScheduledTick);
|
|
191
|
+
if (!source.subscriptions().length) throw new Error('graph triggers.schedule is missing');
|
|
192
|
+
await source.start();
|
|
193
|
+
if (source.handle()?.skipped.length) {
|
|
194
|
+
const reasons = source.handle()!.skipped.map(item => item.reason).join('; ');
|
|
195
|
+
await source.stop();
|
|
196
|
+
throw new Error(`graph schedule skipped: ${reasons}`);
|
|
197
|
+
}
|
|
198
|
+
console.log(`graph schedule: ${source.subscriptions().length} active`);
|
|
199
|
+
const shutdown = () => { void source.stop().then(() => { process.exitCode = 0; }); };
|
|
200
|
+
process.once('SIGINT', shutdown);
|
|
201
|
+
process.once('SIGTERM', shutdown);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
115
204
|
const result = await graphTick(file, { startIfIdle: opts.start, ...(input === undefined ? {} : { input }),
|
|
116
205
|
deps: { notify: async () => { const { notifyGraphEvents } = await import('./graph-notify.js'); await notifyGraphEvents(); } } });
|
|
117
206
|
if (opts.json) await writeStdoutJson(JSON.stringify(result) + '\n');
|
|
118
207
|
else console.log(`graph tick: ${result.action}${result.runId ? ` ${result.runId}: ${result.status}` : ''}`);
|
|
119
|
-
if (result.status === 'failed') process.exitCode = 1;
|
|
208
|
+
if (result.status === 'failed' || result.status === 'refused') process.exitCode = 1;
|
|
120
209
|
} catch (error) {
|
|
121
210
|
console.error(`graph tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
211
|
process.exitCode = 1;
|
|
@@ -11,8 +11,11 @@ import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
|
11
11
|
import { debug } from '../debug/log.js';
|
|
12
12
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
13
13
|
import { jsonResponse } from '../nexus/api/json-response.js';
|
|
14
|
+
import { peerEditRefusalBody, peerEditRunGate } from '../nexus/api/graph-peer-edit.js';
|
|
14
15
|
import { defaultGraphsDir } from '../self-implement/graph-templates.js';
|
|
15
16
|
import { runGraph, type GraphRunState } from './runner.js';
|
|
17
|
+
import { readWizardSteps, wizardRecipesFor } from '../graph-wizard/saved-steps.js';
|
|
18
|
+
import { stringify as stringifyYaml } from 'yaml';
|
|
16
19
|
|
|
17
20
|
const GRAPH_ID = /^[a-z0-9-]+$/;
|
|
18
21
|
const RUN_ID = /^ed-[a-z0-9]+-[a-f0-9]{6}$/;
|
|
@@ -141,6 +144,12 @@ export function handleGraphRunStart(rawId: string, deps: GraphRunApiDeps = {}):
|
|
|
141
144
|
if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
|
|
142
145
|
const mine = findMine(mineDirOf(deps), id);
|
|
143
146
|
if (!mine) return jsonResponse({ error: 'not-found', id, reason: '내 그래프에만 실행이 있다 — 핵심 그래프는 복제해서 돌린다' }, 404);
|
|
147
|
+
// W9c — a peer «edit» save runs nothing on this machine until the owner approves it. Unreadable marker = refused.
|
|
148
|
+
const peerGate = peerEditRunGate(mineDirOf(deps), id);
|
|
149
|
+
if (!peerGate.ok) {
|
|
150
|
+
debug.log('graphs.run', 'refused-peer-edit', { id, error: peerGate.error }, { level: 'warn' });
|
|
151
|
+
return jsonResponse(peerEditRefusalBody(id, peerGate), 409);
|
|
152
|
+
}
|
|
144
153
|
const recipesFile = deps.recipesFile ?? join(defaultGraphsDir(), 'demo', 'editor-recipes.yaml');
|
|
145
154
|
let recipeText: string;
|
|
146
155
|
let allowed: Set<string>;
|
|
@@ -148,6 +157,17 @@ export function handleGraphRunStart(rawId: string, deps: GraphRunApiDeps = {}):
|
|
|
148
157
|
recipeText = readFileSync(recipesFile, 'utf8');
|
|
149
158
|
const parsed: unknown = parseYaml(recipeText);
|
|
150
159
|
allowed = new Set(parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? Object.keys(parsed) : []);
|
|
160
|
+
// GRAPH-WIZARD-SAVE-RECIPES — a wizard graph's own steps (saved as a validated sidecar) become recipes here,
|
|
161
|
+
// generated by the server from the step library; they take precedence for the nodes that carry them.
|
|
162
|
+
const steps = readWizardSteps(mineDirOf(deps), id, readFileSync(mine.file, 'utf8'));
|
|
163
|
+
const generated = steps ? wizardRecipesFor(readFileSync(mine.file, 'utf8'), steps) : {};
|
|
164
|
+
if (Object.keys(generated).length) {
|
|
165
|
+
const base = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed as Record<string, unknown> : {};
|
|
166
|
+
const merged = { ...base, ...generated };
|
|
167
|
+
recipeText = stringifyYaml(merged);
|
|
168
|
+
allowed = new Set(Object.keys(merged));
|
|
169
|
+
debug.log('graphs.run', 'wizard-recipes', { id, nodes: Object.keys(generated) });
|
|
170
|
+
}
|
|
151
171
|
} catch {
|
|
152
172
|
return jsonResponse({ error: 'recipes-unavailable' }, 503);
|
|
153
173
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { closeSync, mkdirSync, openSync, readFileSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
2
|
+
import { closeSync, mkdirSync, openSync, readFileSync, realpathSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
5
6
|
import { debug } from '../debug/log.js';
|
|
7
|
+
import { peerEditRunGate } from '../nexus/api/graph-peer-edit.js';
|
|
6
8
|
import { observeModelInputTokens } from '../harness/model-input-observation.js';
|
|
7
9
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
8
10
|
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
@@ -11,23 +13,74 @@ import { latestGraphRun, runGraph, type GraphRunOptions, type GraphRunState } fr
|
|
|
11
13
|
export interface GraphTickOptions {
|
|
12
14
|
startIfIdle?: boolean;
|
|
13
15
|
input?: unknown;
|
|
16
|
+
mineDir?: string;
|
|
14
17
|
deps?: GraphRunOptions['deps'] & { notify?: () => void | Promise<void>; isAlive?: (pid: number) => boolean };
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export interface GraphTickResult {
|
|
18
|
-
action: 'waiting' | 'resumed' | 'started' | 'idle';
|
|
21
|
+
action: 'waiting' | 'resumed' | 'started' | 'idle' | 'refused';
|
|
19
22
|
runId?: string;
|
|
20
|
-
status?: GraphRunState['status'];
|
|
23
|
+
status?: GraphRunState['status'] | 'refused';
|
|
24
|
+
reason?: 'peer-edit-unapproved' | 'peer-edit-unreadable';
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/** One tick owns the graph from the latest-state read through notification and the chosen action. */
|
|
24
28
|
export async function graphTick(path: string, options: GraphTickOptions = {}): Promise<GraphTickResult> {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
29
|
+
const mineDir = options.mineDir ?? join(elanousStateRoot(), 'graphs');
|
|
30
|
+
const inside = (dir: string, file: string): boolean => {
|
|
31
|
+
const contained = relative(dir, file);
|
|
32
|
+
return contained !== '' && contained !== '..' && !contained.startsWith(`..${sep}`) && !isAbsolute(contained);
|
|
33
|
+
};
|
|
34
|
+
const inspect = (): { source: string; graphId: string; ids: string[] } => {
|
|
35
|
+
const source = readFileSync(path, 'utf8');
|
|
36
|
+
const document: unknown = parseYaml(source);
|
|
37
|
+
const graphId = document && typeof document === 'object' && !Array.isArray(document)
|
|
38
|
+
? (document as Record<string, unknown>).graph_id : undefined;
|
|
39
|
+
if (typeof graphId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(graphId) || graphId === '.' || graphId === '..') {
|
|
40
|
+
throw new Error(`invalid graph id: ${String(graphId)}`);
|
|
41
|
+
}
|
|
42
|
+
const ids: string[] = [];
|
|
43
|
+
const file = resolve(path);
|
|
44
|
+
const realFile = realpathSync(path);
|
|
45
|
+
let realMine: string | undefined;
|
|
46
|
+
try { realMine = realpathSync(mineDir); }
|
|
47
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
|
|
48
|
+
for (const candidate of [file, realFile]) {
|
|
49
|
+
if (inside(resolve(mineDir), candidate) || (realMine && inside(realMine, candidate))) {
|
|
50
|
+
const name = basename(candidate);
|
|
51
|
+
if (/\.ya?ml$/.test(name)) ids.push(name.replace(/\.ya?ml$/, ''));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (ids.length) ids.push(graphId);
|
|
55
|
+
return { source, graphId, ids: [...new Set(ids)] };
|
|
56
|
+
};
|
|
57
|
+
const initial = inspect();
|
|
58
|
+
const graphId = initial.graphId;
|
|
59
|
+
const peerRefusal = (ids: string[]): GraphTickResult | null => {
|
|
60
|
+
for (const id of ids) {
|
|
61
|
+
const gate = peerEditRunGate(mineDir, id);
|
|
62
|
+
if (gate.ok) continue;
|
|
63
|
+
debug.log('graph.runner', 'tick-peer-edit-refused', {
|
|
64
|
+
id,
|
|
65
|
+
editedBy: gate.error === 'peer-edit-unapproved' ? gate.record.editedBy : undefined,
|
|
66
|
+
version: gate.error === 'peer-edit-unapproved' ? gate.record.version : undefined,
|
|
67
|
+
});
|
|
68
|
+
return { action: 'refused', status: 'refused', reason: gate.error };
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
const refused = peerRefusal(initial.ids);
|
|
73
|
+
if (refused) return refused;
|
|
74
|
+
const peerEditRefused = Symbol('tick-peer-edit-refused');
|
|
75
|
+
let nodeRefusal: GraphTickResult | null = null;
|
|
76
|
+
const runDeps: GraphRunOptions['deps'] = {
|
|
77
|
+
...options.deps,
|
|
78
|
+
beforeNode: () => {
|
|
79
|
+
options.deps?.beforeNode?.();
|
|
80
|
+
nodeRefusal = peerRefusal([...new Set([...initial.ids, ...inspect().ids])]);
|
|
81
|
+
if (nodeRefusal) throw peerEditRefused;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
31
84
|
const root = options.deps?.root ?? effectiveInstanceRoot();
|
|
32
85
|
const lock = join(root, 'graph-runs', graphId, '.tick.lock');
|
|
33
86
|
mkdirSync(join(root, 'graph-runs', graphId), { recursive: true });
|
|
@@ -38,6 +91,11 @@ export async function graphTick(path: string, options: GraphTickOptions = {}): P
|
|
|
38
91
|
acquireTickLock(lock, graphId, options.deps?.isAlive ?? processAlive);
|
|
39
92
|
acquired = true;
|
|
40
93
|
await options.deps?.notify?.();
|
|
94
|
+
const current = inspect();
|
|
95
|
+
const refusedAfterNotify = peerRefusal([...new Set([...initial.ids, ...current.ids])]);
|
|
96
|
+
if (refusedAfterNotify) return refusedAfterNotify;
|
|
97
|
+
// The run identity and executed bytes must agree with the ledger and the gate.
|
|
98
|
+
if (current.graphId !== graphId) throw new Error(`graph id changed during tick: ${graphId} -> ${current.graphId}`);
|
|
41
99
|
const latest = latestGraphRun(graphId, root);
|
|
42
100
|
const growthCard = latest?.growthPark
|
|
43
101
|
? (options.deps?.growthDecision?.list ?? ((filters: { status: 'all' }) => new DecisionLedger({ stateDir: root }).list(filters)))({ status: 'all' })
|
|
@@ -49,10 +107,10 @@ export async function graphTick(path: string, options: GraphTickOptions = {}): P
|
|
|
49
107
|
if (latest?.status === 'awaiting-approval' && !latest.pending?.decision && !growthDecided) {
|
|
50
108
|
result = { action: 'waiting', runId: latest.runId, status: latest.status };
|
|
51
109
|
} else if (latest?.status === 'running' || (latest?.status === 'awaiting-approval' && (latest.pending?.decision || growthDecided))) {
|
|
52
|
-
const state = await runGraph(path, { resumeRunId: latest.runId, deps: { ...
|
|
110
|
+
const state = await runGraph(path, { resumeRunId: latest.runId, graphSource: current.source, deps: { ...runDeps, root } });
|
|
53
111
|
result = { action: 'resumed', runId: state.runId, status: state.status };
|
|
54
112
|
} else if (options.startIfIdle) {
|
|
55
|
-
const state = await runGraph(path, { input: options.input, deps: { ...
|
|
113
|
+
const state = await runGraph(path, { input: options.input, graphSource: current.source, deps: { ...runDeps, root } });
|
|
56
114
|
result = { action: 'started', runId: state.runId, status: state.status };
|
|
57
115
|
} else {
|
|
58
116
|
result = { action: 'idle' };
|
|
@@ -60,6 +118,9 @@ export async function graphTick(path: string, options: GraphTickOptions = {}): P
|
|
|
60
118
|
(options.deps?.log ?? ((event: string, data: Record<string, unknown>) => debug.log('graph.runner', event, data)))
|
|
61
119
|
('tick', { graphId, action: result.action, ...(result.runId ? { runId: result.runId } : {}) });
|
|
62
120
|
return result;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error === peerEditRefused && nodeRefusal) return nodeRefusal;
|
|
123
|
+
throw error;
|
|
63
124
|
} finally {
|
|
64
125
|
try {
|
|
65
126
|
if (acquired) {
|