elanous 0.2.18 → 0.2.20
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/{1307-30dff22c9e1f3073.js → 1307-77fad80cf54477c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +25 -0
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d92f7ee3994e84c7.js → page-c855c24de7db74fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-4733c967b56a4277.js → page-85b316bb4235980b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-d22fd849cc22f104.js → page-9b2b26a33cb2cd42.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-bb742f995c6769e7.js → page-c9955fde67b1bcb5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-02b073a2ac793b83.js → page-7a852956c74015a3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-8a9b3594bc23289b.js → page-4ba4abe096b6c2a8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-308111abadf35670.js → page-f5d56ff5f45ad164.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-2260e9eef0357421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-5d97b2901e0d1ea6.js → page-7c058069c5e7fa63.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-8c3d8e63ae2e1c98.js → page-466bf0ee16ad1432.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-9b72a07d424701c6.js → page-a23bc71713bcc08f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-a572f58cd3ca5864.js → page-9cd565005b93aca6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-be5ea965a07fbc41.js → page-474cb5888ea0e8f3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-fa244bd146a00ae8.js → page-9ca21f9ad3208a61.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-44d56d6391965a6a.js → page-f9ccb1b1adbea14d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-83a700d4c754d802.js → page-ec102c9b8b691e37.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-7b3170f374f77b9d.js → not-found-a9c2cfa8dda922a5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1467047104141297.js → page-db03ae3589a81c9e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-102310930791197a.js → page-75bffecc9aa1004e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-6ed0b73bae8351d8.js → page-31d5b875760bb62d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-43336055451f6e37.js → page-68793c6e10331d66.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-3cc47dca0922ab9a.js → page-46d5201d2207a511.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-a77ee355a2304ff0.js → page-d81c07a7b4f618ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-b17cd46cf0e9b8cf.js → page-be1c4e8697f22360.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-7bf54c9bb9274af8.js → page-0fd895fab9b62c1b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0acc8e198f7b0dfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-134da468538baf48.js → page-34882c84f84021c5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-c2782b330447ffb6.js → webpack-c260571513bf43c2.js} +1 -1
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.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/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/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/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/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 +16 -15
- 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 +16 -15
- 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/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +24 -23
- 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/graphs/demo/editor-recipes.yaml +22 -0
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/retro/daily.yaml +25 -0
- package/graphs/retro/recipes.yaml +24 -0
- package/package.json +1 -1
- package/scripts/ask-marker-check.ts +3 -1
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +3 -1
- package/scripts/land-global-checks.ts +89 -0
- package/scripts/lib/pch-regress-map.ts +24 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +628 -65
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +229 -0
- package/scripts/release-loop/preflight.ts +6 -4
- package/scripts/release-loop/publish-window.ts +141 -0
- package/scripts/release-loop/release-docs.ts +1 -4
- package/scripts/release-loop/release-universe.ts +38 -0
- package/scripts/release-loop/resume-release.ts +67 -5
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +12 -2
- package/scripts/release-story/draft.ts +5 -1
- package/scripts/retro/shadow-compare.ts +71 -0
- package/scripts/rhythm/daily.ts +6 -1
- package/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- package/src/agent-mission/driver.ts +34 -6
- package/src/autopilot/build/llm-conflict-merge.ts +132 -7
- package/src/autopilot/build/merge-intent.ts +135 -7
- package/src/away/presence.ts +40 -0
- package/src/away/release-watch.ts +272 -0
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/config-drift.ts +513 -0
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +13 -7
- package/src/cli/log-event-names.ts +5 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/pr-cli.ts +20 -0
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +52 -12
- package/src/cli/retro-cli.ts +78 -0
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +193 -14
- package/src/context-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +68 -40
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +62 -23
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +44 -13
- package/src/domains/outbound-log-probe.ts +6 -1
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- package/src/graph-runner/graph-run-api.ts +205 -0
- package/src/graph-runner/runner.ts +36 -6
- package/src/harness/harness-cli-command.ts +420 -51
- package/src/harness/harness-queue.ts +315 -25
- package/src/harness/harness-salvage-cli.ts +140 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/fence-outcomes.ts +63 -0
- package/src/hq/fence-wrapper.ts +33 -0
- package/src/hq/hq.ts +17 -2
- package/src/index.ts +118 -6
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- package/src/intake-plane/digest.ts +39 -15
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- package/src/loops/orchestrator/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +33 -3
- package/src/loops/orchestrator/tick.ts +166 -45
- package/src/maturity/feature-maturity.ts +6 -4
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/graph-kinds.ts +10 -4
- package/src/nexus/api/graphs-api.ts +178 -6
- package/src/nexus/api/http-server.ts +54 -8
- package/src/nexus/api/loop-edges.ts +292 -17
- package/src/nexus/api/ops-api.ts +112 -29
- package/src/nexus/api/ops-board-route.ts +48 -0
- package/src/nexus/index.ts +7 -0
- package/src/onboarding/entry-hints.ts +5 -0
- package/src/onboarding.ts +24 -6
- package/src/release-loop/checklist.ts +2 -0
- package/src/release-loop/feature-store.ts +68 -18
- package/src/release-loop/gate-shards.ts +158 -0
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +51 -17
- package/src/release-loop/release-schedule.ts +6 -2
- package/src/release-loop/rubric.ts +57 -1
- package/src/retro/daily.ts +372 -0
- package/src/seat-loop/op-decide.ts +173 -0
- package/src/seat-loop/seat-loop.ts +60 -3
- package/src/seat-loop/seat-report.ts +58 -3
- package/src/self-dev/ask-launch-flow.ts +5 -4
- package/src/self-dev/ask-launch-io.ts +49 -1
- package/src/self-dev/ask-path-candidates.ts +40 -2
- package/src/self-dev/dev-cli.ts +36 -0
- package/src/self-dev/draft-sweep.ts +371 -16
- package/src/self-dev/draft-triage-rules.ts +19 -4
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +247 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/blocked-draft-policy.ts +101 -0
- package/src/self-implement/execution-clarification.ts +11 -0
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-env-retry.ts +83 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/orchestrator.ts +220 -6
- package/src/self-implement/run-stop.ts +123 -0
- package/src/self-implement/run-ttl.ts +258 -0
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/self-implement/salvage-retention.ts +123 -0
- package/src/self-implement/seams.ts +4 -0
- package/src/self-implement/self-implement-cli.ts +2 -0
- package/src/self-implement/sibling-resync.ts +223 -0
- package/src/self-implement/stop-autoheal.ts +204 -0
- package/src/task-agent/actions.ts +63 -2
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +292 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +25 -0
- package/src/task-agent/live-moves.ts +276 -0
- package/src/task-agent/mission.ts +33 -7
- package/src/task-agent/shadow.ts +192 -15
- package/src/task-agent/task-hand.ts +124 -17
- package/src/task-orchestrator/surfaces/pod-command-job.ts +82 -8
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +233 -21
- package/src/task-orchestrator/surfaces/pod-pool.ts +155 -34
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +21 -5
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +52 -0
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/testing/real-cli-spawn.ts +106 -0
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +190 -5
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c39554c321576982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a3c58f5c72a5f3f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-44d5f767491692bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +0 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +0 -1
- /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
package/src/esc-abort-gate.ts
CHANGED
|
@@ -34,6 +34,9 @@ import type { KeyEvent } from './display/types.js';
|
|
|
34
34
|
import type { ThemeTokens } from './theme/tokens.js';
|
|
35
35
|
import { debug } from './debug/log.js';
|
|
36
36
|
import { abortTurnOnly } from './turn-abort-scope.js';
|
|
37
|
+
import { SELF_IMPLEMENT_TOOL_NAMES } from './boot/daemon-tools/self-implement-names.js';
|
|
38
|
+
|
|
39
|
+
export const DASHBOARD_CLARIFICATION_REQUEST = Symbol.for('elanous.dashboard.clarification-request');
|
|
37
40
|
|
|
38
41
|
export interface EscAbortGateDeps {
|
|
39
42
|
abortCtrl: AbortController;
|
|
@@ -87,8 +90,14 @@ export async function routeStreamingEscapeKey(
|
|
|
87
90
|
gate: Pick<EscAbortGate, 'handleEscape' | 'handleKey' | 'isGateOpen'>,
|
|
88
91
|
gateEvent: KeyEvent,
|
|
89
92
|
consumeDragEscape: () => Promise<boolean>,
|
|
93
|
+
consumeTopModalEscape: () => boolean | 'passthrough' = () => false,
|
|
90
94
|
): Promise<boolean> {
|
|
91
95
|
if (key.kind && key.kind !== 'press') return false;
|
|
96
|
+
if (key.name === 'escape') {
|
|
97
|
+
const modalResult = consumeTopModalEscape();
|
|
98
|
+
if (modalResult === true) return true;
|
|
99
|
+
if (modalResult === 'passthrough') return false;
|
|
100
|
+
}
|
|
92
101
|
if (gate.isGateOpen()) {
|
|
93
102
|
gate.handleKey(gateEvent);
|
|
94
103
|
return true;
|
|
@@ -99,6 +108,41 @@ export async function routeStreamingEscapeKey(
|
|
|
99
108
|
return true;
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
export function consumeStreamingTopModalEscape(input: {
|
|
112
|
+
modal: Pick<ModalSurface, 'id' | 'onKey'> | null;
|
|
113
|
+
abortGateModalId: string | null;
|
|
114
|
+
event: KeyEvent;
|
|
115
|
+
question?: { id: string; dispose(cancelled: boolean): void; isClarification: boolean };
|
|
116
|
+
onClarificationClosed(): void;
|
|
117
|
+
topModalId(): string | null;
|
|
118
|
+
redraw(): void;
|
|
119
|
+
}): boolean | 'passthrough' {
|
|
120
|
+
const { modal, question } = input;
|
|
121
|
+
if (!modal || modal.id === input.abortGateModalId) return false;
|
|
122
|
+
if (modal.id === question?.id) {
|
|
123
|
+
question.dispose(true);
|
|
124
|
+
if (question.isClarification) input.onClarificationClosed();
|
|
125
|
+
input.redraw();
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
if (!modal.onKey) return 'passthrough';
|
|
129
|
+
modal.onKey(input.event);
|
|
130
|
+
input.redraw();
|
|
131
|
+
return input.topModalId() === modal.id ? 'passthrough' : true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createHarnessEscHint(pushLine: (line: string) => void): (targets: readonly string[]) => boolean {
|
|
135
|
+
let shown = false;
|
|
136
|
+
return (targets) => {
|
|
137
|
+
if (!targets.some(name => (SELF_IMPLEMENT_TOOL_NAMES as readonly string[]).includes(name))) return false;
|
|
138
|
+
if (!shown) {
|
|
139
|
+
pushLine('하니스 런은 따로 계속 돈다 — 멈추려면 /harness stop <space-id> · 목록은 /harness runs');
|
|
140
|
+
shown = true;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
102
146
|
export function createEscAbortGate(deps: EscAbortGateDeps): EscAbortGate {
|
|
103
147
|
let current: ApprovalModalHandle | null = null;
|
|
104
148
|
let currentDispose: (() => void) | null = null;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// CGE-RUN — 편집기에서 «내 그래프»를 실행하고 노드별 상태를 읽는다.
|
|
2
|
+
// 실행은 `elanous graph run` 과 같은 범용 실행기(runGraph)다. 하니스(LLM) 런은 골 종류로만 그래프를 고르므로 여기서 다루지 않는다.
|
|
3
|
+
// 그래프와 레시피는 런마다 작업 폴더로 «복사»해서 돌린다 — 실행 중에 편집기가 파일을 바꿔도 이 런은 시작한 판으로 끝난다.
|
|
4
|
+
// ⛔ 레시피(실제로 도는 명령)는 «저장소»의 편집기 레시피(`graphs/demo/editor-recipes.yaml`)만 쓴다 — «mine» 폴더는 HTTP 로 쓸 수 있으므로
|
|
5
|
+
// 거기 레시피를 읽으면 «편집한 그래프 실행»이 «편집한 명령 실행»이 된다(TC 10-07 19:31). 그래프가 부르는 레시피가 그 목록에 없으면 시작 전에 거부한다.
|
|
6
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { randomBytes } from 'node:crypto';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { parse as parseYaml } from 'yaml';
|
|
10
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
11
|
+
import { debug } from '../debug/log.js';
|
|
12
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
13
|
+
import { jsonResponse } from '../nexus/api/json-response.js';
|
|
14
|
+
import { defaultGraphsDir } from '../self-implement/graph-templates.js';
|
|
15
|
+
import { runGraph, type GraphRunState } from './runner.js';
|
|
16
|
+
|
|
17
|
+
const GRAPH_ID = /^[a-z0-9-]+$/;
|
|
18
|
+
const RUN_ID = /^ed-[a-z0-9]+-[a-f0-9]{6}$/;
|
|
19
|
+
/** 실행기가 받는 끝 노드 이름(runner.ts — `unsupported terminal node`). 미리 말해 준다. */
|
|
20
|
+
const RUNNABLE_TERMINALS = new Set(['done', 'failed']);
|
|
21
|
+
|
|
22
|
+
export interface GraphRunApiDeps {
|
|
23
|
+
mineDir?: string;
|
|
24
|
+
/** 편집기 실행이 쓰는 레시피 파일(저장소 것만) — 기본 `graphs/demo/editor-recipes.yaml`. */
|
|
25
|
+
recipesFile?: string;
|
|
26
|
+
root?: string;
|
|
27
|
+
run?: typeof runGraph;
|
|
28
|
+
now?: () => number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 시작 전에 죽은 런(레시피 없음 등)은 원장 파일이 안 생긴다 — 그 이유를 여기 잠깐 들고 있다가 상태 조회에 돌려준다. */
|
|
32
|
+
const startFailures = new Map<string, { error: string; at: string }>();
|
|
33
|
+
|
|
34
|
+
const mineDirOf = (deps: GraphRunApiDeps) => deps.mineDir ?? join(elanousStateRoot(), 'graphs');
|
|
35
|
+
const rootOf = (deps: GraphRunApiDeps) => deps.root ?? effectiveInstanceRoot();
|
|
36
|
+
|
|
37
|
+
function decodeId(raw: string, pattern: RegExp): string | null {
|
|
38
|
+
try {
|
|
39
|
+
const id = decodeURIComponent(raw);
|
|
40
|
+
return pattern.test(id) ? id : null;
|
|
41
|
+
} catch { return null; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 파일 이름이 아니라 선언된 graph_id 로 찾는다(graphs-api 와 같은 규칙). */
|
|
45
|
+
function findMine(dir: string, id: string): { file: string; doc: Record<string, unknown> } | null {
|
|
46
|
+
let names: string[];
|
|
47
|
+
try { names = readdirSync(dir).filter((name) => name.endsWith('.yaml') || name.endsWith('.yml')).sort(); }
|
|
48
|
+
catch { return null; }
|
|
49
|
+
for (const name of names) {
|
|
50
|
+
if (name === 'recipes.yaml') continue;
|
|
51
|
+
try {
|
|
52
|
+
const doc: unknown = parseYaml(readFileSync(join(dir, name), 'utf8'));
|
|
53
|
+
if (doc && typeof doc === 'object' && !Array.isArray(doc) && (doc as Record<string, unknown>).graph_id === id) {
|
|
54
|
+
return { file: join(dir, name), doc: doc as Record<string, unknown> };
|
|
55
|
+
}
|
|
56
|
+
} catch { continue; }
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 되돌이(순환) 위의 노드가 max_visits 1 이면 두 번째 방문에서 예산 초과로 멈춘다 — 시작 전에 말한다. */
|
|
62
|
+
function loopVisitIssues(doc: Record<string, unknown>): string[] {
|
|
63
|
+
const next = new Map<string, Set<string>>();
|
|
64
|
+
for (const edge of Array.isArray(doc.edges) ? doc.edges : []) {
|
|
65
|
+
if (!edge || typeof edge !== 'object') continue;
|
|
66
|
+
const { from, to, map } = edge as Record<string, unknown>;
|
|
67
|
+
if (typeof from !== 'string') continue;
|
|
68
|
+
const targets = [...(typeof to === 'string' ? [to] : []),
|
|
69
|
+
...(map && typeof map === 'object' ? Object.values(map as Record<string, unknown>).filter((v): v is string => typeof v === 'string') : [])];
|
|
70
|
+
for (const target of targets) (next.get(from) ?? next.set(from, new Set()).get(from)!).add(target);
|
|
71
|
+
}
|
|
72
|
+
const reaches = (start: string, goal: string): boolean => {
|
|
73
|
+
const seen = new Set<string>();
|
|
74
|
+
const stack = [...(next.get(start) ?? [])];
|
|
75
|
+
while (stack.length) {
|
|
76
|
+
const id = stack.pop()!;
|
|
77
|
+
if (id === goal) return true;
|
|
78
|
+
if (seen.has(id)) continue;
|
|
79
|
+
seen.add(id);
|
|
80
|
+
stack.push(...(next.get(id) ?? []));
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
};
|
|
84
|
+
const issues: string[] = [];
|
|
85
|
+
for (const node of Array.isArray(doc.nodes) ? doc.nodes : []) {
|
|
86
|
+
if (!node || typeof node !== 'object') continue;
|
|
87
|
+
const { node_id: id, max_visits: visits } = node as Record<string, unknown>;
|
|
88
|
+
if (typeof id === 'string' && (typeof visits !== 'number' || visits < 2) && reaches(id, id)) {
|
|
89
|
+
issues.push(`노드 '${id}' 는 되돌이 위에 있는데 max_visits 가 ${typeof visits === 'number' ? visits : '없음'} 이다 — 두 번째 방문에서 멈춘다 · 2 이상으로`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return issues;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** 실행기가 받지 않을 모양을 시작 «전»에 사람 말로. 빈 배열이면 돌릴 수 있다. */
|
|
96
|
+
export function runnableIssues(doc: Record<string, unknown>, allowedRecipes: ReadonlySet<string>): string[] {
|
|
97
|
+
const issues: string[] = [];
|
|
98
|
+
for (const node of Array.isArray(doc.nodes) ? doc.nodes : []) {
|
|
99
|
+
if (!node || typeof node !== 'object') continue;
|
|
100
|
+
const { node_id: nodeId, recipe } = node as Record<string, unknown>;
|
|
101
|
+
if (typeof recipe !== 'string' || recipe === 'none') continue;
|
|
102
|
+
if (recipe.startsWith('approval:')) { issues.push(`노드 '${String(nodeId)}' 의 승인 레시피 '${recipe}' 는 편집기 데모 실행에서 쓸 수 없다`); continue; }
|
|
103
|
+
const prefixed = /^cmd:(.*)$/.exec(recipe);
|
|
104
|
+
if (prefixed && !allowedRecipes.has(prefixed[1]!)) {
|
|
105
|
+
issues.push(`노드 '${String(nodeId)}' 의 레시피 '${recipe}' 는 편집기 실행 목록에 없다 — 쓸 수 있는 것: ${[...allowedRecipes].map((r) => `cmd:${r}`).join(' · ')}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// grow 는 매핑 안 된 결과에서 LLM 제안기를 부른다 — 데모 실행 범위 밖(하니스 LLM 런은 0.2.21).
|
|
109
|
+
if (doc.grow !== undefined) issues.push('grow(그래프 자라기)는 편집기 데모 실행에서 쓸 수 없다 — grow 줄을 지우고 저장하라');
|
|
110
|
+
issues.push(...loopVisitIssues(doc));
|
|
111
|
+
const terminals = Array.isArray(doc.terminal_nodes) ? doc.terminal_nodes.filter((t): t is string => typeof t === 'string') : [];
|
|
112
|
+
if (!terminals.length) issues.push('끝 노드가 없다 — done 노드를 잇고 끝으로 지정하라');
|
|
113
|
+
for (const t of terminals) if (!RUNNABLE_TERMINALS.has(t)) issues.push(`끝 노드 '${t}' 는 실행할 수 없다 — 끝 노드 이름은 done 또는 failed 여야 한다`);
|
|
114
|
+
return issues;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface GraphRunNodeView { nodeId: string; ok: boolean; executed: boolean; startedAt?: string; endedAt?: string; error?: string }
|
|
118
|
+
export interface GraphRunView {
|
|
119
|
+
graphId: string; runId: string; status: GraphRunState['status'] | 'starting';
|
|
120
|
+
startedAt?: string; finishedAt?: string; path: string[];
|
|
121
|
+
nodes: GraphRunNodeView[]; currentNode?: { nodeId: string; startedAt: string }; error?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function graphRunView(state: GraphRunState): GraphRunView {
|
|
125
|
+
return {
|
|
126
|
+
graphId: state.graphId, runId: state.runId, status: state.status,
|
|
127
|
+
...(state.startedAt ? { startedAt: state.startedAt } : {}), ...(state.finishedAt ? { finishedAt: state.finishedAt } : {}),
|
|
128
|
+
path: state.path,
|
|
129
|
+
nodes: state.nodes.map((node) => ({
|
|
130
|
+
nodeId: node.nodeId, ok: node.ok, executed: node.executed,
|
|
131
|
+
...(node.startedAt ? { startedAt: node.startedAt } : {}), ...(node.endedAt ? { endedAt: node.endedAt } : {}),
|
|
132
|
+
...(node.error ? { error: node.error.slice(0, 300) } : {}),
|
|
133
|
+
})),
|
|
134
|
+
...(state.currentNode ? { currentNode: state.currentNode } : {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** POST /v1/graphs/<id>/run — 내 그래프만 실행한다(핵심 그래프는 복제해서 돌린다). 응답은 기다리지 않는다(202 ⊕ runId). */
|
|
139
|
+
export function handleGraphRunStart(rawId: string, deps: GraphRunApiDeps = {}): Response {
|
|
140
|
+
const id = decodeId(rawId, GRAPH_ID);
|
|
141
|
+
if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
|
|
142
|
+
const mine = findMine(mineDirOf(deps), id);
|
|
143
|
+
if (!mine) return jsonResponse({ error: 'not-found', id, reason: '내 그래프에만 실행이 있다 — 핵심 그래프는 복제해서 돌린다' }, 404);
|
|
144
|
+
const recipesFile = deps.recipesFile ?? join(defaultGraphsDir(), 'demo', 'editor-recipes.yaml');
|
|
145
|
+
let recipeText: string;
|
|
146
|
+
let allowed: Set<string>;
|
|
147
|
+
try {
|
|
148
|
+
recipeText = readFileSync(recipesFile, 'utf8');
|
|
149
|
+
const parsed: unknown = parseYaml(recipeText);
|
|
150
|
+
allowed = new Set(parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? Object.keys(parsed) : []);
|
|
151
|
+
} catch {
|
|
152
|
+
return jsonResponse({ error: 'recipes-unavailable' }, 503);
|
|
153
|
+
}
|
|
154
|
+
const issues = runnableIssues(mine.doc, allowed);
|
|
155
|
+
if (issues.length) return jsonResponse({ error: 'not-runnable', id, issues }, 422);
|
|
156
|
+
|
|
157
|
+
const now = deps.now?.() ?? Date.now();
|
|
158
|
+
const runId = `ed-${now.toString(36)}-${randomBytes(3).toString('hex')}`;
|
|
159
|
+
const root = rootOf(deps);
|
|
160
|
+
const work = join(root, 'graph-run-work', id, runId);
|
|
161
|
+
try {
|
|
162
|
+
mkdirSync(work, { recursive: true });
|
|
163
|
+
copyFileSync(mine.file, join(work, 'graph.yaml'));
|
|
164
|
+
writeFileSync(join(work, 'recipes.yaml'), recipeText);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return jsonResponse({ error: 'run-workspace-failed', reason: error instanceof Error ? error.message.slice(0, 200) : 'unknown' }, 500);
|
|
167
|
+
}
|
|
168
|
+
debug.log('graphs.run', 'started', { id, runId });
|
|
169
|
+
void (deps.run ?? runGraph)(join(work, 'graph.yaml'), { runId, input: { source: 'editor' }, deps: { root } })
|
|
170
|
+
.then((state) => debug.log('graphs.run', 'finished', { id, runId, status: state.status, executed: state.executed }))
|
|
171
|
+
.catch((error: unknown) => {
|
|
172
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
173
|
+
startFailures.set(runId, { error: message.slice(0, 300), at: new Date().toISOString() });
|
|
174
|
+
if (startFailures.size > 50) startFailures.delete(startFailures.keys().next().value!);
|
|
175
|
+
debug.log('graphs.run', 'failed', { id, runId, error: message.slice(0, 200) }, { level: 'warn' });
|
|
176
|
+
});
|
|
177
|
+
return jsonResponse({ id, runId, demo: true }, 202);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** GET /v1/graphs/<id>/runs/<runId> — 원장 그대로(노드별 ok·시각 · 지금 노드). 원장이 아직 없으면 starting, 시작 전에 죽었으면 failed ⊕ 이유. */
|
|
181
|
+
export function handleGraphRunGet(rawId: string, rawRunId: string, deps: GraphRunApiDeps = {}): Response {
|
|
182
|
+
const id = decodeId(rawId, GRAPH_ID);
|
|
183
|
+
const runId = decodeId(rawRunId, RUN_ID);
|
|
184
|
+
if (!id || !runId) return jsonResponse({ error: 'bad_request', reason: 'invalid id' }, 400);
|
|
185
|
+
const file = join(rootOf(deps), 'graph-runs', id, `${runId}.json`);
|
|
186
|
+
let state: GraphRunState | null = null;
|
|
187
|
+
try { state = JSON.parse(readFileSync(file, 'utf8')) as GraphRunState; } catch { state = null; }
|
|
188
|
+
if (state && state.graphId === id && state.runId === runId) return jsonResponse(graphRunView(state));
|
|
189
|
+
const failed = startFailures.get(runId);
|
|
190
|
+
if (failed) return jsonResponse({ graphId: id, runId, status: 'failed', path: [], nodes: [], error: failed.error } satisfies GraphRunView);
|
|
191
|
+
if (!existsSync(join(rootOf(deps), 'graph-run-work', id, runId))) return jsonResponse({ error: 'not-found', id, runId }, 404);
|
|
192
|
+
return jsonResponse({ graphId: id, runId, status: 'starting', path: [], nodes: [] } satisfies GraphRunView);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const RUN_START = /^\/v1\/graphs\/([^/]+)\/run$/;
|
|
196
|
+
const RUN_GET = /^\/v1\/graphs\/([^/]+)\/runs\/([^/]+)$/;
|
|
197
|
+
|
|
198
|
+
/** http-server 가 인증 «뒤»에 부른다. 이 두 길이 아니면 null. */
|
|
199
|
+
export function handleGraphRunRoute(method: string, pathname: string, deps: GraphRunApiDeps = {}): Response | null {
|
|
200
|
+
const start = RUN_START.exec(pathname);
|
|
201
|
+
if (start && method === 'POST') return handleGraphRunStart(start[1]!, deps);
|
|
202
|
+
const get = RUN_GET.exec(pathname);
|
|
203
|
+
if (get && method === 'GET') return handleGraphRunGet(get[1]!, get[2]!, deps);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
@@ -35,7 +35,11 @@ export interface GraphRunState {
|
|
|
35
35
|
stoppedAt?: string;
|
|
36
36
|
status: 'running' | 'done' | 'failed' | 'budget-exceeded' | 'awaiting-approval';
|
|
37
37
|
path: string[];
|
|
38
|
-
|
|
38
|
+
/** startedAt/endedAt (ISO) and seconds are written when the node runs; ledgers before GRAPH-NODE-TIMES (0.2.19) have none. */
|
|
39
|
+
nodes: Array<{ nodeId: string; ok: boolean; exit: number | null; executed: boolean; output?: unknown; error?: string; decidedBy?: string; decidedAt?: string;
|
|
40
|
+
startedAt?: string; endedAt?: string; seconds?: number }>;
|
|
41
|
+
/** The node now running and when it started; cleared when its record is written. */
|
|
42
|
+
currentNode?: { nodeId: string; startedAt: string };
|
|
39
43
|
input?: unknown;
|
|
40
44
|
pending?: { nodeId: string; message: string; since: string; notifiedAt?: string; decision?: 'approved' | 'rejected'; decidedBy?: string; decidedAt?: string };
|
|
41
45
|
approvalSourceHash?: string;
|
|
@@ -62,6 +66,12 @@ export interface GraphRunOptions {
|
|
|
62
66
|
resumeGraphId?: string;
|
|
63
67
|
fromNodeId?: string;
|
|
64
68
|
useCurrentGraph?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* RELEASE-LEDGER-UNIVERSE — stamp every cmd: child with this run's ledger root (`ELANOUS_STATE_DIR`).
|
|
71
|
+
* Without it a child launched from a non-leader tree (`bun scripts/...` with cwd = that tree) re-resolves
|
|
72
|
+
* its own universe and lands in the tree-derived test universe while the run ledger is prod.
|
|
73
|
+
*/
|
|
74
|
+
pinChildUniverse?: boolean;
|
|
65
75
|
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[] } };
|
|
66
76
|
}
|
|
67
77
|
|
|
@@ -658,6 +668,8 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
658
668
|
}
|
|
659
669
|
state.status = 'running';
|
|
660
670
|
const startedAt = performance.now();
|
|
671
|
+
const nodeStartedAt = new Date().toISOString();
|
|
672
|
+
if (!resumingCompleted) state.currentNode = { nodeId: current, startedAt: nodeStartedAt };
|
|
661
673
|
console.error(`[graph] ${current} start (0.00s)`);
|
|
662
674
|
debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: 'start', dryRun: state.dryRun });
|
|
663
675
|
publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: 'start' });
|
|
@@ -708,7 +720,11 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
708
720
|
// state stamp so a cmd: child resolves its default config in the same universe.
|
|
709
721
|
// The ignored config-dir environment variable must not appear to propagate the flag.
|
|
710
722
|
delete env.ELANOUS_CONFIG_DIR;
|
|
711
|
-
if (
|
|
723
|
+
if (options.pinChildUniverse) {
|
|
724
|
+
// The run's own ledger root is the parent universe — the child writes where the run is recorded.
|
|
725
|
+
env.ELANOUS_STATE_DIR = root;
|
|
726
|
+
debug.log('graph-runner', 'child-universe-pinned', { graphId, runId, node: current, configDir: getElanousConfigDirOverride() ?? null, stateDir: env.ELANOUS_STATE_DIR, why: 'pinChildUniverse' });
|
|
727
|
+
} else if (getElanousConfigDirOverride()) {
|
|
712
728
|
env.ELANOUS_STATE_DIR = effectiveInstanceRoot();
|
|
713
729
|
debug.log('graph-runner', 'child-universe-pinned', { graphId, runId, node: current, configDir: getElanousConfigDirOverride(), stateDir: env.ELANOUS_STATE_DIR });
|
|
714
730
|
}
|
|
@@ -723,6 +739,10 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
723
739
|
error = result.error ? redactCredentials(result.error) : undefined;
|
|
724
740
|
state.executed++;
|
|
725
741
|
}
|
|
742
|
+
const seconds = Number(((performance.now() - startedAt) / 1000).toFixed(2));
|
|
743
|
+
// A wall clock stepped back mid-node must not record an end before the start.
|
|
744
|
+
const nodeEndedAt = new Date(Math.max(Date.now(), Date.parse(nodeStartedAt))).toISOString();
|
|
745
|
+
delete state.currentNode;
|
|
726
746
|
if (resumingCompleted && completed) {
|
|
727
747
|
ok = completed.ok;
|
|
728
748
|
exit = completed.exit;
|
|
@@ -734,7 +754,8 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
734
754
|
output = JSON.stringify({ outcome: ok ? 'approved' : 'rejected', ...(state.pending?.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending?.decidedAt ?? null });
|
|
735
755
|
}
|
|
736
756
|
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 } : {}),
|
|
737
|
-
...(approval && state.pending ? { ...(state.pending.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending.decidedAt } : {})
|
|
757
|
+
...(approval && state.pending ? { ...(state.pending.decidedBy === undefined ? {} : { decidedBy: state.pending.decidedBy }), decidedAt: state.pending.decidedAt } : {}),
|
|
758
|
+
startedAt: nodeStartedAt, endedAt: nodeEndedAt, seconds });
|
|
738
759
|
}
|
|
739
760
|
if (approval) {
|
|
740
761
|
delete state.pending;
|
|
@@ -742,7 +763,6 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
742
763
|
}
|
|
743
764
|
const reported = command && (!state.dryRun || command.dry_run_command) ? lastJsonObject(resumingCompleted ? completed?.output : output)?.outcome : undefined;
|
|
744
765
|
const namedOutcome = typeof reported === 'string' ? reported : undefined;
|
|
745
|
-
const seconds = Number(((performance.now() - startedAt) / 1000).toFixed(2));
|
|
746
766
|
console.error(`[graph] ${current} ${ok ? 'ok' : 'fail'} (${seconds.toFixed(2)}s)`);
|
|
747
767
|
debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds, exit });
|
|
748
768
|
publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds });
|
|
@@ -892,11 +912,21 @@ export function manageGraphRun(graphId: string, runId: string, action: 'stop' |
|
|
|
892
912
|
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error; }
|
|
893
913
|
}
|
|
894
914
|
}
|
|
915
|
+
const current = state.currentNode;
|
|
916
|
+
const running = current && state.path.length === state.nodes.length + 1 && current.nodeId === state.path.at(-1) &&
|
|
917
|
+
Number.isFinite(Date.parse(current.startedAt)) ? current.startedAt : undefined;
|
|
918
|
+
// One stop instant for the node end, finishedAt and stoppedAt — never before the running node's start.
|
|
919
|
+
const stopMs = running === undefined ? Date.now() : Math.max(Date.now(), Date.parse(running));
|
|
895
920
|
if (state.path.length === state.nodes.length + 1) {
|
|
896
|
-
state.nodes.push({ nodeId: state.path.at(-1)!, ok: false, exit: null, executed: false, error: 'stopped before node completed'
|
|
921
|
+
state.nodes.push({ nodeId: state.path.at(-1)!, ok: false, exit: null, executed: false, error: 'stopped before node completed',
|
|
922
|
+
// The stop instant is always the node end; a start (and so seconds) exists only when the ledger named the running
|
|
923
|
+
// node — a run begun by a runner before GRAPH-NODE-TIMES did not, and a guessed start would be a false time.
|
|
924
|
+
endedAt: new Date(stopMs).toISOString(),
|
|
925
|
+
...(running === undefined ? {} : { startedAt: running, seconds: Number(((stopMs - Date.parse(running)) / 1000).toFixed(2)) }) });
|
|
897
926
|
}
|
|
927
|
+
delete state.currentNode;
|
|
898
928
|
state.status = 'failed';
|
|
899
|
-
state.finishedAt = new Date().toISOString();
|
|
929
|
+
state.finishedAt = new Date(stopMs).toISOString();
|
|
900
930
|
state.stoppedAt = state.finishedAt;
|
|
901
931
|
delete state.activeNode;
|
|
902
932
|
persistGraphRun(state);
|