elanous 0.2.21 → 0.2.22
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-08b716476f1d50af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-d781be3e3a9cfd74.js → page-d85c43f46f58716b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-b0a80997903852d8.js → page-4e645600579ae952.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-2b1263165630d9c8.js → page-87edbfd19117823b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-e2d6bed48ac20120.js → page-cbaf66b1e1804adf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-80ed511e02c81412.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/{page-544dd4bd4847eeb7.js → page-9f5610caaa06178c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-3ee191319bf97d87.js → page-ec8f3575e339de74.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-a449f1ed472b81c2.js → page-2a19c5cd3b562580.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-9045094d6d3b5cd0.js → page-b6a09cde2e07521a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-c06673596849ef45.js → page-f7eae1518ca0c353.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{layout-9f81233f29dcd409.js → layout-8462d6a543cbb2d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-7a12f61be4a579d1.js → page-786fb4504d0f2fa9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-c85b0589591a99c9.js → not-found-75209f14597dcb8f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-725a4da02dd129eb.js → page-bf30affe3a85af7e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-21bec4afeb2f0917.js → page-76cf838050a8d681.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-dc39ee7a36d26385.js → page-7ec8e289b4c14df6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-84ae759f76053672.js → page-8c8ac17d52be2a79.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-3db58ddb61fe2be9.js → page-d359ab048d63a9d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-c5315e30f3528dc4.js → page-b8b345c880bf78d1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-2fe368efb6de3b6a.js → page-f151cc10ad026940.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-c69762987b751174.js → page-bf495a1e284f4e00.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-7f8d7fcdf058a845.js → page-69cb93d1a2e57e0e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-22163556cd38bc88.js → page-72ec543928d67c3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
- package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.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 +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/drafts/recipes.yaml +2 -2
- package/graphs/test-diet/recipes.yaml +3 -0
- package/graphs/test-diet/test-diet.yaml +3 -1
- package/package.json +1 -1
- package/scripts/f-session-monitor.ts +69 -0
- package/scripts/hq/standby-snapshot.ts +7 -3
- package/scripts/knowledge-compress.ts +11 -0
- package/scripts/nightly-docops.ts +90 -3
- package/scripts/research-coo-admin-dates.ts +39 -0
- package/scripts/skills/public-check.ts +7 -2
- package/scripts/smart-merge/resync-shadow.ts +5 -3
- package/scripts/test-diet/lib.ts +81 -1
- package/scripts/test-diet/node.ts +48 -2
- package/scripts/verify-coo-admin-evidence.ts +83 -0
- package/src/acp/client.ts +4 -2
- package/src/agent/run-context.ts +4 -0
- package/src/agent-env/profile.ts +25 -5
- package/src/agent-mission/driver.ts +26 -12
- package/src/agent-mission/supervisor-metrics.ts +17 -2
- package/src/agent-substrate/acp-reviewer.ts +12 -5
- package/src/agent-substrate/pr-reviewer.ts +11 -4
- package/src/agent-substrate/review-intent.ts +102 -3
- package/src/autopilot/build/llm-conflict-merge.ts +22 -8
- package/src/autopilot/pr-manager.ts +18 -8
- package/src/away/release-watch.ts +47 -8
- package/src/briefing/brief-items.ts +104 -11
- package/src/chat/index.ts +1 -0
- package/src/cli/brief-cli.ts +20 -2
- package/src/cli/config-classify.ts +83 -0
- package/src/cli/control-cli.ts +14 -0
- package/src/cli/decisions-cli.ts +26 -16
- package/src/cli/logs-model-input.ts +1 -1
- package/src/cli/pr-cli.ts +65 -8
- package/src/cli/release-cli.ts +21 -4
- package/src/cli/tasks-cli.ts +108 -1
- package/src/connectors/types.ts +6 -6
- package/src/context-bus/context-now-surfaces.ts +18 -0
- package/src/context-bus/coord-events.ts +32 -4
- package/src/context-bus/long-term-memory.ts +23 -6
- package/src/context-bus/nightly-condense.ts +169 -21
- package/src/control/control-status.ts +87 -0
- package/src/control-plane/light-member.ts +5 -3
- package/src/control-plane/member.ts +172 -7
- package/src/core-turn/run-goal-loop.ts +22 -1
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
- package/src/decisions/decision-cards.ts +20 -1
- package/src/decisions/decision-ledger.ts +11 -8
- package/src/decisions/web-push-decision-cards.ts +19 -3
- package/src/discord-self-message.ts +2 -2
- package/src/domains/coo-admin-dates.evidence.json +876 -0
- package/src/domains/coo-admin-tool.ts +48 -14
- package/src/domains/ops-status.ts +53 -0
- package/src/domains/outbound-alert.ts +24 -1
- package/src/exec-requests/planner.ts +6 -0
- package/src/graph-kinds/registry.ts +6 -6
- package/src/graph-runner/graph-cli.ts +34 -2
- package/src/graph-runner/graph-run-api.ts +7 -0
- package/src/graph-runner/graph-tick.ts +73 -12
- package/src/graph-runner/runner.ts +43 -11
- package/src/graph-unify/wf-to-graph.ts +79 -0
- package/src/graph-wizard/generate.ts +34 -2
- package/src/graph-wizard/steps.ts +13 -0
- package/src/harness/harness-cli-command.ts +121 -23
- package/src/harness/harness-queue.ts +58 -14
- package/src/harness/launch-advice.ts +23 -0
- package/src/index.ts +36 -7
- package/src/knowledge/conclusion-compress.ts +93 -0
- package/src/knowledge/know-find.ts +9 -2
- package/src/knowledge/query.ts +20 -0
- package/src/llm.ts +26 -16
- package/src/loops/neighbor-runtime.ts +88 -0
- package/src/loops/orchestrator/author-depth.ts +29 -11
- package/src/loops/orchestrator/overlap.ts +7 -1
- package/src/loops/orchestrator/resync-shadow.ts +1 -1
- package/src/loops/orchestrator/tick.ts +35 -2
- package/src/loops/orchestrator/traffic.ts +2 -0
- package/src/loops/registry.ts +5 -0
- package/src/market/publish.ts +17 -1
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/mss/logging/redaction.ts +17 -0
- package/src/nexus/api/draft-metrics.ts +22 -8
- package/src/nexus/api/graph-peer-edit.ts +77 -0
- package/src/nexus/api/graph-wizard-api.ts +9 -1
- package/src/nexus/api/graphs-api.ts +145 -7
- package/src/nexus/api/http-server.ts +93 -8
- package/src/nexus/api/plugins-market.ts +24 -0
- package/src/nexus/index.ts +3 -2
- package/src/oauth/claude-pty-maturity.ts +24 -0
- package/src/oauth/fallback-chain.ts +27 -14
- package/src/plugins/maker/market-bundle.ts +9 -0
- package/src/plugins/maker/plugin-maker.ts +9 -3
- package/src/plugins/maker/wizard-generate.ts +73 -9
- package/src/policy/llm-policy.ts +11 -6
- package/src/release-loop/checklist-board.ts +27 -0
- package/src/release-loop/checklist.ts +38 -0
- package/src/seat-address/seat-address.ts +1 -0
- package/src/seat-loop/seat-loop.ts +174 -20
- package/src/seat-loop/seat-report.ts +29 -1
- package/src/self-dev/dev-cli.ts +90 -8
- package/src/self-dev/dev-pipeline.ts +20 -1
- package/src/self-dev/draft-sweep.ts +45 -8
- package/src/self-dev/orchestrate.ts +3 -0
- package/src/self-dev/review-provider-fallback.ts +83 -0
- package/src/self-dev/run-supervisor.ts +5 -1
- package/src/self-implement/harness-policy-gates.ts +7 -5
- package/src/self-implement/host-regate.ts +28 -14
- package/src/self-implement/l8-merge-queue.ts +80 -8
- package/src/self-implement/orchestrator.ts +68 -27
- package/src/self-implement/seams.ts +5 -3
- package/src/self-implement/self-implement-cli.ts +4 -0
- package/src/shell-env-bootstrap.ts +19 -4
- package/src/steward/triage.ts +25 -8
- package/src/storage/file-lock.ts +33 -4
- package/src/task-agent/completion-evidence.ts +31 -3
- package/src/task-agent/cover.ts +61 -0
- package/src/task-agent/live-moves.ts +126 -3
- package/src/task-agent/shadow.ts +19 -4
- package/src/task-agent/task-hand.ts +2 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
- package/src/telegram-commands.ts +8 -0
- package/src/telegram-project-command.ts +6 -0
- package/src/telegram.ts +2 -2
- package/src/typecheck-ratchet.ts +2 -1
- package/src/user-config.ts +35 -4
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +24 -2
- package/src/workflow-runtime/triggers/registry.ts +27 -0
- package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
- package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
- package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
- /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
|
@@ -4,6 +4,7 @@ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync
|
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { Database } from 'bun:sqlite';
|
|
6
6
|
import { debug } from '../debug/log.js';
|
|
7
|
+
import { stateDirSourceForChild } from '../agent/identity-env.js';
|
|
7
8
|
import { findTreeRoot } from '../cli/test-flag.js';
|
|
8
9
|
import { readLogInstances, type LogInstanceView } from '../mss/logging/instance-registry.js';
|
|
9
10
|
import { effectiveInstanceRoot, prodInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
|
|
@@ -20,6 +21,7 @@ import { hostLeaseCounts, leaseHasPendingPod } from '../pod-lease/host-lease.js'
|
|
|
20
21
|
import { leaseKubectl, measurePoolLease } from '../task-orchestrator/surfaces/pod-lease.js';
|
|
21
22
|
import { parsePodPool, podPoolHostLease, resolvePodPoolSpec } from '../task-orchestrator/surfaces/pod-pool.js';
|
|
22
23
|
import { writeHarnessQueueReceipt } from './harness-queue-child.js';
|
|
24
|
+
import { adviseLaunch, compareLaunchAdvice, type LaunchAdvice } from './launch-advice.js';
|
|
23
25
|
import { finishAdvice, measureFinish, recordFinishHistory, type FinishMetrics } from '../loops/orchestrator/finish-rate.js';
|
|
24
26
|
|
|
25
27
|
export type QueueSeat = 'OP' | 'TC' | 'MK' | 'UX';
|
|
@@ -73,6 +75,8 @@ export interface HarnessQueueDeps {
|
|
|
73
75
|
configPath?: string;
|
|
74
76
|
cap?: (seat: QueueSeat) => number;
|
|
75
77
|
pool?: () => QueuePool;
|
|
78
|
+
/** Shadow-only advice seam; exceptions must not affect the queue decision. */
|
|
79
|
+
advice?: typeof adviseLaunch;
|
|
76
80
|
launch?: (item: QueueItem, args: string[], root: string) => Promise<number>;
|
|
77
81
|
/** Test seam: runs after a direct CLI call enqueued its row and before that call's own tick (race tests). */
|
|
78
82
|
afterEnqueue?: (item: QueueItem) => void | Promise<void>;
|
|
@@ -385,24 +389,25 @@ export function listHarnessQueue(deps: HarnessQueueDeps = {}): QueueItem[] {
|
|
|
385
389
|
return read(harnessQueuePath(deps.root ?? effectiveInstanceRoot()));
|
|
386
390
|
}
|
|
387
391
|
|
|
388
|
-
/** A missing row is not proof of failure
|
|
389
|
-
export function harnessQueueOutcome(id: string, deps: HarnessQueueDeps = {}): 'pending' | 'succeeded' | 'retryable' | 'unknown' {
|
|
392
|
+
/** A missing row is not proof of failure; explicit removal and terminal receipts have distinct markers. */
|
|
393
|
+
export function harnessQueueOutcome(id: string, deps: HarnessQueueDeps = {}): 'pending' | 'succeeded' | 'retryable' | 'removed' | 'unknown' {
|
|
390
394
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
391
395
|
const row = listHarnessQueue(deps).find((item) => item.id === id);
|
|
392
396
|
if (!row) {
|
|
393
397
|
const marker = join(root, 'harness', `${id}.outcome`);
|
|
394
398
|
if (!existsSync(marker)) return 'unknown';
|
|
395
399
|
const value = readFileSync(marker, 'utf8');
|
|
396
|
-
return value === 'succeeded' || value === 'retryable' ? value : 'unknown';
|
|
400
|
+
return value === 'succeeded' || value === 'retryable' || value === 'removed' ? value : 'unknown';
|
|
397
401
|
}
|
|
398
402
|
return queueRowOutcome(row, root);
|
|
399
403
|
}
|
|
400
404
|
|
|
401
405
|
const keyMarkerPath = (root: string, key: string) => join(root, 'harness', 'by-key', `${createHash('sha256').update(key).digest('hex')}.id`);
|
|
402
406
|
|
|
403
|
-
function recordQueueOutcome(root: string, id: string, outcome: 'succeeded' | 'retryable', key?: string): void {
|
|
407
|
+
function recordQueueOutcome(root: string, id: string, outcome: 'succeeded' | 'retryable' | 'removed', key?: string): void {
|
|
404
408
|
const path = join(root, 'harness', `${id}.outcome`);
|
|
405
|
-
if (
|
|
409
|
+
if (outcome === 'removed') writeFileSync(path, outcome, { mode: 0o600 });
|
|
410
|
+
else if (!existsSync(path)) writeFileSync(path, outcome, { flag: 'wx', mode: 0o600 });
|
|
406
411
|
// The row is about to leave the queue; keep «which id last held this key» so a caller that crashed before
|
|
407
412
|
// recording the id can still find the outcome (LOOP-LIVE1 · seat loop recovery).
|
|
408
413
|
if (key) {
|
|
@@ -491,10 +496,8 @@ export async function removeHarnessQueue(id: string, deps: HarnessQueueDeps = {}
|
|
|
491
496
|
if (state !== 'finished' && state !== 'not-started'
|
|
492
497
|
|| (deps.processes ?? readHarnessQueueProcesses)().some((row) => row.launchId === item.launchId)) return false;
|
|
493
498
|
}
|
|
494
|
-
const outcome = item
|
|
495
|
-
if (item.idempotencyKey
|
|
496
|
-
recordQueueOutcome(root, id, outcome, item.idempotencyKey);
|
|
497
|
-
}
|
|
499
|
+
const outcome = queueRowOutcome(item, root) === 'succeeded' ? 'succeeded' : 'removed';
|
|
500
|
+
if (item.idempotencyKey) recordQueueOutcome(root, id, outcome, item.idempotencyKey);
|
|
498
501
|
save(path, rows.filter((row) => row.id !== id));
|
|
499
502
|
observe('skipped', { id, seat: item.seat, reason: 'removed' }, deps);
|
|
500
503
|
return true;
|
|
@@ -699,6 +702,26 @@ export function readHarnessQueueInventory(probe: ProcessProbe = {}): QueueInvent
|
|
|
699
702
|
if (prior && prior !== record.seat) { ledgerSeats.delete(pid); ambiguousPids.add(pid); }
|
|
700
703
|
else ledgerSeats.set(pid, record.seat!);
|
|
701
704
|
}
|
|
705
|
+
// One lsof for every candidate: a per-pid lsof (~0.1–0.3 s each) made a tick on a busy host take 10 s+ (10-08). lsof exits 1
|
|
706
|
+
// when some pid is gone yet still prints the others; a pid it does not print stays unattributed (charged to all seats).
|
|
707
|
+
let batchCwds: Map<number, string> | undefined;
|
|
708
|
+
const readBatchCwds = (): Map<number, string> => {
|
|
709
|
+
const cwds = new Map<number, string>();
|
|
710
|
+
const started = Date.now();
|
|
711
|
+
const lsof = run('lsof', ['-a', '-d', 'cwd', '-Fpn', '-p', candidates.map(({ pid }) => pid).join(',')],
|
|
712
|
+
{ encoding: 'utf8', timeout: 5_000, maxBuffer: 16 * 1024 * 1024 });
|
|
713
|
+
if (!lsof.error && typeof lsof.stdout === 'string') {
|
|
714
|
+
let current: number | undefined;
|
|
715
|
+
for (const line of lsof.stdout.split('\n')) {
|
|
716
|
+
if (line.startsWith('p')) current = Number(line.slice(1));
|
|
717
|
+
else if (line.startsWith('n/') && current !== undefined) cwds.set(current, line.slice(1));
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
try { debug.log('harness.queue', 'cwd-batch', { pids: candidates.length, read: cwds.size, ms: Date.now() - started,
|
|
721
|
+
...(lsof.error ? { error: String(lsof.error).slice(0, 240) } : {}) }); }
|
|
722
|
+
catch { /* Observation cannot affect dispatch. */ }
|
|
723
|
+
return cwds;
|
|
724
|
+
};
|
|
702
725
|
const rows: QueueProcess[] = [];
|
|
703
726
|
// Attribute a run after grouping: the orchestrator may switch working trees while its launcher stays in HQ.
|
|
704
727
|
for (const root of candidates.filter((row) => !byPid.has(row.ppid))) {
|
|
@@ -736,9 +759,9 @@ export function readHarnessQueueInventory(probe: ProcessProbe = {}): QueueInvent
|
|
|
736
759
|
if (probe.cwd) cwd = probe.cwd(pid);
|
|
737
760
|
else if (platform === 'linux') cwd = readlinkSync(`/proc/${pid}/cwd`);
|
|
738
761
|
else {
|
|
739
|
-
const
|
|
740
|
-
if (
|
|
741
|
-
cwd =
|
|
762
|
+
const found = (batchCwds ??= readBatchCwds()).get(pid);
|
|
763
|
+
if (found === undefined) throw new Error('cwd unavailable');
|
|
764
|
+
cwd = found;
|
|
742
765
|
}
|
|
743
766
|
if (!cwd.startsWith('/')) continue;
|
|
744
767
|
const found = queueSeatForCwd(cwd, trees);
|
|
@@ -816,7 +839,8 @@ async function launch(item: QueueItem, args: string[], root: string, command?: s
|
|
|
816
839
|
harnessQueueReceiptPath(root, item.launchId!), command ?? join(repo, 'bin', 'elanous.mjs'),
|
|
817
840
|
...(resolve(root) === prodInstanceRoot() ? [] : [`--test=${root}`]), ...args], {
|
|
818
841
|
cwd: item.launchCwd ?? repo, detached: true, stdio: ['ignore', fd, fd],
|
|
819
|
-
env: { ...process.env, ELANOUS_STATE_DIR: root,
|
|
842
|
+
env: { ...process.env, ELANOUS_STATE_DIR: root,
|
|
843
|
+
ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(root), ELANOUS_HARNESS_SEAT: item.seat,
|
|
820
844
|
ELANOUS_HARNESS_QUEUE_LAUNCH: item.launchId! },
|
|
821
845
|
});
|
|
822
846
|
const pid = await new Promise<number>((done, reject) => {
|
|
@@ -1187,7 +1211,13 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
|
|
|
1187
1211
|
|
|
1188
1212
|
async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string | undefined, followUp: boolean): Promise<QueueTick> {
|
|
1189
1213
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
1190
|
-
|
|
1214
|
+
const caller = 'harness-queue-tick' as const;
|
|
1215
|
+
let advice: LaunchAdvice = { verdict: 'unknown', why: 'pool not measured before queue decision' };
|
|
1216
|
+
const shadow = (pool: QueuePool | undefined): void => {
|
|
1217
|
+
try { advice = (deps.advice ?? adviseLaunch)({ pool, caller }); }
|
|
1218
|
+
catch { advice = { verdict: 'unknown', why: 'launch advice unavailable' }; }
|
|
1219
|
+
};
|
|
1220
|
+
const actual = await locked<QueueTick>(root, async (path): Promise<QueueTick> => {
|
|
1191
1221
|
// A burst follow-up already ran the author shadow and the idle request in this tick's first pass.
|
|
1192
1222
|
if (!followUp) try {
|
|
1193
1223
|
const now = (deps.now ?? (() => new Date()))();
|
|
@@ -1196,7 +1226,11 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
|
|
|
1196
1226
|
debug.log('loops.author-depth', 'shadow-skipped-test', {});
|
|
1197
1227
|
} else {
|
|
1198
1228
|
const shadow = deps.authorShadow ?? (await import('../loops/orchestrator/author-depth.js')).runAuthorDepthShadow;
|
|
1229
|
+
const started = Date.now();
|
|
1199
1230
|
await shadow(root, now);
|
|
1231
|
+
// The shadow runs inside the queue lock; its duration is what other ticks wait on.
|
|
1232
|
+
try { debug.log('harness.queue', 'author-shadow', { ms: Date.now() - started }); }
|
|
1233
|
+
catch { /* Observation cannot affect dispatch. */ }
|
|
1200
1234
|
}
|
|
1201
1235
|
} catch (error) {
|
|
1202
1236
|
try { debug.log('loops.author-depth', 'shadow-failed', { reason: String(error) }); }
|
|
@@ -1463,6 +1497,7 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
|
|
|
1463
1497
|
if (pool && (![pool.running, pool.pending, pool.reserved, pool.limit].every((n) => Number.isSafeInteger(n) && n >= 0) || pool.limit < 1)) {
|
|
1464
1498
|
poolReason = 'pod lease status incomplete';
|
|
1465
1499
|
}
|
|
1500
|
+
shadow(poolReason ? undefined : pool);
|
|
1466
1501
|
if (pool && !poolReason) {
|
|
1467
1502
|
const queueLaunching = current.filter((row) => row.status === 'launching' || row.status === 'launched').length;
|
|
1468
1503
|
// CHILD-UNIV-HOLD: each unreadable child universe is charged one pool slot (conservative), never a seat slot.
|
|
@@ -1545,4 +1580,13 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
|
|
|
1545
1580
|
}
|
|
1546
1581
|
return waiting();
|
|
1547
1582
|
});
|
|
1583
|
+
// An empty queue tick has nothing to advise on; logging it would flood the advice-unknown sample.
|
|
1584
|
+
if (actual.outcome === 'skipped' && !actual.item) return actual;
|
|
1585
|
+
try {
|
|
1586
|
+
const { kind } = compareLaunchAdvice(advice, actual.outcome);
|
|
1587
|
+
debug.log('resource.advice', 'launch-advice', {
|
|
1588
|
+
caller, verdict: advice.verdict, why: advice.why, actual: actual.outcome, kind, itemId: actual.item?.id ?? null,
|
|
1589
|
+
});
|
|
1590
|
+
} catch { /* Shadow observation cannot affect the queue result. */ }
|
|
1591
|
+
return actual;
|
|
1548
1592
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { QueuePool, QueueTick } from './harness-queue.js';
|
|
2
|
+
|
|
3
|
+
export type LaunchAdvice = { verdict: 'launch-now' | 'wait' | 'unknown'; why: string; retryAfterHint?: string };
|
|
4
|
+
export type LaunchAdviceInput = { pool: QueuePool | null | undefined; caller: 'harness-queue-tick' | 'hand-task' | 'release-run' };
|
|
5
|
+
|
|
6
|
+
/** A pool-only suggestion; queue, seat, freeze and finish gates remain the dispatcher's decisions. */
|
|
7
|
+
export function adviseLaunch(input: LaunchAdviceInput): LaunchAdvice {
|
|
8
|
+
const pool = input.pool;
|
|
9
|
+
if (!pool || ![pool.running, pool.pending, pool.reserved, pool.limit].every(n => Number.isSafeInteger(n) && n >= 0)
|
|
10
|
+
|| pool.limit < 1) return { verdict: 'unknown', why: 'pool unavailable or incomplete' };
|
|
11
|
+
const used = pool.running + pool.pending + pool.reserved;
|
|
12
|
+
if (used >= pool.limit) return { verdict: 'wait', why: `pool ${used}/${pool.limit} occupied`, retryAfterHint: 'when a pool slot is released' };
|
|
13
|
+
return { verdict: 'launch-now', why: `pool ${used}/${pool.limit} occupied` };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function compareLaunchAdvice(advice: LaunchAdvice, actualOutcome: QueueTick['outcome']): {
|
|
17
|
+
agree: boolean; kind: 'advice-launch-actual-wait' | 'advice-wait-actual-launch' | 'advice-unknown' | 'agree';
|
|
18
|
+
} {
|
|
19
|
+
if (advice.verdict === 'unknown' || actualOutcome === 'skipped') return { agree: false, kind: 'advice-unknown' };
|
|
20
|
+
if (advice.verdict === 'launch-now' && actualOutcome === 'waiting') return { agree: false, kind: 'advice-launch-actual-wait' };
|
|
21
|
+
if (advice.verdict === 'wait' && actualOutcome === 'launched') return { agree: false, kind: 'advice-wait-actual-launch' };
|
|
22
|
+
return { agree: true, kind: 'agree' };
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -613,9 +613,10 @@ function buildHarnessAskSayDevCliSpec(
|
|
|
613
613
|
input: { file: string },
|
|
614
614
|
cliOpts: ReturnType<typeof harnessAskSayOptionsToDevCliOpts>,
|
|
615
615
|
entranceId: EntranceId,
|
|
616
|
+
runId: string,
|
|
616
617
|
) {
|
|
617
618
|
return {
|
|
618
|
-
...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId),
|
|
619
|
+
...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId, undefined, undefined, runId),
|
|
619
620
|
...(cliOpts.json ? { humanReadableOutput: false } : {}),
|
|
620
621
|
};
|
|
621
622
|
}
|
|
@@ -661,6 +662,7 @@ async function runDevAskFromAskFile(askPath: string, opts: HarnessAskSayChildLlm
|
|
|
661
662
|
{ file: flowResult.goalFile },
|
|
662
663
|
cliOpts,
|
|
663
664
|
CLI_HARNESS_ASK_ENTRANCE.id,
|
|
665
|
+
identity.runId,
|
|
664
666
|
),
|
|
665
667
|
...(pieceFeature === undefined ? {} : { input: { text: pieceFeature } }),
|
|
666
668
|
...(base === undefined ? {} : { base }),
|
|
@@ -765,6 +767,7 @@ async function runDevAskFromGoalFile(goalPath: string, opts: HarnessAskSayChildL
|
|
|
765
767
|
{ file: goalPath },
|
|
766
768
|
cliOpts,
|
|
767
769
|
CLI_HARNESS_ASK_ENTRANCE.id,
|
|
770
|
+
identity.runId,
|
|
768
771
|
),
|
|
769
772
|
runId: identity.runId,
|
|
770
773
|
runIdSource: identity.source,
|
|
@@ -868,6 +871,7 @@ async function runDevSayFromWords(
|
|
|
868
871
|
{ file: flowResult.goalFile },
|
|
869
872
|
cliOpts,
|
|
870
873
|
entrance,
|
|
874
|
+
identity.runId,
|
|
871
875
|
),
|
|
872
876
|
runId: identity.runId,
|
|
873
877
|
runIdSource: identity.source,
|
|
@@ -1271,9 +1275,10 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
|
|
|
1271
1275
|
.option('--source-sha <sha>', 'Git source commit')
|
|
1272
1276
|
.option('--source-base <path>', 'Git source plugin base directory')
|
|
1273
1277
|
.option('--bundle-root <dir>', 'Root that extensions["ai.elanous"].bundle paths resolve against (usually the repository root)')
|
|
1278
|
+
.option('--keep-existing', 'Carry over previous index entries whose name has no folder under --dir (unchanged entry and archive)')
|
|
1274
1279
|
.option('--json', 'JSON result')
|
|
1275
1280
|
.action(async (opts: { dir: string; out: string; key: string; keyId: string; marketName: string; displayName: string;
|
|
1276
|
-
sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; json?: boolean }) => {
|
|
1281
|
+
sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; keepExisting?: boolean; json?: boolean }) => {
|
|
1277
1282
|
try {
|
|
1278
1283
|
const sourceOptions = [opts.sourceRepo, opts.sourceSha, opts.sourceBase];
|
|
1279
1284
|
if (sourceOptions.some(Boolean) && !sourceOptions.every(Boolean)) throw new Error('source-repo, source-sha and source-base must be provided together');
|
|
@@ -1282,12 +1287,14 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
|
|
|
1282
1287
|
market: { name: opts.marketName, displayName: opts.displayName },
|
|
1283
1288
|
key: { keyId: opts.keyId, privateKeyPem: readFileSync(opts.key, 'utf8') },
|
|
1284
1289
|
...(opts.bundleRoot ? { bundleRoot: opts.bundleRoot } : {}),
|
|
1290
|
+
...(opts.keepExisting ? { keepExisting: true } : {}),
|
|
1285
1291
|
...(opts.sourceRepo && opts.sourceSha && opts.sourceBase
|
|
1286
1292
|
? { source: { repoUrl: opts.sourceRepo, sha: opts.sourceSha, basePath: opts.sourceBase } } : {}),
|
|
1287
1293
|
});
|
|
1288
1294
|
if (opts.json) await writeStdoutJson(JSON.stringify(result) + '\n');
|
|
1289
1295
|
else {
|
|
1290
|
-
console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}`
|
|
1296
|
+
console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}` +
|
|
1297
|
+
(opts.keepExisting ? `; kept ${result.kept.length}` : ''));
|
|
1291
1298
|
for (const warning of result.warnings) console.warn(`⚠ ${warning.dir}: ${warning.graph} uses recipes it does not ship (${warning.recipes.join(', ')}) — third-party graphs should use their own recipes.yaml or custom nodes`);
|
|
1292
1299
|
}
|
|
1293
1300
|
for (const item of result.skipped) {
|
|
@@ -3477,8 +3484,10 @@ coordCmd.command('event').command('record')
|
|
|
3477
3484
|
.requiredOption('--header <line>', '채널 글 첫 줄')
|
|
3478
3485
|
.option('--url <url>', '채널 코멘트 URL')
|
|
3479
3486
|
.action(async (opts: { seat: string; header: string; url?: string }) => {
|
|
3480
|
-
const { parseCoordHeader, recordCoordEvent } = await import('./context-bus/coord-events.js');
|
|
3481
|
-
|
|
3487
|
+
const { parseCoordHeader, recordCoordEvent, mirrorCoordDecision } = await import('./context-bus/coord-events.js');
|
|
3488
|
+
const input = { ...parseCoordHeader(opts.seat, opts.header), url: opts.url || null, at: new Date().toISOString() };
|
|
3489
|
+
try { recordCoordEvent(input); }
|
|
3490
|
+
finally { mirrorCoordDecision(input); }
|
|
3482
3491
|
});
|
|
3483
3492
|
coordCmd.command('events')
|
|
3484
3493
|
.option('--since <ISO|2h>', '조회 시작', '2h')
|
|
@@ -7317,8 +7326,8 @@ const selfDevCmd = program
|
|
|
7317
7326
|
console.error(`[dev] 외부 backend 미션의 자기 명령은 \`elanous agent-mission mission\` 입니다; 대응 명령: elanous agent-mission mission --backend ${executor.backend}`);
|
|
7318
7327
|
}
|
|
7319
7328
|
let spec = invokedAsDrive
|
|
7320
|
-
? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command))
|
|
7321
|
-
: buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command));
|
|
7329
|
+
? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command), devRunId)
|
|
7330
|
+
: buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command), 'cli-dev-ask', undefined, undefined, devRunId);
|
|
7322
7331
|
if (!opts.json && spec.notice) console.error(spec.notice);
|
|
7323
7332
|
// 역할별 LLM은 현재 프로세스 설정이다. self-mission 구현 자식에 전달되지 않는 조합은 위 검증이 먼저 거부한다.
|
|
7324
7333
|
if (Array.isArray(opts.roleLlm) && opts.roleLlm.length > 0) {
|
|
@@ -7819,6 +7828,26 @@ configCmd
|
|
|
7819
7828
|
if (report.prodStatus !== 'ok') process.exit(1);
|
|
7820
7829
|
});
|
|
7821
7830
|
|
|
7831
|
+
configCmd
|
|
7832
|
+
.command('classify')
|
|
7833
|
+
.description('읽기 전용 — 운영 config 잎의 자리(기본값·기계 사실·비밀·운영 정책)와 덮어쓰기 수 (CFG-CLASSIFY)')
|
|
7834
|
+
.option('--json', '잎 전체를 JSON 으로 출력 (비밀 값 제외)')
|
|
7835
|
+
.action(async (o: { json?: boolean }) => {
|
|
7836
|
+
const { getElanousConfigDir } = await import('./elanous-config-dir.js');
|
|
7837
|
+
const { readUniverseConfig } = await import('./cli/config-drift.js');
|
|
7838
|
+
const { classifyConfig, renderClassify } = await import('./cli/config-classify.js');
|
|
7839
|
+
const { buildUserConfig } = await import('./user-config.js');
|
|
7840
|
+
const prod = readUniverseConfig(getElanousConfigDir(), []);
|
|
7841
|
+
if (prod.status !== 'ok' || !prod.raw) {
|
|
7842
|
+
console.error(`config classify — config 못 읽음(${prod.status}${prod.error ? `: ${prod.error}` : ''})`);
|
|
7843
|
+
process.exitCode = 1;
|
|
7844
|
+
return;
|
|
7845
|
+
}
|
|
7846
|
+
const report = classifyConfig(prod.raw, buildUserConfig('/nonexistent/elanous-config-drift/config.json') as unknown as Record<string, unknown>);
|
|
7847
|
+
if (o.json) await (await import('./cli/stdout-flush.js')).writeStdoutFully(JSON.stringify(report, null, 2));
|
|
7848
|
+
else console.log(renderClassify(report));
|
|
7849
|
+
});
|
|
7850
|
+
|
|
7822
7851
|
configCmd
|
|
7823
7852
|
.command('explain <key>')
|
|
7824
7853
|
.description('읽기 전용 — 운영 값 ⊕ 출처 층 ⊕ 파생 시험 우주별 값 (CFG-STORE1)')
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
5
|
+
import { listChecklist, setItem } from '../release-loop/checklist.js';
|
|
6
|
+
import { withFileLockSync } from '../storage/file-lock.js';
|
|
7
|
+
|
|
8
|
+
export type ConclusionKind = 'evidence' | 'decision' | 'lesson';
|
|
9
|
+
export type ConclusionSource = 'goal' | 'handoff' | 'run-log' | 'channel' | 'pr-discussion';
|
|
10
|
+
export interface ConclusionRecord {
|
|
11
|
+
source: ConclusionSource;
|
|
12
|
+
cell: string;
|
|
13
|
+
kind: ConclusionKind;
|
|
14
|
+
conclusion: string;
|
|
15
|
+
/** Original URL, or a relative path to the original for non-channel sources. */
|
|
16
|
+
ref: string;
|
|
17
|
+
body: string;
|
|
18
|
+
/** The original post's UTC timestamp; required when compressing a channel day. */
|
|
19
|
+
createdAt?: string;
|
|
20
|
+
/** Terminal status is required before a run can be archived. */
|
|
21
|
+
status?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ConclusionBatch {
|
|
24
|
+
version: string;
|
|
25
|
+
day: string;
|
|
26
|
+
records: ConclusionRecord[];
|
|
27
|
+
}
|
|
28
|
+
export interface ConclusionReceipt {
|
|
29
|
+
archive: string;
|
|
30
|
+
lines: string[];
|
|
31
|
+
originalBytes: number;
|
|
32
|
+
conclusionBytes: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const kinds = new Set<ConclusionKind>(['evidence', 'decision', 'lesson']);
|
|
36
|
+
const sources = new Set<ConclusionSource>(['goal', 'handoff', 'run-log', 'channel', 'pr-discussion']);
|
|
37
|
+
const closed = new Set(['done', 'failed', 'cancelled', 'superseded', 'completed', 'abandoned']);
|
|
38
|
+
const validDay = (day: string) => /^\d{4}-\d{2}-\d{2}$/.test(day) && new Date(`${day}T00:00:00Z`).toISOString().slice(0, 10) === day;
|
|
39
|
+
|
|
40
|
+
/** Archive a complete input batch; publish only source-linked, explicitly supplied conclusions to the checklist ledger. */
|
|
41
|
+
export function compressConclusions(batch: ConclusionBatch, root = releaseLedgerRoot()): ConclusionReceipt {
|
|
42
|
+
if (!batch || !Array.isArray(batch.records) || !batch.records.length || !validDay(batch.day)) throw new Error('invalid day or empty records');
|
|
43
|
+
if (!/^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(batch.version)) throw new Error('invalid checklist version');
|
|
44
|
+
if (resolve(root) !== resolve(releaseLedgerRoot())) throw new Error('archive and checklist must share a ledger root');
|
|
45
|
+
const checklist = listChecklist(batch.version);
|
|
46
|
+
const digest = createHash('sha256').update(JSON.stringify(batch)).digest('hex');
|
|
47
|
+
const archiveRef = `knowledge-compress/archive/${digest}.json`;
|
|
48
|
+
const byCell = new Map<string, string[]>();
|
|
49
|
+
for (const [index, record] of batch.records.entries()) {
|
|
50
|
+
if (!record || !sources.has(record.source) || !kinds.has(record.kind) ||
|
|
51
|
+
!checklist.items.some(item => item.id === record.cell) || !record.body?.trim() ||
|
|
52
|
+
!record.conclusion?.trim() || /[\r\n]/.test(record.conclusion) || !record.ref?.trim()) {
|
|
53
|
+
throw new Error('unverified source, conclusion or checklist cell');
|
|
54
|
+
}
|
|
55
|
+
if (record.source === 'channel') {
|
|
56
|
+
if (!/^https:\/\/github\.com\/[^\s]+#issuecomment-\d+$/.test(record.ref) ||
|
|
57
|
+
!record.createdAt || !Number.isFinite(Date.parse(record.createdAt)) ||
|
|
58
|
+
new Date(record.createdAt).toISOString().slice(0, 10) !== batch.day) throw new Error('channel comment must link to the selected UTC day');
|
|
59
|
+
}
|
|
60
|
+
if (record.source === 'run-log' && !closed.has(record.status ?? '')) throw new Error('run is not finished');
|
|
61
|
+
const conclusion = record.conclusion.trim().replace(/\s+/g, ' ');
|
|
62
|
+
if (conclusion.length > 160 || !conclusion) throw new Error('conclusion exceeds one line');
|
|
63
|
+
const ref = record.ref.trim();
|
|
64
|
+
if (/\s|[\r\n]/.test(ref) || (record.source !== 'channel' && !/^(https:\/\/|[^/][^\s]*$)/.test(ref))) throw new Error('invalid source pointer');
|
|
65
|
+
const line = `${record.kind}: ${conclusion} (${record.source === 'channel' ? ref : `${archiveRef}#record-${index + 1}`})`;
|
|
66
|
+
byCell.set(record.cell, [...(byCell.get(record.cell) ?? []), line]);
|
|
67
|
+
}
|
|
68
|
+
const lines = [...byCell].flatMap(([cell, entries]) => [...new Set(entries)].map(line => `${cell} · ${line}`));
|
|
69
|
+
const originalBytes = Buffer.byteLength(JSON.stringify(batch.records));
|
|
70
|
+
const conclusionBytes = Buffer.byteLength(lines.join('\n'));
|
|
71
|
+
if (conclusionBytes * 10 > originalBytes) throw new Error(`conclusions exceed 1/10 of original (${conclusionBytes}/${originalBytes} bytes)`);
|
|
72
|
+
|
|
73
|
+
const dir = join(root, 'knowledge-compress');
|
|
74
|
+
const archive = join(root, archiveRef);
|
|
75
|
+
const lock = join(dir, 'conclusions.lock');
|
|
76
|
+
mkdirSync(dirname(archive), { recursive: true });
|
|
77
|
+
return withFileLockSync(lock, () => {
|
|
78
|
+
if (existsSync(archive)) {
|
|
79
|
+
if (readFileSync(archive, 'utf8') !== JSON.stringify(batch)) throw new Error('archive digest collision');
|
|
80
|
+
} else {
|
|
81
|
+
const temp = `${archive}.${randomUUID()}.tmp`;
|
|
82
|
+
writeFileSync(temp, JSON.stringify(batch), { flag: 'wx', mode: 0o600 });
|
|
83
|
+
renameSync(temp, archive);
|
|
84
|
+
}
|
|
85
|
+
for (const [cell, entries] of byCell) {
|
|
86
|
+
const existing = listChecklist(batch.version).items.find(item => item.id === cell);
|
|
87
|
+
if (!existing) throw new Error(`checklist cell disappeared: ${cell}`);
|
|
88
|
+
const additions = [...new Set(entries)].filter(line => !existing.evidence?.split('\n').includes(line));
|
|
89
|
+
if (additions.length) setItem(batch.version, cell, { evidence: [existing.evidence, ...additions].filter(Boolean).join('\n') }, 'knowledge-compress');
|
|
90
|
+
}
|
|
91
|
+
return { archive: resolve(archive), lines, originalBytes, conclusionBytes };
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -3,6 +3,7 @@ import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
|
|
|
3
3
|
import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
|
|
4
4
|
import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
|
+
import { queryInstalledPack } from './query.js';
|
|
6
7
|
|
|
7
8
|
/** Directive row fields this search reads — kept local because `src/directives/directive-index` is excluded from the public export. */
|
|
8
9
|
export interface DirectiveRow { ts: string; text: string; source_file: string; line_no: number }
|
|
@@ -14,7 +15,7 @@ function openDirectiveIndex(): { search(q: string): DirectiveRow[]; close(): voi
|
|
|
14
15
|
return new mod.DirectiveIndex();
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson';
|
|
18
|
+
export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson' | 'pack';
|
|
18
19
|
export interface KnowHit {
|
|
19
20
|
source: KnowSource;
|
|
20
21
|
id: string;
|
|
@@ -36,6 +37,7 @@ export interface KnowFindDeps {
|
|
|
36
37
|
lessons: (query: string) => LessonRow[];
|
|
37
38
|
versions: () => string[];
|
|
38
39
|
now: () => Date;
|
|
40
|
+
pack?: typeof queryInstalledPack;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
function versions(): string[] {
|
|
@@ -55,6 +57,7 @@ const defaults: KnowFindDeps = {
|
|
|
55
57
|
lessons: (query) => new LessonLedger().find(query),
|
|
56
58
|
versions,
|
|
57
59
|
now: () => new Date(),
|
|
60
|
+
pack: queryInstalledPack,
|
|
58
61
|
};
|
|
59
62
|
|
|
60
63
|
function matches(text: string, words: string[]): boolean {
|
|
@@ -62,7 +65,7 @@ function matches(text: string, words: string[]): boolean {
|
|
|
62
65
|
return words.every((word) => normalized.includes(word));
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowResult {
|
|
68
|
+
export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}, packId?: string): KnowResult {
|
|
66
69
|
const read = { ...defaults, ...deps };
|
|
67
70
|
const words = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
68
71
|
if (!words.length) throw new Error('know: 질문이 필요합니다');
|
|
@@ -106,6 +109,10 @@ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowR
|
|
|
106
109
|
for (const row of read.lessons(query)) rows.push({ source: 'lesson', id: row.id, title: row.incident,
|
|
107
110
|
status: row.status, at: row.updated_at, current: row.occurrence_count > 1, ref: `lesson show ${row.id}` });
|
|
108
111
|
});
|
|
112
|
+
if (packId) attempt('pack', () => {
|
|
113
|
+
for (const card of (read.pack ?? queryInstalledPack)(packId, query)) rows.push({ source: 'pack', id: card.id, title: card.title,
|
|
114
|
+
status: 'installed', at: card.updatedAt, current: true, ref: card.ref });
|
|
115
|
+
});
|
|
109
116
|
rows.sort((a, b) => Number(b.current) - Number(a.current) || (Date.parse(b.at) || 0) - (Date.parse(a.at) || 0)
|
|
110
117
|
|| a.source.localeCompare(b.source) || a.id.localeCompare(b.id));
|
|
111
118
|
debug.log('knowledge.know', 'query', { query, counts: Object.fromEntries((['directive', 'decision', 'checklist', 'lesson'] as const)
|
package/src/knowledge/query.ts
CHANGED
|
@@ -10,6 +10,26 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
|
10
10
|
import { join, relative, sep } from 'node:path';
|
|
11
11
|
import { parseFrontmatter, type ObsidianVault } from '../auto-research/obsidian-bridge.js';
|
|
12
12
|
import type { KnowledgeKind, KnowledgeNote, KnowledgeQueryInput, KnowledgeQueryResult } from './types.js';
|
|
13
|
+
import { PACK_KINDS, parsePackIdString, packIdString } from './kgs/pack.js';
|
|
14
|
+
import { kgsStoreSingleton, type KgsSqliteStore } from './kgs/sqlite-store.js';
|
|
15
|
+
|
|
16
|
+
/** Search only cards belonging to a pack present in the local KGS index. No vault fallback. */
|
|
17
|
+
export function listInstalledPacks(store: Pick<KgsSqliteStore, 'listPacksByKind'> = kgsStoreSingleton()): Array<{ id: string; title: string }> {
|
|
18
|
+
return PACK_KINDS.flatMap(kind => store.listPacksByKind(kind).map(pack => ({ id: packIdString(pack.metadata.id), title: pack.metadata.title })));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function queryInstalledPack(packId: string, question: string, store: Pick<KgsSqliteStore, 'readPack'> = kgsStoreSingleton()): Array<{ id: string; title: string; body: string; ref: string; updatedAt: string }> {
|
|
22
|
+
const id = parsePackIdString(packId);
|
|
23
|
+
if (!id) throw new Error(`invalid pack id: ${packId}`);
|
|
24
|
+
const pack = store.readPack(id.slug, id.version);
|
|
25
|
+
if (!pack) throw new Error(`pack not installed: ${packId}`);
|
|
26
|
+
const words = question.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
27
|
+
return pack.cards.map(card => ({ card, score: words.filter(word => `${card.title} ${card.body}`.toLocaleLowerCase().includes(word)).length }))
|
|
28
|
+
.filter(hit => hit.score > 0 || words.length === 0)
|
|
29
|
+
.sort((a, b) => b.score - a.score || a.card.id.localeCompare(b.card.id))
|
|
30
|
+
.map(({ card }) => ({ id: card.id, title: card.title, body: card.body,
|
|
31
|
+
ref: `${packIdString(pack.metadata.id)}#${card.id}`, updatedAt: card.updatedAt }));
|
|
32
|
+
}
|
|
13
33
|
|
|
14
34
|
const DEFAULT_LIMIT = 20;
|
|
15
35
|
const MAX_LIMIT = 100;
|
package/src/llm.ts
CHANGED
|
@@ -1746,7 +1746,7 @@ export const REASONING_CYCLE: readonly import('./user-config.js').ReasoningLevel
|
|
|
1746
1746
|
|
|
1747
1747
|
/** Does the (provider, model) pair support a "think before answering"
|
|
1748
1748
|
* reasoning surface? Capability-based: gpt-5 family on Codex /
|
|
1749
|
-
* Responses API + Anthropic
|
|
1749
|
+
* Responses API + Anthropic Claude 3.7+ (extended thinking)
|
|
1750
1750
|
* + OpenAI o-series. Other providers / older models return false so
|
|
1751
1751
|
* the HUD pill auto-hides and the wire body skips the reasoning
|
|
1752
1752
|
* field — no user toggle required. */
|
|
@@ -1768,9 +1768,7 @@ export function modelSupportsReasoning(
|
|
|
1768
1768
|
return m.startsWith('o1') || m.startsWith('o3') || m.startsWith('o4');
|
|
1769
1769
|
}
|
|
1770
1770
|
if (provider === 'anthropic') {
|
|
1771
|
-
|
|
1772
|
-
// (opus-4, sonnet-4, haiku-4) plus the 3.7 line.
|
|
1773
|
-
return m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
|
|
1771
|
+
return claudeSupportsThinking(model);
|
|
1774
1772
|
}
|
|
1775
1773
|
if (provider === 'gemini') {
|
|
1776
1774
|
// Gemini 2.5+ family supports thinking budget (thinkingConfig).
|
|
@@ -1926,16 +1924,32 @@ function mapReasoningLevelToAnthropicEffort(
|
|
|
1926
1924
|
* 🩹 그래서 규칙을 이름 목록이 아니라 «수»로 둔다 — 목록은 새 모델마다 늙지만 세대 비교는 안 늙는다.
|
|
1927
1925
|
* ⚠️ 알 수 없는 형태는 legacy 로 둔다(무회귀). 판정 못 하면 종전 동작이 정답이다. */
|
|
1928
1926
|
function usesAdaptiveThinking(model: string | undefined): boolean {
|
|
1927
|
+
const version = claudeVersion(model, true);
|
|
1928
|
+
if (!version) return false;
|
|
1929
|
+
if (version.generation >= 5) return true;
|
|
1930
|
+
return version.generation === 4 && version.minor >= 7;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
/** Shared numeric version parser for adaptive API shape and extended-thinking support. */
|
|
1934
|
+
function claudeVersion(model: string | undefined, requireTerminal = false): { generation: number; minor: number } | undefined {
|
|
1929
1935
|
// 끝에 붙는 릴리스 날짜(`-20251101`)는 버전이 아니다 — 떼고 본다.
|
|
1930
1936
|
const m = (model ?? '').toLowerCase().replace(/-\d{8}$/, '');
|
|
1931
|
-
const v
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
return generation === 4 && minor >= 7;
|
|
1937
|
+
for (const v of m.matchAll(/-(\d+)(?:[-.](\d+))?(?=-|$)/g)) {
|
|
1938
|
+
if (requireTerminal && v.index + v[0].length !== m.length) continue;
|
|
1939
|
+
return { generation: Number(v[1]), minor: v[2] === undefined ? 0 : Number(v[2]) };
|
|
1940
|
+
}
|
|
1941
|
+
return undefined;
|
|
1937
1942
|
}
|
|
1938
1943
|
|
|
1944
|
+
function claudeSupportsThinking(model: string | undefined): boolean {
|
|
1945
|
+
// includes, not startsWith: prefixed ids (`us.anthropic.claude-…`, `anthropic/claude-…`) passed the old substring check.
|
|
1946
|
+
if (!model?.toLowerCase().includes('claude-')) return false;
|
|
1947
|
+
const version = claudeVersion(model);
|
|
1948
|
+
return version !== undefined && (version.generation > 3 || version.generation === 3 && version.minor >= 7);
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
export const _claudeSupportsThinkingForContractTest = claudeSupportsThinking;
|
|
1952
|
+
|
|
1939
1953
|
/** ⛔ 테스트 전용 노출이 아니다 — 모델 계열 계약은 «이름을 가진 판정»이라 회귀를 테스트로 물어야 한다.
|
|
1940
1954
|
* 이 파일 안 두 조립부(:3102·:9591)가 그 값을 쓰고, 그 계약이 틀리면 provider 가 통째로 막힌다. */
|
|
1941
1955
|
export const _usesAdaptiveThinkingForContractTest = usesAdaptiveThinking;
|
|
@@ -3134,13 +3148,9 @@ async function* streamAnthropicEvents(
|
|
|
3134
3148
|
// forcing a single thinking block before all tools). Improves
|
|
3135
3149
|
// multi-tool turn quality — the model can decide what to call
|
|
3136
3150
|
// next based on the prior tool's output thinking. Only supported
|
|
3137
|
-
// on extended-thinking-capable models (
|
|
3138
|
-
const model: string = (body?.model ?? '') as string;
|
|
3139
|
-
const m = model.toLowerCase();
|
|
3140
|
-
const supportsThinking =
|
|
3141
|
-
m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
|
|
3151
|
+
// on extended-thinking-capable models (Claude 3.7+).
|
|
3142
3152
|
const betas: string[] = ['fine-grained-tool-streaming-2025-05-14'];
|
|
3143
|
-
if (
|
|
3153
|
+
if (claudeSupportsThinking(body?.model)) betas.push('interleaved-thinking-2025-05-14');
|
|
3144
3154
|
|
|
3145
3155
|
const response = await fetchApiWithRetry(
|
|
3146
3156
|
ANTHROPIC_API_URL,
|