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
|
@@ -11,6 +11,7 @@ import { runGitCommand } from '../git-fs/runner.js';
|
|
|
11
11
|
import * as llm from '../llm.js';
|
|
12
12
|
import type { LLMMessage } from '../llm.js';
|
|
13
13
|
import { debug } from '../debug/log.js';
|
|
14
|
+
import { releasePathHold, releasePathHoldShouldPost, releasePathHoldComment, releasePathHoldCommentsArgs, releasePrFilePaths, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
|
|
14
15
|
import { runAgentMission, resolveBackend, type AgentMissionResult, type EvidenceMode } from './driver.js';
|
|
15
16
|
import { judgeWithAcp, prepareJudgeDiff } from './acp-judge.js';
|
|
16
17
|
import { verifyMergeAndMaybeRevert } from './merge-safety.js';
|
|
@@ -114,6 +115,31 @@ function gh(args: string[]): string {
|
|
|
114
115
|
return execFileSync('gh', args, { encoding: 'utf8', timeout: 60000, maxBuffer: 20 * 1024 * 1024, env: process.env });
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
/** Check every page of the PR file list immediately before an unattended review-loop merge. */
|
|
119
|
+
function holdReleasePathBeforeReviewMerge(pr: string, runGh: (args: string[]) => string): boolean {
|
|
120
|
+
let paths: string[];
|
|
121
|
+
try {
|
|
122
|
+
paths = releasePrFilePaths(JSON.parse(runGh(['api', '--paginate', '--slurp', '--method', 'GET', '-f', 'per_page=100', `repos/{owner}/{repo}/pulls/${pr}/files`])));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
debug.log('self-dev.merge', 'release-path-inspection-failed', { pr, error: String(error) });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
const path = releasePathHold(paths);
|
|
128
|
+
if (!path) return false;
|
|
129
|
+
debug.log('self-dev.merge', 'release-path-hold', { pr, path, label: RELEASE_PATH_LABEL, surface: 'review-loop' });
|
|
130
|
+
try {
|
|
131
|
+
const names: unknown = JSON.parse(runGh(['label', 'list', '--search', RELEASE_PATH_LABEL, '--json', 'name']));
|
|
132
|
+
if (!Array.isArray(names)) throw new Error('invalid label list');
|
|
133
|
+
if (!names.some((item: unknown) => !!item && typeof item === 'object' && 'name' in item && item.name === RELEASE_PATH_LABEL)) {
|
|
134
|
+
runGh(['label', 'create', RELEASE_PATH_LABEL, '--color', 'D93F0B', '--description', 'Release path requires OP approval']);
|
|
135
|
+
}
|
|
136
|
+
runGh(['pr', 'edit', pr, '--add-label', RELEASE_PATH_LABEL]);
|
|
137
|
+
} catch (error) { debug.log('self-dev.merge', 'release-path-annotation-failed', { pr, action: 'label', error: String(error) }); }
|
|
138
|
+
try { if (releasePathHoldShouldPost(() => JSON.parse(runGh(releasePathHoldCommentsArgs(pr))))) runGh(['pr', 'comment', pr, '--body', releasePathHoldComment(path)]); }
|
|
139
|
+
catch (error) { debug.log('self-dev.merge', 'release-path-annotation-failed', { pr, action: 'comment', error: String(error) }); }
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
117
143
|
/** ★ G10 안전봉투 — 자율머지 직후 회귀검증(verifyMerge 시). 회귀면 revert PR 이 이미 생성됨(알림 포함) →
|
|
118
144
|
* 원 PR 에 링크 코멘트만. fail-open(검증 오류는 머지를 막지 않음). */
|
|
119
145
|
async function runMergeSafety(pr: string, opts: ReviewLoopOpts, depth: ReviewDepth): Promise<void> {
|
|
@@ -233,6 +259,7 @@ export async function judgeAndFinalize(
|
|
|
233
259
|
|
|
234
260
|
if (j.verdict === 'merge') {
|
|
235
261
|
if (opts.autoMerge) {
|
|
262
|
+
if (holdReleasePathBeforeReviewMerge(pr, runGh)) return { pr, branch, verdict: 'reinforce', asks, action: 'parked', reworkOk: true, pushed: lastPushed, rounds: round, detail: 'OP approval required: release-path hold or PR file inspection unavailable' };
|
|
236
263
|
const approve = opts.approve ?? approvePr;
|
|
237
264
|
const recordReviewOutcome = opts.recordReviewOutcome ?? recordOutcome;
|
|
238
265
|
approve(pr, `2차 ACP 최종심판 MERGE (round ${round})`);
|
|
@@ -480,6 +507,9 @@ export async function runReviewLoop(pr: string, opts: ReviewLoopOpts = {}): Prom
|
|
|
480
507
|
if (cls.verdict === 'ok') {
|
|
481
508
|
// light: 1차 clean 으로 충분 → approve + 머지. heavy: 1차 ok 여도 2차 심판 머스트.
|
|
482
509
|
if (!effectiveFinalJudge) {
|
|
510
|
+
if ((opts.autoMergeOnOk || opts.autoMerge) && holdReleasePathBeforeReviewMerge(pr, runGh)) {
|
|
511
|
+
return { pr, branch, verdict: 'ok', asks: [], action: 'parked', detail: 'OP approval required: release-path hold or PR file inspection unavailable' };
|
|
512
|
+
}
|
|
483
513
|
approve(pr, `소작업(light)·1차 리뷰 clean`);
|
|
484
514
|
let merged = false;
|
|
485
515
|
if (opts.autoMergeOnOk || opts.autoMerge) {
|
|
@@ -9,6 +9,17 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { runGitCommand } from '../../git-fs/runner.js';
|
|
11
11
|
import { countTestDeclarations } from '../../self-implement/test-declarations.js';
|
|
12
|
+
import { NEXT_MD_PATH, resolveNextMdConflict } from '../../release-loop/next-md-merge.js';
|
|
13
|
+
|
|
14
|
+
function deterministicNextMd(git: MergeGitSeam, worktreePath: string, file: string): string | null {
|
|
15
|
+
try {
|
|
16
|
+
const resolved = resolveNextMdConflict(git.readIndexStage(worktreePath, 1, file), git.readIndexStage(worktreePath, 2, file), git.readIndexStage(worktreePath, 3, file));
|
|
17
|
+
void import('../../debug/log.js').then(({ debug }) => debug.log('self-dev.merge', 'next-md-deterministic', { resolved: resolved !== null })).catch(() => {});
|
|
18
|
+
return resolved;
|
|
19
|
+
} catch {
|
|
20
|
+
return null; // a stage is missing (added/deleted on one side) — the usual resolver decides
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
/** 충돌 마커가 남아있나(LLM 이 해결 못 함 판별). ours/base/theirs 3-way 마커 모두. 순수. */
|
|
14
25
|
export function hasConflictMarkers(s: string): boolean {
|
|
@@ -130,8 +141,8 @@ export interface MergeGitSeam {
|
|
|
130
141
|
merge: (wt: string, mergeTarget: string) => { ok: boolean; conflict: boolean; stdout: string; errorDetail?: string };
|
|
131
142
|
/** 충돌(unmerged) 파일 목록(worktree 상대 경로). */
|
|
132
143
|
conflictedFiles: (wt: string) => string[];
|
|
133
|
-
/** Reads an unmerged index stage (2=ours, 3=theirs) for a conflicted file. */
|
|
134
|
-
readIndexStage: (wt: string, stage: 2 | 3, file: string) => string;
|
|
144
|
+
/** Reads an unmerged index stage (1=base, 2=ours, 3=theirs) for a conflicted file. */
|
|
145
|
+
readIndexStage: (wt: string, stage: 1 | 2 | 3, file: string) => string;
|
|
135
146
|
readFile: (absPath: string) => string;
|
|
136
147
|
writeFile: (absPath: string, content: string) => void;
|
|
137
148
|
/** 해결된 파일 스테이징. */
|
|
@@ -193,7 +204,9 @@ export async function mergeMainWithLlmResolve(
|
|
|
193
204
|
let merged: string;
|
|
194
205
|
try {
|
|
195
206
|
conflicted = git.readFile(abs);
|
|
196
|
-
|
|
207
|
+
// release/next.md: concurrent landings each append a line — keep both without asking the LLM (REL7c).
|
|
208
|
+
const deterministic = f === NEXT_MD_PATH ? deterministicNextMd(git, worktreePath, f) : null;
|
|
209
|
+
merged = deterministic ?? await resolve(f, conflicted);
|
|
197
210
|
} catch {
|
|
198
211
|
git.abort(worktreePath); // LLM 예외 → base 유지
|
|
199
212
|
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles });
|
|
@@ -88,7 +88,7 @@ export type ControlDecision =
|
|
|
88
88
|
| { readonly action: 'handoff'; readonly to: 'codex' | 'claude' | 'elanous'; readonly mission: string; readonly carry?: 'diff' | 'summary' }
|
|
89
89
|
| { readonly action: 'ask-human'; readonly reason: string; readonly url?: string; readonly code?: string }
|
|
90
90
|
/** `no-progress` reports observed lack of progress without claiming completion. */
|
|
91
|
-
| { readonly action: 'no-progress'; readonly reason: string };
|
|
91
|
+
| { readonly action: 'no-progress'; readonly reason: string; readonly terminal?: boolean };
|
|
92
92
|
|
|
93
93
|
export interface RunSupervisor {
|
|
94
94
|
/** run supervisor의 관측 → 다음 행동. 동기/비동기 모두 허용(LLM 은 async). `signal` 이 abort 되면(사람 takeover
|
|
@@ -664,6 +664,9 @@ export async function runPtyControlLoop(
|
|
|
664
664
|
previousIntervention = { ...intervention, sameScreenCount: 1 };
|
|
665
665
|
}
|
|
666
666
|
} else if (decision.action === 'wait' || decision.action === 'no-progress') {
|
|
667
|
+
if (decision.action === 'no-progress' && decision.terminal) {
|
|
668
|
+
return finish({ kind: 'stuck', iteration: step, reason: decision.reason }, step, intervention, true);
|
|
669
|
+
}
|
|
667
670
|
// wait/no-progress — 종료된 자식은 새 출력을 낼 수 없으므로 대기 없이 즉시 수렴한다.
|
|
668
671
|
if (deps.isAlive?.() === false) {
|
|
669
672
|
debug.log('autopilot.control', 'child-exited', { step });
|
|
@@ -116,6 +116,8 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
116
116
|
* `--tool-cwd` / `ELANOUS_TOOL_CWD` to keep `/v1/prompt` and ACP WS
|
|
117
117
|
* on the same path-guard boundary. */
|
|
118
118
|
toolCwd?: string;
|
|
119
|
+
/** Per-turn project instructions; unlike the base system preamble these must survive subsequent turns. */
|
|
120
|
+
projectInstructions?: string;
|
|
119
121
|
/** Phase 4 (WT-A-3b) — external abort signal. When provided and
|
|
120
122
|
* fired, the internal AbortController fans the abort through to
|
|
121
123
|
* `runCoreTurn` (LLM stream + tool dispatch). Callers without an
|
|
@@ -286,6 +288,9 @@ export async function runDaemonPromptTurn(opts: {
|
|
|
286
288
|
if (fast && !isFirstTurn) {
|
|
287
289
|
messages.unshift({ role: 'system', content: fastPathGuidance });
|
|
288
290
|
}
|
|
291
|
+
if (!isFirstTurn && opts.projectInstructions) {
|
|
292
|
+
messages.unshift({ role: 'system', content: opts.projectInstructions });
|
|
293
|
+
}
|
|
289
294
|
const result = await runCoreTurn({
|
|
290
295
|
sessionId: request.sessionId,
|
|
291
296
|
userText: request.userText,
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
import type { CoreTurnDispatchTool } from '../core-turn/index.js';
|
|
54
54
|
import { elanousSelfAccessPrompt } from '../agent/self-ambient.js';
|
|
55
55
|
import { createToolCwdResolver, type ToolCwdResolver } from './tool-cwd.js';
|
|
56
|
+
import { resolveSessionProjectContext } from '../project/session-context.js';
|
|
56
57
|
import { debug } from '../debug/log.js';
|
|
57
58
|
|
|
58
59
|
/** C4 (2026-07-12) — arm the non-detached-PTY kill ONCE per turn signal.
|
|
@@ -579,7 +580,9 @@ export function createDaemonRunTurn(
|
|
|
579
580
|
// moved this out of an inline closure to fix the user-message
|
|
580
581
|
// persistence bug uniformly across all daemon paths.
|
|
581
582
|
getMessages: ({ sessionId, userText, promptBlocks, promptMeta }) => {
|
|
582
|
-
const
|
|
583
|
+
const projectInstructions = resolveSessionProjectContext(sessionId)?.instructions;
|
|
584
|
+
const baseSystemPrompt = [composeDaemonSystemPrompt(opts.systemPrompt, promptMeta, sessionId), projectInstructions]
|
|
585
|
+
.filter(Boolean).join('\n\n');
|
|
583
586
|
// Image-pipeline followup #4 (2026-05-05) — append daemon webterm
|
|
584
587
|
// context (current ACP sessionId + active terminal summary) to
|
|
585
588
|
// the system prompt when the webterm surface is active. Helper
|
|
@@ -720,8 +723,11 @@ export function createDaemonRunTurn(
|
|
|
720
723
|
});
|
|
721
724
|
debugBridge.activate();
|
|
722
725
|
}
|
|
723
|
-
|
|
724
|
-
|
|
726
|
+
const projectCwd = resolveSessionProjectContext(turnCtx.sessionId)?.cwd;
|
|
727
|
+
if (projectCwd || opts.acpSessionCwd) {
|
|
728
|
+
// An ACP session's explicit cwd wins; the project folder is only the default when none was given (IA3 review).
|
|
729
|
+
const explicitCwd = opts.acpSessionCwd && turnCtx.cwd ? turnCtx.cwd : undefined;
|
|
730
|
+
const resolver = createToolCwdResolver({ tools: opts.tools ?? 'none', toolCwd: explicitCwd ?? projectCwd ?? turnCtx.cwd });
|
|
725
731
|
await turnToolCwd.run(resolver, () => inner(turnCtx));
|
|
726
732
|
} else {
|
|
727
733
|
await inner(turnCtx);
|
|
@@ -23,6 +23,8 @@ export interface DaemonWebSearchArgs {
|
|
|
23
23
|
export interface DaemonWebSearchResult {
|
|
24
24
|
query: string;
|
|
25
25
|
providerName: string;
|
|
26
|
+
/** Absent for ordinary searches; signals whether X sources were actually cited. */
|
|
27
|
+
xSearch?: 'grok' | 'unavailable';
|
|
26
28
|
hits: Array<{
|
|
27
29
|
url: string;
|
|
28
30
|
title: string;
|
|
@@ -90,6 +92,7 @@ export async function dispatchWebSearch(
|
|
|
90
92
|
return {
|
|
91
93
|
query,
|
|
92
94
|
providerName: result.providerName,
|
|
95
|
+
...(result.xSearch ? { xSearch: result.xSearch } : {}),
|
|
93
96
|
hits: result.hits.map((h) => ({
|
|
94
97
|
url: h.url,
|
|
95
98
|
title: h.title,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
5
|
+
import { lastJsonObject, runGraph as defaultRunGraph, type GraphRunState } from '../graph-runner/runner.js';
|
|
6
|
+
|
|
7
|
+
const GRAPH = resolve(import.meta.dir, '../../plugins/card-followup/graphs/card-followup.yaml');
|
|
8
|
+
export type CardDetectDecision = 'card' | 'skip' | 'ambiguous';
|
|
9
|
+
|
|
10
|
+
export function cardShape(width?: number, height?: number): { decision: 'skip' | 'pass'; signal: string } {
|
|
11
|
+
if (!width || !height) return { decision: 'pass', signal: 'ratio:unknown' };
|
|
12
|
+
const ratio = Math.max(width, height) / Math.min(width, height);
|
|
13
|
+
if (ratio < 1.15) return { decision: 'skip', signal: 'ratio:square' };
|
|
14
|
+
if (ratio > 2.0) return { decision: 'skip', signal: 'ratio:screenshot' };
|
|
15
|
+
return { decision: 'pass', signal: `ratio:${ratio.toFixed(2)}` };
|
|
16
|
+
}
|
|
17
|
+
const EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i;
|
|
18
|
+
const PHONE = /(?:\+?\d{1,3}[\s.-]?)?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{4}/;
|
|
19
|
+
const WEB = /\b(?:https?:\/\/|www\.)\S+|\b[a-z0-9-]+\.(?:com|co\.kr|kr|io|ai|net|org)\b/i;
|
|
20
|
+
const TITLE = /대표|이사|팀장|부장|과장|실장|매니저|주식회사|\(주\)|\b(?:CEO|CTO|COO|CFO|Director|Manager|Founder|Inc|Ltd|Corp)\b/i;
|
|
21
|
+
export function cardText(ocr: string | null): { decision: CardDetectDecision; signal: string } {
|
|
22
|
+
if (ocr === null) return { decision: 'ambiguous', signal: 'ocr:unavailable' };
|
|
23
|
+
const chars = ocr.replace(/\s+/g, '').length;
|
|
24
|
+
if (chars < 15) return { decision: 'ambiguous', signal: 'ocr:chars<15' };
|
|
25
|
+
if (chars > 700) return { decision: 'skip', signal: 'ocr:document' };
|
|
26
|
+
const hits = ([['email', EMAIL], ['phone', PHONE], ['web', WEB], ['title', TITLE]] as const).filter(([, re]) => re.test(ocr)).map(([name]) => name);
|
|
27
|
+
const signal = `ocr:${hits.join(',') || 'none'}`;
|
|
28
|
+
if (hits.length >= 2) return { decision: 'card', signal };
|
|
29
|
+
return { decision: hits.length === 0 ? 'skip' : 'ambiguous', signal };
|
|
30
|
+
}
|
|
31
|
+
export function detectCard({ width, height, ocrText }: { width?: number; height?: number; ocrText: string | null }): { decision: CardDetectDecision; signal: string } {
|
|
32
|
+
const shape = cardShape(width, height);
|
|
33
|
+
return shape.decision === 'skip' ? { decision: 'skip', signal: shape.signal } : cardText(ocrText);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const obj = (value: unknown): Record<string, unknown> => value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
|
37
|
+
const text = (value: unknown): string => typeof value === 'string' ? value.trim() : '';
|
|
38
|
+
const line = (value: unknown): string => (typeof value === 'number' && Number.isFinite(value) ? String(value) : text(value)).replace(/[\r\n]+/g, ' ') || '—';
|
|
39
|
+
|
|
40
|
+
export class NotACardError extends Error {}
|
|
41
|
+
function isNotACard(state: GraphRunState): boolean {
|
|
42
|
+
if (state.status !== 'failed') return false;
|
|
43
|
+
const read = state.nodes.find((node) => node.nodeId === 'read-card');
|
|
44
|
+
if (!read || !read.ok) return false;
|
|
45
|
+
const output = lastJsonObject(read.output);
|
|
46
|
+
if (output?.outcome === 'ok' && output.card && typeof output.card === 'object') {
|
|
47
|
+
const card = obj(output.card);
|
|
48
|
+
return !['name', 'company', 'title', 'email', 'phone', 'url', 'linkedin'].some((field) => text(card[field]));
|
|
49
|
+
}
|
|
50
|
+
return output?.outcome === 'fail' && typeof output.reason === 'string'
|
|
51
|
+
&& /\bnot a (?:business )?card\b|명함이?\s*아님|명함이?\s*아닙니다/i.test(output.reason);
|
|
52
|
+
}
|
|
53
|
+
function validateReport(report: Record<string, unknown>): void {
|
|
54
|
+
const card = obj(report.card), fit = obj(report.fit), approach = obj(report.approach);
|
|
55
|
+
const next = obj(report.nextAction), draft = obj(report.draft);
|
|
56
|
+
const score = fit.score;
|
|
57
|
+
if (report.sent !== false || !text(card.name) || !text(card.company)
|
|
58
|
+
|| !(score === null || (typeof score === 'number' && Number.isFinite(score)))
|
|
59
|
+
|| !text(fit.label) || !text(approach.problem) || !text(approach.proposal)
|
|
60
|
+
|| !text(approach.channel) || !text(next.what) || !text(next.due)
|
|
61
|
+
|| !text(draft.subject) || !text(draft.body)) {
|
|
62
|
+
throw new Error('incomplete or sent card report');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function runCardFollowup({ imagePath, ocrText, runGraph = defaultRunGraph, rootDir = effectiveInstanceRoot(), outDir, runId = randomUUID(), context }: {
|
|
67
|
+
imagePath: string;
|
|
68
|
+
ocrText: string | null;
|
|
69
|
+
runGraph?: typeof defaultRunGraph;
|
|
70
|
+
rootDir?: string;
|
|
71
|
+
outDir?: string;
|
|
72
|
+
runId?: string;
|
|
73
|
+
context?: string;
|
|
74
|
+
}): Promise<{ replies: [string, string, string]; followupPath?: string }> {
|
|
75
|
+
if (ocrText !== null && cardText(ocrText).decision !== 'card') throw new NotACardError('not a card');
|
|
76
|
+
const base = join(rootDir, 'graph-runs', 'card-followup');
|
|
77
|
+
const dir = outDir ?? join(base, runId);
|
|
78
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
79
|
+
chmodSync(base, 0o700);
|
|
80
|
+
chmodSync(dir, 0o700);
|
|
81
|
+
const statePath = join(base, `${runId}.json`);
|
|
82
|
+
const sealState = () => {
|
|
83
|
+
try { chmodSync(statePath, 0o600); } catch { /* runner may not have written state */ }
|
|
84
|
+
const contexts = `${statePath}.contexts`;
|
|
85
|
+
try {
|
|
86
|
+
chmodSync(contexts, 0o700);
|
|
87
|
+
for (const file of readdirSync(contexts)) chmodSync(join(contexts, file), 0o600);
|
|
88
|
+
} catch { /* no contexts yet */ }
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const state = await runGraph(GRAPH, { runId, deps: { root: rootDir }, input: {
|
|
92
|
+
image: imagePath, outDir: dir, ...(context ? { context } : {}),
|
|
93
|
+
} });
|
|
94
|
+
if (isNotACard(state)) throw new NotACardError('not a card');
|
|
95
|
+
if (state.status !== 'done') throw new Error('graph did not finish');
|
|
96
|
+
const report = obj(JSON.parse(readFileSync(join(dir, 'followup.json'), 'utf8')));
|
|
97
|
+
validateReport(report);
|
|
98
|
+
const card = obj(report.card), fit = obj(report.fit), approach = obj(report.approach);
|
|
99
|
+
const next = obj(report.nextAction), draft = obj(report.draft);
|
|
100
|
+
const summary = `① 요약\n${line(card.name)} · ${line(card.company)} · ${line(card.title)}\n타겟 판정: ${line(fit.score)} · ${line(fit.label)}`;
|
|
101
|
+
const strategy = `② 전략\n${[approach.problem, approach.proposal, approach.channel].map(line).join('\n')}\n다음 행동: ${line(next.what)} · ${line(next.due)}`;
|
|
102
|
+
const mail = `③ 팔로업 초안 (보내지 않았습니다)\n제목: ${line(draft.subject)}\n${text(draft.body)}`;
|
|
103
|
+
const followupPath = join(dir, 'followup.md');
|
|
104
|
+
readFileSync(followupPath);
|
|
105
|
+
return { replies: [summary, strategy, mail], followupPath };
|
|
106
|
+
} finally {
|
|
107
|
+
if (runGraph === defaultRunGraph) sealState();
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/chat/index.ts
CHANGED
|
@@ -437,6 +437,8 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
437
437
|
{ name: 'model', aliases: ['m'], description: '모델 바꾸기 — /model <codex|terra|sol|luna|opus|sonnet|grok> (list + current when no arg). OpenAI 는 Codex(Responses API)만. effort 는 /reasoning.', subcommands: ['codex', 'terra', 'sol', 'luna', 'opus', 'sonnet', 'grok'] },
|
|
438
438
|
{ name: 'clear', aliases: ['cls'], description: '대화 기록 지우기' },
|
|
439
439
|
{ name: 'status', aliases: ['st'], description: '지금 상태 — 모델 · 연결 · 계정' },
|
|
440
|
+
{ name: 'now', aliases: [], description: '현재 맥락과 출처 표 — /now [주제]' },
|
|
441
|
+
{ name: 'wish', aliases: [], description: '소원을 카드로 남기기 — /wish <소원 한 줄>' },
|
|
440
442
|
{ name: 'remaining', aliases: [], description: '계정별 남은 사용량' },
|
|
441
443
|
{ name: 'setup', aliases: [], description: '상세 설정 — /setup · /setup reset(다음 실행 때 마법사 다시)', subcommands: ['help', 'reset'] },
|
|
442
444
|
{ name: 'quit', aliases: ['q', 'exit'], description: '나가기' },
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, isAbsolute, relative, sep } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export type DriveInputJailVerdict =
|
|
6
|
+
| { readonly allowed: true }
|
|
7
|
+
| { readonly allowed: false; readonly reason: 'outside-jail'; readonly path: string };
|
|
8
|
+
|
|
9
|
+
/** Resolve each existing component before applying the next '..', including write targets. */
|
|
10
|
+
function canonicalPath(path: string): string {
|
|
11
|
+
let current = isAbsolute(path) ? sep : realpathSync(process.cwd());
|
|
12
|
+
for (const part of path.split(sep)) {
|
|
13
|
+
if (!part || part === '.') continue;
|
|
14
|
+
if (part === '..') {
|
|
15
|
+
current = dirname(current);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const next = `${current === sep ? '' : current}${sep}${part}`;
|
|
19
|
+
try {
|
|
20
|
+
current = realpathSync(next);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
23
|
+
current = next;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function insideJail(boundary: string, candidate: string): boolean {
|
|
30
|
+
const rel = relative(boundary, candidate);
|
|
31
|
+
return rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check path literals in brain-generated PTY text without executing or rewriting the text.
|
|
36
|
+
* Absolute paths and home paths (~ and ~/) are checked against the canonical jail, including symlink
|
|
37
|
+
* ancestors. This is a path-literal guard, not a shell interpreter or a sandbox for the child.
|
|
38
|
+
*/
|
|
39
|
+
export function checkDriveInputJail(input: string, boundary: string, home: string = homedir()): DriveInputJailVerdict {
|
|
40
|
+
const jail = canonicalPath(boundary);
|
|
41
|
+
// Quoted path arguments can contain spaces. Inspect those as one literal before scanning
|
|
42
|
+
// unquoted tokens (which also covers option assignments such as --prefix=/opt/homebrew).
|
|
43
|
+
const quotedRanges: Array<[number, number]> = [];
|
|
44
|
+
const literals: string[] = [];
|
|
45
|
+
for (const match of input.matchAll(/(["'])(.*?)\1/gs)) {
|
|
46
|
+
const start = match.index ?? 0;
|
|
47
|
+
if (match[2]?.startsWith('/') || match[2] === '~' || match[2]?.startsWith('~/')) {
|
|
48
|
+
// A double-quoted string still runs $(…) and `…` — keep scanning inside it so a substituted
|
|
49
|
+
// command's own paths (e.g. "/jail/$(cat /etc/passwd)") are checked too.
|
|
50
|
+
const expands = match[1] === '"' && /\$\(|`/.test(match[2]);
|
|
51
|
+
if (!expands) quotedRanges.push([start, start + match[0].length]);
|
|
52
|
+
if (/\bPATH=$/.test(input.slice(0, start))) literals.push(...match[2].split(':').filter(Boolean));
|
|
53
|
+
else literals.push(match[2]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const candidates = /(^|[\s"'`|&=:<>(\[,;])((?:~\/|\/)[^\s"'`|:;&<>()[\]{}]*|~(?=$|[\s"'`|:;&<>()[\]{},]))/gm;
|
|
57
|
+
for (const match of input.matchAll(candidates)) {
|
|
58
|
+
const literal = match[2]!;
|
|
59
|
+
const start = (match.index ?? 0) + match[1]!.length;
|
|
60
|
+
if (quotedRanges.some(([from, to]) => from < start && start < to)) continue;
|
|
61
|
+
if (literal.startsWith('//') && /\w+:$/.test(input.slice(Math.max(0, start - 12), start))) continue;
|
|
62
|
+
literals.push(literal);
|
|
63
|
+
}
|
|
64
|
+
for (const literal of literals) {
|
|
65
|
+
const expanded = literal === '~' ? home : literal.startsWith('~/') ? `${home.replace(/\/$/, '')}/${literal.slice(2)}` : literal;
|
|
66
|
+
if (!insideJail(jail, canonicalPath(expanded))) return { allowed: false, reason: 'outside-jail', path: literal };
|
|
67
|
+
}
|
|
68
|
+
return { allowed: true };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface DriveJailDeps {
|
|
72
|
+
inject(text: string): boolean;
|
|
73
|
+
observe(): string | Promise<string>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Put a drive's PTY input behind the jail. A blocked input is never sent to the PTY; the next screen the brain
|
|
78
|
+
* reads carries one line saying why, so it can try another way inside the jail (returning false from inject
|
|
79
|
+
* would end the loop as a human takeover). No jail → the deps come back unchanged.
|
|
80
|
+
*/
|
|
81
|
+
export function withDriveJail<T extends DriveJailDeps>(deps: T, jail: string | undefined, log?: (event: string, data: Record<string, unknown>) => void): T {
|
|
82
|
+
if (!jail) return deps;
|
|
83
|
+
let notice: string | null = null;
|
|
84
|
+
return {
|
|
85
|
+
...deps,
|
|
86
|
+
inject: (text: string) => {
|
|
87
|
+
const verdict = checkDriveInputJail(text, jail);
|
|
88
|
+
if (verdict.allowed) return deps.inject(text);
|
|
89
|
+
notice = `⚠ elanous: input blocked — ${verdict.path} is outside the boundary ${jail} · try another way inside it`;
|
|
90
|
+
log?.('jail-block', { path: verdict.path, input: text.slice(0, 80) });
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
observe: async (): Promise<string> => {
|
|
94
|
+
const frame = await deps.observe();
|
|
95
|
+
if (notice === null) return frame;
|
|
96
|
+
const line = notice;
|
|
97
|
+
notice = null;
|
|
98
|
+
return `${frame}\n${line}`;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
package/src/cli/hooks-cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { startHookReceiver } from '../hooks/receiver.js';
|
|
3
3
|
import { HookQueue } from '../hooks/queue.js';
|
|
4
|
+
import { closeIngressAndExposure, hookExposurePlan, hookExposureStatus, startPublicHookIngress, turnHookExposureOff, turnHookExposureOn, type HookExposureDeps } from '../hooks/expose.js';
|
|
4
5
|
import { getSecretAsync, setSecretAsync } from '../nexus/config/secrets/index.js';
|
|
5
6
|
import { getUserConfig } from '../user-config.js';
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ export function asanaEnabled(raw: unknown = getUserConfig().raw): boolean {
|
|
|
9
10
|
return tox?.external?.asana?.enabled === true;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export function registerHooksCommands(program: Command): void {
|
|
13
|
+
export function registerHooksCommands(program: Command, exposureDeps: HookExposureDeps = {}): void {
|
|
13
14
|
const hooks = program.command('hooks').description('외부 웹훅 수신·재전송');
|
|
14
15
|
hooks.command('serve').description('웹훅 수신기를 포그라운드에서 실행')
|
|
15
16
|
.option('--host <host>', '리스닝 주소', '127.0.0.1')
|
|
@@ -20,22 +21,35 @@ export function registerHooksCommands(program: Command): void {
|
|
|
20
21
|
throw new Error('invalid hooks port');
|
|
21
22
|
const secrets = {
|
|
22
23
|
linear: await getSecretAsync('hooks.linear'), asana: await getSecretAsync('hooks.asana'),
|
|
24
|
+
github: await getSecretAsync('hooks.github'),
|
|
23
25
|
// 첫 핸드셰이크의 비밀을 저장하는 것은 «먼저 온 사람이 이긴다» — Asana 를 켠 설치에서만 연다.
|
|
24
26
|
...(asanaEnabled() ? { saveAsana: (value: string) => setSecretAsync('hooks.asana', value) } : {}),
|
|
25
27
|
};
|
|
26
28
|
const server = startHookReceiver({ host: opts.host, port, secrets });
|
|
29
|
+
let ingress: ReturnType<typeof startPublicHookIngress> | undefined;
|
|
30
|
+
try { if (opts.host === '127.0.0.1' && port === 31480) ingress = startPublicHookIngress(server.url); }
|
|
31
|
+
catch (error) { server.stop(); throw error; }
|
|
27
32
|
console.log(`hooks listening ${new URL(server.url).host}`);
|
|
28
33
|
await new Promise<void>(resolve => {
|
|
29
34
|
const stop = () => {
|
|
30
35
|
process.removeListener('SIGINT', stop);
|
|
31
36
|
process.removeListener('SIGTERM', stop);
|
|
32
|
-
server.stop();
|
|
33
|
-
resolve();
|
|
37
|
+
void closeIngressAndExposure(ingress, exposureDeps).finally(() => { server.stop(); resolve(); });
|
|
34
38
|
};
|
|
35
39
|
process.once('SIGINT', stop);
|
|
36
40
|
process.once('SIGTERM', stop);
|
|
37
41
|
});
|
|
38
42
|
});
|
|
43
|
+
const expose = hooks.command('expose').description('결정 원장에 묶인 웹훅 공개 진입');
|
|
44
|
+
expose.command('plan').description('공개 경로와 tailscale 명령만 표시').action(() => console.log(JSON.stringify(hookExposurePlan())));
|
|
45
|
+
expose.command('on').description('결정 뒤 공개 진입 켜기').option('--decision <D-id>')
|
|
46
|
+
.action(async (opts: { decision?: string }) => console.log(JSON.stringify(await turnHookExposureOn(opts.decision, exposureDeps))));
|
|
47
|
+
expose.command('off').description('이 도구가 켠 공개 진입 끄기(결정 불필요)').option('--force', '기록·대상 확인 없이 8443 Funnel 을 끈다')
|
|
48
|
+
.action(async (opts: { force?: boolean }) => {
|
|
49
|
+
const result = await turnHookExposureOff(exposureDeps, { force: opts.force });
|
|
50
|
+
console.log(result.changed ? `hooks exposure off (${result.reason})` : 'hooks exposure: nothing of ours to turn off (8443 Funnel is not recorded and does not target the hook ingress; --force to turn it off anyway)');
|
|
51
|
+
});
|
|
52
|
+
expose.command('status').description('기록된 공개 경로·결정·시각').action(() => console.log(JSON.stringify(hookExposureStatus(exposureDeps))));
|
|
39
53
|
hooks.command('status').description('대기 중인 웹훅 수와 마지막 전달 시각')
|
|
40
54
|
.action(() => {
|
|
41
55
|
const queue = new HookQueue();
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { debug } from '../debug/log.js';
|
|
3
|
+
import { judge } from '../llm/judge-layer.js';
|
|
4
|
+
import { runPluginMake } from './plugin-cli.js';
|
|
5
|
+
import { workflowRun } from './workflow.js';
|
|
6
|
+
|
|
7
|
+
type MakeKind = 'plugin' | 'workflow';
|
|
8
|
+
type MakeDeps = {
|
|
9
|
+
decide?: (request: string) => Promise<MakeKind | null>;
|
|
10
|
+
pluginMake?: (request: string) => Promise<void>;
|
|
11
|
+
workflowMake?: (request: string) => Promise<number>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function classifyMakeRequest(request: string, call?: (args: { prompt: string; provider: string; model: string }) => Promise<{ text: string }>): Promise<MakeKind | null> {
|
|
15
|
+
if (!request.trim()) return null;
|
|
16
|
+
const result = await judge<MakeKind>({
|
|
17
|
+
site: 'cli.make',
|
|
18
|
+
...(call ? { call } : {}),
|
|
19
|
+
prompt: `Classify this request for an Elanous creator. Choose "plugin" for a reusable connector, integration, skill, or capability (including reading a file format). Choose "workflow" for a sequence of jobs, recurring schedule, automation, or publishing task, even when it uses a connector. If you cannot decide, return {"kind":"unclear"}. Respond with JSON only, exactly {"kind":"plugin"} or {"kind":"workflow"} or {"kind":"unclear"}. Do not execute the request.\nRequest: ${JSON.stringify(request)}`,
|
|
20
|
+
schema: value => {
|
|
21
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
22
|
+
const kind = (value as { kind?: unknown }).kind;
|
|
23
|
+
return kind === 'plugin' || kind === 'workflow' ? kind : null;
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return result.ok ? result.value : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function registerMakeCommand(program: Command, deps: MakeDeps = {}): void {
|
|
30
|
+
program.command('make <request>')
|
|
31
|
+
.description('Classify a one-line request and make a plugin or build a workflow')
|
|
32
|
+
.action(async (request: string) => {
|
|
33
|
+
const kind = await (deps.decide ?? classifyMakeRequest)(request).catch(() => null);
|
|
34
|
+
debug.log('cli.make', 'route', { kind: kind ?? 'undecided' });
|
|
35
|
+
if (!kind) {
|
|
36
|
+
console.log('플러그인(커넥터·스킬)을 만들까요, 워크플로(잡)를 만들까요?');
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (kind === 'plugin') await (deps.pluginMake ?? runPluginMake)(request);
|
|
41
|
+
else process.exitCode = await (deps.workflowMake ?? ((text: string) => workflowRun('build-workflow', text)))(request);
|
|
42
|
+
});
|
|
43
|
+
}
|