elanous 0.2.15 → 0.2.17
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-33ffcc49ab45123b.js → 1307-aac7332576c4d6fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6877-7c6208705c284124.js → 6877-fe94e76a5841ee9b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-0e2c57fe6add4b2a.js → page-3143b07a817d1da9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-e05a77df7e005b04.js → page-17ba086ef82d339b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-b8c49f6b4fa2df91.js → page-6e5c3a867557298b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-976813c91d7394ea.js → page-9a41d65355fc25b9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-1d4901eb7c2d8822.js → page-41afec0b255ed2bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-72d3e7ff35a8c954.js → page-4187666132fc98c4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-60c7a8e1a4fe62ce.js → page-09328d499162bae4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-2a52b316cb143d9c.js → page-29ff3a8f84f03b6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-c3ce483c8d5db370.js → page-80ec7401bc83f511.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-96f074f6538e8a2c.js → page-fc7d86467291ae38.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-e97be5334d83a9f5.js → layout-de43a186e5ba5683.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-c1343f9499d86f3f.js → page-043a9111e74f6b24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-8739a2e0ae793130.js → not-found-23a4601ceb7f452e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-d23b12bb92242558.js → page-10d958605a77b3bd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-c7fea475488201a8.js → page-a04260b8ccc968d4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-5efbf81124c474ae.js → page-12916918988ac97e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-06ee266953d9c879.js → page-5a64cb5208c6a8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-153c2b3bcc89e8de.js → page-1407ec83ea64c1f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-0d6d23b40263a7ea.js → page-7f49fe645d5132f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-73735b64bfe1ae67.js → page-424659bacfb8cdd7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-f7e631e9b4daf66e.js → page-fc2df261f8a2c7df.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-9817c7f9007466cc.js → page-5d4fef9ccd7d406b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +15 -15
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +34 -15
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +131 -9
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/docs-land-node.ts +69 -10
- package/scripts/release-loop/gate-node.ts +3 -1
- package/scripts/release-loop/publish-node.ts +2 -2
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/vault-note-node.ts +19 -2
- package/scripts/release-loop/version-node.ts +7 -4
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/monthly.ts +97 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-mission/driver.ts +104 -11
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +191 -36
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/cli/autopilot-cli.ts +66 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pr-cli.ts +92 -13
- package/src/cli/provider-cli.ts +14 -0
- package/src/cli/release-cli.ts +11 -8
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +399 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +9 -0
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +178 -12
- package/src/harness/harness-pod-dispatch.ts +36 -55
- package/src/harness/harness-queue.ts +86 -7
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +139 -8
- package/src/harness/helper-scan.ts +13 -2
- package/src/harness/nested-elanous-policy.ts +133 -0
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +16 -2
- package/src/hq/lease.ts +5 -3
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +168 -13
- package/src/intake-plane/digest.ts +85 -28
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/tick.ts +96 -25
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/maturity/tui-slash-availability.ts +9 -0
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/oauth/codex.ts +82 -2
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/release-loop/checklist.ts +10 -5
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +18 -10
- package/src/release-loop/placement.ts +17 -8
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-loop/seat-loop.ts +172 -21
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +6 -5
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +7 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +107 -25
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/orchestrator.ts +157 -8
- package/src/self-implement/run-ledger.ts +4 -0
- package/src/self-implement/seams.ts +44 -15
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/working-backwards.ts +16 -1
- package/src/task-agent/actions.ts +118 -0
- package/src/task-agent/judge.ts +96 -0
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +9 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +33 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +95 -18
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +168 -37
- package/src/task-orchestrator/surfaces/self-implement.ts +2 -0
- package/src/telegram-commands.ts +6 -1
- package/src/telegram-loops-command.ts +21 -14
- package/src/telegram.ts +47 -0
- package/src/user-config.ts +59 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5597e894c02cd33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +0 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +0 -1
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +0 -15
- package/src/launch-head/seat-goal-queue.ts +0 -198
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { RunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
2
|
+
import type { ContextCard } from './index.js';
|
|
3
|
+
|
|
4
|
+
/** An injected, read-only view of the JSONL run ledger. No writer or store mutation is available. */
|
|
5
|
+
export interface TaskLedgerReader {
|
|
6
|
+
readRuns(): readonly { readonly runId: string; readonly entries: readonly RunLedgerEntry[] }[];
|
|
7
|
+
/** Returns the verbatim original ask from the goal document named by start.goalFile. */
|
|
8
|
+
readOriginalAsk?(goalFile: string): string | null;
|
|
9
|
+
/** Returns must-fix text from the review artifact named by reviewed.artifactPath. */
|
|
10
|
+
readReviewMustFix?(artifactPath: string): readonly string[] | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TaskContextCard extends ContextCard {
|
|
14
|
+
readonly kind: 'task';
|
|
15
|
+
readonly originalAsk: string;
|
|
16
|
+
readonly authoredGoal: { readonly path: string; readonly sha256: string } | null;
|
|
17
|
+
readonly runIds: readonly string[];
|
|
18
|
+
readonly mustFix: readonly string[];
|
|
19
|
+
readonly decisionCards: readonly string[];
|
|
20
|
+
readonly nextAction: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const text = (value: unknown): string | null => typeof value === 'string' && value.trim() ? value : null;
|
|
24
|
+
const strings = (value: unknown): string[] | null => Array.isArray(value) && value.every((item) => typeof item === 'string') ? [...value] : null;
|
|
25
|
+
|
|
26
|
+
/** Produce a task card from source events, not from the previous card or wall-clock time. */
|
|
27
|
+
export function buildTaskCard(taskId: string, reader: TaskLedgerReader): TaskContextCard {
|
|
28
|
+
const runs = reader.readRuns().filter((run) => run.entries.some((entry) => entry.event === 'start' && entry.goalId === taskId))
|
|
29
|
+
.map((run) => ({ runId: run.runId, entries: run.entries.filter((entry) => entry.runId === run.runId && (entry.goalId === undefined || entry.goalId === taskId)) }))
|
|
30
|
+
.sort((a, b) => a.runId < b.runId ? -1 : a.runId > b.runId ? 1 : 0);
|
|
31
|
+
if (!runs.length) throw new Error(`no run-ledger start for task: ${taskId}`);
|
|
32
|
+
const events = runs.flatMap(({ entries }) => entries).sort((a, b) =>
|
|
33
|
+
(a.timestamp ?? '').localeCompare(b.timestamp ?? '') || a.runId.localeCompare(b.runId));
|
|
34
|
+
const start = events.find((event) => event.event === 'start')!;
|
|
35
|
+
const goalFile = text(start.data.goalFile);
|
|
36
|
+
const originalAsk = text(start.data.originalAsk)
|
|
37
|
+
?? (goalFile ? reader.readOriginalAsk?.(goalFile) ?? null : null);
|
|
38
|
+
if (!originalAsk) throw new Error(`original ask unavailable for task: ${taskId}`);
|
|
39
|
+
|
|
40
|
+
const authored = [...events].reverse().find((event) => event.event === 'goal-authored'
|
|
41
|
+
&& text(event.data.path) !== null && /^[a-f0-9]{64}$/i.test(text(event.data.sha256) ?? ''));
|
|
42
|
+
const authoredGoal = authored ? { path: text(authored.data.path)!, sha256: text(authored.data.sha256)! } : null;
|
|
43
|
+
const reviews = events.filter((event) => event.event === 'reviewed' && event.data.reviewed === true);
|
|
44
|
+
const review = reviews.at(-1);
|
|
45
|
+
const artifactPath = review ? text(review.data.artifactPath) : null;
|
|
46
|
+
const mustFix = review
|
|
47
|
+
? strings(review.data.mustFix) ?? (artifactPath ? reader.readReviewMustFix?.(artifactPath) ?? null : null)
|
|
48
|
+
: [];
|
|
49
|
+
if (mustFix === null && review?.data.mustFix !== 0) throw new Error(`review must-fix unavailable for task: ${taskId}`);
|
|
50
|
+
const findings = mustFix ?? [];
|
|
51
|
+
const decisionCards = events.filter((event) => event.event === 'decision-card')
|
|
52
|
+
.flatMap((event) => {
|
|
53
|
+
const cardId = text(event.data.cardId);
|
|
54
|
+
return cardId ? [cardId] : [];
|
|
55
|
+
});
|
|
56
|
+
const nextAction = [...events].reverse().map((event) => text(event.data.nextAction)).find((value): value is string => value !== null)
|
|
57
|
+
?? findings[0] ?? null;
|
|
58
|
+
if (!nextAction) throw new Error(`next action unavailable for task: ${taskId}`);
|
|
59
|
+
const updatedAt = events.at(-1)?.timestamp;
|
|
60
|
+
if (!updatedAt) throw new Error(`timestamp unavailable for task: ${taskId}`);
|
|
61
|
+
const pointers = [
|
|
62
|
+
...(goalFile ? [goalFile] : []),
|
|
63
|
+
...(authoredGoal ? [`${authoredGoal.path}#sha256=${authoredGoal.sha256}`] : []),
|
|
64
|
+
...runs.map(({ runId }) => `run:${runId}`),
|
|
65
|
+
...(artifactPath ? [artifactPath] : []),
|
|
66
|
+
...decisionCards,
|
|
67
|
+
];
|
|
68
|
+
return {
|
|
69
|
+
id: `task:${taskId}`, kind: 'task', conclusion: nextAction,
|
|
70
|
+
why: findings.length ? `최근 리뷰 must-fix ${findings.length}개` : '원문과 런 원장에 따른 다음 행동',
|
|
71
|
+
verdict: text(review?.data.verdict) ?? '리뷰 판정 없음',
|
|
72
|
+
remaining: [...findings], pointers,
|
|
73
|
+
source: `run-ledger:${runs.map(({ runId }) => runId).join(',')}`, updatedAt,
|
|
74
|
+
originalAsk, authoredGoal, runIds: runs.map(({ runId }) => runId), mustFix: [...findings], decisionCards, nextAction,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -2442,6 +2442,8 @@ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashComm
|
|
|
2442
2442
|
cwd: () => process.cwd(),
|
|
2443
2443
|
now: () => Date.now(),
|
|
2444
2444
|
isInteractive: () => ctx.surfaceUx !== undefined,
|
|
2445
|
+
selfResolveClarification: (await import('../../self-implement/goal-author-clarification.js')).defaultGoalAuthorSelfResolve,
|
|
2446
|
+
writeGoalDocument: (await import('../../self-implement/goal-document-write.js')).writeGoalDocumentAtomic,
|
|
2445
2447
|
buildPreflightDeps: askIo.buildAskPreflightDeps,
|
|
2446
2448
|
priorBlockSamples: () => askIo.priorBlockSamplesFrom(rows),
|
|
2447
2449
|
recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(rows),
|
|
@@ -93,7 +93,16 @@ function validate(input: RaiseInput, historical = false): RaiseInput {
|
|
|
93
93
|
const c = scqaField(input.scqa.c, 'c');
|
|
94
94
|
const q = input.scqa.q?.trim() ? scqaField(input.scqa.q, 'q') : undefined;
|
|
95
95
|
const a = input.scqa.a?.trim() ? scqaField(input.scqa.a, 'a') : undefined;
|
|
96
|
-
if (q && q === c) throw new Error('SCQA Q repeats C — leave Q empty');
|
|
96
|
+
if (q && q === c && input.raisedBy.agent !== 'seat-loop') throw new Error('SCQA Q repeats C — leave Q empty');
|
|
97
|
+
if (input.raisedBy.agent === 'seat-loop') {
|
|
98
|
+
if (!q || !a || !input.pendingQuestion?.trim()) throw new Error('seat-loop cards require --q, --a and --pending-question');
|
|
99
|
+
if ('skipped' in input.recommendation && !(['money', 'security', 'secret'].includes(input.category)
|
|
100
|
+
|| (['publish', 'irreversible'].includes(input.category)
|
|
101
|
+
&& /공개|게시|발행|출시|publish|release/i.test(`${title} ${input.pendingQuestion} ${input.recommendation.reason}`)
|
|
102
|
+
&& /되돌릴 수 없|철회 불가|복구 불가|irreversib|cannot (?:undo|retract)/i.test(`${input.pendingQuestion} ${input.recommendation.reason}`)))) {
|
|
103
|
+
throw new Error('seat-loop recommendation may only be skipped for money, security or irreversible publication');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
97
106
|
if (!Array.isArray(input.options) || (input.options.length < 2 && !(historical && input.options.length === 0))) throw new Error('at least two options required');
|
|
98
107
|
const options = input.options.map(o => ({ key: single(o.key, 'option key'), label: safe(single(o.label, 'option label')), consequence: safe(single(o.consequence, 'option consequence')) }));
|
|
99
108
|
if (options.some(o => !/^[a-z]$/.test(o.key)) || new Set(options.map(o => o.key)).size !== options.length) throw new Error('option keys must be unique lowercase letters');
|
|
@@ -66,6 +66,7 @@ import { localOcrText } from './telegram-card-followup.js';
|
|
|
66
66
|
import type { runGraph } from './graph-runner/runner.js';
|
|
67
67
|
import { readSlashContextNow, renderTelegramNow } from './context-bus/context-now-surfaces.js';
|
|
68
68
|
import type { ContextNowDeps } from './context-bus/context-now.js';
|
|
69
|
+
import { botUnavailableSlashReply, deriveTuiSlashAvailability, type TuiSlashAvailability } from './maturity/tui-slash-availability.js';
|
|
69
70
|
|
|
70
71
|
const SLASH_FOCUS_TURNS_DEFAULT = 8;
|
|
71
72
|
|
|
@@ -142,6 +143,8 @@ export interface DiscordSelfMessageDeps {
|
|
|
142
143
|
log?: (msg: string) => void;
|
|
143
144
|
cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
|
|
144
145
|
nowDeps?: ContextNowDeps;
|
|
146
|
+
/** Override the TUI slash projection for isolated message-path tests. */
|
|
147
|
+
tuiSlashAvailability?: readonly TuiSlashAvailability[];
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
/** Compose the full discord self+interweave onMessage handler. */
|
|
@@ -399,6 +402,12 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
399
402
|
log(`session cmd: ${ctx.text.trim().slice(0, 40)}`);
|
|
400
403
|
return sessionReply;
|
|
401
404
|
}
|
|
405
|
+
const slashName = /^\/([a-z0-9_-]{1,32})(?:\s|$)/i.exec(ctx.text.trim())?.[1]?.toLowerCase();
|
|
406
|
+
if (slashName) {
|
|
407
|
+
const entry = (deps.tuiSlashAvailability ?? deriveTuiSlashAvailability()).find(({ name }) => name === slashName);
|
|
408
|
+
const unavailable = botUnavailableSlashReply(entry, 'discord');
|
|
409
|
+
if (unavailable) return unavailable;
|
|
410
|
+
}
|
|
402
411
|
log(`◀ ${ctx.userName ?? ctx.userId}: ${ctx.text.slice(0, 80)}${ctx.attachments.length ? ` (+첨부 ${ctx.attachments.length})` : ''}`);
|
|
403
412
|
debug.log('discord.self.turn', 'inbound', { channelId: ctx.channelId, chars: ctx.text.length, attachments: ctx.attachments.length });
|
|
404
413
|
// C2+ — 첨부 기억(사진만 먼저 올리는 패턴 지원). 명령 여부와 무관.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TaskCard } from '../task-cards/card-store.js';
|
|
2
|
+
|
|
3
|
+
export interface SplitCell { id: string; title: string; seat?: string; host?: string }
|
|
4
|
+
|
|
5
|
+
/** FLOW1: only explicitly assigned work may enter a seat's queue. The same proposal is used in shadow and live. */
|
|
6
|
+
export function splitCard(card: TaskCard, _opts: { shadow?: boolean } = {}): SplitCell[] {
|
|
7
|
+
const intake = [...card.sections].reverse().find(section => section.key.startsWith('intake:wish:'));
|
|
8
|
+
let text = card.title;
|
|
9
|
+
if (intake) {
|
|
10
|
+
let body: unknown;
|
|
11
|
+
try { body = JSON.parse(intake.content); } catch { body = intake.content; }
|
|
12
|
+
if (typeof body === 'string') text = body;
|
|
13
|
+
else if (body && typeof body === 'object' && typeof (body as { text?: unknown }).text === 'string') text = (body as { text: string }).text;
|
|
14
|
+
}
|
|
15
|
+
const lines = text.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
16
|
+
const cells: SplitCell[] = lines.map((line, index) => {
|
|
17
|
+
const match = /^(?:[-*]|\d+[.)])?\s*\[(OP|MK|TC|UX)\]\s*(.+)$/u.exec(line);
|
|
18
|
+
if (match) return { id: `${card.id}-${index + 1}`, title: match[2]!.trim(), seat: match[1]! };
|
|
19
|
+
// 담당 없는 행도 칸으로 남긴다 — seat 가 없으니 대기열엔 못 들어가고 tick 이 unplaced-no-owner 로 센다.
|
|
20
|
+
return { id: `${card.id}-${index + 1}`, title: line.replace(/^(?:[-*]|\d+[.)])\s*/u, '') };
|
|
21
|
+
});
|
|
22
|
+
// 의뢰 본문이 있으면 담당이 하나도 없어도 행마다 칸으로 남긴다. 본문 없이 제목뿐일 때만 카드 한 칸.
|
|
23
|
+
return intake || cells.some(cell => cell.seat) ? cells : [{ id: card.id, title: card.title }];
|
|
24
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
1
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
2
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { constants as osConstants, hostname } from 'node:os';
|
|
6
|
-
import { basename, dirname, resolve, sep } from 'node:path';
|
|
6
|
+
import { basename, dirname, join, resolve, sep } from 'node:path';
|
|
7
7
|
import { findGitDir } from '../git-fs/locate.js';
|
|
8
8
|
import { LogStore, logsDbPath } from '../mss/logging/log-store.js';
|
|
9
9
|
import { loadSelfDevRun } from '../self-dev/run-store.js';
|
|
@@ -14,7 +14,7 @@ import { archiveGoals, type GoalArchiveOptions } from '../self-implement/goal-ar
|
|
|
14
14
|
import { detectBursts, formatIncidentBurstWarning, incidentLastLines, readRunExits, recentBurst, recordRunExit } from './harness-incidents.js';
|
|
15
15
|
import { Command, Option } from 'commander';
|
|
16
16
|
import { runGitCommand } from '../git-fs/runner.js';
|
|
17
|
-
import { GOAL_TYPES, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, type GoalType } from '../self-implement/goal-author.js';
|
|
17
|
+
import { GOAL_TYPES, lintGoalFile, parseGoalId, parseGoalType, resolveGoalAuthorGrade, tracedPathReferences, type GoalAuthorGrade, type GoalAuthorGradeSelection, type GoalType } from '../self-implement/goal-author.js';
|
|
18
18
|
import { templateForGoalType } from '../self-implement/graph-templates.js';
|
|
19
19
|
import { listRunLedgers, loadFederatedRunLedger, loadRunLedger, runLedgerDir, type RunLedgerMatch } from '../self-implement/run-ledger.js';
|
|
20
20
|
import { resolveChildLlmEffort, resolveImplementationChildModel } from '../self-dev/dev-cli.js';
|
|
@@ -23,6 +23,7 @@ import { queryRunningRuns } from '../self-implement/running-runs.js';
|
|
|
23
23
|
import { DevPipelineError } from '../self-dev/dev-pipeline.js';
|
|
24
24
|
import { runDraftSweep, sweepFailureReason, type DraftSweepAdapters, type DraftSweepResult, type SweepDraft, type SweepMergedPr, type SweepReviewGate } from '../self-dev/draft-sweep.js';
|
|
25
25
|
import { debug } from '../debug/log.js';
|
|
26
|
+
import { decideNestedElanousLaunch, readNestedElanousDepth } from './nested-elanous-policy.js';
|
|
26
27
|
import { dispatchTask, type DispatchTaskInput, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
|
|
27
28
|
import { launchRequestId, preLaunchGate, type PreLaunchGateDeps } from '../execution-loop/launch-gate.js';
|
|
28
29
|
import { PR_LABELS } from '../github/pr-labels.js';
|
|
@@ -32,7 +33,8 @@ import { installDeliverableVerifyCliCommand, type InstallDeliverableVerifyCliDep
|
|
|
32
33
|
import { getUserConfig } from '../user-config.js';
|
|
33
34
|
import { resolveRepositoryName } from './repository-name.js';
|
|
34
35
|
import { installHarnessCliSinkHook } from './harness-cli-sink.js';
|
|
35
|
-
import { addHarnessQueue, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueSeat } from './harness-queue.js';
|
|
36
|
+
import { addHarnessQueue, HarnessQueueDuplicateError, harnessQueueReceiptPath, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueItem, type QueueSeat } from './harness-queue.js';
|
|
37
|
+
import { writeHarnessQueueReceipt } from './harness-queue-child.js';
|
|
36
38
|
import { parseDoorSince, queryLaunchDoors, renderLaunchDoors, type DoorTable } from './launch-stamp.js';
|
|
37
39
|
import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
|
|
38
40
|
import { runHarnessPlanRfc } from './harness-plan-rfc.js';
|
|
@@ -109,7 +111,12 @@ export interface HarnessAskSayOptions {
|
|
|
109
111
|
supervise?: boolean;
|
|
110
112
|
supervisorSource?: HarnessSupervisorSource;
|
|
111
113
|
goalType?: GoalType;
|
|
114
|
+
authorGrade?: GoalAuthorGrade;
|
|
115
|
+
authorGradeSource?: GoalAuthorGradeSelection['source'];
|
|
112
116
|
correlation?: string;
|
|
117
|
+
/** Depth 0 only. At depth >= 1 this flag is ignored and the launch stays refused. */
|
|
118
|
+
nestedElanous?: 'allow';
|
|
119
|
+
queue?: boolean;
|
|
113
120
|
}
|
|
114
121
|
|
|
115
122
|
export interface ResolvedHarnessSupervisor {
|
|
@@ -174,7 +181,8 @@ function registerHarnessCommonOptions(command: Command): Command {
|
|
|
174
181
|
.option('--merge-by-host', 'Pod: merge-ready 까지 실행하고 병합은 발사 호스트가 재게이트')
|
|
175
182
|
.option('--observe-only', 'elanous: child boot부터 SelfImplement 호출을 기록만 한다')
|
|
176
183
|
.addOption(new Option('--no-supervise', 'self: supervisor 재개를 끔').hideHelp())
|
|
177
|
-
.option('--dry-run', '변경 없이 발사 계획만 출력')
|
|
184
|
+
.option('--dry-run', '변경 없이 발사 계획만 출력')
|
|
185
|
+
.addOption(new Option('--nested-elanous <policy>', '깊이 0만: 중첩 elanous 발사를 허용(allow). 깊이 1 이상은 무시되고 거부가 유지된다').choices(['allow']));
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
type HarnessDryRunOpts = { dryRun?: boolean };
|
|
@@ -259,6 +267,7 @@ function registerHarnessAskSayOptions(command: Command): Command {
|
|
|
259
267
|
.option('--yes', 'git 아닌 프로젝트에 git init 및 첫 커밋을 만들도록 동의')
|
|
260
268
|
.option('--correlation <id>', '요청과 런을 잇는 불투명 correlation 값')
|
|
261
269
|
.addOption(new Option('--seat <seat>', '런 귀속 자리 (OP|TC|MK|UX)').choices(['OP', 'TC', 'MK', 'UX']))
|
|
270
|
+
.option('--no-queue', '비상시 자리별 대기열을 건너뛰고 직접 발사')
|
|
262
271
|
.addOption(new Option('--goal-type <type>', `골 종류 (${GOAL_TYPES.join('|')})`).choices([...GOAL_TYPES]))
|
|
263
272
|
.option('--force-preflight', '전제 검사 막힘을 명시 요청으로 우회(관측에 남음)')
|
|
264
273
|
.option('--force-gate', '같은 골 실행 중 막힘을 명시 요청으로 우회(관측에 남음; 예산 막힘은 우회 불가)')
|
|
@@ -298,10 +307,36 @@ function normalizeHarnessCommonOptions(opts: HarnessAskSayOptions): HarnessAskSa
|
|
|
298
307
|
...(opts.mergeByHost === true ? { mergeByHost: true } : {}),
|
|
299
308
|
...(opts.observeOnly ? { observeOnly: true } : {}),
|
|
300
309
|
...(opts.goalType !== undefined ? { goalType: opts.goalType } : {}),
|
|
310
|
+
...(opts.authorGrade !== undefined ? { authorGrade: opts.authorGrade } : {}),
|
|
311
|
+
...(opts.authorGradeSource !== undefined ? { authorGradeSource: opts.authorGradeSource } : {}),
|
|
301
312
|
...resolveHarnessSupervisor(opts),
|
|
313
|
+
...resolveNestedElanousOption(opts),
|
|
302
314
|
};
|
|
303
315
|
}
|
|
304
316
|
|
|
317
|
+
export class NestedElanousRefused extends Error {
|
|
318
|
+
constructor(readonly depth: number) {
|
|
319
|
+
super(`nested elanous refused (allow-ignored, depth ${depth})`);
|
|
320
|
+
this.name = 'NestedElanousRefused';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Depth 0 may keep `--nested-elanous allow`.
|
|
326
|
+
* Depth >= 1 cannot flip the refusal: the flag is ignored, logged, and the launch stops.
|
|
327
|
+
* No flag means an ordinary launch — unset depth is 0 and nothing is refused here.
|
|
328
|
+
*/
|
|
329
|
+
export function resolveNestedElanousOption(
|
|
330
|
+
opts: Pick<HarnessAskSayOptions, 'nestedElanous'>,
|
|
331
|
+
env: Record<string, string | undefined> = process.env,
|
|
332
|
+
): { nestedElanous?: 'allow' } {
|
|
333
|
+
if (opts.nestedElanous !== 'allow') return {};
|
|
334
|
+
const decision = decideNestedElanousLaunch({ depth: readNestedElanousDepth(env), allow: true });
|
|
335
|
+
if (decision.reason === 'allow-ignored') throw new NestedElanousRefused(decision.depth);
|
|
336
|
+
if (!decision.allowed) return {};
|
|
337
|
+
return { nestedElanous: 'allow' };
|
|
338
|
+
}
|
|
339
|
+
|
|
305
340
|
function normalizeHarnessAskSayOptions(opts: HarnessAskSayChildLlmOptions): HarnessAskSayChildLlmOptions {
|
|
306
341
|
return {
|
|
307
342
|
...normalizeHarnessCommonOptions(opts),
|
|
@@ -561,6 +596,16 @@ function announceLocalChildProvider(opts: unknown): void {
|
|
|
561
596
|
console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (local)`);
|
|
562
597
|
}
|
|
563
598
|
|
|
599
|
+
/** `harness.queue.directSay` (ONEDOOR-2 · OP 10-06 기본 끔): config > env ELANOUS_HARNESS_QUEUE_DIRECT_SAY > false. */
|
|
600
|
+
export function harnessQueueDirectSay(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
601
|
+
try {
|
|
602
|
+
const configured = getUserConfig().harness?.queue?.directSay;
|
|
603
|
+
if (typeof configured === 'boolean') return configured;
|
|
604
|
+
} catch { /* An unreadable config keeps the old direct path. */ }
|
|
605
|
+
const raw = env.ELANOUS_HARNESS_QUEUE_DIRECT_SAY?.trim().toLowerCase();
|
|
606
|
+
return raw === '1' || raw === 'true';
|
|
607
|
+
}
|
|
608
|
+
|
|
564
609
|
/** `harness.authorOnPod` — 명시 true 만 켠다. 읽기 실패는 끔(호스트 저작)으로 둔다. */
|
|
565
610
|
function harnessAuthorOnPod(): boolean {
|
|
566
611
|
try { return getUserConfig().harness?.authorOnPod === true; }
|
|
@@ -605,12 +650,17 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
|
|
|
605
650
|
let output = '';
|
|
606
651
|
let runId: string | undefined;
|
|
607
652
|
const child = opts as HarnessAskSayChildLlmOptions;
|
|
653
|
+
const podAuthorGrade = authorOnPod && entrance === 'cli-harness-say'
|
|
654
|
+
? child.authorGradeSource && child.authorGrade
|
|
655
|
+
? { grade: child.authorGrade, source: child.authorGradeSource }
|
|
656
|
+
: resolveGoalAuthorGrade(child.authorGrade, getUserConfig().harness?.authorGrade)
|
|
657
|
+
: undefined;
|
|
608
658
|
// 10-05 PODPROVIDER: say which child provider the Pod will use when the launch named one.
|
|
609
659
|
if (child.childLlmProvider?.trim()) console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (pod · 자격 없으면 발사 전 거부)`);
|
|
610
660
|
const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(authorOnPod && entrance === 'cli-harness-say' ? { authorOnPod: true } : {}),
|
|
611
661
|
...(child.childLlmProvider?.trim() ? { childLlmProvider: child.childLlmProvider.trim() } : {}),
|
|
612
662
|
...(child.childLlmModel?.trim() ? { childLlmModel: child.childLlmModel.trim() } : {}),
|
|
613
|
-
...(child.childLlmEffort?.trim() ? { childLlmEffort: child.childLlmEffort.trim() } : {}), ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...((opts as HarnessAskSayChildLlmOptions).goalType ? { goalType: (opts as HarnessAskSayChildLlmOptions).goalType } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
|
|
663
|
+
...(child.childLlmEffort?.trim() ? { childLlmEffort: child.childLlmEffort.trim() } : {}), ...(authorOnPod && podAuthorGrade ? { authorGrade: podAuthorGrade.grade, authorGradeSource: podAuthorGrade.source } : {}), ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...((opts as HarnessAskSayChildLlmOptions).goalType ? { goalType: (opts as HarnessAskSayChildLlmOptions).goalType } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
|
|
614
664
|
{ onOutput: (text) => {
|
|
615
665
|
runId ??= harnessPodRunId(output + text);
|
|
616
666
|
output = (output + text).slice(-16_000);
|
|
@@ -688,23 +738,109 @@ async function dispatchHarnessAskSay(
|
|
|
688
738
|
},
|
|
689
739
|
dispatch: (resolved: ResolvedHarnessSubstrate, stampedOpts: HarnessAskSayChildLlmOptions & HarnessDryRunOpts) => Promise<void>,
|
|
690
740
|
gate: PreLaunchGateDeps & { readBudget?: () => Promise<BudgetDecision | 'unknown'> } = {},
|
|
741
|
+
queueDeps?: HarnessQueueDeps,
|
|
742
|
+
immediateLaunch?: HarnessQueueDeps['launch'],
|
|
743
|
+
immediateExit?: () => Promise<number>,
|
|
691
744
|
): Promise<void> {
|
|
692
745
|
await runInjectedHarnessHandler(async () => {
|
|
693
746
|
assertHarnessChildLlmModel(opts);
|
|
694
|
-
const resolved = resolveLaunchSubstrate(opts);
|
|
695
747
|
if (isHarnessDryRun(opts)) {
|
|
748
|
+
resolveLaunchSubstrate(opts);
|
|
696
749
|
printHarnessLaunchDryRun(dryRunPreview);
|
|
697
750
|
return;
|
|
698
751
|
}
|
|
699
752
|
// Resolve the launch directory once; descendants and the checkpoint retain the same identity.
|
|
700
753
|
const queued = process.env.ELANOUS_HARNESS_QUEUE_LAUNCH && process.env.ELANOUS_HARNESS_SEAT;
|
|
701
754
|
const queueSeat = queued === 'OP' || queued === 'TC' || queued === 'MK' || queued === 'UX' ? queued : undefined;
|
|
702
|
-
|
|
755
|
+
let cwdSeatMemo: { seat: QueueSeat | undefined } | undefined;
|
|
756
|
+
const cwdSeat = (): QueueSeat | undefined =>
|
|
757
|
+
(cwdSeatMemo ??= { seat: queueSeatForCwd(process.cwd(), getUserConfig().loops?.orchestrator?.seatTrees) }).seat;
|
|
758
|
+
const assigned = opts.seat ?? queueSeat ?? cwdSeat();
|
|
703
759
|
const inheritedSeat = process.env.ELANOUS_HARNESS_SEAT;
|
|
760
|
+
const directSay = harnessQueueDirectSay();
|
|
761
|
+
const shouldQueue = directSay && opts.queue !== false && !process.env.ELANOUS_HARNESS_QUEUE_LAUNCH
|
|
762
|
+
&& Boolean(opts.seat ?? cwdSeat());
|
|
763
|
+
const resolved = shouldQueue
|
|
764
|
+
? resolveHarnessSubstrate({ flag: opts as HarnessSubstrateOpts, config: getUserConfig(), env: process.env })
|
|
765
|
+
: resolveLaunchSubstrate(opts);
|
|
704
766
|
const podSeat = resolved.substrate === 'pod' && ['OP', 'TC', 'MK', 'UX'].includes(inheritedSeat ?? '')
|
|
705
767
|
? inheritedSeat as QueueSeat : undefined;
|
|
706
768
|
const stampedSeat = resolved.substrate === 'pod' ? opts.seat ?? queueSeat ?? podSeat ?? assigned : assigned;
|
|
707
|
-
|
|
769
|
+
if (directSay && opts.queue === false) {
|
|
770
|
+
try { debug.log('harness.queue', 'bypass', { seat: stampedSeat ?? null, reason: '--no-queue' }); }
|
|
771
|
+
catch { /* Observation must not prevent an emergency launch. */ }
|
|
772
|
+
} else if (shouldQueue && stampedSeat) {
|
|
773
|
+
resolveNestedElanousOption(opts);
|
|
774
|
+
const flags: string[] = [];
|
|
775
|
+
const values: Array<[string, string | undefined]> = [
|
|
776
|
+
['--base', opts.base], ['--target', opts.target], ['--correlation', opts.correlation],
|
|
777
|
+
['--goal-type', opts.goalType], ...(dryRunPreview.entrance === 'cli-harness-say' ? [['--author-grade', opts.authorGradeSource === 'flag' ? opts.authorGrade : undefined] as [string, string | undefined]] : []), ['--child-llm-provider', opts.childLlmProvider],
|
|
778
|
+
['--child-llm-model', opts.childLlmModel], ['--child-llm-effort', opts.childLlmEffort],
|
|
779
|
+
['--pod-pool', (opts as HarnessSubstrateOpts).podPool], ['--pod-memory', (opts as HarnessSubstrateOpts).podMemory],
|
|
780
|
+
['--after', (opts as HarnessSubstrateOpts).after], ['--source', (opts as HarnessSubstrateOpts).source],
|
|
781
|
+
];
|
|
782
|
+
for (const [flag, value] of values) if (value !== undefined) flags.push(flag, value);
|
|
783
|
+
if ((opts as HarnessSubstrateOpts).source !== undefined && resolved.substrate !== 'pod') {
|
|
784
|
+
console.error('`--source` 는 `--substrate pod` 와 함께');
|
|
785
|
+
process.exitCode = 2;
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (resolved.substrate === 'pod' && opts.target !== undefined) {
|
|
789
|
+
console.error('`--target` 은 Pod 경로에서 아직 지원하지 않는다 — 로컬로 돌리거나 `--target` 을 빼라');
|
|
790
|
+
console.error('Pod 로 특정 원천을 주려면 `--source`');
|
|
791
|
+
process.exitCode = 2;
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
for (const [enabled, flag] of [
|
|
795
|
+
[opts.json, '--json'], [opts.yes, '--yes'], [opts.autoMerge === false, '--no-auto-merge'],
|
|
796
|
+
[opts.mergeByHost, '--merge-by-host'], [opts.observeOnly, '--observe-only'],
|
|
797
|
+
[opts.supervise === false, '--no-supervise'], [opts.forcePreflight, '--force-preflight'],
|
|
798
|
+
[opts.forceGate, '--force-gate'],
|
|
799
|
+
] as const) if (enabled) flags.push(flag);
|
|
800
|
+
let row: QueueItem;
|
|
801
|
+
try {
|
|
802
|
+
row = await addHarnessQueue({ seat: stampedSeat, launchCwd: process.cwd(), refuseDuplicate: true,
|
|
803
|
+
...(dryRunPreview.goalPath ? { ask: dryRunPreview.goalPath } : { say: dryRunPreview.input }),
|
|
804
|
+
launchArgs: ['harness', dryRunPreview.goalPath ? 'ask' : 'say', dryRunPreview.goalPath ?? dryRunPreview.input,
|
|
805
|
+
'--seat', stampedSeat, ...((opts as HarnessSubstrateOpts).substrate ? ['--substrate', (opts as HarnessSubstrateOpts).substrate!] : []), ...flags,
|
|
806
|
+
...(opts.nestedElanous ? ['--nested-elanous', opts.nestedElanous] : [])],
|
|
807
|
+
}, queueDeps);
|
|
808
|
+
await queueDeps?.afterEnqueue?.(row);
|
|
809
|
+
} catch (error) {
|
|
810
|
+
if (!(error instanceof HarnessQueueDuplicateError)) throw error;
|
|
811
|
+
console.error(`발사 거절 — ${error.message.replace(/[\r\n]+/g, ' ')}`);
|
|
812
|
+
process.exitCode = 1;
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const result = await tickHarnessQueue({ ...queueDeps, ...(immediateLaunch ? { launch: immediateLaunch } : {}) }, row.id);
|
|
816
|
+
if (result.outcome === 'launched' && result.item?.id === row.id) {
|
|
817
|
+
if (immediateExit) process.exitCode = await immediateExit();
|
|
818
|
+
} else if (result.outcome === 'waiting') {
|
|
819
|
+
const reason = listHarnessQueue(queueDeps).find((item) => item.id === row.id)?.waitingReason ?? result.reason;
|
|
820
|
+
console.log(`대기열에 들어갔다 — ${reason.replace(/[\r\n]+/g, ' ')}`);
|
|
821
|
+
process.exitCode = 0;
|
|
822
|
+
} else if (result.outcome === 'skipped' && result.item?.id === row.id && result.item.status !== 'queued') {
|
|
823
|
+
// Another tick won the race for this row. Only a confirmed launch (pid recorded) is reported as launched;
|
|
824
|
+
// a row still `launching` may yet fail, so the direct call ends non-zero instead of claiming success.
|
|
825
|
+
const confirmed = (result.item.status === 'launched' || result.item.status === 'finished') && result.item.pid !== undefined;
|
|
826
|
+
const log = join(queueDeps?.root ?? effectiveInstanceRoot(), 'harness', `${row.id}.log`);
|
|
827
|
+
if (confirmed) console.log(`다른 대기열 tick 이 이미 발사했다 — ${row.id} (pid ${result.item.pid}) · 출력: ${log}`);
|
|
828
|
+
else console.error(`다른 대기열 tick 이 발사 중이다(확정 전) — ${row.id} (${result.item.status}) · harness queue list 로 확인`);
|
|
829
|
+
try { debug.log('harness.queue', 'raced-launch', { id: row.id, seat: row.seat, status: result.item.status, confirmed }); }
|
|
830
|
+
catch { /* Observation cannot change the launch outcome. */ }
|
|
831
|
+
process.exitCode = confirmed ? 0 : 1;
|
|
832
|
+
} else {
|
|
833
|
+
throw new Error(`harness queue: ${result.reason}`);
|
|
834
|
+
}
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
// Depth >= 1 cannot re-allow. Drop the flag here so the launch sees the refusal, not the raw argv.
|
|
838
|
+
const nested = resolveNestedElanousOption(opts);
|
|
839
|
+
const stampedOpts = {
|
|
840
|
+
...opts,
|
|
841
|
+
...(stampedSeat ? { seat: stampedSeat } : {}),
|
|
842
|
+
...(nested.nestedElanous ? { nestedElanous: nested.nestedElanous } : { nestedElanous: undefined }),
|
|
843
|
+
};
|
|
708
844
|
const launch = async () => {
|
|
709
845
|
const previous = process.env.ELANOUS_HARNESS_SEAT;
|
|
710
846
|
if (stampedSeat) process.env.ELANOUS_HARNESS_SEAT = stampedSeat;
|
|
@@ -2379,6 +2515,30 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2379
2515
|
installHarnessDraftSweepCommand(harnessCmd, deps.draftSweep);
|
|
2380
2516
|
const queue = harnessCmd.command('queue').description('자리별 영속 발사 대기열');
|
|
2381
2517
|
const queueDeps = deps.queue ?? {};
|
|
2518
|
+
let launchedExit: Promise<number> | undefined;
|
|
2519
|
+
const immediateLaunch: NonNullable<HarnessQueueDeps['launch']> = queueDeps.launch ?? (async (item: QueueItem, args: string[], root: string) => {
|
|
2520
|
+
const child = spawn(process.execPath, [join(import.meta.dir, 'harness-queue-child.ts'),
|
|
2521
|
+
harnessQueueReceiptPath(root, item.launchId!), queueDeps.launchCommand ?? join(import.meta.dir, '../../bin/elanous.mjs'),
|
|
2522
|
+
...(resolve(root) === prodInstanceRoot() ? [] : [`--test=${root}`]), ...args], {
|
|
2523
|
+
cwd: item.launchCwd ?? resolve(import.meta.dir, '../..'), stdio: 'inherit',
|
|
2524
|
+
env: { ...process.env, ELANOUS_STATE_DIR: root, ELANOUS_HARNESS_SEAT: item.seat,
|
|
2525
|
+
ELANOUS_HARNESS_QUEUE_LAUNCH: item.launchId! },
|
|
2526
|
+
});
|
|
2527
|
+
// Capture the end at spawn time: a child that already closed (or died by a signal) must not leave the parent waiting.
|
|
2528
|
+
launchedExit = new Promise<number>((done) => child.once('close', (code, signal) => {
|
|
2529
|
+
const signo = signal ? (osConstants.signals as Record<string, number>)[signal] : undefined;
|
|
2530
|
+
done(code ?? (signo ? 128 + signo : 1));
|
|
2531
|
+
}));
|
|
2532
|
+
return await new Promise<number>((done, reject) => {
|
|
2533
|
+
child.once('spawn', () => done(child.pid!));
|
|
2534
|
+
child.once('error', (error) => {
|
|
2535
|
+
try { writeHarnessQueueReceipt(harnessQueueReceiptPath(root, item.launchId!), 'not-started'); }
|
|
2536
|
+
catch { /* An unreadable receipt leaves the reservation indeterminate. */ }
|
|
2537
|
+
reject(error);
|
|
2538
|
+
});
|
|
2539
|
+
});
|
|
2540
|
+
});
|
|
2541
|
+
const immediateExit = async (): Promise<number> => launchedExit ? await launchedExit : 0;
|
|
2382
2542
|
const queueAction = (action: () => Promise<void>): Promise<void> => runInjectedHarnessHandler(action);
|
|
2383
2543
|
queue.command('add').requiredOption('--seat <seat>', 'OP|TC|MK|UX')
|
|
2384
2544
|
.option('--say <sentence>', '원문 문장').option('--ask <goal-path>', '골 문서 경로')
|
|
@@ -2424,7 +2584,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2424
2584
|
}
|
|
2425
2585
|
return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : (announceLocalChildProvider(stampedOpts), ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts)));
|
|
2426
2586
|
},
|
|
2427
|
-
deps.launchGate,
|
|
2587
|
+
deps.launchGate, queueDeps, immediateLaunch, queueDeps.launch ? undefined : immediateExit,
|
|
2428
2588
|
);
|
|
2429
2589
|
});
|
|
2430
2590
|
}
|
|
@@ -2432,9 +2592,15 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2432
2592
|
const say = deps.say;
|
|
2433
2593
|
if (say) {
|
|
2434
2594
|
registerHarnessAskSayOptions(harnessCmd.command('say <sentence...>').description('문장을 받아 구동한다'))
|
|
2595
|
+
.addOption(new Option('--author-grade <grade>', 'say 저작 등급 (full|lite)').choices(['full', 'lite']))
|
|
2435
2596
|
.action(async (sentence: string[], opts: HarnessAskSayChildLlmOptions & HarnessDryRunOpts) => {
|
|
2597
|
+
const grade = resolveGoalAuthorGrade(opts.authorGrade, getUserConfig().harness?.authorGrade);
|
|
2598
|
+
const podSource = process.env.ELANOUS_POD_AUTHOR_ON_POD === '1' && process.env.ELANOUS_POD_AUTHOR_GRADE === grade.grade
|
|
2599
|
+
? process.env.ELANOUS_POD_AUTHOR_GRADE_SOURCE : undefined;
|
|
2600
|
+
const gradedOpts = grade.source === 'default' && podSource === undefined ? opts
|
|
2601
|
+
: { ...opts, authorGrade: grade.grade, authorGradeSource: podSource === 'config' || podSource === 'default' || podSource === 'flag' ? podSource : grade.source };
|
|
2436
2602
|
await dispatchHarnessAskSay(
|
|
2437
|
-
|
|
2603
|
+
gradedOpts,
|
|
2438
2604
|
{
|
|
2439
2605
|
input: sentence.join(' '),
|
|
2440
2606
|
entrance: 'cli-harness-say',
|
|
@@ -2451,7 +2617,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2451
2617
|
? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask, harnessAuthorOnPod())
|
|
2452
2618
|
: (announceLocalChildProvider(stampedOpts), say(sentence, normalizeHarnessAskSayOptions(stampedOpts)));
|
|
2453
2619
|
},
|
|
2454
|
-
deps.launchGate,
|
|
2620
|
+
deps.launchGate, queueDeps, immediateLaunch, queueDeps.launch ? undefined : immediateExit,
|
|
2455
2621
|
);
|
|
2456
2622
|
});
|
|
2457
2623
|
}
|
|
@@ -7,10 +7,14 @@
|
|
|
7
7
|
* 매니페스트가 실은 런 계약(`ELANOUS_RUN_CONTRACT`)으로 자기가 Pod 인 줄 안다(graph-run-contract.ts).
|
|
8
8
|
*/
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
11
|
+
import { hostname } from 'node:os';
|
|
10
12
|
import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
13
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
12
14
|
import { selfDevRunsDir } from '../self-dev/run-store.js';
|
|
13
15
|
import { findGitDir } from '../git-fs/locate.js';
|
|
16
|
+
import { logsDbPath } from '../mss/logging/log-store.js';
|
|
17
|
+
import { POD_REEMIT_LOGS_DB_ENV } from '../task-orchestrator/surfaces/pod-ledger-prod-sink.js';
|
|
14
18
|
import { debug } from '../debug/log.js';
|
|
15
19
|
import { ELANOUS_ENTRY_SCRIPT } from '../self-implement/seams.js';
|
|
16
20
|
import { parsePodSourceSpec } from '../task-orchestrator/surfaces/pod-source-spec.js';
|
|
@@ -18,6 +22,7 @@ import { declaredGoalType, type GoalType } from '../self-implement/goal-author.j
|
|
|
18
22
|
import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
19
23
|
import { HARNESS_RUN_ID_ENV } from './harness-space.js';
|
|
20
24
|
import { podMemoryLimitFor, type PodMemoryTier } from '../task-orchestrator/surfaces/self-implement-pod.js';
|
|
25
|
+
import { oldDoorInternalEnv } from '../self-dev/old-door.js';
|
|
21
26
|
import { selectPodMemoryTier } from './pod-memory-policy.js';
|
|
22
27
|
|
|
23
28
|
export interface HarnessPodDispatchInput {
|
|
@@ -44,44 +49,8 @@ export interface HarnessPodDispatchInput {
|
|
|
44
49
|
readonly childLlmEffort?: string;
|
|
45
50
|
/** `harness.authorOnPod` — say 문장을 Pod 안에서 저작부터 돌린다. 호스트는 영수증만 남긴다. */
|
|
46
51
|
readonly authorOnPod?: boolean;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/** 골 본문에 넣을 문장 — 코드 펜스가 문장을 삼키지 않게 백틱 런을 늘린다. */
|
|
50
|
-
export function fenceVerbatimSentence(sentence: string): { fence: string; body: string } {
|
|
51
|
-
const body = sentence.replace(/\r\n/g, '\n');
|
|
52
|
-
const longest = body.match(/`+/g)?.reduce((n, run) => Math.max(n, run.length), 0) ?? 0;
|
|
53
|
-
return { fence: '`'.repeat(Math.max(3, longest + 1)), body };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Pod 가 문장 그대로 `harness say` 를 돌리게 하는 골. 자격·풀은 기존 Pod 주입을 그대로 쓴다. */
|
|
57
|
-
export function authorOnPodGoal(sentence: string): string {
|
|
58
|
-
const { fence, body } = fenceVerbatimSentence(sentence);
|
|
59
|
-
return [
|
|
60
|
-
'# 골: Pod 안에서 harness say 저작',
|
|
61
|
-
'',
|
|
62
|
-
'- GoalType: implement',
|
|
63
|
-
'- 대상 경로: src/harness/harness-pod-dispatch.ts',
|
|
64
|
-
'',
|
|
65
|
-
'## 할 일',
|
|
66
|
-
'',
|
|
67
|
-
'이 Pod 안에서, 그리고 이 Pod 안에서만, 아래 펜스의 문장을 한 글자도 바꾸지 않고',
|
|
68
|
-
'`elanous harness say --substrate local` 의 인자로 실행한다.',
|
|
69
|
-
'그 한 발이 골 저작·리뷰·게이트·구현·PR 까지 이 Pod 안에서 끝낸다.',
|
|
70
|
-
'호스트에는 발사 영수증과 원장만 남는다. 호스트에서 저작·리뷰·게이트를 지휘하지 않는다.',
|
|
71
|
-
'저작 자격은 이 Job 에 이미 주입된 Pod 자격 규칙을 그대로 쓴다.',
|
|
72
|
-
'',
|
|
73
|
-
'## 문장 (verbatim)',
|
|
74
|
-
'',
|
|
75
|
-
fence,
|
|
76
|
-
body,
|
|
77
|
-
fence,
|
|
78
|
-
'',
|
|
79
|
-
'## 수용 기준',
|
|
80
|
-
'',
|
|
81
|
-
'- 위 펜스 문장만으로 `elanous harness say --substrate local` 한 발이 이 Pod 에서 저작부터 PR 까지 간다.',
|
|
82
|
-
'- 그 명령의 종료 코드가 0 이 아니면 이 골은 실패다.',
|
|
83
|
-
'',
|
|
84
|
-
].join('\n');
|
|
52
|
+
readonly authorGrade?: import('../self-implement/goal-author.js').GoalAuthorGrade;
|
|
53
|
+
readonly authorGradeSource?: import('../self-implement/goal-author.js').GoalAuthorGradeSource;
|
|
85
54
|
}
|
|
86
55
|
|
|
87
56
|
export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: string): string[] {
|
|
@@ -116,7 +85,9 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
116
85
|
return 2;
|
|
117
86
|
}
|
|
118
87
|
}
|
|
119
|
-
const env = { ...process.env };
|
|
88
|
+
const env = { ...process.env, ...oldDoorInternalEnv('self-orchestrate') };
|
|
89
|
+
// POD-OBS(10-06): 자식 orchestrate 는 트리 파생 우주에서 돈다 — Pod 원장 재방출을 «발사한 집»의 logs.db 에도 남기게 경로를 넘긴다.
|
|
90
|
+
env[POD_REEMIT_LOGS_DB_ENV] = logsDbPath();
|
|
120
91
|
let tempDir: string | undefined;
|
|
121
92
|
let goalFile = '';
|
|
122
93
|
let goalText: string;
|
|
@@ -124,21 +95,22 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
124
95
|
goalFile = resolve(cwd, input.input);
|
|
125
96
|
goalText = (deps.readFile ?? ((p) => readFileSync(p, 'utf8')))(goalFile);
|
|
126
97
|
} else if (input.authorOnPod) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
98
|
+
// AUTHOR-POD2 (10-06) — the Pod runs `harness say` on the verbatim sentence itself (podJobManifest
|
|
99
|
+
// authorSentence branch). No wrapper goal and no host-side file under docs/goals: the 10-06 live run
|
|
100
|
+
// showed a wrapper goal is implemented as code by the Pod child instead of being executed.
|
|
101
|
+
goalText = input.input;
|
|
102
|
+
const runsDir = selfDevRunsDir();
|
|
131
103
|
mkdirSync(runsDir, { recursive: true });
|
|
132
|
-
tempDir = mkdtempSync(join(runsDir, '
|
|
133
|
-
goalFile = join(tempDir, '
|
|
134
|
-
writeFileSync(goalFile,
|
|
135
|
-
const runId = env[HARNESS_RUN_ID_ENV]?.trim()
|
|
136
|
-
|
|
104
|
+
tempDir = mkdtempSync(join(runsDir, 'pod-author-'));
|
|
105
|
+
goalFile = join(tempDir, 'sentence.txt');
|
|
106
|
+
writeFileSync(goalFile, input.input, { mode: 0o600 });
|
|
107
|
+
const runId = env[HARNESS_RUN_ID_ENV]?.trim() || `run-${randomUUID()}`;
|
|
108
|
+
env[HARNESS_RUN_ID_ENV] = runId;
|
|
109
|
+
const receipt = { entrance: input.entrance, host: hostname(), runId, authorOnPod: true,
|
|
110
|
+
sentenceChars: input.input.length, sentenceSha256: createHash('sha256').update(input.input).digest('hex') };
|
|
137
111
|
debug.log('harness.substrate', 'author-on-pod-receipt', receipt);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
catch (error) { debug.log('harness.substrate', 'author-on-pod-receipt-unwritten', { reason: error instanceof Error ? error.message : String(error) }); }
|
|
141
|
-
}
|
|
112
|
+
try { appendRunLedgerEntry({ runId, event: 'author-on-pod-receipt', data: receipt }); }
|
|
113
|
+
catch (error) { debug.log('harness.substrate', 'author-on-pod-receipt-unwritten', { reason: error instanceof Error ? error.message : String(error) }); }
|
|
142
114
|
} else {
|
|
143
115
|
const goal = input.input;
|
|
144
116
|
goalText = goal;
|
|
@@ -176,9 +148,18 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
176
148
|
if (input.after) env.ELANOUS_POD_AFTER = input.after;
|
|
177
149
|
if (input.authorOnPod) env.ELANOUS_POD_AUTHOR_ON_POD = '1';
|
|
178
150
|
else delete env.ELANOUS_POD_AUTHOR_ON_POD;
|
|
179
|
-
if (input.
|
|
151
|
+
if (input.authorOnPod && input.authorGrade) {
|
|
152
|
+
env.ELANOUS_POD_AUTHOR_GRADE = input.authorGrade;
|
|
153
|
+
// 출처는 호스트가 알 때만 싣는다 — 모르면 «flag» 로 지어내지 않고 Pod 가 자기가 본 출처를 쓰게 둔다.
|
|
154
|
+
if (input.authorGradeSource) env.ELANOUS_POD_AUTHOR_GRADE_SOURCE = input.authorGradeSource;
|
|
155
|
+
else delete env.ELANOUS_POD_AUTHOR_GRADE_SOURCE;
|
|
156
|
+
} else {
|
|
157
|
+
delete env.ELANOUS_POD_AUTHOR_GRADE;
|
|
158
|
+
delete env.ELANOUS_POD_AUTHOR_GRADE_SOURCE;
|
|
159
|
+
}
|
|
160
|
+
if (input.entrance === 'cli-harness-ask') {
|
|
180
161
|
const root = findGitDir(cwd)?.root;
|
|
181
|
-
const path =
|
|
162
|
+
const path = resolve(cwd, input.input);
|
|
182
163
|
const relativePath = root ? relative(realpathSync(root), realpathSync(path)) : '';
|
|
183
164
|
if (relativePath && relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)) {
|
|
184
165
|
env.ELANOUS_POD_GOAL_DOC = relativePath;
|
|
@@ -208,7 +189,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
208
189
|
child.once('error', () => { cleanup(); resolveStatus(1); });
|
|
209
190
|
child.once('close', (code, signal) => { cleanup(); resolveStatus(code ?? ((signal ?? forwarded) === 'SIGINT' ? 130 : 143)); });
|
|
210
191
|
}));
|
|
211
|
-
const cleanup = () => { if (tempDir
|
|
192
|
+
const cleanup = () => { if (tempDir) rmSync(tempDir, { recursive: true, force: true }); };
|
|
212
193
|
try {
|
|
213
194
|
const status = run(process.execPath, args, env);
|
|
214
195
|
if (typeof status === 'number' || status === null) {
|