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
package/src/git-fs/worktree.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// the pattern; we trim the hook-based fallback (sandboxed envs)
|
|
10
10
|
// because elanous's primary surface runs native git.
|
|
11
11
|
|
|
12
|
-
import { spawnSync, execFileSync } from 'node:child_process';
|
|
12
|
+
import { spawnSync, execFileSync, type SpawnSyncReturns } from 'node:child_process';
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync, rmSync, symlinkSync, lstatSync } from 'node:fs';
|
|
14
14
|
import { homedir } from 'node:os';
|
|
15
15
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
@@ -17,8 +17,33 @@ import { findGitDir } from './locate.js';
|
|
|
17
17
|
import { isTransientGitError } from './retry.js';
|
|
18
18
|
import { runGitCommand } from './runner.js';
|
|
19
19
|
import { debug } from '../debug/log.js';
|
|
20
|
+
import { getUserConfig } from '../user-config.js';
|
|
20
21
|
|
|
21
22
|
const GIT_TIMEOUT_MS = 30_000;
|
|
23
|
+
const WORKTREE_ADD_TIMEOUT_MS = 5 * 60_000;
|
|
24
|
+
|
|
25
|
+
export interface WorktreeAddDeps {
|
|
26
|
+
spawn?: (command: string, args: string[], options: { cwd: string; encoding: 'utf8'; timeout: number; maxBuffer: number }) => SpawnSyncReturns<string>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function worktreeAddTimeoutMs(): number {
|
|
30
|
+
const seconds = getUserConfig().harness?.worktreeAddTimeoutSec;
|
|
31
|
+
return typeof seconds === 'number' && Number.isSafeInteger(seconds) && seconds > 0 && seconds <= 2_147_483
|
|
32
|
+
? seconds * 1000 : WORKTREE_ADD_TIMEOUT_MS;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function worktreeAddFailureLines(stderr: string): { lastProgress: string | null; details: string[] } {
|
|
36
|
+
let lastProgress: string | null = null;
|
|
37
|
+
const details: string[] = [];
|
|
38
|
+
for (const line of stderr.split(/[\r\n]+/).map((value) => value.trim()).filter(Boolean)) {
|
|
39
|
+
if (/^(?:Updating files|Checking out files|Receiving objects|Resolving deltas):\s*\d+%/.test(line)) {
|
|
40
|
+
lastProgress = line;
|
|
41
|
+
} else if (!/^Preparing worktree\b/.test(line)) {
|
|
42
|
+
details.push(line);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { lastProgress, details: details.slice(-3) };
|
|
46
|
+
}
|
|
22
47
|
|
|
23
48
|
/** Git metadata left by an interrupted operation in one checkout. */
|
|
24
49
|
/** ⚠️ 공개인 이유(무인 리뷰 질의) — `GitResidueObservation.residues` 의 **원소 타입**이라,
|
|
@@ -618,7 +643,7 @@ function worktreeHoldingBranch(repoRoot: string, branch: string): string | null
|
|
|
618
643
|
return null;
|
|
619
644
|
}
|
|
620
645
|
|
|
621
|
-
export function createWorktree(opts: CreateWorktreeOpts): CreateWorktreeResult {
|
|
646
|
+
export function createWorktree(opts: CreateWorktreeOpts, deps: WorktreeAddDeps = {}): CreateWorktreeResult {
|
|
622
647
|
const { repoRoot, branch, worktreeRoot } = opts;
|
|
623
648
|
validateBranchName(branch);
|
|
624
649
|
const branchHolder = worktreeHoldingBranch(repoRoot, branch);
|
|
@@ -684,25 +709,39 @@ export function createWorktree(opts: CreateWorktreeOpts): CreateWorktreeResult {
|
|
|
684
709
|
// ★ 병렬 안전 재시도(2026-07-21) — 동시 worktree add 가 git 락에서 경쟁하면 즉시 실패 → 락/일시적
|
|
685
710
|
// 에러면 백오프 재시도(패자가 승자의 락 해제를 기다렸다 성공). non-transient 는 재시도 없이 throw.
|
|
686
711
|
const MAX_ATTEMPTS = 6;
|
|
687
|
-
|
|
712
|
+
const addTimeoutMs = worktreeAddTimeoutMs();
|
|
713
|
+
const addSpawn = deps.spawn ?? spawnSync;
|
|
688
714
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
689
|
-
res =
|
|
715
|
+
const res = addSpawn(
|
|
690
716
|
'git',
|
|
691
717
|
['worktree', 'add', branchFlag, branch, wtPath, baseCheckout],
|
|
692
|
-
{ cwd: repoRoot, encoding: 'utf8', timeout:
|
|
718
|
+
{ cwd: repoRoot, encoding: 'utf8', timeout: addTimeoutMs, maxBuffer: 4 * 1024 * 1024 },
|
|
693
719
|
);
|
|
694
720
|
if (res.status === 0) break;
|
|
695
721
|
const err = (res.stderr || res.stdout || '').toString();
|
|
722
|
+
const { lastProgress, details } = worktreeAddFailureLines(err);
|
|
723
|
+
const timedOut = (res.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT';
|
|
724
|
+
if (timedOut) {
|
|
725
|
+
debug.log('git-fs.worktree', 'add.failed', { reason: 'timeout', ms: addTimeoutMs, signal: res.signal ?? null, lastProgress }, { level: 'warn' });
|
|
726
|
+
throw new Error(`git worktree add timed out after ${addTimeoutMs / 1000}s (${res.signal ?? 'none'}) — ${[...details, ...(lastProgress ? [`checkout was at «${lastProgress}»`] : [])].join(' | ') || `git exited ${res.status}`}`);
|
|
727
|
+
}
|
|
728
|
+
if (res.signal) {
|
|
729
|
+
debug.log('git-fs.worktree', 'add.failed', { reason: 'error', ms: addTimeoutMs, signal: res.signal, lastProgress }, { level: 'warn' });
|
|
730
|
+
throw new Error(`git worktree add failed (${res.signal}) — ${details.join(' | ') || lastProgress || `git exited ${res.status}`}`);
|
|
731
|
+
}
|
|
696
732
|
const blockingPath = blockingWorktreePath(err);
|
|
697
733
|
if (blockingPath) {
|
|
734
|
+
debug.log('git-fs.worktree', 'add.failed', { reason: 'error', ms: addTimeoutMs, signal: res.signal ?? null, lastProgress }, { level: 'warn' });
|
|
698
735
|
debug.log('git-fs.worktree', 'add.branch-held', { attempt, branch, blockingPath }, { level: 'warn' });
|
|
699
736
|
throw new Error(`git worktree add failed — branch ${branch} is already used by worktree at ${blockingPath}`);
|
|
700
737
|
}
|
|
701
738
|
if (isUnbornHeadError(err)) {
|
|
739
|
+
debug.log('git-fs.worktree', 'add.failed', { reason: 'error', ms: addTimeoutMs, signal: res.signal ?? null, lastProgress }, { level: 'warn' });
|
|
702
740
|
throw new Error('git worktree add failed — this repository has no commits yet; create the first commit with: git commit --allow-empty -m "Initial commit"');
|
|
703
741
|
}
|
|
704
742
|
if (!isTransientGitError(err) || attempt === MAX_ATTEMPTS) {
|
|
705
|
-
|
|
743
|
+
debug.log('git-fs.worktree', 'add.failed', { reason: 'error', ms: addTimeoutMs, signal: res.signal ?? null, lastProgress }, { level: 'warn' });
|
|
744
|
+
throw new Error(`git worktree add failed — ${details.join(' | ') || lastProgress || `git exited ${res.status}`}`);
|
|
706
745
|
}
|
|
707
746
|
debug.log('git-fs.worktree', 'add.retry', { attempt, branch, err: err.trim().slice(0, 120) });
|
|
708
747
|
sleepSyncMs(80 * attempt); // 선형 백오프(80·160·240…ms)
|
|
@@ -22,12 +22,12 @@ export interface NodeKindEntry {
|
|
|
22
22
|
|
|
23
23
|
export const HARNESS_CORE_KINDS = ['agent', 'gate', 'git', 'judge', 'observe', 'hitl', 'subgraph'] as const;
|
|
24
24
|
|
|
25
|
-
// Workflow validation
|
|
26
|
-
export const WORKFLOW_CORE_KINDS = ['prompt', 'bash', 'skill', 'cft', 'approval', 'if', 'switch', 'iteration', 'classify', 'extract', 'set', 'filter', 'template', 'http', 'showroom', 'task', 'scheduleTrigger', 'webhookTrigger', 'discordTrigger', 'telegramTrigger', 'manualTrigger', 'chatTrigger'] as const;
|
|
25
|
+
// Workflow validation, the palette, and executor variant labels read the same keys.
|
|
26
|
+
export const WORKFLOW_CORE_KINDS = ['prompt', 'bash', 'skill', 'cft', 'approval', 'if', 'switch', 'iteration', 'classify', 'extract', 'set', 'filter', 'template', 'http', 'showroom', 'task', 'scheduleTrigger', 'webhookTrigger', 'discordTrigger', 'telegramTrigger', 'manualTrigger', 'chatTrigger', 'subworkflow'] as const;
|
|
27
27
|
|
|
28
28
|
const descriptions: Record<GraphKind, readonly string[]> = {
|
|
29
29
|
harness: ['Run an agent', 'Check an execution gate', 'Perform git operations', 'Judge an outcome', 'Observe a result', 'Request human input', 'Run a child graph'],
|
|
30
|
-
workflow: ['Run an LLM prompt', 'Run a shell command', 'Invoke a skill', 'Invoke a CFT method', 'Request approval', 'Branch on a condition', 'Select a case', 'Iterate items', 'Classify input', 'Extract structured data', 'Assign variables', 'Filter items', 'Render a template', 'Make an HTTP request', 'Run a showroom', 'Create a task', 'Start on a schedule', 'Start on a webhook', 'Start on Discord', 'Start on Telegram', 'Start manually', 'Start on chat'],
|
|
30
|
+
workflow: ['Run an LLM prompt', 'Run a shell command', 'Invoke a skill', 'Invoke a CFT method', 'Request approval', 'Branch on a condition', 'Select a case', 'Iterate items', 'Classify input', 'Extract structured data', 'Assign variables', 'Filter items', 'Render a template', 'Make an HTTP request', 'Run a showroom', 'Create a task', 'Start on a schedule', 'Start on a webhook', 'Start on Discord', 'Start on Telegram', 'Start manually', 'Start on chat', 'Call another workflow'],
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const kinds = new Map<GraphKind, Map<string, NodeKindEntry>>();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { debug } from '../debug/log.js';
|
|
4
|
+
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
5
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
6
|
+
import { decideGraphApproval, type GraphRunState } from './runner.js';
|
|
7
|
+
|
|
8
|
+
export interface GraphApprovalCardDeps {
|
|
9
|
+
root?: string;
|
|
10
|
+
ledger?: Pick<DecisionLedger, 'list' | 'raise'>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Pending = NonNullable<GraphRunState['pending']>;
|
|
14
|
+
const REF = /^graph-approval:([A-Za-z0-9][A-Za-z0-9._-]*):([A-Za-z0-9][A-Za-z0-9._-]*):([A-Za-z0-9][A-Za-z0-9._-]*):([1-9]\d*)$/;
|
|
15
|
+
|
|
16
|
+
function ledgerFor(deps: GraphApprovalCardDeps, root: string): Pick<DecisionLedger, 'list' | 'raise'> {
|
|
17
|
+
return deps.ledger ?? new DecisionLedger({ stateDir: root });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function observation(event: 'raised' | 'dedup' | 'applied' | 'skipped', state: Pick<GraphRunState, 'graphId' | 'runId'> & { nodeId: string }, decisionId?: string, outcome?: string, reason?: string): void {
|
|
21
|
+
debug.log('graph.approval-card', event, { graphId: state.graphId, runId: state.runId, nodeId: state.nodeId, decisionId, outcome, ...(reason ? { reason } : {}) });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function raiseApprovalCard(state: GraphRunState, pending: Pending, deps: GraphApprovalCardDeps = {}): DecisionEntry | undefined {
|
|
25
|
+
const root = deps.root ?? effectiveInstanceRoot();
|
|
26
|
+
const nodeId = pending.nodeId;
|
|
27
|
+
const identity = { graphId: state.graphId, runId: state.runId, nodeId };
|
|
28
|
+
const visit = state.path.filter(node => node === nodeId).length;
|
|
29
|
+
if (state.dryRun || state.status !== 'awaiting-approval' || state.pending?.nodeId !== nodeId ||
|
|
30
|
+
state.pending.decision || state.path.at(-1) !== nodeId || visit < 1 ||
|
|
31
|
+
existsSync(`${join(root, 'graph-runs', state.graphId, `${state.runId}.json`)}.${state.path.length}.decision.json`)) {
|
|
32
|
+
observation('skipped', identity);
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const ref = `graph-approval:${state.graphId}:${state.runId}:${nodeId}:${visit}`;
|
|
36
|
+
const ledger = ledgerFor(deps, root);
|
|
37
|
+
const existing = ledger.list({ status: 'all' }).find(entry => entry.refs?.includes(ref));
|
|
38
|
+
if (existing) {
|
|
39
|
+
observation('dedup', identity, existing.id, existing.choice);
|
|
40
|
+
return existing;
|
|
41
|
+
}
|
|
42
|
+
const publish = state.graphId === 'release-loop' && nodeId === 'approve-publish';
|
|
43
|
+
const firstLine = pending.message.split(/\r?\n/, 1)[0]?.trim();
|
|
44
|
+
const version = state.input && typeof state.input === 'object' && 'version' in state.input && typeof state.input.version === 'string'
|
|
45
|
+
? state.input.version : state.runId;
|
|
46
|
+
const title = publish && firstLine ? firstLine : `${state.graphId} ${version} — ${nodeId} 승인`;
|
|
47
|
+
const entry = ledger.raise({ title, category: publish ? 'publish' : 'scope',
|
|
48
|
+
scqa: { s: `${state.graphId} / ${state.runId} 런이 ${nodeId} 승인에서 대기 중이다.`, c: pending.message.slice(0, 220) || '승인 대기 중이다.' },
|
|
49
|
+
options: [{ key: 'a', label: '승인', consequence: `${nodeId} 승인 후 런을 계속할 수 있다` },
|
|
50
|
+
{ key: 'b', label: '거절', consequence: `${nodeId} 거절로 기록한다` }],
|
|
51
|
+
recommendation: { skipped: true, reason: '사람 판단' }, raisedBy: { agent: 'graph-runner' }, refs: [ref] });
|
|
52
|
+
observation('raised', identity, entry.id);
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** A card answer only claims the same pending visit; runner owns the atomic decision file. */
|
|
57
|
+
export function applyDecidedApprovals(deps: GraphApprovalCardDeps = {}): number {
|
|
58
|
+
const root = deps.root ?? effectiveInstanceRoot();
|
|
59
|
+
let applied = 0;
|
|
60
|
+
for (const entry of ledgerFor(deps, root).list({ status: 'decided' })) {
|
|
61
|
+
for (const ref of entry.refs ?? []) {
|
|
62
|
+
const match = REF.exec(ref);
|
|
63
|
+
if (!match) continue;
|
|
64
|
+
const [, graphId, runId, nodeId, rawVisit] = match;
|
|
65
|
+
const identity = { graphId: graphId!, runId: runId!, nodeId: nodeId! };
|
|
66
|
+
// One unreadable run must not block every later card (L11 review round 1 should-fix).
|
|
67
|
+
try { if (applyOne(entry, identity, Number(rawVisit), root)) applied++; }
|
|
68
|
+
catch (error) {
|
|
69
|
+
observation('skipped', identity, entry.id, entry.choice, error instanceof Error ? error.message : String(error));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return applied;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function applyOne(entry: DecisionEntry, identity: { graphId: string; runId: string; nodeId: string }, visit: number, root: string): boolean {
|
|
77
|
+
const { graphId, runId, nodeId } = identity;
|
|
78
|
+
// Graph approvals gate irreversible steps (publish) — only a person's answer approves or rejects, never a delegated auto decision.
|
|
79
|
+
if ((entry.choice !== 'a' && entry.choice !== 'b') || entry.decidedBy?.kind !== 'human' || !Number.isSafeInteger(visit)) {
|
|
80
|
+
observation('skipped', identity, entry.id, entry.choice);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const statePath = join(root, 'graph-runs', graphId, `${runId}.json`);
|
|
84
|
+
let state: GraphRunState;
|
|
85
|
+
try { state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState; }
|
|
86
|
+
catch (error) {
|
|
87
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
88
|
+
observation('skipped', identity, entry.id, entry.choice);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
if (state.graphId !== graphId || state.runId !== runId || state.dryRun || state.status !== 'awaiting-approval' ||
|
|
92
|
+
!state.pending || state.pending.nodeId !== nodeId || state.pending.decision || state.path.at(-1) !== nodeId ||
|
|
93
|
+
state.path.filter(node => node === nodeId).length !== visit ||
|
|
94
|
+
existsSync(`${statePath}.${state.path.length}.decision.json`)) {
|
|
95
|
+
observation('skipped', identity, entry.id, entry.choice);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const outcome = entry.choice === 'a' ? 'approved' : 'rejected';
|
|
99
|
+
try { decideGraphApproval(graphId, runId, outcome, `owner:${entry.decidedBy.kind}`, root,
|
|
100
|
+
{ nodeId, visit: visit }); }
|
|
101
|
+
catch (error) {
|
|
102
|
+
// A CLI approver (or another notifier) may win the atomic hard-link claim.
|
|
103
|
+
// A resumed run may also have left this visit before the writer reads it.
|
|
104
|
+
const current = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
105
|
+
if (!existsSync(`${statePath}.${state.path.length}.decision.json`) &&
|
|
106
|
+
current.status === 'awaiting-approval' && current.pending?.nodeId === nodeId && !current.pending.decision &&
|
|
107
|
+
current.path.at(-1) === nodeId && current.path.filter(node => node === nodeId).length === visit) throw error;
|
|
108
|
+
observation('skipped', identity, entry.id, outcome);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
observation('applied', identity, entry.id, outcome);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
5
|
import { writeStdoutJson } from '../cli/stdout-json.js';
|
|
3
|
-
import {
|
|
6
|
+
import type { GraphVariantPlan } from '../self-implement/graph-variant.js';
|
|
7
|
+
import { debug } from '../debug/log.js';
|
|
8
|
+
import { decideGraphApproval, graphRunAlive, lastJsonObject, latestGraphRun, listGraphRuns, manageGraphRun, runGraph, type GraphRunState } from './runner.js';
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
function findGraphFile(graphId: string): string | undefined {
|
|
11
|
+
const roots = [resolve('graphs'), resolve(import.meta.dir, '../../graphs')];
|
|
12
|
+
for (const root of roots) {
|
|
13
|
+
for (const dir of [root, ...['release', 'ops'].map((part) => join(root, part))]) {
|
|
14
|
+
if (!existsSync(dir)) continue;
|
|
15
|
+
for (const name of readdirSync(dir)) {
|
|
16
|
+
if (!name.endsWith('.yaml') || name === 'recipes.yaml') continue;
|
|
17
|
+
const file = join(dir, name);
|
|
18
|
+
if (!existsSync(join(dir, 'recipes.yaml'))) continue;
|
|
19
|
+
try {
|
|
20
|
+
const parsed: unknown = parseYaml(readFileSync(file, 'utf8'));
|
|
21
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed) && (parsed as Record<string, unknown>).graph_id === graphId) return file;
|
|
22
|
+
} catch { continue; }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function registerGraphCommands(program: Command, runsDeps: { root?: string; processStartMs?: (pid: number) => number | null } = {}): void {
|
|
6
30
|
const graph = program.command('graph').description('Run a declared command graph or inspect its latest run');
|
|
7
31
|
graph.command('run <file>')
|
|
8
32
|
.option('--dry-run', 'Walk the success path without executing commands')
|
|
9
33
|
.option('--json', 'Print run state as JSON')
|
|
10
34
|
.option('--resume <run_id>', 'Resume a persisted run awaiting approval or restart a failed path with --from')
|
|
11
35
|
.option('--from <node_id>', 'Restart a failed run at a node on its saved path (requires --resume)')
|
|
36
|
+
.option('--use-current-graph', 'Use the installed graph rather than the run snapshot')
|
|
12
37
|
.option('--input <json>', 'JSON object passed to the graph run')
|
|
13
|
-
.action(async (file: string, opts: { dryRun?: boolean; json?: boolean; resume?: string; from?: string; input?: string }) => {
|
|
38
|
+
.action(async (file: string, opts: { dryRun?: boolean; json?: boolean; resume?: string; from?: string; input?: string; useCurrentGraph?: boolean }) => {
|
|
14
39
|
try {
|
|
15
40
|
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
16
41
|
if (opts.resume && opts.dryRun) throw new Error('--resume cannot be combined with --dry-run');
|
|
@@ -23,7 +48,7 @@ export function registerGraphCommands(program: Command): void {
|
|
|
23
48
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('--input must be a JSON object');
|
|
24
49
|
input = parsed as Record<string, unknown>;
|
|
25
50
|
}
|
|
26
|
-
const state = await runGraph(file, { ...(opts.resume ? { resumeRunId: opts.resume } : { dryRun: opts.dryRun }), ...(opts.from ? { fromNodeId: opts.from } : {}), ...(input === undefined ? {} : { input }) });
|
|
51
|
+
const state = await runGraph(file, { ...(opts.resume ? { resumeRunId: opts.resume } : { dryRun: opts.dryRun }), ...(opts.from ? { fromNodeId: opts.from } : {}), useCurrentGraph: opts.useCurrentGraph, ...(input === undefined ? {} : { input }) });
|
|
27
52
|
if (opts.json) await writeStdoutJson(JSON.stringify(state) + '\n');
|
|
28
53
|
else console.log(`${state.graphId} ${state.runId}: ${state.status} (${state.path.join(' → ')}, executed: ${state.executed})${state.pending ? ` — ${state.pending.message}` : ''}`);
|
|
29
54
|
if (state.status !== 'done' && state.status !== 'awaiting-approval') process.exitCode = 1;
|
|
@@ -32,6 +57,43 @@ export function registerGraphCommands(program: Command): void {
|
|
|
32
57
|
process.exitCode = 1;
|
|
33
58
|
}
|
|
34
59
|
});
|
|
60
|
+
graph.command('variant <template>')
|
|
61
|
+
.description('Validate and run a graph variant, preserving the resulting graph beside its run ledger')
|
|
62
|
+
.requiredOption('--plan <json>', 'JSON variant plan (maxVisits, routes, or growth)')
|
|
63
|
+
.option('--dry-run', 'Walk the variant without executing commands')
|
|
64
|
+
.option('--run', 'Execute the validated variant')
|
|
65
|
+
.option('--json', 'Print run state as JSON')
|
|
66
|
+
.action(async (file: string, opts: { plan: string; dryRun?: boolean; run?: boolean; json?: boolean }) => {
|
|
67
|
+
try {
|
|
68
|
+
if (opts.dryRun === opts.run) throw new Error('choose exactly one of --dry-run or --run');
|
|
69
|
+
let plan: unknown;
|
|
70
|
+
try { plan = JSON.parse(opts.plan); } catch { throw new Error('--plan must be a JSON object'); }
|
|
71
|
+
if (!plan || typeof plan !== 'object' || Array.isArray(plan) ||
|
|
72
|
+
Object.keys(plan).some(key => !['maxVisits', 'routes', 'growth'].includes(key)) ||
|
|
73
|
+
(plan as Record<string, unknown>).maxVisits !== undefined &&
|
|
74
|
+
(typeof (plan as Record<string, unknown>).maxVisits !== 'object' || (plan as Record<string, unknown>).maxVisits === null || Array.isArray((plan as Record<string, unknown>).maxVisits)) ||
|
|
75
|
+
(plan as Record<string, unknown>).routes !== undefined && (!Array.isArray((plan as Record<string, unknown>).routes) ||
|
|
76
|
+
!(plan as { routes: unknown[] }).routes.every(route => route !== null && typeof route === 'object' && !Array.isArray(route) &&
|
|
77
|
+
typeof (route as Record<string, unknown>).from === 'string' && typeof (route as Record<string, unknown>).to === 'string' &&
|
|
78
|
+
typeof (route as Record<string, unknown>).outcome === 'string')) ||
|
|
79
|
+
(plan as Record<string, unknown>).growth !== undefined &&
|
|
80
|
+
(!(plan as Record<string, unknown>).growth || typeof (plan as Record<string, unknown>).growth !== 'object' ||
|
|
81
|
+
Array.isArray((plan as Record<string, unknown>).growth) ||
|
|
82
|
+
!(plan as { growth: Record<string, unknown> }).growth.node ||
|
|
83
|
+
typeof (plan as { growth: Record<string, unknown> }).growth.from !== 'string' ||
|
|
84
|
+
typeof (plan as { growth: Record<string, unknown> }).growth.outcome !== 'string')) {
|
|
85
|
+
throw new Error('--plan must be a JSON variant plan');
|
|
86
|
+
}
|
|
87
|
+
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
88
|
+
const state = await runGraph(file, { dryRun: opts.dryRun, variant: { goal: 'g_graph_variant', plan: plan as GraphVariantPlan }, deps: { root: runsDeps.root } });
|
|
89
|
+
if (opts.json) await writeStdoutJson(JSON.stringify(state) + '\n');
|
|
90
|
+
else console.log(`${state.graphId} ${state.runId}: ${state.status} (${state.path.join(' → ')}, executed: ${state.executed})`);
|
|
91
|
+
if (state.status !== 'done' && state.status !== 'awaiting-approval') process.exitCode = 1;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(`graph variant: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
35
97
|
graph.command('tick <file>')
|
|
36
98
|
.description('Advance one graph run, resuming a pending decision or starting only when requested')
|
|
37
99
|
.option('--start', 'Start a new run when the graph is idle')
|
|
@@ -87,6 +149,129 @@ export function registerGraphCommands(program: Command): void {
|
|
|
87
149
|
process.exitCode = 1;
|
|
88
150
|
}
|
|
89
151
|
});
|
|
152
|
+
const runs = graph.command('runs').description('List and inspect persisted graph runs');
|
|
153
|
+
const alive = (state: GraphRunState) => graphRunAlive(state, runsDeps.processStartMs);
|
|
154
|
+
const row = (state: GraphRunState) => ({
|
|
155
|
+
runId: state.runId, graphId: state.graphId, status: state.status,
|
|
156
|
+
lastNode: state.path.at(-1) ?? null, startedAt: state.startedAt ?? null,
|
|
157
|
+
finishedAt: state.finishedAt ?? null, alive: alive(state),
|
|
158
|
+
});
|
|
159
|
+
const warning = (unreadable: number) => { if (unreadable) console.error(`⚠ 못 읽은 원장 ${unreadable}`); };
|
|
160
|
+
runs.command('list')
|
|
161
|
+
.option('--graph <id>', 'Filter by graph id')
|
|
162
|
+
.option('--state <status>', 'Filter by run status')
|
|
163
|
+
.option('--limit <N>', 'Maximum number of runs')
|
|
164
|
+
.option('--json', 'Print runs as JSON')
|
|
165
|
+
.action(async (opts: { graph?: string; state?: string; limit?: string; json?: boolean }) => {
|
|
166
|
+
try {
|
|
167
|
+
if (opts.limit !== undefined && (!/^[1-9]\d*$/.test(opts.limit) || !Number.isSafeInteger(Number(opts.limit)))) throw new Error('--limit must be a positive integer');
|
|
168
|
+
const { runs: all, unreadable } = listGraphRuns(runsDeps.root);
|
|
169
|
+
const selected = all.filter((state) => (!opts.graph || state.graphId === opts.graph) && (!opts.state || state.status === opts.state))
|
|
170
|
+
.slice(0, opts.limit === undefined ? undefined : Number(opts.limit)).map(row);
|
|
171
|
+
debug.log('graph.runs', 'list', { count: selected.length, unreadable, alive: selected.filter((state) => state.alive === true).length });
|
|
172
|
+
if (opts.json) await writeStdoutJson(JSON.stringify(selected) + '\n');
|
|
173
|
+
else for (const state of selected) console.log(`${state.runId} · ${state.graphId} · ${state.status} · ${state.lastNode ?? '-'} · ${state.startedAt ?? '-'} · ${state.finishedAt ?? '-'} · alive=${state.alive}`);
|
|
174
|
+
warning(unreadable);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error(`graph runs list: ${error instanceof Error ? error.message : String(error)}`);
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
for (const action of ['stop', 'destroy'] as const) {
|
|
181
|
+
runs.command(`${action} <run_id>`)
|
|
182
|
+
.description(action === 'stop' ? 'Fail a running or orphaned run so it can be resumed' : 'Stop and delete a run and its owned artifacts')
|
|
183
|
+
.action(async (runId: string) => {
|
|
184
|
+
try {
|
|
185
|
+
// Without the sink the stop/destroy observation lines never reach the log store (only `graph run` had it).
|
|
186
|
+
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
187
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(runId) || runId === '.' || runId === '..') throw new Error(`invalid run id: ${runId}`);
|
|
188
|
+
const { runs: all, unreadable } = listGraphRuns(runsDeps.root);
|
|
189
|
+
const exact = all.filter(state => state.runId === runId);
|
|
190
|
+
const matches = exact.length ? exact : all.filter(state => state.runId.startsWith(runId));
|
|
191
|
+
if (matches.length !== 1) throw new Error(matches.length ? `ambiguous run id: ${runId}\n${matches.map(state => `${state.graphId}/${state.runId}`).join('\n')}` : `no run: ${runId}`);
|
|
192
|
+
const saved = matches[0]!;
|
|
193
|
+
const state = manageGraphRun(saved.graphId, saved.runId, action, runsDeps.root, runsDeps.processStartMs);
|
|
194
|
+
console.log(`${state.graphId} ${state.runId}: ${action === 'stop' ? state.status : 'destroyed'}`);
|
|
195
|
+
warning(unreadable);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`graph runs ${action}: ${error instanceof Error ? error.message : String(error)}`);
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
runs.command('resume <run_id>')
|
|
203
|
+
.option('--from <node_id>', 'Restart at this node on the saved path')
|
|
204
|
+
.option('--use-current-graph', 'Use the installed graph and enforce the original hash')
|
|
205
|
+
.option('--file <path>', 'Graph file for a legacy run without a recorded graph path')
|
|
206
|
+
.option('--json', 'Print run state as JSON')
|
|
207
|
+
.action(async (runId: string, opts: { from?: string; useCurrentGraph?: boolean; file?: string; json?: boolean }) => {
|
|
208
|
+
try {
|
|
209
|
+
await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('graph');
|
|
210
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(runId) || runId === '.' || runId === '..') throw new Error(`invalid run id: ${runId}`);
|
|
211
|
+
const { runs: all, unreadable } = listGraphRuns(runsDeps.root);
|
|
212
|
+
const exact = all.filter((state) => state.runId === runId);
|
|
213
|
+
const matches = exact.length ? exact : all.filter((state) => state.runId.startsWith(runId));
|
|
214
|
+
if (matches.length !== 1) {
|
|
215
|
+
if (matches.length) console.error(`ambiguous run id: ${runId}\n${matches.map((state) => `${state.graphId}/${state.runId}`).join('\n')}`);
|
|
216
|
+
else console.error(`no run: ${runId}`);
|
|
217
|
+
warning(unreadable);
|
|
218
|
+
process.exitCode = 1;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const saved = matches[0]!;
|
|
222
|
+
if (saved.graphSnapshot && opts.file && (!saved.graphPath || resolve(opts.file) !== resolve(saved.graphPath))) {
|
|
223
|
+
throw new Error('--file cannot change the graph path of a snapshot run (이 런은 시작 때 그래프 사본이 있다 — --file 대신 --use-current-graph)');
|
|
224
|
+
}
|
|
225
|
+
const from = opts.from ?? (saved.status === 'failed' ? [...saved.nodes].reverse().find((node) => !node.ok)?.nodeId : undefined);
|
|
226
|
+
const file = saved.graphSnapshot
|
|
227
|
+
? saved.graphPath
|
|
228
|
+
: opts.file ?? (saved.graphPath && existsSync(saved.graphPath) ? saved.graphPath : undefined) ?? findGraphFile(saved.graphId);
|
|
229
|
+
if (!file) throw new Error(`graph file not found for ${saved.graphId}/${saved.runId}; provide --file`);
|
|
230
|
+
const state = await runGraph(file, { resumeRunId: saved.runId, resumeGraphId: saved.graphId, fromNodeId: from,
|
|
231
|
+
useCurrentGraph: opts.useCurrentGraph, deps: { root: runsDeps.root } });
|
|
232
|
+
if (opts.json) await writeStdoutJson(JSON.stringify(state) + '\n');
|
|
233
|
+
else console.log(`${state.graphId} ${state.runId}: ${state.status} (${state.path.join(' → ')}, executed: ${state.executed})`);
|
|
234
|
+
if (state.status !== 'done' && state.status !== 'awaiting-approval') process.exitCode = 1;
|
|
235
|
+
warning(unreadable);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(`graph runs resume: ${error instanceof Error ? error.message : String(error)}`);
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
runs.command('status <run_id>')
|
|
242
|
+
.option('--json', 'Print run detail as JSON')
|
|
243
|
+
.action(async (runId: string, opts: { json?: boolean }) => {
|
|
244
|
+
try {
|
|
245
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(runId) || runId === '.' || runId === '..') throw new Error(`invalid run id: ${runId}`);
|
|
246
|
+
const { runs: all, unreadable } = listGraphRuns(runsDeps.root);
|
|
247
|
+
const exact = all.filter((state) => state.runId === runId);
|
|
248
|
+
const matches = exact.length ? exact : all.filter((state) => state.runId.startsWith(runId));
|
|
249
|
+
debug.log('graph.runs', 'status', { count: matches.length, unreadable, alive: matches.filter((state) => alive(state) === true).length });
|
|
250
|
+
if (matches.length !== 1) {
|
|
251
|
+
if (matches.length) console.error(`ambiguous run id: ${runId}\n${matches.map((state) => `${state.graphId}/${state.runId}`).join('\n')}`);
|
|
252
|
+
else console.error(`no run: ${runId}`);
|
|
253
|
+
warning(unreadable);
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const state = matches[0]!;
|
|
258
|
+
const detail = { ...row(state), nodes: state.nodes.map((node) => {
|
|
259
|
+
const summary = lastJsonObject(node.output)?.summary;
|
|
260
|
+
return { nodeId: node.nodeId, ok: node.ok, summary: typeof summary === 'string' ? summary.slice(0, 300) : null };
|
|
261
|
+
}), pending: state.pending ?? null, resume: state.resume ?? null };
|
|
262
|
+
if (opts.json) await writeStdoutJson(JSON.stringify(detail) + '\n');
|
|
263
|
+
else {
|
|
264
|
+
console.log(`${detail.runId} · ${detail.graphId} · ${detail.status} · alive=${detail.alive}`);
|
|
265
|
+
for (const node of detail.nodes) console.log(` ${node.nodeId} · ok=${node.ok} · ${node.summary ?? '-'}`);
|
|
266
|
+
console.log(`pending: ${JSON.stringify(detail.pending)}`);
|
|
267
|
+
console.log(`resume: ${JSON.stringify(detail.resume)}`);
|
|
268
|
+
}
|
|
269
|
+
warning(unreadable);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error(`graph runs status: ${error instanceof Error ? error.message : String(error)}`);
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
90
275
|
graph.command('status <graph_id>')
|
|
91
276
|
.option('--json', 'Print latest run state as JSON')
|
|
92
277
|
.action(async (graphId: string, opts: { json?: boolean }) => {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { streamLLM } from '../llm.js';
|
|
2
|
+
import type { GrowthProposal, GrowthProposer } from './graph-grow.js';
|
|
3
|
+
|
|
4
|
+
type GrowthLLM = (prompt: string) => Promise<string>;
|
|
5
|
+
const OUTPUT_TAIL_LENGTH = 4000;
|
|
6
|
+
|
|
7
|
+
/** Model output is untrusted: structural and side-effect validation belongs to proposeGrowth. */
|
|
8
|
+
export function createLLMGrowthProposer(callLLM: GrowthLLM = (prompt) =>
|
|
9
|
+
streamLLM([{ role: 'user', content: prompt }], () => {}, { reasoningEffort: 'low' })): GrowthProposer {
|
|
10
|
+
return async (input: Parameters<GrowthProposer>[0]) => {
|
|
11
|
+
const serializedOutput = typeof input.output === 'string' ? input.output : input.output === undefined ? '' : JSON.stringify(input.output);
|
|
12
|
+
const graphSummary = {
|
|
13
|
+
graphId: input.graph.graphId,
|
|
14
|
+
nodes: input.graph.nodes.map(({ nodeId, kind, recipe }) => ({ nodeId, kind, recipe })),
|
|
15
|
+
edges: input.graph.edges.map(({ from, to, on, map, fallback }) => ({ from, ...(to === undefined ? {} : { to }),
|
|
16
|
+
...(on === undefined ? {} : { on }), ...(map === undefined ? {} : { map }), ...(fallback === undefined ? {} : { fallback }) })),
|
|
17
|
+
};
|
|
18
|
+
const prompt = [
|
|
19
|
+
'Propose exactly one new contracted graph node to handle the unseen outcome, then return to an existing node.',
|
|
20
|
+
'Reply with only JSON: {"node":{"nodeId":"...","kind":"...","recipe":"none","maxVisits":1,"contract":{"inputs":[],"tools":"read-only","outputs":[]}},"returnTo":"existing-node-id","reason":"..."}.',
|
|
21
|
+
'Do not alter existing nodes or edges. The proposal will be validated independently; do not assume a recipe is safe.',
|
|
22
|
+
`Graph summary: ${JSON.stringify(graphSummary)}`,
|
|
23
|
+
`Blocked node ID: ${JSON.stringify(input.nodeId)}`,
|
|
24
|
+
`Unseen outcome: ${JSON.stringify(input.outcome)}`,
|
|
25
|
+
`Blocked node output tail: ${JSON.stringify(serializedOutput.slice(-OUTPUT_TAIL_LENGTH))}`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
const response = await callLLM(prompt);
|
|
28
|
+
let parsed: unknown;
|
|
29
|
+
try { parsed = JSON.parse(response.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '')); }
|
|
30
|
+
catch { return undefined; }
|
|
31
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
|
|
32
|
+
const proposal = parsed as Record<string, unknown>;
|
|
33
|
+
if (!proposal.node || typeof proposal.node !== 'object' || Array.isArray(proposal.node) ||
|
|
34
|
+
typeof proposal.reason !== 'string' || !proposal.reason.trim() || typeof proposal.returnTo !== 'string') return undefined;
|
|
35
|
+
return { node: proposal.node, reason: proposal.reason, returnTo: proposal.returnTo } as GrowthProposal;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createGraphVariant } from '../self-implement/graph-variant.js';
|
|
2
|
+
import type { GraphEdgeSpec, GraphNodeSpec, GraphTemplateSpec } from '../self-implement/graph-yaml.js';
|
|
3
|
+
import { createLLMGrowthProposer } from './graph-grow-llm.js';
|
|
4
|
+
|
|
5
|
+
export interface GrowthProposal {
|
|
6
|
+
node: GraphNodeSpec;
|
|
7
|
+
returnTo?: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type GrowthProposer = (input: { graph: GraphTemplateSpec; nodeId: string; outcome: string; runId: string; output?: unknown }) => GrowthProposal | undefined | Promise<GrowthProposal | undefined>;
|
|
12
|
+
export type GrowthRecipeEffect = 'read-only' | 'external-effect' | 'unknown';
|
|
13
|
+
|
|
14
|
+
export interface GraphGrowth {
|
|
15
|
+
node: GraphNodeSpec;
|
|
16
|
+
edges: readonly GraphEdgeSpec[];
|
|
17
|
+
reason: string;
|
|
18
|
+
undo: { removeNode: string; removeOutcome: { from: string; outcome: string }; removeEdge?: GraphEdgeSpec };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function proposeGrowth(input: Parameters<GrowthProposer>[0],
|
|
22
|
+
proposer: GrowthProposer = createLLMGrowthProposer(), classifyRecipe?: (node: GraphNodeSpec) => GrowthRecipeEffect): Promise<{ ok: true; graph: GraphTemplateSpec; growth: GraphGrowth } | { ok: false; reason: string; park?: boolean }> {
|
|
23
|
+
let proposal: GrowthProposal | undefined;
|
|
24
|
+
try { proposal = await proposer(input); }
|
|
25
|
+
catch (error) { return { ok: false, reason: `proposer failed: ${String(error)}` }; }
|
|
26
|
+
if (!proposal) return { ok: false, reason: 'no proposal' };
|
|
27
|
+
if (!proposal.node || typeof proposal.reason !== 'string' || !proposal.reason.trim()) return { ok: false, reason: 'proposal requires a node and reason' };
|
|
28
|
+
const result = createGraphVariant({ template: input.graph, goal: `g_${input.runId.replace(/[^a-z0-9_-]/gi, '_')}`,
|
|
29
|
+
plan: { growth: { node: proposal.node, from: input.nodeId, outcome: input.outcome, returnTo: proposal.returnTo } } });
|
|
30
|
+
if (!result.ok) return { ok: false, reason: `growth rejected: ${JSON.stringify(result.rejections)}` };
|
|
31
|
+
// Neither a recipe id nor its declared tools certify arbitrary executable code.
|
|
32
|
+
// Only a trusted classification of the resolved recipe may authorize execution.
|
|
33
|
+
let effect: GrowthRecipeEffect = 'unknown';
|
|
34
|
+
if (proposal.node.recipe !== 'none') {
|
|
35
|
+
try { effect = classifyRecipe?.(proposal.node) ?? 'unknown'; }
|
|
36
|
+
catch { effect = 'unknown'; }
|
|
37
|
+
if (effect !== 'read-only') {
|
|
38
|
+
return { ok: false, park: true, reason: '사람 확인 필요: external or unclassified side-effect recipe' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const edges: GraphEdgeSpec[] = [
|
|
42
|
+
{ from: input.nodeId, on: 'outcome', map: { [input.outcome]: proposal.node.nodeId } },
|
|
43
|
+
{ from: proposal.node.nodeId, to: proposal.returnTo as string },
|
|
44
|
+
];
|
|
45
|
+
return { ok: true, graph: result.template, growth: { node: proposal.node, edges, reason: proposal.reason,
|
|
46
|
+
undo: { removeNode: proposal.node.nodeId, removeOutcome: { from: input.nodeId, outcome: input.outcome }, removeEdge: edges[1]! } } };
|
|
47
|
+
}
|
|
@@ -2,6 +2,8 @@ import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readdirSy
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { parse as parseYaml } from 'yaml';
|
|
4
4
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
import { applyDecidedApprovals, raiseApprovalCard, type GraphApprovalCardDeps } from './graph-approval-card.js';
|
|
5
7
|
import type { GraphRunState } from './runner.js';
|
|
6
8
|
|
|
7
9
|
export interface GraphNotificationEvent {
|
|
@@ -20,6 +22,7 @@ export interface GraphNotifyOptions {
|
|
|
20
22
|
graphsDir?: string;
|
|
21
23
|
dryRun?: boolean;
|
|
22
24
|
send?: (message: string, event: GraphNotificationEvent) => Promise<void> | void;
|
|
25
|
+
approvalCard?: Pick<GraphApprovalCardDeps, 'ledger'>;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
type NotificationSetting = boolean | string | { on?: string | string[]; enabled?: boolean };
|
|
@@ -177,16 +180,35 @@ export async function notifyGraphEvents(options: GraphNotifyOptions = {}): Promi
|
|
|
177
180
|
return collectGraphEvents({ root, graphsDir: options.graphsDir }).filter((event) => !sent.has(event.id));
|
|
178
181
|
};
|
|
179
182
|
if (options.dryRun) return collectFresh();
|
|
183
|
+
const syncApprovals = () => {
|
|
184
|
+
const deps = { root, ...options.approvalCard };
|
|
185
|
+
try { applyDecidedApprovals(deps); }
|
|
186
|
+
catch (error) {
|
|
187
|
+
debug.log('graph.approval-card', 'skipped', { graphId: undefined, runId: undefined, nodeId: undefined,
|
|
188
|
+
decisionId: undefined, outcome: undefined, reason: error instanceof Error ? error.message : String(error) });
|
|
189
|
+
}
|
|
190
|
+
for (const event of collectGraphEvents({ root, graphsDir: options.graphsDir })) {
|
|
191
|
+
if (event.kind !== 'approval') continue;
|
|
192
|
+
try {
|
|
193
|
+
const state = JSON.parse(readFileSync(join(root, 'graph-runs', event.graphId, `${event.runId}.json`), 'utf8')) as GraphRunState;
|
|
194
|
+
if (state.pending?.nodeId === event.nodeId) raiseApprovalCard(state, state.pending, deps);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
debug.log('graph.approval-card', 'skipped', { graphId: event.graphId, runId: event.runId, nodeId: event.nodeId,
|
|
197
|
+
decisionId: undefined, outcome: undefined, reason: error instanceof Error ? error.message : String(error) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
180
201
|
// The former .lock path was a directory; use a separate, stable inode instead of trying to reclaim it.
|
|
181
202
|
const lock = `${ledgerPath}.flock`;
|
|
182
203
|
mkdirSync(join(root, 'graph-runs'), { recursive: true });
|
|
183
204
|
const flock = await loadFlock();
|
|
184
|
-
if (!flock) return notifyWithExclusiveFile(`${ledgerPath}.owner`, collectFresh, options.send, ledgerPath);
|
|
205
|
+
if (!flock) return notifyWithExclusiveFile(`${ledgerPath}.owner`, collectFresh, options.send, ledgerPath, syncApprovals);
|
|
185
206
|
const fd = openSync(lock, 'a');
|
|
186
207
|
let acquired = false;
|
|
187
208
|
try {
|
|
188
209
|
if (flock(fd, LOCK_EX_NB) !== 0) throw new Error('graph notification is already running');
|
|
189
210
|
acquired = true;
|
|
211
|
+
syncApprovals();
|
|
190
212
|
return await sendFresh(collectFresh(), options.send, ledgerPath);
|
|
191
213
|
} finally {
|
|
192
214
|
try {
|
|
@@ -209,12 +231,12 @@ async function sendFresh(fresh: GraphNotificationEvent[], sendOption: GraphNotif
|
|
|
209
231
|
}
|
|
210
232
|
|
|
211
233
|
/** Platforms without flock (Windows, musl): an exclusive owner file. A crash leaves it behind, and the next run names it instead of double-sending. */
|
|
212
|
-
async function notifyWithExclusiveFile(ownerPath: string, collectFresh: () => GraphNotificationEvent[], send: GraphNotifyOptions['send'], ledgerPath: string): Promise<GraphNotificationEvent[]> {
|
|
234
|
+
async function notifyWithExclusiveFile(ownerPath: string, collectFresh: () => GraphNotificationEvent[], send: GraphNotifyOptions['send'], ledgerPath: string, syncApprovals: () => void): Promise<GraphNotificationEvent[]> {
|
|
213
235
|
try { writeFileSync(ownerPath, String(process.pid), { flag: 'wx' }); }
|
|
214
236
|
catch (error) {
|
|
215
237
|
if ((error as NodeJS.ErrnoException).code === 'EEXIST') throw new Error(`graph notification is already running (or a crashed run left ${ownerPath} — remove it if no notifier is running)`);
|
|
216
238
|
throw error;
|
|
217
239
|
}
|
|
218
|
-
try { return await sendFresh(collectFresh(), send, ledgerPath); }
|
|
240
|
+
try { syncApprovals(); return await sendFresh(collectFresh(), send, ledgerPath); }
|
|
219
241
|
finally { rmSync(ownerPath, { force: true }); }
|
|
220
242
|
}
|