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/cli/tasks-cli.ts
CHANGED
|
@@ -9,9 +9,10 @@ import { readNexusRuntime } from '../nexus/runtime.js';
|
|
|
9
9
|
import { checklistDevVersion } from '../release-loop/checklist.js';
|
|
10
10
|
import { cardsBoard, countBoardTasks, INTERNAL_PACK_PROJECT, mergeBoards, releasePackBoard, type BoardNode } from '../task-agent/board.js';
|
|
11
11
|
import type { Task } from '../task-orchestrator/types.js';
|
|
12
|
-
import { cardPrNumber, COMPLETION_KINDS, handTask, LIVE_MOVE_HISTORY_EVENT, LIVE_MOVE_RESULT_EVENT, nextMoveFor, resolveHandText, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote, TA_LAND_ENV, TASK_CARD_PREFIX, TASK_SEATS, type CompletionKind, type TaskCard, type TaskLaunchContext, type TaskLaunchReceipt, type TaskLauncher, type TaskSeat } from '../task-agent/task-hand.js';
|
|
12
|
+
import { cardPrNumber, CLOSED_BY_HAND_EVENT, closeTaskCard, COMPLETION_KINDS, handTask, updateTaskCard, LIVE_MOVE_HISTORY_EVENT, LIVE_MOVE_RESULT_EVENT, nextMoveFor, resolveHandText, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote, TA_LAND_ENV, TASK_CARD_PREFIX, TASK_SEATS, type CompletionKind, type TaskCard, type TaskLaunchContext, type TaskLaunchReceipt, type TaskLauncher, type TaskSeat } from '../task-agent/task-hand.js';
|
|
13
13
|
import { readCardSalvageBranch, refreshCardRunBinding, type CardRunEvidenceDeps } from '../task-agent/card-evidence.js';
|
|
14
14
|
import { captureReviewResults } from '../task-agent/review-result-capture.js';
|
|
15
|
+
import { HAND_MOVE_KINDS, recordHandMove } from '../task-agent/live-move-history.js';
|
|
15
16
|
import { advanceMission, handMission, MissionAdvanceError, openMissionIds, recordPieceRef, type AdvanceMissionResult, type MissionDecompose, type PieceEvidenceReader } from '../task-agent/mission.js';
|
|
16
17
|
import { formatTaskAgentCover, measureTaskAgentCover, type CoverRow, type TaskAgentCoverInput } from '../task-agent/cover.js';
|
|
17
18
|
import { LogStore, STORE_SAFETY_MAX, type LogQuery, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
@@ -44,9 +45,13 @@ export interface TasksCliDeps {
|
|
|
44
45
|
remoteLedgerMirror?: (card: TaskCard) => void;
|
|
45
46
|
/** `tasks parents` 의 낡은 부모 보고 의존(시험 주입). */
|
|
46
47
|
staleParents?: import('../task-agent/parent-host.js').StaleParentDeps;
|
|
48
|
+
/** `tasks adopt-runs` 의 백필 의존(시험 주입 · 등록부·상태 파일). */
|
|
49
|
+
adoptRuns?: import('../task-agent/run-adopt.js').RunAdoptDeps;
|
|
47
50
|
/** `tasks cover` logs 조회 seam. 기본은 등록된 모든 우주(운영 ⊕ test)의 logs.db 를 read-only 로 연다 —
|
|
48
51
|
* pr land 는 작업 트리마다 파생 test 우주에 기록되므로 현재 우주만 읽으면 분모가 늘 0 이다. */
|
|
49
52
|
coverLogs?: (query: LogQuery) => LogStoreRow[] | CoverLogRead;
|
|
53
|
+
/** `tasks cover --by-card` 카드 원장 읽기 seam(읽기 전용). 플래그가 있을 때만 부른다. 기본 = TA 카드 원장(task-agent-actions.json). null = 못 읽음. */
|
|
54
|
+
coverCards?: () => CoverCardRow[] | null | Promise<CoverCardRow[] | null>;
|
|
50
55
|
/** TA-REJUDGE-ON-HEAD — `tasks rejudge` 의 판단 의존(시험 주입 · 기본 = 설정 liveMoves ⊕ 실물 gh·리뷰·재게이트·land). */
|
|
51
56
|
rejudge?: Partial<import('../task-agent/rejudge-on-head.js').RejudgeDeps>;
|
|
52
57
|
/** TA-REJUDGE-ON-HEAD — `tasks regate-run` 의 재게이트(시험 주입 · 기본 `runHostRegate`). */
|
|
@@ -469,9 +474,31 @@ function coverEvents<T>(rows: readonly LogStoreRow[], category: string, event: s
|
|
|
469
474
|
return events;
|
|
470
475
|
}
|
|
471
476
|
|
|
477
|
+
export interface CoverCardRow { id: string; createdAt: string }
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 분모 = TA 카드 원장(`task-agent-actions.json` · `ta-…` id) — 분자 `task-agent`/`action` 로그의 taskId 가 이 id 다.
|
|
481
|
+
* (task-cards CardStore 의 UUID 카드는 taskId 와 겹치지 않아 언제나 0/N 을 «잰 값처럼» 그린다 — 리뷰 must-fix.)
|
|
482
|
+
*/
|
|
483
|
+
function readCoverCards(statePath: string): CoverCardRow[] {
|
|
484
|
+
const tasks = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {};
|
|
485
|
+
return Object.values(tasks).filter((card) => typeof card?.id === 'string' && typeof card.createdAt === 'string')
|
|
486
|
+
.map(({ id, createdAt }) => ({ id, createdAt }));
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Cards whose createdAt falls in the window; an unparsable createdAt is not placed in any window. */
|
|
490
|
+
export function coverCardsInWindow(cards: readonly CoverCardRow[] | null, window: { sinceMs?: number; untilMs?: number }): { id: string }[] | null {
|
|
491
|
+
if (cards === null) return null;
|
|
492
|
+
return cards.filter(({ createdAt }) => {
|
|
493
|
+
const time = Date.parse(createdAt);
|
|
494
|
+
return Number.isFinite(time) && (window.sinceMs === undefined || time >= window.sinceMs) && (window.untilMs === undefined || time <= window.untilMs);
|
|
495
|
+
}).map(({ id }) => ({ id }));
|
|
496
|
+
}
|
|
497
|
+
|
|
472
498
|
export function readTaskAgentCover(
|
|
473
499
|
window: { sinceMs?: number; untilMs?: number } = {},
|
|
474
500
|
query: NonNullable<TasksCliDeps['coverLogs']> = queryCoverLogs,
|
|
501
|
+
taskCards?: readonly { id: string }[] | null,
|
|
475
502
|
): { rows: CoverRow[]; limitReached: boolean; unreadableStores: number } {
|
|
476
503
|
let unreadableStores = 0;
|
|
477
504
|
const read = (category: 'task-agent' | 'pr.land', event: 'action' | 'step'): { rows: LogStoreRow[]; limitReached: boolean } | null => {
|
|
@@ -490,10 +517,11 @@ export function readTaskAgentCover(
|
|
|
490
517
|
const input: TaskAgentCoverInput = {
|
|
491
518
|
taskAgentActions: actions === null ? null : coverEvents(actions, 'task-agent', 'action', (data) =>
|
|
492
519
|
typeof data.kind === 'string' && typeof data.result === 'string'
|
|
493
|
-
? { kind: data.kind as NonNullable<TaskAgentCoverInput['taskAgentActions']>[number]['kind'], result: data.result } : null),
|
|
520
|
+
? { kind: data.kind as NonNullable<TaskAgentCoverInput['taskAgentActions']>[number]['kind'], result: data.result, ...(typeof data.taskId === 'string' ? { taskId: data.taskId } : {}) } : null),
|
|
494
521
|
allLands: lands === null ? null : coverEvents(lands, 'pr.land', 'step', (data) =>
|
|
495
522
|
typeof data.step === 'string' && typeof data.ok === 'boolean' ? { step: data.step, ok: data.ok } : null),
|
|
496
523
|
truncated,
|
|
524
|
+
...(taskCards === undefined ? {} : { taskCards }),
|
|
497
525
|
};
|
|
498
526
|
return { rows: measureTaskAgentCover(input), limitReached: truncated.taskAgentActions || truncated.allLands, unreadableStores };
|
|
499
527
|
}
|
|
@@ -516,7 +544,8 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
516
544
|
.option('--until <iso>', '끝 시각 (ISO)')
|
|
517
545
|
.option('--json', 'JSON 출력')
|
|
518
546
|
.option('--lines', '동사별 한 줄 요약 출력 (읽기 전용)')
|
|
519
|
-
.
|
|
547
|
+
.option('--by-card', 'review·retry·green 분모를 카드 원장(창 안 createdAt 카드 수)으로 센다 (읽기 전용)')
|
|
548
|
+
.action(async (opts: { since?: string; until?: string; json?: boolean; lines?: boolean; byCard?: boolean }) => {
|
|
520
549
|
const parseIso = (value: string | undefined, flag: string): number | undefined => {
|
|
521
550
|
if (value === undefined) return undefined;
|
|
522
551
|
const time = Date.parse(value);
|
|
@@ -527,7 +556,16 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
527
556
|
const sinceMs = parseIso(opts.since, '--since');
|
|
528
557
|
const untilMs = parseIso(opts.until, '--until');
|
|
529
558
|
if (sinceMs !== undefined && untilMs !== undefined && sinceMs > untilMs) throw new Error('--since 가 --until 보다 늦다');
|
|
530
|
-
|
|
559
|
+
let taskCards: { id: string }[] | null | undefined;
|
|
560
|
+
if (opts.byCard) {
|
|
561
|
+
let cards: CoverCardRow[] | null;
|
|
562
|
+
try { cards = await (deps.coverCards ?? (() => readCoverCards(deps.taskStatePath ?? taskStatePathDefault())))(); } catch (error) {
|
|
563
|
+
cards = null;
|
|
564
|
+
try { debug.log('task-agent.cover', 'by-card-unreadable', { error: String(error).slice(-200) }, { level: 'warn' }); } catch { /* fail-soft */ }
|
|
565
|
+
}
|
|
566
|
+
taskCards = coverCardsInWindow(cards, { sinceMs, untilMs });
|
|
567
|
+
}
|
|
568
|
+
const { rows, limitReached, unreadableStores } = readTaskAgentCover({ sinceMs, untilMs }, deps.coverLogs, taskCards);
|
|
531
569
|
const limitWarning = limitReached ? `조회 상한 ${COVER_QUERY_LIMIT} 도달 — 더 오래된 사건이 있을 수 있다(절단된 수를 전체로 읽지 말 것)` : null;
|
|
532
570
|
const storeWarning = unreadableStores > 0 ? `logs.db ${unreadableStores}개를 못 읽었다 — 그 우주의 사건은 수에 없다(«없다»로 읽지 말 것)` : null;
|
|
533
571
|
if (opts.json) out(JSON.stringify({ rows, limitReached, unreadableStores, ...(limitWarning ? { warning: limitWarning } : {}), ...(storeWarning ? { storeWarning } : {}) }));
|
|
@@ -677,6 +715,61 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
677
715
|
}
|
|
678
716
|
});
|
|
679
717
|
|
|
718
|
+
tasks.command('hand-move <id>')
|
|
719
|
+
.description('HAND-MOVE-LEDGER — 자리가 손으로 마무리한 수(기존 실패 면제 · diff 재적용 · 범위 자르기 · 쪼개기 · 다른 모델 의견)를 그 카드 history 에 한 줄로 남긴다 — live-move 와 같은 원장 칸 · 기존 줄은 건드리지 않는다')
|
|
720
|
+
.requiredOption('--kind <kind>', `손으로 쓴 수 (${HAND_MOVE_KINDS.join('|')})`)
|
|
721
|
+
.requiredOption('--by <seat>', '남긴 자리(예: OP·MK·TC·UX)')
|
|
722
|
+
.requiredOption('--detail <text>', '무엇을 했나 — 한 줄')
|
|
723
|
+
.option('--pr <number>', '관련 PR 번호')
|
|
724
|
+
.action(async (id: string, opts: { kind: string; by: string; detail: string; pr?: string }) => {
|
|
725
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
726
|
+
const pr = opts.pr === undefined ? undefined : Number(opts.pr);
|
|
727
|
+
if (pr !== undefined && (!Number.isInteger(pr) || pr <= 0)) { out(`--pr 은 자연수다: ${opts.pr}`); process.exitCode = 1; return; }
|
|
728
|
+
const outcome = recordHandMove(deps.taskStatePath ?? taskStatePathDefault(), id,
|
|
729
|
+
{ kind: opts.kind, by: opts.by, detail: opts.detail, ...(pr !== undefined ? { pr } : {}) });
|
|
730
|
+
try {
|
|
731
|
+
debug.log('task-agent', 'hand-move', outcome.written
|
|
732
|
+
? { card: id, kind: outcome.entry.kind, by: outcome.entry.by, ...(outcome.entry.pr !== undefined ? { pr: outcome.entry.pr } : {}) }
|
|
733
|
+
: { card: id, kind: opts.kind, by: opts.by, reason: outcome.reason });
|
|
734
|
+
} catch { /* fail-soft */ }
|
|
735
|
+
if (!outcome.written) { out(`hand-move 한 줄을 못 남겼다 — ${outcome.reason}`); process.exitCode = 1; return; }
|
|
736
|
+
out([id, 'hand-move', outcome.entry.kind, outcome.entry.by, outcome.entry.detail, ...(outcome.entry.pr !== undefined ? [`pr ${outcome.entry.pr}`] : [])].join('\t'));
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
tasks.command('close <cardId>')
|
|
740
|
+
.description('과제 카드를 손으로 닫는다 — 상태를 failed 로 두고 사유를 history(closed-by-hand)에 한 줄 남긴다 · 없는 카드 id 는 거부(exit 1)')
|
|
741
|
+
.requiredOption('--reason <사유>', '닫는 사유(비면 거부)')
|
|
742
|
+
.option('--by <seat>', '닫은 자리(예: OP·MK·TC·UX)', 'operator')
|
|
743
|
+
.option('--json', 'JSON 출력')
|
|
744
|
+
.action(async (cardId: string, opts: { reason: string; by: string; json?: boolean }) => {
|
|
745
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
746
|
+
const statePath = deps.taskStatePath ?? taskStatePathDefault();
|
|
747
|
+
try {
|
|
748
|
+
let found: TaskCard | undefined;
|
|
749
|
+
let changed = false;
|
|
750
|
+
const updated = updateTaskCard(statePath, cardId, (current) => {
|
|
751
|
+
if (!current) return undefined;
|
|
752
|
+
found = current;
|
|
753
|
+
const next = closeTaskCard(current, { reason: opts.reason, by: opts.by, at: new Date().toISOString() });
|
|
754
|
+
if (next === current) return undefined; // 이미 손으로 닫힘 — 다시 쓰지 않는다(멱등)
|
|
755
|
+
changed = true;
|
|
756
|
+
return next;
|
|
757
|
+
});
|
|
758
|
+
if (!found) { out(`과제 카드 없음: ${cardId}`); process.exitCode = 1; return; }
|
|
759
|
+
const card: TaskCard = updated ?? found;
|
|
760
|
+
const from = (found as TaskCard).status;
|
|
761
|
+
// 이미 닫힌 카드면 «저장된» 사유를 낸다 — 새로 준 사유는 원장에 없다.
|
|
762
|
+
const stored = [...card.history].reverse().find((item) => item.event === CLOSED_BY_HAND_EVENT);
|
|
763
|
+
const reason = changed ? opts.reason.trim() : (stored?.reason ?? stored?.detail ?? opts.reason.trim());
|
|
764
|
+
if (changed) { try { debug.log('task-agent', 'card-closed-by-hand', { id: cardId, from, by: opts.by, reason }); } catch { /* fail-soft */ } }
|
|
765
|
+
if (opts.json) out(JSON.stringify({ id: card.id, from, to: card.status, reason, ...(changed ? {} : { already: true }) }));
|
|
766
|
+
else out([card.id, card.status, changed ? 'closed-by-hand' : 'already-closed-by-hand', changed ? opts.by : (stored?.by ?? '-'), reason].join('\t'));
|
|
767
|
+
} catch (error) {
|
|
768
|
+
out(error instanceof Error ? error.message : '과제 카드 닫기 실패');
|
|
769
|
+
process.exitCode = 1;
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
680
773
|
tasks.command('parents')
|
|
681
774
|
.description('낡은 하니스 부모 보고(읽기 전용 · 죽이지 않는다) — 나이 ≥ N 시간이고 런 원장이 끝났거나(run-status) Pod 이 없는 `harness say|ask` 부모')
|
|
682
775
|
.option('--older-than <hours>', '나이 하한(시간)', '6')
|
|
@@ -703,6 +796,18 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
703
796
|
for (const row of report.rows) out([row.pid, row.ageHours, row.seat ?? '-', row.version ?? '-', row.runId ?? '-', row.reasons.join(',')].join('\t'));
|
|
704
797
|
});
|
|
705
798
|
|
|
799
|
+
tasks.command('adopt-runs')
|
|
800
|
+
.description('TA-OWN-ALL-RUNS 백필 — 발사 등록부의 이 우주 런 중 카드 없는 것에 `ta-<runId>` 카드를 만든다(발사 훅 이전 런 몫 · 있으면 건드리지 않는다)')
|
|
801
|
+
.option('--json', 'JSON 출력')
|
|
802
|
+
.action(async (opts: { json?: boolean }) => {
|
|
803
|
+
const { adoptUncardedRuns } = await import('../task-agent/run-adopt.js');
|
|
804
|
+
const report = adoptUncardedRuns({ ...(deps.taskStatePath ? { statePath: deps.taskStatePath } : {}), ...deps.adoptRuns });
|
|
805
|
+
try { debug.log('task-agent', 'run-adopt-backfill', report); } catch { /* fail-soft */ }
|
|
806
|
+
if (opts.json) { out(JSON.stringify(report)); return; }
|
|
807
|
+
const skipped = Object.entries(report.skipped).sort(([a], [b]) => a.localeCompare(b)).map(([reason, n]) => `${reason} ${n}`).join(' · ') || '없음';
|
|
808
|
+
out(`등록부 런 ${report.scanned} · 카드 만듦 ${report.adopted} · 건너뜀 ${skipped}`);
|
|
809
|
+
});
|
|
810
|
+
|
|
706
811
|
tasks.command('board')
|
|
707
812
|
.description('보드 — 프로젝트 › 목표 › 이정표 › 과제. 과제 카드(project·goal·milestone)와 내부 팩(릴리스 체크리스트 = elanous › 판 › 칸)을 합쳐 읽는다(쓰지 않는다)')
|
|
708
813
|
.option('--project <id>', '이 프로젝트만')
|
package/src/cli/usage-cli.ts
CHANGED
|
@@ -60,15 +60,87 @@ function readRunLogs(query: LogQuery): LogStoreRow[] {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
type UsageAxis = 'stage' | 'model' | 'account';
|
|
64
|
+
type ExtendedRunUsageInput = RunUsageInput & { stage?: string | null; account?: string | null };
|
|
65
|
+
type AxisUsageRow = {
|
|
66
|
+
stage?: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
account?: string;
|
|
69
|
+
hostIds: string[];
|
|
70
|
+
calls: number;
|
|
71
|
+
inputTokens: number;
|
|
72
|
+
outputTokens: number;
|
|
73
|
+
cacheReadInputTokens: number;
|
|
74
|
+
cacheCreationInputTokens: number;
|
|
75
|
+
reasoningOutputTokens: number;
|
|
76
|
+
usdKnown: number;
|
|
77
|
+
unknownCostCalls: number;
|
|
78
|
+
includedCalls: number;
|
|
79
|
+
apiEquivalentUsd: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function usageData(row: LogStoreRow, unwrapPod = false): ExtendedRunUsageInput | null {
|
|
64
83
|
if (row.category !== 'llm.usage' || row.event !== 'llm-usage' || !row.data) return null;
|
|
65
84
|
try {
|
|
66
|
-
|
|
85
|
+
let value: unknown = JSON.parse(row.data);
|
|
67
86
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
68
|
-
|
|
87
|
+
const wrapper = value as { origin?: unknown; originalData?: unknown };
|
|
88
|
+
if (unwrapPod && wrapper.origin === 'pod') {
|
|
89
|
+
value = typeof wrapper.originalData === 'string' ? JSON.parse(wrapper.originalData) : wrapper.originalData;
|
|
90
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
91
|
+
}
|
|
92
|
+
return value as ExtendedRunUsageInput;
|
|
69
93
|
} catch { return null; }
|
|
70
94
|
}
|
|
71
95
|
|
|
96
|
+
function stageForUsage(data: ExtendedRunUsageInput): string {
|
|
97
|
+
const stages = ['authoring', 'implement', 'review', 'gate'];
|
|
98
|
+
if (typeof data.stage === 'string' && data.stage.length > 0) return stages.includes(data.stage) ? data.stage : '기타';
|
|
99
|
+
// 역할 대체: 저작 호출은 role 'planning' 으로 실린다(ModelRole 에 authoring 이 없다) → authoring 단계로 센다.
|
|
100
|
+
const role = data.role === 'planning' ? 'authoring' : data.role;
|
|
101
|
+
if (typeof role === 'string' && role.length > 0) return stages.includes(role) ? role : '기타';
|
|
102
|
+
return '(none)';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function rollupByAxis(inputs: readonly ExtendedRunUsageInput[], axis: UsageAxis): AxisUsageRow[] {
|
|
106
|
+
const groups = new Map<string, ExtendedRunUsageInput[]>();
|
|
107
|
+
for (const data of inputs) {
|
|
108
|
+
const value = axis === 'stage' ? stageForUsage(data)
|
|
109
|
+
: axis === 'model' ? (data.model || '(none)')
|
|
110
|
+
: (data.account || '(none)');
|
|
111
|
+
const group = groups.get(value) ?? [];
|
|
112
|
+
group.push(data);
|
|
113
|
+
groups.set(value, group);
|
|
114
|
+
}
|
|
115
|
+
return [...groups].map(([value, group]) => {
|
|
116
|
+
const rows = rollupRunUsage(group);
|
|
117
|
+
const result: AxisUsageRow = {
|
|
118
|
+
[axis]: value,
|
|
119
|
+
hostIds: [...new Set(rows.flatMap((row) => row.hostIds))],
|
|
120
|
+
calls: rows.reduce((sum, row) => sum + row.calls, 0),
|
|
121
|
+
inputTokens: rows.reduce((sum, row) => sum + row.inputTokens, 0),
|
|
122
|
+
outputTokens: rows.reduce((sum, row) => sum + row.outputTokens, 0),
|
|
123
|
+
cacheReadInputTokens: rows.reduce((sum, row) => sum + row.cacheReadInputTokens, 0),
|
|
124
|
+
cacheCreationInputTokens: rows.reduce((sum, row) => sum + row.cacheCreationInputTokens, 0),
|
|
125
|
+
reasoningOutputTokens: rows.reduce((sum, row) => sum + row.reasoningOutputTokens, 0),
|
|
126
|
+
usdKnown: rows.reduce((sum, row) => sum + row.usdKnown, 0),
|
|
127
|
+
unknownCostCalls: rows.reduce((sum, row) => sum + row.unknownCostCalls, 0),
|
|
128
|
+
includedCalls: rows.reduce((sum, row) => sum + row.includedCalls, 0),
|
|
129
|
+
apiEquivalentUsd: rows.reduce((sum, row) => sum + row.apiEquivalentUsd, 0),
|
|
130
|
+
};
|
|
131
|
+
return result;
|
|
132
|
+
}).sort((a, b) => b.calls - a.calls);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isUsageAxis(value: string): value is 'run' | 'role' | UsageAxis {
|
|
136
|
+
return value === 'run' || value === 'role' || value === 'stage' || value === 'model' || value === 'account';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function formatAxisUsage(row: AxisUsageRow, axis: UsageAxis): string {
|
|
140
|
+
const value = row[axis] ?? '(none)';
|
|
141
|
+
return `${axis}=${value} hosts=${row.hostIds.join(',') || '(none)'} calls=${row.calls} input=${row.inputTokens} output=${row.outputTokens} cacheRead=${row.cacheReadInputTokens} cacheCreation=${row.cacheCreationInputTokens} reasoning=${row.reasoningOutputTokens} usdKnown=${row.usdKnown} unknownCostCalls=${row.unknownCostCalls} includedCalls=${row.includedCalls} apiEquivalentUsd=${row.apiEquivalentUsd}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
72
144
|
function defaultCreditPlan(): CodexCreditPlan | null {
|
|
73
145
|
try { return codexCreditPlanFromDisk(); } catch { return null; }
|
|
74
146
|
}
|
|
@@ -94,15 +166,15 @@ export function registerUsageCommand(program: Command, deps: UsageCliDeps = {}):
|
|
|
94
166
|
|
|
95
167
|
usage
|
|
96
168
|
.command('runs')
|
|
97
|
-
.description('저장된 llm-usage 로그를
|
|
169
|
+
.description('저장된 llm-usage 로그를 런·역할·단계·모델·계정별로 집계한다')
|
|
98
170
|
.option('--run <runId>', '런 ID 정확 일치')
|
|
99
|
-
.option('--by <axis>', '집계 축 (run | role)', 'run')
|
|
171
|
+
.option('--by <axis>', '집계 축 (run | role | stage | model | account)', 'run')
|
|
100
172
|
.option(...LOGS_SINCE_OPTION)
|
|
101
173
|
.option('--json', '집계 행 JSON 출력')
|
|
102
174
|
.action((opts: { run?: string; by: string; since?: string; json?: boolean }, command: Command) => {
|
|
103
175
|
const json = opts.json || command.optsWithGlobals<{ json?: boolean }>().json;
|
|
104
|
-
if (opts.by
|
|
105
|
-
out.log('⛔ --by 는 run | role');
|
|
176
|
+
if (!isUsageAxis(opts.by)) {
|
|
177
|
+
out.log('⛔ --by 는 run | role | stage | model | account');
|
|
106
178
|
exit(2);
|
|
107
179
|
return;
|
|
108
180
|
}
|
|
@@ -116,8 +188,19 @@ export function registerUsageCommand(program: Command, deps: UsageCliDeps = {}):
|
|
|
116
188
|
exactCategories: ['llm.usage'], events: ['llm-usage'],
|
|
117
189
|
...(sinceMs !== undefined ? { sinceMs } : {}),
|
|
118
190
|
});
|
|
119
|
-
const
|
|
191
|
+
const unwrapPod = opts.by === 'stage' || opts.by === 'model' || opts.by === 'account';
|
|
192
|
+
const inputs = rows.map((row) => usageData(row, unwrapPod)).filter((data): data is ExtendedRunUsageInput => data !== null)
|
|
120
193
|
.filter((data) => opts.run === undefined || data.runId === opts.run);
|
|
194
|
+
if (opts.by === 'stage' || opts.by === 'model' || opts.by === 'account') {
|
|
195
|
+
const report = rollupByAxis(inputs, opts.by);
|
|
196
|
+
if (json) {
|
|
197
|
+
out.log(JSON.stringify(report, null, 2));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (report.length === 0) { out.log('(일치하는 런 사용량 없음)'); return; }
|
|
201
|
+
for (const row of report) out.log(formatAxisUsage(row, opts.by));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
121
204
|
const report = rollupRunUsage(inputs, { by: opts.by });
|
|
122
205
|
if (opts.by === 'role') report.sort((a, b) => b.calls - a.calls);
|
|
123
206
|
if (json) {
|
|
@@ -212,9 +212,10 @@ export const GOAL_INTENT_STALL_PROMPT = [
|
|
|
212
212
|
/** 재촉 상한 — 이 횟수를 쓰고도 텍스트만이면 종전 ④(텍스트 수용)로 그대로 떨어진다. */
|
|
213
213
|
export const GOAL_INTENT_STALL_MAX_REPROMPTS = 2;
|
|
214
214
|
|
|
215
|
-
/** 재촉 가드가 켜지는 provider —
|
|
216
|
-
*
|
|
217
|
-
|
|
215
|
+
/** 재촉 가드가 켜지는 provider — 구현 자식을 내는 provider 전부(10-11 · 첫 조각 #25876 은 OpenRouter 한정이었다 ·
|
|
216
|
+
* 실측 GLM Flash 첫 턴 40토큰 말만 하고 abandoned). 재촉은 «도구 0회·텍스트만» 턴에만 걸리므로 도구를 부르는 자식에는 영향이 없다.
|
|
217
|
+
* 목록 밖 provider(local 등)는 종전 그대로 바로 수용한다. */
|
|
218
|
+
export const GOAL_INTENT_STALL_PROVIDERS: ReadonlySet<string> = new Set(['openrouter', 'openai-codex', 'anthropic', 'grok', 'bedrock']);
|
|
218
219
|
|
|
219
220
|
/** evidence 누락 complete 재주입 — update_goal(complete) 를 호출했으나 evidence 가 비었을 때. */
|
|
220
221
|
export const GOAL_EVIDENCE_REQUIRED_PROMPT = [
|
|
@@ -280,7 +281,7 @@ export interface GoalLoopOptions {
|
|
|
280
281
|
controlSpaceId?: string | null;
|
|
281
282
|
/** Test seam — file-backed child liveness heartbeat. Default starts the workspace-file writer. */
|
|
282
283
|
startLivenessHeartbeat?: typeof startChildLivenessHeartbeat;
|
|
283
|
-
/** GOAL-LOOP-INTENT-STALL — 구현 자식의 LLM provider.
|
|
284
|
+
/** GOAL-LOOP-INTENT-STALL — 구현 자식의 LLM provider. GOAL_INTENT_STALL_PROVIDERS 에 있을 때만 «도구 0회·텍스트만» 턴을
|
|
284
285
|
* 최대 2번 재촉한다. 생략하면 harness role 이 executor 인 프로세스에서만 활성 provider 로 푼다.
|
|
285
286
|
* null = 가드 꺼짐. 시험은 이 인자를 명시해 env 에 기대지 않는다. */
|
|
286
287
|
intentStallProvider?: string | null;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { buildAdRunSetup } from '../ad-pipeline/ad-run-setup.js';
|
|
4
|
+
import type { CommandRunner } from '../ad-pipeline/higgsfield-backend.js';
|
|
5
|
+
import type { ShootRunOptions } from '../ad-pipeline/shoot-run.js';
|
|
6
|
+
import type { QcThresholds } from '../ad-pipeline/qc.js';
|
|
7
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
8
|
+
import {
|
|
9
|
+
createDashboardAdSlashRuntime as createAdSlashRuntime,
|
|
10
|
+
type DashboardAdSlashRuntime,
|
|
11
|
+
type DashboardAdSlashRuntimeDeps,
|
|
12
|
+
} from './ad-slash-runtime.js';
|
|
13
|
+
|
|
14
|
+
const dashboardDefaultShootRunOptions: Required<Pick<ShootRunOptions, 'pollIntervalMs' | 'maxPollsPerJob' | 'submitStaggerMs'>> = {
|
|
15
|
+
pollIntervalMs: 3_000,
|
|
16
|
+
// 2026-09-12 seedance_2_0 measured 330–400s; 174 polls = 522s, leaving 402s after six 20s staggers.
|
|
17
|
+
maxPollsPerJob: 174,
|
|
18
|
+
submitStaggerMs: 20_000,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* ⛔ `src/ad-pipeline/ad-run-setup.ts` 의 `optionalProductionInputs` «사본»이다 — 그 목록은 export 가 아니다.
|
|
23
|
+
* 여기서 다시 세는 이유: 이 파일이 `buildAdRunSetup` «뒤»에 `assemblyMaterials` 를 덧붙이므로,
|
|
24
|
+
* 그 함수가 «입력» 기준으로 낸 목록은 최종 `production` 과 어긋난다(`#17880`).
|
|
25
|
+
* ⚠️ 사본이라 «늙는다» — `src/dashboard/index.test.ts` 가 권위 목록과 같은지 단언한다.
|
|
26
|
+
*/
|
|
27
|
+
export const dashboardOptionalProductionInputs = [
|
|
28
|
+
'assembly',
|
|
29
|
+
'assemblyMaterials',
|
|
30
|
+
'captionFontPath',
|
|
31
|
+
'musicBedPath',
|
|
32
|
+
'qcThresholds',
|
|
33
|
+
'voiceover',
|
|
34
|
+
'soundtrack',
|
|
35
|
+
'referenceAssets',
|
|
36
|
+
'shootRunOptions',
|
|
37
|
+
'ground',
|
|
38
|
+
'invariants',
|
|
39
|
+
] as const;
|
|
40
|
+
|
|
41
|
+
export interface DashboardAdAssetPreset {
|
|
42
|
+
readonly captionFontPath?: string;
|
|
43
|
+
readonly musicBedPath?: string;
|
|
44
|
+
readonly referenceAssets?: Readonly<Record<string, string[]>>;
|
|
45
|
+
readonly shootRunOptions?: ShootRunOptions;
|
|
46
|
+
readonly qcThresholds?: QcThresholds;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Operator-owned, instance-scoped optional asset preset; never a repository asset path. */
|
|
50
|
+
export function dashboardAdAssetPresetPath(stateRoot = elanousStateRoot()): string {
|
|
51
|
+
return join(stateRoot, 'ad-assets.json');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Reads optional operator-owned asset paths without inventing local defaults. */
|
|
55
|
+
export function loadDashboardAdAssetPreset(path: string): DashboardAdAssetPreset {
|
|
56
|
+
let raw: string;
|
|
57
|
+
try {
|
|
58
|
+
raw = readFileSync(path, 'utf8');
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return {};
|
|
61
|
+
throw new Error(`dashboard ad asset preset unreadable at ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let parsed: unknown;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(raw);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
|
+
}
|
|
70
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
71
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: expected an object`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const preset = parsed as Record<string, unknown>;
|
|
75
|
+
for (const key of ['captionFontPath', 'musicBedPath'] as const) {
|
|
76
|
+
if (preset[key] !== undefined && typeof preset[key] !== 'string') {
|
|
77
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: ${key} must be a string`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (preset.referenceAssets !== undefined && (
|
|
81
|
+
preset.referenceAssets === null
|
|
82
|
+
|| typeof preset.referenceAssets !== 'object'
|
|
83
|
+
|| Array.isArray(preset.referenceAssets)
|
|
84
|
+
|| Object.values(preset.referenceAssets).some((value) => !Array.isArray(value) || value.some((entry) => typeof entry !== 'string'))
|
|
85
|
+
)) {
|
|
86
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: referenceAssets must be a record of string arrays`);
|
|
87
|
+
}
|
|
88
|
+
if (preset.shootRunOptions !== undefined && (
|
|
89
|
+
preset.shootRunOptions === null
|
|
90
|
+
|| typeof preset.shootRunOptions !== 'object'
|
|
91
|
+
|| Array.isArray(preset.shootRunOptions)
|
|
92
|
+
)) {
|
|
93
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: shootRunOptions must be an object`);
|
|
94
|
+
}
|
|
95
|
+
const shootRunOptions = preset.shootRunOptions as Record<string, unknown> | undefined;
|
|
96
|
+
for (const key of ['pollIntervalMs', 'maxPollsPerJob', 'maxPollElapsedMs', 'submitStaggerMs', 'submitRetries'] as const) {
|
|
97
|
+
if (shootRunOptions?.[key] !== undefined && (typeof shootRunOptions[key] !== 'number' || !Number.isFinite(shootRunOptions[key]))) {
|
|
98
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: shootRunOptions.${key} must be a finite number`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (preset.qcThresholds !== undefined && (
|
|
102
|
+
preset.qcThresholds === null
|
|
103
|
+
|| typeof preset.qcThresholds !== 'object'
|
|
104
|
+
|| Array.isArray(preset.qcThresholds)
|
|
105
|
+
)) {
|
|
106
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds must be an object`);
|
|
107
|
+
}
|
|
108
|
+
const qcThresholds = preset.qcThresholds as Record<string, unknown> | undefined;
|
|
109
|
+
for (const key of ['dialogueLufsTolerance', 'colorDistance', 'frameDiffVariance'] as const) {
|
|
110
|
+
if (qcThresholds?.[key] !== undefined && (typeof qcThresholds[key] !== 'number' || !Number.isFinite(qcThresholds[key]))) {
|
|
111
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.${key} must be a finite number`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (qcThresholds?.durationRanges !== undefined && (
|
|
115
|
+
qcThresholds.durationRanges === null
|
|
116
|
+
|| typeof qcThresholds.durationRanges !== 'object'
|
|
117
|
+
|| Array.isArray(qcThresholds.durationRanges)
|
|
118
|
+
)) {
|
|
119
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.durationRanges must be an object`);
|
|
120
|
+
}
|
|
121
|
+
const durationRanges = qcThresholds?.durationRanges as Record<string, unknown> | undefined;
|
|
122
|
+
for (const scope of ['cut', 'master'] as const) {
|
|
123
|
+
if (durationRanges?.[scope] !== undefined && (
|
|
124
|
+
durationRanges[scope] === null
|
|
125
|
+
|| typeof durationRanges[scope] !== 'object'
|
|
126
|
+
|| Array.isArray(durationRanges[scope])
|
|
127
|
+
)) {
|
|
128
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.durationRanges.${scope} must be an object`);
|
|
129
|
+
}
|
|
130
|
+
const range = durationRanges?.[scope] as Record<string, unknown> | undefined;
|
|
131
|
+
if (range !== undefined) {
|
|
132
|
+
const minimumSeconds = range.minimumSeconds;
|
|
133
|
+
const maximumSeconds = range.maximumSeconds;
|
|
134
|
+
if (typeof minimumSeconds !== 'number' || !Number.isFinite(minimumSeconds)) {
|
|
135
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.durationRanges.${scope}.minimumSeconds must be a finite number`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof maximumSeconds !== 'number' || !Number.isFinite(maximumSeconds)) {
|
|
138
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.durationRanges.${scope}.maximumSeconds must be a finite number`);
|
|
139
|
+
}
|
|
140
|
+
if (minimumSeconds > maximumSeconds) {
|
|
141
|
+
throw new Error(`dashboard ad asset preset malformed at ${path}: qcThresholds.durationRanges.${scope}.minimumSeconds must be less than or equal to maximumSeconds`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
...(typeof preset.captionFontPath === 'string' ? { captionFontPath: preset.captionFontPath } : {}),
|
|
147
|
+
...(typeof preset.musicBedPath === 'string' ? { musicBedPath: preset.musicBedPath } : {}),
|
|
148
|
+
...(preset.referenceAssets === undefined ? {} : { referenceAssets: preset.referenceAssets as Record<string, string[]> }),
|
|
149
|
+
...(shootRunOptions === undefined ? {} : { shootRunOptions: shootRunOptions as ShootRunOptions }),
|
|
150
|
+
...(qcThresholds === undefined ? {} : { qcThresholds: qcThresholds as QcThresholds }),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const dashboardAdCommandRunner: CommandRunner = {
|
|
155
|
+
async run(argv, options) {
|
|
156
|
+
const process = Bun.spawn([...argv], {
|
|
157
|
+
stdout: 'pipe',
|
|
158
|
+
stderr: 'pipe',
|
|
159
|
+
...(options?.timeoutMs ? { timeout: options.timeoutMs } : {}),
|
|
160
|
+
});
|
|
161
|
+
const [raw, stderr, exitCode] = await Promise.all([
|
|
162
|
+
new Response(process.stdout).bytes(),
|
|
163
|
+
new Response(process.stderr).text(),
|
|
164
|
+
process.exited,
|
|
165
|
+
]);
|
|
166
|
+
return { stdout: new TextDecoder().decode(raw), stderr, exitCode, raw };
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export function createDashboardAdRunSetupFactory(input: {
|
|
171
|
+
readonly home: string;
|
|
172
|
+
readonly date: string;
|
|
173
|
+
readonly contractsJson: string;
|
|
174
|
+
readonly runner: CommandRunner;
|
|
175
|
+
readonly assetPresetPath?: string;
|
|
176
|
+
}): (allowSpend: boolean) => ReturnType<typeof buildAdRunSetup> {
|
|
177
|
+
let runSequence = 0;
|
|
178
|
+
let activeRunSlug: string | undefined;
|
|
179
|
+
|
|
180
|
+
return (allowSpend) => {
|
|
181
|
+
if (!allowSpend) {
|
|
182
|
+
const runStart = new Date();
|
|
183
|
+
const time = [runStart.getHours(), runStart.getMinutes(), runStart.getSeconds()]
|
|
184
|
+
.map((part) => String(part).padStart(2, '0'))
|
|
185
|
+
.join('');
|
|
186
|
+
activeRunSlug = `dashboard-ad-${time}-${++runSequence}`;
|
|
187
|
+
}
|
|
188
|
+
const slug = activeRunSlug ?? (() => {
|
|
189
|
+
const runStart = new Date();
|
|
190
|
+
const time = [runStart.getHours(), runStart.getMinutes(), runStart.getSeconds()]
|
|
191
|
+
.map((part) => String(part).padStart(2, '0'))
|
|
192
|
+
.join('');
|
|
193
|
+
return `dashboard-ad-${time}-${++runSequence}`;
|
|
194
|
+
})();
|
|
195
|
+
const assetPreset = input.assetPresetPath === undefined ? {} : loadDashboardAdAssetPreset(input.assetPresetPath);
|
|
196
|
+
let setup = buildAdRunSetup({
|
|
197
|
+
home: input.home,
|
|
198
|
+
slug,
|
|
199
|
+
date: input.date,
|
|
200
|
+
version: 1,
|
|
201
|
+
aspect: '9x16',
|
|
202
|
+
contractsJson: input.contractsJson,
|
|
203
|
+
runner: input.runner,
|
|
204
|
+
...assetPreset,
|
|
205
|
+
referenceAssets: assetPreset.referenceAssets ?? {},
|
|
206
|
+
shootRunOptions: { ...dashboardDefaultShootRunOptions, ...assetPreset.shootRunOptions },
|
|
207
|
+
soundtrack: input.runner,
|
|
208
|
+
voiceover: { runner: input.runner, lines: [] },
|
|
209
|
+
allowSpend,
|
|
210
|
+
});
|
|
211
|
+
if (!('error' in setup) && setup.production) {
|
|
212
|
+
const production = {
|
|
213
|
+
...setup.production,
|
|
214
|
+
assemblyMaterials: { options: { workDir: setup.workDir, outputName: setup.outputName } },
|
|
215
|
+
};
|
|
216
|
+
setup = {
|
|
217
|
+
...setup,
|
|
218
|
+
production,
|
|
219
|
+
missingProductionInputs: dashboardOptionalProductionInputs.filter((key) => production[key] === undefined),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return setup;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Builds the /ad runtime bindings used by showDashboard for each invocation's spend intent. */
|
|
227
|
+
export function createDashboardAdRunSetupBindings(
|
|
228
|
+
buildDashboardAdRunSetup: ReturnType<typeof createDashboardAdRunSetupFactory>,
|
|
229
|
+
) {
|
|
230
|
+
let adRunSetup: ReturnType<typeof buildAdRunSetup> | undefined;
|
|
231
|
+
let outputSetupError: string | undefined;
|
|
232
|
+
try {
|
|
233
|
+
adRunSetup = buildDashboardAdRunSetup(false);
|
|
234
|
+
outputSetupError = 'error' in adRunSetup ? adRunSetup.error : undefined;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
outputSetupError = error instanceof Error ? error.message : String(error);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
production: adRunSetup && !('error' in adRunSetup) ? adRunSetup.production : undefined,
|
|
241
|
+
productionForSpend: (allowSpend: boolean) => {
|
|
242
|
+
try {
|
|
243
|
+
const setup = buildDashboardAdRunSetup(allowSpend);
|
|
244
|
+
if ('error' in setup) {
|
|
245
|
+
outputSetupError = setup.error;
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
return setup.production;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
outputSetupError = error instanceof Error ? error.message : String(error);
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
missingProductionInputs: adRunSetup && !('error' in adRunSetup) ? adRunSetup.missingProductionInputs : undefined,
|
|
255
|
+
get outputSetupError() {
|
|
256
|
+
return outputSetupError;
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Connects the dashboard's setup state to the actual /ad runtime. */
|
|
262
|
+
export function createDashboardAdSlashRuntime(
|
|
263
|
+
input: Omit<DashboardAdSlashRuntimeDeps, 'production' | 'productionForSpend' | 'missingProductionInputs' | 'outputSetupError'> & {
|
|
264
|
+
readonly adRunSetupBindings?: ReturnType<typeof createDashboardAdRunSetupBindings>;
|
|
265
|
+
readonly adRunSetupError?: string;
|
|
266
|
+
},
|
|
267
|
+
): DashboardAdSlashRuntime {
|
|
268
|
+
const { adRunSetupBindings, adRunSetupError, ...deps } = input;
|
|
269
|
+
return createAdSlashRuntime({
|
|
270
|
+
...deps,
|
|
271
|
+
production: adRunSetupBindings?.production,
|
|
272
|
+
productionForSpend: adRunSetupBindings?.productionForSpend,
|
|
273
|
+
missingProductionInputs: adRunSetupBindings?.missingProductionInputs,
|
|
274
|
+
get outputSetupError() {
|
|
275
|
+
return adRunSetupBindings?.outputSetupError ?? adRunSetupError;
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|