elanous 0.2.12 → 0.2.14
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/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-6e7e0377c1db0bdc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9906-482c6619b3fdda20.js → 8060-cc008cc6fedfa818.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-7b2b4f378354869a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-1512d91c7491ed62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-a627db94ec6904a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-fed69f9759d248d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-a67e11ba962337b5.js → page-15374909d8f1aebd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-012a742f81218b01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-99effd9d893a43dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-938de57ae0315e46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-629e1ede482ec6eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-793fa536b3a6ef37.js → page-2ce5471926761fdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-27e99b4e54a849d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-32cde713be47cddb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e64caff084f7ce21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-db07eaffba0df7e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-86f91034bd5cabe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-ce4d8a1215ce53a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-e297866051595d81.js → page-274703bc41b71333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3b7e821dc492bdfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2f810b18d89ecba6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +1 -0
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -0
- package/apps/pwa/out/loops/index.txt +30 -0
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/examples/teams/personas/backend.yaml +4 -0
- package/examples/teams/personas/pm.yaml +4 -0
- package/examples/teams/personas/qa.yaml +4 -0
- package/examples/teams/small-product.yaml +20 -0
- package/graphs/lecture/lecture-note.yaml +28 -0
- package/graphs/lecture/recipes.yaml +17 -0
- package/graphs/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/rhythm/daily.yaml +38 -0
- package/graphs/rhythm/recipes.yaml +18 -0
- package/graphs/rhythm/weekly.yaml +39 -0
- package/graphs/video/recipes.yaml +3 -0
- package/graphs/video/youtube-summary.yaml +17 -0
- package/package.json +2 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +186 -0
- package/scripts/context-hooks/emit.ts +36 -14
- package/scripts/coord-channel-watch.sh +1 -0
- package/scripts/coord-post.sh +11 -1
- package/scripts/discord-seat-probe.ts +144 -0
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +163 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/hq-standby.sh +65 -0
- package/scripts/hq/standby-promote.ts +173 -0
- package/scripts/hq/standby-role.ts +59 -0
- package/scripts/hq/standby-snapshot.ts +226 -0
- package/scripts/hq/standby-verify.ts +223 -0
- package/scripts/lecture-note/node.ts +256 -0
- package/scripts/lessons/handbook-page.ts +85 -0
- package/scripts/release-loop/gate-node.ts +62 -15
- package/scripts/release-loop/ops-upgrade-node.ts +17 -5
- package/scripts/release-loop/publish-node.ts +14 -1
- package/scripts/release-loop/unattended-release.ts +23 -1
- package/scripts/release-loop/vault-note-node.ts +196 -0
- package/scripts/release-story/draft.ts +172 -0
- package/scripts/rhythm/daily.ts +383 -0
- package/scripts/rhythm/weekly.ts +294 -0
- package/scripts/skills/public-check.ts +169 -0
- package/scripts/test-deterministic.ts +57 -11
- package/scripts/test-diet/effectiveness.ts +195 -0
- package/scripts/test-diet/lib.ts +16 -8
- package/scripts/test-diet/node.ts +77 -9
- package/scripts/ux/demo-rehearsal.ts +2 -2
- package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
- package/scripts/video/youtube-summary.ts +133 -0
- package/skills/diagram-master/SKILL.md +42 -49
- package/skills/diagram-master/references/pyproject.toml +4 -1
- package/skills/diagram-master/references/routing-guide.md +4 -2
- package/skills/grill-me/SKILL.md +1 -0
- package/skills/omni-crawl/SKILL.md +16 -16
- package/skills/omni-crawl/package-lock.json +1 -544
- package/skills/omni-crawl/package.json +3 -4
- package/skills/omni-crawl/scripts/main.ts +13 -13
- package/skills/omni-crawl/scripts/monitor.ts +8 -8
- package/skills/omni-crawl/src/apify.ts +13 -2
- package/skills/omni-crawl/src/capture.ts +6 -0
- package/skills/omni-crawl/src/registry.ts +2 -2
- package/skills/omni-crawl/src/stdout-json.ts +4 -0
- package/skills/omni-digest/SKILL.md +10 -11
- package/skills/omni-digest/package-lock.json +0 -545
- package/skills/omni-digest/package.json +2 -5
- package/skills/omni-digest/scripts/main.ts +3 -3
- package/skills/omni-digest/src/delegate.ts +5 -5
- package/skills/omni-digest/src/fetch.ts +2 -2
- package/skills/omni-digest/src/media.ts +25 -11
- package/skills/omni-digest/src/visual.ts +3 -2
- package/skills/omni-market/scripts/main.ts +1 -1
- package/skills/omni-market/scripts/stdout-json.ts +4 -0
- package/skills/youtube-master/SKILL.md +13 -13
- package/skills/youtube-master/package-lock.json +0 -545
- package/skills/youtube-master/package.json +2 -5
- package/skills/youtube-master/scripts/main.ts +2 -2
- package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
- package/skills/youtube-master/src/util.ts +30 -7
- package/src/agent/identity-env.ts +11 -0
- package/src/agent-mission/driver.ts +15 -10
- package/src/agent-mission/duo.ts +94 -0
- package/src/agent-mission/pty-decision.ts +1 -1
- package/src/autopilot/build/llm-conflict-merge.ts +45 -8
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +4 -1
- package/src/boot/daemon-prompt-request.ts +3 -0
- package/src/boot/daemon-prompt-turn.ts +6 -2
- package/src/boot/daemon-runtime.ts +48 -16
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +157 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +84 -20
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- package/src/cli/lesson-cli.ts +108 -0
- package/src/cli/logs-cli.ts +104 -2
- package/src/cli/logs-model-input.ts +131 -0
- package/src/cli/outputs-cli.ts +92 -0
- package/src/cli/persona-cli.ts +29 -0
- package/src/cli/pod-cli.ts +10 -3
- package/src/cli/pr-cli.ts +44 -2
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +248 -45
- package/src/cli/rfc-status.ts +91 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +189 -0
- package/src/context-bus/context-day.ts +82 -0
- package/src/context-bus/context-now-public.ts +86 -0
- package/src/context-bus/context-now-surfaces.ts +70 -4
- package/src/context-bus/context-now.ts +4 -3
- package/src/context-bus/long-term-memory.ts +201 -24
- package/src/context-bus/nightly-condense.ts +1 -1
- package/src/dashboard/index.ts +75 -22
- package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
- package/src/dashboard/slash-runtime/loops-table.ts +77 -0
- package/src/decisions/decision-cards.ts +13 -7
- package/src/decisions/decision-ledger.ts +83 -18
- package/src/discord/sprint21-runtime.ts +16 -0
- package/src/discord-self-message.ts +8 -0
- package/src/discord-slash-wire.ts +31 -2
- package/src/domains/outbound-alert.ts +1 -1
- package/src/elanous-config-dir.ts +29 -4
- package/src/execution-loop/launch-gate.ts +29 -14
- package/src/flow/cards.ts +239 -0
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/graph-approval-card.ts +2 -2
- package/src/graph-runner/graph-grow.ts +64 -11
- package/src/graph-runner/graph-tick.ts +13 -2
- package/src/graph-runner/runner.ts +111 -22
- package/src/harness/harness-cli-command.ts +178 -27
- package/src/harness/harness-incidents.ts +43 -12
- package/src/harness/harness-pod-dispatch.ts +2 -0
- package/src/harness/harness-queue-child.ts +37 -0
- package/src/harness/harness-queue.ts +494 -0
- package/src/harness/helper-scan.ts +139 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/hooks/github-shadow.ts +72 -2
- package/src/hooks/receiver.ts +4 -2
- package/src/hq/hq.ts +318 -0
- package/src/hq/lease.ts +210 -0
- package/src/hq/ledger-write-target.ts +41 -0
- package/src/hq/promote.ts +301 -0
- package/src/index.ts +281 -27
- package/src/intake-plane/discord-seat-work.ts +13 -6
- package/src/intake-plane/linear-wish.ts +72 -0
- package/src/intake-plane/persona-answer.ts +5 -0
- package/src/intake-plane/telegram-seat-work.ts +13 -6
- package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
- package/src/intake-plane/wish-card.ts +53 -2
- package/src/intake-plane/wish-folder.ts +5 -2
- package/src/intake-plane/wish-reply.ts +72 -0
- package/src/lessons/lesson-import.ts +106 -0
- package/src/lessons/lesson-ledger.ts +206 -0
- package/src/loops/activity.ts +203 -0
- package/src/loops/budget.ts +32 -0
- package/src/loops/checker.ts +180 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +72 -6
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +59 -0
- package/src/loops/orchestrator/author-depth.ts +164 -0
- package/src/loops/orchestrator/author-input-check.ts +77 -0
- package/src/loops/orchestrator/author-ledger.ts +230 -0
- package/src/loops/orchestrator/finish-rate.ts +175 -0
- package/src/loops/orchestrator/overlap.ts +180 -0
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/tick.ts +302 -0
- package/src/loops/orchestrator/traffic.ts +165 -0
- package/src/loops/package/verify.ts +125 -0
- package/src/loops/registry.ts +454 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +34 -0
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/memory.ts +37 -4
- package/src/nexus/api/card-followup-route.ts +118 -0
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/decisions-route.ts +68 -0
- package/src/nexus/api/http-server.ts +65 -2
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/personas.ts +2 -1
- package/src/nexus/api/schedules-read.ts +5 -1
- package/src/nexus/api/skill-exec-route.ts +97 -0
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/persona/loader.ts +18 -0
- package/src/persona/persona-todo.ts +14 -9
- package/src/persona/presets.ts +2 -1
- package/src/persona/team.ts +105 -0
- package/src/persona/types.ts +4 -0
- package/src/pod-lease/host-lease.ts +130 -20
- package/src/release-loop/auto-start.ts +48 -0
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +28 -16
- package/src/release-loop/landed-but-yellow.ts +12 -0
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/placement.ts +242 -0
- package/src/release-loop/release-schedule.ts +3 -1
- package/src/seat-dispatch/ceo-commands.ts +2 -2
- package/src/seat-dispatch/seat-ask.ts +73 -15
- package/src/seat-dispatch/seat-questions.ts +58 -0
- package/src/seat-loop/seat-loop.ts +495 -46
- package/src/seat-loop/seat-pack.ts +127 -0
- package/src/self-dev/ask-launch-flow.ts +3 -0
- package/src/self-dev/dev-pipeline.ts +6 -2
- package/src/self-dev/draft-sweep.ts +76 -0
- package/src/self-dev/run-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +8 -0
- package/src/self-dev/self-orchestrate-runtime.ts +5 -1
- package/src/self-implement/budget-gate.ts +29 -0
- package/src/self-implement/expose-gate.ts +47 -0
- package/src/self-implement/frozen-merges.ts +294 -0
- package/src/self-implement/gate-cli.ts +10 -4
- package/src/self-implement/gate-scope.ts +46 -9
- package/src/self-implement/goal-archive.ts +222 -0
- package/src/self-implement/goal-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +79 -0
- package/src/self-implement/host-regate.ts +25 -2
- package/src/self-implement/l8-merge-queue.ts +157 -1
- package/src/self-implement/orchestrator.ts +114 -10
- package/src/self-implement/revert-guard.ts +62 -0
- package/src/self-implement/run-ledger.ts +1 -1
- package/src/self-implement/seams.ts +28 -8
- package/src/session/index.ts +2 -0
- package/src/steward/launch.ts +5 -1
- package/src/steward/readiness.ts +125 -0
- package/src/steward/rescue.ts +75 -0
- package/src/steward/triage.ts +48 -9
- package/src/task-cards/card-cli.ts +86 -0
- package/src/task-cards/card-store.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-lease.ts +23 -8
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +112 -18
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-dispatch-forward.ts +1 -1
- package/src/telegram-fabric-plan.ts +90 -0
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram-project-command.ts +10 -4
- package/src/telegram.ts +5 -0
- package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
- package/src/user-config.ts +204 -16
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +4 -0
- package/src/workflow-runtime/node-catalog.ts +13 -0
- package/src/workflow-runtime/nodes/knowledge.ts +38 -0
- package/src/workflow-runtime/schema.ts +17 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/src/workflow-runtime/types.ts +13 -1
- package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5c05b0da4bc1392c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
- /package/apps/pwa/out/_next/static/{-6XrAoD3eU7NyMHAE4xPS → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
|
@@ -9,6 +9,32 @@ type Incomplete = { runId: string; reason: string };
|
|
|
9
9
|
type ParseResult = Ledger[] | { error: Incomplete[]; ledgers: Ledger[] };
|
|
10
10
|
|
|
11
11
|
const NON_PROGRESS_EVENTS = new Set(['progress-delivery-outcome']);
|
|
12
|
+
const REEMIT_EXCLUDED_PREFIX = 'progress-delivery';
|
|
13
|
+
type Emit = (category: string, event: string, data: Record<string, unknown>) => void;
|
|
14
|
+
|
|
15
|
+
/** Reemit only complete ledger records; offsets name the first byte of each line in the Pod ledger. */
|
|
16
|
+
function reemitLedgerLines(jsonl: string, runId: string, startOffset: number, emit: Emit): number {
|
|
17
|
+
let offset = startOffset;
|
|
18
|
+
let lines = 0;
|
|
19
|
+
for (const raw of jsonl.split('\n').slice(0, -1)) {
|
|
20
|
+
const podLedgerOffset = offset;
|
|
21
|
+
offset += Buffer.byteLength(raw) + 1;
|
|
22
|
+
try {
|
|
23
|
+
const entry: unknown = JSON.parse(raw);
|
|
24
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) continue;
|
|
25
|
+
const { category, event, data } = entry as Record<string, unknown>;
|
|
26
|
+
if (typeof event !== 'string' || !event || event.startsWith(REEMIT_EXCLUDED_PREFIX)
|
|
27
|
+
|| !data || typeof data !== 'object' || Array.isArray(data)) continue;
|
|
28
|
+
emit(typeof category === 'string' && category ? category : 'self-implement', event,
|
|
29
|
+
{ ...data, runId, origin: 'pod', podLedgerOffset });
|
|
30
|
+
lines += 1;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
try { debug.log('self-implement.pod', 'reemit-failed', { runId, reason: error instanceof Error ? error.message : String(error) }); }
|
|
33
|
+
catch { /* Host log failure must not interrupt the ledger append or collection. */ }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return lines;
|
|
37
|
+
}
|
|
12
38
|
|
|
13
39
|
/** Reassemble only complete, unambiguous transfers; retain good runs when another run is incomplete. */
|
|
14
40
|
export function parsePodLedgerChunks(logs: string): ParseResult {
|
|
@@ -68,9 +94,10 @@ export function parsePodLedgerChunks(logs: string): ParseResult {
|
|
|
68
94
|
* except a ledger that THIS run's live follower created (`replace`): that one is a partial copy and the final is complete. */
|
|
69
95
|
export function collectPodLedgers(
|
|
70
96
|
logs: string,
|
|
71
|
-
{ dir = runLedgerDir(), log = (c, e, d) => debug.log(c, e, d), replace = new Set<string>() }: {
|
|
97
|
+
{ dir = runLedgerDir(), log = (c, e, d) => debug.log(c, e, d), emit = (c, e, d) => debug.log(c, e, d), replace = new Set<string>() }: {
|
|
72
98
|
dir?: string;
|
|
73
99
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
100
|
+
emit?: Emit;
|
|
74
101
|
replace?: ReadonlySet<string>;
|
|
75
102
|
} = {},
|
|
76
103
|
): void {
|
|
@@ -82,7 +109,11 @@ export function collectPodLedgers(
|
|
|
82
109
|
for (const { runId, jsonl } of ledgers) {
|
|
83
110
|
try {
|
|
84
111
|
mkdirSync(dir, { recursive: true });
|
|
85
|
-
|
|
112
|
+
const path = runLedgerPath(runId, dir);
|
|
113
|
+
const previousBytes = replace.has(runId) && existsSync(path) ? readFileSync(path).length : 0;
|
|
114
|
+
writeFileSync(path, jsonl, { flag: replace.has(runId) ? 'w' : 'wx' });
|
|
115
|
+
const newLines = Buffer.from(jsonl).subarray(previousBytes).toString('utf8');
|
|
116
|
+
reemitLedgerLines(newLines, runId, previousBytes, emit);
|
|
86
117
|
log('self-implement.pod', 'ledger-collected', { runId, lines: jsonl.split('\n').filter(Boolean).length, bytes: Buffer.byteLength(jsonl) });
|
|
87
118
|
} catch (e) {
|
|
88
119
|
if ((e as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
@@ -109,12 +140,14 @@ export function createPodLedgerFollower(opts: {
|
|
|
109
140
|
exec: (script: string) => { status: number | null; stdout: string; stderr: string };
|
|
110
141
|
dir?: string;
|
|
111
142
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
143
|
+
emit?: Emit;
|
|
112
144
|
stallMinutes?: number;
|
|
113
145
|
now?: () => number;
|
|
114
146
|
onStall?: (message: string) => void;
|
|
115
147
|
}): { poll(): void; readonly owned: boolean } {
|
|
116
148
|
const dir = opts.dir ?? runLedgerDir();
|
|
117
149
|
const log = opts.log ?? ((c, e, d) => debug.log(c, e, d));
|
|
150
|
+
const emit: Emit = opts.emit ?? ((c, e, d) => debug.log(c, e, d));
|
|
118
151
|
const now = opts.now ?? Date.now;
|
|
119
152
|
const stallMinutes = opts.stallMinutes ?? 30;
|
|
120
153
|
if (!Number.isFinite(stallMinutes) || stallMinutes <= 0) throw new RangeError('stallMinutes must be positive');
|
|
@@ -188,17 +221,24 @@ export function createPodLedgerFollower(opts: {
|
|
|
188
221
|
mkdirSync(dir, { recursive: true });
|
|
189
222
|
appendFileSync(path, complete);
|
|
190
223
|
owned = true;
|
|
224
|
+
const reemitted = reemitLedgerLines(complete, opts.runId, offset - Buffer.byteLength(text), emit);
|
|
225
|
+
if (reemitted) {
|
|
226
|
+
try { debug.log('self-implement.pod', 'reemitted', { runId: opts.runId, lines: reemitted }); }
|
|
227
|
+
catch { /* The observation cannot interrupt following. */ }
|
|
228
|
+
}
|
|
191
229
|
log('self-implement.pod', 'ledger-live-appended', { runId: opts.runId, lines: complete.split('\n').filter(Boolean).length, bytes: Buffer.byteLength(complete), offset });
|
|
192
230
|
for (const line of complete.split('\n')) {
|
|
193
231
|
if (!line) continue;
|
|
194
232
|
let logEvent: ReturnType<typeof ledgerLineToLogEvent>;
|
|
195
|
-
try {
|
|
196
|
-
logEvent = ledgerLineToLogEvent(line, opts.runId);
|
|
197
|
-
if (logEvent) log(logEvent.category, logEvent.event, logEvent.data);
|
|
198
|
-
} catch { /* A failed host log write must not interrupt ledger following or stall accounting. */ }
|
|
199
233
|
let entry: unknown;
|
|
200
234
|
try { entry = JSON.parse(line); } catch { continue; }
|
|
201
235
|
const event = (entry && typeof entry === 'object' && 'event' in entry) ? entry.event : undefined;
|
|
236
|
+
try {
|
|
237
|
+
if (typeof event === 'string' && !event.startsWith(REEMIT_EXCLUDED_PREFIX)) {
|
|
238
|
+
logEvent = ledgerLineToLogEvent(line, opts.runId);
|
|
239
|
+
if (logEvent && opts.log) log(logEvent.category, logEvent.event, logEvent.data);
|
|
240
|
+
}
|
|
241
|
+
} catch { /* A failed host log write must not interrupt ledger following or stall accounting. */ }
|
|
202
242
|
if (typeof event !== 'string' || !event || NON_PROGRESS_EVENTS.has(event)) continue;
|
|
203
243
|
const idleMinutes = Math.max(0, (now() - lastProgressAt) / 60_000);
|
|
204
244
|
if (stalled) log('self-implement.pod', 'stall-cleared', { runId: opts.runId, idleMinutes });
|
|
@@ -19,7 +19,7 @@ import { join, resolve } from 'node:path';
|
|
|
19
19
|
import { debug } from '../../debug/log.js';
|
|
20
20
|
import { PodLeaseAdmission, type PodLeasePredecessor, type PodLeaseRelease } from '../../pod-lease/admission.js';
|
|
21
21
|
import { predecessorState, type PredecessorState } from '../../pod-lease/dependency-state.js';
|
|
22
|
-
import { HostPoolLease } from '../../pod-lease/host-lease.js';
|
|
22
|
+
import { HostPoolLease, leaseHasPendingPod } from '../../pod-lease/host-lease.js';
|
|
23
23
|
import { measurePoolLease, recommendConcurrency, type PoolDnsProbe, type PoolLeaseRecommendation } from './pod-lease.js';
|
|
24
24
|
|
|
25
25
|
export interface PodPoolMember {
|
|
@@ -143,6 +143,10 @@ export function genericConfigPodPool(config: { pod?: { pool?: string }; harness?
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
/** 노드 자리 배분 — 우선순위 순서로 첫 빈 자리. 순수(시각·대기는 호출자가). */
|
|
146
|
+
export function podPoolHostLease(members: readonly PodPoolMember[]): HostPoolLease {
|
|
147
|
+
return new HostPoolLease([...members.map((m) => `${m.context}@${m.sshHost ?? ''}`)].sort().join(','));
|
|
148
|
+
}
|
|
149
|
+
|
|
146
150
|
export class PodPoolScheduler {
|
|
147
151
|
private readonly inflight = new Map<string, number>();
|
|
148
152
|
private readonly admission: PodLeaseAdmission;
|
|
@@ -157,7 +161,7 @@ export class PodPoolScheduler {
|
|
|
157
161
|
constructor(readonly members: readonly PodPoolMember[], options: { status?: () => PoolLeaseRecommendation | Promise<PoolLeaseRecommendation>; kubectl?: PoolKubectl; dns?: (context: string) => PoolDnsProbe; pollMs?: number; hostLease?: HostPoolLease; dependencyMerged?: (after: PodLeasePredecessor, signal?: AbortSignal) => PredecessorState | boolean | Promise<PredecessorState | boolean> } = {}) {
|
|
158
162
|
this.pollMs = options.pollMs ?? 15_000;
|
|
159
163
|
// One lease directory per pool (contexts, not caps) — every CLI process launching into the same clusters shares it.
|
|
160
|
-
this.hostLease = options.hostLease ??
|
|
164
|
+
this.hostLease = options.hostLease ?? podPoolHostLease(members);
|
|
161
165
|
const measureStatus = options.status ?? (() => {
|
|
162
166
|
const measure = measurePoolLease(members, { ...(options.kubectl ? { kubectl: options.kubectl } : {}), ...(options.dns ? { dns: options.dns } : {}) });
|
|
163
167
|
return recommendConcurrency(measure, { capacity: members.reduce((n, m) => n + m.capacity, 0), accounts: 0, perAccount: 0 });
|
|
@@ -166,9 +170,13 @@ export class PodPoolScheduler {
|
|
|
166
170
|
this.admission = new PodLeaseAdmission({ status: async () => {
|
|
167
171
|
const measured = await measureStatus();
|
|
168
172
|
this.lastRaw = measured;
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const
|
|
173
|
+
const pendingJobs = measured.pendingJobs ?? [];
|
|
174
|
+
const ownStart = this.hostLease.selfStart();
|
|
175
|
+
const transferId = process.env.ELANOUS_POD_RESERVED_LEASE;
|
|
176
|
+
const transferIsOther = !!transferId && this.hostLease.reservationIsOther(transferId);
|
|
177
|
+
const others = measured.recommended === null ? 0 : this.hostLease.live().filter((r) =>
|
|
178
|
+
(r.pid !== process.pid || r.startedAt !== ownStart) && !leaseHasPendingPod(r, pendingJobs)).length;
|
|
179
|
+
const decision = measured.recommended === null ? measured : { ...measured, recommended: Math.max(0, measured.recommended - others + (transferIsOther ? 1 : 0)) };
|
|
172
180
|
// An unknown reading cannot erase permits already granted: on recovery,
|
|
173
181
|
// the first healthy recommendation must still account for their slots.
|
|
174
182
|
if (decision.recommended === null) return decision;
|
|
@@ -181,19 +189,28 @@ export class PodPoolScheduler {
|
|
|
181
189
|
}, ...(options.pollMs ? { pollMs: options.pollMs } : {}), dependencyMerged: options.dependencyMerged ?? ((after, signal) => predecessorState(after, {}, signal)) });
|
|
182
190
|
}
|
|
183
191
|
/** FIFO gate within this process, then a host lease so independent CLI processes cannot over-admit the same pool. */
|
|
184
|
-
async acquireAdmission(signal?: AbortSignal, after?: PodLeasePredecessor): Promise<PodLeaseRelease & { applied: () => void }> {
|
|
192
|
+
async acquireAdmission(signal?: AbortSignal, after?: PodLeasePredecessor): Promise<PodLeaseRelease & { applied: (job: string, context: string, namespace: string) => void; observed: () => void }> {
|
|
185
193
|
const local = await this.acquireLocalAdmission(signal, after);
|
|
186
194
|
try {
|
|
187
195
|
for (;;) {
|
|
188
196
|
if (signal?.aborted) throw new Error('pod lease admission aborted');
|
|
189
197
|
const free = this.lastRaw?.recommended ?? null;
|
|
190
|
-
// Same check as the local gate, but atomic across processes: other
|
|
191
|
-
const
|
|
198
|
+
// Same check as the local gate, but atomic across processes: other authoring and pre-Running leases hold slots.
|
|
199
|
+
const reservedId = process.env.ELANOUS_POD_RESERVED_LEASE;
|
|
200
|
+
const transferred = reservedId ? this.hostLease.claim(reservedId) : null;
|
|
201
|
+
if (reservedId && !transferred) throw new Error('pod lease: authoring reservation could not be transferred');
|
|
202
|
+
if (transferred) delete process.env.ELANOUS_POD_RESERVED_LEASE;
|
|
203
|
+
const pendingJobs = this.lastRaw?.pendingJobs ?? [];
|
|
204
|
+
const lease = transferred ?? (free !== null && free > 0 ? this.hostLease.tryReserve((others) =>
|
|
205
|
+
others - this.hostLease.live().filter((r) => leaseHasPendingPod(r, pendingJobs) &&
|
|
206
|
+
(r.pid !== process.pid || r.startedAt !== this.hostLease.selfStart())).length < free) : null);
|
|
192
207
|
if (lease) {
|
|
193
208
|
let hostReleased = false;
|
|
194
209
|
const releaseHost = () => { if (!hostReleased) { hostReleased = true; lease(); } };
|
|
195
|
-
|
|
196
|
-
|
|
210
|
+
return Object.assign(() => { releaseHost(); local(); }, {
|
|
211
|
+
applied: (job: string, context: string, namespace: string) => lease.applied(job, context, namespace),
|
|
212
|
+
observed: releaseHost,
|
|
213
|
+
});
|
|
197
214
|
}
|
|
198
215
|
await new Promise<void>((resolve, reject) => {
|
|
199
216
|
const timer = setTimeout(() => { signal?.removeEventListener('abort', onAbort); resolve(); }, this.pollMs);
|
|
@@ -40,7 +40,7 @@ export const POD_JOB_DEADLINE_SECONDS = 10_800;
|
|
|
40
40
|
* 예약만 실사용에 맞춘다. */
|
|
41
41
|
export const POD_CHILD_REQUESTS = { cpu: '1', memory: '4Gi' } as const;
|
|
42
42
|
import type { PodPoolMember, PodPoolScheduler } from './pod-pool.js';
|
|
43
|
-
import { measurePoolLease, recommendConcurrency, POD_HOST_LEASE_ANNOTATION } from './pod-lease.js';
|
|
43
|
+
import { measurePoolLease, recommendConcurrency, POD_HOST_LEASE_ANNOTATION, LEASE_KUBECTL_MAX_BUFFER } from './pod-lease.js';
|
|
44
44
|
import { ACTUAL_SUBSTRATE_ENV, RUN_CONTRACT_ENV, carryRunContract, completionFloorFor } from '../../self-implement/graph-run-contract.js';
|
|
45
45
|
import { spawnSync } from 'node:child_process';
|
|
46
46
|
import { createHash } from 'node:crypto';
|
|
@@ -58,6 +58,9 @@ import { envLiteral } from '../../platform/env-literal.js';
|
|
|
58
58
|
import { LLM_TIER_MAP_BY_PROVIDER, lookupLlmTierSpec, type LlmTierProvider } from '../../model-tier/llm-tier-map.js';
|
|
59
59
|
import { parseSelfImplementJson, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
|
|
60
60
|
import { codexQuotaPolicyFromConfig } from '../../oauth/codex-account-store.js';
|
|
61
|
+
import { goalTypeOf, type GoalType } from '../../../scripts/measure-pod-memory-by-goal.js';
|
|
62
|
+
import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
|
|
63
|
+
import { getUserConfig } from '../../user-config.js';
|
|
61
64
|
|
|
62
65
|
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
63
66
|
|
|
@@ -132,14 +135,21 @@ export interface PodSpawnOptions {
|
|
|
132
135
|
env?: NodeJS.ProcessEnv;
|
|
133
136
|
/** Detail switch file (test injection; defaults to the host's local/production selection). */
|
|
134
137
|
liveDetailFile?: string;
|
|
138
|
+
/** Test seam for the opt-in user configuration; otherwise read the active user config. */
|
|
139
|
+
adviseDefaults?: boolean;
|
|
140
|
+
/** Test seam for the read-only measured advice. */
|
|
141
|
+
memoryAdvice?: () => PodMemoryAdvice;
|
|
135
142
|
}
|
|
136
143
|
|
|
137
144
|
export function defaultKubectl(args: readonly string[], input?: string): { status: number | null; stdout: string; stderr: string } {
|
|
138
145
|
// 셸 프록시가 kubectl 의 로컬 API 요청을 가로챈다(2026-09-25 실측) — 자식 env 에서 뺀다.
|
|
139
146
|
const env = { ...process.env };
|
|
140
147
|
for (const k of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[k];
|
|
141
|
-
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env, timeout: 120_000 });
|
|
142
|
-
|
|
148
|
+
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env, timeout: 120_000, maxBuffer: LEASE_KUBECTL_MAX_BUFFER });
|
|
149
|
+
const error = (r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS'
|
|
150
|
+
? `kubectl 출력 너무 큼 (ENOBUFS; maxBuffer=${LEASE_KUBECTL_MAX_BUFFER} bytes)`
|
|
151
|
+
: r.error ? String(r.error) : '';
|
|
152
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + error };
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/** Job·Secret 이름 — 런마다 다르게(병렬) · k8s 이름 규칙(소문자·숫자·하이픈 · 63자 이하). */
|
|
@@ -213,6 +223,46 @@ export function hostGrokCredentials(opts: {
|
|
|
213
223
|
throw new Error('grok: 구독 자격 없음 · API 키 opt-in 꺼짐 또는 키 없음');
|
|
214
224
|
}
|
|
215
225
|
|
|
226
|
+
export type PodGrokSkipReason = 'missing' | 'expiring' | 'refresh-failed';
|
|
227
|
+
|
|
228
|
+
/** GROK-OPTIONAL (10-05) — may this launch plan put grok in the Pod's chain at all?
|
|
229
|
+
* The Pod copy must outlive the Job (POD_GROK_MIN_VALIDITY_MS), so an expiring subscription that the host cannot
|
|
230
|
+
* refresh is «not available» here: the plan drops grok and goes on with codex instead of failing every launch. */
|
|
231
|
+
export function podGrokSubscriptionUsable(opts: {
|
|
232
|
+
home?: string; env?: NodeJS.ProcessEnv;
|
|
233
|
+
refresh?: () => void; isExpiring?: () => boolean; expiresAt?: () => string | null;
|
|
234
|
+
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
235
|
+
} = {}): { usable: true } | { usable: false; reason: PodGrokSkipReason; expiresAt: string | null } {
|
|
236
|
+
const log = opts.log ?? ((category, event, data) => debug.log(category, event, data));
|
|
237
|
+
const expiresAt = () => {
|
|
238
|
+
if (opts.expiresAt) return opts.expiresAt();
|
|
239
|
+
try {
|
|
240
|
+
const scopes = JSON.parse(readFileSync(grokAuthFilePath(opts.home), 'utf8')) as Record<string, { expires_at?: unknown }>;
|
|
241
|
+
const all = Object.values(scopes).flatMap((s) => (s && typeof s.expires_at === 'string' ? [s.expires_at] : []));
|
|
242
|
+
return all.sort().at(-1) ?? null;
|
|
243
|
+
} catch { return null; }
|
|
244
|
+
};
|
|
245
|
+
const skip = (reason: PodGrokSkipReason) => {
|
|
246
|
+
const at = expiresAt();
|
|
247
|
+
try { log('pod.grok-credentials', 'skipped', { reason, expiresAt: at }); } catch { /* observation is fail-soft */ }
|
|
248
|
+
return { usable: false as const, reason, expiresAt: at };
|
|
249
|
+
};
|
|
250
|
+
const credential = resolveGrokCredential({ home: opts.home, env: opts.env });
|
|
251
|
+
if (credential?.kind !== 'subscription') return skip('missing');
|
|
252
|
+
const expiring = opts.isExpiring ?? (() => isGrokSubscriptionExpiring({ home: opts.home, bufferMs: POD_GROK_MIN_VALIDITY_MS }));
|
|
253
|
+
if (!expiring()) return { usable: true };
|
|
254
|
+
let refreshed = true;
|
|
255
|
+
try { (opts.refresh ?? (() => { refreshGrokSubscriptionToken({ home: opts.home }); }))(); } catch { refreshed = false; }
|
|
256
|
+
if (!expiring()) return { usable: true };
|
|
257
|
+
return skip(refreshed ? 'expiring' : 'refresh-failed');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The one launch line for a skipped grok copy — the reader sees why grok left this run's chain. */
|
|
261
|
+
export function podGrokSkippedLine(skip: { reason: PodGrokSkipReason; expiresAt: string | null }): string {
|
|
262
|
+
const why = skip.reason === 'missing' ? '구독 자격 없음' : skip.reason === 'expiring' ? '만료 임박' : '만료 임박 · 호스트 갱신 실패';
|
|
263
|
+
return `⚠️ grok 자격 없음(${why}${skip.expiresAt ? ` · 만료 ${skip.expiresAt}` : ''}) — 이 런의 폴백 체인에서 grok 을 뺐다`;
|
|
264
|
+
}
|
|
265
|
+
|
|
216
266
|
/** 키 캐시 관례: `~/.cache/<env 이름 소문자>`(예: OPENROUTER_API_KEY → ~/.cache/openrouter_api_key · src/config.ts 와 같다). */
|
|
217
267
|
function defaultReadKeyCache(name: string): string | undefined {
|
|
218
268
|
try { const v = readFileSync(join(homedir(), '.cache', name.toLowerCase()), 'utf8').trim(); return v || undefined; } catch { return undefined; }
|
|
@@ -409,9 +459,23 @@ export const POD_MEMORY_DEFAULT = '16Gi';
|
|
|
409
459
|
export const POD_MEMORY_HIGH_DEFAULT = '32Gi';
|
|
410
460
|
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi): 명시할 때만(인자·문면 줄) · 자동 판정은 없다(성공 런 피크 기록 0 — 설계 §②). OOM 이면 POD9 가 high 로 한 번 다시 띄운다. */
|
|
411
461
|
export const POD_MEMORY_LITE_DEFAULT = '2Gi';
|
|
462
|
+
|
|
463
|
+
/** Reserve from measured per-run peaks, never from the advisory limit tier. Sparse or missing measurements keep the old reservation. */
|
|
464
|
+
export function podMemoryRequestFor(kind: GoalType | null, advice?: PodMemoryAdvice): string {
|
|
465
|
+
const evidence = advice?.byGoalType.find((entry) => entry.goalType === kind)?.evidence;
|
|
466
|
+
const peak = evidence?.peakMiB.p95;
|
|
467
|
+
const reason = !kind ? 'unknown-kind' : !evidence ? 'no-advice'
|
|
468
|
+
: evidence.measured < 3 || evidence.runs < 3 || evidence.measured * 2 < evidence.runs ? 'insufficient-sample'
|
|
469
|
+
: peak === null || peak === undefined || !Number.isFinite(peak) || peak < 0 ? 'no-peak' : null;
|
|
470
|
+
if (reason) {
|
|
471
|
+
debug.log('pod.memory', 'request-default', { kind, reason });
|
|
472
|
+
return POD_CHILD_REQUESTS.memory;
|
|
473
|
+
}
|
|
474
|
+
return `${Math.max(1, Math.ceil(peak! * 1.25 / 1024))}Gi`;
|
|
475
|
+
}
|
|
412
476
|
export const POD_MEMORY_TIERS = ['lite', 'standard', 'high'] as const;
|
|
413
477
|
export type PodMemoryTier = (typeof POD_MEMORY_TIERS)[number];
|
|
414
|
-
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto';
|
|
478
|
+
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise';
|
|
415
479
|
const PWA_PATH = /(?:^|[\s`'"(,·])apps\/pwa\//m;
|
|
416
480
|
const GOAL_LINE = /^[ \t]*(?:Pod 메모리|pod-memory)[ \t]*:[ \t]*(lite|standard|high)[ \t]*\r?$/im;
|
|
417
481
|
|
|
@@ -453,14 +517,21 @@ function memoryGi(v: string): number | null {
|
|
|
453
517
|
return m[2] === 'Gi' ? Number(m[1]) : Number(m[1]) / 1024;
|
|
454
518
|
}
|
|
455
519
|
|
|
456
|
-
export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Readonly<Record<string, string | undefined>>, parentGoal?: string): { limit: string; tier: PodMemoryTier; source: PodMemorySource } {
|
|
520
|
+
export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Readonly<Record<string, string | undefined>>, parentGoal?: string, advice?: PodMemoryAdvice): { limit: string; tier: PodMemoryTier; source: PodMemorySource } {
|
|
457
521
|
const option = parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER);
|
|
458
522
|
const line = parsePodMemoryTier(GOAL_LINE.exec(feature)?.[1]);
|
|
459
|
-
const parentLine = !option && !line
|
|
523
|
+
const parentLine = !option && !line ? parsePodMemoryTier(GOAL_LINE.exec(parentGoal ?? '')?.[1]) : null;
|
|
524
|
+
const goalType = advice && !option && !line && !parentLine
|
|
525
|
+
? (broadPodTestWarning(feature) ? 'test' : goalTypeOf(feature))
|
|
526
|
+
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
527
|
+
: null;
|
|
528
|
+
const recommendation = advice?.byGoalType.find((entry) => entry.goalType === goalType && entry.evidence.runs > 0)?.recommended;
|
|
460
529
|
const [tier, source]: [PodMemoryTier, PodMemorySource] = option ? [option, 'option']
|
|
461
530
|
: line ? [line, 'goal-line']
|
|
531
|
+
// 조각 자신의 PWA 경로가 부모 줄·실측 권고보다 먼저다 — 부모 «standard» 가 PWA 조각을 16Gi 로 내리면 OOM(POD7 · #23569 순서 회귀)
|
|
462
532
|
: goalTouchesPwa(feature) ? ['high', 'pwa-auto']
|
|
463
533
|
: parentLine ? [parentLine, 'parent-goal-line']
|
|
534
|
+
: recommendation ? [recommendation, 'advise']
|
|
464
535
|
: parentGoal && goalTouchesPwa(parentGoal) ? ['high', 'parent-pwa-auto']
|
|
465
536
|
: ['standard', 'default'];
|
|
466
537
|
if (tier === 'lite') return { limit: env.ELANOUS_POD_MEMORY_LITE?.trim() || POD_MEMORY_LITE_DEFAULT, tier, source };
|
|
@@ -471,7 +542,7 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
471
542
|
return { limit: a !== null && b !== null && a > b ? base : high, tier, source };
|
|
472
543
|
}
|
|
473
544
|
|
|
474
|
-
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; 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> {
|
|
545
|
+
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> {
|
|
475
546
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
476
547
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
477
548
|
const askBaseIndex = o.goalDoc ? o.args.indexOf('--base') : -1;
|
|
@@ -584,8 +655,8 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
584
655
|
name: 'child', image: o.image, imagePullPolicy: o.imagePullPolicy ?? 'Never',
|
|
585
656
|
// 📏 09-25: 6Gi 는 빠듯했다 — 자식이 6,127Mi 에 붙어 OOMKilled(137) → 12Gi.
|
|
586
657
|
// 📏 09-27: 12Gi 에서도 OOMKilled 셋(구현 노드 도중) — 같은 골을 16Gi 로 다시 쏘니 끝까지 갔다(#20930) → 기본 16Gi.
|
|
587
|
-
//
|
|
588
|
-
resources: { requests: { ...POD_CHILD_REQUESTS }, limits: { memory: o.memoryLimit ?? POD_MEMORY_DEFAULT, cpu: '4' } },
|
|
658
|
+
// 요청은 실측 피크별로 고르고 상한은 독립적으로 유지한다. ELANOUS_POD_MEMORY 로 상한 조정.
|
|
659
|
+
resources: { requests: { ...POD_CHILD_REQUESTS, memory: o.memoryRequest ?? POD_CHILD_REQUESTS.memory }, limits: { memory: o.memoryLimit ?? POD_MEMORY_DEFAULT, cpu: '4' } },
|
|
589
660
|
command: ['bash', '-c'], args: [script],
|
|
590
661
|
env: [
|
|
591
662
|
...(o.runId ? [{ name: 'ELANOUS_RUN_ID', value: o.runId }] : []),
|
|
@@ -640,7 +711,6 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
640
711
|
const hostSupervised = options.hostSupervised ?? true;
|
|
641
712
|
return (input) => {
|
|
642
713
|
const name = podJobName(input.spaceId);
|
|
643
|
-
const featureMemory = podMemoryLimitFor(input.feature, env);
|
|
644
714
|
let parentGoal: string | undefined;
|
|
645
715
|
let parentGoalUnreadable = false;
|
|
646
716
|
let inheritedFrom: string | undefined;
|
|
@@ -658,7 +728,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
658
728
|
inheritedFrom = withinRoot;
|
|
659
729
|
} catch { parentGoalUnreadable = true; }
|
|
660
730
|
}
|
|
661
|
-
|
|
731
|
+
let advice: PodMemoryAdvice | undefined;
|
|
732
|
+
const explicitMemory = Boolean(parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER) || GOAL_LINE.test(input.feature) || GOAL_LINE.test(parentGoal ?? ''));
|
|
733
|
+
const useAdvice = options.adviseDefaults ?? getUserConfig().pod?.memory?.adviseDefaults === true;
|
|
734
|
+
try { advice = (options.memoryAdvice ?? readPodMemoryAdvice)(); }
|
|
735
|
+
catch (error) { debug.log('self-implement.pod', 'memory-advice-unavailable', { spaceId: input.spaceId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
736
|
+
const { limit: selectedMemoryLimit, tier: memoryTier, source: memorySource } = podMemoryLimitFor(input.feature, env, parentGoal, useAdvice && !explicitMemory ? advice : undefined);
|
|
737
|
+
const requestKind = broadPodTestWarning(input.feature) ? 'test' : goalTypeOf(input.feature)
|
|
738
|
+
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null);
|
|
662
739
|
for (const [where, text] of [['feature', input.feature], ['parent-goal', parentGoal]] as const) {
|
|
663
740
|
if (text && text.split(/\r?\n/u).some((line) => /Pod 메모리/u.test(line) && !GOAL_LINE.test(line))) {
|
|
664
741
|
const reason = '한 줄 단독 `Pod 메모리: high|standard` 형식이 아님';
|
|
@@ -674,7 +751,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
674
751
|
let oomRetried = false;
|
|
675
752
|
let retryFromChildRunId: string | undefined;
|
|
676
753
|
let retryFromMemoryLimit: string | undefined;
|
|
677
|
-
debug.log('self-implement.pod', 'memory-limit', { spaceId: input.spaceId, memoryLimit, tier: memoryTier, source: memorySource,
|
|
754
|
+
debug.log('self-implement.pod', 'memory-limit', { spaceId: input.spaceId, goalType: requestKind, memoryLimit, tier: memoryTier, source: memorySource,
|
|
678
755
|
...(memorySource.startsWith('parent-') ? { inheritedFrom } : {}), ...(parentGoalUnreadable ? { parentGoal: 'unreadable' } : {}) });
|
|
679
756
|
const childRunId = mintRunId();
|
|
680
757
|
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');
|
|
@@ -716,7 +793,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
716
793
|
if (recoveryError) return { exitCode: 1, output: '', error: { code: 'pod-oom-recovery', message: recoveryError } };
|
|
717
794
|
// Each Job attempt has its own host lease; releasing the first after OOM lets the retry
|
|
718
795
|
// compete with other processes for the newly measured capacity before applying another Job.
|
|
719
|
-
let releaseAdmission: ((() => void) & { applied?: () => void }) | undefined;
|
|
796
|
+
let releaseAdmission: ((() => void) & { applied?: (job: string, context: string, namespace: string) => void; observed?: () => void }) | undefined;
|
|
720
797
|
try {
|
|
721
798
|
// Every attempt, including an OOM retry, must acquire a pool slot before applying a Job.
|
|
722
799
|
const acquireSlot = async (): Promise<PodPoolMember | null> => {
|
|
@@ -1054,19 +1131,26 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1054
1131
|
const detailUntil = detail && (detail.scope === 'all' || detail.scope === parentRunId || detail.scope === launchRunId) && detail.until > Date.now()
|
|
1055
1132
|
? String(detail.until) : undefined;
|
|
1056
1133
|
const armEnv = Object.fromEntries(Object.entries(options.armEnv ?? {}).filter(([key]) => key !== 'ELANOUS_LIVE_DETAIL_UNTIL' && key !== POD_GITHUB_CREDENTIAL_TOKEN_ENV && key !== POD_GITHUB_CREDENTIAL_URL_ENV && (!hostRefresh || !['GH_TOKEN', 'GITHUB_TOKEN', 'GH_CONFIG_DIR'].includes(key))));
|
|
1057
|
-
const
|
|
1134
|
+
const seat = env.ELANOUS_HARNESS_SEAT;
|
|
1135
|
+
const jobArmEnv = grounding || credentialRelay || githubRelay || env.ELANOUS_DISPATCH_RECORDED === '1' || options.armEnv || detailUntil || seat
|
|
1058
1136
|
? {
|
|
1059
1137
|
...armEnv,
|
|
1060
1138
|
...(detailUntil ? { ELANOUS_LIVE_DETAIL_UNTIL: detailUntil } : {}),
|
|
1061
1139
|
...(env.ELANOUS_DISPATCH_RECORDED === '1' ? { ELANOUS_DISPATCH_RECORDED: '1' } : {}),
|
|
1140
|
+
...(seat === 'OP' || seat === 'TC' || seat === 'MK' || seat === 'UX' ? { ELANOUS_HARNESS_SEAT: seat } : {}),
|
|
1062
1141
|
...(grounding ? { [GROUNDING_URL_ENV]: groundingUrl! } : {}),
|
|
1063
1142
|
...(credentialRelay ? { [POD_CREDENTIAL_URL_ENV]: `${new URL(groundingUrl!).origin}${POD_CREDENTIAL_GROK_PATH}` } : {}),
|
|
1064
1143
|
...(githubRelay ? { [POD_GITHUB_CREDENTIAL_URL_ENV]: `${new URL(groundingUrl!).origin}${POD_CREDENTIAL_GITHUB_PATH}` } : {}),
|
|
1065
1144
|
}
|
|
1066
1145
|
: undefined;
|
|
1067
|
-
const
|
|
1146
|
+
const measuredRequest = podMemoryRequestFor(requestKind, advice);
|
|
1147
|
+
const requestGi = memoryGi(measuredRequest);
|
|
1148
|
+
const limitGi = memoryGi(memoryLimit);
|
|
1149
|
+
const memoryRequest = requestGi !== null && limitGi !== null && requestGi > limitGi ? memoryLimit : measuredRequest;
|
|
1150
|
+
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 를 부르지 않는다
|
|
1068
1151
|
const a = kubectl(['apply', '-f', '-'], JSON.stringify(job));
|
|
1069
1152
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
1153
|
+
releaseAdmission?.applied?.(name, context, namespace);
|
|
1070
1154
|
if (options.source?.kind === 'bundle') {
|
|
1071
1155
|
const podName = await waitForRunningPod(kubectl, namespace, name, sleep);
|
|
1072
1156
|
if (!podName) {
|
|
@@ -1074,6 +1158,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1074
1158
|
cleanupSecret();
|
|
1075
1159
|
return { exitCode: 1, output: '', error: { code: 'pod-source', message: `Pod for ${name} did not become Running` } };
|
|
1076
1160
|
}
|
|
1161
|
+
releaseAdmission?.observed?.();
|
|
1162
|
+
if (releaseAdmission) delete releaseAdmission.observed;
|
|
1077
1163
|
const copied = kubectl(['cp', options.source.bundlePath, `${podName}:/tmp/source.bundle`, '-c', 'child', '-n', namespace]);
|
|
1078
1164
|
if (copied.status !== 0) {
|
|
1079
1165
|
debug.log('self-implement.pod', 'source-mismatch', { job: name, kind: 'bundle', headCommit: options.source.headCommit });
|
|
@@ -1106,7 +1192,6 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1106
1192
|
}
|
|
1107
1193
|
writePodFragment({ spaceId: input.spaceId, context, namespace, job: name, inboxDir: POD_CONTROL_INBOX_DIR, ...(liveChildRunId === normalizeRunId(liveChildRunId) ? { runId: liveChildRunId } : {}), ...(parentRunId && normalizeRunId(parentRunId) === parentRunId ? { parentRunId } : {}) }, env);
|
|
1108
1194
|
recorded = true;
|
|
1109
|
-
releaseAdmission?.applied?.();
|
|
1110
1195
|
debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
|
|
1111
1196
|
let state: 'complete' | 'failed' | 'aborted' = 'failed';
|
|
1112
1197
|
let failedReason = '';
|
|
@@ -1122,8 +1207,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1122
1207
|
}
|
|
1123
1208
|
const g = kubectl(['-n', namespace, 'get', 'job', name, '-o', 'jsonpath={.status.conditions[*].type}']);
|
|
1124
1209
|
const types = g.stdout;
|
|
1125
|
-
if (/Complete|SuccessCriteriaMet/.test(types)) { state = 'complete'; break; }
|
|
1210
|
+
if (/Complete|SuccessCriteriaMet/.test(types)) { releaseAdmission?.observed?.(); state = 'complete'; break; }
|
|
1126
1211
|
if (/Failed|FailureTarget/.test(types)) {
|
|
1212
|
+
releaseAdmission?.observed?.();
|
|
1127
1213
|
state = 'failed';
|
|
1128
1214
|
const reason = kubectl(['-n', namespace, 'get', 'job', name, '-o', 'jsonpath={.status.conditions[?(@.type=="Failed")].reason}']);
|
|
1129
1215
|
failedReason = reason.status === 0 ? reason.stdout.trim() : '';
|
|
@@ -1143,6 +1229,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1143
1229
|
debug.log('self-implement.pod', 'container-terminated', { job: name, container: 'child', reason: containerReason, exitCode: containerExitCode, jobReason: failedReason || null, memoryLimit });
|
|
1144
1230
|
break;
|
|
1145
1231
|
}
|
|
1232
|
+
if (releaseAdmission?.observed) {
|
|
1233
|
+
const podPhase = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o', 'jsonpath={.items[*].status.phase}']);
|
|
1234
|
+
if (podPhase.status === 0 && /\b(?:Running|Succeeded|Failed)\b/.test(podPhase.stdout)) {
|
|
1235
|
+
releaseAdmission.observed();
|
|
1236
|
+
delete releaseAdmission.observed;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1146
1239
|
if (ghTokenExpiresAt !== null && ghTokenExpiresAt - now() <= 10 * 60_000) {
|
|
1147
1240
|
try {
|
|
1148
1241
|
const fresh = await issueVerifiedAppCredential();
|
|
@@ -1237,7 +1330,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1237
1330
|
catch (error) { debug.log('harness.host-regate', 'unmeasured', { pr: prNumber, files: [], os: process.platform, failedStep: 'comment', detail: error instanceof Error ? error.message : String(error) }); }
|
|
1238
1331
|
}
|
|
1239
1332
|
const releaseHold = regate.failures[0]?.step === 'release-path-hold';
|
|
1240
|
-
|
|
1333
|
+
const frozen = regate.status === 'frozen';
|
|
1334
|
+
disposition = { ...disposition, stage: frozen || releaseHold ? 'pr-opened' : regate.passed ? 'merged' : 'host-regate-failed', merged: regate.passed && !frozen, hostRegate: regate, ok: regate.passed || releaseHold || frozen };
|
|
1241
1335
|
}
|
|
1242
1336
|
const childFailure = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1243
1337
|
? lastPodChildFailure(logs) : null;
|
package/src/telegram-commands.ts
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
import type { TgIncoming, TgMessageStreamer } from './telegram.js';
|
|
15
15
|
import type { UserConfig } from './user-config.js';
|
|
16
16
|
import { botCommandsToTelegram } from './bots/command-surface.js';
|
|
17
|
+
import { SLASH_COMMANDS, type SlashCommand } from './chat/index.js';
|
|
18
|
+
import { FEATURE_MATURITY, type Maturity } from './maturity/feature-maturity.js';
|
|
19
|
+
import { buildBotSlashCatalog } from './maturity/bot-slash-catalog.js';
|
|
17
20
|
import {
|
|
18
21
|
appendMessage,
|
|
19
22
|
attachTelegramBinding,
|
|
@@ -46,6 +49,7 @@ import { classifyIntake } from './ad-pipeline/intake.js';
|
|
|
46
49
|
import { createAdPipelineDeps, runAdPipeline } from './ad-pipeline/run.js';
|
|
47
50
|
import { projectCommand } from './telegram-project-command.js';
|
|
48
51
|
import { telegramNowSlash } from './context-bus/context-now-surfaces.js';
|
|
52
|
+
import { telegramLoopsStatus } from './telegram-loops-command.js';
|
|
49
53
|
import { createWishCard } from './intake-plane/wish-card.js';
|
|
50
54
|
import { telegramDecisionOwner } from './decisions/telegram-decision-cards.js';
|
|
51
55
|
import { CardStore } from './task-cards/card-store.js';
|
|
@@ -298,8 +302,12 @@ function dropChatSessionReply(ctx: TgIncoming): string {
|
|
|
298
302
|
/** Build the default command set for a Telegram bot wired via
|
|
299
303
|
* botFromConfig. Returns the array rather than mutating globals so
|
|
300
304
|
* tests can construct a variant set with stubbed handlers. */
|
|
301
|
-
export function defaultTelegramCommands(
|
|
302
|
-
|
|
305
|
+
export function defaultTelegramCommands(
|
|
306
|
+
nowDeps?: ContextNowDeps,
|
|
307
|
+
coreCommands: readonly SlashCommand[] = SLASH_COMMANDS,
|
|
308
|
+
maturity: { tuiSlash: Readonly<Record<string, Maturity>>; telegramCommand: Readonly<Record<string, Maturity>>; discordCommand: Readonly<Record<string, Maturity>> } = FEATURE_MATURITY,
|
|
309
|
+
): TgSlashCommand[] {
|
|
310
|
+
const handled: TgSlashCommand[] = [
|
|
303
311
|
{
|
|
304
312
|
name: 'help',
|
|
305
313
|
description: 'List available commands',
|
|
@@ -316,6 +324,11 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
|
|
|
316
324
|
description: 'Show current release, decisions, seats and context: /now [topic]',
|
|
317
325
|
handler: async (args) => telegramNowSlash(args, nowDeps),
|
|
318
326
|
},
|
|
327
|
+
{
|
|
328
|
+
name: 'loops',
|
|
329
|
+
description: '루프·크론 현황 — 늦음·실패를 먼저 보여줍니다',
|
|
330
|
+
handler: async (args) => telegramLoopsStatus(args),
|
|
331
|
+
},
|
|
319
332
|
{
|
|
320
333
|
name: 'status',
|
|
321
334
|
description: 'Show bot identity, current session, provider + model',
|
|
@@ -604,7 +617,8 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
|
|
|
604
617
|
if (args.length === 0) return '/wish <소원 한 줄>';
|
|
605
618
|
const store = new CardStore();
|
|
606
619
|
try {
|
|
607
|
-
const card = createWishCard({ text: args.join(' '), source: 'telegram', ref: `${ctx.chatId}:${ctx.messageId}
|
|
620
|
+
const card = createWishCard({ text: args.join(' '), source: 'telegram', ref: `${ctx.chatId}:${ctx.messageId}`,
|
|
621
|
+
replyTo: { surface: 'telegram', chatId: String(ctx.chatId), ...(ctx.threadId != null ? { threadId: String(ctx.threadId) } : {}), ...(ctx.botId ? { botId: ctx.botId } : {}) } }, store);
|
|
608
622
|
return `소원 카드로 남겼습니다 — ${card.title} (카드 ${card.cardId.slice(0, 8)})`;
|
|
609
623
|
} finally {
|
|
610
624
|
store.close();
|
|
@@ -951,6 +965,16 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
|
|
|
951
965
|
},
|
|
952
966
|
...botCommandsToTelegram(),
|
|
953
967
|
];
|
|
968
|
+
const catalog = buildBotSlashCatalog({
|
|
969
|
+
surface: 'telegram', coreCommands, maturity,
|
|
970
|
+
handledCommands: handled,
|
|
971
|
+
});
|
|
972
|
+
const handlers = new Map(handled.map((command) => [command.name, command]));
|
|
973
|
+
return catalog.commands.map((entry) => handlers.get(entry.name) ?? {
|
|
974
|
+
name: entry.name,
|
|
975
|
+
description: entry.description,
|
|
976
|
+
handler: async () => catalog.unsupportedReply(entry.name)!,
|
|
977
|
+
});
|
|
954
978
|
}
|
|
955
979
|
|
|
956
980
|
/** Telegram-side adapter over the messenger-agnostic ACP turn runner.
|
|
@@ -42,7 +42,7 @@ export function defaultBaseUrl(deps: { runtime?: () => ReturnType<typeof readNex
|
|
|
42
42
|
return `http://${host}:${rt.httpPort}`;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function defaultToken(): string | null {
|
|
45
|
+
export function defaultToken(): string | null {
|
|
46
46
|
const p = join(getElanousConfigDir(), 'acp-token');
|
|
47
47
|
try { return existsSync(p) ? readFileSync(p, 'utf-8').trim() || null : null; } catch { return null; }
|
|
48
48
|
}
|