elanous 0.2.14 → 0.2.16
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-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.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/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.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/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- 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 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- 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/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- 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 +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- 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 +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- 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 +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- 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/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- 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 +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_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
|
};
|
package/src/loops/budget.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface SpawnBudgetInput {
|
|
|
8
8
|
caps?: Partial<Record<OrchestratorSeat, number>>;
|
|
9
9
|
/** Release-gate seat limits. Missing seats use the gate default of four. */
|
|
10
10
|
gate?: Partial<Record<OrchestratorSeat, number>>;
|
|
11
|
+
/** Optional queue-injected cap; it may lower, never replace, the configured limit. */
|
|
12
|
+
injectedCap?: number;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export interface SpawnBudgetDecision {
|
|
@@ -16,13 +18,42 @@ export interface SpawnBudgetDecision {
|
|
|
16
18
|
cap: number;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
export type SeatCapDetails = {
|
|
22
|
+
cap: number;
|
|
23
|
+
seatCaps: number | '?';
|
|
24
|
+
releaseGate: number | '?';
|
|
25
|
+
injectedCap?: number | '?';
|
|
26
|
+
winners: ('releaseGate' | 'seatCaps' | 'injectedCap')[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Keep the cap calculation and the names shown to operators on the same source of truth. */
|
|
30
|
+
export function seatCapDetails({ seat, caps, gate, injectedCap }: Pick<SpawnBudgetInput, 'seat' | 'caps' | 'gate' | 'injectedCap'>): SeatCapDetails {
|
|
21
31
|
const configured = caps?.[seat] ?? ORCHESTRATOR_DEFAULTS.seatCaps[seat];
|
|
22
32
|
const gated = gate?.[seat] ?? 4;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
const valid = (value: number): boolean => Number.isSafeInteger(value) && value >= 0;
|
|
34
|
+
const seatCaps = valid(configured) ? configured : '?';
|
|
35
|
+
const releaseGate = valid(gated) ? gated : '?';
|
|
36
|
+
const injected = injectedCap === undefined ? undefined : valid(injectedCap) ? injectedCap : '?';
|
|
37
|
+
const cap = [seatCaps, releaseGate, injected].some((value) => value === '?') ? 0
|
|
38
|
+
: Math.min(seatCaps as number, releaseGate as number, injected === undefined ? Infinity : injected as number);
|
|
39
|
+
const winners: SeatCapDetails['winners'] = [];
|
|
40
|
+
if (seatCaps !== '?' && releaseGate !== '?' && injected !== '?') {
|
|
41
|
+
if (releaseGate === cap) winners.push('releaseGate');
|
|
42
|
+
if (seatCaps === cap) winners.push('seatCaps');
|
|
43
|
+
if (injected === cap) winners.push('injectedCap');
|
|
44
|
+
}
|
|
45
|
+
return { cap, seatCaps, releaseGate, ...(injected === undefined ? {} : { injectedCap: injected }), winners };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function seatCapReason(seat: OrchestratorSeat, active: number, details: SeatCapDetails): string {
|
|
49
|
+
const keys = (['releaseGate', 'seatCaps', 'injectedCap'] as const).filter((name) => name !== 'injectedCap' || details.injectedCap !== undefined);
|
|
50
|
+
const candidates = [...details.winners, ...keys.filter((name) => !details.winners.includes(name))];
|
|
51
|
+
return `seat ${seat}: ${active}/${details.cap} (${candidates.map((name) => `${name}.${seat}=${details[name]}`).join(' · ')})`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A release gate or injected queue cap can lower, but never raise, the configured per-seat cap. */
|
|
55
|
+
export function decideSpawn({ seat, running, caps, gate, injectedCap }: SpawnBudgetInput): SpawnBudgetDecision {
|
|
56
|
+
const { cap } = seatCapDetails({ seat, caps, gate, injectedCap });
|
|
26
57
|
if (!Number.isSafeInteger(running) || running === null || running === undefined || running < 0) {
|
|
27
58
|
return { allow: false, reason: 'unknown-running', cap };
|
|
28
59
|
}
|
package/src/loops/checker.ts
CHANGED
|
@@ -14,8 +14,8 @@ export type LoopScope = 'graph-only' | 'registry';
|
|
|
14
14
|
export interface CheckEntry {
|
|
15
15
|
id: string;
|
|
16
16
|
owner?: string;
|
|
17
|
-
ownerSource?: 'header' | 'config' | 'seat' | 'default';
|
|
18
|
-
evidence?: 'log-mtime';
|
|
17
|
+
ownerSource?: 'header' | 'config' | 'seat' | 'seat-arg' | 'default';
|
|
18
|
+
evidence?: 'log-mtime' | 'registry';
|
|
19
19
|
mode?: string;
|
|
20
20
|
expectEveryMinutes?: number;
|
|
21
21
|
/** The scheduled fire before the latest one (cron loops). A run older than this missed two scheduled fires. */
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { join } from 'node:path';
|
|
4
|
+
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
3
5
|
import { debug } from '../../debug/log.js';
|
|
4
6
|
import { defaultListHarnessProcesses } from '../../harness/harness-cli-command.js';
|
|
5
7
|
import { listHarnessQueue, type QueueItem } from '../../harness/harness-queue.js';
|
|
@@ -7,15 +9,19 @@ import { releaseLedgerRoot } from '../../instance/resolve.js';
|
|
|
7
9
|
import { listChecklist, type ChecklistItem } from '../../release-loop/checklist.js';
|
|
8
10
|
import { listSchedules } from '../../release-loop/release-schedule.js';
|
|
9
11
|
import { getUserConfig, ORCHESTRATOR_DEFAULTS, type OrchestratorSeat } from '../../user-config.js';
|
|
12
|
+
import { checkAuthorInput } from './author-input-check.js';
|
|
13
|
+
import { AuthorLedger } from './author-ledger.js';
|
|
10
14
|
import { collectWork, findOverlaps, type WorkItem } from './overlap.js';
|
|
11
15
|
import { seatOfTree, trafficTick, TRAFFIC_SEATS } from './traffic.js';
|
|
12
16
|
|
|
13
|
-
export type AuthorCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status'> & { version: string };
|
|
17
|
+
export type AuthorCell = Pick<ChecklistItem, 'id' | 'title' | 'owner' | 'status' | 'evidence'> & { version: string };
|
|
14
18
|
export type AuthorQueueRow = Pick<QueueItem, 'seat' | 'status'> & Partial<Pick<QueueItem, 'input' | 'kind'>>;
|
|
15
19
|
export type AuthorDepthResult = {
|
|
16
20
|
seats: Array<{ seat: OrchestratorSeat; cap: number | null; running: number | null; queued: number | null;
|
|
17
21
|
target: number | null; short: number | null; wouldAuthor: Array<{ cellId: string; version: string; title: string }> }>;
|
|
18
22
|
unreadable: Array<{ source: string; reason: string }>;
|
|
23
|
+
receiptCounts?: { held: number; queuedForAuthor: number };
|
|
24
|
+
receiptFailures?: number;
|
|
19
25
|
};
|
|
20
26
|
export type AuthorDepthInput = {
|
|
21
27
|
seats: readonly OrchestratorSeat[];
|
|
@@ -84,6 +90,7 @@ export function authorDepth({ seats, caps, running, queued, cells, overlaps, now
|
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
export interface CollectAuthorDepthDeps {
|
|
93
|
+
root?: string;
|
|
87
94
|
seats?: readonly OrchestratorSeat[];
|
|
88
95
|
now?: Date;
|
|
89
96
|
caps?: () => Partial<Record<OrchestratorSeat, number>>;
|
|
@@ -92,12 +99,13 @@ export interface CollectAuthorDepthDeps {
|
|
|
92
99
|
cells?: (version: string) => readonly AuthorCell[];
|
|
93
100
|
overlaps?: () => readonly WorkItem[];
|
|
94
101
|
versions?: () => readonly [string, string];
|
|
102
|
+
ledger?: Pick<AuthorLedger, 'request'>;
|
|
95
103
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
function defaultVersions(now: Date): readonly [string, string] {
|
|
99
|
-
if (!existsSync(join(
|
|
100
|
-
const upcoming = listSchedules().filter(row => row.cutAt && Date.parse(row.cutAt) > now.getTime())
|
|
106
|
+
function defaultVersions(now: Date, root = releaseLedgerRoot()): readonly [string, string] {
|
|
107
|
+
if (!existsSync(join(root, 'release', 'features.sqlite'))) throw new Error('release schedule ledger unavailable');
|
|
108
|
+
const upcoming = listSchedules(root).filter(row => row.cutAt && Date.parse(row.cutAt) > now.getTime())
|
|
101
109
|
.sort((a, b) => Date.parse(a.cutAt) - Date.parse(b.cutAt));
|
|
102
110
|
if (upcoming.length < 2) throw new Error('current and next release schedules unavailable');
|
|
103
111
|
return [upcoming[0]!.version, upcoming[1]!.version];
|
|
@@ -123,12 +131,13 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
|
|
|
123
131
|
if (measured.unassigned) throw new Error(`${measured.unassigned} unassigned harness launch process(es)`);
|
|
124
132
|
return Object.fromEntries(measured.seats.map(row => [row.seat, row.running]));
|
|
125
133
|
});
|
|
126
|
-
const queued = observe('queue', deps.queued ?? (() => listHarnessQueue()));
|
|
127
|
-
const
|
|
134
|
+
const queued = observe('queue', deps.queued ?? (() => listHarnessQueue({ root: deps.root })));
|
|
135
|
+
const releaseRoot = deps.root ?? releaseLedgerRoot();
|
|
136
|
+
const versions = observe('versions', deps.versions ?? (() => defaultVersions(now, releaseRoot)));
|
|
128
137
|
const cells = versions === null ? null : observe('checklist', () => versions.flatMap(version => {
|
|
129
|
-
if (!deps.cells && !existsSync(join(
|
|
138
|
+
if (!deps.cells && !existsSync(join(releaseRoot, 'release', 'features.sqlite')))
|
|
130
139
|
throw new Error('checklist ledger unavailable');
|
|
131
|
-
return deps.cells?.(version) ?? listChecklist(version).items.map(item => ({ ...item, version }));
|
|
140
|
+
return deps.cells?.(version) ?? listChecklist(version, deps.root).items.map(item => ({ ...item, version }));
|
|
132
141
|
}));
|
|
133
142
|
const overlaps = observe('overlap', deps.overlaps ?? (() => {
|
|
134
143
|
const observed = collectWork();
|
|
@@ -141,9 +150,56 @@ export function collectAuthorDepth(deps: CollectAuthorDepthDeps = {}): AuthorDep
|
|
|
141
150
|
for (const row of result.seats) log('loop.orchestrator', 'would-author', {
|
|
142
151
|
seat: row.seat, target: row.target, have: row.queued, short: row.short, cells: row.wouldAuthor,
|
|
143
152
|
});
|
|
153
|
+
const ledger = deps.ledger ?? new AuthorLedger(deps.root === undefined ? {} : { path: join(deps.root, 'orchestrator', 'author-ledger.sqlite') });
|
|
154
|
+
const receiptCounts = { held: 0, queuedForAuthor: 0 };
|
|
155
|
+
let receiptFailures = 0;
|
|
156
|
+
for (const row of result.seats) for (const candidate of row.wouldAuthor) {
|
|
157
|
+
const cell = cells?.find(item => item.id === candidate.cellId && item.version === candidate.version);
|
|
158
|
+
try {
|
|
159
|
+
if (!cell) throw new Error('selected cell unavailable');
|
|
160
|
+
const text = cell.evidence ?? '';
|
|
161
|
+
const check = checkAuthorInput({ title: cell.title, text, cellId: cell.id, version: cell.version });
|
|
162
|
+
const receipt = ledger.request({ seat: row.seat, cellId: cell.id, version: cell.version, title: cell.title,
|
|
163
|
+
text, check: { verdict: check.verdict, signals: check.signals, ratio: check.implementationRatio } });
|
|
164
|
+
if (receipt.status === 'held') receiptCounts.held++;
|
|
165
|
+
if (receipt.status === 'queued-for-author') receiptCounts.queuedForAuthor++;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
receiptFailures++;
|
|
168
|
+
result.unreadable.push({ source: 'author-ledger', reason: `${candidate.version} ${candidate.cellId}: ${error instanceof Error ? error.message : String(error)}` });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
result.receiptCounts = receiptCounts;
|
|
172
|
+
result.receiptFailures = receiptFailures;
|
|
144
173
|
return result;
|
|
145
174
|
}
|
|
146
175
|
|
|
176
|
+
const SHADOW_INTERVAL_MS = 30 * 60_000;
|
|
177
|
+
|
|
178
|
+
/** The queue tick serializes callers; stamp before collection so even a failing shadow cannot run every two minutes. */
|
|
179
|
+
export function runAuthorDepthShadow(root = effectiveInstanceRoot(), now = new Date(), collect = collectAuthorDepth): void {
|
|
180
|
+
const path = join(root, 'orchestrator', 'author-depth-shadow-at');
|
|
181
|
+
const time = now.getTime();
|
|
182
|
+
if (!Number.isFinite(time)) throw new Error('invalid author-depth shadow clock');
|
|
183
|
+
if (existsSync(path)) {
|
|
184
|
+
const last = Date.parse(readFileSync(path, 'utf8').trim());
|
|
185
|
+
if (!Number.isFinite(last)) throw new Error('invalid author-depth shadow marker');
|
|
186
|
+
if (time - last < SHADOW_INTERVAL_MS) return;
|
|
187
|
+
}
|
|
188
|
+
mkdirSync(join(root, 'orchestrator'), { recursive: true });
|
|
189
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
190
|
+
try {
|
|
191
|
+
writeFileSync(temporary, now.toISOString(), { flag: 'wx', mode: 0o600 });
|
|
192
|
+
renameSync(temporary, path);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
try { unlinkSync(temporary); } catch { /* No temporary file to remove. */ }
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
const result = collect({ root, now });
|
|
198
|
+
if (result.unreadable.length || result.receiptFailures || !result.receiptCounts) {
|
|
199
|
+
throw new Error(`author-depth shadow incomplete: ${JSON.stringify({ unreadable: result.unreadable, receiptFailures: result.receiptFailures })}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
147
203
|
export function authorDepthLines(result: AuthorDepthResult): string[] {
|
|
148
204
|
return result.seats.map(row => `${row.seat} ${row.running ?? '?'}/${row.cap ?? '?'} · 대기 ${row.queued ?? '?'} · 목표 ${row.target ?? '?'} · 모자람 ${row.short ?? '?'} → ${row.wouldAuthor.map(cell => cell.cellId).join(', ') || '없음'}`);
|
|
149
205
|
}
|
|
@@ -8,7 +8,7 @@ export interface AuthorInput {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface AuthorInputSignal {
|
|
11
|
-
kind: 'path' | 'symbol' | 'command' | 'marker';
|
|
11
|
+
kind: 'path' | 'symbol' | 'command' | 'marker' | 'missing';
|
|
12
12
|
match: string;
|
|
13
13
|
field: 'title' | 'text';
|
|
14
14
|
reason: string;
|
|
@@ -40,15 +40,19 @@ function sentences(value: string): string[] {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function checkAuthorInput({ title, text, cellId, version }: AuthorInput): AuthorInputCheck {
|
|
43
|
-
const
|
|
43
|
+
const validIdentity = [title, cellId, version].every(value => typeof value === 'string' && !!value.trim())
|
|
44
44
|
&& /^\d+\.\d+\.\d+$/.test(version);
|
|
45
|
+
const missingText = typeof text !== 'string' || !text.trim();
|
|
46
|
+
const valid = validIdentity && !missingText;
|
|
45
47
|
const signals: AuthorInputSignal[] = [];
|
|
48
|
+
if (validIdentity && missingText) signals.push({ kind: 'missing', match: '', field: 'text', reason: 'missing cell text' });
|
|
46
49
|
let checked = 0;
|
|
47
50
|
let candidates = 0;
|
|
48
51
|
let directed = false;
|
|
49
52
|
|
|
50
|
-
if (
|
|
53
|
+
if (validIdentity) {
|
|
51
54
|
for (const field of ['title', 'text'] as const) {
|
|
55
|
+
if (field === 'text' && missingText) continue;
|
|
52
56
|
for (const sentence of sentences(field === 'title' ? title : text)) {
|
|
53
57
|
checked++;
|
|
54
58
|
let found = false;
|
|
@@ -70,6 +74,7 @@ export function checkAuthorInput({ title, text, cellId, version }: AuthorInput):
|
|
|
70
74
|
const verdict: AuthorInputCheck['verdict'] = !valid ? 'uncheckable'
|
|
71
75
|
: signals.length === 0 ? 'approved' : directed ? 'resubmit' : 'confirm';
|
|
72
76
|
const implementationRatio = valid ? candidates / checked : null;
|
|
77
|
+
if (!valid) { checked = 0; candidates = 0; }
|
|
73
78
|
debug.log('author.par', 'input-checked', {
|
|
74
79
|
cellId, version, verdict, signals: signals.length, ratio: implementationRatio,
|
|
75
80
|
});
|
|
@@ -120,9 +120,11 @@ export class AuthorLedger {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
request(input: AuthorRequest): AuthorRow & { duplicate: boolean } {
|
|
123
|
-
for (const key of ['seat', 'cellId', 'version', 'title'
|
|
123
|
+
for (const key of ['seat', 'cellId', 'version', 'title'] as const) {
|
|
124
124
|
if (typeof input[key] !== 'string' || !input[key].trim()) throw new Error(`${key} is required`);
|
|
125
125
|
}
|
|
126
|
+
if (typeof input.text !== 'string' || (!input.text.trim() && input.check?.verdict !== 'uncheckable'))
|
|
127
|
+
throw new Error('text is required unless uncheckable');
|
|
126
128
|
if (!['approved', 'resubmit', 'confirm', 'uncheckable'].includes(input.check?.verdict)
|
|
127
129
|
|| !Array.isArray(input.check.signals)
|
|
128
130
|
|| (input.check.ratio !== null && (typeof input.check.ratio !== 'number' || !Number.isFinite(input.check.ratio))))
|
|
@@ -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
|
}
|