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,230 @@
|
|
|
1
|
+
// KPACK6 — 선택 실행 운영 스모크 (읽기 전용)
|
|
2
|
+
//
|
|
3
|
+
// #25780 으로 두 공개 표본 팩의 서명 설치·CLI KnowledgeQuery·그래프 검색 인용은 착지했다.
|
|
4
|
+
// 이 스크립트는 그 다음 칸이다: 설치된 두 팩에 대해 실제 CLI 채팅 LLM 답변이
|
|
5
|
+
// 팩 출처(`slug@version#cardId`)를 인용하는지 확인한다.
|
|
6
|
+
//
|
|
7
|
+
// 팩 미설치 / LLM 호출 불가 / 스토어 오류 → 미측정 (실패 아님)
|
|
8
|
+
// 답변은 왔으나 팩 출처 인용 없음 → 실패
|
|
9
|
+
// 두 팩 모두 인용 답변 실물 확보 전까지 → green 금지
|
|
10
|
+
//
|
|
11
|
+
// 실행: KPACK6_LIVE=1 bun scripts/verify-kpack6-live.ts
|
|
12
|
+
// CLI 명령은 KPACK6_AGENT_CMD 로 바꿀 수 있다(기본 `bun bin/elanous.mjs agent --new --json`).
|
|
13
|
+
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { listInstalledPacks, queryInstalledPack } from '../src/knowledge/query.js';
|
|
18
|
+
|
|
19
|
+
export const KPACK6_TARGET_SLUGS = ['semiconductor-process-public', 'b2b-sales-public'] as const;
|
|
20
|
+
|
|
21
|
+
export type Kpack6PackStatus =
|
|
22
|
+
| 'pack-not-installed'
|
|
23
|
+
| 'pack-unreadable'
|
|
24
|
+
| 'llm-unavailable'
|
|
25
|
+
| 'llm-timeout'
|
|
26
|
+
| 'cli-contract-error'
|
|
27
|
+
| 'citation-missing'
|
|
28
|
+
| 'cited';
|
|
29
|
+
|
|
30
|
+
export type Kpack6PackResult = {
|
|
31
|
+
slug: string;
|
|
32
|
+
packId: string | null;
|
|
33
|
+
status: Kpack6PackStatus;
|
|
34
|
+
measured: boolean;
|
|
35
|
+
question?: string;
|
|
36
|
+
reply?: string;
|
|
37
|
+
ref?: string;
|
|
38
|
+
reason?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type Kpack6SmokeStatus = 'green' | 'fail' | 'not-measured' | 'skipped';
|
|
42
|
+
|
|
43
|
+
export type Kpack6SmokeReport = {
|
|
44
|
+
status: Kpack6SmokeStatus;
|
|
45
|
+
reason?: string;
|
|
46
|
+
packs: Kpack6PackResult[];
|
|
47
|
+
generatedAt: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type Kpack6Deps = {
|
|
51
|
+
installedPacks?: () => Array<{ id: string; title: string }>;
|
|
52
|
+
queryTopCard?: (packId: string) => { id: string; title: string; ref: string };
|
|
53
|
+
ask?: (question: string) => Promise<{ reply: string }>;
|
|
54
|
+
env?: Record<string, string | undefined>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
58
|
+
|
|
59
|
+
function escapeRegExp(text: string): string {
|
|
60
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 설치된 팩에서 조회한 바로 그 카드의 ref만 인용으로 인정한다. */
|
|
64
|
+
export function packCitationIn(reply: string, expectedRef: string): string | null {
|
|
65
|
+
const exactRef = new RegExp(`(?<![\\w./@#-])${escapeRegExp(expectedRef)}(?=$|[^\\w./@#-])`);
|
|
66
|
+
return exactRef.test(reply) ? expectedRef : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function defaultQueryTopCard(packId: string): { id: string; title: string; ref: string } {
|
|
70
|
+
const hits = queryInstalledPack(packId, '');
|
|
71
|
+
if (hits.length === 0) throw new Error(`팩에 카드가 없다: ${packId}`);
|
|
72
|
+
return { id: hits[0].id, title: hits[0].title, ref: hits[0].ref };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class Kpack6CliContractError extends Error {}
|
|
76
|
+
export class Kpack6LlmTimeoutError extends Error {}
|
|
77
|
+
|
|
78
|
+
function isProviderUnavailable(stderr: string): boolean {
|
|
79
|
+
return /No LLM provider available|usage_limit_reached|(?:^|\W)429(?:\W|$)/i.test(stderr);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function askViaCli(question: string, env: Record<string, string | undefined>): Promise<{ reply: string }> {
|
|
83
|
+
const argv = (env.KPACK6_AGENT_CMD ?? 'bun bin/elanous.mjs agent --new --json').split(/\s+/);
|
|
84
|
+
const timeoutMs = Number(env.KPACK6_TIMEOUT_MS ?? 180_000);
|
|
85
|
+
return await new Promise((resolve, reject) => {
|
|
86
|
+
const child = spawn(argv[0], [...argv.slice(1), question], {
|
|
87
|
+
cwd: REPO_ROOT, env: { ...process.env, ...env }, stdio: ['ignore', 'pipe', 'pipe'],
|
|
88
|
+
});
|
|
89
|
+
let stdout = '';
|
|
90
|
+
let stderr = '';
|
|
91
|
+
let timedOut = false;
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
timedOut = true;
|
|
94
|
+
child.kill('SIGKILL');
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
child.stdout.on('data', (chunk) => { stdout += String(chunk); });
|
|
97
|
+
child.stderr.on('data', (chunk) => { stderr += String(chunk); });
|
|
98
|
+
child.on('error', (err) => {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
reject(new Kpack6CliContractError(`agent CLI 실행 실패 — ${err.message}`));
|
|
101
|
+
});
|
|
102
|
+
child.on('close', (code) => {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
if (timedOut) {
|
|
105
|
+
reject(new Kpack6LlmTimeoutError(`agent CLI LLM 응답 시간 초과 (${timeoutMs}ms)`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const jsonLine = stdout.split('\n').filter((line) => line.trim().startsWith('{')).pop();
|
|
109
|
+
if (code !== 0) {
|
|
110
|
+
const message = `agent CLI exit=${code} stderr=${stderr.slice(0, 300)}`;
|
|
111
|
+
reject(isProviderUnavailable(stderr) ? new Error(message) : new Kpack6CliContractError(message));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!jsonLine) {
|
|
115
|
+
reject(new Kpack6CliContractError('agent CLI 성공 출력에 JSON 객체가 없다'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(jsonLine) as { reply?: unknown };
|
|
120
|
+
if (typeof parsed.reply !== 'string' || parsed.reply.trim() === '') {
|
|
121
|
+
reject(new Kpack6CliContractError('agent CLI 응답에 reply 문자열이 없다'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
resolve({ reply: parsed.reply });
|
|
125
|
+
} catch (err) {
|
|
126
|
+
reject(err instanceof Kpack6CliContractError
|
|
127
|
+
? err : new Kpack6CliContractError(`agent CLI 출력 JSON 파싱 실패 — ${(err as Error).message}`));
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function kpack6ExitCode(report: Kpack6SmokeReport): number {
|
|
134
|
+
switch (report.status) {
|
|
135
|
+
case 'green': return 0;
|
|
136
|
+
case 'fail': return 1;
|
|
137
|
+
case 'skipped': return 3;
|
|
138
|
+
default: return 4;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function runKpack6LiveSmoke(deps: Kpack6Deps = {}): Promise<Kpack6SmokeReport> {
|
|
143
|
+
const env = deps.env ?? process.env;
|
|
144
|
+
const generatedAt = new Date().toISOString();
|
|
145
|
+
|
|
146
|
+
if (env.KPACK6_LIVE !== '1') {
|
|
147
|
+
return {
|
|
148
|
+
status: 'skipped',
|
|
149
|
+
reason: '선택 실행 스모크다: KPACK6_LIVE=1 없이는 돌지 않는다 (두 팩 인용 답변 실물 1회 전까지 green 금지)',
|
|
150
|
+
packs: [],
|
|
151
|
+
generatedAt,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const ask = deps.ask ?? ((question: string) => askViaCli(question, env));
|
|
156
|
+
let installed: Array<{ id: string; title: string }>;
|
|
157
|
+
try {
|
|
158
|
+
installed = deps.installedPacks?.() ?? listInstalledPacks();
|
|
159
|
+
} catch (err) {
|
|
160
|
+
return {
|
|
161
|
+
status: 'not-measured',
|
|
162
|
+
reason: `KGS 스토어 조회 불가 — ${(err as Error).message}`,
|
|
163
|
+
packs: KPACK6_TARGET_SLUGS.map((slug) => ({
|
|
164
|
+
slug, packId: null, status: 'pack-unreadable' as const, measured: false,
|
|
165
|
+
reason: `스토어 오류: ${(err as Error).message}`,
|
|
166
|
+
})),
|
|
167
|
+
generatedAt,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const packs: Kpack6PackResult[] = [];
|
|
172
|
+
for (const slug of KPACK6_TARGET_SLUGS) {
|
|
173
|
+
const match = installed.find((pack) => pack.id === slug || pack.id.startsWith(`${slug}@`));
|
|
174
|
+
if (!match) {
|
|
175
|
+
packs.push({
|
|
176
|
+
slug, packId: null, status: 'pack-not-installed', measured: false,
|
|
177
|
+
reason: 'KGS 스토어에 설치된 팩이 없다 (미측정)',
|
|
178
|
+
});
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const packId = match.id;
|
|
182
|
+
|
|
183
|
+
let top: { id: string; title: string; ref: string };
|
|
184
|
+
try {
|
|
185
|
+
top = deps.queryTopCard?.(packId) ?? defaultQueryTopCard(packId);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
packs.push({
|
|
188
|
+
slug, packId, status: 'pack-unreadable', measured: false,
|
|
189
|
+
reason: `설치됐으나 카드 조회 불가 — ${(err as Error).message}`,
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const question = `설치된 ${packId} (${match.title}) 팩의 «${top.title}» 카드를 지식 조회 도구로 읽고 요약하라. 답변에 카드 제목을 쓰고, 도구가 반환한 출처를 그대로 인용해 붙여라. 출처를 추측하지 마라.`;
|
|
195
|
+
let reply: string;
|
|
196
|
+
try {
|
|
197
|
+
reply = (await ask(question)).reply;
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const contractError = err instanceof Kpack6CliContractError;
|
|
200
|
+
const timeoutError = err instanceof Kpack6LlmTimeoutError;
|
|
201
|
+
packs.push({
|
|
202
|
+
slug, packId, status: contractError ? 'cli-contract-error' : timeoutError ? 'llm-timeout' : 'llm-unavailable',
|
|
203
|
+
measured: false, question,
|
|
204
|
+
reason: `${contractError ? 'CLI JSON 계약 오류' : timeoutError ? 'LLM 응답 시간 초과' : 'CLI 채팅 LLM 호출 불가'} — ${(err as Error).message}`,
|
|
205
|
+
});
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const ref = packCitationIn(reply, top.ref);
|
|
210
|
+
if (ref && reply.includes(top.title)) {
|
|
211
|
+
packs.push({ slug, packId, status: 'cited', measured: true, question, reply, ref });
|
|
212
|
+
} else {
|
|
213
|
+
packs.push({
|
|
214
|
+
slug, packId, status: 'citation-missing', measured: true, question, reply,
|
|
215
|
+
reason: '답변은 왔으나 조회한 설치 카드의 제목과 정확한 출처 인용이 함께 없다 — 실패',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const failed = packs.some((pack) => pack.status === 'citation-missing' || pack.status === 'cli-contract-error');
|
|
221
|
+
const allCited = packs.length > 0 && packs.every((pack) => pack.status === 'cited');
|
|
222
|
+
const status: Kpack6SmokeStatus = failed ? 'fail' : allCited ? 'green' : 'not-measured';
|
|
223
|
+
return { status, packs, generatedAt };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (import.meta.main) {
|
|
227
|
+
const report = await runKpack6LiveSmoke();
|
|
228
|
+
console.log(JSON.stringify(report, null, 2));
|
|
229
|
+
process.exit(kpack6ExitCode(report));
|
|
230
|
+
}
|
package/src/acp/codex-plugins.ts
CHANGED
|
@@ -43,6 +43,23 @@ export interface FetchCodexPluginsOpts {
|
|
|
43
43
|
noCache?: boolean;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/** Read the full inventory for an explicit package import. Unlike the chip projector, this keeps
|
|
47
|
+
* the package location and never substitutes a stale cached inventory after an RPC failure. */
|
|
48
|
+
export async function readCodexPluginList(
|
|
49
|
+
spawn: typeof spawnCodexAppServer = spawnCodexAppServer,
|
|
50
|
+
): Promise<unknown> {
|
|
51
|
+
const server = spawn({ requestTimeoutMs: 5000 });
|
|
52
|
+
try {
|
|
53
|
+
await server.client.request('initialize', {
|
|
54
|
+
clientInfo: { name: 'elanous', version: '0.x' }, capabilities: { experimentalApi: true },
|
|
55
|
+
});
|
|
56
|
+
return await server.client.request<Record<string, never>, unknown>('plugin/list', {});
|
|
57
|
+
} finally {
|
|
58
|
+
try { await server.client.close(); }
|
|
59
|
+
finally { server.child.kill(); }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
/** Uncached inventory probe: distinguish no session / RPC failure from an empty installed list. */
|
|
47
64
|
export async function readCodexPluginsObservation(client: CodexAppServerClient | null | undefined): Promise<
|
|
48
65
|
{ status: 'ok'; plugins: ReadonlyArray<CodexPlugin> } | { status: 'unknown'; plugins: readonly [] }
|
|
@@ -62,11 +62,39 @@ export class TaskNotificationQueue {
|
|
|
62
62
|
* listens to both runner events and a separate `onTaskDone` hook.
|
|
63
63
|
* Keyed by taskId. */
|
|
64
64
|
private seen = new Set<string>();
|
|
65
|
+
/** Wake listeners — fired once per REAL enqueue (not on seen-dup).
|
|
66
|
+
* TUI-SEAT-READY ③ first shard: one completion event = one wake
|
|
67
|
+
* signal so an idle parent can open the next turn. Opening the turn
|
|
68
|
+
* itself is deliberately NOT wired here (next shard). */
|
|
69
|
+
private wakeListeners = new Set<(n: PendingTaskNotification) => void>();
|
|
70
|
+
|
|
71
|
+
/** Register a wake listener; returns an unsubscribe function.
|
|
72
|
+
* Listeners survive `clear()` — they belong to the wiring, not the
|
|
73
|
+
* queue contents. */
|
|
74
|
+
onEnqueue(listener: (n: PendingTaskNotification) => void): () => void {
|
|
75
|
+
this.wakeListeners.add(listener);
|
|
76
|
+
return () => { this.wakeListeners.delete(listener); };
|
|
77
|
+
}
|
|
65
78
|
|
|
66
79
|
enqueue(n: PendingTaskNotification): void {
|
|
67
80
|
if (this.seen.has(n.taskId)) return;
|
|
68
81
|
this.seen.add(n.taskId);
|
|
69
82
|
this.queue.push(n);
|
|
83
|
+
debug.log('agent.task-notification', 'wake', { taskId: n.taskId, size: this.queue.length });
|
|
84
|
+
for (const listener of this.wakeListeners) {
|
|
85
|
+
try {
|
|
86
|
+
listener(n);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// fail-soft — a throwing wake listener must not lose the
|
|
89
|
+
// queued notification or break sibling listeners.
|
|
90
|
+
try {
|
|
91
|
+
debug.log('agent.task-notification', 'wake-listener-failed', {
|
|
92
|
+
taskId: n.taskId,
|
|
93
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
94
|
+
});
|
|
95
|
+
} catch { /* */ }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
70
98
|
}
|
|
71
99
|
|
|
72
100
|
/** Return all pending notifications AND clear the queue. Parents
|
|
@@ -85,7 +113,8 @@ export class TaskNotificationQueue {
|
|
|
85
113
|
return this.queue.length;
|
|
86
114
|
}
|
|
87
115
|
|
|
88
|
-
/** Forget everything — tests use this for isolation.
|
|
116
|
+
/** Forget everything — tests use this for isolation. Listeners are
|
|
117
|
+
* preserved: clear() empties queue + seen, not the wake wiring. */
|
|
89
118
|
clear(): void {
|
|
90
119
|
this.queue = [];
|
|
91
120
|
this.seen.clear();
|
|
@@ -194,7 +223,8 @@ function escapeXml(s: string): string {
|
|
|
194
223
|
return s
|
|
195
224
|
.replace(/&/g, '&')
|
|
196
225
|
.replace(/</g, '<')
|
|
197
|
-
.replace(/>/g, '>')
|
|
226
|
+
.replace(/>/g, '>')
|
|
227
|
+
.replace(/"/g, '"');
|
|
198
228
|
}
|
|
199
229
|
|
|
200
230
|
function formatDuration(ms: number): string {
|
|
@@ -2,7 +2,8 @@ import type { PtyHandle } from '../pty-shell/registry.js';
|
|
|
2
2
|
import { classifierFrameLines } from '../capture/frame-state-detect.js';
|
|
3
3
|
import { emitDecision, type DecisionEvent } from '../live/detail-switch.js';
|
|
4
4
|
import { debug } from '../debug/log.js';
|
|
5
|
-
import
|
|
5
|
+
import * as llm from '../llm.js';
|
|
6
|
+
import { resolveRoleLlm } from '../user-config.js';
|
|
6
7
|
|
|
7
8
|
export interface PluginRequest { agent: 'codex' | 'claude'; plugin: string; marketplace: string }
|
|
8
9
|
export type PluginResult = { outcome: 'installed' } | { outcome: 'escalate'; reason: 'plugins-disabled' | 'unknown-menu' | 'timeout' | 'wrong-target' | 'credentials' };
|
|
@@ -103,11 +104,15 @@ export async function installPluginInsideAgent(request: PluginRequest, pty: Pick
|
|
|
103
104
|
? ['[credential screen]']
|
|
104
105
|
: lines(screen).slice(-10).map(line => line.slice(0, 160));
|
|
105
106
|
const brain = deps.brain ?? (async (screen: string, target: string) => {
|
|
106
|
-
const messages: LLMMessage[] = [
|
|
107
|
+
const messages: llm.LLMMessage[] = [
|
|
107
108
|
{ role: 'system', content: 'You navigate a plugin menu using only up/down/tab/enter/wait/unknown. Return exactly one of those words. Do not choose enter unless the requested plugin@marketplace is visibly selected. Never enter credentials.' },
|
|
108
109
|
{ role: 'user', content: `Target: ${target}\nScreen:\n${screen.slice(-3500)}` },
|
|
109
110
|
];
|
|
110
|
-
const
|
|
111
|
+
const resolved = resolveRoleLlm('classify');
|
|
112
|
+
const provider = llm.PROVIDERS[resolved.provider];
|
|
113
|
+
if (!provider) throw new Error(`Unknown classify provider: ${resolved.provider}`);
|
|
114
|
+
debug.log('llm.role-pin', 'resolved', { site: 'in-agent-plugin', role: 'classify', model: resolved.model, provider: resolved.provider, source: resolved.source });
|
|
115
|
+
const answer = (await llm.streamLLM(messages, () => {}, { model: resolved.model, provider, usageRole: 'classify', maxTokens: 20 })).trim().toLowerCase();
|
|
111
116
|
return /^(up|down|tab|enter|wait)$/.test(answer) ? answer as 'up' | 'down' | 'tab' | 'enter' | 'wait' : 'unknown';
|
|
112
117
|
});
|
|
113
118
|
const target = `${request.plugin}@${request.marketplace}`;
|
|
@@ -60,8 +60,11 @@ interface ReviewObservationInput {
|
|
|
60
60
|
readonly fallbackAttempted?: boolean;
|
|
61
61
|
/** 폴백이 실제로 시도한 백엔드. */
|
|
62
62
|
readonly fallbackBackend?: string;
|
|
63
|
+
/** Explicitly requested primary model and the ACP backend that actually completed review. */
|
|
64
|
+
readonly requestedModel?: string;
|
|
65
|
+
readonly actualBackend?: string;
|
|
63
66
|
/** 폴백을 시도하지 않은 이유. */
|
|
64
|
-
readonly fallbackNotAttemptedReason?: 'explicit-acp' | 'primary-review-succeeded';
|
|
67
|
+
readonly fallbackNotAttemptedReason?: 'explicit-acp' | 'primary-review-succeeded' | 'explicit-model';
|
|
65
68
|
/** 1차가 왜 못 돌았나 — 폴백의 «사인». */
|
|
66
69
|
readonly fallbackTriggerReason?: string;
|
|
67
70
|
/** Each failed ACP fallback attempt, retained in order for diagnosis. */
|
|
@@ -80,7 +83,9 @@ interface ReviewObservation {
|
|
|
80
83
|
readonly reviewRoute?: 'api' | 'acp' | 'acp-fallback';
|
|
81
84
|
readonly fallbackAttempted?: boolean;
|
|
82
85
|
readonly fallbackBackend?: string;
|
|
83
|
-
readonly
|
|
86
|
+
readonly requestedModel?: string;
|
|
87
|
+
readonly actualBackend?: string;
|
|
88
|
+
readonly fallbackNotAttemptedReason?: 'explicit-acp' | 'primary-review-succeeded' | 'explicit-model';
|
|
84
89
|
readonly fallbackTriggerReason?: string;
|
|
85
90
|
readonly fallbackFailures?: readonly { backend: string; reason: string }[];
|
|
86
91
|
readonly skippedAcpFallbackBackends?: readonly { backend: string; reason: string }[];
|
|
@@ -168,6 +173,8 @@ export function buildReviewObservation(input: ReviewObservationInput): ReviewObs
|
|
|
168
173
|
...(input.reviewRoute ? { reviewRoute: input.reviewRoute } : {}),
|
|
169
174
|
...(input.fallbackAttempted !== undefined ? { fallbackAttempted: input.fallbackAttempted } : {}),
|
|
170
175
|
...(input.fallbackBackend ? { fallbackBackend: safeLogText(input.fallbackBackend) } : {}),
|
|
176
|
+
...(input.requestedModel ? { requestedModel: safeLogText(input.requestedModel) } : {}),
|
|
177
|
+
...(input.actualBackend ? { actualBackend: safeLogText(input.actualBackend) } : {}),
|
|
171
178
|
...(input.fallbackNotAttemptedReason ? { fallbackNotAttemptedReason: input.fallbackNotAttemptedReason } : {}),
|
|
172
179
|
...(input.fallbackTriggerReason ? { fallbackTriggerReason: safeLogText(input.fallbackTriggerReason) } : {}),
|
|
173
180
|
...(input.fallbackFailures?.length ? {
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
//
|
|
15
15
|
// index.ts 의 액션은 옵션 파싱과 실제 의존 배선만 남는다(얇은 껍데기).
|
|
16
16
|
|
|
17
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
17
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { tmpdir } from 'node:os';
|
|
19
|
-
import { join } from 'node:path';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
20
21
|
import { tierModel } from '../llm/model-defaults.js';
|
|
21
22
|
import { buildReviewObservation, safeLogText } from './review-observation.js';
|
|
22
23
|
import { DEFAULT_REVIEW_BACKEND } from './acp-reviewer.js';
|
|
@@ -24,7 +25,7 @@ import { ACP_BACKENDS, canonicalizeBackendId } from '../acp/backend-registry.js'
|
|
|
24
25
|
import { buildManualReviewIntent, intentFromPr, prIntentSectionCoverage, reviewIntentTruncationFromPr, reviewIntentTruncationObservation } from './review-intent.js';
|
|
25
26
|
import { budgetReviewerContext, reviewDiffBudgetObservation, splitDiffByFile, type ReviewImage, type ReviewInput, type ReviewerContextItem, type ReviewResult } from './pr-reviewer.js';
|
|
26
27
|
import type { ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
27
|
-
import type
|
|
28
|
+
import { getUserConfig, type SubscriptionReviewerSpec } from '../user-config.js';
|
|
28
29
|
import { listRunLedgers, lookupPrGoalAcceptance, lookupRunLedger, type PrGoalAcceptanceLookup, type RunLedgerEntry, type RunLedgerLookup } from '../self-implement/run-ledger.js';
|
|
29
30
|
|
|
30
31
|
/** SUBSCRIPTION-EXHAUST-REROUTE — API reviewer when the configured `roleLlm.reviewer` lane is unavailable (off codex). */
|
|
@@ -37,6 +38,8 @@ export interface SelfReviewCliOpts {
|
|
|
37
38
|
acpModel?: string | undefined;
|
|
38
39
|
acpBackend?: string | undefined;
|
|
39
40
|
configuredAcpBackend?: string | undefined;
|
|
41
|
+
/** ACP review backends excluded from automatic fallback by `acp.reviewDenyBackends`. */
|
|
42
|
+
reviewDenyBackends?: string[] | undefined;
|
|
40
43
|
/** `roleLlm.reviewer` (same resolution as the harness). Used only when no explicit reviewer flag is given. */
|
|
41
44
|
roleReviewer?: SubscriptionReviewerSpec | undefined;
|
|
42
45
|
acpTimeout?: string | undefined;
|
|
@@ -44,6 +47,8 @@ export interface SelfReviewCliOpts {
|
|
|
44
47
|
contextText?: string[] | undefined;
|
|
45
48
|
contextOrder?: Array<{ kind: 'file' | 'text'; value: string }> | undefined;
|
|
46
49
|
json?: boolean | undefined;
|
|
50
|
+
/** Save completed reviews under the task-agent state's review-results directory. */
|
|
51
|
+
save?: string | undefined;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/** 셸/네트워크/로그 경계 — 테스트는 전부 대역으로 채운다. */
|
|
@@ -1120,10 +1125,11 @@ function roleAcpErrorReason(error: unknown): 'acp-handshake-timeout' | 'acp-erro
|
|
|
1120
1125
|
|
|
1121
1126
|
type AcpFallbackSkip = { backend: string; reason: string };
|
|
1122
1127
|
|
|
1123
|
-
function orderedAcpReviewFallbackBackends(configuredBackend: string | undefined): {
|
|
1128
|
+
function orderedAcpReviewFallbackBackends(configuredBackend: string | undefined, denyBackends: readonly string[] = []): {
|
|
1124
1129
|
backends: string[];
|
|
1125
1130
|
skipped: AcpFallbackSkip[];
|
|
1126
1131
|
} {
|
|
1132
|
+
const denied = new Set(denyBackends.map((backend) => backend.trim().toLowerCase()).filter(Boolean));
|
|
1127
1133
|
const candidates = [configuredBackend, ...Object.keys(ACP_BACKENDS)]
|
|
1128
1134
|
.map((backend) => backend?.trim())
|
|
1129
1135
|
.filter((backend): backend is string => Boolean(backend));
|
|
@@ -1135,6 +1141,10 @@ function orderedAcpReviewFallbackBackends(configuredBackend: string | undefined)
|
|
|
1135
1141
|
const spec = ACP_BACKENDS[backend];
|
|
1136
1142
|
if (!spec || seen.has(backend)) continue;
|
|
1137
1143
|
seen.add(backend);
|
|
1144
|
+
if (denied.has(backend)) {
|
|
1145
|
+
skipped.push({ backend, reason: 'denied-by-acp.reviewDenyBackends' });
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1138
1148
|
if (spec.unsupportedReason) {
|
|
1139
1149
|
skipped.push({ backend, reason: spec.unsupportedReason });
|
|
1140
1150
|
continue;
|
|
@@ -1269,6 +1279,11 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1269
1279
|
return { results: [], failed: 0 };
|
|
1270
1280
|
}
|
|
1271
1281
|
|
|
1282
|
+
if (opts.save !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(opts.save)) {
|
|
1283
|
+
deps.error('--save 이름은 영문자·숫자로 시작하고 영문자·숫자·점·밑줄·하이픈만 포함해야 합니다.');
|
|
1284
|
+
return { results: [], failed: 1 };
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1272
1287
|
const useAcp = opts.acp === true;
|
|
1273
1288
|
const acpModel = opts.acpModel?.trim() || undefined;
|
|
1274
1289
|
const acpBackendResolution = opts.acpBackend
|
|
@@ -1277,7 +1292,12 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1277
1292
|
? { backend: opts.configuredAcpBackend, source: 'config' as const }
|
|
1278
1293
|
: { backend: DEFAULT_REVIEW_BACKEND, source: 'default' as const };
|
|
1279
1294
|
const acpBackend = acpBackendResolution.backend;
|
|
1280
|
-
const
|
|
1295
|
+
const requestedModel = opts.model?.trim() || undefined;
|
|
1296
|
+
const rawReviewDenyBackends = (getUserConfig().raw?.acp as { reviewDenyBackends?: unknown } | undefined)?.reviewDenyBackends;
|
|
1297
|
+
const configuredReviewDenyBackends = Array.isArray(rawReviewDenyBackends)
|
|
1298
|
+
? rawReviewDenyBackends.filter((backend): backend is string => typeof backend === 'string')
|
|
1299
|
+
: [];
|
|
1300
|
+
const automaticFallbacks = orderedAcpReviewFallbackBackends(opts.configuredAcpBackend, opts.reviewDenyBackends ?? configuredReviewDenyBackends);
|
|
1281
1301
|
const fallbackAcpBackends = opts.acpBackend
|
|
1282
1302
|
? [canonicalizeBackendId(opts.acpBackend)]
|
|
1283
1303
|
: automaticFallbacks.backends;
|
|
@@ -1300,6 +1320,14 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1300
1320
|
const envModel = deps.envModel();
|
|
1301
1321
|
const roleFallbackModel = !useAcp && !opts.model && !envModel && reviewerSource === 'role-config'
|
|
1302
1322
|
? deps.roleFallbackModel?.()?.trim() || undefined : undefined;
|
|
1323
|
+
if (requestedModel && /^glm-\d+(?:\.\d+)?$/i.test(requestedModel)) {
|
|
1324
|
+
const canonical = requestedModel.toLowerCase() === 'glm-5.3' ? 'openrouter/z-ai/glm-5.3' : undefined;
|
|
1325
|
+
const suggestion = canonical ? ` 정본 모델 ID 후보: ${canonical}` : ' `local:<model>` 또는 공급자 정본 ID를 지정하세요.';
|
|
1326
|
+
const message = `모델 ID가 모호합니다: ${requestedModel}.${suggestion} 짧은 별칭을 공급자 정본 ID로 바꿔 다시 시도하세요.`;
|
|
1327
|
+
deps.log('model-rejected', { requestedModel: safeLogText(requestedModel), reason: 'ambiguous-model-id', ...(canonical ? { canonicalModelCandidate: canonical } : {}) }, { level: 'warn' });
|
|
1328
|
+
deps.error(message);
|
|
1329
|
+
return { results: opts.json ? [{ error: message, requestedModel }] : [], failed: 1 };
|
|
1330
|
+
}
|
|
1303
1331
|
const model = useAcp ? `acp:${acpBackend}${acpModel ? `/${acpModel}` : ''}` : (opts.model || envModel || roleFallbackModel || tierModel('better'));
|
|
1304
1332
|
let roleReviewer: { llm: (prompt: string, images?: readonly ReviewImage[]) => Promise<string>; model: string } | undefined;
|
|
1305
1333
|
let roleReviewerUnavailableReason: string | undefined;
|
|
@@ -1411,8 +1439,8 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1411
1439
|
{ pr: safeLogText(pr), exit: diff.status, error: safeLogText(diff.stderr || err, 160) },
|
|
1412
1440
|
{ level: 'warn' });
|
|
1413
1441
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
1414
|
-
if (opts.json) results.push({ pr, error: err, ...contextOutput, ...comparisonData, ...(mismatchWarning ?? {}) });
|
|
1415
|
-
|
|
1442
|
+
if (opts.json || opts.save !== undefined) results.push({ pr, error: err, ...contextOutput, ...comparisonData, ...(mismatchWarning ?? {}) });
|
|
1443
|
+
if (!opts.json) deps.error(`PR ${pr}: ${err}\n${reviewerContextStatus}`);
|
|
1416
1444
|
failed += 1;
|
|
1417
1445
|
continue;
|
|
1418
1446
|
}
|
|
@@ -1472,8 +1500,8 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1472
1500
|
pr, model: primaryModel, verdict: null, mustFix: [], shouldFix: [], reviewed: false,
|
|
1473
1501
|
refusal: 'stale-head', ...provenance, ...reviewerSourceObservation(), ...contextOutput,
|
|
1474
1502
|
};
|
|
1475
|
-
if (opts.json) results.push(refused);
|
|
1476
|
-
|
|
1503
|
+
if (opts.json || opts.save !== undefined) results.push(refused);
|
|
1504
|
+
if (!opts.json) deps.print(`\n━━ PR ${pr} (${primaryModel}) ━━\n${renderStaleRefusal(diffRead)}\n${reviewerContextStatus}`);
|
|
1477
1505
|
continue;
|
|
1478
1506
|
}
|
|
1479
1507
|
// ⭐ 머지된 PR 만 머지 커밋 detached worktree 에서 리뷰한다 — main 최신이 아니라 그 PR 이 머지된 시점의 코드를 본다.
|
|
@@ -1558,9 +1586,9 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1558
1586
|
let fallbackAttempted = false;
|
|
1559
1587
|
let fallbackBackend: string | undefined;
|
|
1560
1588
|
let fallbackTriggerReason: string | undefined;
|
|
1561
|
-
let fallbackNotAttemptedReason: 'explicit-acp' | 'primary-review-succeeded' | undefined;
|
|
1589
|
+
let fallbackNotAttemptedReason: 'explicit-acp' | 'primary-review-succeeded' | 'explicit-model' | undefined;
|
|
1562
1590
|
const fallbackFailures: Array<{ backend: string; reason: string }> = [];
|
|
1563
|
-
if (!useAcp && !roleReview && review.reviewed !== true) {
|
|
1591
|
+
if (!useAcp && !roleReview && !opts.acpModel?.trim() && (!requestedModel || Boolean(opts.acpBackend)) && review.reviewed !== true) {
|
|
1564
1592
|
fallbackAttempted = true;
|
|
1565
1593
|
fallbackTriggerReason = safeLogText(review.failureReason || 'reviewed=false', 400);
|
|
1566
1594
|
for (const backend of fallbackAcpBackends) {
|
|
@@ -1569,6 +1597,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1569
1597
|
pr: safeLogText(pr),
|
|
1570
1598
|
failureReason: fallbackTriggerReason,
|
|
1571
1599
|
fallbackBackend: safeLogText(backend),
|
|
1600
|
+
requestedModel: safeLogText(primaryModel),
|
|
1572
1601
|
}, { level: 'warn' });
|
|
1573
1602
|
try {
|
|
1574
1603
|
const fallbackReview = await deps.reviewPullRequest(
|
|
@@ -1598,12 +1627,13 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1598
1627
|
...(reviewCwdPinFallbackReason ? { reviewCwdPinFallbackReason } : {}),
|
|
1599
1628
|
};
|
|
1600
1629
|
if (!fallbackAttempted) {
|
|
1601
|
-
fallbackNotAttemptedReason = useAcp ? 'explicit-acp' : 'primary-review-succeeded';
|
|
1630
|
+
fallbackNotAttemptedReason = useAcp ? 'explicit-acp' : requestedModel && review.reviewed !== true ? 'explicit-model' : 'primary-review-succeeded';
|
|
1602
1631
|
}
|
|
1603
1632
|
const reviewProvenance = {
|
|
1604
1633
|
reviewRoute,
|
|
1605
1634
|
fallbackAttempted,
|
|
1606
1635
|
...(fallbackBackend ? { fallbackBackend } : {}),
|
|
1636
|
+
...(fallbackBackend ? { requestedModel: primaryModel, actualBackend: `acp:${fallbackBackend}` } : {}),
|
|
1607
1637
|
...(fallbackNotAttemptedReason ? { fallbackNotAttemptedReason } : {}),
|
|
1608
1638
|
...(fallbackTriggerReason ? { fallbackTriggerReason } : {}),
|
|
1609
1639
|
...(fallbackFailures.length ? { fallbackFailures } : {}),
|
|
@@ -1644,17 +1674,33 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1644
1674
|
if (useAcp && review.reviewed === false) {
|
|
1645
1675
|
const err = 'ACP 리뷰 실행 실패(reviewed=false) — 관측: elanous logs --category acp-review';
|
|
1646
1676
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
1647
|
-
if (opts.json) results.push({ pr, error: err, ...review, verdict: null, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput });
|
|
1648
|
-
|
|
1677
|
+
if (opts.json || opts.save !== undefined) results.push({ pr, error: err, ...review, verdict: null, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput });
|
|
1678
|
+
if (!opts.json) deps.error(`PR ${pr}: ${err}\n${renderProvenance(diffRead)}\n${reviewerContextStatus}`);
|
|
1649
1679
|
failed += 1;
|
|
1650
1680
|
continue;
|
|
1651
1681
|
}
|
|
1652
1682
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
1653
1683
|
const jsonReview = review.reviewed === true ? review : { ...review, verdict: null };
|
|
1654
|
-
if (opts.json) results.push({ pr, model: selectedModel, ...jsonReview, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput, evidence: evidence.evidence, prDiffFiles: evidence.prDiffFiles, ...(evidence.authoredFiles !== undefined ? { authoredFiles: evidence.authoredFiles } : {}) });
|
|
1655
|
-
|
|
1684
|
+
if (opts.json || opts.save !== undefined) results.push({ pr, model: selectedModel, ...jsonReview, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput, evidence: evidence.evidence, prDiffFiles: evidence.prDiffFiles, ...(evidence.authoredFiles !== undefined ? { authoredFiles: evidence.authoredFiles } : {}) });
|
|
1685
|
+
if (!opts.json) deps.print(`\n━━ PR ${pr} (${selectedModel}) ━━\n${renderProvenance(diffRead)}\n${evidenceLine}\n${reviewerContextStatus}\n${deps.renderReview(review)}`);
|
|
1656
1686
|
// REVIEW-ERROR-RC — 기본(API)·role 경로가 1차·폴백 모두 실패해 reviewed≠true 로 끝나도 «못 리뷰한 PR» 이다(verdict:null).
|
|
1657
1687
|
if (review.reviewed !== true) failed += 1;
|
|
1688
|
+
if (opts.save !== undefined) {
|
|
1689
|
+
if (review.reviewed !== true || diffRead.headCommitState !== 'known' || diffRead.currentHeadCommitState !== 'known' || diffRead.currentHeadCommit !== diffRead.headCommit || diffRead.headCommit.length !== 40) {
|
|
1690
|
+
deps.error(`PR ${pr}: --save 불가 — 완료된 리뷰와 확인된 40자 PR head가 필요합니다.`);
|
|
1691
|
+
if (review.reviewed === true) failed += 1;
|
|
1692
|
+
} else {
|
|
1693
|
+
const path = join(effectiveInstanceRoot(), 'review-results', `${opts.save}-${pr}-${diffRead.headCommit.slice(0, 12)}.json`);
|
|
1694
|
+
const result = results.at(-1);
|
|
1695
|
+
try {
|
|
1696
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1697
|
+
writeFileSync(path, selfReviewJsonLine([result]));
|
|
1698
|
+
} catch (error) {
|
|
1699
|
+
failed += 1;
|
|
1700
|
+
deps.error(`PR ${pr}: --save 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1658
1704
|
}
|
|
1659
1705
|
return { results, failed };
|
|
1660
1706
|
}
|