elanous 0.2.14 → 0.2.16
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-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.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/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.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/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- 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 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- 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/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- 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 +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- 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 +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- 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 +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- 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/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- 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 +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
decideRetry,
|
|
26
26
|
DoomLoopTracker,
|
|
27
27
|
fingerprintError,
|
|
28
|
+
llmCallOutcomeKind,
|
|
29
|
+
type LlmCallOutcomeKind,
|
|
28
30
|
type RetryAction,
|
|
29
31
|
} from './retry-policy.js';
|
|
30
32
|
|
|
@@ -75,6 +77,35 @@ export interface FetchApiWithRetryOpts {
|
|
|
75
77
|
* row" pattern within a single request. Pass a session-scoped
|
|
76
78
|
* tracker if you want cross-request doom detection. */
|
|
77
79
|
doomTracker?: DoomLoopTracker;
|
|
80
|
+
/** Fallbacks still unused. When > 0, k consecutive overload-class
|
|
81
|
+
* failures throw OverloadFailoverError before another retry sleep. */
|
|
82
|
+
remainingFallbacks?: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Raised when overload repeats k times and a fallback is still unused.
|
|
86
|
+
* The caller switches provider; this is not a retry exhaustion. */
|
|
87
|
+
export class OverloadFailoverError extends Error {
|
|
88
|
+
readonly provider: string;
|
|
89
|
+
readonly status: number;
|
|
90
|
+
readonly kind: LlmCallOutcomeKind;
|
|
91
|
+
readonly streak: number;
|
|
92
|
+
readonly cause: unknown;
|
|
93
|
+
|
|
94
|
+
constructor(opts: {
|
|
95
|
+
provider: string;
|
|
96
|
+
status: number;
|
|
97
|
+
kind: LlmCallOutcomeKind;
|
|
98
|
+
streak: number;
|
|
99
|
+
cause: unknown;
|
|
100
|
+
}) {
|
|
101
|
+
super(`overload failover after ${opts.streak} ${opts.kind} from ${opts.provider} (status ${opts.status})`);
|
|
102
|
+
this.name = 'OverloadFailoverError';
|
|
103
|
+
this.provider = opts.provider;
|
|
104
|
+
this.status = opts.status;
|
|
105
|
+
this.kind = opts.kind;
|
|
106
|
+
this.streak = opts.streak;
|
|
107
|
+
this.cause = opts.cause;
|
|
108
|
+
}
|
|
78
109
|
}
|
|
79
110
|
|
|
80
111
|
/** POST a fetch with the standard retry policy. On 2xx returns the
|
|
@@ -91,13 +122,17 @@ export async function fetchApiWithRetry(
|
|
|
91
122
|
): Promise<Response> {
|
|
92
123
|
const maxAttempts = opts.maxAttempts ?? 4;
|
|
93
124
|
const tracker = opts.doomTracker ?? new DoomLoopTracker();
|
|
125
|
+
const remainingFallbacks = opts.remainingFallbacks ?? 0;
|
|
94
126
|
let attempt = 0;
|
|
127
|
+
let overloadStreak = 0;
|
|
95
128
|
let lastErr: unknown;
|
|
96
129
|
while (attempt < maxAttempts) {
|
|
97
130
|
let response: Response | null = null;
|
|
98
131
|
try {
|
|
99
132
|
response = await fetch(url, init);
|
|
100
133
|
if (response.ok) {
|
|
134
|
+
recordLlmCallOutcome(opts.provider, response.status, 'ok');
|
|
135
|
+
overloadStreak = 0;
|
|
101
136
|
return response;
|
|
102
137
|
}
|
|
103
138
|
// Drain body so the error can carry the API's explanation. We
|
|
@@ -136,6 +171,11 @@ export async function fetchApiWithRetry(
|
|
|
136
171
|
// would couple the helper to the caller's signal.
|
|
137
172
|
if (isAbortLike(err)) throw err;
|
|
138
173
|
|
|
174
|
+
const status = err instanceof ApiHttpError ? err.status : 0;
|
|
175
|
+
const kind = llmCallOutcomeKind(status, err instanceof Error ? err.message : String(err));
|
|
176
|
+
recordLlmCallOutcome(opts.provider, status, kind);
|
|
177
|
+
if (kind === 'overloaded' || kind === '5xx' || kind === 'rate-limit') overloadStreak += 1;
|
|
178
|
+
else overloadStreak = 0;
|
|
139
179
|
const fp = fingerprintError(err, `api:${opts.provider}`);
|
|
140
180
|
const doomStatus = tracker.record(fp);
|
|
141
181
|
const retryAfter = err instanceof ApiHttpError ? err.retryAfter : undefined;
|
|
@@ -143,6 +183,8 @@ export async function fetchApiWithRetry(
|
|
|
143
183
|
attempt,
|
|
144
184
|
doomStatus,
|
|
145
185
|
retryAfter,
|
|
186
|
+
remainingFallbacks,
|
|
187
|
+
overloadStreak,
|
|
146
188
|
});
|
|
147
189
|
debug.log('llm.retry', decisionEvent(decision.action), {
|
|
148
190
|
provider: opts.provider,
|
|
@@ -151,7 +193,19 @@ export async function fetchApiWithRetry(
|
|
|
151
193
|
reason: decision.reason,
|
|
152
194
|
delayMs: decision.delayMs,
|
|
153
195
|
status: err instanceof ApiHttpError ? err.status : undefined,
|
|
196
|
+
kind,
|
|
197
|
+
overloadStreak,
|
|
198
|
+
remainingFallbacks,
|
|
154
199
|
});
|
|
200
|
+
if (decision.action === 'switch-provider') {
|
|
201
|
+
throw new OverloadFailoverError({
|
|
202
|
+
provider: opts.provider,
|
|
203
|
+
status,
|
|
204
|
+
kind,
|
|
205
|
+
streak: overloadStreak,
|
|
206
|
+
cause: err,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
155
209
|
if (decision.action !== 'retry') {
|
|
156
210
|
throw err;
|
|
157
211
|
}
|
|
@@ -172,6 +226,39 @@ export async function fetchApiWithRetry(
|
|
|
172
226
|
throw lastErr ?? new Error(`${opts.errorPrefix}: retry loop terminated unexpectedly`);
|
|
173
227
|
}
|
|
174
228
|
|
|
229
|
+
type OutcomeWriter = (row: { provider: string; status: number; kind: LlmCallOutcomeKind }) => void;
|
|
230
|
+
|
|
231
|
+
let outcomeWriterForTesting: OutcomeWriter | undefined;
|
|
232
|
+
|
|
233
|
+
/** Test seam. Production uses the log store. `undefined` restores it. */
|
|
234
|
+
export function setLlmCallOutcomeWriterForTesting(writer: OutcomeWriter | undefined): void {
|
|
235
|
+
outcomeWriterForTesting = writer;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** The one event launch failover is allowed to read.
|
|
239
|
+
* Category `llm.call`, event `outcome`, payload {provider,status,kind}.
|
|
240
|
+
* Written straight to the log store — `debug.log` drops the row when no
|
|
241
|
+
* sink is armed, which is the normal launch path. */
|
|
242
|
+
export function recordLlmCallOutcome(provider: string, status: number, kind: LlmCallOutcomeKind): void {
|
|
243
|
+
const data = { provider, status, kind };
|
|
244
|
+
// Trace only — a store sink would otherwise write a second `llm.call`/`outcome` row and halve the streak threshold.
|
|
245
|
+
debug.log('llm.call', 'outcome-trace', data);
|
|
246
|
+
if (outcomeWriterForTesting) {
|
|
247
|
+
outcomeWriterForTesting(data);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (process.env.NODE_ENV === 'test') return;
|
|
251
|
+
try {
|
|
252
|
+
const { getDefaultLogStore } = require('../mss/logging/log-store.js') as typeof import('../mss/logging/log-store.js');
|
|
253
|
+
getDefaultLogStore()?.insertBatch([{
|
|
254
|
+
rec: { ts: new Date().toISOString(), category: 'llm.call', event: 'outcome', data },
|
|
255
|
+
surface: 'llm',
|
|
256
|
+
}]);
|
|
257
|
+
} catch {
|
|
258
|
+
// A failed observation must not fail the call. Launch then sees no sample.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
175
262
|
function decisionEvent(action: RetryAction): string {
|
|
176
263
|
// Stable category names for log filtering. The action is the most
|
|
177
264
|
// useful axis ("how many turns hit retry vs abort"); the category
|
|
@@ -364,7 +364,61 @@ export function decideProviderFallback(input: {
|
|
|
364
364
|
return { action: 'advance', nextProvider, category, attempts };
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
|
|
367
|
+
/** Consecutive overload/5xx/rate-limit outcomes that switch the child off codex
|
|
368
|
+
* before the same provider spends its remaining retries. Other errors (400)
|
|
369
|
+
* never count. Recovery is this many later non-overload outcomes. */
|
|
370
|
+
export const OVERLOAD_FAILOVER_STREAK = 3;
|
|
371
|
+
|
|
372
|
+
export type LlmCallOutcomeKind = 'ok' | 'overloaded' | '5xx' | 'rate-limit' | 'other';
|
|
373
|
+
|
|
374
|
+
/** One finished LLM call. Launch failover reads only this shape. */
|
|
375
|
+
export interface LlmCallOutcome {
|
|
376
|
+
readonly provider: string;
|
|
377
|
+
readonly status: number;
|
|
378
|
+
readonly kind: LlmCallOutcomeKind;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const CODEX_CALL_PROVIDERS = new Set(['codex', 'openai-codex']);
|
|
382
|
+
|
|
383
|
+
export function llmCallOutcomeKind(status: number, message = ''): LlmCallOutcomeKind {
|
|
384
|
+
if (status >= 200 && status < 300) return 'ok';
|
|
385
|
+
const msg = message.toLowerCase();
|
|
386
|
+
if (status === 429 || msg.includes('rate limit') || msg.includes('too many requests')) return 'rate-limit';
|
|
387
|
+
if (
|
|
388
|
+
status === 500 || status === 502 || status === 503 || status === 504 ||
|
|
389
|
+
msg.includes('overloaded') || msg.includes('server_busy') || msg.includes('server_error')
|
|
390
|
+
) {
|
|
391
|
+
return status >= 500 ? '5xx' : 'overloaded';
|
|
392
|
+
}
|
|
393
|
+
if (msg.includes('overloaded')) return 'overloaded';
|
|
394
|
+
return 'other';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function isOverloadOutcomeKind(kind: LlmCallOutcomeKind): boolean {
|
|
398
|
+
return kind === 'overloaded' || kind === '5xx' || kind === 'rate-limit';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** True when the newest `limit` codex outcomes are all overload-class.
|
|
402
|
+
* A non-codex row or a non-overload row breaks the streak. Empty input is false. */
|
|
403
|
+
export function codexOverloadStreakReached(
|
|
404
|
+
outcomes: readonly LlmCallOutcome[],
|
|
405
|
+
limit = OVERLOAD_FAILOVER_STREAK,
|
|
406
|
+
): boolean {
|
|
407
|
+
if (limit < 1 || outcomes.length < limit) return false;
|
|
408
|
+
const tail = outcomes.slice(-limit);
|
|
409
|
+
return tail.every((row) => CODEX_CALL_PROVIDERS.has(row.provider) && isOverloadOutcomeKind(row.kind));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** True when the newest `limit` outcomes are successful (any provider). */
|
|
413
|
+
export function overloadRecovered(
|
|
414
|
+
outcomes: readonly LlmCallOutcome[],
|
|
415
|
+
limit = OVERLOAD_FAILOVER_STREAK,
|
|
416
|
+
): boolean {
|
|
417
|
+
if (limit < 1 || outcomes.length < limit) return false;
|
|
418
|
+
return outcomes.slice(-limit).every((row) => row.kind === 'ok');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export type RetryAction = 'retry' | 'abort' | 'ask-user' | 'auto-undo' | 'switch-provider';
|
|
368
422
|
|
|
369
423
|
export interface RetryDecision {
|
|
370
424
|
action: RetryAction;
|
|
@@ -417,6 +471,16 @@ export interface RetryContext {
|
|
|
417
471
|
/** Optional: retry-after header string from a 429 response (ms or
|
|
418
472
|
* seconds or HTTP date). decideRetry parses it. */
|
|
419
473
|
retryAfter?: string;
|
|
474
|
+
/** Configured fallbacks still unused. When > 0, an overload-class
|
|
475
|
+
* failure that has already repeated `overloadStreak` times switches
|
|
476
|
+
* provider instead of spending another retry on the same one.
|
|
477
|
+
* Omitted or 0 keeps the historical retry-until-exhausted behaviour. */
|
|
478
|
+
remainingFallbacks?: number;
|
|
479
|
+
/** Consecutive overload-class failures already seen for this call,
|
|
480
|
+
* including the one under decision. Default threshold is
|
|
481
|
+
* OVERLOAD_FAILOVER_STREAK. */
|
|
482
|
+
overloadStreak?: number;
|
|
483
|
+
overloadStreakLimit?: number;
|
|
420
484
|
}
|
|
421
485
|
|
|
422
486
|
/** Classify an error into a RetryCategory without making an action
|
|
@@ -519,10 +583,42 @@ export function classifyError(err: unknown): RetryCategory {
|
|
|
519
583
|
return 'unknown';
|
|
520
584
|
}
|
|
521
585
|
|
|
586
|
+
function isOverloadSwitchCandidate(msg: string, code: string | null): boolean {
|
|
587
|
+
return (
|
|
588
|
+
code === '429' || code === '500' || code === '502' || code === '503' || code === '504' ||
|
|
589
|
+
msg.includes('rate limit') || msg.includes('too many requests') ||
|
|
590
|
+
msg.includes('overloaded') || msg.includes('server_busy') || msg.includes('server_error')
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function overloadSwitchDecision(ctx: RetryContext): RetryDecision | undefined {
|
|
595
|
+
const remaining = ctx.remainingFallbacks ?? 0;
|
|
596
|
+
if (remaining <= 0) return undefined;
|
|
597
|
+
const limit = ctx.overloadStreakLimit ?? OVERLOAD_FAILOVER_STREAK;
|
|
598
|
+
const streak = ctx.overloadStreak ?? 1;
|
|
599
|
+
if (streak < limit) return undefined;
|
|
600
|
+
return {
|
|
601
|
+
action: 'switch-provider',
|
|
602
|
+
category: 'overloaded',
|
|
603
|
+
reason: `overload streak ${streak} with ${remaining} fallback(s) left; switch before spending retries`,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
522
607
|
/** Pure verdict. Caller sleeps + retries or aborts per the returned
|
|
523
608
|
* action. Exponential backoff baseline: 500ms * 2^attempt, clamped
|
|
524
609
|
* to [500, 30_000]. */
|
|
525
610
|
export function decideRetry(err: unknown, ctx: RetryContext): RetryDecision {
|
|
611
|
+
const msg = extractMessage(err).toLowerCase();
|
|
612
|
+
const code = extractErrorCode(err);
|
|
613
|
+
|
|
614
|
+
// Overload with a fallback still unused switches before the doom
|
|
615
|
+
// window (k === 3) turns the same 503 into auto-undo. A 400 is not
|
|
616
|
+
// a candidate. Doom still wins for every other repeated failure.
|
|
617
|
+
if (isOverloadSwitchCandidate(msg, code)) {
|
|
618
|
+
const switched = overloadSwitchDecision(ctx);
|
|
619
|
+
if (switched) return switched;
|
|
620
|
+
}
|
|
621
|
+
|
|
526
622
|
// Doom-loop always wins — even a technically-retryable error
|
|
527
623
|
// becomes auto-undo after N identical failures. The caller may
|
|
528
624
|
// still degrade to ask-user when plan mode is active or when the
|
|
@@ -535,9 +631,6 @@ export function decideRetry(err: unknown, ctx: RetryContext): RetryDecision {
|
|
|
535
631
|
};
|
|
536
632
|
}
|
|
537
633
|
|
|
538
|
-
const msg = extractMessage(err).toLowerCase();
|
|
539
|
-
const code = extractErrorCode(err);
|
|
540
|
-
|
|
541
634
|
// Context window exhausted — never retry (the next call will also
|
|
542
635
|
// be over-sized). Abort and surface the error to the caller; upstream
|
|
543
636
|
// can trigger compaction.
|
|
@@ -589,8 +682,16 @@ export function decideRetry(err: unknown, ctx: RetryContext): RetryDecision {
|
|
|
589
682
|
};
|
|
590
683
|
}
|
|
591
684
|
|
|
592
|
-
// Overloaded / server busy.
|
|
593
|
-
if (
|
|
685
|
+
// Overloaded / server busy / 5xx.
|
|
686
|
+
if (
|
|
687
|
+
msg.includes('overloaded') ||
|
|
688
|
+
msg.includes('server_busy') ||
|
|
689
|
+
msg.includes('server_error') ||
|
|
690
|
+
code === '500' ||
|
|
691
|
+
code === '502' ||
|
|
692
|
+
code === '503' ||
|
|
693
|
+
code === '504'
|
|
694
|
+
) {
|
|
594
695
|
return {
|
|
595
696
|
action: 'retry',
|
|
596
697
|
delayMs: backoffMs(ctx.attempt),
|
package/src/steward/triage.ts
CHANGED
|
@@ -260,9 +260,56 @@ function skipOverlappingRun(root: string, open: FailureStreak['open'] & { pid?:
|
|
|
260
260
|
}
|
|
261
261
|
debug.log('steward.stage', 'skipped-overlap', { openStage: open.stage, pid: open.pid });
|
|
262
262
|
writeFileSync(skippedPath(root), JSON.stringify({ openStage: open.stage, pid: open.pid }));
|
|
263
|
+
logTickDecision(root, 'skipped', `overlapping ${open.stage} (pid ${open.pid ?? 'unknown'})`);
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
function reportPath(root: string): string { return join(root, 'steward', 'observe.json'); }
|
|
267
|
+
|
|
268
|
+
type TickDecision = { verdict: 'launched' | 'failed'; reason: string; goalId?: string };
|
|
269
|
+
|
|
270
|
+
function logTickDecision(root: string | undefined, verdict: 'launched' | 'skipped' | 'reported' | 'failed', reason: string, goalId?: string): void {
|
|
271
|
+
const runId = graphRunId();
|
|
272
|
+
const marker = root && runId && existsSync(join(root, 'steward')) ? join(root, 'steward', `decision-logged-${runId}.json`) : undefined;
|
|
273
|
+
const decision = { verdict, reason: redactSecretText(reason), ...(goalId ? { goalId } : {}), ...(runId ? { runId } : {}) };
|
|
274
|
+
if (marker) try {
|
|
275
|
+
const previous = JSON.parse(readFileSync(marker, 'utf8')) as { verdict?: string; reason?: string };
|
|
276
|
+
// A replay keeps the first success; a failure replaces a success; a success after a failure replaces the failure.
|
|
277
|
+
if (previous.verdict === decision.verdict && previous.reason === decision.reason) return;
|
|
278
|
+
if (previous.verdict !== 'failed' && verdict !== 'failed') return;
|
|
279
|
+
} catch { /* Missing or stale marker: retry the observation. */ }
|
|
280
|
+
try {
|
|
281
|
+
debug.log('loop.steward', 'decision', decision);
|
|
282
|
+
if (marker) writeFileSync(marker, JSON.stringify(decision));
|
|
283
|
+
} catch { /* Observation must not change the tick outcome. */ }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function tickDecisionPath(root: string, runId: string): string {
|
|
287
|
+
return join(root, 'steward', `decision-${runId}.json`);
|
|
288
|
+
}
|
|
289
|
+
function rememberLaunchDecision(root: string, entry: TickDecision): void {
|
|
290
|
+
const runId = graphRunId();
|
|
291
|
+
if (!runId) return;
|
|
292
|
+
const path = tickDecisionPath(root, runId);
|
|
293
|
+
try {
|
|
294
|
+
let current: TickDecision | undefined;
|
|
295
|
+
try { current = JSON.parse(readFileSync(path, 'utf8')) as TickDecision; }
|
|
296
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return; }
|
|
297
|
+
// Keep the first failure (and its goal/cause), even if a retried schedule later succeeds.
|
|
298
|
+
if (current?.verdict === 'failed' || current?.verdict === entry.verdict) return;
|
|
299
|
+
writeFileSync(path, JSON.stringify(entry));
|
|
300
|
+
} catch { /* An unavailable observation store must not block execution. */ }
|
|
301
|
+
}
|
|
302
|
+
function readLaunchDecision(root: string): TickDecision | undefined {
|
|
303
|
+
const runId = graphRunId();
|
|
304
|
+
if (!runId) return undefined;
|
|
305
|
+
try { return JSON.parse(readFileSync(tickDecisionPath(root, runId), 'utf8')) as TickDecision; }
|
|
306
|
+
catch { return undefined; }
|
|
307
|
+
}
|
|
308
|
+
function logStageFailure(root: string, stage: StewardStage, reason: string): void {
|
|
309
|
+
const runId = graphRunId();
|
|
310
|
+
if (runId) try { rmSync(tickDecisionPath(root, runId), { force: true }); } catch { /* Observation must not change the failure. */ }
|
|
311
|
+
logTickDecision(root, 'failed', `${stage}: ${reason}`);
|
|
312
|
+
}
|
|
266
313
|
function readState(path: string): { issues: Record<string, string>; digestDay?: string } {
|
|
267
314
|
try { return JSON.parse(readFileSync(path, 'utf8')) as ReturnType<typeof readState>; }
|
|
268
315
|
catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { issues: {} }; throw error; }
|
|
@@ -396,10 +443,18 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
396
443
|
const entry = raiseHitl(issue, row, launchDeps);
|
|
397
444
|
recordHitlOnCard(issue, entry, cardDeps);
|
|
398
445
|
}
|
|
446
|
+
let launchDecision: TickDecision | undefined;
|
|
399
447
|
for (const item of plan.launches) {
|
|
400
448
|
const entry = launch(item, launchDeps);
|
|
449
|
+
const decision: TickDecision | undefined = entry.status === 'launched'
|
|
450
|
+
? { verdict: 'launched', reason: 'harness run spawned', goalId: `linear:${item.issue.identifier}` }
|
|
451
|
+
: entry.status === 'failed'
|
|
452
|
+
? { verdict: 'failed', reason: entry.reason ?? 'launch failed', goalId: `linear:${item.issue.identifier}` }
|
|
453
|
+
: undefined;
|
|
454
|
+
if (decision && (!launchDecision || (decision.verdict === 'failed' && launchDecision.verdict !== 'failed'))) launchDecision = decision;
|
|
401
455
|
recordLaunchOnCard(item.issue, entry, cardDeps);
|
|
402
456
|
}
|
|
457
|
+
if (launchDecision) rememberLaunchDecision(root, launchDecision);
|
|
403
458
|
if (settings.mode === 'shadow') {
|
|
404
459
|
const { recordShadowTick } = await import('./readiness.js');
|
|
405
460
|
const tick = recordShadowTick(root, rows, plan, (deps.now ?? (() => new Date()))());
|
|
@@ -459,12 +514,18 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
459
514
|
}
|
|
460
515
|
}
|
|
461
516
|
const day = at.slice(0, 10);
|
|
517
|
+
let digested = false;
|
|
462
518
|
if (state.digestDay !== day) {
|
|
463
519
|
const send = deps.sendDigest ?? sendStewardDigest;
|
|
464
520
|
await send(`스튜어드 ${day}: ${rows.map(row => `${row.issue} ${row.disposition}`).join(' · ')}`);
|
|
465
521
|
state.digestDay = day;
|
|
466
522
|
writeFileSync(path, JSON.stringify(state));
|
|
523
|
+
digested = true;
|
|
467
524
|
}
|
|
525
|
+
const launched = readLaunchDecision(root);
|
|
526
|
+
if (launched) logTickDecision(root, launched.verdict, launched.reason, launched.goalId);
|
|
527
|
+
else if (outcomes.length || digested) logTickDecision(root, 'reported', outcomes.length ? `${outcomes.length} outcome(s) reported` : 'daily digest sent');
|
|
528
|
+
else logTickDecision(root, 'skipped', 'no new outcome or digest');
|
|
468
529
|
}
|
|
469
530
|
outcome = 'ok';
|
|
470
531
|
} finally {
|
|
@@ -513,10 +574,14 @@ async function withStageSlot<T>(root: string, run: () => Promise<T>, onBusy?: ()
|
|
|
513
574
|
|
|
514
575
|
/** The command-node boundary records both normal exits and an unclosed stage from a killed process. */
|
|
515
576
|
export async function runStewardStageCommand(stage: StewardStage, deps: StewardCommandDeps = {}): Promise<0 | 1> {
|
|
516
|
-
if (resolvedStewardSettings(deps.launchSettings).mode === 'off')
|
|
577
|
+
if (resolvedStewardSettings(deps.launchSettings).mode === 'off') {
|
|
578
|
+
if (stage === 'sync') logTickDecision(deps.root ?? effectiveInstanceRoot(), 'skipped', 'steward mode off');
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
517
581
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
518
582
|
if (stage !== 'sync' && existsSync(skippedPath(root))) return 0;
|
|
519
|
-
|
|
583
|
+
try {
|
|
584
|
+
return await withStageSlot(root, async () => {
|
|
520
585
|
const now = deps.now ?? (() => new Date());
|
|
521
586
|
const threshold = stewardSettings().alertAfterFailures ?? 3;
|
|
522
587
|
const alertIfNeeded = async (state: FailureStreak): Promise<void> => {
|
|
@@ -564,10 +629,16 @@ export async function runStewardStageCommand(stage: StewardStage, deps: StewardC
|
|
|
564
629
|
if (!ok) {
|
|
565
630
|
console.error(`steward ${stage}: stage failed — ${reason}`);
|
|
566
631
|
debug.log('steward.stage', 'failed', { stage, reason });
|
|
632
|
+
if (stage === 'sync' || !existsSync(skippedPath(root))) logStageFailure(root, stage, reason);
|
|
567
633
|
return 1;
|
|
568
634
|
}
|
|
569
635
|
return 0;
|
|
570
|
-
|
|
636
|
+
}, stage === 'sync' ? () => { skipOverlappingRun(root, currentOpen(root, stage)); return 0 as const; } : undefined);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
try { if (stage === 'sync' || !existsSync(skippedPath(root))) logStageFailure(root, stage, error instanceof Error ? error.message : String(error)); }
|
|
639
|
+
catch { /* Observation must not change the stage error. */ }
|
|
640
|
+
throw error;
|
|
641
|
+
}
|
|
571
642
|
}
|
|
572
643
|
|
|
573
644
|
export async function runStewardNodeCommand(
|
|
@@ -53,8 +53,26 @@ export async function recordWorkingBackwardsOnCards(
|
|
|
53
53
|
}
|
|
54
54
|
const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
|
|
55
55
|
if (card.sections.some(section => section.key.startsWith('prfaq:')) && card.sections.some(section => section.key.startsWith('manual:'))) continue;
|
|
56
|
-
if (card.sections.some(section => section.key.startsWith('launch:')))
|
|
57
|
-
|
|
56
|
+
if (card.sections.some(section => section.key.startsWith('launch:'))) {
|
|
57
|
+
debug.log('steward.working-backwards', 'skipped-launched-before-draft', { issueId: issue.identifier });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// An invalid draft for the same issue text is not re-asked every tick — each ask is a 70-100 s LLM call (10-05 ELA-29 timed the schedule stage out 3×).
|
|
61
|
+
const invalidKey = `hitl:draft-invalid-${createHash('sha256').update(JSON.stringify({ title: issue.title, body: issue.body ?? '' })).digest('hex').slice(0, 16)}`;
|
|
62
|
+
if (card.sections.some(section => section.key === invalidKey)) {
|
|
63
|
+
debug.log('steward.working-backwards', 'skipped-known-invalid', { issueId: issue.identifier });
|
|
64
|
+
throw new InvalidWorkingBackwardsDraft();
|
|
65
|
+
}
|
|
66
|
+
let draft: WorkingBackwardsDraft;
|
|
67
|
+
try { draft = await draftWorkingBackwards(issue, ask); }
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof InvalidWorkingBackwardsDraft) {
|
|
70
|
+
// The memo is best-effort — failing to write it must not replace the invalid-draft error the caller routes to hitl.
|
|
71
|
+
try { store.appendSection(card.id, { key: invalidKey, owner: 'steward', content: 'working-backwards draft invalid for this issue text — not re-asked until the issue changes' }); }
|
|
72
|
+
catch (memoError) { debug.log('steward.working-backwards', 'invalid-memo-failed', { issueId: issue.identifier, error: memoError instanceof Error ? memoError.message : String(memoError) }); }
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
58
76
|
for (const [section, content] of [['prfaq', draft.prfaq], ['manual', draft.manual]] as const) {
|
|
59
77
|
const key = `${section}:${createHash('sha256').update(content).digest('hex')}`;
|
|
60
78
|
if (!card.sections.some(item => item.key === key)) store.appendSection(card.id, { key, owner: 'steward', content });
|
|
@@ -46,6 +46,31 @@ export function registerCardCommand(program: Command, deps: CardCliDeps = {}): C
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
card.command('close <id>')
|
|
50
|
+
.description('Close a task card with a reason (owner operation)')
|
|
51
|
+
.requiredOption('--reason <text>', 'One-line close reason')
|
|
52
|
+
.action((id: string, options: { reason: string }) => {
|
|
53
|
+
const store = createStore();
|
|
54
|
+
try {
|
|
55
|
+
const reason = options.reason.trim();
|
|
56
|
+
if (!reason || /[\r\n]/.test(options.reason)) throw new Error('Close reason must be one line');
|
|
57
|
+
const existing = store.getCard(id);
|
|
58
|
+
if (!existing) {
|
|
59
|
+
process.stderr.write(`Card not found: ${id}\n`);
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (existing.status === 'closed') {
|
|
64
|
+
write(`이미 닫힘: ${id}\n`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
store.closeCard(id, reason);
|
|
68
|
+
write(`닫힘: ${id}\n`);
|
|
69
|
+
} finally {
|
|
70
|
+
store.close();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
49
74
|
card.command('list')
|
|
50
75
|
.description('List task cards')
|
|
51
76
|
.option('--open', 'Only show open cards')
|
|
@@ -32,7 +32,7 @@ export interface AppendSectionInput {
|
|
|
32
32
|
type CardEvent =
|
|
33
33
|
| { type: 'created'; id: string; goalId: string; title: string; createdAt: string }
|
|
34
34
|
| { type: 'section'; key: string; owner: string; content: string; createdAt: string }
|
|
35
|
-
| { type: 'closed'; createdAt: string };
|
|
35
|
+
| { type: 'closed'; createdAt: string; reason?: string };
|
|
36
36
|
|
|
37
37
|
/** RFC §3 (내부 문서 `RFC-execution-and-landing-loop-agents-parallel-first-2026-09-29`) — each card section has one owner.
|
|
38
38
|
* A key is `<section>` or `<section>:<idempotency key>`: the same section can be appended many times over a
|
|
@@ -197,13 +197,15 @@ export class CardStore {
|
|
|
197
197
|
}).immediate();
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
closeCard(id: string): TaskCard {
|
|
200
|
+
closeCard(id: string, reason?: string): TaskCard {
|
|
201
|
+
if (reason !== undefined && !reason.trim()) throw new Error('Close reason is required');
|
|
201
202
|
return this.db.transaction(() => {
|
|
202
203
|
this.reconcile();
|
|
203
204
|
const card = this.readCard(id);
|
|
204
205
|
if (!card) throw new Error(`Card not found: ${id}`);
|
|
205
206
|
if (card.status === 'closed') return card;
|
|
206
|
-
this.append(id, { type: 'closed', createdAt: new Date().toISOString()
|
|
207
|
+
this.append(id, { type: 'closed', createdAt: new Date().toISOString(),
|
|
208
|
+
...(reason === undefined ? {} : { reason: redactSecrets(reason.trim()) }) });
|
|
207
209
|
return { ...card, status: 'closed' as const };
|
|
208
210
|
}).immediate();
|
|
209
211
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { lstatSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { gunzipSync } from 'node:zlib';
|
|
4
4
|
import { debug } from '../../debug/log.js';
|
|
5
5
|
import { getDefaultLogStore, type LogStore } from '../../mss/logging/log-store.js';
|
|
6
|
-
import type
|
|
6
|
+
import { fromJsonl, type LogRecord } from '../../mss/logging/record.js';
|
|
7
7
|
|
|
8
8
|
const FILE_LIMIT = 5 * 1024 * 1024;
|
|
9
9
|
const TOTAL_LIMIT = 20 * 1024 * 1024;
|
|
@@ -83,50 +83,58 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
|
|
|
83
83
|
|
|
84
84
|
const POD_LOGS_PATH = 'pod-logs/logs.jsonl';
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
category: 'harness.decision',
|
|
106
|
-
event: row.event,
|
|
107
|
-
data,
|
|
108
|
-
...(typeof row.level === 'string' ? { level: row.level as LogRecord['level'] } : {}),
|
|
109
|
-
...(typeof row.trace_id === 'string' ? { trace_id: row.trace_id } : {}),
|
|
110
|
-
},
|
|
111
|
-
surface: typeof row.surface === 'string' && row.surface ? row.surface : 'pod',
|
|
112
|
-
};
|
|
86
|
+
/** Last measured child ledger context when a failed Pod emitted no terminal JSON. */
|
|
87
|
+
export function podNoResultDiagnostic(ledger: string): string | null {
|
|
88
|
+
let stage: string | undefined;
|
|
89
|
+
let round: number | undefined;
|
|
90
|
+
let mustFix: string | undefined;
|
|
91
|
+
for (const line of ledger.split(/\r?\n/)) {
|
|
92
|
+
try {
|
|
93
|
+
const entry = JSON.parse(line) as { event?: unknown; data?: Record<string, unknown> };
|
|
94
|
+
if (!entry || typeof entry !== 'object' || !entry.data || typeof entry.data !== 'object' || typeof entry.event !== 'string' || entry.event === 'pod-ledger-incomplete') continue;
|
|
95
|
+
if (typeof entry.data.round === 'number' && Number.isSafeInteger(entry.data.round)) round = entry.data.round;
|
|
96
|
+
if (entry.event !== 'run-origin') stage = entry.event;
|
|
97
|
+
// `reviewed` carries pointers only (count + finding ids); the text stays in the review artifact.
|
|
98
|
+
if (entry.event === 'reviewed' && typeof entry.data.mustFix === 'number') {
|
|
99
|
+
const ids = Array.isArray(entry.data.findingIds) ? entry.data.findingIds.filter((id): id is string => typeof id === 'string') : [];
|
|
100
|
+
mustFix = `${entry.data.mustFix}${ids.length ? ` ${JSON.stringify(ids)}` : ''}`;
|
|
101
|
+
}
|
|
102
|
+
} catch { /* incomplete ledger line */ }
|
|
103
|
+
}
|
|
104
|
+
return stage ? `child terminal result missing; last ledger stage=${stage}; round=${round ?? 'unknown'}; mustFix=${mustFix ?? 'unknown'}` : null;
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
/** Import
|
|
116
|
-
export function
|
|
107
|
+
/** Import valid JSONL log records into the launching host store, with durable per-job line receipts. */
|
|
108
|
+
export function importPodLogs(bytes: Buffer, job: string, store: LogStore | null = getDefaultLogStore()): { imported: number; skipped: number } {
|
|
117
109
|
if (!store) return { imported: 0, skipped: 0 };
|
|
118
|
-
const
|
|
119
|
-
const rows: Array<{ rec: LogRecord; surface: string }> = [];
|
|
110
|
+
const rows: Array<{ rec: LogRecord; surface: string; line: number }> = [];
|
|
120
111
|
let skipped = 0;
|
|
121
|
-
for (const line of
|
|
112
|
+
for (const [lineNumber, line] of bytes.toString('utf8').split(/\r?\n/).entries()) {
|
|
122
113
|
if (!line) continue;
|
|
123
|
-
const
|
|
124
|
-
if (!
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
const rec = fromJsonl(line);
|
|
115
|
+
if (!rec || !Number.isFinite(Date.parse(rec.ts))) { skipped++; continue; }
|
|
116
|
+
const data = rec.data && typeof rec.data === 'object' && !Array.isArray(rec.data)
|
|
117
|
+
? rec.data as Record<string, unknown>
|
|
118
|
+
: rec.data === undefined ? {} : { originalData: rec.data };
|
|
119
|
+
const surface = (rec as LogRecord & { surface?: unknown }).surface;
|
|
120
|
+
rows.push({
|
|
121
|
+
rec: { ...rec, data: { ...data, origin: 'pod', podJob: job } },
|
|
122
|
+
surface: typeof surface === 'string' && surface ? surface : 'pod',
|
|
123
|
+
line: lineNumber,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return { imported: store.insertPodBatch(job, rows), skipped };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function importReturnedLogs(bytes: Buffer, job: string, store: LogStore | null,
|
|
130
|
+
log: (category: string, event: string, data: Record<string, unknown>) => void): void {
|
|
131
|
+
try {
|
|
132
|
+
if (!store) throw new Error('host log store unavailable');
|
|
133
|
+
const imported = importPodLogs(bytes, job, store);
|
|
134
|
+
log('self-implement.pod', 'pod-logs-imported', { job, imported: imported.imported, skipped: imported.skipped });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
debug.log('self-implement.pod', 'pod-logs-import-failed', { job, reason: error instanceof Error ? error.message : String(error) });
|
|
127
137
|
}
|
|
128
|
-
if (rows.length > 0) store.insertBatch(rows);
|
|
129
|
-
return { imported: rows.length, skipped };
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
/** Reject symlink parents as well as lexical traversal; never replace a host file. */
|
|
@@ -176,17 +184,24 @@ export function collectPodArtifacts(logs: string, { dir, job, log = (c, e, d) =>
|
|
|
176
184
|
podLogsReturned = true;
|
|
177
185
|
const lines = bytes.length ? bytes.toString('utf8').split('\n').length - (bytes.at(-1) === 10 ? 1 : 0) : 0;
|
|
178
186
|
log('self-implement.pod', 'pod-logs-returned', { job, path: destination, lines });
|
|
179
|
-
|
|
180
|
-
const imported = importPodDecisionLogs(bytes, job, logStore);
|
|
181
|
-
log('self-implement.pod', 'pod-decisions-imported', { job, imported: imported.imported, skipped: imported.skipped });
|
|
182
|
-
} catch (error) {
|
|
183
|
-
log('self-implement.pod', 'pod-logs-import-failed', { job, reason: error instanceof Error ? error.message : String(error) });
|
|
184
|
-
}
|
|
187
|
+
importReturnedLogs(bytes, job, logStore, log);
|
|
185
188
|
}
|
|
186
189
|
} catch (e) {
|
|
187
190
|
const reason = (e as NodeJS.ErrnoException).code === 'EEXIST' ? 'exists' : e instanceof Error ? e.message : String(e);
|
|
188
|
-
if ((e as NodeJS.ErrnoException).code === 'EEXIST')
|
|
189
|
-
|
|
191
|
+
if ((e as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
192
|
+
log('self-implement.pod', 'artifact-collect-skipped', { job, path, reason });
|
|
193
|
+
if (path === podLogsPath) {
|
|
194
|
+
try {
|
|
195
|
+
const existing = resolve(dir, job, path);
|
|
196
|
+
if (!lstatSync(existing).isSymbolicLink() && readFileSync(existing).equals(bytes)) {
|
|
197
|
+
podLogsReturned = true;
|
|
198
|
+
importReturnedLogs(bytes, job, logStore, log);
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
debug.log('self-implement.pod', 'pod-logs-import-failed', { job, reason: error instanceof Error ? error.message : String(error) });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} else log('self-implement.pod', 'artifact-collect-incomplete', { job, path, reason });
|
|
190
205
|
if (path === podLogsPath) podLogsMissingReason = reason;
|
|
191
206
|
}
|
|
192
207
|
}
|