elanous 0.2.14 → 0.2.15
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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-33ffcc49ab45123b.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/app/approvals/{page-74383f890f075511.js → page-0e2c57fe6add4b2a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-e05a77df7e005b04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-b8c49f6b4fa2df91.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-976813c91d7394ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-fb260b9283dbf646.js → page-28b9448091776c06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-60c7a8e1a4fe62ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-2a52b316cb143d9c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-c3ce483c8d5db370.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-96f074f6538e8a2c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-e97be5334d83a9f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-955ec2da3be4c560.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-c1343f9499d86f3f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-d23b12bb92242558.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-04250f0e2c0d03df.js → page-e2ee2896811e98d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-5efbf81124c474ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-85f1a1fc0a82738c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-8ac8d71c2adf9c8a.js → page-da6c08aae0f92768.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-153c2b3bcc89e8de.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-94dc383d3d54db1a.js → page-87a11e6a05f15199.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-b3759cc14da80908.js → page-7d4182964407c09c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-0d6d23b40263a7ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-f5597e894c02cd33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-35bdeae2e2c17147.js → page-73735b64bfe1ae67.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-8fcec4c9ea2e7f70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-9817c7f9007466cc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-134c1bc762aee62c.js → page-97f9f43172a3a90e.js} +1 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -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 +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/expose-rubric-suggest.ts +18 -12
- 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/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/gate-node.ts +80 -14
- package/scripts/release-loop/vault-note-node.ts +240 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/agent-env/profile.ts +161 -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/briefing/brief-items.ts +121 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -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/pod-cli.ts +2 -0
- package/src/cli/release-cli.ts +61 -27
- package/src/cli/seat-requests-cli.ts +52 -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/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/harness/harness-cli-command.ts +111 -15
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +100 -11
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +16 -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/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 +62 -4
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/index.ts +77 -7
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +37 -15
- 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/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -0
- 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/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +11 -4
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/maturity/tui-slash-availability.ts +31 -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/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +58 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +86 -26
- 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 +126 -10
- package/src/self-dev/dev-pipeline.ts +8 -4
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- 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/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- 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 +187 -40
- 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 +2 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +1 -1
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- 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-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +147 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +29 -5
- 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/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/control/page-e6b84ab07d3567d5.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/loops/page-160089123a40dd91.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/release/page-e7e10cd3386222e5.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/page-341480ed8e3c1ea8.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/showroom/page-58043db78ba519ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.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/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.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 → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
|
@@ -30,7 +30,7 @@ import { LOGS_QUERY_SPEC, dispatchLogsQuery } from './logs-tool.js';
|
|
|
30
30
|
import { MISSION_DECIDE_SPEC, dispatchMissionDecide } from '../autopilot/mission-decide-tool.js';
|
|
31
31
|
import { COO_ADMIN_SPEC, dispatchCooAdmin } from './coo-admin-tool.js';
|
|
32
32
|
import { RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, dispatchReleaseStatus, dispatchReleaseChange } from './release-tool.js';
|
|
33
|
-
import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, dispatchOpsSeats, dispatchDecisionsPending } from './ops-facts-tool.js';
|
|
33
|
+
import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC, dispatchOpsSeats, dispatchDecisionsPending, dispatchProactMeter } from './ops-facts-tool.js';
|
|
34
34
|
|
|
35
35
|
/** memory_recall — 크로스서피스 기억(자기 발송 원장 회상). 도메인 무관(domain 필터는 옵션).
|
|
36
36
|
* finance-tools 에서 L2 로 이관(2026-07-08). */
|
|
@@ -142,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
|
|
|
142
142
|
},
|
|
143
143
|
},
|
|
144
144
|
};
|
|
145
|
-
export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
|
|
145
|
+
export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC];
|
|
146
146
|
|
|
147
147
|
export interface CoreTools {
|
|
148
148
|
specs: LLMToolSpec[];
|
|
@@ -159,10 +159,11 @@ export function buildCoreTools(recallSearch?: RecallSearch): CoreTools {
|
|
|
159
159
|
names,
|
|
160
160
|
dispatch: async (name, args, onPartial, context) => {
|
|
161
161
|
const h = CORE_TOOL_HANDLERS[name];
|
|
162
|
-
if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending') return { error: `unknown core tool: ${name}` };
|
|
162
|
+
if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending' && name !== 'proact_meter') return { error: `unknown core tool: ${name}` };
|
|
163
163
|
if (name === 'release_change') return dispatchReleaseChange(args, context);
|
|
164
164
|
if (name === 'ops_seats') return dispatchOpsSeats(args, context);
|
|
165
165
|
if (name === 'decisions_pending') return dispatchDecisionsPending(context);
|
|
166
|
+
if (name === 'proact_meter') return dispatchProactMeter(args, context);
|
|
166
167
|
if (name === 'memory_recall' || name === 'self_recall') {
|
|
167
168
|
return runRecall(name, args, context ?? { surface: 'skill' }, recallSearch, onPartial);
|
|
168
169
|
}
|
|
@@ -2,6 +2,7 @@ import type { LLMToolSpec } from '../llm.js';
|
|
|
2
2
|
import type { ToolRuntimeContext } from '../tool-runtime/types.js';
|
|
3
3
|
import { debug } from '../debug/log.js';
|
|
4
4
|
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
5
|
+
import { readProactMeter, type ProactMeter } from '../decisions/proact-meter.js';
|
|
5
6
|
import { buildSeatsBoard, todayKst, type SeatsSources } from '../nexus/api/ops-seats.js';
|
|
6
7
|
import { liveSeatsSources } from '../nexus/api/ops-seats-sources.js';
|
|
7
8
|
|
|
@@ -11,6 +12,12 @@ export const OPS_SEATS_SPEC: LLMToolSpec = {
|
|
|
11
12
|
parameters: { type: 'object', properties: { date: { type: 'string', description: '조회할 KST 날짜 YYYY-MM-DD. 생략하면 KST 오늘.' } }, required: [] },
|
|
12
13
|
};
|
|
13
14
|
|
|
15
|
+
export const PROACT_METER_SPEC: LLMToolSpec = {
|
|
16
|
+
name: 'proact_meter',
|
|
17
|
+
description: '오너 전용 읽기 — 선제성 기준선. 지난 7 KST일마다 대표가 시키지 않고 시스템이 먼저 낸 수(offered), 그중 채택된 수(adopted), 대표가 먼저 물어서야 드러난 수(asked). 표본은 결정·지시·조율·루프 원문 링크를 가진다. 못 읽은 원천은 sources.read=false 이며 0이 아니다.',
|
|
18
|
+
parameters: { type: 'object', properties: { days: { type: 'number', description: 'KST 일수. 생략하면 7.' } }, required: [] },
|
|
19
|
+
};
|
|
20
|
+
|
|
14
21
|
export const DECISIONS_PENDING_SPEC: LLMToolSpec = {
|
|
15
22
|
name: 'decisions_pending',
|
|
16
23
|
description: '오너 전용 읽기 — 결정 원장의 열린 결정을 기한 순으로 조회한다. 결정 선택·기록은 하지 않는다.',
|
|
@@ -21,13 +28,14 @@ export interface OpsFactsDeps {
|
|
|
21
28
|
seatsSources?: () => SeatsSources;
|
|
22
29
|
listDecisions?: (filters: { status: 'open' }) => DecisionEntry[];
|
|
23
30
|
today?: () => string;
|
|
31
|
+
readProact?: (days: number) => ProactMeter;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
function ownerVerified(context?: ToolRuntimeContext): boolean {
|
|
27
35
|
return context?.requestOrigin !== 'external-agent' && !!context?.sessionId && !!context?.verifiedOwner?.id.trim();
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
function logCall(tool: 'ops_seats' | 'decisions_pending', ok: boolean, nulls: number): void {
|
|
38
|
+
function logCall(tool: 'ops_seats' | 'decisions_pending' | 'proact_meter', ok: boolean, nulls: number): void {
|
|
31
39
|
debug.log('ops.facts', 'call', { tool, ok, nulls });
|
|
32
40
|
}
|
|
33
41
|
|
|
@@ -56,6 +64,28 @@ export async function dispatchOpsSeats(args: Record<string, unknown>, context?:
|
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
66
|
|
|
67
|
+
/** Seven KST days. An unreadable ledger stays sources.read=false; it is not a zero day. */
|
|
68
|
+
export function dispatchProactMeter(args: Record<string, unknown>, context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
|
|
69
|
+
if (!ownerVerified(context)) {
|
|
70
|
+
logCall('proact_meter', false, 0);
|
|
71
|
+
return { error: '오너 확인이 필요한 조회다' };
|
|
72
|
+
}
|
|
73
|
+
const days = args.days === undefined ? 7 : args.days;
|
|
74
|
+
if (typeof days !== 'number' || !Number.isInteger(days) || days < 1 || days > 31) {
|
|
75
|
+
logCall('proact_meter', false, 0);
|
|
76
|
+
return { error: 'days 는 1 이상 31 이하의 정수여야 한다' };
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const meter = (deps.readProact ?? ((n: number) => readProactMeter({ days: n })))(days);
|
|
80
|
+
const unread = Object.values(meter.sources).filter(source => !source.read).length;
|
|
81
|
+
logCall('proact_meter', true, unread);
|
|
82
|
+
return meter;
|
|
83
|
+
} catch {
|
|
84
|
+
logCall('proact_meter', false, 1);
|
|
85
|
+
return { error: '선제성 원장을 읽지 못했다', days: null };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
59
89
|
/** Only the five requested fields leave the decision ledger. Undated entries follow dated entries. */
|
|
60
90
|
export function dispatchDecisionsPending(context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
|
|
61
91
|
if (!ownerVerified(context)) {
|
|
@@ -21,7 +21,7 @@ import { resolveChildLlmEffort, resolveImplementationChildModel } from '../self-
|
|
|
21
21
|
import { resolveHarnessTarget } from '../self-implement/harness-target-options.js';
|
|
22
22
|
import { queryRunningRuns } from '../self-implement/running-runs.js';
|
|
23
23
|
import { DevPipelineError } from '../self-dev/dev-pipeline.js';
|
|
24
|
-
import { runDraftSweep, type DraftSweepAdapters, type DraftSweepResult, type SweepDraft, type SweepMergedPr } from '../self-dev/draft-sweep.js';
|
|
24
|
+
import { runDraftSweep, sweepFailureReason, type DraftSweepAdapters, type DraftSweepResult, type SweepDraft, type SweepMergedPr, type SweepReviewGate } from '../self-dev/draft-sweep.js';
|
|
25
25
|
import { debug } from '../debug/log.js';
|
|
26
26
|
import { dispatchTask, type DispatchTaskInput, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
|
|
27
27
|
import { launchRequestId, preLaunchGate, type PreLaunchGateDeps } from '../execution-loop/launch-gate.js';
|
|
@@ -30,8 +30,10 @@ import { CODEX_PROVIDER, GROK_PROVIDER, decideBudget, decideLaunchBudget, readBu
|
|
|
30
30
|
import { DEFAULT_FALLBACK_CHAIN } from '../oauth/fallback-chain.js';
|
|
31
31
|
import { installDeliverableVerifyCliCommand, type InstallDeliverableVerifyCliDeps } from './deliverable-verify-cli.js';
|
|
32
32
|
import { getUserConfig } from '../user-config.js';
|
|
33
|
+
import { resolveRepositoryName } from './repository-name.js';
|
|
33
34
|
import { installHarnessCliSinkHook } from './harness-cli-sink.js';
|
|
34
35
|
import { addHarnessQueue, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueSeat } from './harness-queue.js';
|
|
36
|
+
import { parseDoorSince, queryLaunchDoors, renderLaunchDoors, type DoorTable } from './launch-stamp.js';
|
|
35
37
|
import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
|
|
36
38
|
import { runHarnessPlanRfc } from './harness-plan-rfc.js';
|
|
37
39
|
import { classifyGarbage, isGarbageProcessTarget, type GarbageProcess } from './process-garbage.js';
|
|
@@ -333,7 +335,7 @@ function assertHarnessChildLlmModel(opts: HarnessAskSayChildLlmOptions): void {
|
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
/** Shared post-parse gate: validate child model before any ask/say early return (including `--dry-run`). */
|
|
336
|
-
type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?:
|
|
338
|
+
type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: 'lite' | 'standard' | 'high'; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string; seat?: QueueSeat };
|
|
337
339
|
function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubstrate {
|
|
338
340
|
const resolved = resolveHarnessSubstrate({ flag: opts, config: getUserConfig(), env: process.env });
|
|
339
341
|
debug.log('harness.substrate', 'resolved', resolved);
|
|
@@ -552,9 +554,23 @@ function hasHarnessPodJobApplied(runId: string | undefined, output: string): boo
|
|
|
552
554
|
} finally { store.close(); }
|
|
553
555
|
}
|
|
554
556
|
|
|
557
|
+
/** 10-05 PODPROVIDER — say which child provider a local run will use (an unnamed choice follows config). */
|
|
558
|
+
function announceLocalChildProvider(opts: unknown): void {
|
|
559
|
+
const child = opts as HarnessAskSayChildLlmOptions;
|
|
560
|
+
if (!child.childLlmProvider?.trim()) return;
|
|
561
|
+
console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (local)`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** `harness.authorOnPod` — 명시 true 만 켠다. 읽기 실패는 끔(호스트 저작)으로 둔다. */
|
|
565
|
+
function harnessAuthorOnPod(): boolean {
|
|
566
|
+
try { return getUserConfig().harness?.authorOnPod === true; }
|
|
567
|
+
catch { return false; }
|
|
568
|
+
}
|
|
569
|
+
|
|
555
570
|
/** ⭐ 런 계약의 실행 칸 = pod — 호스트는 그래프를 안 돌리고 Pod 로 보낸다(harness-pod-dispatch.ts). */
|
|
556
571
|
async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-say', input: string, pool: string,
|
|
557
|
-
recordDispatch: (input: DispatchTaskInput, deps?: DispatchTaskDeps) => ReturnType<typeof dispatchTask> = dispatchTask
|
|
572
|
+
recordDispatch: (input: DispatchTaskInput, deps?: DispatchTaskDeps) => ReturnType<typeof dispatchTask> = dispatchTask,
|
|
573
|
+
authorOnPod = false): Promise<void> {
|
|
558
574
|
const o = opts as HarnessSubstrateOpts;
|
|
559
575
|
if (o.target !== undefined) {
|
|
560
576
|
debug.log('harness.pod', 'target-refused', { target: o.target });
|
|
@@ -588,7 +604,13 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
|
|
|
588
604
|
const { dispatchHarnessOnPod } = await import('./harness-pod-dispatch.js');
|
|
589
605
|
let output = '';
|
|
590
606
|
let runId: string | undefined;
|
|
591
|
-
const
|
|
607
|
+
const child = opts as HarnessAskSayChildLlmOptions;
|
|
608
|
+
// 10-05 PODPROVIDER: say which child provider the Pod will use when the launch named one.
|
|
609
|
+
if (child.childLlmProvider?.trim()) console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (pod · 자격 없으면 발사 전 거부)`);
|
|
610
|
+
const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(authorOnPod && entrance === 'cli-harness-say' ? { authorOnPod: true } : {}),
|
|
611
|
+
...(child.childLlmProvider?.trim() ? { childLlmProvider: child.childLlmProvider.trim() } : {}),
|
|
612
|
+
...(child.childLlmModel?.trim() ? { childLlmModel: child.childLlmModel.trim() } : {}),
|
|
613
|
+
...(child.childLlmEffort?.trim() ? { childLlmEffort: child.childLlmEffort.trim() } : {}), ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...((opts as HarnessAskSayChildLlmOptions).goalType ? { goalType: (opts as HarnessAskSayChildLlmOptions).goalType } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
|
|
592
614
|
{ onOutput: (text) => {
|
|
593
615
|
runId ??= harnessPodRunId(output + text);
|
|
594
616
|
output = (output + text).slice(-16_000);
|
|
@@ -1631,6 +1653,12 @@ export async function runHarnessBudget(opts: { json?: boolean } = {}, io: {
|
|
|
1631
1653
|
|
|
1632
1654
|
const DRAFT_SWEEP_REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
1633
1655
|
|
|
1656
|
+
/** A review body still names a must-fix. «must-fix 0» and «must-fix: 0» are a cleared list, not a remaining one. */
|
|
1657
|
+
function mustFixRemains(text: string): boolean {
|
|
1658
|
+
if (!/(?:^|\n)\s*(?:[-*]\s*)?(?:must-fix|must fix)\b/i.test(text)) return false;
|
|
1659
|
+
return !/(?:^|\n)\s*(?:[-*]\s*)?(?:must-fix|must fix)\s*[:=]?\s*0\b/i.test(text);
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1634
1662
|
type GithubPull = {
|
|
1635
1663
|
number: number;
|
|
1636
1664
|
title: string;
|
|
@@ -1814,6 +1842,36 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
|
|
|
1814
1842
|
}
|
|
1815
1843
|
return undefined;
|
|
1816
1844
|
};
|
|
1845
|
+
const reviewGateForDraft = (draft: SweepDraft, repository: string): SweepReviewGate | undefined => {
|
|
1846
|
+
const head = ghJson<{ head?: { sha?: string } }>(['api', `repos/${repository}/pulls/${draft.number}`], execute);
|
|
1847
|
+
const sha = head?.head?.sha;
|
|
1848
|
+
if (!sha) return undefined;
|
|
1849
|
+
const comments: string[] = [];
|
|
1850
|
+
for (let page = 1; page <= 100; page++) {
|
|
1851
|
+
const batch = ghJson<Array<{ body?: string }>>(['api', `repos/${repository}/issues/${draft.number}/comments?per_page=100&page=${page}`], execute);
|
|
1852
|
+
if (!Array.isArray(batch) || batch.length > 100) throw new Error('Incomplete review comment inventory');
|
|
1853
|
+
comments.push(...batch.map((comment) => comment.body ?? ''));
|
|
1854
|
+
if (batch.length < 100) break;
|
|
1855
|
+
if (page === 100) throw new Error('Review comment inventory exceeded pagination limit');
|
|
1856
|
+
}
|
|
1857
|
+
const reviews = ghJson<Array<{ state?: string; body?: string }>>(['api', `repos/${repository}/pulls/${draft.number}/reviews?per_page=100`], execute);
|
|
1858
|
+
if (!Array.isArray(reviews) || reviews.length > 100) throw new Error('Incomplete review inventory');
|
|
1859
|
+
const text = [...comments, ...reviews.map((review) => review.body ?? '')].join('\n');
|
|
1860
|
+
const status = ghJson<{ state?: string }>(['api', `repos/${repository}/commits/${sha}/status`], execute);
|
|
1861
|
+
const checkRuns = ghJson<{ check_runs?: Array<{ conclusion?: string | null }> }>(
|
|
1862
|
+
['api', `repos/${repository}/commits/${sha}/check-runs?per_page=100`], execute);
|
|
1863
|
+
if (!checkRuns || !Array.isArray(checkRuns.check_runs)) throw new Error('Incomplete check-run inventory');
|
|
1864
|
+
const conclusions = checkRuns.check_runs.map((run) => run.conclusion ?? null);
|
|
1865
|
+
const gatePass = (status?.state === 'success' || status?.state === undefined)
|
|
1866
|
+
&& conclusions.length > 0 && conclusions.every((conclusion) => conclusion === 'success' || conclusion === 'skipped' || conclusion === 'neutral');
|
|
1867
|
+
const gateFail = status?.state === 'failure' || status?.state === 'error'
|
|
1868
|
+
|| conclusions.some((conclusion) => conclusion === 'failure' || conclusion === 'cancelled' || conclusion === 'timed_out' || conclusion === 'action_required');
|
|
1869
|
+
return {
|
|
1870
|
+
...(reviews.some((review) => review.state === 'APPROVED') || /(?:^|\n)\s*VERDICT:\s*PASS\b/i.test(text) ? { review: 'pass' as const } : {}),
|
|
1871
|
+
...(mustFixRemains(text) ? { mustFix: true } : {}),
|
|
1872
|
+
...(gatePass && !gateFail ? { gate: 'pass' as const } : gateFail ? { gate: 'fail' as const } : {}),
|
|
1873
|
+
};
|
|
1874
|
+
};
|
|
1817
1875
|
const landingCommentsFor = (repository: string, number: number): string[] => {
|
|
1818
1876
|
const comments: string[] = [];
|
|
1819
1877
|
for (let page = 1; page <= 100; page++) {
|
|
@@ -1902,6 +1960,7 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
|
|
|
1902
1960
|
execute(['pr', 'close', String(number), '--repo', repository, '--comment', comment]);
|
|
1903
1961
|
},
|
|
1904
1962
|
getClaimOwner: async (repository, number) => claimCommentOwner(repository, number, execute),
|
|
1963
|
+
getReviewGate: async (draft, repository) => reviewGateForDraft(draft, repository),
|
|
1905
1964
|
};
|
|
1906
1965
|
}
|
|
1907
1966
|
|
|
@@ -1936,7 +1995,10 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
|
|
|
1936
1995
|
.action(async (number: string, opts: { owner?: string; note?: string; release?: boolean; repo?: string }) => {
|
|
1937
1996
|
try {
|
|
1938
1997
|
const execute = deps.execute ?? executeGh;
|
|
1939
|
-
const repository =
|
|
1998
|
+
const repository = resolveRepositoryName({
|
|
1999
|
+
repo: opts.repo ?? deps.repository?.(),
|
|
2000
|
+
executeGh: (args) => execute(args),
|
|
2001
|
+
});
|
|
1940
2002
|
if (!DRAFT_SWEEP_REPOSITORY.test(repository) || repository.includes('..')) throw new HarnessCliInputError(`invalid --repo (expected owner/name): ${repository}`);
|
|
1941
2003
|
if (!/^[1-9]\d*$/.test(number) || !Number.isSafeInteger(Number(number))) throw new HarnessCliInputError(`invalid PR number: ${number}`);
|
|
1942
2004
|
const owner = opts.owner?.trim();
|
|
@@ -1973,21 +2035,32 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
|
|
|
1973
2035
|
.action(async (opts: { apply?: boolean; json?: boolean; repo?: string }) => {
|
|
1974
2036
|
try {
|
|
1975
2037
|
const write = deps.write ?? console.log;
|
|
1976
|
-
const
|
|
1977
|
-
|
|
2038
|
+
const execute = deps.execute ?? executeGh;
|
|
2039
|
+
const repository = resolveRepositoryName({
|
|
2040
|
+
repo: opts.repo ?? deps.repository?.(),
|
|
2041
|
+
executeGh: (args) => execute(args),
|
|
2042
|
+
});
|
|
1978
2043
|
if (!DRAFT_SWEEP_REPOSITORY.test(repository) || repository.includes('..')) {
|
|
1979
2044
|
throw new HarnessCliInputError(`invalid --repo (expected owner/name): ${repository}`);
|
|
1980
2045
|
}
|
|
1981
2046
|
const result: DraftSweepResult = await runDraftSweep({ repository, apply: opts.apply === true, adapters: deps.adapters ?? githubDraftSweepAdapters(deps.execute) });
|
|
1982
|
-
|
|
2047
|
+
const reason = result.error ? sweepFailureReason(result.error) : undefined;
|
|
2048
|
+
if (opts.json) write(JSON.stringify(reason ? { ...result, error: reason } : result));
|
|
1983
2049
|
else {
|
|
1984
|
-
write(`draft sweep ${result.repository}: ${result.complete ? 'complete' : `incomplete (${
|
|
1985
|
-
for (const entry of result.entries) write(`#${entry.number} ${entry.action}: ${entry.reason}${entry.statusLabel ? ` → ${entry.statusLabel}` : ''}${entry.error ? ` ERROR: ${entry.error}` : ''}`);
|
|
1986
|
-
if (result.complete)
|
|
2050
|
+
write(`draft sweep ${result.repository}: ${result.complete ? 'complete' : `incomplete (${reason})`} · ${result.apply ? 'apply' : 'dry-run'}`);
|
|
2051
|
+
for (const entry of result.entries) write(`#${entry.number} ${entry.action}: ${entry.reason}${entry.statusLabel ? ` → ${entry.statusLabel}` : ''}${entry.error ? ` ERROR: ${sweepFailureReason(entry.error)}` : ''}`);
|
|
2052
|
+
if (result.complete) {
|
|
2053
|
+
const daily = result.daily
|
|
2054
|
+
? ` · 24h 넘음 ${result.daily.over24h} · 정리 대상 ${result.daily.closable} · 수확 대기 ${result.daily.harvestable} · 막힘 ${result.daily.blocked}`
|
|
2055
|
+
: '';
|
|
2056
|
+
write(`못 본 초안 ${result.unobserved ?? 0} · 이번에 닫음 ${result.closed ?? 0}${result.apply ? '' : '(dry-run)'} · 처리 중 ${result.claimed ?? 0} · 표식 만료 ${result.claimExpired ?? 0}${daily}`);
|
|
2057
|
+
}
|
|
2058
|
+
if (reason) write(reason);
|
|
1987
2059
|
}
|
|
1988
2060
|
if (!result.complete || result.entries.some((entry) => entry.error)) process.exitCode = 1;
|
|
1989
2061
|
} catch (error) {
|
|
1990
2062
|
console.error(humanErrorLine(error));
|
|
2063
|
+
try { debug.log('drafts.cleanup', 'failed', { reason: sweepFailureReason(error) }); } catch { /* observation is fail-soft */ }
|
|
1991
2064
|
process.exitCode = 1;
|
|
1992
2065
|
}
|
|
1993
2066
|
});
|
|
@@ -2248,6 +2321,27 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2248
2321
|
.command('harness')
|
|
2249
2322
|
.description('dev-harness worktree 수명 — 워크트리 생성(worktree add)·조회(worktrees)·정리(clean)·프로세스 관찰(processes)');
|
|
2250
2323
|
installHarnessCliSinkHook(harnessCmd, deps.registerSink, deps.resolveSurface);
|
|
2324
|
+
harnessCmd.command('doors').description('기간 안 런을 입구×대기열 경유 표로 읽는다')
|
|
2325
|
+
.option('--since <window>', '조회 창 (30m · 12h · 1d, 기본 1d)', '1d')
|
|
2326
|
+
.option('--json', '구조화 출력')
|
|
2327
|
+
.action((opts: { since?: string; json?: boolean }) => {
|
|
2328
|
+
const parsed = parseDoorSince(opts.since);
|
|
2329
|
+
if ('error' in parsed) {
|
|
2330
|
+
console.error(`❌ ${parsed.error}`);
|
|
2331
|
+
process.exitCode = 2;
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
let table: DoorTable;
|
|
2335
|
+
try {
|
|
2336
|
+
table = queryLaunchDoors({ sinceMs: parsed.sinceMs });
|
|
2337
|
+
} catch (error) {
|
|
2338
|
+
console.error(`❌ doors: ${error instanceof Error ? error.message : String(error)}`);
|
|
2339
|
+
process.exitCode = 1;
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
if (opts.json) console.log(JSON.stringify(table));
|
|
2343
|
+
else console.log(renderLaunchDoors(table));
|
|
2344
|
+
});
|
|
2251
2345
|
harnessCmd.command('incidents').description('최근 하니스 Pod 종료 사고 원장을 읽는다')
|
|
2252
2346
|
.option('--since <minutes>', '최근 몇 분의 종료를 볼지 (기본 15분)', '15')
|
|
2253
2347
|
.option('--json', '구조화 출력')
|
|
@@ -2294,7 +2388,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2294
2388
|
console.log(`${row.id} ${row.seat} ${row.kind} queued`);
|
|
2295
2389
|
}));
|
|
2296
2390
|
queue.command('list').description('대기·발사 원장 조회').action(() => queueAction(async () => {
|
|
2297
|
-
for (const row of listHarnessQueue(queueDeps)) console.log(`${row.id} ${row.seat} ${row.status} ${row.kind} ${row.input}`);
|
|
2391
|
+
for (const row of listHarnessQueue(queueDeps)) console.log(`${row.id} ${row.seat} ${row.status} ${row.kind} ${row.input}${row.waitingReason ? ` · ${row.waitingReason}` : ''}`);
|
|
2298
2392
|
}));
|
|
2299
2393
|
queue.command('remove <id>').description('대기 중 또는 종료 확인된 항목 제거').action((id: string) => queueAction(async () => {
|
|
2300
2394
|
if (!await removeHarnessQueue(id, queueDeps)) throw new Error(`queue item active or not found: ${id}`);
|
|
@@ -2303,7 +2397,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2303
2397
|
queue.command('reconcile <id>').description('불확정 발사를 확인하고 종료 또는 미발사 증거가 있으면 예약 해소').action((id: string) => queueAction(async () => {
|
|
2304
2398
|
console.log(`${id} ${await reconcileHarnessQueue(id, queueDeps)}`);
|
|
2305
2399
|
}));
|
|
2306
|
-
queue.command('tick').description('맨 앞 한
|
|
2400
|
+
queue.command('tick').description('자리별 맨 앞 한 건을 라운드로빈으로 돌며 자리 몫·Pod 풀 여유·마무리 관문을 확인하고 한 건 발사').action(() => queueAction(async () => {
|
|
2307
2401
|
const result = await tickHarnessQueue(queueDeps);
|
|
2308
2402
|
console.log(`${result.outcome}${result.item ? ` ${result.item.id}` : ''}: ${result.reason}`);
|
|
2309
2403
|
}));
|
|
@@ -2328,7 +2422,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2328
2422
|
process.exitCode = 2;
|
|
2329
2423
|
return Promise.resolve();
|
|
2330
2424
|
}
|
|
2331
|
-
return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts));
|
|
2425
|
+
return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : (announceLocalChildProvider(stampedOpts), ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts)));
|
|
2332
2426
|
},
|
|
2333
2427
|
deps.launchGate,
|
|
2334
2428
|
);
|
|
@@ -2353,7 +2447,9 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2353
2447
|
process.exitCode = 2;
|
|
2354
2448
|
return Promise.resolve();
|
|
2355
2449
|
}
|
|
2356
|
-
return resolved.substrate === 'pod'
|
|
2450
|
+
return resolved.substrate === 'pod'
|
|
2451
|
+
? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask, harnessAuthorOnPod())
|
|
2452
|
+
: (announceLocalChildProvider(stampedOpts), say(sentence, normalizeHarnessAskSayOptions(stampedOpts)));
|
|
2357
2453
|
},
|
|
2358
2454
|
deps.launchGate,
|
|
2359
2455
|
);
|
|
@@ -2,6 +2,7 @@ import { Database } from 'bun:sqlite';
|
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { redactSecretText } from '../debug/log.js';
|
|
5
|
+
import { refuseProductionLedgerWriteInTest } from './ledger-write-guard.js';
|
|
5
6
|
|
|
6
7
|
export interface RunExit {
|
|
7
8
|
runId: string;
|
|
@@ -83,6 +84,7 @@ export function readRunExits(root: string): RunExit[] {
|
|
|
83
84
|
|
|
84
85
|
/** SQLite's process-owned write lock serializes ledger checks and appends across processes. */
|
|
85
86
|
export function recordRunExit(row: RunExit, root: string): void {
|
|
87
|
+
if (refuseProductionLedgerWriteInTest(root, 'incidents')) return;
|
|
86
88
|
if (!row.runId?.trim()) return;
|
|
87
89
|
const at = Date.parse(row.at);
|
|
88
90
|
if (!Number.isFinite(at)) throw new Error('invalid incident exit time');
|
|
@@ -14,6 +14,11 @@ import { findGitDir } from '../git-fs/locate.js';
|
|
|
14
14
|
import { debug } from '../debug/log.js';
|
|
15
15
|
import { ELANOUS_ENTRY_SCRIPT } from '../self-implement/seams.js';
|
|
16
16
|
import { parsePodSourceSpec } from '../task-orchestrator/surfaces/pod-source-spec.js';
|
|
17
|
+
import { declaredGoalType, type GoalType } from '../self-implement/goal-author.js';
|
|
18
|
+
import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
19
|
+
import { HARNESS_RUN_ID_ENV } from './harness-space.js';
|
|
20
|
+
import { podMemoryLimitFor, type PodMemoryTier } from '../task-orchestrator/surfaces/self-implement-pod.js';
|
|
21
|
+
import { selectPodMemoryTier } from './pod-memory-policy.js';
|
|
17
22
|
|
|
18
23
|
export interface HarnessPodDispatchInput {
|
|
19
24
|
readonly entrance: 'cli-harness-ask' | 'cli-harness-say';
|
|
@@ -23,8 +28,9 @@ export interface HarnessPodDispatchInput {
|
|
|
23
28
|
readonly seat?: 'OP' | 'TC' | 'MK' | 'UX';
|
|
24
29
|
/** Host already recorded the observe-only dispatch; suppress a second decision in the Pod. */
|
|
25
30
|
readonly dispatchRecorded?: boolean;
|
|
26
|
-
/** Pod 메모리 등급(standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
|
|
27
|
-
readonly podMemory?:
|
|
31
|
+
/** Pod 메모리 등급(lite|standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
|
|
32
|
+
readonly podMemory?: PodMemoryTier;
|
|
33
|
+
readonly goalType?: GoalType;
|
|
28
34
|
/** L7c — Pod queue predecessor (goal ID or PR) — `ELANOUS_POD_AFTER` carries it to every Pod Job admission. */
|
|
29
35
|
readonly after?: string;
|
|
30
36
|
readonly autoMerge?: boolean;
|
|
@@ -32,6 +38,50 @@ export interface HarnessPodDispatchInput {
|
|
|
32
38
|
readonly json?: boolean;
|
|
33
39
|
/** 사람이 고른 원천 spec — `commit:` · `pr:` · `worktree:` · `files:`. 검증은 발사 전. */
|
|
34
40
|
readonly source?: string;
|
|
41
|
+
/** `--child-llm-provider/--child-llm-model/--child-llm-effort` — carried to `self orchestrate` so the Pod child uses them (10-05 PODPROVIDER). */
|
|
42
|
+
readonly childLlmProvider?: string;
|
|
43
|
+
readonly childLlmModel?: string;
|
|
44
|
+
readonly childLlmEffort?: string;
|
|
45
|
+
/** `harness.authorOnPod` — say 문장을 Pod 안에서 저작부터 돌린다. 호스트는 영수증만 남긴다. */
|
|
46
|
+
readonly authorOnPod?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 골 본문에 넣을 문장 — 코드 펜스가 문장을 삼키지 않게 백틱 런을 늘린다. */
|
|
50
|
+
export function fenceVerbatimSentence(sentence: string): { fence: string; body: string } {
|
|
51
|
+
const body = sentence.replace(/\r\n/g, '\n');
|
|
52
|
+
const longest = body.match(/`+/g)?.reduce((n, run) => Math.max(n, run.length), 0) ?? 0;
|
|
53
|
+
return { fence: '`'.repeat(Math.max(3, longest + 1)), body };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Pod 가 문장 그대로 `harness say` 를 돌리게 하는 골. 자격·풀은 기존 Pod 주입을 그대로 쓴다. */
|
|
57
|
+
export function authorOnPodGoal(sentence: string): string {
|
|
58
|
+
const { fence, body } = fenceVerbatimSentence(sentence);
|
|
59
|
+
return [
|
|
60
|
+
'# 골: Pod 안에서 harness say 저작',
|
|
61
|
+
'',
|
|
62
|
+
'- GoalType: implement',
|
|
63
|
+
'- 대상 경로: src/harness/harness-pod-dispatch.ts',
|
|
64
|
+
'',
|
|
65
|
+
'## 할 일',
|
|
66
|
+
'',
|
|
67
|
+
'이 Pod 안에서, 그리고 이 Pod 안에서만, 아래 펜스의 문장을 한 글자도 바꾸지 않고',
|
|
68
|
+
'`elanous harness say --substrate local` 의 인자로 실행한다.',
|
|
69
|
+
'그 한 발이 골 저작·리뷰·게이트·구현·PR 까지 이 Pod 안에서 끝낸다.',
|
|
70
|
+
'호스트에는 발사 영수증과 원장만 남는다. 호스트에서 저작·리뷰·게이트를 지휘하지 않는다.',
|
|
71
|
+
'저작 자격은 이 Job 에 이미 주입된 Pod 자격 규칙을 그대로 쓴다.',
|
|
72
|
+
'',
|
|
73
|
+
'## 문장 (verbatim)',
|
|
74
|
+
'',
|
|
75
|
+
fence,
|
|
76
|
+
body,
|
|
77
|
+
fence,
|
|
78
|
+
'',
|
|
79
|
+
'## 수용 기준',
|
|
80
|
+
'',
|
|
81
|
+
'- 위 펜스 문장만으로 `elanous harness say --substrate local` 한 발이 이 Pod 에서 저작부터 PR 까지 간다.',
|
|
82
|
+
'- 그 명령의 종료 코드가 0 이 아니면 이 골은 실패다.',
|
|
83
|
+
'',
|
|
84
|
+
].join('\n');
|
|
35
85
|
}
|
|
36
86
|
|
|
37
87
|
export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: string): string[] {
|
|
@@ -44,6 +94,9 @@ export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: str
|
|
|
44
94
|
...(input.base ? ['--base', input.base] : []),
|
|
45
95
|
...(input.json ? ['--json'] : []),
|
|
46
96
|
...(input.source ? ['--pod-source', input.source] : []),
|
|
97
|
+
...(input.childLlmProvider?.trim() ? ['--child-llm-provider', input.childLlmProvider.trim()] : []),
|
|
98
|
+
...(input.childLlmModel?.trim() ? ['--child-llm-model', input.childLlmModel.trim()] : []),
|
|
99
|
+
...(input.childLlmEffort?.trim() ? ['--child-llm-effort', input.childLlmEffort.trim()] : []),
|
|
47
100
|
'--concurrency', '1',
|
|
48
101
|
];
|
|
49
102
|
}
|
|
@@ -63,34 +116,69 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
63
116
|
return 2;
|
|
64
117
|
}
|
|
65
118
|
}
|
|
119
|
+
const env = { ...process.env };
|
|
66
120
|
let tempDir: string | undefined;
|
|
67
|
-
let goalFile
|
|
68
|
-
let
|
|
121
|
+
let goalFile = '';
|
|
122
|
+
let goalText: string;
|
|
69
123
|
if (input.entrance === 'cli-harness-ask') {
|
|
70
124
|
goalFile = resolve(cwd, input.input);
|
|
71
|
-
|
|
125
|
+
goalText = (deps.readFile ?? ((p) => readFileSync(p, 'utf8')))(goalFile);
|
|
126
|
+
} else if (input.authorOnPod) {
|
|
127
|
+
const goal = authorOnPodGoal(input.input);
|
|
128
|
+
goalText = goal;
|
|
129
|
+
const root = findGitDir(cwd)?.root ?? cwd;
|
|
130
|
+
const runsDir = join(root, 'docs', 'goals');
|
|
131
|
+
mkdirSync(runsDir, { recursive: true });
|
|
132
|
+
tempDir = mkdtempSync(join(runsDir, '.pod-author-'));
|
|
133
|
+
goalFile = join(tempDir, 'GOAL-pod-author-2026-10-05.md');
|
|
134
|
+
writeFileSync(goalFile, goal, { mode: 0o600 });
|
|
135
|
+
const runId = env[HARNESS_RUN_ID_ENV]?.trim();
|
|
136
|
+
const receipt = { entrance: input.entrance, host: 'receipt-only', authorOnPod: true, goalFile };
|
|
137
|
+
debug.log('harness.substrate', 'author-on-pod-receipt', receipt);
|
|
138
|
+
if (runId) {
|
|
139
|
+
try { appendRunLedgerEntry({ runId, event: 'author-on-pod-receipt', data: receipt }); }
|
|
140
|
+
catch (error) { debug.log('harness.substrate', 'author-on-pod-receipt-unwritten', { reason: error instanceof Error ? error.message : String(error) }); }
|
|
141
|
+
}
|
|
72
142
|
} else {
|
|
73
143
|
const goal = input.input;
|
|
144
|
+
goalText = goal;
|
|
74
145
|
const runsDir = selfDevRunsDir();
|
|
75
146
|
mkdirSync(runsDir, { recursive: true });
|
|
76
147
|
tempDir = mkdtempSync(join(runsDir, 'pod-goal-'));
|
|
77
148
|
goalFile = join(tempDir, 'goal.txt');
|
|
78
149
|
writeFileSync(goalFile, goal, { mode: 0o600 });
|
|
79
|
-
goalChars = goal.length;
|
|
80
150
|
}
|
|
151
|
+
const goalChars = goalText.length;
|
|
81
152
|
const args = podOrchestrateArgs(input, goalFile);
|
|
82
153
|
debug.log('harness.substrate', 'goal-file', { entrance: input.entrance, mode: tempDir ? 'temp' : 'path' });
|
|
83
|
-
const
|
|
154
|
+
const goalType = input.goalType ?? declaredGoalType(goalText);
|
|
155
|
+
// A process-level memory tier is already an explicit selection; do not replace it with a goal-type default.
|
|
156
|
+
const existing = podMemoryLimitFor(goalText, env);
|
|
157
|
+
const selection = !input.podMemory && ['option', 'goal-line', 'pwa-auto', 'advise'].includes(existing.source)
|
|
158
|
+
? { tier: existing.tier, reason: 'existing-tier' as const }
|
|
159
|
+
: goalType || input.podMemory ? selectPodMemoryTier(goalType ?? 'implement', input.podMemory, existing.tier) : undefined;
|
|
160
|
+
if (selection) {
|
|
161
|
+
env.ELANOUS_POD_MEMORY_TIER = selection.tier;
|
|
162
|
+
env.ELANOUS_POD_MEMORY_REASON = selection.reason;
|
|
163
|
+
} else {
|
|
164
|
+
delete env.ELANOUS_POD_MEMORY_REASON;
|
|
165
|
+
}
|
|
166
|
+
if (goalType) env.ELANOUS_POD_GOAL_TYPE = goalType;
|
|
167
|
+
else delete env.ELANOUS_POD_GOAL_TYPE;
|
|
84
168
|
if (input.seat) env.ELANOUS_HARNESS_SEAT = input.seat;
|
|
85
169
|
delete env.ELANOUS_POD_GOAL_DOC;
|
|
86
170
|
delete env.ELANOUS_DISPATCH_RECORDED;
|
|
87
171
|
if (input.dispatchRecorded) env.ELANOUS_DISPATCH_RECORDED = '1';
|
|
88
|
-
|
|
172
|
+
debug.log('harness.substrate', 'memory-selected', { entrance: input.entrance, goalType,
|
|
173
|
+
tier: selection?.tier ?? existing.tier, reason: selection?.reason ?? existing.source,
|
|
174
|
+
memoryLimit: podMemoryLimitFor(goalText, env).limit });
|
|
89
175
|
delete env.ELANOUS_POD_AFTER;
|
|
90
176
|
if (input.after) env.ELANOUS_POD_AFTER = input.after;
|
|
91
|
-
if (input.
|
|
177
|
+
if (input.authorOnPod) env.ELANOUS_POD_AUTHOR_ON_POD = '1';
|
|
178
|
+
else delete env.ELANOUS_POD_AUTHOR_ON_POD;
|
|
179
|
+
if (input.entrance === 'cli-harness-ask' || input.authorOnPod) {
|
|
92
180
|
const root = findGitDir(cwd)?.root;
|
|
93
|
-
const path = resolve(cwd, input.input);
|
|
181
|
+
const path = input.authorOnPod ? goalFile : resolve(cwd, input.input);
|
|
94
182
|
const relativePath = root ? relative(realpathSync(root), realpathSync(path)) : '';
|
|
95
183
|
if (relativePath && relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)) {
|
|
96
184
|
env.ELANOUS_POD_GOAL_DOC = relativePath;
|
|
@@ -100,6 +188,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
100
188
|
}
|
|
101
189
|
debug.log('harness.substrate', 'dispatch-pod', {
|
|
102
190
|
entrance: input.entrance, podPool: input.podPool ?? null, podMemory: input.podMemory ?? null, after: input.after ?? null, autoMerge: input.autoMerge !== false,
|
|
191
|
+
childLlmProvider: input.childLlmProvider ?? null, childLlmModel: input.childLlmModel ?? null,
|
|
103
192
|
goalChars, ...(input.entrance === 'cli-harness-ask' ? { goalPath: input.input } : {}),
|
|
104
193
|
});
|
|
105
194
|
const run = deps.run ?? ((cmd: string, a: readonly string[], childEnv: NodeJS.ProcessEnv) => new Promise<number>((resolveStatus) => {
|
|
@@ -119,7 +208,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
119
208
|
child.once('error', () => { cleanup(); resolveStatus(1); });
|
|
120
209
|
child.once('close', (code, signal) => { cleanup(); resolveStatus(code ?? ((signal ?? forwarded) === 'SIGINT' ? 130 : 143)); });
|
|
121
210
|
}));
|
|
122
|
-
const cleanup = () => { if (tempDir) rmSync(tempDir, { recursive: true, force: true }); };
|
|
211
|
+
const cleanup = () => { if (tempDir && !input.authorOnPod) rmSync(tempDir, { recursive: true, force: true }); };
|
|
123
212
|
try {
|
|
124
213
|
const status = run(process.execPath, args, env);
|
|
125
214
|
if (typeof status === 'number' || status === null) {
|