elanous 0.2.17 → 0.2.18
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/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-aac7332576c4d6fc.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-3143b07a817d1da9.js → page-d92f7ee3994e84c7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-17ba086ef82d339b.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-7eb661ff23af7da2.js → page-d22fd849cc22f104.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-bb742f995c6769e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-6e5c3a867557298b.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-5e77cd1802567c89.js → page-a0eb02638b1e6f31.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-308111abadf35670.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-4187666132fc98c4.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-09328d499162bae4.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-29ff3a8f84f03b6a.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-80ec7401bc83f511.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-fc7d86467291ae38.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-0e2d6985fb42cd2f.js → page-c130b63862437814.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-13e4108999f58d13.js → page-fa244bd146a00ae8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-e8296ee284aa1293.js → page-44d56d6391965a6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-043a9111e74f6b24.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-23a4601ceb7f452e.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-10d958605a77b3bd.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-7a1d85e121065cc8.js → page-e8e1ecaea7cd71d2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-12916918988ac97e.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d8a3347a9d0c3f4a.js → page-2da6f9b8a38c6978.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-1d824510df025d18.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-7f49fe645d5132f7.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-27d71f131acd9320.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-424659bacfb8cdd7.js → page-7bf54c9bb9274af8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-5d4fef9ccd7d406b.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.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 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- 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/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/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +8 -1
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -7
- package/scripts/release-loop/version-node.ts +145 -40
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +73 -1
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +10 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/helper-scan.ts +10 -3
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +133 -15
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +12 -6
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +59 -12
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +39 -6
- package/src/task-agent/judge.ts +31 -5
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +86 -18
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +63 -3
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import type { ChecklistItem } from './checklist.js';
|
|
5
|
+
|
|
6
|
+
export interface Rubric {
|
|
7
|
+
A: number;
|
|
8
|
+
E: number;
|
|
9
|
+
R: number;
|
|
10
|
+
D: number;
|
|
11
|
+
M: number;
|
|
12
|
+
B: number;
|
|
13
|
+
S: number;
|
|
14
|
+
X: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const AXES = ['A', 'E', 'R', 'D', 'M', 'B', 'S', 'X'] as const;
|
|
18
|
+
|
|
19
|
+
/** Read one explicit rubric line; an old score in parentheses is not an axis. */
|
|
20
|
+
export function parseRubric(text: string): Rubric | null {
|
|
21
|
+
for (const line of text.split(/\r?\n/)) {
|
|
22
|
+
const match = /(?:^|\s)루브릭:\s*([^\r\n]*)/.exec(line);
|
|
23
|
+
if (!match) continue;
|
|
24
|
+
const values: Partial<Rubric> = {};
|
|
25
|
+
const body = match[1]!.replace(/\([^)]*\)/g, ' ');
|
|
26
|
+
for (const token of body.trim().split(/\s+/)) {
|
|
27
|
+
const axis = /^([AERDMBSX])([0-3])$/.exec(token);
|
|
28
|
+
if (axis) values[axis[1] as keyof Rubric] = Number(axis[2]);
|
|
29
|
+
}
|
|
30
|
+
if (AXES.every((axis) => values[axis] !== undefined)) return values as Rubric;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function rubricScore(r: Rubric): number {
|
|
36
|
+
return 2 * r.A + 2 * r.E + 2 * r.R + 1.5 * r.D + r.M + r.B - 0.5 * r.S - 0.5 * r.X;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function rubricGrade(score: number): 'P1' | 'P2' | 'P3' | 'P4' {
|
|
40
|
+
if (score >= 11) return 'P1';
|
|
41
|
+
if (score >= 8) return 'P2';
|
|
42
|
+
if (score >= 5) return 'P3';
|
|
43
|
+
return 'P4';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Inspect an existing checklist without initializing, importing, or migrating its ledger. */
|
|
47
|
+
export function readRubricItems(version: string, ledgerRoot: string): Pick<ChecklistItem, 'id' | 'title' | 'evidence' | 'priority'>[] {
|
|
48
|
+
const dbPath = join(ledgerRoot, 'release', 'features.sqlite');
|
|
49
|
+
if (existsSync(dbPath)) {
|
|
50
|
+
const db = new Database(dbPath, { readonly: true, strict: true });
|
|
51
|
+
try {
|
|
52
|
+
const columns = new Set((db.query('PRAGMA table_info(assignments)').all() as Array<{ name: string }>).map((row) => row.name));
|
|
53
|
+
const priority = columns.has('priority') ? 'a.priority' : 'NULL';
|
|
54
|
+
const rows = db.query(`SELECT f.id, COALESCE(a.title_override, f.title) AS title, a.evidence, ${priority} AS priority
|
|
55
|
+
FROM assignments a JOIN features f ON f.id = a.feature_id WHERE a.version = ? ORDER BY a.rowid`)
|
|
56
|
+
.all(version) as Array<{ id: string; title: string; evidence: string | null; priority: ChecklistItem['priority'] | null }>;
|
|
57
|
+
const imported = db.query('SELECT 1 FROM imported_versions WHERE version = ?').get(version);
|
|
58
|
+
if (rows.length || imported) {
|
|
59
|
+
const refs = db.query('SELECT feature_id, ref FROM evidence WHERE version = ? ORDER BY rowid')
|
|
60
|
+
.all(version) as Array<{ feature_id: string; ref: string }>;
|
|
61
|
+
const refsById = new Map<string, string[]>();
|
|
62
|
+
for (const { feature_id, ref } of refs) {
|
|
63
|
+
const list = refsById.get(feature_id) ?? [];
|
|
64
|
+
list.push(ref);
|
|
65
|
+
refsById.set(feature_id, list);
|
|
66
|
+
}
|
|
67
|
+
return rows.map((row) => {
|
|
68
|
+
const evidence = [row.evidence, ...(refsById.get(row.id) ?? [])]
|
|
69
|
+
.filter((value): value is string => value !== null).join('\n');
|
|
70
|
+
return { id: row.id, title: row.title, ...(evidence ? { evidence } : {}), ...(row.priority ? { priority: row.priority } : {}) };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
} finally { db.close(); }
|
|
74
|
+
}
|
|
75
|
+
const path = join(ledgerRoot, 'release', version, 'checklist.json');
|
|
76
|
+
if (!existsSync(path)) return [];
|
|
77
|
+
const legacy = JSON.parse(readFileSync(path, 'utf8')) as { version: string; items: ChecklistItem[] };
|
|
78
|
+
if (legacy.version !== version || !Array.isArray(legacy.items)) throw new Error(`잘못된 체크리스트: ${path}`);
|
|
79
|
+
return legacy.items;
|
|
80
|
+
}
|
|
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { debug } from '../debug/log.js';
|
|
4
4
|
import { openMsgStore, type MsgStore } from '../msg/msg-store.js';
|
|
5
|
-
import { dispatchCeoTask, type CeoCommandDeps } from './ceo-commands.js';
|
|
5
|
+
import { CEO_SEAT_COMMANDS, dispatchCeoTask, type CeoCommandDeps } from './ceo-commands.js';
|
|
6
6
|
import type { SeatId } from './seat-questions.js';
|
|
7
7
|
|
|
8
8
|
export type AskOrigin =
|
|
@@ -23,7 +23,7 @@ function createTable(store: MsgStore): void {
|
|
|
23
23
|
answer TEXT, answered_at INTEGER, asked_at INTEGER
|
|
24
24
|
); CREATE TABLE IF NOT EXISTS seat_ask_outbox (
|
|
25
25
|
id TEXT PRIMARY KEY, origin TEXT NOT NULL, text TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending',
|
|
26
|
-
claim_until INTEGER NOT NULL DEFAULT 0, claim_token TEXT
|
|
26
|
+
claim_until INTEGER NOT NULL DEFAULT 0, claim_token TEXT, sent_at INTEGER
|
|
27
27
|
)`);
|
|
28
28
|
const askColumns = store.db.query('PRAGMA table_info(seat_asks)').all() as Array<{ name: string }>;
|
|
29
29
|
if (!askColumns.some(({ name }) => name === 'answer')) store.db.exec('ALTER TABLE seat_asks ADD COLUMN answer TEXT');
|
|
@@ -32,6 +32,7 @@ function createTable(store: MsgStore): void {
|
|
|
32
32
|
const columns = store.db.query('PRAGMA table_info(seat_ask_outbox)').all() as Array<{ name: string }>;
|
|
33
33
|
if (!columns.some(({ name }) => name === 'claim_until')) store.db.exec('ALTER TABLE seat_ask_outbox ADD COLUMN claim_until INTEGER NOT NULL DEFAULT 0');
|
|
34
34
|
if (!columns.some(({ name }) => name === 'claim_token')) store.db.exec('ALTER TABLE seat_ask_outbox ADD COLUMN claim_token TEXT');
|
|
35
|
+
if (!columns.some(({ name }) => name === 'sent_at')) store.db.exec('ALTER TABLE seat_ask_outbox ADD COLUMN sent_at INTEGER');
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export interface SeatAskShadow {
|
|
@@ -77,10 +78,24 @@ export interface SeatAskDeps {
|
|
|
77
78
|
botId?: string;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
const seatTitle = (seat: SeatId): string => Object.entries(CEO_SEAT_COMMANDS)
|
|
82
|
+
.find(([, id]) => id === seat)![0]!.toUpperCase();
|
|
83
|
+
|
|
84
|
+
/** Persist the return address before delivering through the coordination channel and seat inbox. */
|
|
85
|
+
export function askSeatAs(seat: SeatId, body: string, origin: AskOrigin, command: CeoCommandDeps, deps: SeatAskDeps,
|
|
86
|
+
kind: 'task' | 'question' = 'task'): Promise<string> {
|
|
87
|
+
return recordAndDispatchSeatAsk(seat, body, origin, command, deps, kind);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Preserve the CTO question form and its original receipt. */
|
|
91
|
+
export function askSeat(text: string, origin: AskOrigin, command: CeoCommandDeps, deps: SeatAskDeps): Promise<string> {
|
|
82
92
|
const body = parseSeatAsk(text);
|
|
83
93
|
if (!body) throw new Error('not a CTO ask');
|
|
94
|
+
return askSeatAs('TC', body, origin, command, deps, 'question');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function recordAndDispatchSeatAsk(seat: SeatId, body: string, origin: AskOrigin, command: CeoCommandDeps, deps: SeatAskDeps,
|
|
98
|
+
kind: 'task' | 'question'): Promise<string> {
|
|
84
99
|
const id = randomUUID();
|
|
85
100
|
const timeout = deps.timeoutMs ?? DEFAULT_ASK_TIMEOUT_MS;
|
|
86
101
|
if (!Number.isSafeInteger(timeout) || timeout < 1) throw new Error('invalid seat ask timeout');
|
|
@@ -90,13 +105,14 @@ export async function askSeat(text: string, origin: AskOrigin, command: CeoComma
|
|
|
90
105
|
createTable(store);
|
|
91
106
|
const askedAt = (deps.now ?? Date.now)();
|
|
92
107
|
store.db.query('INSERT INTO seat_asks (id, seat, origin, deadline, timeout_minutes, status, asked_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
|
|
93
|
-
.run(id,
|
|
108
|
+
.run(id, seat, JSON.stringify(origin), askedAt + timeout, minutes, 'pending', askedAt);
|
|
94
109
|
} finally { store.close(); }
|
|
95
110
|
try {
|
|
96
|
-
const result = await (deps.dispatch ?? dispatchCeoTask)('
|
|
111
|
+
const result = await (deps.dispatch ?? dispatchCeoTask)(seat, `${kind === 'question' ? '질문' : '일'}: ${body}\n답장 요청: ${id}\n회신: elanous seat answer ${id} "<답>"`, command,
|
|
97
112
|
{ via: origin.channel, ref: `ask:${id}` });
|
|
98
|
-
debug.log('seat.ask', 'sent', { seat
|
|
99
|
-
|
|
113
|
+
debug.log('seat.ask', 'sent', { seat, id, via: origin.channel, channel: result.channel });
|
|
114
|
+
const title = seatTitle(seat);
|
|
115
|
+
return `${kind === 'question' ? `${title}에게 물었습니다. 답을 기다립니다` : `${title}에게 맡겼습니다. 결과를 이 대화로 돌려드립니다`} (최대 ${minutes}분). 요청: ${id}${result.channel === 'failed' ? ` · 조율 채널 전송 실패: ${result.channelError}` : ''}`;
|
|
100
116
|
} catch (error) {
|
|
101
117
|
const failed = (deps.open ?? openMsgStore)();
|
|
102
118
|
try { createTable(failed); failed.db.query('DELETE FROM seat_asks WHERE id = ?').run(id); }
|
|
@@ -114,10 +130,11 @@ export function answerSeatAsk(id: string, text: string, deps: Pick<SeatAskDeps,
|
|
|
114
130
|
createTable(store);
|
|
115
131
|
const now = (deps.now ?? Date.now)();
|
|
116
132
|
const updated = store.db.query(`UPDATE seat_asks SET answer = ?, answered_at = ?
|
|
117
|
-
WHERE id = ? AND
|
|
133
|
+
WHERE id = ? AND status = 'pending' AND answer IS NULL AND deadline > ?`)
|
|
118
134
|
.run(answer, now, id, now);
|
|
119
135
|
if (updated.changes !== 1) throw new Error(`seat ask not pending or deadline passed: ${id}`);
|
|
120
|
-
|
|
136
|
+
const row = store.db.query('SELECT seat FROM seat_asks WHERE id = ?').get(id) as { seat: string };
|
|
137
|
+
debug.log('seat.ask', 'reply-recorded', { seat: row.seat, id });
|
|
121
138
|
} finally { store.close(); }
|
|
122
139
|
}
|
|
123
140
|
|
|
@@ -133,6 +150,49 @@ export function seatAskReplyInfo(id: string, open: () => MsgStore = openMsgStore
|
|
|
133
150
|
} finally { store.close(); }
|
|
134
151
|
}
|
|
135
152
|
|
|
153
|
+
export interface SeatAskRoundTrip {
|
|
154
|
+
id: string;
|
|
155
|
+
surface: AskOrigin['channel'];
|
|
156
|
+
askedAt: number | null;
|
|
157
|
+
answeredAt: number | null;
|
|
158
|
+
deliveredAt: number | null;
|
|
159
|
+
roundTripMs: number | null;
|
|
160
|
+
verdict: '10분 안' | '10분 넘음' | '미답' | '답 대기' | '전달 대기' | '시각 미상';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Read one row per CTO question, including undelivered and legacy rows. since is an epoch-millisecond lower bound. */
|
|
164
|
+
export function listSeatAskRoundTrips({ since, now = Date.now(), open = openMsgStore }: {
|
|
165
|
+
since: number; now?: number; open?: () => MsgStore;
|
|
166
|
+
}): SeatAskRoundTrip[] {
|
|
167
|
+
if (!Number.isFinite(since) || !Number.isFinite(now)) throw new Error('invalid seat ask time window');
|
|
168
|
+
const store = open();
|
|
169
|
+
try {
|
|
170
|
+
createTable(store);
|
|
171
|
+
const rows = store.db.query(`SELECT a.id, a.origin, a.asked_at, a.answered_at, a.deadline, a.status,
|
|
172
|
+
a.answer IS NOT NULL AS has_answer, o.status AS outbox_status, o.sent_at
|
|
173
|
+
FROM seat_asks a LEFT JOIN seat_ask_outbox o ON o.id = a.id
|
|
174
|
+
WHERE a.seat = 'TC' AND a.status != 'shadow'
|
|
175
|
+
AND COALESCE(a.asked_at, a.deadline - a.timeout_minutes * 60000) >= ?
|
|
176
|
+
ORDER BY COALESCE(a.asked_at, a.deadline - a.timeout_minutes * 60000) DESC, a.id DESC`)
|
|
177
|
+
.all(since) as Array<{ id: string; origin: string; asked_at: number | null; answered_at: number | null;
|
|
178
|
+
deadline: number; status: string; has_answer: number; outbox_status: string | null; sent_at: number | null }>;
|
|
179
|
+
return rows.map(row => {
|
|
180
|
+
const surface = (JSON.parse(row.origin) as AskOrigin).channel;
|
|
181
|
+
const deliveredAt = row.outbox_status === 'sent' ? row.sent_at : null;
|
|
182
|
+
const roundTripMs = row.answered_at === null || row.asked_at === null || deliveredAt === null ? null : deliveredAt - row.asked_at;
|
|
183
|
+
const verdict: SeatAskRoundTrip['verdict'] = row.asked_at === null ? '시각 미상'
|
|
184
|
+
: row.status === 'expired' || (!row.has_answer && row.status === 'pending' && now >= row.deadline) ? '미답'
|
|
185
|
+
: row.status === 'answered' || row.has_answer ? row.outbox_status === 'sent' && deliveredAt === null ? '시각 미상'
|
|
186
|
+
: deliveredAt === null ? '전달 대기'
|
|
187
|
+
: row.answered_at === null || roundTripMs === null ? '시각 미상'
|
|
188
|
+
: roundTripMs <= 600_000 ? '10분 안' : '10분 넘음'
|
|
189
|
+
: '답 대기';
|
|
190
|
+
return { id: row.id, surface, askedAt: row.asked_at, answeredAt: row.answered_at,
|
|
191
|
+
deliveredAt, roundTripMs, verdict };
|
|
192
|
+
});
|
|
193
|
+
} finally { store.close(); }
|
|
194
|
+
}
|
|
195
|
+
|
|
136
196
|
/** Deliver recorded seat answers or expiration notices via the original surface. */
|
|
137
197
|
export async function deliverSeatAnswers(deps: SeatAskDeps & { askId?: string }): Promise<void> {
|
|
138
198
|
const store = (deps.open ?? openMsgStore)();
|
|
@@ -150,9 +210,11 @@ export async function deliverSeatAnswers(deps: SeatAskDeps & { askId?: string })
|
|
|
150
210
|
: reply && Date.parse(reply.created_at) < ask.deadline ? reply.body.slice(id.length + 2).trim() : '';
|
|
151
211
|
const status = answer ? 'answered' : now >= ask.deadline ? 'expired' : null;
|
|
152
212
|
if (!status) return null;
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
213
|
+
const title = seatTitle(ask.seat as SeatId);
|
|
214
|
+
const text = status === 'answered' ? `${title} 답변 (${id}): ${answer}`
|
|
215
|
+
: `${title} 미답 (${id}): 아직 답 없음 (${ask.timeout_minutes}분 경과).`;
|
|
216
|
+
const updated = store.db.query("UPDATE seat_asks SET status = ?, answered_at = COALESCE(answered_at, ?) WHERE id = ? AND status = 'pending'")
|
|
217
|
+
.run(status, status === 'answered' && reply ? Date.parse(reply.created_at) : null, id);
|
|
156
218
|
if (updated.changes !== 1) return null;
|
|
157
219
|
store.db.query('INSERT INTO seat_ask_outbox (id, origin, text) VALUES (?, ?, ?)').run(id, ask.origin, text);
|
|
158
220
|
return status;
|
|
@@ -184,7 +246,8 @@ export async function deliverSeatAnswers(deps: SeatAskDeps & { askId?: string })
|
|
|
184
246
|
if (claimed.changes !== 1) continue;
|
|
185
247
|
try {
|
|
186
248
|
await deps.send(origin, item.text);
|
|
187
|
-
store.db.query("UPDATE seat_ask_outbox SET status = 'sent', claim_token = NULL WHERE id = ? AND claim_token = ?")
|
|
249
|
+
store.db.query("UPDATE seat_ask_outbox SET status = 'sent', sent_at = ?, claim_token = NULL WHERE id = ? AND claim_token = ?")
|
|
250
|
+
.run((deps.now ?? Date.now)(), item.id, token);
|
|
188
251
|
} catch (error) {
|
|
189
252
|
store.db.query('UPDATE seat_ask_outbox SET claim_until = 0, claim_token = NULL WHERE id = ? AND claim_token = ?').run(item.id, token);
|
|
190
253
|
throw error;
|
|
@@ -209,7 +272,7 @@ export function acknowledgeSeatAnswers(clientId: string, ids: string[], open: ()
|
|
|
209
272
|
const store = open();
|
|
210
273
|
try {
|
|
211
274
|
createTable(store);
|
|
212
|
-
const ack = store.db.query("UPDATE seat_ask_outbox SET status = 'sent' WHERE id = ? AND origin = ? AND status = 'pending'");
|
|
213
|
-
for (const id of ids) ack.run(id, JSON.stringify({ channel: 'pwa', clientId }));
|
|
275
|
+
const ack = store.db.query("UPDATE seat_ask_outbox SET status = 'sent', sent_at = ? WHERE id = ? AND origin = ? AND status = 'pending'");
|
|
276
|
+
for (const id of ids) ack.run(Date.now(), id, JSON.stringify({ channel: 'pwa', clientId }));
|
|
214
277
|
} finally { store.close(); }
|
|
215
278
|
}
|
|
@@ -6,7 +6,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { TRACK_AGENT_FORBIDDEN_ACTION_REGEX, universeLaunch } from '../autopilot/track-agent.js';
|
|
8
8
|
import { debug } from '../debug/log.js';
|
|
9
|
-
import { surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
9
|
+
import { openSurfaceEventsDb, recordEvent, surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
10
10
|
import { judgeNeighbor } from '../loops/neighbors.js';
|
|
11
11
|
import { addHarnessQueue, harnessQueueIdForKey, harnessQueueOutcome, listHarnessQueue } from '../harness/harness-queue.js';
|
|
12
12
|
import { isRescueAllowedAction } from '../steward/rescue.js';
|
|
@@ -68,7 +68,7 @@ export type SeatDeps = {
|
|
|
68
68
|
now?: () => Date;
|
|
69
69
|
read?: (path: string) => string;
|
|
70
70
|
run?: (args: string[]) => Promise<string>;
|
|
71
|
-
enqueue?: (seat: string, text: string, root: string, idempotencyKey: string) => Promise<{ id: string }>;
|
|
71
|
+
enqueue?: (seat: string, text: string, root: string, idempotencyKey: string, item: SeatItem) => Promise<{ id: string }>;
|
|
72
72
|
queueOutcome?: (id: string, root: string) => ReturnType<typeof harnessQueueOutcome>;
|
|
73
73
|
queueItems?: (root: string) => ReturnType<typeof listHarnessQueue>;
|
|
74
74
|
queueIdForKey?: (key: string, root: string) => string | undefined;
|
|
@@ -910,11 +910,11 @@ function neighborLastSeen(root: string, isolated: boolean, now: Date, wanted: Re
|
|
|
910
910
|
if (!existsSync(path)) return last;
|
|
911
911
|
const db = new Database(path, { readonly: true });
|
|
912
912
|
try {
|
|
913
|
-
//
|
|
914
|
-
//
|
|
913
|
+
// Read outbound seat context and loop heartbeats across the whole journal,
|
|
914
|
+
// unlike context day's 24h display window, so a genuinely old ACK can be judged absent.
|
|
915
915
|
// Newest first and stop once every wanted seat has an ACK, so a long journal is not read and sorted in full each turn.
|
|
916
916
|
const statement = db.prepare(`SELECT ts, refs FROM events
|
|
917
|
-
WHERE surface IN ('coord:channel', 'context:session') AND direction='outbound' AND ts<=? ORDER BY ts DESC`);
|
|
917
|
+
WHERE surface IN ('coord:channel', 'context:session', 'loop:heartbeat') AND direction='outbound' AND ts<=? ORDER BY ts DESC`);
|
|
918
918
|
try {
|
|
919
919
|
const events = statement.iterate(now.toISOString()) as Iterable<{ ts: string; refs: string | null }>;
|
|
920
920
|
for (const event of events) {
|
|
@@ -996,6 +996,12 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
996
996
|
const directory = dirname(path);
|
|
997
997
|
return withSeatLock(directory, async () => {
|
|
998
998
|
const stateRoot = deps.root ?? effectiveInstanceRoot();
|
|
999
|
+
try {
|
|
1000
|
+
const db = openSurfaceEventsDb(deps.root ? join(stateRoot, 'surface_events.db') : surfaceEventsDbPath());
|
|
1001
|
+
try { recordEvent(db, { surface: 'loop:heartbeat', direction: 'outbound', kind: 'heartbeat',
|
|
1002
|
+
text: `seat loop ${seat} heartbeat`, refs: JSON.stringify({ seat }), ts: now.toISOString() }); }
|
|
1003
|
+
finally { db.close(); }
|
|
1004
|
+
} catch (error) { observe('heartbeat-write-failed', { seat, error: String(error).slice(0, 200) }); }
|
|
999
1005
|
const recorded = readSeatLedger(seat, deps, now);
|
|
1000
1006
|
const ledger = config.mode === 'live-safe' ? reconciledQueueLedger(recorded, stateRoot, deps) : recorded;
|
|
1001
1007
|
let turnError: unknown;
|
|
@@ -1556,7 +1562,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1556
1562
|
}
|
|
1557
1563
|
let queued: { id: string };
|
|
1558
1564
|
try {
|
|
1559
|
-
queued = await (deps.enqueue ?? ((assigned, text, root, idempotencyKey) => addHarnessQueue({ seat: assigned, say: text, idempotencyKey }, { root })))(seat, action.text, stateRoot, queueKey(seat, item));
|
|
1565
|
+
queued = await (deps.enqueue ?? ((assigned, text, root, idempotencyKey) => addHarnessQueue({ seat: assigned, say: text, idempotencyKey }, { root })))(seat, action.text, stateRoot, queueKey(seat, item), item);
|
|
1560
1566
|
} catch (error) {
|
|
1561
1567
|
// Same key, different task body: never adopt the other work. Refuse this turn; the key frees once that item finishes.
|
|
1562
1568
|
if (!/idempotency key collision/.test(String(error))) throw error;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// 그래서 결과를 «세 값»으로 낸다: 발사 / 저작 전 중단 / 전제 검사 중단.
|
|
13
13
|
import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
|
|
14
14
|
import { recordAuthoredGoal } from './authored-goal-trace.js';
|
|
15
|
+
import { findAskPathCandidates, type AskPathCandidateOptions } from './ask-path-candidates.js';
|
|
15
16
|
import { retiredEntranceNotice, type EntranceDeclaration } from './entrance-registry.js';
|
|
16
17
|
import {
|
|
17
18
|
classifyConcurrentAuthoring,
|
|
@@ -164,6 +165,8 @@ export interface AskLaunchFlowDeps {
|
|
|
164
165
|
/** ⛔ 못 얻으면 `null` — 같은 이유. */
|
|
165
166
|
recentAuthoringSamples(): RecentAuthoringSample[] | null;
|
|
166
167
|
authorGoal(args: readonly string[], options: Record<string, unknown>): Promise<AskAuthoredGoal>;
|
|
168
|
+
/** Code-search seam for the one automatic no-target-paths retry. */
|
|
169
|
+
findPathCandidates?: (askText: string, options: AskPathCandidateOptions) => ReturnType<typeof findAskPathCandidates>;
|
|
167
170
|
/** AUTHOR-TRACE seam (tests). */
|
|
168
171
|
recordAuthoredGoal?: typeof recordAuthoredGoal;
|
|
169
172
|
/** Pod launches reserve a host slot before goal authoring begins. */
|
|
@@ -849,7 +852,7 @@ export async function runAskLaunchFlow(
|
|
|
849
852
|
if (launchPreflight === false) return { kind: 'stopped-before-authoring' };
|
|
850
853
|
|
|
851
854
|
await deps.beforeAuthoring?.();
|
|
852
|
-
const authorAndPreflight = async (): Promise<{
|
|
855
|
+
const authorAndPreflight = async (authorAskText: string = input.askText): Promise<{
|
|
853
856
|
readonly goalFile: string;
|
|
854
857
|
readonly decision: ReturnType<typeof decideAskPreflight>;
|
|
855
858
|
}> => {
|
|
@@ -868,7 +871,7 @@ export async function runAskLaunchFlow(
|
|
|
868
871
|
};
|
|
869
872
|
// ⭐ RFC §5 5단계 — 확인된 implement 저작만 활성 구현 그래프에 이름을 잇는다.
|
|
870
873
|
observeFrontNodeEntry('author', { ...frontObservation, provenance: 'authoring-start' });
|
|
871
|
-
const authored = await deps.authorGoal([
|
|
874
|
+
const authored = await deps.authorGoal([authorAskText], {
|
|
872
875
|
cwd: deps.cwd(),
|
|
873
876
|
...(input.groundingCwd === undefined ? {} : { groundingCwd: input.groundingCwd }),
|
|
874
877
|
...(input.authorGrade === undefined ? {} : { authorGrade: input.authorGrade }),
|
|
@@ -1011,8 +1014,26 @@ export async function runAskLaunchFlow(
|
|
|
1011
1014
|
const first = authored;
|
|
1012
1015
|
reauthored = true;
|
|
1013
1016
|
deps.print('[ask] ⑴ ⚠️ 대상 경로를 못 찾아 자동 재저작 한 번을 시도한다');
|
|
1014
|
-
|
|
1017
|
+
let candidates: ReturnType<typeof findAskPathCandidates> = { tokens: [], paths: [] };
|
|
1018
|
+
if (parseAskTargetPathHintsResult(input.askText).labelMissing) {
|
|
1019
|
+
try {
|
|
1020
|
+
candidates = (deps.findPathCandidates ?? findAskPathCandidates)(input.askText, { cwd: deps.cwd() });
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
candidates = { tokens: [], paths: [], error: error instanceof Error ? error.message : String(error) };
|
|
1023
|
+
}
|
|
1024
|
+
deps.log('ask-auto-target-candidates', {
|
|
1025
|
+
tokens: candidates.tokens, paths: candidates.paths,
|
|
1026
|
+
...(candidates.error === undefined ? {} : { error: candidates.error }),
|
|
1027
|
+
}, candidates.error === undefined ? 'info' : 'warn');
|
|
1028
|
+
}
|
|
1029
|
+
if (candidates.paths.length > 0) {
|
|
1030
|
+
deps.print(`[ask] ⑴ 코드에서 후보 경로 ${candidates.paths.length}개를 찾아 넣고 다시 저작한다: ${candidates.paths.join(' · ')}`);
|
|
1031
|
+
}
|
|
1032
|
+
authored = await authorAndPreflight(candidates.paths.length > 0
|
|
1033
|
+
? `대상 경로: ${candidates.paths.join(' · ')}\n${input.askText}`
|
|
1034
|
+
: input.askText);
|
|
1015
1035
|
deps.log('ask-auto-reauthor', {
|
|
1036
|
+
candidatePathCount: candidates.paths.length,
|
|
1016
1037
|
firstGoalFile: first.goalFile,
|
|
1017
1038
|
secondGoalFile: authored.goalFile,
|
|
1018
1039
|
firstPathCount: first.decision.result.paths.length,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { statSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface AskPathCandidateOptions {
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly run?: (command: string, args: readonly string[], options: { cwd: string; encoding: 'utf8' }) => string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type AskPathCandidates =
|
|
11
|
+
| { readonly tokens: string[]; readonly paths: string[]; readonly error?: undefined }
|
|
12
|
+
| { readonly tokens: string[]; readonly paths: string[]; readonly error: string };
|
|
13
|
+
|
|
14
|
+
const STOP_WORDS = new Set(['the', 'and', 'test', 'src', 'scripts', 'file', 'path', 'paths', 'code', 'with', 'from', 'this', 'that', 'into', 'for', 'then', 'when', 'have', 'does', 'should', 'will', 'please', 'target', 'change']);
|
|
15
|
+
const WORD = /[-/]?[A-Za-z][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_.-]+)*/g;
|
|
16
|
+
|
|
17
|
+
function existingFile(fragment: string, cwd: string): string | null {
|
|
18
|
+
const path = fragment.replace(/^\.\//, '');
|
|
19
|
+
if (!path.includes('/') && !/\.[A-Za-z][A-Za-z0-9]*$/.test(path)) return null;
|
|
20
|
+
const absolute = resolve(cwd, path);
|
|
21
|
+
const rel = relative(cwd, absolute);
|
|
22
|
+
if (!rel || rel.startsWith('..') || isAbsolute(rel)) return null;
|
|
23
|
+
try { return statSync(absolute).isFile() ? rel.split('\\').join('/') : null; }
|
|
24
|
+
catch { return null; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Find existing code paths by lexical evidence from an unmodified ask. */
|
|
28
|
+
export function findAskPathCandidates(askText: string, { cwd, run = execFileSync }: AskPathCandidateOptions): AskPathCandidates {
|
|
29
|
+
const scores = new Map<string, Set<string>>();
|
|
30
|
+
const tokens: string[] = [];
|
|
31
|
+
const seen = new Set<string>();
|
|
32
|
+
const chunks = [...askText.matchAll(/`([^`]+)`/g)].map((match) => match[1]!).concat([askText]);
|
|
33
|
+
for (const chunk of chunks) {
|
|
34
|
+
for (const match of chunk.matchAll(WORD)) {
|
|
35
|
+
const raw = match[0];
|
|
36
|
+
const token = raw.replace(/^\//, '');
|
|
37
|
+
const file = existingFile(token, cwd);
|
|
38
|
+
if (file) {
|
|
39
|
+
const hits = scores.get(file) ?? new Set<string>();
|
|
40
|
+
hits.add(token);
|
|
41
|
+
scores.set(file, hits);
|
|
42
|
+
}
|
|
43
|
+
if (STOP_WORDS.has(token.toLowerCase()) || seen.has(token)) continue;
|
|
44
|
+
if (!file && !token.includes('/') && !token.includes('.') && !token.includes('-') && !token.includes('_')
|
|
45
|
+
&& !/[a-z][A-Z]/.test(token) && token.length < 4) continue;
|
|
46
|
+
seen.add(token);
|
|
47
|
+
tokens.push(token);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const token of tokens) {
|
|
51
|
+
let matches: string;
|
|
52
|
+
try {
|
|
53
|
+
matches = run('rg', ['-l', '-F', '--glob', '!*.test.ts', '--', token, 'src', 'scripts'], { cwd, encoding: 'utf8' });
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if ((error as { status?: number }).status === 1) continue; // rg: no matches
|
|
56
|
+
return { tokens, paths: [], error: error instanceof Error ? error.message : String(error) };
|
|
57
|
+
}
|
|
58
|
+
for (const path of matches.split(/\r?\n/).filter(Boolean)) {
|
|
59
|
+
const hits = scores.get(path) ?? new Set<string>();
|
|
60
|
+
hits.add(token);
|
|
61
|
+
scores.set(path, hits);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
tokens,
|
|
66
|
+
paths: [...scores].sort(([a, aHits], [b, bHits]) => bHits.size - aHits.size || a.localeCompare(b, 'en')).slice(0, 5).map(([path]) => path),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -721,6 +721,8 @@ export function buildSelfImplementDevSpec(o: {
|
|
|
721
721
|
ground?: boolean;
|
|
722
722
|
draft: boolean;
|
|
723
723
|
autoMerge?: boolean;
|
|
724
|
+
/** Pod child: stop at merge-ready so the launching host re-gates (and checks the landing freeze) before merging. */
|
|
725
|
+
mergeByHost?: boolean;
|
|
724
726
|
openPr?: boolean;
|
|
725
727
|
autoReview?: boolean;
|
|
726
728
|
autoReviewSource?: DevSelectionSource;
|
|
@@ -755,6 +757,8 @@ export function buildSelfImplementDevSpec(o: {
|
|
|
755
757
|
...(o.runId ? { runId: o.runId } : {}),
|
|
756
758
|
self: {
|
|
757
759
|
draft: o.draft,
|
|
760
|
+
// FREEZE-POD: this flag used to be dropped here, so `self implement --merge-by-host` in a Pod merged by itself (#24478).
|
|
761
|
+
...(o.mergeByHost === true ? { mergeByHost: true } : {}),
|
|
758
762
|
...(o.childLlm ? { childLlm: o.childLlm } : {}),
|
|
759
763
|
...(o.maxWaitSec !== undefined ? { maxWaitSec: o.maxWaitSec } : {}),
|
|
760
764
|
...(o.ground ? { ground: true } : {}),
|
|
@@ -31,7 +31,7 @@ export type DraftDecision = { action: 'close' | 'keep'; reason: string };
|
|
|
31
31
|
|
|
32
32
|
// Labels the sweeper must never close: an approval-waiting state and the human «keep» / release holds (R-PRK3).
|
|
33
33
|
// RELGUARD's release-path hold waits for OP approval — a long wait is not «stale» (TC review #23217).
|
|
34
|
-
const PROTECTED_LABELS: ReadonlySet<string> = new Set<string>([...PR_LABELS
|
|
34
|
+
export const PROTECTED_LABELS: ReadonlySet<string> = new Set<string>([...PR_LABELS
|
|
35
35
|
.filter((label) => label.sweep.action === 'exclude' || (label.sweep.action === 'none' && (label.axis === 'state' || label.axis === 'addon')))
|
|
36
36
|
.map((label) => label.name), RELEASE_PATH_LABEL]);
|
|
37
37
|
|
|
@@ -16,6 +16,7 @@ import { resolveOrchestrateConcurrency } from './orchestrate.js';
|
|
|
16
16
|
import type { FailureClassification, SelfDevGoal, SelfDevJobResult } from './orchestrate.js';
|
|
17
17
|
import { superviseRun, type SupervisorDecision } from './run-supervisor.js';
|
|
18
18
|
import { readDecomposeProposals, applyDecomposeProposals } from './decompose-proposal.js';
|
|
19
|
+
import { createRoundPrTracker, defaultRoundPrAdapters, type RoundPrAdapters } from './round-pr-supersede.js';
|
|
19
20
|
import { createHash } from 'node:crypto';
|
|
20
21
|
import { debug } from '../debug/log.js';
|
|
21
22
|
|
|
@@ -211,6 +212,8 @@ export interface OrchestrateCliDeps {
|
|
|
211
212
|
pipelineDeps?: DevPipelineDeps;
|
|
212
213
|
/** 분해 제안과 골 개정 시도 되채움 조회(테스트 주입). 기본 readDecomposeProposals(원장). */
|
|
213
214
|
readProposals?: typeof readDecomposeProposals;
|
|
215
|
+
/** DRAFT-TRIAGE — gh seams for superseding the previous supervisor round's draft PR (tests). */
|
|
216
|
+
roundPrAdapters?: RoundPrAdapters;
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
/**
|
|
@@ -399,6 +402,23 @@ export async function runSelfOrchestrateCliCommand(
|
|
|
399
402
|
};
|
|
400
403
|
// ⭐ 루프는 «공용»이다(run-supervisor.superviseRun) — 단일 실행도 같은 자를 쓴다.
|
|
401
404
|
// ⛔ 여기가 아는 것은 「어떻게 다시 거나」뿐이고, 「걸까 말까」는 그 자가 안다.
|
|
405
|
+
// DRAFT-TRIAGE — a rework round of the same goal opens a new PR; the previous round's draft is superseded.
|
|
406
|
+
let roundPrTracker: ReturnType<typeof createRoundPrTracker> | undefined;
|
|
407
|
+
let supervisedRound = 0;
|
|
408
|
+
const recordRoundPrs = async (rs: readonly SelfDevJobResult[]): Promise<void> => {
|
|
409
|
+
try {
|
|
410
|
+
// Once a PR is tracked, every round runs — an unfinished supersede is retried even without a new PR.
|
|
411
|
+
if (!roundPrTracker && !rs.some((r) => r.prUrl)) return;
|
|
412
|
+
roundPrTracker ??= createRoundPrTracker({
|
|
413
|
+
runId: process.env.ELANOUS_RUN_ID ?? rs.find((r) => r.runId?.trim())?.runId ?? null,
|
|
414
|
+
adapters: deps.roundPrAdapters ?? await defaultRoundPrAdapters(),
|
|
415
|
+
});
|
|
416
|
+
roundPrTracker.record(rs, supervisedRound);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
try { debug.log('self-dev.supervisor', 'round-pr-record-failed', { round: supervisedRound, error: String(error) }); } catch { /* fail-open */ }
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
await recordRoundPrs(results);
|
|
402
422
|
results = await superviseRun({
|
|
403
423
|
initial: results,
|
|
404
424
|
limits,
|
|
@@ -467,6 +487,8 @@ export async function runSelfOrchestrateCliCommand(
|
|
|
467
487
|
const resumeHold = currentDecision?.needsHuman ?? [];
|
|
468
488
|
const next = await exec(spec, { ...baseRuntime, resumeFrom: [...previous], ...(resumeHold.length > 0 ? { resumeHold } : {}) }, pipelineDeps);
|
|
469
489
|
exitCode = next.exitCode;
|
|
490
|
+
supervisedRound += 1;
|
|
491
|
+
await recordRoundPrs(next.results);
|
|
470
492
|
return next.results;
|
|
471
493
|
},
|
|
472
494
|
});
|