elanous 0.2.25 → 0.2.28
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/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
|
@@ -628,17 +628,74 @@ function sameProviderStep(child: ExplicitChildForEscalation): EscalateTarget | n
|
|
|
628
628
|
return null;
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
+
/** escalateChild 매핑 해석 근원 — `env` 가 `config` 를 이기고, 둘 다 없으면 같은 provider 사다리(`ladder`). */
|
|
632
|
+
export type EscalateTargetSource = 'config' | 'env' | 'ladder';
|
|
633
|
+
|
|
634
|
+
/** `self-dev.rework`/`escalate-target` 관측 한 건. 관측 전용 — 판정·분기는 이 값을 읽지 않는다. */
|
|
635
|
+
export interface EscalateTargetObservation {
|
|
636
|
+
readonly category: 'self-dev.rework';
|
|
637
|
+
readonly event: 'escalate-target';
|
|
638
|
+
readonly data: {
|
|
639
|
+
readonly source: EscalateTargetSource;
|
|
640
|
+
readonly from: string;
|
|
641
|
+
readonly to: string;
|
|
642
|
+
readonly applied: boolean;
|
|
643
|
+
/** `applied=false` 의 이유 — provider 경계 또는 불완전한 모델 ID. */
|
|
644
|
+
readonly reason?: 'cross-provider' | 'invalid-target';
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/** 명시 자식 승급 해석 옵션. `escalateChild`(`tools.selfImplement.escalateChild` from→to 맵)는 호출부가
|
|
649
|
+
* config 에서 읽어 전달한다 — ⚠️ Pod 안엔 운영 config 가 없다(POD-CONFIG-SNAPSHOT 전) → Pod 자식은
|
|
650
|
+
* `ELANOUS_SELFDEV_ESCALATE_CHILD` env 로만 받는다. 관측 싱크도 호출부가 주입한다(정책은 순수 유지). */
|
|
651
|
+
export interface ExplicitChildEscalateOptions {
|
|
652
|
+
readonly escalateChild?: Readonly<Record<string, string>>;
|
|
653
|
+
readonly observe?: (observation: EscalateTargetObservation) => void;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** 모델 id(`openrouter/z-ai/glm-5.3` 등)의 provider 세그먼트 — 첫 `/` 앞. */
|
|
657
|
+
function escalateModelProvider(model: string): string {
|
|
658
|
+
return (model.split('/')[0] ?? '').trim();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/** 명시 매핑은 provider/model, OpenRouter 는 openrouter/vendor/model 형태의 완전한 ID 여야 한다. */
|
|
662
|
+
function validEscalateModelId(model: string): boolean {
|
|
663
|
+
const segments = model.split('/');
|
|
664
|
+
const required = segments[0] === 'openrouter' ? 3 : 2;
|
|
665
|
+
return segments.length === required && segments.every((segment) => segment.length > 0 && !/\s/.test(segment));
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/** `ELANOUS_SELFDEV_ESCALATE_CHILD=<from>=<to>` 해석 — 첫 `=` 기준. 빈 조각·구문 파손은 매핑 없음. */
|
|
669
|
+
function parseEscalateChildEnv(raw: string | undefined): { from: string; to: string } | undefined {
|
|
670
|
+
const value = raw?.trim();
|
|
671
|
+
if (!value) return undefined;
|
|
672
|
+
const separator = value.indexOf('=');
|
|
673
|
+
if (separator <= 0) return undefined;
|
|
674
|
+
const from = value.slice(0, separator).trim();
|
|
675
|
+
const to = value.slice(separator + 1).trim();
|
|
676
|
+
return from && to ? { from, to } : undefined;
|
|
677
|
+
}
|
|
678
|
+
|
|
631
679
|
/** 명시 자식의 승급 타깃 — 같은 provider 안에서만(위 규칙). `ELANOUS_SELFDEV_SOL_*` 는 운영자의 명시
|
|
632
680
|
* 덮어쓰기라 그대로 우선한다. ⛔ `effort` 가 빈 문자열이면 «안 보낸다»는 뜻이다(OpenRouter 는 노력을
|
|
633
|
-
* 보내면 덜 생각했다 — 2026-09-23 실측).
|
|
681
|
+
* 보내면 덜 생각했다 — 2026-09-23 실측).
|
|
682
|
+
* escalateChild 짝지기(env 가 config 를 이긴다)는 사다리(`sameProviderStep`)보다 «먼저» —
|
|
683
|
+
* 같은 provider 타깃이면 그 타깃으로, provider 가 다르면 무시 ⊕ 관측하고 사다리로 돌아간다.
|
|
684
|
+
* 둘 다 없으면 종전 그대로(바이트 불변). */
|
|
634
685
|
export function resolveExplicitChildEscalateTarget(
|
|
635
686
|
tier: EscalateTier,
|
|
636
687
|
env: Record<string, string | undefined> = process.env,
|
|
637
688
|
child?: ExplicitChildForEscalation,
|
|
689
|
+
options?: ExplicitChildEscalateOptions,
|
|
638
690
|
): EscalateTarget | null {
|
|
639
691
|
if (tier === 'none') return null;
|
|
692
|
+
const sink = options?.observe;
|
|
693
|
+
const emit = sink
|
|
694
|
+
? (data: EscalateTargetObservation['data']): void => sink({ category: 'self-dev.rework', event: 'escalate-target', data })
|
|
695
|
+
: undefined;
|
|
640
696
|
const overrideModel = env.ELANOUS_SELFDEV_SOL_MODEL?.trim();
|
|
641
697
|
if (overrideModel) {
|
|
698
|
+
if (child) emit?.({ source: 'env', from: child.model, to: overrideModel, applied: true });
|
|
642
699
|
return {
|
|
643
700
|
model: overrideModel,
|
|
644
701
|
provider: env.ELANOUS_SELFDEV_SOL_PROVIDER?.trim() || child?.provider || 'openai-codex',
|
|
@@ -646,9 +703,30 @@ export function resolveExplicitChildEscalateTarget(
|
|
|
646
703
|
};
|
|
647
704
|
}
|
|
648
705
|
if (child === undefined) return null;
|
|
706
|
+
const effortOverride = env.ELANOUS_SELFDEV_SOL_EFFORT?.trim();
|
|
707
|
+
// env → config 순서로 이 자식 모델에 대한 매핑을 모은다(없는 층은 건너뛴다).
|
|
708
|
+
const pairings: Array<{ source: 'env' | 'config'; to: string }> = [];
|
|
709
|
+
const envPairing = parseEscalateChildEnv(env.ELANOUS_SELFDEV_ESCALATE_CHILD);
|
|
710
|
+
if (envPairing && envPairing.from === child.model) pairings.push({ source: 'env', to: envPairing.to });
|
|
711
|
+
const configMap = options?.escalateChild;
|
|
712
|
+
const configTo = configMap && Object.hasOwn(configMap, child.model) ? configMap[child.model]?.trim() : undefined;
|
|
713
|
+
if (configTo) pairings.push({ source: 'config', to: configTo });
|
|
714
|
+
for (const pairing of pairings) {
|
|
715
|
+
if (!validEscalateModelId(pairing.to)) {
|
|
716
|
+
emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: false, reason: 'invalid-target' });
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
if (escalateModelProvider(pairing.to) === child.provider) {
|
|
720
|
+
emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: true });
|
|
721
|
+
return effortOverride
|
|
722
|
+
? { model: pairing.to, provider: child.provider, effort: effortOverride }
|
|
723
|
+
: { model: pairing.to, provider: child.provider, effort: '' };
|
|
724
|
+
}
|
|
725
|
+
emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: false, reason: 'cross-provider' });
|
|
726
|
+
}
|
|
649
727
|
const step = sameProviderStep(child);
|
|
650
728
|
if (step === null) return null;
|
|
651
|
-
|
|
729
|
+
emit?.({ source: 'ladder', from: child.model, to: step.model, applied: true });
|
|
652
730
|
return effortOverride ? { ...step, effort: effortOverride } : step;
|
|
653
731
|
}
|
|
654
732
|
|
|
@@ -456,11 +456,36 @@ function parseGoals(req: SelfImplementRuntimeRequest): string[] | undefined {
|
|
|
456
456
|
return goals;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
|
|
459
|
+
interface SelfImplementGoalResult {
|
|
460
|
+
goal: string;
|
|
461
|
+
status: SelfDevJobResult['status'];
|
|
462
|
+
runId?: string;
|
|
463
|
+
stage?: string;
|
|
464
|
+
reason?: string;
|
|
465
|
+
parentPrNumber?: number;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function observedGoalResults(results: readonly SelfDevJobResult[]): SelfImplementGoalResult[] {
|
|
469
|
+
return results.map((result, index) => ({
|
|
470
|
+
goal: result.feature || `골 ${index + 1}`,
|
|
471
|
+
status: result.status,
|
|
472
|
+
...(result.runId ? { runId: result.runId } : {}),
|
|
473
|
+
...(result.stage ? { stage: result.stage } : {}),
|
|
474
|
+
...(result.error?.message || result.stopReason || result.blockReason
|
|
475
|
+
? { reason: result.error?.message || result.stopReason || (result.blockReason === 'parent-unlanded'
|
|
476
|
+
? result.parentPrNumber ? `선행 PR #${result.parentPrNumber} 미착지` : '선행 골 미착지'
|
|
477
|
+
: result.blockReason) }
|
|
478
|
+
: {}),
|
|
479
|
+
...(result.parentPrNumber ? { parentPrNumber: result.parentPrNumber } : {}),
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function formatOrchestrationResults(results: readonly SelfDevJobResult[], goals: readonly SelfImplementGoalResult[]): string {
|
|
460
484
|
const done = results.filter(result => result.status === 'done').length;
|
|
461
485
|
const merged = results.filter(result => result.merged).length;
|
|
462
486
|
const failed = results.filter(result => result.status === 'failed').length;
|
|
463
|
-
|
|
487
|
+
const summary = `[self-implement] parallel ${results.length} goal · done ${done}${merged ? ` (병합 ${merged})` : ''}${failed ? ` · 실패 ${failed}` : ''}`;
|
|
488
|
+
return [summary, ...goals.map(goal => `- ${goal.goal}: ${goal.status} · 런 ${goal.runId ?? '미관측'}${goal.stage ? ` · 단계 ${goal.stage}` : ''}${goal.reason ? ` · 이유 ${goal.reason}` : ''}`)].join('\n');
|
|
464
489
|
}
|
|
465
490
|
|
|
466
491
|
export function resolveDocumentReferences(paths: readonly string[], repositoryRoot = process.cwd()): DocumentReferenceStatus[] {
|
|
@@ -476,7 +501,15 @@ function documentReferenceStatusLines(references: readonly DocumentReferenceStat
|
|
|
476
501
|
|
|
477
502
|
function formatResult(feature: string, r: SelfImplementResult, references: readonly DocumentReferenceStatus[] = []): string {
|
|
478
503
|
const head = `[self-implement] ${r.stage}`;
|
|
479
|
-
const
|
|
504
|
+
const next = r.runId ? `eln self run ${r.runId}` : r.ok ? 'eln self running-runs' : '실패 이유를 확인한 뒤 다시 요청';
|
|
505
|
+
const lines: string[] = [
|
|
506
|
+
r.ok
|
|
507
|
+
? r.runId
|
|
508
|
+
? `하니스가 맡았습니다 · 런 ${r.runId} · 노드 ${r.node || '미관측'} · 다음 조회: ${next} · 이 턴에서 파일을 직접 편집하지 마세요`
|
|
509
|
+
: `하니스 응답을 받았지만 런 식별자는 아직 미관측입니다 · 진행 중 런 조회: ${next} · 일치하는 런이 없으면 요청을 다시 보내세요 · 이 턴에서 파일을 직접 편집하지 마세요`
|
|
510
|
+
: `하니스가 발사되지 않았거나 실패했습니다: ${r.detail ?? r.stage} · 다음: ${next} · 이 턴에서 파일을 직접 편집하지 마세요`,
|
|
511
|
+
head, ...documentReferenceStatusLines(references),
|
|
512
|
+
];
|
|
480
513
|
if (r.branch) lines.push(`branch: ${r.branch}`);
|
|
481
514
|
if (r.worktreePath) lines.push(`worktree: ${r.worktreePath}`);
|
|
482
515
|
if (r.stage === 'pr-opened' && r.prUrl) {
|
|
@@ -494,6 +527,12 @@ function formatResult(feature: string, r: SelfImplementResult, references: reado
|
|
|
494
527
|
export interface SelfImplementRunResult {
|
|
495
528
|
output: string;
|
|
496
529
|
ok: boolean;
|
|
530
|
+
kind: 'launched' | 'observed-only' | 'failed' | 'pending';
|
|
531
|
+
next?: string;
|
|
532
|
+
reason?: string;
|
|
533
|
+
missing?: string[];
|
|
534
|
+
/** 병렬 골마다 실제로 관측된 종료 상태·식별자·이유. */
|
|
535
|
+
goalResults?: SelfImplementGoalResult[];
|
|
497
536
|
// ⛔ 관측 전용 반환에는 **단계가 없다** — 아무 노드도 돌지 않았다. 없는 것을 있는 값으로 적으면
|
|
498
537
|
// «안 돌았다» 와 «어느 단계에서 끝났다» 가 같은 값이 된다(부재와 미지를 같은 값으로 적지 않는다).
|
|
499
538
|
stage?: SelfImplementResult['stage'];
|
|
@@ -506,8 +545,10 @@ export interface SelfImplementRunResult {
|
|
|
506
545
|
observed?: true;
|
|
507
546
|
/** 명시적 Fabric 분해의 상태·기존 3결말·미산출 사유. */
|
|
508
547
|
fabricDecomposition?: FabricDecompositionOutcome;
|
|
509
|
-
/**
|
|
548
|
+
/** 실제 런 결과에서 확인한 식별자. 선할당된 비동기 ID는 반환하지 않는다. */
|
|
510
549
|
runId?: string;
|
|
550
|
+
/** 비동기 요청 조회용 상관 ID. 발사된 런의 증거가 아니다. */
|
|
551
|
+
requestId?: string;
|
|
511
552
|
/** 호출이 블로킹인지 비블로킹인지. 기본 경로에는 없어 기존 반환 모양을 보존한다. */
|
|
512
553
|
invocationMode?: 'blocking' | 'non-blocking';
|
|
513
554
|
}
|
|
@@ -516,6 +557,7 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
516
557
|
id: 'self_implement',
|
|
517
558
|
spec: buildSelfImplementSpec(),
|
|
518
559
|
async run(req, ctx: ToolRuntimeContext): Promise<SelfImplementRunResult> {
|
|
560
|
+
try {
|
|
519
561
|
const hasFeature = req.feature !== undefined;
|
|
520
562
|
const hasGoals = req.goals !== undefined;
|
|
521
563
|
if (hasFeature === hasGoals) {
|
|
@@ -604,7 +646,7 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
604
646
|
if (observeOnly.enabled) {
|
|
605
647
|
const requestSummary = feature ?? goals!.join(' · ');
|
|
606
648
|
debug.log('self-implement', 'runtime.observed', { surface: ctx.surface, ...(feature ? { feature: feature.slice(0, 120) } : { goals: goals!.length }), observeOnly: observeOnly.enabled, observeOnlySource: observeOnly.source });
|
|
607
|
-
return { output:
|
|
649
|
+
return { output: `관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요. 실제 발사를 원하면 관측 전용 모드를 끈 뒤 다시 요청하세요: ${requestSummary.slice(0, 120)}${documentReferenceStatusLines(documentReferences).length ? `\n${documentReferenceStatusLines(documentReferences).join('\n')}` : ''}`, ok: true, kind: 'observed-only', observed: true, missing: ['runId', 'node'], next: '관측 전용 모드를 끈 뒤 다시 요청' };
|
|
608
650
|
}
|
|
609
651
|
|
|
610
652
|
const allocatedRunId = nonBlocking ? mintRunId() : undefined;
|
|
@@ -637,9 +679,14 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
637
679
|
fabricDecomposition = fabricDecompositionOutcome(fabricResult);
|
|
638
680
|
observeFabricDecomposition(fabricDecomposition, ctx.surface, fabricResult.status === 'decomposed' ? fabricResult.goals.length : undefined);
|
|
639
681
|
if (fabricResult.status !== 'decomposed') {
|
|
682
|
+
const observedOnly = fabricDecomposition.outcome !== 'llm-failed';
|
|
640
683
|
return {
|
|
641
|
-
output: formatFabricDecompositionOutcome(fabricDecomposition)
|
|
642
|
-
ok:
|
|
684
|
+
output: `${formatFabricDecompositionOutcome(fabricDecomposition)}${observedOnly ? '\n관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요.' : '\n이 턴에서 파일을 직접 편집하지 마세요.'}`,
|
|
685
|
+
ok: observedOnly,
|
|
686
|
+
kind: observedOnly ? 'observed-only' : 'failed',
|
|
687
|
+
next: observedOnly ? '분해 결과를 확인하고 구현 골을 다시 요청' : '분해 실패 이유를 확인한 뒤 다시 요청',
|
|
688
|
+
missing: ['runId', 'node'],
|
|
689
|
+
...(observedOnly ? { observed: true as const } : { reason: formatFabricDecompositionOutcome(fabricDecomposition) }),
|
|
643
690
|
fabricDecomposition,
|
|
644
691
|
};
|
|
645
692
|
}
|
|
@@ -674,10 +721,40 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
674
721
|
...(forwardedSupervise === undefined ? {} : { supervise: forwardedSupervise }),
|
|
675
722
|
...(deliverable === undefined ? {} : { deliverable }),
|
|
676
723
|
});
|
|
677
|
-
const
|
|
724
|
+
const goalResults = observedGoalResults(results);
|
|
725
|
+
const failedGoals = goalResults.filter(result => result.status === 'failed' || result.status === 'cancelled');
|
|
726
|
+
const blockedGoals = goalResults.filter(result => result.status === 'blocked');
|
|
727
|
+
const allRunsIdentified = goalResults.length > 0 && goalResults.every(result => result.runId);
|
|
728
|
+
const kind = goalResults.length === 0 || failedGoals.length > 0 || blockedGoals.length > 0 ? 'failed' : allRunsIdentified ? 'launched' : 'pending';
|
|
729
|
+
const parentBlockedPrNumbers = [...new Set(results.flatMap(result => result.status === 'blocked' && result.blockReason === 'parent-unlanded' && result.parentPrNumber ? [result.parentPrNumber] : []))];
|
|
730
|
+
const parentBlockedWithoutPr = results.some(result => result.status === 'blocked' && result.blockReason === 'parent-unlanded' && !result.parentPrNumber && !result.runId);
|
|
731
|
+
const otherFailureWithoutId = results.some(result => !result.runId && ['failed', 'cancelled', 'blocked'].includes(result.status) && result.blockReason !== 'parent-unlanded');
|
|
732
|
+
const runQueries = [...new Set(goalResults.flatMap(result => result.runId ? [`eln self run ${result.runId}`] : []))];
|
|
733
|
+
const availableQueries = [
|
|
734
|
+
...parentBlockedPrNumbers.map(pr => `eln gh pr view ${pr}`),
|
|
735
|
+
...runQueries,
|
|
736
|
+
];
|
|
737
|
+
const pendingWithoutId = goalResults.some(result => !result.runId && !['failed', 'cancelled', 'blocked'].includes(result.status));
|
|
738
|
+
const next = [
|
|
739
|
+
...availableQueries,
|
|
740
|
+
...(pendingWithoutId ? ['eln self running-runs', '일치하는 런이 없으면 ID 미확정 골을 다시 요청'] : []),
|
|
741
|
+
...(parentBlockedPrNumbers.length ? ['선행 PR 착지 뒤 차단된 골을 재실행하세요'] : []),
|
|
742
|
+
...(parentBlockedWithoutPr ? ['선행 골 상태를 확인한 뒤 해당 골을 재실행하세요'] : []),
|
|
743
|
+
...(otherFailureWithoutId ? ['실패·차단 이유를 확인한 뒤 해당 골을 다시 요청'] : []),
|
|
744
|
+
].join('\n') || '골 결과가 0건이라 상태를 확인한 뒤 다시 요청';
|
|
745
|
+
const reason = goalResults.length === 0
|
|
746
|
+
? '골 결과가 0건이라 상태 미관측'
|
|
747
|
+
: [...failedGoals, ...blockedGoals].map(result => `${result.goal}: ${result.reason ?? '이유 미관측'}`).join('; ');
|
|
678
748
|
return {
|
|
679
|
-
output: formatOrchestrationResults(results)
|
|
680
|
-
|
|
749
|
+
output: formatOrchestrationResults(results, goalResults)
|
|
750
|
+
+ (kind === 'failed' ? `\n실패/차단: ${reason} · 다음: ${next}` : `\n다음 조회: ${next}`)
|
|
751
|
+
+ '\n이 턴에서 파일을 직접 편집하지 마세요',
|
|
752
|
+
ok: kind !== 'failed',
|
|
753
|
+
kind,
|
|
754
|
+
next,
|
|
755
|
+
missing: [ ...(goalResults.length === 0 || goalResults.some(result => !result.runId) ? ['runId'] : []), 'node' ],
|
|
756
|
+
goalResults,
|
|
757
|
+
...(kind === 'failed' ? { reason } : {}),
|
|
681
758
|
...(fabricDecomposition ? { fabricDecomposition } : {}),
|
|
682
759
|
};
|
|
683
760
|
}
|
|
@@ -751,12 +828,12 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
751
828
|
...(runId ? { runId } : {}),
|
|
752
829
|
}, { pipelineDeps });
|
|
753
830
|
if (!outcome.ok) {
|
|
754
|
-
return { output: withGoalAuthorFailure(
|
|
831
|
+
return { output: withGoalAuthorFailure(`하니스가 발사되지 않았거나 실패했습니다: ${outcome.message} · 다음: 실패 이유를 확인한 뒤 다시 요청하세요 · 이 턴에서 파일을 직접 편집하지 마세요`), ok: false, kind: 'failed', reason: outcome.message, next: '실패 이유를 확인한 뒤 다시 요청', missing: ['runId', 'node'] };
|
|
755
832
|
}
|
|
756
833
|
if (outcome.kind !== 'self') {
|
|
757
834
|
return {
|
|
758
|
-
output: withGoalAuthorFailure('
|
|
759
|
-
ok: true,
|
|
835
|
+
output: withGoalAuthorFailure('관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요'),
|
|
836
|
+
ok: true, kind: 'observed-only', observed: true, missing: ['runId', 'node'], next: '관측 전용 모드를 끈 뒤 다시 요청',
|
|
760
837
|
};
|
|
761
838
|
}
|
|
762
839
|
|
|
@@ -764,6 +841,11 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
764
841
|
return {
|
|
765
842
|
output: withGoalAuthorFailure(formatResult(feature, result, documentReferences)),
|
|
766
843
|
ok: result.ok,
|
|
844
|
+
kind: result.ok ? (result.runId ? 'launched' : 'pending') : 'failed',
|
|
845
|
+
next: result.runId ? `eln self run ${result.runId}` : result.ok ? 'eln self running-runs' : '실패 이유를 확인한 뒤 다시 요청',
|
|
846
|
+
missing: [...(result.runId ? [] : ['runId']), ...(result.node ? [] : ['node'])],
|
|
847
|
+
...(!result.ok ? { reason: result.detail ?? result.stage } : {}),
|
|
848
|
+
runId: result.runId,
|
|
767
849
|
stage: result.stage,
|
|
768
850
|
node: result.node,
|
|
769
851
|
...(result.prUrl ? { prUrl: result.prUrl } : {}),
|
|
@@ -775,28 +857,51 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
|
|
|
775
857
|
|
|
776
858
|
if (nonBlocking) {
|
|
777
859
|
const runId = allocatedRunId ?? mintRunId();
|
|
860
|
+
const requestId = runId;
|
|
861
|
+
const lookup = `eln logs --category self-implement --event runtime.non-blocking-completed,runtime.non-blocking-failed --grep ${requestId} --since 3h --all --include-test`;
|
|
778
862
|
void drive(runId).then(
|
|
779
863
|
(result) => {
|
|
780
|
-
debug.log('self-implement', 'runtime.non-blocking-completed', {
|
|
781
|
-
|
|
864
|
+
debug.log('self-implement', result.kind === 'failed' ? 'runtime.non-blocking-failed' : 'runtime.non-blocking-completed', {
|
|
865
|
+
requestId,
|
|
866
|
+
kind: result.kind,
|
|
867
|
+
...(result.runId ? { runId: result.runId } : {}),
|
|
868
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
869
|
+
next: result.next ?? '요청 결과를 확인한 뒤 다시 요청',
|
|
782
870
|
ok: result.ok,
|
|
783
871
|
...(result.stage === undefined ? {} : { stage: result.stage }),
|
|
784
|
-
});
|
|
872
|
+
}, result.kind === 'failed' ? { level: 'warn' } : undefined);
|
|
785
873
|
},
|
|
786
874
|
(error) => {
|
|
875
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
787
876
|
debug.log('self-implement', 'runtime.non-blocking-failed', {
|
|
788
|
-
|
|
789
|
-
|
|
877
|
+
requestId,
|
|
878
|
+
kind: 'failed',
|
|
879
|
+
reason,
|
|
880
|
+
next: '요청과 실패 이유를 확인한 뒤 다시 요청',
|
|
790
881
|
}, { level: 'warn' });
|
|
791
882
|
},
|
|
792
883
|
);
|
|
793
884
|
return {
|
|
794
885
|
ok: true,
|
|
795
|
-
|
|
886
|
+
kind: 'pending',
|
|
796
887
|
invocationMode: 'non-blocking',
|
|
797
|
-
|
|
888
|
+
requestId,
|
|
889
|
+
next: lookup,
|
|
890
|
+
missing: ['runId', 'node'],
|
|
891
|
+
output: `하니스 요청을 접수했습니다 · 런과 노드는 아직 미관측 · 요청 ID ${requestId}(발사 증거 아님) · 다음 조회: ${lookup} · 이 턴에서 파일을 직접 편집하지 마세요\n[self-implement] non-blocking pending`,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
return await drive();
|
|
895
|
+
} catch (error) {
|
|
896
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
897
|
+
return {
|
|
898
|
+
ok: false,
|
|
899
|
+
kind: 'failed',
|
|
900
|
+
reason,
|
|
901
|
+
next: '요청과 실패 이유를 확인한 뒤 다시 요청',
|
|
902
|
+
missing: ['runId', 'node'],
|
|
903
|
+
output: `SelfImplement 실패: ${reason} · 다음: 요청과 실패 이유를 확인한 뒤 다시 요청 · 이 턴에서 파일을 직접 편집하지 마세요`,
|
|
798
904
|
};
|
|
799
905
|
}
|
|
800
|
-
return drive();
|
|
801
906
|
},
|
|
802
907
|
};
|
|
@@ -2,7 +2,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
5
|
+
import { prodInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
|
|
6
6
|
import { seatOfTree } from '../loops/orchestrator/traffic.js';
|
|
7
7
|
import { getUserConfig, ORCHESTRATOR_DEFAULTS } from '../user-config.js';
|
|
8
8
|
import { isRunStatus } from './run-status-mapping.js';
|
|
@@ -30,6 +30,7 @@ export interface SpeedClaimsMeasurement {
|
|
|
30
30
|
rows: SpeedClaimsRow[];
|
|
31
31
|
note: string;
|
|
32
32
|
parent: ParentSpeedClaimsMeasurement;
|
|
33
|
+
daily: SpeedClaimsDailyMeasurement;
|
|
33
34
|
}
|
|
34
35
|
export interface ParentSpeedClaimsEvent {
|
|
35
36
|
ts: string;
|
|
@@ -58,6 +59,32 @@ export interface ParentSpeedClaimsMeasurement {
|
|
|
58
59
|
note: string;
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
export interface SpeedClaimsReleaseEvent {
|
|
63
|
+
ts: string;
|
|
64
|
+
version: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface SpeedClaimsDailyRow {
|
|
68
|
+
date: string;
|
|
69
|
+
landedPrCount: number | null;
|
|
70
|
+
releasedCount: number | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface SpeedClaimsDailySourceStatus {
|
|
74
|
+
status: 'measured' | 'unmeasured';
|
|
75
|
+
undatedCount: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface SpeedClaimsDailyMeasurement {
|
|
79
|
+
scope: 'last-30-days';
|
|
80
|
+
windowStart: string;
|
|
81
|
+
windowEnd: string;
|
|
82
|
+
landedPrs: SpeedClaimsDailySourceStatus;
|
|
83
|
+
releases: SpeedClaimsDailySourceStatus;
|
|
84
|
+
rows: SpeedClaimsDailyRow[];
|
|
85
|
+
note: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
61
88
|
export interface SpeedClaimsOptions {
|
|
62
89
|
dir?: string;
|
|
63
90
|
list?: (path: string) => string[];
|
|
@@ -65,6 +92,8 @@ export interface SpeedClaimsOptions {
|
|
|
65
92
|
readSeat?: (runId: string, ledgerDirectory: string, entries: readonly RunLedgerEntry[]) => SpeedClaimsSeat;
|
|
66
93
|
readParentEvents?: () => readonly ParentSpeedClaimsEvent[];
|
|
67
94
|
readParentQueue?: () => unknown;
|
|
95
|
+
now?: number;
|
|
96
|
+
readReleaseEvents?: () => readonly SpeedClaimsReleaseEvent[];
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
const NOTE = 'Child-ledger elapsed minutes from the first child event (or checkpoint creation when unavailable) to the recorded child end, including retries and resumes; this is not the parent launch-to-done interval. Only finished runs enter quartiles (linear interpolation at ranks (n-1)*p). In-progress runs (including parked) and missing, unreadable or invalid timestamps are excluded, not assigned zero. Seats come from the same state root checkpoint, launch queue or recorded launch tree; unresolved seats are counted separately. A completed run without a recorded merge is not a merge.';
|
|
@@ -192,6 +221,98 @@ function readParentQueueFromState(): unknown {
|
|
|
192
221
|
return JSON.parse(readFileSync(join(prodInstanceRoot(), 'harness', 'queue.json'), 'utf8')) as unknown;
|
|
193
222
|
}
|
|
194
223
|
|
|
224
|
+
const DAILY_WINDOW_DAYS = 30;
|
|
225
|
+
const DAY_MS = 86_400_000;
|
|
226
|
+
const DAILY_NOTE = 'Daily rows cover the last 30 UTC calendar days ending on the measurement day. Landed PRs are child run-ledger merged events (merged=true), identified by PR number with the run id as fallback when no number is recorded, de-duplicated across runs, retries and resumes, and attributed to the earliest recorded landing timestamp. Issued releases use the release ledger publishedAt, never a tag target commit date, and are de-duplicated by version. Identities without any valid timestamp are counted as undated, never as a day. A count of zero means the day was queried and had no events; unmeasured means the source could not be queried, and the JSON value is null. Start-to-land and external-change-to-reflection timings stay with the parent and child measurements above.';
|
|
227
|
+
const VERSION_TAG = /^v?\d+(?:\.\d+)+(?:-[\w.]+)?$/;
|
|
228
|
+
|
|
229
|
+
interface DailyObservation {
|
|
230
|
+
key: string;
|
|
231
|
+
ts: number | null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function utcDayOf(ts: number): string {
|
|
235
|
+
return new Date(ts).toISOString().slice(0, 10);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Landed-PR identity: the recorded PR number, else the run id (one run lands at most one PR). */
|
|
239
|
+
function landedPrIdentity(data: unknown, runId: string): string {
|
|
240
|
+
const number = object(data)?.number;
|
|
241
|
+
if (typeof number === 'number' && Number.isInteger(number) && number > 0) return `pr-${number}`;
|
|
242
|
+
if (typeof number === 'string' && /^\d+$/.test(number.trim()) && Number(number.trim()) > 0) return `pr-${Number(number.trim())}`;
|
|
243
|
+
return runId;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** De-duplicate repeated identities by their earliest valid timestamp; identities without any valid timestamp are reported as undated, never dropped silently. */
|
|
247
|
+
function dailyBuckets(observations: readonly DailyObservation[]): { counts: Map<string, number>; undated: number } {
|
|
248
|
+
const earliest = new Map<string, number>();
|
|
249
|
+
const undated = new Set<string>();
|
|
250
|
+
for (const { key, ts } of observations) {
|
|
251
|
+
if (key.length === 0) continue;
|
|
252
|
+
if (ts === null) { undated.add(key); continue; }
|
|
253
|
+
const known = earliest.get(key);
|
|
254
|
+
if (known === undefined || ts < known) earliest.set(key, ts);
|
|
255
|
+
}
|
|
256
|
+
const counts = new Map<string, number>();
|
|
257
|
+
for (const ts of earliest.values()) {
|
|
258
|
+
const day = utcDayOf(ts);
|
|
259
|
+
counts.set(day, (counts.get(day) ?? 0) + 1);
|
|
260
|
+
}
|
|
261
|
+
let undatedCount = 0;
|
|
262
|
+
for (const key of undated) if (!earliest.has(key)) undatedCount++;
|
|
263
|
+
return { counts, undated: undatedCount };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Read-only daily census of landed PRs and issued releases over the last 30 UTC days. */
|
|
267
|
+
function measureDailySpeedClaims(
|
|
268
|
+
landed: readonly DailyObservation[],
|
|
269
|
+
landedMeasured: boolean,
|
|
270
|
+
releases: readonly SpeedClaimsReleaseEvent[] | null,
|
|
271
|
+
now: number,
|
|
272
|
+
): SpeedClaimsDailyMeasurement {
|
|
273
|
+
const windowEnd = utcDayOf(now);
|
|
274
|
+
const startMs = Date.parse(`${windowEnd}T00:00:00.000Z`) - (DAILY_WINDOW_DAYS - 1) * DAY_MS;
|
|
275
|
+
const landedBuckets = dailyBuckets(landed);
|
|
276
|
+
const releaseBuckets = releases === null
|
|
277
|
+
? null
|
|
278
|
+
: dailyBuckets(releases.map((event) => ({ key: event.version.trim().replace(/^v(?=\d)/, ''), ts: parseTime(event.ts) })));
|
|
279
|
+
const landedComplete = landedMeasured && landedBuckets.undated === 0;
|
|
280
|
+
const releasesComplete = releaseBuckets !== null && releaseBuckets.undated === 0;
|
|
281
|
+
return {
|
|
282
|
+
scope: 'last-30-days', windowStart: utcDayOf(startMs), windowEnd,
|
|
283
|
+
landedPrs: { status: landedComplete ? 'measured' : 'unmeasured', undatedCount: landedBuckets.undated },
|
|
284
|
+
releases: { status: releasesComplete ? 'measured' : 'unmeasured', undatedCount: releaseBuckets?.undated ?? 0 },
|
|
285
|
+
rows: Array.from({ length: DAILY_WINDOW_DAYS }, (_, index) => {
|
|
286
|
+
const date = utcDayOf(startMs + index * DAY_MS);
|
|
287
|
+
return {
|
|
288
|
+
date,
|
|
289
|
+
landedPrCount: landedComplete ? (landedBuckets.counts.get(date) ?? 0) : null,
|
|
290
|
+
releasedCount: releasesComplete ? (releaseBuckets!.counts.get(date) ?? 0) : null,
|
|
291
|
+
};
|
|
292
|
+
}),
|
|
293
|
+
note: DAILY_NOTE,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Release manifests record the actual publication time; lightweight tags have no creation timestamp. */
|
|
298
|
+
function readReleaseEventsFromLedger(): SpeedClaimsReleaseEvent[] {
|
|
299
|
+
const root = join(releaseLedgerRoot(), 'release');
|
|
300
|
+
const versions = readdirSync(root).filter((version) => VERSION_TAG.test(version));
|
|
301
|
+
const events: SpeedClaimsReleaseEvent[] = [];
|
|
302
|
+
for (const version of versions) {
|
|
303
|
+
// Absence and corruption are both unmeasured; only a readable manifest proves preparation.
|
|
304
|
+
const value: unknown = JSON.parse(readFileSync(join(root, version, 'release.json'), 'utf8')) as unknown;
|
|
305
|
+
const record = object(value);
|
|
306
|
+
if (record?.version !== version) throw new Error(`release manifest version mismatch: ${version}`);
|
|
307
|
+
if (!Object.hasOwn(record, 'publishedAt')) continue; // Prepared, not yet published.
|
|
308
|
+
if (typeof record.publishedAt !== 'string' || parseTime(record.publishedAt) === null) throw new Error(`invalid release publication time: ${version}`);
|
|
309
|
+
events.push({ version, ts: record.publishedAt });
|
|
310
|
+
}
|
|
311
|
+
// A checkout without tags or a release history cannot establish a queried zero.
|
|
312
|
+
if (versions.length === 0) throw new Error('release publication history unavailable');
|
|
313
|
+
return events;
|
|
314
|
+
}
|
|
315
|
+
|
|
195
316
|
/** Parent launch-to-done census; no child ledger id is used as a join key. */
|
|
196
317
|
export function measureParentSpeedClaims(
|
|
197
318
|
events: readonly ParentSpeedClaimsEvent[], queue?: unknown,
|
|
@@ -262,7 +383,13 @@ export function measureParentSpeedClaims(
|
|
|
262
383
|
|
|
263
384
|
/** Read-only elapsed-time census of canonical local run ledgers, including unsuccessful runs. */
|
|
264
385
|
export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaimsMeasurement {
|
|
386
|
+
const now = options.now ?? Date.now();
|
|
265
387
|
const ledgerDirectory = resolve(options.dir ?? runLedgerDir());
|
|
388
|
+
let releaseEvents: readonly SpeedClaimsReleaseEvent[] | null = null;
|
|
389
|
+
try {
|
|
390
|
+
const events = (options.readReleaseEvents ?? readReleaseEventsFromLedger)();
|
|
391
|
+
releaseEvents = Array.isArray(events) ? events : null;
|
|
392
|
+
} catch { /* An unreadable release source marks releases unmeasured; it does not erase landed-PR observations. */ }
|
|
266
393
|
let parent: ParentSpeedClaimsMeasurement;
|
|
267
394
|
try {
|
|
268
395
|
const events = (options.readParentEvents ?? readParentEventsFromLogs)();
|
|
@@ -272,21 +399,27 @@ export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaim
|
|
|
272
399
|
} catch {
|
|
273
400
|
parent = { ...measureParentSpeedClaims([]), status: 'cannot-measure' };
|
|
274
401
|
}
|
|
275
|
-
const result: SpeedClaimsMeasurement = {
|
|
402
|
+
const result: Omit<SpeedClaimsMeasurement, 'daily'> = {
|
|
276
403
|
scope: 'self-implement-run-ledger', ledgerDirectory, status: 'measured',
|
|
277
404
|
completedCount: 0, inProgressCount: 0, cannotMeasureCount: 0, unknownSeatCount: 0, rows: [], note: NOTE, parent,
|
|
278
405
|
};
|
|
279
406
|
let files: string[];
|
|
280
407
|
try { files = (options.list ?? readdirSync)(ledgerDirectory); }
|
|
281
|
-
catch { return { ...result, status: 'cannot-measure' }; }
|
|
408
|
+
catch { return { ...result, status: 'cannot-measure', daily: measureDailySpeedClaims([], false, releaseEvents, now) }; }
|
|
282
409
|
|
|
410
|
+
const landed: DailyObservation[] = [];
|
|
411
|
+
let ledgerUnreadable = false;
|
|
283
412
|
const groups = new Map<string, { seat: SpeedClaimsSeat; outcome: SpeedClaimsOutcome; values: number[] }>();
|
|
284
413
|
for (const file of files.sort()) {
|
|
285
414
|
const runId = file.endsWith('.jsonl') ? file.slice(0, -'.jsonl'.length) : '';
|
|
286
415
|
if (!CANONICAL_RUN_ID.test(runId)) continue;
|
|
287
416
|
try {
|
|
288
417
|
const entries = loadRunLedger(runId, ledgerDirectory, options.read);
|
|
289
|
-
if (!entries || entries.length === 0) { result.cannotMeasureCount++; continue; }
|
|
418
|
+
if (!entries || entries.length === 0) { ledgerUnreadable = true; result.cannotMeasureCount++; continue; }
|
|
419
|
+
const merges = entries.filter((entry) => entry.event === 'merged' && entry.data.merged === true);
|
|
420
|
+
const numbered = new Set(merges.map((entry) => landedPrIdentity(entry.data, runId)).filter((key) => key !== runId));
|
|
421
|
+
if (numbered.size > 1) ledgerUnreadable = true; // Conflicting PR identities cannot establish a daily count.
|
|
422
|
+
else for (const entry of merges) landed.push({ key: numbered.size === 1 ? [...numbered][0]! : runId, ts: parseTime(entry.timestamp) });
|
|
290
423
|
const end = endOfRun(entries);
|
|
291
424
|
const first = time(entries[0]);
|
|
292
425
|
const createdAt = checkpointFor(runId, ledgerDirectory)?.createdAt;
|
|
@@ -306,14 +439,14 @@ export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaim
|
|
|
306
439
|
group.values.push((endTime! - start) / 60_000);
|
|
307
440
|
groups.set(key, group);
|
|
308
441
|
result.completedCount++;
|
|
309
|
-
} catch { result.cannotMeasureCount++; }
|
|
442
|
+
} catch { result.cannotMeasureCount++; ledgerUnreadable = true; }
|
|
310
443
|
}
|
|
311
444
|
result.rows = [...groups.values()].map(({ seat, outcome, values }) => {
|
|
312
445
|
values.sort((a, b) => a - b);
|
|
313
446
|
return { seat, outcome, count: values.length, underOneMinuteCount: values.filter((minutes) => minutes < 1).length, medianMinutes: percentile(values, 0.5), q1Minutes: percentile(values, 0.25), q3Minutes: percentile(values, 0.75) };
|
|
314
447
|
}).sort((a, b) => SEATS.indexOf(a.seat) - SEATS.indexOf(b.seat) || a.outcome.localeCompare(b.outcome));
|
|
315
448
|
if (result.cannotMeasureCount > 0) result.status = 'cannot-measure';
|
|
316
|
-
return result;
|
|
449
|
+
return { ...result, daily: measureDailySpeedClaims(landed, !ledgerUnreadable, releaseEvents, now) };
|
|
317
450
|
}
|
|
318
451
|
|
|
319
452
|
export function renderSpeedClaims(result: SpeedClaimsMeasurement): string {
|
|
@@ -334,5 +467,11 @@ export function renderSpeedClaims(result: SpeedClaimsMeasurement): string {
|
|
|
334
467
|
`parent cannot measure (excluded): ${result.parent.cannotMeasureCount}`,
|
|
335
468
|
...result.parent.rows.map((row) => `seat=${row.seat} stage=${row.stage} merged=${row.merged} count=${row.count} median=${row.medianMinutes}m q1=${row.q1Minutes}m q3=${row.q3Minutes}m`),
|
|
336
469
|
`parent note: ${result.parent.note}`,
|
|
470
|
+
'daily landed PRs / issued releases (last 30 days, UTC):',
|
|
471
|
+
`daily window: ${result.daily.windowStart}..${result.daily.windowEnd}`,
|
|
472
|
+
`landed PRs status: ${result.daily.landedPrs.status} (undated: ${result.daily.landedPrs.undatedCount})`,
|
|
473
|
+
`issued releases status: ${result.daily.releases.status} (undated: ${result.daily.releases.undatedCount})`,
|
|
474
|
+
...result.daily.rows.map((row) => `date=${row.date} landed=${row.landedPrCount ?? 'unmeasured'} released=${row.releasedCount ?? 'unmeasured'}`),
|
|
475
|
+
`daily note: ${result.daily.note}`,
|
|
337
476
|
].join('\n');
|
|
338
477
|
}
|
package/src/session/index.ts
CHANGED
|
@@ -142,10 +142,20 @@ function migrateLegacySessions(): void {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
function stateRoot(): string {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
const universe = process.env.ELANOUS_STATE_DIR?.trim();
|
|
146
|
+
const root = universe
|
|
147
|
+
? join(universe, 'elanous')
|
|
148
|
+
: join(
|
|
149
|
+
process.env.XDG_STATE_HOME && process.env.XDG_STATE_HOME.trim()
|
|
150
|
+
? process.env.XDG_STATE_HOME
|
|
151
|
+
: join(homedir(), '.local', 'state'),
|
|
152
|
+
'elanous',
|
|
153
|
+
);
|
|
154
|
+
try {
|
|
155
|
+
const { debug } = require('../debug/log.js') as typeof import('../debug/log.js');
|
|
156
|
+
debug.log('session.active', 'pointer-root', { root, universe });
|
|
157
|
+
} catch { /* 관측 실패가 포인터 경로 결정을 막지 않는다 */ }
|
|
158
|
+
return root;
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
function indexPath(root: string = sessionRoot()): string { return join(root, 'index.json'); }
|