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
|
@@ -79,6 +79,10 @@ import { declaredGoalType, type GoalType as DeclaredGoalType } from '../../self-
|
|
|
79
79
|
import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
|
|
80
80
|
import { getUserConfig, roleLlmEnvName, type UserConfig } from '../../user-config.js';
|
|
81
81
|
import { podChildProviderOf, podChildProviderRefusal, type PodChildProvider } from './pod-child-providers.js';
|
|
82
|
+
import { BEDROCK_POD_EGRESS_LABEL, bedrockPodEnv, checkBedrockPodSecret, type BedrockPodSpec } from './pod-bedrock-child.js';
|
|
83
|
+
import { resolveBedrockRegion, validBedrockRegion } from '../../llm/bedrock.js';
|
|
84
|
+
import { ZAI_POD_EGRESS_LABEL, checkZaiPodSecret, countActiveZaiJobs, decideZaiPodSlot, zaiOverflowChildArgs, zaiPodEnv, type ZaiPodSpec } from './pod-zai-child.js';
|
|
85
|
+
import { ZAI_CODING_DEFAULT_MODEL, ZAI_OPENROUTER_FALLBACK_MODEL, zaiSettingsFromConfig, type ZaiCodingPlanSettings } from '../../llm/zai-coding-plan.js';
|
|
82
86
|
import { selectEnhanceLlm } from '../../prompt-enhance/enhance.js';
|
|
83
87
|
import { extractPodFailureReason, podTerminalRow } from './pod-failure-reason.js';
|
|
84
88
|
import { OLD_DOOR_STAMP_ENV } from '../../self-dev/old-door.js';
|
|
@@ -183,6 +187,11 @@ export interface PodSpawnOptions {
|
|
|
183
187
|
/** POD-ANTHROPIC-PROVIDER — anthropic 자식의 API 키(시험 주입). 기본 = 제품 자격 해석 `getAnthropicApiKey()`(키 캐시 → env).
|
|
184
188
|
* ⛔ kubectl 주입(=시험)이면 실 캐시를 안 읽는다 — env ⊕ readKeyCache 만 본다. */
|
|
185
189
|
anthropicKey?: () => string | undefined;
|
|
190
|
+
/** BEDROCK-PROVIDER ② — 호스트 리전(시험 주입). 기본 = `resolveBedrockRegion`(env → ~/.aws/config 의 region 한 칸).
|
|
191
|
+
* ⛔ kubectl 주입(=시험)이면 실 ~/.aws 를 안 읽는다 — env 의 AWS_REGION/AWS_DEFAULT_REGION 만 본다. */
|
|
192
|
+
bedrockRegion?: () => string | undefined;
|
|
193
|
+
/** ZAI-POD-CHILD — glm(ZAI) Pod 의 동시 상한·넘침 정책(시험 주입). 기본 = `llm.zaiCodingPlan`(`zaiSettingsFromConfig`). */
|
|
194
|
+
zaiSettings?: () => ZaiCodingPlanSettings;
|
|
186
195
|
/** 호스트 키 캐시(`~/.cache/<소문자 이름>`)에서 키를 읽는다(시험 주입) — env 에 없을 때. */
|
|
187
196
|
readKeyCache?: (name: string) => string | undefined;
|
|
188
197
|
env?: NodeJS.ProcessEnv;
|
|
@@ -449,6 +458,31 @@ function githubRepositoryFromUrl(repoUrl: string): string | null {
|
|
|
449
458
|
return owner && name && ![owner, name].some((part) => part === '.' || part === '..') ? `${owner}/${name}` : null;
|
|
450
459
|
}
|
|
451
460
|
|
|
461
|
+
/** GROK-CHILD-GIT-GUARD — the Pod holds a GitHub write token and our repo cannot protect main (private free plan · API 403),
|
|
462
|
+
* so a child tool could `git push origin main`, force-push another run's branch or delete remote branches. A global
|
|
463
|
+
* pre-push hook lets a Pod push only its own run branches (self-impl/* · salvage/* · bench/<id>/<arm>) and refuses
|
|
464
|
+
* everything else, including deletes and the shared bench base. It stops accidental and reset-then-push overwrites;
|
|
465
|
+
* it is not a sandbox — credential isolation from child tools is the next slice. */
|
|
466
|
+
export const POD_GIT_HOOKS_DIR = '$HOME/.elanous-git-hooks';
|
|
467
|
+
export function podGitPushGuardScript(): string {
|
|
468
|
+
return [
|
|
469
|
+
`mkdir -p "${POD_GIT_HOOKS_DIR}" && cat > "${POD_GIT_HOOKS_DIR}/pre-push" <<'ELANOUS_PRE_PUSH'`,
|
|
470
|
+
'#!/bin/sh',
|
|
471
|
+
'while read -r lref lsha rref rsha; do',
|
|
472
|
+
' case "$lsha" in *[!0]*) ;; *) echo "[pod] git-guard: refused remote delete $rref" >&2; exit 1 ;; esac',
|
|
473
|
+
' case "$rref" in',
|
|
474
|
+
' refs/heads/self-impl/*|refs/heads/salvage/*) ;;',
|
|
475
|
+
' refs/heads/bench/*/base) echo "[pod] git-guard: refused push to shared bench base $rref" >&2; exit 1 ;;',
|
|
476
|
+
' refs/heads/bench/*/*) ;;',
|
|
477
|
+
' *) echo "[pod] git-guard: refused push to $rref (only this run\'s branches)" >&2; exit 1 ;;',
|
|
478
|
+
' esac',
|
|
479
|
+
'done',
|
|
480
|
+
'exit 0',
|
|
481
|
+
'ELANOUS_PRE_PUSH',
|
|
482
|
+
`chmod 755 "${POD_GIT_HOOKS_DIR}/pre-push" && git config --global core.hooksPath "${POD_GIT_HOOKS_DIR}"`,
|
|
483
|
+
].join('\n');
|
|
484
|
+
}
|
|
485
|
+
|
|
452
486
|
/** Job 스크립트의 salvage 단계 — 자식 rc 가 0 이 아닐 때만, 변경·미푸시 커밋이 있는 격리 워크트리를 `salvage/<job>/<worktree>` 로 push.
|
|
453
487
|
* main·self-impl/ 로는 절대 안 민다. 실패해도 제어 흐름을 바꾸지 않는다(호출자는 이 뒤에 `exit $rc`). */
|
|
454
488
|
/**
|
|
@@ -746,6 +780,10 @@ export function podNamedChildProvider(named: string | undefined, credential: { g
|
|
|
746
780
|
if (!childModel?.trim()) return { refuse: 'pod: openrouter 자식 모델 없음 — --child-llm-model 필요' };
|
|
747
781
|
return { provider: 'openrouter' };
|
|
748
782
|
}
|
|
783
|
+
// BEDROCK-PROVIDER ② — 자격은 클러스터의 이름 있는 Secret 에서 온다. 있나·키가 갖춰졌나는 Pod 발사 «전»(apply 0)에 판정한다(checkBedrockPodSecret).
|
|
784
|
+
if (named === 'bedrock') return { provider: 'bedrock' };
|
|
785
|
+
// ZAI-POD-CHILD — glm 도 같은 결: 자격은 클러스터의 이름 있는 ZAI Secret · 있나·동시 상한은 Pod 발사 «전»(apply 0)에 판정한다(pod-zai-child.ts).
|
|
786
|
+
if (named === 'glm') return { provider: 'glm' };
|
|
749
787
|
// anthropic — API 과금(구독 아님). «명시적으로 anthropic» 일 때만 탄다 · 키 없이 띄우지 않는다(fail-closed).
|
|
750
788
|
if (named !== 'anthropic') return { refuse: podChildProviderRefusal(named) ?? `pod: Pod 자식 provider 판정 불가 — 받음 ${String(named)}` };
|
|
751
789
|
if (!credential.anthropicKey) return { refuse: 'pod: Pod 에 anthropic 키 없음 — 호스트 키 캐시 또는 ANTHROPIC_API_KEY 필요' };
|
|
@@ -757,11 +795,25 @@ export function podFallbackCredentials(named: PodChildProvider | undefined, fall
|
|
|
757
795
|
return named === 'openai-codex' ? { grokSubscription: false, grokApiKey: false } : fallback;
|
|
758
796
|
}
|
|
759
797
|
|
|
798
|
+
/** ZAI-POD-CHILD — 이 프로세스가 «ZAI 로 정했지만 아직 apply 안 한» Job 이름(같은 프로세스의 동시 결정이 상한을 넘지 않게). apply·오류 뒤 바로 지운다. */
|
|
799
|
+
const zaiPendingApply = new Set<string>();
|
|
800
|
+
|
|
760
801
|
/** Child LLM flags for the Pod child — grok always names its model; codex only when the launch named it (10-05 PODPROVIDER). */
|
|
761
802
|
export function podChildLlmArgs(options: Pick<PodSpawnOptions, 'provider' | 'childModel' | 'childEffort' | 'childProviderExplicit'>): string[] {
|
|
762
|
-
|
|
803
|
+
// BEDROCK-POD-PROVIDER-DROP (10-11) — 명시 발사는 provider 를 «이름으로» 받는다. 이름이 비었거나 목록 밖이면 조용히 codex 로 풀지 않고 거부한다
|
|
804
|
+
// (🩸 bedrock 명시가 podBase 에서 떨어져 이 줄이 openai-codex 로 풀었다 — Pod 자식이 codex(team)로 돌았다). throw = Job apply 전.
|
|
805
|
+
const refusal = options.provider && !podChildProviderOf(options.provider)
|
|
806
|
+
? podChildProviderRefusal(options.provider)
|
|
807
|
+
: options.childProviderExplicit && !options.provider
|
|
808
|
+
? 'pod: --child-llm-provider 가 명시됐는데 Pod 옵션에 provider 가 없다 — openai-codex 로 조용히 풀지 않는다'
|
|
809
|
+
: undefined;
|
|
810
|
+
if (refusal) {
|
|
811
|
+
debug.log('self-implement.pod', 'child-provider-refused', { provider: options.provider ?? null, childProviderExplicit: options.childProviderExplicit === true, reason: refusal }, { level: 'warn' });
|
|
812
|
+
throw new Error(refusal);
|
|
813
|
+
}
|
|
814
|
+
const provider = options.provider === 'openai-codex' && !options.childProviderExplicit ? undefined : options.provider;
|
|
763
815
|
if (!provider) return [];
|
|
764
|
-
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : undefined);
|
|
816
|
+
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : provider === 'glm' ? ZAI_CODING_DEFAULT_MODEL : undefined);
|
|
765
817
|
if (provider === 'openrouter' && !model) throw new Error('pod: openrouter 자식 모델 없음 — --child-llm-model 필요');
|
|
766
818
|
// `self implement` registers no --child-llm-effort — passing it kills the Pod at argument parsing, so effort stays host-only.
|
|
767
819
|
return ['--child-llm-provider', provider, ...(model ? ['--child-llm-model', model] : [])];
|
|
@@ -788,7 +840,32 @@ export function resolvePodOpenRouterReviewer(option: PodOpenRouterReviewer | und
|
|
|
788
840
|
return { choice: 'glm', source: 'default', ...(raw ? { rejected: raw } : {}) };
|
|
789
841
|
}
|
|
790
842
|
|
|
791
|
-
|
|
843
|
+
/** CHILD-LLM-PIN-POD (0.2.24) — the child LLM pair from Pod args, for the delegated `harness say`/`harness ask` lines.
|
|
844
|
+
* 🩸 Those lines are built from `quotedAsk` (only --base/merge flags), so `--child-llm-*` never reached the Pod child and an
|
|
845
|
+
* openrouter Pod fell back to the provider default model (10-11: runs labelled Luna/Sonnet all ran GLM-5.3).
|
|
846
|
+
* ⛔ Effort is not carried — `podChildLlmArgs` never puts it in args (host-only by design). */
|
|
847
|
+
export function podDelegatedChildLlm(args: readonly string[]): { provider: string; model?: string } | undefined {
|
|
848
|
+
const providerIndex = args.indexOf('--child-llm-provider');
|
|
849
|
+
const provider = providerIndex >= 0 ? args[providerIndex + 1] : undefined;
|
|
850
|
+
if (!provider || provider.startsWith('--')) return undefined;
|
|
851
|
+
const modelIndex = args.indexOf('--child-llm-model');
|
|
852
|
+
const model = modelIndex >= 0 ? args[modelIndex + 1] : undefined;
|
|
853
|
+
return { provider, ...(model && !model.startsWith('--') ? { model } : {}) };
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/** ⭐ MF-d3b5a70f — Pod 스크립트의 SIGTERM trap 한 줄. 두 겹을 고친다:
|
|
857
|
+
* 🩸 (a) bash 는 foreground 명령(`elanous self implement`, 수시간) 실행 중 받은 trap 을 그 명령이 끝날 때까지
|
|
858
|
+
* 미룬다 ⇒ ~30s grace 안에 발화하지 못하고 SIGKILL 로 넘어갔다. 그래서 메인 명령은 podJobManifest 안에서
|
|
859
|
+
* `… & si_pid=$!` 로 띄우고 `wait "$si_pid"` 로 기다린다 — `wait` 는 trap 에 즉시 끊긴다(grace 내 발화).
|
|
860
|
+
* 🩸 (b) trap 이 TERM 기본 종료를 삼키면 컨테이너 rc 가 143→137(SIGKILL) 로 바뀐다 ⇒ trap 은 PARTIAL 롤업을
|
|
861
|
+
* 짧게(≤8s) 백그라운드로 쏘고 `exit 143` 으로 원래 rc 를 보존한다 — podUsageMissingReason 의 `sigterm-143`
|
|
862
|
+
* 분기가 실물 경로로 도달한다(시험은 합성 exitCode:143 이 아니라 실물 신호로 잰다).
|
|
863
|
+
* ⛔ 롤업 실패는 rc 를 바꾸지 않는다(각 단계 `|| true`). trap 몸통은 짧게 유지한다(골 ①). */
|
|
864
|
+
export function podSigtermTrapScript(): string {
|
|
865
|
+
return `trap '{ [ -n "$si_pid" ] && kill "$si_pid" 2>/dev/null || true; { [ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h --partial; } & si_rollup_pid=$!; for si_i in 1 2 3 4 5 6 7 8; do kill -0 "$si_rollup_pid" 2>/dev/null || break; sleep 1; done; kill "$si_rollup_pid" 2>/dev/null || true; wait "$si_rollup_pid" 2>/dev/null || true; exit 143; }' TERM`;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export function podJobManifest(o: { name: string; namespace: string; image: string; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; /** AUTHOR-POD2 — run `harness say` on /creds/feature inside the Pod (authoring happens off the host). */ authorSentence?: boolean; authorGrade?: 'full' | 'lite'; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean; /** POD-ROLE-LLM — OpenRouter 자식 Pod 에 리뷰어용 codex 자격을 깐다. */ reviewerCodexAuth?: boolean; /** BEDROCK-PROVIDER ② — bedrock 자식만: 이름 있는 AWS Secret 의 키(secretKeyRef) ⊕ 리전 ⊕ 이그레스 라벨. */ bedrock?: BedrockPodSpec; /** ZAI-POD-CHILD — glm(ZAI) 자식만: 이름 있는 ZAI Secret 의 키(secretKeyRef) ⊕ 이그레스·동시 상한 라벨(Pod ⊕ Job). */ zai?: ZaiPodSpec }): Record<string, unknown> {
|
|
792
869
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
793
870
|
const childProviderIndex = o.args.indexOf('--child-llm-provider');
|
|
794
871
|
const childModelIndex = o.args.indexOf('--child-llm-model');
|
|
@@ -801,8 +878,13 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
801
878
|
const askBaseIndex = delegated ? o.args.indexOf('--base') : -1;
|
|
802
879
|
const quotedAsk = (askBaseIndex >= 0 && o.args[askBaseIndex + 1]
|
|
803
880
|
? ` '--base' '${o.args[askBaseIndex + 1]!.replace(/'/g, `'\\''`)}'`
|
|
804
|
-
: '') + (delegated && o.args.includes('--merge-by-host') ? '
|
|
881
|
+
: '') + (delegated && o.args.includes('--merge-by-host') ? " '--merge-by-host'" : delegated && o.args.includes('--open-pr') && !o.args.includes('--auto-merge') ? ' --no-auto-merge' : '')
|
|
805
882
|
+ (delegated && o.args.includes('--no-supervise') ? ' --no-supervise' : '');
|
|
883
|
+
const delegatedChildLlm = delegated ? podDelegatedChildLlm(o.args) : undefined;
|
|
884
|
+
const shellQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
885
|
+
const quotedChildLlm = delegatedChildLlm
|
|
886
|
+
? ` '--child-llm-provider' ${shellQuote(delegatedChildLlm.provider)}${delegatedChildLlm.model ? ` '--child-llm-model' ${shellQuote(delegatedChildLlm.model)}` : ''}`
|
|
887
|
+
: '';
|
|
806
888
|
const script = [
|
|
807
889
|
'set -u',
|
|
808
890
|
openrouterModel
|
|
@@ -811,6 +893,10 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
811
893
|
: 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=openrouter'
|
|
812
894
|
: anthropicChild
|
|
813
895
|
? 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=anthropic'
|
|
896
|
+
: o.bedrock
|
|
897
|
+
? 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=bedrock'
|
|
898
|
+
: o.zai
|
|
899
|
+
? 'mkdir -p ~/.elanous && export ELANOUS_LLM_PROVIDER=glm'
|
|
814
900
|
: o.grokCredential === 'subscription'
|
|
815
901
|
? 'mkdir -p ~/.grok && install -m 600 /creds/grok-auth.json ~/.grok/auth.json && export ELANOUS_LLM_PROVIDER=grok'
|
|
816
902
|
: o.grokCredential === 'api_key'
|
|
@@ -820,6 +906,7 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
820
906
|
// 🔑 스킬 키(.env) — 이미지엔 없다. 이 런의 Secret 에서 각 스킬 폴더로 0600 복사(값은 로그에 안 나온다).
|
|
821
907
|
...(o.skillEnvs?.length ? [`for n in ${o.skillEnvs.join(' ')}; do [ -d ~/.claude/skills/$n ] && install -m 600 /creds/skillenv-$n ~/.claude/skills/$n/.env; done; echo "[pod] skill env: ${o.skillEnvs.join(',')}"`] : []),
|
|
822
908
|
'git config --global user.name "elanous pod child" && git config --global user.email "noreply@anthropic.com" && gh auth setup-git',
|
|
909
|
+
podGitPushGuardScript(),
|
|
823
910
|
'curl -s -m 3 -o /dev/null http://host.orb.internal:31415/health && { echo "[pod] ISOLATION FAIL"; exit 3; }',
|
|
824
911
|
podSourceScript(o.source ?? { kind: 'default' }, o.repoUrl),
|
|
825
912
|
...(goalPath ? [
|
|
@@ -828,14 +915,21 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
828
915
|
// Only fixed command shapes leave the Pod; arbitrary argv (including shell -c text) is never emitted.
|
|
829
916
|
`(while :; do { mem=$(if [ -r /sys/fs/cgroup/memory.current ]; then cat /sys/fs/cgroup/memory.current; elif [ -r /sys/fs/cgroup/memory/memory.usage_in_bytes ]; then cat /sys/fs/cgroup/memory/memory.usage_in_bytes; else printf -- -; fi); top=$(ps -eo rss=,comm=,args= --sort=-rss | head -5 | awk 'function encode(s) { gsub(/%/, "%25", s); gsub(/:/, "%3A", s); gsub(/ /, "%20", s); gsub(/\\t/, "%09", s); return s } { rss=$1; name=$2; sub(/^[[:space:]]*[0-9]+[[:space:]]+[^[:space:]]+[[:space:]]*/, ""); n=split($0, a, /[[:space:]]+/); cmd="<redacted>"; if (name=="sleep" && a[2]=="30") cmd="sleep 30" (n>2 ? " <redacted>" : ""); else if (name=="bun") { cmd="bun <redacted>"; if (a[2]=="test") cmd="bun test <redacted>"; else if (a[2]=="run") cmd="bun run <redacted>"; else if ((a[2]=="x" || a[2]=="exec") && a[3]=="tsc") cmd="bun x tsc <redacted>" } else if (name=="tsc") cmd="tsc <redacted>"; else if (name=="elanous") { cmd="elanous <redacted>"; if (a[2]=="self") cmd="elanous self <redacted>"; else if (a[2]=="harness") cmd="elanous harness <redacted>" } else if (name=="node") cmd="node <redacted>"; if (name=="sleep" && cmd=="<redacted>") cmd="sleep <redacted>"; else if (cmd=="<redacted>" && name!="bash" && name!="sh") name="other"; printf " %s:%s:%s", rss, encode(name), encode(substr(cmd,1,120)) }'); printf "ELANOUS_MEM %s %s%s\\n" "$(date +%s)" "$mem" "$top"; } || true; sleep 15 || break; done) & mem_sampler_pid=$!`,
|
|
830
917
|
...(o.appCredential ? [podGithubWatchdogScript(o.githubStaleSeconds ?? POD_GH_STALE_SECONDS)] : []),
|
|
918
|
+
// METER-POD-ROLLUP-GAP — 부분 롤업 표본기: 최종 줄까지 못 사는 Pod 도 N 분마다(기본 5 · ELANOUS_USAGE_PARTIAL_MINUTES) PARTIAL 줄을 하나씩 남긴다.
|
|
919
|
+
`(usage_partial_seq=0; while :; do { usage_partial_seq=$((usage_partial_seq+1)); [ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h --partial --seq "$usage_partial_seq" || true; } || true; usage_partial_sleep=$(( \${ELANOUS_USAGE_PARTIAL_MINUTES:-5} * 60 )); sleep "$usage_partial_sleep" || break; done) & usage_partial_pid=$!`,
|
|
920
|
+
// ⭐ MF-d3b5a70f — si_pid 초기화 + trap: 메인 명령을 아래에서 `& si_pid=$!` 로 띄우고 `wait "$si_pid"` 로
|
|
921
|
+
// 기다려야 trap 이 grace 안에 발화한다(bash 는 foreground 명령 실행 중 trap 을 미룬다). trap 은
|
|
922
|
+
// podSigtermTrapScript() 가 내놓는다(수신 측 reemitPodUsage 포맷과 계약을 같은 파일 안에서 공유).
|
|
923
|
+
'si_pid=""',
|
|
924
|
+
podSigtermTrapScript(),
|
|
831
925
|
// 마지막 줄 JSON 이 «맨 끝»이어야 한다(parseSelfImplementJson) — rollup 은 그 앞에.
|
|
832
926
|
// ⛔ `--author-grade` 는 Pod 안 `elanous`(이미지에 깔린 판)가 모를 수 있어 넘기지 않는다 — 10-06 «unknown option» 즉사(#24445 되돌림).
|
|
833
927
|
// AUTHOR-LITE2-POD — 등급은 환경변수 `ELANOUS_AUTHOR_GRADE` 로 싣는다(옛 Pod elanous 는 모르는 환경변수를 무시하고 full 로 돈다).
|
|
834
928
|
o.authorSentence
|
|
835
|
-
? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); ${o.authorGrade === 'lite' || o.authorGrade === 'full' ? `ELANOUS_AUTHOR_GRADE=${o.authorGrade} ` : ''}elanous harness say --substrate local --json${quotedAsk} -- "$(cat /creds/feature)" > /tmp/si.out 2>&1; rc=$?; echo "ELANOUS_AUTHOR_ON_POD finished host=$(hostname) rc=$rc seconds=$(( $(date +%s) - author_t0 ))"; ${POD_RESULT_FLATTEN}`
|
|
929
|
+
? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); ${o.authorGrade === 'lite' || o.authorGrade === 'full' ? `ELANOUS_AUTHOR_GRADE=${o.authorGrade} ` : ''}elanous harness say --substrate local --json${quotedAsk}${quotedChildLlm} -- "$(cat /creds/feature)" > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; echo "ELANOUS_AUTHOR_ON_POD finished host=$(hostname) rc=$rc seconds=$(( $(date +%s) - author_t0 ))"; ${POD_RESULT_FLATTEN}`
|
|
836
930
|
: goalPath
|
|
837
|
-
? `elanous harness ask ${goalPath} --json${quotedAsk} > /tmp/si.out 2>&1; rc=$?; ${POD_RESULT_FLATTEN}`
|
|
838
|
-
: `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1; rc=$?`,
|
|
931
|
+
? `elanous harness ask ${goalPath} --json${quotedAsk}${quotedChildLlm} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${POD_RESULT_FLATTEN}`
|
|
932
|
+
: `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?`,
|
|
839
933
|
'cat /tmp/si.out',
|
|
840
934
|
'[ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h || echo "ELANOUS_USAGE_ROLLUP {\"measured\":false,\"reason\":\"no rollup script\"}"',
|
|
841
935
|
// A long run's log export can pass the 5MB artifact limit; skipping it whole left no window into why a
|
|
@@ -893,7 +987,9 @@ done
|
|
|
893
987
|
if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
894
988
|
'kill "$mem_sampler_pid" 2>/dev/null || true',
|
|
895
989
|
...(o.appCredential ? ['kill "$gh_watch_pid" 2>/dev/null || true'] : []),
|
|
990
|
+
'[ -n "${usage_partial_pid:-}" ] && kill "$usage_partial_pid" 2>/dev/null || true',
|
|
896
991
|
'wait "$mem_sampler_pid" 2>/dev/null || true',
|
|
992
|
+
'[ -n "${usage_partial_pid:-}" ] && wait "$usage_partial_pid" 2>/dev/null || true',
|
|
897
993
|
'tail -n 1 /tmp/si.out',
|
|
898
994
|
podSalvageScript(),
|
|
899
995
|
'exit $rc',
|
|
@@ -901,7 +997,7 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
901
997
|
return {
|
|
902
998
|
apiVersion: 'batch/v1',
|
|
903
999
|
kind: 'Job',
|
|
904
|
-
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, ...podRunLabels(o) },
|
|
1000
|
+
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, ...podRunLabels(o), ...(o.zai ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}) },
|
|
905
1001
|
...((o.execution || o.hostLeaseAdmitted) ? { annotations: { ...(o.execution ? { [POD_EXECUTION_KEY_ANNOTATION]: o.execution.key, [POD_ATTEMPT_ANNOTATION]: String(o.execution.attempt) } : {}), ...(o.hostLeaseAdmitted ? { [POD_HOST_LEASE_ANNOTATION]: 'true' } : {}) } } : {}) },
|
|
906
1002
|
spec: {
|
|
907
1003
|
backoffLimit: 0,
|
|
@@ -909,7 +1005,7 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
909
1005
|
activeDeadlineSeconds: o.deadlineSeconds,
|
|
910
1006
|
template: {
|
|
911
1007
|
// ⭐ Pod 라벨 — local 팔만 `elanous.egress/local-llm` 을 단다. docker/h1/policy-local-llm.yaml 이 그 라벨에만 호스트 LLM 포트 «하나»를 연다.
|
|
912
|
-
metadata: { labels: { 'elanous.job': o.name, ...podRunLabels(o), ...(o.armEnv?.ELANOUS_LLM_PROVIDER === 'local' ? { 'elanous.egress/local-llm': 'true' } : {}) } },
|
|
1008
|
+
metadata: { labels: { 'elanous.job': o.name, ...podRunLabels(o), ...(o.armEnv?.ELANOUS_LLM_PROVIDER === 'local' ? { 'elanous.egress/local-llm': 'true' } : {}), ...(o.bedrock ? { [BEDROCK_POD_EGRESS_LABEL]: 'true' } : {}), ...(o.zai ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}) } },
|
|
913
1009
|
spec: {
|
|
914
1010
|
restartPolicy: 'Never',
|
|
915
1011
|
securityContext: { runAsUser: 1000, fsGroup: 1000 },
|
|
@@ -936,6 +1032,8 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
936
1032
|
{ name: ACTUAL_SUBSTRATE_ENV, value: 'pod' }, { name: RUN_CONTRACT_ENV, value: carryRunContract({ substrate: 'pod' }) },
|
|
937
1033
|
...Object.entries(o.armEnv ?? {}).map(([name, value]) => ({ name, value })),
|
|
938
1034
|
...o.passEnv.map((key) => ({ name: key, valueFrom: { secretKeyRef: { name: `${o.name}-creds`, key: `env-${key}` } } })),
|
|
1035
|
+
...(o.bedrock ? bedrockPodEnv(o.bedrock) : []),
|
|
1036
|
+
...(o.zai ? zaiPodEnv(o.zai) : []),
|
|
939
1037
|
],
|
|
940
1038
|
volumeMounts: [{ name: 'creds', mountPath: '/creds', readOnly: true }, ...(o.hostMirror ? [{ name: 'host-mirror', mountPath: '/host-mirror', readOnly: true }] : [])],
|
|
941
1039
|
}],
|
|
@@ -1206,12 +1304,46 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1206
1304
|
let ghLoginRetried = false;
|
|
1207
1305
|
for (;;) {
|
|
1208
1306
|
try {
|
|
1307
|
+
// ZAI-POD-CHILD — glm(ZAI) 자식은 apply «전»에 동시 상한을 본다: 풀 멤버들의 끝나지 않은 ZAI Job ⊕ 이 프로세스가 정했지만 아직 apply 안 한 수.
|
|
1308
|
+
// 상한 안 → ZAI(이름 있는 Secret · 없으면 거부) · 넘침 → 정책대로(openrouter·balance = 기존 OpenRouter GLM · stop = 거부 · apply 0).
|
|
1309
|
+
let zaiRoute: 'zai' | 'openrouter' | undefined;
|
|
1310
|
+
let zaiSpec: ZaiPodSpec | undefined;
|
|
1311
|
+
zaiPendingApply.delete(name);
|
|
1312
|
+
if (options.provider === 'glm') {
|
|
1313
|
+
const settings = (options.zaiSettings ?? (() => zaiSettingsFromConfig(getUserConfig().llm)))();
|
|
1314
|
+
const poolMembers = (options.pool as { members?: readonly PodPoolMember[] } | undefined)?.members;
|
|
1315
|
+
const contexts = Array.isArray(poolMembers) && poolMembers.length > 0 ? poolMembers.map((m) => m.context) : [context];
|
|
1316
|
+
let active: number | null = 0;
|
|
1317
|
+
for (const c of contexts) {
|
|
1318
|
+
const n = c === context ? countActiveZaiJobs(kubectl, namespace) : countActiveZaiJobs((a, i) => baseKubectl(['--context', c, ...a], i), namespace);
|
|
1319
|
+
if (n === null) { active = null; break; }
|
|
1320
|
+
active += n;
|
|
1321
|
+
}
|
|
1322
|
+
const pending = zaiPendingApply.size;
|
|
1323
|
+
const slot = decideZaiPodSlot({ active, pending, maxConcurrent: settings.maxConcurrent, overage: settings.overage });
|
|
1324
|
+
debug.log('self-implement.pod', 'zai-pod-slot', { job: name, route: slot.route, active: slot.active, pending, maxConcurrent: slot.maxConcurrent, overage: settings.overage, contexts: contexts.length, ...(slot.route !== 'zai' ? { reason: slot.route === 'refuse' ? 'refused' : slot.reason } : {}) }, { level: slot.route === 'zai' ? 'info' : 'warn' });
|
|
1325
|
+
if (slot.route === 'refuse') throw new Error(slot.reason);
|
|
1326
|
+
zaiRoute = slot.route;
|
|
1327
|
+
if (zaiRoute === 'zai') {
|
|
1328
|
+
const checked = checkZaiPodSecret(kubectl, namespace);
|
|
1329
|
+
if (!checked.ok) {
|
|
1330
|
+
debug.log('self-implement.pod', 'zai-secret-refused', { job: name, namespace, reason: checked.reason }, { level: 'warn' });
|
|
1331
|
+
throw new Error(checked.reason);
|
|
1332
|
+
}
|
|
1333
|
+
zaiSpec = checked.spec;
|
|
1334
|
+
zaiPendingApply.add(name);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
/** 넘친 glm Pod 는 자식 인자도 OpenRouter GLM 으로 — Job 스크립트·리뷰어 고정·키 전달이 openrouter 갈래를 그대로 탄다. */
|
|
1338
|
+
const jobArgs = zaiRoute === 'openrouter' ? zaiOverflowChildArgs(args, ZAI_OPENROUTER_FALLBACK_MODEL) : args;
|
|
1209
1339
|
const grok = options.provider === 'grok';
|
|
1210
|
-
const openrouter = options.provider === 'openrouter';
|
|
1340
|
+
const openrouter = options.provider === 'openrouter' || zaiRoute === 'openrouter';
|
|
1211
1341
|
const anthropic = options.provider === 'anthropic';
|
|
1212
|
-
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1342
|
+
const bedrockChild = options.provider === 'bedrock';
|
|
1343
|
+
const zaiChild = zaiRoute === 'zai';
|
|
1344
|
+
/** API 키 자식 — codex 계정·자격 파일을 안 쓴다(키는 작업별 creds Secret 의 `env-<KEY>` · bedrock·glm 은 이름 있는 Secret 으로만). */
|
|
1345
|
+
const apiKeyChild = openrouter || anthropic || bedrockChild || zaiChild;
|
|
1346
|
+
const plannedAccount = retryAccount ?? (grok ? 'grok' : openrouter ? 'openrouter' : anthropic ? 'anthropic' : bedrockChild ? 'bedrock' : zaiChild ? 'glm' : options.accountBroker?.() ?? options.account ?? 'team');
|
|
1215
1347
|
debug.log('self-implement.pod', 'account', { spaceId: input.spaceId, account: plannedAccount, brokered: Boolean(options.accountBroker) });
|
|
1216
1348
|
const plannedCodexAccounts = grok || apiKeyChild || !options.accountBroker || !options.rotationAccounts ? undefined : [plannedAccount, ...options.rotationAccounts.filter((candidate) => candidate !== plannedAccount && candidate !== failedAccount)];
|
|
1217
1349
|
if (plannedCodexAccounts && !options.rotationAccounts!.includes(plannedAccount)) throw new Error(`pod: 배분 계정 ${plannedAccount} 이 회전 계획에 없다`);
|
|
@@ -1262,6 +1394,19 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1262
1394
|
: (await import('../../config.js')).getAnthropicApiKey))()?.trim() || undefined
|
|
1263
1395
|
: undefined;
|
|
1264
1396
|
if (anthropic && !anthropicKey) throw new Error('pod: Pod 에 anthropic 키 없음 — 호스트 키 캐시 또는 ANTHROPIC_API_KEY 필요');
|
|
1397
|
+
// BEDROCK-PROVIDER ② — 이름 있는 AWS Secret 의 «키 이름»만 본다(값은 kubectl 밖으로 안 나온다). 없거나 모자라면 apply 0 으로 거부(fail-closed).
|
|
1398
|
+
let bedrockSpec: BedrockPodSpec | undefined;
|
|
1399
|
+
if (bedrockChild) {
|
|
1400
|
+
const hostRegion = (options.bedrockRegion ?? (options.kubectl
|
|
1401
|
+
? () => validBedrockRegion(env.AWS_REGION?.trim() || env.AWS_DEFAULT_REGION?.trim())
|
|
1402
|
+
: () => resolveBedrockRegion({ env })))();
|
|
1403
|
+
const checked = checkBedrockPodSecret(kubectl, namespace, hostRegion);
|
|
1404
|
+
if (!checked.ok) {
|
|
1405
|
+
debug.log('self-implement.pod', 'bedrock-secret-refused', { job: name, namespace, reason: checked.reason }, { level: 'warn' });
|
|
1406
|
+
throw new Error(checked.reason);
|
|
1407
|
+
}
|
|
1408
|
+
bedrockSpec = checked.spec;
|
|
1409
|
+
}
|
|
1265
1410
|
const creds = grok
|
|
1266
1411
|
? (options.grokCredentials ?? (() => hostGrokCredentials({ env, apiKeyOptIn: options.grokApiKeyOptIn })))()
|
|
1267
1412
|
: apiKeyChild ? { ghToken: (options.openrouterGhToken ?? defaultGhToken)() }
|
|
@@ -1435,7 +1580,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1435
1580
|
apiVersion: 'v1', kind: 'Secret', type: 'Opaque',
|
|
1436
1581
|
metadata: { name: `${name}-creds`, namespace, labels: { 'elanous.job': name } },
|
|
1437
1582
|
stringData: {
|
|
1438
|
-
...(anthropic ? {}
|
|
1583
|
+
...(anthropic || bedrockChild || zaiChild ? {}
|
|
1439
1584
|
: openrouter ? (reviewerCredential ? { 'elanous-auth.json': reviewerCredential.elanousAuth, 'codex-auth.json': reviewerCredential.codexAuth } : {})
|
|
1440
1585
|
: grok
|
|
1441
1586
|
? ('grokAuth' in creds && creds.grokAuth
|
|
@@ -1587,10 +1732,15 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1587
1732
|
debug.log('self-implement.pod', 'memory-request', { spaceId: input.spaceId, job: name,
|
|
1588
1733
|
tier: oomRetried ? retryTier : memoryTier, reason: oomRetried ? 'OOMKilled' : memoryReason,
|
|
1589
1734
|
memoryLimit, memoryRequest });
|
|
1590
|
-
const job = podJobManifest({ name, namespace, image: jobImage, ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), args, passEnv: jobPassEnv, deadlineSeconds: options.deadlineSeconds ?? POD_JOB_DEADLINE_SECONDS, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: options.imageCommit !== undefined ? options.imageCommit : options.kubectl ? null : podImageFreshness({ image }).imageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission, ...(reviewerCredential ? { reviewerCodexAuth: true } : {}) }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
|
|
1735
|
+
const job = podJobManifest({ name, namespace, image: jobImage, ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), args: jobArgs, passEnv: jobPassEnv, deadlineSeconds: options.deadlineSeconds ?? POD_JOB_DEADLINE_SECONDS, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: options.imageCommit !== undefined ? options.imageCommit : options.kubectl ? null : podImageFreshness({ image }).imageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission, ...(reviewerCredential ? { reviewerCodexAuth: true } : {}), ...(bedrockSpec ? { bedrock: bedrockSpec } : {}), ...(zaiSpec ? { zai: zaiSpec } : {}) }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
|
|
1736
|
+
const carriedBranch = goalDoc ? 'goal-doc' as const : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? 'author' as const : undefined;
|
|
1737
|
+
const carriedChildLlm = carriedBranch ? podDelegatedChildLlm(jobArgs) : undefined;
|
|
1591
1738
|
const a = kubectl(['apply', '-f', '-'], JSON.stringify(job));
|
|
1739
|
+
zaiPendingApply.delete(name); // apply 뒤엔 Job 라벨이 센다(성공) · 실패면 셀 것이 없다
|
|
1592
1740
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
1593
1741
|
releaseAdmission?.applied?.(name, context, namespace);
|
|
1742
|
+
// Logged only once the Job is applied — a failed apply carried nothing.
|
|
1743
|
+
if (carriedBranch && carriedChildLlm) debug.log('self-implement.pod', 'child-llm-carried', { spaceId: input.spaceId, job: name, branch: carriedBranch, provider: carriedChildLlm.provider, model: carriedChildLlm.model ?? null });
|
|
1594
1744
|
if (options.source?.kind === 'bundle') {
|
|
1595
1745
|
const podName = await waitForRunningPod(kubectl, namespace, name, sleep);
|
|
1596
1746
|
if (!podName) {
|
|
@@ -1634,15 +1784,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1634
1784
|
}
|
|
1635
1785
|
writePodFragment({ spaceId: input.spaceId, context, namespace, job: name, inboxDir: POD_CONTROL_INBOX_DIR, ...(liveChildRunId === normalizeRunId(liveChildRunId) ? { runId: liveChildRunId } : {}), ...(parentRunId && normalizeRunId(parentRunId) === parentRunId ? { parentRunId } : {}) }, env);
|
|
1636
1786
|
recorded = true;
|
|
1637
|
-
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouter ? { openrouterKeyPresent: passEnv.includes('OPENROUTER_API_KEY') } : {}), ...(anthropic ? { anthropicKeyPresent: passEnv.includes('ANTHROPIC_API_KEY') } : {}), extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
|
|
1787
|
+
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouter ? { openrouterKeyPresent: passEnv.includes('OPENROUTER_API_KEY') } : {}), ...(anthropic ? { anthropicKeyPresent: passEnv.includes('ANTHROPIC_API_KEY') } : {}), ...(bedrockSpec ? { bedrockSecret: bedrockSpec.secretName, bedrockSecretKeys: bedrockSpec.keys, bedrockRegionFrom: bedrockSpec.region.from } : {}), ...(zaiRoute ? { zaiRoute, ...(zaiSpec ? { zaiSecret: zaiSpec.secretName } : {}) } : {}), extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
|
|
1638
1788
|
let state: 'complete' | 'failed' | 'aborted' = 'failed';
|
|
1639
1789
|
let failedReason = '';
|
|
1640
1790
|
let containerReason: string | null = null;
|
|
1641
1791
|
let containerExitCode: number | null = null;
|
|
1792
|
+
// METER-POD-ROLLUP-GAP — abort 때 Job 삭제 «전»에 거둔 로그 사본(삭제 뒤엔 PARTIAL 줄도 못 읽는다).
|
|
1793
|
+
let abortLogs = '';
|
|
1642
1794
|
// ⭐ 런 «도중» 원장 증분 회수 — 호스트 슈퍼바이저가 Pod 걸음을 실시간으로 본다(🅣 요청 · 힐 루프 runtime 성형의 전제).
|
|
1643
1795
|
const follower = createPodLedgerFollower({ runId: liveChildRunId, dir: ledgerDir, exec: (script) => kubectl(['-n', namespace, 'exec', `job/${name}`, '-c', 'child', '--', 'sh', '-c', script]) });
|
|
1644
1796
|
for (;;) {
|
|
1645
1797
|
if (input.signal?.aborted) {
|
|
1798
|
+
// METER-POD-ROLLUP-GAP — Job 삭제 «전»에 로그를 한 번 거둔다: PARTIAL 줄이 삭제와 함께 사라지면 사용량이 0 처럼 보인다.
|
|
1799
|
+
try {
|
|
1800
|
+
const captured = kubectl(['-n', namespace, 'logs', `job/${name}`, '-c', 'child', '--tail=400']);
|
|
1801
|
+
if (captured.status === 0 && captured.stdout) abortLogs = captured.stdout;
|
|
1802
|
+
} catch { /* 수거 실패가 abort 자체를 막아선 안 된다 */ }
|
|
1646
1803
|
kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=false']);
|
|
1647
1804
|
state = 'aborted';
|
|
1648
1805
|
break;
|
|
@@ -1705,6 +1862,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1705
1862
|
else if (!fetched.stdout) logTailReason = 'empty child logs';
|
|
1706
1863
|
else logs = fetched.stdout;
|
|
1707
1864
|
} catch (error) { logTailReason = error instanceof Error ? error.message : String(error); }
|
|
1865
|
+
// METER-POD-ROLLUP-GAP — 삭제된 abort Job 의 로그는 이후 못 읽는다: abort 때 거둔 사본으로 대신한다.
|
|
1866
|
+
if (!logs && abortLogs) logs = abortLogs;
|
|
1708
1867
|
let ledgerReason = '';
|
|
1709
1868
|
let ledgerCollected = false;
|
|
1710
1869
|
const collection = collectFullLogs('ledger-collect-incomplete', follower.owned ? new Set([liveChildRunId]) : undefined, (event, data) => {
|
|
@@ -1731,7 +1890,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1731
1890
|
cleanupSecret();
|
|
1732
1891
|
// ⭐ 호스트 단가로 다시 매긴다(BACKLOG C1b) — Pod 엔 레지스트리 스냅숏이 없다.
|
|
1733
1892
|
const { estimateLlmCost } = await import('../../budget/llm-cost.js');
|
|
1734
|
-
|
|
1893
|
+
// METER-POD-ROLLUP-GAP — 최종 줄 → 마지막 PARTIAL 폴백 → 결손 행(measured:false · 종료 사유): 사용량이 0 처럼 보이는 침묵을 남기지 않는다.
|
|
1894
|
+
reemitPodUsage(logs, name, undefined, (u) => estimateLlmCost(u) as { kind: string; usd?: number }, { reason: podUsageMissingReason({ failedReason, exitCode: containerExitCode, logsAvailable: Boolean(logs) }), runId: liveChildRunId });
|
|
1735
1895
|
const parsed = parseSelfImplementJson(logs);
|
|
1736
1896
|
const terminal = parseSelfImplementJson(logs.trimEnd().split('\n').at(-1) ?? '');
|
|
1737
1897
|
const nextAccount = codexAccounts?.find((candidate) => candidate !== account);
|
|
@@ -1928,6 +2088,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1928
2088
|
...(finishedDisposition ? { disposition: finishedDisposition } : {}),
|
|
1929
2089
|
};
|
|
1930
2090
|
} catch (err) {
|
|
2091
|
+
zaiPendingApply.delete(name);
|
|
1931
2092
|
cleanupSecret();
|
|
1932
2093
|
const message = err instanceof Error ? err.message : String(err);
|
|
1933
2094
|
debug.log('self-implement.pod', 'job-error', { job: name, message }, { level: 'error' });
|
|
@@ -2046,11 +2207,33 @@ function formatLastMemSample(sample: ReturnType<typeof parseMemSamples>[number]
|
|
|
2046
2207
|
return `마지막 샘플: cgroup ${sample.cgroupBytes === null ? '-' : gib(sample.cgroupBytes)} · 상위 ${sample.top.length ? sample.top.map((p) => `${p.name} ${gib(p.rssKb * 1024)}`).join(' · ') : '없음'}`;
|
|
2047
2208
|
}
|
|
2048
2209
|
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2210
|
+
/** METER-POD-ROLLUP-GAP — 결손 칸의 종료 사유 토큰: 최종도 부분도 없는 사용량을 0 이 아니 «측정 불가» 로 남긴다. */
|
|
2211
|
+
export type PodUsageMissingReason = 'stopped' | 'sigterm-143' | 'backoff-limit' | 'deadline' | 'oom' | 'logs-unavailable';
|
|
2212
|
+
|
|
2213
|
+
/** 종료 사유 분류 — 로그를 못 읽으면 그것부터, 그다음 SIGTERM 143 → Backoff → Deadline → OOM, 나머지는 stopped. */
|
|
2214
|
+
export function podUsageMissingReason(input: { logsAvailable: boolean; exitCode?: number | null; failedReason?: string }): PodUsageMissingReason {
|
|
2215
|
+
if (!input.logsAvailable) return 'logs-unavailable';
|
|
2216
|
+
if (input.exitCode === 143) return 'sigterm-143';
|
|
2217
|
+
if (input.failedReason === 'BackoffLimitExceeded') return 'backoff-limit';
|
|
2218
|
+
if (input.failedReason === 'DeadlineExceeded') return 'deadline';
|
|
2219
|
+
if (input.failedReason === 'OOMKilled' || input.exitCode === 137) return 'oom';
|
|
2220
|
+
return 'stopped';
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
export function reemitPodUsage(logs: string, job: string, log: (category: string, event: string, data: Record<string, unknown>) => void = (c, e, d) => debug.log(c, e, d), reprice?: PodRowReprice, missing?: { reason: PodUsageMissingReason; runId?: string | null }): number {
|
|
2224
|
+
const lines = logs.split('\n').reverse();
|
|
2225
|
+
const line = lines.find((l) => l.startsWith('ELANOUS_USAGE_ROLLUP '));
|
|
2226
|
+
// ⭐ METER-POD-ROLLUP-GAP — 최종 줄이 없으면 마지막 PARTIAL 줄로 폴백한다(부분 롤업 표본기가 남긴 줄).
|
|
2227
|
+
const partialLine = line ? null : lines.find((l) => l.startsWith('ELANOUS_USAGE_ROLLUP_PARTIAL '));
|
|
2228
|
+
if (!line && !partialLine) {
|
|
2229
|
+
log('self-implement.pod', 'usage-rollup-missing', { job });
|
|
2230
|
+
// ⭐ 결손 행 — «측정 불가» 를 한 줄로 남긴다: 0 처럼 보이지 않게.
|
|
2231
|
+
if (missing) log('llm.usage', 'llm-usage', { site: 'pod-rollup:missing', measured: false, reason: missing.reason, job, ...(missing.runId ? { runId: missing.runId } : {}), substrate: 'pod', podRollupKey: `${missing.runId ?? job}|missing` });
|
|
2232
|
+
return 0;
|
|
2233
|
+
}
|
|
2234
|
+
const prefix = line ? 'ELANOUS_USAGE_ROLLUP ' : 'ELANOUS_USAGE_ROLLUP_PARTIAL ';
|
|
2235
|
+
let parsed: { measured?: boolean; truncated?: boolean; runId?: string | null; armId?: string | null; podName?: string | null; nodeName?: string | null; hostId?: string | null; seq?: number; at?: string; rows?: Array<Record<string, unknown>> };
|
|
2236
|
+
try { parsed = JSON.parse((line ?? partialLine)!.slice(prefix.length)); } catch { log('self-implement.pod', 'usage-rollup-unparsable', { job }); return 0; }
|
|
2054
2237
|
const rows = parsed.rows ?? [];
|
|
2055
2238
|
for (const r of rows) {
|
|
2056
2239
|
const cost = podRowCost(r, reprice);
|
|
@@ -2060,12 +2243,19 @@ export function reemitPodUsage(logs: string, job: string, log: (category: string
|
|
|
2060
2243
|
// ⭐ 캐시 쓰기 — 옛 Pod 이미지 rollup 엔 칸이 없다 ⇒ 없으면 «잰 0» 으로 꾸미지 않고 뺀다.
|
|
2061
2244
|
...(r.cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens: r.cacheCreationInputTokens } : {}),
|
|
2062
2245
|
...(Number(r.cacheCreationUnmeasuredCalls ?? 0) > 0 ? { cacheCreationUnmeasuredCalls: r.cacheCreationUnmeasuredCalls } : {}),
|
|
2246
|
+
// OR-BYOK-COST — BYOK 출처 칸(비용 자체는 usdKnown/unknownCostCalls 에 이미 있다). 옛 Pod rollup·비-BYOK 행엔 없다 ⇒ 안 싣는다.
|
|
2247
|
+
...(Number(r.byokCalls ?? 0) > 0 ? { byokCalls: r.byokCalls, ...(typeof r.byokUpstreamUsd === 'number' ? { byokUpstreamUsd: r.byokUpstreamUsd } : {}), ...(Number(r.byokUpstreamUnknownCalls ?? 0) > 0 ? { byokUpstreamUnknownCalls: r.byokUpstreamUnknownCalls } : {}) } : {}),
|
|
2063
2248
|
cost,
|
|
2064
2249
|
substrate: 'pod', job, ...(parsed.runId ? { podRunId: parsed.runId } : {}), ...(parsed.armId ? { armId: parsed.armId } : {}),
|
|
2250
|
+
// ⭐ MF-3c0d253e — 재방출 행의 downstream dedup 키: 같은 Pod(job·runId)의 같은 버킷(site·provider·model)은
|
|
2251
|
+
// 부분(seq 달라도)·최종·abort⊕resume 재방출이 몇 번 만나도 집계(rollup)에서 한 번만 센다. 키 없는 기존 행은 종전대로.
|
|
2252
|
+
podRollupKey: `${job}|${parsed.runId ?? ''}|pod-rollup:${String(r.site)}|${String(r.provider)}|${String(r.model)}`,
|
|
2065
2253
|
...(parsed.podName ? { podName: parsed.podName } : {}), ...(parsed.nodeName ? { nodeName: parsed.nodeName } : {}), ...(parsed.hostId ? { podHostId: parsed.hostId } : {}),
|
|
2254
|
+
// ⭐ PARTIAL 폴백 재방출 꼬리표 — 최종 줄 행은 그대로(부분이 아님을 꾸미지 않는다).
|
|
2255
|
+
...(partialLine ? { partial: true, ...(parsed.seq !== undefined ? { partialSeq: parsed.seq } : {}), ...(parsed.at !== undefined ? { partialAt: parsed.at } : {}) } : {}),
|
|
2066
2256
|
});
|
|
2067
2257
|
}
|
|
2068
|
-
log('self-implement.pod', 'usage-rollup', { job, rows: rows.length, measured: parsed.measured ?? null, truncated: parsed.truncated ?? null });
|
|
2258
|
+
log('self-implement.pod', 'usage-rollup', { job, rows: rows.length, measured: parsed.measured ?? null, truncated: parsed.truncated ?? null, ...(partialLine ? { partial: true, ...(parsed.seq !== undefined ? { seq: parsed.seq } : {}), ...(parsed.at !== undefined ? { at: parsed.at } : {}) } : {}) });
|
|
2069
2259
|
return rows.length;
|
|
2070
2260
|
}
|
|
2071
2261
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* Cf. PLAN-parallel-self-dev-orchestrator-2026-07-21.
|
|
20
20
|
*/
|
|
21
21
|
import { lstatSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import type { ReviewRework } from '../../self-dev/run-types.js';
|
|
22
23
|
import { oldDoorInternalEnv } from '../../self-dev/old-door.js';
|
|
23
24
|
import { selfDevRunsDir } from '../../self-dev/run-store.js';
|
|
24
25
|
import { debug } from '../../debug/log.js';
|
|
@@ -89,6 +90,7 @@ export interface SelfImplementDisposition {
|
|
|
89
90
|
reviewVerdict?: 'pass' | 'warn' | 'fail';
|
|
90
91
|
/** 마지막 리뷰의 must-fix 수(`review.mustFix.length`). 같은 조건. */
|
|
91
92
|
reviewMustFixCount?: number;
|
|
93
|
+
rework?: ReviewRework;
|
|
92
94
|
mustFix?: string[];
|
|
93
95
|
}
|
|
94
96
|
|
|
@@ -463,6 +465,16 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
463
465
|
const o = JSON.parse(line) as Record<string, unknown>;
|
|
464
466
|
if (typeof o.stage !== 'string' && typeof o.ok !== 'boolean') continue;
|
|
465
467
|
const failureClassification = failureClassificationFromJson(o);
|
|
468
|
+
const rawRework = o.rework && typeof o.rework === 'object' && !Array.isArray(o.rework)
|
|
469
|
+
? o.rework as Record<string, unknown> : undefined;
|
|
470
|
+
const rework = rawRework
|
|
471
|
+
&& typeof rawRework.trend === 'string'
|
|
472
|
+
&& ['unmeasured', 'improving', 'stable', 'worsening', 'oscillating'].includes(rawRework.trend)
|
|
473
|
+
&& typeof rawRework.signature === 'string'
|
|
474
|
+
&& typeof rawRework.rounds === 'number' && Number.isSafeInteger(rawRework.rounds) && rawRework.rounds >= 1
|
|
475
|
+
// A measured trend needs two reviews, a direction change three — a contradictory pair is not an observation.
|
|
476
|
+
&& (rawRework.trend === 'unmeasured' ? rawRework.rounds === 1 : rawRework.rounds >= (rawRework.trend === 'oscillating' ? 3 : 2))
|
|
477
|
+
? { trend: rawRework.trend as ReviewRework['trend'], signature: rawRework.signature, rounds: rawRework.rounds } : undefined;
|
|
466
478
|
const mapped: SelfImplementDisposition = {
|
|
467
479
|
...(typeof o.stage === 'string' ? { stage: o.stage } : {}),
|
|
468
480
|
...(typeof o.ok === 'boolean' ? { ok: o.ok } : {}),
|
|
@@ -499,7 +511,13 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
499
511
|
? { providerErrors: o.providerErrors as SelfImplementDisposition['providerErrors'] } : {}),
|
|
500
512
|
...(typeof o.runId === 'string' && o.runId.trim() ? { runId: o.runId, childRunId: o.runId } : {}),
|
|
501
513
|
...reviewGateFields(o),
|
|
502
|
-
...(
|
|
514
|
+
...(rework ? { rework } : {}),
|
|
515
|
+
...(Array.isArray(o.mustFix) && o.mustFix.every((item) => typeof item === 'string') ? { mustFix: o.mustFix as string[] }
|
|
516
|
+
: o.review && typeof o.review === 'object' && !Array.isArray(o.review)
|
|
517
|
+
&& (o.review as { reviewed?: unknown }).reviewed === true
|
|
518
|
+
&& Array.isArray((o.review as { mustFix?: unknown }).mustFix)
|
|
519
|
+
&& ((o.review as { mustFix: unknown[] }).mustFix).every((item) => typeof item === 'string')
|
|
520
|
+
? { mustFix: (o.review as { mustFix: string[] }).mustFix } : {}),
|
|
503
521
|
};
|
|
504
522
|
reportUnmappedDispositionFields(o, mapped);
|
|
505
523
|
return mapped;
|
package/src/typecheck-ratchet.ts
CHANGED
|
@@ -12,6 +12,7 @@ export const TYPECHECK_GATE_CONFIG = 'tsconfig.gate.json';
|
|
|
12
12
|
* ⚠️ 호출자가 이미 `--max-old-space-size` 를 줬으면 «그것을» 존중한다(덮지 않는다).
|
|
13
13
|
*/
|
|
14
14
|
export const TSC_HEAP_MB = 20480;
|
|
15
|
+
export const TSC_OOM_RETRY_HEAP_MB = 32768;
|
|
15
16
|
export function tscEnv(base: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
16
17
|
const cur = base.NODE_OPTIONS ?? '';
|
|
17
18
|
if (/--max-old-space-size[= ]/.test(cur)) return base;
|