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
package/scripts/rhythm/weekly.ts
CHANGED
|
@@ -7,13 +7,15 @@ import { effectiveInstanceRoot, prodInstanceRoot } from '../../src/instance/reso
|
|
|
7
7
|
import { getUserConfig } from '../../src/user-config.js';
|
|
8
8
|
import { loopEvent } from '../../src/loops/observe.js';
|
|
9
9
|
import { sendOutbound } from '../../src/domains/outbound-alert.js';
|
|
10
|
-
import {
|
|
10
|
+
import { listSchedules } from '../../src/release-loop/release-schedule.js';
|
|
11
|
+
import { listChecklist } from '../../src/release-loop/checklist.js';
|
|
12
|
+
import { collectLandings, collectDecisions, type Landing, type Release, type Pending, type Section } from './daily.js';
|
|
11
13
|
|
|
12
14
|
type Issue = { id: string; title: string; owner: string; due: string; status: 'open' | 'done'; at: string; by: string };
|
|
13
15
|
type WeeklyParts = { landings: Section<Landing[]>; release: Section<Release>; issues: Section<Issue[]>;
|
|
14
16
|
followThrough: Section<{ issue: Issue; outcome: 'done' | '기한 뒤 완료' | '기한 넘김' | '진행'; doneAt?: string }[]>;
|
|
15
17
|
decisions: Section<Pending[]>; news: Section<{ title: string; url: string }[]> };
|
|
16
|
-
export type WeeklyDeps = { now?: () => Date; root?: string; vaultRoot?: string | null; sendEnabled?: boolean;
|
|
18
|
+
export type WeeklyDeps = { now?: () => Date; root?: string; vaultRoot?: string | null; sendEnabled?: boolean; repoRoot?: string; repoName?: string;
|
|
17
19
|
landings?: (now: Date, window: { from: Date; to: Date }) => Promise<Landing[]>; release?: () => Promise<Release>;
|
|
18
20
|
decisions?: () => Promise<Pending[]>; send?: (text: string, kind: string) => boolean;
|
|
19
21
|
receipt?: (key: string) => 'sent' | 'not-sent' | 'unknown';
|
|
@@ -60,6 +62,20 @@ function appendIssue(root: string, row: Issue) {
|
|
|
60
62
|
mkdirSync(dirname(file), { recursive: true });
|
|
61
63
|
appendFileSync(file, `${JSON.stringify(row)}\n`);
|
|
62
64
|
}
|
|
65
|
+
async function collectWeeklyRelease(now: Date, root: string): Promise<Release> {
|
|
66
|
+
if (!existsSync(join(root, 'release', 'features.sqlite'))) throw new Error('판 원장 없음');
|
|
67
|
+
const schedules = listSchedules(root).sort((a, b) => a.cutAt.localeCompare(b.cutAt));
|
|
68
|
+
const index = schedules.findIndex(schedule => Date.parse(schedule.cutAt) >= now.getTime());
|
|
69
|
+
if (index < 0) throw new Error('남은 판 일정 없음');
|
|
70
|
+
const current = schedules[index]!;
|
|
71
|
+
const items = listChecklist(current.version, root).items;
|
|
72
|
+
const next = schedules[index + 1];
|
|
73
|
+
const nextItems = next ? listChecklist(next.version, root).items : null;
|
|
74
|
+
return { version: current.version, green: items.filter(i => i.status === 'green').length, total: items.length,
|
|
75
|
+
nextVersion: next?.version ?? null, nextGreen: nextItems ? nextItems.filter(i => i.status === 'green').length : null,
|
|
76
|
+
nextTotal: nextItems?.length ?? null, cutAt: current.cutAt,
|
|
77
|
+
red: items.filter(i => i.status === 'red').map(i => ({ name: `${i.id} ${i.title}` })) };
|
|
78
|
+
}
|
|
63
79
|
function readDailyNews(root: string, from: Date, to: Date): Section<{ title: string; url: string }[]> {
|
|
64
80
|
const found = new Map<string, { title: string; url: string }>();
|
|
65
81
|
const failures: string[] = [];
|
|
@@ -67,7 +83,7 @@ function readDailyNews(root: string, from: Date, to: Date): Section<{ title: str
|
|
|
67
83
|
for (let at = from.getTime(); at < to.getTime(); at += DAY) {
|
|
68
84
|
const day = kstDate(new Date(at));
|
|
69
85
|
const file = join(root, 'rhythm', 'daily', `${day}.md`);
|
|
70
|
-
if (!existsSync(file))
|
|
86
|
+
if (!existsSync(file)) continue;
|
|
71
87
|
files++;
|
|
72
88
|
try {
|
|
73
89
|
const text = readFileSync(file, 'utf8');
|
|
@@ -83,7 +99,8 @@ function readDailyNews(root: string, from: Date, to: Date): Section<{ title: str
|
|
|
83
99
|
} catch (e) { failures.push(`${day} ${reasonOf(e)}`); }
|
|
84
100
|
}
|
|
85
101
|
const news = [...found.values()].slice(0, 10);
|
|
86
|
-
if (
|
|
102
|
+
if (!files) return { status: 'unreadable', reason: '데일리 없음' };
|
|
103
|
+
if (failures.length) return { status: 'unreadable', reason: `${failures.join('; ')}${news.length ? `\n부분 수집 (완전한 주간 집계 아님):\n${news.map(n => `- ${n.title} — ${n.url}`).join('\n')}` : ''}` };
|
|
87
104
|
return { status: 'ok', value: news };
|
|
88
105
|
}
|
|
89
106
|
const labels: Record<keyof WeeklyParts, string> = {
|
|
@@ -133,8 +150,10 @@ function vaultCopy(markdown: string, key: string, deps: WeeklyDeps) {
|
|
|
133
150
|
let root = deps.vaultRoot;
|
|
134
151
|
if (root === undefined) {
|
|
135
152
|
try {
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
const config = getUserConfig();
|
|
154
|
+
const obsidian = object(config.raw.obsidian);
|
|
155
|
+
root = (typeof obsidian.vault === 'string' && obsidian.vault) || (typeof obsidian.vaultRoot === 'string' && obsidian.vaultRoot) || config.obsidian.vault || null;
|
|
156
|
+
if (root && effectiveInstanceRoot() !== prodInstanceRoot() && !root.startsWith(`${effectiveInstanceRoot()}/`)) return { vaultError: '격리 인스턴스 — 운영 볼트 사본 건너뜀', vaultFatal: false };
|
|
138
157
|
} catch (e) { return { vaultError: reasonOf(e), vaultFatal: true }; }
|
|
139
158
|
}
|
|
140
159
|
if (!root) return { vaultError: null, vaultFatal: false };
|
|
@@ -181,6 +200,8 @@ export async function runWeekly(options: { dryRun?: boolean; stage?: 'collect' |
|
|
|
181
200
|
const now = (deps.now ?? (() => new Date()))();
|
|
182
201
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
183
202
|
const { from, to, key } = weekOf(now);
|
|
203
|
+
const newsTo = new Date(`${kstDate(now)}T00:00:00+09:00`);
|
|
204
|
+
const newsFrom = new Date(newsTo.getTime() - 7 * DAY);
|
|
184
205
|
const file = join(root, 'rhythm', 'weekly', `${key}.md`);
|
|
185
206
|
let markdown: string;
|
|
186
207
|
let header: string;
|
|
@@ -205,8 +226,8 @@ export async function runWeekly(options: { dryRun?: boolean; stage?: 'collect' |
|
|
|
205
226
|
};
|
|
206
227
|
const issues = await safe(async () => issueHistory(root));
|
|
207
228
|
const parts: WeeklyParts = {
|
|
208
|
-
landings: await safe(() =>
|
|
209
|
-
release: await safe(deps.release ??
|
|
229
|
+
landings: await safe(() => deps.landings ? deps.landings(now, { from, to }) : collectLandings(now, { from, to }, { repoRoot: deps.repoRoot, repoName: deps.repoName })),
|
|
230
|
+
release: await safe(deps.release ?? (() => collectWeeklyRelease(now, root))),
|
|
210
231
|
issues: issues.status === 'ok' ? { status: 'ok', value: [...latest(issues.value).values()].filter(i => i.status === 'open') } : issues,
|
|
211
232
|
followThrough: issues.status === 'ok' ? { status: 'ok', value: [...new Set([
|
|
212
233
|
...[...latest(issues.value, from.getTime()).values()].filter(i => i.status === 'open').map(i => i.id),
|
|
@@ -219,7 +240,7 @@ export async function runWeekly(options: { dryRun?: boolean; stage?: 'collect' |
|
|
|
219
240
|
return { issue, outcome: Date.parse(issue.due) < now.getTime() ? '기한 넘김' as const : '진행' as const };
|
|
220
241
|
}) } : issues,
|
|
221
242
|
decisions: await safe(deps.decisions ?? collectDecisions),
|
|
222
|
-
news: readDailyNews(root,
|
|
243
|
+
news: readDailyNews(root, newsFrom, newsTo),
|
|
223
244
|
};
|
|
224
245
|
const versions = await safe(async () => publishedVersions(root, from, to));
|
|
225
246
|
if (versions.status === 'unreadable') {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { appendFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
4
|
+
import { homedir, tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { debug } from '../../src/debug/log.js';
|
|
7
|
+
import {
|
|
8
|
+
defaultGitMergeSeam, defaultLlmResolve, mergeMainWithLlmResolve,
|
|
9
|
+
type MergeGitSeam,
|
|
10
|
+
} from '../../src/autopilot/build/llm-conflict-merge.js';
|
|
11
|
+
import { NEXT_MD_PATH } from '../../src/release-loop/next-md-merge.js';
|
|
12
|
+
|
|
13
|
+
type Command = (cwd: string, program: 'git' | 'gh', args: readonly string[]) => { status: number; stdout: string; stderr: string };
|
|
14
|
+
export type ShadowOutcome = 'clean' | 'resolved-deterministic' | 'resolved-llm' | 'unresolved' | 'unmeasured';
|
|
15
|
+
export interface ShadowRow { pr: number; outcome: ShadowOutcome; files: string[]; durationMs: number }
|
|
16
|
+
interface Draft { number: number; headRefName: string; isDraft: boolean; mergeable: string | boolean | null }
|
|
17
|
+
|
|
18
|
+
export interface ShadowOptions {
|
|
19
|
+
repo: string;
|
|
20
|
+
stateRoot: string;
|
|
21
|
+
limit?: number;
|
|
22
|
+
run?: Command;
|
|
23
|
+
git?: MergeGitSeam;
|
|
24
|
+
resolve?: (file: string, conflicted: string) => Promise<string>;
|
|
25
|
+
log?: (line: string) => void;
|
|
26
|
+
now?: () => number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const runCommand: Command = (cwd, program, args) => {
|
|
30
|
+
const result = spawnSync(program, [...args], { cwd, encoding: 'utf8' });
|
|
31
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? '', stderr: result.stderr ?? String(result.error ?? '') };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function mustRun(run: Command, cwd: string, program: 'git' | 'gh', args: readonly string[]): string {
|
|
35
|
+
const result = run(cwd, program, args);
|
|
36
|
+
if (result.status !== 0) throw new Error(`${program} ${args.slice(0, 3).join(' ')}: ${result.stderr || 'failed'}`);
|
|
37
|
+
return result.stdout;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Use the same harness ownership boundary as the draft sweep; never touch a non-harness PR. */
|
|
41
|
+
export function conflictingHarnessDrafts(json: string): Draft[] {
|
|
42
|
+
const rows: unknown = JSON.parse(json);
|
|
43
|
+
if (!Array.isArray(rows)) throw new Error('gh PR inventory is not an array');
|
|
44
|
+
if (rows.some((row) => !row || typeof row !== 'object' || !Number.isSafeInteger(row.number)
|
|
45
|
+
|| row.number <= 0 || typeof row.headRefName !== 'string' || typeof row.isDraft !== 'boolean'
|
|
46
|
+
|| !('mergeable' in row) || !['string', 'boolean'].includes(typeof row.mergeable) && row.mergeable !== null)) {
|
|
47
|
+
throw new Error('gh PR inventory is incomplete');
|
|
48
|
+
}
|
|
49
|
+
return (rows as Draft[]).filter((row) => row.isDraft && row.headRefName.startsWith('self-impl/')
|
|
50
|
+
&& (row.mergeable === 'CONFLICTING' || row.mergeable === false));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** GitHub 는 병합 가능 여부를 늦게 계산한다 — 첫 조회에 UNKNOWN 이면 «충돌 0»이 아니라 «아직 못 잼»이다(10-05 첫 운영 실행이 조용히 0). */
|
|
54
|
+
export function unknownMergeableDrafts(json: string): number {
|
|
55
|
+
const rows = JSON.parse(json) as Draft[];
|
|
56
|
+
return rows.filter((row) => row.isDraft && row.headRefName.startsWith('self-impl/') && (row.mergeable === 'UNKNOWN' || row.mergeable === null)).length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function shadowSummary(rows: readonly ShadowRow[]): string {
|
|
60
|
+
const measured = rows.filter((row) => row.outcome !== 'unmeasured');
|
|
61
|
+
const recoverable = measured.filter((row) => row.outcome !== 'unresolved').length;
|
|
62
|
+
return `충돌 draft ${measured.length} 중 살릴 수 있음 ${recoverable} = ${measured.length ? Math.round(recoverable / measured.length * 100) : 0}%`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Shadow only: fetch a PR snapshot, detach it outside the repo, simulate merging fresh origin/main, then discard it. */
|
|
66
|
+
export async function runResyncShadow(options: ShadowOptions): Promise<ShadowRow[]> {
|
|
67
|
+
const { repo, stateRoot, limit = 10, run = runCommand, log = console.log, now = Date.now } = options;
|
|
68
|
+
if (!Number.isSafeInteger(limit) || limit < 1) throw new Error('--limit must be a positive integer');
|
|
69
|
+
const rows: ShadowRow[] = [];
|
|
70
|
+
const output = join(stateRoot, 'smart-merge', 'resync-shadow.jsonl');
|
|
71
|
+
const record = (row: ShadowRow): void => {
|
|
72
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
73
|
+
appendFileSync(output, `${JSON.stringify(row)}\n`);
|
|
74
|
+
rows.push(row);
|
|
75
|
+
debug.log('smart-merge.resync', 'shadow-result', { pr: row.pr, outcome: row.outcome, files: row.files });
|
|
76
|
+
};
|
|
77
|
+
let drafts: Draft[];
|
|
78
|
+
try {
|
|
79
|
+
// gh's mergeable GraphQL field is CONFLICTING/MERGEABLE/UNKNOWN (some adapters return false/true).
|
|
80
|
+
const inventory = mustRun(run, repo, 'gh', [
|
|
81
|
+
'pr', 'list', '--state', 'open', '--limit', '1000', '--json', 'number,isDraft,headRefName,mergeable',
|
|
82
|
+
]);
|
|
83
|
+
drafts = conflictingHarnessDrafts(inventory).slice(0, limit);
|
|
84
|
+
const unknown = unknownMergeableDrafts(inventory);
|
|
85
|
+
if (unknown) log(`병합 가능 여부 미계산 draft ${unknown} — GitHub 계산 뒤 다시 돌려라(이번 수에 안 들어감)`);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
log(`못 잼: GitHub draft 조회 실패 (${String(error)})`);
|
|
88
|
+
log(shadowSummary(rows));
|
|
89
|
+
return rows;
|
|
90
|
+
}
|
|
91
|
+
for (const draft of drafts) {
|
|
92
|
+
const start = now();
|
|
93
|
+
let temp: string | undefined;
|
|
94
|
+
let attached = false;
|
|
95
|
+
let outcome: ShadowOutcome = 'unmeasured';
|
|
96
|
+
let files: string[] = [];
|
|
97
|
+
try {
|
|
98
|
+
// refs/pull/N/head supports fork heads too; FETCH_HEAD is consumed before the next fetch.
|
|
99
|
+
mustRun(run, repo, 'git', ['fetch', 'origin', `refs/pull/${draft.number}/head`]);
|
|
100
|
+
temp = mkdtempSync(join(tmpdir(), 'elanous-resync-shadow-'));
|
|
101
|
+
mustRun(run, repo, 'git', ['worktree', 'add', '--detach', temp, 'FETCH_HEAD']);
|
|
102
|
+
attached = true;
|
|
103
|
+
const seam = options.git ?? defaultGitMergeSeam();
|
|
104
|
+
const observedGit: MergeGitSeam = {
|
|
105
|
+
...seam,
|
|
106
|
+
conflictedFiles: (wt) => { files = seam.conflictedFiles(wt); return files; },
|
|
107
|
+
};
|
|
108
|
+
const result = await mergeMainWithLlmResolve(temp, 'origin/main',
|
|
109
|
+
options.resolve ?? ((file, conflicted) => defaultLlmResolve(file, conflicted, 'origin/main', { worktreePath: temp })), observedGit);
|
|
110
|
+
if (result.status === 'merged' || result.status === 'up-to-date') outcome = 'clean';
|
|
111
|
+
else if (result.status === 'llm-resolved') {
|
|
112
|
+
files = result.resolvedFiles ?? files;
|
|
113
|
+
outcome = files.length > 0 && files.every((file) => file === NEXT_MD_PATH) ? 'resolved-deterministic' : 'resolved-llm';
|
|
114
|
+
} else if (result.status === 'conflict-unresolved') outcome = 'unresolved';
|
|
115
|
+
// A git error is a measurement failure, not evidence of an unresolvable conflict.
|
|
116
|
+
} catch {
|
|
117
|
+
outcome = 'unmeasured';
|
|
118
|
+
} finally {
|
|
119
|
+
if (attached && temp) {
|
|
120
|
+
try { mustRun(run, repo, 'git', ['worktree', 'remove', '--force', temp]); }
|
|
121
|
+
catch { outcome = 'unmeasured'; }
|
|
122
|
+
}
|
|
123
|
+
if (temp) {
|
|
124
|
+
try { rmSync(temp, { recursive: true, force: true }); }
|
|
125
|
+
catch { outcome = 'unmeasured'; }
|
|
126
|
+
}
|
|
127
|
+
record({ pr: draft.number, outcome, files, durationMs: Math.max(0, now() - start) });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
log(shadowSummary(rows));
|
|
131
|
+
return rows;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (import.meta.main) {
|
|
135
|
+
const args = process.argv.slice(2);
|
|
136
|
+
const valid = args.length === 0 || args.length === 2 && args[0] === '--limit' && /^[1-9]\d*$/.test(args[1]!);
|
|
137
|
+
if (!valid) {
|
|
138
|
+
console.error('usage: bun scripts/smart-merge/resync-shadow.ts [--limit N]');
|
|
139
|
+
process.exitCode = 2;
|
|
140
|
+
} else {
|
|
141
|
+
await runResyncShadow({
|
|
142
|
+
repo: mustRun(runCommand, process.cwd(), 'git', ['rev-parse', '--show-toplevel']).trim(),
|
|
143
|
+
stateRoot: process.env.ELANOUS_STATE_DIR || join(homedir(), '.elanous'),
|
|
144
|
+
...(args.length ? { limit: Number(args[1]) } : {}),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/acp/client.ts
CHANGED
|
@@ -302,7 +302,7 @@ const ACP_FORCED_BILLING_ENV_BY_BACKEND: Readonly<Record<string, Readonly<Record
|
|
|
302
302
|
};
|
|
303
303
|
|
|
304
304
|
function scrubAcpBillingEnv(spec: AcpBackendSpec, env: NodeJS.ProcessEnv): AcpBillingEnvScrubObservation {
|
|
305
|
-
const billingEnvScrubEnabled = getUserConfig().acp.scrubBillingEnv !== false;
|
|
305
|
+
const billingEnvScrubEnabled = spec.id === 'claude' || getUserConfig().acp.scrubBillingEnv !== false;
|
|
306
306
|
const scrubbedBillingEnv: string[] = [];
|
|
307
307
|
const forcedBillingEnv: string[] = [];
|
|
308
308
|
if (billingEnvScrubEnabled) {
|
|
@@ -378,6 +378,8 @@ export interface AcpAgentOpts {
|
|
|
378
378
|
/** Extra env vars to pass to the subprocess. Inherits from
|
|
379
379
|
* process.env unless overridden. */
|
|
380
380
|
env?: Record<string, string>;
|
|
381
|
+
/** Test seam: replace the child process without launching Claude. */
|
|
382
|
+
spawnChild?: typeof spawn;
|
|
381
383
|
/** Session-scoped Codex app-server configuration arguments. Ignored by
|
|
382
384
|
* non-Codex ACP backends. */
|
|
383
385
|
codexArgs?: readonly string[];
|
|
@@ -419,6 +421,7 @@ export class AcpAgent {
|
|
|
419
421
|
private readonly spec: AcpBackendSpec;
|
|
420
422
|
private readonly cwd: string;
|
|
421
423
|
private readonly env: NodeJS.ProcessEnv;
|
|
424
|
+
private readonly spawnChild: typeof spawn;
|
|
422
425
|
private readonly billingEnvScrub: AcpBillingEnvScrubObservation;
|
|
423
426
|
private readonly log: (msg: string) => void;
|
|
424
427
|
private readonly onCapabilities: ((capabilities: ElanousCapabilities) => void) | undefined;
|
|
@@ -461,6 +464,7 @@ export class AcpAgent {
|
|
|
461
464
|
}
|
|
462
465
|
this.billingEnvScrub = scrubAcpBillingEnv(this.spec, cleanEnv);
|
|
463
466
|
this.env = cleanEnv;
|
|
467
|
+
this.spawnChild = opts.spawnChild ?? spawn;
|
|
464
468
|
this.log = opts.log ?? ((m) => console.error(`[acp:${this.spec.id}] ${m}`));
|
|
465
469
|
this.onCapabilities = opts.onCapabilities;
|
|
466
470
|
this.permissionApprover = opts.permissionApprover ?? null;
|
|
@@ -501,7 +505,18 @@ export class AcpAgent {
|
|
|
501
505
|
throw new Error(`acp spawn: cwd does not exist: ${this.cwd}`);
|
|
502
506
|
}
|
|
503
507
|
|
|
504
|
-
|
|
508
|
+
if (this.spec.id === 'claude') {
|
|
509
|
+
const remaining = (claudeBackend.scrubEnv ?? []).filter(key => key in this.env);
|
|
510
|
+
debug.log('acp.claude', 'auth-source', {
|
|
511
|
+
apiKeyPresent: remaining.length > 0,
|
|
512
|
+
source: remaining.length > 0 ? 'billing-env' : 'subscription',
|
|
513
|
+
});
|
|
514
|
+
if (remaining.length > 0 && process.env.NODE_ENV !== 'test' && !process.env.ELANOUS_TEST_HOME) {
|
|
515
|
+
throw new Error('ACP Claude refused: billing authentication environment remains after scrub');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const child = this.spawnChild(resolvedBin.path, this.spec.args, {
|
|
505
520
|
cwd: this.cwd,
|
|
506
521
|
env: this.env,
|
|
507
522
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
type Agent = 'claude' | 'codex' | 'grok';
|
|
6
|
+
type Presence = 'present' | '«없음»';
|
|
7
|
+
|
|
8
|
+
function present(path: string): Presence {
|
|
9
|
+
return existsSync(path) ? 'present' : '«없음»';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function record(value: unknown): Record<string, unknown> {
|
|
13
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
14
|
+
? value as Record<string, unknown> : {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function json(path: string): Record<string, unknown> | null {
|
|
18
|
+
if (!existsSync(path)) return null;
|
|
19
|
+
return record(JSON.parse(readFileSync(path, 'utf8')));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Command strings can contain tokens even without a shell; export only known executable names.
|
|
23
|
+
function safeCommand(value: unknown): string | undefined {
|
|
24
|
+
if (typeof value !== 'string') return undefined;
|
|
25
|
+
return /^(?:npx|npm|node|bun|uv|uvx|python|python3|docker|deno|pnpm|yarn|bash|sh|claude|codex|grok|cct)$/.test(value)
|
|
26
|
+
? value : '<redacted>';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Key-shaped values (sk-…, ghp_…, AKIA…) pass the character check, so shape alone cannot prove a value is not a secret (10-05 post-review).
|
|
30
|
+
const SECRET_PREFIX = /^(?:sk-|sk_|pk_|rk_|xai-|ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|glpat-|AKIA|ASIA|AIza|ya29\.|xox[abprs]-|eyJ)/;
|
|
31
|
+
|
|
32
|
+
export function safeIdentifier(value: unknown): string | null {
|
|
33
|
+
if (typeof value !== 'string') return null;
|
|
34
|
+
const digits = (value.match(/[0-9]/g) ?? []).length;
|
|
35
|
+
const keyShaped = SECRET_PREFIX.test(value) || value.length > 40 || (value.length >= 20 && digits >= 8);
|
|
36
|
+
return /^[a-zA-Z0-9_.:-]{1,90}$/.test(value) && !keyShaped && !/(?:token|secret|password|api.?key|credential)/i.test(value)
|
|
37
|
+
? value : '<redacted>';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type McpSource = 'settings' | 'user' | 'project';
|
|
41
|
+
|
|
42
|
+
function mcpEntry(server: unknown, source: McpSource): Record<string, unknown> {
|
|
43
|
+
const body = record(server);
|
|
44
|
+
return {
|
|
45
|
+
source,
|
|
46
|
+
command: safeCommand(body.command) ?? null,
|
|
47
|
+
env: Object.fromEntries(Object.keys(record(body.env)).map(key => [key, '<redacted>'])),
|
|
48
|
+
headers: Object.fromEntries(Object.keys(record(body.headers)).map(key => [key, '<redacted>'])),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function collectMcp(
|
|
53
|
+
into: Record<string, unknown>,
|
|
54
|
+
servers: unknown,
|
|
55
|
+
source: McpSource,
|
|
56
|
+
): void {
|
|
57
|
+
for (const [name, server] of Object.entries(record(servers))) into[name] = mcpEntry(server, source);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hookGroups(hooks: Record<string, unknown>): Record<string, unknown[]> {
|
|
61
|
+
return Object.fromEntries(Object.entries(hooks).map(([event, groups]) => [event,
|
|
62
|
+
Array.isArray(groups) ? groups.map(group => {
|
|
63
|
+
const body = record(group);
|
|
64
|
+
const entries = body.hooks;
|
|
65
|
+
return {
|
|
66
|
+
matcher: safeIdentifier(body.matcher),
|
|
67
|
+
hooks: Array.isArray(entries)
|
|
68
|
+
? entries.map(entry => ({ command: safeCommand(record(entry).command) ?? null }))
|
|
69
|
+
: [],
|
|
70
|
+
};
|
|
71
|
+
}) : [],
|
|
72
|
+
]));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function claude(home: string): Record<string, unknown> {
|
|
76
|
+
const dir = join(home, '.claude');
|
|
77
|
+
const settingsPath = join(dir, 'settings.json');
|
|
78
|
+
const userPath = join(home, '.claude.json');
|
|
79
|
+
const settings = json(settingsPath);
|
|
80
|
+
const user = json(userPath);
|
|
81
|
+
const data = record(settings);
|
|
82
|
+
const userData = record(user);
|
|
83
|
+
const plugins = record(data.enabledPlugins);
|
|
84
|
+
const hooks = record(data.hooks);
|
|
85
|
+
const statusLine = record(data.statusLine);
|
|
86
|
+
const skillsDir = join(dir, 'skills');
|
|
87
|
+
const team = join(home, '.teamclaude');
|
|
88
|
+
const teamManifest = json(join(team, 'package.json'));
|
|
89
|
+
const teamVersion = typeof teamManifest?.version === 'string' && /^\d+\.\d+\.\d+(?:[-+.][a-zA-Z0-9.-]+)?$/.test(teamManifest.version)
|
|
90
|
+
? teamManifest.version : undefined;
|
|
91
|
+
const mcp: Record<string, unknown> = {};
|
|
92
|
+
if (settings !== null) collectMcp(mcp, data.mcpServers, 'settings');
|
|
93
|
+
if (user !== null) {
|
|
94
|
+
collectMcp(mcp, userData.mcpServers, 'user');
|
|
95
|
+
for (const project of Object.values(record(userData.projects))) {
|
|
96
|
+
collectMcp(mcp, record(project).mcpServers, 'project');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const mcpFilesMissing = settings === null && user === null;
|
|
100
|
+
return {
|
|
101
|
+
settings: settings === null ? '«없음»' : 'present',
|
|
102
|
+
userConfig: user === null ? '«없음»' : 'present',
|
|
103
|
+
permissionMode: safeIdentifier(record(data.permissions).defaultMode),
|
|
104
|
+
statusLine: settings === null ? '«없음»' : data.statusLine === undefined ? '«없음»' : {
|
|
105
|
+
type: safeIdentifier(statusLine.type),
|
|
106
|
+
command: safeCommand(statusLine.command) ?? null,
|
|
107
|
+
},
|
|
108
|
+
hooks: settings === null ? '«없음»' : hookGroups(hooks),
|
|
109
|
+
enabledPlugins: settings === null ? '«없음»' : Object.fromEntries(Object.entries(plugins).map(([name, enabled]) => [name, enabled === true])),
|
|
110
|
+
mcpServers: mcpFilesMissing ? '«없음»' : mcp,
|
|
111
|
+
skills: !existsSync(skillsDir) ? '«없음»' : readdirSync(skillsDir, { withFileTypes: true })
|
|
112
|
+
.filter(entry => entry.isDirectory()).map(entry => entry.name).sort(),
|
|
113
|
+
teamclaude: { installed: present(team), version: teamVersion ?? null },
|
|
114
|
+
auth: present(join(dir, 'auth.json')),
|
|
115
|
+
credentials: present(join(dir, '.credentials.json')),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function codex(home: string): Record<string, unknown> {
|
|
120
|
+
const dir = join(home, '.codex');
|
|
121
|
+
const file = join(dir, 'config.toml');
|
|
122
|
+
if (!existsSync(file)) return { config: '«없음»', model: null, profile: null, mcpServers: '«없음»', auth: present(join(dir, 'auth.json')) };
|
|
123
|
+
const data = record(Bun.TOML.parse(readFileSync(file, 'utf8')));
|
|
124
|
+
const mcp = record(data.mcp_servers);
|
|
125
|
+
return {
|
|
126
|
+
config: 'present',
|
|
127
|
+
model: safeIdentifier(data.model),
|
|
128
|
+
profile: safeIdentifier(data.profile),
|
|
129
|
+
mcpServers: Object.keys(mcp).sort(),
|
|
130
|
+
auth: present(join(dir, 'auth.json')),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function grok(home: string): Record<string, unknown> {
|
|
135
|
+
const dir = join(home, '.grok');
|
|
136
|
+
const jsonPath = join(dir, 'config.json');
|
|
137
|
+
const tomlPath = join(dir, 'config.toml');
|
|
138
|
+
const file = existsSync(jsonPath) ? jsonPath : tomlPath;
|
|
139
|
+
const data = !existsSync(file) ? null : file === jsonPath ? json(file) : record(Bun.TOML.parse(readFileSync(file, 'utf8')));
|
|
140
|
+
return {
|
|
141
|
+
config: data === null ? '«없음»' : 'present',
|
|
142
|
+
keys: data === null ? '«없음»' : Object.keys(data).sort(),
|
|
143
|
+
auth: present(join(dir, 'auth.json')),
|
|
144
|
+
credentials: present(join(dir, 'credentials.json')),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function captureAgentEnv(options: { agent?: Agent; home?: string; now?: Date } = {}): {
|
|
149
|
+
version: number; capturedAt: string; agents: Record<string, Record<string, unknown>>;
|
|
150
|
+
} {
|
|
151
|
+
const home = options.home ?? homedir();
|
|
152
|
+
const selected = options.agent ? [options.agent] : ['claude', 'codex', 'grok'] as const;
|
|
153
|
+
const agents: Record<string, Record<string, unknown>> = {};
|
|
154
|
+
for (const agent of selected) {
|
|
155
|
+
if (agent === 'claude') agents.claude = claude(home);
|
|
156
|
+
else if (agent === 'codex') agents.codex = codex(home);
|
|
157
|
+
else if (agent === 'grok') agents.grok = grok(home);
|
|
158
|
+
else throw new Error('Unknown agent');
|
|
159
|
+
}
|
|
160
|
+
return { version: 1, capturedAt: (options.now ?? new Date()).toISOString(), agents };
|
|
161
|
+
}
|
|
@@ -15,6 +15,8 @@ import { join, relative, resolve, isAbsolute, sep } from 'node:path';
|
|
|
15
15
|
import { startPty, mintPtyId, onPtyEvent, type PtyHandle, type StartOpts } from '../pty-shell/registry.js';
|
|
16
16
|
import { withChildPtyIdentity } from '../agent/pty-identity.js';
|
|
17
17
|
import { NESTED_AGENT_ENV_BLOCKLIST } from '../agent/nested-agent-env.js';
|
|
18
|
+
import { getNestDepth } from '../agent/nest-depth.js';
|
|
19
|
+
import { gateNestedElanousLaunch, nestedDepthCodexArgs, readNestedElanousDepth } from '../harness/nested-elanous-policy.js';
|
|
18
20
|
import { createWorktree, resolveMainRepoRoot, type CreateWorktreeOpts } from '../git-fs/worktree.js';
|
|
19
21
|
import { configuredWorktreeRoot } from '../user-config.js';
|
|
20
22
|
import { worktreeHasChanges, commitWorktree, changedFiles } from '../self-implement/seams.js';
|
|
@@ -321,7 +323,36 @@ export function resolveBackendSpawn(
|
|
|
321
323
|
// 지운 키 이름 — PTY 합성이 로그인 셸 캡처본에서 되살리지 않게 그대로 넘긴다(강제 env 로 다시 넣은 키는 뺀다).
|
|
322
324
|
// ⚠️ 과금 스크럽 키만 — 중첩 표지는 정체성 allowlist 가 다시 싣는 키가 있어 이 PR 의 범위 밖이다.
|
|
323
325
|
const unsetEnv = [...new Set(backend.scrubEnv ?? [])].filter((k) => !(k in env));
|
|
324
|
-
|
|
326
|
+
// Codex drops parent env under shell_environment_policy inherit=core, so the nested-depth
|
|
327
|
+
// marker rides on argv (`-c shell_environment_policy.set.ELANOUS_NESTED_DEPTH=<child>`).
|
|
328
|
+
// Backends that cannot carry it are refused only when a nested elanous launch was requested.
|
|
329
|
+
const args = [...backend.args, ...nestedDepthCodexArgs(backend.name, getNestDepth() + 1)];
|
|
330
|
+
return { cmd: backend.cmd, args, env, unsetEnv, nestedEnvRemovedCount, forcedEnv };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Refuse a requested nested elanous launch when this backend cannot carry the depth marker.
|
|
335
|
+
* Unset depth is 0. A normal mission spawn (`nestedLaunchRequested: false`) is never refused here.
|
|
336
|
+
*/
|
|
337
|
+
export function refuseNestedElanousOnBackend(input: {
|
|
338
|
+
readonly backendName: string;
|
|
339
|
+
readonly nestedLaunchRequested: boolean;
|
|
340
|
+
readonly allow?: boolean;
|
|
341
|
+
readonly configAllow?: boolean;
|
|
342
|
+
readonly env?: Record<string, string | undefined>;
|
|
343
|
+
}): { readonly refused: boolean; readonly reason?: string; readonly depth: number } {
|
|
344
|
+
const depth = readNestedElanousDepth(input.env);
|
|
345
|
+
const decision = gateNestedElanousLaunch({
|
|
346
|
+
backendName: input.backendName,
|
|
347
|
+
depth,
|
|
348
|
+
nestedLaunchRequested: input.nestedLaunchRequested,
|
|
349
|
+
...(input.allow === true ? { allow: true } : {}),
|
|
350
|
+
...(input.configAllow === true ? { configAllow: true } : {}),
|
|
351
|
+
});
|
|
352
|
+
if (input.nestedLaunchRequested && !decision.allowed) {
|
|
353
|
+
return { refused: true, reason: decision.reason, depth };
|
|
354
|
+
}
|
|
355
|
+
return { refused: false, depth };
|
|
325
356
|
}
|
|
326
357
|
|
|
327
358
|
/** Build the mission PTY options after identity is preallocated for child env propagation. */
|
|
@@ -491,6 +522,12 @@ export interface AgentMissionSpec {
|
|
|
491
522
|
memory?: boolean;
|
|
492
523
|
/** PTY 닉네임(휴먼 리더블·goto 로 나중 접근). 기본 branch. */
|
|
493
524
|
nickname?: string;
|
|
525
|
+
/**
|
|
526
|
+
* True only when this mission is itself a nested elanous launch.
|
|
527
|
+
* A backend that cannot carry the depth marker is then refused.
|
|
528
|
+
* Absent means an ordinary mission spawn — never refused for a missing marker.
|
|
529
|
+
*/
|
|
530
|
+
nestedElanousLaunch?: boolean;
|
|
494
531
|
/** Concurrent missions may share one decision stream alias without sharing a PTY or worktree. */
|
|
495
532
|
decisionMissionId?: string;
|
|
496
533
|
/** ⭐⭐ 그 브랜치를 이미 쥔 «소유» 워크트리가 있으면 지우지 말고 그대로 재사용하라고 요청한다
|
|
@@ -1534,6 +1571,19 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1534
1571
|
// 구독 모드 — backend 지정 env 키 스크럽 후 강제 env까지 적용해 cmd·args·env를 순수 seam으로 구성한다.
|
|
1535
1572
|
// forcedEnv 이름의 후속 로깅은 이 착지의 의도적 경계이며, spawn에는 같은 seam의 env가 그대로 흐른다.
|
|
1536
1573
|
const spawnParams = resolveBackendSpawn(backend, process.env);
|
|
1574
|
+
const nestedRefusal = refuseNestedElanousOnBackend({
|
|
1575
|
+
backendName: backend.name,
|
|
1576
|
+
nestedLaunchRequested: spec.nestedElanousLaunch === true,
|
|
1577
|
+
env: process.env,
|
|
1578
|
+
});
|
|
1579
|
+
if (nestedRefusal.refused) {
|
|
1580
|
+
debug.log('agent-mission.nested', 'refused', { agent: backend.name, depth: nestedRefusal.depth, reason: nestedRefusal.reason });
|
|
1581
|
+
return {
|
|
1582
|
+
ok: false, worktree: spec.workdir ?? '', branch: spec.branch ?? null, rounds: 0,
|
|
1583
|
+
evidencePath: null, committed: false, usedOmniCrawl: false,
|
|
1584
|
+
detail: `nested elanous refused (${nestedRefusal.reason})`,
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1537
1587
|
const env = spawnParams.env;
|
|
1538
1588
|
if (backend.name === 'claude') {
|
|
1539
1589
|
const status = (deps.checkClaudeSubscription ?? checkClaudeSubscription)({ env });
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { parseSince } from '../cli/logs-cli.js';
|
|
3
|
+
import {
|
|
4
|
+
SUPERVISOR_METRICS_DEFAULT_SINCE,
|
|
5
|
+
formatSupervisorMetrics,
|
|
6
|
+
loadSupervisorMetrics,
|
|
7
|
+
type ReadSupervisorLogs,
|
|
8
|
+
} from './supervisor-metrics.js';
|
|
9
|
+
|
|
10
|
+
export interface SupervisorMetricsCliDeps {
|
|
11
|
+
readonly read?: ReadSupervisorLogs;
|
|
12
|
+
readonly write?: (text: string) => void;
|
|
13
|
+
readonly nowMs?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function registerSupervisorMetricsCommand(parent: Command, deps: SupervisorMetricsCliDeps = {}): Command {
|
|
17
|
+
return parent.command('metrics')
|
|
18
|
+
.description('외부 에이전트 미션 슈퍼바이저 품질 지표 — 이미 남은 미션 기록만 읽는다')
|
|
19
|
+
.option('--since <window>', '조회 창 (30s/15m/2h/7d · 기본 7d)', SUPERVISOR_METRICS_DEFAULT_SINCE)
|
|
20
|
+
.option('--json', 'JSON 출력')
|
|
21
|
+
.action((opts: { since?: string; json?: boolean }) => {
|
|
22
|
+
const since = opts.since?.trim() || SUPERVISOR_METRICS_DEFAULT_SINCE;
|
|
23
|
+
const nowMs = deps.nowMs ?? Date.now();
|
|
24
|
+
const relative = /^(\d+)(s|m|h|d)$/.exec(since);
|
|
25
|
+
const unitMs = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 } as const;
|
|
26
|
+
// parseSince returns an absolute epoch (including for relative inputs); anchor relative windows to this invocation's clock.
|
|
27
|
+
const sinceMs = relative
|
|
28
|
+
? nowMs - Number(relative[1]) * unitMs[relative[2] as keyof typeof unitMs]
|
|
29
|
+
: parseSince(since);
|
|
30
|
+
const write = deps.write ?? ((text: string) => { process.stdout.write(text); });
|
|
31
|
+
if (sinceMs === null || !Number.isFinite(sinceMs)) {
|
|
32
|
+
write(`--since 를 읽지 못함: ${since}\n`);
|
|
33
|
+
process.exitCode = 2;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const report = loadSupervisorMetrics(since, sinceMs, nowMs, deps.read);
|
|
37
|
+
if (opts.json) write(`${JSON.stringify(report)}\n`);
|
|
38
|
+
else write(`${formatSupervisorMetrics(report)}\n`);
|
|
39
|
+
});
|
|
40
|
+
}
|