elanous 0.2.25 → 0.2.28
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/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
|
@@ -75,6 +75,66 @@ export function _setCreateGoalAuthorDecomposeStepsForTesting(fn?: CreateGoalAuth
|
|
|
75
75
|
/** 진행 종료 스테이지(phase='end') — 그 외는 delta(start 제외). */
|
|
76
76
|
const END_STAGES: readonly SelfImplementProgressStage[] = ['pr-opened', 'aborted', 'gate-failed', 'pr-declined'];
|
|
77
77
|
|
|
78
|
+
// ⭐ 결과 네 꼴(HARNESS-RESULT-CONTRACT · RFC-tui-tool-surface-on-graph-harness §4 · 0.2.26) —
|
|
79
|
+
// 종전 `{observed:true}` 한 비트는 모델이 «안 됐다 → 직접 편집»으로 읽었다(UX S1-02 · 47 도구).
|
|
80
|
+
// ⇒ launched / pending / observed-only / failed 네 꼴로 구조화해 «다음 행동»을 문면으로 고정한다.
|
|
81
|
+
// ⚠️ launched 꼴은 SelfImplementResult 필드를 **그대로 보존**한다 — runId 조인·원장 소비자 무손상.
|
|
82
|
+
export type SelfImplementLaunchedToolResult = SelfImplementResult & { runId: string; kind: 'launched'; message: string; next: string; missing: string[] };
|
|
83
|
+
export type SelfImplementPendingToolResult = Partial<SelfImplementResult> & { kind: 'pending'; message: string; next: string; missing: string[] };
|
|
84
|
+
export type SelfImplementObservedOnlyToolResult = { kind: 'observed-only'; observed: true; message: string; next: string; missing: ['runId', 'node'] };
|
|
85
|
+
export type SelfImplementFailedToolResult = Partial<SelfImplementResult> & { kind: 'failed'; reason: string; message: string; next: string; missing: string[] };
|
|
86
|
+
export type SelfImplementToolResult = SelfImplementLaunchedToolResult | SelfImplementPendingToolResult | SelfImplementObservedOnlyToolResult | SelfImplementFailedToolResult;
|
|
87
|
+
|
|
88
|
+
/** 하니스가 런을 맡았다 — 파일 편집은 하니스 몫이므로 모델의 직접 Write/Edit 을 문면으로 막는다. */
|
|
89
|
+
export function buildLaunchedToolResult(result: SelfImplementResult): SelfImplementLaunchedToolResult | SelfImplementPendingToolResult {
|
|
90
|
+
if (!result.runId) return buildPendingToolResult(result);
|
|
91
|
+
const runId = result.runId;
|
|
92
|
+
const node = result.node || null;
|
|
93
|
+
const missing = node ? [] : ['node'];
|
|
94
|
+
const query = `eln self run ${runId}`;
|
|
95
|
+
return {
|
|
96
|
+
...result,
|
|
97
|
+
runId,
|
|
98
|
+
kind: 'launched',
|
|
99
|
+
next: query,
|
|
100
|
+
missing,
|
|
101
|
+
message: `하니스가 맡았습니다 · 런 ${runId} · 노드 ${node ?? '미관측'} · 다음 조회: ${query} · 이 턴에서 파일을 직접 편집하지 마세요`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 완료 또는 접수는 관측했지만 실제 런 ID가 없어 발사를 단언할 수 없다. */
|
|
106
|
+
export function buildPendingToolResult(result: SelfImplementResult): SelfImplementPendingToolResult {
|
|
107
|
+
const { runId: _unconfirmedRunId, ...observed } = result;
|
|
108
|
+
return {
|
|
109
|
+
...observed,
|
|
110
|
+
kind: 'pending',
|
|
111
|
+
next: 'eln self running-runs',
|
|
112
|
+
missing: ['runId', ...(result.node ? [] : ['node'])],
|
|
113
|
+
message: '하니스 응답을 받았지만 런 식별자는 아직 미관측입니다 · 진행 중 런 조회: eln self running-runs · 일치하는 런이 없으면 요청을 다시 보내세요 · 이 턴에서 파일을 직접 편집하지 마세요',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 관측 전용 — 런이 발사되지 «않았다»는 사실과 «구현 실패가 아니다»를 함께 말한다. */
|
|
118
|
+
export function buildObservedOnlyToolResult(): SelfImplementObservedOnlyToolResult {
|
|
119
|
+
return {
|
|
120
|
+
kind: 'observed-only',
|
|
121
|
+
observed: true,
|
|
122
|
+
missing: ['runId', 'node'],
|
|
123
|
+
next: '실제 발사를 원하면 관측 전용 모드를 끈 뒤 다시 요청하세요',
|
|
124
|
+
message: '관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요 · 실제 발사를 원하면 관측 전용 모드를 끈 뒤 다시 요청하세요',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** 거부·실패 — 이유와 다음 행동을 구조화해 모델이 재시도·포기·인계를 판단하게 한다. */
|
|
129
|
+
export function buildFailedToolResult(reason: string, result?: SelfImplementResult): SelfImplementFailedToolResult {
|
|
130
|
+
const next = result?.runId ? `eln self run ${result.runId}` : '요청과 실패 이유를 확인한 뒤 다시 요청하세요';
|
|
131
|
+
return {
|
|
132
|
+
...(result ?? {}), kind: 'failed', reason, next,
|
|
133
|
+
missing: [...(result?.runId ? [] : ['runId']), ...(result?.node ? [] : ['node'])],
|
|
134
|
+
message: `하니스가 발사되지 않았거나 실패했습니다: ${reason} · 다음: ${next} · 이 턴에서 파일을 직접 편집하지 마세요`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
78
138
|
/**
|
|
79
139
|
* ★ base seam 에 막(SurfaceUx) 배선을 얹는다(P0·테스트 가능). approvePr=ux.confirm(fail-closed)·
|
|
80
140
|
* onProgress=ux.progress. 코어(runSelfImplement)·다른 seam(worktree/implement/gate/openPr)은 무손상.
|
|
@@ -180,9 +240,10 @@ export async function dispatchSelfImplement(
|
|
|
180
240
|
authorGoal: SelfImplementGoalAuthor = writeAuthoredGoal,
|
|
181
241
|
runCli: RunSelfImplementCliFn = runSelfImplementCliCommand,
|
|
182
242
|
seamsFactory?: SelfImplementSeamsFactory,
|
|
183
|
-
): Promise<
|
|
243
|
+
): Promise<SelfImplementToolResult> {
|
|
184
244
|
const feature = typeof args.feature === 'string' ? args.feature.trim() : '';
|
|
185
|
-
if (!feature) return
|
|
245
|
+
if (!feature) return buildFailedToolResult('SelfImplement: `feature` is required');
|
|
246
|
+
try {
|
|
186
247
|
const base = typeof args.base === 'string' && args.base.trim() ? args.base.trim() : undefined;
|
|
187
248
|
const draft = typeof args.draft === 'boolean' ? args.draft : true;
|
|
188
249
|
const entry = resolveDispatchEntry(ctx);
|
|
@@ -216,7 +277,7 @@ export async function dispatchSelfImplement(
|
|
|
216
277
|
debug.log('daemon-tools.self-implement', 'runtime.observe-only-decision', { surface: ux.surface, observeOnly: observeOnly.enabled, observeOnlySource: observeOnly.source });
|
|
217
278
|
if (observeOnly.enabled) {
|
|
218
279
|
debug.log('daemon-tools.self-implement', 'runtime.observed', { surface: ux.surface, observeOnly: observeOnly.enabled, observeOnlySource: observeOnly.source });
|
|
219
|
-
return
|
|
280
|
+
return buildObservedOnlyToolResult();
|
|
220
281
|
}
|
|
221
282
|
|
|
222
283
|
let goalFile: string | undefined;
|
|
@@ -298,5 +359,12 @@ export async function dispatchSelfImplement(
|
|
|
298
359
|
// ⭐ runId 를 반드시 싣는다 — 관측이 전부 runId 로 키잉되므로(`self run <runId>`·`logs --grep`)
|
|
299
360
|
// 이게 없으면 **이 경계에서 조인이 끊긴다**(N3 재측정 2026-07-28: 계약은 섰는데 로그가 못 따라갔다).
|
|
300
361
|
debug.log('daemon-tools.self-implement', 'done', { runId: result.runId, stage: result.stage, ok: result.ok, pr: result.prUrl ?? null });
|
|
301
|
-
return result
|
|
362
|
+
return !result.ok
|
|
363
|
+
? buildFailedToolResult(result.detail ?? result.stage, result)
|
|
364
|
+
: result.runId ? buildLaunchedToolResult(result) : buildPendingToolResult(result);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
367
|
+
debug.log('daemon-tools.self-implement', 'failed', { reason, stage: 'dispatch' });
|
|
368
|
+
return buildFailedToolResult(reason);
|
|
369
|
+
}
|
|
302
370
|
}
|
|
@@ -49,6 +49,15 @@ export interface AddBriefItem {
|
|
|
49
49
|
dedupeWithinMs?: number;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export interface BriefTelegramFeedback {
|
|
53
|
+
botId: string;
|
|
54
|
+
chatId: number;
|
|
55
|
+
messageId: number;
|
|
56
|
+
updateId: string;
|
|
57
|
+
reaction: BriefReaction;
|
|
58
|
+
itemId?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
export interface BriefItemsOptions {
|
|
53
62
|
stateDir?: string;
|
|
54
63
|
now?: () => Date;
|
|
@@ -112,7 +121,11 @@ export function composeBriefMarkdown(items: readonly BriefItem[], slot: BriefSlo
|
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/** The ids are exactly the rows represented by the composed page, after deduplication. */
|
|
115
|
-
export function composeBriefPage(
|
|
124
|
+
export function composeBriefPage(
|
|
125
|
+
items: readonly BriefItem[], slot: BriefSendSlot, now: Date,
|
|
126
|
+
actions?: BriefWeeklyActions,
|
|
127
|
+
observedKinds?: ReadonlySet<BriefDomain>,
|
|
128
|
+
): { markdown: string; ids: number[] } {
|
|
116
129
|
const today = dateKey(now, { timeZone: KST });
|
|
117
130
|
// A duplicate left out of a delivered page stays unsent in the ledger, but must not reappear on a later page.
|
|
118
131
|
// A new undeliverable observation after a sent page is a new occurrence, not an old duplicate.
|
|
@@ -138,11 +151,22 @@ export function composeBriefPage(items: readonly BriefItem[], slot: BriefSendSlo
|
|
|
138
151
|
for (const item of decisions) lines.push(itemLine(item));
|
|
139
152
|
lines.push('');
|
|
140
153
|
}
|
|
141
|
-
|
|
154
|
+
const domains = [...BRIEF_DOMAINS];
|
|
155
|
+
// Measured action leads. An unobserved kind keeps its normal position; a sent-but-unobserved
|
|
156
|
+
// item is not evidence of being ignored. Keep every domain heading in this first slice.
|
|
157
|
+
if (actions && Object.values(actions.byKind).some(count => count.acted > 0)) {
|
|
158
|
+
domains.sort((a, b) => {
|
|
159
|
+
const x = actions.byKind[a], y = actions.byKind[b];
|
|
160
|
+
const xRate = observedKinds?.has(a) ? x.rate : 0;
|
|
161
|
+
const yRate = observedKinds?.has(b) ? y.rate : 0;
|
|
162
|
+
return yRate - xRate || BRIEF_DOMAINS.indexOf(a) - BRIEF_DOMAINS.indexOf(b);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
for (const domain of domains) {
|
|
142
166
|
const group = unique.filter(item => item.domain === domain && !decisionIds.has(item.id));
|
|
143
167
|
if (group.length === 0) continue;
|
|
144
168
|
lines.push(`## ${domain}`, '');
|
|
145
|
-
for (const item of group
|
|
169
|
+
for (const item of group) lines.push(itemLine(item));
|
|
146
170
|
lines.push('');
|
|
147
171
|
}
|
|
148
172
|
return { markdown: `${lines.join('\n').trimEnd()}\n`, ids: unique.map(item => item.id) };
|
|
@@ -203,12 +227,12 @@ export class BriefItemsLedger {
|
|
|
203
227
|
this.requirementFunnel = options.requirementFunnel ?? ((now) => readRequirementFunnel({ stateDir: options.stateDir, now }));
|
|
204
228
|
}
|
|
205
229
|
|
|
206
|
-
private using<T>(work: (db: Database) => T): T {
|
|
230
|
+
private using<T>(work: (db: Database) => T, busyTimeoutMs = 10000): T {
|
|
207
231
|
mkdirSync(dirname(this.path), { recursive: true });
|
|
208
232
|
const db = new Database(this.path, { create: true, strict: true });
|
|
209
233
|
try {
|
|
210
234
|
chmodSync(this.path, 0o600);
|
|
211
|
-
db.exec(
|
|
235
|
+
db.exec(`PRAGMA busy_timeout = ${busyTimeoutMs}`);
|
|
212
236
|
migrateLegacyItems(db, this.log);
|
|
213
237
|
db.exec(`CREATE TABLE IF NOT EXISTS items (
|
|
214
238
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -237,7 +261,20 @@ export class BriefItemsLedger {
|
|
|
237
261
|
reacted_at TEXT NOT NULL
|
|
238
262
|
);
|
|
239
263
|
CREATE INDEX IF NOT EXISTS reactions_item_time ON reactions(item_id, reacted_at);
|
|
240
|
-
CREATE INDEX IF NOT EXISTS sends_time ON sends(sent_at)
|
|
264
|
+
CREATE INDEX IF NOT EXISTS sends_time ON sends(sent_at);
|
|
265
|
+
CREATE TABLE IF NOT EXISTS telegram_sends (
|
|
266
|
+
bot_id TEXT NOT NULL,
|
|
267
|
+
chat_id INTEGER NOT NULL,
|
|
268
|
+
message_id INTEGER NOT NULL,
|
|
269
|
+
item_id INTEGER NOT NULL REFERENCES items(id),
|
|
270
|
+
PRIMARY KEY (bot_id, chat_id, message_id, item_id)
|
|
271
|
+
);
|
|
272
|
+
CREATE TABLE IF NOT EXISTS telegram_feedback_events (
|
|
273
|
+
bot_id TEXT NOT NULL,
|
|
274
|
+
chat_id INTEGER NOT NULL,
|
|
275
|
+
update_id TEXT NOT NULL,
|
|
276
|
+
PRIMARY KEY (bot_id, chat_id, update_id)
|
|
277
|
+
);`);
|
|
241
278
|
if (!(db.query("SELECT name FROM pragma_table_info('items')").all() as { name: string }[]).some(column => column.name === 'dedupe_key')) {
|
|
242
279
|
db.exec('ALTER TABLE items ADD COLUMN dedupe_key TEXT');
|
|
243
280
|
}
|
|
@@ -318,34 +355,72 @@ export class BriefItemsLedger {
|
|
|
318
355
|
this.log('briefing.items', 'reaction-recorded', { itemId, reaction });
|
|
319
356
|
}
|
|
320
357
|
|
|
358
|
+
/** Attribute one update to an item in its delivered Telegram message, without rewriting send history. */
|
|
359
|
+
recordTelegramFeedback(event: BriefTelegramFeedback): boolean {
|
|
360
|
+
if (!event.botId || !Number.isSafeInteger(event.chatId) || !Number.isSafeInteger(event.messageId)
|
|
361
|
+
|| event.messageId < 1 || !event.updateId || !(BRIEF_REACTIONS as readonly string[]).includes(event.reaction)) {
|
|
362
|
+
throw new BriefItemsInputError('invalid telegram feedback');
|
|
363
|
+
}
|
|
364
|
+
if (event.itemId !== undefined && (!Number.isSafeInteger(event.itemId) || event.itemId < 1)) {
|
|
365
|
+
throw new BriefItemsInputError('invalid item id');
|
|
366
|
+
}
|
|
367
|
+
const recorded = this.using(db => {
|
|
368
|
+
// Wait for an in-flight brief send before looking up its coordinates. A deferred read
|
|
369
|
+
// could see the pre-send snapshot and discard an early callback as unmatched.
|
|
370
|
+
db.exec('BEGIN IMMEDIATE');
|
|
371
|
+
try {
|
|
372
|
+
const linked = db.query('SELECT item_id FROM telegram_sends WHERE bot_id = ? AND chat_id = ? AND message_id = ? ORDER BY item_id')
|
|
373
|
+
.all(event.botId, event.chatId, event.messageId) as Array<{ item_id: number }>;
|
|
374
|
+
const targets = event.itemId === undefined ? (linked.length === 1 ? linked : []) : linked.filter(row => row.item_id === event.itemId);
|
|
375
|
+
if (targets.length !== 1) { db.exec('COMMIT'); return false; }
|
|
376
|
+
if (!db.query('INSERT OR IGNORE INTO telegram_feedback_events (bot_id, chat_id, update_id) VALUES (?, ?, ?) RETURNING update_id')
|
|
377
|
+
.get(event.botId, event.chatId, event.updateId)) { db.exec('COMMIT'); return false; }
|
|
378
|
+
db.query('INSERT INTO reactions (item_id, reaction, reacted_at) VALUES (?, ?, ?)')
|
|
379
|
+
.run(targets[0]!.item_id, event.reaction, this.now().toISOString());
|
|
380
|
+
db.exec('COMMIT');
|
|
381
|
+
return targets[0]!.item_id;
|
|
382
|
+
} catch (cause) {
|
|
383
|
+
try { db.exec('ROLLBACK'); } catch { /* connection closes next */ }
|
|
384
|
+
throw cause;
|
|
385
|
+
}
|
|
386
|
+
}, 120000);
|
|
387
|
+
if (recorded) this.log('briefing.items', 'reaction-recorded', { itemId: recorded, reaction: event.reaction, source: 'telegram' });
|
|
388
|
+
return recorded !== false;
|
|
389
|
+
}
|
|
390
|
+
|
|
321
391
|
/** KST Monday through now: distinct sent items acted on (button/reply/correction), grouped by domain. */
|
|
322
392
|
weeklyActions(): BriefWeeklyActions {
|
|
323
|
-
|
|
393
|
+
return this.using(db => this.weeklyActionsFromDb(db, this.now()));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private weekStart(now: Date): string {
|
|
324
397
|
const today = dateKey(now, { timeZone: KST });
|
|
325
398
|
const day = new Date(`${today}T00:00:00Z`);
|
|
326
399
|
day.setUTCDate(day.getUTCDate() - ((day.getUTCDay() + 6) % 7));
|
|
327
|
-
|
|
400
|
+
return new Date(`${day.toISOString().slice(0, 10)}T00:00:00+09:00`).toISOString();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private weeklyActionsFromDb(db: Database, now: Date): BriefWeeklyActions {
|
|
404
|
+
const since = this.weekStart(now);
|
|
328
405
|
const until = now.toISOString();
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
FROM
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return { acted, total, rate: total === 0 ? 0 : acted / total, byKind };
|
|
348
|
-
});
|
|
406
|
+
const rows = db.query(`SELECT items.domain AS kind,
|
|
407
|
+
COUNT(*) AS total,
|
|
408
|
+
SUM(CASE WHEN EXISTS (
|
|
409
|
+
SELECT 1 FROM reactions r WHERE r.item_id = items.id
|
|
410
|
+
AND r.reaction IN ('버튼', '답장', '정정') AND r.reacted_at >= ? AND r.reacted_at <= ?
|
|
411
|
+
) THEN 1 ELSE 0 END) AS acted
|
|
412
|
+
FROM items WHERE EXISTS (
|
|
413
|
+
SELECT 1 FROM sends s WHERE s.item_id = items.id AND s.sent_at >= ? AND s.sent_at <= ?
|
|
414
|
+
) GROUP BY items.domain`).all(since, until, since, until) as Array<{ kind: BriefDomain; total: number; acted: number }>;
|
|
415
|
+
const byKind = Object.fromEntries(BRIEF_DOMAINS.map(kind => [kind, { acted: 0, total: 0, rate: 0 }])) as Record<BriefDomain, BriefActionCount>;
|
|
416
|
+
let acted = 0;
|
|
417
|
+
let total = 0;
|
|
418
|
+
for (const row of rows) {
|
|
419
|
+
byKind[row.kind] = { acted: row.acted, total: row.total, rate: row.acted / row.total };
|
|
420
|
+
acted += row.acted;
|
|
421
|
+
total += row.total;
|
|
422
|
+
}
|
|
423
|
+
return { acted, total, rate: total === 0 ? 0 : acted / total, byKind };
|
|
349
424
|
}
|
|
350
425
|
|
|
351
426
|
/** Insertion order. Optional domain filter. Item text is the stored original. */
|
|
@@ -388,7 +463,14 @@ export class BriefItemsLedger {
|
|
|
388
463
|
this.log('briefing.items', 'requirement-funnel-failed', { slot, error: error instanceof Error ? error.message : String(error) });
|
|
389
464
|
}
|
|
390
465
|
}
|
|
391
|
-
const
|
|
466
|
+
const since = this.weekStart(now);
|
|
467
|
+
const until = now.toISOString();
|
|
468
|
+
const observedKinds = new Set((db.query(`SELECT DISTINCT i.domain FROM items i
|
|
469
|
+
JOIN reactions r ON r.item_id = i.id
|
|
470
|
+
WHERE r.reacted_at >= ? AND r.reacted_at <= ? AND EXISTS (
|
|
471
|
+
SELECT 1 FROM sends s WHERE s.item_id = i.id AND s.sent_at >= ? AND s.sent_at <= ?
|
|
472
|
+
)`).all(since, until, since, until) as Array<{ domain: BriefDomain }>).map(row => row.domain));
|
|
473
|
+
const composed = composeBriefPage([...items, ...funnelItems], slot, now, this.weeklyActionsFromDb(db, now), observedKinds);
|
|
392
474
|
// Ephemeral funnel lines appear on the page but have no ledger row to mark sent.
|
|
393
475
|
const page = { markdown: composed.markdown, ids: composed.ids.filter(id => id > 0) };
|
|
394
476
|
const candidates = (slot === 'realtime' ? pendingRealtime(items, now) : items.filter(item => item.sent_at === null && beforeSlot(item.created_at, slot, now))).length;
|
|
@@ -403,24 +485,58 @@ export class BriefItemsLedger {
|
|
|
403
485
|
}
|
|
404
486
|
|
|
405
487
|
/** Hold the ledger's SQLite writer lock from selecting unsent rows through delivery and marking.
|
|
406
|
-
* A
|
|
488
|
+
* A delivery that fails before anything was sent rolls back and releases the lock; the next
|
|
489
|
+
* sender can retry. A delivery that fails after some messages already went out commits what
|
|
490
|
+
* the 대표 actually received (sends + telegram_sends) so a retry never re-sends them.
|
|
407
491
|
*/
|
|
408
|
-
withSendLock<T>(slot: string, work: (page: { markdown: string; ids: number[] }, markSent: (ids: readonly number[], slot: BriefSendSlot) => number) => T): T {
|
|
492
|
+
withSendLock<T>(slot: string, work: (page: { markdown: string; ids: number[]; itemLines: ReadonlyMap<string, readonly number[]> }, markSent: (ids: readonly number[], slot: BriefSendSlot, telegram?: { botId: string; chatId: number; messageId: number } | ReadonlyArray<{ botId: string; chatId: number; messageId: number; itemId: number }>) => number) => T): T {
|
|
409
493
|
if (slot !== 'realtime' && !(BRIEF_SLOTS as readonly string[]).includes(slot)) throw new BriefItemsInputError('invalid slot');
|
|
410
494
|
return this.using(db => {
|
|
411
495
|
db.exec('BEGIN IMMEDIATE');
|
|
496
|
+
let marked = false;
|
|
412
497
|
try {
|
|
413
498
|
const page = this.pageFromDb(db, slot as BriefSendSlot);
|
|
414
|
-
const
|
|
415
|
-
|
|
499
|
+
const itemIds = new Set(page.ids);
|
|
500
|
+
const itemLines = new Map<string, number[]>();
|
|
501
|
+
for (const item of this.itemsFromDb(db).filter(item => itemIds.has(item.id))) {
|
|
502
|
+
const line = itemLine(item);
|
|
503
|
+
itemLines.set(line, [...(itemLines.get(line) ?? []), item.id]);
|
|
504
|
+
}
|
|
505
|
+
const result = work({ ...page, itemLines }, (ids, sentSlot, telegram) => {
|
|
506
|
+
const pageIdSet = new Set(page.ids);
|
|
507
|
+
if (sentSlot !== slot || ids.length === 0 || new Set(ids).size !== ids.length
|
|
508
|
+
|| ids.some(id => !pageIdSet.has(id))) {
|
|
416
509
|
throw new BriefItemsInputError('marked ids must match composed page');
|
|
417
510
|
}
|
|
418
|
-
|
|
511
|
+
const coordinates = telegram === undefined ? [] : Array.isArray(telegram)
|
|
512
|
+
? telegram : ids.map(itemId => ({ ...telegram, itemId }));
|
|
513
|
+
if (telegram && (coordinates.some(row => !row.botId || !Number.isSafeInteger(row.chatId)
|
|
514
|
+
|| !Number.isSafeInteger(row.messageId) || row.messageId < 1 || !ids.includes(row.itemId))
|
|
515
|
+
|| new Set(coordinates.map(row => row.itemId)).size !== ids.length)) {
|
|
516
|
+
throw new BriefItemsInputError('invalid telegram send');
|
|
517
|
+
}
|
|
518
|
+
db.exec('SAVEPOINT brief_mark');
|
|
519
|
+
try {
|
|
520
|
+
const count = this.markSentInDb(db, ids, sentSlot);
|
|
521
|
+
if (telegram) {
|
|
522
|
+
const insert = db.query('INSERT OR IGNORE INTO telegram_sends (bot_id, chat_id, message_id, item_id) VALUES (?, ?, ?, ?)');
|
|
523
|
+
for (const row of coordinates) insert.run(row.botId, row.chatId, row.messageId, row.itemId);
|
|
524
|
+
}
|
|
525
|
+
db.exec('RELEASE brief_mark');
|
|
526
|
+
marked = true;
|
|
527
|
+
return count;
|
|
528
|
+
} catch (cause) {
|
|
529
|
+
db.exec('ROLLBACK TO brief_mark');
|
|
530
|
+
db.exec('RELEASE brief_mark');
|
|
531
|
+
throw cause;
|
|
532
|
+
}
|
|
419
533
|
});
|
|
420
534
|
db.exec('COMMIT');
|
|
421
535
|
return result;
|
|
422
536
|
} catch (cause) {
|
|
423
|
-
|
|
537
|
+
// The only writes inside this lock are the marks; committing on failure preserves what
|
|
538
|
+
// the 대표 already received, so a retry sends only the unsent remainder.
|
|
539
|
+
try { db.exec(marked ? 'COMMIT' : 'ROLLBACK'); } catch { /* the connection closes next */ }
|
|
424
540
|
throw cause;
|
|
425
541
|
}
|
|
426
542
|
});
|
package/src/budget/llm-cost.ts
CHANGED
|
@@ -24,7 +24,11 @@ export interface LlmPricingUsd {
|
|
|
24
24
|
longContext?: { thresholdTokens: number; inputPerM: number; outputPerM: number; cacheReadPerM?: number; cacheWritePerM?: number };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** AWS Bedrock Global Standard prices, USD per million tokens (AWS pricing API, ap-northeast-2).
|
|
27
|
+
/** AWS Bedrock Global Standard prices, USD per million tokens (AWS pricing API, ap-northeast-2).
|
|
28
|
+
* ⛔ BEDROCK-COST-LEDGER(0.2.26) — mantle-openai 비-Anthropic Bedrock id(`openai.gpt-6-sol`·`zai.glm-5` 등
|
|
29
|
+
* `src/llm/bedrock.ts` BEDROCK_MODELS 의 `route:'mantle-openai'` 행)는 여기에 행이 «없다»: 단가 수치 근거가
|
|
30
|
+
* 하나도 확보되지 않았다. 근거 없는 id는 `isBedrockPricingModel` 게이트(`claude-` 시작)를 지나지 않아
|
|
31
|
+
* `unknownCost()` 로 `kind:'unknown'` 에 머문다 — 0원으로 꾸미지 않는다. */
|
|
28
32
|
export const BEDROCK_PRICING_USD: Readonly<Record<string, LlmPricingUsd>> = {
|
|
29
33
|
'claude-haiku-5-5': { inputPerM: 0.1, outputPerM: 0.5, cacheWritePerM: 0.125, cacheReadPerM: 0.01 },
|
|
30
34
|
'claude-sonnet-5-5': { inputPerM: 2, outputPerM: 10, cacheWritePerM: 2.5, cacheReadPerM: 0.1 },
|
|
@@ -299,7 +303,19 @@ export function estimateLlmCost(
|
|
|
299
303
|
|
|
300
304
|
/** Discriminated cost fields to spread onto an `llm.usage` observation. */
|
|
301
305
|
/** 과금 경로(BACKLOG C6 · hermes `resolve_billing_route`). 구독(OAuth)·local 은 «토큰당 청구가 없다». */
|
|
302
|
-
export type BillingRoute = 'subscription' | 'local' | 'api' | 'unknown';
|
|
306
|
+
export type BillingRoute = 'subscription' | 'local' | 'credit' | 'api' | 'unknown';
|
|
307
|
+
|
|
308
|
+
/** Credit is known only when the quota window is exhausted and credits are present. */
|
|
309
|
+
export function creditRouteFromQuotaSignal(signal: {
|
|
310
|
+
usedPercent?: number | null;
|
|
311
|
+
rateLimitReached?: boolean | null;
|
|
312
|
+
hasCredits?: boolean | null;
|
|
313
|
+
} | null): 'credit' | null {
|
|
314
|
+
if (!signal || signal.hasCredits !== true) return null;
|
|
315
|
+
const exhausted = signal.rateLimitReached === true
|
|
316
|
+
|| (typeof signal.usedPercent === 'number' && Number.isFinite(signal.usedPercent) && signal.usedPercent >= 100);
|
|
317
|
+
return exhausted ? 'credit' : null;
|
|
318
|
+
}
|
|
303
319
|
|
|
304
320
|
/** 구독·local 호출의 비용 — 청구 0 이지만 «API 로 샀다면」 값은 따로 남긴다(환산가 · 모르면 칸이 없다). */
|
|
305
321
|
/** provider 가 보고한 실제 청구액(OpenRouter `usage.cost`) — 카탈로그 추정과 다르면 이것이 맞다. */
|
|
@@ -319,14 +335,25 @@ export interface IncludedLlmCost {
|
|
|
319
335
|
apiEquivalentUsd?: number;
|
|
320
336
|
}
|
|
321
337
|
|
|
338
|
+
export interface CreditLlmCost {
|
|
339
|
+
kind: 'credit';
|
|
340
|
+
model: string;
|
|
341
|
+
usd: null;
|
|
342
|
+
apiEquivalentUsd?: number;
|
|
343
|
+
}
|
|
344
|
+
|
|
322
345
|
export function llmUsageCostFields(
|
|
323
346
|
model: string,
|
|
324
347
|
usage: Omit<LlmCostUsage, 'model'>,
|
|
325
348
|
opts?: EstimateLlmCostOptions,
|
|
326
349
|
billing?: BillingRoute,
|
|
327
|
-
): { cost: LlmCostEstimate | IncludedLlmCost | ActualLlmCost | ByokActualCost | ByokPartialCost } {
|
|
350
|
+
): { cost: LlmCostEstimate | IncludedLlmCost | CreditLlmCost | ActualLlmCost | ByokActualCost | ByokPartialCost } {
|
|
328
351
|
const resolved = opts ?? { configPricing: readLlmConfigPricing() };
|
|
329
352
|
const estimate = estimateLlmCost({ model, ...usage }, resolved);
|
|
353
|
+
// An explicit credit route owns this cost row; API/BYOK amounts belong to other routes.
|
|
354
|
+
if (billing === 'credit') {
|
|
355
|
+
return { cost: { kind: 'credit', model, usd: null, ...(estimate.kind === 'known' || estimate.kind === 'partial' ? { apiEquivalentUsd: estimate.usd } : {}) } };
|
|
356
|
+
}
|
|
330
357
|
// ⛔⭐ 구독을 API 단가로 «known» 이라 적지 않는다 — 09-25 벤치에서 codex 구독 팔이 `$7.81 known` 으로 보였다.
|
|
331
358
|
// ⭐ BACKLOG C7 — provider 가 실어 보낸 «실제 청구액»이 있으면 그것이 비용이다(추정은 곁에 남긴다).
|
|
332
359
|
// OR-BYOK-COST — BYOK 면 «OR 청구 + 상류 청구»가 비용이다(상류를 모르면 partial · 0 으로 가정하지 않는다).
|
|
@@ -33,6 +33,14 @@ export interface UsageOutcomesReport {
|
|
|
33
33
|
readonly unreadableLedgers: number;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export interface AutoLandRatioRow {
|
|
37
|
+
readonly selfLanded: number;
|
|
38
|
+
readonly handMerges: number | null;
|
|
39
|
+
readonly totalMerges: number | null;
|
|
40
|
+
readonly autoLandRatio: number | null;
|
|
41
|
+
readonly blockReasons: Readonly<Record<string, number>>;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
export interface UsageOutcomesDeps {
|
|
37
45
|
readonly dir?: string;
|
|
38
46
|
readonly nowMs?: number;
|
|
@@ -139,6 +147,30 @@ export function aggregateUsageOutcomes(
|
|
|
139
147
|
return { sinceDays, sinceMs, rows, unreadableLedgers };
|
|
140
148
|
}
|
|
141
149
|
|
|
150
|
+
/** Join the existing run-ledger aggregate with an externally measured hand-merge count. */
|
|
151
|
+
export function measureAutoLandRatio(report: UsageOutcomesReport, handMerges: number | null): AutoLandRatioRow {
|
|
152
|
+
const selfLanded = report.rows.reduce((sum, row) => sum + row.landed, 0);
|
|
153
|
+
const hand = handMerges !== null && Number.isSafeInteger(handMerges) && handMerges >= 0 ? handMerges : null;
|
|
154
|
+
const sum = hand === null ? null : selfLanded + hand;
|
|
155
|
+
// An empty report also represents a missing ledger directory; do not turn that into 0%.
|
|
156
|
+
const totalMerges = report.rows.length === 0 || report.unreadableLedgers > 0 || sum === null || !Number.isSafeInteger(sum) ? null : sum;
|
|
157
|
+
const autoLandRatio = totalMerges === null || totalMerges === 0 ? null : selfLanded / totalMerges;
|
|
158
|
+
const reasons = new Map<string, number>();
|
|
159
|
+
for (const row of report.rows) {
|
|
160
|
+
for (const [reason, count] of Object.entries(row.blockReasons)) {
|
|
161
|
+
reasons.set(reason, (reasons.get(reason) ?? 0) + count);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const blockReasons = Object.fromEntries([...reasons].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))) as Record<string, number>;
|
|
165
|
+
return { selfLanded, handMerges: hand, totalMerges, autoLandRatio, blockReasons };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function formatAutoLandRatio(row: AutoLandRatioRow): string {
|
|
169
|
+
const ratio = row.autoLandRatio === null ? '-' : `${Math.round(row.autoLandRatio * 1000) / 10}%`;
|
|
170
|
+
const reasons = Object.entries(row.blockReasons).map(([reason, count]) => `${reason}=${count}`).join(',') || '-';
|
|
171
|
+
return `자율 착지 ${row.selfLanded} / 전체 ${row.totalMerges ?? '-'} (${ratio}) · 손 착지 ${row.handMerges ?? '-'} · 멈춤 사유 ${reasons}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
142
174
|
export function queryUsageOutcomes(sinceDays: number, deps: UsageOutcomesDeps = {}): UsageOutcomesReport {
|
|
143
175
|
const days = Number.isFinite(sinceDays) && sinceDays > 0 ? sinceDays : 2;
|
|
144
176
|
const nowMs = deps.nowMs ?? Date.now();
|
|
@@ -3,6 +3,7 @@ import { chmodSync, mkdirSync } from 'node:fs';
|
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
|
+
import type { PatentCandidate, PatentLedger } from '../patents/ledger.js';
|
|
6
7
|
|
|
7
8
|
export type ClaimStatus = 'draft' | 'verified' | 'public' | 'stale' | 'retracted';
|
|
8
9
|
|
|
@@ -41,6 +42,19 @@ function iso(value: string, field: string): string {
|
|
|
41
42
|
return date.toISOString();
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
function patentClaimRow(candidate: PatentCandidate, owner: string, audience: string): ClaimRow {
|
|
46
|
+
return {
|
|
47
|
+
id: candidate.id,
|
|
48
|
+
claim: candidate.title,
|
|
49
|
+
audience,
|
|
50
|
+
status: 'public',
|
|
51
|
+
owner,
|
|
52
|
+
contrast: null,
|
|
53
|
+
created_at: candidate.created_at,
|
|
54
|
+
updated_at: candidate.updated_at,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
export class ClaimsLedger {
|
|
45
59
|
readonly path: string;
|
|
46
60
|
private readonly now: () => Date;
|
|
@@ -229,4 +243,13 @@ export class ClaimsLedger {
|
|
|
229
243
|
};
|
|
230
244
|
}));
|
|
231
245
|
}
|
|
246
|
+
|
|
247
|
+
/** 특허 원장의 공개 허용 상태 후보만 읽어 공개 주장 행으로 내온다 — 조사·명세·출원 상태 후보는 공개 주장으로 나오지 않는다. */
|
|
248
|
+
patentClaims(patents: PatentLedger, options: { owner: string; audience: string }): ClaimRow[] {
|
|
249
|
+
const owner = required(options.owner, 'owner');
|
|
250
|
+
const audience = required(options.audience, 'audience');
|
|
251
|
+
const rows = patents.listPublicCandidates().map(candidate => patentClaimRow(candidate, owner, audience));
|
|
252
|
+
debug.log('claims.ledger', 'patent-claims', { count: rows.length });
|
|
253
|
+
return rows;
|
|
254
|
+
}
|
|
232
255
|
}
|
|
@@ -6,6 +6,7 @@ import { BRAND_RULES_UNMEASURED, checkBrand } from '../../scripts/brand/check.js
|
|
|
6
6
|
import { debug } from '../debug/log.js';
|
|
7
7
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
8
8
|
import type { ClaimsLedger, ClaimEvidence } from './claims-ledger.js';
|
|
9
|
+
import type { PatentLedger } from '../patents/ledger.js';
|
|
9
10
|
|
|
10
11
|
export type ClaimSurface = 'deck' | 'site' | 'notice';
|
|
11
12
|
export interface RenderedClaims {
|
|
@@ -49,12 +50,14 @@ export function representativeNumber(value: string): string | undefined {
|
|
|
49
50
|
return matches[0];
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurface; audience?: string; rulesPath?: string }): RenderedClaims {
|
|
53
|
+
export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurface; audience?: string; rulesPath?: string; patents?: { ledger: PatentLedger; owner: string; audience: string } }): RenderedClaims {
|
|
53
54
|
const { surface, audience } = options;
|
|
54
55
|
if (!(surface in headings)) throw new Error(`invalid claims surface: ${surface}`);
|
|
55
56
|
const excluded: RenderedClaims['excluded'] = [];
|
|
56
57
|
const candidates: Array<{ id: string; line: string }> = [];
|
|
57
|
-
|
|
58
|
+
const rows = ledger.list();
|
|
59
|
+
const known = new Set(rows.map(row => row.id));
|
|
60
|
+
for (const row of rows) {
|
|
58
61
|
if (audience && !row.audience.split(',').includes(audience)) continue;
|
|
59
62
|
if (row.status !== 'public') {
|
|
60
63
|
excluded.push({ id: row.id, reason: row.status === 'stale' ? 'stale' : row.status === 'retracted' ? 'retracted' : 'not-public' });
|
|
@@ -83,6 +86,23 @@ export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurf
|
|
|
83
86
|
candidates.push({ id: row.id, line: `- ${claim} — ${number} <!-- claim:${row.id} measured:${evidence.measured_at} cmd:${commentValue(evidence.command)}${evidence.source ? ` source:${commentValue(evidence.source)}` : ''} --> <!-- src:${commentValue(evidence.source ?? evidence.command)} -->` });
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
// 공개 허용 상태는 렌더 시점에 특허 원장에서 읽는다. 임의의 ClaimRow 배열은 받지 않는다.
|
|
90
|
+
// 저장된 주장과 같은 식별자를 가진 유도 행은 원장 행이 이긴다.
|
|
91
|
+
for (const row of options.patents ? ledger.patentClaims(options.patents.ledger, options.patents) : []) {
|
|
92
|
+
if (known.has(row.id)) continue;
|
|
93
|
+
if (audience && !row.audience.split(',').includes(audience)) continue;
|
|
94
|
+
if (row.status !== 'public') {
|
|
95
|
+
excluded.push({ id: row.id, reason: 'not-public' });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const patentClaim = firstSentence(row.claim);
|
|
99
|
+
if (!patentClaim) {
|
|
100
|
+
excluded.push({ id: row.id, reason: 'brand' });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
candidates.push({ id: row.id, line: `- ${patentClaim} <!-- claim:${row.id} patent --> <!-- src:patents -->` });
|
|
104
|
+
}
|
|
105
|
+
|
|
86
106
|
const rulesMissing = checkBrand(scopes[surface], [], options.rulesPath).missing;
|
|
87
107
|
if (rulesMissing) {
|
|
88
108
|
excluded.push(...candidates.map(({ id }) => ({ id, reason: 'rules-missing' as const })));
|