elanous 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-5c05b0da4bc1392c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a67e11ba962337b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-793fa536b3a6ef37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-e297866051595d81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -0
- package/apps/pwa/out/inside/index.txt +31 -0
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -0
- package/apps/pwa/out/outputs/index.txt +30 -0
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -24
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -24
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/context/nightly-condense.yaml +21 -0
- package/graphs/context/recipes.yaml +12 -0
- package/graphs/demo/inside-seed.yaml +20 -0
- package/graphs/demo/recipes.yaml +12 -0
- package/graphs/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/intake/intake-daily.yaml +2 -1
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/ops/secret-exposure.yaml +24 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/video/explainer-line.yaml +2 -2
- package/package.json +2 -1
- package/plugins/card-followup/examples/sample-card.png +0 -0
- package/plugins/elanous-hwp/README.md +25 -0
- package/plugins/elanous-hwp/cli.ts +22 -0
- package/plugins/elanous-hwp/converter.ts +237 -0
- package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
- package/plugins/elanous-hwp/plugin.json +16 -0
- package/plugins/elanous-hwp/plugin.ts +40 -0
- package/scripts/ci-public-export-leak-gate.ts +75 -14
- package/scripts/ci-public-leak-gate.ts +44 -9
- package/scripts/context-hooks/claude-stop.sh +4 -0
- package/scripts/context-hooks/emit.ts +24 -0
- package/scripts/context-hooks/post-tool-use.sh +4 -0
- package/scripts/coord-channel-watch.sh +12 -0
- package/scripts/coord-requests-bridge.ts +143 -0
- package/scripts/field-feed/lib.ts +4 -0
- package/scripts/fleet/node-setup.sh +20 -3
- package/scripts/homebrew-formula-bump.ts +69 -0
- package/scripts/lib/telegram-inject-args.ts +31 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/measure-pod-memory-by-goal.ts +130 -0
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/checklist-gate-node.ts +2 -1
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/gate-node.ts +22 -15
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- package/scripts/release-loop/ops-upgrade-node.ts +47 -6
- package/scripts/release-loop/publish-node.ts +4 -1
- package/scripts/release-loop/tui-node-checks.ts +42 -0
- package/scripts/release-loop/tui-sim-node.ts +32 -8
- package/scripts/release-loop/unattended-release.ts +5 -3
- package/scripts/release-loop/version-node.ts +74 -13
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/telegram-inject.ts +14 -6
- package/scripts/test-diet/node.ts +1 -1
- package/scripts/test-diet/remeasure-list.ts +112 -0
- package/scripts/tui-regress.ts +2 -2
- package/scripts/ux/demo-rehearsal.ts +247 -0
- package/scripts/ux/demo-seed.ts +108 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +80 -0
- package/skills/diagram-master/SKILL.md +366 -0
- package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
- package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
- package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
- package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
- package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
- package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
- package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
- package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
- package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
- package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
- package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
- package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
- package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
- package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
- package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
- package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
- package/skills/diagram-master/references/examples/README.md +69 -0
- package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
- package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
- package/skills/diagram-master/references/pyproject.toml +16 -0
- package/skills/diagram-master/references/quality-checklist.md +33 -0
- package/skills/diagram-master/references/routing-guide.md +164 -0
- package/skills/diagram-master/references/uv.lock +1356 -0
- package/skills/google-workspace/SKILL.md +33 -0
- package/skills/google-workspace/scripts/gws-safe.sh +177 -0
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/lecture-note-digitizer/SKILL.md +926 -0
- package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
- package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
- package/skills/lecture-note-digitizer/references/html-template.md +167 -0
- package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
- package/skills/omni-crawl/SKILL.md +21 -11
- package/skills/omni-crawl/scripts/main.ts +6 -6
- package/skills/omni-crawl/src/router.ts +3 -3
- package/skills/omni-digest/SKILL.md +17 -9
- package/skills/omni-digest/digest.md +2 -2
- package/skills/omni-digest/src/fetch.ts +30 -3
- package/skills/pitch-deck/SKILL.md +2 -2
- package/skills/pitch-deck/template.html +2 -2
- package/skills/youtube-master/SKILL.md +163 -0
- package/skills/youtube-master/package-lock.json +558 -0
- package/skills/youtube-master/package.json +13 -0
- package/skills/youtube-master/references/study-note-template.md +124 -0
- package/skills/youtube-master/scripts/main.ts +435 -0
- package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
- package/skills/youtube-master/src/audio.ts +63 -0
- package/skills/youtube-master/src/domain-staging.ts +50 -0
- package/skills/youtube-master/src/env.ts +36 -0
- package/skills/youtube-master/src/obsidian.ts +155 -0
- package/skills/youtube-master/src/router.ts +110 -0
- package/skills/youtube-master/src/stt.ts +199 -0
- package/skills/youtube-master/src/study-note.ts +175 -0
- package/skills/youtube-master/src/summarize.ts +358 -0
- package/skills/youtube-master/src/transcript.ts +76 -0
- package/skills/youtube-master/src/types.ts +91 -0
- package/skills/youtube-master/src/util.ts +93 -0
- package/skills/youtube-master/src/youtube.ts +70 -0
- package/skills/youtube-master/tsconfig.json +13 -0
- package/src/acp/server.ts +5 -2
- package/src/agent-mission/driver.ts +215 -23
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/pty-control-loop.ts +4 -1
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +5 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/web-search.ts +3 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/index.ts +2 -0
- package/src/cli/drive-input-jail.ts +101 -0
- package/src/cli/hooks-cli.ts +17 -3
- package/src/cli/make-cli.ts +43 -0
- package/src/cli/persona-cli.ts +98 -9
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +36 -9
- package/src/cli/pod-memory-advice.ts +35 -0
- package/src/cli/pr-cli.ts +51 -15
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/release-cli.ts +63 -10
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +65 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +2 -2
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/context-bus/nightly-condense.ts +153 -0
- package/src/context-bus/session-events.ts +35 -0
- package/src/dashboard/dashboard-status-lines.ts +2 -0
- package/src/dashboard/index.ts +17 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +31 -1
- package/src/decisions/decision-cards.ts +10 -3
- package/src/decisions/decision-ledger.ts +13 -5
- package/src/discord/sprint21-runtime.ts +5 -1
- package/src/discord-self-message.ts +32 -0
- package/src/discord-slash-wire.ts +5 -1
- package/src/discord.ts +17 -1
- package/src/domains/core-tools.ts +3 -2
- package/src/domains/outbound-alert.ts +105 -32
- package/src/domains/outbound-log-probe.ts +8 -0
- package/src/domains/telegram-kind-route.ts +51 -13
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +10 -2
- package/src/exec-requests/runner.ts +20 -6
- package/src/exec-requests/store.ts +3 -0
- package/src/execution-loop/launch-gate.ts +109 -0
- package/src/field/field-reel.ts +5 -1
- package/src/field-note/index.ts +201 -0
- package/src/field-note/telegram.ts +142 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-cli.ts +61 -1
- package/src/graph-runner/graph-grow-llm.ts +47 -0
- package/src/graph-runner/graph-grow.ts +50 -0
- package/src/graph-runner/runner.ts +329 -33
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/harness-cli-command.ts +375 -13
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +23 -4
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hooks/consult-intake.ts +153 -0
- package/src/hooks/dispatch.ts +82 -0
- package/src/hooks/expose.ts +254 -0
- package/src/hooks/github-shadow.ts +108 -0
- package/src/hooks/providers.ts +71 -5
- package/src/hooks/queue.ts +1 -1
- package/src/hooks/receiver.ts +94 -16
- package/src/index.ts +76 -14
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/discord-seat-work.ts +38 -2
- package/src/intake-plane/front-route-classifier.ts +33 -39
- package/src/intake-plane/persona-answer.ts +30 -0
- package/src/intake-plane/runtime-callables.ts +8 -1
- package/src/intake-plane/telegram-seat-work.ts +36 -2
- package/src/intake-plane/wish-card.ts +33 -0
- package/src/intake-plane/wish-folder.ts +67 -0
- package/src/intent-gate/gate.ts +30 -10
- package/src/live/detail-switch.ts +5 -0
- package/src/llm/difficulty.ts +57 -0
- package/src/llm/judge-layer.ts +101 -0
- package/src/llm/mission-router.ts +33 -19
- package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
- package/src/loops/contract/examples/steward.loop.yaml +50 -0
- package/src/loops/contract/loop-agent.schema.json +138 -0
- package/src/loops/contract/validate.ts +64 -0
- package/src/loops/registry.ts +20 -6
- package/src/market/signed-index.ts +4 -0
- package/src/maturity/bot-command-maturity.ts +45 -0
- package/src/maturity/feature-maturity.ts +27 -4
- package/src/model-tier/index.ts +2 -2
- package/src/model-tier/tier-resolver.ts +20 -20
- package/src/model-tier/types.ts +6 -6
- package/src/mss/logging/log-store.ts +15 -0
- package/src/nexus/api/config-model-tier.ts +5 -4
- package/src/nexus/api/fabric-plans.ts +53 -0
- package/src/nexus/api/harness-api.ts +5 -1
- package/src/nexus/api/health.ts +2 -0
- package/src/nexus/api/http-server.ts +67 -8
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +21 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-seats.ts +33 -0
- package/src/nexus/api/outbound-report.ts +40 -7
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/personas.ts +95 -53
- package/src/nexus/api/seat-requests.ts +44 -2
- package/src/nexus/api/sessions-store.ts +17 -38
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/workflow-history.ts +88 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +132 -6
- package/src/nexus/index.ts +2 -0
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/outbound/delivery-ledger.ts +17 -8
- package/src/nexus/outbound/router.ts +8 -3
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/org/seat-budget.ts +48 -0
- package/src/org/stall-escalation.ts +63 -0
- package/src/outputs/ledger.ts +120 -0
- package/src/persona/persona-todo.ts +79 -0
- package/src/persona/presets.ts +123 -2
- package/src/persona/team-set.ts +155 -0
- package/src/plugins/install/plugin-install.ts +14 -0
- package/src/plugins/maker/market-bundle.ts +110 -0
- package/src/plugins/maker/plugin-maker.ts +254 -11
- package/src/plugins/maker/wizard-generate.ts +103 -0
- package/src/pod-lease/admission.ts +150 -0
- package/src/pod-lease/dependency-state.ts +85 -0
- package/src/pod-lease/host-lease.ts +127 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +17 -5
- package/src/release-loop/feature-store.ts +29 -13
- package/src/release-loop/landed-but-yellow.ts +48 -0
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/seat-dispatch/seat-ask.ts +130 -0
- package/src/seat-dispatch/seat-questions.ts +100 -0
- package/src/seat-loop/seat-loop.ts +709 -29
- package/src/seat-loop/seat-report.ts +37 -2
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +37 -1
- package/src/self-dev/draft-sweep.ts +79 -40
- package/src/self-dev/draft-triage-rules.ts +62 -7
- package/src/self-dev/fabric-plan-core.ts +297 -0
- package/src/self-dev/orchestrate.ts +36 -4
- package/src/self-dev/release-path-guard.ts +62 -0
- package/src/self-dev/shard-aggregate.ts +230 -0
- package/src/self-dev/shard-placement.ts +60 -0
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-pod-run.ts +77 -0
- package/src/self-dev/shard-run.ts +105 -0
- package/src/self-implement/budget-gate.ts +29 -12
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-candidate-ranking.ts +60 -0
- package/src/self-implement/gate-cli.ts +40 -1
- package/src/self-implement/gate-scope.ts +44 -13
- package/src/self-implement/graph-variant.ts +171 -0
- package/src/self-implement/harness-policy-gates.ts +1 -1
- package/src/self-implement/headless-elanous-driver.ts +27 -3
- package/src/self-implement/host-regate.ts +22 -1
- package/src/self-implement/importer-test-index.ts +2 -1
- package/src/self-implement/l8-merge-queue.ts +130 -0
- package/src/self-implement/orchestrator.ts +257 -9
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +2 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/index.ts +36 -13
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/launch.ts +42 -26
- package/src/steward/steward-cards.ts +17 -9
- package/src/steward/triage.ts +46 -14
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-orchestrator/mission.ts +5 -0
- package/src/task-orchestrator/store.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +43 -17
- package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +222 -40
- package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
- package/src/task-orchestrator/types.ts +2 -0
- package/src/telegram-card-followup.ts +16 -96
- package/src/telegram-commands.ts +31 -1
- package/src/telegram-project-command.ts +145 -0
- package/src/telegram-report.ts +21 -17
- package/src/telegram.ts +61 -8
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/user-config.ts +110 -24
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/grok.ts +7 -1
- package/src/web-search/index.ts +28 -1
- package/src/web-search/provider.ts +4 -0
- package/src/web-search/x-intent.ts +5 -0
- package/src/workflow-runtime/executor.ts +277 -47
- package/src/workflow-runtime/index.ts +1 -0
- package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
- package/src/workflow-runtime/plugin-kind-node.ts +1 -1
- package/src/workflow-runtime/schema.ts +79 -4
- package/src/workflow-runtime/types.ts +35 -4
- package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
- package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
- /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → -6XrAoD3eU7NyMHAE4xPS}/_ssgManifest.js +0 -0
|
@@ -61,6 +61,9 @@ import { executionFooter } from './telegram-exec-footer.js';
|
|
|
61
61
|
import { debug } from './debug/log.js';
|
|
62
62
|
import { makeChunkProducer } from './session/streaming/chunk-producer.js';
|
|
63
63
|
import { getUserConfig } from './user-config.js';
|
|
64
|
+
import { detectCard, runCardFollowup, NotACardError } from './card-followup/core.js';
|
|
65
|
+
import { localOcrText } from './telegram-card-followup.js';
|
|
66
|
+
import type { runGraph } from './graph-runner/runner.js';
|
|
64
67
|
|
|
65
68
|
const SLASH_FOCUS_TURNS_DEFAULT = 8;
|
|
66
69
|
|
|
@@ -135,6 +138,7 @@ export interface DiscordSelfMessageDeps {
|
|
|
135
138
|
* (legacy). */
|
|
136
139
|
questionChannelFor?: (channelId: string) => import('./hitl/question.js').QuestionChannel;
|
|
137
140
|
log?: (msg: string) => void;
|
|
141
|
+
cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
/** Compose the full discord self+interweave onMessage handler. */
|
|
@@ -352,6 +356,34 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
352
356
|
// Voice text commands pass the guild gate for the voice adapter's
|
|
353
357
|
// sake — never route them into the LLM turn (legacy behavior).
|
|
354
358
|
if (/^\/voice-(join|leave|status)\b/.test(ctx.text.trim())) return undefined;
|
|
359
|
+
if (!ctx.text.trim() && ctx.attachments.length === 1 && ctx.attachments[0]!.contentType?.startsWith('image/')) {
|
|
360
|
+
const photo = ctx.attachments[0]!;
|
|
361
|
+
if (detectCard({ width: photo.width, height: photo.height, ocrText: null }).decision !== 'skip') {
|
|
362
|
+
const bot = deps.getBot();
|
|
363
|
+
if (bot) {
|
|
364
|
+
let imagePath: string;
|
|
365
|
+
let ocrText: string | null;
|
|
366
|
+
try {
|
|
367
|
+
imagePath = (await bot.downloadAttachment(photo)).localPath;
|
|
368
|
+
ocrText = await (deps.cardFollowupDeps?.ocrText ?? localOcrText)(imagePath);
|
|
369
|
+
} catch {
|
|
370
|
+
return '명함을 읽지 못했습니다';
|
|
371
|
+
}
|
|
372
|
+
if (detectCard({ width: photo.width, height: photo.height, ocrText }).decision === 'card') {
|
|
373
|
+
try {
|
|
374
|
+
const result = await runCardFollowup({ imagePath, ocrText,
|
|
375
|
+
...(deps.cardFollowupDeps?.runGraph ? { runGraph: deps.cardFollowupDeps.runGraph } : {}),
|
|
376
|
+
...(deps.cardFollowupDeps?.rootDir ? { rootDir: deps.cardFollowupDeps.rootDir() } : {}),
|
|
377
|
+
});
|
|
378
|
+
for (const reply of result.replies) await bot.sendMessage(ctx.channelId, reply);
|
|
379
|
+
return streamer ? '명함 정리를 완료했습니다.' : undefined;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (!(error instanceof NotACardError)) return '명함을 읽지 못했습니다';
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
355
387
|
// S1 session commands (!sessions·!new·!attach) — before delegation
|
|
356
388
|
// and self-turn routing, mirroring telegram's command precedence.
|
|
357
389
|
const sessionReply = handleSessionCommand(ctx);
|
|
@@ -21,6 +21,7 @@ import { makeCommandRest } from './discord/slash-registry.js';
|
|
|
21
21
|
import { normalizeInteractionPayload } from './discord/slash-router.js';
|
|
22
22
|
import type { SlashCommandSchema } from './discord/slash-types.js';
|
|
23
23
|
import { debug } from './debug/log.js';
|
|
24
|
+
import { gateDiscordSchemas, readBotAudience } from './maturity/bot-command-maturity.js';
|
|
24
25
|
import { resolveChannelBotToken } from './channel-bot-token.js';
|
|
25
26
|
|
|
26
27
|
const REST_BASE = 'https://discord.com/api/v10';
|
|
@@ -121,8 +122,11 @@ export function buildDiscordSlashWire(deps: DiscordSlashWireDeps): DiscordSlashW
|
|
|
121
122
|
if (!app.id) { log('[slash] application id unresolved — skip'); return; }
|
|
122
123
|
const guilds = await (await fetchImpl(`${REST_BASE}/users/@me/guilds`, auth)).json() as Array<{ id: string }>;
|
|
123
124
|
const rest = makeCommandRest({ token, ...(deps.__fetchImpl ? { fetchImpl: deps.__fetchImpl } : {}) });
|
|
125
|
+
const audience = readBotAudience((deps.userConfig.raw?.discord as { commandAudience?: unknown } | undefined)?.commandAudience);
|
|
126
|
+
const { schemas, hidden } = gateDiscordSchemas(ELANOUS_SLASH_COMMANDS, audience);
|
|
127
|
+
debug.log('discord.command', 'menu-filtered', { total: ELANOUS_SLASH_COMMANDS.length, shown: schemas.length - hidden, ...audience });
|
|
124
128
|
for (const g of Array.isArray(guilds) ? guilds : []) {
|
|
125
|
-
const registered = await rest.bulkOverwriteGuild(app.id, g.id,
|
|
129
|
+
const registered = await rest.bulkOverwriteGuild(app.id, g.id, schemas);
|
|
126
130
|
log(`[slash] registered ${registered.length} commands in guild ${g.id}`);
|
|
127
131
|
}
|
|
128
132
|
}
|
package/src/discord.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { markdownToTelegramHtml as _unusedMarkdownFn } from './telegram-format.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
24
24
|
import { debug } from './debug/log.js';
|
|
25
25
|
import { getUserConfig } from './user-config.js';
|
|
26
|
+
import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
|
|
26
27
|
import { handleDiscordSeatWork, type DiscordSeatWorkDeps } from './intake-plane/discord-seat-work.js';
|
|
27
28
|
import { parseSeatAddress } from './seat-address/seat-address.js';
|
|
28
29
|
|
|
@@ -226,6 +227,7 @@ export class DiscordBot {
|
|
|
226
227
|
private ws: WebSocket | null = null;
|
|
227
228
|
private running = false;
|
|
228
229
|
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
230
|
+
private seatAskTimer: ReturnType<typeof setInterval> | null = null;
|
|
229
231
|
private lastHeartbeatAck = true;
|
|
230
232
|
private lastSeq: number | null = null;
|
|
231
233
|
|
|
@@ -246,7 +248,15 @@ export class DiscordBot {
|
|
|
246
248
|
this.onInteraction = opts.onInteraction ?? null;
|
|
247
249
|
this.onReaction = opts.onReaction ?? null;
|
|
248
250
|
this.onTriggerTap = opts.onTriggerTap ?? null;
|
|
249
|
-
this.seatWorkDeps = opts.seatWorkDeps
|
|
251
|
+
this.seatWorkDeps = { ...opts.seatWorkDeps, askDeps: {
|
|
252
|
+
...opts.seatWorkDeps?.askDeps,
|
|
253
|
+
channel: 'discord',
|
|
254
|
+
send: opts.seatWorkDeps?.askDeps?.send ?? (async (origin, text) => {
|
|
255
|
+
if (origin.channel !== 'discord') throw new Error('seat ask belongs to another surface');
|
|
256
|
+
const posted = await this.sendMessage(origin.threadId ?? origin.channelId, text);
|
|
257
|
+
if (!posted) throw new Error('Discord seat ask reply was not posted');
|
|
258
|
+
}),
|
|
259
|
+
} };
|
|
250
260
|
}
|
|
251
261
|
|
|
252
262
|
/** Send a raw payload on the gateway WebSocket. Voice adapter uses
|
|
@@ -523,8 +533,12 @@ export class DiscordBot {
|
|
|
523
533
|
async start(): Promise<void> {
|
|
524
534
|
this.running = true;
|
|
525
535
|
this.log(`discord bot starting (allowlist size ${this.allowedUsers.size})`);
|
|
536
|
+
this.seatAskTimer = setInterval(() => {
|
|
537
|
+
void deliverSeatAnswers(this.seatWorkDeps.askDeps!).catch((error) => this.log(`seat ask delivery failed: ${String(error)}`));
|
|
538
|
+
}, 10_000);
|
|
526
539
|
while (this.running) {
|
|
527
540
|
try {
|
|
541
|
+
await deliverSeatAnswers(this.seatWorkDeps.askDeps!);
|
|
528
542
|
await this.runConnection();
|
|
529
543
|
} catch (err: any) {
|
|
530
544
|
this.log(`gateway error: ${err?.message ?? String(err)}`);
|
|
@@ -538,6 +552,8 @@ export class DiscordBot {
|
|
|
538
552
|
|
|
539
553
|
stop(): void {
|
|
540
554
|
this.running = false;
|
|
555
|
+
if (this.seatAskTimer) clearInterval(this.seatAskTimer);
|
|
556
|
+
this.seatAskTimer = null;
|
|
541
557
|
if (this.ws) {
|
|
542
558
|
try { this.ws.close(1000, 'client stop'); } catch { /* ignore */ }
|
|
543
559
|
this.ws = null;
|
|
@@ -15,7 +15,7 @@ import { existsSync } from 'node:fs';
|
|
|
15
15
|
import { Database } from 'bun:sqlite';
|
|
16
16
|
import type { LLMToolSpec } from '../llm.js';
|
|
17
17
|
import { debug } from '../debug/log.js';
|
|
18
|
-
import { runRecall, type RecallSearch } from '../tool-runtime/self-cognition-runtimes.js';
|
|
18
|
+
import { runRecall, CONTEXT_NOW_SPEC, type RecallSearch } from '../tool-runtime/self-cognition-runtimes.js';
|
|
19
19
|
import type { ToolRuntimeContext } from '../tool-runtime/types.js';
|
|
20
20
|
import { SCHEDULE_MANAGE_SPEC, dispatchScheduleManage } from './schedule-manage-tool.js';
|
|
21
21
|
import { SESSION_MANAGE_SPEC, dispatchSessionQuery } from './session-query-tool.js';
|
|
@@ -114,6 +114,7 @@ const CORE_TOOL_HANDLERS: Record<string, (args: Record<string, unknown>, onParti
|
|
|
114
114
|
schedule_manage: dispatchScheduleManage,
|
|
115
115
|
session_manage: (args) => dispatchSessionQuery(args), // 대화 세션 검색·열람·목록·삭제(내용/ID/텔레그램)
|
|
116
116
|
memory_recall: dispatchMemoryRecall,
|
|
117
|
+
context_now: async (args) => (await import('../context-bus/context-now.js')).contextNow({ ...(typeof args.topic === 'string' ? { topic: args.topic } : {}) }),
|
|
117
118
|
fact_check: dispatchFactCheck, // 팩트체크 캐스케이드(내부 발송원장→외부 X/레딧/웹)·도메인 무관 코어
|
|
118
119
|
self_recall: dispatchSelfRecall, // self-awareness(elanous 구현 이력)·도메인 무관 코어
|
|
119
120
|
autopilot_missions: dispatchAutopilotMissions, // 오토파일럿 계보 조회(AL3)·도메인 무관 코어
|
|
@@ -141,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
|
|
|
141
142
|
},
|
|
142
143
|
},
|
|
143
144
|
};
|
|
144
|
-
export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
|
|
145
|
+
export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
|
|
145
146
|
|
|
146
147
|
export interface CoreTools {
|
|
147
148
|
specs: LLMToolSpec[];
|
|
@@ -17,6 +17,8 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
|
17
17
|
import { resolveChannelBotToken } from '../channel-bot-token.js';
|
|
18
18
|
import { isOperationalKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './telegram-kind-route.js';
|
|
19
19
|
import { debug } from '../debug/log.js';
|
|
20
|
+
import { registerLogStoreSink, setLogInstanceName } from '../mss/logging/log-store.js';
|
|
21
|
+
import type { LogSink } from '../mss/logging/sink.js';
|
|
20
22
|
import { resolveDaemonEndpoint } from '../nexus/daemon-endpoint.js';
|
|
21
23
|
import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
|
|
22
24
|
// ★ origin 되돌림(대표 2026-07-12) — 미션 알림을 발신 채널(메인 Q&A 봇)로 되돌린다. type-only
|
|
@@ -36,6 +38,29 @@ function nexusUrl(): string | null {
|
|
|
36
38
|
const ACP_TOKEN_PATH = join(getElanousConfigDir(), 'acp-token');
|
|
37
39
|
const DEFERRED_PATH = conatusPath('outbound_deferred.jsonl');
|
|
38
40
|
|
|
41
|
+
// CLI/cron do not inherit the daemon's StoreSink. Register once before the first
|
|
42
|
+
// send, but never attach a second sink to a daemon which already owns one.
|
|
43
|
+
let outboundLogSink: (() => void) | null = null;
|
|
44
|
+
function ensureOutboundLogSink(): void {
|
|
45
|
+
if (outboundLogSink || inProcessOutbound || process.env.NODE_ENV === 'test') return;
|
|
46
|
+
try {
|
|
47
|
+
const logs = getUserConfig().logs;
|
|
48
|
+
setLogInstanceName(logs.instanceName);
|
|
49
|
+
outboundLogSink = registerLogStoreSink((s: LogSink) => debug.registerSink(s), 'outbound', logs.retention);
|
|
50
|
+
if (outboundLogSink) process.once('exit', outboundLogSink);
|
|
51
|
+
} catch { /* observations must not prevent delivery */ }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function botLabel(token: string): string {
|
|
55
|
+
// Telegram bot IDs are public numeric IDs; never log the token's secret suffix.
|
|
56
|
+
const id = token.split(':', 1)[0];
|
|
57
|
+
return /^\d+$/.test(id ?? '') ? `telegram:${id}` : 'telegram:configured';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function safeObservationLabel(value: string): string {
|
|
61
|
+
return /^[a-zA-Z0-9_./-]{1,160}$/.test(value) ? value : 'unknown';
|
|
62
|
+
}
|
|
63
|
+
|
|
39
64
|
// ── 발송 관측 (대표 지시 2026-07-15): 발송 시각·mode·밀림(burst/lag) 를 logs.db 에 남겨
|
|
40
65
|
// "실시간인지 밀린 것인지" 를 `elanous logs --category outbound.send` 로 판단 가능하게. ──
|
|
41
66
|
/** 밀림(버스트) 판정 — 최근 창 내 이 수 이상 발송이 몰리면 밀려 나가는 중으로 본다. */
|
|
@@ -61,7 +86,7 @@ function logSend(
|
|
|
61
86
|
extra: Record<string, unknown> = {},
|
|
62
87
|
opts?: { level?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'critical' },
|
|
63
88
|
): void {
|
|
64
|
-
try { debug.log('outbound.send', mode, { kind, ...extra }, opts); } catch { /* fail-open */ }
|
|
89
|
+
try { debug.log('outbound.send', mode, { kind: safeObservationLabel(kind), ...extra }, opts); } catch { /* fail-open */ }
|
|
65
90
|
}
|
|
66
91
|
|
|
67
92
|
/** 밀림 경고 임계 env 이름 — 비교 값은 여기서만 읽는다(호출부에 리터럴을 박지 않는다). */
|
|
@@ -143,12 +168,13 @@ function resolveBotToken(botId?: string): string | null {
|
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
/** origin(발신 채널)으로 직접 발송. 실패하면 호출자가 report 폴백/재시도한다. */
|
|
146
|
-
function deliverToOrigin(origin: MissionOrigin, text: string): boolean {
|
|
171
|
+
function deliverToOrigin(origin: MissionOrigin, text: string, onBot?: (bot: string) => void): boolean {
|
|
147
172
|
if (origin.channel === 'discord') {
|
|
148
173
|
if (!origin.channelId) return false;
|
|
149
174
|
try {
|
|
150
175
|
const token = resolveChannelBotToken('discord', getUserConfig())?.token;
|
|
151
176
|
if (!token) return false;
|
|
177
|
+
onBot?.('discord:configured');
|
|
152
178
|
const channelId = origin.discordThreadId || origin.channelId;
|
|
153
179
|
const out = spillLongContent(text).text;
|
|
154
180
|
for (let start = 0; start < out.length;) {
|
|
@@ -168,19 +194,21 @@ function deliverToOrigin(origin: MissionOrigin, text: string): boolean {
|
|
|
168
194
|
if (origin.channel !== 'telegram' || origin.chatId == null) return false;
|
|
169
195
|
const token = resolveBotToken(origin.botId);
|
|
170
196
|
if (!token) return false;
|
|
197
|
+
onBot?.(botLabel(token));
|
|
171
198
|
try { return sendTelegramRaw(token, origin.chatId, text, origin.threadId); } catch { return false; }
|
|
172
199
|
}
|
|
173
200
|
|
|
174
201
|
/** 보류분 일괄 발송 — 묶음 1건(아침 폭주 방지). 발송 건수 반환.
|
|
175
202
|
* 무음 창 밖 첫 sendOutbound 가 자동 호출 + 06:31 플러시 크론이 보장. */
|
|
176
203
|
export function flushDeferred(path = DEFERRED_PATH): number {
|
|
204
|
+
ensureOutboundLogSink();
|
|
177
205
|
const observeFlush = (count: number, lagMin: number, kinds: string[]): void => {
|
|
178
206
|
const lagWarnMin = flushLagWarnMin();
|
|
179
207
|
const over = count > 0 && lagMin > lagWarnMin;
|
|
180
208
|
logSend(
|
|
181
209
|
'flush',
|
|
182
210
|
'deferred-batch',
|
|
183
|
-
{ count, lagMin, kinds, path, lagWarnMin },
|
|
211
|
+
{ count, lagMin, kinds: kinds.map(safeObservationLabel), path, lagWarnMin },
|
|
184
212
|
over ? { level: 'warn' } : undefined,
|
|
185
213
|
);
|
|
186
214
|
};
|
|
@@ -209,14 +237,28 @@ export function flushDeferred(path = DEFERRED_PATH): number {
|
|
|
209
237
|
...list.map(i => `\n── [${kst(i.ts)} · ${i.kind}] ──\n${i.text}`),
|
|
210
238
|
].join('\n');
|
|
211
239
|
observeFlush(items.length, lagMin, [...new Set(items.map(i => i.kind))]);
|
|
240
|
+
const sendBatch = (text: string, kind: string, origin?: MissionOrigin): boolean => {
|
|
241
|
+
let bot = 'unknown';
|
|
242
|
+
const path = origin
|
|
243
|
+
? (deliverToOrigin(origin, text, label => { bot = label; }) ? 'origin' : false)
|
|
244
|
+
: deliver(text, kind, label => { bot = label; }, 'deferred-flush');
|
|
245
|
+
if (path === 'daemon') return true;
|
|
246
|
+
try {
|
|
247
|
+
debug.log('outbound.send', path ? 'sent' : 'failed', {
|
|
248
|
+
kind: safeObservationLabel(kind), source: 'deferred-flush', bot,
|
|
249
|
+
chars: text.length, path: path || 'undeliverable',
|
|
250
|
+
});
|
|
251
|
+
} catch { /* observation must not change delivery */ }
|
|
252
|
+
return path !== false;
|
|
253
|
+
};
|
|
212
254
|
let delivered = 0;
|
|
213
|
-
// origin 없는
|
|
255
|
+
// origin 없는 매매 알림만 report 묶음. 그 외는 kind 별로 운영 봇 경계를 유지한다.
|
|
214
256
|
const noOrigin = items.filter(i => !i.origin);
|
|
215
257
|
const tradingBatch = noOrigin.filter(i => !isOperationalKind(i.kind));
|
|
216
|
-
if (tradingBatch.length &&
|
|
258
|
+
if (tradingBatch.length && sendBatch(fmt(tradingBatch), 'report')) delivered += tradingBatch.length;
|
|
217
259
|
for (const kind of new Set(noOrigin.filter(i => isOperationalKind(i.kind)).map(i => i.kind))) {
|
|
218
260
|
const batch = noOrigin.filter(i => i.kind === kind);
|
|
219
|
-
if (
|
|
261
|
+
if (sendBatch(fmt(batch), kind)) delivered += batch.length;
|
|
220
262
|
}
|
|
221
263
|
// origin 있는 것 = 발신 채널·스레드별로 묶어 서로 다른 수신자에게 섞이지 않게 발송.
|
|
222
264
|
const groups = new Map<string, { origin: MissionOrigin; list: Item[] }>();
|
|
@@ -228,7 +270,10 @@ export function flushDeferred(path = DEFERRED_PATH): number {
|
|
|
228
270
|
if (!groups.has(key)) groups.set(key, { origin: it.origin, list: [] });
|
|
229
271
|
groups.get(key)!.list.push(it);
|
|
230
272
|
}
|
|
231
|
-
for (const g of groups.values()) {
|
|
273
|
+
for (const g of groups.values()) {
|
|
274
|
+
const kinds = new Set(g.list.map(i => i.kind));
|
|
275
|
+
if (sendBatch(fmt(g.list), kinds.size === 1 ? g.list[0]!.kind : 'mixed', g.origin)) delivered += g.list.length;
|
|
276
|
+
}
|
|
232
277
|
// 전량 배달 성공 시에만 파일 제거(부분 실패는 다음 flush 재시도 — 성공분 중복은 드문 엣지 수용).
|
|
233
278
|
if (delivered >= items.length) { try { unlinkSync(path); } catch { /* */ } }
|
|
234
279
|
return delivered;
|
|
@@ -268,17 +313,19 @@ export function sendOutbound(text: string, kind = 'alert', origin?: (MissionOrig
|
|
|
268
313
|
const source = origin?.surface || (callerPath
|
|
269
314
|
? relative(repoRoot, callerPath.startsWith('file://') ? fileURLToPath(callerPath) : callerPath).replace(/\\/g, '/')
|
|
270
315
|
: 'unknown');
|
|
271
|
-
|
|
272
|
-
|
|
316
|
+
ensureOutboundLogSink();
|
|
317
|
+
let bot = origin?.channel === 'discord' ? 'discord:configured' : 'unknown';
|
|
318
|
+
const logOutcome = (event: 'sent' | 'deferred' | 'failed', path?: string): void => {
|
|
319
|
+
try { debug.log('outbound.send', event, { kind: safeObservationLabel(kind), source: safeObservationLabel(source), chars: text.length, bot, ...(path ? { path } : {}) }); } catch { /* fail-open */ }
|
|
273
320
|
};
|
|
274
321
|
// ★ 무음 우회(대표 2026-07-14) — 사용자가 최근(기본 30분) genuine 인텐트(타이핑/버튼탭)를 냈으면
|
|
275
322
|
// 깨어있으므로 야간 무음이어도 즉시 발송(사용자 발원 흐름의 결과물이 아침까지 묶이지 않게).
|
|
276
323
|
// 우회 시 flushDeferred 로 그간 보류분도 함께 전달(사용자가 지금 볼 수 있음).
|
|
277
324
|
if (inQuietHours() && !userRecentlyActive()) {
|
|
278
325
|
const queued = deferOutbound(text, kind, origin);
|
|
279
|
-
recordOutbound(text, kind); //
|
|
280
|
-
logOutcome(queued ? 'deferred' : 'failed');
|
|
281
|
-
return
|
|
326
|
+
if (queued) recordOutbound(text, kind); // 성공한 보류만 회상 대상
|
|
327
|
+
logOutcome(queued ? 'deferred' : 'failed', queued ? 'deferred' : 'queue-failed');
|
|
328
|
+
return queued; // 적재가 성공했을 때만 보류 수락
|
|
282
329
|
}
|
|
283
330
|
const bypass = inQuietHours();
|
|
284
331
|
if (bypass) console.error('[outbound] 야간 무음 우회 — 최근 사용자 활동(깨어있음) → 즉시 발송 + 보류분 flush');
|
|
@@ -288,16 +335,15 @@ export function sendOutbound(text: string, kind = 'alert', origin?: (MissionOrig
|
|
|
288
335
|
const { recentCount, burst } = recentSendBurst();
|
|
289
336
|
logSend(bypass ? 'quiet-bypass' : 'realtime', kind, { burst, recentCount, ...(burst ? { backlog: true } : {}) });
|
|
290
337
|
// ★ origin 되돌림(무음 밖) — 발신 채널로 직접 발송. 성공 시 종료, 실패면 report 폴백.
|
|
291
|
-
if (origin && deliverToOrigin(origin, text)) {
|
|
338
|
+
if (origin && deliverToOrigin(origin, text, (label) => { bot = label; })) {
|
|
292
339
|
recordOutbound(text, kind);
|
|
293
|
-
logOutcome('sent');
|
|
340
|
+
logOutcome('sent', 'origin');
|
|
294
341
|
return true;
|
|
295
342
|
}
|
|
296
|
-
const path = deliver(text, kind);
|
|
297
|
-
//
|
|
298
|
-
// 가 기록하므로 클라는 중복 금지 — 직접 폴백(direct·데몬 다운)만 클라가 기록.
|
|
343
|
+
const path = deliver(text, kind, (label) => { bot = label; }, source);
|
|
344
|
+
// 데몬 경유는 실제 라우터의 최종 결과만 기록한다. 직접 폴백과 미전달만 여기서 기록.
|
|
299
345
|
if (path === 'direct') recordOutbound(text, kind);
|
|
300
|
-
logOutcome(path === false ? 'failed' : 'sent');
|
|
346
|
+
if (path !== 'daemon') logOutcome(path === false ? 'failed' : 'sent', path === false ? 'undeliverable' : path);
|
|
301
347
|
return path !== false;
|
|
302
348
|
}
|
|
303
349
|
|
|
@@ -318,7 +364,14 @@ export function classifyDaemonResponse(j: unknown): DaemonPathClass {
|
|
|
318
364
|
/** 데몬 경로를 못 쓴 이유를 남긴다. 관측 실패가 발송을 막지 않음(recordOutbound 과 같은 fail-soft).
|
|
319
365
|
* 비-ok 분류는 크론 운영자가 읽는 표준 출력에도 한 줄 — 싱크 미등록 스크립트에서도 즉시 보이게. */
|
|
320
366
|
function logDaemonPath(classification: DaemonPathClass, kind: string, extra: Record<string, unknown> = {}): void {
|
|
321
|
-
|
|
367
|
+
ensureOutboundLogSink();
|
|
368
|
+
try {
|
|
369
|
+
debug.log('outbound.send', 'daemon-path', {
|
|
370
|
+
classification, kind: safeObservationLabel(kind),
|
|
371
|
+
...('hasToken' in extra ? { hasToken: extra.hasToken === true } : {}),
|
|
372
|
+
...('inProcess' in extra ? { inProcess: extra.inProcess === true } : {}),
|
|
373
|
+
});
|
|
374
|
+
} catch { /* fail-soft */ }
|
|
322
375
|
if (classification === 'ok') return;
|
|
323
376
|
try { console.error(`[outbound] daemon-path ${classification}`); } catch { /* fail-soft */ }
|
|
324
377
|
}
|
|
@@ -326,23 +379,33 @@ function logDaemonPath(classification: DaemonPathClass, kind: string, extra: Rec
|
|
|
326
379
|
/** OB8 — the daemon registers its own in-process sender at startup. Inside the daemon, deliver() must never curl
|
|
327
380
|
* its own `/v1/outbound`: the curl is synchronous, the event loop that would answer it is the one it blocks, and the
|
|
328
381
|
* whole daemon freezes until the curl times out (~25 s · `unreachable`) — the message is lost (CS1 · 10-01). */
|
|
329
|
-
export type InProcessOutbound = (text: string, kind: string) => Promise<boolean>;
|
|
382
|
+
export type InProcessOutbound = (text: string, kind: string, source?: string, deferFailureObservation?: boolean) => Promise<boolean>;
|
|
330
383
|
let inProcessOutbound: InProcessOutbound | null = null;
|
|
331
384
|
export function setInProcessOutbound(send: InProcessOutbound | null): void { inProcessOutbound = send; }
|
|
332
385
|
|
|
333
386
|
/** elanous `/v1/outbound` 우선 → 실패 시 텔레그램 직접. 성공 경로 반환(원장 중복방지용). */
|
|
334
|
-
export function deliver(text: string, kind = 'alert'): 'daemon' | 'direct' | false {
|
|
387
|
+
export function deliver(text: string, kind = 'alert', onBot?: (bot: string) => void, source?: string): 'daemon' | 'direct' | false {
|
|
335
388
|
// 0) inside the daemon — route in-process, asynchronously; the caller's synchronous answer is «accepted».
|
|
336
389
|
if (inProcessOutbound && process.env.SEND_VIA_ELANOUS !== '0') {
|
|
390
|
+
onBot?.('daemon:configured');
|
|
337
391
|
const send = inProcessOutbound;
|
|
338
|
-
|
|
392
|
+
const fallback = (): void => {
|
|
393
|
+
let bot = 'unknown';
|
|
394
|
+
const ok = sendTelegramDirect(text, kind, {}, label => { bot = label; });
|
|
395
|
+
if (ok) recordOutbound(text, kind);
|
|
396
|
+
try { debug.log('outbound.send', ok ? 'sent' : 'failed', {
|
|
397
|
+
kind: safeObservationLabel(kind), source: safeObservationLabel(source ?? 'daemon-fallback'),
|
|
398
|
+
bot, path: ok ? 'direct' : 'undeliverable', chars: text.length,
|
|
399
|
+
}); } catch { /* fail-open */ }
|
|
400
|
+
};
|
|
401
|
+
void send(text, kind, source, true)
|
|
339
402
|
.then((ok) => {
|
|
340
403
|
logDaemonPath(ok ? 'ok' : 'rejected', kind, { inProcess: true });
|
|
341
|
-
if (!ok
|
|
404
|
+
if (!ok) fallback();
|
|
342
405
|
})
|
|
343
|
-
.catch((
|
|
344
|
-
logDaemonPath('rejected', kind, { inProcess: true
|
|
345
|
-
|
|
406
|
+
.catch(() => {
|
|
407
|
+
logDaemonPath('rejected', kind, { inProcess: true });
|
|
408
|
+
fallback();
|
|
346
409
|
});
|
|
347
410
|
return 'daemon';
|
|
348
411
|
}
|
|
@@ -351,20 +414,22 @@ export function deliver(text: string, kind = 'alert'): 'daemon' | 'direct' | fal
|
|
|
351
414
|
if (process.env.SEND_VIA_ELANOUS !== '0') {
|
|
352
415
|
let token = '';
|
|
353
416
|
try { if (existsSync(ACP_TOKEN_PATH)) token = readFileSync(ACP_TOKEN_PATH, 'utf-8').trim(); } catch { /* no token */ }
|
|
354
|
-
const headers = ['Content-Type: application/json',
|
|
417
|
+
const headers = ['Content-Type: application/json', 'X-Elanous-Client-Fallback: direct',
|
|
418
|
+
...(source ? [`X-Elanous-Outbound-Source: ${safeObservationLabel(source)}`] : []),
|
|
419
|
+
...(token ? [`Authorization: Bearer ${token}`] : [])];
|
|
355
420
|
const nexus = nexusUrl();
|
|
356
421
|
const j = nexus
|
|
357
422
|
? curlPost(`${nexus}/v1/outbound`, JSON.stringify({ text, markdown: false, kind }), headers)
|
|
358
423
|
: null;
|
|
359
424
|
const classification = classifyDaemonResponse(j);
|
|
360
|
-
if (classification === 'ok') return 'daemon';
|
|
425
|
+
if (classification === 'ok') { onBot?.('daemon:configured'); return 'daemon'; }
|
|
361
426
|
daemonPath = nexus ? classification : 'not-found';
|
|
362
427
|
const extra: Record<string, unknown> = { hasToken: token.length > 0 };
|
|
363
428
|
if (j && typeof j === 'object' && 'error' in (j as object)) extra.error = (j as { error?: unknown }).error;
|
|
364
429
|
logDaemonPath(classification, kind, extra);
|
|
365
430
|
}
|
|
366
431
|
// 2) fallback: 텔레그램 sendMessage 직접(3900자 분할) — 데몬 미경유라 클라가 원장 기록.
|
|
367
|
-
if (sendTelegramDirect(text, kind)) return 'direct';
|
|
432
|
+
if (sendTelegramDirect(text, kind, {}, onBot)) return 'direct';
|
|
368
433
|
reportUndeliverable(kind, daemonPath);
|
|
369
434
|
return false;
|
|
370
435
|
}
|
|
@@ -379,7 +444,8 @@ function reportUndeliverable(kind: string, daemonPath: DaemonPathClass | 'not-fo
|
|
|
379
444
|
root = effectiveInstanceRoot();
|
|
380
445
|
universe = root === prodInstanceRoot() ? 'prod' : 'test';
|
|
381
446
|
} catch { /* the report still goes out */ }
|
|
382
|
-
|
|
447
|
+
ensureOutboundLogSink();
|
|
448
|
+
try { debug.log('outbound.send', 'undeliverable', { kind: safeObservationLabel(kind), daemonPath, universe, root }); } catch { /* fail-soft */ }
|
|
383
449
|
const hint = universe === 'prod'
|
|
384
450
|
? '운영 데몬이 떠 있는지 확인: elanous nexus show'
|
|
385
451
|
: '운영으로 보내려면 설치본 elanous 로 실행하거나 ELANOUS_STATE_DIR=~/.elanous ELANOUS_CONFIG_DIR=~/.elanous 를 준다';
|
|
@@ -411,6 +477,7 @@ function sendTelegramRaw(token: string, chatId: string | number, text: string, t
|
|
|
411
477
|
export function sendTelegramDirect(
|
|
412
478
|
text: string, kind?: string,
|
|
413
479
|
deps: { config?: UserConfig; sendRaw?: typeof sendTelegramRaw; legacyEnv?: typeof conatusEnv } = {},
|
|
480
|
+
onBot?: (bot: string) => void,
|
|
414
481
|
): boolean {
|
|
415
482
|
const sendRaw = deps.sendRaw ?? sendTelegramRaw;
|
|
416
483
|
let cfg: UserConfig | undefined;
|
|
@@ -418,8 +485,13 @@ export function sendTelegramDirect(
|
|
|
418
485
|
if (cfg) {
|
|
419
486
|
try {
|
|
420
487
|
const target = kindRouteTarget(cfg, kind);
|
|
421
|
-
if (target) return sendRaw(target.botToken, String(target.chatId), text);
|
|
488
|
+
if (target) { onBot?.(botLabel(target.botToken)); return sendRaw(target.botToken, String(target.chatId), text); }
|
|
422
489
|
} catch { /* unavailable channel routing: fall through */ }
|
|
490
|
+
// A declared table is authoritative: missing roles cannot escape to an env bot.
|
|
491
|
+
if (cfg.telegram.channels?.length) {
|
|
492
|
+
logKindRouteFallback(kind, 'none', false);
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
423
495
|
}
|
|
424
496
|
if (isOperationalKind(kind)) {
|
|
425
497
|
let home: ReturnType<typeof mainHomeTarget> = null;
|
|
@@ -432,7 +504,7 @@ export function sendTelegramDirect(
|
|
|
432
504
|
} catch { /* unavailable main bot */ }
|
|
433
505
|
const rcToken = cfg?.telegram.reportChannel?.botToken;
|
|
434
506
|
logKindRouteFallback(kind, home ? 'main-home' : 'none', !!mainToken && !!cfg?.telegram.reportChannel && (!rcToken || rcToken === mainToken));
|
|
435
|
-
if (home) return sendRaw(home.botToken, String(home.chatId), text);
|
|
507
|
+
if (home) { onBot?.(botLabel(home.botToken)); return sendRaw(home.botToken, String(home.chatId), text); }
|
|
436
508
|
console.error('[outbound] 운영 kind 메인 봇/홈 미설정 — 콘솔 출력\n' + text);
|
|
437
509
|
return false;
|
|
438
510
|
}
|
|
@@ -445,5 +517,6 @@ export function sendTelegramDirect(
|
|
|
445
517
|
}
|
|
446
518
|
logKindRouteFallback(kind, tok && chat ? 'conatus-env' : 'none', false);
|
|
447
519
|
if (!tok || !chat) { console.error('[outbound] 토큰/chat 미설정 — 콘솔 출력\n' + text); return false; }
|
|
520
|
+
onBot?.(botLabel(tok));
|
|
448
521
|
return sendRaw(tok, chat, text);
|
|
449
522
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { sendOutbound, setInProcessOutbound } from './outbound-alert.js';
|
|
2
|
+
import { setUserConfigOverlay } from '../user-config.js';
|
|
3
|
+
|
|
4
|
+
// Focused subprocess fixture: isolated state/config roots and a fake curl on PATH.
|
|
5
|
+
setUserConfigOverlay(cfg => ({ ...cfg, telegram: { ...cfg.telegram, botToken: '123456:private-secret', homeChannel: 98765 } }));
|
|
6
|
+
process.env.SEND_VIA_ELANOUS = '0';
|
|
7
|
+
setInProcessOutbound(null);
|
|
8
|
+
if (!sendOutbound('SECRET-BODY-DO-NOT-LOG', 'ops-alert', { channel: 'cli', surface: process.env.OUTBOUND_PROBE_SOURCE ?? 'outbound-log-probe' })) process.exitCode = 1;
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
// 발송 목적(kind) → 텔레그램 채널 역할 · 대상. 가벼운 모듈(telegram.ts 를 import 하지 않는다 — outbound-alert 순환 방지).
|
|
2
|
-
//
|
|
2
|
+
// 명시적으로 매매 kind 인 경우만 trading; 나머지는 system. 역할 채널이 없으면 다른 역할로 폴백하지 않는다.
|
|
3
3
|
import type { UserConfig } from '../user-config.js';
|
|
4
4
|
import { resolveChannelBotToken } from '../channel-bot-token.js';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
|
-
import {
|
|
6
|
+
import { resolveTelegramChannels } from './telegram-channels.js';
|
|
7
7
|
|
|
8
8
|
export const DEFAULT_KIND_ROLES: Readonly<Record<string, string>> = {
|
|
9
|
+
report: 'report', alert: 'report', digest: 'report',
|
|
9
10
|
intake: 'system', 'ops-report': 'system', 'ops-alert': 'system', 'ops-health': 'system',
|
|
10
|
-
regression: 'system', 'agent-mission': 'system',
|
|
11
|
+
regression: 'system', 'agent-mission': 'system', brief: 'system', 'op-report': 'system',
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
/**
|
|
14
|
+
/** Only named finance delivery kinds can use the trading/report bot. */
|
|
15
|
+
export function isTradingKind(kind?: string): boolean {
|
|
16
|
+
return kind === 'report' || kind === 'alert' || kind === 'digest';
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
export function isOperationalKind(kind?: string): boolean {
|
|
15
|
-
return kind
|
|
20
|
+
return !isTradingKind(kind);
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
export function mainHomeTarget(cfg: UserConfig): { botToken: string; chatId: number } | null {
|
|
19
|
-
const botToken =
|
|
24
|
+
const botToken = operationsBotToken(cfg);
|
|
20
25
|
const chatId = cfg.telegram.homeChannel ?? cfg.telegram.allowedUsers[0];
|
|
21
26
|
return botToken && chatId != null && Number.isFinite(chatId) ? { botToken, chatId } : null;
|
|
22
27
|
}
|
|
@@ -25,15 +30,48 @@ export function logKindRouteFallback(kind: string | undefined, to: 'report-chann
|
|
|
25
30
|
try { debug.log('telegram.kind-route', 'fallback', { kind, to, sameBot }); } catch { /* logging must not prevent delivery */ }
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
function operationsBotToken(cfg: UserConfig): string | undefined {
|
|
34
|
+
const main = resolveChannelBotToken('telegram', cfg);
|
|
35
|
+
const channels = cfg.telegram.channels?.length ? resolveTelegramChannels(cfg.telegram) : [];
|
|
36
|
+
const tradingTokens = new Set(channels.filter(c => c.roles.includes('report')).map(c => c.botToken));
|
|
37
|
+
// The report (trading) bot is never the operations bot — with or without a channels table (review must-fix · OUT1).
|
|
38
|
+
if (cfg.telegram.reportChannel?.botToken) tradingTokens.add(cfg.telegram.reportChannel.botToken);
|
|
39
|
+
if (main.source !== 'channels.main') return main.token && !tradingTokens.has(main.token) ? main.token : undefined;
|
|
40
|
+
// channels.main may mean "first channel", which can be the trading bot.
|
|
41
|
+
// Only a named operations channel (or the named main channel) with an
|
|
42
|
+
// operational role establishes identity; array order and role alone do not.
|
|
43
|
+
const tokens = new Set(channels
|
|
44
|
+
.filter(c => (c.name === 'ops' && c.roles.includes('system'))
|
|
45
|
+
|| (c.name === 'main' && c.roles.some(r => r === 'system' || r === 'qa' || r === 'default')))
|
|
46
|
+
.filter(c => !tradingTokens.has(c.botToken))
|
|
47
|
+
.map(c => c.botToken));
|
|
48
|
+
return tokens.size === 1 ? tokens.values().next().value : undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isAllowedBot(cfg: UserConfig, kind: string | undefined, token: string): boolean {
|
|
52
|
+
const mainToken = operationsBotToken(cfg);
|
|
53
|
+
if (!mainToken) return false;
|
|
54
|
+
if (!isTradingKind(kind)) return token === mainToken;
|
|
55
|
+
const reportToken = cfg.telegram.reportChannel?.botToken;
|
|
56
|
+
return token !== mainToken && (!reportToken || token === reportToken);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function roleForKind(cfg: UserConfig, kind?: string): string {
|
|
60
|
+
const fallback = isTradingKind(kind) ? 'report' : 'system';
|
|
61
|
+
if (!kind || !cfg.telegram.channels?.length) return fallback;
|
|
62
|
+
const override = cfg.telegram.kindRoles?.[kind];
|
|
63
|
+
if (!override) return fallback;
|
|
64
|
+
const channel = resolveTelegramChannels(cfg.telegram)
|
|
65
|
+
.find(c => c.roles.includes(override) && isAllowedBot(cfg, kind, c.botToken));
|
|
66
|
+
return channel ? override : fallback;
|
|
31
67
|
}
|
|
32
68
|
|
|
33
|
-
/**
|
|
69
|
+
/** Exact role only: channelForRole's default/first fallback can cross bot boundaries. */
|
|
34
70
|
export function kindRouteTarget(cfg: UserConfig, kind?: string): { botToken: string; chatId: number } | null {
|
|
71
|
+
if (!cfg.telegram.channels?.length) return null;
|
|
35
72
|
const role = roleForKind(cfg, kind);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
const ch = resolveTelegramChannels(cfg.telegram)
|
|
74
|
+
.find(c => c.roles.includes(role) && isAllowedBot(cfg, kind, c.botToken));
|
|
75
|
+
if (!ch || !Number.isFinite(ch.chatId)) return null;
|
|
76
|
+
return { botToken: ch.botToken, chatId: ch.chatId };
|
|
39
77
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const OUTPUT_KINDS = {
|
|
2
|
+
report: { fileName: 'report.md', cardTitle: '한 장 보고서', guidance: '보고·정리·요약·회고 → 한 장 보고서', words: ['보고', '정리', '요약', '회고', 'report', 'summary', 'summarize', 'recap', 'brief'] },
|
|
3
|
+
slides: { fileName: 'slides.md', cardTitle: '슬라이드', guidance: '발표·자료·덱·피치 → 슬라이드(장마다 --- 구분)', words: ['발표', '자료', '덱', '피치', 'presentation', 'slides', 'slide', 'deck', 'pitch'] },
|
|
4
|
+
research: { fileName: 'research.md', cardTitle: '조사 문서', guidance: '조사·비교·경쟁사·시장 → 조사 문서(출처 링크 칸 필수)', words: ['조사', '비교', '경쟁사', '시장', 'research', 'compare', 'comparison', 'competitor', 'competitors', 'market'] },
|
|
5
|
+
post: { fileName: 'post.md', cardTitle: '게시글 초안', guidance: '게시글·캡션·공지·SNS → 게시글 초안(게시는 승인 노드 뒤)', words: ['게시글', '캡션', '공지', 'SNS', 'post', 'caption', 'announcement', 'social media'] },
|
|
6
|
+
video: { fileName: '', cardTitle: '영상', guidance: '영상·릴스·쇼츠 → 영상(그래프가 있을 때만)', words: ['영상', '릴스', '쇼츠', 'video', 'reels', 'shorts'] },
|
|
7
|
+
answer: { fileName: '', cardTitle: '답', guidance: '단순한 질문 → 답 그대로(산출물 없음)', words: [] },
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type OutputKind = keyof typeof OUTPUT_KINDS;
|
|
11
|
+
|
|
12
|
+
export function pickDefaultOutput(text: string): { kind: OutputKind; fileName: string; cardTitle: string } {
|
|
13
|
+
const normalized = text.toLowerCase();
|
|
14
|
+
// Explicit artifact formats outrank activity words such as 정리, 요약 and 비교.
|
|
15
|
+
for (const kind of ['slides', 'post', 'video', 'research', 'report'] as const) {
|
|
16
|
+
const entry = OUTPUT_KINDS[kind];
|
|
17
|
+
if (entry.words.some(word => /[a-z]/i.test(word)
|
|
18
|
+
? new RegExp(`(^|[^a-z])${word.toLowerCase()}(?=$|[^a-z])`).test(normalized)
|
|
19
|
+
: normalized.includes(word))) {
|
|
20
|
+
return { kind, fileName: entry.fileName, cardTitle: entry.cardTitle };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const kind = /(?:[??]|뭐|어떻게)\s*$/.test(text) ? 'answer' : 'report';
|
|
24
|
+
return { kind, fileName: OUTPUT_KINDS[kind].fileName, cardTitle: OUTPUT_KINDS[kind].cardTitle };
|
|
25
|
+
}
|