elanous 0.2.25 → 0.2.28
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/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Detached HQ parent for one same-PR Pod repair. It never pushes or opens a PR. */
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, renameSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import type { RepairCommitResult } from './repair-branch-cas.js';
|
|
6
|
+
import type { TaskChildLlm } from './task-hand.js';
|
|
7
|
+
|
|
8
|
+
const SHA40 = /^[0-9a-f]{40}$/i;
|
|
9
|
+
const SHA256 = /^[0-9a-f]{64}$/i;
|
|
10
|
+
const MARKER = 'ELANOUS_POD_REPAIR_RESULT ';
|
|
11
|
+
|
|
12
|
+
export interface RepairWorkerRequest {
|
|
13
|
+
card: string; pr: number; head: string; repo: string; resultPath: string;
|
|
14
|
+
entry: string; configDir: string; runId: string; seat?: string;
|
|
15
|
+
model: TaskChildLlm; goal: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RepairWorkerResult {
|
|
19
|
+
pr: number; head: string; runId: string; ok: boolean; reason?: string;
|
|
20
|
+
job?: string; artifactDir?: string; result?: RepairCommitResult;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function repairHarnessArgs(request: RepairWorkerRequest): string[] {
|
|
24
|
+
if (!Number.isSafeInteger(request.pr) || request.pr < 1 || !SHA40.test(request.head)
|
|
25
|
+
|| !request.goal.trim() || !request.runId) throw new Error('repair worker request invalid');
|
|
26
|
+
return [request.entry, '--config-dir', request.configDir, 'harness', 'say', '--substrate', 'pod',
|
|
27
|
+
'--source', `pr:${request.pr}`, '--repair-commit-only', request.head,
|
|
28
|
+
...(request.seat ? ['--seat', request.seat] : []), '--no-queue', '--no-auto-merge',
|
|
29
|
+
'--child-llm-provider', request.model.provider, '--child-llm-model', request.model.model,
|
|
30
|
+
...(request.model.effort ? ['--child-llm-effort', request.model.effort] : []), '--', request.goal];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseRepairMarker(output: string, pr: number, head: string, runId: string): Pick<RepairWorkerResult, 'job' | 'artifactDir' | 'result'> | null {
|
|
34
|
+
const line = output.split(/\r?\n/).reverse().find((entry: string) => entry.startsWith(MARKER));
|
|
35
|
+
if (!line) return null;
|
|
36
|
+
let value: unknown;
|
|
37
|
+
try { value = JSON.parse(line.slice(MARKER.length)); } catch { return null; }
|
|
38
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
39
|
+
const v = value as Record<string, unknown>;
|
|
40
|
+
if (v.pr !== pr || v.runId !== runId || typeof v.baseHead !== 'string' || v.baseHead.toLowerCase() !== head.toLowerCase()
|
|
41
|
+
|| typeof v.commit !== 'string' || !SHA40.test(v.commit) || typeof v.bundleSha256 !== 'string' || !SHA256.test(v.bundleSha256)
|
|
42
|
+
|| typeof v.job !== 'string' || !v.job || typeof v.artifactDir !== 'string' || !v.artifactDir.startsWith('/')) return null;
|
|
43
|
+
return { job: v.job, artifactDir: v.artifactDir,
|
|
44
|
+
result: { pr, baseHead: v.baseHead, runId, commit: v.commit, bundleSha256: v.bundleSha256 } };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function runRepairWorker(request: RepairWorkerRequest): Promise<RepairWorkerResult> {
|
|
48
|
+
const result: RepairWorkerResult = { pr: request.pr, head: request.head, runId: request.runId, ok: false };
|
|
49
|
+
try {
|
|
50
|
+
const args = repairHarnessArgs(request);
|
|
51
|
+
const child = spawn(process.execPath, args, {
|
|
52
|
+
cwd: request.repo, env: { ...process.env, ELANOUS_RUN_ID: request.runId }, stdio: ['ignore', 'pipe', 'pipe'],
|
|
53
|
+
});
|
|
54
|
+
let output = '';
|
|
55
|
+
const append = (data: Buffer) => { output = (output + data.toString('utf8')).slice(-512_000); };
|
|
56
|
+
child.stdout?.on('data', append);
|
|
57
|
+
child.stderr?.on('data', append);
|
|
58
|
+
const code = await new Promise<number | null>((resolve, reject) => {
|
|
59
|
+
child.once('error', reject);
|
|
60
|
+
child.once('close', resolve);
|
|
61
|
+
});
|
|
62
|
+
const marker = parseRepairMarker(output, request.pr, request.head, request.runId);
|
|
63
|
+
if (code !== 0) result.reason = `harness exited ${code}: ${output.slice(-600)}`;
|
|
64
|
+
else if (!marker) result.reason = `repair artifact marker missing or invalid: ${output.slice(-600)}`;
|
|
65
|
+
else Object.assign(result, { ok: true, ...marker });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
result.reason = error instanceof Error ? error.message : String(error);
|
|
68
|
+
}
|
|
69
|
+
mkdirSync(dirname(request.resultPath), { recursive: true });
|
|
70
|
+
const tmp = `${request.resultPath}.${process.pid}.tmp`;
|
|
71
|
+
writeFileSync(tmp, JSON.stringify(result));
|
|
72
|
+
renameSync(tmp, request.resultPath);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function parseRepairWorkerResult(text: string, pr: number, head: string, runId: string): RepairWorkerResult | null {
|
|
77
|
+
let value: unknown;
|
|
78
|
+
try { value = JSON.parse(text); } catch { return null; }
|
|
79
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
80
|
+
const v = value as RepairWorkerResult;
|
|
81
|
+
if (v.pr !== pr || typeof v.head !== 'string' || v.head.toLowerCase() !== head.toLowerCase() || v.runId !== runId || typeof v.ok !== 'boolean') return null;
|
|
82
|
+
if (v.ok && (typeof v.artifactDir !== 'string' || !v.artifactDir.startsWith('/') || typeof v.job !== 'string' || !v.job
|
|
83
|
+
|| !v.result || v.result.pr !== pr || typeof v.result.baseHead !== 'string'
|
|
84
|
+
|| v.result.baseHead.toLowerCase() !== head.toLowerCase() || v.result.runId !== runId || !SHA40.test(v.result.commit)
|
|
85
|
+
|| !SHA256.test(v.result.bundleSha256))) return null;
|
|
86
|
+
return v;
|
|
87
|
+
}
|
|
@@ -13,15 +13,23 @@
|
|
|
13
13
|
import { readFileSync } from 'node:fs';
|
|
14
14
|
import { dirname, join } from 'node:path';
|
|
15
15
|
import { isNoneSentinelFinding } from '../agent-substrate/pr-reviewer.js';
|
|
16
|
+
import { repairReviewDigest } from './repair-review.js';
|
|
16
17
|
import { debug } from '../debug/log.js';
|
|
17
|
-
import { LIVE_MOVE_RESULT_EVENT, readTaskAgentState, updateTaskCard, type TaskCard, type TaskCardEvent } from './task-hand.js';
|
|
18
|
+
import { LIVE_MOVE_RESULT_EVENT, cardPrNumber, readTaskAgentState, readTaskCard, updateTaskCard, type TaskCard, type TaskCardEvent } from './task-hand.js';
|
|
19
|
+
import type { RejudgeDeps } from './rejudge-on-head.js';
|
|
18
20
|
|
|
19
21
|
export const REVIEW_RESULT_MISSING_EFFECT = 'review process produced no result';
|
|
20
22
|
/** 결과 파일은 있는데 읽지 못했다(EACCES 등) — «결과 없음»과 가른다(결과가 있었을 수 있다). */
|
|
21
23
|
export const REVIEW_RESULT_UNREADABLE_EFFECT = 'review result file unreadable';
|
|
24
|
+
/** Detached self review child: only this card and exact result file may trigger a completion tick. */
|
|
25
|
+
export const TA_REVIEW_COMPLETION_CARD_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_CARD';
|
|
26
|
+
export const TA_REVIEW_COMPLETION_STATE_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_STATE';
|
|
27
|
+
export const TA_REVIEW_COMPLETION_RESULT_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_RESULT';
|
|
22
28
|
/** 리뷰 한 판(1차 ⊕ 폴백 · ACP 300s 상한 여러 번)을 넉넉히 덮는 대기 상한 — 이 안에는 «아직»으로 본다. */
|
|
23
29
|
export const REVIEW_RESULT_WAIT_MS = 90 * 60_000;
|
|
24
30
|
|
|
31
|
+
const reviewRejudgeInFlight = new Set<string>();
|
|
32
|
+
|
|
25
33
|
const SHA40 = /^[0-9a-f]{40}$/i;
|
|
26
34
|
|
|
27
35
|
/** 결과 파일 경로 — 카드 상태 파일 옆 `review-results/` · 카드 id 는 파일 이름에 안전한 글자만. */
|
|
@@ -88,6 +96,8 @@ export function pendingReviewResults(card: TaskCard): ReviewRequest[] {
|
|
|
88
96
|
export interface CaptureReviewResultsOptions {
|
|
89
97
|
/** 이 카드들만 본다 — 없으면 상태 파일의 모든 카드. */
|
|
90
98
|
cardIds?: readonly string[];
|
|
99
|
+
/** A detached completion callback may only harvest its own result file. */
|
|
100
|
+
resultPaths?: readonly string[];
|
|
91
101
|
now?: () => Date;
|
|
92
102
|
/** 시험 seam — 결과 파일 읽기(없으면 ENOENT 를 던진다). */
|
|
93
103
|
readFile?: (path: string) => string;
|
|
@@ -123,6 +133,7 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
|
|
|
123
133
|
if (!card) continue;
|
|
124
134
|
for (const request of pendingReviewResults(card)) {
|
|
125
135
|
const resultPath = request.resultPath!;
|
|
136
|
+
if (opts.resultPaths && !opts.resultPaths.includes(resultPath)) continue;
|
|
126
137
|
let text: string | null = null;
|
|
127
138
|
let readError: string | null = null;
|
|
128
139
|
let unreadable = false;
|
|
@@ -145,7 +156,7 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
|
|
|
145
156
|
at: now.toISOString(), event: LIVE_MOVE_RESULT_EVENT, kind: 'review', pr: request.pr,
|
|
146
157
|
...(parsed.head ? { head: parsed.head } : {}),
|
|
147
158
|
verdict: parsed.verdict, reviewed: parsed.reviewed, mustFix: parsed.mustFix, reviewRoute: parsed.reviewRoute,
|
|
148
|
-
resultPath,
|
|
159
|
+
resultPath, reviewSha256: repairReviewDigest(text!),
|
|
149
160
|
effect: `review result captured · verdict ${parsed.verdict ?? 'none'} · reviewed ${parsed.reviewed ?? 'unknown'} · must-fix ${parsed.mustFix ?? 'unknown'}`,
|
|
150
161
|
...(parsed.error ? { detail: parsed.error.slice(0, 300) } : {}),
|
|
151
162
|
};
|
|
@@ -184,6 +195,59 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
|
|
|
184
195
|
return out;
|
|
185
196
|
}
|
|
186
197
|
|
|
198
|
+
/** Run after the detached review child has fully written its JSON stdout. Never infer a card from PR alone. */
|
|
199
|
+
export async function rejudgeCompletedTaskReview(
|
|
200
|
+
env: Record<string, string | undefined> = process.env,
|
|
201
|
+
rejudge?: (cardId: string, statePath: string) => Promise<unknown>,
|
|
202
|
+
rejudgeDeps?: RejudgeDeps,
|
|
203
|
+
): Promise<boolean> {
|
|
204
|
+
const cardId = env[TA_REVIEW_COMPLETION_CARD_ENV];
|
|
205
|
+
const statePath = env[TA_REVIEW_COMPLETION_STATE_ENV];
|
|
206
|
+
const resultPath = env[TA_REVIEW_COMPLETION_RESULT_ENV];
|
|
207
|
+
if (!cardId || !statePath || !resultPath) return false;
|
|
208
|
+
const before = readTaskCard(cardId, statePath);
|
|
209
|
+
const bound = before?.reviewRequests?.find((item) => item.resultPath === resultPath);
|
|
210
|
+
if (before?.status !== 'launched' || before.taLand !== true || !bound || cardPrNumber(before) !== bound.pr
|
|
211
|
+
|| !SHA40.test(bound.head) || (before.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)) return false;
|
|
212
|
+
captureReviewResults(statePath, { cardIds: [cardId], resultPaths: [resultPath] });
|
|
213
|
+
const key = `${statePath}\0${cardId}\0${resultPath}`;
|
|
214
|
+
if (reviewRejudgeInFlight.has(key)) return false;
|
|
215
|
+
const card = readTaskCard(cardId, statePath);
|
|
216
|
+
const request = card?.reviewRequests?.find((item) => item.resultPath === resultPath);
|
|
217
|
+
if (card?.status !== 'launched' || card.taLand !== true || !request || cardPrNumber(card) !== request.pr
|
|
218
|
+
|| !(card.history ?? []).some((item) => item.event === LIVE_MOVE_RESULT_EVENT && item.resultPath === resultPath && item.kind === 'review'
|
|
219
|
+
&& item.pr === request.pr && SHA40.test(request.head) && SHA40.test(item.head ?? '')
|
|
220
|
+
&& item.effect?.startsWith('review result captured') && item.reviewed === true && item.head?.toLowerCase() === request.head.toLowerCase())
|
|
221
|
+
|| (card.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)) return false;
|
|
222
|
+
reviewRejudgeInFlight.add(key);
|
|
223
|
+
try {
|
|
224
|
+
const run = async (): Promise<unknown> => {
|
|
225
|
+
if (rejudgeDeps) {
|
|
226
|
+
const { rejudgeShadowCard } = await import('./rejudge-on-head.js');
|
|
227
|
+
return rejudgeShadowCard(cardId, statePath, rejudgeDeps, { pr: request.pr, head: request.head, kind: 'review', resultPath });
|
|
228
|
+
} else if (rejudge) return rejudge(cardId, statePath);
|
|
229
|
+
else {
|
|
230
|
+
const { rejudgeAsyncResultCard } = await import('./rejudge-on-head.js');
|
|
231
|
+
return rejudgeAsyncResultCard(cardId, statePath, {}, { pr: request.pr, head: request.head, kind: 'review', resultPath });
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const { requireCompletedHeadRejudge } = await import('./rejudge-on-head.js');
|
|
235
|
+
try { requireCompletedHeadRejudge(await run(), request.pr, request.head); }
|
|
236
|
+
catch { requireCompletedHeadRejudge(await run(), request.pr, request.head); } // Bounded retry; no marker on failed outcomes.
|
|
237
|
+
updateTaskCard(statePath, cardId, (current) => current?.status === 'launched' && current.taLand === true && cardPrNumber(current) === request.pr
|
|
238
|
+
&& (current.reviewRequests ?? []).some((item) => item.pr === request.pr && SHA40.test(item.head) && item.head.toLowerCase() === request.head.toLowerCase() && item.resultPath === resultPath)
|
|
239
|
+
&& (current.history ?? []).some((item) => item.event === LIVE_MOVE_RESULT_EVENT && item.resultPath === resultPath && item.kind === 'review'
|
|
240
|
+
&& item.pr === request.pr && SHA40.test(item.head ?? '') && item.head?.toLowerCase() === request.head.toLowerCase()
|
|
241
|
+
&& item.reviewed === true && item.effect?.startsWith('review result captured'))
|
|
242
|
+
&& !(current.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)
|
|
243
|
+
? { ...current, history: [...(current.history ?? []), { at: new Date().toISOString(), event: 'async-result-rejudge', kind: 'review', pr: request.pr, head: request.head, resultPath, effect: 'completion callback succeeded' }] }
|
|
244
|
+
: undefined);
|
|
245
|
+
} finally {
|
|
246
|
+
reviewRejudgeInFlight.delete(key);
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
|
|
187
251
|
/**
|
|
188
252
|
* 다음 수 판단의 입력 — 카드에 회수된 리뷰 결과 중 «그 머리(`head`)»의 «가장 최근» 결과가 실제로 돈(reviewed) pass 이고
|
|
189
253
|
* 리뷰한 머리가 40자 sha 일 때만 `selfReview` 모양으로 돌려준다(슈퍼바이저가 나르는 `self-implement-cli` 의 규칙과 같다 ·
|
package/src/task-agent/shadow.ts
CHANGED
|
@@ -10,17 +10,22 @@
|
|
|
10
10
|
* - 관측: `debug.log('task-agent', 'shadow-move', …)` → `elanous logs --category task-agent`.
|
|
11
11
|
*/
|
|
12
12
|
import { lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
13
|
+
import { randomUUID } from 'node:crypto';
|
|
13
14
|
import { extname, join, relative, resolve } from 'node:path';
|
|
14
15
|
import { debug } from '../debug/log.js';
|
|
15
|
-
import { readTaskAgentState, readTaskCard, taskAgentStatePath, type CompletionKind, type TaskCard } from './task-hand.js';
|
|
16
|
+
import { readTaskAgentState, readTaskCard, taskAgentStatePath, TASK_CARD_PREFIX, type CompletionKind, type TaskCard } from './task-hand.js';
|
|
16
17
|
import type { SupervisorJobResult, SupervisorStopReason } from '../self-dev/run-supervisor.js';
|
|
17
|
-
import { executeNextAction, type DeliveryKind, type NextAction } from './actions.js';
|
|
18
|
-
import { judgeNextMove, type TaskJudgeInput, type TaskJudgement } from './judge.js';
|
|
18
|
+
import { executeNextAction, type ActionDeps, type DeliveryKind, type NextAction } from './actions.js';
|
|
19
|
+
import { judgeNextMove, LIVE_RETRY_MOVES, type TaskJudgeInput, type TaskJudgement } from './judge.js';
|
|
19
20
|
import { HARNESS_RUN_ID_ENV } from '../harness/harness-space.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
21
|
+
import { appendRunLedgerEntry, loadRunLedger } from '../self-implement/run-ledger.js';
|
|
22
|
+
import { collectWiringEvidence, readCardRunSnapshot, type WiringEvidenceDeps, type CardRunEvidenceDeps } from './card-evidence.js';
|
|
23
|
+
import { remoteLedgerMirrorDir } from './parent-host.js';
|
|
24
|
+
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
25
|
+
import { liveMoveHistoryEntry, recordLiveMoveHistory } from './live-move-history.js';
|
|
26
|
+
import { updateTaskCard } from './task-hand.js';
|
|
22
27
|
import { capturedSelfReview, captureReviewResults, hasCapturedPass } from './review-result-capture.js';
|
|
23
|
-
import { configuredTaskAgentLiveMoves, defaultPrHead, executeLiveGreenProposal, executeLiveLand, executeLiveReview, type LiveMoveDeps, type LiveMoveResult, type TaskAgentLiveMove } from './live-moves.js';
|
|
28
|
+
import { configuredTaskAgentLiveMoves, defaultPrHead, executeLiveDecisionCard, executeLiveGreenProposal, executeLiveLand, executeLiveReview, type LiveMoveDeps, type LiveMoveResult, type TaskAgentLiveMove } from './live-moves.js';
|
|
24
29
|
|
|
25
30
|
export interface TaskAgentShadowInput {
|
|
26
31
|
runId: string | null;
|
|
@@ -46,12 +51,16 @@ export interface TaskAgentShadowMove {
|
|
|
46
51
|
pr: number | null;
|
|
47
52
|
/** TA-JUDGE-LIVE-SAFE — `taskAgent.liveMoves` 가 이 수를 실제로 했을 때만 실린다(없으면 종전 그대로 그림자). */
|
|
48
53
|
liveMove?: LiveMoveResult;
|
|
54
|
+
/** 원격 카드 결선이 미결일 때 런 원장 대기열 보존 결과. */
|
|
55
|
+
cardBinding?: 'pending' | 'queue-failed';
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
export interface TaskAgentShadowDeps {
|
|
52
59
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
53
|
-
/**
|
|
54
|
-
command?:
|
|
60
|
+
/** 재발사 live 실행부 명령(주입되지 않으면 shadow). */
|
|
61
|
+
command?: ActionDeps['command'];
|
|
62
|
+
/** 재발사 live 부하 관문(주입되지 않으면 shadow). */
|
|
63
|
+
loadGate?: ActionDeps['loadGate'];
|
|
55
64
|
readCard?: (taskId: string) => TaskCard | undefined;
|
|
56
65
|
/** Explicit live invocation only; supervisor's default hook always uses shadow. */
|
|
57
66
|
mode?: 'shadow' | 'live';
|
|
@@ -65,8 +74,12 @@ export interface TaskAgentShadowDeps {
|
|
|
65
74
|
wiring?: Omit<WiringEvidenceDeps, 'card'>;
|
|
66
75
|
/** Process env for the launched-run id (`ELANOUS_RUN_ID`) — test seam; default process.env. */
|
|
67
76
|
env?: NodeJS.ProcessEnv;
|
|
77
|
+
/** Remote pending-move ledger seam; defaults to the run ledger. */
|
|
78
|
+
queueMove?: typeof appendRunLedgerEntry;
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
export const REMOTE_CARD_MOVE_PENDING = 'task-agent-card-move-pending';
|
|
82
|
+
|
|
70
83
|
/** 카드 조회 상태 파일 — 조회 경로 전부가 같은 파일을 본다. */
|
|
71
84
|
function cardStatePath(deps: TaskAgentShadowDeps): string {
|
|
72
85
|
return deps.statePath ?? deps.live?.statePath ?? taskAgentStatePath();
|
|
@@ -175,6 +188,13 @@ function shadowCompletion(card: TaskCard | undefined, lookupFailed: boolean): Co
|
|
|
175
188
|
}
|
|
176
189
|
|
|
177
190
|
export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, deps: TaskAgentShadowDeps = {}): Promise<TaskAgentShadowMove> {
|
|
191
|
+
const env = deps.env ?? process.env;
|
|
192
|
+
const remoteChild = !!env.ELANOUS_POD_NAME;
|
|
193
|
+
const hintedCard = env.ELANOUS_TASK_CARD_ID?.trim();
|
|
194
|
+
const pendingCardId = hintedCard?.startsWith(TASK_CARD_PREFIX) && hintedCard.length <= 63 && /^[A-Za-z0-9._-]+$/.test(hintedCard) ? hintedCard : null;
|
|
195
|
+
const parentRunId = env.ELANOUS_PARENT_RUN_ID?.trim() || null;
|
|
196
|
+
const matchesLaunchCard = (candidate: TaskCard | undefined): boolean => !!candidate && !!parentRunId
|
|
197
|
+
&& candidate.id === pendingCardId && candidate.runId === parentRunId;
|
|
178
198
|
// TA-REVIEW-RESULT-CAPTURE — 판단 «전»에 지난 live review 의 결과 파일을 회수한다(카드 원장을 아는 때만 · 던지지 않는다).
|
|
179
199
|
// 대기 중인 요청이 없으면 아무것도 쓰지 않는다(결과 파일 없는 카드의 동작은 종전과 같다).
|
|
180
200
|
const reviewLedger = cardLedgerPath(deps);
|
|
@@ -188,14 +208,22 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
188
208
|
let card: TaskCard | undefined;
|
|
189
209
|
let cardLookupFailed = false;
|
|
190
210
|
try {
|
|
191
|
-
|
|
192
|
-
if (
|
|
211
|
+
// Pod 에 발사 카드 id 가 있으면 그 id 만 믿는다. 같은 문장을 가진 다른 카드가 있어도 묶지 않는다.
|
|
212
|
+
if (remoteChild) {
|
|
213
|
+
if (pendingCardId) {
|
|
214
|
+
const found = deps.readCard ? deps.readCard(pendingCardId) : readTaskCard(pendingCardId, cardStatePath(deps));
|
|
215
|
+
if (matchesLaunchCard(found)) card = found;
|
|
216
|
+
}
|
|
217
|
+
} else if (successful) {
|
|
218
|
+
card = deps.readCard ? deps.readCard(successful.taskId) : readTaskCard(successful.taskId, cardStatePath(deps));
|
|
219
|
+
}
|
|
220
|
+
if (!card && successful && !deps.readCard && !remoteChild) {
|
|
193
221
|
const tasks = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(cardStatePath(deps)).tasks ?? {};
|
|
194
222
|
const matches = Object.values(tasks).filter(candidate => candidate.status === 'launched' && candidate.text === successful.feature);
|
|
195
223
|
if (matches.length === 1) card = matches[0];
|
|
196
224
|
}
|
|
197
225
|
// TA-OWN-ALL-RUNS — 런 소유 카드: 입력 runId ⊕ 상속 ELANOUS_RUN_ID(Pod 자식의 부모 런) 중 카드 runId 와 «정확히» 같은 것만.
|
|
198
|
-
if (!card && !deps.readCard) card = runCardFor(input.runId, deps);
|
|
226
|
+
if (!card && !deps.readCard && !remoteChild) card = runCardFor(input.runId, deps);
|
|
199
227
|
} catch {
|
|
200
228
|
// Card lookup is optional; preserve the original PR judgement when unavailable — but mark the completion kind as unmeasured.
|
|
201
229
|
cardLookupFailed = true;
|
|
@@ -209,7 +237,7 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
209
237
|
try {
|
|
210
238
|
const reviewedPr = judgeInput.pr;
|
|
211
239
|
const prRun = input.results.find((result) => result.prNumber === reviewedPr);
|
|
212
|
-
const prCard = prRun ? (card && successful?.taskId === prRun.taskId ? card : liveCardFor([prRun], deps, input.runId)) : undefined;
|
|
240
|
+
const prCard = prRun ? (remoteChild ? card : card && successful?.taskId === prRun.taskId ? card : liveCardFor([prRun], deps, input.runId)) : undefined;
|
|
213
241
|
// 회수한 판정은 «리뷰한 머리»의 것이다 — 지금 PR 머리의 결과만 입력으로 쓴다(A 의 pass 로 B 를 land 쪽으로 보내지 않게 ·
|
|
214
242
|
// 여러 머리 결과가 역순으로 회수돼도 지금 머리 것을 고른다). 지금 머리 = 그 런 작업 트리에서 PR 머리 조회 ·
|
|
215
243
|
// 작업 트리가 없으면(Pod) 런이 낸 머리. 못 정하면 쓰지 않는다(종전처럼 review 수 → 새 머리면 새 리뷰).
|
|
@@ -239,6 +267,9 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
239
267
|
: { move: 'wait', stage: 'waiting', reason: '전달 후보 없음' }
|
|
240
268
|
: judgeNextMove(judgeInput);
|
|
241
269
|
if (cardLookupFailed) judgement.reason = `${judgement.reason} · 종결 종류 미확인(카드 조회 실패)`;
|
|
270
|
+
// 판단마다 해석한다 — 허용되지 않은 수를 판단할 때도 모르는 항목 경고가 남게.
|
|
271
|
+
const liveMoves = deps.liveMoves ?? await configuredTaskAgentLiveMoves();
|
|
272
|
+
const retryLaunches = { count: 0 };
|
|
242
273
|
let executorResult: string | null = null;
|
|
243
274
|
let wouldDo: string | null = null;
|
|
244
275
|
if (judgement.executorKind) {
|
|
@@ -253,10 +284,13 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
253
284
|
checklistId: delivery ? card?.checklistId ?? '' : '',
|
|
254
285
|
...(delivery ? { delivery } : {}),
|
|
255
286
|
};
|
|
287
|
+
const liveRetry = !delivery && deps.command !== undefined && deps.loadGate !== undefined
|
|
288
|
+
&& (LIVE_RETRY_MOVES as readonly string[]).includes(judgement.move) && liveMoves.has(judgement.move as TaskAgentLiveMove);
|
|
256
289
|
executorResult = await executeNextAction(action, {
|
|
257
|
-
mode: delivery ? (deps.mode ?? 'shadow') : 'shadow',
|
|
290
|
+
mode: delivery ? (deps.mode ?? 'shadow') : liveRetry ? 'live' : 'shadow',
|
|
258
291
|
command: deps.command ?? (async () => { throw new Error('task-agent shadow path must not run commands'); }),
|
|
259
292
|
observe: (_event, data) => { wouldDo = data.reason; },
|
|
293
|
+
...(liveRetry ? { liveMoves, retryLaunches, loadGate: deps.loadGate } : {}),
|
|
260
294
|
});
|
|
261
295
|
}
|
|
262
296
|
const move: TaskAgentShadowMove = {
|
|
@@ -278,20 +312,21 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
278
312
|
move.reason = wouldDo ?? judgement.reason;
|
|
279
313
|
move.wouldDo = null;
|
|
280
314
|
}
|
|
281
|
-
const liveKind: TaskAgentLiveMove | null = move.move === 'review' || move.move === 'propose-green' || move.move === 'propose-land' ? move.move : null;
|
|
282
|
-
// 판단마다 해석한다 — 허용되지 않은 수를 판단할 때도 모르는 항목 경고가 남게.
|
|
283
|
-
const liveMoves = deps.liveMoves ?? await configuredTaskAgentLiveMoves();
|
|
284
|
-
const live = liveKind !== null && liveMoves.has(liveKind);
|
|
315
|
+
const liveKind: TaskAgentLiveMove | null = move.move === 'review' || move.move === 'propose-green' || move.move === 'propose-land' || move.move === 'decision-card' ? move.move : null;
|
|
285
316
|
// 카드 id 는 모든 판단 관측에 싣는다(그림자 일치율을 카드로 잇는다) — 읽기 전용 조회.
|
|
286
317
|
// live 수는 «근거 결과»에 묶는다 — PR 수는 그 PR 을 낸 결과의 카드·작업 트리, 전달은 전달한 카드(다중 결과에서 남의 카드에 쓰지 않게).
|
|
287
318
|
const prResult = typeof judgeInput.pr === 'number' ? input.results.find((result) => result.prNumber === judgeInput.pr) : undefined;
|
|
288
|
-
const
|
|
319
|
+
const candidateBoundCard = delivery
|
|
289
320
|
? card
|
|
290
321
|
: prResult
|
|
291
|
-
? (card && successful?.taskId === prResult.taskId ? card : liveCardFor([prResult], deps, input.runId))
|
|
292
|
-
: undefined;
|
|
322
|
+
? (card && (successful?.taskId === prResult.taskId || (remoteChild && pendingCardId === card.id)) ? card : liveCardFor([prResult], deps, input.runId))
|
|
323
|
+
: liveKind === 'decision-card' ? card ?? liveCardFor(input.results, deps, input.runId) : undefined;
|
|
324
|
+
const boundCard = remoteChild && !matchesLaunchCard(candidateBoundCard) ? undefined : candidateBoundCard;
|
|
293
325
|
// PR 에 묶인 수는 그 PR 의 카드만 관측에 싣는다(못 찾으면 null — 다른 결과의 카드로 대체하지 않는다).
|
|
294
|
-
const
|
|
326
|
+
const candidateLiveCard = prResult || delivery ? boundCard : card ?? (cardLookupFailed ? undefined : liveCardFor(input.results, deps, input.runId));
|
|
327
|
+
const liveCard = remoteChild && !matchesLaunchCard(candidateLiveCard) ? undefined : candidateLiveCard;
|
|
328
|
+
// 원격 Pod 는 HQ 카드 파일을 갖지 않는다. id 를 전달받아도 현지 원장이 없으면 실행 수는 보류한다.
|
|
329
|
+
const live = liveKind !== null && liveMoves.has(liveKind) && (!remoteChild || boundCard !== undefined);
|
|
295
330
|
let liveMove: LiveMoveResult | undefined;
|
|
296
331
|
if (live) {
|
|
297
332
|
if (liveKind === 'review') {
|
|
@@ -316,6 +351,17 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
316
351
|
...(prResult.prUrl ? { prUrl: prResult.prUrl } : {}),
|
|
317
352
|
} } : {}),
|
|
318
353
|
}, liveMoveDeps(deps));
|
|
354
|
+
} else if (liveKind === 'decision-card') {
|
|
355
|
+
// Supervisor results carry failure text and reviewer must-fix items, but no screen tail.
|
|
356
|
+
// A stop reason alone is not evidence: the executor correctly raises no card when both are absent.
|
|
357
|
+
const evidenceResults = prResult ? [prResult] : input.results;
|
|
358
|
+
const signature = evidenceResults.find((result) => result.error?.message?.trim())?.error?.message;
|
|
359
|
+
const mustFix = evidenceResults.flatMap((result) => result.reviewMustFix ?? []);
|
|
360
|
+
liveMove = await executeLiveDecisionCard(boundCard, {
|
|
361
|
+
runId: input.runId, stopReason: input.stopReason, reason: move.reason,
|
|
362
|
+
...(prResult ? { pr: prResult.prNumber } : {}),
|
|
363
|
+
evidence: { ...(signature ? { signature } : {}), mustFix },
|
|
364
|
+
}, liveMoveDeps(deps));
|
|
319
365
|
} else {
|
|
320
366
|
const evidence: Record<string, string> = {};
|
|
321
367
|
if (boundCard && !delivery && judgeInput.prState === 'MERGED' && prResult) evidence[boundCard.id] = `#${prResult.prNumber}`;
|
|
@@ -323,11 +369,6 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
323
369
|
liveMove = executeLiveGreenProposal(boundCard, evidence, liveMoveDeps(deps));
|
|
324
370
|
}
|
|
325
371
|
}
|
|
326
|
-
// 판단한 수마다 live/shadow 를 남긴다 — OP 가 «오판 건수»·«그림자 일치율»을 카드·수 종류로 잰다.
|
|
327
|
-
(deps.log ?? ((category, event, data) => debug.log(category, event, data)))('task-agent', 'shadow-move', {
|
|
328
|
-
...move, completion: shadowCompletion(card, cardLookupFailed), card: liveCard?.id ?? null, live, liveExecuted: liveMove?.executed ?? false, ...(liveMove ? { liveOk: liveMove.ok } : {}),
|
|
329
|
-
...(reviewFromCapture ? { reviewSource: 'captured-review-result' } : {}),
|
|
330
|
-
});
|
|
331
372
|
// TA-WIRING-CHECK — 병합 PR 은 판단한 «뒤» 배선 근거를 관측만 한다(move 불변 · 실패는 unknown 으로 남기고 던지지 않는다).
|
|
332
373
|
if (judgeInput.prState === 'MERGED' && typeof judgeInput.pr === 'number' && !delivery && liveCard) {
|
|
333
374
|
try {
|
|
@@ -339,19 +380,51 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
|
|
|
339
380
|
}
|
|
340
381
|
// TA-LIVE-MOVE-CARD-HISTORY — live-move 수(review·propose-green·propose-land)는 실행이든 그림자든 그 카드 history 에 한 줄.
|
|
341
382
|
// 결정·실행이 끝난 «뒤»에 덧붙이기만 한다(반환값·관측 무변경) · 실패는 관측만(live-move 를 막지 않는다).
|
|
342
|
-
if (liveKind !== null) recordLiveMoveOnCard({
|
|
383
|
+
if (liveKind !== null && (liveKind !== 'decision-card' || live) && (!remoteChild || liveCard)) recordLiveMoveOnCard({
|
|
343
384
|
liveKind, live, liveMove, card: live ? boundCard : liveCard, runId: input.runId,
|
|
344
385
|
pr: prResult?.prNumber ?? (typeof judgeInput.pr === 'number' ? judgeInput.pr : undefined),
|
|
345
386
|
shadowDetail: move.wouldDo ?? move.reason,
|
|
346
387
|
}, deps);
|
|
347
|
-
|
|
388
|
+
let cardBinding: TaskAgentShadowMove['cardBinding'];
|
|
389
|
+
if (liveKind !== null && !liveCard && remoteChild) {
|
|
390
|
+
const inheritedRunId = parentRunId || env[HARNESS_RUN_ID_ENV]?.trim() || null;
|
|
391
|
+
const reason = !parentRunId ? 'parent run id absent; HQ binding required'
|
|
392
|
+
: pendingCardId ? 'card id carried but ledger unavailable' : 'card id absent; parent run lookup required';
|
|
393
|
+
try {
|
|
394
|
+
if (!input.runId) throw new Error('runId absent');
|
|
395
|
+
(deps.queueMove ?? appendRunLedgerEntry)({
|
|
396
|
+
timestamp: new Date().toISOString(), runId: input.runId, event: REMOTE_CARD_MOVE_PENDING,
|
|
397
|
+
data: { id: randomUUID(), card: pendingCardId, parentRunId: inheritedRunId, kind: liveKind, pr: move.pr,
|
|
398
|
+
detail: move.wouldDo ?? move.reason, reason, executorResult: 'shadow' },
|
|
399
|
+
});
|
|
400
|
+
cardBinding = 'pending';
|
|
401
|
+
} catch (error) {
|
|
402
|
+
cardBinding = 'queue-failed';
|
|
403
|
+
try {
|
|
404
|
+
(deps.log ?? ((c, e, d) => debug.log(c, e, d)))('task-agent', 'card-binding-queue-failed',
|
|
405
|
+
{ card: pendingCardId, runId: input.runId, parentRunId: inheritedRunId, kind: liveKind, reason: error instanceof Error ? error.message : String(error) });
|
|
406
|
+
} catch { /* queue failure remains visible in the return value */ }
|
|
407
|
+
}
|
|
408
|
+
if (cardBinding === 'pending') try {
|
|
409
|
+
(deps.log ?? ((c, e, d) => debug.log(c, e, d)))('task-agent', 'card-binding-pending',
|
|
410
|
+
{ card: pendingCardId, runId: input.runId, parentRunId: inheritedRunId, kind: liveKind, reason });
|
|
411
|
+
} catch { /* queued evidence remains authoritative */ }
|
|
412
|
+
}
|
|
413
|
+
// 판단 관측은 원격 미결 수를 실제 원장에 기록한 결과까지 확정한 뒤 남긴다.
|
|
414
|
+
(deps.log ?? ((category, event, data) => debug.log(category, event, data)))('task-agent', 'shadow-move', {
|
|
415
|
+
...move, completion: shadowCompletion(card, cardLookupFailed), card: liveCard?.id ?? pendingCardId,
|
|
416
|
+
live, liveExecuted: liveMove?.executed ?? false, ...(liveMove ? { liveOk: liveMove.ok } : {}),
|
|
417
|
+
...(cardBinding ? { cardBinding } : {}),
|
|
418
|
+
...(reviewFromCapture ? { reviewSource: 'captured-review-result' } : {}),
|
|
419
|
+
});
|
|
420
|
+
return { ...move, ...(liveMove ? { liveMove } : {}), ...(cardBinding ? { cardBinding } : {}) };
|
|
348
421
|
}
|
|
349
422
|
|
|
350
423
|
/**
|
|
351
424
|
* 런 id 폴백 — 입력 runId(슈퍼바이저 · Pod 면 자식 런) → 상속 `ELANOUS_RUN_ID`(발사기가 넘긴 부모 런) 순으로,
|
|
352
425
|
* 카드 runId 와 정확히 같고 그 카드가 하나뿐일 때만 쓴다. 못 찾으면 추측하지 않고 이유 한 줄을 남긴다(조용한 미스 0).
|
|
353
426
|
*/
|
|
354
|
-
function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps): TaskCard | undefined {
|
|
427
|
+
export function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps): TaskCard | undefined {
|
|
355
428
|
const own = runId?.trim() || undefined;
|
|
356
429
|
const inherited = (deps.env ?? process.env)[HARNESS_RUN_ID_ENV]?.trim() || undefined;
|
|
357
430
|
if (!own && !inherited) return undefined;
|
|
@@ -370,6 +443,54 @@ function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps)
|
|
|
370
443
|
return undefined;
|
|
371
444
|
}
|
|
372
445
|
|
|
446
|
+
/** HQ `tasks show` 가 원격 원장 거울의 미결 수를 카드에 회수한다. 부모→자식 원장 간선과 카드 id 를 함께 확인한다. */
|
|
447
|
+
export async function replayRemoteCardMoves(cardId: string, statePath: string, evidenceDeps: CardRunEvidenceDeps = {},
|
|
448
|
+
log: (category: string, event: string, data: Record<string, unknown>) => void = (c, e, d) => debug.log(c, e, d)): Promise<number> {
|
|
449
|
+
const card = readTaskCard(cardId, statePath);
|
|
450
|
+
if (!card?.runId || !card.parentHost) return 0;
|
|
451
|
+
// 원격 부모와 동일 runId 의 HQ 국소 원장은 발사 영수증만 있을 수 있다. 이 회수는 원격 거울을 우선한다.
|
|
452
|
+
const loadLedger = evidenceDeps.loadLedger ?? ((runId: string) =>
|
|
453
|
+
loadRunLedger(runId, remoteLedgerMirrorDir(prodInstanceRoot(), card.parentHost!.host)));
|
|
454
|
+
const snapshot = await readCardRunSnapshot(card, { ...evidenceDeps, loadLedger });
|
|
455
|
+
if (snapshot.readErrors?.length) {
|
|
456
|
+
log('task-agent', 'card-binding-replay-failed', { card: cardId, runId: card.runId, reason: snapshot.readErrors.join(' · ') });
|
|
457
|
+
return 0;
|
|
458
|
+
}
|
|
459
|
+
// 카드 id 가 빠진 옛 원격 줄은 부모 runId 의 유일 카드일 때만 역조회한다.
|
|
460
|
+
const matches = Object.values(readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {})
|
|
461
|
+
.filter((candidate) => candidate.runId === card.runId);
|
|
462
|
+
let written = 0;
|
|
463
|
+
for (const [childRunId, entries] of Object.entries(snapshot.children)) {
|
|
464
|
+
for (const entry of entries ?? []) {
|
|
465
|
+
if (entry.event !== REMOTE_CARD_MOVE_PENDING) continue;
|
|
466
|
+
const data = entry.data;
|
|
467
|
+
const id = data.id;
|
|
468
|
+
const kind = data.kind;
|
|
469
|
+
const linked = data.card === cardId || (data.card == null && matches.length === 1 && matches[0]?.id === cardId);
|
|
470
|
+
const parentMatches = data.parentRunId == null || data.parentRunId === card.runId;
|
|
471
|
+
if (!linked || !parentMatches || typeof id !== 'string' || !/^[a-f0-9-]{36}$/i.test(id)
|
|
472
|
+
|| !['review', 'propose-green', 'propose-land', 'decision-card'].includes(String(kind))) {
|
|
473
|
+
log('task-agent', 'card-binding-replay-skipped', { card: cardId, childRunId, pendingId: typeof id === 'string' ? id : null,
|
|
474
|
+
reason: !linked ? 'card mismatch or ambiguous runId' : !parentMatches ? 'parent run mismatch' : 'invalid pending move' });
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const at = entry.timestamp ?? '';
|
|
478
|
+
if (!Number.isFinite(Date.parse(at))) continue;
|
|
479
|
+
const pr = Number.isSafeInteger(data.pr) && (data.pr as number) > 0 ? data.pr as number : undefined;
|
|
480
|
+
updateTaskCard(statePath, cardId, (current) => {
|
|
481
|
+
if (!current || current.runId !== card.runId || current.history.some((item) => item.sourceId === id)) return undefined;
|
|
482
|
+
const item = liveMoveHistoryEntry(current, { at, kind: kind as TaskAgentLiveMove, executorResult: 'shadow',
|
|
483
|
+
shadowDetail: typeof data.detail === 'string' ? data.detail : 'remote card binding pending', runId: childRunId,
|
|
484
|
+
...(pr ? { pr } : {}) });
|
|
485
|
+
written++;
|
|
486
|
+
return { ...current, history: [...current.history, { ...item, sourceId: id }] };
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (written) log('task-agent', 'card-binding-replayed', { card: cardId, parentRunId: card.runId, written });
|
|
491
|
+
return written;
|
|
492
|
+
}
|
|
493
|
+
|
|
373
494
|
/**
|
|
374
495
|
* 카드 history 에 live-move 한 줄 — 상태 파일은 카드를 읽은 곳과 같아야 한다: 주입한 경로(`live.statePath`) 또는
|
|
375
496
|
* 기본 리더를 썼을 때의 기본 경로. 주입한 카드 리더(`readCard`)만 있고 경로가 없으면 그 카드의 원장을 모른다 — 적지 않는다.
|