elanous 0.2.15 → 0.2.17
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/chunks/{1307-33ffcc49ab45123b.js → 1307-aac7332576c4d6fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6877-7c6208705c284124.js → 6877-fe94e76a5841ee9b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-0e2c57fe6add4b2a.js → page-3143b07a817d1da9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-e05a77df7e005b04.js → page-17ba086ef82d339b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-b8c49f6b4fa2df91.js → page-6e5c3a867557298b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-976813c91d7394ea.js → page-9a41d65355fc25b9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-1d4901eb7c2d8822.js → page-41afec0b255ed2bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-72d3e7ff35a8c954.js → page-4187666132fc98c4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-60c7a8e1a4fe62ce.js → page-09328d499162bae4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-2a52b316cb143d9c.js → page-29ff3a8f84f03b6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-c3ce483c8d5db370.js → page-80ec7401bc83f511.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-96f074f6538e8a2c.js → page-fc7d86467291ae38.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-e97be5334d83a9f5.js → layout-de43a186e5ba5683.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-c1343f9499d86f3f.js → page-043a9111e74f6b24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-8739a2e0ae793130.js → not-found-23a4601ceb7f452e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-d23b12bb92242558.js → page-10d958605a77b3bd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-c7fea475488201a8.js → page-a04260b8ccc968d4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-5efbf81124c474ae.js → page-12916918988ac97e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-06ee266953d9c879.js → page-5a64cb5208c6a8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-153c2b3bcc89e8de.js → page-1407ec83ea64c1f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-0d6d23b40263a7ea.js → page-7f49fe645d5132f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-73735b64bfe1ae67.js → page-424659bacfb8cdd7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-f7e631e9b4daf66e.js → page-fc2df261f8a2c7df.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-9817c7f9007466cc.js → page-5d4fef9ccd7d406b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +15 -15
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- 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 +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- 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 +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +34 -15
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +131 -9
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/docs-land-node.ts +69 -10
- package/scripts/release-loop/gate-node.ts +3 -1
- package/scripts/release-loop/publish-node.ts +2 -2
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/vault-note-node.ts +19 -2
- package/scripts/release-loop/version-node.ts +7 -4
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/monthly.ts +97 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-mission/driver.ts +104 -11
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +191 -36
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/cli/autopilot-cli.ts +66 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pr-cli.ts +92 -13
- package/src/cli/provider-cli.ts +14 -0
- package/src/cli/release-cli.ts +11 -8
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +399 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +9 -0
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +178 -12
- package/src/harness/harness-pod-dispatch.ts +36 -55
- package/src/harness/harness-queue.ts +86 -7
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +139 -8
- package/src/harness/helper-scan.ts +13 -2
- package/src/harness/nested-elanous-policy.ts +133 -0
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +16 -2
- package/src/hq/lease.ts +5 -3
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +168 -13
- package/src/intake-plane/digest.ts +85 -28
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/tick.ts +96 -25
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/maturity/tui-slash-availability.ts +9 -0
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/oauth/codex.ts +82 -2
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/release-loop/checklist.ts +10 -5
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +18 -10
- package/src/release-loop/placement.ts +17 -8
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-loop/seat-loop.ts +172 -21
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +6 -5
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +7 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +107 -25
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/orchestrator.ts +157 -8
- package/src/self-implement/run-ledger.ts +4 -0
- package/src/self-implement/seams.ts +44 -15
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/working-backwards.ts +16 -1
- package/src/task-agent/actions.ts +118 -0
- package/src/task-agent/judge.ts +96 -0
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +9 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +33 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +95 -18
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +168 -37
- package/src/task-orchestrator/surfaces/self-implement.ts +2 -0
- package/src/telegram-commands.ts +6 -1
- package/src/telegram-loops-command.ts +21 -14
- package/src/telegram.ts +47 -0
- package/src/user-config.ts +59 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5597e894c02cd33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +0 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +0 -1
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +0 -15
- package/src/launch-head/seat-goal-queue.ts +0 -198
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
|
|
3
|
+
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
4
|
+
import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
|
|
7
|
+
/** Directive row fields this search reads — kept local because `src/directives/directive-index` is excluded from the public export. */
|
|
8
|
+
export interface DirectiveRow { ts: string; text: string; source_file: string; line_no: number }
|
|
9
|
+
|
|
10
|
+
/** Loads the private directive index at run time only, so public builds bundle cleanly and report the directive source as unreadable. */
|
|
11
|
+
function openDirectiveIndex(): { search(q: string): DirectiveRow[]; close(): void } {
|
|
12
|
+
const spec = ['..', 'directives', 'directive-index.js'].join('/');
|
|
13
|
+
const mod = createRequire(import.meta.url)(spec) as { DirectiveIndex: new () => ReturnType<typeof openDirectiveIndex> };
|
|
14
|
+
return new mod.DirectiveIndex();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson';
|
|
18
|
+
export interface KnowHit {
|
|
19
|
+
source: KnowSource;
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
status: string;
|
|
23
|
+
at: string;
|
|
24
|
+
current: boolean;
|
|
25
|
+
ref: string;
|
|
26
|
+
}
|
|
27
|
+
export interface KnowResult {
|
|
28
|
+
rows: KnowHit[];
|
|
29
|
+
unavailable: Array<{ source: KnowSource; reason: string }>;
|
|
30
|
+
}
|
|
31
|
+
export interface KnowFindDeps {
|
|
32
|
+
directives: (query: string) => DirectiveRow[];
|
|
33
|
+
decisions: () => DecisionEntry[];
|
|
34
|
+
checklist: (version: string) => Checklist;
|
|
35
|
+
lessons: (query: string) => LessonRow[];
|
|
36
|
+
versions: () => string[];
|
|
37
|
+
now: () => Date;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function versions(): string[] {
|
|
41
|
+
const current = devVersion().replace(/-dev\.\d+$/, '');
|
|
42
|
+
const parts = current.split('.').map(Number);
|
|
43
|
+
return [current, `${parts[0]}.${parts[1]}.${parts[2]! + 1}`];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const defaults: KnowFindDeps = {
|
|
47
|
+
directives: (query) => {
|
|
48
|
+
const index = openDirectiveIndex();
|
|
49
|
+
try { return index.search(query); } finally { index.close(); }
|
|
50
|
+
},
|
|
51
|
+
decisions: () => new DecisionLedger().list({ status: 'all' }),
|
|
52
|
+
checklist: (version) => listChecklist(version),
|
|
53
|
+
lessons: (query) => new LessonLedger().find(query),
|
|
54
|
+
versions,
|
|
55
|
+
now: () => new Date(),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function matches(text: string, words: string[]): boolean {
|
|
59
|
+
const normalized = text.toLocaleLowerCase();
|
|
60
|
+
return words.every((word) => normalized.includes(word));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowResult {
|
|
64
|
+
const read = { ...defaults, ...deps };
|
|
65
|
+
const words = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
66
|
+
if (!words.length) throw new Error('know: 질문이 필요합니다');
|
|
67
|
+
const rows: KnowHit[] = [];
|
|
68
|
+
const unavailable: KnowResult['unavailable'] = [];
|
|
69
|
+
const attempt = (source: KnowSource, work: () => void): void => {
|
|
70
|
+
try { work(); }
|
|
71
|
+
catch (error) { unavailable.push({ source, reason: error instanceof Error ? error.message : String(error) }); }
|
|
72
|
+
};
|
|
73
|
+
const cutoff = read.now().getTime() - 30 * 86400000;
|
|
74
|
+
attempt('directive', () => {
|
|
75
|
+
for (const row of read.directives(query)) rows.push({ source: 'directive', id: `${row.source_file}:${row.line_no}`,
|
|
76
|
+
title: row.text.replace(/\s+/g, ' ').slice(0, 160), status: 'recorded', at: row.ts,
|
|
77
|
+
current: Number.isFinite(Date.parse(row.ts)) && Date.parse(row.ts) >= cutoff, ref: `${row.source_file}:${row.line_no}` });
|
|
78
|
+
});
|
|
79
|
+
attempt('decision', () => {
|
|
80
|
+
for (const row of read.decisions()) {
|
|
81
|
+
if (!matches([row.title, row.scqa.s, row.scqa.c, row.scqa.q, row.scqa.a].filter(Boolean).join(' '), words)) continue;
|
|
82
|
+
rows.push({ source: 'decision', id: row.id, title: row.title, status: row.status,
|
|
83
|
+
at: row.decidedAt ?? row.withdrawnAt ?? row.raisedAt ?? row.importedAt ?? '', current: row.status === 'open',
|
|
84
|
+
ref: row.refs?.[0] ?? `decisions show ${row.id}` });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
attempt('checklist', () => {
|
|
88
|
+
for (const version of new Set(read.versions())) {
|
|
89
|
+
for (const row of read.checklist(version).items) {
|
|
90
|
+
if (!matches(`${row.id} ${row.title}`, words)) continue;
|
|
91
|
+
rows.push({ source: 'checklist', id: row.id, title: row.title, status: row.status,
|
|
92
|
+
at: row.updatedAt, current: row.status === 'yellow', ref: `release checklist status --version ${version}` });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
attempt('lesson', () => {
|
|
97
|
+
for (const row of read.lessons(query)) rows.push({ source: 'lesson', id: row.id, title: row.incident,
|
|
98
|
+
status: row.status, at: row.updated_at, current: row.occurrence_count > 1, ref: `lesson show ${row.id}` });
|
|
99
|
+
});
|
|
100
|
+
rows.sort((a, b) => Number(b.current) - Number(a.current) || (Date.parse(b.at) || 0) - (Date.parse(a.at) || 0)
|
|
101
|
+
|| a.source.localeCompare(b.source) || a.id.localeCompare(b.id));
|
|
102
|
+
debug.log('knowledge.know', 'query', { query, counts: Object.fromEntries((['directive', 'decision', 'checklist', 'lesson'] as const)
|
|
103
|
+
.map(source => [source, rows.filter(row => row.source === source).length])), unavailable });
|
|
104
|
+
return { rows, unavailable };
|
|
105
|
+
}
|
package/src/llm.ts
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
type ProviderFallbackAttempt,
|
|
37
37
|
type ProviderFallbackTerminalVerdict,
|
|
38
38
|
} from './session-runtime/retry-policy.js';
|
|
39
|
-
import { fetchApiWithRetry } from './session-runtime/retry-api.js';
|
|
39
|
+
import { fetchApiWithRetry, OverloadFailoverError, type FetchApiWithRetryOpts } from './session-runtime/retry-api.js';
|
|
40
40
|
import { resolveModelAlias } from './intelligence-map/model-alias.js';
|
|
41
41
|
import { reasoningEffortCeiling as reasoningEffortCeilingOf } from './intelligence-map/model-catalog.js';
|
|
42
42
|
import { inferProviderFromModel as registryInferProviderFromModel } from './registry/normalize.js';
|
|
@@ -407,6 +407,10 @@ export interface LLMOpts {
|
|
|
407
407
|
background?: 'transparent' | 'opaque' | 'auto';
|
|
408
408
|
};
|
|
409
409
|
};
|
|
410
|
+
/** Unused configured fallbacks. The HTTP retry layer switches provider
|
|
411
|
+
* after k consecutive overload responses before spending those retries.
|
|
412
|
+
* Callers that already pinned a provider omit this (0). */
|
|
413
|
+
remainingFallbacks?: number;
|
|
410
414
|
}
|
|
411
415
|
|
|
412
416
|
export interface LLMProvider {
|
|
@@ -1310,6 +1314,20 @@ export async function* parseOpenAISSELines(
|
|
|
1310
1314
|
* the request URL. The OpenAI streamer is shared by openai/grok/local
|
|
1311
1315
|
* endpoints — all three pass through here. The string lands in the
|
|
1312
1316
|
* `llm.retry` debug snapshot and the `ApiHttpError.provider` field. */
|
|
1317
|
+
/** Retries still left for this HTTP call. Callers that already know a
|
|
1318
|
+
* fallback remains pass it so overload switches before those retries. */
|
|
1319
|
+
function retryOptsFor(
|
|
1320
|
+
provider: string,
|
|
1321
|
+
errorPrefix: string,
|
|
1322
|
+
remainingFallbacks?: number,
|
|
1323
|
+
): FetchApiWithRetryOpts {
|
|
1324
|
+
return {
|
|
1325
|
+
provider,
|
|
1326
|
+
errorPrefix,
|
|
1327
|
+
...(remainingFallbacks !== undefined && remainingFallbacks > 0 ? { remainingFallbacks } : {}),
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1313
1331
|
function inferOpenAIProviderName(url: string): string {
|
|
1314
1332
|
if (url.includes('x.ai')) return 'grok';
|
|
1315
1333
|
if (url.includes('openai.com')) return 'openai';
|
|
@@ -1326,6 +1344,7 @@ async function* streamOpenAIEvents(
|
|
|
1326
1344
|
* `x-grok-client-version` 이 빠지면 HTTP 426 으로 거절된다(실측).
|
|
1327
1345
|
* ⛔ 생략하면 종전과 «바이트 동일** — 기존 호출자 무회귀. */
|
|
1328
1346
|
extraHeaders?: Record<string, string>,
|
|
1347
|
+
remainingFallbacks?: number,
|
|
1329
1348
|
): AsyncGenerator<LLMStreamEvent, void, unknown> {
|
|
1330
1349
|
const headers: Record<string, string> = { 'Content-Type': 'application/json', ...(extraHeaders ?? {}) };
|
|
1331
1350
|
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
@@ -1353,10 +1372,7 @@ async function* streamOpenAIEvents(
|
|
|
1353
1372
|
body: JSON.stringify({ ...body, stream: true }),
|
|
1354
1373
|
signal,
|
|
1355
1374
|
},
|
|
1356
|
-
|
|
1357
|
-
provider: inferOpenAIProviderName(url),
|
|
1358
|
-
errorPrefix: 'LLM API',
|
|
1359
|
-
},
|
|
1375
|
+
retryOptsFor(inferOpenAIProviderName(url), 'LLM API', remainingFallbacks),
|
|
1360
1376
|
);
|
|
1361
1377
|
|
|
1362
1378
|
debug.log('llm.response.status', `${response.status} ${response.statusText || ''}`.trim(), {
|
|
@@ -2030,6 +2046,8 @@ export async function* streamCodexResponsesEvents(
|
|
|
2030
2046
|
* call. Best-effort; null/undefined if the backend skips the
|
|
2031
2047
|
* event. */
|
|
2032
2048
|
onResponseCreated?: (id: string) => void;
|
|
2049
|
+
/** Unused configured fallbacks. Overload k times switches before retries. */
|
|
2050
|
+
remainingFallbacks?: number;
|
|
2033
2051
|
} = {},
|
|
2034
2052
|
): AsyncGenerator<LLMStreamEvent, void, unknown> {
|
|
2035
2053
|
const startedAt = Date.now();
|
|
@@ -2118,10 +2136,7 @@ export async function* streamCodexResponsesEvents(
|
|
|
2118
2136
|
}),
|
|
2119
2137
|
signal,
|
|
2120
2138
|
},
|
|
2121
|
-
|
|
2122
|
-
provider: 'codex',
|
|
2123
|
-
errorPrefix: 'Codex API',
|
|
2124
|
-
},
|
|
2139
|
+
retryOptsFor('codex', 'Codex API', extra.remainingFallbacks),
|
|
2125
2140
|
);
|
|
2126
2141
|
debug.log('llm.response.status', `codex ${response.status}`, {
|
|
2127
2142
|
url, status: response.status, elapsedMs: Date.now() - startedAt,
|
|
@@ -3103,6 +3118,7 @@ async function* streamAnthropicEvents(
|
|
|
3103
3118
|
apiKey: string,
|
|
3104
3119
|
body: any,
|
|
3105
3120
|
signal?: AbortSignal,
|
|
3121
|
+
remainingFallbacks?: number,
|
|
3106
3122
|
): AsyncGenerator<LLMStreamEvent, void, unknown> {
|
|
3107
3123
|
// Wave 6 (2026-05-04) — Anthropic beta headers from
|
|
3108
3124
|
// ref/opencode `provider/provider.ts:181-185` analysis.
|
|
@@ -3139,10 +3155,7 @@ async function* streamAnthropicEvents(
|
|
|
3139
3155
|
body: JSON.stringify({ ...body, stream: true }),
|
|
3140
3156
|
signal,
|
|
3141
3157
|
},
|
|
3142
|
-
|
|
3143
|
-
provider: 'anthropic',
|
|
3144
|
-
errorPrefix: 'Anthropic API',
|
|
3145
|
-
},
|
|
3158
|
+
retryOptsFor('anthropic', 'Anthropic API', remainingFallbacks),
|
|
3146
3159
|
);
|
|
3147
3160
|
|
|
3148
3161
|
try {
|
|
@@ -4507,7 +4520,11 @@ export async function streamLLM(
|
|
|
4507
4520
|
const consumed = await consumeProviderText(
|
|
4508
4521
|
activeProvider,
|
|
4509
4522
|
messages,
|
|
4510
|
-
{
|
|
4523
|
+
{
|
|
4524
|
+
...opts,
|
|
4525
|
+
model: activeModel,
|
|
4526
|
+
remainingFallbacks: opts.provider ? 0 : remainingFallbackProviderNames(attemptedProviders, opts).length,
|
|
4527
|
+
},
|
|
4511
4528
|
onChunk,
|
|
4512
4529
|
);
|
|
4513
4530
|
const full = consumed.full;
|
|
@@ -4528,6 +4545,22 @@ export async function streamLLM(
|
|
|
4528
4545
|
}
|
|
4529
4546
|
|
|
4530
4547
|
const err: any = consumed.error;
|
|
4548
|
+
if (err instanceof OverloadFailoverError && !opts.provider) {
|
|
4549
|
+
const switched = providerByName('grok');
|
|
4550
|
+
if (switched && !attemptedProviders.has(switched.name) && switched.available() && full.length === 0) {
|
|
4551
|
+
debug.log('llm.router', 'overload-failover', {
|
|
4552
|
+
blockedProvider: activeProvider.name,
|
|
4553
|
+
fallbackProvider: switched.name,
|
|
4554
|
+
streak: err.streak,
|
|
4555
|
+
status: err.status,
|
|
4556
|
+
kind: err.kind,
|
|
4557
|
+
}, { level: 'warn' });
|
|
4558
|
+
activeProvider = switched;
|
|
4559
|
+
modelOverride = undefined;
|
|
4560
|
+
attemptedProviders.add(activeProvider.name);
|
|
4561
|
+
continue;
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4531
4564
|
const reason = sanitizeProviderFailureReason(err?.message || String(err));
|
|
4532
4565
|
debug.log('llm.router.error', 'streamLLM', {
|
|
4533
4566
|
provider: activeProvider.name,
|
|
@@ -8350,9 +8383,11 @@ export async function streamLLMWithTools(
|
|
|
8350
8383
|
// `tools: activeTools` (not opts.tools) so mid-loop hydration reaches the
|
|
8351
8384
|
// provider on the next turn. turn-0 keeps opts.toolChoice; later turns
|
|
8352
8385
|
// revert to auto (see above).
|
|
8353
|
-
const buildTurnOpts = () =>
|
|
8354
|
-
|
|
8355
|
-
|
|
8386
|
+
const buildTurnOpts = () => {
|
|
8387
|
+
const remainingFallbacks = opts.provider ? 0 : remainingFallbackProviderNames(attemptedProviders, opts).length;
|
|
8388
|
+
const base = { ...opts, model: activeModel, tools: activeTools, remainingFallbacks };
|
|
8389
|
+
return turn === 0 ? base : { ...base, toolChoice: undefined };
|
|
8390
|
+
};
|
|
8356
8391
|
let turnOpts = buildTurnOpts();
|
|
8357
8392
|
// reasoning-heavy family(codex/claude/gemini/grok/local)는 침묵 추론 구간이 길어 idle watchdog 를
|
|
8358
8393
|
// family-aware 로 상향(2026-07-19 goal-exec → 아크4 전-family). fast/mini(gpt·other)만 45s 유지.
|
|
@@ -8390,6 +8425,26 @@ export async function streamLLMWithTools(
|
|
|
8390
8425
|
]);
|
|
8391
8426
|
} catch (err) {
|
|
8392
8427
|
if (idleTimer) clearTimeout(idleTimer);
|
|
8428
|
+
if (err instanceof OverloadFailoverError && !opts.provider) {
|
|
8429
|
+
const switched = providerByName('grok');
|
|
8430
|
+
if (switched && !attemptedProviders.has(switched.name) && switched.available() && evCount === 0) {
|
|
8431
|
+
debug.log('llm.router', 'overload-failover', {
|
|
8432
|
+
turn,
|
|
8433
|
+
blockedProvider: activeProvider.name,
|
|
8434
|
+
fallbackProvider: switched.name,
|
|
8435
|
+
streak: err.streak,
|
|
8436
|
+
status: err.status,
|
|
8437
|
+
kind: err.kind,
|
|
8438
|
+
}, { level: 'warn' });
|
|
8439
|
+
const finalized = finalizeStreamingProviderModel(switched, switched.defaultModel);
|
|
8440
|
+
activeProvider = finalized.provider;
|
|
8441
|
+
activeModel = finalized.model;
|
|
8442
|
+
attemptedProviders.add(activeProvider.name);
|
|
8443
|
+
turnOpts = buildTurnOpts();
|
|
8444
|
+
streamIter = undefined;
|
|
8445
|
+
continue;
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8393
8448
|
// 명시 provider 선택만 폴백 대상이 아니다 — `streamLLM` 과 같은 계약으로
|
|
8394
8449
|
// «원본 오류»를 그대로 올린다. 모델만 전달된 호출은 다음 provider의 기본 모델로 재시도한다.
|
|
8395
8450
|
if (opts.provider) throw err;
|
|
@@ -10958,6 +11013,7 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
10958
11013
|
// lastResponseId stays undefined and we fall back to full
|
|
10959
11014
|
// input on the next turn (safe — just no token savings).
|
|
10960
11015
|
onResponseCreated: useStore ? (id) => { lastResponseId = id; } : undefined,
|
|
11016
|
+
remainingFallbacks: opts.remainingFallbacks,
|
|
10961
11017
|
});
|
|
10962
11018
|
// Snapshot full input AFTER the stream completes so the next
|
|
10963
11019
|
// call's delta computation uses what the backend actually has
|
|
@@ -10991,7 +11047,7 @@ function makeCodexProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
10991
11047
|
...openAiTemperatureField(apiKeyModel, opts.temperature ?? 0.3),
|
|
10992
11048
|
...openAiOutputTokenField(apiKeyModel, opts.maxTokens ?? 2048),
|
|
10993
11049
|
...(tools ? { tools } : {}),
|
|
10994
|
-
}, opts.signal);
|
|
11050
|
+
}, opts.signal, undefined, opts.remainingFallbacks);
|
|
10995
11051
|
},
|
|
10996
11052
|
async *chat(messages, opts = {}) { yield* textOnly(this.streamChat!(messages, opts)); },
|
|
10997
11053
|
};
|
|
@@ -11085,7 +11141,7 @@ function makeAnthropicProvider(cfg: UCLLMConfig): LLMProvider {
|
|
|
11085
11141
|
...(effort ? { thinking: { type: 'adaptive' }, output_config: { effort } } : {}),
|
|
11086
11142
|
...anthropicTemperatureField(anthropicModel, thinkingActive, opts.temperature),
|
|
11087
11143
|
...(tools ? { tools } : {}),
|
|
11088
|
-
}, opts.signal);
|
|
11144
|
+
}, opts.signal, opts.remainingFallbacks);
|
|
11089
11145
|
},
|
|
11090
11146
|
async *chat(messages, opts = {}) { yield* textOnly(this.streamChat!(messages, opts)); },
|
|
11091
11147
|
};
|
|
@@ -6,6 +6,8 @@ import { loadRunLedger, resolveFederatedRunLedgerTargets } from '../../self-impl
|
|
|
6
6
|
import type { LogTarget } from '../../cli/logs-cli.js';
|
|
7
7
|
|
|
8
8
|
const DAY_MS = 24 * 60 * 60 * 1_000;
|
|
9
|
+
/** Host-side records a Pod-dispatched launch leaves when its run starts inside the Pod. */
|
|
10
|
+
const LAUNCH_ONLY_EVENTS = new Set(['launch-quota-policy', 'author-on-pod-receipt']);
|
|
9
11
|
const GH_LIMIT = 1000;
|
|
10
12
|
|
|
11
13
|
export type FinishMetric = 'launched' | 'landed' | 'landingRate' | 'staleDrafts' | 'conflictRatio' | 'unknownMergeable';
|
|
@@ -38,6 +40,7 @@ function defaultListStarts(cutoff: number, targets?: readonly LogTarget[]): Star
|
|
|
38
40
|
const childLedgers = new Set<string>();
|
|
39
41
|
let unreadableDirectories = 0;
|
|
40
42
|
let unresolvedDirectories = 0;
|
|
43
|
+
const launchOnlyIds = new Set<string>();
|
|
41
44
|
for (const { ledgerDirectory } of resolveFederatedRunLedgerTargets({ includeTest: true, ...(targets ? { targets } : {}) })) {
|
|
42
45
|
let dir: string;
|
|
43
46
|
try {
|
|
@@ -68,16 +71,24 @@ function defaultListStarts(cutoff: number, targets?: readonly LogTarget[]): Star
|
|
|
68
71
|
continue;
|
|
69
72
|
}
|
|
70
73
|
const start = entries.find(entry => entry.event === 'start');
|
|
71
|
-
|
|
74
|
+
// A Pod-dispatched launch leaves only a host launch record (launch-quota-policy · author-on-pod-receipt) — the run
|
|
75
|
+
// itself starts inside the Pod. That is still a launch: its time is the record's timestamp, else the ledger mtime.
|
|
76
|
+
const launchRecord = !start ? entries.find(entry => LAUNCH_ONLY_EVENTS.has(entry.event)) : undefined;
|
|
77
|
+
const startedAt = start?.timestamp ?? (launchRecord ? (launchRecord.timestamp ?? new Date(statSync(path).mtimeMs).toISOString()) : undefined);
|
|
78
|
+
if (!startedAt) {
|
|
72
79
|
if (!starts.has(runId)) unreadable.set(runId, { runId, reason: entries[0]?.event ?? 'start missing' });
|
|
73
80
|
continue;
|
|
74
81
|
}
|
|
75
|
-
|
|
82
|
+
// One runId can leave a host launch record in one directory and a Pod `start` in another: a real `start`
|
|
83
|
+
// always wins and is never overwritten by a launch record, whatever order the directories are read in.
|
|
84
|
+
if (launchRecord && starts.has(runId)) continue;
|
|
85
|
+
if (launchRecord) launchOnlyIds.add(runId); else launchOnlyIds.delete(runId);
|
|
86
|
+
starts.set(runId, { runId, startedAt });
|
|
76
87
|
unreadable.delete(runId);
|
|
77
88
|
}
|
|
78
89
|
}
|
|
79
90
|
debug.log('loop.orchestrator', 'finish-launch-population', {
|
|
80
|
-
directories: directories.size + unresolvedDirectories, launched: starts.size, childLedgers: childLedgers.size, unreadable: unreadable.size,
|
|
91
|
+
directories: directories.size + unresolvedDirectories, launched: starts.size, launchOnly: launchOnlyIds.size, childLedgers: childLedgers.size, unreadable: unreadable.size,
|
|
81
92
|
});
|
|
82
93
|
return { starts: [...starts.values()], unreadable: [...unreadable.values()], unreadableDirectories };
|
|
83
94
|
}
|
|
@@ -8,9 +8,11 @@ import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
|
8
8
|
import { getUserConfig } from '../../user-config.js';
|
|
9
9
|
import { debug } from '../../debug/log.js';
|
|
10
10
|
import { listSelfDevRuns, runSummaryLine, selfDevRunsDir } from '../../self-dev/run-store.js';
|
|
11
|
+
import { planAction, runSeatLoopOnce, type SeatItem } from '../../seat-loop/seat-loop.js';
|
|
12
|
+
import { harnessQueueOutcome } from '../../harness/harness-queue.js';
|
|
11
13
|
|
|
12
14
|
export type Window = '08' | '12' | '18';
|
|
13
|
-
export type Node = 'intake' | 'split' | 'place' | 'delegate' | 'reconcile' | 'report';
|
|
15
|
+
export type Node = 'intake' | 'split' | 'place' | 'delegate' | 'launch' | 'reconcile' | 'report';
|
|
14
16
|
export type Mode = 'off' | 'shadow' | 'live';
|
|
15
17
|
const SEAT_LOOP_IDS: Readonly<Record<string, string>> = { OP: 'op-seat', MK: 'cmo-seat', TC: 'tc-seat', UX: 'ux-seat' };
|
|
16
18
|
export interface Cell { id: string; title: string; seat?: string; host?: string; version?: string; cardId: string; origin: 'candidate' | 'flow1a'; priority?: 'P0' | 'P1' | 'P2'; predecessors?: string[] }
|
|
@@ -19,6 +21,9 @@ export interface TickState {
|
|
|
19
21
|
cards: TaskCard[]; skippedCards: Array<{ id: string; reason: string }>; cells: Cell[]; placed: number; unplaced?: number; delegated: number; wouldDelegate: number; rebalanced?: number; rebalanceBlocked?: Array<{ id: string; reason: string }>;
|
|
20
22
|
reconciled: { reached: number; progressing: number; blocked: number; unknown: number };
|
|
21
23
|
runSummaries?: string[];
|
|
24
|
+
launched?: Array<{ seat: string; status: string; key?: string; queueId?: string; runId?: string }>;
|
|
25
|
+
queueOutcomes?: Array<{ queueId: string; key?: string; outcome: string }>;
|
|
26
|
+
steps: Array<{ node: Node; reason: string; count: number; targetLoopId?: string }>;
|
|
22
27
|
nodes: Partial<Record<Node, 'ok' | 'skipped'>>;
|
|
23
28
|
missing: string[];
|
|
24
29
|
}
|
|
@@ -26,11 +31,13 @@ export interface TickState {
|
|
|
26
31
|
export type SplitAdapter = (card: TaskCard, opts?: { shadow?: boolean }) => Promise<Array<{ id: string; title: string; seat?: string; host?: string }>> | Array<{ id: string; title: string; seat?: string; host?: string }>;
|
|
27
32
|
/** RELPLAN1 `placeCell(input: PlacementCell)` — owner·priority·predecessors are required; the result carries no seat. */
|
|
28
33
|
export type PlacementInput = { id: string; title: string; owner: string; priority: 'P0' | 'P1' | 'P2'; predecessors: string[] };
|
|
29
|
-
export type PlaceAdapter = (cell: PlacementInput) => Promise<{ version?: string } | null> | { version?: string } | null;
|
|
34
|
+
export type PlaceAdapter = (cell: PlacementInput, deps?: { dryRun?: boolean; now?: Date }) => Promise<{ version?: string } | null> | { version?: string } | null;
|
|
30
35
|
export type RebalanceAdapter = (version: string) => Promise<{ decisions: unknown[]; blocked: Array<{ id: string; reason: string }> }> | { decisions: unknown[]; blocked: Array<{ id: string; reason: string }> };
|
|
31
36
|
export interface TickDeps {
|
|
32
37
|
root?: string; now?: Date; mode?: Mode; runId?: string; window?: Window;
|
|
33
38
|
cards?: () => TaskCard[]; split?: SplitAdapter; placeCell?: PlaceAdapter;
|
|
39
|
+
seatTurn?: (seat: string, root: string) => Promise<{ status: string; queueId?: string; runId?: string; item?: { source: string; id: string } }>;
|
|
40
|
+
queueOutcome?: (queueId: string, root: string) => 'pending' | 'unknown' | 'retryable' | 'succeeded';
|
|
34
41
|
loadAdapter?: (path: string, name: string) => Promise<unknown>;
|
|
35
42
|
checklist?: (version: string) => Pick<ChecklistItem, 'id' | 'status'>[];
|
|
36
43
|
observe?: (event: string, data: { loopId: string; runId: string; window: Window; node: Node; reason: string; count: number; targetLoopId?: string }) => void;
|
|
@@ -54,7 +61,10 @@ export function wishCards(cards: readonly TaskCard[]): TaskCard[] {
|
|
|
54
61
|
return cards.filter(card => card.status === 'open' && card.goalId.startsWith('wish:') &&
|
|
55
62
|
!card.sections.some(section => section.key === 'orch:split' || section.key.startsWith('orch:split:')));
|
|
56
63
|
}
|
|
57
|
-
export function reconciliation(cells: readonly Cell[], requests: readonly { key: string; status: string }[], checklist: (version: string) => Pick<ChecklistItem, 'id' | 'status'>[]
|
|
64
|
+
export function reconciliation(cells: readonly Cell[], requests: readonly { key: string; status: string }[], checklist: (version: string) => Pick<ChecklistItem, 'id' | 'status'>[],
|
|
65
|
+
queue: readonly { key: string; outcome: string }[] = []): TickState['reconciled'] {
|
|
66
|
+
// 체크리스트가 정본이고, 칸이 아직 체크리스트에 없을 때만 «그 칸의 의뢰 키»로 발사된 대기열 결과를 본다.
|
|
67
|
+
const queueByKey = new Map(queue.map(row => [row.key, row.outcome]));
|
|
58
68
|
const result = { reached: 0, progressing: 0, blocked: 0, unknown: 0 };
|
|
59
69
|
const byVersion = new Map<string, Pick<ChecklistItem, 'id' | 'status'>[]>();
|
|
60
70
|
const keys = new Set(requests.filter(row => row.status === 'queued').map(row => row.key));
|
|
@@ -65,7 +75,12 @@ export function reconciliation(cells: readonly Cell[], requests: readonly { key:
|
|
|
65
75
|
if (status === 'green' || status === 'done') result.reached++;
|
|
66
76
|
else if (status === 'yellow') result.progressing++;
|
|
67
77
|
else if (status === 'red') result.blocked++;
|
|
68
|
-
else
|
|
78
|
+
else {
|
|
79
|
+
const outcome = queueByKey.get(`orch:${cell.cardId}:${cell.id}`);
|
|
80
|
+
if (outcome === 'pending' || outcome === 'succeeded') result.progressing++;
|
|
81
|
+
else if (outcome === 'retryable') result.blocked++;
|
|
82
|
+
else result.unknown++;
|
|
83
|
+
}
|
|
69
84
|
}
|
|
70
85
|
return result;
|
|
71
86
|
}
|
|
@@ -116,7 +131,7 @@ function stateFile(root: string, runId: string): string {
|
|
|
116
131
|
if (!/^[a-zA-Z0-9._-]+$/.test(runId) || runId === '.' || runId === '..') throw new Error('Invalid orchestrator runId');
|
|
117
132
|
return join(root, 'loop', 'orchestrator', `${runId}.json`);
|
|
118
133
|
}
|
|
119
|
-
function restorePrevious(root: string, day: string, mode: Mode): Pick<TickState, 'cards' | 'cells'> | null {
|
|
134
|
+
function restorePrevious(root: string, day: string, mode: Mode): Pick<TickState, 'cards' | 'cells' | 'launched'> | null {
|
|
120
135
|
const dir = join(root, 'loop', 'orchestrator');
|
|
121
136
|
if (!existsSync(dir)) return null;
|
|
122
137
|
const states = readdirSync(dir).filter(name => /^[a-zA-Z0-9._-]+\.json$/.test(name))
|
|
@@ -126,15 +141,17 @@ function restorePrevious(root: string, day: string, mode: Mode): Pick<TickState,
|
|
|
126
141
|
states.sort((a, b) => a.name.localeCompare(b.name));
|
|
127
142
|
const cards = new Map<string, TaskCard>();
|
|
128
143
|
const cells = new Map<string, Cell>();
|
|
144
|
+
const launched = new Map<string, NonNullable<TickState['launched']>[number]>();
|
|
129
145
|
for (const { state } of states) {
|
|
130
146
|
for (const card of state.cards) cards.set(card.id, card);
|
|
131
147
|
for (const cell of state.cells) cells.set(`${cell.cardId}:${cell.id}`, cell);
|
|
148
|
+
for (const turn of state.launched ?? []) if (turn.queueId) launched.set(turn.queueId, turn);
|
|
132
149
|
}
|
|
133
|
-
return { cards: [...cards.values()], cells: [...cells.values()] };
|
|
150
|
+
return { cards: [...cards.values()], cells: [...cells.values()], launched: [...launched.values()] };
|
|
134
151
|
}
|
|
135
152
|
function initial(runId: string, window: Window, mode: Mode, day: string): TickState {
|
|
136
153
|
return { runId, window, mode, day, cards: [], skippedCards: [], cells: [], placed: 0, delegated: 0, wouldDelegate: 0,
|
|
137
|
-
reconciled: { reached: 0, progressing: 0, blocked: 0, unknown: 0 }, nodes: {}, missing: [] };
|
|
154
|
+
reconciled: { reached: 0, progressing: 0, blocked: 0, unknown: 0 }, steps: [], nodes: {}, missing: [] };
|
|
138
155
|
}
|
|
139
156
|
function graphRunId(): string | undefined {
|
|
140
157
|
const context = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
@@ -163,8 +180,9 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
163
180
|
const configured = deps.mode ?? getUserConfig().loops?.orchestrator?.mode;
|
|
164
181
|
const mode: Mode = configured === 'off' || configured === 'live' ? configured : 'shadow';
|
|
165
182
|
const state: TickState = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) as TickState : initial(runId, window, mode, kstDay(now));
|
|
183
|
+
state.steps ??= [];
|
|
166
184
|
if (state.window !== window || state.mode !== mode) throw new Error('orchestrator run window/mode changed');
|
|
167
|
-
const predecessor: Partial<Record<Node, Node>> = { split: 'intake', place: 'split', delegate: 'place',
|
|
185
|
+
const predecessor: Partial<Record<Node, Node>> = { split: 'intake', place: 'split', delegate: 'place', launch: 'delegate', reconcile: 'launch', report: 'reconcile' };
|
|
168
186
|
const preceding = predecessor[node];
|
|
169
187
|
if (preceding && !state.nodes[preceding]) throw new Error(`orchestrator ${node}: missing ${preceding} result`);
|
|
170
188
|
const loadAdapter = deps.loadAdapter ?? optionalAdapter;
|
|
@@ -174,6 +192,7 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
174
192
|
};
|
|
175
193
|
const observe = (event: string, reason = '', count = 0, targetLoopId?: string): void => {
|
|
176
194
|
const data = { loopId: 'orchestrator', runId, window, node, reason, count, ...(targetLoopId ? { targetLoopId } : {}) };
|
|
195
|
+
if (event !== 'tick') state.steps.push({ node, reason, count, ...(targetLoopId ? { targetLoopId } : {}) });
|
|
177
196
|
try { (deps.observe ?? ((name, payload) => debug.log('loop.orchestrator', name, payload)))(event, data); }
|
|
178
197
|
catch { /* observation must not change the run */ }
|
|
179
198
|
};
|
|
@@ -198,6 +217,7 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
198
217
|
if (!cell.id?.trim() || !cell.title?.trim()) throw new Error('invalid split cell');
|
|
199
218
|
state.cells.push({ cardId: card.id, id: cell.id, title: cell.title, origin: split ? 'flow1a' : 'candidate', ...(cell.seat ? { seat: cell.seat } : {}), ...(cell.host !== undefined ? { host: cell.host } : {}) });
|
|
200
219
|
}
|
|
220
|
+
observe('exchange', `split:${card.id}`, cells.length);
|
|
201
221
|
if (split && cells.length && mode === 'live') {
|
|
202
222
|
const store = new CardStore(root);
|
|
203
223
|
try {
|
|
@@ -209,15 +229,24 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
209
229
|
const place = deps.placeCell ?? await adapter<PlaceAdapter>('../../release-loop/placement.js', 'placeCell');
|
|
210
230
|
const available = typeof place === 'function';
|
|
211
231
|
if (!available) { state.missing.push('relplan1-absent'); observe('node-missing', 'relplan1-absent', state.cells.length); }
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
if (mode === 'shadow' && deps.placeCell) observe('exchange', 'relplan1-shadow-not-invoked', state.cells.length);
|
|
233
|
+
// 담당 없는 칸은 배치를 부르든 안 부르든 센다 — 배치 호출 여부와 집계를 묶지 않는다.
|
|
234
|
+
for (const cell of state.cells) {
|
|
235
|
+
if (cell.origin === 'flow1a' && (!cell.seat || !SEAT_LOOP_IDS[cell.seat])) { state.unplaced = (state.unplaced ?? 0) + 1; observe('exchange', 'unplaced-no-owner', 1); }
|
|
236
|
+
}
|
|
237
|
+
if (available && (mode === 'live' || !deps.placeCell)) for (const cell of state.cells) {
|
|
238
|
+
if (cell.origin !== 'flow1a' || !cell.seat || !SEAT_LOOP_IDS[cell.seat]) continue;
|
|
239
|
+
// RELPLAN's dryRun calculates the same release without writing a checklist in shadow.
|
|
240
|
+
try {
|
|
241
|
+
const placed = await place({ id: cell.id, title: cell.title, owner: cell.seat, priority: cell.priority ?? 'P2', predecessors: cell.predecessors ?? [] }, { dryRun: mode === 'shadow', now });
|
|
242
|
+
if (validVersion(placed?.version)) {
|
|
243
|
+
cell.version = placed.version;
|
|
244
|
+
state.placed++;
|
|
245
|
+
observe('exchange', mode === 'shadow' ? `would-place:${cell.id}:${cell.version}` : `placed:${cell.id}:${cell.version}`, 1);
|
|
246
|
+
} else { state.unplaced = (state.unplaced ?? 0) + 1; observe('exchange', 'unplaced-no-release', 1); }
|
|
247
|
+
} catch (error) {
|
|
248
|
+
state.unplaced = (state.unplaced ?? 0) + 1;
|
|
249
|
+
observe('exchange', `unplaced:${cell.id}:${String(error).slice(0, 160)}`, 1);
|
|
221
250
|
}
|
|
222
251
|
}
|
|
223
252
|
} else if (node === 'delegate') {
|
|
@@ -229,9 +258,11 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
229
258
|
if (!targetLoopId) continue;
|
|
230
259
|
const key = `orch:${cell.cardId}:${cell.id}`;
|
|
231
260
|
if (keys.has(key)) continue;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
261
|
+
if (!validVersion(cell.version)) {
|
|
262
|
+
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; observe('would-delegate', `${key} (unplaced)`, 1, targetLoopId); }
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; observe('would-delegate', key, 1, targetLoopId); continue; }
|
|
235
266
|
keys.add(key);
|
|
236
267
|
mkdirSync(dirname(path), { recursive: true });
|
|
237
268
|
// TC review must-fix ①: the seat-requests journal contract needs receiptId, and appends are serialized under the
|
|
@@ -249,13 +280,53 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
249
280
|
state.delegated++;
|
|
250
281
|
observe('exchange', 'queued', 1, targetLoopId);
|
|
251
282
|
}
|
|
283
|
+
} else if (node === 'launch') {
|
|
284
|
+
const eligible = state.cells.filter(cell => cell.origin === 'flow1a' && cell.seat && SEAT_LOOP_IDS[cell.seat]
|
|
285
|
+
&& validVersion(cell.version) && (mode === 'shadow' || readRequests(root).some(row => row.key === `orch:${cell.cardId}:${cell.id}` && row.status === 'queued')));
|
|
286
|
+
const seats = [...new Set(eligible.map(cell => cell.seat!))];
|
|
287
|
+
state.launched = [];
|
|
288
|
+
for (const seat of seats) {
|
|
289
|
+
if (mode === 'shadow') {
|
|
290
|
+
const cell = eligible.find(row => row.seat === seat)!;
|
|
291
|
+
const item: SeatItem = { source: 'request', id: `orch:${cell.cardId}:${cell.id}`, title: cell.title, text: cell.title };
|
|
292
|
+
const action = planAction(item, seat);
|
|
293
|
+
state.launched.push({ seat, key: item.id, status: action.kind === 'harness' ? 'would-launch' : `would-${action.kind}` });
|
|
294
|
+
observe('exchange', `would-${action.kind}:${seat}`, 1, SEAT_LOOP_IDS[seat]);
|
|
295
|
+
} else {
|
|
296
|
+
const turn = await (deps.seatTurn ?? ((assigned: string, stateRoot: string) => runSeatLoopOnce(assigned, {
|
|
297
|
+
root: stateRoot, config: { ...getUserConfig().loops?.seat, mode: 'live-safe', seats: [assigned] },
|
|
298
|
+
})))(seat, root);
|
|
299
|
+
// 자리 턴이 «이 틱이 넣은 의뢰»를 처리했을 때만 칸에 잇는다 — 대기열의 다른 의뢰는 이 카드의 발사가 아니다.
|
|
300
|
+
const targets = new Set(eligible.filter(row => row.seat === seat).map(row => `orch:${row.cardId}:${row.id}`));
|
|
301
|
+
const handled = 'item' in turn && turn.item?.source === 'request' ? turn.item.id : undefined;
|
|
302
|
+
const key = handled && targets.has(handled) ? handled : undefined;
|
|
303
|
+
if (handled && !key) observe('exchange', `seat-turn-other-request:${seat}`, 1, SEAT_LOOP_IDS[seat]);
|
|
304
|
+
state.launched.push({ seat, status: turn.status, ...(key ? { key } : {}),
|
|
305
|
+
...('queueId' in turn && turn.queueId ? { queueId: turn.queueId } : {}),
|
|
306
|
+
...('runId' in turn && turn.runId ? { runId: turn.runId } : {}) });
|
|
307
|
+
observe('exchange', `seat-turn:${seat}:${turn.status}`, 1, SEAT_LOOP_IDS[seat]);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
252
310
|
} else if (node === 'reconcile') {
|
|
253
|
-
if (window !== '08'
|
|
311
|
+
if (window !== '08') {
|
|
312
|
+
// 새 카드가 있어도 앞 틱의 의뢰·발사를 같이 대조한다 — 지금 틱 것이 같은 키를 이긴다.
|
|
254
313
|
const previous = restorePrevious(root, state.day, mode);
|
|
255
|
-
if (previous) {
|
|
314
|
+
if (previous) {
|
|
315
|
+
const merge = <T>(older: readonly T[], newer: readonly T[], key: (row: T) => string) =>
|
|
316
|
+
[...new Map([...older, ...newer].map(row => [key(row), row])).values()];
|
|
317
|
+
state.cards = merge(previous.cards, state.cards, card => card.id);
|
|
318
|
+
state.cells = merge(previous.cells, state.cells, cell => `${cell.cardId}:${cell.id}`);
|
|
319
|
+
state.launched = merge(previous.launched ?? [], state.launched ?? [], turn => turn.queueId ?? `${turn.seat}:${turn.key ?? ''}`);
|
|
320
|
+
}
|
|
256
321
|
}
|
|
322
|
+
state.queueOutcomes = (state.launched ?? []).filter(turn => turn.queueId).map(turn => {
|
|
323
|
+
const outcome = (deps.queueOutcome ?? ((id: string, stateRoot: string) => harnessQueueOutcome(id, { root: stateRoot })))(turn.queueId!, root);
|
|
324
|
+
observe('exchange', `queue-outcome:${turn.queueId}:${outcome}`, 1, SEAT_LOOP_IDS[turn.seat]);
|
|
325
|
+
return { queueId: turn.queueId!, ...(turn.key ? { key: turn.key } : {}), outcome };
|
|
326
|
+
});
|
|
257
327
|
const requests = readRequests(root);
|
|
258
|
-
state.reconciled = reconciliation(state.cells, requests, mode === 'shadow' ? version => readChecklist(root, version) : (deps.checklist ?? (version => readChecklist(root, version)))
|
|
328
|
+
state.reconciled = reconciliation(state.cells, requests, mode === 'shadow' ? version => readChecklist(root, version) : (deps.checklist ?? (version => readChecklist(root, version))),
|
|
329
|
+
state.queueOutcomes.flatMap(row => row.key ? [{ key: row.key, outcome: row.outcome }] : []));
|
|
259
330
|
const from = new Date(`${state.day}T00:00:00+09:00`).getTime();
|
|
260
331
|
state.runSummaries = listSelfDevRuns(selfDevRunsDir(root))
|
|
261
332
|
.filter(run => run.updatedAt >= from && run.updatedAt < from + 86_400_000)
|
|
@@ -295,9 +366,9 @@ if (import.meta.main) {
|
|
|
295
366
|
const node = process.argv[2];
|
|
296
367
|
const index = process.argv.indexOf('--window');
|
|
297
368
|
const window = index < 0 ? undefined : process.argv[index + 1];
|
|
298
|
-
if (!['intake', 'split', 'place', 'delegate', 'reconcile', 'report'].includes(node ?? '') ||
|
|
369
|
+
if (!['intake', 'split', 'place', 'delegate', 'launch', 'reconcile', 'report'].includes(node ?? '') ||
|
|
299
370
|
(window !== undefined && !['08', '12', '18'].includes(window))) {
|
|
300
|
-
console.error('orchestrator: expected <intake|split|place|delegate|reconcile|report> [--window 08|12|18]');
|
|
371
|
+
console.error('orchestrator: expected <intake|split|place|delegate|launch|reconcile|report> [--window 08|12|18]');
|
|
301
372
|
process.exitCode = 2;
|
|
302
373
|
} else {
|
|
303
374
|
void (async () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SlashCommand } from '../chat/index.js';
|
|
2
2
|
import type { Maturity } from './feature-maturity.js';
|
|
3
3
|
import type { BotSurface } from './bot-command-maturity.js';
|
|
4
|
+
import { botUnavailableSlashReply, deriveTuiSlashAvailability, formatBotUnavailableSlashReply } from './tui-slash-availability.js';
|
|
4
5
|
|
|
5
6
|
export interface BotSlashCatalogEntry {
|
|
6
7
|
name: string;
|
|
@@ -56,11 +57,19 @@ export function buildBotSlashCatalog(options: {
|
|
|
56
57
|
commands.push({ name, description: `${description.slice(0, 100 - suffix.length)}${suffix}`, supported: false });
|
|
57
58
|
seen.add(name);
|
|
58
59
|
}
|
|
59
|
-
const
|
|
60
|
+
const handled = new Set(handledCommands.map(({ name }) => name));
|
|
61
|
+
const unsupported = new Set(commands.filter(({ supported }) => !supported).map(({ name }) => name));
|
|
62
|
+
const availability = new Map(deriveTuiSlashAvailability(
|
|
63
|
+
coreCommands.filter(({ name }) => Object.hasOwn(maturity.tuiSlash, name)), maturity,
|
|
64
|
+
).map((entry) => [entry.name, entry]));
|
|
60
65
|
return {
|
|
61
66
|
commands,
|
|
62
67
|
unsupportedReply(name) {
|
|
63
|
-
|
|
68
|
+
if (handled.has(name)) return null;
|
|
69
|
+
const entry = availability.get(name);
|
|
70
|
+
if (entry?.maturity === 'system') return null;
|
|
71
|
+
if (unsupported.has(name)) return formatBotUnavailableSlashReply(name, surface);
|
|
72
|
+
return botUnavailableSlashReply(entry, surface);
|
|
64
73
|
},
|
|
65
74
|
};
|
|
66
75
|
}
|
|
@@ -59,7 +59,7 @@ export const FEATURE_MATURITY = {
|
|
|
59
59
|
pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
60
60
|
where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
61
61
|
pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
|
|
62
|
-
research: 'beta', release: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
62
|
+
research: 'beta', release: 'beta', know: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
63
63
|
control: 'beta', resources: 'beta', hooks: 'beta', setup: 'stable', start: 'stable',
|
|
64
64
|
grounding: 'beta', graph: 'beta', loop: 'stable', card: 'stable', 'launch-head': 'beta',
|
|
65
65
|
a2a: 'beta', plugin: 'stable', make: 'beta', skills: 'beta', connect: 'beta', import: 'beta',
|
|
@@ -75,7 +75,7 @@ export const FEATURE_MATURITY = {
|
|
|
75
75
|
schedule: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
|
|
76
76
|
decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
|
|
77
77
|
env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
|
|
78
|
-
config: 'stable', tasks: 'stable', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
78
|
+
config: 'stable', tasks: 'stable', brief: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
79
79
|
onboarding: 'beta', login: 'stable', session: 'beta', chat: 'stable', repl: 'stable',
|
|
80
80
|
agent: 'stable', registry: 'beta', tier: 'stable', local: 'beta', scheduler: 'beta',
|
|
81
81
|
attach: 'beta', wf: 'beta', nexus: 'stable', phone: 'beta', token: 'beta',
|