elanous 0.2.6 → 0.2.7
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/.env.example +10 -7
- package/README.md +4 -0
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.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/{3995-bee512a7063036ce.js → 2375-cb6debea56cce4fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8d0d9faf-97887ff99713b84e.js → 2ea96958-e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5329.8b7e4bca684b6eec.js → 3403.4bc97b4066945a94.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7633.0654fe6ea259c1ee.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4836-2eba6079f31b88d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2984-977cfa339fcd65a5.js → 5080-1613752891fcadb4.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{763-7d0e1913b5074e98.js → 5791-bb82743fa6299562.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4240-7153881b0b377306.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
- 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/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-f6b5d8a5833a7603.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8894-e92491a164192cc1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9740-67051141115ce775.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.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-d5ebde04844689e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-10ed24f413ed66e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-211810b6c93ab66a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-624f7a2f89b60c8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5a5fb385a4df0ffe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-80497e93a878ddc5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-7457f52683562161.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-cee86394de407aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4ff8edab44196b8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-92e4f44428d5e290.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-b7dcf5d48a854b76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-51480757e7d68b92.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/intake/page-2a05dfe80c8bdf16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d9979633aa82f8e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-30533117beb53625.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7857826c2f97ad7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-07267f1391b96aba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-55d50e9b0006a91d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-9699b1786be6171d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-43dd772aee8af75f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a7e37af7d0399cb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-973ab7af59c718b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a9f8d3865b91e436.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-15e5a15db84c56aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a2e572e4511f2a16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-dd107bf1c7d52a90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d42b99cdfb9f27c6.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-2764ff40dad5a995.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-c5f1152db116ff96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-f933bca81ea38a6f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-c1c95739c0bff3b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-a7aaffcf771662d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-71d18eec33b9c667.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-56adbe1a1f1379bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-2c4e59079ee357ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a81f113f3ef89d72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d422f38c052967e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fc27b00e20733146.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-6870f30ea99f8346.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-37b8bf78a3c6eec5.js → webpack-e5bea36f183b25e6.js} +1 -1
- package/apps/pwa/out/_next/static/css/f537bdb19c69306a.css +1 -0
- package/apps/pwa/out/_next/static/keajW7DDyghEs1y8oJe6T/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -0
- package/apps/pwa/out/consult/index.txt +32 -0
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -0
- package/apps/pwa/out/exec/index.txt +32 -0
- package/apps/pwa/out/field/index.html +1 -0
- package/apps/pwa/out/field/index.txt +32 -0
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.cjs +107 -0
- package/graphs/field/field-feed.yaml +24 -0
- package/graphs/field/recipes.yaml +12 -0
- package/graphs/landing/landing-heal.yaml +3 -1
- package/graphs/landing/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +10 -0
- package/graphs/release/release-loop.yaml +10 -3
- package/package.json +5 -5
- package/plugins/card-followup/README.md +13 -4
- package/plugins/card-followup/graphs/card-followup.yaml +3 -1
- package/plugins/card-followup/graphs/recipes.yaml +5 -1
- package/plugins/card-followup/graphs/run-step.ts +122 -18
- package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
- package/plugins/card-followup/plugin.json +1 -1
- package/plugins/doc-draft/README.md +13 -0
- package/plugins/doc-draft/examples/brief-sample.json +8 -0
- package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
- package/plugins/doc-draft/graphs/recipes.yaml +9 -0
- package/plugins/doc-draft/graphs/run-step.ts +113 -0
- package/plugins/doc-draft/plugin.json +24 -0
- package/plugins/geo-check/README.md +3 -1
- package/plugins/geo-check/graphs/run-step.ts +24 -3
- package/plugins/geo-check/plugin.json +1 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
- package/plugins/job-coach/README.md +1 -1
- package/plugins/job-coach/graphs/run-step.ts +2 -2
- package/plugins/job-coach/plugin.json +1 -1
- package/plugins/job-coach/plugin.ts +1 -1
- package/scripts/affinity/endcard.sh +3 -2
- package/scripts/affinity/reframe.sh +3 -1
- package/scripts/ci-public-export-leak-gate.ts +48 -0
- package/scripts/codex-quota-alert.ts +122 -258
- package/scripts/coord-tracks.json +4 -4
- package/scripts/field-feed/deliver-node.ts +52 -0
- package/scripts/field-feed/feed-node.ts +67 -0
- package/scripts/field-feed/lib.ts +162 -0
- package/scripts/field-feed/reel-node.ts +24 -0
- package/scripts/field-feed/start.ts +62 -0
- package/scripts/fixtures/vflow-sample.ndjson +6 -0
- package/scripts/install.sh +92 -13
- package/scripts/landing-heal/clean-checkout.ts +114 -0
- package/scripts/landing-heal/run-mission.ts +38 -3
- package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
- package/scripts/public-export.ts +8 -4
- package/scripts/release-loop/baseline-from-logs.ts +92 -0
- package/scripts/release-loop/env-known-failures.ts +31 -0
- package/scripts/release-loop/gate-node.ts +81 -19
- package/scripts/release-loop/known-issues-node.ts +159 -0
- package/scripts/release-loop/npm-publish-node.ts +118 -0
- package/scripts/release-loop/ops-upgrade-node.ts +87 -0
- package/scripts/release-loop/release-readiness.ts +58 -0
- package/scripts/release-loop/unattended-release.ts +93 -0
- package/scripts/ux/measure-first-text.ts +102 -0
- package/scripts/vflow-collect.ts +20 -7
- package/scripts/vflow-search.ts +73 -0
- package/scripts/video-free-line-test-walker.ts +36 -0
- package/scripts/video-pipeline.ts +2 -2
- package/skills/explainer-video/SKILL.md +12 -9
- package/skills/explainer-video/engine/build.mjs +1 -1
- package/skills/explainer-video/engine/reel.mjs +138 -31
- package/skills/explainer-video/engine/reel.sh +2 -1
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
- package/src/a2a/server.ts +2 -0
- package/src/acp/elanous-aware-peers.ts +30 -0
- package/src/acp/server.ts +91 -15
- package/src/agent/monad-agent-turn.ts +25 -6
- package/src/agent/shared-app-tools.ts +5 -3
- package/src/agent-substrate/review-intent.ts +6 -2
- package/src/agent-substrate/shard-boundary-intent.ts +70 -0
- package/src/auth/github-app-token.ts +4 -1
- package/src/autopilot/track-agent-shadow.ts +151 -0
- package/src/autopilot/track-agent.ts +23 -3
- package/src/boot/acp-server-help.ts +16 -0
- package/src/boot/daemon-prompt-turn.ts +31 -10
- package/src/boot/daemon-tools/index.ts +7 -1
- package/src/budget/codex-credit-pace.ts +84 -0
- package/src/budget/codex-credit-plan.ts +154 -0
- package/src/budget/codex-reset-credit-state.ts +2 -0
- package/src/chat/chat-cli.ts +4 -1
- package/src/chat/short-question.ts +40 -0
- package/src/cli/agent-cli.ts +24 -15
- package/src/cli/ask-bare.ts +42 -0
- package/src/cli/cli-user-error.ts +25 -0
- package/src/cli/codex-credit-plan-view.ts +25 -0
- package/src/cli/dependence-gauge.ts +237 -0
- package/src/cli/doctor-cli.ts +5 -0
- package/src/cli/doctor-modes.ts +48 -0
- package/src/cli/intake-cli.ts +12 -2
- package/src/cli/intake-source-cli.ts +58 -0
- package/src/cli/machine-cli.ts +23 -0
- package/src/cli/msg-cli.ts +137 -0
- package/src/cli/plugin-cli.ts +25 -0
- package/src/cli/pr-cli.ts +36 -0
- package/src/cli/provider-cli.ts +5 -0
- package/src/cli/release-cli.ts +42 -1
- package/src/cli/release-run-if-ready.ts +48 -0
- package/src/cli/setup-cli.ts +27 -6
- package/src/cli/setup-recommend.ts +8 -0
- package/src/cli/usage-cli.ts +11 -1
- package/src/codex/setup.ts +27 -8
- package/src/dashboard/index.ts +1 -1
- package/src/discord.ts +56 -6
- package/src/domains/core-tools.ts +37 -14
- package/src/domains/outbound-alert.ts +50 -11
- package/src/exec-requests/planner.ts +140 -0
- package/src/exec-requests/runner.ts +338 -0
- package/src/exec-requests/store.ts +76 -0
- package/src/field/field-media.ts +8 -3
- package/src/field/field-reel.ts +196 -0
- package/src/grok/credential.ts +7 -5
- package/src/harness/aside-run-status.ts +33 -0
- package/src/harness/browser-verify.ts +48 -17
- package/src/harness/harness-cli-command.ts +25 -3
- package/src/harness/image-load-state.ts +21 -0
- package/src/harness/process-garbage.ts +45 -0
- package/src/index.ts +86 -14
- package/src/instance/leader.ts +13 -1
- package/src/intake-plane/collect-github.ts +5 -3
- package/src/intake-plane/collect-telegram-saved.ts +15 -3
- package/src/intake-plane/discord-seat-work.ts +56 -0
- package/src/intake-plane/intake-sources.ts +202 -0
- package/src/intake-plane/items.ts +28 -12
- package/src/intake-plane/seat-doc-route.ts +207 -0
- package/src/intake-plane/submit-intake-work.ts +9 -1
- package/src/intake-plane/telegram-seat-work.ts +72 -0
- package/src/llm/model-defaults.ts +24 -1
- package/src/llm.ts +5 -4
- package/src/machines/machine-ledger.ts +127 -0
- package/src/market/pack-internal-refs.ts +28 -0
- package/src/market/publish.ts +31 -4
- package/src/maturity/feature-maturity.ts +65 -0
- package/src/model-tier/turn-tier.ts +24 -0
- package/src/msg/msg-store.ts +149 -0
- package/src/native-tool-catalog.ts +2 -1
- package/src/nexus/api/chat-fast-path-config.ts +28 -0
- package/src/nexus/api/consult-requests.ts +117 -0
- package/src/nexus/api/discord-trigger-bot.ts +3 -0
- package/src/nexus/api/exec-requests.ts +51 -0
- package/src/nexus/api/field-uploads.ts +40 -3
- package/src/nexus/api/graph-approvals-feed.ts +140 -0
- package/src/nexus/api/graph-approvals.ts +71 -4
- package/src/nexus/api/http-server.ts +61 -7
- package/src/nexus/api/seat-requests.ts +254 -0
- package/src/nexus/api/trace-run-scan.ts +37 -0
- package/src/nexus/api/trace.ts +19 -3
- package/src/nexus/index.ts +3 -0
- package/src/nexus/setup-status.ts +4 -5
- package/src/notify/llm-budget-alert.ts +16 -10
- package/src/oauth/codex-account-rotation.ts +26 -4
- package/src/oauth/codex-account-store.ts +20 -2
- package/src/oauth/codex-browser-login.ts +173 -0
- package/src/oauth/codex.ts +4 -1
- package/src/oauth/fallback-chain.ts +5 -5
- package/src/oauth/store.ts +42 -5
- package/src/onboarding/continue-here.ts +29 -0
- package/src/onboarding/entry-hints.ts +8 -0
- package/src/onboarding/full-screen-io.ts +35 -3
- package/src/onboarding/help/skills.md +1 -1
- package/src/onboarding/non-interactive.ts +1 -0
- package/src/onboarding/subscription-hint.ts +27 -0
- package/src/onboarding/web-first.ts +83 -0
- package/src/onboarding.ts +230 -23
- package/src/plugins/maker/node-install.ts +92 -0
- package/src/plugins/maker/node-maker.ts +139 -0
- package/src/plugins/maker/plugin-maker.ts +19 -7
- package/src/policy/llm-policy.ts +268 -0
- package/src/prompt-library/session-guidance.ts +7 -1
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/relay/relay-cli.ts +20 -0
- package/src/relay/relay-server.ts +220 -0
- package/src/release-loop/checklist.ts +9 -8
- package/src/self-dev/decompose.ts +1 -0
- package/src/self-dev/dev-pipeline.ts +6 -3
- package/src/self-dev/run-record-mirror.ts +44 -0
- package/src/self-dev/run-store.ts +12 -3
- package/src/self-implement/harness-policy-gates.ts +4 -1
- package/src/self-implement/report-origin.ts +12 -1
- package/src/session/chat.ts +17 -5
- package/src/skills/shared-agent-skill-roots.ts +25 -0
- package/src/steward/failure-streak.ts +78 -0
- package/src/steward/steward-cards.ts +24 -0
- package/src/steward/triage-budget.ts +127 -0
- package/src/steward/triage-plan.ts +75 -0
- package/src/steward/triage.ts +244 -42
- package/src/task-cards/card-store.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
- package/src/telegram.ts +38 -2
- package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
- package/src/user-config.ts +80 -4
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/character.ts +42 -5
- package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
- package/src/video-pipeline/recipes/free-line.ts +16 -3
- package/src/video-pipeline/recipes/remote.ts +7 -1
- package/src/video-pipeline/vflow-reference.ts +109 -0
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
- package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → keajW7DDyghEs1y8oJe6T}/_ssgManifest.js +0 -0
|
@@ -50,7 +50,7 @@ import { findGitDir } from '../../git-fs/locate.js';
|
|
|
50
50
|
import { debug } from '../../debug/log.js';
|
|
51
51
|
import { selectLiveDetail } from '../../live/detail-switch.js';
|
|
52
52
|
import { authStorePath } from '../../oauth/store.js';
|
|
53
|
-
import { grokAuthFilePath, resolveGrokCredential } from '../../grok/credential.js';
|
|
53
|
+
import { grokAuthFilePath, isGrokSubscriptionExpiring, refreshGrokSubscriptionToken, resolveGrokCredential } from '../../grok/credential.js';
|
|
54
54
|
import { defaultGrokModel } from '../../grok/models.js';
|
|
55
55
|
import { resolveHostId } from '../../platform/host-id.js';
|
|
56
56
|
import { envLiteral } from '../../platform/env-literal.js';
|
|
@@ -171,12 +171,26 @@ export function hostCredentials(account: string, storePath: string = authStorePa
|
|
|
171
171
|
return { elanousAuth, codexAuth, ghToken: ghToken() };
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
/**
|
|
174
|
+
/** A Pod gets a refresh-less access copy, so the copy must outlive the Job: same rule as the Codex path (3 h). */
|
|
175
|
+
export const POD_GROK_MIN_VALIDITY_MS = 3 * 3600_000;
|
|
176
|
+
|
|
177
|
+
/** resolveGrokCredential 과 동일한 출처를 읽되 Pod 로는 refresh 없는 access 사본만 보낸다.
|
|
178
|
+
* POD4 (10-01): the copy used to ship whatever the host had — an AUTH1 shard copied a token that had expired
|
|
179
|
+
* 4 minutes earlier and died at `grok-credential-preflight status=expired`. Refresh on the host first. */
|
|
175
180
|
export function hostGrokCredentials(opts: {
|
|
176
181
|
home?: string; env?: NodeJS.ProcessEnv; apiKeyOptIn?: boolean; ghToken?: () => string;
|
|
182
|
+
/** Test seams — the host refresh (read-only `grok models`) and the expiry probe. */
|
|
183
|
+
refresh?: () => void; isExpiring?: () => boolean;
|
|
177
184
|
} = {}): { grokAuth?: string; grokApiKey?: string; ghToken: string } {
|
|
178
|
-
|
|
185
|
+
let credential = resolveGrokCredential({ home: opts.home, env: opts.env });
|
|
179
186
|
if (credential?.kind === 'subscription') {
|
|
187
|
+
const expiring = opts.isExpiring ?? (() => isGrokSubscriptionExpiring({ home: opts.home, bufferMs: POD_GROK_MIN_VALIDITY_MS }));
|
|
188
|
+
if (expiring()) {
|
|
189
|
+
try { (opts.refresh ?? (() => { refreshGrokSubscriptionToken({ home: opts.home }); }))(); } catch { /* judged below */ }
|
|
190
|
+
if (expiring()) throw new Error('grok: 구독 access 토큰이 3시간 안에 만료 — 호스트 갱신(grok models)도 못 늘렸다 · 호스트에서 grok login 후 다시');
|
|
191
|
+
credential = resolveGrokCredential({ home: opts.home, env: opts.env });
|
|
192
|
+
if (credential?.kind !== 'subscription') throw new Error('grok: 갱신 뒤 구독 자격을 다시 읽지 못했다');
|
|
193
|
+
}
|
|
180
194
|
const scopes = JSON.parse(readFileSync(grokAuthFilePath(opts.home), 'utf8')) as Record<string, unknown>;
|
|
181
195
|
const redacted: Record<string, { key: string; expires_at?: string; user_id?: string }> = {};
|
|
182
196
|
for (const [name, value] of Object.entries(scopes)) {
|
|
@@ -219,6 +233,64 @@ function githubRepositoryFromUrl(repoUrl: string): string | null {
|
|
|
219
233
|
|
|
220
234
|
/** Job 스크립트의 salvage 단계 — 자식 rc 가 0 이 아닐 때만, 변경·미푸시 커밋이 있는 격리 워크트리를 `salvage/<job>/<worktree>` 로 push.
|
|
221
235
|
* main·self-impl/ 로는 절대 안 민다. 실패해도 제어 흐름을 바꾸지 않는다(호출자는 이 뒤에 `exit $rc`). */
|
|
236
|
+
/**
|
|
237
|
+
* App installation tokens live one hour and only the host process renews them (at 10 minutes left).
|
|
238
|
+
* If that process dies (host reboot), nothing renews the Pod's token. hosts.yml unchanged for this long
|
|
239
|
+
* means the host is gone. Push a snapshot while the token still works.
|
|
240
|
+
*/
|
|
241
|
+
export const POD_GH_STALE_SECONDS = 53 * 60;
|
|
242
|
+
|
|
243
|
+
/** Snapshot every worktree to salvage/<job>/<wt>-early without touching the child's index, worktree or HEAD. */
|
|
244
|
+
export function podEarlySalvageScript(): string {
|
|
245
|
+
return [
|
|
246
|
+
'job_name="${ELANOUS_POD_NAME:-unknown-job}"',
|
|
247
|
+
'while IFS= read -r wt; do',
|
|
248
|
+
' [ -n "$wt" ] || continue',
|
|
249
|
+
' case "$wt" in /*) ;; *) continue ;; esac',
|
|
250
|
+
' wt_name=$(basename -- "$wt")',
|
|
251
|
+
" case \"$wt_name\" in ''|.*|*/*|*'..'*) continue ;; esac",
|
|
252
|
+
' branch="salvage/${job_name}/${wt_name}-early"',
|
|
253
|
+
' (',
|
|
254
|
+
' set +e',
|
|
255
|
+
' cd -- "$wt" || exit 0',
|
|
256
|
+
' head=$(git rev-parse --verify -q HEAD) || { printf \'ELANOUS_POD_SALVAGE_NONE early-no-head\\n\'; exit 0; }',
|
|
257
|
+
' idx=$(mktemp) || exit 0',
|
|
258
|
+
' cp -- "$(git rev-parse --git-path index)" "$idx" 2>/dev/null || :',
|
|
259
|
+
' GIT_INDEX_FILE="$idx" git add -A -- . >/dev/null 2>&1',
|
|
260
|
+
' tree=$(GIT_INDEX_FILE="$idx" git write-tree 2>/dev/null)',
|
|
261
|
+
' rm -f -- "$idx"',
|
|
262
|
+
' [ -n "$tree" ] || { printf \'ELANOUS_POD_SALVAGE_NONE early-write-tree-failed\\n\'; exit 0; }',
|
|
263
|
+
' base=$(git rev-parse --verify -q origin/HEAD || git rev-parse --verify -q origin/main || true)',
|
|
264
|
+
' if [ "$tree" = "$(git rev-parse "${head}^{tree}")" ]; then',
|
|
265
|
+
' if [ -z "$base" ] || [ "$(git rev-list --count "${base}..${head}" 2>/dev/null || echo 1)" = 0 ]; then printf \'ELANOUS_POD_SALVAGE_NONE clean\\n\'; exit 0; fi',
|
|
266
|
+
' commit=$head',
|
|
267
|
+
' else',
|
|
268
|
+
' commit=$(git commit-tree "$tree" -p "$head" -m "salvage(early): ${job_name} host token refresh stopped") || { printf \'ELANOUS_POD_SALVAGE_NONE commit-failed\\n\'; exit 0; }',
|
|
269
|
+
' fi',
|
|
270
|
+
' git push --force origin "${commit}:refs/heads/${branch}" || { printf \'ELANOUS_POD_SALVAGE_NONE push-failed\\n\'; exit 0; }',
|
|
271
|
+
" printf 'ELANOUS_POD_SALVAGE %s %s\\n' \"$branch\" \"$commit\"",
|
|
272
|
+
' )',
|
|
273
|
+
"done < <(git worktree list --porcelain 2>/dev/null | awk '/^worktree / { sub(/^worktree /, \"\"); print }')",
|
|
274
|
+
].join('\n');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Background watchdog: fires the early snapshot once when hosts.yml has not been rewritten for staleSeconds. */
|
|
278
|
+
export function podGithubWatchdogScript(staleSeconds: number, intervalSeconds = 30): string {
|
|
279
|
+
return [
|
|
280
|
+
'(',
|
|
281
|
+
` while sleep ${intervalSeconds}; do`,
|
|
282
|
+
' f="$GH_CONFIG_DIR/hosts.yml"',
|
|
283
|
+
' m=$(stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null) || continue',
|
|
284
|
+
' age=$(( $(date +%s) - m ))',
|
|
285
|
+
` [ "$age" -ge ${staleSeconds} ] || continue`,
|
|
286
|
+
" printf 'ELANOUS_POD_GH_STALE %s\\n' \"$age\"",
|
|
287
|
+
` ${podEarlySalvageScript().split('\n').join('\n ')}`,
|
|
288
|
+
' break',
|
|
289
|
+
' done',
|
|
290
|
+
') & gh_watch_pid=$!',
|
|
291
|
+
].join('\n');
|
|
292
|
+
}
|
|
293
|
+
|
|
222
294
|
export function podSalvageScript(): string {
|
|
223
295
|
return [
|
|
224
296
|
'if [ "${rc:-0}" -ne 0 ]; then',
|
|
@@ -368,7 +440,7 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
368
440
|
return { limit: a !== null && b !== null && a > b ? base : high, tier, source };
|
|
369
441
|
}
|
|
370
442
|
|
|
371
|
-
export function podJobManifest(o: { name: string; namespace: string; image: string; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; goalDoc?: string; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean }): Record<string, unknown> {
|
|
443
|
+
export function podJobManifest(o: { name: string; namespace: string; image: string; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; goalDoc?: string; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number }): Record<string, unknown> {
|
|
372
444
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
373
445
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
374
446
|
const askBaseIndex = o.goalDoc ? o.args.indexOf('--base') : -1;
|
|
@@ -394,6 +466,7 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
|
|
|
394
466
|
] : []),
|
|
395
467
|
// Only fixed command shapes leave the Pod; arbitrary argv (including shell -c text) is never emitted.
|
|
396
468
|
`(while :; do { mem=$(if [ -r /sys/fs/cgroup/memory.current ]; then cat /sys/fs/cgroup/memory.current; elif [ -r /sys/fs/cgroup/memory/memory.usage_in_bytes ]; then cat /sys/fs/cgroup/memory/memory.usage_in_bytes; else printf -- -; fi); top=$(ps -eo rss=,comm=,args= --sort=-rss | head -5 | awk 'function encode(s) { gsub(/%/, "%25", s); gsub(/:/, "%3A", s); gsub(/ /, "%20", s); gsub(/\\t/, "%09", s); return s } { rss=$1; name=$2; sub(/^[[:space:]]*[0-9]+[[:space:]]+[^[:space:]]+[[:space:]]*/, ""); n=split($0, a, /[[:space:]]+/); cmd="<redacted>"; if (name=="sleep" && a[2]=="30") cmd="sleep 30" (n>2 ? " <redacted>" : ""); else if (name=="bun") { cmd="bun <redacted>"; if (a[2]=="test") cmd="bun test <redacted>"; else if (a[2]=="run") cmd="bun run <redacted>"; else if ((a[2]=="x" || a[2]=="exec") && a[3]=="tsc") cmd="bun x tsc <redacted>" } else if (name=="tsc") cmd="tsc <redacted>"; else if (name=="elanous") { cmd="elanous <redacted>"; if (a[2]=="self") cmd="elanous self <redacted>"; else if (a[2]=="harness") cmd="elanous harness <redacted>" } else if (name=="node") cmd="node <redacted>"; if (name=="sleep" && cmd=="<redacted>") cmd="sleep <redacted>"; else if (cmd=="<redacted>" && name!="bash" && name!="sh") name="other"; printf " %s:%s:%s", rss, encode(name), encode(substr(cmd,1,120)) }'); printf "ELANOUS_MEM %s %s%s\\n" "$(date +%s)" "$mem" "$top"; } || true; sleep 15 || break; done) & mem_sampler_pid=$!`,
|
|
469
|
+
...(o.appCredential ? [podGithubWatchdogScript(o.githubStaleSeconds ?? POD_GH_STALE_SECONDS)] : []),
|
|
397
470
|
// 마지막 줄 JSON 이 «맨 끝»이어야 한다(parseSelfImplementJson) — rollup 은 그 앞에.
|
|
398
471
|
goalPath
|
|
399
472
|
? `elanous harness ask ${goalPath} --json${quotedAsk} > /tmp/si.out 2>&1; rc=$?; bun -e 'const fs=require("fs");const p="/tmp/si.out";const s=fs.readFileSync(p,"utf8");const line=s.trimEnd().split("\\n").at(-1);try{const o=JSON.parse(line);if(o.kind==="self"&&o.result&&typeof o.result==="object")fs.appendFileSync(p,"\\n"+JSON.stringify({...o.result,...(typeof o.ok==="boolean"?{ok:o.ok}:{})})+"\\n")}catch{}'`
|
|
@@ -454,6 +527,7 @@ for ledger in "\${ELANOUS_STATE_DIR:-$HOME/.elanous}"/run-ledger/*.jsonl; do
|
|
|
454
527
|
done
|
|
455
528
|
if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
456
529
|
'kill "$mem_sampler_pid" 2>/dev/null || true',
|
|
530
|
+
...(o.appCredential ? ['kill "$gh_watch_pid" 2>/dev/null || true'] : []),
|
|
457
531
|
'wait "$mem_sampler_pid" 2>/dev/null || true',
|
|
458
532
|
'tail -n 1 /tmp/si.out',
|
|
459
533
|
podSalvageScript(),
|
package/src/telegram.ts
CHANGED
|
@@ -66,9 +66,11 @@ import { observeDevRequestRouteFailSoft } from './skills/dev-request-router.js';
|
|
|
66
66
|
import { runUrlRoute } from './skills/url-route-exec.js';
|
|
67
67
|
import { recordRoutedLinkAbsorbed } from './intake-plane/link-ledger.js';
|
|
68
68
|
import { classifyFrontInput } from './intake-plane/front-classifier.js';
|
|
69
|
+
import { handleTelegramSeatWork, type TelegramSeatWorkDeps } from './intake-plane/telegram-seat-work.js';
|
|
69
70
|
import { effectiveInstanceRoot } from './instance/resolve.js';
|
|
70
71
|
import { getElanousConfigDir } from './elanous-config-dir.js';
|
|
71
72
|
import { defaultFieldEvent, FieldUploadError, parseFieldCaption, resolveFieldMime, saveFieldMedia } from './field/field-media.js';
|
|
73
|
+
import { scheduleFieldReel, type FieldReelOptions } from './field/field-reel.js';
|
|
72
74
|
|
|
73
75
|
export type TgAttachmentKind = 'photo' | 'voice' | 'audio' | 'document';
|
|
74
76
|
|
|
@@ -206,10 +208,14 @@ export interface TelegramBotOpts {
|
|
|
206
208
|
* independently of the LLM chat path. Best-effort: errors are
|
|
207
209
|
* swallowed by the caller — never blocks chat reply. */
|
|
208
210
|
onTriggerTap?: (event: TgTriggerEvent) => void | Promise<void>;
|
|
211
|
+
/** Test seam for addressed seat intake; defaults to the real intake door. */
|
|
212
|
+
seatWorkDeps?: TelegramSeatWorkDeps;
|
|
209
213
|
/** `#현장` 기본 행사 — 없으면 `slashContext.userConfig.telegram.fieldDefaultEvent` · 그것도 없으면 `field-<로컬 날짜>`. */
|
|
210
214
|
fieldDefaultEvent?: string;
|
|
211
215
|
/** 시험 seam — 현장 폴더 뿌리(기본 `getElanousConfigDir()`). */
|
|
212
216
|
fieldRootDir?: () => string;
|
|
217
|
+
/** 시험에서는 실제 렌더 대신 가짜 실행기를 주입한다. */
|
|
218
|
+
fieldReel?: FieldReelOptions;
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
/** Surface-unification v2 (FU-2) — Telegram trigger event passed to
|
|
@@ -326,6 +332,8 @@ export class TelegramBot {
|
|
|
326
332
|
private readonly globalWindow: number[] = [];
|
|
327
333
|
private readonly fieldDefaultEventOpt?: string;
|
|
328
334
|
private readonly fieldRootDir: () => string;
|
|
335
|
+
private readonly fieldReel?: FieldReelOptions;
|
|
336
|
+
private readonly seatWorkDeps: TelegramSeatWorkDeps;
|
|
329
337
|
/** 앨범 id → `#현장` 행사 · 답장 디바운스. 태그된 앨범만 들어온다. */
|
|
330
338
|
private readonly fieldGroups = new Map<string, {
|
|
331
339
|
event: string; at: number; chatId: number; threadId?: number; replyTo: number; count: number;
|
|
@@ -359,6 +367,8 @@ export class TelegramBot {
|
|
|
359
367
|
this.voiceAdapter = opts.voiceAdapter;
|
|
360
368
|
this.fieldDefaultEventOpt = opts.fieldDefaultEvent;
|
|
361
369
|
this.fieldRootDir = opts.fieldRootDir ?? getElanousConfigDir;
|
|
370
|
+
this.fieldReel = opts.fieldReel;
|
|
371
|
+
this.seatWorkDeps = opts.seatWorkDeps ?? {};
|
|
362
372
|
if (opts.onTriggerTap) {
|
|
363
373
|
// Assign through `as unknown` to bypass `readonly` + private —
|
|
364
374
|
// the tap is set once at construction and never reassigned.
|
|
@@ -796,6 +806,7 @@ export class TelegramBot {
|
|
|
796
806
|
replyTo?: number;
|
|
797
807
|
threadId?: number;
|
|
798
808
|
caption?: string;
|
|
809
|
+
mimeType?: string;
|
|
799
810
|
} = {}): Promise<{ messageId: number } | undefined> {
|
|
800
811
|
// Per-chat gap mirror — keep telegram happy on rapid document spills.
|
|
801
812
|
const last = this.chatLastSentAt.get(chatId) ?? 0;
|
|
@@ -816,7 +827,7 @@ export class TelegramBot {
|
|
|
816
827
|
new Uint8Array(blobBytes).set(bytes);
|
|
817
828
|
form.set(
|
|
818
829
|
'document',
|
|
819
|
-
new Blob([blobBytes], { type: 'text/plain' }),
|
|
830
|
+
new Blob([blobBytes], { type: opts.mimeType ?? 'text/plain' }),
|
|
820
831
|
filename,
|
|
821
832
|
);
|
|
822
833
|
|
|
@@ -1402,7 +1413,23 @@ export class TelegramBot {
|
|
|
1402
1413
|
}
|
|
1403
1414
|
}
|
|
1404
1415
|
// 글(자막)은 캡션이 달린 항목에만 있다 — 앨범이면 첫 장에만 붙는다(MK 2026-09-30).
|
|
1405
|
-
|
|
1416
|
+
const saved = saveFieldMedia(inputs, { rootDir: this.fieldRootDir(), event, device: 'telegram', ...(tag?.text ? { caption: tag.text } : {}) });
|
|
1417
|
+
count = saved.count;
|
|
1418
|
+
scheduleFieldReel(saved.dir, {
|
|
1419
|
+
...this.fieldReel,
|
|
1420
|
+
notificationKey: `telegram:${this.botId}:${ctx.chatId}:${ctx.threadId ?? ''}:${ctx.mediaGroupId ?? ctx.messageId}`,
|
|
1421
|
+
onDone: async (result) => {
|
|
1422
|
+
const reply = { replyTo: ctx.messageId, threadId: ctx.threadId };
|
|
1423
|
+
if (result.ok && result.file) {
|
|
1424
|
+
const sent = await this.sendDocument(ctx.chatId, readFileSync(result.file), 'reel-9x16.mp4', {
|
|
1425
|
+
...reply, mimeType: 'video/mp4', caption: `현장 영상 · ${event} · ${result.seconds}초`,
|
|
1426
|
+
});
|
|
1427
|
+
if (!sent) throw new Error('sendDocument failed');
|
|
1428
|
+
} else {
|
|
1429
|
+
await this.sendMessage(ctx.chatId, `현장 영상 실패 · ${event} · ${result.error ?? 'render failed'}`, reply);
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
});
|
|
1406
1433
|
debug.log('field.upload', 'saved', { surface: 'telegram', files: inputs.length, count, album: !!ctx.mediaGroupId });
|
|
1407
1434
|
} catch (err) {
|
|
1408
1435
|
failure = err instanceof FieldUploadError ? err.code : 'download-failed';
|
|
@@ -1440,6 +1467,15 @@ export class TelegramBot {
|
|
|
1440
1467
|
}
|
|
1441
1468
|
// 현장 업로드 — `#현장` 캡션(또는 태그된 앨범의 나머지)의 사진·영상은 LLM 대신 현장 폴더로.
|
|
1442
1469
|
if (await this.tryHandleFieldUpload(ctx)) return;
|
|
1470
|
+
if (ctx.text.startsWith('@')) {
|
|
1471
|
+
const reply = await handleTelegramSeatWork(ctx.text, {
|
|
1472
|
+
chatId: ctx.chatId, messageId: ctx.messageId, threadId: ctx.threadId, botId: ctx.botId,
|
|
1473
|
+
}, this.seatWorkDeps);
|
|
1474
|
+
if (reply !== null) {
|
|
1475
|
+
await this.sendMessage(ctx.chatId, reply, { replyTo: ctx.messageId, threadId: ctx.threadId });
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1443
1479
|
// 영상은 `#현장` 저장 전용으로만 파싱한다 — 그 밖의 영상은 종전처럼 무시한다.
|
|
1444
1480
|
if (ctx.video && ctx.attachments.length === 0) return;
|
|
1445
1481
|
// Cascade-zyu U2 — capture utterance intent at update ingress.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// `elanous_skills_list` MCP tool. Mirrors the daemon-side ACP method
|
|
3
3
|
// `elanous/skills/list` (src/acp/server.ts:2241) but session-agnostic so
|
|
4
4
|
// the codex app-server can call it through the elanous-tools MCP server
|
|
5
|
-
// without first opening an ACP session. Enumerates
|
|
5
|
+
// without first opening an ACP session. Enumerates configured skill roots
|
|
6
6
|
// and surfaces the first non-heading line of each SKILL.md as the
|
|
7
7
|
// description.
|
|
8
8
|
//
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { readdir, readFile } from 'node:fs/promises';
|
|
16
16
|
import { defaultSkillDirs } from '../user-config.js';
|
|
17
|
+
import { SKILLS_LIST_DESCRIPTION } from '../onboarding/entry-hints.js';
|
|
17
18
|
import type { LLMToolSpec } from '../llm.js';
|
|
18
19
|
import type { ToolRuntime } from './types.js';
|
|
19
20
|
|
|
@@ -41,8 +42,7 @@ export interface ElanousSkillsListResult extends Record<string, unknown> {
|
|
|
41
42
|
export function buildElanousSkillsListTool(): LLMToolSpec {
|
|
42
43
|
return {
|
|
43
44
|
name: 'elanous_skills_list',
|
|
44
|
-
description:
|
|
45
|
-
'List installed elanous skills (sub-directories of ~/.elanous/skills with optional SKILL.md). Codex app-server callback via the elanous-tools MCP server. Read-only.',
|
|
45
|
+
description: SKILLS_LIST_DESCRIPTION,
|
|
46
46
|
parameters: {
|
|
47
47
|
type: 'object',
|
|
48
48
|
properties: {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
+
import { isMainThread, parentPort, Worker } from 'node:worker_threads';
|
|
3
|
+
import { debug } from '../debug/log.js';
|
|
2
4
|
import type { LLMToolSpec } from '../llm.js';
|
|
3
5
|
import type { NativeToolCatalogEntry } from '../native-tool-catalog.js';
|
|
4
|
-
import type { ToolRunResult, ToolRuntime } from './types.js';
|
|
6
|
+
import type { ToolRunResult, ToolRuntime, ToolRuntimeContext } from './types.js';
|
|
5
7
|
|
|
6
8
|
/** Canonical names for the read-only self-cognition surface. */
|
|
7
9
|
export const SELF_COGNITION_TOOL_NAMES = [
|
|
@@ -12,6 +14,7 @@ export const SELF_COGNITION_TOOL_NAMES = [
|
|
|
12
14
|
] as const;
|
|
13
15
|
|
|
14
16
|
type SelfCognitionToolName = (typeof SELF_COGNITION_TOOL_NAMES)[number];
|
|
17
|
+
type RecallToolName = 'memory_recall' | 'self_recall';
|
|
15
18
|
type CoreToolsModule = typeof import('../domains/core-tools.js');
|
|
16
19
|
|
|
17
20
|
const require = createRequire(import.meta.url);
|
|
@@ -32,12 +35,167 @@ function toToolRunResult(result: unknown): ToolRunResult {
|
|
|
32
35
|
: { output: String(result) };
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
const PROGRESS_MS = 10_000;
|
|
39
|
+
const DEFAULT_BUDGET_MS = 60_000;
|
|
40
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
41
|
+
|
|
42
|
+
function hitCount(result: ToolRunResult): number {
|
|
43
|
+
const data: Record<string, unknown> = result;
|
|
44
|
+
return Array.isArray(data.hits) ? data.hits.length
|
|
45
|
+
: Array.isArray(data.events) ? data.events.length
|
|
46
|
+
: data.restored ? 1 : 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function partialResult(name: RecallToolName, result: ToolRunResult | undefined, budgetMs: number): ToolRunResult {
|
|
50
|
+
const fallback = name === 'memory_recall' ? { hits: [], count: 0 } : { events: [], count: 0, docs: null };
|
|
51
|
+
const snapshot: Record<string, unknown> = result ?? fallback;
|
|
52
|
+
const budgetSeconds = budgetMs / 1000;
|
|
53
|
+
return {
|
|
54
|
+
...snapshot,
|
|
55
|
+
partial: true,
|
|
56
|
+
note: `${typeof snapshot.note === 'string' ? `${snapshot.note}\n` : ''}(찾던 중 ${budgetSeconds}초에서 멈췄습니다 — 더 찾을까요?)`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A deadline returns the latest completed search stage; late results cannot replace it. */
|
|
61
|
+
export async function runBudgetedRecall(
|
|
62
|
+
name: RecallToolName,
|
|
63
|
+
args: Record<string, unknown>,
|
|
64
|
+
ctx: ToolRuntimeContext,
|
|
65
|
+
search: (onPartial: (result: ToolRunResult) => void) => Promise<ToolRunResult>,
|
|
66
|
+
stop: () => void = () => {},
|
|
67
|
+
progressAfterMs = PROGRESS_MS,
|
|
68
|
+
): Promise<ToolRunResult> {
|
|
69
|
+
const started = Date.now();
|
|
70
|
+
const budgetMs = typeof args.timeBudgetMs === 'number' && Number.isFinite(args.timeBudgetMs) && args.timeBudgetMs > 0
|
|
71
|
+
? Math.min(args.timeBudgetMs, MAX_TIMER_MS) : DEFAULT_BUDGET_MS;
|
|
72
|
+
let latest: ToolRunResult | undefined;
|
|
73
|
+
let settled = false;
|
|
74
|
+
const query = typeof args.query === 'string' && args.query.trim()
|
|
75
|
+
? args.query.trim().replace(/\s+/g, ' ').slice(0, 80)
|
|
76
|
+
: name === 'self_recall' ? '자기 구현·자율행동 이력' : '최근 발송·대화';
|
|
77
|
+
const log = (event: 'progress' | 'budget-hit' | 'done', result?: ToolRunResult) => {
|
|
78
|
+
debug.log('tool.memory-recall', event, { elapsedMs: Date.now() - started, hits: hitCount(result ?? latest ?? {}) });
|
|
79
|
+
};
|
|
80
|
+
let progressTimer: ReturnType<typeof setTimeout> | undefined;
|
|
81
|
+
let budgetTimer: ReturnType<typeof setTimeout> | undefined;
|
|
82
|
+
return new Promise<ToolRunResult>((resolve, reject) => {
|
|
83
|
+
const finish = (result: ToolRunResult, budgetHit: boolean) => {
|
|
84
|
+
if (settled) return;
|
|
85
|
+
settled = true;
|
|
86
|
+
clearTimeout(progressTimer);
|
|
87
|
+
clearTimeout(budgetTimer);
|
|
88
|
+
log(budgetHit ? 'budget-hit' : 'done', result);
|
|
89
|
+
if (budgetHit) stop();
|
|
90
|
+
resolve(result);
|
|
91
|
+
};
|
|
92
|
+
progressTimer = setTimeout(() => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
const line = `기억을 찾는 중 — ${query}`;
|
|
95
|
+
ctx.emitFeedback?.({
|
|
96
|
+
envelopeVersion: 1,
|
|
97
|
+
sessionId: ctx.sessionId ?? '',
|
|
98
|
+
blockId: `${ctx.sessionId ?? 'recall'}:tool:${ctx.toolCallId ?? name}:progress`,
|
|
99
|
+
parentToolCallId: ctx.toolCallId,
|
|
100
|
+
kind: 'tool.progress', phase: 'delta', emittedAt: Date.now(), seq: 1,
|
|
101
|
+
payload: { stream: 'generic', lines: [line] }, asciiFallback: [line],
|
|
102
|
+
});
|
|
103
|
+
log('progress');
|
|
104
|
+
}, progressAfterMs);
|
|
105
|
+
budgetTimer = setTimeout(() => finish(partialResult(name, latest, budgetMs), true), budgetMs);
|
|
106
|
+
try {
|
|
107
|
+
void search(result => { if (!settled) latest = result; }).then(
|
|
108
|
+
result => finish(result, false),
|
|
109
|
+
error => {
|
|
110
|
+
if (settled) return;
|
|
111
|
+
settled = true;
|
|
112
|
+
clearTimeout(progressTimer);
|
|
113
|
+
clearTimeout(budgetTimer);
|
|
114
|
+
stop();
|
|
115
|
+
reject(error);
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
settled = true;
|
|
120
|
+
clearTimeout(progressTimer);
|
|
121
|
+
clearTimeout(budgetTimer);
|
|
122
|
+
stop();
|
|
123
|
+
reject(error);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Separate thread: synchronous SQLite and S3 restore must not block progress/deadline timers.
|
|
129
|
+
if (!isMainThread && parentPort) {
|
|
130
|
+
parentPort.on('message', async ({ name, args }: { name: RecallToolName; args: Record<string, unknown> }) => {
|
|
131
|
+
try {
|
|
132
|
+
if (name === 'self_recall') {
|
|
133
|
+
const { existsSync } = await import('node:fs');
|
|
134
|
+
const { surfaceEventsDbPath, openSurfaceEventsDb, recallEvents } = await import('../domains/surface-events.js');
|
|
135
|
+
if (existsSync(surfaceEventsDbPath())) {
|
|
136
|
+
const db = openSurfaceEventsDb();
|
|
137
|
+
try {
|
|
138
|
+
const query = typeof args.query === 'string' ? args.query : '';
|
|
139
|
+
const hits = recallEvents(db, {
|
|
140
|
+
domain: 'elanous',
|
|
141
|
+
sinceHours: typeof args.sinceHours === 'number' ? args.sinceHours : 720,
|
|
142
|
+
limit: typeof args.limit === 'number' ? args.limit : 8,
|
|
143
|
+
...(query ? { query } : {}),
|
|
144
|
+
});
|
|
145
|
+
const events = hits.map(h => ({
|
|
146
|
+
when: h.ts, tool: h.surface, kind: h.kind ?? 'impl',
|
|
147
|
+
summary: (h.summary ?? h.text).slice(0, 300), score: Math.round(h.score * 1000) / 1000,
|
|
148
|
+
}));
|
|
149
|
+
parentPort?.postMessage({ stage: 'partial', result: { events, count: events.length, docs: null } });
|
|
150
|
+
} catch { /* self_recall's episode stage is fail-soft */ } finally { db.close(); }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const result = await coreTools().dispatchRecallUnbudgeted(name, args, partial => {
|
|
154
|
+
parentPort?.postMessage({ stage: 'partial', result: partial });
|
|
155
|
+
});
|
|
156
|
+
parentPort?.postMessage({ stage: 'done', result });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
parentPort?.postMessage({ stage: 'error', error: error instanceof Error ? error.message : String(error) });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type RecallSearch = (name: RecallToolName, args: Record<string, unknown>, onPartial: (result: ToolRunResult) => void) => Promise<ToolRunResult>;
|
|
164
|
+
|
|
165
|
+
export function runRecall(name: RecallToolName, args: Record<string, unknown>, ctx: ToolRuntimeContext, injectedSearch?: RecallSearch, onPartial?: (result: ToolRunResult) => void): Promise<ToolRunResult> {
|
|
166
|
+
const worker = injectedSearch ? undefined : new Worker(new URL(import.meta.url));
|
|
167
|
+
let active = true;
|
|
168
|
+
const search = (capture: (result: ToolRunResult) => void) => {
|
|
169
|
+
const report = (result: ToolRunResult) => {
|
|
170
|
+
if (!active) return;
|
|
171
|
+
capture(result);
|
|
172
|
+
onPartial?.(result);
|
|
173
|
+
};
|
|
174
|
+
return injectedSearch
|
|
175
|
+
? injectedSearch(name, args, report)
|
|
176
|
+
: new Promise<ToolRunResult>((resolve, reject) => {
|
|
177
|
+
worker!.on('message', (message: { stage: string; result?: unknown; error?: string }) => {
|
|
178
|
+
if (message.stage === 'partial') report(toToolRunResult(message.result));
|
|
179
|
+
else if (message.stage === 'done') resolve(toToolRunResult(message.result));
|
|
180
|
+
else if (message.stage === 'error') reject(new Error(message.error));
|
|
181
|
+
});
|
|
182
|
+
worker!.on('error', reject);
|
|
183
|
+
worker!.on('exit', code => { if (code !== 0) reject(new Error(`Recall worker exited: ${code}`)); });
|
|
184
|
+
worker!.postMessage({ name, args });
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
return runBudgetedRecall(name, args, ctx, search, () => { active = false; if (worker) void worker.terminate(); })
|
|
188
|
+
.finally(() => { active = false; if (worker) void worker.terminate(); });
|
|
189
|
+
}
|
|
190
|
+
|
|
35
191
|
export const SELF_COGNITION_RUNTIMES: readonly ToolRuntime[] = SELF_COGNITION_TOOL_NAMES.map(name => ({
|
|
36
192
|
id: name,
|
|
37
193
|
get spec() {
|
|
38
194
|
return coreSpec(name);
|
|
39
195
|
},
|
|
40
|
-
run: async (args) =>
|
|
196
|
+
run: async (args, ctx) => name === 'memory_recall' || name === 'self_recall'
|
|
197
|
+
? runRecall(name, args, ctx)
|
|
198
|
+
: toToolRunResult(await coreTools().buildCoreTools().dispatch(name, args)),
|
|
41
199
|
}));
|
|
42
200
|
|
|
43
201
|
/** MCP catalog metadata derived from the same name ledger as the runtimes. */
|