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
|
@@ -8,6 +8,7 @@ import { getUserConfig } from '../user-config.js';
|
|
|
8
8
|
import { decideBudget, readBudgetInputsLive } from '../self-implement/budget-gate.js';
|
|
9
9
|
import { findTrack } from './mission-phase-track.js';
|
|
10
10
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
11
|
+
import { decideShadowTrackAction, launchTrackAgentShadow, type ShadowOptions } from './track-agent-shadow.js';
|
|
11
12
|
|
|
12
13
|
export type TrackAgentInput = { missionId: string; taskId: string; title: string; prompt: string; track: string };
|
|
13
14
|
export type TrackAgentDecision = { action: 'say' | 'hold'; reason?: string };
|
|
@@ -25,6 +26,8 @@ type RunnerDeps = {
|
|
|
25
26
|
session?: (persona: string) => string;
|
|
26
27
|
root?: string;
|
|
27
28
|
maxConcurrent?: number;
|
|
29
|
+
shadow?: boolean;
|
|
30
|
+
shadowOptions?: ShadowOptions;
|
|
28
31
|
};
|
|
29
32
|
const root = resolve(import.meta.dir, '../..');
|
|
30
33
|
// The installed package is not a git checkout — the daemon's target repository comes first.
|
|
@@ -41,7 +44,8 @@ const runCli = async (args: string[]): Promise<{ stdout: string }> => {
|
|
|
41
44
|
const { argv, env } = universeLaunch(args);
|
|
42
45
|
return exec('bun', argv, { cwd: repoDir(), env, timeout: 120_000, maxBuffer: 1024 * 1024, encoding: 'utf8' });
|
|
43
46
|
};
|
|
44
|
-
const
|
|
47
|
+
const forbiddenActionRegex = /\b(?:delete|remove|deploy|publish|release|restart|reboot|merge|force|pay|purchase|secret|credential|sudo|rm\s+-rf|config\s+set)\b|(?:^|\s)--prod\b|삭제|배포|게시|릴리스|재시작|재부팅|병합|결제|자격|비밀|강제/i;
|
|
48
|
+
export const TRACK_AGENT_FORBIDDEN_ACTION_REGEX = new RegExp(forbiddenActionRegex.source, forbiddenActionRegex.flags);
|
|
45
49
|
let running = 0;
|
|
46
50
|
|
|
47
51
|
function context(input: TrackAgentInput, repo: string): string {
|
|
@@ -70,6 +74,17 @@ function context(input: TrackAgentInput, repo: string): string {
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export async function decideTrackAction(input: TrackAgentInput, deps: RunnerDeps = {}): Promise<TrackAgentDecision> {
|
|
77
|
+
if (!(deps.shadow ?? getUserConfig().autopilot?.trackAgent?.shadow ?? false)) {
|
|
78
|
+
return decideTrackActionOnce(input, deps);
|
|
79
|
+
}
|
|
80
|
+
return decideShadowTrackAction(input, (value) => decideTrackActionOnce(value, deps), {
|
|
81
|
+
...deps.shadowOptions,
|
|
82
|
+
maxDecisionsPerDay: deps.shadowOptions?.maxDecisionsPerDay
|
|
83
|
+
?? getUserConfig().autopilot?.trackAgent?.shadowMaxDecisionsPerDay ?? 20,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function decideTrackActionOnce(input: TrackAgentInput, deps: RunnerDeps): Promise<TrackAgentDecision> {
|
|
73
88
|
try {
|
|
74
89
|
const prompt = context(input, deps.root ?? repoDir());
|
|
75
90
|
const persona = `track-${input.track}`;
|
|
@@ -83,7 +98,7 @@ export async function decideTrackAction(input: TrackAgentInput, deps: RunnerDeps
|
|
|
83
98
|
const values = decision as Record<string, unknown>;
|
|
84
99
|
if (!['say', 'hold'].includes(String(values.action)) || (values.reason !== undefined && typeof values.reason !== 'string')
|
|
85
100
|
|| Object.keys(values).some((key) => !['action', 'reason'].includes(key))) throw new Error('invalid decision');
|
|
86
|
-
if (typeof values.reason === 'string' &&
|
|
101
|
+
if (typeof values.reason === 'string' && forbiddenActionRegex.test(values.reason)) throw new Error('forbidden action');
|
|
87
102
|
return decision as TrackAgentDecision;
|
|
88
103
|
} catch (error) {
|
|
89
104
|
log('decision-failed', { track: input.track, taskId: input.taskId, error: redactSecretText(String(error)) });
|
|
@@ -93,7 +108,7 @@ export async function decideTrackAction(input: TrackAgentInput, deps: RunnerDeps
|
|
|
93
108
|
|
|
94
109
|
export async function executeTrackAction(decision: TrackAgentDecision, input: TrackAgentInput, deps: RunnerDeps = {}): Promise<TrackAgentResult> {
|
|
95
110
|
if (decision.action !== 'say') return { status: 'held', detail: decision.reason ?? '사람 확인' };
|
|
96
|
-
if (
|
|
111
|
+
if (forbiddenActionRegex.test(`${input.title}\n${input.prompt}`)) return { status: 'held', detail: '금지 작업 — 사람 확인' };
|
|
97
112
|
if (!findTrack(input.track)) return { status: 'held', detail: '트랙 미정 — 사람 확인' };
|
|
98
113
|
try {
|
|
99
114
|
const checkBudget = deps.checkBudget ?? (async () => {
|
|
@@ -112,6 +127,11 @@ export async function executeTrackAction(decision: TrackAgentDecision, input: Tr
|
|
|
112
127
|
running++;
|
|
113
128
|
let held = false;
|
|
114
129
|
try {
|
|
130
|
+
if (deps.shadow ?? getUserConfig().autopilot?.trackAgent?.shadow ?? false) {
|
|
131
|
+
const result = await launchTrackAgentShadow(input, deps.shadowOptions);
|
|
132
|
+
log('shadow-orchestrate', { track: input.track, taskId: input.taskId, status: result.status, detail: result.detail });
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
115
135
|
const launch = deps.launch ?? (async (args: string[]) => {
|
|
116
136
|
const { argv, env } = universeLaunch(args);
|
|
117
137
|
const child = spawn('bun', argv, { cwd: repoDir(), env, stdio: 'ignore', detached: true });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function acpServerHelpText(): string {
|
|
2
|
+
return `
|
|
3
|
+
ACP server (Agent Client Protocol)
|
|
4
|
+
Usage: elanous --acp-server [--transport=stdio|unix-socket|websocket] [options]
|
|
5
|
+
Example: elanous --acp-server --transport=websocket
|
|
6
|
+
|
|
7
|
+
--transport=stdio|unix-socket|websocket Connection transport (default: stdio)
|
|
8
|
+
--socket-path=<path> Unix socket path (unix-socket only)
|
|
9
|
+
--port=<number> Listening port (websocket only)
|
|
10
|
+
--host=<host> Bind host (websocket only)
|
|
11
|
+
--tool-cwd=<path> Default working directory for tools
|
|
12
|
+
--no-auth Disable websocket token authentication
|
|
13
|
+
|
|
14
|
+
Zed setup and connection check: docs/manual/MANUAL-zed-acp.md
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { classifyShortQuestion } from '../chat/short-question.js';
|
|
2
|
+
import { getUserConfig } from '../user-config.js';
|
|
1
3
|
import { runCoreTurn } from '../core-turn/index.js';
|
|
2
4
|
import type { LLMMessage, LLMToolSpec } from '../llm.js';
|
|
3
5
|
import { maybeImageBearingResult } from '../llm.js';
|
|
@@ -83,6 +85,8 @@ export function summarizeToolResult(result: unknown): string | undefined {
|
|
|
83
85
|
|
|
84
86
|
export async function runDaemonPromptTurn(opts: {
|
|
85
87
|
history: DaemonSessionHistory;
|
|
88
|
+
/** B1 빠른 경로 스위치 — 없으면 config `chat.fastPath`(기본 true). 시험·호출부가 못 박을 때만 준다. */
|
|
89
|
+
fastPathEnabled?: boolean;
|
|
86
90
|
request: DaemonPromptRequest;
|
|
87
91
|
/** Optional multimodal prompt blocks for the current user turn.
|
|
88
92
|
* When present, history persistence keeps the ACP block shape
|
|
@@ -173,6 +177,19 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
173
177
|
reason: surfaceResolutionReason,
|
|
174
178
|
hasUserText: request.userText.length > 0,
|
|
175
179
|
});
|
|
180
|
+
const hasAttachments = (opts.promptBlocks?.length ? opts.promptBlocks : request.userContent)
|
|
181
|
+
?.some((block) => block.type !== 'text') ?? false;
|
|
182
|
+
// SW2 스위치 `chat.fastPath`(기본 켜짐 · PWA «짧은 물음은 빠르게» 카드) — 끄면 분류하지 않는다.
|
|
183
|
+
const fastPathEnabled = opts.fastPathEnabled ?? (() => { try { return getUserConfig().chat?.fastPath !== false; } catch { return true; } })();
|
|
184
|
+
const { fast, reason } = fastPathEnabled
|
|
185
|
+
? classifyShortQuestion(request.userText, { hasAttachments })
|
|
186
|
+
: { fast: false, reason: 'disabled' };
|
|
187
|
+
const activeToolSurface = fast ? undefined : toolSurface;
|
|
188
|
+
const toolsBefore = toolSurface && toolSurface.kind !== 'none' ? toolSurface.specs.length : 0;
|
|
189
|
+
const tools: LLMToolSpec[] = activeToolSurface && activeToolSurface.kind !== 'none'
|
|
190
|
+
? activeToolSurface.specs
|
|
191
|
+
: [];
|
|
192
|
+
debug.log('chat.fast-path', 'decided', { fast, reason, toolsBefore, toolsAfter: tools.length });
|
|
176
193
|
// Image-pipeline followup #4 (2026-05-05) — when the webterm surface
|
|
177
194
|
// is active, prepend a daemon-context block ("Current ACP sessionId:
|
|
178
195
|
// …" + active terminals) to the system prompt so the LLM has the
|
|
@@ -181,21 +198,18 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
181
198
|
const augmentedSystemPrompt = appendWebtermContext(
|
|
182
199
|
request.effectiveSystemPrompt,
|
|
183
200
|
request.sessionId,
|
|
184
|
-
|
|
201
|
+
activeToolSurface,
|
|
185
202
|
);
|
|
186
203
|
const ctrl = new AbortController();
|
|
187
204
|
if (opts.signal) {
|
|
188
205
|
if (opts.signal.aborted) ctrl.abort();
|
|
189
206
|
else opts.signal.addEventListener('abort', () => ctrl.abort(), { once: true });
|
|
190
207
|
}
|
|
191
|
-
const tools: LLMToolSpec[] = toolSurface && toolSurface.kind !== 'none'
|
|
192
|
-
? toolSurface.specs
|
|
193
|
-
: [];
|
|
194
208
|
const toolCwdResolver = createToolCwdResolver({
|
|
195
|
-
tools:
|
|
209
|
+
tools: activeToolSurface?.kind ?? 'none',
|
|
196
210
|
...(opts.toolCwd !== undefined ? { toolCwd: opts.toolCwd } : {}),
|
|
197
211
|
});
|
|
198
|
-
const rawDispatchTool =
|
|
212
|
+
const rawDispatchTool = activeToolSurface && activeToolSurface.kind !== 'none'
|
|
199
213
|
? async (name: string, args: Record<string, unknown>, perCallCtx?: { callId: string }): Promise<unknown> => {
|
|
200
214
|
const dispatchCtrl = new AbortController();
|
|
201
215
|
const onAbort = (): void => dispatchCtrl.abort();
|
|
@@ -204,7 +218,7 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
204
218
|
// Image-pipeline followup #1 (2026-05-05) — pass the current
|
|
205
219
|
// request.sessionId through so WebTerminal* tools can auto-
|
|
206
220
|
// resolve scope when the LLM omits sessionId from args.
|
|
207
|
-
return await
|
|
221
|
+
return await activeToolSurface.dispatch(name, args, {
|
|
208
222
|
cwd: toolCwdResolver.cwd!,
|
|
209
223
|
resolveWriteCwd: toolCwdResolver.resolveWriteCwd,
|
|
210
224
|
signal: dispatchCtrl.signal,
|
|
@@ -246,9 +260,13 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
246
260
|
})
|
|
247
261
|
: null;
|
|
248
262
|
const dispatchTool = terminal ? terminal.dispatch : rawDispatchTool;
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
const fastPathGuidance = '확인이 필요하면 "확인해 볼까요?" 한 줄로 끝낸다';
|
|
264
|
+
const effectiveSystemPrompt = fast
|
|
265
|
+
? [augmentedSystemPrompt, fastPathGuidance].filter(Boolean).join('\n')
|
|
266
|
+
: terminal
|
|
267
|
+
? terminal.systemPromptParts.join('\n\n')
|
|
268
|
+
: augmentedSystemPrompt;
|
|
269
|
+
const isFirstTurn = history.get(request.sessionId).length === 0;
|
|
252
270
|
const messages = opts.promptBlocks && opts.promptBlocks.length > 0
|
|
253
271
|
? appendUserPromptBlocksAndBuildMessages(
|
|
254
272
|
history,
|
|
@@ -262,6 +280,9 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
262
280
|
request.userText,
|
|
263
281
|
effectiveSystemPrompt,
|
|
264
282
|
);
|
|
283
|
+
if (fast && !isFirstTurn) {
|
|
284
|
+
messages.unshift({ role: 'system', content: fastPathGuidance });
|
|
285
|
+
}
|
|
265
286
|
const result = await runCoreTurn({
|
|
266
287
|
sessionId: request.sessionId,
|
|
267
288
|
userText: request.userText,
|
|
@@ -389,7 +389,13 @@ export function toolSurface(kind: DaemonToolSurfaceKind, cfg?: import('../../use
|
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
391
|
if (shared.names.has(name)) {
|
|
392
|
-
|
|
392
|
+
// L2 core + L3 finance(gated) — buildSharedAppTools 단일 dispatch · B5: 진행 한 줄(emitFeedback)·세션·호출 id 를 넘긴다.
|
|
393
|
+
return shared.dispatch(name, args, {
|
|
394
|
+
surface: 'tui',
|
|
395
|
+
...(ctx.sessionId ? { sessionId: ctx.sessionId } : {}),
|
|
396
|
+
...(ctx.toolCallId ? { toolCallId: ctx.toolCallId } : {}),
|
|
397
|
+
...(ctx.emitFeedback ? { emitFeedback: ctx.emitFeedback } : {}),
|
|
398
|
+
});
|
|
393
399
|
}
|
|
394
400
|
if (name === 'Bash') {
|
|
395
401
|
// PLAN-ios-rich-dev-feedback-hydrate M3 (2026-05-13) — pass
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { codexCredentialRoot } from './codex-reset-credit-state.js';
|
|
4
|
+
import type { LlmPolicy } from '../policy/llm-policy.js';
|
|
5
|
+
|
|
6
|
+
interface CreditDay {
|
|
7
|
+
day: string;
|
|
8
|
+
accounts: Record<string, { dayOpen: number; balance: number }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type CreditBalances = Readonly<Record<string, number>>;
|
|
12
|
+
|
|
13
|
+
function kstDay(now: Date): string {
|
|
14
|
+
return new Date(now.getTime() + 9 * 3_600_000).toISOString().slice(0, 10);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function dayNumber(day: string): number {
|
|
18
|
+
return Date.parse(`${day}T00:00:00Z`) / 86_400_000;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function creditDayPath(stateDir?: string): string {
|
|
22
|
+
return join(stateDir ?? codexCredentialRoot(), 'budget', 'codex-credit-day.json');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readCreditDay(stateDir?: string): CreditDay | undefined {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(readFileSync(creditDayPath(stateDir), 'utf8')) as CreditDay;
|
|
28
|
+
return typeof parsed.day === 'string' && parsed.accounts && typeof parsed.accounts === 'object' && !Array.isArray(parsed.accounts)
|
|
29
|
+
? parsed : undefined;
|
|
30
|
+
} catch { return undefined; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Only a poller's observed balance can move the opening balance; readers never modify the ledger. */
|
|
34
|
+
export function recordCreditBalances(balances: CreditBalances, now: Date = new Date(), stateDir?: string): void {
|
|
35
|
+
const day = kstDay(now);
|
|
36
|
+
const previous = readCreditDay(stateDir);
|
|
37
|
+
const accounts: CreditDay['accounts'] = previous?.day === day ? { ...previous.accounts } : {};
|
|
38
|
+
for (const [name, balance] of Object.entries(balances)) {
|
|
39
|
+
if (!Number.isFinite(balance) || balance < 0) continue;
|
|
40
|
+
const prior = Object.prototype.hasOwnProperty.call(accounts, name) ? accounts[name] : undefined;
|
|
41
|
+
const dayOpen = prior && Number.isFinite(prior.dayOpen) && Number.isFinite(prior.balance)
|
|
42
|
+
? (balance > prior.balance ? balance : prior.dayOpen)
|
|
43
|
+
: balance;
|
|
44
|
+
Object.defineProperty(accounts, name, { value: { dayOpen, balance }, enumerable: true, configurable: true, writable: true });
|
|
45
|
+
}
|
|
46
|
+
const path = creditDayPath(stateDir);
|
|
47
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
48
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
49
|
+
writeFileSync(temporary, `${JSON.stringify({ day, accounts }, null, 2)}\n`);
|
|
50
|
+
renameSync(temporary, path);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function creditPaceStatus({ policy, balances, now = new Date(), stateDir }: {
|
|
54
|
+
policy: LlmPolicy;
|
|
55
|
+
balances: CreditBalances;
|
|
56
|
+
now?: Date;
|
|
57
|
+
stateDir?: string;
|
|
58
|
+
}): { active: boolean; todaySpent: number; target: number; remaining: number; daysLeft: number; projectedAtUntil: number } {
|
|
59
|
+
const day = kstDay(now);
|
|
60
|
+
const ledger = readCreditDay(stateDir);
|
|
61
|
+
const observed = Object.entries(balances).filter(([, balance]) => Number.isFinite(balance) && balance >= 0);
|
|
62
|
+
const todaySpent = ledger?.day === day ? observed.reduce((sum, [name, balance]) => {
|
|
63
|
+
const open = Object.prototype.hasOwnProperty.call(ledger.accounts, name) ? ledger.accounts[name]?.dayOpen : undefined;
|
|
64
|
+
return sum + (typeof open === 'number' && Number.isFinite(open) ? Math.max(0, open - balance) : 0);
|
|
65
|
+
}, 0) : 0;
|
|
66
|
+
const remaining = observed.reduce((sum, [, balance]) => sum + balance, 0);
|
|
67
|
+
const until = policy.credits.pace?.until;
|
|
68
|
+
const expiry = until ? Date.parse(`${until}T15:00:00Z`) : NaN;
|
|
69
|
+
const daysLeft = until ? Math.max(1, dayNumber(until) - dayNumber(day) + 1) : 1;
|
|
70
|
+
// The poller's opening observation anchors today's allocation. Spending cannot lower it;
|
|
71
|
+
// a new grant or the next KST day changes the opening observation.
|
|
72
|
+
const startingBalance = ledger?.day === day
|
|
73
|
+
? observed.reduce((sum, [name, balance]) => {
|
|
74
|
+
const entry = Object.prototype.hasOwnProperty.call(ledger.accounts, name) ? ledger.accounts[name] : undefined;
|
|
75
|
+
return sum + (entry && Number.isFinite(entry.dayOpen) ? entry.dayOpen : balance);
|
|
76
|
+
}, 0)
|
|
77
|
+
: remaining;
|
|
78
|
+
const target = until ? Math.ceil(startingBalance / daysLeft) : 0;
|
|
79
|
+
return {
|
|
80
|
+
active: policy.credits.codex === 'use' && !!until && now.getTime() < expiry && remaining > 0 && todaySpent < target,
|
|
81
|
+
todaySpent, target, remaining, daysLeft,
|
|
82
|
+
projectedAtUntil: Math.max(0, remaining - Math.max(0, target - todaySpent) - target * (daysLeft - 1)),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Codex prepaid-credit plan: days left until the credits expire, the daily spend that would use the balance up,
|
|
2
|
+
// and what was actually spent over the last 7 days. Disk only — the balance history is appended by
|
|
3
|
+
// writeQuotaSignal whenever a measured balance changes; nothing here touches the network.
|
|
4
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const DAY_MS = 86_400_000;
|
|
8
|
+
const HISTORY_KEEP_MS = 30 * DAY_MS;
|
|
9
|
+
const HISTORY_FILE = 'codex-credit-history.jsonl';
|
|
10
|
+
|
|
11
|
+
export interface CreditHistoryEntry { at: string; home: string; balance: number }
|
|
12
|
+
|
|
13
|
+
export function creditHistoryPath(signalDir: string): string {
|
|
14
|
+
return join(signalDir, HISTORY_FILE);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readCreditHistory(signalDir: string): CreditHistoryEntry[] {
|
|
18
|
+
try {
|
|
19
|
+
const path = creditHistoryPath(signalDir);
|
|
20
|
+
if (!existsSync(path)) return [];
|
|
21
|
+
const out: CreditHistoryEntry[] = [];
|
|
22
|
+
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
23
|
+
if (!line.trim()) continue;
|
|
24
|
+
try {
|
|
25
|
+
const e = JSON.parse(line) as Partial<CreditHistoryEntry>;
|
|
26
|
+
if (typeof e.at === 'string' && typeof e.home === 'string' && typeof e.balance === 'number' && Number.isFinite(e.balance)) out.push(e as CreditHistoryEntry);
|
|
27
|
+
} catch { /* a torn line is skipped */ }
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
} catch { return []; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Appends only when this home's balance changed; prunes entries older than 30 days. Fail-soft. */
|
|
34
|
+
export function appendCreditHistory(signalDir: string, home: string, balance: number, now: number = Date.now()): void {
|
|
35
|
+
try {
|
|
36
|
+
if (!Number.isFinite(balance)) return;
|
|
37
|
+
const history = readCreditHistory(signalDir);
|
|
38
|
+
const last = [...history].reverse().find((e) => e.home === home);
|
|
39
|
+
if (last && last.balance === balance) return;
|
|
40
|
+
mkdirSync(signalDir, { recursive: true });
|
|
41
|
+
const entry: CreditHistoryEntry = { at: new Date(now).toISOString(), home, balance };
|
|
42
|
+
const kept = history.filter((e) => now - Date.parse(e.at) <= HISTORY_KEEP_MS);
|
|
43
|
+
if (kept.length !== history.length) {
|
|
44
|
+
writeFileSync(creditHistoryPath(signalDir), [...kept, entry].map((e) => JSON.stringify(e)).join('\n') + '\n', 'utf8');
|
|
45
|
+
} else {
|
|
46
|
+
appendFileSync(creditHistoryPath(signalDir), `${JSON.stringify(entry)}\n`, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
} catch { /* observation never blocks the fetch */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Credits spent in the window: sum of balance drops per home (rises are top-ups and are not spend). */
|
|
52
|
+
export function creditSpend(history: readonly CreditHistoryEntry[], now: number, windowMs = 7 * DAY_MS): { spent: number; spanDays: number } | null {
|
|
53
|
+
const since = now - windowMs;
|
|
54
|
+
let spent = 0;
|
|
55
|
+
let earliest = Infinity;
|
|
56
|
+
const byHome = new Map<string, CreditHistoryEntry[]>();
|
|
57
|
+
for (const e of history) {
|
|
58
|
+
const at = Date.parse(e.at);
|
|
59
|
+
if (!Number.isFinite(at) || at > now) continue;
|
|
60
|
+
byHome.set(e.home, [...(byHome.get(e.home) ?? []), e]);
|
|
61
|
+
}
|
|
62
|
+
for (const entries of byHome.values()) {
|
|
63
|
+
entries.sort((a, b) => Date.parse(a.at) - Date.parse(b.at));
|
|
64
|
+
// The last entry before the window is the baseline for the first change inside it.
|
|
65
|
+
let start = entries.findIndex((e) => Date.parse(e.at) >= since);
|
|
66
|
+
if (start === -1) continue;
|
|
67
|
+
if (start > 0) start -= 1;
|
|
68
|
+
const used = entries.slice(start);
|
|
69
|
+
if (used.length < 2) continue;
|
|
70
|
+
earliest = Math.min(earliest, Math.max(since, Date.parse(used[0]!.at)));
|
|
71
|
+
for (let i = 1; i < used.length; i++) {
|
|
72
|
+
const drop = used[i - 1]!.balance - used[i]!.balance;
|
|
73
|
+
if (drop > 0) spent += drop;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!Number.isFinite(earliest)) return null;
|
|
77
|
+
return { spent, spanDays: Math.max((now - earliest) / DAY_MS, 1 / 24) };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CodexCreditPlan {
|
|
81
|
+
/** policy credits.pace.until, else the earliest grant expiry; null when the policy records neither. */
|
|
82
|
+
expiresAt: string | null;
|
|
83
|
+
daysLeft: number | null;
|
|
84
|
+
/** Sum of the known per-account balances; null when no balance is known. */
|
|
85
|
+
totalBalance: number | null;
|
|
86
|
+
unknownBalances: number;
|
|
87
|
+
accounts: Array<{ name: string; balance: number | null; expires: string | null }>;
|
|
88
|
+
/** Credits per day that would use the balance up by the expiry date. */
|
|
89
|
+
dailyNeeded: number | null;
|
|
90
|
+
/** policy credits.pace.targetPerDay — the declared pace, shown beside the computed one. */
|
|
91
|
+
targetPerDay: number | null;
|
|
92
|
+
/** Credits actually spent per day over the last 7 days (or the shorter recorded span). */
|
|
93
|
+
actualPerDay: number | null;
|
|
94
|
+
actualSpanDays: number | null;
|
|
95
|
+
/** policy credits.pace.why */
|
|
96
|
+
note: string | null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CreditPolicyView {
|
|
100
|
+
codex: string;
|
|
101
|
+
grants: ReadonlyArray<{ account: string; amount: number; expires: string }>;
|
|
102
|
+
pace?: { targetPerDay: number; until: string; why?: string };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
106
|
+
|
|
107
|
+
export function codexCreditPlan(input: {
|
|
108
|
+
now: number;
|
|
109
|
+
balances: ReadonlyArray<{ name: string; balance?: number }>;
|
|
110
|
+
credits: CreditPolicyView;
|
|
111
|
+
history: readonly CreditHistoryEntry[];
|
|
112
|
+
}): CodexCreditPlan {
|
|
113
|
+
const { credits } = input;
|
|
114
|
+
const accounts = input.balances.map((a) => {
|
|
115
|
+
const grantDates = credits.grants.filter((g) => g.account === a.name && DATE.test(g.expires)).map((g) => g.expires).sort();
|
|
116
|
+
return { name: a.name, balance: typeof a.balance === 'number' && Number.isFinite(a.balance) ? a.balance : null, expires: grantDates[0] ?? null };
|
|
117
|
+
});
|
|
118
|
+
const known = accounts.map((a) => a.balance).filter((b): b is number => b !== null);
|
|
119
|
+
const totalBalance = known.length ? known.reduce((x, y) => x + y, 0) : null;
|
|
120
|
+
const earliestGrant = credits.grants.map((g) => g.expires).filter((d) => DATE.test(d)).sort()[0];
|
|
121
|
+
const expiresAt = credits.pace?.until && DATE.test(credits.pace.until) ? credits.pace.until : earliestGrant ?? null;
|
|
122
|
+
const expiry = expiresAt ? Date.parse(`${expiresAt}T23:59:59+09:00`) : NaN;
|
|
123
|
+
const daysLeft = Number.isFinite(expiry) ? Math.max(0, Math.ceil((expiry - input.now) / DAY_MS)) : null;
|
|
124
|
+
const spend = creditSpend(input.history, input.now);
|
|
125
|
+
return {
|
|
126
|
+
expiresAt,
|
|
127
|
+
daysLeft,
|
|
128
|
+
totalBalance,
|
|
129
|
+
unknownBalances: accounts.length - known.length,
|
|
130
|
+
accounts,
|
|
131
|
+
dailyNeeded: daysLeft && totalBalance !== null ? totalBalance / daysLeft : null,
|
|
132
|
+
targetPerDay: credits.pace?.targetPerDay ?? null,
|
|
133
|
+
actualPerDay: spend ? spend.spent / Math.min(7, spend.spanDays) : null,
|
|
134
|
+
actualSpanDays: spend ? Math.round(Math.min(7, spend.spanDays) * 10) / 10 : null,
|
|
135
|
+
note: credits.pace?.why?.trim() || null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The lines both surfaces print under the policy line. */
|
|
140
|
+
export function formatCodexCreditPlan(plan: CodexCreditPlan): string[] {
|
|
141
|
+
const n = (v: number) => String(Math.round(v));
|
|
142
|
+
const unknown = plan.unknownBalances ? ` (+${plan.unknownBalances}개 계정 모름)` : '';
|
|
143
|
+
const perAccount = plan.accounts.map((a) => `${a.name} ${a.balance === null ? '?' : n(a.balance)}${a.expires ? `(~${a.expires})` : ''}`).join(' · ');
|
|
144
|
+
const lines = [
|
|
145
|
+
`크레딧 잔액 합 ${plan.totalBalance === null ? '?' : n(plan.totalBalance)}${unknown} · 만료 ${plan.expiresAt ?? '모름 (policy credits.pace.until · credits.grants)'}${perAccount ? ` · ${perAccount}` : ''}`,
|
|
146
|
+
];
|
|
147
|
+
const days = plan.daysLeft === null ? '만료일 모름' : `만료까지 ${plan.daysLeft}일`;
|
|
148
|
+
const needed = plan.dailyNeeded === null ? '?' : n(plan.dailyNeeded);
|
|
149
|
+
const target = plan.targetPerDay === null ? '' : ` (목표 ${n(plan.targetPerDay)})`;
|
|
150
|
+
const actual = plan.actualPerDay === null ? '기록 없음' : `${n(plan.actualPerDay)}/일${plan.actualSpanDays !== null && plan.actualSpanDays < 7 ? ` (기록 ${plan.actualSpanDays}일)` : ''}`;
|
|
151
|
+
lines.push(`계산 ${days} · 하루 소진 필요 ${needed}${target} · 최근 7일 실제 ${actual}`);
|
|
152
|
+
if (plan.note) lines.push(`메모 ${plan.note}`);
|
|
153
|
+
return lines;
|
|
154
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// ⇒ 직접 부르는 쪽은 «자기가» 감싸야 한다. 여기서 삼키면 저장 실패가 영영 안 보인다.
|
|
10
10
|
// ⭐ 반면 `writeQuotaSignal` 은 «판정 신호»라 조회를 막으면 안 되므로 자기가 삼킨다(아래 주석).
|
|
11
11
|
|
|
12
|
+
import { appendCreditHistory } from './codex-credit-plan.js';
|
|
12
13
|
import { mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
13
14
|
import { createHash } from 'node:crypto';
|
|
14
15
|
import { homedir } from 'node:os';
|
|
@@ -233,6 +234,7 @@ export function writeQuotaSignal(
|
|
|
233
234
|
...(typeof credits?.hasCredits === 'boolean' ? { hasCredits: credits.hasCredits } : {}),
|
|
234
235
|
};
|
|
235
236
|
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
237
|
+
if (next.creditsBalance !== undefined && next.measuredHome) appendCreditHistory(dirname(path), next.measuredHome, next.creditsBalance);
|
|
236
238
|
} catch { /* fail-soft */ }
|
|
237
239
|
}
|
|
238
240
|
|
package/src/chat/chat-cli.ts
CHANGED
|
@@ -49,5 +49,8 @@ export function toDevChatOpts(opts: ChatCliOpts): DevChatOpts {
|
|
|
49
49
|
export async function runChatCliCommand(text: string, opts: ChatCliOpts, deps: ChatCliDeps): Promise<void> {
|
|
50
50
|
const spec = buildChatDevSpec(text, toDevChatOpts(opts));
|
|
51
51
|
const run = deps.runDevPipeline ?? runDevPipeline;
|
|
52
|
-
await run(spec, {
|
|
52
|
+
await run(spec, {
|
|
53
|
+
runChatTurn: deps.runChatTurn,
|
|
54
|
+
...(opts.json ? { progress: (message: string) => { process.stderr.write(`${message}\n`); } } : {}),
|
|
55
|
+
});
|
|
53
56
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// B1(UX · 0.2.7) — 짧은 «물음»은 도구 없이 빠른 경로로(첫 글자 ≤2초 · 내부 문서 `EVAL-bot-chat-vs-grok-2026-09-30`).
|
|
2
|
+
// 허용 방식(골 ASK-bot-short-question-fast-path-b1b): 아래가 «모두» 참일 때만 fast — 하나라도 거짓이면 fast 아님(보수 쪽).
|
|
3
|
+
// ⓐ 한 줄 · 80자 이하 · 첨부 없음 ⓑ 인사이거나 «물음 꼴» ⓒ 요청·작업 표지 · 경로·URL·코드 꼴 · 실시간 낱말이 없다.
|
|
4
|
+
// 거부 목록으로 변형을 다 막을 수는 없다 — 오분류 비용은 «확인해 볼까요?» 한 줄로 막힌다(도구가 안 실릴 뿐 대화는 이어진다).
|
|
5
|
+
// 판정 표본 = src/chat/short-question.test.ts 의 고정 표 20개(⊕ 리뷰가 든 변형).
|
|
6
|
+
|
|
7
|
+
export interface ShortQuestionContext {
|
|
8
|
+
hasAttachments?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ShortQuestionVerdict {
|
|
11
|
+
fast: boolean;
|
|
12
|
+
reason: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const GREETING = /^(?:안녕(?:하세요|하십니까)?|반가워(?:요)?|고마워(?:요)?|감사합니다|하이|hi|hello|hey|thanks|thank you)[!?.~\s]*$/i;
|
|
16
|
+
// «한 줄로» 같은 짧은 꼬리를 떼고 본다.
|
|
17
|
+
const TAIL = /\s*(?:한\s*줄로|짧게|간단히|briefly|in one line)[.!]?\s*$/i;
|
|
18
|
+
const KO_QUESTION_END = /(?:\?|까|니|나요|냐|야|어때|뭐|몇|언제|어디|누구|왜|인가|일까)[?.!~]*$/;
|
|
19
|
+
const EN_QUESTION = /^(?:what|why|how|who|where|when|which|is|are|does|do)\b.*\?$/i;
|
|
20
|
+
const REQUEST_KO = /(?:해\s*줘|해\s*주세요|줘(?:요)?(?=[?.!\s]|$)|주세요|줄래|할래|볼래|줄\s*수\s*있|돌려|실행|고쳐|만들어\s*줘|찾아|검색|확인해|열어|보내|올려|지워|써\s*줘|작성해|요약해|정리해|분석해)/;
|
|
21
|
+
const REQUEST_EN = /\b(?:please|can you|could you|would you|run|test|tests|fix|build|open|search|check|send|create|write|deploy|install|summarize)\b/i;
|
|
22
|
+
const TASK = /(?:작업|파일|폴더|디렉터리|레포|저장소|커밋|브랜치|\bPR\b|코드|스크립트|테스트|빌드|배포|설치|설정|명령어|로그|버그|이슈)/i;
|
|
23
|
+
const PATH_OR_CODE = /(?:https?:\/\/|www\.|[\w.-]+\/[\w./-]+|\.(?:ts|tsx|js|py|md|json|yaml|swift|kt)\b|[`{};=<>])/i;
|
|
24
|
+
const LIVE = /(?:오늘\s*(?:날짜|며칠|무슨\s*요일)|지금\s*몇\s*시|최신|뉴스|속보|주가|시세|환율|날씨|today'?s|latest|news|stock|weather|right now)/i;
|
|
25
|
+
|
|
26
|
+
export function classifyShortQuestion(text: string, ctx: ShortQuestionContext = {}): ShortQuestionVerdict {
|
|
27
|
+
const q = text.trim();
|
|
28
|
+
if (!q) return { fast: false, reason: 'empty' };
|
|
29
|
+
if (ctx.hasAttachments) return { fast: false, reason: 'attachment' };
|
|
30
|
+
if (/[\r\n\u2028\u2029]/.test(q)) return { fast: false, reason: 'multiline' };
|
|
31
|
+
if ([...q].length > 80) return { fast: false, reason: 'long' };
|
|
32
|
+
if (PATH_OR_CODE.test(q)) return { fast: false, reason: 'path-or-code' };
|
|
33
|
+
if (LIVE.test(q)) return { fast: false, reason: 'live' };
|
|
34
|
+
if (REQUEST_KO.test(q) || REQUEST_EN.test(q)) return { fast: false, reason: 'request' };
|
|
35
|
+
if (TASK.test(q)) return { fast: false, reason: 'task' };
|
|
36
|
+
if (GREETING.test(q)) return { fast: true, reason: 'greeting' };
|
|
37
|
+
const core = q.replace(TAIL, '').trim();
|
|
38
|
+
if (KO_QUESTION_END.test(core) || EN_QUESTION.test(core)) return { fast: true, reason: 'question' };
|
|
39
|
+
return { fast: false, reason: 'not-a-question' };
|
|
40
|
+
}
|
package/src/cli/agent-cli.ts
CHANGED
|
@@ -393,8 +393,8 @@ tierCmd
|
|
|
393
393
|
* When `json` is true, the function suppresses the streaming text +
|
|
394
394
|
* ui.info trailer and instead emits exactly ONE JSON line on stdout
|
|
395
395
|
* at end-of-turn:
|
|
396
|
-
* {sessionId, provider, model, reply,
|
|
397
|
-
*
|
|
396
|
+
* {sessionId, provider, model, reply, finalReply, transcript, logPath,
|
|
397
|
+
* budget, durationMs, ts}
|
|
398
398
|
* Stable shape so LLMs can self-spawn `elanous chat` for follow-ups
|
|
399
399
|
* without parsing human-readable terminal output. */
|
|
400
400
|
export async function runChatTurnCli(opts: {
|
|
@@ -442,12 +442,14 @@ export async function runChatTurnCli(opts: {
|
|
|
442
442
|
dbg.setAmbientSessionId?.(session.id);
|
|
443
443
|
} catch { /* debug module unavailable — fine */ }
|
|
444
444
|
const replyChunks: string[] = [];
|
|
445
|
-
//
|
|
445
|
+
// Tool calls delimit assistant messages. finalReply retains its last non-empty-message meaning.
|
|
446
446
|
let segmentChunks: string[] = [];
|
|
447
447
|
let lastSegment = '';
|
|
448
|
+
let finalMessage = '';
|
|
449
|
+
let sawToolCall = false;
|
|
448
450
|
const closeSegment = (): void => {
|
|
449
|
-
|
|
450
|
-
if (
|
|
451
|
+
finalMessage = segmentChunks.join('').trim();
|
|
452
|
+
if (finalMessage) lastSegment = finalMessage;
|
|
451
453
|
segmentChunks = [];
|
|
452
454
|
};
|
|
453
455
|
if (!opts.json) {
|
|
@@ -528,15 +530,17 @@ export async function runChatTurnCli(opts: {
|
|
|
528
530
|
userText: opts.userText,
|
|
529
531
|
systemPrompt: agentSystemPrompt,
|
|
530
532
|
onDelta: (d) => {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
+
replyChunks.push(d);
|
|
534
|
+
segmentChunks.push(d);
|
|
535
|
+
if (!opts.json && !opts.enableTools) process.stdout.write(d);
|
|
533
536
|
},
|
|
534
537
|
tools: cliToolSpecs,
|
|
535
538
|
dispatchTool,
|
|
536
539
|
...(opts.goalLoop ? { goalLoop: true } : {}),
|
|
537
540
|
onToolCall: (call) => {
|
|
538
|
-
//
|
|
539
|
-
|
|
541
|
+
// The next delta belongs to the next assistant message.
|
|
542
|
+
sawToolCall = true;
|
|
543
|
+
closeSegment();
|
|
540
544
|
if (!opts.json) {
|
|
541
545
|
process.stdout.write(`\n ⏺ ${call.name}(${truncateArgsForLog(call.args)})\n`);
|
|
542
546
|
}
|
|
@@ -555,6 +559,12 @@ export async function runChatTurnCli(opts: {
|
|
|
555
559
|
}
|
|
556
560
|
throw err;
|
|
557
561
|
}
|
|
562
|
+
if (!sawToolCall && replyChunks.length === 0 && result.text) {
|
|
563
|
+
replyChunks.push(result.text);
|
|
564
|
+
segmentChunks.push(result.text);
|
|
565
|
+
if (!opts.json && !opts.enableTools) process.stdout.write(result.text);
|
|
566
|
+
}
|
|
567
|
+
closeSegment();
|
|
558
568
|
if (opts.json) {
|
|
559
569
|
let logPath: string | null = null;
|
|
560
570
|
try {
|
|
@@ -566,11 +576,9 @@ export async function runChatTurnCli(opts: {
|
|
|
566
576
|
sessionId: session.id,
|
|
567
577
|
provider: result.provider,
|
|
568
578
|
model: result.model ?? null,
|
|
569
|
-
reply:
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
// `finalReply` = 마지막 어시스턴트 메시지 본문 — 「최종 답만」이 필요한 소비자용(뒤호환 추가 칸).
|
|
573
|
-
finalReply: (closeSegment(), lastSegment),
|
|
579
|
+
reply: finalMessage,
|
|
580
|
+
transcript: replyChunks.join(''),
|
|
581
|
+
finalReply: lastSegment,
|
|
574
582
|
budget: sessionBudget(session.id),
|
|
575
583
|
durationMs: Date.now() - startedAt,
|
|
576
584
|
logPath,
|
|
@@ -579,7 +587,8 @@ export async function runChatTurnCli(opts: {
|
|
|
579
587
|
await writeStdoutJson(JSON.stringify(out) + '\n');
|
|
580
588
|
return;
|
|
581
589
|
}
|
|
582
|
-
process.stdout.write(
|
|
590
|
+
if (opts.enableTools) process.stdout.write(`${finalMessage}\n`);
|
|
591
|
+
else process.stdout.write('\n');
|
|
583
592
|
ui.info(`[session ${session.id.slice(0, 8)} ${result.provider}${result.model ? '/' + result.model : ''} ${sessionBudget(session.id)}]`);
|
|
584
593
|
}
|
|
585
594
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { decideProviderForConfig, getProviderForConfig, streamLLM, type LLMMessage } from '../llm.js';
|
|
2
|
+
import { getUserConfig } from '../user-config.js';
|
|
3
|
+
import { DEFAULT_TOKEN_BUDGET, trimToBudget } from '../tokens.js';
|
|
4
|
+
|
|
5
|
+
export interface AskBareDeps {
|
|
6
|
+
getUserConfig: typeof getUserConfig;
|
|
7
|
+
decideProviderForConfig: typeof decideProviderForConfig;
|
|
8
|
+
getProviderForConfig: typeof getProviderForConfig;
|
|
9
|
+
streamLLM: typeof streamLLM;
|
|
10
|
+
trimToBudget: typeof trimToBudget;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function askBare({ text, provider, model, deps }: {
|
|
14
|
+
text: string;
|
|
15
|
+
provider?: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
deps: AskBareDeps;
|
|
18
|
+
}): Promise<{ provider: string; model: string; reply: string }> {
|
|
19
|
+
const messages: LLMMessage[] = [{ role: 'user', content: text }];
|
|
20
|
+
if (deps.trimToBudget(messages, DEFAULT_TOKEN_BUDGET).used > DEFAULT_TOKEN_BUDGET) {
|
|
21
|
+
throw new Error(`ask --bare: input exceeds the ${DEFAULT_TOKEN_BUDGET}-token context budget`);
|
|
22
|
+
}
|
|
23
|
+
const cfg = deps.getUserConfig();
|
|
24
|
+
const selectedConfig = provider === undefined ? cfg : {
|
|
25
|
+
...cfg,
|
|
26
|
+
llm: {
|
|
27
|
+
...cfg.llm,
|
|
28
|
+
provider: provider as typeof cfg.llm.provider,
|
|
29
|
+
model: model ?? (provider === cfg.llm.provider ? cfg.llm.model : undefined),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const selected = deps.decideProviderForConfig(selectedConfig, model);
|
|
33
|
+
const chosenProvider = deps.getProviderForConfig(selectedConfig, model);
|
|
34
|
+
let resolvedProvider = chosenProvider.name;
|
|
35
|
+
let resolvedModel = selected.model;
|
|
36
|
+
const reply = await deps.streamLLM(messages, () => {}, {
|
|
37
|
+
...(provider === undefined ? { initialProvider: chosenProvider } : { provider: chosenProvider }),
|
|
38
|
+
...(selected.model === '(none)' ? {} : { model: selected.model }),
|
|
39
|
+
onResolvedProvider: (name, activeModel) => { resolvedProvider = name; resolvedModel = activeModel; },
|
|
40
|
+
});
|
|
41
|
+
return { provider: resolvedProvider, model: resolvedModel, reply };
|
|
42
|
+
}
|