elanous 0.2.23 → 0.2.24
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/{272-b23d2e92161d11fd.js → 272-0eed0fb0aab716b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3961-fbef064c213b2b18.js → 3961-aaae04ed1d32622f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d5341ac96203a983.js → page-3b9fc39362473ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/{page-55c631c032dcc47e.js → page-d7602d489edf5f37.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-24282bf9f90031a5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/{page-5c5d0e370f495ef6.js → page-4eb84d454f8c3415.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-c325e21543cd48a8.js → page-b2e494bdad57fcb5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-6168a7c43e6ef8f5.js → page-c0ab229673a8b0eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-25bb425afdb27541.js → page-bf88c0f558d8ef54.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-32d72fdef1afe11a.js → page-52fec523e5b5d3ad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-2bd9ce9c020d02d4.js → page-22725e2570f1126b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-a34096245e63b9f2.js → page-1891335950d68b70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-4cc66d22ca4ec502.js → page-c314b75c11234998.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83b14c729fcea837.js → page-9f9c85415f61782b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-ccdb83ed40c8d724.js → page-8526c17ae38e6a50.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-7ad3de8fa06fdea9.js → page-f998637a8402115c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-786111f30b6f0969.js → page-6f23159fedb41f67.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-1d809a87139dd024.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-4e17cce5e2ed0fee.js → page-da9e570234d481fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b92caddb5c715f30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-c2dd2b19c93f90ab.js → page-57ffeeb6019440fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-d3c007c8a0ddd7ee.js → page-2c2b3f29cec1efa4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-7837ce2aacb97b04.js → page-9369279f72fc7b51.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-119be9a0444d2482.js → page-b5c0c996f329371e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-fa806c018833e332.js → page-89881ccbcf728978.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d431fbdea6ad93dd.js → page-58d4e5cb30ef4871.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-847c3de6ca8a4416.js → page-acf868c64d77f950.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-74e69ba2c8e970d4.js → page-bc79f0833533fa84.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-70038f41ae7cf931.js → page-d5c4398eea03d8f3.js} +1 -1
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -0
- package/package.json +1 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/ci-typecheck-changed.ts +71 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-node.ts +262 -26
- package/scripts/release-loop/resume-release.ts +9 -3
- package/scripts/release-loop/resume.ts +41 -0
- package/scripts/release-loop/unattended-release.ts +22 -7
- package/scripts/tsc-baseline-cache.ts +147 -0
- package/scripts/usage-rollup.ts +58 -6
- package/src/agent/run-context.ts +10 -0
- package/src/agent-mission/driver.ts +18 -3
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +8 -2
- package/src/agent-substrate/acp-implementer.ts +139 -0
- package/src/agent-substrate/pr-reviewer.ts +31 -4
- package/src/agent-substrate/self-review-cli.ts +48 -9
- package/src/autopilot/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/types.ts +8 -2
- package/src/budget/unified-usage.ts +88 -6
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/pr-cli.ts +62 -5
- package/src/cli/pr-land-overlap-evidence.ts +160 -12
- package/src/cli/release-cli.ts +18 -9
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/self-update.ts +104 -0
- package/src/cli/tasks-cli.ts +109 -4
- package/src/cli/usage-cli.ts +91 -8
- package/src/core-turn/run-goal-loop.ts +5 -4
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/index.ts +18 -276
- package/src/dashboard/stale-install-check.ts +11 -0
- package/src/debug/log-diet.ts +38 -7
- package/src/debug/log.ts +19 -1
- package/src/domains/release-tool.ts +39 -0
- package/src/error-report/report.ts +21 -4
- package/src/github/pr-labels.ts +1 -0
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +167 -6
- package/src/grok/usage.ts +23 -2
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +10 -2
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +122 -7
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-stop.ts +4 -2
- package/src/harness/harness-substrate-default.ts +142 -7
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/seats.ts +38 -22
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +93 -11
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intelligence-map/model-catalog.ts +72 -0
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +114 -41
- package/src/llm/bedrock.ts +174 -28
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +87 -16
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/model-tier/llm-tier-map.ts +9 -5
- package/src/models/prompts.ts +2 -1
- package/src/mss/logging/log-store.ts +58 -11
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/index.ts +7 -75
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +39 -2
- package/src/oauth/codex-account-store.ts +323 -24
- package/src/oauth/fallback-chain.ts +53 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- package/src/prompt-cache/openai.ts +3 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +23 -0
- package/src/release-loop/placement.ts +16 -1
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +5 -5
- package/src/seat-loop/seat-loop.ts +32 -10
- package/src/self-dev/dev-cli.ts +44 -10
- package/src/self-dev/dev-pipeline.ts +7 -2
- package/src/self-dev/draft-residue.ts +14 -2
- package/src/self-dev/draft-sweep.ts +103 -0
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/orchestrate-cli.ts +25 -3
- package/src/self-dev/orchestrate.ts +41 -6
- package/src/self-dev/run-supervisor.ts +188 -15
- package/src/self-dev/run-types.ts +13 -0
- package/src/self-dev/supervisor-verdict-edges.ts +1 -0
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +24 -9
- package/src/self-implement/graph-overlay-condition.ts +28 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/heal-intake.ts +5 -1
- package/src/self-implement/orchestrator.ts +144 -15
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/seams.ts +47 -1
- package/src/self-implement/self-implement-cli.ts +9 -4
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/steward/launch.ts +5 -4
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +83 -22
- package/src/task-agent/actions.ts +62 -5
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +101 -3
- package/src/task-agent/judge.ts +16 -0
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/live-move-history.ts +87 -1
- package/src/task-agent/live-moves.ts +350 -20
- package/src/task-agent/rejudge-on-head.ts +75 -20
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +53 -9
- package/src/task-agent/task-hand.ts +54 -7
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-pool.ts +20 -4
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +213 -23
- package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +51 -11
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
- package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
package/src/llm.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from './config.js';
|
|
22
22
|
import { debug, redactSecrets, redactSecretText } from './debug/log.js';
|
|
23
23
|
import { llmUsageCostFields } from './budget/llm-cost.js';
|
|
24
|
+
import { accumulateByokUsage } from './llm/openrouter-byok-cost.js';
|
|
24
25
|
import { ToolCallTiming } from './chat/tool-call-timing.js';
|
|
25
26
|
import {
|
|
26
27
|
dispatchWithParallelSafety,
|
|
@@ -41,6 +42,8 @@ import {
|
|
|
41
42
|
} from './session-runtime/retry-policy.js';
|
|
42
43
|
import { fetchApiWithRetry, OverloadFailoverError, type FetchApiWithRetryOpts } from './session-runtime/retry-api.js';
|
|
43
44
|
import { BedrockProvider, isBedrockCompatibleModel, makeBedrockProvider } from './llm/bedrock-provider.js';
|
|
45
|
+
import { makeZaiCodingPlanProvider, zaiCodingPlanAvailable } from './llm/zai-coding-plan.js';
|
|
46
|
+
import { isOpenRouterDeepSeekWireModel, nonEmptyReasoningDetails, openRouterDeepSeekCatalogEffort, openRouterDeepSeekEffort, openRouterDeepSeekRequestExtras, openRouterNeedsKimiToolSchema } from './llm/openrouter-deepseek.js';
|
|
44
47
|
import { resolveModelAlias } from './intelligence-map/model-alias.js';
|
|
45
48
|
import { BUILTIN_CATALOG, reasoningEffortCeiling as reasoningEffortCeilingOf } from './intelligence-map/model-catalog.js';
|
|
46
49
|
import { inferProviderFromModel as registryInferProviderFromModel } from './registry/normalize.js';
|
|
@@ -94,7 +97,8 @@ export interface ProviderToolMeta { thoughtSignature?: string; reasoningDetails?
|
|
|
94
97
|
export interface ToolArgsInvalid { chars: number; finishReason: string | null }
|
|
95
98
|
|
|
96
99
|
export type ContentBlock =
|
|
97
|
-
|
|
100
|
+
/** `providerMeta` — OR-DEEPSEEK-TUNE: OpenRouter deepseek/* 의 «도구 없는» assistant 턴 추론(reasoning_details) 운반. 다른 경로는 안 싣는다. */
|
|
101
|
+
| { type: 'text'; text: string; providerMeta?: ProviderToolMeta }
|
|
98
102
|
| { type: 'image'; mediaType: string; base64: string }
|
|
99
103
|
| { type: 'audio'; mediaType: string; base64: string }
|
|
100
104
|
| { type: 'video'; mediaType: string; base64: string }
|
|
@@ -318,7 +322,9 @@ export type LLMStreamEvent =
|
|
|
318
322
|
source?: string;
|
|
319
323
|
revisedPrompt?: string;
|
|
320
324
|
}
|
|
321
|
-
| { type: 'usage'; usage: import('./prompt-cache/types.js').LLMUsage }
|
|
325
|
+
| { type: 'usage'; usage: import('./prompt-cache/types.js').LLMUsage }
|
|
326
|
+
/** OR-DEEPSEEK-TUNE — 도구 호출이 없는 턴의 OpenRouter `reasoning_details`(스트림 끝에 한 번). deepseek/* 에서만 나온다. */
|
|
327
|
+
| { type: 'reasoning_details'; details: Array<Record<string, unknown>> };
|
|
322
328
|
|
|
323
329
|
/** Tool-calling forcing (opencode parity · 2026-07-09). `'auto'` (default) lets
|
|
324
330
|
* the model decide; `'required'` forces at least one tool call; `'none'` blocks
|
|
@@ -338,6 +344,8 @@ export interface LLMOpts {
|
|
|
338
344
|
onUsage?(usage: LLMUsage): void;
|
|
339
345
|
/** Optional model role attached to this request's usage observation. */
|
|
340
346
|
usageRole?: ModelRole;
|
|
347
|
+
/** Optional streamLLM usage-ledger site override (defaults to "stream-llm"). */
|
|
348
|
+
usageSite?: string;
|
|
341
349
|
/**
|
|
342
350
|
* ⭐⭐⭐ `B3`(2026-08-19 · 대표 지시 ②) — ***턴 «안»으로 들어오는 사용자 발화의 배수구.***
|
|
343
351
|
*
|
|
@@ -473,6 +481,8 @@ export interface ToOpenAIMessagesOpts {
|
|
|
473
481
|
acceptUserMessageImages?: boolean;
|
|
474
482
|
/** OpenRouter 전용 — 도구 호출이 든 어시스턴트 메시지에 `reasoning_details` 를 되돌린다. 다른 제공자엔 안 싣는다(모르는 칸 400 방지). */
|
|
475
483
|
echoReasoningDetails?: boolean;
|
|
484
|
+
/** OR-DEEPSEEK-TUNE — deepseek/* 만: 도구 없는 assistant 턴에도 되돌리고, 빈 조각은 걷는다(`src/llm/openrouter-deepseek.ts`). */
|
|
485
|
+
deepSeekReplay?: boolean;
|
|
476
486
|
}
|
|
477
487
|
|
|
478
488
|
/**
|
|
@@ -561,9 +571,10 @@ function toOpenAIMessages(
|
|
|
561
571
|
.filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')
|
|
562
572
|
.map(b => b.text)
|
|
563
573
|
.join('');
|
|
564
|
-
const
|
|
574
|
+
const foundDetails = opts.echoReasoningDetails
|
|
565
575
|
? toolUses.find((t) => t.providerMeta?.reasoningDetails?.length)?.providerMeta?.reasoningDetails
|
|
566
576
|
: undefined;
|
|
577
|
+
const reasoningDetails = opts.deepSeekReplay ? nonEmptyReasoningDetails(foundDetails) : foundDetails;
|
|
567
578
|
return [{
|
|
568
579
|
role: m.role,
|
|
569
580
|
content: textContent || null,
|
|
@@ -589,8 +600,12 @@ function toOpenAIMessages(
|
|
|
589
600
|
// Composer Q3=B is the primary handler; this is the wire-side
|
|
590
601
|
// defensive fallback for non-vision OpenAI / Grok / local routes.
|
|
591
602
|
const acceptUserImages = opts.acceptUserMessageImages ?? true;
|
|
603
|
+
const textTurnDetails = opts.deepSeekReplay && m.role === 'assistant'
|
|
604
|
+
? nonEmptyReasoningDetails(blocks.find((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text' && !!b.providerMeta?.reasoningDetails?.length)?.providerMeta?.reasoningDetails)
|
|
605
|
+
: undefined;
|
|
592
606
|
return [{
|
|
593
607
|
role: m.role,
|
|
608
|
+
...(textTurnDetails ? { reasoning_details: textTurnDetails } : {}),
|
|
594
609
|
content: blocks.map(block => {
|
|
595
610
|
if (block.type === 'text') return { type: 'text', text: block.text };
|
|
596
611
|
if (block.type === 'image') {
|
|
@@ -1126,7 +1141,7 @@ export async function* parseOpenAISSELines(
|
|
|
1126
1141
|
/** OR-TOOLARGS-REPAIR (2026-10-10) — `flagInvalidToolArgs` 가 참이면(OpenRouter 경로) 인자 JSON 파싱 실패를
|
|
1127
1142
|
* `{}` 로 «조용히» 바꾸지 않고 `argsInvalid` 표지를 단다(도구 루프가 실행을 막는다) ⊕ 관측을 남긴다.
|
|
1128
1143
|
* ⛔ 생략·거짓이면 종전과 바이트 동일(다른 공급자 무변경). */
|
|
1129
|
-
parseOpts: { flagInvalidToolArgs?: boolean } = {},
|
|
1144
|
+
parseOpts: { flagInvalidToolArgs?: boolean; emitTrailingReasoningDetails?: boolean } = {},
|
|
1130
1145
|
): AsyncGenerator<LLMStreamEvent, void, unknown> {
|
|
1131
1146
|
const flagInvalidToolArgs = parseOpts.flagInvalidToolArgs === true;
|
|
1132
1147
|
const { parseOpenAIUsage } = await import('./prompt-cache/openai.js');
|
|
@@ -1352,6 +1367,11 @@ export async function* parseOpenAISSELines(
|
|
|
1352
1367
|
yield { type: 'reasoning', kind: 'inline_delta', delta: tail.reasoningDelta };
|
|
1353
1368
|
}
|
|
1354
1369
|
yield* emitHarmonyTools(tail.toolCalls);
|
|
1370
|
+
// OR-DEEPSEEK-TUNE — 도구 호출에 실리지 못한 추론(텍스트만 낸 턴)을 끝에 한 번 낸다. deepseek/* 만 켠다.
|
|
1371
|
+
if (parseOpts.emitTrailingReasoningDetails) {
|
|
1372
|
+
const details = takeReasoningDetails();
|
|
1373
|
+
if (details) yield { type: 'reasoning_details', details };
|
|
1374
|
+
}
|
|
1355
1375
|
}
|
|
1356
1376
|
|
|
1357
1377
|
/** Pick a stable provider name for `fetchApiWithRetry` telemetry from
|
|
@@ -1430,7 +1450,8 @@ async function* streamOpenAIEvents(
|
|
|
1430
1450
|
try {
|
|
1431
1451
|
// OR-TOOLARGS-REPAIR — 깨진 도구 인자 표지는 OpenRouter 엔드포인트에서만 켠다(다른 공급자 무변경).
|
|
1432
1452
|
const flagInvalidToolArgs = inferOpenAIProviderName(url) === 'openrouter';
|
|
1433
|
-
|
|
1453
|
+
const emitTrailingReasoningDetails = flagInvalidToolArgs && typeof body?.model === 'string' && isOpenRouterDeepSeekWireModel(body.model);
|
|
1454
|
+
for await (const ev of parseOpenAISSELines(sseLineStream(response.body!), flagInvalidToolArgs ? { flagInvalidToolArgs, ...(emitTrailingReasoningDetails ? { emitTrailingReasoningDetails } : {}) } : {})) {
|
|
1434
1455
|
if (ev.type === 'text') textChars += ev.delta.length;
|
|
1435
1456
|
else if (ev.type === 'tool_call') toolCalls++;
|
|
1436
1457
|
yield ev;
|
|
@@ -4294,6 +4315,26 @@ function finalizeStreamingProviderModel(provider: LLMProvider, model: string): {
|
|
|
4294
4315
|
return { provider: finalizedProvider, model: finalized.model };
|
|
4295
4316
|
}
|
|
4296
4317
|
|
|
4318
|
+
/** ZAI-POD-CHILD(10-11 실측 · si-task-aaf5d691e4ed) — 레지스트리는 `glm-` 접두를 local 로 본다.
|
|
4319
|
+
* 활성 provider 가 `glm`(`ELANOUS_LLM_PROVIDER=glm` 포함)일 때 모델 이름으로 provider 를 고르는 길
|
|
4320
|
+
* (gate 진단·리뷰 `getProvider(model)`)이 local 로 떨어져 «Local LLM unavailable» 로 죽었다.
|
|
4321
|
+
* ⇒ 그때만 설정 경로(`getProviderForConfig`)를 탄다 — 키가 있으면 Z.ai 공급자, 없으면 OpenRouter 안내 오류.
|
|
4322
|
+
* 활성 provider 가 glm 이 아니면 undefined(종전 그대로). */
|
|
4323
|
+
function activeGlmProviderForModel(model: string): LLMProvider | undefined {
|
|
4324
|
+
if (!model.trim().toLowerCase().startsWith('glm-')) return undefined;
|
|
4325
|
+
let cfg: UserConfig;
|
|
4326
|
+
try {
|
|
4327
|
+
const { getUserConfig } = require('./user-config.js') as typeof import('./user-config.js');
|
|
4328
|
+
cfg = getUserConfig();
|
|
4329
|
+
} catch (e) {
|
|
4330
|
+
debug.log('llm.router', 'getProvider.glm-config-unreadable', { requestedModel: model, error: e instanceof Error ? e.message : String(e) }, { level: 'warn' });
|
|
4331
|
+
return undefined;
|
|
4332
|
+
}
|
|
4333
|
+
if (cfg.llm.provider !== 'glm') return undefined;
|
|
4334
|
+
debug.log('llm.router', 'getProvider.glm-active', { requestedModel: model }, { level: 'info' });
|
|
4335
|
+
return getProviderForConfig(cfg, model);
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4297
4338
|
/**
|
|
4298
4339
|
* Pick a provider based on the model name. If model is undefined, use first
|
|
4299
4340
|
* available provider (preferring DEFAULT_PROVIDER env var if set).
|
|
@@ -4305,6 +4346,8 @@ export function getProvider(model?: string): LLMProvider {
|
|
|
4305
4346
|
// ids (e.g. `claude-opus-4-7`) so prefix matching works. Existing
|
|
4306
4347
|
// full ids pass through unchanged.
|
|
4307
4348
|
const resolved = resolveModelAlias(model) ?? model;
|
|
4349
|
+
const glmDirect = activeGlmProviderForModel(resolved);
|
|
4350
|
+
if (glmDirect) return glmDirect;
|
|
4308
4351
|
// Preserve the historical Chat Completions route for gpt-* model-only
|
|
4309
4352
|
// requests, including future ids that the current catalog does not list.
|
|
4310
4353
|
if (resolved.toLowerCase().startsWith('gpt-')) return PROVIDERS.openai!;
|
|
@@ -4443,6 +4486,7 @@ export function buildMessagesWithContext(
|
|
|
4443
4486
|
// Re-export converters so tests/adapters can assert wire-format correctness.
|
|
4444
4487
|
export { toOpenAIMessage, toOpenAIMessages, toAnthropicMessage, systemToAnthropicString };
|
|
4445
4488
|
export { usesAdaptiveThinking, mapReasoningLevelToAnthropicThinking, mapReasoningLevelToAnthropicEffort, sseLineStream, retryOptsFor }; // src/llm/bedrock-provider.ts
|
|
4489
|
+
export { makeOpenAICompatProvider, makeOpenRouterProvider }; // src/llm/zai-coding-plan.ts
|
|
4446
4490
|
|
|
4447
4491
|
/**
|
|
4448
4492
|
* Best-effort classification of whether a model accepts image inputs.
|
|
@@ -4502,7 +4546,7 @@ export function isLikelyVisionModel(model: string | undefined): boolean {
|
|
|
4502
4546
|
// ⭐ OPENROUTER-OPTIMIZE G9 — `reportedCostUsd`(OpenRouter `usage.cost`)도 싣는다. 빠지면 `stream-llm` 행이
|
|
4503
4547
|
// 실청구액을 잃고 카탈로그 추정(대개 `cost.kind=unknown`)으로 떨어진다(리뷰·저작 행 실측).
|
|
4504
4548
|
// 합산은 토큰 칸과 같은 규약이다 — OpenAI 호환 스트림은 `usage` 를 마지막 청크에 «한 번» 싣는다(누적 반복 보고 아님).
|
|
4505
|
-
type ObservedUsage = Pick<LLMUsage, 'inputTokens' | 'outputTokens' | 'cacheReadInputTokens' | 'cacheCreationInputTokens' | 'reportedCostUsd'>;
|
|
4549
|
+
type ObservedUsage = Pick<LLMUsage, 'inputTokens' | 'outputTokens' | 'cacheReadInputTokens' | 'cacheCreationInputTokens' | 'reportedCostUsd' | 'byok' | 'upstreamCostUsd'>;
|
|
4506
4550
|
|
|
4507
4551
|
type ProviderTextResult =
|
|
4508
4552
|
| { ok: true; full: string; usage: ObservedUsage | 'unmeasured' }
|
|
@@ -4513,7 +4557,7 @@ function accumulateUsage(usage: ObservedUsage | undefined, received: LLMUsage):
|
|
|
4513
4557
|
for (const field of ['inputTokens', 'outputTokens', 'cacheReadInputTokens', 'cacheCreationInputTokens', 'reportedCostUsd'] as const) {
|
|
4514
4558
|
if (received[field] !== undefined) total[field] = (total[field] ?? 0) + received[field];
|
|
4515
4559
|
}
|
|
4516
|
-
return total;
|
|
4560
|
+
return accumulateByokUsage(total, received); // OR-BYOK-COST
|
|
4517
4561
|
}
|
|
4518
4562
|
|
|
4519
4563
|
async function consumeProviderText(
|
|
@@ -4637,7 +4681,7 @@ export async function streamLLM(
|
|
|
4637
4681
|
if (consumed.ok) {
|
|
4638
4682
|
if (consumed.usage !== 'unmeasured') {
|
|
4639
4683
|
try {
|
|
4640
|
-
logAgentTurnUsage(activeModel, consumed.usage, { providerName: activeProvider.name, site: 'stream-llm', role: opts.usageRole });
|
|
4684
|
+
logAgentTurnUsage(activeModel, consumed.usage, { providerName: activeProvider.name, site: opts.usageSite ?? 'stream-llm', role: opts.usageRole });
|
|
4641
4685
|
} catch { /* best-effort observation */ }
|
|
4642
4686
|
}
|
|
4643
4687
|
debug.log('llm', 'outcome', { provider: activeProvider.name, status: 'ok', kind: 'streamLLM', durationMs: Date.now() - started });
|
|
@@ -8214,11 +8258,16 @@ export async function streamLLMWithTools(
|
|
|
8214
8258
|
},
|
|
8215
8259
|
};
|
|
8216
8260
|
}
|
|
8261
|
+
// OR-DEEPSEEK-TUNE — 마지막 턴의 «도구 없는» 추론(deepseek/* 만 채워진다). 매 턴 시작에 비운다.
|
|
8262
|
+
let turnReasoningDetails: Array<Record<string, unknown>> | undefined;
|
|
8217
8263
|
const emitTurnComplete = (finalAssistantText?: string): void => {
|
|
8218
8264
|
if (finalAssistantText && finalAssistantText.length > 0) {
|
|
8219
8265
|
history.push({
|
|
8220
8266
|
role: 'assistant',
|
|
8221
|
-
content: [{
|
|
8267
|
+
content: [{
|
|
8268
|
+
type: 'text', text: finalAssistantText,
|
|
8269
|
+
...(turnReasoningDetails ? { providerMeta: { reasoningDetails: turnReasoningDetails } } : {}),
|
|
8270
|
+
}],
|
|
8222
8271
|
});
|
|
8223
8272
|
}
|
|
8224
8273
|
handlers.onTurnComplete?.(history.slice(initialHistoryLen));
|
|
@@ -8427,6 +8476,7 @@ export async function streamLLMWithTools(
|
|
|
8427
8476
|
}
|
|
8428
8477
|
}
|
|
8429
8478
|
let turnText = '';
|
|
8479
|
+
turnReasoningDetails = undefined;
|
|
8430
8480
|
const pendingCalls: Array<{
|
|
8431
8481
|
id: string;
|
|
8432
8482
|
name: string;
|
|
@@ -8642,6 +8692,8 @@ export async function streamLLMWithTools(
|
|
|
8642
8692
|
turnText += ev.delta;
|
|
8643
8693
|
fullText += ev.delta;
|
|
8644
8694
|
handlers.onText(ev.delta, fullText);
|
|
8695
|
+
} else if (ev.type === 'reasoning_details') {
|
|
8696
|
+
turnReasoningDetails = ev.details;
|
|
8645
8697
|
} else if (ev.type === 'tool_call') {
|
|
8646
8698
|
pendingCalls.push({
|
|
8647
8699
|
id: ev.id,
|
|
@@ -10938,7 +10990,8 @@ function makeOpenAICompatProvider(
|
|
|
10938
10990
|
throw new Error(`${name} unavailable: configure apiKey via \`elanous setup\``);
|
|
10939
10991
|
}
|
|
10940
10992
|
const activeModel = opts.model || model;
|
|
10941
|
-
const tools = toOpenAITools(opts.tools, name === 'openrouter' && openRouterWireModel(activeModel)
|
|
10993
|
+
const tools = toOpenAITools(opts.tools, name === 'openrouter' && openRouterNeedsKimiToolSchema(openRouterWireModel(activeModel)));
|
|
10994
|
+
const deepSeek = name === 'openrouter' && isOpenRouterDeepSeekWireModel(openRouterWireModel(activeModel));
|
|
10942
10995
|
// OR-TOOLCHOICE-PASS (2026-10-10) — OpenRouter 경로만 호출자 toolChoice 를 싣는다.
|
|
10943
10996
|
// ⛔ grok/openai 호환 경로는 손대지 않는다(무회귀). toolChoice 가 없거나 tools 가 없으면 키 자체를 안 싣는다.
|
|
10944
10997
|
const toolChoice = name === 'openrouter' && tools
|
|
@@ -10967,7 +11020,11 @@ function makeOpenAICompatProvider(
|
|
|
10967
11020
|
acceptToolImagesViaFollowup: followup,
|
|
10968
11021
|
acceptUserMessageImages: followup,
|
|
10969
11022
|
...(name === 'openrouter' ? { echoReasoningDetails: true } : {}),
|
|
11023
|
+
...(deepSeek ? { deepSeekReplay: true } : {}),
|
|
10970
11024
|
}));
|
|
11025
|
+
// OR-DEEPSEEK-TUNE — deepseek/* 만: 설정 기본값의 상류 고정(명시 라우팅이 없을 때) ⊕ 관측 `deepseek-replay`.
|
|
11026
|
+
const deepSeekProvider = deepSeek ? openRouterDeepSeekRequestExtras(openRouterWireModel(activeModel), wireMessages,
|
|
11027
|
+
(wire.extraBody as { provider?: unknown } | undefined)?.provider, cfg.openrouter?.providerPreferences, cfg.openrouter?.upstreamPins) : {};
|
|
10971
11028
|
// OR-ANTHROPIC-CACHE (2026-10-10) — OpenRouter 뒤 anthropic/* · moonshotai/* 만 cache_control 중단점을 싣는다.
|
|
10972
11029
|
// ⛔ 다른 OpenRouter 모델과 grok/openai/local 호환 경로는 이 분기를 안 탄다 — 본문 바이트 동일(무회귀).
|
|
10973
11030
|
const cacheFamily = name === 'openrouter' ? openRouterPromptCacheFamily(openRouterWireModel(activeModel)) : null;
|
|
@@ -11013,6 +11070,7 @@ function makeOpenAICompatProvider(
|
|
|
11013
11070
|
...(tools ? { tools } : {}),
|
|
11014
11071
|
...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}),
|
|
11015
11072
|
...(wire.extraBody ?? {}),
|
|
11073
|
+
...deepSeekProvider,
|
|
11016
11074
|
// OpenRouter alone resolves per-call effort against the configured level and catalog default.
|
|
11017
11075
|
...(name === 'openrouter'
|
|
11018
11076
|
? openRouterReasoningField(activeModel, opts.reasoningEffort, cfg.reasoningLevel)
|
|
@@ -11112,7 +11170,7 @@ const OPENROUTER_REASONING_EFFORT: Record<string, 'low' | 'medium' | 'high'> = {
|
|
|
11112
11170
|
};
|
|
11113
11171
|
function openRouterReasoningField(
|
|
11114
11172
|
model: string, requested: string | undefined, configured: UCLLMConfig['reasoningLevel'],
|
|
11115
|
-
): { reasoning?: { effort: 'low' | 'medium' | 'high' }; verbosity?: 'low' | 'medium' | 'high' } {
|
|
11173
|
+
): { reasoning?: { effort: 'low' | 'medium' | 'high' | 'xhigh' }; verbosity?: 'low' | 'medium' | 'high' } {
|
|
11116
11174
|
const wireModel = openRouterWireModel(model);
|
|
11117
11175
|
// The catalog's model-specific open-weight verdict, not a vendor/slug prefix,
|
|
11118
11176
|
// gates the default: an unregistered release may be closed-weight.
|
|
@@ -11121,13 +11179,18 @@ function openRouterReasoningField(
|
|
|
11121
11179
|
(entry) => entry.id === slug && entry.openWeight === true,
|
|
11122
11180
|
);
|
|
11123
11181
|
const configEffort = configured === 'off' ? undefined : configured;
|
|
11124
|
-
|
|
11125
|
-
const
|
|
11126
|
-
const
|
|
11127
|
-
|
|
11182
|
+
// OR-DEEPSEEK-TUNE — deepseek/* 의 기본은 카탈로그 손 레코드(YAML `reasoning`) · 없으면 아래 기존 폴백.
|
|
11183
|
+
const catalogEffort = requested === undefined && configured === undefined ? openRouterDeepSeekCatalogEffort(model) : undefined;
|
|
11184
|
+
const source = requested !== undefined ? 'call' : configured !== undefined ? 'config' : catalogEffort ? 'catalog' : openWeight ? 'default' : 'none';
|
|
11185
|
+
const effort = requested ?? (configured !== undefined ? configEffort : catalogEffort ?? (openWeight ? 'medium' : undefined));
|
|
11186
|
+
// OR-DEEPSEEK-TUNE — DeepSeek V4 는 자기 표(xhigh·max→xhigh · off→칸 없음 · src/llm/openrouter-deepseek.ts).
|
|
11187
|
+
const deepSeekEffort = openRouterDeepSeekEffort(wireModel, effort);
|
|
11188
|
+
const sent = deepSeekEffort !== undefined ? (deepSeekEffort ?? undefined)
|
|
11189
|
+
: effort !== undefined && Object.hasOwn(OPENROUTER_REASONING_EFFORT, effort)
|
|
11190
|
+
? OPENROUTER_REASONING_EFFORT[effort] : undefined;
|
|
11128
11191
|
const field = sent ? (wireModel.startsWith('anthropic/') ? 'verbosity' : 'reasoning') : 'none';
|
|
11129
11192
|
debug.log('llm.openrouter', 'reasoning', { model, requested: requested ?? null, sent: sent ?? null, source, field });
|
|
11130
|
-
return field === 'verbosity' ? { verbosity: sent }
|
|
11193
|
+
return field === 'verbosity' ? { verbosity: sent as 'low' | 'medium' | 'high' }
|
|
11131
11194
|
: field === 'reasoning' ? { reasoning: { effort: sent! } } : {};
|
|
11132
11195
|
}
|
|
11133
11196
|
|
|
@@ -11532,8 +11595,16 @@ export function getProviderForConfig(
|
|
|
11532
11595
|
model?: string,
|
|
11533
11596
|
): LLMProvider {
|
|
11534
11597
|
const { provider } = userConfig.llm;
|
|
11598
|
+
// CLAUDE-IMPL-LANE(10-11) — `claude-acp` 는 LLM provider 가 아니라 구독 ACP 구현 실행기 선택이다.
|
|
11599
|
+
// 여기서 풀리면 모델 이름(claude-…)이 anthropic(API 키 provider)으로 넘어가 «구독» 런이 API 로 과금된다 → 거부.
|
|
11600
|
+
// 구현은 implement seam 의 claude-acp 갈래(src/self-implement/claude-acp-implement.ts)만 쓴다.
|
|
11601
|
+
if ((provider as string) === 'claude-acp') {
|
|
11602
|
+
throw new Error('claude-acp 는 LLM provider 가 아니다 — 구독 ACP 구현 레인 전용(src/self-implement/claude-acp-implement.ts) · API provider 로 풀지 않는다');
|
|
11603
|
+
}
|
|
11535
11604
|
// ⛔ 2026-09-23 — `kimi`·`qwen`·`glm` 은 union·사다리에 있지만 «직결» 배선이 없다(사다리 `wip`).
|
|
11536
11605
|
// 종전엔 `unknown provider: kimi` 로 «아무것도» 안 말하고 죽었다. 이제 실제로 도는 길이 있다(`openrouter`).
|
|
11606
|
+
// ZAI-CODING-PLAN-PROVIDER(대표 10-11) — `ZAI_API_KEY` 가 있을 때만 glm 직결. 없으면 아래 안내 그대로(src/llm/zai-coding-plan.ts).
|
|
11607
|
+
if (provider === 'glm' && zaiCodingPlanAvailable()) return makeZaiCodingPlanProvider({ ...userConfig.llm, model: model || userConfig.llm.model });
|
|
11537
11608
|
const direct = OPENROUTER_ROUTE_FOR_WIP_PROVIDER[provider as keyof typeof OPENROUTER_ROUTE_FOR_WIP_PROVIDER];
|
|
11538
11609
|
if (direct) {
|
|
11539
11610
|
throw new Error(
|
package/src/loops/checker.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join, dirname } from 'node:path';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, resolve } from 'node:path';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { Database } from 'bun:sqlite';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
6
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
7
7
|
import * as registry from './registry.js';
|
|
8
8
|
import { cronMatches } from '../domains/cron-match.js';
|
|
9
9
|
import { resolveTimeZone } from '../time/format.js';
|
|
10
|
+
import type { ScheduleRow } from '../domains/schedule-registry.js';
|
|
10
11
|
import type { LoopEntry, LoopRun } from './registry.js';
|
|
11
12
|
|
|
12
13
|
type LoopState = 'off' | 'unregistered' | 'failing' | 'late' | 'unknown' | 'alive';
|
|
@@ -23,6 +24,15 @@ export interface CheckEntry {
|
|
|
23
24
|
lastRunAt?: string;
|
|
24
25
|
lastStatus?: string;
|
|
25
26
|
recentStatuses?: string[];
|
|
27
|
+
/** Inventory sighting of the scheduled registration (schedule-registry `last_seen`, refreshed on each inventory — not the first sighting); never represented as an execution. */
|
|
28
|
+
observedSince?: string;
|
|
29
|
+
/** Optional launcher evidence; absent values are not inferred from a missing heartbeat. */
|
|
30
|
+
health?: 'healthy' | 'degraded';
|
|
31
|
+
endsAt?: string;
|
|
32
|
+
installedVersion?: string;
|
|
33
|
+
expectedVersion?: string;
|
|
34
|
+
/** Registry graph path, supplied only by graph inventory. */
|
|
35
|
+
file?: string;
|
|
26
36
|
enabled: boolean;
|
|
27
37
|
registered: boolean;
|
|
28
38
|
}
|
|
@@ -59,6 +69,110 @@ export function checkLoops(entries: CheckEntry[], now: Date, opts: { failureStat
|
|
|
59
69
|
});
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
export type ScheduleFinding = { id: string; owner?: string; kind: 'late' | 'failing' | 'degraded' | 'end-expired' | 'version-mismatch'; reason: string; lastRunAt?: string };
|
|
73
|
+
|
|
74
|
+
/** Additional scheduler evidence is evaluated only when supplied; the six-state CLI contract stays intact. */
|
|
75
|
+
export function scheduleFindings(results: CheckResult[], now: Date): ScheduleFinding[] {
|
|
76
|
+
const findings: ScheduleFinding[] = [];
|
|
77
|
+
for (const row of results) {
|
|
78
|
+
if (!row.enabled || !row.registered) continue;
|
|
79
|
+
const common = { id: row.id, ...(row.owner ? { owner: row.owner } : {}),
|
|
80
|
+
...(row.lastRunAt ? { lastRunAt: row.lastRunAt } : {}) };
|
|
81
|
+
if (row.state === 'late' || row.state === 'failing') findings.push({ ...common, kind: row.state, reason: row.reason });
|
|
82
|
+
else if (row.state === 'unknown' && !row.lastRunAt && row.observedSince && row.dueAt &&
|
|
83
|
+
Number.isFinite(Date.parse(row.observedSince)) && Number.isFinite(Date.parse(row.dueAt)) &&
|
|
84
|
+
Date.parse(row.observedSince) < Date.parse(row.dueAt))
|
|
85
|
+
findings.push({ ...common, kind: 'late', reason: `no run since observed registration ${row.observedSince}` });
|
|
86
|
+
if (row.health === 'degraded') findings.push({ ...common, kind: 'degraded', reason: 'launcher degraded' });
|
|
87
|
+
if (row.endsAt && Number.isFinite(Date.parse(row.endsAt)) && Date.parse(row.endsAt) <= now.getTime())
|
|
88
|
+
findings.push({ ...common, kind: 'end-expired', reason: `launcher ended at ${row.endsAt}` });
|
|
89
|
+
if (row.installedVersion && row.expectedVersion && row.installedVersion !== row.expectedVersion)
|
|
90
|
+
findings.push({ ...common, kind: 'version-mismatch', reason: `installed ${row.installedVersion}; expected ${row.expectedVersion}` });
|
|
91
|
+
}
|
|
92
|
+
return findings;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function daemonScheduleEntries(rows: readonly ScheduleRow[], now: Date): CheckEntry[] {
|
|
96
|
+
return rows.filter(row => row.enabled && row.cron && (row.run_via === 'elanous' || row.run_via === 'trigger'))
|
|
97
|
+
.map(row => ({ id: row.id, owner: 'OP', enabled: true, registered: true, dueAt: dueBefore(row.cron!, now) ?? undefined,
|
|
98
|
+
...(row.last_seen ? { observedSince: row.last_seen } : {}),
|
|
99
|
+
...(row.last_run ? { lastRunAt: row.last_run } : {}),
|
|
100
|
+
...(row.last_status ? { lastStatus: row.last_status, recentStatuses: [row.last_status] } : {}),
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function versionEvidence(entries: CheckEntry[], repoRoot: string, stateRoot: string): CheckEntry[] {
|
|
105
|
+
return entries.map(entry => {
|
|
106
|
+
if (!entry.file || !/^[\w./-]+\.ya?ml$/.test(entry.file) || entry.file.split('/').includes('..')) return entry;
|
|
107
|
+
const graphPath = resolve(repoRoot, entry.file);
|
|
108
|
+
if (!graphPath.startsWith(resolve(repoRoot) + '/')) return entry;
|
|
109
|
+
const runsPath = join(stateRoot, 'graph-runs', entry.id);
|
|
110
|
+
try {
|
|
111
|
+
if (!existsSync(graphPath) || !existsSync(runsPath)) return entry;
|
|
112
|
+
const latest = readdirSync(runsPath).filter(name => name.endsWith('.json') && !name.endsWith('.decision.json'))
|
|
113
|
+
.sort((a, b) => statSync(join(runsPath, b)).mtimeMs - statSync(join(runsPath, a)).mtimeMs)[0];
|
|
114
|
+
if (!latest) return entry;
|
|
115
|
+
const run = JSON.parse(readFileSync(join(runsPath, latest), 'utf8')) as { graphId?: string; variant?: unknown; graphSnapshot?: { graphSha?: string } };
|
|
116
|
+
if (run.graphId !== entry.id || run.variant || !run.graphSnapshot?.graphSha) return entry;
|
|
117
|
+
const installedVersion = run.graphSnapshot.graphSha;
|
|
118
|
+
const current = createHash('sha256').update(readFileSync(graphPath)).digest('hex');
|
|
119
|
+
return { ...entry, installedVersion, expectedVersion: current };
|
|
120
|
+
} catch (error) {
|
|
121
|
+
debug.log('loop.checker', 'version-unavailable', { id: entry.id, reason: String(error) });
|
|
122
|
+
return entry;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function launcherEvidence(entries: CheckEntry[], root: string): CheckEntry[] {
|
|
128
|
+
const path = join(root, 'msg', 'messages.db');
|
|
129
|
+
if (!existsSync(path)) return entries;
|
|
130
|
+
try {
|
|
131
|
+
const db = new Database(path, { readonly: true });
|
|
132
|
+
try {
|
|
133
|
+
if (!db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='loop_signals'").get()) return entries;
|
|
134
|
+
const signals = db.query('SELECT loop_id, health, ends_at FROM loop_signals').all() as Array<{
|
|
135
|
+
loop_id: string; health: string; ends_at: string | null;
|
|
136
|
+
}>;
|
|
137
|
+
const byId = new Map(signals.map(signal => [signal.loop_id, signal]));
|
|
138
|
+
return entries.map(entry => {
|
|
139
|
+
const signal = byId.get(entry.id);
|
|
140
|
+
return signal ? { ...entry, ...(signal.health === 'degraded' ? { health: 'degraded' as const } : {}),
|
|
141
|
+
...(signal.ends_at ? { endsAt: signal.ends_at } : {}) } : entry;
|
|
142
|
+
});
|
|
143
|
+
} finally { db.close(); }
|
|
144
|
+
} catch (error) {
|
|
145
|
+
debug.log('loop.checker', 'launcher-evidence-unavailable', { reason: String(error) });
|
|
146
|
+
return entries;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Extend only an observed healthy launcher that is still heartbeating. The CAS prevents replacing a renewed lease.
|
|
151
|
+
export function extendHealthyLauncher(root: string, finding: ScheduleFinding, now: Date): boolean {
|
|
152
|
+
if (finding.kind !== 'end-expired' || !Number.isFinite(now.getTime())) return false;
|
|
153
|
+
const path = join(root, 'msg', 'messages.db');
|
|
154
|
+
if (!existsSync(path)) return false;
|
|
155
|
+
const db = new Database(path);
|
|
156
|
+
try {
|
|
157
|
+
db.exec('PRAGMA busy_timeout = 10000');
|
|
158
|
+
db.exec('BEGIN IMMEDIATE');
|
|
159
|
+
try {
|
|
160
|
+
const row = db.query('SELECT at, health, ends_at FROM loop_signals WHERE loop_id=?')
|
|
161
|
+
.get(finding.id) as { at: string; health: string; ends_at: string | null } | null;
|
|
162
|
+
const age = row ? now.getTime() - Date.parse(row.at) : NaN;
|
|
163
|
+
if (!row || row.health !== 'healthy' || !Number.isFinite(age) || age < 0 || age > 15 * 60_000 ||
|
|
164
|
+
!row.ends_at || !Number.isFinite(Date.parse(row.ends_at)) || Date.parse(row.ends_at) > now.getTime()) {
|
|
165
|
+
db.exec('COMMIT');
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
const result = db.query('UPDATE loop_signals SET ends_at=? WHERE loop_id=? AND at=? AND health=? AND ends_at=?')
|
|
169
|
+
.run(new Date(now.getTime() + 15 * 60_000).toISOString(), finding.id, row.at, row.health, row.ends_at);
|
|
170
|
+
db.exec('COMMIT');
|
|
171
|
+
return result.changes === 1;
|
|
172
|
+
} catch (error) { db.exec('ROLLBACK'); throw error; }
|
|
173
|
+
} finally { db.close(); }
|
|
174
|
+
}
|
|
175
|
+
|
|
62
176
|
export function countLoopStates(results: CheckResult[]): LoopCounts {
|
|
63
177
|
const counts: LoopCounts = { alive: 0, late: 0, failing: 0, off: 0, unregistered: 0, unknown: 0 };
|
|
64
178
|
for (const result of results) counts[result.state]++;
|
|
@@ -118,8 +232,14 @@ export interface CheckerDeps {
|
|
|
118
232
|
}
|
|
119
233
|
|
|
120
234
|
/** Appends only new daily keys to the seat request journal; never rewrites existing lines. */
|
|
121
|
-
export function notifyOwners(results: CheckResult[], deps: CheckerDeps = {}): number {
|
|
122
|
-
const eligible =
|
|
235
|
+
export function notifyOwners(results: CheckResult[], deps: CheckerDeps = {}, findings: ScheduleFinding[] = [], escalation = false): number {
|
|
236
|
+
const eligible = [
|
|
237
|
+
...results.filter(result => result.owner && (result.state === 'late' || result.state === 'failing'))
|
|
238
|
+
.map(result => ({ ...result, alertState: result.state })),
|
|
239
|
+
...findings.filter(result => result.owner && (result.kind !== 'late' && result.kind !== 'failing' ||
|
|
240
|
+
!results.some(row => row.id === result.id && row.state === result.kind)))
|
|
241
|
+
.map(result => ({ ...result, alertState: result.kind })),
|
|
242
|
+
].filter(result => escalation ? result.owner !== 'OP' : true);
|
|
123
243
|
if (!eligible.length) return 0;
|
|
124
244
|
const now = deps.now ?? new Date();
|
|
125
245
|
const path = join(deps.root ?? effectiveInstanceRoot(), 'seat-requests', 'requests.jsonl');
|
|
@@ -132,19 +252,38 @@ export function notifyOwners(results: CheckResult[], deps: CheckerDeps = {}): nu
|
|
|
132
252
|
lock.exec('BEGIN EXCLUSIVE');
|
|
133
253
|
try {
|
|
134
254
|
const existing = new Set<string>();
|
|
255
|
+
const ownerRequests = new Map<string, { queuedAt?: string; status?: string }>();
|
|
135
256
|
if (existsSync(path)) {
|
|
136
257
|
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
137
258
|
if (line.trim()) {
|
|
138
|
-
const row = JSON.parse(line) as { key?: string };
|
|
139
|
-
if (row.key)
|
|
259
|
+
const row = JSON.parse(line) as { key?: string; queuedAt?: string; status?: string };
|
|
260
|
+
if (row.key) {
|
|
261
|
+
existing.add(row.key);
|
|
262
|
+
ownerRequests.set(row.key, row);
|
|
263
|
+
}
|
|
140
264
|
}
|
|
141
265
|
}
|
|
142
266
|
}
|
|
143
267
|
let added = 0;
|
|
144
268
|
for (const result of eligible) {
|
|
145
|
-
const
|
|
269
|
+
const ownerKey = `loopcheck:${result.id}:${now.toISOString().slice(0, 10)}:${result.alertState}`;
|
|
270
|
+
let requestKey = ownerKey;
|
|
271
|
+
if (escalation) {
|
|
272
|
+
const yesterday = new Date(now.getTime() - 24 * 3600_000).toISOString().slice(0, 10);
|
|
273
|
+
const priorKey = `loopcheck:${result.id}:${yesterday}:${result.alertState}`;
|
|
274
|
+
const prior = ownerRequests.get(priorKey);
|
|
275
|
+
if (prior?.status === 'queued' && prior.queuedAt &&
|
|
276
|
+
now.getTime() - Date.parse(prior.queuedAt) >= 30 * 60_000 && !existing.has(`${priorKey}:op`))
|
|
277
|
+
requestKey = priorKey;
|
|
278
|
+
}
|
|
279
|
+
const key = escalation ? `${requestKey}:op` : ownerKey;
|
|
146
280
|
if (existing.has(key)) continue;
|
|
147
|
-
|
|
281
|
+
if (escalation) {
|
|
282
|
+
const request = ownerRequests.get(requestKey);
|
|
283
|
+
if (!request || request.status !== 'queued' || !request.queuedAt ||
|
|
284
|
+
now.getTime() - Date.parse(request.queuedAt) < 30 * 60_000) continue;
|
|
285
|
+
}
|
|
286
|
+
const row = { key, receiptId: key, seat: escalation ? 'OP' : result.owner, text: `${result.id} ${result.alertState} — 마지막 ${result.lastRunAt ?? '없음'} · ${result.reason}`,
|
|
148
287
|
status: 'queued', queuedAt: now.toISOString(), source: 'loop-checker' };
|
|
149
288
|
appendFileSync(path, JSON.stringify(row) + '\n');
|
|
150
289
|
existing.add(key);
|
|
@@ -10,11 +10,15 @@ import {
|
|
|
10
10
|
type LlmMergeOutcome, type MergeGitSeam,
|
|
11
11
|
} from '../../autopilot/build/llm-conflict-merge.js';
|
|
12
12
|
|
|
13
|
-
type ResyncVerdict = 'saved' | 'merge-ok-tests-failed' | 'merge-ok-no-tests' | 'unresolved' | 'guard-tripped' | 'error';
|
|
13
|
+
type ResyncVerdict = 'saved' | 'merge-ok-tests-failed' | 'merge-ok-no-tests' | 'unresolved' | 'guard-tripped' | 'push-rejected' | 'error';
|
|
14
|
+
export type ResyncShadowMode = 'shadow' | 'live';
|
|
14
15
|
interface ResyncShadowEntry {
|
|
15
16
|
at: string;
|
|
16
17
|
pr: number;
|
|
17
18
|
head: string;
|
|
19
|
+
mode: ResyncShadowMode;
|
|
20
|
+
pushed: boolean;
|
|
21
|
+
labelRemoved: boolean;
|
|
18
22
|
verdict: ResyncVerdict;
|
|
19
23
|
status: LlmMergeOutcome['status'] | 'error';
|
|
20
24
|
resolvedFiles: string[];
|
|
@@ -28,6 +32,7 @@ export interface ResyncShadowDeps {
|
|
|
28
32
|
repoRoot?: string;
|
|
29
33
|
instanceRoot?: string;
|
|
30
34
|
runGh?: (args: string[]) => string;
|
|
35
|
+
runGit?: (args: string[]) => void;
|
|
31
36
|
resolve?: (file: string, conflicted: string) => Promise<string>;
|
|
32
37
|
gitSeam?: MergeGitSeam;
|
|
33
38
|
runTests?: (cwd: string, files: string[]) => { ok: boolean; failedTests?: string[]; noTests?: boolean };
|
|
@@ -134,9 +139,10 @@ function defaultRunTests(cwd: string, files: string[]): { ok: boolean; failedTes
|
|
|
134
139
|
return failedTests.length ? { ok: false, failedTests } : { ok: true };
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
/** Re-merge open conflicts only in disposable detached worktrees;
|
|
138
|
-
export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Promise<ResyncShadowSummary> {
|
|
142
|
+
/** Re-merge open conflicts only in disposable detached worktrees; shadow mode never writes to remotes. */
|
|
143
|
+
export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}, mode: ResyncShadowMode = 'shadow'): Promise<ResyncShadowSummary> {
|
|
139
144
|
if (!Number.isSafeInteger(max) || max < 0) throw new Error('--max must be a nonnegative integer');
|
|
145
|
+
if (mode !== 'shadow' && mode !== 'live') throw new Error('mode must be shadow or live');
|
|
140
146
|
const repoRoot = deps.repoRoot ?? resolve(import.meta.dir, '../../..');
|
|
141
147
|
const gh = deps.runGh ?? ((args: string[]) => command('gh', args, repoRoot));
|
|
142
148
|
const observed: unknown = JSON.parse(gh(['pr', 'list', '--state', 'open', '--json', 'number,headRefName,mergeable,isDraft,labels', '--limit', '1000']));
|
|
@@ -146,6 +152,7 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
|
|
|
146
152
|
throw new Error('incomplete or invalid PR observation');
|
|
147
153
|
const candidates = (observed as OpenPr[]).filter(pr => pr.headRefName.startsWith('self-impl/')
|
|
148
154
|
&& pr.mergeable === 'CONFLICTING'
|
|
155
|
+
&& (mode === 'shadow' || pr.labels.some(label => label.name === 'elanous:needs-rebase'))
|
|
149
156
|
&& !pr.labels.some(label => label.name === 'elanous:stalled' || label.name === 'elanous:superseded'))
|
|
150
157
|
.sort((a, b) => a.number - b.number).slice(0, max);
|
|
151
158
|
const ledger = join(deps.instanceRoot ?? effectiveInstanceRoot(), 'orchestrator', 'resync-shadow.jsonl');
|
|
@@ -154,6 +161,7 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
|
|
|
154
161
|
for (const pr of candidates) {
|
|
155
162
|
const entry: ResyncShadowEntry = {
|
|
156
163
|
at: (deps.now?.() ?? new Date()).toISOString(), pr: pr.number, head: pr.headRefName,
|
|
164
|
+
mode, pushed: false, labelRemoved: false,
|
|
157
165
|
verdict: 'error', status: 'error', resolvedFiles: [], testFiles: [],
|
|
158
166
|
};
|
|
159
167
|
let tmp: string | undefined;
|
|
@@ -179,6 +187,21 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
|
|
|
179
187
|
if (!test.ok) entry.failedTests = test.failedTests?.length ? test.failedTests : ['test runner failed'];
|
|
180
188
|
}
|
|
181
189
|
} else entry.verdict = outcome.status === 'conflict-unresolved' || outcome.status === 'up-to-date' ? 'unresolved' : 'error';
|
|
190
|
+
if (mode === 'live' && entry.verdict === 'saved') {
|
|
191
|
+
try {
|
|
192
|
+
(deps.runGit ?? (args => { command('git', args, tmp!); }))(
|
|
193
|
+
['push', 'origin', `HEAD:refs/heads/${pr.headRefName}`]);
|
|
194
|
+
entry.pushed = true;
|
|
195
|
+
gh(['pr', 'edit', String(pr.number), '--remove-label', 'elanous:needs-rebase']);
|
|
196
|
+
entry.labelRemoved = true;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
199
|
+
// push 가 이미 됐으면 원격은 살았다 — verdict 는 saved 로 두고 라벨 실패만 남긴다(다음 회차가 같은 PR 을 다시 고를 수 있다).
|
|
200
|
+
// push 실패는 거부 문구(rejected · non-fast-forward)일 때만 push-rejected · 인증·네트워크·시간초과는 error.
|
|
201
|
+
if (!entry.pushed) entry.verdict = /rejected|non-fast-forward|fetch first/i.test(message) ? 'push-rejected' : 'error';
|
|
202
|
+
entry.failedTests = [message];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
182
205
|
} catch (error) {
|
|
183
206
|
entry.verdict = 'error';
|
|
184
207
|
entry.failedTests = [error instanceof Error ? error.message : String(error)];
|
|
@@ -199,6 +222,9 @@ export async function runResyncShadow(max = 3, deps: ResyncShadowDeps = {}): Pro
|
|
|
199
222
|
mkdirSync(dirname(ledger), { recursive: true });
|
|
200
223
|
appendFileSync(ledger, `${JSON.stringify(entry)}\n`);
|
|
201
224
|
debug.log('loop.orchestrator', 'resync-shadow', { pr: entry.pr, verdict: entry.verdict, status: entry.status });
|
|
225
|
+
if (mode === 'live') debug.log('loop.orchestrator', 'resync-live', {
|
|
226
|
+
pr: entry.pr, verdict: entry.verdict, pushed: entry.pushed, labelRemoved: entry.labelRemoved,
|
|
227
|
+
});
|
|
202
228
|
if (entry.verdict !== 'error') tried++;
|
|
203
229
|
if (entry.verdict === 'saved') saved++;
|
|
204
230
|
}
|
|
@@ -211,13 +237,14 @@ if (import.meta.main) {
|
|
|
211
237
|
try {
|
|
212
238
|
const args = process.argv.slice(2);
|
|
213
239
|
if (args.includes('--help')) {
|
|
214
|
-
console.log('Usage: bun src/loops/orchestrator/resync-shadow.ts [--max N] [--json]');
|
|
240
|
+
console.log('Usage: bun src/loops/orchestrator/resync-shadow.ts [--max N] [--live] [--json]');
|
|
215
241
|
} else {
|
|
216
242
|
const index = args.indexOf('--max');
|
|
217
|
-
if (args.some((arg, i) => arg !== '--json' && arg !== '--max' && (index < 0 || i !== index + 1))
|
|
218
|
-
|| (index >= 0 && (index === args.length - 1 || args.lastIndexOf('--max') !== index))) throw new Error('usage: resync-shadow.ts [--max N] [--json]');
|
|
243
|
+
if (args.some((arg, i) => arg !== '--json' && arg !== '--live' && arg !== '--max' && (index < 0 || i !== index + 1))
|
|
244
|
+
|| (index >= 0 && (index === args.length - 1 || args.lastIndexOf('--max') !== index))) throw new Error('usage: resync-shadow.ts [--max N] [--live] [--json]');
|
|
219
245
|
const max = index < 0 ? 3 : Number(args[index + 1]);
|
|
220
|
-
const
|
|
246
|
+
const mode: ResyncShadowMode = args.includes('--live') ? 'live' : 'shadow';
|
|
247
|
+
const summary = await runResyncShadow(max, {}, mode);
|
|
221
248
|
console.log(process.argv.includes('--json') ? JSON.stringify(summary) : `resync-shadow candidates=${summary.candidates} tried=${summary.tried} saved=${summary.saved} savedRatio=${summary.savedRatio === null ? 'n/a' : `${(summary.savedRatio * 100).toFixed(1)}%`}`);
|
|
222
249
|
}
|
|
223
250
|
} catch (error) {
|
|
@@ -59,8 +59,8 @@ export const FEATURE_MATURITY = {
|
|
|
59
59
|
},
|
|
60
60
|
// Canonical CLI command names only; aliases inherit their command's grade.
|
|
61
61
|
cliRoot: {
|
|
62
|
-
preview: 'beta', pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
63
|
-
where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
62
|
+
preview: 'beta', pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', image: 'beta', 'agent-env': 'beta',
|
|
63
|
+
where: 'stable', bench: 'beta', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
64
64
|
pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
|
|
65
65
|
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',
|
|
66
66
|
control: 'beta', resources: 'beta', hooks: 'beta', setup: 'stable', start: 'stable',
|
|
@@ -425,12 +425,16 @@ const OPENROUTER: TierMap = {
|
|
|
425
425
|
// ⛔ bedrock 은 명시 선택 전용 — 이 표는 `--role-llm <role>=bedrock/<tier>` 같은 «명시» 해석에만 쓰인다.
|
|
426
426
|
// ⚠️ status = `wip`: 실 AWS 호출(계정별 5.5 접근 조건)을 아직 안 쟀고, 셋업 키 감지 표(PROVIDER_ENV_SPEC)에도
|
|
427
427
|
// 없다(자격이 API 키가 아니라 AWS 체인이라). 첫 실호출 스모크 뒤 `shipping` 으로 올린다.
|
|
428
|
+
// ⭐ BEDROCK-RUNTIME-PATH(10-11): 메시지 엔드포인트(mantle)에 5.5 가 없어(📏 `GET /v1/models`) 사다리 id 를 전역 추론 프로필
|
|
429
|
+
// `global.anthropic.…`(runtime 경로 · bedrock.ts BEDROCK_MODELS 의 `route` 칸)로 옮겼다. 공급자 이름은 그대로 `bedrock`.
|
|
430
|
+
// 📏 budget·balanced(haiku/sonnet 5.5)는 runtime 실호출 200 실측(ap-northeast-2) · opus 5.5 는 «추론 프로필 목록에 있음»까지만(호출 0).
|
|
431
|
+
// status 는 그대로 `wip` — `shipping` 은 셋업 키 감지 표(PROVIDER_ENV_SPEC) 칸을 요구한다(provider-wiring-contract 시험 · 범위 밖).
|
|
428
432
|
const BEDROCK: TierMap = {
|
|
429
|
-
budget: { model: 'anthropic.claude-haiku-5-5', reasoningLevel: 'off', label: 'Claude Haiku 5.5 (Bedrock)', rationale: 'Fast · cheap · bulk/classification', status: 'wip' },
|
|
430
|
-
balanced: { model: 'anthropic.claude-sonnet-5-5', reasoningLevel: 'off', label: 'Claude Sonnet 5.5 (Bedrock)', rationale: 'Default · everyday coding (BEDROCK_DEFAULT_MODEL)', status: 'wip' },
|
|
431
|
-
better: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'ANTHROPIC better 와 같은 배치(구현 = Opus 5.5)', status: 'wip' },
|
|
432
|
-
best: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'Best reasoning · architecture', status: 'wip' },
|
|
433
|
-
loaded: { model: 'anthropic.claude-opus-5-5', reasoningLevel: 'high', label: 'Claude Opus 5.5 · extended thinking (Bedrock)', rationale: 'Loaded · deep multi-step', status: 'wip' },
|
|
433
|
+
budget: { model: 'global.anthropic.claude-haiku-5-5', reasoningLevel: 'off', label: 'Claude Haiku 5.5 (Bedrock)', rationale: 'Fast · cheap · bulk/classification', status: 'wip' },
|
|
434
|
+
balanced: { model: 'global.anthropic.claude-sonnet-5-5', reasoningLevel: 'off', label: 'Claude Sonnet 5.5 (Bedrock)', rationale: 'Default · everyday coding (BEDROCK_DEFAULT_MODEL)', status: 'wip' },
|
|
435
|
+
better: { model: 'global.anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'ANTHROPIC better 와 같은 배치(구현 = Opus 5.5)', status: 'wip' },
|
|
436
|
+
best: { model: 'global.anthropic.claude-opus-5-5', reasoningLevel: 'medium', label: 'Claude Opus 5.5 · thinking medium (Bedrock)', rationale: 'Best reasoning · architecture', status: 'wip' },
|
|
437
|
+
loaded: { model: 'global.anthropic.claude-opus-5-5', reasoningLevel: 'high', label: 'Claude Opus 5.5 · extended thinking (Bedrock)', rationale: 'Loaded · deep multi-step', status: 'wip' },
|
|
434
438
|
};
|
|
435
439
|
|
|
436
440
|
/** 가격·창 미측정이라 기존 tier 배치를 바꾸지 않고 명시 모델로만 고르는 칸.
|