elanous 0.2.13 → 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/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.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/{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-c952712c87a1f757.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/8060-cc008cc6fedfa818.js +1 -0
- 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/899-46946fe8363e1262.js +1 -0
- 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-0e2c57fe6add4b2a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
- 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-b8c49f6b4fa2df91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
- 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-60c7a8e1a4fe62ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.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-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
- 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-955ec2da3be4c560.js +1 -0
- 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-c1343f9499d86f3f.js +1 -0
- 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-d23b12bb92242558.js +1 -0
- 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-e2ee2896811e98d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
- 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-153c2b3bcc89e8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.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-7d4182964407c09c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
- 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-63d14a4d8fe1e1c1.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-73735b64bfe1ae67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
- 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-9817c7f9007466cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.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/1f7c958c41ffa3b9.css +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/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 +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/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +6 -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 +1 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +188 -0
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/coord-post.sh +11 -1
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +169 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/standby-verify.ts +11 -7
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +112 -0
- 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 +89 -14
- 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 +428 -0
- package/scripts/release-story/draft.ts +220 -0
- package/scripts/rhythm/daily.ts +570 -0
- package/scripts/rhythm/weekly.ts +315 -0
- package/scripts/skills/public-check.ts +169 -0
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/scripts/test-deterministic.ts +4 -8
- 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/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-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +3 -3
- 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 +73 -10
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/briefing/brief-items.ts +121 -0
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +166 -0
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +44 -3
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- package/src/cli/intake-cli.ts +46 -17
- 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/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +29 -1
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +190 -33
- package/src/cli/rfc-status.ts +91 -0
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +15 -20
- package/src/context-bus/context-now-public.ts +96 -0
- package/src/context-bus/context-now-surfaces.ts +55 -2
- package/src/context-bus/context-now.ts +6 -3
- package/src/context-bus/coord-events.ts +6 -4
- package/src/context-bus/long-term-memory.ts +92 -11
- 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 +33 -7
- package/src/decisions/decision-ledger.ts +40 -1
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord/sprint21-runtime.ts +16 -0
- package/src/discord-self-message.ts +8 -0
- package/src/discord-slash-wire.ts +31 -2
- 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/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-runner/graph-tick.ts +4 -0
- package/src/graph-runner/runner.ts +30 -5
- package/src/harness/harness-cli-command.ts +259 -38
- package/src/harness/harness-incidents.ts +45 -12
- package/src/harness/harness-pod-dispatch.ts +102 -11
- package/src/harness/harness-queue-child.ts +3 -3
- package/src/harness/harness-queue.ts +583 -65
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +151 -0
- 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/model-input-observation.ts +37 -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 +63 -5
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +332 -0
- package/src/index.ts +159 -27
- 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/linear-wish.ts +39 -11
- package/src/intake-plane/route.ts +145 -1
- 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/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -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 +63 -0
- package/src/loops/checker.ts +5 -3
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +36 -4
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +10 -0
- package/src/loops/orchestrator/author-depth.ts +220 -0
- package/src/loops/orchestrator/author-input-check.ts +82 -0
- package/src/loops/orchestrator/author-ledger.ts +232 -0
- package/src/loops/orchestrator/finish-rate.ts +175 -0
- package/src/loops/orchestrator/overlap.ts +51 -12
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +309 -0
- package/src/loops/orchestrator/traffic.ts +134 -13
- package/src/loops/registry.ts +535 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +17 -5
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +9 -5
- package/src/maturity/tui-slash-availability.ts +31 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/http-server.ts +33 -3
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/schedules-read.ts +18 -1
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +51 -0
- package/src/release-loop/checklist.ts +22 -4
- package/src/release-loop/feature-store.ts +71 -32
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +302 -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 +16 -3
- package/src/seat-dispatch/seat-questions.ts +58 -0
- 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 +531 -40
- package/src/self-dev/dev-pipeline.ts +14 -6
- package/src/self-dev/draft-sweep.ts +162 -6
- 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-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +57 -2
- 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 +295 -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-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/goal-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +197 -0
- package/src/self-implement/host-regate.ts +32 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +299 -48
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +97 -0
- package/src/self-implement/run-ledger.ts +3 -1
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +29 -9
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session/index.ts +2 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- package/src/task-cards/card-cli.ts +10 -2
- package/src/task-cards/card-store.ts +10 -6
- 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-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +146 -18
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.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-fe5c06d6b31f8b25.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/5250-c78ca295585bbf95.js +0 -1
- 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/6511-ff8360be3d65aaa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.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/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.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-9da4678f8a310d18.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/9906-e7b8b5310a169dfe.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-e5b4e1b256f41076.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.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-cb2eb0fd809980da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.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-d92e32d51ab5157e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.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/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
type Agent = 'claude' | 'codex' | 'grok';
|
|
6
|
+
type Presence = 'present' | '«없음»';
|
|
7
|
+
|
|
8
|
+
function present(path: string): Presence {
|
|
9
|
+
return existsSync(path) ? 'present' : '«없음»';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function record(value: unknown): Record<string, unknown> {
|
|
13
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
14
|
+
? value as Record<string, unknown> : {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function json(path: string): Record<string, unknown> | null {
|
|
18
|
+
if (!existsSync(path)) return null;
|
|
19
|
+
return record(JSON.parse(readFileSync(path, 'utf8')));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Command strings can contain tokens even without a shell; export only known executable names.
|
|
23
|
+
function safeCommand(value: unknown): string | undefined {
|
|
24
|
+
if (typeof value !== 'string') return undefined;
|
|
25
|
+
return /^(?:npx|npm|node|bun|uv|uvx|python|python3|docker|deno|pnpm|yarn|bash|sh|claude|codex|grok|cct)$/.test(value)
|
|
26
|
+
? value : '<redacted>';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Key-shaped values (sk-…, ghp_…, AKIA…) pass the character check, so shape alone cannot prove a value is not a secret (10-05 post-review).
|
|
30
|
+
const SECRET_PREFIX = /^(?:sk-|sk_|pk_|rk_|xai-|ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|glpat-|AKIA|ASIA|AIza|ya29\.|xox[abprs]-|eyJ)/;
|
|
31
|
+
|
|
32
|
+
export function safeIdentifier(value: unknown): string | null {
|
|
33
|
+
if (typeof value !== 'string') return null;
|
|
34
|
+
const digits = (value.match(/[0-9]/g) ?? []).length;
|
|
35
|
+
const keyShaped = SECRET_PREFIX.test(value) || value.length > 40 || (value.length >= 20 && digits >= 8);
|
|
36
|
+
return /^[a-zA-Z0-9_.:-]{1,90}$/.test(value) && !keyShaped && !/(?:token|secret|password|api.?key|credential)/i.test(value)
|
|
37
|
+
? value : '<redacted>';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type McpSource = 'settings' | 'user' | 'project';
|
|
41
|
+
|
|
42
|
+
function mcpEntry(server: unknown, source: McpSource): Record<string, unknown> {
|
|
43
|
+
const body = record(server);
|
|
44
|
+
return {
|
|
45
|
+
source,
|
|
46
|
+
command: safeCommand(body.command) ?? null,
|
|
47
|
+
env: Object.fromEntries(Object.keys(record(body.env)).map(key => [key, '<redacted>'])),
|
|
48
|
+
headers: Object.fromEntries(Object.keys(record(body.headers)).map(key => [key, '<redacted>'])),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function collectMcp(
|
|
53
|
+
into: Record<string, unknown>,
|
|
54
|
+
servers: unknown,
|
|
55
|
+
source: McpSource,
|
|
56
|
+
): void {
|
|
57
|
+
for (const [name, server] of Object.entries(record(servers))) into[name] = mcpEntry(server, source);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hookGroups(hooks: Record<string, unknown>): Record<string, unknown[]> {
|
|
61
|
+
return Object.fromEntries(Object.entries(hooks).map(([event, groups]) => [event,
|
|
62
|
+
Array.isArray(groups) ? groups.map(group => {
|
|
63
|
+
const body = record(group);
|
|
64
|
+
const entries = body.hooks;
|
|
65
|
+
return {
|
|
66
|
+
matcher: safeIdentifier(body.matcher),
|
|
67
|
+
hooks: Array.isArray(entries)
|
|
68
|
+
? entries.map(entry => ({ command: safeCommand(record(entry).command) ?? null }))
|
|
69
|
+
: [],
|
|
70
|
+
};
|
|
71
|
+
}) : [],
|
|
72
|
+
]));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function claude(home: string): Record<string, unknown> {
|
|
76
|
+
const dir = join(home, '.claude');
|
|
77
|
+
const settingsPath = join(dir, 'settings.json');
|
|
78
|
+
const userPath = join(home, '.claude.json');
|
|
79
|
+
const settings = json(settingsPath);
|
|
80
|
+
const user = json(userPath);
|
|
81
|
+
const data = record(settings);
|
|
82
|
+
const userData = record(user);
|
|
83
|
+
const plugins = record(data.enabledPlugins);
|
|
84
|
+
const hooks = record(data.hooks);
|
|
85
|
+
const statusLine = record(data.statusLine);
|
|
86
|
+
const skillsDir = join(dir, 'skills');
|
|
87
|
+
const team = join(home, '.teamclaude');
|
|
88
|
+
const teamManifest = json(join(team, 'package.json'));
|
|
89
|
+
const teamVersion = typeof teamManifest?.version === 'string' && /^\d+\.\d+\.\d+(?:[-+.][a-zA-Z0-9.-]+)?$/.test(teamManifest.version)
|
|
90
|
+
? teamManifest.version : undefined;
|
|
91
|
+
const mcp: Record<string, unknown> = {};
|
|
92
|
+
if (settings !== null) collectMcp(mcp, data.mcpServers, 'settings');
|
|
93
|
+
if (user !== null) {
|
|
94
|
+
collectMcp(mcp, userData.mcpServers, 'user');
|
|
95
|
+
for (const project of Object.values(record(userData.projects))) {
|
|
96
|
+
collectMcp(mcp, record(project).mcpServers, 'project');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const mcpFilesMissing = settings === null && user === null;
|
|
100
|
+
return {
|
|
101
|
+
settings: settings === null ? '«없음»' : 'present',
|
|
102
|
+
userConfig: user === null ? '«없음»' : 'present',
|
|
103
|
+
permissionMode: safeIdentifier(record(data.permissions).defaultMode),
|
|
104
|
+
statusLine: settings === null ? '«없음»' : data.statusLine === undefined ? '«없음»' : {
|
|
105
|
+
type: safeIdentifier(statusLine.type),
|
|
106
|
+
command: safeCommand(statusLine.command) ?? null,
|
|
107
|
+
},
|
|
108
|
+
hooks: settings === null ? '«없음»' : hookGroups(hooks),
|
|
109
|
+
enabledPlugins: settings === null ? '«없음»' : Object.fromEntries(Object.entries(plugins).map(([name, enabled]) => [name, enabled === true])),
|
|
110
|
+
mcpServers: mcpFilesMissing ? '«없음»' : mcp,
|
|
111
|
+
skills: !existsSync(skillsDir) ? '«없음»' : readdirSync(skillsDir, { withFileTypes: true })
|
|
112
|
+
.filter(entry => entry.isDirectory()).map(entry => entry.name).sort(),
|
|
113
|
+
teamclaude: { installed: present(team), version: teamVersion ?? null },
|
|
114
|
+
auth: present(join(dir, 'auth.json')),
|
|
115
|
+
credentials: present(join(dir, '.credentials.json')),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function codex(home: string): Record<string, unknown> {
|
|
120
|
+
const dir = join(home, '.codex');
|
|
121
|
+
const file = join(dir, 'config.toml');
|
|
122
|
+
if (!existsSync(file)) return { config: '«없음»', model: null, profile: null, mcpServers: '«없음»', auth: present(join(dir, 'auth.json')) };
|
|
123
|
+
const data = record(Bun.TOML.parse(readFileSync(file, 'utf8')));
|
|
124
|
+
const mcp = record(data.mcp_servers);
|
|
125
|
+
return {
|
|
126
|
+
config: 'present',
|
|
127
|
+
model: safeIdentifier(data.model),
|
|
128
|
+
profile: safeIdentifier(data.profile),
|
|
129
|
+
mcpServers: Object.keys(mcp).sort(),
|
|
130
|
+
auth: present(join(dir, 'auth.json')),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function grok(home: string): Record<string, unknown> {
|
|
135
|
+
const dir = join(home, '.grok');
|
|
136
|
+
const jsonPath = join(dir, 'config.json');
|
|
137
|
+
const tomlPath = join(dir, 'config.toml');
|
|
138
|
+
const file = existsSync(jsonPath) ? jsonPath : tomlPath;
|
|
139
|
+
const data = !existsSync(file) ? null : file === jsonPath ? json(file) : record(Bun.TOML.parse(readFileSync(file, 'utf8')));
|
|
140
|
+
return {
|
|
141
|
+
config: data === null ? '«없음»' : 'present',
|
|
142
|
+
keys: data === null ? '«없음»' : Object.keys(data).sort(),
|
|
143
|
+
auth: present(join(dir, 'auth.json')),
|
|
144
|
+
credentials: present(join(dir, 'credentials.json')),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function captureAgentEnv(options: { agent?: Agent; home?: string; now?: Date } = {}): {
|
|
149
|
+
version: number; capturedAt: string; agents: Record<string, Record<string, unknown>>;
|
|
150
|
+
} {
|
|
151
|
+
const home = options.home ?? homedir();
|
|
152
|
+
const selected = options.agent ? [options.agent] : ['claude', 'codex', 'grok'] as const;
|
|
153
|
+
const agents: Record<string, Record<string, unknown>> = {};
|
|
154
|
+
for (const agent of selected) {
|
|
155
|
+
if (agent === 'claude') agents.claude = claude(home);
|
|
156
|
+
else if (agent === 'codex') agents.codex = codex(home);
|
|
157
|
+
else if (agent === 'grok') agents.grok = grok(home);
|
|
158
|
+
else throw new Error('Unknown agent');
|
|
159
|
+
}
|
|
160
|
+
return { version: 1, capturedAt: (options.now ?? new Date()).toISOString(), agents };
|
|
161
|
+
}
|
|
@@ -2202,8 +2202,8 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
2202
2202
|
rounds: round, evidencePath: null, committed: false, usedOmniCrawl: usedOmni, detail: 'aborted' };
|
|
2203
2203
|
const finalEv = verifyEvidence(wt.path, spec.evidence);
|
|
2204
2204
|
const finalScreen = await h.renderScreen();
|
|
2205
|
-
const driveVerdict = verdictForFinalPtyScreen({ screen: finalScreen, inputHistory: submittedInputs, exitCode: h.exitCode ?? null });
|
|
2206
|
-
debug.log('agent-mission', 'drive-
|
|
2205
|
+
const driveVerdict = verdictForFinalPtyScreen({ screen: finalScreen, inputHistory: submittedInputs, exitCode: h.exitCode ?? null, artifactEvidence: finalEv.ok });
|
|
2206
|
+
debug.log('agent-mission', 'drive-ok', { verdict: driveVerdict.kind, reason: driveVerdict.reason.slice(0, 200), evidence: finalEv.ok });
|
|
2207
2207
|
const recoverExhausted = control.termination.kind === 'stuck' && control.termination.reason.startsWith('사람 필요');
|
|
2208
2208
|
const accepted = finalEv.ok && !recoverExhausted && driveVerdict.kind !== 'done-but-failed';
|
|
2209
2209
|
const evidencePath = accepted ? finalEv.path : null;
|
|
@@ -2223,7 +2223,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
2223
2223
|
ok: accepted, worktree: wt.path, branch: wt.branch, rounds: round,
|
|
2224
2224
|
evidencePath, evidenceSatisfied: finalEv.ok, committed, usedOmniCrawl: usedOmni,
|
|
2225
2225
|
ptyId: h.id, webUrl: webAddress.webUrl, driveVerdict: driveVerdict.kind,
|
|
2226
|
-
detail: recoverExhausted ? control.termination.reason : driveVerdict.kind === 'done-but-failed' ? `미완(DRIVE-OK: ${driveVerdict.reason})` : done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
|
|
2226
|
+
detail: recoverExhausted ? control.termination.reason : driveVerdict.kind === 'done-but-failed' ? `미완(DRIVE-OK: ${driveVerdict.reason})` : driveVerdict.kind === 'success-unverified' && finalEv.ok ? '완료(증거)' : done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
|
|
2227
2227
|
};
|
|
2228
2228
|
} finally { stopLive(); finishContext(false); }
|
|
2229
2229
|
}
|
|
@@ -825,6 +825,8 @@ export async function reviewPullRequest(
|
|
|
825
825
|
|
|
826
826
|
/** 리뷰 결과를 PR 코멘트/알림/CLI 용 markdown 으로 렌더. */
|
|
827
827
|
export function renderReview(r: ReviewResult): string {
|
|
828
|
+
// A review that never ran must not read as PASS anywhere it is rendered (PR body, comments, CLI) — 10-05 #23942·#24151.
|
|
829
|
+
if (r.reviewed === false) return `⚠️ 자율 PR 리뷰: 리뷰 안 돎 — 사유 ${r.failureReason?.trim() || '미상'} (통과 판정 아님)`;
|
|
828
830
|
const icon = r.verdict === 'pass' ? '✅' : r.verdict === 'warn' ? '⚠️' : '⛔';
|
|
829
831
|
const head = `${icon} 자율 PR 리뷰: ${r.verdict.toUpperCase()}`;
|
|
830
832
|
const parts = [head];
|
|
@@ -9,6 +9,8 @@ export interface PendingQuestion {
|
|
|
9
9
|
id: string;
|
|
10
10
|
sessionId?: string;
|
|
11
11
|
runId?: string;
|
|
12
|
+
/** Explicit provenance for isolated test questions; a temporary goal path alone is not proof. */
|
|
13
|
+
testOrigin?: true;
|
|
12
14
|
questions: Array<Pick<Question, 'id' | 'question' | 'options' | 'impact' | 'recommendedIndex'>>;
|
|
13
15
|
startedAt: string;
|
|
14
16
|
expiresAt?: string;
|
|
@@ -84,6 +86,7 @@ function isPendingQuestion(value: unknown): value is PendingQuestion {
|
|
|
84
86
|
return typeof pending.id === 'string'
|
|
85
87
|
&& (pending.sessionId === undefined || typeof pending.sessionId === 'string')
|
|
86
88
|
&& (pending.runId === undefined || typeof pending.runId === 'string')
|
|
89
|
+
&& (pending.testOrigin === undefined || pending.testOrigin === true)
|
|
87
90
|
&& Array.isArray(pending.questions)
|
|
88
91
|
&& pending.questions.every((question) => {
|
|
89
92
|
if (question === null || typeof question !== 'object' || Array.isArray(question)) return false;
|
|
@@ -175,6 +178,7 @@ export function createPendingQuestion(
|
|
|
175
178
|
sessionId?: string,
|
|
176
179
|
deps: Pick<PendingQuestionStoreDeps, 'now'> = {},
|
|
177
180
|
presentation: PendingQuestionPresentation = { surface: 'tui', delivery: request.delivery ?? 'modal' },
|
|
181
|
+
provenance: { testOrigin?: true } = {},
|
|
178
182
|
): PendingQuestion {
|
|
179
183
|
// File-surface waits must carry a concrete deadline so the CLI answer path can
|
|
180
184
|
// reject an expired record (see isPendingQuestion). Fail loudly at creation
|
|
@@ -186,6 +190,7 @@ export function createPendingQuestion(
|
|
|
186
190
|
id,
|
|
187
191
|
...(sessionId === undefined ? {} : { sessionId }),
|
|
188
192
|
...(request.runId === undefined ? {} : { runId: request.runId }),
|
|
193
|
+
...(provenance.testOrigin ? { testOrigin: true as const } : {}),
|
|
189
194
|
questions: request.questions.map(({ id, question, options, impact, recommendedIndex }) => ({
|
|
190
195
|
id, question, options,
|
|
191
196
|
...(impact === undefined ? {} : { impact }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, createSign } from 'node:crypto';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, isAbsolute, join } from 'node:path';
|
|
5
5
|
import { getElanousConfigDir, getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
6
6
|
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
@@ -45,21 +45,23 @@ export function githubAutomationToken(deps: GithubAutomationTokenDeps = {}): str
|
|
|
45
45
|
return githubInstallationCredential(deps)?.token ?? null;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/** The App credential is machine-level (like `gh auth` in ~/.config/gh), not per universe: a tree-derived test
|
|
49
|
+
* universe resolves its config dir to `<tree>/.elanous-test`, where the file never is — 09-29 #21879 was inert there.
|
|
50
|
+
* Order: explicit path → this universe's config dir → the prod root `~/.elanous`.
|
|
51
|
+
* An explicit config-dir override (tests · `--config-dir`) is a deliberate boundary — never reach past it.
|
|
52
|
+
* ELANOUS_GITHUB_APP_CONFIG_PATH pins the machine-level fallback (the bun test preload points it at nothing,
|
|
53
|
+
* so tests never mint); an explicit config-dir override still wins. */
|
|
54
|
+
function appConfigPath(explicit?: string): string {
|
|
55
|
+
const rel = join('secrets', 'github-app', 'app.json');
|
|
56
|
+
const own = join(getElanousConfigDir(), rel);
|
|
57
|
+
const pinned = process.env.ELANOUS_GITHUB_APP_CONFIG_PATH?.trim();
|
|
58
|
+
return explicit ?? (getElanousConfigDirOverride() ? own : pinned || (existsSync(own) ? own : join(prodInstanceRoot(), rel)));
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
/** Scoped requests always mint anew; the returned expiry comes from GitHub's response. */
|
|
49
62
|
export function githubInstallationCredential(deps: GithubAutomationTokenDeps = {}): { token: string; expires_at: string } | null {
|
|
50
63
|
let path: string;
|
|
51
|
-
|
|
52
|
-
// universe resolves its config dir to `<tree>/.elanous-test`, where the file never is — 09-29 #21879 was inert there.
|
|
53
|
-
// Order: explicit path → this universe's config dir → the prod root `~/.elanous`.
|
|
54
|
-
try {
|
|
55
|
-
const rel = join('secrets', 'github-app', 'app.json');
|
|
56
|
-
const own = join(getElanousConfigDir(), rel);
|
|
57
|
-
// An explicit config-dir override (tests · `--config-dir`) is a deliberate boundary — never reach past it.
|
|
58
|
-
// ELANOUS_GITHUB_APP_CONFIG_PATH pins the machine-level fallback (the bun test preload points it at nothing,
|
|
59
|
-
// so tests never mint); an explicit config-dir override still wins.
|
|
60
|
-
const pinned = process.env.ELANOUS_GITHUB_APP_CONFIG_PATH?.trim();
|
|
61
|
-
path = deps.configPath ?? (getElanousConfigDirOverride() ? own : pinned || (existsSync(own) ? own : join(prodInstanceRoot(), rel)));
|
|
62
|
-
}
|
|
64
|
+
try { path = appConfigPath(deps.configPath); }
|
|
63
65
|
catch {
|
|
64
66
|
try { debug.log('auth.github-app', 'token-failed', { reason: 'config-path-failed' }); } catch { /* fail open */ }
|
|
65
67
|
return null;
|
|
@@ -113,3 +115,100 @@ export function githubInstallationCredential(deps: GithubAutomationTokenDeps = {
|
|
|
113
115
|
return null;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
118
|
+
|
|
119
|
+
/** PODCRED1 (10-05): simultaneous launches each minted a scoped token in the same second and the Pods' logins died
|
|
120
|
+
* together (12:49Z · 5 in 2 s). Concurrent issuers for one repository — across processes — share one mint: the first
|
|
121
|
+
* takes a lock and mints, the others wait and reuse that result while it is recent and long-lived. */
|
|
122
|
+
export interface CoalescedInstallationOptions {
|
|
123
|
+
cacheDir?: string;
|
|
124
|
+
/** Reuse a token minted at most this long ago. */
|
|
125
|
+
windowMs?: number;
|
|
126
|
+
/** …and only while it still has this much life left. */
|
|
127
|
+
minRemainingMs?: number;
|
|
128
|
+
/** Skip reuse (still mint under the lock and publish the result) — for a retry after the shared token failed. */
|
|
129
|
+
fresh?: boolean;
|
|
130
|
+
lockWaitMs?: number;
|
|
131
|
+
staleLockMs?: number;
|
|
132
|
+
now?: () => number;
|
|
133
|
+
sleepSync?: (ms: number) => void;
|
|
134
|
+
mint?: (scope: GithubInstallationTokenScope) => { token: string; expires_at: string } | null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const COALESCE_WINDOW_MS = 120_000;
|
|
138
|
+
const COALESCE_MIN_REMAINING_MS = 50 * 60_000;
|
|
139
|
+
|
|
140
|
+
function blockingSleep(ms: number): void {
|
|
141
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function coalescedInstallationCredential(scope: GithubInstallationTokenScope, opts: CoalescedInstallationOptions = {}): { token: string; expires_at: string } | null {
|
|
145
|
+
const now = opts.now ?? Date.now;
|
|
146
|
+
const mint = opts.mint ?? ((s: GithubInstallationTokenScope) => githubInstallationCredential({ scope: s }));
|
|
147
|
+
const windowMs = opts.windowMs ?? COALESCE_WINDOW_MS;
|
|
148
|
+
const minRemainingMs = opts.minRemainingMs ?? COALESCE_MIN_REMAINING_MS;
|
|
149
|
+
const sleepSync = opts.sleepSync ?? blockingSleep;
|
|
150
|
+
// Same boundary as the App config: a test process (or config-dir override) never writes the operational root.
|
|
151
|
+
if (!opts.cacheDir && (process.env.NODE_ENV === 'test' || process.env.ELANOUS_TEST_HOME)) return mint(scope);
|
|
152
|
+
let dir: string;
|
|
153
|
+
let identity: string;
|
|
154
|
+
try {
|
|
155
|
+
const configPath = appConfigPath();
|
|
156
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8')) as Partial<AppConfig>;
|
|
157
|
+
identity = JSON.stringify([configPath, config.id, config.installation_id]);
|
|
158
|
+
dir = opts.cacheDir ?? join(getElanousConfigDirOverride() ? getElanousConfigDir() : prodInstanceRoot(), 'cache', 'github-app-scoped');
|
|
159
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
160
|
+
} catch {
|
|
161
|
+
if (!opts.cacheDir) return mint(scope);
|
|
162
|
+
dir = opts.cacheDir;
|
|
163
|
+
identity = 'injected';
|
|
164
|
+
}
|
|
165
|
+
// Keyed by App installation and repository, so another App (another universe or owner) never reuses this token.
|
|
166
|
+
const key = createHash('sha256').update(`${identity}\0${scope.repository}`).digest('hex').slice(0, 32);
|
|
167
|
+
const file = join(dir, `${key}.json`);
|
|
168
|
+
const lock = join(dir, `${key}.lock`);
|
|
169
|
+
let waitStart: number | undefined;
|
|
170
|
+
const reuse = (): { token: string; expires_at: string } | null => {
|
|
171
|
+
try {
|
|
172
|
+
const entry = JSON.parse(readFileSync(file, 'utf8')) as { token?: unknown; expiresAt?: unknown; mintedAt?: unknown };
|
|
173
|
+
const t = now();
|
|
174
|
+
if (typeof entry.token === 'string' && entry.token && typeof entry.expiresAt === 'number' && typeof entry.mintedAt === 'number'
|
|
175
|
+
&& t - entry.mintedAt <= windowMs && entry.expiresAt - t >= minRemainingMs) {
|
|
176
|
+
try { debug.log('auth.github-app', 'token-coalesced', { ageMs: t - entry.mintedAt, expiresAt: new Date(entry.expiresAt).toISOString(), ...(waitStart === undefined ? {} : { waitedMs: t - waitStart }) }); } catch { /* fail open */ }
|
|
177
|
+
return { token: entry.token, expires_at: new Date(entry.expiresAt).toISOString() };
|
|
178
|
+
}
|
|
179
|
+
} catch { /* no usable entry */ }
|
|
180
|
+
return null;
|
|
181
|
+
};
|
|
182
|
+
const hit = opts.fresh ? null : reuse();
|
|
183
|
+
if (hit) return hit;
|
|
184
|
+
waitStart = now();
|
|
185
|
+
const deadline = waitStart + (opts.lockWaitMs ?? 15_000);
|
|
186
|
+
let locked = false;
|
|
187
|
+
for (;;) {
|
|
188
|
+
try { mkdirSync(lock); locked = true; break; }
|
|
189
|
+
catch {
|
|
190
|
+
try { if (now() - statSync(lock).mtimeMs > (opts.staleLockMs ?? 30_000)) { rmSync(lock, { recursive: true, force: true }); continue; } } catch { continue; }
|
|
191
|
+
if (now() >= deadline) break;
|
|
192
|
+
sleepSync(200);
|
|
193
|
+
const waited = opts.fresh ? null : reuse();
|
|
194
|
+
if (waited) return waited;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!locked) { try { debug.log('auth.github-app', 'token-coalesce-lock-timeout', { waitedMs: now() - (waitStart ?? now()) }); } catch { /* fail open */ } }
|
|
198
|
+
try {
|
|
199
|
+
const again = opts.fresh ? null : reuse();
|
|
200
|
+
if (again) return again;
|
|
201
|
+
const minted = mint(scope);
|
|
202
|
+
const expiresAt = minted ? Date.parse(minted.expires_at) : NaN;
|
|
203
|
+
if (minted && Number.isFinite(expiresAt)) {
|
|
204
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
205
|
+
try {
|
|
206
|
+
writeFileSync(temp, JSON.stringify({ token: minted.token, expiresAt, mintedAt: now() }), { mode: 0o600 });
|
|
207
|
+
renameSync(temp, file);
|
|
208
|
+
} catch { try { rmSync(temp, { force: true }); } catch { /* best effort */ } }
|
|
209
|
+
}
|
|
210
|
+
return minted;
|
|
211
|
+
} finally {
|
|
212
|
+
if (locked) { try { rmSync(lock, { recursive: true, force: true }); } catch { /* stale-lock rule recovers */ } }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -10,6 +10,9 @@ import { readFileSync, writeFileSync } from 'node:fs';
|
|
|
10
10
|
import { runGitCommand } from '../../git-fs/runner.js';
|
|
11
11
|
import { countTestDeclarations } from '../../self-implement/test-declarations.js';
|
|
12
12
|
import { NEXT_MD_PATH, resolveNextMdConflict } from '../../release-loop/next-md-merge.js';
|
|
13
|
+
import { getUserConfig } from '../../user-config.js';
|
|
14
|
+
import { debug } from '../../debug/log.js';
|
|
15
|
+
import { collectMergeIntent, defaultIntentGit, type IntentGit } from './merge-intent.js';
|
|
13
16
|
|
|
14
17
|
function deterministicNextMd(git: MergeGitSeam, worktreePath: string, file: string): string | null {
|
|
15
18
|
try {
|
|
@@ -64,6 +67,9 @@ export interface LlmMergeOutcome {
|
|
|
64
67
|
testDeclarationUnmeasured?: string[];
|
|
65
68
|
/** 해결기 응답이 LLM 공급자 오류 문구였다 — 파일 내용이 아니다(🅢 2026-09-27 #21086: 세 파일이 통째로 그 문구가 됐다). */
|
|
66
69
|
providerFailure?: string[];
|
|
70
|
+
/** LLM 입력 상한 초과: 사람 수확으로 넘긴다. 원래 브랜치는 abort 후 보존된다. */
|
|
71
|
+
reason?: 'conflict-input-too-large' | 'conflict-resolver-interrupted';
|
|
72
|
+
inputChars?: number;
|
|
67
73
|
/** 병합 결과가 양쪽 판 중 작은 쪽의 절반 아래로 줄었다 — 통째로 날린 것으로 보고 해결 실패로 친다. */
|
|
68
74
|
sizeCollapse?: Array<{ file: string; ours: number; theirs: number; merged: number }>;
|
|
69
75
|
/** `status: 'error'` 일 때 어느 단계인지. 선택 — 옛 호출자는 이 칸 없이 돌아도 된다. */
|
|
@@ -76,6 +82,13 @@ export interface LlmMergeOutcome {
|
|
|
76
82
|
export const PROVIDER_FAILURE_TEXT = /^\s*\[LLM PROVIDER (?:BLOCKED|STOPPED)\]/;
|
|
77
83
|
/** 이 줄 수 미만 파일은 크기 붕괴 판정에서 뺀다(작은 파일은 정당하게 크게 줄 수 있다). */
|
|
78
84
|
export const SIZE_COLLAPSE_MIN_LINES = 50;
|
|
85
|
+
export const DEFAULT_MERGE_CONFLICT_INPUT_MAX_CHARS = 60_000;
|
|
86
|
+
|
|
87
|
+
export class MergeConflictInputTooLarge extends Error {
|
|
88
|
+
constructor(readonly inputChars: number) {
|
|
89
|
+
super(`충돌 큼 — 사람 수확 (LLM 입력 ${inputChars}자)`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
79
92
|
|
|
80
93
|
function lineCount(s: string): number {
|
|
81
94
|
if (s.length === 0) return 0;
|
|
@@ -204,12 +217,21 @@ export async function mergeMainWithLlmResolve(
|
|
|
204
217
|
let merged: string;
|
|
205
218
|
try {
|
|
206
219
|
conflicted = git.readFile(abs);
|
|
220
|
+
} catch {
|
|
221
|
+
git.abort(worktreePath);
|
|
222
|
+
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles });
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
207
225
|
// release/next.md: concurrent landings each append a line — keep both without asking the LLM (REL7c).
|
|
208
226
|
const deterministic = f === NEXT_MD_PATH ? deterministicNextMd(git, worktreePath, f) : null;
|
|
209
227
|
merged = deterministic ?? await resolve(f, conflicted);
|
|
210
|
-
} catch {
|
|
228
|
+
} catch (error) {
|
|
211
229
|
git.abort(worktreePath); // LLM 예외 → base 유지
|
|
212
|
-
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles
|
|
230
|
+
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles,
|
|
231
|
+
...(error instanceof MergeConflictInputTooLarge
|
|
232
|
+
? { reason: 'conflict-input-too-large' as const, inputChars: error.inputChars }
|
|
233
|
+
: { reason: 'conflict-resolver-interrupted' as const }),
|
|
234
|
+
});
|
|
213
235
|
}
|
|
214
236
|
if (PROVIDER_FAILURE_TEXT.test(merged)) {
|
|
215
237
|
git.abort(worktreePath); // 해결기가 공급자 오류 문구를 «내용»으로 돌려줬다 → base 유지
|
|
@@ -328,27 +350,68 @@ export function defaultGitMergeSeam(): MergeGitSeam {
|
|
|
328
350
|
}
|
|
329
351
|
|
|
330
352
|
/** 실 LLM 충돌 해결 어댑터(streamLLM·sol). 코드펜스/설명 제거해 완결 파일만. */
|
|
331
|
-
export async function defaultLlmResolve(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
353
|
+
export async function defaultLlmResolve(
|
|
354
|
+
filePath: string, conflicted: string, mergeTarget: string,
|
|
355
|
+
options: { worktreePath?: string; mode?: 'off' | 'shadow' | 'on'; git?: IntentGit; stream?: typeof import('../../llm.js')['streamLLM'] } = {},
|
|
356
|
+
): Promise<string> {
|
|
357
|
+
let rawConfig: { mergeConflictInputMaxChars?: unknown; mergeIntent?: unknown } | undefined;
|
|
358
|
+
try { rawConfig = getUserConfig().raw.selfImplement as typeof rawConfig; }
|
|
359
|
+
catch { /* unreadable config keeps the old resolver and default input limit */ }
|
|
360
|
+
const configuredMax = rawConfig?.mergeConflictInputMaxChars;
|
|
361
|
+
const maxChars = typeof configuredMax === 'number' && Number.isSafeInteger(configuredMax) && configuredMax > 0
|
|
362
|
+
? configuredMax : DEFAULT_MERGE_CONFLICT_INPUT_MAX_CHARS;
|
|
363
|
+
let rawMode: unknown = options.mode;
|
|
364
|
+
if (rawMode === undefined) {
|
|
365
|
+
rawMode = rawConfig?.mergeIntent;
|
|
366
|
+
}
|
|
367
|
+
const mode = rawMode === 'shadow' || rawMode === 'on' ? rawMode : 'off';
|
|
368
|
+
const resolve = async (intent?: { ours: string | null; theirs: string[] }): Promise<string> => {
|
|
369
|
+
const prompt = conflictResolvePrompt(filePath, conflicted, mergeTarget, intent);
|
|
370
|
+
if (prompt.length > maxChars) throw new MergeConflictInputTooLarge(prompt.length);
|
|
371
|
+
const streamLLM = options.stream ?? (await import('../../llm.js')).streamLLM;
|
|
372
|
+
const out = await streamLLM([{ role: 'user', content: prompt }], () => {}, { model: process.env.ELANOUS_CONFLICT_MODEL || tierModel('better'), reasoningEffort: 'medium' });
|
|
373
|
+
// ⛔ 라우터는 공급자가 전부 막히면 던지지 않고 오류 문구를 돌려준다 — 그것은 파일 내용이 아니다.
|
|
374
|
+
if (PROVIDER_FAILURE_TEXT.test(out)) throw new Error(`conflict resolve: LLM provider failure for ${filePath}`);
|
|
375
|
+
return `${out.replace(/^```[\w.-]*\n?/, '').replace(/\n?```\s*$/, '').trimEnd()}\n`;
|
|
376
|
+
};
|
|
377
|
+
if (mode === 'off') return resolve();
|
|
378
|
+
const collect = () => collectMergeIntent({ worktreePath: options.worktreePath ?? process.cwd(), filePath, mergeTarget, git: options.git ?? defaultIntentGit });
|
|
379
|
+
if (mode === 'on') return resolve(collect());
|
|
380
|
+
const plain = await resolve();
|
|
381
|
+
try {
|
|
382
|
+
const intent = collect();
|
|
383
|
+
const withIntent = await resolve(intent);
|
|
384
|
+
debug.log('self-dev.merge', 'intent-shadow', {
|
|
385
|
+
file: filePath, same: plain === withIntent,
|
|
386
|
+
plainMarkers: hasConflictMarkers(plain), intentMarkers: hasConflictMarkers(withIntent),
|
|
387
|
+
plainLines: lineCount(plain), intentLines: lineCount(withIntent),
|
|
388
|
+
oursIntent: intent.ours !== null, theirsCount: intent.theirs.length,
|
|
389
|
+
});
|
|
390
|
+
} catch (error) {
|
|
391
|
+
try { debug.log('self-dev.merge', 'intent-shadow-failed', { file: filePath, error: String(error) }); }
|
|
392
|
+
catch { /* shadow observation must not affect the adopted result */ }
|
|
393
|
+
}
|
|
394
|
+
return plain;
|
|
337
395
|
}
|
|
338
396
|
|
|
339
397
|
/** 편의 — 실 git+LLM 으로 호출부가 해석한 ref를 worktree 에 지능 정합한다. */
|
|
340
398
|
export async function mergeMainIntoWorktreeWithLlm(worktreePath: string, mergeTarget: string): Promise<LlmMergeOutcome> {
|
|
341
|
-
return mergeMainWithLlmResolve(worktreePath, mergeTarget, (filePath, conflicted) => defaultLlmResolve(filePath, conflicted, mergeTarget), defaultGitMergeSeam());
|
|
399
|
+
return mergeMainWithLlmResolve(worktreePath, mergeTarget, (filePath, conflicted) => defaultLlmResolve(filePath, conflicted, mergeTarget, { worktreePath }), defaultGitMergeSeam());
|
|
342
400
|
}
|
|
343
401
|
|
|
344
402
|
/** LLM 충돌 해결 프롬프트(순수·테스트) — ours(walker)·theirs(호출자가 전달한 정합 대상) 종합 지시. */
|
|
345
|
-
export function conflictResolvePrompt(filePath: string, conflictedContent: string, mergeTarget: string): string {
|
|
403
|
+
export function conflictResolvePrompt(filePath: string, conflictedContent: string, mergeTarget: string, intent?: { ours: string | null; theirs: string[] }): string {
|
|
346
404
|
return [
|
|
347
405
|
'너는 git merge 충돌을 지능적으로 해결하는 엔지니어다. 아래 파일은 3-way merge 충돌 마커를 포함한다:',
|
|
348
406
|
' <<<<<<< ours = 현재 브랜치(walker 가 이 미션에서 만든 산출물)',
|
|
349
407
|
' ======= 사이 = 양쪽 버전',
|
|
350
408
|
` >>>>>>> theirs = ${mergeTarget}(호출자가 해석해 전달한 정합 대상)`,
|
|
351
409
|
'',
|
|
410
|
+
...(intent ? [
|
|
411
|
+
`ours 의 의도: ${intent.ours ?? '(확인 불가)'}`,
|
|
412
|
+
`theirs 에 먼저 착지한 변경: ${intent.theirs.length ? intent.theirs.join(' · ') : '(확인 불가)'}`,
|
|
413
|
+
'',
|
|
414
|
+
] : []),
|
|
352
415
|
'해결 원칙:',
|
|
353
416
|
'- 양쪽의 의도를 **모두 보존**하며 종합한다(한쪽을 통째로 버리지 않는다).',
|
|
354
417
|
`- 같은 목적의 중복(예: 같은 테스트·같은 함수)은 **theirs(${mergeTarget}) 버전을 채택**하고 ours 의 중복은 제거.`,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { runGitCommand } from '../../git-fs/runner.js';
|
|
4
|
+
|
|
5
|
+
export interface MergeIntent {
|
|
6
|
+
ours: string | null;
|
|
7
|
+
theirs: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type IntentGit = (worktreePath: string, args: string[]) => { status: number | null; stdout: string };
|
|
11
|
+
|
|
12
|
+
export const defaultIntentGit: IntentGit = (worktreePath, args) => {
|
|
13
|
+
const result = runGitCommand(worktreePath, args, { encoding: 'utf8' });
|
|
14
|
+
return { status: result.status, stdout: result.stdout ?? '' };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Read only commits introduced on each side of the merge base; a missing ref/document never blocks resolution. */
|
|
18
|
+
export function collectMergeIntent({ worktreePath, filePath, mergeTarget, git }: {
|
|
19
|
+
worktreePath: string;
|
|
20
|
+
filePath: string;
|
|
21
|
+
mergeTarget: string;
|
|
22
|
+
git: IntentGit;
|
|
23
|
+
}): MergeIntent {
|
|
24
|
+
const read = (...args: string[]): string | null => {
|
|
25
|
+
try {
|
|
26
|
+
const result = git(worktreePath, args);
|
|
27
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
28
|
+
} catch { return null; }
|
|
29
|
+
};
|
|
30
|
+
const base = read('merge-base', 'HEAD', mergeTarget);
|
|
31
|
+
if (!base) return { ours: null, theirs: [] };
|
|
32
|
+
|
|
33
|
+
let ours: string | null = null;
|
|
34
|
+
const added = read('log', '--reverse', '--diff-filter=A', '--format=', '--name-only', '-z', `${base}..HEAD`, '--', 'docs/goals/');
|
|
35
|
+
for (const goal of added?.split('\0').map((file) => file.trim()).filter((file) => /^docs\/goals\/[^/]+$/.test(file)) ?? []) {
|
|
36
|
+
try {
|
|
37
|
+
const content = readFileSync(join(worktreePath, goal), 'utf8');
|
|
38
|
+
const lines = content.split(/\r?\n/);
|
|
39
|
+
const title = lines.find((line) => /^#\s+\S/.test(line))?.replace(/^#\s+/, '').trim()
|
|
40
|
+
?? lines.find((line) => line.trim().length > 0)?.trim();
|
|
41
|
+
const situation = lines.find((line) => /^Situation:/.test(line))?.trim();
|
|
42
|
+
const complication = lines.find((line) => /^Complication:/.test(line))?.trim();
|
|
43
|
+
ours = [title, situation, complication].filter(Boolean).join('\n') || null;
|
|
44
|
+
if (ours) break;
|
|
45
|
+
} catch { /* missing goal: try the next added document */ }
|
|
46
|
+
}
|
|
47
|
+
if (!ours) {
|
|
48
|
+
const subjects = read('log', '-3', '--format=%s', `${base}..HEAD`);
|
|
49
|
+
ours = subjects || null;
|
|
50
|
+
}
|
|
51
|
+
const subjects = read('log', '-5', '--format=%s', `${base}..${mergeTarget}`, '--', filePath);
|
|
52
|
+
return { ours, theirs: subjects ? subjects.split('\n') : [] };
|
|
53
|
+
}
|