elanous 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -15,10 +15,10 @@ import { podSkillsDigest, readSkillEnvFiles, resolvePodSkills } from './pod-skil
|
|
|
15
15
|
import { podSourceScript, type PodSource } from './pod-source-receive.js';
|
|
16
16
|
import { GROUNDING_TOKEN_ENV, GROUNDING_URL_ENV, mintGroundingToken, revokeGroundingRun, type GroundingTokenScope } from '../../grounding/token.js';
|
|
17
17
|
import { POD_CREDENTIAL_GROK_PATH, POD_CREDENTIAL_GITHUB_PATH, POD_GITHUB_CREDENTIAL_TOKEN_ENV, POD_GITHUB_CREDENTIAL_URL_ENV, installationRepositories } from '../../nexus/api/pod-credential-api.js';
|
|
18
|
-
import {
|
|
18
|
+
import { coalescedInstallationCredential } from '../../auth/github-app-token.js';
|
|
19
19
|
import { POD_CREDENTIAL_TOKEN_ENV, POD_CREDENTIAL_URL_ENV } from '../../grok/credential.js';
|
|
20
20
|
import { collectPodLedgers, createPodLedgerFollower } from './pod-ledger-collect.js';
|
|
21
|
-
import { collectPodArtifacts } from './pod-artifact-return.js';
|
|
21
|
+
import { collectPodArtifacts, podNoResultDiagnostic } from './pod-artifact-return.js';
|
|
22
22
|
import { isValidAccountName, resolveCodexAccount } from '../../oauth/codex-account.js';
|
|
23
23
|
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
24
24
|
import { controlInboxEnv } from '../../harness/control-inbox.js';
|
|
@@ -38,7 +38,10 @@ export const POD_JOB_DEADLINE_SECONDS = 10_800;
|
|
|
38
38
|
/** 자식 컨테이너 «요청» — 🩸 2026-09-27: limits 만 두면 k8s 가 requests=limits(cpu 4)로 잡아, 32코어 노드에
|
|
39
39
|
* «자리 요청»이 88% 차서 잡이 Pending 인데 실사용은 14%였다(풀 25 자리 중 ~8 만 떴다). 상한(limits)은 그대로 두고
|
|
40
40
|
* 예약만 실사용에 맞춘다. */
|
|
41
|
-
|
|
41
|
+
// POD-DIET (10-06 03:47~05:18 · node-b goal Pods · 89 one-minute samples · 42 Pods): per-Pod observed max median 4.6Gi ·
|
|
42
|
+
// p75 6.2Gi · p90 11.7Gi — 23/42 went above the old 4Gi request, so the scheduler over-packed. 6Gi ≈ p75; limits stay.
|
|
43
|
+
// Node allocatable ≈ 343Gi ≫ 25 × 6Gi. One-minute samples can miss the true peak (lower bound).
|
|
44
|
+
export const POD_CHILD_REQUESTS = { cpu: '1', memory: '6Gi' } as const;
|
|
42
45
|
import type { PodPoolMember, PodPoolScheduler } from './pod-pool.js';
|
|
43
46
|
import { measurePoolLease, recommendConcurrency, POD_HOST_LEASE_ANNOTATION, LEASE_KUBECTL_MAX_BUFFER } from './pod-lease.js';
|
|
44
47
|
import { ACTUAL_SUBSTRATE_ENV, RUN_CONTRACT_ENV, carryRunContract, completionFloorFor } from '../../self-implement/graph-run-contract.js';
|
|
@@ -59,8 +62,11 @@ import { LLM_TIER_MAP_BY_PROVIDER, lookupLlmTierSpec, type LlmTierProvider } fro
|
|
|
59
62
|
import { parseSelfImplementJson, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
|
|
60
63
|
import { codexQuotaPolicyFromConfig } from '../../oauth/codex-account-store.js';
|
|
61
64
|
import { goalTypeOf, type GoalType } from '../../../scripts/measure-pod-memory-by-goal.js';
|
|
65
|
+
import { declaredGoalType, type GoalType as DeclaredGoalType } from '../../self-implement/goal-author.js';
|
|
62
66
|
import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
|
|
63
67
|
import { getUserConfig } from '../../user-config.js';
|
|
68
|
+
import { extractPodFailureReason } from './pod-failure-reason.js';
|
|
69
|
+
import { OLD_DOOR_STAMP_ENV } from '../../self-dev/old-door.js';
|
|
64
70
|
|
|
65
71
|
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
66
72
|
|
|
@@ -68,7 +74,9 @@ export type Kubectl = (args: readonly string[], input?: string) => { status: num
|
|
|
68
74
|
// still the credential source for gh and setup-git's helper. Install it privately
|
|
69
75
|
// on that failure; rename keeps concurrent readers from seeing a partial token.
|
|
70
76
|
const APP_GH_CONFIG_PATH = '$HOME/.config/gh';
|
|
71
|
-
|
|
77
|
+
// PODCRED2 (10-05) — GitHub App installation tokens now carry `.` and `-` (390 chars on node-b); the old
|
|
78
|
+
// `[A-Za-z0-9_]+` check rejected every real token, so any transient `gh auth login` failure became exit 7.
|
|
79
|
+
export const APP_GH_AUTH_SCRIPT = 'const fs=require("node:fs"),path=require("node:path"),crypto=require("node:crypto");const token=fs.readFileSync(0,"utf8").replace(/\\r?\\n$/,"");if(!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(token))process.exit(1);const dir=process.env.GH_CONFIG_DIR||path.join(process.env.HOME,".config/gh");fs.mkdirSync(dir,{recursive:true,mode:0o700});const dest=path.join(dir,"hosts.yml"),temp=path.join(dir,".hosts.yml."+crypto.randomUUID());try{fs.writeFileSync(temp,"github.com:\\n oauth_token: "+token+"\\n git_protocol: https\\n",{mode:0o600,flag:"wx"});fs.renameSync(temp,dest);fs.chmodSync(dest,0o600)}finally{try{fs.unlinkSync(temp)}catch{}}';
|
|
72
80
|
const APP_GH_AUTH_COMMAND = `bun -e '${APP_GH_AUTH_SCRIPT}'`;
|
|
73
81
|
const APP_GH_EXEC = `unset GH_TOKEN GITHUB_TOKEN; GH_CONFIG_DIR="${APP_GH_CONFIG_PATH}" exec "$@"`;
|
|
74
82
|
|
|
@@ -83,6 +91,11 @@ export interface PodSpawnOptions {
|
|
|
83
91
|
rotationAccounts?: readonly string[];
|
|
84
92
|
/** 미지정이면 종전 Codex Job. */
|
|
85
93
|
provider?: 'openai-codex' | 'grok';
|
|
94
|
+
/** Child model/effort chosen at launch (`--child-llm-model/--child-llm-effort`) — carried into the Pod child. */
|
|
95
|
+
childModel?: string;
|
|
96
|
+
childEffort?: string;
|
|
97
|
+
/** True when the child provider was named at launch — the child gets explicit flags even for codex. */
|
|
98
|
+
childProviderExplicit?: boolean;
|
|
86
99
|
/** 🔐 API 키 과금 허용은 명시 true 뿐. */
|
|
87
100
|
grokApiKeyOptIn?: boolean;
|
|
88
101
|
namespace?: string;
|
|
@@ -108,7 +121,7 @@ export interface PodSpawnOptions {
|
|
|
108
121
|
pollMs?: number;
|
|
109
122
|
kubectl?: Kubectl;
|
|
110
123
|
/** Host-side regate (injected for Pod tests). */
|
|
111
|
-
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string }) => Promise<HostRegateResult>;
|
|
124
|
+
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string; goalFile?: string }) => Promise<HostRegateResult>;
|
|
112
125
|
/** PR comment for host-regate failures that never reached runHostRegate (injectable for tests). */
|
|
113
126
|
ghComment?: (prNumber: number, body: string) => void;
|
|
114
127
|
/** 🔑 Pod 필수 스킬의 키(.env)를 이 런의 Secret 으로 넘긴다 — 명시 opt-in(유료 크레딧을 쓴다). */
|
|
@@ -123,7 +136,8 @@ export interface PodSpawnOptions {
|
|
|
123
136
|
mintGrounding?: (claims: { runId: string; job: string; ttlMs: number; scope?: GroundingTokenScope; repository?: string }) => Promise<{ token: string; exp: number }>;
|
|
124
137
|
revokeGrounding?: (runId: string) => void;
|
|
125
138
|
/** Test seam for the host's repository-scoped GitHub App installation token. */
|
|
126
|
-
|
|
139
|
+
/** `fresh` = do not reuse a coalesced token (the exit-7 retry must not get the token that just failed back). */
|
|
140
|
+
githubInstallation?: (repository: string, opts?: { fresh?: boolean }) => { token: string; expires_at?: string; expiresAt?: string | number } | null;
|
|
127
141
|
/** GitHub installation repository lookup; required to prove the scoped token matches owner/name. */
|
|
128
142
|
githubRepositories?: (token: string) => Promise<readonly string[] | null>;
|
|
129
143
|
now?: () => number;
|
|
@@ -184,6 +198,17 @@ export function hostCredentials(account: string, storePath: string = authStorePa
|
|
|
184
198
|
|
|
185
199
|
/** A Pod gets a refresh-less access copy, so the copy must outlive the Job: same rule as the Codex path (3 h). */
|
|
186
200
|
export const POD_GROK_MIN_VALIDITY_MS = 3 * 3600_000;
|
|
201
|
+
/** PODCRED1 (10-05): an App installation token lives ~60 min and the host renews it in the Pod at the ten-minute
|
|
202
|
+
* boundary; below this remaining life at launch the token is observed (`github-app-short-lived`). */
|
|
203
|
+
export const POD_APP_TOKEN_MIN_START_MS = 20 * 60_000;
|
|
204
|
+
/** Exit 7 (in-Pod GitHub login failed) is retried once after this spread backoff — bursts were simultaneous launches. */
|
|
205
|
+
export const POD_GH_LOGIN_RETRY_BACKOFF_MS = 60_000;
|
|
206
|
+
/** Base backoff plus a 0-59 s spread derived from the job name, so jobs that failed together retry apart. */
|
|
207
|
+
export function podGhLoginRetryBackoffMs(job: string): number {
|
|
208
|
+
let spread = 0;
|
|
209
|
+
for (const ch of job) spread = (spread * 31 + ch.charCodeAt(0)) % 60;
|
|
210
|
+
return POD_GH_LOGIN_RETRY_BACKOFF_MS + spread * 1000;
|
|
211
|
+
}
|
|
187
212
|
|
|
188
213
|
/** resolveGrokCredential 과 동일한 출처를 읽되 Pod 로는 refresh 없는 access 사본만 보낸다.
|
|
189
214
|
* POD4 (10-01): the copy used to ship whatever the host had — an AUTH1 shard copied a token that had expired
|
|
@@ -453,11 +478,11 @@ export function podRunLabels(o: { runId?: string; parentRunId?: string }): Recor
|
|
|
453
478
|
|
|
454
479
|
/** Pod 메모리 «등급» — 대표 09-27: PWA Pod 골이 16Gi 에서 OOM 으로 죽는다 ⇒ «high» 등급을 옵션으로 고른다.
|
|
455
480
|
* 등급 = `standard`(`ELANOUS_POD_MEMORY` 또는 16Gi) · `high`(`ELANOUS_POD_MEMORY_HIGH` 또는 32Gi · standard 보다 작아지지 않는다).
|
|
456
|
-
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면
|
|
481
|
+
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면 `Pod 메모리: lite|standard|high` ③ 조각의 `apps/pwa/` ④ 부모의 명시 메모리 줄·PWA 경로 ⑤ 실측 권고 ⑥ 권고 없는 research/document 는 lite ⑦ standard.
|
|
457
482
|
* node-b 노드 할당 가능 약 251Gi. */
|
|
458
483
|
export const POD_MEMORY_DEFAULT = '16Gi';
|
|
459
484
|
export const POD_MEMORY_HIGH_DEFAULT = '32Gi';
|
|
460
|
-
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi):
|
|
485
|
+
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi): research/document 기본값; OOM 시 standard 로 한 단계 승급한다. */
|
|
461
486
|
export const POD_MEMORY_LITE_DEFAULT = '2Gi';
|
|
462
487
|
|
|
463
488
|
/** Reserve from measured per-run peaks, never from the advisory limit tier. Sparse or missing measurements keep the old reservation. */
|
|
@@ -475,7 +500,7 @@ export function podMemoryRequestFor(kind: GoalType | null, advice?: PodMemoryAdv
|
|
|
475
500
|
}
|
|
476
501
|
export const POD_MEMORY_TIERS = ['lite', 'standard', 'high'] as const;
|
|
477
502
|
export type PodMemoryTier = (typeof POD_MEMORY_TIERS)[number];
|
|
478
|
-
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise';
|
|
503
|
+
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise' | 'goal-type-auto';
|
|
479
504
|
const PWA_PATH = /(?:^|[\s`'"(,·])apps\/pwa\//m;
|
|
480
505
|
const GOAL_LINE = /^[ \t]*(?:Pod 메모리|pod-memory)[ \t]*:[ \t]*(lite|standard|high)[ \t]*\r?$/im;
|
|
481
506
|
|
|
@@ -511,6 +536,10 @@ export function parsePodMemoryTier(v: string | undefined): PodMemoryTier | null
|
|
|
511
536
|
return t && (POD_MEMORY_TIERS as readonly string[]).includes(t) ? (t as PodMemoryTier) : null;
|
|
512
537
|
}
|
|
513
538
|
|
|
539
|
+
function parsePodGoalType(value: string | undefined): DeclaredGoalType | null {
|
|
540
|
+
return value === 'implement' || value === 'research' || value === 'document' || value === 'operate' ? value : null;
|
|
541
|
+
}
|
|
542
|
+
|
|
514
543
|
function memoryGi(v: string): number | null {
|
|
515
544
|
const m = /^(\d+(?:\.\d+)?)(Gi|Mi)$/.exec(v);
|
|
516
545
|
if (!m) return null;
|
|
@@ -521,9 +550,13 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
521
550
|
const option = parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER);
|
|
522
551
|
const line = parsePodMemoryTier(GOAL_LINE.exec(feature)?.[1]);
|
|
523
552
|
const parentLine = !option && !line ? parsePodMemoryTier(GOAL_LINE.exec(parentGoal ?? '')?.[1]) : null;
|
|
553
|
+
const declared = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE)
|
|
554
|
+
?? declaredGoalType(feature) ?? declaredGoalType(parentGoal ?? '');
|
|
555
|
+
// 선언 «document» 는 실측 분류의 «docs» 다 — 낱말 판정(goalTypeOf)이 «document» 를 못 읽어 권고가 안 이어지던 구멍
|
|
524
556
|
const goalType = advice && !option && !line && !parentLine
|
|
525
557
|
? (broadPodTestWarning(feature) ? 'test' : goalTypeOf(feature))
|
|
526
558
|
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
559
|
+
?? (declared === 'document' ? 'docs' : null)
|
|
527
560
|
: null;
|
|
528
561
|
const recommendation = advice?.byGoalType.find((entry) => entry.goalType === goalType && entry.evidence.runs > 0)?.recommended;
|
|
529
562
|
const [tier, source]: [PodMemoryTier, PodMemorySource] = option ? [option, 'option']
|
|
@@ -531,8 +564,11 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
531
564
|
// 조각 자신의 PWA 경로가 부모 줄·실측 권고보다 먼저다 — 부모 «standard» 가 PWA 조각을 16Gi 로 내리면 OOM(POD7 · #23569 순서 회귀)
|
|
532
565
|
: goalTouchesPwa(feature) ? ['high', 'pwa-auto']
|
|
533
566
|
: parentLine ? [parentLine, 'parent-goal-line']
|
|
534
|
-
: recommendation ? [recommendation, 'advise']
|
|
535
567
|
: parentGoal && goalTouchesPwa(parentGoal) ? ['high', 'parent-pwa-auto']
|
|
568
|
+
// 실측이 high(OOM 증거)를 권하면 선언 종류의 자동 lite 보다 먼저다 — 문서 골도 OOM 이 났으면 다시 2Gi 로 띄우지 않는다
|
|
569
|
+
: recommendation === 'high' ? ['high', 'advise']
|
|
570
|
+
: declared === 'research' || declared === 'document' ? ['lite', 'goal-type-auto']
|
|
571
|
+
: recommendation ? [recommendation, 'advise']
|
|
536
572
|
: ['standard', 'default'];
|
|
537
573
|
if (tier === 'lite') return { limit: env.ELANOUS_POD_MEMORY_LITE?.trim() || POD_MEMORY_LITE_DEFAULT, tier, source };
|
|
538
574
|
const base = env.ELANOUS_POD_MEMORY?.trim() || POD_MEMORY_DEFAULT;
|
|
@@ -542,14 +578,37 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
542
578
|
return { limit: a !== null && b !== null && a > b ? base : high, tier, source };
|
|
543
579
|
}
|
|
544
580
|
|
|
545
|
-
|
|
581
|
+
/** A named child provider decides the Pod provider (10-05 PODPROVIDER) — grok needs a usable credential, other names are refused. */
|
|
582
|
+
export function podNamedChildProvider(named: string | undefined, credential: { grokSubscription: boolean; grokApiKey: boolean }): { provider?: 'openai-codex' | 'grok'; refuse?: string } {
|
|
583
|
+
if (!named) return {};
|
|
584
|
+
if (named === 'openai-codex' || named === 'codex') return { provider: 'openai-codex' };
|
|
585
|
+
if (named === 'grok') return credential.grokSubscription || credential.grokApiKey ? { provider: 'grok' } : { refuse: 'pod: Pod 에 grok 자격 없음 — 구독 자격이 쓸 수 없거나(만료 임박·갱신 불가) API 키 과금 동의(harness.pod.grokApiKeyOptIn)가 없다 · codex 로 조용히 돌리지 않는다' };
|
|
586
|
+
return { refuse: `pod: Pod 자식 provider 는 openai-codex|grok 만 — 받음 ${named}` };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Fallback credentials for the codex plan — a launch that named openai-codex never falls back to grok (post-review must-fix). */
|
|
590
|
+
export function podFallbackCredentials(named: 'openai-codex' | 'grok' | undefined, fallback: { grokSubscription: boolean; grokApiKey: boolean }): { grokSubscription: boolean; grokApiKey: boolean } {
|
|
591
|
+
return named === 'openai-codex' ? { grokSubscription: false, grokApiKey: false } : fallback;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** Child LLM flags for the Pod child — grok always names its model; codex only when the launch named it (10-05 PODPROVIDER). */
|
|
595
|
+
export function podChildLlmArgs(options: Pick<PodSpawnOptions, 'provider' | 'childModel' | 'childEffort' | 'childProviderExplicit'>): string[] {
|
|
596
|
+
const provider = options.provider === 'grok' ? 'grok' : options.childProviderExplicit ? 'openai-codex' : undefined;
|
|
597
|
+
if (!provider) return [];
|
|
598
|
+
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : undefined);
|
|
599
|
+
// `self implement` registers no --child-llm-effort — passing it kills the Pod at argument parsing, so effort stays host-only.
|
|
600
|
+
return ['--child-llm-provider', provider, ...(model ? ['--child-llm-model', model] : [])];
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
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; 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 }): Record<string, unknown> {
|
|
546
604
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
547
605
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
548
|
-
const
|
|
606
|
+
const delegated = Boolean(o.goalDoc) || o.authorSentence === true;
|
|
607
|
+
const askBaseIndex = delegated ? o.args.indexOf('--base') : -1;
|
|
549
608
|
const quotedAsk = (askBaseIndex >= 0 && o.args[askBaseIndex + 1]
|
|
550
609
|
? ` '--base' '${o.args[askBaseIndex + 1]!.replace(/'/g, `'\\''`)}'`
|
|
551
|
-
: '') + (
|
|
552
|
-
+ (
|
|
610
|
+
: '') + (delegated && o.args.includes('--merge-by-host') ? ' --merge-by-host' : delegated && o.args.includes('--open-pr') && !o.args.includes('--auto-merge') ? ' --no-auto-merge' : '')
|
|
611
|
+
+ (delegated && o.args.includes('--no-supervise') ? ' --no-supervise' : '');
|
|
553
612
|
const script = [
|
|
554
613
|
'set -u',
|
|
555
614
|
o.grokCredential === 'subscription'
|
|
@@ -557,7 +616,7 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
557
616
|
: o.grokCredential === 'api_key'
|
|
558
617
|
? 'mkdir -p ~/.grok && install -m 600 /creds/grok-api-key ~/.grok/api-key && export XAI_API_KEY="$(cat ~/.grok/api-key)" && export ELANOUS_LLM_PROVIDER=grok'
|
|
559
618
|
: o.codexAccounts ? podCodexAccountScript(o.codexAccounts) : `mkdir -p ~/.elanous ~/.codex && cp /creds/elanous-auth.json ~/.elanous/auth.json && cp /creds/codex-auth.json ~/.codex/auth.json && chmod 600 ~/.elanous/auth.json ~/.codex/auth.json\n${podQuotaPolicyExport()}`,
|
|
560
|
-
o.appCredential ? `unset GH_TOKEN GITHUB_TOKEN; export GH_CONFIG_DIR="${APP_GH_CONFIG_PATH}"; mkdir -p "$GH_CONFIG_DIR" && chmod 700 "$GH_CONFIG_DIR" && { gh auth login --with-token < /creds/gh-token
|
|
619
|
+
o.appCredential ? `unset GH_TOKEN GITHUB_TOKEN; export GH_CONFIG_DIR="${APP_GH_CONFIG_PATH}"; mkdir -p "$GH_CONFIG_DIR" && chmod 700 "$GH_CONFIG_DIR" && { (umask 077; gh auth login --with-token < /creds/gh-token >"$HOME/.gh-login.log" 2>&1) || ${APP_GH_AUTH_COMMAND} < /creds/gh-token; } && chmod 600 "$GH_CONFIG_DIR/hosts.yml" && chmod 700 "$GH_CONFIG_DIR" || { echo "[pod] gh-login-failed: $(head -c 300 "$HOME/.gh-login.log" 2>/dev/null | tr '\\n' ' ' | sed -E 's/(gh[a-z]_|github_pat_)[A-Za-z0-9_.-]+/\\1***/g')"; exit 7; }` : 'export GH_TOKEN="$(cat /creds/gh-token)"',
|
|
561
620
|
// 🔑 스킬 키(.env) — 이미지엔 없다. 이 런의 Secret 에서 각 스킬 폴더로 0600 복사(값은 로그에 안 나온다).
|
|
562
621
|
...(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(',')}"`] : []),
|
|
563
622
|
'git config --global user.name "elanous pod child" && git config --global user.email "noreply@anthropic.com" && gh auth setup-git',
|
|
@@ -570,9 +629,11 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
570
629
|
`(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=$!`,
|
|
571
630
|
...(o.appCredential ? [podGithubWatchdogScript(o.githubStaleSeconds ?? POD_GH_STALE_SECONDS)] : []),
|
|
572
631
|
// 마지막 줄 JSON 이 «맨 끝»이어야 한다(parseSelfImplementJson) — rollup 은 그 앞에.
|
|
573
|
-
|
|
632
|
+
o.authorSentence
|
|
633
|
+
? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); 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 ))"; bun -e 'const fs=require("fs");const p="/tmp/si.out";const s=fs.readFileSync(p,"utf8");const line=s.trimEnd().split("\\n").at(-1);try{const o=JSON.parse(line);if(o.kind==="self"&&o.result&&typeof o.result==="object")fs.appendFileSync(p,"\\n"+JSON.stringify({...o.result,...(typeof o.ok==="boolean"?{ok:o.ok}:{})})+"\\n")}catch{}'`
|
|
634
|
+
: goalPath
|
|
574
635
|
? `elanous harness ask ${goalPath} --json${quotedAsk} > /tmp/si.out 2>&1; rc=$?; bun -e 'const fs=require("fs");const p="/tmp/si.out";const s=fs.readFileSync(p,"utf8");const line=s.trimEnd().split("\\n").at(-1);try{const o=JSON.parse(line);if(o.kind==="self"&&o.result&&typeof o.result==="object")fs.appendFileSync(p,"\\n"+JSON.stringify({...o.result,...(typeof o.ok==="boolean"?{ok:o.ok}:{})})+"\\n")}catch{}'`
|
|
575
|
-
: `elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1; rc=$?`,
|
|
636
|
+
: `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1; rc=$?`,
|
|
576
637
|
'cat /tmp/si.out',
|
|
577
638
|
'[ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h || echo "ELANOUS_USAGE_ROLLUP {\"measured\":false,\"reason\":\"no rollup script\"}"',
|
|
578
639
|
// A long run's log export can pass the 5MB artifact limit; skipping it whole left no window into why a
|
|
@@ -735,7 +796,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
735
796
|
catch (error) { debug.log('self-implement.pod', 'memory-advice-unavailable', { spaceId: input.spaceId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
736
797
|
const { limit: selectedMemoryLimit, tier: memoryTier, source: memorySource } = podMemoryLimitFor(input.feature, env, parentGoal, useAdvice && !explicitMemory ? advice : undefined);
|
|
737
798
|
const requestKind = broadPodTestWarning(input.feature) ? 'test' : goalTypeOf(input.feature)
|
|
738
|
-
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
799
|
+
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
800
|
+
?? ((parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '')) === 'document' ? 'docs' : null);
|
|
739
801
|
for (const [where, text] of [['feature', input.feature], ['parent-goal', parentGoal]] as const) {
|
|
740
802
|
if (text && text.split(/\r?\n/u).some((line) => /Pod 메모리/u.test(line) && !GOAL_LINE.test(line))) {
|
|
741
803
|
const reason = '한 줄 단독 `Pod 메모리: high|standard` 형식이 아님';
|
|
@@ -749,9 +811,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
749
811
|
const after = input.after ?? podPredecessorFor(input.feature, env, parentGoal);
|
|
750
812
|
if (after !== undefined) debug.log('self-implement.pod', 'predecessor', { spaceId: input.spaceId, after, source: input.after !== undefined ? 'input' : 'goal-or-option' });
|
|
751
813
|
let oomRetried = false;
|
|
814
|
+
let retryTier: PodMemoryTier = memoryTier === 'lite' ? 'standard' : 'high';
|
|
752
815
|
let retryFromChildRunId: string | undefined;
|
|
753
816
|
let retryFromMemoryLimit: string | undefined;
|
|
754
|
-
|
|
817
|
+
const selectedGoalType = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '');
|
|
818
|
+
const memoryReason = memorySource === 'option' && ['explicit-option', 'goal-type-default', 'existing-tier'].includes(env.ELANOUS_POD_MEMORY_REASON ?? '')
|
|
819
|
+
? env.ELANOUS_POD_MEMORY_REASON!
|
|
820
|
+
: memorySource === 'goal-type-auto' ? `declared goal type ${selectedGoalType}` : memorySource;
|
|
821
|
+
debug.log('self-implement.pod', 'memory-limit', { spaceId: input.spaceId, goalType: requestKind, declaredGoalType: selectedGoalType, memoryLimit, tier: memoryTier, source: memorySource, reason: memoryReason,
|
|
755
822
|
...(memorySource.startsWith('parent-') ? { inheritedFrom } : {}), ...(parentGoalUnreadable ? { parentGoal: 'unreadable' } : {}) });
|
|
756
823
|
const childRunId = mintRunId();
|
|
757
824
|
const executionKey = createHash('sha256').update(JSON.stringify({ spaceId: input.spaceId, feature: input.feature, base: input.base, autoMerge: input.autoMerge, autoReview: input.autoReview, openPr: input.openPr, draft: input.draft })).digest('hex');
|
|
@@ -769,6 +836,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
769
836
|
if (typeof intent.data.to !== 'string' || memoryGi(intent.data.to) === null) throw new Error('invalid OOM retry memory limit');
|
|
770
837
|
oomRetried = true;
|
|
771
838
|
memoryLimit = intent.data.to;
|
|
839
|
+
retryTier = parsePodMemoryTier(typeof intent.data.tier === 'string' ? intent.data.tier : undefined)
|
|
840
|
+
?? ((memoryGi(intent.data.to) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? 'high' : 'standard');
|
|
772
841
|
retryFromChildRunId = typeof intent.data.fromChildRunId === 'string' ? intent.data.fromChildRunId : undefined;
|
|
773
842
|
retryFromMemoryLimit = typeof intent.data.from === 'string' ? intent.data.from : undefined;
|
|
774
843
|
}
|
|
@@ -783,7 +852,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
783
852
|
...(input.draft === false ? ['--no-draft'] : []),
|
|
784
853
|
...(hostSupervised ? ['--no-supervise'] : []),
|
|
785
854
|
...(options.extraArgs ?? []),
|
|
786
|
-
...(options
|
|
855
|
+
...podChildLlmArgs(options),
|
|
787
856
|
];
|
|
788
857
|
debug.log('pod.self-implement', 'child-supervise', { hostSupervised, childSupervise: !hostSupervised });
|
|
789
858
|
const done = (async (): Promise<SelfImplementJobDone> => {
|
|
@@ -800,7 +869,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
800
869
|
if (!options.pool) return null;
|
|
801
870
|
for (;;) {
|
|
802
871
|
if (input.signal?.aborted) return null;
|
|
803
|
-
const acquired = options.pool.tryAcquire();
|
|
872
|
+
const acquired = await options.pool.tryAcquire();
|
|
804
873
|
if (acquired) {
|
|
805
874
|
debug.log('self-implement.pod', 'pool-slot', { spaceId: input.spaceId, context: acquired.context, inflight: options.pool.snapshot() });
|
|
806
875
|
return acquired;
|
|
@@ -868,6 +937,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
868
937
|
let retrySamples: ReturnType<typeof parseMemSamples> = [];
|
|
869
938
|
let failedAccount: string | undefined;
|
|
870
939
|
let usageLimitRetried = false;
|
|
940
|
+
let ghLoginRetried = false;
|
|
871
941
|
for (;;) {
|
|
872
942
|
try {
|
|
873
943
|
const grok = options.provider === 'grok';
|
|
@@ -930,10 +1000,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
930
1000
|
debug.log('self-implement.pod', 'credential-relay-skipped', { reason: 'no-host-url' });
|
|
931
1001
|
}
|
|
932
1002
|
const now = options.now ?? Date.now;
|
|
933
|
-
const issueVerifiedAppCredential = async (): Promise<{ token: string; expiresAt: number } | null> => {
|
|
1003
|
+
const issueVerifiedAppCredential = async (fresh = false): Promise<{ token: string; expiresAt: number } | null> => {
|
|
934
1004
|
if (!repository) return null;
|
|
935
1005
|
// This is the same repository-scoped, contents+pull_requests installation request on launch and refresh.
|
|
936
|
-
const candidate = (options.githubInstallation ?? ((repo) =>
|
|
1006
|
+
const candidate = (options.githubInstallation ?? ((repo, o) => coalescedInstallationCredential({ repository: repo.split('/')[1]! }, { fresh: o?.fresh })))(repository, { fresh });
|
|
937
1007
|
if (!candidate?.token) return null;
|
|
938
1008
|
const expiry = candidate.expires_at ?? ('expiresAt' in candidate ? candidate.expiresAt : undefined);
|
|
939
1009
|
const expiresAt = typeof expiry === 'number' ? expiry : Date.parse(expiry ?? '');
|
|
@@ -943,8 +1013,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
943
1013
|
? { token: candidate.token, expiresAt } : null;
|
|
944
1014
|
};
|
|
945
1015
|
let appCredential: Awaited<ReturnType<typeof issueVerifiedAppCredential>> = null;
|
|
946
|
-
try { appCredential = await issueVerifiedAppCredential(); }
|
|
1016
|
+
try { appCredential = await issueVerifiedAppCredential(ghLoginRetried); }
|
|
947
1017
|
catch { debug.log('self-implement.pod', 'github-app-unavailable', { job: name, reason: 'mint-or-verification-failed' }); }
|
|
1018
|
+
// PODCRED1: a short token is still shipped — the host renews it in the Pod on the first live poll — but it is
|
|
1019
|
+
// observed, so an exit-7 burst can be checked against the remaining life the Pods actually started with.
|
|
1020
|
+
if (appCredential && appCredential.expiresAt - now() < POD_APP_TOKEN_MIN_START_MS) {
|
|
1021
|
+
debug.log('self-implement.pod', 'github-app-short-lived', { job: name, expiresAt: new Date(appCredential.expiresAt).toISOString(), requiredSeconds: POD_APP_TOKEN_MIN_START_MS / 1000 });
|
|
1022
|
+
}
|
|
948
1023
|
const githubRelay = appCredential && groundingUrl && repository
|
|
949
1024
|
? await mint({ runId: groundingRunId, job: name, ttlMs: groundingTtlMs, scope: 'gh-credential', repository })
|
|
950
1025
|
: null;
|
|
@@ -1042,7 +1117,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1042
1117
|
// 🩸 종전엔 여기서 delete 부터 해서, 재개가 원격에서 멀쩡히 돌던 Job 을 죽였다. 실패로 끝난 Job 만 지우고 다시 만든다.
|
|
1043
1118
|
// ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 대표 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
|
|
1044
1119
|
const jobImage = member?.imageRef ?? image;
|
|
1045
|
-
const launchRunId = retryAccount || oomRetried ? mintRunId() : childRunId;
|
|
1120
|
+
const launchRunId = retryAccount || oomRetried || ghLoginRetried ? mintRunId() : childRunId;
|
|
1046
1121
|
let liveChildRunId = launchRunId;
|
|
1047
1122
|
let reattachedMemoryLimit: string | undefined;
|
|
1048
1123
|
let existingExecutionKey: string | undefined;
|
|
@@ -1083,7 +1158,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1083
1158
|
}
|
|
1084
1159
|
if (failedExisting) {
|
|
1085
1160
|
const pods = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o', 'jsonpath={range .items[*]}{.metadata.creationTimestamp}{"\\t"}{range .status.containerStatuses[?(@.name=="child")]}{.state.terminated.reason}{"\\t"}{.state.terminated.exitCode}{end}{"\\n"}{end}']);
|
|
1086
|
-
const
|
|
1161
|
+
const latestPod = pods.status === 0 ? pods.stdout.trimEnd().split('\n').map((line) => line.split('\t'))
|
|
1162
|
+
.filter(([created]) => created && !Number.isNaN(Date.parse(created)))
|
|
1163
|
+
.sort((a, b) => Date.parse(b[0]!) - Date.parse(a[0]!))[0] : undefined;
|
|
1164
|
+
const failedOom = latestPod?.[1]?.trim() === 'OOMKilled' && latestPod[2]?.trim() === '137';
|
|
1087
1165
|
// Once attempt 2 exists, consume its terminal result even if it failed for a non-OOM reason.
|
|
1088
1166
|
// Only the old, lower-memory attempt may be removed to make room for high.
|
|
1089
1167
|
if (failedOom || oomRetried) reattach = !oomRetried || (memoryGi(reattachedMemoryLimit ?? '') ?? 0) >= (memoryGi(memoryLimit) ?? Infinity);
|
|
@@ -1096,21 +1174,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1096
1174
|
debug.log('self-implement.pod', 'job-reattach', { job: name, conditions: existingConditions.join(' ') || 'running', spaceId: input.spaceId });
|
|
1097
1175
|
}
|
|
1098
1176
|
if (!hostRefresh && ghTokenExpiresAt === null && !githubRelay) debug.log('self-implement.pod', 'gh-token-app-skipped', { job: name, reason: 'no-verified-app-or-refresh-path' });
|
|
1177
|
+
let appliedMemoryRequest: string | undefined;
|
|
1099
1178
|
if (!reattach) {
|
|
1100
1179
|
if (oomRetried && hasExistingJob && !failedExisting && memoryGi(reattachedMemoryLimit ?? '') === null) {
|
|
1101
1180
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1102
1181
|
}
|
|
1103
1182
|
if (oomRetried && !retryAccount) {
|
|
1104
|
-
const
|
|
1105
|
-
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(
|
|
1183
|
+
const retryLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: retryTier }).limit;
|
|
1184
|
+
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(retryLimit) ?? Infinity)) throw new Error(`pod: OOM 재시도 한도가 ${retryTier} 와 다름: ${memoryLimit}`);
|
|
1106
1185
|
const leaseMembers = member ? [member] : [{ context, capacity: 1, k3dCluster: '' }];
|
|
1107
1186
|
const lease = recommendConcurrency(measurePoolLease(leaseMembers, { kubectl: (args) => baseKubectl(args) }), {
|
|
1108
|
-
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory:
|
|
1187
|
+
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory: retryLimit, accounts: 0, perAccount: 0,
|
|
1109
1188
|
});
|
|
1110
1189
|
if (lease.recommended === null || lease.placeableSlots === null || lease.placeableSlots < 1 || lease.memorySlots === null || lease.memorySlots < 1) {
|
|
1111
|
-
return { exitCode: 1, output: '', error: { code: 'pod-lease', message: `OOM 재시도
|
|
1190
|
+
return { exitCode: 1, output: '', error: { code: 'pod-lease', message: `OOM 재시도 ${retryTier} 자리 판정 실패: ${lease.reason ?? lease.limitedBy ?? 'no capacity'}\n${formatLastMemSample(retrySamples.at(-1))}` } };
|
|
1112
1191
|
}
|
|
1113
|
-
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit:
|
|
1192
|
+
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit: retryLimit, tier: retryTier, recommended: lease.recommended });
|
|
1114
1193
|
}
|
|
1115
1194
|
if (failedExisting && !reattach && !oomRetried && collectFullLogs('failed-job-logs-unavailable').status === 'incomplete') {
|
|
1116
1195
|
return { exitCode: 1, output: '', error: { code: 'pod-collection-incomplete', message: `Job ${name} recovery incomplete — old Job retained` } };
|
|
@@ -1146,8 +1225,15 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1146
1225
|
const measuredRequest = podMemoryRequestFor(requestKind, advice);
|
|
1147
1226
|
const requestGi = memoryGi(measuredRequest);
|
|
1148
1227
|
const limitGi = memoryGi(memoryLimit);
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1228
|
+
const documentDefaultLite = !oomRetried && selectedGoalType === 'document' && memoryTier === 'lite'
|
|
1229
|
+
&& (memorySource === 'goal-type-auto' || memoryReason === 'goal-type-default');
|
|
1230
|
+
const memoryRequest = documentDefaultLite ? memoryLimit
|
|
1231
|
+
: requestGi !== null && limitGi !== null && requestGi > limitGi ? memoryLimit : measuredRequest;
|
|
1232
|
+
appliedMemoryRequest = memoryRequest;
|
|
1233
|
+
debug.log('self-implement.pod', 'memory-request', { spaceId: input.spaceId, job: name,
|
|
1234
|
+
tier: oomRetried ? retryTier : memoryTier, reason: oomRetried ? 'OOMKilled' : memoryReason,
|
|
1235
|
+
memoryLimit, memoryRequest });
|
|
1236
|
+
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 } : {}), 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 }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
|
|
1151
1237
|
const a = kubectl(['apply', '-f', '-'], JSON.stringify(job));
|
|
1152
1238
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
1153
1239
|
releaseAdmission?.applied?.(name, context, namespace);
|
|
@@ -1183,7 +1269,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1183
1269
|
}
|
|
1184
1270
|
try {
|
|
1185
1271
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-run', data: { childRunId: liveChildRunId, job: name, attempt: oomRetried ? 2 : 1, ...(goalFile ? { goalFile } : {}) } }, ledgerDir);
|
|
1186
|
-
|
|
1272
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-memory-selected', data: { job: name, childRunId: liveChildRunId, attempt: oomRetried ? 2 : 1, limit: memoryLimit, ...(appliedMemoryRequest ? { request: appliedMemoryRequest } : {}), tier: oomRetried ? retryTier : memoryTier, source: oomRetried ? 'oom-retry' : memorySource, reason: oomRetried ? 'OOMKilled' : memoryReason, declaredGoalType: selectedGoalType } }, ledgerDir);
|
|
1273
|
+
if (oomRetried && retryFromChildRunId) appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry', data: { attempt: 2, from: retryFromMemoryLimit ?? selectedMemoryLimit, to: memoryLimit, tier: retryTier, reason: 'OOMKilled', job: name, childRunId: liveChildRunId, fromChildRunId: retryFromChildRunId } }, ledgerDir);
|
|
1187
1274
|
} catch (error) {
|
|
1188
1275
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1189
1276
|
debug.log('self-implement.pod', 'parent-ledger-unavailable', { job: name, reason, attempt: oomRetried ? 2 : 1 });
|
|
@@ -1293,6 +1380,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1293
1380
|
const parsed = parseSelfImplementJson(logs);
|
|
1294
1381
|
const terminal = parseSelfImplementJson(logs.trimEnd().split('\n').at(-1) ?? '');
|
|
1295
1382
|
const nextAccount = codexAccounts?.find((candidate) => candidate !== account);
|
|
1383
|
+
if (state === 'failed' && containerExitCode === 7 && !input.signal?.aborted) {
|
|
1384
|
+
debug.log('self-implement.pod', 'gh-login-failed', { job: name, attempt: ghLoginRetried ? 2 : 1, tokenExpiresInSec: ghTokenExpiresAt === null ? null : Math.floor((ghTokenExpiresAt - now()) / 1000) });
|
|
1385
|
+
if (!ghLoginRetried) {
|
|
1386
|
+
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1387
|
+
if (deleted.status !== 0) throw new Error(`pod: GitHub 로그인 실패 Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1388
|
+
// Bursts came from simultaneous launches (10-05 12:49Z · 5 deaths in 2 s) — wait, spread by job name, then
|
|
1389
|
+
// retry once with a newly issued token so the retries do not collide again.
|
|
1390
|
+
const backoffMs = podGhLoginRetryBackoffMs(name);
|
|
1391
|
+
debug.log('self-implement.pod', 'gh-login-retry-backoff', { job: name, backoffMs });
|
|
1392
|
+
await sleep(backoffMs);
|
|
1393
|
+
if (input.signal?.aborted) return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before GitHub login retry' } };
|
|
1394
|
+
ghLoginRetried = true;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1296
1398
|
if (!usageLimitRetried && !input.signal?.aborted && state !== 'aborted' && terminal?.ok === false
|
|
1297
1399
|
&& /\b429\b/.test(terminal.error ?? '') && /\busage_limit_reached\b/.test(terminal.error ?? '') && nextAccount) {
|
|
1298
1400
|
debug.log('self-implement.pod', 'usage-limit-retry', { job: name, spaceId: input.spaceId, from: account, to: nextAccount });
|
|
@@ -1316,7 +1418,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1316
1418
|
try {
|
|
1317
1419
|
const hostRoot = findGitDir(process.cwd())?.root;
|
|
1318
1420
|
if (!hostRoot) throw new Error('host repository unavailable');
|
|
1319
|
-
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot });
|
|
1421
|
+
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot, ...(goalFile ? { goalFile } : {}) });
|
|
1320
1422
|
} catch (error) {
|
|
1321
1423
|
debug.log('harness.host-regate', 'unmeasured', { pr: prNumber, files: [], os: process.platform, failedStep: 'host-regate' });
|
|
1322
1424
|
regate = { passed: false, failures: [{ step: 'host-regate', detail: error instanceof Error ? error.message : String(error) }], os: process.platform };
|
|
@@ -1335,7 +1437,16 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1335
1437
|
}
|
|
1336
1438
|
const childFailure = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1337
1439
|
? lastPodChildFailure(logs) : null;
|
|
1440
|
+
let noResultDiagnostic: string | null = null;
|
|
1441
|
+
if (state === 'failed' && containerExitCode !== null && containerExitCode !== 0 && !disposition && !oomKilled && failedReason !== 'DeadlineExceeded' && failedReason !== 'BackoffLimitExceeded') {
|
|
1442
|
+
try { noResultDiagnostic = podNoResultDiagnostic(readFileSync(runLedgerPath(liveChildRunId, ledgerDir), 'utf8')); }
|
|
1443
|
+
catch { /* ledger not returned: do not invent a stage */ }
|
|
1444
|
+
noResultDiagnostic ??= 'child terminal result missing; last ledger stage=unknown; round=unknown; mustFix=unknown';
|
|
1445
|
+
}
|
|
1338
1446
|
if (childFailure) debug.log('self-implement.pod', 'child-error', { job: name, stage: childFailure.stage, error: childFailure.error }, { compact: { stringMax: 500 } });
|
|
1447
|
+
if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
|
|
1448
|
+
debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
|
|
1449
|
+
}
|
|
1339
1450
|
debug.log('self-implement.pod', 'job-finished', { job: name, ...(member ? { context: member.context } : {}), state, containerReason, stage: disposition?.stage ?? null, prUrl: disposition?.prUrl ?? null, childRunId: liveChildRunId, ledgerCompleteness,
|
|
1340
1451
|
...(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1341
1452
|
? { ...(childFailure ? { childStage: childFailure.stage } : {}), childError: childFailure?.error ?? 'no-result-line' } : {}) },
|
|
@@ -1344,22 +1455,25 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1344
1455
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1345
1456
|
}
|
|
1346
1457
|
if (oomKilled && failedReason !== 'DeadlineExceeded' && !input.signal?.aborted && !oomRetried) {
|
|
1347
|
-
const high = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit;
|
|
1348
1458
|
const actual = memoryGi(reattachedMemoryLimit ?? memoryLimit);
|
|
1459
|
+
const standardLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'standard' }).limit;
|
|
1460
|
+
const nextTier: PodMemoryTier = actual !== null && actual < (memoryGi(standardLimit) ?? 0) ? 'standard' : 'high';
|
|
1461
|
+
const nextLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: nextTier }).limit;
|
|
1349
1462
|
if (actual === null) {
|
|
1350
1463
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 판정할 수 없다: ${memoryLimit}` } };
|
|
1351
1464
|
}
|
|
1352
|
-
if (actual < (memoryGi(
|
|
1465
|
+
if (actual < (memoryGi(nextLimit) ?? actual)) {
|
|
1353
1466
|
const from = reattachedMemoryLimit ?? memoryLimit;
|
|
1354
|
-
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-intent', data: { attempt: 2, from, to:
|
|
1355
|
-
debug.log('self-implement.pod', 'oom-retry', { from, to:
|
|
1467
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-intent', data: { attempt: 2, from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name, executionKey, fromChildRunId: liveChildRunId } }, ledgerDir);
|
|
1468
|
+
debug.log('self-implement.pod', 'oom-retry', { from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name });
|
|
1356
1469
|
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1357
1470
|
if (deleted.status !== 0) throw new Error(`pod: OOM Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1358
1471
|
oomRetried = true;
|
|
1359
1472
|
retryFromChildRunId = liveChildRunId;
|
|
1360
1473
|
retryFromMemoryLimit = from;
|
|
1361
1474
|
retrySamples = samples;
|
|
1362
|
-
memoryLimit =
|
|
1475
|
+
memoryLimit = nextLimit;
|
|
1476
|
+
retryTier = nextTier;
|
|
1363
1477
|
if (member) { options.pool!.release(member); member = null; }
|
|
1364
1478
|
releaseAdmission?.();
|
|
1365
1479
|
releaseAdmission = undefined;
|
|
@@ -1378,7 +1492,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1378
1492
|
const record: GoalExecutionRecord = {
|
|
1379
1493
|
runId: liveChildRunId,
|
|
1380
1494
|
stage: (state === 'aborted' || !disposition ? 'aborted' : disposition.stage) as GoalExecutionRecord['stage'],
|
|
1381
|
-
outcome: (!disposition ? 'pod-no-result' : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
1495
|
+
outcome: (!disposition ? noResultDiagnostic ? 'abandoned' : 'pod-no-result' : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
1382
1496
|
ok: state === 'complete' && (disposition?.ok ?? false),
|
|
1383
1497
|
...(disposition?.prNumber !== undefined ? { prNumber: disposition.prNumber } : {}),
|
|
1384
1498
|
completedAt: new Date().toISOString(),
|
|
@@ -1402,16 +1516,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1402
1516
|
const finishedDisposition = deadlineExceeded && disposition
|
|
1403
1517
|
? { ...disposition, failureClassification: 'run-deadline-exceeded' as const }
|
|
1404
1518
|
: disposition;
|
|
1519
|
+
const podExitCode = state === 'complete' && disposition?.stage !== 'host-regate-failed' && disposition?.ok !== false ? 0 : 1;
|
|
1520
|
+
// SCHED-CONTRACT: a failed child must say why outside the Pod, not «no error diagnostic».
|
|
1521
|
+
const podReason = podExitCode === 1 ? extractPodFailureReason({ logs, logTailReason, containerReason, jobReason: failedReason || undefined, deadlineSeconds }) : undefined;
|
|
1522
|
+
if (podReason) debug.log('self-implement.pod', 'failure-reason', { job: name, reason: podReason });
|
|
1405
1523
|
return {
|
|
1406
|
-
exitCode:
|
|
1524
|
+
exitCode: podExitCode,
|
|
1407
1525
|
output: tail,
|
|
1408
1526
|
...(deadlineExceeded
|
|
1409
1527
|
? { error: { code: 'pod-deadline-exceeded', message: `Job ${name} 이 수명 상한 ${deadlineSeconds}초에 닿았다` } }
|
|
1410
1528
|
: state === 'failed'
|
|
1411
|
-
?
|
|
1412
|
-
? { error: { code: 'pod-
|
|
1413
|
-
|
|
1414
|
-
|
|
1529
|
+
? containerExitCode === 7
|
|
1530
|
+
? { error: { code: 'pod-gh-login-failed', message: `Job ${name} GitHub 앱 로그인 실패 (container=${containerReason ?? 'unknown'}/7, attempt=${ghLoginRetried ? 2 : 1}) — 새 토큰으로 ${ghLoginRetried ? '1회 재시도했으나 다시 실패' : '재시도 불가'}` } }
|
|
1531
|
+
: oomKilled
|
|
1532
|
+
? { error: { code: 'pod-oom-killed', message: `Job ${name} failed (OOMKilled/${containerExitCode ?? 'unknown'}, memoryLimit=${memoryLimit})${oomRetried || (memoryGi(reattachedMemoryLimit ?? memoryLimit) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? ` — OOM · ${oomRetried ? retryTier : 'high'} 에서도` : ''}\n${formatLastMemSample(samples.at(-1))}` } }
|
|
1533
|
+
: { error: { code: 'pod-job-failed', message: `Job ${name} failed${failedReason || containerReason ? ` (${[failedReason, containerReason ? `container=${containerReason}/${containerExitCode ?? 'unknown'}` : ''].filter(Boolean).join(', ')})` : ''}${childFailure ? ` — childStage=${childFailure.stage} · childError=${childFailure.error}` : ` — childError=no-result-line · reason=${podReason}`}${noResultDiagnostic ? ` · ${noResultDiagnostic}` : ''}` } }
|
|
1534
|
+
: podReason && !childFailure?.error ? { error: { code: 'pod-child-failed', message: podReason } } : {}),
|
|
1415
1535
|
...(finishedDisposition ? { disposition: finishedDisposition } : {}),
|
|
1416
1536
|
};
|
|
1417
1537
|
} catch (err) {
|
|
@@ -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 { oldDoorInternalEnv } from '../../self-dev/old-door.js';
|
|
22
23
|
import { selfDevRunsDir } from '../../self-dev/run-store.js';
|
|
23
24
|
import { debug } from '../../debug/log.js';
|
|
24
25
|
import { join, resolve } from 'node:path';
|
|
@@ -49,6 +50,7 @@ export interface SelfImplementDisposition {
|
|
|
49
50
|
ok?: boolean;
|
|
50
51
|
worktreePath?: string;
|
|
51
52
|
branch?: string;
|
|
53
|
+
harvestable?: true;
|
|
52
54
|
prUrl?: string;
|
|
53
55
|
prNumber?: number;
|
|
54
56
|
/** Head SHA checked by the Pod before host regate. */
|
|
@@ -84,6 +86,7 @@ export interface SelfImplementDisposition {
|
|
|
84
86
|
reviewVerdict?: 'pass' | 'warn' | 'fail';
|
|
85
87
|
/** 마지막 리뷰의 must-fix 수(`review.mustFix.length`). 같은 조건. */
|
|
86
88
|
reviewMustFixCount?: number;
|
|
89
|
+
mustFix?: string[];
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
/**
|
|
@@ -265,7 +268,7 @@ export function createSelfImplementAdapter(opts: SelfImplementAdapterOptions) {
|
|
|
265
268
|
: r.output.length === 0
|
|
266
269
|
? `${exitMessage} (child produced no output)`
|
|
267
270
|
: r.disposition
|
|
268
|
-
? `${exitMessage} (child
|
|
271
|
+
? `${exitMessage} (child stage=${r.disposition.stage ?? 'unknown'}; ${r.disposition.detail ?? `mustFix=${JSON.stringify(r.disposition.mustFix ?? 'unknown')}`})`
|
|
269
272
|
: `${exitMessage} (child output did not contain a parseable terminal JSON diagnostic)`;
|
|
270
273
|
return {
|
|
271
274
|
...exec,
|
|
@@ -354,6 +357,7 @@ export function defaultSelfImplementSpawn(): SelfImplementJobSpawn {
|
|
|
354
357
|
...childNestEnv(),
|
|
355
358
|
...harnessSpaceEnv('self-implement', input.spaceId),
|
|
356
359
|
...executorRoleEnv(), // ⭐ 세포 role 분화 — 자식은 executor(실행자)로 자기인지.
|
|
360
|
+
...oldDoorInternalEnv('self-implement'),
|
|
357
361
|
},
|
|
358
362
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
359
363
|
});
|
|
@@ -461,6 +465,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
461
465
|
...(typeof o.ok === 'boolean' ? { ok: o.ok } : {}),
|
|
462
466
|
...(typeof o.worktreePath === 'string' ? { worktreePath: o.worktreePath } : {}),
|
|
463
467
|
...(typeof o.branch === 'string' ? { branch: o.branch } : {}),
|
|
468
|
+
...(o.harvestable === true && typeof o.branch === 'string' && o.branch.trim() ? { harvestable: true } : {}),
|
|
464
469
|
...(typeof o.prUrl === 'string' ? { prUrl: o.prUrl } : {}),
|
|
465
470
|
...(typeof o.prNumber === 'number' ? { prNumber: o.prNumber } : {}),
|
|
466
471
|
...(typeof o.checkedHeadCommit === 'string' ? { checkedHeadCommit: o.checkedHeadCommit } : {}),
|
|
@@ -483,6 +488,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
483
488
|
? { providerErrors: o.providerErrors as SelfImplementDisposition['providerErrors'] } : {}),
|
|
484
489
|
...(typeof o.runId === 'string' && o.runId.trim() ? { runId: o.runId, childRunId: o.runId } : {}),
|
|
485
490
|
...reviewGateFields(o),
|
|
491
|
+
...(Array.isArray(o.mustFix) && o.mustFix.every((item) => typeof item === 'string') ? { mustFix: o.mustFix as string[] } : {}),
|
|
486
492
|
};
|
|
487
493
|
reportUnmappedDispositionFields(o, mapped);
|
|
488
494
|
return mapped;
|
package/src/telegram-commands.ts
CHANGED
|
@@ -233,6 +233,11 @@ export function parseTelegramSlash(text: string, commands: TgSlashCommand[]): Tg
|
|
|
233
233
|
* caller (handleIncoming) can send it directly without going through
|
|
234
234
|
* dispatchTelegramSlash when it already has the parse result. */
|
|
235
235
|
export function buildUnknownSlashReply(name: string, commands: TgSlashCommand[]): string {
|
|
236
|
+
const unsupported = buildBotSlashCatalog({
|
|
237
|
+
surface: 'telegram', coreCommands: SLASH_COMMANDS, maturity: FEATURE_MATURITY,
|
|
238
|
+
handledCommands: commands,
|
|
239
|
+
}).unsupportedReply(name);
|
|
240
|
+
if (unsupported) return unsupported;
|
|
236
241
|
const names = commands.map(c => `/${c.name}`).join(' ');
|
|
237
242
|
return `Unknown command: /${name}\nAvailable: ${names}`;
|
|
238
243
|
}
|