elanous 0.2.17 → 0.2.19
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/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +25 -24
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +24 -23
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +28 -27
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +6 -3
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/lib/pch-regress-map.ts +23 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +436 -59
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +120 -0
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +8 -1
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +174 -0
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +45 -7
- package/scripts/release-loop/version-node.ts +145 -40
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/away/presence.ts +40 -0
- package/src/away/release-watch.ts +255 -0
- package/src/briefing/brief-items.ts +86 -17
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +85 -7
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +128 -19
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +403 -4
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +244 -40
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/graph-runner/runner.ts +25 -5
- package/src/harness/harness-cli-command.ts +12 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/harness-salvage-cli.ts +78 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/hq.ts +6 -1
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +185 -18
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -15
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +106 -4
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +450 -54
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/http-server.ts +16 -5
- package/src/nexus/api/loop-edges.ts +352 -23
- package/src/nexus/api/ops-api.ts +96 -28
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/nexus/index.ts +7 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +106 -6
- package/src/release-loop/feature-store.ts +64 -16
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +64 -20
- package/src/release-loop/rubric.ts +136 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +12 -6
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-cli.ts +36 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +179 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +61 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +101 -7
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +203 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +49 -5
- package/src/task-agent/live-moves.ts +176 -0
- package/src/task-agent/mission.ts +449 -0
- package/src/task-agent/shadow.ts +258 -0
- package/src/task-agent/task-hand.ts +337 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
- package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +88 -20
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +11 -0
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/telegram.ts +5 -20
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +196 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-aac7332576c4d6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3143b07a817d1da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-17ba086ef82d339b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-6e5c3a867557298b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4187666132fc98c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-09328d499162bae4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-29ff3a8f84f03b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-80ec7401bc83f511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-fc7d86467291ae38.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-043a9111e74f6b24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-23a4601ceb7f452e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-10d958605a77b3bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-12916918988ac97e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7f49fe645d5132f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-424659bacfb8cdd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d4fef9ccd7d406b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
|
@@ -46,7 +46,9 @@ export const FEATURE_MATURITY = {
|
|
|
46
46
|
'/ops/seats': { pwa: 'ops' },
|
|
47
47
|
'/ops/checklist': { pwa: 'ops' },
|
|
48
48
|
'/ceo': { pwa: 'ops' },
|
|
49
|
+
'/decisions': { pwa: 'ops' },
|
|
49
50
|
'/dashboard': { pwa: 'ops' },
|
|
51
|
+
'/pitch': { pwa: 'ops' }, // PITCH-ROOM — 투자자 피치 방(운영자 전용 · 공개 내보내기 제외)
|
|
50
52
|
'/bots': { pwa: 'ops' },
|
|
51
53
|
'/botlab': { pwa: 'ops' },
|
|
52
54
|
'/morning': { pwa: 'broken' },
|
|
@@ -56,7 +58,7 @@ export const FEATURE_MATURITY = {
|
|
|
56
58
|
},
|
|
57
59
|
// Canonical CLI command names only; aliases inherit their command's grade.
|
|
58
60
|
cliRoot: {
|
|
59
|
-
pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
61
|
+
preview: 'beta', pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
60
62
|
where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
61
63
|
pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
|
|
62
64
|
research: 'beta', release: 'beta', know: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
@@ -72,10 +74,10 @@ export const FEATURE_MATURITY = {
|
|
|
72
74
|
loops: 'beta', buzz: 'beta', harness: 'stable', self: 'beta', factcheck: 'beta',
|
|
73
75
|
provider: 'stable', 'provider:set': 'beta', 'provider:restore': 'beta',
|
|
74
76
|
'provider:rotate': 'beta', 'provider:use': 'beta', 'status-bar': 'beta',
|
|
75
|
-
schedule: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
|
|
77
|
+
schedule: 'beta', guardian: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
|
|
76
78
|
decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
|
|
77
79
|
env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
|
|
78
|
-
config: 'stable', tasks: 'stable', brief: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
80
|
+
config: 'stable', tasks: 'stable', brief: 'beta', notify: 'beta', away: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
79
81
|
onboarding: 'beta', login: 'stable', session: 'beta', chat: 'stable', repl: 'stable',
|
|
80
82
|
agent: 'stable', registry: 'beta', tier: 'stable', local: 'beta', scheduler: 'beta',
|
|
81
83
|
attach: 'beta', wf: 'beta', nexus: 'stable', phone: 'beta', token: 'beta',
|
|
@@ -98,6 +100,7 @@ export const FEATURE_MATURITY = {
|
|
|
98
100
|
'run-skill': 'stable',
|
|
99
101
|
ad: 'beta',
|
|
100
102
|
design: 'beta',
|
|
103
|
+
decide: 'beta',
|
|
101
104
|
provider: 'stable',
|
|
102
105
|
reasoning: 'stable',
|
|
103
106
|
local: 'tool',
|
|
@@ -157,7 +160,7 @@ export const FEATURE_MATURITY = {
|
|
|
157
160
|
ping: 'stable', provider: 'stable', sessions: 'stable', fork: 'stable', resume: 'stable',
|
|
158
161
|
decisions: 'stable', work: 'stable', cancel: 'stable',
|
|
159
162
|
skills: 'beta', skill: 'beta', digest: 'beta', intake: 'beta', ad: 'beta',
|
|
160
|
-
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', project: 'beta', wish: 'beta',
|
|
163
|
+
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', release: 'beta', away: 'beta', project: 'beta', wish: 'beta',
|
|
161
164
|
brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool', local: 'tool',
|
|
162
165
|
harness: 'ops',
|
|
163
166
|
// Existing commands outside the CXO four-grade menu list are owner-only.
|
|
@@ -166,7 +169,7 @@ export const FEATURE_MATURITY = {
|
|
|
166
169
|
},
|
|
167
170
|
discordCommand: {
|
|
168
171
|
status: 'stable', sessions: 'stable', new: 'stable', fork: 'stable',
|
|
169
|
-
persona: 'beta', poll: 'beta', attach: 'beta', now: 'beta',
|
|
172
|
+
persona: 'beta', poll: 'beta', attach: 'beta', now: 'beta', loops: 'beta',
|
|
170
173
|
brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool',
|
|
171
174
|
relay: 'ops', showroom: 'ops', bots: 'ops', bot: 'ops', screen: 'ops', chart: 'ops',
|
|
172
175
|
routines: 'ops', botsay: 'ops', 'voice-join': 'ops', 'voice-leave': 'ops', 'voice-status': 'ops',
|
|
@@ -12,6 +12,7 @@ export function readTuiSlashAudience(raw: unknown = getUserConfig().raw?.tui): {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function slashMaturity(name: string): Maturity | undefined {
|
|
15
|
+
// Aliases inherit the canonical grade (including /dec from /decide).
|
|
15
16
|
const command = SLASH_COMMANDS.find((entry) => entry.name === name || entry.aliases?.includes(name));
|
|
16
17
|
if (!command) return undefined;
|
|
17
18
|
return FEATURE_MATURITY.tuiSlash[command.name as keyof typeof FEATURE_MATURITY.tuiSlash];
|
|
@@ -26,6 +26,7 @@ import type { LogSink } from './sink.js';
|
|
|
26
26
|
import { setInstanceName, resolveInstanceName } from '../../instance-identity.js';
|
|
27
27
|
import * as nestDepth from '../../agent/nest-depth.js';
|
|
28
28
|
import { debug } from '../../debug/log.js';
|
|
29
|
+
import { assertNotTestWritingOps } from '../../instance/test-write-guard.js';
|
|
29
30
|
|
|
30
31
|
/** 스토어 경로 — `ELANOUS_STATE_DIR` 존중(격리 테스트/`nexus run --test` 의
|
|
31
32
|
* 기존 knob 그대로), 기본 `~/.elanous/logs/logs.db`. lazy 함수 — env 를
|
|
@@ -209,6 +210,8 @@ export class LogStore {
|
|
|
209
210
|
this.db.run(`PRAGMA busy_timeout = ${LOG_STORE_READONLY_BUSY_TIMEOUT_MS}`);
|
|
210
211
|
return;
|
|
211
212
|
}
|
|
213
|
+
// TEST-PROD-LEAK: a test process never opens the ops logs.db for write, preload or not.
|
|
214
|
+
assertNotTestWritingOps(path, 'open logs.db for write');
|
|
212
215
|
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
|
|
213
216
|
const openedAt = Date.now();
|
|
214
217
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { debug } from '../../debug/log.js';
|
|
2
2
|
import { DecisionLedger, type DecisionEntry } from '../../decisions/decision-ledger.js';
|
|
3
|
+
import { isIrreversible } from '../../decisions/decision-cards.js';
|
|
3
4
|
import { jsonResponse } from './json-response.js';
|
|
4
5
|
|
|
5
6
|
export const DECISIONS_PATH = '/v1/decisions';
|
|
@@ -27,10 +28,19 @@ export async function handleDecisions(req: Request, deps: DecisionsRouteDeps = {
|
|
|
27
28
|
id: entry.id,
|
|
28
29
|
title: entry.title,
|
|
29
30
|
situation: entry.scqa.s.slice(0, 200),
|
|
30
|
-
options: entry.options.map(option => ({ id: option.key, label: option.label })),
|
|
31
|
+
options: entry.options.map(option => ({ id: option.key, label: option.label, consequence: option.consequence })),
|
|
31
32
|
recommendation: entry.recommendation,
|
|
32
33
|
raisedAt: entry.raisedAt,
|
|
33
34
|
dueAt: entry.dueAt,
|
|
35
|
+
scqa: entry.scqa,
|
|
36
|
+
category: entry.category,
|
|
37
|
+
irreversible: isIrreversible(entry),
|
|
38
|
+
raisedBy: { agent: entry.raisedBy.agent, ...(entry.raisedBy.track ? { track: entry.raisedBy.track } : {}) },
|
|
39
|
+
alternative: entry.alternative,
|
|
40
|
+
dissent: entry.dissent,
|
|
41
|
+
crossCheck: entry.crossCheck,
|
|
42
|
+
crossCheckSkipped: entry.crossCheckSkipped,
|
|
43
|
+
pendingQuestion: entry.pendingQuestion,
|
|
34
44
|
}));
|
|
35
45
|
debug.log('decisions.api', 'listed', { count: decisions.length });
|
|
36
46
|
return jsonResponse({ decisions });
|
|
@@ -413,6 +413,8 @@ import { handleSelfEvent } from './self-event.js';
|
|
|
413
413
|
/** 매매 «실행»은 애드온이다(대표 09-20 결정 ③ · 별도 상용 저장소 · release/trading-export.yaml) — 공개 코어엔 없다.
|
|
414
414
|
* 있으면 그 경로를 켜고, 없으면 501. 경로를 변수로 둬 공개본 타입 검사가 «모듈 없음»으로 막히지 않게 한다. */
|
|
415
415
|
const TRADE_PROPOSAL_MODULE: string = './trade-proposal.js';
|
|
416
|
+
/** PITCH-ROOM — 투자자 피치 방(덱·티저)은 비공개다(release/public-export.yaml 이 pitch-api 를 뺀다). 공개본엔 모듈이 없어 404. */
|
|
417
|
+
const PITCH_MODULE: string = './pitch-api.js';
|
|
416
418
|
import {
|
|
417
419
|
handleShowroomLayoutDelete,
|
|
418
420
|
handleShowroomLayoutGet,
|
|
@@ -926,6 +928,16 @@ export async function routeRequest(
|
|
|
926
928
|
if (pathname === '/v1/ops' || pathname.startsWith('/v1/ops/')) {
|
|
927
929
|
return handleOpsApi(req, opts.metaApi);
|
|
928
930
|
}
|
|
931
|
+
// PITCH-ROOM — same operatorSignal gate, inside the (private-only) pitch module.
|
|
932
|
+
if (pathname.startsWith('/v1/pitch/')) {
|
|
933
|
+
let pitch: { handlePitchApi(req: Request, metaApi: typeof opts.metaApi): Response };
|
|
934
|
+
try {
|
|
935
|
+
pitch = await import(PITCH_MODULE) as typeof pitch;
|
|
936
|
+
} catch {
|
|
937
|
+
return jsonResponse({ error: 'not-found' }, 404);
|
|
938
|
+
}
|
|
939
|
+
return pitch.handlePitchApi(req, opts.metaApi);
|
|
940
|
+
}
|
|
929
941
|
|
|
930
942
|
// Default-deny for every `/v1/` path that is not on PUBLIC_ROUTES.
|
|
931
943
|
// Reuses checkAuth (same-origin exemption + constant-time bearer).
|
|
@@ -1299,12 +1311,11 @@ export async function routeRequest(
|
|
|
1299
1311
|
return handleDevices(req, opts.devices);
|
|
1300
1312
|
}
|
|
1301
1313
|
|
|
1314
|
+
// Same owner auth as the neighbouring GETs (short-lived owner tokens · same-origin PWA); #23932 compared the admin
|
|
1315
|
+
// bearer only, so a PWA on a short-lived token got 401 and the loop map stayed empty.
|
|
1302
1316
|
if (pathname === '/v1/loops/edges' && method === 'GET') {
|
|
1303
|
-
if (!opts.metaApi
|
|
1304
|
-
|
|
1305
|
-
if (!offered?.startsWith('Bearer ') || !compareTokenConstTime(offered.slice('Bearer '.length).trim(), opts.metaApi.bearerToken)) {
|
|
1306
|
-
return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1307
|
-
}
|
|
1317
|
+
if (!opts.metaApi) return jsonResponse({ error: 'meta-api-runtime-not-wired' }, 503);
|
|
1318
|
+
if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1308
1319
|
return handleLoopEdgesGet(req);
|
|
1309
1320
|
}
|
|
1310
1321
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
4
|
+
import { listSeatRequests, type SeatRequestRow } from '../../seat-dispatch/seat-request-ledger.js';
|
|
3
5
|
import { listCoordEvents } from '../../context-bus/coord-events.js';
|
|
4
6
|
import { CardStore, cardIndexPath, type TaskCard } from '../../task-cards/card-store.js';
|
|
5
7
|
import { loadRunLedger, resolveFederatedRunLedgerDirectories, type RunLedgerEntry } from '../../self-implement/run-ledger.js';
|
|
@@ -7,23 +9,78 @@ import { listLoops } from '../../loops/registry.js';
|
|
|
7
9
|
import { listSubSeatCharters } from './sub-seat-charters.js';
|
|
8
10
|
import { SEATS } from './ops-seats.js';
|
|
9
11
|
import { jsonResponse } from './json-response.js';
|
|
12
|
+
import { readTaskAgentState, type TaskCard as TaskAgentCard } from '../../task-agent/task-hand.js';
|
|
13
|
+
import { LogStore, logsDbPath } from '../../mss/logging/log-store.js';
|
|
14
|
+
|
|
15
|
+
/** `move` 칸에 실을 수 있는 값(allowlist) — 판단부 수 종류만, 문면은 싣지 않는다. */
|
|
16
|
+
export const LOOP_EDGE_MOVES = ['green-proposal'] as const;
|
|
10
17
|
|
|
11
18
|
export interface LoopEdge {
|
|
12
19
|
at: string;
|
|
13
|
-
kind: 'request' | 'decision' | 'report' | 'card' | 'run';
|
|
20
|
+
kind: 'request' | 'decision' | 'report' | 'card' | 'run' | 'hand' | 'launch' | 'move';
|
|
14
21
|
from: string;
|
|
15
22
|
to: string;
|
|
16
23
|
ref: string;
|
|
24
|
+
/** 체크리스트 칸 id(ID 정규식) — hand 간선만. */
|
|
25
|
+
cell?: string;
|
|
26
|
+
/** LOOP_EDGE_MOVES 중 하나 — move 간선만. */
|
|
27
|
+
move?: typeof LOOP_EDGE_MOVES[number];
|
|
28
|
+
/** hand 간선만 — 원장 행 mode, 없으면 key 접두(`shadow:orch:` = shadow · `orch:` = live). 못 정하면 생략. */
|
|
29
|
+
mode?: 'shadow' | 'live';
|
|
30
|
+
/** hand 간선만 — 그 행을 쓴 오케스트레이터 틱 runId(ID 정규식). 없으면 생략. */
|
|
31
|
+
tick?: string;
|
|
32
|
+
/** live claimed 뒤 handed·released 없이 N분 지남 = 발사 실패(끊긴 넘김). ref = 오케스트레이터 카드 id. */
|
|
33
|
+
broken?: true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** 노드 접두 — 조율 루프는 레지스트리 행 `loop:orchestrator` 그대로 쓴다. `queue:` 는 대기열 대체 간선 자리(조각 A 밖). */
|
|
37
|
+
export const TASK_AGENT_NODE = 'agent:task-agent';
|
|
38
|
+
export const ORCHESTRATOR_NODE = 'loop:orchestrator';
|
|
39
|
+
|
|
40
|
+
/** 한 런 원장에서 투영한 사실 — 문면은 feature(연결 비교 전용 · 응답에 싣지 않는다)뿐. */
|
|
41
|
+
export interface RunFact {
|
|
42
|
+
runId: string;
|
|
43
|
+
startAt?: string;
|
|
44
|
+
seat?: string;
|
|
45
|
+
feature?: string;
|
|
46
|
+
goalId?: string;
|
|
47
|
+
mergedAt?: string;
|
|
48
|
+
prs: Array<{ at: string; number: number }>;
|
|
49
|
+
children: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** handed.jsonl 행 투영 — status 가 없으면 handed 로 읽는다(조각 A 주입 호환). claimed·released 는 끊긴 간선 판정에만 쓴다. */
|
|
53
|
+
export interface HandedRow {
|
|
54
|
+
at: string;
|
|
55
|
+
cardId?: string;
|
|
56
|
+
cell?: string;
|
|
57
|
+
status?: 'claimed' | 'handed' | 'released';
|
|
58
|
+
key?: string;
|
|
59
|
+
mode?: 'shadow' | 'live';
|
|
60
|
+
runId?: string;
|
|
17
61
|
}
|
|
62
|
+
export interface PodFinish { at: string; childRunId: string; prNumber: number }
|
|
18
63
|
|
|
19
64
|
export interface LoopEdgesDeps {
|
|
20
65
|
listCoord?: typeof listCoordEvents;
|
|
21
66
|
cardRoot?: string;
|
|
22
67
|
ledgerDir?: string;
|
|
23
68
|
listCards?: () => TaskCard[];
|
|
24
|
-
listRunStarts?: (since: string) => Array<{ runId: string; at: string; seat: string }>;
|
|
69
|
+
listRunStarts?: (since: string) => Array<{ runId: string; at: string; seat: string; mergedAt?: string }>;
|
|
70
|
+
listSeatRequests?: () => SeatRequestRow[];
|
|
71
|
+
listOrchestratorLaunches?: () => Array<{ key: string; runId: string }>;
|
|
25
72
|
listLoopOwners?: () => Array<{ id: string; owner?: string | null }>;
|
|
26
73
|
seatIds?: () => string[];
|
|
74
|
+
/** 기본 = `<root>/loop/orchestrator/handed.jsonl` · root = cardRoot ?? effectiveInstanceRoot(). */
|
|
75
|
+
listHanded?: () => HandedRow[];
|
|
76
|
+
/** live claimed 가 handed·released 없이 이만큼 지나면 끊긴 hand 간선(기본 10분). */
|
|
77
|
+
handBrokenAfterMs?: number;
|
|
78
|
+
/** 기본 = `<root>/task-agent-actions.json` 의 tasks(readTaskAgentState). */
|
|
79
|
+
listTaskCards?: () => TaskAgentCard[];
|
|
80
|
+
/** 기본 = ledgerDir(또는 연합 원장)에서 투영. listRunStarts 를 주입하고 이것을 안 주면 [] 다. */
|
|
81
|
+
listRunFacts?: () => RunFact[];
|
|
82
|
+
/** 대체 원천 — logs `self-implement.pod` `job-finished` 의 childRunId·prUrl(PR 번호만). */
|
|
83
|
+
listPodFinishes?: (since: string) => PodFinish[];
|
|
27
84
|
}
|
|
28
85
|
|
|
29
86
|
const ID = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
@@ -47,7 +104,9 @@ function inWindow(at: string, since: number, now: number): boolean {
|
|
|
47
104
|
return Number.isFinite(ms) && ms >= since && ms <= now;
|
|
48
105
|
}
|
|
49
106
|
|
|
50
|
-
|
|
107
|
+
type PlacedCell = { cardId: string; cellId: string; owner: string };
|
|
108
|
+
|
|
109
|
+
function cardEdges(card: TaskCard, since: number, now: number, seats: ReadonlySet<string>, placedCells: PlacedCell[]): LoopEdge[] {
|
|
51
110
|
if (!card.goalId.startsWith('wish:') || !ID.test(card.id)) return [];
|
|
52
111
|
const node = `card:${card.id}`;
|
|
53
112
|
const ref = card.id;
|
|
@@ -75,6 +134,7 @@ function cardEdges(card: TaskCard, since: number, now: number, seats: ReadonlySe
|
|
|
75
134
|
if (!owner || !placement || placement.id !== cellId || !ID.test(cellId)
|
|
76
135
|
|| typeof placement.version !== 'string' || !/^\d+\.\d+\.\d+$/.test(placement.version)) continue;
|
|
77
136
|
placedOwners.add(owner);
|
|
137
|
+
placedCells.push({ cardId: card.id, cellId, owner });
|
|
78
138
|
if (inWindow(section.createdAt, since, now)) {
|
|
79
139
|
const cellNode = `loop:${cellId}`;
|
|
80
140
|
edges.push({ at: section.createdAt, kind: 'card', from: node, to: cellNode, ref });
|
|
@@ -93,9 +153,9 @@ function cardEdges(card: TaskCard, since: number, now: number, seats: ReadonlySe
|
|
|
93
153
|
return edges;
|
|
94
154
|
}
|
|
95
155
|
|
|
96
|
-
function
|
|
156
|
+
function runFacts(dir: string, owners: ReadonlyMap<string, string>, seats: ReadonlySet<string>): RunFact[] {
|
|
97
157
|
if (!existsSync(dir)) return [];
|
|
98
|
-
const result:
|
|
158
|
+
const result: RunFact[] = [];
|
|
99
159
|
let names: string[];
|
|
100
160
|
try { names = readdirSync(dir); } catch { return []; }
|
|
101
161
|
for (const name of names) {
|
|
@@ -106,28 +166,269 @@ function runStarts(since: string, dir: string, owners: ReadonlyMap<string, strin
|
|
|
106
166
|
const runId = name.slice(0, -6);
|
|
107
167
|
let entries: RunLedgerEntry[] | null;
|
|
108
168
|
try { entries = loadRunLedger(runId, dir); } catch { continue; }
|
|
109
|
-
|
|
169
|
+
if (!entries) continue;
|
|
170
|
+
const start = entries.find(entry => entry.event === 'start' && entry.runId === runId);
|
|
110
171
|
const feature = typeof start?.data.feature === 'string' ? start.data.feature : '';
|
|
111
172
|
const launchSeat = start?.data.launchSeat ?? start?.data.seat ?? owners.get(feature)
|
|
112
173
|
?? start?.data.originAgent ?? start?.data.controller;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
174
|
+
const merged = entries.find(entry => entry.event === 'merged' && entry.data.merged === true && typeof entry.timestamp === 'string');
|
|
175
|
+
const goalId = [start, ...entries].map(entry => entry?.goalId ?? (typeof entry?.data.goalId === 'string' ? entry.data.goalId : undefined))
|
|
176
|
+
.find((value): value is string => typeof value === 'string' && /^[a-f0-9]{16}$/.test(value));
|
|
177
|
+
// pr-opened data = {url, number, branch, …} (TC 실측 10-07) — 번호는 data.number(양의 정수)만 읽는다. 못 읽으면 간선 0.
|
|
178
|
+
const prs = entries.filter(entry => entry.event === 'pr-opened' && typeof entry.timestamp === 'string'
|
|
179
|
+
&& Number.isSafeInteger(entry.data.number) && (entry.data.number as number) > 0)
|
|
180
|
+
.map(entry => ({ at: entry.timestamp!, number: entry.data.number as number }));
|
|
181
|
+
// `pod-ledger-incomplete` 첫 줄 원장은 시작이 없어도 버리지 않는다 — pr 근거로만 쓰고 run 단계는 만들지 않는다.
|
|
182
|
+
const children = [...new Set(entries.filter(entry => entry.event === 'pod-child-run' && typeof entry.data.childRunId === 'string'
|
|
183
|
+
&& ID.test(entry.data.childRunId) && entry.data.childRunId !== runId).map(entry => entry.data.childRunId as string))];
|
|
184
|
+
result.push({
|
|
185
|
+
runId, prs, children,
|
|
186
|
+
...(start?.timestamp ? { startAt: start.timestamp } : {}),
|
|
187
|
+
...(start?.timestamp && seat(launchSeat, seats) ? { seat: launchSeat } : {}),
|
|
188
|
+
...(start?.timestamp && feature ? { feature } : {}),
|
|
189
|
+
...(goalId ? { goalId } : {}),
|
|
190
|
+
...(merged?.timestamp ? { mergedAt: merged.timestamp } : {}),
|
|
191
|
+
});
|
|
116
192
|
}
|
|
117
193
|
return result;
|
|
118
194
|
}
|
|
119
195
|
|
|
120
|
-
|
|
196
|
+
function runStartsFrom(facts: readonly RunFact[], since: string): Array<{ runId: string; at: string; seat: string; mergedAt?: string }> {
|
|
197
|
+
return facts.filter(fact => fact.startAt !== undefined && fact.seat !== undefined
|
|
198
|
+
&& (fact.startAt >= since || (fact.mergedAt !== undefined && fact.mergedAt >= since)))
|
|
199
|
+
.map(fact => ({ runId: fact.runId, at: fact.startAt!, seat: fact.seat!, ...(fact.mergedAt ? { mergedAt: fact.mergedAt } : {}) }));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** handed.jsonl(`tick.ts` ORCH-TA-HAND) — 행의 at·status·key·cardId·cell·mode·runId 만. 넘김은 handed 뿐이고 claimed·released 는 끊긴 간선 판정용이다. */
|
|
203
|
+
function handedRows(root: string): HandedRow[] {
|
|
204
|
+
const path = join(root, 'loop', 'orchestrator', 'handed.jsonl');
|
|
205
|
+
try { if (!existsSync(path) || statSync(path).size > 5 * 1024 * 1024) return []; } catch { return []; }
|
|
206
|
+
let raw: string;
|
|
207
|
+
try { raw = readFileSync(path, 'utf8'); } catch { return []; }
|
|
208
|
+
const rows: HandedRow[] = [];
|
|
209
|
+
for (const line of raw.split('\n')) {
|
|
210
|
+
const row = line.trim() ? parse(line) : null;
|
|
211
|
+
const status = row?.status;
|
|
212
|
+
if ((status !== 'handed' && status !== 'claimed' && status !== 'released') || typeof row!.at !== 'string') continue;
|
|
213
|
+
if (status === 'handed' && typeof row!.cardId !== 'string') continue;
|
|
214
|
+
rows.push({
|
|
215
|
+
at: row!.at as string, status,
|
|
216
|
+
...(typeof row!.cardId === 'string' ? { cardId: row!.cardId } : {}),
|
|
217
|
+
...(typeof row!.cell === 'string' ? { cell: row!.cell } : {}),
|
|
218
|
+
...(typeof row!.key === 'string' ? { key: row!.key } : {}),
|
|
219
|
+
...(row!.mode === 'shadow' || row!.mode === 'live' ? { mode: row!.mode } : {}),
|
|
220
|
+
...(typeof row!.runId === 'string' ? { runId: row!.runId } : {}),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return rows;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function taskCards(root: string): TaskAgentCard[] {
|
|
227
|
+
const tasks = object(readTaskAgentState<{ tasks?: unknown }>(join(root, 'task-agent-actions.json')).tasks);
|
|
228
|
+
return tasks ? Object.values(tasks).filter((card): card is TaskAgentCard => object(card) !== null) : [];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** 대체 원천: Pod 런은 도는 동안 원장이 Pod 안에만 있다 — 호스트 logs 의 job-finished 에서 childRunId·PR 번호만 읽는다. */
|
|
232
|
+
function podFinishes(since: string): PodFinish[] {
|
|
233
|
+
const path = logsDbPath();
|
|
234
|
+
if (!existsSync(path)) return [];
|
|
235
|
+
const store = LogStore.openReadOnly(path);
|
|
236
|
+
try {
|
|
237
|
+
const result: PodFinish[] = [];
|
|
238
|
+
for (const row of store.queryAll({ exactCategories: ['self-implement.pod'], events: ['job-finished'], sinceMs: Date.parse(since) })) {
|
|
239
|
+
const data = row.data ? parse(row.data) : null;
|
|
240
|
+
const pr = typeof data?.prUrl === 'string' ? /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/(\d+)$/.exec(data.prUrl) : null;
|
|
241
|
+
if (typeof data?.childRunId === 'string' && pr) result.push({ at: row.ts, childRunId: data.childRunId, prNumber: Number(pr[1]) });
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
} finally { store.close(); }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Project only the key and runId of orchestrator launches, never their card snapshots or prose. */
|
|
248
|
+
function orchestratorLaunches(root: string): Array<{ key: string; runId: string }> {
|
|
249
|
+
const dir = join(root, 'loop', 'orchestrator');
|
|
250
|
+
if (!existsSync(dir)) return [];
|
|
251
|
+
const launches: Array<{ key: string; runId: string }> = [];
|
|
252
|
+
let names: string[];
|
|
253
|
+
try { names = readdirSync(dir); } catch { return []; }
|
|
254
|
+
for (const name of names) {
|
|
255
|
+
if (!/^[a-zA-Z0-9._-]+\.json$/.test(name)) continue;
|
|
256
|
+
const path = join(dir, name);
|
|
257
|
+
try {
|
|
258
|
+
if (!statSync(path).isFile() || statSync(path).size > 5 * 1024 * 1024) continue;
|
|
259
|
+
const state = parse(readFileSync(path, 'utf8'));
|
|
260
|
+
if (state?.mode !== 'live' || !Array.isArray(state.launched)) continue;
|
|
261
|
+
for (const item of state.launched) {
|
|
262
|
+
const launch = object(item);
|
|
263
|
+
if (typeof launch?.key === 'string' && typeof launch.runId === 'string') {
|
|
264
|
+
launches.push({ key: launch.key, runId: launch.runId });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch { /* An unreadable tick cannot establish a launch. */ }
|
|
268
|
+
}
|
|
269
|
+
return launches;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
type JourneyLink = { from: string; to: string; both: boolean };
|
|
273
|
+
type Journey = { links: JourneyLink[]; pieces: Map<string, string[]> };
|
|
274
|
+
|
|
275
|
+
/** 런 원장 start.feature 는 `boundReadableText(feature, 120)` 로 잘린다(orchestrator.ts RUN_START_FEATURE_MAX_CHARS) —
|
|
276
|
+
* 꼬리 표식 ` [truncated; originalChars=N]`·`[truncated]`·`…`(⊕ 손으로 쓴 `...`)을 떼고 공백을 정규화한다. */
|
|
277
|
+
const FEATURE_TRUNCATION_TAIL = /(?:\s*\[truncated(?:; originalChars=\d+)?\]|…|\.\.\.)$/;
|
|
278
|
+
const FEATURE_PREFIX_MIN = 40;
|
|
279
|
+
function normalizeText(text: string): string { return text.replace(/\s+/g, ' ').trim(); }
|
|
280
|
+
/** 잘린 feature 의 비교 접두 — 표식이 없으면 undefined(완전 일치만). 끝의 짝 잃은 서로게이트도 뗀다(UTF-16 절단). */
|
|
281
|
+
function featurePrefix(feature: string): string | undefined {
|
|
282
|
+
if (!FEATURE_TRUNCATION_TAIL.test(feature)) return undefined;
|
|
283
|
+
const prefix = normalizeText(feature.replace(FEATURE_TRUNCATION_TAIL, '').replace(/[\ud800-\udbff]$/, ''));
|
|
284
|
+
return prefix.length >= FEATURE_PREFIX_MIN ? prefix : undefined;
|
|
285
|
+
}
|
|
286
|
+
/** 카드 text ↔ 런 feature — 완전 일치, 또는 잘린 feature(≥40자)가 카드 text(공백 정규화)의 접두. */
|
|
287
|
+
function featureMatches(feature: string | undefined, text: string): boolean {
|
|
288
|
+
if (!feature || !text) return false;
|
|
289
|
+
if (feature === text) return true;
|
|
290
|
+
const prefix = featurePrefix(feature);
|
|
291
|
+
return prefix !== undefined && normalizeText(text).startsWith(prefix);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** 카드 → 런 연결: 카드 runId(있으면 최우선) → goalId 일치 → feature 일치(완전 또는 잘린 접두) ∧ start ≥ launch.at — 뒤 둘은 «유일할 때만»
|
|
295
|
+
* (같은 feature 에 닿는 발사 카드가 둘이면 잇지 않는다). 자식 런(pod-child-run)은 부모로 닿는다. */
|
|
296
|
+
function linkedRun(card: TaskAgentCard, launchAt: string | undefined, launchedTexts: readonly string[], facts: readonly RunFact[], childIds: ReadonlySet<string>): string | undefined {
|
|
297
|
+
const explicitRun = (card as { runId?: unknown }).runId;
|
|
298
|
+
if (typeof explicitRun === 'string' && ID.test(explicitRun)) return explicitRun;
|
|
299
|
+
const pick = (candidates: RunFact[]): string | undefined => {
|
|
300
|
+
const top = candidates.filter(fact => !childIds.has(fact.runId));
|
|
301
|
+
return top.length === 1 ? top[0]!.runId : candidates.length === 1 ? candidates[0]!.runId : undefined;
|
|
302
|
+
};
|
|
303
|
+
if (typeof card.goalId === 'string' && card.goalId) {
|
|
304
|
+
const byGoal = pick(facts.filter(fact => fact.goalId === card.goalId));
|
|
305
|
+
if (byGoal) return byGoal;
|
|
306
|
+
}
|
|
307
|
+
const launchMs = launchAt === undefined ? NaN : Date.parse(launchAt);
|
|
308
|
+
if (!Number.isFinite(launchMs) || typeof card.text !== 'string' || !card.text) return undefined;
|
|
309
|
+
const runId = pick(facts.filter(fact => featureMatches(fact.feature, card.text) && fact.startAt !== undefined && Date.parse(fact.startAt) >= launchMs));
|
|
310
|
+
if (!runId) return undefined;
|
|
311
|
+
const feature = facts.find(fact => fact.runId === runId)!.feature;
|
|
312
|
+
return launchedTexts.filter(text => featureMatches(feature, text)).length === 1 ? runId : undefined;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** hand 행의 mode — 행의 mode 필드가 우선, 없으면 key 접두(`shadow:orch:` = shadow · `orch:` = live). */
|
|
316
|
+
function handMode(row: HandedRow): 'shadow' | 'live' | undefined {
|
|
317
|
+
if (row.mode === 'shadow' || row.mode === 'live') return row.mode;
|
|
318
|
+
if (typeof row.key !== 'string') return undefined;
|
|
319
|
+
if (row.key.startsWith('shadow:orch:')) return 'shadow';
|
|
320
|
+
if (row.key.startsWith('orch:')) return 'live';
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** 넘김(hand)·발사(launch)·런→PR·green 제안(move) 간선을 더하고, ?ref= 여정에 쓸 id 연결을 돌려준다. 문면은 싣지 않는다. */
|
|
325
|
+
function taskAgentEdges(deps: LoopEdgesDeps, facts: readonly RunFact[], seats: ReadonlySet<string>, since: number, now: number, sinceIso: string, edges: LoopEdge[]): Journey {
|
|
326
|
+
// 주입된 카드 목록(시험)에 뿌리가 없으면 운영 뿌리를 읽지 않는다 — 공개본엔 bunfig preload 격리가 없다.
|
|
327
|
+
const root = deps.cardRoot ?? (deps.listCards ? undefined : effectiveInstanceRoot());
|
|
328
|
+
const journey: Journey = { links: [], pieces: new Map() };
|
|
329
|
+
let handed: HandedRow[] = [];
|
|
330
|
+
try { handed = (deps.listHanded ?? (() => root === undefined ? [] : handedRows(root)))(); } catch { /* An unreadable hand ledger hands nothing. */ }
|
|
331
|
+
const settled = new Set<string>();
|
|
332
|
+
for (const row of handed) if (row.key !== undefined && (row.status ?? 'handed') !== 'claimed') settled.add(row.key);
|
|
333
|
+
const brokenAfter = deps.handBrokenAfterMs ?? 10 * 60 * 1000;
|
|
334
|
+
const brokenKeys = new Set<string>();
|
|
335
|
+
for (const row of handed) {
|
|
336
|
+
const status = row.status ?? 'handed';
|
|
337
|
+
const mode = handMode(row);
|
|
338
|
+
const tick = typeof row.runId === 'string' && ID.test(row.runId) ? { tick: row.runId } : {};
|
|
339
|
+
if (status === 'handed') {
|
|
340
|
+
if (typeof row.cardId !== 'string' || !ID.test(row.cardId) || !inWindow(row.at, since, now)) continue;
|
|
341
|
+
edges.push({ at: row.at, kind: 'hand', from: ORCHESTRATOR_NODE, to: TASK_AGENT_NODE, ref: row.cardId,
|
|
342
|
+
...(row.cell && ID.test(row.cell) ? { cell: row.cell } : {}), ...(mode ? { mode } : {}), ...tick });
|
|
343
|
+
// 넘김 행의 cardId 는 TASK-AGENT 카드다 — 소원(wish) 카드는 key(`orch:<wish>:<칸>`)에만 있다. 둘을 잇지 않으면
|
|
344
|
+
// `?ref=<wish>` 여정이 넘김에서 끊겨 데모 띠가 1단계 뒤로 «남음»에 멈춘다(10-07 리허설 2 실측 · JOURNEY-HAND-LINK).
|
|
345
|
+
const wish = row.key === undefined ? undefined : /^orch:([^:]+):([^:]+)$/.exec(row.key)?.[1];
|
|
346
|
+
if (wish !== undefined && ID.test(wish) && wish !== row.cardId) journey.links.push({ from: wish, to: row.cardId, both: true });
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
// live claimed 만(handed·released 없음 · N분 지남) = 발사 실패 — 끊긴 간선 하나. shadow claimed→released 는 간선 0.
|
|
350
|
+
if (status !== 'claimed' || mode !== 'live' || row.key === undefined || settled.has(row.key) || brokenKeys.has(row.key)) continue;
|
|
351
|
+
const claimedMs = Date.parse(row.at);
|
|
352
|
+
if (!Number.isFinite(claimedMs) || now - claimedMs < brokenAfter || !inWindow(row.at, since, now)) continue;
|
|
353
|
+
const parts = /^orch:([^:]+):([^:]+)$/.exec(row.key);
|
|
354
|
+
if (!parts || !ID.test(parts[1]!) || !ID.test(parts[2]!)) continue;
|
|
355
|
+
brokenKeys.add(row.key);
|
|
356
|
+
edges.push({ at: row.at, kind: 'hand', from: ORCHESTRATOR_NODE, to: TASK_AGENT_NODE, ref: parts[1]!, cell: parts[2]!, mode, ...tick, broken: true });
|
|
357
|
+
}
|
|
358
|
+
let cards: TaskAgentCard[] = [];
|
|
359
|
+
try { cards = (deps.listTaskCards ?? (() => root === undefined ? [] : taskCards(root)))().filter(card => typeof card?.id === 'string' && ID.test(card.id)); }
|
|
360
|
+
catch { /* An unreadable task-agent state launches nothing. */ }
|
|
361
|
+
const launchesOf = (card: TaskAgentCard) => (Array.isArray(card.history) ? card.history : [])
|
|
362
|
+
.filter(event => event?.event === 'launch' && typeof event.at === 'string');
|
|
363
|
+
const launchedTexts: string[] = [];
|
|
364
|
+
for (const card of cards) {
|
|
365
|
+
if (launchesOf(card).length && typeof card.text === 'string') launchedTexts.push(card.text);
|
|
366
|
+
}
|
|
367
|
+
const childIds = new Set(facts.flatMap(fact => fact.children));
|
|
368
|
+
for (const card of cards) {
|
|
369
|
+
if (typeof card.mission === 'string' && ID.test(card.mission)) journey.links.push({ from: card.id, to: card.mission, both: false });
|
|
370
|
+
if (Array.isArray(card.pieces)) journey.pieces.set(card.id, card.pieces.filter(piece => typeof piece === 'string' && ID.test(piece)));
|
|
371
|
+
const launches = launchesOf(card);
|
|
372
|
+
for (const launch of launches) {
|
|
373
|
+
if (seat(card.seat, seats) && inWindow(launch.at, since, now)) edges.push({ at: launch.at, kind: 'launch', from: TASK_AGENT_NODE, to: card.seat, ref: card.id });
|
|
374
|
+
}
|
|
375
|
+
const runId = linkedRun(card, launches[0]?.at, launchedTexts, facts, childIds);
|
|
376
|
+
if (runId) journey.links.push({ from: card.id, to: runId, both: true });
|
|
377
|
+
const green = object(card.greenProposal);
|
|
378
|
+
if (green && typeof green.at === 'string' && typeof green.checklistId === 'string' && ID.test(green.checklistId) && inWindow(green.at, since, now)) {
|
|
379
|
+
edges.push({ at: green.at, kind: 'move', from: TASK_AGENT_NODE, to: `release:${green.checklistId}`, ref: card.id, move: 'green-proposal' });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const prSeen = new Set<string>();
|
|
383
|
+
const prEdge = (runId: string, at: string, number: number) => {
|
|
384
|
+
const key = `${runId}:${number}`;
|
|
385
|
+
if (!ID.test(runId) || !Number.isSafeInteger(number) || number < 1 || prSeen.has(key) || !inWindow(at, since, now)) return;
|
|
386
|
+
prSeen.add(key);
|
|
387
|
+
edges.push({ at, kind: 'run', from: `loop:${runId}`, to: `pr:${number}`, ref: runId });
|
|
388
|
+
};
|
|
389
|
+
for (const fact of facts) {
|
|
390
|
+
for (const child of fact.children) journey.links.push({ from: fact.runId, to: child, both: true });
|
|
391
|
+
for (const pr of fact.prs) prEdge(fact.runId, pr.at, pr.number);
|
|
392
|
+
}
|
|
393
|
+
let pods: PodFinish[] = [];
|
|
394
|
+
// 대체 원천은 연합 원장(운영 기본)과 짝이다 — 원장을 주입했으면(시험·한 디렉터리) logs 를 읽지 않는다.
|
|
395
|
+
const injectedLedger = deps.ledgerDir !== undefined || deps.listRunStarts !== undefined || deps.listRunFacts !== undefined;
|
|
396
|
+
try { pods = (deps.listPodFinishes ?? (injectedLedger ? () => [] : podFinishes))(sinceIso); } catch { /* logs outage — the run ledger stays the primary source. */ }
|
|
397
|
+
for (const pod of pods) prEdge(pod.childRunId, pod.at, pod.prNumber);
|
|
398
|
+
return journey;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** ?ref= 의 여정 id 집합 — 카드↔런·런↔자식 런은 양방향, 조각→미션은 위로만(형제 조각은 미션을 ref 로 줄 때만). */
|
|
402
|
+
function journeyIds(ref: string, journey: Journey): Set<string> {
|
|
403
|
+
const ids = new Set([ref, ...(journey.pieces.get(ref) ?? [])]);
|
|
404
|
+
const queue = [...ids];
|
|
405
|
+
while (queue.length) {
|
|
406
|
+
const id = queue.shift()!;
|
|
407
|
+
for (const link of journey.links) {
|
|
408
|
+
const next = link.from === id ? link.to : link.both && link.to === id ? link.from : undefined;
|
|
409
|
+
if (next !== undefined && !ids.has(next)) { ids.add(next); queue.push(next); }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return ids;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** GET only; project an allowlist of fields from the ledgers. Never serialize ledger records. */
|
|
121
416
|
export function handleLoopEdgesGet(req: Request, deps: LoopEdgesDeps = {}): Response {
|
|
122
417
|
const params = new URL(req.url).searchParams;
|
|
123
418
|
const rawSince = params.get('since');
|
|
124
|
-
const
|
|
419
|
+
const journeyRef = params.get('ref');
|
|
420
|
+
// ?mode=live|shadow — hand 간선만 그 mode 로 거른다(다른 종류 간선은 그대로).
|
|
421
|
+
const handModeFilter = params.get('mode');
|
|
422
|
+
// ?ref= 는 한 카드·런의 여정 — 60분 창과 limit 에 잘리지 않게 since 를 안 주면 처음부터 본다.
|
|
423
|
+
const since = rawSince === null ? (journeyRef === null ? Date.now() - 60 * 60 * 1000 : 0) : Date.parse(rawSince);
|
|
125
424
|
const rawLimit = params.get('limit');
|
|
126
425
|
const limit = rawLimit === null ? 100 : Number(rawLimit);
|
|
127
426
|
const now = Date.now();
|
|
128
427
|
if (rawSince !== null && !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.test(rawSince)
|
|
129
|
-
|| !Number.isFinite(since) || since > now || !Number.isSafeInteger(limit) || limit < 1 || limit > 500
|
|
130
|
-
|
|
428
|
+
|| !Number.isFinite(since) || since > now || !Number.isSafeInteger(limit) || limit < 1 || limit > 500
|
|
429
|
+
|| (journeyRef !== null && !ID.test(journeyRef))
|
|
430
|
+
|| (handModeFilter !== null && handModeFilter !== 'live' && handModeFilter !== 'shadow')) {
|
|
431
|
+
return jsonResponse({ error: 'usage: GET /v1/loops/edges?since=<ISO>&limit=<1..500>&ref=<card|run|mission id>' }, 400);
|
|
131
432
|
}
|
|
132
433
|
const sinceIso = new Date(since).toISOString();
|
|
133
434
|
const seats = new Set((deps.seatIds ?? (() => [...SEATS.map(s => s.seat), ...listSubSeatCharters().map(s => s.id)]))());
|
|
@@ -143,17 +444,18 @@ export function handleLoopEdgesGet(req: Request, deps: LoopEdgesDeps = {}): Resp
|
|
|
143
444
|
if (seat(recipient, seats)) edges.push({ at: event.at, kind, from: event.refs.seat, to: recipient, ref });
|
|
144
445
|
}
|
|
145
446
|
}
|
|
447
|
+
const placedCells: PlacedCell[] = [];
|
|
146
448
|
if (deps.listCards) {
|
|
147
|
-
for (const card of deps.listCards()) edges.push(...cardEdges(card, since, now, seats));
|
|
449
|
+
for (const card of deps.listCards()) edges.push(...cardEdges(card, since, now, seats, placedCells));
|
|
148
450
|
} else {
|
|
149
451
|
const root = deps.cardRoot;
|
|
150
452
|
if (existsSync(cardIndexPath(root))) {
|
|
151
453
|
const store = new CardStore(root, true);
|
|
152
|
-
try { for (const card of store.listCards()) edges.push(...cardEdges(card, since, now, seats)); }
|
|
454
|
+
try { for (const card of store.listCards()) edges.push(...cardEdges(card, since, now, seats, placedCells)); }
|
|
153
455
|
finally { store.close(); }
|
|
154
456
|
}
|
|
155
457
|
}
|
|
156
|
-
const
|
|
458
|
+
const readFacts = deps.listRunFacts ?? (deps.listRunStarts ? () => [] : () => {
|
|
157
459
|
const owners = new Map<string, string>();
|
|
158
460
|
try {
|
|
159
461
|
for (const entry of (deps.listLoopOwners ?? (() => listLoops()))()) {
|
|
@@ -161,13 +463,40 @@ export function handleLoopEdgesGet(req: Request, deps: LoopEdgesDeps = {}): Resp
|
|
|
161
463
|
}
|
|
162
464
|
} catch { /* A registry outage does not erase explicit launch-seat evidence. */ }
|
|
163
465
|
return (deps.ledgerDir ? [deps.ledgerDir] : resolveFederatedRunLedgerDirectories({ includeTest: false }))
|
|
164
|
-
.flatMap(dir =>
|
|
466
|
+
.flatMap(dir => runFacts(dir, owners, seats));
|
|
165
467
|
});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
468
|
+
let facts: RunFact[] = [];
|
|
469
|
+
try { facts = readFacts().filter(fact => ID.test(fact.runId)); } catch { /* An unreadable ledger set links nothing. */ }
|
|
470
|
+
const readStarts = deps.listRunStarts ?? ((from: string) => runStartsFrom(facts, from));
|
|
471
|
+
const runs = readStarts(sinceIso).filter(run => seat(run.seat, seats) && ID.test(run.runId)
|
|
472
|
+
&& (inWindow(run.at, since, now) || (run.mergedAt !== undefined && inWindow(run.mergedAt, since, now))));
|
|
473
|
+
for (const run of runs) {
|
|
474
|
+
if (inWindow(run.at, since, now)) edges.push({ at: run.at, kind: 'run', from: run.seat, to: `loop:${run.runId}`, ref: run.runId });
|
|
475
|
+
}
|
|
476
|
+
if (placedCells.length && runs.length) {
|
|
477
|
+
const root = deps.cardRoot ?? effectiveInstanceRoot();
|
|
478
|
+
let requests: SeatRequestRow[] = [];
|
|
479
|
+
let launches: Array<{ key: string; runId: string }> = [];
|
|
480
|
+
try { requests = (deps.listSeatRequests ?? (() => listSeatRequests(root)))(); } catch { /* Missing request evidence is not a match. */ }
|
|
481
|
+
try { launches = (deps.listOrchestratorLaunches ?? (() => orchestratorLaunches(root)))(); } catch { /* Missing launch evidence is not a match. */ }
|
|
482
|
+
const confirmed = new Set(launches.filter(item => ID.test(item.runId) && runs.some(run => run.runId === item.runId))
|
|
483
|
+
.map(item => JSON.stringify([item.key, item.runId])));
|
|
484
|
+
for (const cell of placedCells) {
|
|
485
|
+
const key = `orch:${cell.cardId}:${cell.cellId}`;
|
|
486
|
+
if (!requests.some(row => row.key === key && row.cell === cell.cellId && row.source === 'orchestrator' && row.seat === cell.owner)) continue;
|
|
487
|
+
for (const run of runs) {
|
|
488
|
+
if (run.seat !== cell.owner || !confirmed.has(JSON.stringify([key, run.runId]))) continue;
|
|
489
|
+
if (inWindow(run.at, since, now)) edges.push({ at: run.at, kind: 'card', from: cell.owner, to: `loop:${run.runId}`, ref: cell.cardId });
|
|
490
|
+
if (run.mergedAt && inWindow(run.mergedAt, since, now)) {
|
|
491
|
+
edges.push({ at: run.mergedAt, kind: 'card', from: `loop:${run.runId}`, to: `landed:${cell.cellId}`, ref: cell.cardId });
|
|
492
|
+
}
|
|
493
|
+
}
|
|
169
494
|
}
|
|
170
495
|
}
|
|
171
|
-
|
|
172
|
-
|
|
496
|
+
const links = taskAgentEdges(deps, facts, seats, since, now, sinceIso, edges);
|
|
497
|
+
const journey = journeyRef === null ? null : journeyIds(journeyRef, links);
|
|
498
|
+
const out = edges.filter(edge => (!journey || journey.has(edge.ref))
|
|
499
|
+
&& (handModeFilter === null || edge.kind !== 'hand' || edge.mode === handModeFilter));
|
|
500
|
+
out.sort((a, b) => b.at.localeCompare(a.at) || a.kind.localeCompare(b.kind) || a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
|
|
501
|
+
return jsonResponse({ edges: out.slice(0, limit) });
|
|
173
502
|
}
|