elanous 0.2.11 → 0.2.13
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/chunks/{1394-556de6d71bbdb0fb.js → 1394-931c9827067b2276.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3992-2e522a8ac5f2afde.js → 3992-fe5c06d6b31f8b25.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8605-ea5bee9dfae512c7.js → 8605-9da4678f8a310d18.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-6a061bd97f0b9398.js → page-e49b9b76c565a0b1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-7c97dcdfe2e37a60.js → page-4c1dbb21861beab2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-5721537ca8a3598c.js → page-f6bc66b919b2ea7d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-b654240a572e4c48.js → page-60603c1e056c9b25.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-b9750abc897508c3.js → page-a0ea8f5a2341ee6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-c8b1ccda6ba7ae98.js → page-31012264da8a341f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-d6b1d156fb95e74b.js → layout-50309aeb0bdd554a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-9688f5b4f1ff950f.js → page-0696ee3fac9fe56c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-35414e0a007b4082.js → page-6ddd2109b7fc3a80.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-e001823c35c9dd67.js → page-7a2bf830038a2264.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-371423d1a573d64b.js → page-1f327f18694fabca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-4b764b125339ac17.js → page-e5cef2fe6e4028df.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-cdf8e4b01067bcf1.js → page-cf596128d2b420ba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-3a24072b35470157.js → page-f672536f14f0969f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d37cb0d52f9b6bd5.js → page-29a709616df9b050.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-686da0df9bf23e5c.js → page-fee842d7c2151129.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-4dc614f3ce043e63.js → page-78b40a344ed22f24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-d02c782fc792e2af.js → page-a011dd5b0bff29c5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-239c17b911b84e72.js → page-d92e32d51ab5157e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-dc6a79fb003f746c.js → page-5f21aed6932ef375.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-0fffb84cafdc9da6.js → page-1e1a53946804debf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-1218753c82d585f6.js → page-a1833cd43d832601.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-4b5935e67531efb5.js → page-63d14a4d8fe1e1c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-3285bc87caf1eaa5.js → page-2a511d320f762e06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-ad553b0e06505ecd.js → page-1c3e03f88dac1e21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-fca096e3e860306f.js → page-c4d5289c2ea97a89.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9ebfa78f0d9885c3.js → page-b91f8952deaeaf27.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-3dc5f21f7e39622d.js → page-71b43a2588489987.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +1 -0
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/examples/teams/personas/backend.yaml +4 -0
- package/examples/teams/personas/pm.yaml +4 -0
- package/examples/teams/personas/qa.yaml +4 -0
- package/examples/teams/small-product.yaml +20 -0
- package/graphs/context/nightly-condense.yaml +21 -0
- package/graphs/context/recipes.yaml +12 -0
- package/graphs/demo/inside-seed.yaml +20 -0
- package/graphs/demo/recipes.yaml +12 -0
- package/graphs/lecture/lecture-note.yaml +28 -0
- package/graphs/lecture/recipes.yaml +17 -0
- package/package.json +2 -1
- package/plugins/card-followup/examples/sample-card.png +0 -0
- package/scripts/context-hooks/emit.ts +36 -14
- package/scripts/coord-channel-watch.sh +1 -0
- package/scripts/discord-seat-probe.ts +144 -0
- package/scripts/hq/hq-standby.sh +65 -0
- package/scripts/hq/standby-promote.ts +173 -0
- package/scripts/hq/standby-role.ts +59 -0
- package/scripts/hq/standby-snapshot.ts +226 -0
- package/scripts/hq/standby-verify.ts +219 -0
- package/scripts/lecture-note/node.ts +256 -0
- package/scripts/lib/telegram-inject-args.ts +31 -0
- package/scripts/measure-pod-memory-by-goal.ts +130 -0
- package/scripts/release-loop/auto-approve-node.ts +1 -1
- package/scripts/release-loop/checklist-gate-node.ts +2 -1
- package/scripts/release-loop/gate-node.ts +61 -17
- package/scripts/release-loop/ops-upgrade-node.ts +59 -6
- package/scripts/release-loop/publish-node.ts +4 -1
- package/scripts/release-loop/version-node.ts +13 -3
- package/scripts/telegram-inject.ts +14 -6
- package/scripts/test-deterministic.ts +53 -3
- package/scripts/test-diet/remeasure-list.ts +112 -0
- package/scripts/ux/demo-rehearsal.ts +17 -13
- package/scripts/ux/demo-seed.ts +108 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +12 -5
- package/skills/omni-crawl/SKILL.md +16 -16
- package/skills/omni-crawl/package-lock.json +1 -544
- package/skills/omni-crawl/package.json +3 -4
- package/skills/omni-crawl/scripts/main.ts +13 -13
- package/skills/omni-crawl/scripts/monitor.ts +8 -8
- package/skills/omni-crawl/src/apify.ts +13 -2
- package/skills/omni-crawl/src/capture.ts +6 -0
- package/skills/omni-crawl/src/registry.ts +2 -2
- package/skills/omni-crawl/src/stdout-json.ts +4 -0
- package/skills/omni-digest/SKILL.md +10 -11
- package/skills/omni-digest/package-lock.json +0 -545
- package/skills/omni-digest/package.json +2 -5
- package/skills/omni-digest/scripts/main.ts +3 -3
- package/skills/omni-digest/src/delegate.ts +5 -5
- package/skills/omni-digest/src/fetch.ts +2 -2
- package/skills/omni-digest/src/media.ts +25 -11
- package/skills/omni-digest/src/visual.ts +3 -2
- package/skills/omni-market/scripts/main.ts +1 -1
- package/skills/omni-market/scripts/stdout-json.ts +4 -0
- package/src/agent/identity-env.ts +11 -0
- package/src/agent-mission/driver.ts +150 -23
- package/src/agent-mission/duo.ts +94 -0
- package/src/agent-mission/pty-decision.ts +2 -2
- package/src/autopilot/pty-control-loop.ts +4 -1
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +4 -1
- package/src/boot/daemon-prompt-request.ts +3 -0
- package/src/boot/daemon-prompt-turn.ts +6 -2
- package/src/boot/daemon-runtime.ts +48 -16
- package/src/chat/index.ts +1 -0
- package/src/cli/decisions-cli.ts +51 -19
- package/src/cli/outputs-cli.ts +92 -0
- package/src/cli/persona-cli.ts +45 -13
- package/src/cli/pod-cli.ts +27 -3
- package/src/cli/pod-memory-advice.ts +35 -0
- package/src/cli/pr-cli.ts +15 -1
- package/src/cli/release-cli.ts +135 -15
- package/src/context-bus/condense.ts +194 -0
- package/src/context-bus/context-day.ts +82 -0
- package/src/context-bus/context-now-surfaces.ts +32 -2
- package/src/context-bus/context-now.ts +2 -2
- package/src/context-bus/coord-events.ts +2 -2
- package/src/context-bus/long-term-memory.ts +118 -22
- package/src/context-bus/nightly-condense.ts +153 -0
- package/src/context-bus/session-events.ts +35 -0
- package/src/dashboard/dashboard-status-lines.ts +2 -0
- package/src/dashboard/index.ts +8 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +24 -0
- package/src/decisions/decision-cards.ts +13 -7
- package/src/decisions/decision-ledger.ts +43 -17
- package/src/domains/outbound-alert.ts +99 -31
- package/src/domains/outbound-log-probe.ts +8 -0
- package/src/execution-loop/launch-gate.ts +17 -8
- package/src/field-note/telegram.ts +142 -0
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/graph-approval-card.ts +2 -2
- package/src/graph-runner/graph-grow-llm.ts +23 -13
- package/src/graph-runner/graph-grow.ts +69 -13
- package/src/graph-runner/graph-tick.ts +9 -2
- package/src/graph-runner/runner.ts +92 -24
- package/src/harness/harness-cli-command.ts +165 -17
- package/src/harness/harness-queue-child.ts +37 -0
- package/src/harness/harness-queue.ts +306 -0
- package/src/hooks/consult-intake.ts +153 -0
- package/src/hooks/github-shadow.ts +72 -2
- package/src/hooks/receiver.ts +82 -3
- package/src/hq/hq.ts +318 -0
- package/src/hq/lease.ts +210 -0
- package/src/hq/ledger-write-target.ts +41 -0
- package/src/index.ts +232 -16
- package/src/intake-plane/discord-seat-work.ts +36 -3
- package/src/intake-plane/linear-wish.ts +44 -0
- package/src/intake-plane/persona-answer.ts +5 -0
- package/src/intake-plane/telegram-seat-work.ts +13 -6
- package/src/intake-plane/wish-card.ts +33 -0
- package/src/llm/difficulty.ts +1 -1
- package/src/loops/checker.ts +178 -0
- package/src/loops/loop-cli.ts +37 -3
- package/src/loops/observe.ts +49 -0
- package/src/loops/orchestrator/overlap.ts +141 -0
- package/src/loops/orchestrator/traffic.ts +150 -0
- package/src/loops/package/verify.ts +125 -0
- package/src/loops/verdict.ts +22 -0
- package/src/maturity/feature-maturity.ts +5 -4
- package/src/memory.ts +37 -4
- package/src/nexus/api/card-followup-route.ts +118 -0
- package/src/nexus/api/decisions-route.ts +68 -0
- package/src/nexus/api/http-server.ts +78 -8
- package/src/nexus/api/outbound-report.ts +40 -7
- package/src/nexus/api/personas.ts +97 -54
- package/src/nexus/api/skill-exec-route.ts +97 -0
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-history.ts +88 -0
- package/src/nexus/api/workflows.ts +52 -0
- package/src/nexus/outbound/delivery-ledger.ts +17 -8
- package/src/nexus/outbound/router.ts +8 -3
- package/src/persona/loader.ts +18 -0
- package/src/persona/persona-todo.ts +14 -9
- package/src/persona/presets.ts +10 -2
- package/src/persona/team.ts +105 -0
- package/src/persona/types.ts +4 -0
- package/src/plugins/maker/market-bundle.ts +2 -1
- package/src/pod-lease/host-lease.ts +130 -20
- package/src/release-loop/checklist.ts +15 -3
- package/src/release-loop/feature-store.ts +24 -10
- package/src/release-loop/landed-but-yellow.ts +60 -0
- package/src/seat-dispatch/seat-ask.ts +57 -12
- package/src/seat-loop/seat-loop.ts +253 -18
- package/src/seat-loop/seat-pack.ts +127 -0
- package/src/self-dev/ask-launch-flow.ts +3 -0
- package/src/self-dev/draft-sweep.ts +44 -10
- package/src/self-dev/draft-triage-rules.ts +54 -2
- package/src/self-implement/budget-gate.ts +29 -12
- package/src/self-implement/gate-candidate-ranking.ts +60 -0
- package/src/self-implement/gate-scope.ts +9 -8
- package/src/self-implement/headless-elanous-driver.ts +7 -2
- package/src/self-implement/l8-merge-queue.ts +157 -1
- package/src/self-implement/orchestrator.ts +15 -0
- package/src/self-implement/test-seams.ts +1 -0
- package/src/steward/launch.ts +5 -1
- package/src/steward/readiness.ts +125 -0
- package/src/steward/rescue.ts +75 -0
- package/src/steward/triage.ts +48 -9
- package/src/task-cards/card-cli.ts +78 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -10
- package/src/task-orchestrator/surfaces/pod-lease.ts +18 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +46 -8
- package/src/telegram-commands.ts +21 -0
- package/src/telegram-dispatch-forward.ts +1 -1
- package/src/telegram-fabric-plan.ts +90 -0
- package/src/telegram-project-command.ts +84 -4
- package/src/telegram.ts +37 -5
- package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
- package/src/user-config.ts +101 -13
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +4 -0
- package/src/workflow-runtime/node-catalog.ts +13 -0
- package/src/workflow-runtime/nodes/knowledge.ts +38 -0
- package/src/workflow-runtime/schema.ts +17 -1
- package/src/workflow-runtime/types.ts +13 -1
- package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-b12b6d2f8d2d1aea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +0 -1
- package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +0 -1
- /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
3
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
1
4
|
import { createGraphVariant } from '../self-implement/graph-variant.js';
|
|
2
5
|
import type { GraphEdgeSpec, GraphNodeSpec, GraphTemplateSpec } from '../self-implement/graph-yaml.js';
|
|
3
6
|
import { createLLMGrowthProposer } from './graph-grow-llm.js';
|
|
@@ -10,6 +13,8 @@ export interface GrowthProposal {
|
|
|
10
13
|
|
|
11
14
|
export type GrowthProposer = (input: { graph: GraphTemplateSpec; nodeId: string; outcome: string; runId: string; output?: unknown }) => GrowthProposal | undefined | Promise<GrowthProposal | undefined>;
|
|
12
15
|
export type GrowthRecipeEffect = 'read-only' | 'external-effect' | 'unknown';
|
|
16
|
+
export interface GrowthCommand { command: string; dry_run_command?: string; timeout_ms?: number }
|
|
17
|
+
export interface GrowthRecipeClassification { effect: GrowthRecipeEffect; command?: GrowthCommand; approval?: string }
|
|
13
18
|
|
|
14
19
|
export interface GraphGrowth {
|
|
15
20
|
node: GraphNodeSpec;
|
|
@@ -18,30 +23,81 @@ export interface GraphGrowth {
|
|
|
18
23
|
undo: { removeNode: string; removeOutcome: { from: string; outcome: string }; removeEdge?: GraphEdgeSpec };
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
export interface GrowthDecisionDeps {
|
|
27
|
+
ledger?: Pick<DecisionLedger, 'raiseOnce'>;
|
|
28
|
+
root?: string;
|
|
29
|
+
now?: () => Date;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function proposalGrowth(input: Parameters<GrowthProposer>[0], proposal: GrowthProposal): GraphGrowth {
|
|
33
|
+
const edges: GraphEdgeSpec[] = [
|
|
34
|
+
{ from: input.nodeId, on: 'outcome', map: { [input.outcome]: proposal.node.nodeId } },
|
|
35
|
+
{ from: proposal.node.nodeId, to: proposal.returnTo as string },
|
|
36
|
+
];
|
|
37
|
+
return { node: proposal.node, edges, reason: proposal.reason,
|
|
38
|
+
undo: { removeNode: proposal.node.nodeId, removeOutcome: { from: input.nodeId, outcome: input.outcome }, removeEdge: edges[1]! } };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The card's own ref commits to the entire growth and resolved recipe, independently of the parked run state. */
|
|
42
|
+
export function growthApprovalRef(identity: { graphId: string; runId: string; from: string; outcome: string },
|
|
43
|
+
growth: GraphGrowth, command?: GrowthCommand, approval?: string): string {
|
|
44
|
+
const hash = createHash('sha256').update(JSON.stringify({ identity, growth, command, approval })).digest('hex');
|
|
45
|
+
return `graph-growth:${hash}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function growthDecisionRef(input: Parameters<GrowthProposer>[0], proposal: GrowthProposal, command?: GrowthCommand, approval?: string): string {
|
|
49
|
+
return growthApprovalRef({ graphId: input.graph.graphId, runId: input.runId, from: input.nodeId, outcome: input.outcome },
|
|
50
|
+
proposalGrowth(input, proposal), command, approval);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function growthDecision(input: Parameters<GrowthProposer>[0], proposal: GrowthProposal, deps: GrowthDecisionDeps = {}, command?: GrowthCommand, approval?: string): DecisionEntry {
|
|
54
|
+
const ledger = deps.ledger ?? new DecisionLedger({ stateDir: deps.root ?? effectiveInstanceRoot() });
|
|
55
|
+
const ref = growthDecisionRef(input, proposal, command, approval);
|
|
56
|
+
const summary = `${proposal.node.nodeId} (${proposal.node.kind}, ${proposal.node.recipe}) → ${proposal.returnTo}`;
|
|
57
|
+
const commandDigest = command ? createHash('sha256').update(JSON.stringify(command)).digest('hex') : undefined;
|
|
58
|
+
return ledger.raiseOnce({ title: `${input.graph.graphId} / ${input.nodeId} / ${input.outcome} growth approval`, category: 'scope',
|
|
59
|
+
scqa: { s: `Graph ${input.graph.graphId}, node ${input.nodeId}, outcome ${input.outcome} has no mapped growth edge.`,
|
|
60
|
+
c: `Proposed node: ${summary}; command SHA-256: ${commandDigest ?? 'unresolved'}; reason: ${proposal.reason}`.slice(0, 240) },
|
|
61
|
+
options: [{ key: 'a', label: '승인', consequence: `Apply proposed node ${proposal.node.nodeId} and continue the run` },
|
|
62
|
+
{ key: 'b', label: '거절', consequence: 'Keep the original fallback route' }],
|
|
63
|
+
recommendation: { skipped: true, reason: 'Side-effect recipe requires a human decision' },
|
|
64
|
+
...(command || approval ? { pendingQuestion: [approval ? `Approval recipe: ${approval}` : undefined,
|
|
65
|
+
command ? `Proposed command (${commandDigest}):\n${command.command}` : undefined].filter(Boolean).join('\n') } : {}),
|
|
66
|
+
raisedBy: { agent: 'graph-runner' }, refs: [ref], dueAt: new Date((deps.now?.() ?? new Date()).getTime() + 8 * 60 * 60 * 1000).toISOString() }, ref);
|
|
67
|
+
}
|
|
68
|
+
|
|
21
69
|
export async function proposeGrowth(input: Parameters<GrowthProposer>[0],
|
|
22
|
-
proposer: GrowthProposer = createLLMGrowthProposer(), classifyRecipe?: (node: GraphNodeSpec) => GrowthRecipeEffect
|
|
70
|
+
proposer: GrowthProposer = createLLMGrowthProposer(), classifyRecipe?: (node: GraphNodeSpec) => GrowthRecipeEffect | GrowthRecipeClassification,
|
|
71
|
+
decisionDeps: GrowthDecisionDeps = {}): Promise<{ ok: true; graph: GraphTemplateSpec; growth: GraphGrowth } | { ok: false; reason: string; park?: boolean; growth?: GraphGrowth; decisionId?: string; decisionRef?: string; command?: GrowthCommand; approval?: string }> {
|
|
23
72
|
let proposal: GrowthProposal | undefined;
|
|
24
73
|
try { proposal = await proposer(input); }
|
|
25
74
|
catch (error) { return { ok: false, reason: `proposer failed: ${String(error)}` }; }
|
|
26
75
|
if (!proposal) return { ok: false, reason: 'no proposal' };
|
|
27
76
|
if (!proposal.node || typeof proposal.reason !== 'string' || !proposal.reason.trim()) return { ok: false, reason: 'proposal requires a node and reason' };
|
|
28
|
-
|
|
29
|
-
|
|
77
|
+
let result: ReturnType<typeof createGraphVariant>;
|
|
78
|
+
try {
|
|
79
|
+
result = createGraphVariant({ template: input.graph, goal: `g_${input.runId.replace(/[^a-z0-9_-]/gi, '_')}`,
|
|
80
|
+
plan: { growth: { node: proposal.node, from: input.nodeId, outcome: input.outcome, returnTo: proposal.returnTo } } });
|
|
81
|
+
} catch (error) { return { ok: false, reason: `growth rejected: ${String(error)}` }; }
|
|
30
82
|
if (!result.ok) return { ok: false, reason: `growth rejected: ${JSON.stringify(result.rejections)}` };
|
|
31
83
|
// Neither a recipe id nor its declared tools certify arbitrary executable code.
|
|
32
84
|
// Only a trusted classification of the resolved recipe may authorize execution.
|
|
33
85
|
let effect: GrowthRecipeEffect = 'unknown';
|
|
34
86
|
if (proposal.node.recipe !== 'none') {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
87
|
+
let command: GrowthCommand | undefined;
|
|
88
|
+
let approval: string | undefined;
|
|
89
|
+
try {
|
|
90
|
+
const classification = classifyRecipe?.(proposal.node) ?? 'unknown';
|
|
91
|
+
effect = typeof classification === 'string' ? classification : classification.effect;
|
|
92
|
+
command = typeof classification === 'string' ? undefined : classification.command;
|
|
93
|
+
approval = typeof classification === 'string' ? undefined : classification.approval;
|
|
94
|
+
} catch { effect = 'unknown'; }
|
|
95
|
+
if (effect !== 'read-only' || approval) {
|
|
96
|
+
const decision = growthDecision(input, proposal, decisionDeps, command, approval);
|
|
97
|
+
return { ok: false, park: true, reason: '사람 확인 필요: external or unclassified side-effect recipe',
|
|
98
|
+
decisionId: decision.id, decisionRef: growthDecisionRef(input, proposal, command, approval), command, approval,
|
|
99
|
+
growth: proposalGrowth(input, proposal) };
|
|
39
100
|
}
|
|
40
101
|
}
|
|
41
|
-
|
|
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]! } } };
|
|
102
|
+
return { ok: true, graph: result.template, growth: proposalGrowth(input, proposal) };
|
|
47
103
|
}
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { parse as parseYaml } from 'yaml';
|
|
4
4
|
import { debug } from '../debug/log.js';
|
|
5
5
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
6
|
+
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
6
7
|
import { latestGraphRun, runGraph, type GraphRunOptions, type GraphRunState } from './runner.js';
|
|
7
8
|
|
|
8
9
|
export interface GraphTickOptions {
|
|
@@ -35,10 +36,16 @@ export async function graphTick(path: string, options: GraphTickOptions = {}): P
|
|
|
35
36
|
acquired = true;
|
|
36
37
|
await options.deps?.notify?.();
|
|
37
38
|
const latest = latestGraphRun(graphId, root);
|
|
39
|
+
const growthCard = latest?.growthPark
|
|
40
|
+
? (options.deps?.growthDecision?.list ?? ((filters: { status: 'all' }) => new DecisionLedger({ stateDir: root }).list(filters)))({ status: 'all' })
|
|
41
|
+
.find(entry => entry.id === latest.growthPark?.decisionId && entry.refs?.includes(latest.growthPark.decisionRef))
|
|
42
|
+
: undefined;
|
|
43
|
+
const growthDecided = growthCard?.status === 'decided' && growthCard.decidedBy?.kind === 'human' &&
|
|
44
|
+
(growthCard.choice === 'a' || growthCard.choice === 'b');
|
|
38
45
|
let result: GraphTickResult;
|
|
39
|
-
if (latest?.status === 'awaiting-approval' && !latest.pending?.decision) {
|
|
46
|
+
if (latest?.status === 'awaiting-approval' && !latest.pending?.decision && !growthDecided) {
|
|
40
47
|
result = { action: 'waiting', runId: latest.runId, status: latest.status };
|
|
41
|
-
} else if (latest?.status === 'running' || (latest?.status === 'awaiting-approval' && latest.pending?.decision)) {
|
|
48
|
+
} else if (latest?.status === 'running' || (latest?.status === 'awaiting-approval' && (latest.pending?.decision || growthDecided))) {
|
|
42
49
|
const state = await runGraph(path, { resumeRunId: latest.runId, deps: { ...options.deps, root } });
|
|
43
50
|
result = { action: 'resumed', runId: state.runId, status: state.status };
|
|
44
51
|
} else if (options.startIfIdle) {
|
|
@@ -11,7 +11,8 @@ import { listInstalledPlugins } from '../plugins/install/plugin-install.js';
|
|
|
11
11
|
import { credentialStatus, pluginEnv } from '../plugins/install/plugin-credentials.js';
|
|
12
12
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
13
13
|
import { loadNodeCatalog } from '../self-implement/graph-catalog.js';
|
|
14
|
-
import { proposeGrowth, type GraphGrowth, type GrowthProposer, type GrowthRecipeEffect } from './graph-grow.js';
|
|
14
|
+
import { growthApprovalRef, proposeGrowth, type GraphGrowth, type GrowthProposer, type GrowthRecipeEffect, type GrowthDecisionDeps, type GrowthCommand } from './graph-grow.js';
|
|
15
|
+
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
15
16
|
import { createLLMGrowthProposer } from './graph-grow-llm.js';
|
|
16
17
|
import { parseGraphTemplateYaml, type GraphEdgeSpec, type GraphNodeSpec, type GraphTemplateSpec } from '../self-implement/graph-yaml.js';
|
|
17
18
|
import { createGraphVariant, type GraphVariantPlan } from '../self-implement/graph-variant.js';
|
|
@@ -43,7 +44,7 @@ export interface GraphRunState {
|
|
|
43
44
|
variant?: { overlayId: string; goal: string; plan: GraphVariantPlan };
|
|
44
45
|
growth?: GraphGrowth[];
|
|
45
46
|
growthRejections?: Array<{ from: string; outcome: string; reason: string }>;
|
|
46
|
-
growthPark?: { from: string; outcome: string; reason: string };
|
|
47
|
+
growthPark?: { from: string; outcome: string; reason: string; decisionId: string; decisionRef: string; growth: GraphGrowth; command?: GrowthCommand; approval?: string };
|
|
47
48
|
resume?: { from: string; at: string; previousStatus: GraphRunState['status']; graph?: 'snapshot' | 'current' };
|
|
48
49
|
executed: number;
|
|
49
50
|
dryRun: boolean;
|
|
@@ -59,7 +60,7 @@ export interface GraphRunOptions {
|
|
|
59
60
|
resumeGraphId?: string;
|
|
60
61
|
fromNodeId?: string;
|
|
61
62
|
useCurrentGraph?: boolean;
|
|
62
|
-
deps?: { root?: string; runBash?: BashRun; log?: (event: string, data: Record<string, unknown>) => void; processStartMs?: (pid: number) => number | null; growthProposer?: GrowthProposer; growthLLM?: (prompt: string) => Promise<string>; classifyGrowthRecipe?: (node: GraphNodeSpec, resolved: { command?: string; approval?: string }) => GrowthRecipeEffect };
|
|
63
|
+
deps?: { root?: string; runBash?: BashRun; log?: (event: string, data: Record<string, unknown>) => void; processStartMs?: (pid: number) => number | null; growthProposer?: GrowthProposer; growthLLM?: (prompt: string) => Promise<string>; classifyGrowthRecipe?: (node: GraphNodeSpec, resolved: { command?: string; approval?: string }) => GrowthRecipeEffect; growthDecision?: GrowthDecisionDeps & { list?: (filters: { status: 'all' }) => DecisionEntry[] } };
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
function safeSegment(value: string): string {
|
|
@@ -69,14 +70,17 @@ function safeSegment(value: string): string {
|
|
|
69
70
|
return value;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
type CommandRecipe = { command: string; timeout_ms?: number };
|
|
73
|
-
type Recipe = CommandRecipe | { approval: string };
|
|
73
|
+
type CommandRecipe = { command: string; dry_run_command?: string; timeout_ms?: number };
|
|
74
|
+
type Recipe = CommandRecipe | ({ approval: string } & Partial<CommandRecipe>);
|
|
74
75
|
|
|
75
76
|
/** 접두 없는 recipe 가 카탈로그 역할이고, 그래프 옆 recipes.yaml 에 같은 키의 `{ command }` 가 있을 때만 cmd 처럼 실행한다. */
|
|
76
77
|
function roleCommand(recipe: string, recipes: Record<string, Recipe>, catalogRoles: ReadonlySet<string>): CommandRecipe | undefined {
|
|
77
78
|
if (!recipe || recipe === 'none' || recipe.includes(':') || !catalogRoles.has(recipe)) return undefined;
|
|
78
79
|
const entry = recipes[recipe];
|
|
79
|
-
|
|
80
|
+
// An entry that carries an approval only runs through `approval:` — never as a bare command.
|
|
81
|
+
return entry && !('approval' in entry) && typeof entry.command === 'string' ? { command: entry.command,
|
|
82
|
+
...(entry.dry_run_command ? { dry_run_command: entry.dry_run_command } : {}),
|
|
83
|
+
...(entry.timeout_ms ? { timeout_ms: entry.timeout_ms } : {}) } : undefined;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
function resolveNodeRecipe(node: GraphNodeSpec, recipes: Record<string, Recipe>, catalogRoles: ReadonlySet<string>): { command?: CommandRecipe; approval?: { approval: string } } {
|
|
@@ -84,12 +88,17 @@ function resolveNodeRecipe(node: GraphNodeSpec, recipes: Record<string, Recipe>,
|
|
|
84
88
|
if (node.recipe.startsWith('cmd:')) {
|
|
85
89
|
const id = node.recipe.slice(4);
|
|
86
90
|
const entry = recipes[id];
|
|
87
|
-
return entry && '
|
|
91
|
+
return entry && !('approval' in entry) && typeof entry.command === 'string' ? { command: { command: entry.command,
|
|
92
|
+
...(entry.dry_run_command ? { dry_run_command: entry.dry_run_command } : {}),
|
|
93
|
+
...(entry.timeout_ms ? { timeout_ms: entry.timeout_ms } : {}) } } : {};
|
|
88
94
|
}
|
|
89
95
|
if (node.recipe.startsWith('approval:')) {
|
|
90
96
|
const id = node.recipe.slice(9);
|
|
91
97
|
const entry = recipes[id];
|
|
92
|
-
return entry && 'approval' in entry ? { approval: entry
|
|
98
|
+
return entry && 'approval' in entry ? { approval: entry,
|
|
99
|
+
...('command' in entry && typeof entry.command === 'string' ? { command: { command: entry.command,
|
|
100
|
+
...(entry.dry_run_command ? { dry_run_command: entry.dry_run_command } : {}),
|
|
101
|
+
...(entry.timeout_ms ? { timeout_ms: entry.timeout_ms } : {}) } } : {}) } : {};
|
|
93
102
|
}
|
|
94
103
|
const command = roleCommand(node.recipe, recipes, catalogRoles);
|
|
95
104
|
return command ? { command } : {};
|
|
@@ -113,14 +122,25 @@ function recipesFor(path: string, recipeSource: string): Record<string, Recipe>
|
|
|
113
122
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error(`invalid recipe: ${id}`);
|
|
114
123
|
const item = raw as Record<string, unknown>;
|
|
115
124
|
if (Object.hasOwn(item, 'approval')) {
|
|
116
|
-
if (typeof item.approval !== 'string' || !item.approval.trim() ||
|
|
117
|
-
|
|
125
|
+
if (typeof item.approval !== 'string' || !item.approval.trim() ||
|
|
126
|
+
(item.command !== undefined && (typeof item.command !== 'string' || !item.command.trim())) ||
|
|
127
|
+
(item.timeout_ms !== undefined && (!Number.isSafeInteger(item.timeout_ms) || (item.timeout_ms as number) <= 0)) ||
|
|
128
|
+
(item.dry_run_command !== undefined && (typeof item.dry_run_command !== 'string' || !item.dry_run_command.trim())) ||
|
|
129
|
+
(item.command === undefined && (item.timeout_ms !== undefined || item.dry_run_command !== undefined)) ||
|
|
130
|
+
Object.keys(item).some(key => !['approval', 'command', 'dry_run_command', 'timeout_ms'].includes(key))) throw new Error(`invalid recipe: ${id}`);
|
|
131
|
+
recipes[id] = { approval: item.approval,
|
|
132
|
+
...(item.command === undefined ? {} : { command: item.command as string }),
|
|
133
|
+
...(item.dry_run_command === undefined ? {} : { dry_run_command: item.dry_run_command as string }),
|
|
134
|
+
...(item.timeout_ms === undefined ? {} : { timeout_ms: item.timeout_ms as number }) };
|
|
118
135
|
} else {
|
|
119
136
|
if (typeof item.command !== 'string' || !item.command.trim() ||
|
|
120
|
-
(item.timeout_ms !== undefined && (!Number.isSafeInteger(item.timeout_ms) || (item.timeout_ms as number) <= 0))
|
|
137
|
+
(item.timeout_ms !== undefined && (!Number.isSafeInteger(item.timeout_ms) || (item.timeout_ms as number) <= 0)) ||
|
|
138
|
+
(item.dry_run_command !== undefined && (typeof item.dry_run_command !== 'string' || !item.dry_run_command.trim()))) {
|
|
121
139
|
throw new Error(`invalid recipe: ${id}`);
|
|
122
140
|
}
|
|
123
|
-
recipes[id] = { command: item.command,
|
|
141
|
+
recipes[id] = { command: item.command,
|
|
142
|
+
...(item.dry_run_command === undefined ? {} : { dry_run_command: item.dry_run_command as string }),
|
|
143
|
+
...(item.timeout_ms === undefined ? {} : { timeout_ms: item.timeout_ms as number }) };
|
|
124
144
|
}
|
|
125
145
|
}
|
|
126
146
|
return recipes;
|
|
@@ -490,8 +510,51 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
490
510
|
// Persist the restart boundary before executing it, so previous outputs remain durable.
|
|
491
511
|
persistOwnedRun(state, saved!, true);
|
|
492
512
|
}
|
|
513
|
+
let resumedGrowth = false;
|
|
493
514
|
if (options.resumeRunId && state.growthPark) {
|
|
494
|
-
|
|
515
|
+
if (state.graphId !== graphId || state.runId !== runId || (graphMode === 'current' && state.sourceHash !== approvalSourceHash)) {
|
|
516
|
+
throw new Error(`growth source changed since run was paused: ${graphId}/${runId}`);
|
|
517
|
+
}
|
|
518
|
+
const parked = state.growthPark;
|
|
519
|
+
const decision = (options.deps?.growthDecision?.list?.({ status: 'all' }) ??
|
|
520
|
+
new DecisionLedger({ stateDir: root }).list({ status: 'all' }))
|
|
521
|
+
.find(entry => entry.id === parked.decisionId && entry.refs?.includes(parked.decisionRef));
|
|
522
|
+
if (!decision || decision.status !== 'decided' || decision.decidedBy?.kind !== 'human' ||
|
|
523
|
+
(decision.choice !== 'a' && decision.choice !== 'b')) {
|
|
524
|
+
throw new Error(`growth is parked for human confirmation: ${graphId}/${runId}`);
|
|
525
|
+
}
|
|
526
|
+
if (typeof parked.decisionRef !== 'string' ||
|
|
527
|
+
growthApprovalRef({ graphId, runId, from: parked.from, outcome: parked.outcome },
|
|
528
|
+
parked.growth, parked.command, parked.approval) !== parked.decisionRef) {
|
|
529
|
+
throw new Error(`growth approval target changed since card was raised: ${graphId}/${runId}`);
|
|
530
|
+
}
|
|
531
|
+
if (state.status !== 'awaiting-approval' || state.dryRun || state.path.at(-1) !== parked.from ||
|
|
532
|
+
state.path.length !== state.nodes.length || state.nodes.at(-1)?.nodeId !== parked.from ||
|
|
533
|
+
state.pending?.nodeId !== parked.from || state.growthRejections?.at(-1)?.from !== parked.from ||
|
|
534
|
+
state.growthRejections.at(-1)?.outcome !== parked.outcome ||
|
|
535
|
+
parked.growth.undo.removeOutcome.from !== parked.from || parked.growth.undo.removeOutcome.outcome !== parked.outcome) {
|
|
536
|
+
throw new Error(`invalid parked growth: ${graphId}/${runId}`);
|
|
537
|
+
}
|
|
538
|
+
if (decision.choice === 'a') {
|
|
539
|
+
const currentCommand = resolveNodeRecipe(parked.growth.node, recipes, catalogRoles).command;
|
|
540
|
+
const currentApproval = resolveNodeRecipe(parked.growth.node, recipes, catalogRoles).approval?.approval;
|
|
541
|
+
// The approved target is the (approval, command) pair as raised — a command appearing, vanishing or changing all fail.
|
|
542
|
+
if (parked.growth.node.recipe !== 'none' &&
|
|
543
|
+
(currentApproval !== parked.approval ||
|
|
544
|
+
JSON.stringify(currentCommand ?? null) !== JSON.stringify(parked.command ?? null) ||
|
|
545
|
+
(parked.approval === undefined && !parked.command))) {
|
|
546
|
+
throw new Error(`growth command changed since approval: ${graphId}/${runId}`);
|
|
547
|
+
}
|
|
548
|
+
graph = withGrowth(graph, [parked.growth]);
|
|
549
|
+
state.growth ??= [];
|
|
550
|
+
state.growth.push(parked.growth);
|
|
551
|
+
state.growthRejections!.pop();
|
|
552
|
+
}
|
|
553
|
+
resumedGrowth = true;
|
|
554
|
+
delete state.pending;
|
|
555
|
+
delete state.growthPark;
|
|
556
|
+
state.status = 'running';
|
|
557
|
+
persistOwnedRun(state, saved!);
|
|
495
558
|
}
|
|
496
559
|
if (options.resumeRunId && state.status === 'running' && state.pending) {
|
|
497
560
|
throw new Error(`run is not awaiting approval: ${graphId}/${runId}`);
|
|
@@ -590,7 +653,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
590
653
|
let exit: number | null = null;
|
|
591
654
|
let error: string | undefined;
|
|
592
655
|
let output: unknown;
|
|
593
|
-
if (command && !state.dryRun && !resumingCompleted) {
|
|
656
|
+
if (command && (!approval || state.dryRun || ok) && (!state.dryRun || command.dry_run_command) && !resumingCompleted) {
|
|
594
657
|
const outputs: Record<string, unknown> = Object.create(null);
|
|
595
658
|
// 구조 산출(마지막 JSON 줄)이 있으면 그것을, 없으면 원문을 준다 — 다음 노드가 판단을 «값»으로 받는다.
|
|
596
659
|
for (const previous of state.nodes) outputs[previous.nodeId] = lastJsonObject(previous.output) ?? previous.output ?? null;
|
|
@@ -635,9 +698,10 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
635
698
|
debug.log('graph-runner', 'child-universe-pinned', { graphId, runId, node: current, configDir: env.ELANOUS_CONFIG_DIR, stateDir: env.ELANOUS_STATE_DIR });
|
|
636
699
|
}
|
|
637
700
|
env.ELANOUS_GRAPH_CONTEXT = contextPath;
|
|
701
|
+
env.ELANOUS_GRAPH_DRY_RUN = state.dryRun ? '1' : '0';
|
|
638
702
|
env.ELANOUS_GRAPH_DIR = dirname(resolve(path));
|
|
639
703
|
const ctx = { arguments: '', artifactsDir: dirname(statePath), outputs: {}, resolvedProvider: undefined, resolvedModel: undefined, toolPolicy: {}, env } as NodeExecContext;
|
|
640
|
-
const result = await executeBashNode({ id: current, type: 'bash', bash: command.command, ...(command.timeout_ms ? { idle_timeout: command.timeout_ms } : {}) } as BashNode, ctx, { runBash } as WorkflowDeps);
|
|
704
|
+
const result = await executeBashNode({ id: current, type: 'bash', bash: state.dryRun ? command.dry_run_command! : command.command, ...(command.timeout_ms ? { idle_timeout: command.timeout_ms } : {}) } as BashNode, ctx, { runBash } as WorkflowDeps);
|
|
641
705
|
output = result.output;
|
|
642
706
|
ok = result.ok;
|
|
643
707
|
exit = code;
|
|
@@ -654,14 +718,14 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
654
718
|
if (approval && !state.dryRun && !command) {
|
|
655
719
|
output = JSON.stringify({ outcome: ok ? 'approved' : 'rejected', ...(state.pending?.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending?.decidedAt ?? null });
|
|
656
720
|
}
|
|
657
|
-
state.nodes.push({ nodeId: current, ok, exit, executed: !!command && !state.dryRun, ...((command || approval) && !state.dryRun && output !== undefined ? { output } : {}), ...(error ? { error } : {}),
|
|
721
|
+
state.nodes.push({ nodeId: current, ok, exit, executed: !!command && (!state.dryRun || !!command.dry_run_command), ...((command || approval) && (!state.dryRun || !!command?.dry_run_command) && output !== undefined ? { output } : {}), ...(error ? { error } : {}),
|
|
658
722
|
...(approval && state.pending ? { ...(state.pending.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending.decidedAt } : {}) });
|
|
659
723
|
}
|
|
660
724
|
if (approval) {
|
|
661
725
|
delete state.pending;
|
|
662
726
|
delete state.approvalSourceHash;
|
|
663
727
|
}
|
|
664
|
-
const reported = command && !state.dryRun ? lastJsonObject(resumingCompleted ? completed?.output : output)?.outcome : undefined;
|
|
728
|
+
const reported = command && (!state.dryRun || command.dry_run_command) ? lastJsonObject(resumingCompleted ? completed?.output : output)?.outcome : undefined;
|
|
665
729
|
const namedOutcome = typeof reported === 'string' ? reported : undefined;
|
|
666
730
|
const seconds = Number(((performance.now() - startedAt) / 1000).toFixed(2));
|
|
667
731
|
console.error(`[graph] ${current} ${ok ? 'ok' : 'fail'} (${seconds.toFixed(2)}s)`);
|
|
@@ -677,7 +741,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
677
741
|
}
|
|
678
742
|
const fromNodeId = current;
|
|
679
743
|
const edge = graph.edges.find((candidate) => candidate.from === current);
|
|
680
|
-
if (document.grow === 'on' && !state.dryRun && namedOutcome !== undefined &&
|
|
744
|
+
if (!resumedGrowth && document.grow === 'on' && !state.dryRun && namedOutcome !== undefined &&
|
|
681
745
|
edge?.on === 'outcome' && edge.map && !Object.hasOwn(edge.map, namedOutcome) &&
|
|
682
746
|
!state.growth?.some((item) => item.undo.removeOutcome.from === current && item.undo.removeOutcome.outcome === namedOutcome) &&
|
|
683
747
|
!state.growthRejections?.some((item) => item.from === current && item.outcome === namedOutcome)) {
|
|
@@ -685,9 +749,10 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
685
749
|
: await proposeGrowth({ graph, nodeId: current, outcome: namedOutcome, runId, output: state.nodes.at(-1)?.output },
|
|
686
750
|
options.deps?.growthProposer ?? createLLMGrowthProposer(options.deps?.growthLLM), (candidate) => {
|
|
687
751
|
const resolved = resolveNodeRecipe(candidate, recipes, catalogRoles);
|
|
688
|
-
if (resolved.
|
|
689
|
-
return options.deps?.classifyGrowthRecipe?.(candidate, { command: resolved.command?.command }) ?? 'unknown'
|
|
690
|
-
|
|
752
|
+
if (!resolved.command && !resolved.approval && candidate.recipe !== 'none') return 'unknown';
|
|
753
|
+
return { effect: resolved.approval ? 'unknown' : options.deps?.classifyGrowthRecipe?.(candidate, { command: resolved.command?.command }) ?? 'unknown',
|
|
754
|
+
command: resolved.command, approval: resolved.approval?.approval };
|
|
755
|
+
}, { ...options.deps?.growthDecision, root });
|
|
691
756
|
if (result.ok) {
|
|
692
757
|
graph = result.graph;
|
|
693
758
|
state.growth ??= [];
|
|
@@ -699,17 +764,20 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
699
764
|
} else {
|
|
700
765
|
state.growthRejections ??= [];
|
|
701
766
|
state.growthRejections.push({ from: current, outcome: namedOutcome, reason: result.reason });
|
|
702
|
-
if (result.park) {
|
|
767
|
+
if (result.park && result.growth && result.decisionId && result.decisionRef) {
|
|
703
768
|
state.status = 'awaiting-approval';
|
|
704
769
|
state.pending = { nodeId: current, message: result.reason, since: new Date().toISOString() };
|
|
705
|
-
state.growthPark = { from: current, outcome: namedOutcome, reason: result.reason
|
|
770
|
+
state.growthPark = { from: current, outcome: namedOutcome, reason: result.reason,
|
|
771
|
+
growth: result.growth, decisionId: result.decisionId, decisionRef: result.decisionRef,
|
|
772
|
+
...(result.command ? { command: result.command } : {}), ...(result.approval ? { approval: result.approval } : {}) };
|
|
706
773
|
persist();
|
|
707
774
|
break;
|
|
708
775
|
}
|
|
709
776
|
}
|
|
710
777
|
persist();
|
|
711
778
|
}
|
|
712
|
-
current = nextNode(graph.edges, current, state.dryRun ? 'ok' : ok ? 'ok' : 'fail',
|
|
779
|
+
current = nextNode(graph.edges, current, state.dryRun && !command?.dry_run_command ? 'ok' : ok ? 'ok' : 'fail', namedOutcome);
|
|
780
|
+
resumedGrowth = false;
|
|
713
781
|
if (current !== undefined) publishInsideEvent({ kind: 'edge', graphId, runId, from: fromNodeId, to: current });
|
|
714
782
|
if (current === undefined) {
|
|
715
783
|
state.status = 'failed';
|