elanous 0.2.4 → 0.2.5
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/README.md +3 -3
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 1751.50b96ea9c37b49b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +25 -0
- package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +5 -0
- package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 4daa2798-1363d725c79f3593.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 5348-bbf838e119661eee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5391-8793f88b03b30161.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7527.943753b6ed829f09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 8617.465ca22a319c95b3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 8918-482b30ce2a8ebf4c.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238.2cd5f47f4bf7a2c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 9704-3221c102623c8d73.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-9fa879ebf5959096.js → page-0ac5d053f0444d21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-4472ddf1e2458e69.js → page-72bd384898d88955.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-112f7e477bda77fd.js → page-bccce0babceae30f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6f3850d-7f324c6566002113.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-2bf59c720fe9542c.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +8 -1
- package/graphs/release/release-loop.yaml +6 -1
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +12 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/gate-node.ts +332 -44
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +50 -0
- package/skills/explainer-video/SKILL.md +99 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/vo.ts +37 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/plugin-cli.ts +109 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +94 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +8 -2
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +56 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/graph-approvals.ts +125 -0
- package/src/nexus/api/http-server.ts +60 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/release-loop/checklist.ts +191 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/seams.ts +4 -2
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +27 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +28 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-011e1f474efb8b33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_ssgManifest.js +0 -0
|
@@ -15,8 +15,12 @@
|
|
|
15
15
|
// self 는 backend 리스트 항목이 아니라 transport 축을 붕괴시키는 특수 갈래. 감독 축도 self=turn 레벨.
|
|
16
16
|
|
|
17
17
|
import { execFileSync } from 'node:child_process';
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
18
19
|
import { readFileSync } from 'node:fs';
|
|
19
|
-
import {
|
|
20
|
+
import { dispatchTask, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
|
|
21
|
+
import { getNestDepth } from '../agent/nest-depth.js';
|
|
22
|
+
import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
23
|
+
import { appendEvidenceLocationRequirement, extractVerbatimOriginalAsk, formatGoalFileLintFinding, inspectAskDecisionSignalMarker, leadingGoalMetadata, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, SUPERSEDED_BY_LINE, type GoalFileLintFinding, type GoalFileLintResult } from '../self-implement/goal-author.js';
|
|
20
24
|
import { createRepositoryReferencedFileReader, type ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
21
25
|
import { parseGoalDocumentClarifications } from '../self-implement/goal-author-clarification.js';
|
|
22
26
|
import { requiredEvidenceFromGoal } from '../self-implement/off-diff-evidence.js';
|
|
@@ -55,6 +59,8 @@ export type OrchestrateRuntime = Omit<OrchestrateSelfDevOptions, 'goals' | 'conc
|
|
|
55
59
|
export interface DevMissionOpts {
|
|
56
60
|
chain?: readonly ('codex' | 'claude' | 'elanous')[];
|
|
57
61
|
plugin?: { plugin: string; marketplace: string };
|
|
62
|
+
resources?: 'on' | 'off';
|
|
63
|
+
backendExplicit?: string;
|
|
58
64
|
headless?: boolean;
|
|
59
65
|
evidence?: EvidenceMode;
|
|
60
66
|
maxRounds?: number;
|
|
@@ -619,6 +625,8 @@ export function toAgentMissionSpec(
|
|
|
619
625
|
...(m.headless === true ? { headless: true } : {}),
|
|
620
626
|
...(m.chain ? { chain: m.chain } : {}),
|
|
621
627
|
...(m.plugin ? { plugin: m.plugin } : {}),
|
|
628
|
+
...(m.resources !== undefined ? { resources: m.resources } : {}),
|
|
629
|
+
...(m.backendExplicit !== undefined ? { backendExplicit: m.backendExplicit } : {}),
|
|
622
630
|
...(plan.base ? { base: plan.base } : {}),
|
|
623
631
|
...(plan.enhance !== undefined ? { enhance: plan.enhance } : {}),
|
|
624
632
|
...(m.maxRounds !== undefined ? { maxRounds: m.maxRounds } : {}),
|
|
@@ -644,6 +652,8 @@ export function buildAgentMissionDevSpec(o: {
|
|
|
644
652
|
headless?: boolean;
|
|
645
653
|
chain?: readonly ('codex' | 'claude' | 'elanous')[];
|
|
646
654
|
plugin?: { plugin: string; marketplace: string };
|
|
655
|
+
resources?: 'on' | 'off';
|
|
656
|
+
backendExplicit?: string;
|
|
647
657
|
/** --no-enhance 시 false(그 외 미지정 → driver 가 entry 로 구동). */
|
|
648
658
|
enhanceOff?: boolean;
|
|
649
659
|
evidence: EvidenceMode;
|
|
@@ -663,6 +673,8 @@ export function buildAgentMissionDevSpec(o: {
|
|
|
663
673
|
...(o.headless === true ? { headless: true } : {}),
|
|
664
674
|
...(o.chain ? { chain: o.chain } : {}),
|
|
665
675
|
...(o.plugin ? { plugin: o.plugin } : {}),
|
|
676
|
+
...(o.resources !== undefined ? { resources: o.resources } : {}),
|
|
677
|
+
...(o.backendExplicit !== undefined ? { backendExplicit: o.backendExplicit } : {}),
|
|
666
678
|
maxRounds: o.maxRounds,
|
|
667
679
|
commit: o.commit,
|
|
668
680
|
entry: 'elanous-apparatus',
|
|
@@ -918,6 +930,10 @@ export interface DevHarnessDispatchArgs extends Record<string, unknown> {
|
|
|
918
930
|
}
|
|
919
931
|
|
|
920
932
|
export interface DevPipelineDeps {
|
|
933
|
+
dispatchTask?: typeof dispatchTask;
|
|
934
|
+
dispatchTaskDeps?: DispatchTaskDeps;
|
|
935
|
+
/** Caller has already recorded this dispatch decision at an outer entrance. */
|
|
936
|
+
skipDispatchTask?: boolean;
|
|
921
937
|
/** Caller-managed human progress sink (for example SurfaceUx.progress). Absent callers retain byte-for-byte CLI stdout. */
|
|
922
938
|
progress?: SurfaceUx['progress'];
|
|
923
939
|
runSelfImplement?: (o: SelfImplementOptions) => Promise<SelfImplementResult>;
|
|
@@ -1777,6 +1793,65 @@ export async function runDevPipeline(spec: DevPipelineSpec, deps: DevPipelineDep
|
|
|
1777
1793
|
}
|
|
1778
1794
|
}
|
|
1779
1795
|
|
|
1796
|
+
async function observeDevTaskDispatch(spec: DevPipelineSpec, plan: ResolvedDevPlan, deps: DevPipelineDeps): Promise<void> {
|
|
1797
|
+
const skipped = plan.dispatch === 'interactive' && getNestDepth() > 0 ? 'nested-interactive'
|
|
1798
|
+
: plan.relaunch === true ? 'relaunch'
|
|
1799
|
+
: plan.dispatch === 'parallel' ? 'parallel-parent'
|
|
1800
|
+
: deps.skipDispatchTask === true ? 'skipDispatchTask'
|
|
1801
|
+
: process.env.ELANOUS_DISPATCH_RECORDED === '1' ? 'dispatch-recorded'
|
|
1802
|
+
// `bun test` preload sets this so pipeline tests do not run the real gates (usage/recall
|
|
1803
|
+
// lookups) on every launch; tests that inject a dispatcher or gates still observe.
|
|
1804
|
+
: process.env.ELANOUS_EXECUTION_LOOP_DISPATCH === 'off' && !deps.dispatchTask && !deps.dispatchTaskDeps ? 'disabled'
|
|
1805
|
+
: undefined;
|
|
1806
|
+
if (skipped) {
|
|
1807
|
+
try { debug.log('execution-loop.gate', 'dispatch-skipped', { reason: skipped, ...(spec.runId ? { runId: spec.runId } : {}) }); } catch { /* fail-soft */ }
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
try {
|
|
1811
|
+
const goalText = 'text' in plan.input ? plan.input.text : (deps.readFile ?? ((path: string) => readFileSync(path, 'utf8')))(plan.input.file);
|
|
1812
|
+
const goalId = 'file' in plan.input ? parseGoalId(goalText) : null;
|
|
1813
|
+
let recordedCardId: string | undefined;
|
|
1814
|
+
const recordCard = (cardId: string): void => {
|
|
1815
|
+
if (!spec.runId || recordedCardId) return;
|
|
1816
|
+
recordedCardId = cardId;
|
|
1817
|
+
try {
|
|
1818
|
+
appendRunLedgerEntry({ timestamp: new Date().toISOString(), runId: spec.runId,
|
|
1819
|
+
event: 'execution-loop-card', data: { cardId } });
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
try { debug.log('execution-loop.gate', 'ledger-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
const dispatchDeps: DispatchTaskDeps = {
|
|
1825
|
+
...deps.dispatchTaskDeps,
|
|
1826
|
+
onCardCreated: (cardId) => {
|
|
1827
|
+
recordCard(cardId);
|
|
1828
|
+
deps.dispatchTaskDeps?.onCardCreated?.(cardId);
|
|
1829
|
+
},
|
|
1830
|
+
};
|
|
1831
|
+
const work = Promise.resolve().then(() => (deps.dispatchTask ?? dispatchTask)({
|
|
1832
|
+
goalId: goalId ?? `request-${createHash('sha256').update(goalText).digest('hex').slice(0, 32)}`,
|
|
1833
|
+
title: goalText.split(/\r?\n/, 1)[0]?.trim() || 'Untitled request',
|
|
1834
|
+
goalText,
|
|
1835
|
+
targetPaths: 'file' in plan.input ? tracedPathReferences(goalText).map((reference) => reference.path) : [],
|
|
1836
|
+
spec,
|
|
1837
|
+
...(spec.runId ? { runKey: spec.runId } : {}),
|
|
1838
|
+
}, dispatchDeps));
|
|
1839
|
+
// The default dispatcher bounds its gates to five seconds. Await its completed
|
|
1840
|
+
// observation so short-lived callers cannot exit before decisions reach the card.
|
|
1841
|
+
// Injected dispatchers retain an outer timeout to protect the original launch.
|
|
1842
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
1843
|
+
const result = await (deps.dispatchTask
|
|
1844
|
+
? Promise.race([
|
|
1845
|
+
work,
|
|
1846
|
+
new Promise<never>((_, reject) => { timeout = setTimeout(() => reject(new Error('dispatch timeout after 5000ms')), 5_000); }),
|
|
1847
|
+
]).finally(() => { if (timeout) clearTimeout(timeout); })
|
|
1848
|
+
: work);
|
|
1849
|
+
recordCard(result.cardId);
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
try { debug.log('execution-loop.gate', 'dispatch-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1780
1855
|
async function runDevPipelineDispatch(
|
|
1781
1856
|
spec: DevPipelineSpec,
|
|
1782
1857
|
deps: DevPipelineDeps,
|
|
@@ -1785,6 +1860,7 @@ async function runDevPipelineDispatch(
|
|
|
1785
1860
|
): Promise<DevPipelineRunResult> {
|
|
1786
1861
|
const plan = planDevPipeline(spec);
|
|
1787
1862
|
seen.plan = plan;
|
|
1863
|
+
await observeDevTaskDispatch(spec, plan, deps);
|
|
1788
1864
|
if (!plan.wired) {
|
|
1789
1865
|
throw new DevPipelineError(
|
|
1790
1866
|
`runDevPipeline: '${plan.dispatch}' 경로는 U4 골격에서 미배선(NotYetUnified). ` +
|
|
@@ -682,7 +682,9 @@ function runIsolatedBunTest(cwd: string, args: string[], timeout: number): Proce
|
|
|
682
682
|
return { status: null, stdout: '', stderr: message, error: error instanceof Error ? error : new Error(message) };
|
|
683
683
|
}
|
|
684
684
|
try {
|
|
685
|
-
|
|
685
|
+
// SIGKILL — a test that traps SIGTERM while blocked in a native wait never runs its handler, so spawnSync
|
|
686
|
+
// waited forever and its drive grandchildren lived on as ppid=1 orphans (09-30 O1: 19 processes, up to 11h).
|
|
687
|
+
return spawnSync('bun', ['test', ...args], { cwd, env: isolated.env, encoding: 'utf8', timeout, killSignal: 'SIGKILL', maxBuffer: 32 * 1024 * 1024 });
|
|
686
688
|
} finally {
|
|
687
689
|
isolated.cleanup();
|
|
688
690
|
}
|
|
@@ -32,6 +32,7 @@ import { quoteShellArg } from '../cli/logs-cli.js';
|
|
|
32
32
|
import type { HarnessGroundingProvenance } from './context-capsule.js';
|
|
33
33
|
import { hasGoalStepCodeName } from './goal-step-code-name.js';
|
|
34
34
|
import { targetScopedGoalText } from './goal-text-path-scope.js';
|
|
35
|
+
import { parseReleaseNoteSection } from '../release-loop/release-note.js';
|
|
35
36
|
|
|
36
37
|
// ⭐ `situation`/`complication` 은 **선택**이다 — 안 오면 `scqaNarrative` 가 종전 문면을 쓴다.
|
|
37
38
|
// ⛔ 필수로 만들지 않는다: 그러면 인핸싱이 폴백으로 떨어진 저작(LLM 실패·`disabled`)이 전부 죽는다.
|
|
@@ -334,7 +335,7 @@ function endGoalAuthorPhase(phase: GoalAuthorTimedPhase, startedAt: number, auth
|
|
|
334
335
|
|
|
335
336
|
export type GoalFileLintLevel = 'ERROR' | 'WARN';
|
|
336
337
|
|
|
337
|
-
export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation';
|
|
338
|
+
export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation' | 'release-note';
|
|
338
339
|
|
|
339
340
|
type GoalFileLintOrigin =
|
|
340
341
|
| { readonly kind: 'known-incident'; readonly incident: string; readonly reference: string }
|
|
@@ -369,6 +370,7 @@ export const GOAL_FILE_LINT_ORIGINS: Record<GoalFileLintTag, GoalFileLintOrigin>
|
|
|
369
370
|
'identifier-name-observation': { kind: 'known-incident', incident: "an authored observation of 'the list of test names this file registers' produced a test that greps its own source for test-name strings, which passes with every named test body emptied; 59 of 3164 goal documents carried the same wording", reference: 'PR #15791 (closed; superseded by #15802); docs/harness/observability/ISSUES.md OBS-T415' },
|
|
370
371
|
'self-reported-observation': { kind: 'known-incident', incident: "an authored observation of 'the captureScope field of the emitted JSON' stayed green across two failed implementations: a Chrome CLI flag Chrome silently ignores (capture stayed 1280x900 against a true 4651) and a CDP call that timed out leaving no file at all, both of which still emitted captureScope:'full-page'", reference: 'docs/manual/MANUAL-web-clone-to-reproducible-resource-2026-09-08.md section 6b; PR #16201; measured 2026-09-08: 190 of 2867 goal documents that carry a decision signal match this shape, three times the 63 that match identifier-name-observation' },
|
|
371
372
|
'default-invocation-observation': { kind: 'known-incident', incident: 'a scripts/*.ts checker landed green from fixture tests and, run once with no arguments from the repository root, reported covered 1 and uncovered 22 because its default catalog path never opened the map the goal named', reference: 'PR #19126; measured 2026-09-20 against the same-day controls #19123 and #19127' },
|
|
373
|
+
'release-note': { kind: 'unknown-origin', label: 'ORIGIN-UNKNOWN' },
|
|
372
374
|
} as const satisfies Record<GoalFileLintTag, GoalFileLintOrigin>;
|
|
373
375
|
|
|
374
376
|
/** Machine-countable reason for a finding that retains the canonical-structure tag. */
|
|
@@ -1452,6 +1454,21 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
|
|
|
1452
1454
|
for (const finding of askSectionCountLintFindings(document)) findings.push(finding);
|
|
1453
1455
|
for (const finding of headingFormMarkerLintFindings(document)) findings.push(finding);
|
|
1454
1456
|
for (const finding of blanketInvariantLintFindings(document)) findings.push(finding);
|
|
1457
|
+
const releaseNote = parseReleaseNoteSection(document);
|
|
1458
|
+
for (const problem of releaseNote.problems) {
|
|
1459
|
+
findings.push({
|
|
1460
|
+
level: 'WARN', tag: 'release-note',
|
|
1461
|
+
message: `## 릴리스 노트 ${problem} invalid or missing; use - 한 줄: English summary, - 종류: feat|fix|security|internal (feature → feat), - 문서: path or 없음(이유), - 대상: next|later`,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
if (releaseNote.fragment || releaseNote.problems.length) {
|
|
1465
|
+
const section = markdownSection(document, '릴리스 노트') ?? '';
|
|
1466
|
+
const kind = releaseNote.fragment?.kind ?? /^[ \t]*- 종류:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
|
|
1467
|
+
const line = releaseNote.fragment?.line ?? /^[ \t]*- 한 줄:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
|
|
1468
|
+
if (kind && ['feat', 'fix', 'security'].includes(kind) && line && /[가-힣ㄱ-ㅎㅏ-ㅣ]/u.test(line)) {
|
|
1469
|
+
findings.push({ level: 'WARN', tag: 'release-note', message: '## 릴리스 노트 한 줄: 공개 노트에 실리는 문장은 영어로(internal 은 한국어 무방)' });
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1455
1472
|
for (const violation of selfQuestionSubjectViolations) {
|
|
1456
1473
|
findings.push({
|
|
1457
1474
|
level: 'WARN',
|
|
@@ -27,9 +27,13 @@ export type HostRegateDeps = {
|
|
|
27
27
|
log?: (event: 'passed' | 'failed' | 'unmeasured' | 'base-raced', data: Record<string, unknown>) => void;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
const SPAWN_MAX_BUFFER = 256 * 1024 * 1024;
|
|
31
|
+
|
|
30
32
|
const defaultCommand: NonNullable<HostRegateDeps['command']> = (bin, args, cwd, env) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
// bun ≥1.4 applies Node's 1MB default maxBuffer and silently truncates — test output here is often larger.
|
|
34
|
+
const r = spawnSync(bin, [...args], { cwd, env: env ?? process.env, encoding: 'utf8', timeout: 600_000, maxBuffer: SPAWN_MAX_BUFFER });
|
|
35
|
+
const overflow = (r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS';
|
|
36
|
+
return { status: overflow ? null : r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? `${r.error.message} (${overflow ? `output exceeded ${SPAWN_MAX_BUFFER} bytes` : 'command unavailable'})` : '') };
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
/** The informational pr-land gate always returns zero; enforce its measured report and the combined test result instead. */
|
|
@@ -3307,9 +3307,14 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3307
3307
|
if (opts.childLlm && (typeof opts.childLlm.provider !== 'string' || !opts.childLlm.provider.trim() || typeof opts.childLlm.model !== 'string' || !opts.childLlm.model.trim())) {
|
|
3308
3308
|
throw new Error('childLlm.provider and childLlm.model must both be non-empty');
|
|
3309
3309
|
}
|
|
3310
|
-
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
|
|
3311
3310
|
const identity = resolveRunIdentity({ explicit: opts.runId });
|
|
3312
3311
|
const { runId } = identity;
|
|
3312
|
+
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
|
|
3313
|
+
try {
|
|
3314
|
+
debug.log('self-implement.branch', 'planned', {
|
|
3315
|
+
runId, goalId: opts.goalId, branch, runSuffix: opts.branchName === undefined ? branch.match(/-r[a-z0-9]{6}$/)?.[0] ?? null : null,
|
|
3316
|
+
});
|
|
3317
|
+
} catch { /* observation must not block the run */ }
|
|
3313
3318
|
const attemptOrdinal = incrementRunAttemptOrdinal(runId);
|
|
3314
3319
|
const shardIdentity = parseRunShardIdentity(opts.feature);
|
|
3315
3320
|
const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
|
|
@@ -4517,8 +4522,8 @@ async function runSelfImplementInner(
|
|
|
4517
4522
|
roundClassifications.push(classification);
|
|
4518
4523
|
onRoundClassification(classification);
|
|
4519
4524
|
};
|
|
4520
|
-
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
|
|
4521
4525
|
const { runId, source: runIdSource } = identity ?? resolveRunIdentity({ explicit: opts.runId });
|
|
4526
|
+
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
|
|
4522
4527
|
try {
|
|
4523
4528
|
debug.log('run-identity', 'own', {
|
|
4524
4529
|
runId, branch, source: runIdSource, nestDepth: nestInfo().depth, ...originObservationFields(),
|
|
@@ -60,7 +60,7 @@ import { pwaReachableSrcFiles } from './pwa-import-graph.js';
|
|
|
60
60
|
import { stageNonGitDir, stageFile, applyShadowToTarget, applyFileToTarget } from './shadow-stage.js';
|
|
61
61
|
import type { TargetKind } from './target-kind.js';
|
|
62
62
|
import { basename } from 'node:path';
|
|
63
|
-
import { makePrManager, type PrManager } from '../autopilot/pr-manager.js';
|
|
63
|
+
import { ghAutomationEnv, makePrManager, type PrManager } from '../autopilot/pr-manager.js';
|
|
64
64
|
import { defaultSpawnGh, lookupOpenDraftPrs } from '../cli/logs-abandoned-draft-prs.js';
|
|
65
65
|
import { loadRunLedger, runLedgerDir } from './run-ledger.js';
|
|
66
66
|
import { readdirSync } from 'node:fs';
|
|
@@ -2347,8 +2347,10 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2347
2347
|
closePr: (prUrl, comment) => (o.prManager ?? makePrManager()).closePr(prUrl, comment),
|
|
2348
2348
|
|
|
2349
2349
|
postPrComment: async ({ number, body, cwd }) => {
|
|
2350
|
+
// Round comments go out as the GitHub App like the PR itself — a personal `gh` login made the owner a
|
|
2351
|
+
// participant and mailed them on merge (#22089).
|
|
2350
2352
|
await execFileAsync('gh', ['pr', 'comment', String(number), '--body', body], {
|
|
2351
|
-
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024,
|
|
2353
|
+
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
|
|
2352
2354
|
});
|
|
2353
2355
|
},
|
|
2354
2356
|
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
// ★ 제1원칙: front door 결정 observe(harness.frontdoor) — objective digest·autoDrive·terminal.
|
|
14
14
|
// autoDrive 'safe'(기본): 저위험 자율·고위험(PR open)만 ux.confirm(막 채널 없으면 fail-closed).
|
|
15
15
|
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
16
17
|
import { tierModel } from '../../llm/model-defaults.js';
|
|
18
|
+
import { dispatchTask, type DispatchTaskDeps } from '../../execution-loop/dispatch-task.js';
|
|
17
19
|
import type { LLMToolSpec } from '../../llm.js';
|
|
18
20
|
import type { DaemonToolDispatchCtx } from '../../boot/daemon-tools/types.js';
|
|
19
21
|
import type { SelfImplementSeams } from '../../self-implement/orchestrator.js';
|
|
@@ -96,6 +98,9 @@ export function buildRunDevHarnessTool(): LLMToolSpec {
|
|
|
96
98
|
export interface DevHarnessDeps {
|
|
97
99
|
/** 하니스 구동(기본 runStagedHarnessOnSurface). */
|
|
98
100
|
runHarness?: (opts: RunHarnessOnSurfaceOptions) => Promise<HarnessResult>;
|
|
101
|
+
/** In-process 실행 직전의 관측 전용 디스패치 seam. */
|
|
102
|
+
dispatchTask?: typeof dispatchTask;
|
|
103
|
+
dispatchTaskDeps?: DispatchTaskDeps;
|
|
99
104
|
/** seam 팩토리(기본 defaultSeams). */
|
|
100
105
|
seamsFactory?: (o: DefaultSeamsOptions) => SelfImplementSeams;
|
|
101
106
|
/** 비대화형 HITL relay 팩토리 seam(기본 buildLlmHitlRelay). */
|
|
@@ -468,6 +473,43 @@ export async function dispatchRunDevHarness(
|
|
|
468
473
|
const runHarness = deps?.runHarness ?? runStagedHarnessOnSurface;
|
|
469
474
|
const knobs = resolveHarnessKnobs(rawArgs, ctx?.userText); // ★ B1 관통 — C1~C4 노브를 surface 파라미터에서 막으로
|
|
470
475
|
const harnessMention = harnessMentionState(ctx?.userText);
|
|
476
|
+
// Outer pipeline/Pod observations are not repeated; this decision never controls the harness.
|
|
477
|
+
const dispatchSkipped = rawArgs.plan_staged === true ? 'plan-staged'
|
|
478
|
+
: rawArgs.dispatch_recorded === true || process.env.ELANOUS_DISPATCH_RECORDED === '1' ? 'dispatch-recorded'
|
|
479
|
+
: process.env.ELANOUS_EXECUTION_LOOP_DISPATCH === 'off' && !deps?.dispatchTask && !deps?.dispatchTaskDeps ? 'disabled'
|
|
480
|
+
: undefined;
|
|
481
|
+
if (dispatchSkipped) {
|
|
482
|
+
try { debug.log('execution-loop.gate', 'dispatch-skipped', { reason: dispatchSkipped, runId }); } catch { /* fail-soft */ }
|
|
483
|
+
} else {
|
|
484
|
+
try {
|
|
485
|
+
const spec = { input: { text: objective }, executor: { kind: 'self' as const }, plan: true, runId };
|
|
486
|
+
const observer = new AbortController();
|
|
487
|
+
const timeoutMs = Math.min(5_000, Math.max(0, deps?.dispatchTaskDeps?.timeoutMs ?? 5_000));
|
|
488
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
489
|
+
try {
|
|
490
|
+
await Promise.race([
|
|
491
|
+
Promise.resolve().then(() => (deps?.dispatchTask ?? dispatchTask)({
|
|
492
|
+
goalId: `request-${createHash('sha256').update(objective).digest('hex').slice(0, 32)}`,
|
|
493
|
+
title: objective.split(/\r?\n/, 1)[0]?.trim() || 'Untitled request',
|
|
494
|
+
goalText: objective,
|
|
495
|
+
targetPaths: [],
|
|
496
|
+
spec,
|
|
497
|
+
runKey: runId,
|
|
498
|
+
}, { ...deps?.dispatchTaskDeps, signal: observer.signal })),
|
|
499
|
+
new Promise<never>((_, reject) => {
|
|
500
|
+
timer = setTimeout(() => {
|
|
501
|
+
observer.abort();
|
|
502
|
+
reject(new Error(`dispatch observation timed out after ${timeoutMs}ms`));
|
|
503
|
+
}, timeoutMs);
|
|
504
|
+
}),
|
|
505
|
+
]);
|
|
506
|
+
} finally {
|
|
507
|
+
if (timer) clearTimeout(timer);
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
try { debug.log('execution-loop.gate', 'dispatch-unavailable', { reason: String(error), runId }); } catch { /* fail-soft */ }
|
|
511
|
+
}
|
|
512
|
+
}
|
|
471
513
|
const result = await runHarness({
|
|
472
514
|
// ⛔⭐ `naturalLanguageDispatch` 를 «단정»하지 않는다 — 증거로 정한다.
|
|
473
515
|
// 이 함수는 «둘» 이 지난다: ⑴ 모델이 부른 도구(사용자 턴 문면이 ctx.userText 로 온다)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { CardStore, redactSecrets, type AppendSectionInput, type CreateCardInput, type TaskCard } from '../task-cards/card-store.js';
|
|
3
|
+
import type { ScheduledDecision, TriageIssue } from './triage.js';
|
|
4
|
+
|
|
5
|
+
export interface StewardCardStore {
|
|
6
|
+
createCard(input: CreateCardInput): TaskCard;
|
|
7
|
+
appendSection(id: string, input: AppendSectionInput): TaskCard;
|
|
8
|
+
close(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StewardCardDeps {
|
|
12
|
+
store?: StewardCardStore;
|
|
13
|
+
root?: string;
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Record observe-only intake and the latest scheduled judgment without launching any work. */
|
|
18
|
+
export function recordTriageOnCards(decisions: ScheduledDecision[], issues: TriageIssue[], deps: StewardCardDeps = {}): void {
|
|
19
|
+
const store = deps.store ?? new CardStore(deps.root);
|
|
20
|
+
const byIssue = new Map(issues.map(issue => [issue.identifier, issue]));
|
|
21
|
+
try {
|
|
22
|
+
for (const decision of decisions) {
|
|
23
|
+
const issue = byIssue.get(decision.issue);
|
|
24
|
+
if (!issue) throw new Error(`Missing issue ${decision.issue}`);
|
|
25
|
+
const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
|
|
26
|
+
const intakeKey = `intake:${issue.identifier}`;
|
|
27
|
+
if (!card.sections.some(section => section.key === intakeKey)) {
|
|
28
|
+
const directiveSource = /^출처: (telegram|pwa|tui|cli)$/m.exec(issue.body)?.[1];
|
|
29
|
+
const directiveTime = /^시각: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)$/m.exec(issue.body)?.[1];
|
|
30
|
+
store.appendSection(card.id, {
|
|
31
|
+
key: intakeKey, owner: 'steward',
|
|
32
|
+
content: JSON.stringify({ summary: redactSecrets(issue.title), source: directiveSource ?? `linear:${issue.identifier}`, at: directiveTime ?? (deps.now ?? (() => new Date()))().toISOString() }),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const { rung, dependsOn, priority, disposition, hitlReason, why, owner } = decision;
|
|
36
|
+
const judgment = { rung, dependsOn, priority, disposition: rung === 'hitl' || hitlReason ? 'hitl' : disposition, hitlReason: hitlReason ?? null, why: redactSecrets(why), ...(owner ? { owner } : {}) };
|
|
37
|
+
const key = `triage:${createHash('sha256').update(JSON.stringify(judgment)).digest('hex')}`;
|
|
38
|
+
if (!card.sections.some(section => section.key === key)) {
|
|
39
|
+
store.appendSection(card.id, { key, owner: 'steward', content: JSON.stringify(judgment) });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} finally {
|
|
43
|
+
if (!deps.store) store.close();
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/steward/triage.ts
CHANGED
|
@@ -4,30 +4,39 @@ import { fetchLinearIssues } from '../connectors/linear.js';
|
|
|
4
4
|
import { getSecretAsync } from '../nexus/config/secrets/index.js';
|
|
5
5
|
import { getUserConfig } from '../user-config.js';
|
|
6
6
|
import { emitDecision } from '../live/detail-switch.js';
|
|
7
|
+
import { debug, redactSecretText } from '../debug/log.js';
|
|
7
8
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
9
|
+
import { redactSecrets } from '../task-cards/card-store.js';
|
|
8
10
|
import { directiveHash } from './directive.js';
|
|
11
|
+
import { recordTriageOnCards, type StewardCardDeps } from './steward-cards.js';
|
|
9
12
|
|
|
10
13
|
export type Rung = 0 | 1 | 2 | 3 | 4 | 5 | 'hitl';
|
|
11
|
-
|
|
14
|
+
/** `other` = the judge chose human review for a reason outside the four named ones (kept as HITL, never downgraded). */
|
|
15
|
+
export type HitlReason = 'money' | 'public' | 'security' | 'irreversible' | 'other';
|
|
12
16
|
export interface TriageIssue { identifier: string; ref: string; title: string; body: string }
|
|
13
17
|
export interface TriageDecision {
|
|
14
18
|
issue: string; rung: Rung; dependsOn: string[]; priority: number; duplicateOf?: string;
|
|
15
19
|
hitlReason?: HitlReason; why: string; role?: string; cost?: number;
|
|
20
|
+
/** Owner track from `loops.steward.tracks` (absent when no table is configured or the judge picked none of its keys). */
|
|
21
|
+
owner?: string;
|
|
16
22
|
}
|
|
17
23
|
export interface ScheduledDecision extends TriageDecision { disposition: 'now' | 'wait' | 'hitl' }
|
|
18
24
|
export interface StewardSettings {
|
|
19
25
|
mode?: 'observe' | 'act'; linearTeam?: string;
|
|
20
26
|
roles?: Record<string, { maxConcurrent?: number }>;
|
|
21
27
|
budget?: number;
|
|
28
|
+
tracks?: Record<string, string>;
|
|
22
29
|
}
|
|
23
30
|
export interface StewardDeps {
|
|
24
31
|
fetch?: typeof fetch;
|
|
25
32
|
getSecret?: (id: string) => Promise<string | undefined>;
|
|
26
|
-
judge?: (issue: TriageIssue, issues: TriageIssue[]) => Promise<unknown>;
|
|
33
|
+
judge?: (issue: TriageIssue, issues: TriageIssue[], tracks?: Record<string, string>) => Promise<unknown>;
|
|
27
34
|
decide?: typeof emitDecision;
|
|
28
35
|
sendDigest?: (text: string) => Promise<void>;
|
|
29
36
|
now?: () => Date;
|
|
30
37
|
root?: string;
|
|
38
|
+
cardStore?: StewardCardDeps['store'];
|
|
39
|
+
warn?: (message: string) => void;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export function stewardSettings(): StewardSettings {
|
|
@@ -41,7 +50,7 @@ const unsafe: Array<[HitlReason, RegExp]> = [
|
|
|
41
50
|
['irreversible', /비가역|강제\s*푸시|force.push|영구\s*삭제|리셋권\s*소비|drop\s*table|초기화/i],
|
|
42
51
|
];
|
|
43
52
|
|
|
44
|
-
function parseJudgment(issue: TriageIssue, raw: unknown): TriageDecision {
|
|
53
|
+
function parseJudgment(issue: TriageIssue, raw: unknown, tracks?: Record<string, string>): TriageDecision {
|
|
45
54
|
const value = typeof raw === 'string' ? JSON.parse(raw) as unknown : raw;
|
|
46
55
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`Invalid triage judgment: ${issue.identifier}`);
|
|
47
56
|
const d = value as Record<string, unknown>;
|
|
@@ -51,8 +60,9 @@ function parseJudgment(issue: TriageIssue, raw: unknown): TriageDecision {
|
|
|
51
60
|
throw new Error(`Invalid triage judgment: ${issue.identifier}`);
|
|
52
61
|
}
|
|
53
62
|
const forced = unsafe.find(([, pattern]) => pattern.test(`${issue.title}\n${issue.body}`))?.[0];
|
|
54
|
-
const
|
|
55
|
-
|
|
63
|
+
const named = forced ?? (unsafe.some(([reason]) => reason === d.hitlReason) ? d.hitlReason as HitlReason : undefined);
|
|
64
|
+
// A HITL judgment without one of the four named reasons stays HITL (`other`) — one issue must not stop the stage.
|
|
65
|
+
const hitlReason = named ?? (d.rung === 'hitl' ? 'other' as const : undefined);
|
|
56
66
|
return {
|
|
57
67
|
issue: issue.identifier, rung: hitlReason ? 'hitl' : d.rung as Rung,
|
|
58
68
|
dependsOn: d.dependsOn as string[], priority: d.priority as number,
|
|
@@ -60,24 +70,40 @@ function parseJudgment(issue: TriageIssue, raw: unknown): TriageDecision {
|
|
|
60
70
|
...(hitlReason ? { hitlReason } : {}), why: (d.why as string).trim().replace(/\s+/g, ' '),
|
|
61
71
|
...(typeof d.role === 'string' ? { role: d.role } : {}),
|
|
62
72
|
...(typeof d.cost === 'number' && Number.isFinite(d.cost) && d.cost >= 0 ? { cost: d.cost } : {}),
|
|
73
|
+
...(tracks && typeof d.owner === 'string' && Object.hasOwn(tracks, d.owner) ? { owner: d.owner } : {}),
|
|
63
74
|
};
|
|
64
75
|
}
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
/** The role table as prompt lines — read from config so a reorg edits one place. */
|
|
78
|
+
export function trackTablePrompt(tracks?: Record<string, string>): string {
|
|
79
|
+
if (!tracks || !Object.keys(tracks).length) return '';
|
|
80
|
+
const keys = Object.keys(tracks);
|
|
81
|
+
return `\n담당 트랙(owner) — 아래 중 하나(${keys.map(k => JSON.stringify(k)).join('|')}) · 이슈가 어느 트랙 일인지:\n${keys.map(k => `- ${k}: ${tracks[k]}`).join('\n')}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function judgeStewardIssue(issue: TriageIssue, issues: TriageIssue[], tracks?: Record<string, string>): Promise<unknown> {
|
|
85
|
+
const ownerField = tracks && Object.keys(tracks).length ? `,"owner":${JSON.stringify(Object.keys(tracks)[0])}` : '';
|
|
86
|
+
const prompt = `스튜어드 트리아지. JSON 객체만 출력: {"rung":0|1|2|3|4|5|"hitl","dependsOn":[],"priority":1,"duplicateOf":null,"hitlReason":null,"why":"한 문장","role":"builder","cost":0${ownerField}}.\n0=이미됨/중복, 1=셸, 2=CLI, 3=조사, 4=하니스 골, 5=외부 에이전트. 돈·공개·보안·비가역은 hitl(hitlReason = "money"|"public"|"security"|"irreversible" · 그 밖의 이유로 사람 확인이 필요하면 "other"). 확인되지 않은 완료/중복은 추측하지 마라. 의존은 제공된 이슈 키만 써라.${trackTablePrompt(tracks)}\n이슈 목록: ${JSON.stringify(issues.map(i => ({ key: i.identifier, title: i.title })))}\n판정 대상: ${JSON.stringify(issue)}`;
|
|
68
87
|
const { streamLLM } = await import('../llm.js');
|
|
69
88
|
const { tierModel } = await import('../llm/model-defaults.js');
|
|
70
89
|
const result = await streamLLM([{ role: 'user', content: prompt }], () => {}, { model: tierModel('best') });
|
|
71
90
|
return JSON.parse(result.replace(/^```(?:json)?\s*|\s*```$/g, '').trim()) as unknown;
|
|
72
91
|
}
|
|
73
92
|
|
|
74
|
-
export async function triageIssues(issues: TriageIssue[], judge: NonNullable<StewardDeps['judge']> = judgeStewardIssue, decide: typeof emitDecision = emitDecision): Promise<TriageDecision[]> {
|
|
93
|
+
export async function triageIssues(issues: TriageIssue[], judge: NonNullable<StewardDeps['judge']> = judgeStewardIssue, decide: typeof emitDecision = emitDecision, tracks?: Record<string, string>): Promise<TriageDecision[]> {
|
|
75
94
|
const decisions: TriageDecision[] = [];
|
|
76
95
|
for (const issue of issues) {
|
|
77
|
-
|
|
96
|
+
let result: TriageDecision;
|
|
97
|
+
try {
|
|
98
|
+
result = parseJudgment(issue, await judge(issue, issues, tracks), tracks);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// A malformed or failed judgment for one issue goes to a human; the other issues are still triaged.
|
|
101
|
+
try { debug.log('steward.stage', 'judgment-invalid', { issue: issue.identifier, reason: redactSecrets(String(error)) }); } catch { /* fail-soft */ }
|
|
102
|
+
result = { issue: issue.identifier, rung: 'hitl', hitlReason: 'other', dependsOn: [], priority: 0, why: 'triage judgment unavailable — human review' };
|
|
103
|
+
}
|
|
78
104
|
decisions.push(result);
|
|
79
105
|
decide({ kind: result.rung === 'hitl' ? 'ESCALATE' : 'ROUTE', what: issue.identifier, reason: result.why,
|
|
80
|
-
purpose: 'steward triage', target: String(result.rung), refs: { issue: issue.identifier } });
|
|
106
|
+
purpose: 'steward triage', target: result.owner ? `${result.rung} · ${result.owner}` : String(result.rung), refs: { issue: issue.identifier } });
|
|
81
107
|
}
|
|
82
108
|
return decisions;
|
|
83
109
|
}
|
|
@@ -99,7 +125,7 @@ export function scheduleTriage(decisions: TriageDecision[], settings: StewardSet
|
|
|
99
125
|
const slots = { ...running };
|
|
100
126
|
let remaining = settings.budget ?? Infinity;
|
|
101
127
|
return sorted.map(item => {
|
|
102
|
-
const role = item.role ?? String(item.rung);
|
|
128
|
+
const role = item.owner ?? item.role ?? String(item.rung);
|
|
103
129
|
const max = settings.roles?.[role]?.maxConcurrent ?? Infinity;
|
|
104
130
|
const blocked = item.dependsOn.some(dep => !completed.has(dep));
|
|
105
131
|
const status: ScheduledDecision['disposition'] = item.rung === 'hitl' || item.hitlReason ? 'hitl' :
|
|
@@ -146,7 +172,7 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
146
172
|
} else if (stage === 'triage') {
|
|
147
173
|
const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
|
|
148
174
|
const judge = deps.judge ?? judgeStewardIssue;
|
|
149
|
-
writeFileSync(judgments, JSON.stringify(await triageIssues(issues, judge, deps.decide)));
|
|
175
|
+
writeFileSync(judgments, JSON.stringify(await triageIssues(issues, judge, deps.decide, settings.tracks)));
|
|
150
176
|
} else if (stage === 'schedule') {
|
|
151
177
|
const decisions = JSON.parse(readFileSync(judgments, 'utf8')) as TriageDecision[];
|
|
152
178
|
const pending = new Set(decisions.flatMap(decision => decision.dependsOn));
|
|
@@ -163,6 +189,13 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
163
189
|
const state = readState(path);
|
|
164
190
|
const at = (deps.now ?? (() => new Date()))().toISOString();
|
|
165
191
|
const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
|
|
192
|
+
try {
|
|
193
|
+
recordTriageOnCards(rows, issues, { root, store: deps.cardStore, now: deps.now });
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const message = `steward report: card write failed: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
|
|
196
|
+
debug.log('steward.cards', 'write-failed', { error: message });
|
|
197
|
+
(deps.warn ?? console.warn)(message);
|
|
198
|
+
}
|
|
166
199
|
for (const row of rows) {
|
|
167
200
|
const issue = issues.find(item => item.identifier === row.issue);
|
|
168
201
|
if (!issue) throw new Error(`Missing issue ${row.issue}`);
|
|
@@ -195,8 +228,11 @@ if (import.meta.main) {
|
|
|
195
228
|
console.error('steward: expected sync|triage|schedule|report');
|
|
196
229
|
process.exitCode = 2;
|
|
197
230
|
} else {
|
|
198
|
-
runStewardStage(stage as 'sync' | 'triage' | 'schedule' | 'report').catch(() => {
|
|
199
|
-
|
|
231
|
+
runStewardStage(stage as 'sync' | 'triage' | 'schedule' | 'report').catch((error: unknown) => {
|
|
232
|
+
// Say why (secrets masked) — 09-29 the first resident run failed as a bare «stage failed» on a missing Linear key.
|
|
233
|
+
const reason = redactSecretText(error instanceof Error ? error.message : String(error)).slice(0, 300);
|
|
234
|
+
console.error(`steward ${stage}: stage failed — ${reason}`);
|
|
235
|
+
debug.log('steward.stage', 'failed', { stage, reason });
|
|
200
236
|
process.exitCode = 1;
|
|
201
237
|
});
|
|
202
238
|
}
|
|
@@ -2,6 +2,8 @@ import { lstatSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { gunzipSync } from 'node:zlib';
|
|
4
4
|
import { debug } from '../../debug/log.js';
|
|
5
|
+
import { getDefaultLogStore, type LogStore } from '../../mss/logging/log-store.js';
|
|
6
|
+
import type { LogRecord } from '../../mss/logging/record.js';
|
|
5
7
|
|
|
6
8
|
const FILE_LIMIT = 5 * 1024 * 1024;
|
|
7
9
|
const TOTAL_LIMIT = 20 * 1024 * 1024;
|
|
@@ -79,14 +81,63 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
|
|
|
79
81
|
return error.length ? { error, artifacts } : artifacts;
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
const POD_LOGS_PATH = 'pod-logs/logs.jsonl';
|
|
85
|
+
|
|
86
|
+
function childRunId(data: unknown): string | undefined {
|
|
87
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined;
|
|
88
|
+
const value = (data as Record<string, unknown>).runId;
|
|
89
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function decisionRecord(line: string): { rec: LogRecord; surface: string } | undefined {
|
|
93
|
+
let parsed: unknown;
|
|
94
|
+
try { parsed = JSON.parse(line); } catch { return undefined; }
|
|
95
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
|
|
96
|
+
const row = parsed as Record<string, unknown>;
|
|
97
|
+
if (row.category !== 'harness.decision') return undefined;
|
|
98
|
+
if (typeof row.ts !== 'string' || typeof row.event !== 'string') return undefined;
|
|
99
|
+
const runId = childRunId(row.data);
|
|
100
|
+
if (!runId) return undefined;
|
|
101
|
+
const data = { ...(row.data as Record<string, unknown>), podJob: '', importedFrom: 'pod' as const };
|
|
102
|
+
return {
|
|
103
|
+
rec: {
|
|
104
|
+
ts: row.ts,
|
|
105
|
+
category: 'harness.decision',
|
|
106
|
+
event: row.event,
|
|
107
|
+
data,
|
|
108
|
+
...(typeof row.level === 'string' ? { level: row.level as LogRecord['level'] } : {}),
|
|
109
|
+
...(typeof row.trace_id === 'string' ? { trace_id: row.trace_id } : {}),
|
|
110
|
+
},
|
|
111
|
+
surface: typeof row.surface === 'string' && row.surface ? row.surface : 'pod',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Import only harness.decision rows from a returned pod log into the launching host store. */
|
|
116
|
+
export function importPodDecisionLogs(bytes: Buffer, job: string, store: LogStore | null = getDefaultLogStore()): { imported: number; skipped: number } {
|
|
117
|
+
if (!store) return { imported: 0, skipped: 0 };
|
|
118
|
+
const text = bytes.toString('utf8');
|
|
119
|
+
const rows: Array<{ rec: LogRecord; surface: string }> = [];
|
|
120
|
+
let skipped = 0;
|
|
121
|
+
for (const line of text.split(/\r?\n/)) {
|
|
122
|
+
if (!line) continue;
|
|
123
|
+
const row = decisionRecord(line);
|
|
124
|
+
if (!row) { skipped += 1; continue; }
|
|
125
|
+
row.rec.data = { ...(row.rec.data as Record<string, unknown>), podJob: job };
|
|
126
|
+
rows.push(row);
|
|
127
|
+
}
|
|
128
|
+
if (rows.length > 0) store.insertBatch(rows);
|
|
129
|
+
return { imported: rows.length, skipped };
|
|
130
|
+
}
|
|
131
|
+
|
|
82
132
|
/** Reject symlink parents as well as lexical traversal; never replace a host file. */
|
|
83
|
-
export function collectPodArtifacts(logs: string, { dir, job, log = (c, e, d) => debug.log(c, e, d) }: {
|
|
133
|
+
export function collectPodArtifacts(logs: string, { dir, job, log = (c, e, d) => debug.log(c, e, d), logStore = getDefaultLogStore() }: {
|
|
84
134
|
dir: string;
|
|
85
135
|
job: string;
|
|
86
136
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
137
|
+
logStore?: LogStore | null;
|
|
87
138
|
}): void {
|
|
88
139
|
const parsed = parsePodArtifactChunks(logs);
|
|
89
|
-
const podLogsPath =
|
|
140
|
+
const podLogsPath = POD_LOGS_PATH;
|
|
90
141
|
let podLogsReturned = false;
|
|
91
142
|
let podLogsMissingReason = logs.split(/\r?\n/).find((line) => line.startsWith(`ELANOUS_POD_ARTIFACT_SKIPPED ${podLogsPath} `))
|
|
92
143
|
? 'artifact skipped (size limit or encoding failure)'
|
|
@@ -123,7 +174,14 @@ export function collectPodArtifacts(logs: string, { dir, job, log = (c, e, d) =>
|
|
|
123
174
|
log('self-implement.pod', 'artifact-collected', { job, path, bytes: bytes.length });
|
|
124
175
|
if (path === podLogsPath) {
|
|
125
176
|
podLogsReturned = true;
|
|
126
|
-
|
|
177
|
+
const lines = bytes.length ? bytes.toString('utf8').split('\n').length - (bytes.at(-1) === 10 ? 1 : 0) : 0;
|
|
178
|
+
log('self-implement.pod', 'pod-logs-returned', { job, path: destination, lines });
|
|
179
|
+
try {
|
|
180
|
+
const imported = importPodDecisionLogs(bytes, job, logStore);
|
|
181
|
+
log('self-implement.pod', 'pod-decisions-imported', { job, imported: imported.imported, skipped: imported.skipped });
|
|
182
|
+
} catch (error) {
|
|
183
|
+
log('self-implement.pod', 'pod-logs-import-failed', { job, reason: error instanceof Error ? error.message : String(error) });
|
|
184
|
+
}
|
|
127
185
|
}
|
|
128
186
|
} catch (e) {
|
|
129
187
|
const reason = (e as NodeJS.ErrnoException).code === 'EEXIST' ? 'exists' : e instanceof Error ? e.message : String(e);
|