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
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
-
import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
4
4
|
import { Database } from 'bun:sqlite';
|
|
5
|
-
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { TRACK_AGENT_FORBIDDEN_ACTION_REGEX, universeLaunch } from '../autopilot/track-agent.js';
|
|
8
8
|
import { debug } from '../debug/log.js';
|
|
9
9
|
import { openSurfaceEventsDb, recordEvent, surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
10
10
|
import { judgeNeighbor } from '../loops/neighbors.js';
|
|
11
|
+
import { checkLoopNeighbors, emitLoopHeartbeat, SEAT_NEIGHBORS } from '../loops/neighbor-runtime.js';
|
|
11
12
|
import { addHarnessQueue, harnessQueueIdForKey, harnessQueueOutcome, listHarnessQueue } from '../harness/harness-queue.js';
|
|
12
13
|
import { isRescueAllowedAction } from '../steward/rescue.js';
|
|
13
14
|
import { DecisionLedger, type DecisionEntry, type DecisionOption, type Recommendation } from '../decisions/decision-ledger.js';
|
|
@@ -20,6 +21,7 @@ import { openMsgStore, type MessageEnvelope } from '../msg/msg-store.js';
|
|
|
20
21
|
import { loadLayeredPersonaDirs, resolveRepositoryPersonaDir, resolveStatePersonaDir } from '../persona/global-registry.js';
|
|
21
22
|
import { orderedPersonaTodos, personaTodoPath, readPersonaTodos, type PersonaTodo } from '../persona/persona-todo.js';
|
|
22
23
|
import { PersonaRegistry } from '../persona/registry.js';
|
|
24
|
+
import { registeredSeats, resolveSeat } from '../seat-address/seat-address.js';
|
|
23
25
|
import { recordSeatAskShadow } from '../seat-dispatch/seat-ask.js';
|
|
24
26
|
import { appendSeatRequestRows, listSeatRequests, withSeatRequestLedgerLock } from '../seat-dispatch/seat-request-ledger.js';
|
|
25
27
|
import { answerCrossCheck, answerSeat, askCrossCheck, askSeat, crossCheckAnswer, deliveredSeatQuestion, hasSeatAnswer, isSeatId, seatCrossChecks, seatQuestions, type CrossCheckJudgment, type SeatId } from '../seat-dispatch/seat-questions.js';
|
|
@@ -29,7 +31,7 @@ import { readSchedules, releasedVersion } from '../release-loop/feature-store.js
|
|
|
29
31
|
import { runOpDecide, type OpDecideCandidate } from './op-decide.js';
|
|
30
32
|
|
|
31
33
|
// `kind`/`createdAt` are the V3 shadow-compare keys (내부 문서 `METHOD-v3-shadow-compare-2026-10-02` · MK 10-02 18:54).
|
|
32
|
-
export type SeatItem = { source: 'request' | 'checklist' | 'hook' | 'seat-question'; kind?: 'request' | 'cell' | 'hook-task' | 'seat-question'; id: string; title: string; text: string; evidence?: string; status?: string; version?: string; seat?: string; evidenceHash?: string; asOf?: string; queuedAt?: string; createdAt?: string; from?: SeatId };
|
|
34
|
+
export type SeatItem = { source: 'request' | 'checklist' | 'hook' | 'seat-question' | 'idle'; kind?: 'request' | 'cell' | 'hook-task' | 'seat-question'; id: string; title: string; text: string; evidence?: string; status?: string; version?: string; seat?: string; evidenceHash?: string; asOf?: string; queuedAt?: string; createdAt?: string; from?: SeatId; idleRung?: 1 | 2 | 3 | 4 | 5 };
|
|
33
35
|
export type SeatInputs = { requests: SeatItem[]; checklist: SeatItem[]; role: string };
|
|
34
36
|
// V3 ledger row: `ts` · `item.id` · `item.kind` · `item.createdAt` · `action` (door, wait or skipped-*) · `reason` (matched rule or forbidden word).
|
|
35
37
|
export type SeatAction = 'decision' | 'harness' | 'wait' | 'seat-question' | 'seat-answer' | 'skipped-budget' | 'skipped-empty';
|
|
@@ -44,7 +46,7 @@ export type TcCandidate =
|
|
|
44
46
|
| { kind: 'publication-pr-approval-wait'; pr: number; title: string; readyAt: string; approvalWaitAt: string; paths: string[] }
|
|
45
47
|
| { kind: 'other-seat-cell-defect'; version: string; id: string; title: string; to: SeatId };
|
|
46
48
|
export type TcPullRequest = { number: number; title: string; body?: string; state: 'OPEN' | 'MERGED' | 'CLOSED'; isDraft: boolean; createdAt: string; updatedAt?: string; mergedAt?: string | null; readyAt?: string; approvalWaitAt?: string; paths?: string[]; reviewDecision?: string };
|
|
47
|
-
export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'resolved' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate | OpDecideCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number };
|
|
49
|
+
export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'resolved' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate | OpDecideCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number; emptyQueueMinutes?: number };
|
|
48
50
|
export type SeatLoopResult = SeatEntry | { seat: string; status: 'skipped-off'; modeDowngradeReason?: string };
|
|
49
51
|
|
|
50
52
|
export function seatLoopTickLine(result: SeatLoopResult): string {
|
|
@@ -93,6 +95,8 @@ export type SeatDeps = {
|
|
|
93
95
|
pullRequests?: () => Promise<TcPullRequest[]>;
|
|
94
96
|
ledgerFiles?: (directory: string) => string[];
|
|
95
97
|
lockContended?: () => void;
|
|
98
|
+
/** Override the heartbeat timer in isolated long-turn tests; production uses the seat contract's interval. */
|
|
99
|
+
heartbeatIntervalMs?: number;
|
|
96
100
|
/** Supply a question only when this seat lacks evidence for the selected work item. */
|
|
97
101
|
inquire?: (seat: SeatId, item: SeatItem) => Promise<SeatInquiry | null> | SeatInquiry | null;
|
|
98
102
|
/** Supply the receiving seat's answer; absent evidence must not invent a reply. */
|
|
@@ -570,8 +574,8 @@ function opDecidePass(config: SeatLoopConfig, deps: SeatDeps, now: Date, path: s
|
|
|
570
574
|
mode,
|
|
571
575
|
ledger: cardLedger(stateRoot, deps, now),
|
|
572
576
|
runOwner: deps.runOwner ?? ((runId) => {
|
|
573
|
-
owners ??= new Map((
|
|
574
|
-
readSeatLedger(owner, deps, now).filter((row) => row.runId).map((row) => [row.runId!, owner] as const)));
|
|
577
|
+
owners ??= new Map(registeredSeats().filter(({ id }) => isSeatId(id)).flatMap(({ id: owner }) =>
|
|
578
|
+
readSeatLedger(owner, deps, now).filter((row) => row.runId).map((row) => [row.runId!, owner as SeatId] as const)));
|
|
575
579
|
return owners.get(runId);
|
|
576
580
|
}),
|
|
577
581
|
routeToSeat: (to, card, reason) => {
|
|
@@ -691,9 +695,11 @@ function readTcChecklist(root: string): Array<{ version: string; id: string; tit
|
|
|
691
695
|
if (!existsSync(path)) return [];
|
|
692
696
|
const db = new Database(path, { readonly: true, strict: true });
|
|
693
697
|
try {
|
|
698
|
+
const seats = registeredSeats().filter(({ id }) => isSeatId(id)).map(({ id }) => id);
|
|
699
|
+
if (!seats.length) return [];
|
|
694
700
|
return db.query(`SELECT a.version, f.id, COALESCE(a.title_override, f.title) AS title, a.status, a.owner
|
|
695
|
-
FROM assignments a JOIN features f ON f.id = a.feature_id WHERE a.status IN ('yellow', 'red') AND a.owner IN (
|
|
696
|
-
ORDER BY a.version, f.id`).all() as Array<{ version: string; id: string; title: string; status: string; owner: string | null }>;
|
|
701
|
+
FROM assignments a JOIN features f ON f.id = a.feature_id WHERE a.status IN ('yellow', 'red') AND a.owner IN (${seats.map(() => '?').join(', ')})
|
|
702
|
+
ORDER BY a.version, f.id`).all(...seats) as Array<{ version: string; id: string; title: string; status: string; owner: string | null }>;
|
|
697
703
|
} finally { db.close(); }
|
|
698
704
|
}
|
|
699
705
|
|
|
@@ -946,13 +952,48 @@ export function planAction(item: SeatItem, seat?: string): { kind: 'decision' |
|
|
|
946
952
|
const kind = rule?.kind ?? 'harness';
|
|
947
953
|
const reason = rule?.reason;
|
|
948
954
|
observe('plan', { kind, reason: reason ?? '' });
|
|
949
|
-
|
|
955
|
+
const text = seat && kind === 'harness' ? seatTaskText(seat, item) : `${item.id} ${item.title}`;
|
|
956
|
+
// Observed once per plan (not per queue comparison): how many target paths the launched ask carries.
|
|
957
|
+
if (seat && kind === 'harness' && text.startsWith('대상 경로: ')) observe('target-paths', { item: item.id, count: text.split('\n', 1)[0]!.split(' · ').length });
|
|
958
|
+
return { kind, text, ...(reason ? { reason } : {}) };
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/** A path counts only when it resolves to a regular file whose real location stays inside `root` (symlinks out are refused). */
|
|
962
|
+
export function repoFileExists(root: string = repoRoot): (path: string) => boolean {
|
|
963
|
+
return (path) => {
|
|
964
|
+
try {
|
|
965
|
+
const absolute = resolve(root, path);
|
|
966
|
+
const real = relative(realpathSync(root), realpathSync(absolute));
|
|
967
|
+
return !!real && real !== '..' && !real.startsWith(`..${sep}`) && !isAbsolute(real) && statSync(absolute).isFile();
|
|
968
|
+
} catch { return false; }
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Repository paths already written in a checklist item's title or evidence (dedup · first-seen order · at most six).
|
|
973
|
+
export function seatTargetPaths(item: SeatItem, isFile: (path: string) => boolean = repoFileExists()): string[] {
|
|
974
|
+
const paths: string[] = [];
|
|
975
|
+
const text = `${item.title}\n${item.evidence ?? ''}`;
|
|
976
|
+
const matches = text.matchAll(/(?:^|[^A-Za-z0-9_./-])((?:src|apps|scripts|test|docs|bin|graphs|packs|plugins)\/[A-Za-z0-9_./-]+)/g);
|
|
977
|
+
for (const match of matches) {
|
|
978
|
+
const path = match[1]!.replace(/[./]+$/, '');
|
|
979
|
+
if (path.split('/').includes('..')) continue;
|
|
980
|
+
const fromRoot = relative(repoRoot, resolve(repoRoot, path));
|
|
981
|
+
if (!fromRoot || fromRoot === '..' || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot) || !isFile(path)) continue;
|
|
982
|
+
if (!paths.includes(path)) paths.push(path);
|
|
983
|
+
if (paths.length === 6) break;
|
|
984
|
+
}
|
|
985
|
+
return paths;
|
|
950
986
|
}
|
|
951
987
|
|
|
952
988
|
// The launched sentence names the seat, the source and the release so same-named items stay distinct.
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
989
|
+
// Checklist items whose title/evidence already name repository files get a leading `대상 경로:` line
|
|
990
|
+
// (the label parseAskTargetPathHints reads); without paths the text is byte-identical to the old one-liner.
|
|
991
|
+
export function seatTaskText(seat: string, item: SeatItem, isFile?: (path: string) => boolean): string {
|
|
992
|
+
const where = item.source === 'checklist' ? `${item.version ?? '?'} 체크리스트 칸 ${item.id}` : item.source === 'idle' ? `쉬는 시간 사다리 ${item.id} · ${item.idleRung}단계${item.version ? ` · ${item.version}` : ''}` : item.source === 'hook' ? `웹훅 작업 카드 ${item.id}` : `자리 요청 ${item.id}`;
|
|
993
|
+
const text = `[${seat} 자리 · ${where} · 역할 docs/roles/${seat}.md] ${item.title.replace(/[\r\n]+/g, ' ')}`;
|
|
994
|
+
if (item.source !== 'checklist') return text;
|
|
995
|
+
const paths = seatTargetPaths(item, isFile);
|
|
996
|
+
return paths.length ? `대상 경로: ${paths.join(' · ')}\n${text}` : text;
|
|
956
997
|
}
|
|
957
998
|
|
|
958
999
|
function xcheckEvent(event: string, seat: string, neighbor: string, item: string, outcome: string): void {
|
|
@@ -986,8 +1027,10 @@ function neighborLastSeen(root: string, isolated: boolean, now: Date, wanted: Re
|
|
|
986
1027
|
}
|
|
987
1028
|
|
|
988
1029
|
function neighborSeat(id: string): string | null {
|
|
989
|
-
const match = /^(
|
|
990
|
-
|
|
1030
|
+
const match = /^([A-Za-z][A-Za-z0-9_-]*)-seat$/i.exec(id);
|
|
1031
|
+
if (!match) return null;
|
|
1032
|
+
const resolved = resolveSeat(match[1]!)?.id;
|
|
1033
|
+
return resolved && isSeatId(resolved) ? resolved : null;
|
|
991
1034
|
}
|
|
992
1035
|
|
|
993
1036
|
function judgeSeatNeighbors(seat: string, config: SeatLoopConfig, deps: SeatDeps, root: string, now: Date): void {
|
|
@@ -1034,8 +1077,89 @@ function judgeSeatNeighbors(seat: string, config: SeatLoopConfig, deps: SeatDeps
|
|
|
1034
1077
|
}
|
|
1035
1078
|
}
|
|
1036
1079
|
|
|
1080
|
+
const IDLE_WORK: Record<SeatId, string> = {
|
|
1081
|
+
MK: '문서 최적화: 현재 문서의 탐색·명료성·검색 유입을 실물 근거로 개선',
|
|
1082
|
+
UX: '멀티 서피스: 실제 채널별 사용자 흐름을 대조하고 결함을 개선',
|
|
1083
|
+
TC: '하니스 자가 치유: 실패 원장을 대조하여 재현 가능한 결함을 수리',
|
|
1084
|
+
OP: '보충 저작: 판 체크리스트의 빠진 칸을 근거와 반증 기준으로 보완',
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
function idleCell(seat: string, rung: 1 | 2 | 3 | 4 | 5, id: string, title: string, now: Date, version?: string, owner?: string, evidence?: string): SeatItem {
|
|
1088
|
+
return { source: 'idle', id, title, text: title, idleRung: rung, seat: owner ?? seat,
|
|
1089
|
+
...(version ? { version } : {}), ...(evidence ? { evidence } : {}), asOf: now.toISOString() };
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function emptyQueueMinutes(ledger: readonly SeatEntry[], now: Date): number {
|
|
1093
|
+
const previousIndex = [...ledger].reverse().findIndex((row) => row.emptyQueueMinutes !== undefined);
|
|
1094
|
+
const workIndex = [...ledger].reverse().findIndex((row) => row.item && row.item.source !== 'idle' && row.status !== 'skipped-empty');
|
|
1095
|
+
const previousPosition = previousIndex < 0 ? -1 : ledger.length - 1 - previousIndex;
|
|
1096
|
+
const workPosition = workIndex < 0 ? -1 : ledger.length - 1 - workIndex;
|
|
1097
|
+
const previous = ledger[previousPosition];
|
|
1098
|
+
const lastWork = ledger[workPosition];
|
|
1099
|
+
const previousAt = previous ? Date.parse(previous.at) : NaN;
|
|
1100
|
+
const workAt = lastWork ? Date.parse(lastWork.at) : NaN;
|
|
1101
|
+
const start = Number.isFinite(previousAt) && Number.isFinite(workAt) ? Math.max(previousAt, workAt)
|
|
1102
|
+
: Number.isFinite(previousAt) ? previousAt : workAt;
|
|
1103
|
+
if (!Number.isFinite(start)) return 0;
|
|
1104
|
+
const prior = previousPosition > workPosition ? previous!.emptyQueueMinutes! : 0;
|
|
1105
|
+
return prior + Math.max(0, (now.getTime() - start) / 60_000);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
function pickIdleWork(seat: SeatId, deps: SeatDeps, ledger: readonly SeatEntry[], now: Date, root: string, shadow: boolean): SeatItem | null {
|
|
1109
|
+
const releaseRoot = deps.root ?? releaseLedgerRoot();
|
|
1110
|
+
const schedules = (deps.schedules ?? (() => readSchedules(releaseRoot)))()
|
|
1111
|
+
.filter((row) => /^\d+\.\d+\.\d+$/.test(row.version) && Number.isFinite(Date.parse(row.cutAt)))
|
|
1112
|
+
.sort((a, b) => versionOrder(a.version, b.version));
|
|
1113
|
+
const shipped = releasedVersion(releaseRoot);
|
|
1114
|
+
const current = schedules.find((row) => Date.parse(row.cutAt) > now.getTime() && (!shipped || versionOrder(row.version, shipped) > 0));
|
|
1115
|
+
const itemsOf = deps.checklistItems ?? ((version: string) => listChecklist(version, releaseRoot).items);
|
|
1116
|
+
const handled = handledKeys(ledger, shadow, root, !shadow);
|
|
1117
|
+
const available = (item: SeatItem) => eligibleItem(item, ledger, handled, shadow);
|
|
1118
|
+
// The normal queue handles open owned cells first; a landed yellow cell is revisited here for physical rebuttal.
|
|
1119
|
+
for (const row of schedules.filter((row) => row.version === current?.version || Date.parse(row.cutAt) <= now.getTime() || (shipped && versionOrder(row.version, shipped) <= 0))) {
|
|
1120
|
+
for (const cell of itemsOf(row.version).filter((cell) => cell.owner === seat && cell.status === 'yellow' && /착지|merged|병합/i.test(cell.evidence ?? '')).sort((a, b) => a.id.localeCompare(b.id))) {
|
|
1121
|
+
const item = idleCell(seat, 2, cell.id, `착지 후 노랑 실물 반증: ${cell.id} · ${cell.title}`, now, row.version, seat, cell.evidence);
|
|
1122
|
+
item.evidenceHash = createHash('sha256').update(cell.evidence ?? '').digest('hex');
|
|
1123
|
+
if (available(item)) return item;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const next = schedules.find((row) => current && versionOrder(row.version, current.version) > 0);
|
|
1127
|
+
if (next) {
|
|
1128
|
+
const title = `${next.version} 다음 판 칸 저작: ${seat} 자리의 자율성·효율성·동시성을 높일 칸을 근거와 실물 반증부터 작성`;
|
|
1129
|
+
const item = idleCell(seat, 3, `author:${seat}`, title, now, next.version);
|
|
1130
|
+
if (available(item)) return item;
|
|
1131
|
+
}
|
|
1132
|
+
const recurring = idleCell(seat, 4, `recurring:${seat}:${seatDay(now)}`, IDLE_WORK[seat], now);
|
|
1133
|
+
if (available(recurring)) return recurring;
|
|
1134
|
+
const queue = (deps.queueItems ?? ((stateRoot: string) => listHarnessQueue({ root: stateRoot })))(root);
|
|
1135
|
+
const candidates: Array<{ owner: SeatId; item: SeatItem; pressure: number }> = [];
|
|
1136
|
+
for (const owner of ['OP', 'MK', 'UX', 'TC'] as const) {
|
|
1137
|
+
if (owner === seat) continue;
|
|
1138
|
+
const ownerLedger = readSeatLedger(owner, deps, now);
|
|
1139
|
+
const busy = queue.filter((row) => row.seat === owner && ['queued', 'launching', 'launched'].includes(row.status)).length;
|
|
1140
|
+
for (const row of schedules.filter((row) => Date.parse(row.cutAt) > now.getTime() && (!shipped || versionOrder(row.version, shipped) > 0))) {
|
|
1141
|
+
for (const cell of itemsOf(row.version).filter((cell) => cell.owner === owner && cell.status === 'yellow')) {
|
|
1142
|
+
const evidence = cell.evidence ?? '';
|
|
1143
|
+
const runIds = [...evidence.matchAll(RUN_REFERENCE)].map(([id]) => id.toLowerCase());
|
|
1144
|
+
if (runIds.length) {
|
|
1145
|
+
const observed = (deps.cutoffRuns ?? ((ids: readonly string[]) => queryRunningRuns({ runIds: ids, caller: 'seat-loop.idle' })))(runIds);
|
|
1146
|
+
if (observed.completeness !== 'complete' || observed.pty.unreadable.length || observed.entries.some((entry) => entry.status !== 'ended-unclosed')) continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (ownerLedger.some((entry) => entry.item?.source === 'checklist' && entry.item.id === cell.id && entry.item.version === row.version
|
|
1149
|
+
&& ['queued', 'attempting', 'outcome-unknown', 'launched'].includes(entry.status))) continue;
|
|
1150
|
+
if (queue.some((entry) => entry.seat === owner && ['queued', 'launching', 'launched'].includes(entry.status)
|
|
1151
|
+
&& entry.input.includes(`체크리스트 칸 ${cell.id} ·`))) continue;
|
|
1152
|
+
const item = idleCell(seat, 5, `borrow:${owner}:${cell.id}`, `${row.version} ${cell.id} · ${cell.title} — ${seat} 지원 (주인 ${owner} 유지)`, now, row.version, owner, evidence);
|
|
1153
|
+
item.evidenceHash = createHash('sha256').update(evidence).digest('hex');
|
|
1154
|
+
if (available(item)) candidates.push({ owner, item, pressure: busy + itemsOf(row.version).filter((entry) => entry.owner === owner && entry.status === 'yellow').length });
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return candidates.sort((a, b) => b.pressure - a.pressure || a.owner.localeCompare(b.owner) || a.item.id.localeCompare(b.item.id))[0]?.item ?? null;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1037
1161
|
export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promise<SeatLoopResult> {
|
|
1038
|
-
if (
|
|
1162
|
+
if (!isSeatId(seat) || !registeredSeats().some(({ id }) => id === seat)) throw new Error(`unknown seat: ${seat}`);
|
|
1039
1163
|
const config = deps.config ?? getUserConfig().loops?.seat ?? { mode: 'shadow' };
|
|
1040
1164
|
if (config.mode === 'off') {
|
|
1041
1165
|
observe('skipped-off', { seat });
|
|
@@ -1060,6 +1184,25 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1060
1184
|
} catch (error) { observe('heartbeat-write-failed', { seat, error: String(error).slice(0, 200) }); }
|
|
1061
1185
|
const recorded = readSeatLedger(seat, deps, now);
|
|
1062
1186
|
const ledger = config.mode === 'live-safe' ? reconciledQueueLedger(recorded, stateRoot, deps) : recorded;
|
|
1187
|
+
const loopId = seat === 'MK' ? 'cmo-seat' : `${seat.toLowerCase()}-seat`;
|
|
1188
|
+
const heartbeatEveryMs = (SEAT_NEIGHBORS[seat]?.find((peer) => peer.id !== 'orchestrator')?.heartbeat.everyMinutes ?? 15) * 60_000;
|
|
1189
|
+
const leaseMs = heartbeatEveryMs * 2;
|
|
1190
|
+
const pulse = (health: 'healthy' | 'degraded', at: Date): void => {
|
|
1191
|
+
try { emitLoopHeartbeat(stateRoot, loopId, health, at, new Date(at.getTime() + leaseMs).toISOString()); }
|
|
1192
|
+
catch (error) { observe('neighbor-runtime-failed', { seat, error: String(error).slice(0, 200) }); }
|
|
1193
|
+
};
|
|
1194
|
+
pulse('healthy', (deps.now ?? (() => new Date()))());
|
|
1195
|
+
const watchNeighbors = (at: Date): void => {
|
|
1196
|
+
try { checkLoopNeighbors(stateRoot, loopId, SEAT_NEIGHBORS[seat] ?? [], at); }
|
|
1197
|
+
catch (error) { observe('neighbor-runtime-failed', { seat, error: String(error).slice(0, 200) }); }
|
|
1198
|
+
};
|
|
1199
|
+
// A long turn keeps both duties periodic: renew our lease and watch neighbors (incidents are idempotent).
|
|
1200
|
+
const heartbeatTimer = setInterval(() => {
|
|
1201
|
+
const at = (deps.now ?? (() => new Date()))();
|
|
1202
|
+
pulse('healthy', at);
|
|
1203
|
+
watchNeighbors(at);
|
|
1204
|
+
}, deps.heartbeatIntervalMs ?? heartbeatEveryMs / 2);
|
|
1205
|
+
heartbeatTimer.unref();
|
|
1063
1206
|
if (seat === 'OP') opDecidePass(config, deps, now, path, stateRoot, ledger);
|
|
1064
1207
|
let turnError: unknown;
|
|
1065
1208
|
try {
|
|
@@ -1259,8 +1402,14 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1259
1402
|
checklist: inputs.checklist.filter((candidate) => !ledger.some((row) => row.status === 'awaiting-resolution'
|
|
1260
1403
|
&& row.item && itemKey(row.item) === itemKey(candidate) && itemSnapshot(row.item) === itemSnapshot(candidate))) }, ledger,
|
|
1261
1404
|
{ shadow: config.mode === 'shadow', liveSafe: config.mode === 'live-safe', root: stateRoot, history: deps.checklistHistory ?? checklistHistory });
|
|
1262
|
-
const
|
|
1263
|
-
const
|
|
1405
|
+
const ownQueueEmpty = !pendingQuestion && !ordinary;
|
|
1406
|
+
const idleMinutes = ownQueueEmpty ? emptyQueueMinutes(ledger, now) : undefined;
|
|
1407
|
+
// IDLE-LADDER: off unless loops.seat.idleLadder is set; 'shadow' records the idle pick only, 'live' lets live-safe/on launch it.
|
|
1408
|
+
const idleShadow = config.mode === 'shadow' || config.idleLadder !== 'live';
|
|
1409
|
+
const item = pendingQuestion ?? ordinary ?? (ownQueueEmpty && !lastDecisionFailure && (config.idleLadder === 'shadow' || config.idleLadder === 'live')
|
|
1410
|
+
? pickIdleWork(seat as SeatId, deps, ledger, now, stateRoot, idleShadow) : null);
|
|
1411
|
+
const appendBase = deps.append ?? defaultAppend;
|
|
1412
|
+
const append: typeof appendBase = (file, row) => appendBase(file, idleMinutes === undefined ? row : { ...row, emptyQueueMinutes: idleMinutes });
|
|
1264
1413
|
const revisedProposal = item?.source === 'checklist' && ledger.some((row) => row.status === 'awaiting-resolution'
|
|
1265
1414
|
&& row.item && itemKey(row.item) === itemKey(item) && itemSnapshot(row.item) !== itemSnapshot(item));
|
|
1266
1415
|
if (config.mode === 'live-safe' && !pendingQuestion) {
|
|
@@ -1448,11 +1597,12 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1448
1597
|
}
|
|
1449
1598
|
if (!item && config.mode === 'live-safe' && tcDowngrade) return tcDowngrade;
|
|
1450
1599
|
const entry: SeatEntry = { seat, ts: now.toISOString(), at: now.toISOString(), status: item ? 'shadow' : 'skipped-empty',
|
|
1600
|
+
...(idleMinutes === undefined ? {} : { emptyQueueMinutes: idleMinutes }),
|
|
1451
1601
|
...(item ? { item, action: planned!.kind, ...(planned!.reason ? { reason: planned!.reason } : {}) } : { action: 'skipped-empty' as const }) };
|
|
1452
|
-
if (config.mode === 'shadow' || !item) {
|
|
1602
|
+
if (config.mode === 'shadow' || !item || (item.source === 'idle' && idleShadow)) {
|
|
1453
1603
|
if (!item && lastDecisionFailure) return lastDecisionFailure;
|
|
1454
1604
|
append(path, entry);
|
|
1455
|
-
if (config.mode === 'shadow') observe('shadow', { seat, item, status: entry.status });
|
|
1605
|
+
if (config.mode === 'shadow' || item?.source === 'idle') observe('shadow', { seat, item, status: entry.status });
|
|
1456
1606
|
return entry;
|
|
1457
1607
|
}
|
|
1458
1608
|
if (planned!.kind === 'wait') {
|
|
@@ -1669,8 +1819,12 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1669
1819
|
turnError = error;
|
|
1670
1820
|
throw error;
|
|
1671
1821
|
} finally {
|
|
1822
|
+
clearInterval(heartbeatTimer);
|
|
1672
1823
|
// A turn ends even when there is no picked item or a launch fails.
|
|
1673
|
-
|
|
1824
|
+
const endedAt = (deps.now ?? (() => new Date()))();
|
|
1825
|
+
pulse(turnError === undefined ? 'healthy' : 'degraded', endedAt);
|
|
1826
|
+
watchNeighbors(endedAt);
|
|
1827
|
+
try { judgeSeatNeighbors(seat, config, deps, stateRoot, endedAt); }
|
|
1674
1828
|
catch (error) {
|
|
1675
1829
|
observe('neighbor-judgment-failed', { seat, error: String(error).slice(0, 200) });
|
|
1676
1830
|
const neighbors = config.neighbors?.[seat as keyof NonNullable<SeatLoopConfig['neighbors']>] ?? [];
|
|
@@ -7,6 +7,10 @@ import { debug } from '../debug/log.js';
|
|
|
7
7
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
8
8
|
import { getUserConfig, type SeatLoopConfig } from '../user-config.js';
|
|
9
9
|
import { collectDraftMetrics, collectOverlapMetrics, countSalvagedToday, type DraftMetrics, type OverlapMetrics } from '../self-dev/draft-sweep.js';
|
|
10
|
+
import { queryRunningRuns } from '../self-implement/running-runs.js';
|
|
11
|
+
import { readTaskAgentCover } from '../cli/tasks-cli.js';
|
|
12
|
+
import { formatTaskAgentCover } from '../task-agent/cover.js';
|
|
13
|
+
import type { TasksCliDeps } from '../cli/tasks-cli.js';
|
|
10
14
|
import { seatDay, seatLedgerPath, type OpCandidate, type SeatEntry } from './seat-loop.js';
|
|
11
15
|
|
|
12
16
|
export type SeatReportDeps = {
|
|
@@ -19,8 +23,10 @@ export type SeatReportDeps = {
|
|
|
19
23
|
send?: (body: string, seat: string, pr: number) => Promise<void> | void;
|
|
20
24
|
draftMetrics?: (now: Date) => Promise<DraftMetrics>;
|
|
21
25
|
overlapMetrics?: (now: Date) => Promise<OverlapMetrics>;
|
|
26
|
+
coverLogs?: TasksCliDeps['coverLogs'];
|
|
22
27
|
/** DRAFT-NOT-ARCHIVE salvage branches since the KST day start; null = unreadable. */
|
|
23
28
|
salvagedToday?: (dayStart: Date, now: Date) => Promise<number | null>;
|
|
29
|
+
runningRuns?: (runIds: readonly string[]) => { running: number; unknown: number } | null;
|
|
24
30
|
};
|
|
25
31
|
export type SeatReportResult = { seat: string; date: string; body: string; posted: boolean };
|
|
26
32
|
const repoRoot = resolve(import.meta.dir, '../..');
|
|
@@ -144,8 +150,30 @@ export async function seatReport(seat: string, deps: SeatReportDeps = {}): Promi
|
|
|
144
150
|
}))(new Date(`${date}T00:00:00+09:00`), now);
|
|
145
151
|
} catch { salvagedToday = null; }
|
|
146
152
|
draftDetail += ` · 수확 가지 오늘 ${salvagedToday === null ? '못 잼' : salvagedToday}`;
|
|
153
|
+
try {
|
|
154
|
+
const cover = readTaskAgentCover({}, deps.coverLogs);
|
|
155
|
+
draftDetail += ` · TASK-AGENT cover ${formatTaskAgentCover(cover.rows[0]!)}${cover.unreadableStores ? ` (logs.db ${cover.unreadableStores}개 못 읽음)` : ''}`;
|
|
156
|
+
} catch {
|
|
157
|
+
draftDetail += ` · TASK-AGENT cover ${formatTaskAgentCover({ verb: 'land', byTaskAgent: null, total: null, observedActions: null, ratio: null, state: 'unreadable', liveActions: null, shadowActions: null, stewardTransition: 'unmeasured' })}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const runIds = [...new Set(entries.filter((entry) => entry.status === 'launched' && entry.runId).map((entry) => entry.runId!))];
|
|
161
|
+
let runningDetail = ' · 도는 런 0';
|
|
162
|
+
if (runIds.length) {
|
|
163
|
+
try {
|
|
164
|
+
const counts = (deps.runningRuns ?? ((ids: readonly string[]) => {
|
|
165
|
+
const observed = queryRunningRuns({ runIds: ids, caller: 'seat-report' });
|
|
166
|
+
if (observed.completeness !== 'complete') return null;
|
|
167
|
+
return {
|
|
168
|
+
running: observed.entries.filter((entry) => ids.includes(entry.runId) && entry.status === 'running').length,
|
|
169
|
+
unknown: observed.entries.filter((entry) => ids.includes(entry.runId) && (entry.status === 'probable-running' || entry.status === 'unknown')).length,
|
|
170
|
+
};
|
|
171
|
+
}))(runIds);
|
|
172
|
+
runningDetail = counts === null ? ' · 도는 런 못 잼'
|
|
173
|
+
: ` · 도는 런 ${counts.running}(불확실 ${counts.unknown})`;
|
|
174
|
+
} catch { runningDetail = ' · 도는 런 못 잼'; }
|
|
147
175
|
}
|
|
148
|
-
const body = `**[${seat}]** {{TS}} → 보고 ${date}: ${detail.join(' · ') || (classified.length ? 'OP 그림자 판단' : '원장 기록 없음')}${opDetail}${draftDetail}`;
|
|
176
|
+
const body = `**[${seat}]** {{TS}} → 보고 ${date}: ${detail.join(' · ') || (classified.length ? 'OP 그림자 판단' : '원장 기록 없음')}${opDetail}${draftDetail}${runningDetail}`;
|
|
149
177
|
const pr = (deps.config ?? getUserConfig().loops?.seat)?.reportPr;
|
|
150
178
|
const posted = deps.post === true && pr !== undefined;
|
|
151
179
|
if (posted) await (deps.send ?? ((text, id, number) => defaultSend(text, id, number, deps.repo ?? repoRoot)))(body, seat, pr);
|
|
@@ -852,7 +852,7 @@ export async function runAskLaunchFlow(
|
|
|
852
852
|
if (launchPreflight === false) return { kind: 'stopped-before-authoring' };
|
|
853
853
|
|
|
854
854
|
await deps.beforeAuthoring?.();
|
|
855
|
-
const authorAndPreflight = async (authorAskText: string = input.askText): Promise<{
|
|
855
|
+
const authorAndPreflight = async (authorAskText: string = input.askText, machineSuggestedTargetPaths: readonly string[] = []): Promise<{
|
|
856
856
|
readonly goalFile: string;
|
|
857
857
|
readonly decision: ReturnType<typeof decideAskPreflight>;
|
|
858
858
|
}> => {
|
|
@@ -950,6 +950,8 @@ export async function runAskLaunchFlow(
|
|
|
950
950
|
recentChangeWindowDays: input.recentChangeWindowDays,
|
|
951
951
|
openPrsLimit: OPEN_PR_LIMIT,
|
|
952
952
|
...(askOutsidePathHints.length > 0 ? { askOutsidePathHints } : {}),
|
|
953
|
+
// AUTO-TARGET: paths injected by the candidate search (not written in the ask) — a NOT-GROUNDED goal stops on them.
|
|
954
|
+
...(machineSuggestedTargetPaths.length > 0 ? { machineSuggestedTargetPaths } : {}),
|
|
953
955
|
declaredPathsRoot: deps.cwd(),
|
|
954
956
|
},
|
|
955
957
|
preflightDeps,
|
|
@@ -1032,7 +1034,7 @@ export async function runAskLaunchFlow(
|
|
|
1032
1034
|
}
|
|
1033
1035
|
authored = await authorAndPreflight(selectedPaths.length > 0
|
|
1034
1036
|
? `대상 경로: ${selectedPaths.join(' · ')}\n${input.askText}`
|
|
1035
|
-
: input.askText);
|
|
1037
|
+
: input.askText, selectedPaths);
|
|
1036
1038
|
deps.log('ask-auto-reauthor', {
|
|
1037
1039
|
candidatePathCount: candidates.paths.length,
|
|
1038
1040
|
firstGoalFile: first.goalFile,
|
|
@@ -97,7 +97,7 @@ function inspectLaunchingTree(cwd: string): LaunchingTreeProbe {
|
|
|
97
97
|
|
|
98
98
|
/** 발사 전 검사가 쓰는 조회 구현 — ⛔ 이 «한 벌»을 모든 표면이 공유한다. */
|
|
99
99
|
export async function buildAskPreflightDeps(options: BuildAskPreflightDepsOptions = {}): Promise<AskPreflightDeps & { inspectLaunchingTree(cwd: string): LaunchingTreeProbe }> {
|
|
100
|
-
const [{ tracedPathReferences, verbatimOriginalAsk }, { loadRunLedger, queryFederatedCompletedRunLedgers, queryFederatedInterruptedRunLedgers, queryFederatedUnfinishedRunLedgers }] = await Promise.all([
|
|
100
|
+
const [{ tracedPathReferences, verbatimOriginalAsk, isNotGroundedWithoutPersistentEvidence }, { loadRunLedger, queryFederatedCompletedRunLedgers, queryFederatedInterruptedRunLedgers, queryFederatedUnfinishedRunLedgers }] = await Promise.all([
|
|
101
101
|
import('../self-implement/goal-author.js'),
|
|
102
102
|
import('../self-implement/run-ledger.js'),
|
|
103
103
|
]);
|
|
@@ -166,6 +166,8 @@ export async function buildAskPreflightDeps(options: BuildAskPreflightDepsOption
|
|
|
166
166
|
// ⛔ ask 표지가 없으면 빈 배열 — 그때는 표시가 «안 붙는다»(지어내지 않는다).
|
|
167
167
|
askTargetPaths: (document: string) => parseAskTargetPathHints(verbatimOriginalAsk(document) ?? ''),
|
|
168
168
|
askTargetPathRejections: (document: string) => parseAskTargetPathHintsResult(verbatimOriginalAsk(document) ?? '').rejected,
|
|
169
|
+
// AUTO-TARGET: a NOT-GROUNDED goal (no persistent evidence) is a scope card — machine-suggested paths do not launch it.
|
|
170
|
+
notGroundedWithoutPersistentEvidence: isNotGroundedWithoutPersistentEvidence,
|
|
169
171
|
// ⭐ 충돌 문면이 경로마다 「대상/근거」를 말하게 한다 — 판정은 «안» 바꾼다(문면만 는다).
|
|
170
172
|
// ⛔ TRACED PATHS 는 「바꿀 파일」과 「근거로 읽은 파일」을 섞는다(실측: TRACED 8 · 변경 3).
|
|
171
173
|
// ⇒ 사람이 막혔을 때 「그게 근거였네」를 «즉시» 알 수 있어야 한다.
|
package/src/self-dev/dev-cli.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
|
45
45
|
import { resolveChildLlmPreference, type ChildLlmPreferenceInput, type ResolvedChildLlmPreference } from '../self-implement/child-llm-preference.js';
|
|
46
46
|
import { applyLaunchOverloadFailover, codexProviderNamed } from '../oauth/overload-failover-launch.js';
|
|
47
47
|
import { readCachedGrokQuota } from '../oauth/codex-account-store.js';
|
|
48
|
+
import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
48
49
|
import { queryAbandonedDraftPrs, type AbandonedDraftPr } from '../cli/logs-abandoned-draft-prs.js';
|
|
49
50
|
import { branchLineageSlug } from '../cli/pr-lineage.js';
|
|
50
51
|
import { decideDraft, type DraftTriagePr } from './draft-triage-rules.js';
|
|
@@ -1494,6 +1495,20 @@ export function formatChildLlmInterpretationLine(
|
|
|
1494
1495
|
return `[dev] child-llm: entered=${resolution.entered} · resolved=${resolution.resolvedId} · tier=${resolution.tier} · thinking=${resolution.supportsThinking}${tail}`;
|
|
1495
1496
|
}
|
|
1496
1497
|
|
|
1498
|
+
/** Only the weekly Grok credits period measures the weekly cap; missing or malformed periods are unmeasured. */
|
|
1499
|
+
export function grokWeeklyUsedPctFromUsageJson(stdout: string): number | undefined {
|
|
1500
|
+
try {
|
|
1501
|
+
const parsed = JSON.parse(stdout) as { rows?: Array<{ provider?: string; credits?: { status?: string; periodType?: string | null; usedPercent?: unknown } }> };
|
|
1502
|
+
if (!Array.isArray(parsed.rows)) return undefined;
|
|
1503
|
+
const percents = parsed.rows
|
|
1504
|
+
.filter((row) => row?.provider === 'grok' && row.credits?.status === 'ok'
|
|
1505
|
+
&& row.credits.periodType === 'USAGE_PERIOD_TYPE_WEEKLY')
|
|
1506
|
+
.map((row) => row.credits?.usedPercent)
|
|
1507
|
+
.filter((pct): pct is number => typeof pct === 'number' && Number.isFinite(pct) && pct >= 0 && pct <= 100);
|
|
1508
|
+
return percents.length > 0 ? Math.max(...percents) : undefined;
|
|
1509
|
+
} catch { return undefined; }
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1497
1512
|
/** `elanous usage --json` 의 grok 행 → 잔량 판정. 한 행이라도 100% 미만이면 usable, 모두 100% 이상이면 exhausted. */
|
|
1498
1513
|
export function grokQuotaFromUsageJson(stdout: string): 'usable' | 'exhausted' | 'unknown' {
|
|
1499
1514
|
try {
|
|
@@ -1519,17 +1534,75 @@ export function setLaunchGrokQuotaReaderForTesting(reader: (() => 'usable' | 'ex
|
|
|
1519
1534
|
launchGrokQuotaReaderForTesting = reader;
|
|
1520
1535
|
}
|
|
1521
1536
|
|
|
1537
|
+
let launchGrokWeeklyReaderForTesting: (() => number | undefined) | undefined;
|
|
1538
|
+
export function setLaunchGrokWeeklyReaderForTesting(reader: (() => number | undefined) | undefined): void {
|
|
1539
|
+
launchGrokWeeklyReaderForTesting = reader;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* 발사 한 번에 `elanous usage --json`(약 3초) 을 «한 번만» 부른다 — 주간 사용률(재배분)과 잔량(경고)이 같은 출력을 나눈다.
|
|
1544
|
+
* 한 CLI 프로세스 = 한 발사이므로 프로세스 안 60초 캐시로 충분하다(실패 null 도 캐시 — 같은 발사에서 20초 타임아웃을 두 번 물지 않게).
|
|
1545
|
+
*/
|
|
1546
|
+
let launchUsageJsonCache: { at: number; stdout: string | null } | undefined;
|
|
1547
|
+
const LAUNCH_USAGE_CACHE_MS = 60_000;
|
|
1548
|
+
export function runLaunchUsageJsonOnce(): string | null {
|
|
1549
|
+
const now = Date.now();
|
|
1550
|
+
if (launchUsageJsonCache && now - launchUsageJsonCache.at < LAUNCH_USAGE_CACHE_MS) return launchUsageJsonCache.stdout;
|
|
1551
|
+
let stdout: string | null = null;
|
|
1552
|
+
try {
|
|
1553
|
+
const cli = new URL('../../bin/elanous.mjs', import.meta.url).pathname;
|
|
1554
|
+
const r = spawnSync(process.execPath, [cli, ...(IN_TEST_PROCESS() || process.env.ELANOUS_TEST_MODE === '1' ? ['--test'] : []), 'usage', '--json'], { encoding: 'utf8', timeout: 20_000 });
|
|
1555
|
+
stdout = r.status === 0 ? r.stdout : null;
|
|
1556
|
+
} catch { stdout = null; }
|
|
1557
|
+
launchUsageJsonCache = { at: now, stdout };
|
|
1558
|
+
return stdout;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/** Launch's weekly percentage comes from the live usage period, not the coarse cached usable flag. */
|
|
1562
|
+
export function readGrokWeeklyUsedPctForLaunch(runUsage?: () => string | null): number | undefined {
|
|
1563
|
+
if (launchGrokWeeklyReaderForTesting && !runUsage) return launchGrokWeeklyReaderForTesting();
|
|
1564
|
+
try {
|
|
1565
|
+
const stdout = (runUsage ?? runLaunchUsageJsonOnce)();
|
|
1566
|
+
return stdout ? grokWeeklyUsedPctFromUsageJson(stdout) : undefined;
|
|
1567
|
+
} catch { return undefined; }
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
export function rebalanceGrokChildForLaunch(
|
|
1571
|
+
child: ChildLlmSelection,
|
|
1572
|
+
cap: number,
|
|
1573
|
+
used: number | undefined,
|
|
1574
|
+
runId?: string,
|
|
1575
|
+
deps: { write?: (line: string) => void; record?: typeof appendRunLedgerEntry } = {},
|
|
1576
|
+
): ChildLlmSelection {
|
|
1577
|
+
if (child.provider !== 'grok') return child;
|
|
1578
|
+
const measured = typeof used === 'number' && Number.isFinite(used) && used >= 0 && used <= 100;
|
|
1579
|
+
const rebalanced = measured && used >= cap;
|
|
1580
|
+
const selected = rebalanced
|
|
1581
|
+
? { ...child, provider: 'openai-codex', model: defaultChildLlmModel('openai-codex'), codexQuotaPolicy: 'credits' as const }
|
|
1582
|
+
: child;
|
|
1583
|
+
const reason = !measured ? '측정 불가' : rebalanced ? `grok 주간 사용률 ${used}% ≥ 상한 ${cap}%` : `grok 주간 사용률 ${used}% < 상한 ${cap}%`;
|
|
1584
|
+
if (rebalanced || !measured) (deps.write ?? ((line) => process.stderr.write(line)))(
|
|
1585
|
+
`[dev] child-llm ${child.provider} → ${selected.provider} · ${reason}\n`,
|
|
1586
|
+
);
|
|
1587
|
+
if (rebalanced || !measured) {
|
|
1588
|
+
const data = { requestedProvider: child.provider, selectedProvider: selected.provider,
|
|
1589
|
+
weeklyUsedPct: measured ? used : null, capPct: cap, reason, selection: child.source };
|
|
1590
|
+
// 실제 전환과 «측정 못 해 그대로 둠» 을 다른 이벤트로 — 관측에서 둘을 섞지 않는다.
|
|
1591
|
+
const event = rebalanced ? 'launch-provider-rebalanced' : 'launch-provider-weekly-unmeasured';
|
|
1592
|
+
debug.log('self-implement', event, { runId: runId ?? null, ...data });
|
|
1593
|
+
if (runId) (deps.record ?? appendRunLedgerEntry)({ timestamp: new Date().toISOString(), runId,
|
|
1594
|
+
event, data: { runId, ...data } });
|
|
1595
|
+
}
|
|
1596
|
+
return selected;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1522
1599
|
export function readGrokQuotaForLaunch(
|
|
1523
1600
|
deps: { readCached?: typeof readCachedGrokQuota; runUsage?: () => string | null } = {},
|
|
1524
1601
|
): 'usable' | 'exhausted' | 'unknown' {
|
|
1525
1602
|
if (launchGrokQuotaReaderForTesting && !deps.readCached && !deps.runUsage) return launchGrokQuotaReaderForTesting();
|
|
1526
1603
|
const cached = (deps.readCached ?? readCachedGrokQuota)();
|
|
1527
1604
|
if (cached !== 'unknown') return cached;
|
|
1528
|
-
const runUsage = deps.runUsage ??
|
|
1529
|
-
const cli = new URL('../../bin/elanous.mjs', import.meta.url).pathname;
|
|
1530
|
-
const r = spawnSync(process.execPath, [cli, 'usage', '--json'], { encoding: 'utf8', timeout: 20_000 });
|
|
1531
|
-
return r.status === 0 ? r.stdout : null;
|
|
1532
|
-
});
|
|
1605
|
+
const runUsage = deps.runUsage ?? runLaunchUsageJsonOnce;
|
|
1533
1606
|
let stdout: string | null = null;
|
|
1534
1607
|
try { stdout = runUsage(); } catch { stdout = null; }
|
|
1535
1608
|
return stdout ? grokQuotaFromUsageJson(stdout) : 'unknown';
|
|
@@ -1851,6 +1924,7 @@ export function buildDriveAliasDevSpec(
|
|
|
1851
1924
|
command: string | undefined,
|
|
1852
1925
|
opts: DevCliOpts,
|
|
1853
1926
|
explicitOptionNames?: readonly string[],
|
|
1927
|
+
runId?: string,
|
|
1854
1928
|
): DevPipelineSpec {
|
|
1855
1929
|
if (opts.elanous === true) {
|
|
1856
1930
|
throw new DevPipelineError('drive: --elanous 는 지원하지 않음 — TUI target은 elanous dev --elanous 를 사용');
|
|
@@ -1861,7 +1935,7 @@ export function buildDriveAliasDevSpec(
|
|
|
1861
1935
|
if (!opts.goal?.trim()) {
|
|
1862
1936
|
throw new DevPipelineError('drive: --goal 필요');
|
|
1863
1937
|
}
|
|
1864
|
-
const spec = buildDevCliSpec({ text: command.trim() }, { kind: 'self' }, opts, explicitOptionNames, 'cli-drive');
|
|
1938
|
+
const spec = buildDevCliSpec({ text: command.trim() }, { kind: 'self' }, opts, explicitOptionNames, 'cli-drive', undefined, undefined, runId);
|
|
1865
1939
|
return { ...spec, completion: 'worktree-only', autoReview: false };
|
|
1866
1940
|
}
|
|
1867
1941
|
|
|
@@ -1872,6 +1946,9 @@ export function buildDevCliSpec(
|
|
|
1872
1946
|
explicitOptionNames?: readonly string[],
|
|
1873
1947
|
entrance: EntranceId = 'cli-dev-ask',
|
|
1874
1948
|
readGrokQuota: typeof readCachedGrokQuota = readGrokQuotaForLaunch,
|
|
1949
|
+
readGrokWeekly: () => number | undefined = readGrokWeeklyUsedPctForLaunch,
|
|
1950
|
+
runId?: string,
|
|
1951
|
+
recordLaunch?: typeof appendRunLedgerEntry,
|
|
1875
1952
|
): DevPipelineSpec {
|
|
1876
1953
|
if (opts.plan === true) {
|
|
1877
1954
|
throw new DevPipelineError(`--plan 은 은퇴했고 명시적으로 거부됨 · 대응 문: ${DEV_PLAN_REPLACEMENT}`);
|
|
@@ -2016,8 +2093,13 @@ export function buildDevCliSpec(
|
|
|
2016
2093
|
}
|
|
2017
2094
|
|
|
2018
2095
|
if (path === 'self-mission') {
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2096
|
+
const requestedChildLlm = buildChildLlmSelection({ ...opts, readPreference: () => getUserConfig() });
|
|
2097
|
+
// 주간 상한 재배분(grok → codex)을 먼저, 과부하 페일오버(codex → grok)를 그 뒤에 — 과부하는 «지금 실패 중»이라 더 강한 신호다.
|
|
2098
|
+
const capped = requestedChildLlm?.provider === 'grok'
|
|
2099
|
+
? rebalanceGrokChildForLaunch(requestedChildLlm, getUserConfig().llm.grokWeeklyCapPct ?? 80, readGrokWeekly(), runId,
|
|
2100
|
+
recordLaunch ? { record: recordLaunch } : {})
|
|
2101
|
+
: requestedChildLlm;
|
|
2102
|
+
const childLlm = applyLaunchOverloadFailover(capped, {
|
|
2021
2103
|
provider: opts.childLlmProvider,
|
|
2022
2104
|
model: opts.childLlmModel,
|
|
2023
2105
|
codexPinned: childLlmCodexPinned(opts.childLlmProvider),
|
|
@@ -43,7 +43,8 @@ import { addSelfDevRunParticipant, processBirthId, saveSelfDevRun } from './run-
|
|
|
43
43
|
import { getUserConfig, resolveRoleLlm } from '../user-config.js';
|
|
44
44
|
import { reviewReasoningEffort } from '../model-tier/review-effort.js';
|
|
45
45
|
import { getProvider, inferProviderFromModel } from '../llm.js';
|
|
46
|
-
import { buildReviewProviderAttempts, defaultReviewFallbackModel, runReviewWithFallback, reviewFallbackModelsFromConfig } from './review-provider-fallback.js';
|
|
46
|
+
import { buildReviewProviderAttempts, defaultReviewFallbackModel, runReviewWithFallback, reviewFallbackModelsFromConfig, runSubscriptionReviewer, type SubscriptionReviewerDeps } from './review-provider-fallback.js';
|
|
47
|
+
import { loadLlmPolicy } from '../policy/llm-policy.js';
|
|
47
48
|
import type { DefaultSeamsOptions } from '../self-implement/seams.js';
|
|
48
49
|
import { harnessTargetOptions, resolveHarnessTarget, revalidateHarnessTarget, type HarnessTargetResolution } from '../self-implement/harness-target-options.js';
|
|
49
50
|
import { provisionRepository, type RepoProvisionResult } from '../self-implement/repo-provision.js';
|
|
@@ -1077,6 +1078,8 @@ export async function buildDefaultSelfImplementSeams(
|
|
|
1077
1078
|
toolReviewerRoll?: () => number;
|
|
1078
1079
|
/** Tool-reviewer factory (same prompt→text shape as llmReview). Tests stub this; default is ACP. */
|
|
1079
1080
|
makeToolReviewLLM?: () => UnmannedToolReviewLLM | Promise<UnmannedToolReviewLLM>;
|
|
1081
|
+
subscriptionReviewerDeps?: SubscriptionReviewerDeps;
|
|
1082
|
+
makeSubscriptionReviewLLM?: (spawn: ReturnType<typeof import('./review-provider-fallback.js').subscriptionReviewerSpawn>) => (prompt: string) => Promise<string>;
|
|
1080
1083
|
} = {},
|
|
1081
1084
|
): Promise<SelfImplementSeams> {
|
|
1082
1085
|
const { defaultSeams } = await import('../self-implement/seams.js');
|
|
@@ -1143,6 +1146,21 @@ export async function buildDefaultSelfImplementSeams(
|
|
|
1143
1146
|
reviewerCanSelfRead = false;
|
|
1144
1147
|
}
|
|
1145
1148
|
}
|
|
1149
|
+
let subscriptionReviewLLM: ((prompt: string) => Promise<string>) | undefined;
|
|
1150
|
+
if (reviewerIsDefaultApiCall) {
|
|
1151
|
+
const userConfig = getUserConfig();
|
|
1152
|
+
const policy = loadLlmPolicy();
|
|
1153
|
+
const spec = policy.policy.roles.reviewer ?? userConfig.roleLlm?.reviewer;
|
|
1154
|
+
if (spec) {
|
|
1155
|
+
const cap = policy.policy.caps.claude.harness;
|
|
1156
|
+
subscriptionReviewLLM = (prompt) => runSubscriptionReviewer(spec, cap, prompt, defaultApiReview, async (text, spawn) => {
|
|
1157
|
+
if (deps.makeSubscriptionReviewLLM) return deps.makeSubscriptionReviewLLM(spawn)(text);
|
|
1158
|
+
const { makeAcpReviewLLM } = await import('../agent-substrate/acp-reviewer.js');
|
|
1159
|
+
return makeAcpReviewLLM({ cwd: targetOptions?.repoRoot ?? childScope.elanousBinRoot, backend: 'claude', backendSpec: spawn.backendSpec, env: spawn.env })(text);
|
|
1160
|
+
}, process.env, deps.subscriptionReviewerDeps);
|
|
1161
|
+
reviewerCanSelfRead = undefined;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1146
1164
|
const opensPr = plan.completion !== 'worktree-only';
|
|
1147
1165
|
const target = plan.target === undefined ? undefined : revalidateHarnessTarget(plan.target);
|
|
1148
1166
|
if (target?.status === 'outside-home') {
|
|
@@ -1159,6 +1177,7 @@ export async function buildDefaultSelfImplementSeams(
|
|
|
1159
1177
|
...(plan.self?.maxWaitSec !== undefined ? { implementMaxWaitSec: plan.self.maxWaitSec } : {}),
|
|
1160
1178
|
...(plan.self?.activityGraceSec !== undefined ? { implementActivityGraceSec: plan.self.activityGraceSec } : {}),
|
|
1161
1179
|
llmReview,
|
|
1180
|
+
...(subscriptionReviewLLM ? { subscriptionReviewLLM } : {}),
|
|
1162
1181
|
...(reviewerCanSelfRead !== undefined ? { reviewerCanSelfRead } : {}),
|
|
1163
1182
|
...(deps.reviewScopeDiff ? { reviewScopeDiff: deps.reviewScopeDiff } : {}),
|
|
1164
1183
|
onProgress: ({ stage, message }) => {
|