elanous 0.2.14 → 0.2.15
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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-33ffcc49ab45123b.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/app/approvals/{page-74383f890f075511.js → page-0e2c57fe6add4b2a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-e05a77df7e005b04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-b8c49f6b4fa2df91.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-976813c91d7394ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-fb260b9283dbf646.js → page-28b9448091776c06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-60c7a8e1a4fe62ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-2a52b316cb143d9c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-c3ce483c8d5db370.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-96f074f6538e8a2c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-e97be5334d83a9f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-955ec2da3be4c560.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-c1343f9499d86f3f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-d23b12bb92242558.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-04250f0e2c0d03df.js → page-e2ee2896811e98d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-5efbf81124c474ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-85f1a1fc0a82738c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-8ac8d71c2adf9c8a.js → page-da6c08aae0f92768.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-153c2b3bcc89e8de.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-94dc383d3d54db1a.js → page-87a11e6a05f15199.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-b3759cc14da80908.js → page-7d4182964407c09c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-0d6d23b40263a7ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-f5597e894c02cd33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-35bdeae2e2c17147.js → page-73735b64bfe1ae67.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-8fcec4c9ea2e7f70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-9817c7f9007466cc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-134c1bc762aee62c.js → page-97f9f43172a3a90e.js} +1 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.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 -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 +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/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/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/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/expose-rubric-suggest.ts +18 -12
- 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/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/gate-node.ts +80 -14
- package/scripts/release-loop/vault-note-node.ts +240 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/agent-env/profile.ts +161 -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/briefing/brief-items.ts +121 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -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/pod-cli.ts +2 -0
- package/src/cli/release-cli.ts +61 -27
- package/src/cli/seat-requests-cli.ts +52 -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/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/harness/harness-cli-command.ts +111 -15
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +100 -11
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +16 -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/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 +62 -4
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/index.ts +77 -7
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +37 -15
- 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/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -0
- 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/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +11 -4
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/maturity/tui-slash-availability.ts +31 -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/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +58 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +86 -26
- 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 +126 -10
- package/src/self-dev/dev-pipeline.ts +8 -4
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- 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/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- 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 +187 -40
- 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 +2 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +1 -1
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- 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-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +147 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +29 -5
- 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/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/control/page-e6b84ab07d3567d5.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/loops/page-160089123a40dd91.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/release/page-e7e10cd3386222e5.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/page-341480ed8e3c1ea8.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/showroom/page-58043db78ba519ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.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/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.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 → LZcstaO9_NXypPFmujolv}/_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';
|
|
@@ -59,8 +59,10 @@ import { LLM_TIER_MAP_BY_PROVIDER, lookupLlmTierSpec, type LlmTierProvider } fro
|
|
|
59
59
|
import { parseSelfImplementJson, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
|
|
60
60
|
import { codexQuotaPolicyFromConfig } from '../../oauth/codex-account-store.js';
|
|
61
61
|
import { goalTypeOf, type GoalType } from '../../../scripts/measure-pod-memory-by-goal.js';
|
|
62
|
+
import { declaredGoalType, type GoalType as DeclaredGoalType } from '../../self-implement/goal-author.js';
|
|
62
63
|
import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
|
|
63
64
|
import { getUserConfig } from '../../user-config.js';
|
|
65
|
+
import { extractPodFailureReason } from './pod-failure-reason.js';
|
|
64
66
|
|
|
65
67
|
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
66
68
|
|
|
@@ -83,6 +85,11 @@ export interface PodSpawnOptions {
|
|
|
83
85
|
rotationAccounts?: readonly string[];
|
|
84
86
|
/** 미지정이면 종전 Codex Job. */
|
|
85
87
|
provider?: 'openai-codex' | 'grok';
|
|
88
|
+
/** Child model/effort chosen at launch (`--child-llm-model/--child-llm-effort`) — carried into the Pod child. */
|
|
89
|
+
childModel?: string;
|
|
90
|
+
childEffort?: string;
|
|
91
|
+
/** True when the child provider was named at launch — the child gets explicit flags even for codex. */
|
|
92
|
+
childProviderExplicit?: boolean;
|
|
86
93
|
/** 🔐 API 키 과금 허용은 명시 true 뿐. */
|
|
87
94
|
grokApiKeyOptIn?: boolean;
|
|
88
95
|
namespace?: string;
|
|
@@ -108,7 +115,7 @@ export interface PodSpawnOptions {
|
|
|
108
115
|
pollMs?: number;
|
|
109
116
|
kubectl?: Kubectl;
|
|
110
117
|
/** Host-side regate (injected for Pod tests). */
|
|
111
|
-
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string }) => Promise<HostRegateResult>;
|
|
118
|
+
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string; goalFile?: string }) => Promise<HostRegateResult>;
|
|
112
119
|
/** PR comment for host-regate failures that never reached runHostRegate (injectable for tests). */
|
|
113
120
|
ghComment?: (prNumber: number, body: string) => void;
|
|
114
121
|
/** 🔑 Pod 필수 스킬의 키(.env)를 이 런의 Secret 으로 넘긴다 — 명시 opt-in(유료 크레딧을 쓴다). */
|
|
@@ -123,7 +130,8 @@ export interface PodSpawnOptions {
|
|
|
123
130
|
mintGrounding?: (claims: { runId: string; job: string; ttlMs: number; scope?: GroundingTokenScope; repository?: string }) => Promise<{ token: string; exp: number }>;
|
|
124
131
|
revokeGrounding?: (runId: string) => void;
|
|
125
132
|
/** Test seam for the host's repository-scoped GitHub App installation token. */
|
|
126
|
-
|
|
133
|
+
/** `fresh` = do not reuse a coalesced token (the exit-7 retry must not get the token that just failed back). */
|
|
134
|
+
githubInstallation?: (repository: string, opts?: { fresh?: boolean }) => { token: string; expires_at?: string; expiresAt?: string | number } | null;
|
|
127
135
|
/** GitHub installation repository lookup; required to prove the scoped token matches owner/name. */
|
|
128
136
|
githubRepositories?: (token: string) => Promise<readonly string[] | null>;
|
|
129
137
|
now?: () => number;
|
|
@@ -184,6 +192,17 @@ export function hostCredentials(account: string, storePath: string = authStorePa
|
|
|
184
192
|
|
|
185
193
|
/** A Pod gets a refresh-less access copy, so the copy must outlive the Job: same rule as the Codex path (3 h). */
|
|
186
194
|
export const POD_GROK_MIN_VALIDITY_MS = 3 * 3600_000;
|
|
195
|
+
/** PODCRED1 (10-05): an App installation token lives ~60 min and the host renews it in the Pod at the ten-minute
|
|
196
|
+
* boundary; below this remaining life at launch the token is observed (`github-app-short-lived`). */
|
|
197
|
+
export const POD_APP_TOKEN_MIN_START_MS = 20 * 60_000;
|
|
198
|
+
/** Exit 7 (in-Pod GitHub login failed) is retried once after this spread backoff — bursts were simultaneous launches. */
|
|
199
|
+
export const POD_GH_LOGIN_RETRY_BACKOFF_MS = 60_000;
|
|
200
|
+
/** Base backoff plus a 0-59 s spread derived from the job name, so jobs that failed together retry apart. */
|
|
201
|
+
export function podGhLoginRetryBackoffMs(job: string): number {
|
|
202
|
+
let spread = 0;
|
|
203
|
+
for (const ch of job) spread = (spread * 31 + ch.charCodeAt(0)) % 60;
|
|
204
|
+
return POD_GH_LOGIN_RETRY_BACKOFF_MS + spread * 1000;
|
|
205
|
+
}
|
|
187
206
|
|
|
188
207
|
/** resolveGrokCredential 과 동일한 출처를 읽되 Pod 로는 refresh 없는 access 사본만 보낸다.
|
|
189
208
|
* POD4 (10-01): the copy used to ship whatever the host had — an AUTH1 shard copied a token that had expired
|
|
@@ -453,11 +472,11 @@ export function podRunLabels(o: { runId?: string; parentRunId?: string }): Recor
|
|
|
453
472
|
|
|
454
473
|
/** Pod 메모리 «등급» — 대표 09-27: PWA Pod 골이 16Gi 에서 OOM 으로 죽는다 ⇒ «high» 등급을 옵션으로 고른다.
|
|
455
474
|
* 등급 = `standard`(`ELANOUS_POD_MEMORY` 또는 16Gi) · `high`(`ELANOUS_POD_MEMORY_HIGH` 또는 32Gi · standard 보다 작아지지 않는다).
|
|
456
|
-
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면
|
|
475
|
+
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면 `Pod 메모리: lite|standard|high` ③ 조각의 `apps/pwa/` ④ 부모의 명시 메모리 줄·PWA 경로 ⑤ 실측 권고 ⑥ 권고 없는 research/document 는 lite ⑦ standard.
|
|
457
476
|
* node-b 노드 할당 가능 약 251Gi. */
|
|
458
477
|
export const POD_MEMORY_DEFAULT = '16Gi';
|
|
459
478
|
export const POD_MEMORY_HIGH_DEFAULT = '32Gi';
|
|
460
|
-
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi):
|
|
479
|
+
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi): research/document 기본값; OOM 시 standard 로 한 단계 승급한다. */
|
|
461
480
|
export const POD_MEMORY_LITE_DEFAULT = '2Gi';
|
|
462
481
|
|
|
463
482
|
/** Reserve from measured per-run peaks, never from the advisory limit tier. Sparse or missing measurements keep the old reservation. */
|
|
@@ -475,7 +494,7 @@ export function podMemoryRequestFor(kind: GoalType | null, advice?: PodMemoryAdv
|
|
|
475
494
|
}
|
|
476
495
|
export const POD_MEMORY_TIERS = ['lite', 'standard', 'high'] as const;
|
|
477
496
|
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';
|
|
497
|
+
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise' | 'goal-type-auto';
|
|
479
498
|
const PWA_PATH = /(?:^|[\s`'"(,·])apps\/pwa\//m;
|
|
480
499
|
const GOAL_LINE = /^[ \t]*(?:Pod 메모리|pod-memory)[ \t]*:[ \t]*(lite|standard|high)[ \t]*\r?$/im;
|
|
481
500
|
|
|
@@ -511,6 +530,10 @@ export function parsePodMemoryTier(v: string | undefined): PodMemoryTier | null
|
|
|
511
530
|
return t && (POD_MEMORY_TIERS as readonly string[]).includes(t) ? (t as PodMemoryTier) : null;
|
|
512
531
|
}
|
|
513
532
|
|
|
533
|
+
function parsePodGoalType(value: string | undefined): DeclaredGoalType | null {
|
|
534
|
+
return value === 'implement' || value === 'research' || value === 'document' || value === 'operate' ? value : null;
|
|
535
|
+
}
|
|
536
|
+
|
|
514
537
|
function memoryGi(v: string): number | null {
|
|
515
538
|
const m = /^(\d+(?:\.\d+)?)(Gi|Mi)$/.exec(v);
|
|
516
539
|
if (!m) return null;
|
|
@@ -521,9 +544,13 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
521
544
|
const option = parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER);
|
|
522
545
|
const line = parsePodMemoryTier(GOAL_LINE.exec(feature)?.[1]);
|
|
523
546
|
const parentLine = !option && !line ? parsePodMemoryTier(GOAL_LINE.exec(parentGoal ?? '')?.[1]) : null;
|
|
547
|
+
const declared = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE)
|
|
548
|
+
?? declaredGoalType(feature) ?? declaredGoalType(parentGoal ?? '');
|
|
549
|
+
// 선언 «document» 는 실측 분류의 «docs» 다 — 낱말 판정(goalTypeOf)이 «document» 를 못 읽어 권고가 안 이어지던 구멍
|
|
524
550
|
const goalType = advice && !option && !line && !parentLine
|
|
525
551
|
? (broadPodTestWarning(feature) ? 'test' : goalTypeOf(feature))
|
|
526
552
|
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
553
|
+
?? (declared === 'document' ? 'docs' : null)
|
|
527
554
|
: null;
|
|
528
555
|
const recommendation = advice?.byGoalType.find((entry) => entry.goalType === goalType && entry.evidence.runs > 0)?.recommended;
|
|
529
556
|
const [tier, source]: [PodMemoryTier, PodMemorySource] = option ? [option, 'option']
|
|
@@ -531,8 +558,11 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
531
558
|
// 조각 자신의 PWA 경로가 부모 줄·실측 권고보다 먼저다 — 부모 «standard» 가 PWA 조각을 16Gi 로 내리면 OOM(POD7 · #23569 순서 회귀)
|
|
532
559
|
: goalTouchesPwa(feature) ? ['high', 'pwa-auto']
|
|
533
560
|
: parentLine ? [parentLine, 'parent-goal-line']
|
|
534
|
-
: recommendation ? [recommendation, 'advise']
|
|
535
561
|
: parentGoal && goalTouchesPwa(parentGoal) ? ['high', 'parent-pwa-auto']
|
|
562
|
+
// 실측이 high(OOM 증거)를 권하면 선언 종류의 자동 lite 보다 먼저다 — 문서 골도 OOM 이 났으면 다시 2Gi 로 띄우지 않는다
|
|
563
|
+
: recommendation === 'high' ? ['high', 'advise']
|
|
564
|
+
: declared === 'research' || declared === 'document' ? ['lite', 'goal-type-auto']
|
|
565
|
+
: recommendation ? [recommendation, 'advise']
|
|
536
566
|
: ['standard', 'default'];
|
|
537
567
|
if (tier === 'lite') return { limit: env.ELANOUS_POD_MEMORY_LITE?.trim() || POD_MEMORY_LITE_DEFAULT, tier, source };
|
|
538
568
|
const base = env.ELANOUS_POD_MEMORY?.trim() || POD_MEMORY_DEFAULT;
|
|
@@ -542,6 +572,28 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
542
572
|
return { limit: a !== null && b !== null && a > b ? base : high, tier, source };
|
|
543
573
|
}
|
|
544
574
|
|
|
575
|
+
/** A named child provider decides the Pod provider (10-05 PODPROVIDER) — grok needs a usable credential, other names are refused. */
|
|
576
|
+
export function podNamedChildProvider(named: string | undefined, credential: { grokSubscription: boolean; grokApiKey: boolean }): { provider?: 'openai-codex' | 'grok'; refuse?: string } {
|
|
577
|
+
if (!named) return {};
|
|
578
|
+
if (named === 'openai-codex' || named === 'codex') return { provider: 'openai-codex' };
|
|
579
|
+
if (named === 'grok') return credential.grokSubscription || credential.grokApiKey ? { provider: 'grok' } : { refuse: 'pod: Pod 에 grok 자격 없음 — 구독 자격이 쓸 수 없거나(만료 임박·갱신 불가) API 키 과금 동의(harness.pod.grokApiKeyOptIn)가 없다 · codex 로 조용히 돌리지 않는다' };
|
|
580
|
+
return { refuse: `pod: Pod 자식 provider 는 openai-codex|grok 만 — 받음 ${named}` };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Fallback credentials for the codex plan — a launch that named openai-codex never falls back to grok (post-review must-fix). */
|
|
584
|
+
export function podFallbackCredentials(named: 'openai-codex' | 'grok' | undefined, fallback: { grokSubscription: boolean; grokApiKey: boolean }): { grokSubscription: boolean; grokApiKey: boolean } {
|
|
585
|
+
return named === 'openai-codex' ? { grokSubscription: false, grokApiKey: false } : fallback;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** Child LLM flags for the Pod child — grok always names its model; codex only when the launch named it (10-05 PODPROVIDER). */
|
|
589
|
+
export function podChildLlmArgs(options: Pick<PodSpawnOptions, 'provider' | 'childModel' | 'childEffort' | 'childProviderExplicit'>): string[] {
|
|
590
|
+
const provider = options.provider === 'grok' ? 'grok' : options.childProviderExplicit ? 'openai-codex' : undefined;
|
|
591
|
+
if (!provider) return [];
|
|
592
|
+
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : undefined);
|
|
593
|
+
// `self implement` registers no --child-llm-effort — passing it kills the Pod at argument parsing, so effort stays host-only.
|
|
594
|
+
return ['--child-llm-provider', provider, ...(model ? ['--child-llm-model', model] : [])];
|
|
595
|
+
}
|
|
596
|
+
|
|
545
597
|
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; 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
598
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
547
599
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
@@ -735,7 +787,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
735
787
|
catch (error) { debug.log('self-implement.pod', 'memory-advice-unavailable', { spaceId: input.spaceId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
736
788
|
const { limit: selectedMemoryLimit, tier: memoryTier, source: memorySource } = podMemoryLimitFor(input.feature, env, parentGoal, useAdvice && !explicitMemory ? advice : undefined);
|
|
737
789
|
const requestKind = broadPodTestWarning(input.feature) ? 'test' : goalTypeOf(input.feature)
|
|
738
|
-
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
790
|
+
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
791
|
+
?? ((parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '')) === 'document' ? 'docs' : null);
|
|
739
792
|
for (const [where, text] of [['feature', input.feature], ['parent-goal', parentGoal]] as const) {
|
|
740
793
|
if (text && text.split(/\r?\n/u).some((line) => /Pod 메모리/u.test(line) && !GOAL_LINE.test(line))) {
|
|
741
794
|
const reason = '한 줄 단독 `Pod 메모리: high|standard` 형식이 아님';
|
|
@@ -749,9 +802,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
749
802
|
const after = input.after ?? podPredecessorFor(input.feature, env, parentGoal);
|
|
750
803
|
if (after !== undefined) debug.log('self-implement.pod', 'predecessor', { spaceId: input.spaceId, after, source: input.after !== undefined ? 'input' : 'goal-or-option' });
|
|
751
804
|
let oomRetried = false;
|
|
805
|
+
let retryTier: PodMemoryTier = memoryTier === 'lite' ? 'standard' : 'high';
|
|
752
806
|
let retryFromChildRunId: string | undefined;
|
|
753
807
|
let retryFromMemoryLimit: string | undefined;
|
|
754
|
-
|
|
808
|
+
const selectedGoalType = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '');
|
|
809
|
+
const memoryReason = memorySource === 'option' && ['explicit-option', 'goal-type-default', 'existing-tier'].includes(env.ELANOUS_POD_MEMORY_REASON ?? '')
|
|
810
|
+
? env.ELANOUS_POD_MEMORY_REASON!
|
|
811
|
+
: memorySource === 'goal-type-auto' ? `declared goal type ${selectedGoalType}` : memorySource;
|
|
812
|
+
debug.log('self-implement.pod', 'memory-limit', { spaceId: input.spaceId, goalType: requestKind, declaredGoalType: selectedGoalType, memoryLimit, tier: memoryTier, source: memorySource, reason: memoryReason,
|
|
755
813
|
...(memorySource.startsWith('parent-') ? { inheritedFrom } : {}), ...(parentGoalUnreadable ? { parentGoal: 'unreadable' } : {}) });
|
|
756
814
|
const childRunId = mintRunId();
|
|
757
815
|
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 +827,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
769
827
|
if (typeof intent.data.to !== 'string' || memoryGi(intent.data.to) === null) throw new Error('invalid OOM retry memory limit');
|
|
770
828
|
oomRetried = true;
|
|
771
829
|
memoryLimit = intent.data.to;
|
|
830
|
+
retryTier = parsePodMemoryTier(typeof intent.data.tier === 'string' ? intent.data.tier : undefined)
|
|
831
|
+
?? ((memoryGi(intent.data.to) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? 'high' : 'standard');
|
|
772
832
|
retryFromChildRunId = typeof intent.data.fromChildRunId === 'string' ? intent.data.fromChildRunId : undefined;
|
|
773
833
|
retryFromMemoryLimit = typeof intent.data.from === 'string' ? intent.data.from : undefined;
|
|
774
834
|
}
|
|
@@ -783,7 +843,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
783
843
|
...(input.draft === false ? ['--no-draft'] : []),
|
|
784
844
|
...(hostSupervised ? ['--no-supervise'] : []),
|
|
785
845
|
...(options.extraArgs ?? []),
|
|
786
|
-
...(options
|
|
846
|
+
...podChildLlmArgs(options),
|
|
787
847
|
];
|
|
788
848
|
debug.log('pod.self-implement', 'child-supervise', { hostSupervised, childSupervise: !hostSupervised });
|
|
789
849
|
const done = (async (): Promise<SelfImplementJobDone> => {
|
|
@@ -800,7 +860,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
800
860
|
if (!options.pool) return null;
|
|
801
861
|
for (;;) {
|
|
802
862
|
if (input.signal?.aborted) return null;
|
|
803
|
-
const acquired = options.pool.tryAcquire();
|
|
863
|
+
const acquired = await options.pool.tryAcquire();
|
|
804
864
|
if (acquired) {
|
|
805
865
|
debug.log('self-implement.pod', 'pool-slot', { spaceId: input.spaceId, context: acquired.context, inflight: options.pool.snapshot() });
|
|
806
866
|
return acquired;
|
|
@@ -868,6 +928,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
868
928
|
let retrySamples: ReturnType<typeof parseMemSamples> = [];
|
|
869
929
|
let failedAccount: string | undefined;
|
|
870
930
|
let usageLimitRetried = false;
|
|
931
|
+
let ghLoginRetried = false;
|
|
871
932
|
for (;;) {
|
|
872
933
|
try {
|
|
873
934
|
const grok = options.provider === 'grok';
|
|
@@ -930,10 +991,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
930
991
|
debug.log('self-implement.pod', 'credential-relay-skipped', { reason: 'no-host-url' });
|
|
931
992
|
}
|
|
932
993
|
const now = options.now ?? Date.now;
|
|
933
|
-
const issueVerifiedAppCredential = async (): Promise<{ token: string; expiresAt: number } | null> => {
|
|
994
|
+
const issueVerifiedAppCredential = async (fresh = false): Promise<{ token: string; expiresAt: number } | null> => {
|
|
934
995
|
if (!repository) return null;
|
|
935
996
|
// This is the same repository-scoped, contents+pull_requests installation request on launch and refresh.
|
|
936
|
-
const candidate = (options.githubInstallation ?? ((repo) =>
|
|
997
|
+
const candidate = (options.githubInstallation ?? ((repo, o) => coalescedInstallationCredential({ repository: repo.split('/')[1]! }, { fresh: o?.fresh })))(repository, { fresh });
|
|
937
998
|
if (!candidate?.token) return null;
|
|
938
999
|
const expiry = candidate.expires_at ?? ('expiresAt' in candidate ? candidate.expiresAt : undefined);
|
|
939
1000
|
const expiresAt = typeof expiry === 'number' ? expiry : Date.parse(expiry ?? '');
|
|
@@ -943,8 +1004,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
943
1004
|
? { token: candidate.token, expiresAt } : null;
|
|
944
1005
|
};
|
|
945
1006
|
let appCredential: Awaited<ReturnType<typeof issueVerifiedAppCredential>> = null;
|
|
946
|
-
try { appCredential = await issueVerifiedAppCredential(); }
|
|
1007
|
+
try { appCredential = await issueVerifiedAppCredential(ghLoginRetried); }
|
|
947
1008
|
catch { debug.log('self-implement.pod', 'github-app-unavailable', { job: name, reason: 'mint-or-verification-failed' }); }
|
|
1009
|
+
// PODCRED1: a short token is still shipped — the host renews it in the Pod on the first live poll — but it is
|
|
1010
|
+
// observed, so an exit-7 burst can be checked against the remaining life the Pods actually started with.
|
|
1011
|
+
if (appCredential && appCredential.expiresAt - now() < POD_APP_TOKEN_MIN_START_MS) {
|
|
1012
|
+
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 });
|
|
1013
|
+
}
|
|
948
1014
|
const githubRelay = appCredential && groundingUrl && repository
|
|
949
1015
|
? await mint({ runId: groundingRunId, job: name, ttlMs: groundingTtlMs, scope: 'gh-credential', repository })
|
|
950
1016
|
: null;
|
|
@@ -1042,7 +1108,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1042
1108
|
// 🩸 종전엔 여기서 delete 부터 해서, 재개가 원격에서 멀쩡히 돌던 Job 을 죽였다. 실패로 끝난 Job 만 지우고 다시 만든다.
|
|
1043
1109
|
// ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 대표 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
|
|
1044
1110
|
const jobImage = member?.imageRef ?? image;
|
|
1045
|
-
const launchRunId = retryAccount || oomRetried ? mintRunId() : childRunId;
|
|
1111
|
+
const launchRunId = retryAccount || oomRetried || ghLoginRetried ? mintRunId() : childRunId;
|
|
1046
1112
|
let liveChildRunId = launchRunId;
|
|
1047
1113
|
let reattachedMemoryLimit: string | undefined;
|
|
1048
1114
|
let existingExecutionKey: string | undefined;
|
|
@@ -1083,7 +1149,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1083
1149
|
}
|
|
1084
1150
|
if (failedExisting) {
|
|
1085
1151
|
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
|
|
1152
|
+
const latestPod = pods.status === 0 ? pods.stdout.trimEnd().split('\n').map((line) => line.split('\t'))
|
|
1153
|
+
.filter(([created]) => created && !Number.isNaN(Date.parse(created)))
|
|
1154
|
+
.sort((a, b) => Date.parse(b[0]!) - Date.parse(a[0]!))[0] : undefined;
|
|
1155
|
+
const failedOom = latestPod?.[1]?.trim() === 'OOMKilled' && latestPod[2]?.trim() === '137';
|
|
1087
1156
|
// Once attempt 2 exists, consume its terminal result even if it failed for a non-OOM reason.
|
|
1088
1157
|
// Only the old, lower-memory attempt may be removed to make room for high.
|
|
1089
1158
|
if (failedOom || oomRetried) reattach = !oomRetried || (memoryGi(reattachedMemoryLimit ?? '') ?? 0) >= (memoryGi(memoryLimit) ?? Infinity);
|
|
@@ -1096,21 +1165,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1096
1165
|
debug.log('self-implement.pod', 'job-reattach', { job: name, conditions: existingConditions.join(' ') || 'running', spaceId: input.spaceId });
|
|
1097
1166
|
}
|
|
1098
1167
|
if (!hostRefresh && ghTokenExpiresAt === null && !githubRelay) debug.log('self-implement.pod', 'gh-token-app-skipped', { job: name, reason: 'no-verified-app-or-refresh-path' });
|
|
1168
|
+
let appliedMemoryRequest: string | undefined;
|
|
1099
1169
|
if (!reattach) {
|
|
1100
1170
|
if (oomRetried && hasExistingJob && !failedExisting && memoryGi(reattachedMemoryLimit ?? '') === null) {
|
|
1101
1171
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1102
1172
|
}
|
|
1103
1173
|
if (oomRetried && !retryAccount) {
|
|
1104
|
-
const
|
|
1105
|
-
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(
|
|
1174
|
+
const retryLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: retryTier }).limit;
|
|
1175
|
+
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(retryLimit) ?? Infinity)) throw new Error(`pod: OOM 재시도 한도가 ${retryTier} 와 다름: ${memoryLimit}`);
|
|
1106
1176
|
const leaseMembers = member ? [member] : [{ context, capacity: 1, k3dCluster: '' }];
|
|
1107
1177
|
const lease = recommendConcurrency(measurePoolLease(leaseMembers, { kubectl: (args) => baseKubectl(args) }), {
|
|
1108
|
-
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory:
|
|
1178
|
+
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory: retryLimit, accounts: 0, perAccount: 0,
|
|
1109
1179
|
});
|
|
1110
1180
|
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 재시도
|
|
1181
|
+
return { exitCode: 1, output: '', error: { code: 'pod-lease', message: `OOM 재시도 ${retryTier} 자리 판정 실패: ${lease.reason ?? lease.limitedBy ?? 'no capacity'}\n${formatLastMemSample(retrySamples.at(-1))}` } };
|
|
1112
1182
|
}
|
|
1113
|
-
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit:
|
|
1183
|
+
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit: retryLimit, tier: retryTier, recommended: lease.recommended });
|
|
1114
1184
|
}
|
|
1115
1185
|
if (failedExisting && !reattach && !oomRetried && collectFullLogs('failed-job-logs-unavailable').status === 'incomplete') {
|
|
1116
1186
|
return { exitCode: 1, output: '', error: { code: 'pod-collection-incomplete', message: `Job ${name} recovery incomplete — old Job retained` } };
|
|
@@ -1146,7 +1216,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1146
1216
|
const measuredRequest = podMemoryRequestFor(requestKind, advice);
|
|
1147
1217
|
const requestGi = memoryGi(measuredRequest);
|
|
1148
1218
|
const limitGi = memoryGi(memoryLimit);
|
|
1149
|
-
const
|
|
1219
|
+
const documentDefaultLite = !oomRetried && selectedGoalType === 'document' && memoryTier === 'lite'
|
|
1220
|
+
&& (memorySource === 'goal-type-auto' || memoryReason === 'goal-type-default');
|
|
1221
|
+
const memoryRequest = documentDefaultLite ? memoryLimit
|
|
1222
|
+
: requestGi !== null && limitGi !== null && requestGi > limitGi ? memoryLimit : measuredRequest;
|
|
1223
|
+
appliedMemoryRequest = memoryRequest;
|
|
1224
|
+
debug.log('self-implement.pod', 'memory-request', { spaceId: input.spaceId, job: name,
|
|
1225
|
+
tier: oomRetried ? retryTier : memoryTier, reason: oomRetried ? 'OOMKilled' : memoryReason,
|
|
1226
|
+
memoryLimit, memoryRequest });
|
|
1150
1227
|
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 } : {}), 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
1228
|
const a = kubectl(['apply', '-f', '-'], JSON.stringify(job));
|
|
1152
1229
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
@@ -1183,7 +1260,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1183
1260
|
}
|
|
1184
1261
|
try {
|
|
1185
1262
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-run', data: { childRunId: liveChildRunId, job: name, attempt: oomRetried ? 2 : 1, ...(goalFile ? { goalFile } : {}) } }, ledgerDir);
|
|
1186
|
-
|
|
1263
|
+
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);
|
|
1264
|
+
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
1265
|
} catch (error) {
|
|
1188
1266
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1189
1267
|
debug.log('self-implement.pod', 'parent-ledger-unavailable', { job: name, reason, attempt: oomRetried ? 2 : 1 });
|
|
@@ -1293,6 +1371,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1293
1371
|
const parsed = parseSelfImplementJson(logs);
|
|
1294
1372
|
const terminal = parseSelfImplementJson(logs.trimEnd().split('\n').at(-1) ?? '');
|
|
1295
1373
|
const nextAccount = codexAccounts?.find((candidate) => candidate !== account);
|
|
1374
|
+
if (state === 'failed' && containerExitCode === 7 && !input.signal?.aborted) {
|
|
1375
|
+
debug.log('self-implement.pod', 'gh-login-failed', { job: name, attempt: ghLoginRetried ? 2 : 1, tokenExpiresInSec: ghTokenExpiresAt === null ? null : Math.floor((ghTokenExpiresAt - now()) / 1000) });
|
|
1376
|
+
if (!ghLoginRetried) {
|
|
1377
|
+
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1378
|
+
if (deleted.status !== 0) throw new Error(`pod: GitHub 로그인 실패 Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1379
|
+
// Bursts came from simultaneous launches (10-05 12:49Z · 5 deaths in 2 s) — wait, spread by job name, then
|
|
1380
|
+
// retry once with a newly issued token so the retries do not collide again.
|
|
1381
|
+
const backoffMs = podGhLoginRetryBackoffMs(name);
|
|
1382
|
+
debug.log('self-implement.pod', 'gh-login-retry-backoff', { job: name, backoffMs });
|
|
1383
|
+
await sleep(backoffMs);
|
|
1384
|
+
if (input.signal?.aborted) return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before GitHub login retry' } };
|
|
1385
|
+
ghLoginRetried = true;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1296
1389
|
if (!usageLimitRetried && !input.signal?.aborted && state !== 'aborted' && terminal?.ok === false
|
|
1297
1390
|
&& /\b429\b/.test(terminal.error ?? '') && /\busage_limit_reached\b/.test(terminal.error ?? '') && nextAccount) {
|
|
1298
1391
|
debug.log('self-implement.pod', 'usage-limit-retry', { job: name, spaceId: input.spaceId, from: account, to: nextAccount });
|
|
@@ -1316,7 +1409,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1316
1409
|
try {
|
|
1317
1410
|
const hostRoot = findGitDir(process.cwd())?.root;
|
|
1318
1411
|
if (!hostRoot) throw new Error('host repository unavailable');
|
|
1319
|
-
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot });
|
|
1412
|
+
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot, ...(goalFile ? { goalFile } : {}) });
|
|
1320
1413
|
} catch (error) {
|
|
1321
1414
|
debug.log('harness.host-regate', 'unmeasured', { pr: prNumber, files: [], os: process.platform, failedStep: 'host-regate' });
|
|
1322
1415
|
regate = { passed: false, failures: [{ step: 'host-regate', detail: error instanceof Error ? error.message : String(error) }], os: process.platform };
|
|
@@ -1335,7 +1428,16 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1335
1428
|
}
|
|
1336
1429
|
const childFailure = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1337
1430
|
? lastPodChildFailure(logs) : null;
|
|
1431
|
+
let noResultDiagnostic: string | null = null;
|
|
1432
|
+
if (state === 'failed' && containerExitCode !== null && containerExitCode !== 0 && !disposition && !oomKilled && failedReason !== 'DeadlineExceeded' && failedReason !== 'BackoffLimitExceeded') {
|
|
1433
|
+
try { noResultDiagnostic = podNoResultDiagnostic(readFileSync(runLedgerPath(liveChildRunId, ledgerDir), 'utf8')); }
|
|
1434
|
+
catch { /* ledger not returned: do not invent a stage */ }
|
|
1435
|
+
noResultDiagnostic ??= 'child terminal result missing; last ledger stage=unknown; round=unknown; mustFix=unknown';
|
|
1436
|
+
}
|
|
1338
1437
|
if (childFailure) debug.log('self-implement.pod', 'child-error', { job: name, stage: childFailure.stage, error: childFailure.error }, { compact: { stringMax: 500 } });
|
|
1438
|
+
if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
|
|
1439
|
+
debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
|
|
1440
|
+
}
|
|
1339
1441
|
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
1442
|
...(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1341
1443
|
? { ...(childFailure ? { childStage: childFailure.stage } : {}), childError: childFailure?.error ?? 'no-result-line' } : {}) },
|
|
@@ -1344,22 +1446,25 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1344
1446
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1345
1447
|
}
|
|
1346
1448
|
if (oomKilled && failedReason !== 'DeadlineExceeded' && !input.signal?.aborted && !oomRetried) {
|
|
1347
|
-
const high = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit;
|
|
1348
1449
|
const actual = memoryGi(reattachedMemoryLimit ?? memoryLimit);
|
|
1450
|
+
const standardLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'standard' }).limit;
|
|
1451
|
+
const nextTier: PodMemoryTier = actual !== null && actual < (memoryGi(standardLimit) ?? 0) ? 'standard' : 'high';
|
|
1452
|
+
const nextLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: nextTier }).limit;
|
|
1349
1453
|
if (actual === null) {
|
|
1350
1454
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 판정할 수 없다: ${memoryLimit}` } };
|
|
1351
1455
|
}
|
|
1352
|
-
if (actual < (memoryGi(
|
|
1456
|
+
if (actual < (memoryGi(nextLimit) ?? actual)) {
|
|
1353
1457
|
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:
|
|
1458
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-intent', data: { attempt: 2, from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name, executionKey, fromChildRunId: liveChildRunId } }, ledgerDir);
|
|
1459
|
+
debug.log('self-implement.pod', 'oom-retry', { from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name });
|
|
1356
1460
|
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1357
1461
|
if (deleted.status !== 0) throw new Error(`pod: OOM Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1358
1462
|
oomRetried = true;
|
|
1359
1463
|
retryFromChildRunId = liveChildRunId;
|
|
1360
1464
|
retryFromMemoryLimit = from;
|
|
1361
1465
|
retrySamples = samples;
|
|
1362
|
-
memoryLimit =
|
|
1466
|
+
memoryLimit = nextLimit;
|
|
1467
|
+
retryTier = nextTier;
|
|
1363
1468
|
if (member) { options.pool!.release(member); member = null; }
|
|
1364
1469
|
releaseAdmission?.();
|
|
1365
1470
|
releaseAdmission = undefined;
|
|
@@ -1378,7 +1483,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1378
1483
|
const record: GoalExecutionRecord = {
|
|
1379
1484
|
runId: liveChildRunId,
|
|
1380
1485
|
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'],
|
|
1486
|
+
outcome: (!disposition ? noResultDiagnostic ? 'abandoned' : 'pod-no-result' : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
1382
1487
|
ok: state === 'complete' && (disposition?.ok ?? false),
|
|
1383
1488
|
...(disposition?.prNumber !== undefined ? { prNumber: disposition.prNumber } : {}),
|
|
1384
1489
|
completedAt: new Date().toISOString(),
|
|
@@ -1402,16 +1507,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1402
1507
|
const finishedDisposition = deadlineExceeded && disposition
|
|
1403
1508
|
? { ...disposition, failureClassification: 'run-deadline-exceeded' as const }
|
|
1404
1509
|
: disposition;
|
|
1510
|
+
const podExitCode = state === 'complete' && disposition?.stage !== 'host-regate-failed' && disposition?.ok !== false ? 0 : 1;
|
|
1511
|
+
// SCHED-CONTRACT: a failed child must say why outside the Pod, not «no error diagnostic».
|
|
1512
|
+
const podReason = podExitCode === 1 ? extractPodFailureReason({ logs, logTailReason, containerReason, jobReason: failedReason || undefined, deadlineSeconds }) : undefined;
|
|
1513
|
+
if (podReason) debug.log('self-implement.pod', 'failure-reason', { job: name, reason: podReason });
|
|
1405
1514
|
return {
|
|
1406
|
-
exitCode:
|
|
1515
|
+
exitCode: podExitCode,
|
|
1407
1516
|
output: tail,
|
|
1408
1517
|
...(deadlineExceeded
|
|
1409
1518
|
? { error: { code: 'pod-deadline-exceeded', message: `Job ${name} 이 수명 상한 ${deadlineSeconds}초에 닿았다` } }
|
|
1410
1519
|
: state === 'failed'
|
|
1411
|
-
?
|
|
1412
|
-
? { error: { code: 'pod-
|
|
1413
|
-
|
|
1414
|
-
|
|
1520
|
+
? containerExitCode === 7
|
|
1521
|
+
? { error: { code: 'pod-gh-login-failed', message: `Job ${name} GitHub 앱 로그인 실패 (container=${containerReason ?? 'unknown'}/7, attempt=${ghLoginRetried ? 2 : 1}) — 새 토큰으로 ${ghLoginRetried ? '1회 재시도했으나 다시 실패' : '재시도 불가'}` } }
|
|
1522
|
+
: oomKilled
|
|
1523
|
+
? { 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))}` } }
|
|
1524
|
+
: { 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}` : ''}` } }
|
|
1525
|
+
: podReason && !childFailure?.error ? { error: { code: 'pod-child-failed', message: podReason } } : {}),
|
|
1415
1526
|
...(finishedDisposition ? { disposition: finishedDisposition } : {}),
|
|
1416
1527
|
};
|
|
1417
1528
|
} catch (err) {
|
|
@@ -49,6 +49,7 @@ export interface SelfImplementDisposition {
|
|
|
49
49
|
ok?: boolean;
|
|
50
50
|
worktreePath?: string;
|
|
51
51
|
branch?: string;
|
|
52
|
+
harvestable?: true;
|
|
52
53
|
prUrl?: string;
|
|
53
54
|
prNumber?: number;
|
|
54
55
|
/** Head SHA checked by the Pod before host regate. */
|
|
@@ -84,6 +85,7 @@ export interface SelfImplementDisposition {
|
|
|
84
85
|
reviewVerdict?: 'pass' | 'warn' | 'fail';
|
|
85
86
|
/** 마지막 리뷰의 must-fix 수(`review.mustFix.length`). 같은 조건. */
|
|
86
87
|
reviewMustFixCount?: number;
|
|
88
|
+
mustFix?: string[];
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
/**
|
|
@@ -265,7 +267,7 @@ export function createSelfImplementAdapter(opts: SelfImplementAdapterOptions) {
|
|
|
265
267
|
: r.output.length === 0
|
|
266
268
|
? `${exitMessage} (child produced no output)`
|
|
267
269
|
: r.disposition
|
|
268
|
-
? `${exitMessage} (child
|
|
270
|
+
? `${exitMessage} (child stage=${r.disposition.stage ?? 'unknown'}; ${r.disposition.detail ?? `mustFix=${JSON.stringify(r.disposition.mustFix ?? 'unknown')}`})`
|
|
269
271
|
: `${exitMessage} (child output did not contain a parseable terminal JSON diagnostic)`;
|
|
270
272
|
return {
|
|
271
273
|
...exec,
|
|
@@ -461,6 +463,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
461
463
|
...(typeof o.ok === 'boolean' ? { ok: o.ok } : {}),
|
|
462
464
|
...(typeof o.worktreePath === 'string' ? { worktreePath: o.worktreePath } : {}),
|
|
463
465
|
...(typeof o.branch === 'string' ? { branch: o.branch } : {}),
|
|
466
|
+
...(o.harvestable === true && typeof o.branch === 'string' && o.branch.trim() ? { harvestable: true } : {}),
|
|
464
467
|
...(typeof o.prUrl === 'string' ? { prUrl: o.prUrl } : {}),
|
|
465
468
|
...(typeof o.prNumber === 'number' ? { prNumber: o.prNumber } : {}),
|
|
466
469
|
...(typeof o.checkedHeadCommit === 'string' ? { checkedHeadCommit: o.checkedHeadCommit } : {}),
|
|
@@ -483,6 +486,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
|
|
|
483
486
|
? { providerErrors: o.providerErrors as SelfImplementDisposition['providerErrors'] } : {}),
|
|
484
487
|
...(typeof o.runId === 'string' && o.runId.trim() ? { runId: o.runId, childRunId: o.runId } : {}),
|
|
485
488
|
...reviewGateFields(o),
|
|
489
|
+
...(Array.isArray(o.mustFix) && o.mustFix.every((item) => typeof item === 'string') ? { mustFix: o.mustFix as string[] } : {}),
|
|
486
490
|
};
|
|
487
491
|
reportUnmappedDispositionFields(o, mapped);
|
|
488
492
|
return mapped;
|
package/src/telegram.ts
CHANGED
|
@@ -66,7 +66,9 @@ import { detectUrlRoute, type UrlRouteDecision } from './skills/url-router.js';
|
|
|
66
66
|
import { observeDevRequestRouteFailSoft } from './skills/dev-request-router.js';
|
|
67
67
|
import { runUrlRoute } from './skills/url-route-exec.js';
|
|
68
68
|
import { recordRoutedLinkAbsorbed } from './intake-plane/link-ledger.js';
|
|
69
|
-
import { classifyFrontInput } from './intake-plane/front-classifier.js';
|
|
69
|
+
import { classifyFrontInput, telegramAbsorbUrls } from './intake-plane/front-classifier.js';
|
|
70
|
+
import { scheduleIntakeEvent } from './intake-plane/route.js';
|
|
71
|
+
import { dispatchTaskCreate } from './task-orchestrator/runtimes/create.js';
|
|
70
72
|
import { handleTelegramSeatWork, type TelegramSeatWorkDeps } from './intake-plane/telegram-seat-work.js';
|
|
71
73
|
import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
|
|
72
74
|
import { effectiveInstanceRoot } from './instance/resolve.js';
|
|
@@ -1919,11 +1921,30 @@ export class TelegramBot {
|
|
|
1919
1921
|
? getActiveDelegation(delegationChatKey(this.botId, ctx.chatId, ctx.threadId))
|
|
1920
1922
|
: null;
|
|
1921
1923
|
if (!activeDeleg) {
|
|
1922
|
-
try {
|
|
1923
|
-
classifyFrontInput({ text: ctx.text, surface: 'telegram' }, { urlRouting: getUserConfig().skills.urlRouting });
|
|
1924
|
-
} catch { /* observation must not interrupt URL routing */ }
|
|
1925
1924
|
const urlDec = detectUrlRoute(ctx.text, getUserConfig().skills.urlRouting);
|
|
1926
1925
|
if (urlDec) {
|
|
1926
|
+
try {
|
|
1927
|
+
const frontInput = { text: ctx.text, surface: 'telegram' as const };
|
|
1928
|
+
const decision = classifyFrontInput(frontInput, { urlRouting: getUserConfig().skills.urlRouting });
|
|
1929
|
+
const absorbUrls = telegramAbsorbUrls(frontInput, decision);
|
|
1930
|
+
for (const url of absorbUrls) {
|
|
1931
|
+
const result = await scheduleIntakeEvent(effectiveInstanceRoot(), url, {
|
|
1932
|
+
eventMax: getUserConfig().intake.eventMax,
|
|
1933
|
+
post: async ({ url: link }) => {
|
|
1934
|
+
const posted = await dispatchTaskCreate({
|
|
1935
|
+
title: `흡수: ${link}`.slice(0, 80),
|
|
1936
|
+
surface: { kind: 'llm-direct', prompt: `이 링크를 흡수하고 노트로 저장하세요: ${link}` },
|
|
1937
|
+
});
|
|
1938
|
+
if (!posted.taskId) throw new Error('intake event task not created');
|
|
1939
|
+
return { taskId: posted.taskId };
|
|
1940
|
+
},
|
|
1941
|
+
});
|
|
1942
|
+
if (result.reply) await this.sendMessage(ctx.chatId, result.reply, { threadId: ctx.threadId, markdown: true });
|
|
1943
|
+
}
|
|
1944
|
+
if (absorbUrls.length) return;
|
|
1945
|
+
} catch (error) {
|
|
1946
|
+
debug.log('intake.event', 'failed', { reason: error instanceof Error ? error.name : 'unknown' });
|
|
1947
|
+
}
|
|
1927
1948
|
const handled = await this.runUrlRouteReply(ctx, urlDec);
|
|
1928
1949
|
if (handled) return;
|
|
1929
1950
|
}
|