elanous 0.2.9 → 0.2.11
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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.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/2310-7a59f043f6db207e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.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-a39d1d9921e10d43.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/507-5acd98bd6dab2d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +1 -0
- 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/{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/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-ea5bee9dfae512c7.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/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f972fc1c00a62e88.js → page-b12b6d2f8d2d1aea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.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-50cb2a0eb15e6c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.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-239c17b911b84e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.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/543f56bcb1f79fca.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 -0
- package/apps/pwa/out/ops/seats/index.txt +30 -0
- 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 -0
- package/apps/pwa/out/today/index.txt +33 -0
- 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/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/field/field-feed.yaml +2 -0
- package/graphs/implement-loop.yaml +1 -1
- 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/graphs/run-step.ts +48 -13
- package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
- package/plugins/doc-draft/graphs/run-step.ts +13 -6
- 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/ci-pwa-build-gate.ts +160 -0
- 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 +54 -8
- package/scripts/coord-digest.ts +199 -0
- package/scripts/coord-post.sh +60 -3
- 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/install.ps1 +68 -9
- package/scripts/install.sh +39 -12
- package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
- package/scripts/lib/tui-regress-checks.ts +146 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/memory-lifecycle-cycle.ts +2 -2
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/ops-now-probe.ts +327 -0
- package/scripts/publish-internal-dist.ts +11 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/cutoff.ts +19 -8
- package/scripts/release-loop/gate-node.ts +14 -13
- 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 +72 -16
- package/scripts/release-loop/release-readiness.ts +9 -0
- 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 +112 -13
- package/scripts/repo-watch-cycle.ts +1 -1
- package/scripts/retro-cycle.ts +2 -2
- package/scripts/schedule-health-report.ts +1 -1
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/storyboard/storyboard.ts +5 -161
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/test-diet/lib.ts +30 -1
- package/scripts/test-diet/node.ts +83 -8
- package/scripts/test-timeout-audit.ts +114 -0
- package/scripts/tree-sync-alert.ts +1 -1
- package/scripts/tui-regress.ts +71 -0
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
- package/scripts/waitlist-daily-digest.ts +1 -1
- 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/explainer-video/SKILL.md +4 -0
- package/skills/explainer-video/engine/reel.mjs +18 -13
- package/skills/explainer-video/engine/reel.sh +24 -5
- package/skills/explainer-video/engine/stitch.mjs +145 -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 +36 -0
- package/skills/pitch-deck/template.html +80 -0
- 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/codex-approval-adapter.ts +9 -3
- package/src/acp/core-turn-bridge.ts +117 -18
- package/src/acp/fact-question.ts +11 -0
- package/src/acp/server.ts +53 -9
- package/src/acp/tool-approval.ts +126 -0
- package/src/acp/turn-runner.ts +36 -0
- package/src/agent/monad-agent-turn.ts +8 -0
- package/src/agent-mission/driver.ts +80 -10
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/ask-user-question/pending-questions.ts +25 -10
- package/src/ask-user-question/types.ts +9 -0
- package/src/auth/setup-link-tokens.ts +16 -5
- package/src/autopilot/build/integrity-gate.ts +12 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +8 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/index.ts +8 -0
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/boot/daemon-tools/web-search.ts +4 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/ctrl-c-in-input.ts +14 -0
- package/src/chat/index.ts +53 -0
- package/src/chat/pickers/state.ts +6 -2
- package/src/cli/agent-cli.ts +7 -0
- package/src/cli/decisions-cli.ts +25 -3
- package/src/cli/doctor-bundle-upload.ts +68 -0
- package/src/cli/doctor-cli.ts +38 -4
- 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/ops-cli.ts +20 -0
- package/src/cli/persona-cli.ts +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +61 -2
- package/src/cli/pr-cli.ts +82 -17
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/pwa-stop.ts +163 -13
- package/src/cli/pwa-test.ts +3 -0
- package/src/cli/release-cli.ts +80 -24
- package/src/cli/self-update.ts +14 -0
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/cli/storyboard-cli.ts +37 -0
- package/src/cli/test-state-dir-flag.ts +11 -4
- package/src/connectors/linear.ts +146 -0
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +48 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +115 -0
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/dashboard/dashboard-boot-fixture.ts +23 -0
- package/src/dashboard/index.ts +63 -21
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
- package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
- package/src/decisions/decision-cards.ts +58 -3
- package/src/decisions/decision-ledger.ts +85 -6
- package/src/decisions/decision-linear-projection.ts +157 -0
- package/src/decisions/web-push-decision-cards.ts +50 -0
- package/src/directive/interpretation-card.ts +36 -0
- 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 +19 -2
- package/src/domains/coo-admin-tool.ts +62 -0
- package/src/domains/core-tools.ts +13 -4
- package/src/domains/elanous-card.ts +29 -0
- package/src/domains/ops-facts-tool.ts +76 -0
- package/src/domains/outbound-alert.ts +46 -13
- package/src/domains/release-tool.ts +168 -0
- package/src/domains/telegram-kind-route.ts +65 -9
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +106 -28
- package/src/exec-requests/runner.ts +123 -12
- package/src/exec-requests/store.ts +11 -2
- package/src/execution-loop/launch-gate.ts +100 -0
- package/src/field/field-media.ts +10 -7
- package/src/field/field-reel.ts +19 -5
- package/src/field-note/index.ts +201 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/git-fs/worktree.ts +45 -6
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-approval-card.ts +113 -0
- package/src/graph-runner/graph-cli.ts +189 -4
- package/src/graph-runner/graph-grow-llm.ts +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/graph-notify.ts +25 -3
- package/src/graph-runner/runner.ts +453 -35
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/browser-verify.ts +25 -2
- package/src/harness/harness-cli-command.ts +478 -42
- package/src/harness/harness-incidents-writer.fixture.ts +24 -0
- package/src/harness/harness-incidents.ts +129 -0
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +75 -6
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hitl/clarification-policy.ts +3 -1
- 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 +21 -20
- package/src/index.ts +206 -49
- package/src/intake-plane/author-goals.ts +37 -4
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/discord-seat-work.ts +57 -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/seat-doc-route.ts +170 -6
- package/src/intake-plane/telegram-seat-work.ts +80 -4
- package/src/intake-plane/telegram-work.ts +1 -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/cli-maturity.ts +34 -0
- package/src/maturity/feature-maturity.ts +120 -1
- package/src/maturity/tui-slash-maturity.ts +31 -0
- package/src/memory.ts +32 -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/exec-requests.ts +33 -3
- 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 +89 -1
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +32 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-api.ts +10 -1
- package/src/nexus/api/ops-seats-sources.ts +37 -7
- package/src/nexus/api/ops-seats.ts +49 -6
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/seat-requests.ts +132 -10
- package/src/nexus/api/sessions-store.ts +42 -0
- package/src/nexus/api/setup-codex-login.ts +160 -0
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/usage-api.ts +102 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +47 -4
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/first-run.ts +89 -0
- package/src/onboarding/lan-address.ts +45 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/onboarding/web-first.ts +119 -24
- package/src/onboarding.ts +4 -0
- package/src/ops-now/ops-now-refresh.ts +100 -0
- 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 +116 -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 +109 -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-store.ts +115 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +79 -5
- package/src/release-loop/feature-store.ts +159 -24
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/release-loop/release-schedule.ts +53 -0
- package/src/repl/index.ts +7 -0
- package/src/seat-dispatch/ceo-commands.ts +41 -8
- package/src/seat-dispatch/ceo-intent.ts +38 -0
- 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 +750 -0
- package/src/seat-loop/seat-report.ts +99 -0
- package/src/self-dev/decompose.ts +66 -11
- 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 +38 -33
- package/src/self-dev/draft-triage-rules.ts +8 -5
- 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/run-store.ts +10 -1
- 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/execution-clarification.ts +115 -0
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +46 -4
- package/src/self-implement/gate-scope.ts +37 -7
- 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 +22 -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 +279 -12
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/run-status-mapping.ts +5 -3
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +1 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/chat.ts +2 -0
- package/src/session/index.ts +45 -13
- package/src/shell-env-bootstrap.ts +17 -0
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/directive.ts +51 -2
- package/src/steward/launch.ts +266 -0
- package/src/steward/outcome-notify.ts +115 -0
- package/src/steward/steward-cards.ts +25 -11
- package/src/steward/triage-plan.ts +32 -4
- package/src/steward/triage.ts +129 -13
- package/src/steward/working-backwards.ts +64 -0
- package/src/storyboard/storyboard.ts +141 -0
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-cards/card-store.ts +2 -2
- 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 +29 -7
- package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -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 +153 -0
- package/src/telegram-commands.ts +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +29 -19
- package/src/telegram-run.ts +7 -0
- package/src/telegram.ts +58 -11
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/tool-runtime/types.ts +4 -0
- package/src/tui-client/dashboard-session.ts +2 -0
- package/src/user-config.ts +216 -27
- package/src/version/packed-revision.json +1 -1
- package/src/web-push/notify-exec-request.ts +32 -0
- 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/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.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/2929-617f1e66a55be184.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/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.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/7032-f4ef698518fba61c.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/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.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-fb7c2d458f32fcad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.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-ec49a466121233ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.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-4647be5dbcbd0795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.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-6870f30ea99f8346.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/f92f63a5df0ba229.css +0 -1
- package/src/dashboard/turn-typeahead-echo.ts +0 -23
- /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* intake check → 골 저작 연결. 판정 「없음」마다 기존 골 중복을 먼저 보고, 없으면 골 저작기로 골 문서를 쓰고 lint 한 뒤 멈춘다.
|
|
3
3
|
* 하니스는 발사하지 않는다. 골: 내부 문서 `ASK-intake-check-authors-goals-for-gaps-2026-09-24`
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { CardStore, taskCardsDir } from '../task-cards/card-store.js';
|
|
6
7
|
import { join, relative } from 'node:path';
|
|
7
8
|
import { debug } from '../debug/log.js';
|
|
8
9
|
import { resolveGoalDocumentsDir } from '../self-implement/goal-documents-dir.js';
|
|
@@ -42,7 +43,22 @@ function defaultGoalDocs(root: string): { path: string; text: string }[] {
|
|
|
42
43
|
.map((name) => ({ path: relative(root, join(dir, name)), text: readFileSync(join(dir, name), 'utf8') }));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function
|
|
46
|
+
function prfaqSignals(sourceRef: string | undefined, root: string): { source: string; signals: string[] } | undefined {
|
|
47
|
+
if (!sourceRef?.startsWith('linear:') || !existsSync(taskCardsDir(root))) return undefined;
|
|
48
|
+
const store = new CardStore(root);
|
|
49
|
+
try {
|
|
50
|
+
const cards = store.listCards();
|
|
51
|
+
const matches = cards.filter(row => row.goalId === sourceRef);
|
|
52
|
+
const card = matches.length === 1 ? matches[0] : undefined;
|
|
53
|
+
const section = [...(card?.sections ?? [])].reverse().find(row => row.key.startsWith('prfaq:'));
|
|
54
|
+
if (!section) return undefined;
|
|
55
|
+
const criteria = section.content.split('⑧ 판정 기준')[1]?.split('⑨')[0] ?? '';
|
|
56
|
+
const signals = criteria.split('\n').map(line => line.trim().replace(/^-\s*/, '')).filter(line => /^판정 신호: 조건 = .+; 관측 = .+; 기대 = .+\.$/.test(line)).slice(0, 2);
|
|
57
|
+
return signals.length ? { source: `${card!.goalId} / ${section.key}`, signals } : undefined;
|
|
58
|
+
} finally { store.close(); }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function authorAsk(item: IntakeCheckItem, evidence?: { source: string; signals: string[] }): string {
|
|
46
62
|
// The first line leads the ask and `제목:` is the title the author forwards (parseAskProseTitle), so both are the fact.
|
|
47
63
|
return [
|
|
48
64
|
item.fact,
|
|
@@ -52,9 +68,23 @@ function authorAsk(item: IntakeCheckItem): string {
|
|
|
52
68
|
'백틱 이름은 대조에 쓴 검색 표지일 뿐이다 — 그 문자열이 저장소에 생기는 것을 판정 신호로 삼지 말고, 사실이 말하는 동작을 판정 신호로 쓴다.',
|
|
53
69
|
`대조 결과: ${item.current}`,
|
|
54
70
|
...(item.quotes.length > 0 ? [`출처 인용: ${item.quotes.join(' / ')}`] : []),
|
|
71
|
+
...(evidence ? [`아래 판정 신호를 골 문서 ## 판정 신호에 원문 그대로 인용하라. 출처: 카드 ${evidence.source}`, ...evidence.signals] : []),
|
|
55
72
|
].join('\n');
|
|
56
73
|
}
|
|
57
74
|
|
|
75
|
+
function appendPrfaqSignals(document: string, evidence: { source: string; signals: string[] }): string {
|
|
76
|
+
const section = /^## 판정 신호\s*$/m.exec(document);
|
|
77
|
+
if (!section) throw new Error('Authored goal has no ## 판정 신호 section');
|
|
78
|
+
const start = section.index + section[0].length;
|
|
79
|
+
const end = document.slice(start).search(/^## /m);
|
|
80
|
+
const boundary = end < 0 ? document.length : start + end;
|
|
81
|
+
const block = document.slice(start, boundary);
|
|
82
|
+
const missing = evidence.signals.filter(signal => !block.includes(signal));
|
|
83
|
+
const source = block.includes(`출처: 카드 ${evidence.source}`) ? '' : `출처: 카드 ${evidence.source}\n`;
|
|
84
|
+
if (!missing.length && !source) return document;
|
|
85
|
+
return document.slice(0, boundary) + `\n${source}${missing.map(signal => `- ${signal}`).join('\n')}\n` + document.slice(boundary);
|
|
86
|
+
}
|
|
87
|
+
|
|
58
88
|
export async function authorIntakeGoals(
|
|
59
89
|
items: readonly IntakeCheckItem[],
|
|
60
90
|
deps: IntakeAuthorDeps,
|
|
@@ -84,8 +114,11 @@ export async function authorIntakeGoals(
|
|
|
84
114
|
}
|
|
85
115
|
authored++;
|
|
86
116
|
try {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
117
|
+
const evidence = prfaqSignals(item.sourceRef, deps.root);
|
|
118
|
+
const written = await deps.author(authorAsk(item, evidence), `intake check: ${opts.source}`);
|
|
119
|
+
const document = evidence ? appendPrfaqSignals(written.document, evidence) : written.document;
|
|
120
|
+
if (document !== written.document) writeFileSync(join(deps.root, written.path), document);
|
|
121
|
+
const lintErrors = deps.lintErrors(document);
|
|
89
122
|
outcomes.push({ fact: item.fact, status: 'authored', goalPath: written.path, lintErrors });
|
|
90
123
|
} catch (error) {
|
|
91
124
|
outcomes.push({ fact: item.fact, status: 'author-failed', reason: error instanceof Error ? error.message : String(error) });
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
} from './enrich.js';
|
|
26
26
|
import { summariseContext } from './enrich.js';
|
|
27
27
|
import { buildResponseFormatHeader } from './prompt-format.js';
|
|
28
|
+
import { judge } from '../llm/judge-layer.js';
|
|
28
29
|
|
|
29
30
|
// ──────────────────── Closed sets ──────────────────────────────────────
|
|
30
31
|
|
|
@@ -69,7 +70,7 @@ export interface CategorizeResult {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export interface CategorizeCallable {
|
|
72
|
-
(args: { prompt: string; signal?: AbortSignal }): Promise<{
|
|
73
|
+
(args: { prompt: string; signal?: AbortSignal; provider?: string; model?: string }): Promise<{
|
|
73
74
|
text: string;
|
|
74
75
|
promptTokens?: number;
|
|
75
76
|
completionTokens?: number;
|
|
@@ -219,26 +220,6 @@ export function buildCategorizePrompt(decomposition: EnrichedDecomposition): str
|
|
|
219
220
|
|
|
220
221
|
// ──────────────────── Parse + coerce ──────────────────────────────────
|
|
221
222
|
|
|
222
|
-
function extractJsonBlock(text: string): unknown | null {
|
|
223
|
-
const trimmed = text.trim();
|
|
224
|
-
const fence = trimmed.match(/```(?:json)?\n([\s\S]*?)```/);
|
|
225
|
-
const candidate = fence ? fence[1]! : trimmed;
|
|
226
|
-
try {
|
|
227
|
-
return JSON.parse(candidate);
|
|
228
|
-
} catch {
|
|
229
|
-
const open = candidate.indexOf('{');
|
|
230
|
-
const close = candidate.lastIndexOf('}');
|
|
231
|
-
if (open >= 0 && close > open) {
|
|
232
|
-
try {
|
|
233
|
-
return JSON.parse(candidate.slice(open, close + 1));
|
|
234
|
-
} catch {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
223
|
function coerceConfidence(v: unknown): 'high' | 'medium' | 'low' {
|
|
243
224
|
if (v === 'high' || v === 'medium' || v === 'low') return v;
|
|
244
225
|
return 'medium';
|
|
@@ -306,27 +287,18 @@ export async function categorizeDecomposition(
|
|
|
306
287
|
|
|
307
288
|
const prompt = buildCategorizePrompt(decomposition);
|
|
308
289
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
'LLM_CALL_FAILED',
|
|
316
|
-
err instanceof Error ? err.message : String(err),
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
categorizations: Object.fromEntries(tasks.map((t) => [t.key, fallbackRow(t.key)])),
|
|
321
|
-
fallback: true,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const parsed = extractJsonBlock(raw.text);
|
|
326
|
-
const coerced = parsed ? coerceCategorizations(parsed, validKeys) : null;
|
|
327
|
-
if (coerced === null) {
|
|
290
|
+
const decision = await judge({
|
|
291
|
+
site: 'intake.categorize', prompt, signal: opts.signal,
|
|
292
|
+
call: ({ prompt, signal, provider, model }) => opts.callable({ prompt, signal, provider, model }),
|
|
293
|
+
schema: (parsed) => coerceCategorizations(parsed, validKeys),
|
|
294
|
+
});
|
|
295
|
+
if (!decision.ok) {
|
|
328
296
|
if (opts.strict) {
|
|
329
|
-
|
|
297
|
+
if (decision.reason === 'call') {
|
|
298
|
+
throw new CategorizeError('LLM_CALL_FAILED',
|
|
299
|
+
decision.error instanceof Error ? decision.error.message : String(decision.error));
|
|
300
|
+
}
|
|
301
|
+
throw new CategorizeError('PARSE_FAILED', 'no usable categorizations', decision.rawText);
|
|
330
302
|
}
|
|
331
303
|
return {
|
|
332
304
|
categorizations: Object.fromEntries(tasks.map((t) => [t.key, fallbackRow(t.key)])),
|
|
@@ -336,7 +308,7 @@ export async function categorizeDecomposition(
|
|
|
336
308
|
|
|
337
309
|
// Backfill any missing tasks with the fallback shape so callers can
|
|
338
310
|
// index by key without null checks.
|
|
339
|
-
const filled: Record<string, TaskCategorization> = { ...
|
|
311
|
+
const filled: Record<string, TaskCategorization> = { ...decision.value };
|
|
340
312
|
for (const t of tasks) {
|
|
341
313
|
if (!filled[t.key]) filled[t.key] = fallbackRow(t.key);
|
|
342
314
|
}
|
|
@@ -344,10 +316,10 @@ export async function categorizeDecomposition(
|
|
|
344
316
|
categorizations: filled,
|
|
345
317
|
fallback: false,
|
|
346
318
|
usage: {
|
|
347
|
-
promptTokens:
|
|
348
|
-
completionTokens:
|
|
349
|
-
costUsd:
|
|
350
|
-
modelId:
|
|
319
|
+
promptTokens: decision.reply.promptTokens,
|
|
320
|
+
completionTokens: decision.reply.completionTokens,
|
|
321
|
+
costUsd: decision.reply.costUsd,
|
|
322
|
+
modelId: decision.reply.modelId,
|
|
351
323
|
},
|
|
352
324
|
};
|
|
353
325
|
}
|
|
@@ -63,6 +63,8 @@ export interface IntakeCheckEvidence {
|
|
|
63
63
|
|
|
64
64
|
export interface IntakeCheckItem {
|
|
65
65
|
readonly fact: string;
|
|
66
|
+
/** Source identity preserved from the supplied fact for card-backed goal evidence. */
|
|
67
|
+
readonly sourceRef?: string;
|
|
66
68
|
readonly originalClaims?: readonly string[];
|
|
67
69
|
readonly quotes: readonly string[];
|
|
68
70
|
readonly verdict: IntakeCheckVerdict;
|
|
@@ -1415,6 +1417,7 @@ export function runIntakeCheck(
|
|
|
1415
1417
|
const item: IntakeCheckItem = {
|
|
1416
1418
|
...judged,
|
|
1417
1419
|
quotes: [quote],
|
|
1420
|
+
...(fact.sourceRef ? { sourceRef: fact.sourceRef } : {}),
|
|
1418
1421
|
...(fact.originalClaim ? { originalClaims: [fact.originalClaim] } : {}),
|
|
1419
1422
|
};
|
|
1420
1423
|
merged.set(key, item);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { discordDecisionOwner } from '../decisions/discord-decision-cards.js';
|
|
3
|
+
import { canonicalSeatId } from '../msg/msg-store.js';
|
|
4
|
+
import { dispatchCeoTask, type CeoCommandDeps } from '../seat-dispatch/ceo-commands.js';
|
|
5
|
+
import { ceoTaskDeps, classifyCeoIntent } from '../seat-dispatch/ceo-intent.js';
|
|
6
|
+
import { askSeat, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
|
|
1
7
|
import { parseSeatAddress, resolveSeat } from '../seat-address/seat-address.js';
|
|
8
|
+
import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
9
|
+
import { answerAsSeat } from './seat-answer.js';
|
|
2
10
|
import {
|
|
3
11
|
submitIntakeWork,
|
|
4
12
|
type SubmitIntakeWorkDeps,
|
|
@@ -7,21 +15,56 @@ import {
|
|
|
7
15
|
export interface DiscordSeatWorkMessage {
|
|
8
16
|
channelId: string;
|
|
9
17
|
messageId: string;
|
|
18
|
+
userId?: string;
|
|
19
|
+
isDm?: boolean;
|
|
10
20
|
threadId?: string;
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
export interface DiscordSeatWorkDeps extends SubmitIntakeWorkDeps {
|
|
14
24
|
submit?: typeof submitIntakeWork;
|
|
25
|
+
config?: UserConfig;
|
|
26
|
+
answer?: typeof answerAsSeat;
|
|
27
|
+
dispatch?: typeof dispatchCeoTask;
|
|
28
|
+
commandDeps?: CeoCommandDeps;
|
|
29
|
+
askDeps?: SeatAskDeps;
|
|
15
30
|
}
|
|
16
31
|
|
|
17
|
-
/** Addressed
|
|
32
|
+
/** Addressed work preserves graph intake except for one owner's private seat request. */
|
|
18
33
|
export async function handleDiscordSeatWork(
|
|
19
34
|
text: string,
|
|
20
35
|
msg: DiscordSeatWorkMessage,
|
|
21
36
|
deps: DiscordSeatWorkDeps = {},
|
|
22
37
|
): Promise<string | null> {
|
|
38
|
+
const owner = (): { cfg: UserConfig; id: string } | null => {
|
|
39
|
+
if (!msg.isDm || !msg.userId) return null;
|
|
40
|
+
const cfg = deps.config ?? getUserConfig();
|
|
41
|
+
const id = discordDecisionOwner(cfg);
|
|
42
|
+
return id && id === msg.userId ? { cfg, id } : null;
|
|
43
|
+
};
|
|
44
|
+
const sendTask = async (seat: string, body: string, cfg: UserConfig, id: string): Promise<string> => {
|
|
45
|
+
const result = await (deps.dispatch ?? dispatchCeoTask)(seat, body, deps.commandDeps ?? ceoTaskDeps(cfg, id), { via: 'discord' });
|
|
46
|
+
debug.log('seat.dispatch', 'intent', { seat, intent: 'task', via: 'discord', outcome: result.channel });
|
|
47
|
+
return result.reply;
|
|
48
|
+
};
|
|
49
|
+
if (parseSeatAsk(text)) {
|
|
50
|
+
const auth = owner();
|
|
51
|
+
if (!auth) return null;
|
|
52
|
+
if (!deps.askDeps) throw new Error('seat ask delivery unavailable');
|
|
53
|
+
return askSeat(text, { channel: 'discord', channelId: msg.channelId, messageId: msg.messageId,
|
|
54
|
+
...(msg.threadId ? { threadId: msg.threadId } : {}) },
|
|
55
|
+
deps.commandDeps ?? ceoTaskDeps(auth.cfg, auth.id), deps.askDeps);
|
|
56
|
+
}
|
|
23
57
|
const address = parseSeatAddress(text);
|
|
24
|
-
if (!address)
|
|
58
|
+
if (!address) {
|
|
59
|
+
if (!text.trim() || /^\s*[/@@]/.test(text)) return null;
|
|
60
|
+
const auth = owner();
|
|
61
|
+
const configured = (auth?.cfg.raw?.seatDispatch as { defaultSeat?: unknown } | undefined)?.defaultSeat;
|
|
62
|
+
if (typeof configured !== 'string' || classifyCeoIntent(text) !== 'task') return null;
|
|
63
|
+
const seat = resolveSeat(configured);
|
|
64
|
+
if (!seat) return null;
|
|
65
|
+
const id = canonicalSeatId(seat.id);
|
|
66
|
+
return ['OP', 'TC', 'MK', 'UX'].includes(id) ? sendTask(id, text, auth!.cfg, auth!.id) : null;
|
|
67
|
+
}
|
|
25
68
|
|
|
26
69
|
const seats = address.seats.map((name) => ({ name, seat: resolveSeat(name) }));
|
|
27
70
|
const unknown = seats.filter(({ seat }) => !seat).map(({ name }) => `@${name}`);
|
|
@@ -29,6 +72,18 @@ export async function handleDiscordSeatWork(
|
|
|
29
72
|
const body = address.body.trim();
|
|
30
73
|
if (!body) return '어떤 일을 맡길까요? 좌석 주소 뒤에 요청 내용을 적어 다시 보내 주세요.';
|
|
31
74
|
|
|
75
|
+
if (seats.length === 1 && seats[0]?.seat) {
|
|
76
|
+
const auth = owner();
|
|
77
|
+
const seatId = canonicalSeatId(seats[0].seat.id);
|
|
78
|
+
if (auth && ['OP', 'TC', 'MK', 'UX'].includes(seatId)) {
|
|
79
|
+
const intent = classifyCeoIntent(body);
|
|
80
|
+
if (intent === 'task') return sendTask(seatId, body, auth.cfg, auth.id);
|
|
81
|
+
const answer = await (deps.answer ?? answerAsSeat)(seats[0].name, body);
|
|
82
|
+
debug.log('seat.dispatch', 'intent', { seat: seatId, intent, via: 'discord', outcome: answer ? 'answered' : 'intake' });
|
|
83
|
+
if (answer) return answer.text;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
32
87
|
const reportTo = {
|
|
33
88
|
channel: 'discord' as const,
|
|
34
89
|
channelId: msg.channelId,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { debug } from '../debug/log.js';
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import { extractJsonBlock } from './decompose.js';
|
|
2
|
+
import type { ResolveRoleProviderFn, StreamLlmFn } from './runtime-callables.js';
|
|
3
|
+
import { judge } from '../llm/judge-layer.js';
|
|
5
4
|
import {
|
|
6
5
|
INTAKE_FRONT_ROUTE_TRACKS,
|
|
7
6
|
type IntakeFrontRouteDecision,
|
|
@@ -14,17 +13,6 @@ interface ClassifierDeps {
|
|
|
14
13
|
minConfidence?: number;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
function loadRoleProvider(): ResolveRoleProviderFn {
|
|
18
|
-
const { resolveRoleLlm } = require('../user-config.js') as {
|
|
19
|
-
resolveRoleLlm: (role: ModelRole) => { provider: string; model: string };
|
|
20
|
-
};
|
|
21
|
-
const { PROVIDERS } = require('../llm.js') as { PROVIDERS: ProviderRegistry };
|
|
22
|
-
return (role) => {
|
|
23
|
-
const resolved = resolveRoleLlm(role);
|
|
24
|
-
return { provider: PROVIDERS[resolved.provider] ?? { name: resolved.provider }, model: resolved.model };
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
16
|
/** Classify only after the deterministic front-route rules return rule-unknown. */
|
|
29
17
|
export async function classifyIntakeFrontRoute(
|
|
30
18
|
text: string,
|
|
@@ -43,32 +31,38 @@ export async function classifyIntakeFrontRoute(
|
|
|
43
31
|
const failed = (cause: string) => finish('ask-human', 0, `classifier-failed:${cause}`, 'failed');
|
|
44
32
|
|
|
45
33
|
try {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
34
|
+
const decision = await judge({
|
|
35
|
+
site: 'intake.front-route',
|
|
36
|
+
prompt: text,
|
|
37
|
+
messages: [
|
|
38
|
+
{ role: 'system', content: [
|
|
39
|
+
'Classify the intake text into exactly one track:',
|
|
40
|
+
'absorb: read, save, or summarize external material.',
|
|
41
|
+
'tasks: a list of independent to-dos.',
|
|
42
|
+
'graph: an implementation or multi-step workflow request.',
|
|
43
|
+
'ask-human: ambiguous intent requiring human choice.',
|
|
44
|
+
'Return only one JSON object: {"track":"...","confidence":0.0,"reason":"..."}. Confidence must be between 0 and 1.',
|
|
45
|
+
].join('\n') },
|
|
46
|
+
{ role: 'user', content: text },
|
|
47
|
+
],
|
|
48
|
+
streamLLM: deps.streamLLM,
|
|
49
|
+
resolveRoleProvider: deps.resolveRoleProvider,
|
|
50
|
+
schema: (parsed) => {
|
|
51
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
52
|
+
const candidate = parsed as Record<string, unknown>;
|
|
53
|
+
if (typeof candidate.track !== 'string' ||
|
|
54
|
+
!(INTAKE_FRONT_ROUTE_TRACKS as readonly string[]).includes(candidate.track)) return null;
|
|
55
|
+
if (typeof candidate.confidence !== 'number' || !Number.isFinite(candidate.confidence) ||
|
|
56
|
+
candidate.confidence < 0 || candidate.confidence > 1) return null;
|
|
57
|
+
if (typeof candidate.reason !== 'string') return null;
|
|
58
|
+
return { track: candidate.track as IntakeFrontRouteTrack, confidence: candidate.confidence, reason: candidate.reason };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
provider = decision.ok ? decision.provider : provider;
|
|
62
|
+
if (!decision.ok) return failed(decision.reason === 'schema' ? 'invalid-json' : 'llm-error');
|
|
63
|
+
const candidate = decision.value;
|
|
70
64
|
|
|
71
|
-
const track = candidate.track
|
|
65
|
+
const track = candidate.track;
|
|
72
66
|
const confidence = candidate.confidence;
|
|
73
67
|
if (confidence < (deps.minConfidence ?? 0.6)) {
|
|
74
68
|
return finish('ask-human', confidence, `classifier-low-confidence:${track}`, 'low-confidence');
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { resolveMention, type PersonaSource } from '../persona/mention-parser.js';
|
|
3
|
+
import type { PersonaProfile } from '../persona/types.js';
|
|
4
|
+
|
|
5
|
+
export interface PersonaAnswerDeps {
|
|
6
|
+
complete?: (prompt: string) => Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Resolve the registry mention first, then an exact display name. */
|
|
10
|
+
export function resolvePersonaAddress(name: string, source: PersonaSource): PersonaProfile | null {
|
|
11
|
+
const mention = resolveMention(name, source);
|
|
12
|
+
if (mention) return mention;
|
|
13
|
+
const needle = name.replace(/^@/, '').toLocaleLowerCase();
|
|
14
|
+
return source.list().find((persona) => persona.displayName.toLocaleLowerCase() === needle) ?? null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function defaultComplete(prompt: string): Promise<string> {
|
|
18
|
+
const { streamLLM } = await import('../llm.js');
|
|
19
|
+
const { tierModel } = await import('../llm/model-defaults.js');
|
|
20
|
+
return streamLLM([{ role: 'user', content: prompt }], () => {}, { model: tierModel('better') });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function answerAsPersona(persona: PersonaProfile, question: string, deps: PersonaAnswerDeps = {}): Promise<string> {
|
|
24
|
+
const prompt = `${persona.systemPrompt ?? `너는 ${persona.displayName} 이다`}\n\n## 질문\n${question}`;
|
|
25
|
+
const raw = await (deps.complete ?? defaultComplete)(prompt);
|
|
26
|
+
const answer = `@${persona.displayName}\n${raw.trim()}`;
|
|
27
|
+
const text = Array.from(answer).slice(0, 3_500).join('');
|
|
28
|
+
debug.log('persona.address', 'answered', { personaId: persona.personaId, chars: Array.from(text).length });
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
@@ -305,7 +305,14 @@ export function buildRealIntakeCallables(
|
|
|
305
305
|
};
|
|
306
306
|
|
|
307
307
|
const decompose: DecomposeMemoCallable = (args) => promptCallable(args);
|
|
308
|
-
const categorize: CategorizeCallable = (args) =>
|
|
308
|
+
const categorize: CategorizeCallable = async (args) => {
|
|
309
|
+
const text = await streamLLM([{ role: 'user', content: args.prompt }], () => {}, {
|
|
310
|
+
...(args.provider ? { provider: providers[args.provider] } : {}),
|
|
311
|
+
...(args.model ? { model: args.model } : {}),
|
|
312
|
+
...(args.signal ? { signal: args.signal } : {}), usageRole: 'classify',
|
|
313
|
+
});
|
|
314
|
+
return { text, modelId: args.model };
|
|
315
|
+
};
|
|
309
316
|
const align: AlignCallable = (args) => promptCallable(args);
|
|
310
317
|
|
|
311
318
|
/** Synth callable — wraps R3 `synthWorkflowFromIntent`. Hands it a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
2
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import type { MissionOrigin } from '../autopilot/mission-origin.js';
|
|
6
6
|
import { debug } from '../debug/log.js';
|
|
@@ -15,7 +15,7 @@ import { answerAsSeat, isNoGraphFailure } from './seat-answer.js';
|
|
|
15
15
|
const POLL_MS = 5_000;
|
|
16
16
|
const TIMEOUT_MS = 30 * 60_000;
|
|
17
17
|
const DOC = /전략|한\s*장|원페이저|기획|보도자료|글|메모|초안|\b(?:one-pager|strategy|post|memo|draft)\b/i;
|
|
18
|
-
const CODE = /구현|버그|고쳐|\b(?:fix|implement
|
|
18
|
+
const CODE = /구현|버그|고쳐|\b(?:fix|implement)\b|\bPR\b(?!\s*#?\d+\b)/i;
|
|
19
19
|
const RESEARCH = /전략|시장|경쟁|\bstrategy\b/i;
|
|
20
20
|
/** EV12a — a question or a request for status / a report is answered, never turned into a code goal. */
|
|
21
21
|
const ASK = /상황|현황|진행|준비|어때|어떻게|알려|보고|정리|요약|공유|\?|?|\b(?:status|update|report|summary|summari[sz]e|how|what)\b/i;
|
|
@@ -30,6 +30,8 @@ export interface SeatDocDeps {
|
|
|
30
30
|
submitExec?: (text: string) => Promise<{ id: string }>;
|
|
31
31
|
getExec?: (id: string) => Promise<ExecSnapshot | null>;
|
|
32
32
|
getFile?: (path: string) => Promise<string>;
|
|
33
|
+
/** Resolve only references explicitly supplied in the request; null means their claims cannot be verified. */
|
|
34
|
+
readSource?: (ref: string) => Promise<string | null>;
|
|
33
35
|
publishFile?: (content: string, key: string) => string | null;
|
|
34
36
|
sendOutbound?: typeof sendOutbound;
|
|
35
37
|
sleep?: (ms: number) => Promise<void>;
|
|
@@ -97,6 +99,160 @@ async function execFile(path: string): Promise<string> {
|
|
|
97
99
|
return (await execResponse(path.slice('/v1/exec-requests'.length))).text();
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
const SUPPLIED_REF = /https?:\/\/[^\s<>()[\]]+|\bPR\s*#?\d+\b|(?:\.?\.?\/|[\w.-]+\/)[\w./-]+\.(?:md|txt|csv|json|pdf)\b|\b[\w.-]+\.(?:md|txt|csv|json|pdf)\b/gi;
|
|
103
|
+
// Keep units and grammatical particles with the figure; never backtrack from 42% to 42.
|
|
104
|
+
const NUMBER = /(?<![\p{L}\p{N}_#.,])([+-]?\d+(?:[,.]\d+)*(?:(?:%|년|월|일|명|건|원|배)(?![\p{N}_%A-Za-z]|[.,]\d)|(?![\p{L}\p{N}_%]|[.,]\d)))(은|는|이|가|을|를|에|의|도|만|에서|으로|와|과)?/gu;
|
|
105
|
+
|
|
106
|
+
function suppliedReferences(text: string): string[] {
|
|
107
|
+
return [...new Set([...text.matchAll(SUPPLIED_REF)].map(([ref]) => ref!.replace(/[.,;]+$/, '')))];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const SOURCE_TIMEOUT_MS = 5_000;
|
|
111
|
+
const MAX_SOURCE_BYTES = 200_000;
|
|
112
|
+
// The default resolver only reads repository-local files and these two public HTTPS hosts.
|
|
113
|
+
const SOURCE_URL_HOSTS = new Set(['github.com', 'raw.githubusercontent.com']);
|
|
114
|
+
|
|
115
|
+
function allowedSourceUrl(value: string): boolean {
|
|
116
|
+
try {
|
|
117
|
+
const url = new URL(value);
|
|
118
|
+
return url.protocol === 'https:' && SOURCE_URL_HOSTS.has(url.hostname)
|
|
119
|
+
&& !url.username && !url.password && !url.port;
|
|
120
|
+
} catch { return false; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function readPublicSource(ref: string): Promise<string | null> {
|
|
124
|
+
let url = ref;
|
|
125
|
+
const signal = AbortSignal.timeout(SOURCE_TIMEOUT_MS);
|
|
126
|
+
for (let redirects = 0; redirects < 3; redirects++) {
|
|
127
|
+
if (!allowedSourceUrl(url)) return null;
|
|
128
|
+
const response = await fetch(url, { signal, redirect: 'manual' });
|
|
129
|
+
if (response.status >= 300 && response.status < 400) {
|
|
130
|
+
const location = response.headers.get('location');
|
|
131
|
+
if (!location) return null;
|
|
132
|
+
url = new URL(location, url).href;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!response.ok || !/^(?:text\/|application\/(?:json|xml))/.test(response.headers.get('content-type') ?? '')) return null;
|
|
136
|
+
if (Number(response.headers.get('content-length') ?? 0) > MAX_SOURCE_BYTES) {
|
|
137
|
+
await response.body?.cancel();
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (!response.body) return null;
|
|
141
|
+
const reader = response.body.getReader();
|
|
142
|
+
const chunks: Uint8Array[] = [];
|
|
143
|
+
let size = 0;
|
|
144
|
+
try {
|
|
145
|
+
while (true) {
|
|
146
|
+
const { done, value } = await reader.read();
|
|
147
|
+
if (done) break;
|
|
148
|
+
size += value.byteLength;
|
|
149
|
+
if (size > MAX_SOURCE_BYTES) return null;
|
|
150
|
+
chunks.push(value);
|
|
151
|
+
}
|
|
152
|
+
const bytes = new Uint8Array(size);
|
|
153
|
+
let offset = 0;
|
|
154
|
+
for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }
|
|
155
|
+
return new TextDecoder().decode(bytes);
|
|
156
|
+
} finally {
|
|
157
|
+
if (size > MAX_SOURCE_BYTES) await reader.cancel();
|
|
158
|
+
reader.releaseLock();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function defaultReadSource(ref: string): Promise<string | null> {
|
|
165
|
+
try {
|
|
166
|
+
if (/^https?:\/\//i.test(ref)) return await readPublicSource(ref);
|
|
167
|
+
if (/^PR\s*#?\d+$/i.test(ref)) {
|
|
168
|
+
const number = ref.match(/\d+/)![0];
|
|
169
|
+
const child = Bun.spawn(['gh', 'pr', 'view', number, '--json', 'title,body'], { stdout: 'pipe', stderr: 'ignore' });
|
|
170
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
171
|
+
const timeout = new Promise<null>(finish => {
|
|
172
|
+
timer = setTimeout(() => { child.kill(9); finish(null); }, SOURCE_TIMEOUT_MS);
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
const result = await Promise.race([
|
|
176
|
+
Promise.all([new Response(child.stdout).text(), child.exited]).then(([out, code]) => ({ out, code })),
|
|
177
|
+
timeout,
|
|
178
|
+
]);
|
|
179
|
+
if (!result || result.code !== 0 || result.out.length > MAX_SOURCE_BYTES) return null;
|
|
180
|
+
const pr = JSON.parse(result.out) as { title?: string; body?: string };
|
|
181
|
+
return `${pr.title ?? ''}\n${pr.body ?? ''}`;
|
|
182
|
+
} finally { clearTimeout(timer); }
|
|
183
|
+
}
|
|
184
|
+
const root = realpathSync(process.cwd());
|
|
185
|
+
const actual = realpathSync(resolve(root, ref));
|
|
186
|
+
const within = relative(root, actual);
|
|
187
|
+
if (isAbsolute(within) || within === '..' || within.startsWith('../')) return null;
|
|
188
|
+
const file = statSync(actual);
|
|
189
|
+
if (!file.isFile() || file.size > MAX_SOURCE_BYTES || /\.pdf$/i.test(actual)) return null;
|
|
190
|
+
return readFileSync(actual, 'utf8');
|
|
191
|
+
} catch { return null; }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function citationFor(ref: string): string {
|
|
195
|
+
return /^PR\s*#?(\d+)$/i.test(ref) ? `PR #${ref.match(/\d+/)![0]}` : ref;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function claimLabel(line: string, offset: number, length: number): string | null {
|
|
199
|
+
const before = line.slice(0, offset).split(/[,;:,;。.!?\n]/).at(-1) ?? '';
|
|
200
|
+
const after = (line.slice(offset + length).split(/[,;:,;。.!?\n]/)[0] ?? '').replace(/^\s*(?:\[출처: [^\]]+\]|«확인 필요»)/, '');
|
|
201
|
+
// Never borrow a label from another figure in the same clause.
|
|
202
|
+
if ([...before.matchAll(NUMBER), ...after.matchAll(NUMBER)].length) return null;
|
|
203
|
+
const left = before.trim().replace(/^(?:[#*>-]+\s*)+/, '');
|
|
204
|
+
const right = after.match(/^\s*([\p{L}]{2,}(?:\s+[\p{L}]{2,})*)/u)?.[1];
|
|
205
|
+
return /[\p{L}]{2,}/u.test(left) ? left : right ?? null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const CLAIM_DIRECTION = /증가|상승|성장|확대|개선|감소|하락|축소|악화/g;
|
|
209
|
+
const CLAIM_FORECAST = /예상|전망|예측|추정|계획|예정|기대|목표|가능성/;
|
|
210
|
+
// Range and approximation words change what a figure claims («42%» ≠ «42% 미만»); both sides must carry the same set.
|
|
211
|
+
const CLAIM_QUALIFIER = /미만|이하|이상|초과|이내|약|최대|최소|가량|정도|내외|안팎|넘게|넘는|남짓|까지|부터|대략|거의|이상의|under|over|about|up to|at least|at most|less than|more than|nearly|almost/giu;
|
|
212
|
+
|
|
213
|
+
function claimDirection(segment: string): string | undefined {
|
|
214
|
+
const directions = [...segment.matchAll(CLAIM_DIRECTION)];
|
|
215
|
+
if (directions.length > 1) return undefined;
|
|
216
|
+
const direction = directions[0];
|
|
217
|
+
const after = direction ? segment.slice(direction.index + direction[0].length) : '';
|
|
218
|
+
const negated = direction && /^\s*(?:하지\s*(?:않|못)|않|못|안\s*(?:했|함)|없|아니)/u.test(after);
|
|
219
|
+
// Polarity outside the recognized predicate is ambiguous; do not use it to establish provenance.
|
|
220
|
+
if (/않|못|안\s|없|아니/u.test(segment) && !negated) return undefined;
|
|
221
|
+
const qualifiers = [...new Set([...segment.matchAll(CLAIM_QUALIFIER)].map(match => match[0].toLowerCase().replace(/^이상의$/, '이상')))].sort().join(',');
|
|
222
|
+
return `${direction?.[0] ?? ''}|${negated ? 'negated' : 'affirmed'}|${CLAIM_FORECAST.test(segment) ? 'forecast' : 'observed'}|${qualifiers}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function hasClaimPair(source: string, number: string, label: string, direction: string | undefined): boolean {
|
|
226
|
+
if (direction === undefined) return false;
|
|
227
|
+
return source.split(/\n|,(?=\s)|[;:,;。!?]|\.(?!\d)/).some(segment => {
|
|
228
|
+
const figures = [...segment.matchAll(NUMBER)];
|
|
229
|
+
if (figures.length !== 1 || figures[0]![1] !== number) return false;
|
|
230
|
+
return claimLabel(segment, figures[0]!.index, figures[0]![0].length) === label
|
|
231
|
+
&& claimDirection(segment) === direction;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function citeDocumentNumbers(content: string, sources: Map<string, string | null>): string {
|
|
236
|
+
let fenced = false;
|
|
237
|
+
return content.split('\n').map(line => {
|
|
238
|
+
if (/^\s*```/.test(line)) { fenced = !fenced; return line; }
|
|
239
|
+
if (fenced || /^\s*(?:참고 자료|출처:)/.test(line)) return line;
|
|
240
|
+
const occupied = [...line.matchAll(/https?:\/\/\S+|\[출처: [^\]]+\]|(?:\.?\.?\/|[\w.-]+\/)[\w./-]+\.(?:md|txt|csv|json|pdf)|\b[\w.-]+\.(?:md|txt|csv|json|pdf)\b/g)]
|
|
241
|
+
.map(match => [match.index, match.index + match[0].length] as const);
|
|
242
|
+
const annotated = new RegExp(`${NUMBER.source}(?:\\s*(?:\\[출처: [^\\]]+\\]|«확인 필요»))?`, 'gu');
|
|
243
|
+
return line.replace(annotated, (claim, number: string, particle: string | undefined, offset: number) => {
|
|
244
|
+
if (occupied.some(([start, end]) => offset >= start && offset < end)) return claim;
|
|
245
|
+
const figure = `${number}${particle ?? ''}`;
|
|
246
|
+
const label = claimLabel(line, offset, claim.length);
|
|
247
|
+
const before = line.slice(0, offset).split(/[,;:,;。.!?]/).at(-1) ?? '';
|
|
248
|
+
const after = line.slice(offset + claim.length).split(/[,;:,;。.!?]/)[0] ?? '';
|
|
249
|
+
const direction = claimDirection(`${before} ${after}`);
|
|
250
|
+
const matches = label ? [...sources].filter(([, source]) => source && hasClaimPair(source, number, label, direction)) : [];
|
|
251
|
+
return matches.length === 1 ? `${figure} [출처: ${citationFor(matches[0]![0])}]` : `${figure} «확인 필요»`;
|
|
252
|
+
});
|
|
253
|
+
}).join('\n');
|
|
254
|
+
}
|
|
255
|
+
|
|
100
256
|
function chatPwaUrl(): string | undefined {
|
|
101
257
|
const endpoint = resolveDaemonEndpoint({ purpose: 'write' });
|
|
102
258
|
return endpoint?.pwaUrl;
|
|
@@ -115,14 +271,15 @@ function accessiblePwaUrl(value: string | undefined): string | undefined {
|
|
|
115
271
|
} catch { return undefined; }
|
|
116
272
|
}
|
|
117
273
|
|
|
118
|
-
async function fileLinks(id: string, result: ExecSnapshot, deps: SeatDocDeps): Promise<Map<string, string>> {
|
|
274
|
+
async function fileLinks(id: string, result: ExecSnapshot, deps: SeatDocDeps, sources: Map<string, string | null>): Promise<Map<string, string>> {
|
|
119
275
|
const links = new Map<string, string>();
|
|
120
276
|
for (const entry of result.results) {
|
|
121
277
|
if (entry.kind !== 'report' && entry.kind !== 'text') continue;
|
|
122
278
|
if (!entry.url.startsWith(`/v1/exec-requests/${encodeURIComponent(id)}/files/`)) continue;
|
|
123
279
|
try {
|
|
124
280
|
// The authenticated daemon route is the only source of bytes; a raw file path is never trusted.
|
|
125
|
-
const
|
|
281
|
+
const raw = await (deps.getFile ?? execFile)(entry.url);
|
|
282
|
+
const content = sources.size ? citeDocumentNumbers(raw, sources) : raw;
|
|
126
283
|
const ext = entry.kind === 'report' ? 'md' : 'txt';
|
|
127
284
|
const digest = createHash('sha256').update(content).digest('hex');
|
|
128
285
|
const key = `seat-doc/${encodeURIComponent(id)}/${digest}.${ext}`;
|
|
@@ -158,6 +315,13 @@ export async function submitSeatDocRequest({ text, reportTo, deps = {} }: {
|
|
|
158
315
|
const body = parseSeatAddress(text)?.body.trim() ?? text.trim();
|
|
159
316
|
let withoutResearch = false;
|
|
160
317
|
let requestText = text;
|
|
318
|
+
const refs = suppliedReferences(body);
|
|
319
|
+
const sources = new Map<string, string | null>();
|
|
320
|
+
for (const ref of refs) {
|
|
321
|
+
try { sources.set(ref, await (deps.readSource ?? defaultReadSource)(ref)); }
|
|
322
|
+
catch { sources.set(ref, null); }
|
|
323
|
+
}
|
|
324
|
+
if (refs.length) requestText += '\n\n문서 수치 근거 규칙: 제공된 파일·URL·PR 번호의 실제 내용을 확인하고, 근거가 확인된 각 수치 바로 뒤에 [출처: <파일|PR|명령>] 형식으로 실물 참조를 붙이세요. 근거를 확인할 수 없는 수치는 삭제하거나 바꾸지 말고 «확인 필요»로 표시하세요. 서로 다른 출처에 같은 숫자가 있다는 이유만으로 출처를 추정하지 마세요.';
|
|
161
325
|
if (RESEARCH.test(body)) {
|
|
162
326
|
try {
|
|
163
327
|
const sources = (await (deps.research ?? research)(body)).slice(0, 5);
|
|
@@ -196,7 +360,7 @@ export async function submitSeatDocRequest({ text, reportTo, deps = {} }: {
|
|
|
196
360
|
if (now() < deadline) await sleep(Math.min(POLL_MS, Math.max(0, deadline - now())));
|
|
197
361
|
}
|
|
198
362
|
log('settled', { id, status: result?.status ?? (lookupAvailable ? 'timeout' : 'lookup-unavailable'), lastConfirmedStatus });
|
|
199
|
-
const links = result?.status === 'done' ? await fileLinks(id, result, deps) : new Map<string, string>();
|
|
363
|
+
const links = result?.status === 'done' ? await fileLinks(id, result, deps, sources) : new Map<string, string>();
|
|
200
364
|
let reply = message(id, result, withoutResearch, lookupAvailable, accessiblePwaUrl((deps.pwaUrl ?? chatPwaUrl)()), links);
|
|
201
365
|
if (isNoGraphFailure(result)) {
|
|
202
366
|
// EV12d — a question with no fitting graph gets the seat's own answer, not «문서 요청 실패».
|