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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// X2 — 외부 에이전트 미션 슈퍼바이저 품질 지표. 읽기 전용.
|
|
2
|
+
// 새 기록을 만들지 않는다. 이미 남는 미션 로그(logs.db)만 집계한다.
|
|
3
|
+
// 기록이 없는 지표는 0 이 아니라 «못 잼 · 이유»다. 창 안 미션이 0 이면 «미션 없음».
|
|
4
|
+
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { LogStore, logsDbPath, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
7
|
+
|
|
8
|
+
export const SUPERVISOR_METRICS_DEFAULT_SINCE = '7d';
|
|
9
|
+
|
|
10
|
+
const RESULT_CATEGORY = 'agent-mission';
|
|
11
|
+
const RESULT_EVENT = 'result';
|
|
12
|
+
const DECISION_CATEGORY = 'pty.decision';
|
|
13
|
+
const ANSWER_EVENT = 'answer';
|
|
14
|
+
const CONTROL_CATEGORY = 'autopilot.control';
|
|
15
|
+
const CONTROL_END_EVENT = 'end';
|
|
16
|
+
const STALL_CATEGORY = 'self-implement';
|
|
17
|
+
const STALL_EVENT = 'frame-stall';
|
|
18
|
+
const LADDER_CATEGORY = 'agent-mission.resources';
|
|
19
|
+
|
|
20
|
+
const LADDER_STEPS = ['needs', 'have', 'official-index', 'backend', 'discover'] as const;
|
|
21
|
+
|
|
22
|
+
export interface MetricRow {
|
|
23
|
+
readonly id: 'completion' | 'questionsAnswered' | 'unnecessaryInterventions' | 'stall' | 'ladderHits';
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly status: 'measured' | 'unmeasured' | 'no-missions';
|
|
26
|
+
readonly reason?: string;
|
|
27
|
+
readonly value?: number | string;
|
|
28
|
+
readonly detail?: Record<string, number | string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SupervisorMetricsReport {
|
|
32
|
+
readonly since: string;
|
|
33
|
+
readonly sinceMs: number;
|
|
34
|
+
readonly missions: number;
|
|
35
|
+
readonly status: 'measured' | 'no-missions';
|
|
36
|
+
readonly metrics: readonly MetricRow[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SupervisorLogRow {
|
|
40
|
+
readonly tsMs: number;
|
|
41
|
+
readonly category: string;
|
|
42
|
+
readonly event: string;
|
|
43
|
+
readonly data: Record<string, unknown> | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ReadSupervisorLogs {
|
|
47
|
+
(sinceMs: number, nowMs: number): readonly SupervisorLogRow[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseData(raw: string | null): Record<string, unknown> | null {
|
|
51
|
+
if (!raw) return null;
|
|
52
|
+
try {
|
|
53
|
+
const parsed: unknown = JSON.parse(raw);
|
|
54
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed as Record<string, unknown> : null;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function missionKey(data: Record<string, unknown> | null): string | null {
|
|
61
|
+
const runId = data?.runId;
|
|
62
|
+
if (typeof runId === 'string' && runId.trim()) return runId.trim();
|
|
63
|
+
const missionId = data?.missionId;
|
|
64
|
+
if (typeof missionId === 'string' && missionId.trim()) return missionId.trim();
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function linkedRows(rows: readonly SupervisorLogRow[], missionIds: ReadonlySet<string>): { linked: SupervisorLogRow[]; unlinked: boolean } {
|
|
69
|
+
const linked: SupervisorLogRow[] = [];
|
|
70
|
+
let unlinked = false;
|
|
71
|
+
for (const row of rows) {
|
|
72
|
+
const key = missionKey(row.data);
|
|
73
|
+
if (key && missionIds.has(key)) linked.push(row);
|
|
74
|
+
else unlinked = true;
|
|
75
|
+
}
|
|
76
|
+
return { linked, unlinked };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const UNLINKED_REASON = '못 잼 · 미션과 연결 불가';
|
|
80
|
+
|
|
81
|
+
function unmeasured(id: MetricRow['id'], label: string, reason: string): MetricRow {
|
|
82
|
+
return { id, label, status: 'unmeasured', reason };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** 주입된 로그만으로 다섯 지표를 집계한다. 스토어를 열지 않는다. */
|
|
86
|
+
export function aggregateSupervisorMetrics(
|
|
87
|
+
rows: readonly SupervisorLogRow[],
|
|
88
|
+
opts: { since?: string; sinceMs: number },
|
|
89
|
+
): SupervisorMetricsReport {
|
|
90
|
+
const since = opts.since ?? SUPERVISOR_METRICS_DEFAULT_SINCE;
|
|
91
|
+
const results = rows.filter((row) => row.category === RESULT_CATEGORY && row.event === RESULT_EVENT);
|
|
92
|
+
const missionIds = new Set(results.map((row) => missionKey(row.data)).filter((key): key is string => key !== null));
|
|
93
|
+
const missions = results.length;
|
|
94
|
+
if (missions === 0) {
|
|
95
|
+
const reason = '미션 없음';
|
|
96
|
+
return {
|
|
97
|
+
since,
|
|
98
|
+
sinceMs: opts.sinceMs,
|
|
99
|
+
missions: 0,
|
|
100
|
+
status: 'no-missions',
|
|
101
|
+
metrics: [
|
|
102
|
+
{ id: 'completion', label: '완주율', status: 'no-missions', reason },
|
|
103
|
+
{ id: 'questionsAnswered', label: '대화형 질문에 답해 넘긴 수', status: 'no-missions', reason },
|
|
104
|
+
{ id: 'unnecessaryInterventions', label: '불필요했던 슈퍼바이저 개입', status: 'no-missions', reason },
|
|
105
|
+
{ id: 'stall', label: '멈춤 감지 · 실제 멈춤', status: 'no-missions', reason },
|
|
106
|
+
{ id: 'ladderHits', label: '사다리 단계별 적중', status: 'no-missions', reason },
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const completed = results.filter((row) => row.data?.ok === true).length;
|
|
112
|
+
const completion: MetricRow = {
|
|
113
|
+
id: 'completion',
|
|
114
|
+
label: '완주율',
|
|
115
|
+
status: 'measured',
|
|
116
|
+
value: `${completed}/${missions}`,
|
|
117
|
+
detail: { completed, missions, rate: completed / missions },
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const answerCandidates = rows.filter((row) => row.category === DECISION_CATEGORY && row.event === ANSWER_EVENT);
|
|
121
|
+
const answers = linkedRows(answerCandidates, missionIds);
|
|
122
|
+
const questionsAnswered: MetricRow = answers.unlinked
|
|
123
|
+
? unmeasured('questionsAnswered', '대화형 질문에 답해 넘긴 수', UNLINKED_REASON)
|
|
124
|
+
: answers.linked.length === 0
|
|
125
|
+
? unmeasured('questionsAnswered', '대화형 질문에 답해 넘긴 수', '못 잼 · pty.decision answer 기록 없음')
|
|
126
|
+
: {
|
|
127
|
+
id: 'questionsAnswered',
|
|
128
|
+
label: '대화형 질문에 답해 넘긴 수',
|
|
129
|
+
status: 'measured',
|
|
130
|
+
value: answers.linked.length,
|
|
131
|
+
detail: { answered: answers.linked.length },
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const endCandidates = rows.filter((row) => row.category === CONTROL_CATEGORY && row.event === CONTROL_END_EVENT && row.data?.interventionLevel !== undefined);
|
|
135
|
+
const ends = linkedRows(endCandidates, missionIds);
|
|
136
|
+
// 종료 상태와 verdict 는 개입이 불필요했다는 인과 근거가 아니다.
|
|
137
|
+
const unnecessaryInterventions: MetricRow = unmeasured('unnecessaryInterventions', '불필요했던 슈퍼바이저 개입', ends.unlinked
|
|
138
|
+
? UNLINKED_REASON
|
|
139
|
+
: ends.linked.length === 0
|
|
140
|
+
? '못 잼 · autopilot.control end 개입 기록 없음'
|
|
141
|
+
: '못 잼 · 개입 불필요성 근거 기록 없음');
|
|
142
|
+
|
|
143
|
+
const stallCandidates = rows.filter((row) => row.category === STALL_CATEGORY && row.event === STALL_EVENT);
|
|
144
|
+
const stalls = linkedRows(stallCandidates, missionIds);
|
|
145
|
+
// frame-stall is a detector stage, not a record that a mission actually stopped.
|
|
146
|
+
const stall: MetricRow = unmeasured('stall', '멈춤 감지 · 실제 멈춤', stalls.unlinked
|
|
147
|
+
? UNLINKED_REASON
|
|
148
|
+
: stalls.linked.length === 0
|
|
149
|
+
? '못 잼 · self-implement frame-stall 기록 없음'
|
|
150
|
+
: '못 잼 · 실제 멈춤 근거 기록 없음');
|
|
151
|
+
|
|
152
|
+
const ladderCandidates = rows.filter((row) => row.category === LADDER_CATEGORY && LADDER_STEPS.includes(row.event as typeof LADDER_STEPS[number]));
|
|
153
|
+
const ladderRows = linkedRows(ladderCandidates, missionIds);
|
|
154
|
+
// VERIFY/ROUTE are decisions. No outcome record connects a step to an observed success.
|
|
155
|
+
const ladderHits: MetricRow = unmeasured('ladderHits', '사다리 단계별 적중', ladderRows.unlinked
|
|
156
|
+
? UNLINKED_REASON
|
|
157
|
+
: ladderRows.linked.length === 0
|
|
158
|
+
? '못 잼 · agent-mission.resources 사다리 기록 없음'
|
|
159
|
+
: '못 잼 · 사다리 적중 근거 기록 없음');
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
since,
|
|
163
|
+
sinceMs: opts.sinceMs,
|
|
164
|
+
missions,
|
|
165
|
+
status: 'measured',
|
|
166
|
+
metrics: [completion, questionsAnswered, unnecessaryInterventions, stall, ladderHits],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function readSupervisorLogsFromStore(path: string = logsDbPath()): ReadSupervisorLogs {
|
|
171
|
+
return (sinceMs, nowMs) => {
|
|
172
|
+
if (!existsSync(path)) return [];
|
|
173
|
+
const store = LogStore.openReadOnly(path);
|
|
174
|
+
try {
|
|
175
|
+
const queried: LogStoreRow[] = store.queryAll({
|
|
176
|
+
exactCategories: [RESULT_CATEGORY, DECISION_CATEGORY, CONTROL_CATEGORY, STALL_CATEGORY, LADDER_CATEGORY],
|
|
177
|
+
sinceMs,
|
|
178
|
+
untilMs: nowMs,
|
|
179
|
+
});
|
|
180
|
+
return queried
|
|
181
|
+
.filter((row) => row.ts_ms >= sinceMs && row.ts_ms <= nowMs)
|
|
182
|
+
.filter((row) =>
|
|
183
|
+
(row.category === RESULT_CATEGORY && row.event === RESULT_EVENT)
|
|
184
|
+
|| (row.category === DECISION_CATEGORY && row.event === ANSWER_EVENT)
|
|
185
|
+
|| (row.category === CONTROL_CATEGORY && row.event === CONTROL_END_EVENT)
|
|
186
|
+
|| (row.category === STALL_CATEGORY && row.event === STALL_EVENT)
|
|
187
|
+
|| (row.category === LADDER_CATEGORY && (LADDER_STEPS as readonly string[]).includes(row.event)))
|
|
188
|
+
.map((row) => ({ tsMs: row.ts_ms, category: row.category, event: row.event, data: parseData(row.data) }));
|
|
189
|
+
} finally {
|
|
190
|
+
store.close();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function loadSupervisorMetrics(
|
|
196
|
+
since: string,
|
|
197
|
+
sinceMs: number,
|
|
198
|
+
nowMs: number,
|
|
199
|
+
read: ReadSupervisorLogs = readSupervisorLogsFromStore(),
|
|
200
|
+
): SupervisorMetricsReport {
|
|
201
|
+
return aggregateSupervisorMetrics(read(sinceMs, nowMs), { since, sinceMs });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function formatSupervisorMetrics(report: SupervisorMetricsReport): string {
|
|
205
|
+
const lines = [`agent-mission metrics · since ${report.since} · 미션 ${report.missions}`];
|
|
206
|
+
if (report.status === 'no-missions') {
|
|
207
|
+
lines.push('미션 없음');
|
|
208
|
+
return lines.join('\n');
|
|
209
|
+
}
|
|
210
|
+
for (const metric of report.metrics) {
|
|
211
|
+
if (metric.status === 'unmeasured') {
|
|
212
|
+
lines.push(`${metric.label}: ${metric.reason}`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
lines.push(`${metric.label}: ${metric.value}`);
|
|
216
|
+
}
|
|
217
|
+
return lines.join('\n');
|
|
218
|
+
}
|
|
@@ -825,6 +825,8 @@ export async function reviewPullRequest(
|
|
|
825
825
|
|
|
826
826
|
/** 리뷰 결과를 PR 코멘트/알림/CLI 용 markdown 으로 렌더. */
|
|
827
827
|
export function renderReview(r: ReviewResult): string {
|
|
828
|
+
// A review that never ran must not read as PASS anywhere it is rendered (PR body, comments, CLI) — 10-05 #23942·#24151.
|
|
829
|
+
if (r.reviewed === false) return `⚠️ 자율 PR 리뷰: 리뷰 안 돎 — 사유 ${r.failureReason?.trim() || '미상'} (통과 판정 아님)`;
|
|
828
830
|
const icon = r.verdict === 'pass' ? '✅' : r.verdict === 'warn' ? '⚠️' : '⛔';
|
|
829
831
|
const head = `${icon} 자율 PR 리뷰: ${r.verdict.toUpperCase()}`;
|
|
830
832
|
const parts = [head];
|
|
@@ -9,6 +9,8 @@ export interface PendingQuestion {
|
|
|
9
9
|
id: string;
|
|
10
10
|
sessionId?: string;
|
|
11
11
|
runId?: string;
|
|
12
|
+
/** Explicit provenance for isolated test questions; a temporary goal path alone is not proof. */
|
|
13
|
+
testOrigin?: true;
|
|
12
14
|
questions: Array<Pick<Question, 'id' | 'question' | 'options' | 'impact' | 'recommendedIndex'>>;
|
|
13
15
|
startedAt: string;
|
|
14
16
|
expiresAt?: string;
|
|
@@ -84,6 +86,7 @@ function isPendingQuestion(value: unknown): value is PendingQuestion {
|
|
|
84
86
|
return typeof pending.id === 'string'
|
|
85
87
|
&& (pending.sessionId === undefined || typeof pending.sessionId === 'string')
|
|
86
88
|
&& (pending.runId === undefined || typeof pending.runId === 'string')
|
|
89
|
+
&& (pending.testOrigin === undefined || pending.testOrigin === true)
|
|
87
90
|
&& Array.isArray(pending.questions)
|
|
88
91
|
&& pending.questions.every((question) => {
|
|
89
92
|
if (question === null || typeof question !== 'object' || Array.isArray(question)) return false;
|
|
@@ -175,6 +178,7 @@ export function createPendingQuestion(
|
|
|
175
178
|
sessionId?: string,
|
|
176
179
|
deps: Pick<PendingQuestionStoreDeps, 'now'> = {},
|
|
177
180
|
presentation: PendingQuestionPresentation = { surface: 'tui', delivery: request.delivery ?? 'modal' },
|
|
181
|
+
provenance: { testOrigin?: true } = {},
|
|
178
182
|
): PendingQuestion {
|
|
179
183
|
// File-surface waits must carry a concrete deadline so the CLI answer path can
|
|
180
184
|
// reject an expired record (see isPendingQuestion). Fail loudly at creation
|
|
@@ -186,6 +190,7 @@ export function createPendingQuestion(
|
|
|
186
190
|
id,
|
|
187
191
|
...(sessionId === undefined ? {} : { sessionId }),
|
|
188
192
|
...(request.runId === undefined ? {} : { runId: request.runId }),
|
|
193
|
+
...(provenance.testOrigin ? { testOrigin: true as const } : {}),
|
|
189
194
|
questions: request.questions.map(({ id, question, options, impact, recommendedIndex }) => ({
|
|
190
195
|
id, question, options,
|
|
191
196
|
...(impact === undefined ? {} : { impact }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, createSign } from 'node:crypto';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, isAbsolute, join } from 'node:path';
|
|
5
5
|
import { getElanousConfigDir, getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
6
6
|
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
@@ -45,21 +45,23 @@ export function githubAutomationToken(deps: GithubAutomationTokenDeps = {}): str
|
|
|
45
45
|
return githubInstallationCredential(deps)?.token ?? null;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/** The App credential is machine-level (like `gh auth` in ~/.config/gh), not per universe: a tree-derived test
|
|
49
|
+
* universe resolves its config dir to `<tree>/.elanous-test`, where the file never is — 09-29 #21879 was inert there.
|
|
50
|
+
* Order: explicit path → this universe's config dir → the prod root `~/.elanous`.
|
|
51
|
+
* An explicit config-dir override (tests · `--config-dir`) is a deliberate boundary — never reach past it.
|
|
52
|
+
* ELANOUS_GITHUB_APP_CONFIG_PATH pins the machine-level fallback (the bun test preload points it at nothing,
|
|
53
|
+
* so tests never mint); an explicit config-dir override still wins. */
|
|
54
|
+
function appConfigPath(explicit?: string): string {
|
|
55
|
+
const rel = join('secrets', 'github-app', 'app.json');
|
|
56
|
+
const own = join(getElanousConfigDir(), rel);
|
|
57
|
+
const pinned = process.env.ELANOUS_GITHUB_APP_CONFIG_PATH?.trim();
|
|
58
|
+
return explicit ?? (getElanousConfigDirOverride() ? own : pinned || (existsSync(own) ? own : join(prodInstanceRoot(), rel)));
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
/** Scoped requests always mint anew; the returned expiry comes from GitHub's response. */
|
|
49
62
|
export function githubInstallationCredential(deps: GithubAutomationTokenDeps = {}): { token: string; expires_at: string } | null {
|
|
50
63
|
let path: string;
|
|
51
|
-
|
|
52
|
-
// universe resolves its config dir to `<tree>/.elanous-test`, where the file never is — 09-29 #21879 was inert there.
|
|
53
|
-
// Order: explicit path → this universe's config dir → the prod root `~/.elanous`.
|
|
54
|
-
try {
|
|
55
|
-
const rel = join('secrets', 'github-app', 'app.json');
|
|
56
|
-
const own = join(getElanousConfigDir(), rel);
|
|
57
|
-
// An explicit config-dir override (tests · `--config-dir`) is a deliberate boundary — never reach past it.
|
|
58
|
-
// ELANOUS_GITHUB_APP_CONFIG_PATH pins the machine-level fallback (the bun test preload points it at nothing,
|
|
59
|
-
// so tests never mint); an explicit config-dir override still wins.
|
|
60
|
-
const pinned = process.env.ELANOUS_GITHUB_APP_CONFIG_PATH?.trim();
|
|
61
|
-
path = deps.configPath ?? (getElanousConfigDirOverride() ? own : pinned || (existsSync(own) ? own : join(prodInstanceRoot(), rel)));
|
|
62
|
-
}
|
|
64
|
+
try { path = appConfigPath(deps.configPath); }
|
|
63
65
|
catch {
|
|
64
66
|
try { debug.log('auth.github-app', 'token-failed', { reason: 'config-path-failed' }); } catch { /* fail open */ }
|
|
65
67
|
return null;
|
|
@@ -113,3 +115,100 @@ export function githubInstallationCredential(deps: GithubAutomationTokenDeps = {
|
|
|
113
115
|
return null;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
118
|
+
|
|
119
|
+
/** PODCRED1 (10-05): simultaneous launches each minted a scoped token in the same second and the Pods' logins died
|
|
120
|
+
* together (12:49Z · 5 in 2 s). Concurrent issuers for one repository — across processes — share one mint: the first
|
|
121
|
+
* takes a lock and mints, the others wait and reuse that result while it is recent and long-lived. */
|
|
122
|
+
export interface CoalescedInstallationOptions {
|
|
123
|
+
cacheDir?: string;
|
|
124
|
+
/** Reuse a token minted at most this long ago. */
|
|
125
|
+
windowMs?: number;
|
|
126
|
+
/** …and only while it still has this much life left. */
|
|
127
|
+
minRemainingMs?: number;
|
|
128
|
+
/** Skip reuse (still mint under the lock and publish the result) — for a retry after the shared token failed. */
|
|
129
|
+
fresh?: boolean;
|
|
130
|
+
lockWaitMs?: number;
|
|
131
|
+
staleLockMs?: number;
|
|
132
|
+
now?: () => number;
|
|
133
|
+
sleepSync?: (ms: number) => void;
|
|
134
|
+
mint?: (scope: GithubInstallationTokenScope) => { token: string; expires_at: string } | null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const COALESCE_WINDOW_MS = 120_000;
|
|
138
|
+
const COALESCE_MIN_REMAINING_MS = 50 * 60_000;
|
|
139
|
+
|
|
140
|
+
function blockingSleep(ms: number): void {
|
|
141
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function coalescedInstallationCredential(scope: GithubInstallationTokenScope, opts: CoalescedInstallationOptions = {}): { token: string; expires_at: string } | null {
|
|
145
|
+
const now = opts.now ?? Date.now;
|
|
146
|
+
const mint = opts.mint ?? ((s: GithubInstallationTokenScope) => githubInstallationCredential({ scope: s }));
|
|
147
|
+
const windowMs = opts.windowMs ?? COALESCE_WINDOW_MS;
|
|
148
|
+
const minRemainingMs = opts.minRemainingMs ?? COALESCE_MIN_REMAINING_MS;
|
|
149
|
+
const sleepSync = opts.sleepSync ?? blockingSleep;
|
|
150
|
+
// Same boundary as the App config: a test process (or config-dir override) never writes the operational root.
|
|
151
|
+
if (!opts.cacheDir && (process.env.NODE_ENV === 'test' || process.env.ELANOUS_TEST_HOME)) return mint(scope);
|
|
152
|
+
let dir: string;
|
|
153
|
+
let identity: string;
|
|
154
|
+
try {
|
|
155
|
+
const configPath = appConfigPath();
|
|
156
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8')) as Partial<AppConfig>;
|
|
157
|
+
identity = JSON.stringify([configPath, config.id, config.installation_id]);
|
|
158
|
+
dir = opts.cacheDir ?? join(getElanousConfigDirOverride() ? getElanousConfigDir() : prodInstanceRoot(), 'cache', 'github-app-scoped');
|
|
159
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
160
|
+
} catch {
|
|
161
|
+
if (!opts.cacheDir) return mint(scope);
|
|
162
|
+
dir = opts.cacheDir;
|
|
163
|
+
identity = 'injected';
|
|
164
|
+
}
|
|
165
|
+
// Keyed by App installation and repository, so another App (another universe or owner) never reuses this token.
|
|
166
|
+
const key = createHash('sha256').update(`${identity}\0${scope.repository}`).digest('hex').slice(0, 32);
|
|
167
|
+
const file = join(dir, `${key}.json`);
|
|
168
|
+
const lock = join(dir, `${key}.lock`);
|
|
169
|
+
let waitStart: number | undefined;
|
|
170
|
+
const reuse = (): { token: string; expires_at: string } | null => {
|
|
171
|
+
try {
|
|
172
|
+
const entry = JSON.parse(readFileSync(file, 'utf8')) as { token?: unknown; expiresAt?: unknown; mintedAt?: unknown };
|
|
173
|
+
const t = now();
|
|
174
|
+
if (typeof entry.token === 'string' && entry.token && typeof entry.expiresAt === 'number' && typeof entry.mintedAt === 'number'
|
|
175
|
+
&& t - entry.mintedAt <= windowMs && entry.expiresAt - t >= minRemainingMs) {
|
|
176
|
+
try { debug.log('auth.github-app', 'token-coalesced', { ageMs: t - entry.mintedAt, expiresAt: new Date(entry.expiresAt).toISOString(), ...(waitStart === undefined ? {} : { waitedMs: t - waitStart }) }); } catch { /* fail open */ }
|
|
177
|
+
return { token: entry.token, expires_at: new Date(entry.expiresAt).toISOString() };
|
|
178
|
+
}
|
|
179
|
+
} catch { /* no usable entry */ }
|
|
180
|
+
return null;
|
|
181
|
+
};
|
|
182
|
+
const hit = opts.fresh ? null : reuse();
|
|
183
|
+
if (hit) return hit;
|
|
184
|
+
waitStart = now();
|
|
185
|
+
const deadline = waitStart + (opts.lockWaitMs ?? 15_000);
|
|
186
|
+
let locked = false;
|
|
187
|
+
for (;;) {
|
|
188
|
+
try { mkdirSync(lock); locked = true; break; }
|
|
189
|
+
catch {
|
|
190
|
+
try { if (now() - statSync(lock).mtimeMs > (opts.staleLockMs ?? 30_000)) { rmSync(lock, { recursive: true, force: true }); continue; } } catch { continue; }
|
|
191
|
+
if (now() >= deadline) break;
|
|
192
|
+
sleepSync(200);
|
|
193
|
+
const waited = opts.fresh ? null : reuse();
|
|
194
|
+
if (waited) return waited;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!locked) { try { debug.log('auth.github-app', 'token-coalesce-lock-timeout', { waitedMs: now() - (waitStart ?? now()) }); } catch { /* fail open */ } }
|
|
198
|
+
try {
|
|
199
|
+
const again = opts.fresh ? null : reuse();
|
|
200
|
+
if (again) return again;
|
|
201
|
+
const minted = mint(scope);
|
|
202
|
+
const expiresAt = minted ? Date.parse(minted.expires_at) : NaN;
|
|
203
|
+
if (minted && Number.isFinite(expiresAt)) {
|
|
204
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
205
|
+
try {
|
|
206
|
+
writeFileSync(temp, JSON.stringify({ token: minted.token, expiresAt, mintedAt: now() }), { mode: 0o600 });
|
|
207
|
+
renameSync(temp, file);
|
|
208
|
+
} catch { try { rmSync(temp, { force: true }); } catch { /* best effort */ } }
|
|
209
|
+
}
|
|
210
|
+
return minted;
|
|
211
|
+
} finally {
|
|
212
|
+
if (locked) { try { rmSync(lock, { recursive: true, force: true }); } catch { /* stale-lock rule recovers */ } }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -67,6 +67,9 @@ export interface LlmMergeOutcome {
|
|
|
67
67
|
testDeclarationUnmeasured?: string[];
|
|
68
68
|
/** 해결기 응답이 LLM 공급자 오류 문구였다 — 파일 내용이 아니다(🅢 2026-09-27 #21086: 세 파일이 통째로 그 문구가 됐다). */
|
|
69
69
|
providerFailure?: string[];
|
|
70
|
+
/** LLM 입력 상한 초과: 사람 수확으로 넘긴다. 원래 브랜치는 abort 후 보존된다. */
|
|
71
|
+
reason?: 'conflict-input-too-large' | 'conflict-resolver-interrupted';
|
|
72
|
+
inputChars?: number;
|
|
70
73
|
/** 병합 결과가 양쪽 판 중 작은 쪽의 절반 아래로 줄었다 — 통째로 날린 것으로 보고 해결 실패로 친다. */
|
|
71
74
|
sizeCollapse?: Array<{ file: string; ours: number; theirs: number; merged: number }>;
|
|
72
75
|
/** `status: 'error'` 일 때 어느 단계인지. 선택 — 옛 호출자는 이 칸 없이 돌아도 된다. */
|
|
@@ -79,6 +82,13 @@ export interface LlmMergeOutcome {
|
|
|
79
82
|
export const PROVIDER_FAILURE_TEXT = /^\s*\[LLM PROVIDER (?:BLOCKED|STOPPED)\]/;
|
|
80
83
|
/** 이 줄 수 미만 파일은 크기 붕괴 판정에서 뺀다(작은 파일은 정당하게 크게 줄 수 있다). */
|
|
81
84
|
export const SIZE_COLLAPSE_MIN_LINES = 50;
|
|
85
|
+
export const DEFAULT_MERGE_CONFLICT_INPUT_MAX_CHARS = 60_000;
|
|
86
|
+
|
|
87
|
+
export class MergeConflictInputTooLarge extends Error {
|
|
88
|
+
constructor(readonly inputChars: number) {
|
|
89
|
+
super(`충돌 큼 — 사람 수확 (LLM 입력 ${inputChars}자)`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
82
92
|
|
|
83
93
|
function lineCount(s: string): number {
|
|
84
94
|
if (s.length === 0) return 0;
|
|
@@ -207,12 +217,21 @@ export async function mergeMainWithLlmResolve(
|
|
|
207
217
|
let merged: string;
|
|
208
218
|
try {
|
|
209
219
|
conflicted = git.readFile(abs);
|
|
220
|
+
} catch {
|
|
221
|
+
git.abort(worktreePath);
|
|
222
|
+
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles });
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
210
225
|
// release/next.md: concurrent landings each append a line — keep both without asking the LLM (REL7c).
|
|
211
226
|
const deterministic = f === NEXT_MD_PATH ? deterministicNextMd(git, worktreePath, f) : null;
|
|
212
227
|
merged = deterministic ?? await resolve(f, conflicted);
|
|
213
|
-
} catch {
|
|
228
|
+
} catch (error) {
|
|
214
229
|
git.abort(worktreePath); // LLM 예외 → base 유지
|
|
215
|
-
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles
|
|
230
|
+
return measuredOutcome({ status: 'conflict-unresolved', resolvedFiles,
|
|
231
|
+
...(error instanceof MergeConflictInputTooLarge
|
|
232
|
+
? { reason: 'conflict-input-too-large' as const, inputChars: error.inputChars }
|
|
233
|
+
: { reason: 'conflict-resolver-interrupted' as const }),
|
|
234
|
+
});
|
|
216
235
|
}
|
|
217
236
|
if (PROVIDER_FAILURE_TEXT.test(merged)) {
|
|
218
237
|
git.abort(worktreePath); // 해결기가 공급자 오류 문구를 «내용»으로 돌려줬다 → base 유지
|
|
@@ -335,15 +354,22 @@ export async function defaultLlmResolve(
|
|
|
335
354
|
filePath: string, conflicted: string, mergeTarget: string,
|
|
336
355
|
options: { worktreePath?: string; mode?: 'off' | 'shadow' | 'on'; git?: IntentGit; stream?: typeof import('../../llm.js')['streamLLM'] } = {},
|
|
337
356
|
): Promise<string> {
|
|
357
|
+
let rawConfig: { mergeConflictInputMaxChars?: unknown; mergeIntent?: unknown } | undefined;
|
|
358
|
+
try { rawConfig = getUserConfig().raw.selfImplement as typeof rawConfig; }
|
|
359
|
+
catch { /* unreadable config keeps the old resolver and default input limit */ }
|
|
360
|
+
const configuredMax = rawConfig?.mergeConflictInputMaxChars;
|
|
361
|
+
const maxChars = typeof configuredMax === 'number' && Number.isSafeInteger(configuredMax) && configuredMax > 0
|
|
362
|
+
? configuredMax : DEFAULT_MERGE_CONFLICT_INPUT_MAX_CHARS;
|
|
338
363
|
let rawMode: unknown = options.mode;
|
|
339
364
|
if (rawMode === undefined) {
|
|
340
|
-
|
|
341
|
-
catch { /* unreadable config keeps the old resolver */ }
|
|
365
|
+
rawMode = rawConfig?.mergeIntent;
|
|
342
366
|
}
|
|
343
367
|
const mode = rawMode === 'shadow' || rawMode === 'on' ? rawMode : 'off';
|
|
344
|
-
const streamLLM = options.stream ?? (await import('../../llm.js')).streamLLM;
|
|
345
368
|
const resolve = async (intent?: { ours: string | null; theirs: string[] }): Promise<string> => {
|
|
346
|
-
const
|
|
369
|
+
const prompt = conflictResolvePrompt(filePath, conflicted, mergeTarget, intent);
|
|
370
|
+
if (prompt.length > maxChars) throw new MergeConflictInputTooLarge(prompt.length);
|
|
371
|
+
const streamLLM = options.stream ?? (await import('../../llm.js')).streamLLM;
|
|
372
|
+
const out = await streamLLM([{ role: 'user', content: prompt }], () => {}, { model: process.env.ELANOUS_CONFLICT_MODEL || tierModel('better'), reasoningEffort: 'medium' });
|
|
347
373
|
// ⛔ 라우터는 공급자가 전부 막히면 던지지 않고 오류 문구를 돌려준다 — 그것은 파일 내용이 아니다.
|
|
348
374
|
if (PROVIDER_FAILURE_TEXT.test(out)) throw new Error(`conflict resolve: LLM provider failure for ${filePath}`);
|
|
349
375
|
return `${out.replace(/^```[\w.-]*\n?/, '').replace(/\n?```\s*$/, '').trimEnd()}\n`;
|
|
@@ -3,6 +3,7 @@ import { appendFileSync, mkdirSync, readFileSync, rmdirSync } from 'node:fs';
|
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { stateDirSourceForChild } from '../agent/identity-env.js';
|
|
6
|
+
import { oldDoorInternalEnv } from '../self-dev/old-door.js';
|
|
6
7
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
7
8
|
import type { TrackAgentDecision, TrackAgentInput, TrackAgentResult } from './track-agent.js';
|
|
8
9
|
|
|
@@ -128,7 +129,7 @@ export async function launchTrackAgentShadow(input: TrackAgentInput, options: Sh
|
|
|
128
129
|
const instanceRoot = effectiveInstanceRoot();
|
|
129
130
|
const { stdout } = await exec('bun', ['bin/elanous.mjs', '--config-dir', instanceRoot, ...values], {
|
|
130
131
|
cwd: process.env.ELANOUS_TOOL_CWD?.trim() || resolve(import.meta.dir, '../..'),
|
|
131
|
-
env: { ...process.env, ELANOUS_STATE_DIR: instanceRoot, ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(instanceRoot) },
|
|
132
|
+
env: { ...process.env, ELANOUS_STATE_DIR: instanceRoot, ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(instanceRoot), ...oldDoorInternalEnv('self-orchestrate') },
|
|
132
133
|
encoding: 'utf8', maxBuffer: 1024 * 1024,
|
|
133
134
|
});
|
|
134
135
|
return { stdout };
|