elanous 0.2.31 → 0.2.32
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/{1834-50625904996a8fa8.js → 1834-98b64172bc1efe77.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3535a4a69f1fafaa.js → page-a9717a67c2eecbaf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/{page-5a503541353e60a8.js → page-c0f1ca371f710b06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-6025101384e361a5.js → page-46430d45a9e04eac.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a645f00a06483e9a.js → page-a27b1e5a0d1b308f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-e9bab7413b028b28.js → page-8ea49a4c6dd2c6e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-e547e6ce55f1fb33.js → page-0e5a41fac1c75396.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-d27e1b9850fd54bf.js → page-31aaf1ed37c6df9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-0fec29c7ba01894b.js → page-c69247e7134d329d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-339a07410cd6f36b.js → layout-66dc5cd3a0999abe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-2fd93ed8c9f16580.js → page-d256f5d5d9552823.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-9b564dbff0fefc35.js → page-ce8f1907b8a6101c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-814ba51385225776.js → page-32866485913d6a04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-397d479626e5a459.js → not-found-7607cca9b84950c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-af1bba4e3251b728.js → page-eeab86176b03dc3b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-3ad3be5e7d3fdce9.js → page-5b93dbae3432b996.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-4da93ad35d4a8d28.js → page-f47e768b5b242b5a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-3a658d5fa6d16a25.js → page-95306f892aa2d0ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-0157de6e85f8bb6b.js → page-940fb6c217f6b470.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-0a07363b130182a1.js → page-8c66a3b24a745630.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-841d73d4ac44ed49.js → page-7b675dfd35c36fc9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-6f43a3d1146e6377.js → page-8bd0d3cf7665bcfc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-521ee23ac8c0abfa.js → page-c219f2ed171131be.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-a9992ee285efaa83.js → page-40f8fa21bdae955b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-3d48e85fa89ec04f.js → page-468f8313e4f1fbd9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-04d00ae9b0890349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-233c71cd5f68f0d7.js → page-4214b855d9233ea8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-70151d78de2f375e.js → page-936752c7aa58762e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-dcac33a3e2cb72c0.js → page-ec052ad359df1797.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0448145ccd5c45c1.js → page-fad650e4793adb65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +1 -0
- package/apps/pwa/out/_next/static/css/b5d88eba78a3d7bf.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 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- 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/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/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 +17 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/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/implement-loop.yaml +5 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +26 -1
- package/scripts/measure-si-task-cpu.ts +143 -0
- package/scripts/ratchets/test-interference-patterns.allowlist.txt +0 -2
- package/scripts/release-loop/prepare-node.ts +102 -4
- package/src/acp/core-turn-bridge.ts +16 -4
- package/src/acp/elanous-extensions.ts +3 -0
- package/src/acp/server.ts +3 -0
- package/src/acp/tool-trace.ts +186 -11
- package/src/agent/self-ambient-cache.ts +156 -0
- package/src/agent/self-ambient.ts +8 -1
- package/src/agent/task-notification.ts +5 -0
- package/src/agent-substrate/pr-reviewer.ts +3 -1
- package/src/ask-user-question/tool.ts +22 -0
- package/src/autopilot/build/llm-conflict-merge.ts +56 -10
- package/src/autopilot/pr-manager.ts +23 -5
- package/src/capture/frame-state-detect.ts +39 -0
- package/src/capture/tui-self-report.ts +134 -6
- package/src/chat/index.ts +61 -15
- package/src/chat/pickers/modal-runtime.ts +6 -0
- package/src/chat/pickers/modals.ts +8 -2
- package/src/cli/ask-bare.ts +1 -0
- package/src/cli/import-cli.ts +12 -0
- package/src/cli/log-event-names.ts +10 -0
- package/src/cli/msg-cli.ts +1 -0
- package/src/cli/pod-cli.ts +14 -3
- package/src/cli/pr-cli.ts +67 -32
- package/src/cli/pr-land-base-read.ts +34 -0
- package/src/cli/pr-land-heavy-remote.ts +81 -25
- package/src/cli/pr-land-overlap-evidence.ts +46 -2
- package/src/cli/pr-land-pod-shadow-job.ts +26 -3
- package/src/cli/pr-land-refusal.ts +20 -5
- package/src/cli/pty-takeover-cli.ts +88 -14
- package/src/cli/sim-cli.ts +18 -0
- package/src/context-bus/ops-events.ts +6 -2
- package/src/core-turn/run-core-turn.ts +1 -0
- package/src/core-turn/run-goal-loop.ts +21 -0
- package/src/core-turn/types.ts +3 -0
- package/src/dashboard/acp-active-model.ts +42 -1
- package/src/dashboard/background-jobs-status.ts +66 -0
- package/src/dashboard/index.ts +117 -44
- package/src/dashboard/input/agent-wake.ts +242 -0
- package/src/dashboard/input/chat-main-entry-runtime.ts +5 -0
- package/src/dashboard/input/external-input-host.ts +23 -9
- package/src/dashboard/input/mouse-wiring.ts +108 -11
- package/src/dashboard/log-click-runtime.ts +118 -2
- package/src/dashboard/turn-preamble.ts +116 -40
- package/src/dashboard/turn-session-layers.ts +4 -2
- package/src/dashboard/turn-start-segments.ts +222 -0
- package/src/display/prompt-row-owner.ts +146 -0
- package/src/display/resize-listener.ts +33 -2
- package/src/fold-stack.ts +4 -2
- package/src/git-fs/index.ts +263 -36
- package/src/harness/gate-reserve.ts +87 -0
- package/src/harness/harness-incidents.ts +13 -0
- package/src/harness/harness-queue.ts +15 -0
- package/src/index.ts +8 -6
- package/src/input/external-input-queue.ts +34 -9
- package/src/llm/route-decision.ts +36 -0
- package/src/llm/tool-loop-steered-rejection.ts +8 -0
- package/src/llm-request-shape.ts +209 -0
- package/src/llm.ts +166 -267
- package/src/loops/orchestrator/tick.ts +24 -2
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/mss/logging/render-categories.ts +5 -0
- package/src/ops/overview.ts +59 -2
- package/src/pr-tool/census.ts +19 -4
- package/src/pr-tool/inflight-registry.ts +143 -0
- package/src/pty-shell/pty-manifest.ts +12 -3
- package/src/pty-shell/tui-run-state.ts +34 -3
- package/src/seat-loop/traffic-signal.ts +2 -1
- package/src/self-dev/ask-launch-flow.ts +13 -0
- package/src/self-dev/draft-residue.ts +243 -24
- package/src/self-dev/shard-plan.ts +11 -3
- package/src/self-dev/shard-run.ts +103 -10
- package/src/self-implement/elanous-tui-spawn.ts +14 -0
- package/src/self-implement/follow-up-placement.ts +79 -0
- package/src/self-implement/gate-artifacts.ts +15 -4
- package/src/self-implement/gate-baseline.ts +96 -5
- package/src/self-implement/gate-cli.ts +7 -2
- package/src/self-implement/gate-remote.ts +26 -1
- package/src/self-implement/host-regate.ts +36 -4
- package/src/self-implement/orchestrator.ts +101 -11
- package/src/self-implement/run-ledger.ts +16 -14
- package/src/session-runtime/index.ts +22 -267
- package/src/shell-runner/background-surface.ts +15 -5
- package/src/skills/tools/agent.ts +39 -6
- package/src/skills/tools/list-dir.ts +11 -0
- package/src/skills/tools/schedule-wakeup.ts +408 -0
- package/src/skills/tools/shell.ts +245 -3
- package/src/status/bar.ts +26 -0
- package/src/steward/digest-delivery.ts +118 -0
- package/src/steward/hold-hygiene.ts +120 -0
- package/src/steward/triage.ts +10 -0
- package/src/surface/shell-runner-boot.ts +5 -2
- package/src/task-agent/task-hand.ts +24 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +65 -11
- package/src/task-orchestrator/surfaces/pod-lease.ts +98 -0
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-pool.ts +3 -1
- package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +525 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runner.ts +90 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +156 -2
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +7 -5
- package/src/tui/chat/execution-badge.ts +5 -1
- package/src/tui/input-lag-probe.ts +230 -0
- package/src/tui.ts +4 -0
- package/src/ux-sim/scenario.ts +255 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-2ebb8fd59b9461f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-349f2752d8cd37ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-149dc1caba411bb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +0 -1
- package/apps/pwa/out/_next/static/css/3f5fc6f96e2cf7b2.css +0 -1
- /package/apps/pwa/out/_next/static/{LSXfY0u9QtfDbejY0Ukf_ → IVKr3AlRUVLKsw2fh7eXF}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{LSXfY0u9QtfDbejY0Ukf_ → IVKr3AlRUVLKsw2fh7eXF}/_ssgManifest.js +0 -0
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { accessSync, constants as fsConstants, existsSync, lstatSync, readFileSy
|
|
|
6
6
|
import type { SelfDevRunParticipant, SelfDevRunState } from './self-dev/run-store.js';
|
|
7
7
|
import { applyConfigDirFlagFromArgv } from './cli/config-dir-flag.js';
|
|
8
8
|
import { LOGS_SINCE_OPTION, registerLogsCommands } from './cli/logs-cli.js';
|
|
9
|
+
import { registerSimCommands } from './cli/sim-cli.js';
|
|
9
10
|
import { registerDocsCommands } from './cli/docs-cli.js';
|
|
10
11
|
import { registerBenchCommands } from './cli/bench-cli.js';
|
|
11
12
|
import { registerRetroCommands } from './cli/retro-cli.js';
|
|
@@ -4370,7 +4371,7 @@ selfCmd
|
|
|
4370
4371
|
}
|
|
4371
4372
|
};
|
|
4372
4373
|
try {
|
|
4373
|
-
const { dispatchHeavyCheck } = await import('./self-implement/gate-remote.js');
|
|
4374
|
+
const { dispatchHeavyCheck, remoteSelfGateArgv } = await import('./self-implement/gate-remote.js');
|
|
4374
4375
|
const { runGitCommand } = await import('./git-fs/runner.js');
|
|
4375
4376
|
const topLevel = runGitCommand(process.cwd(), ['rev-parse', '--show-toplevel'], { encoding: 'utf8' });
|
|
4376
4377
|
if (topLevel.status !== 0) throw new Error(`git rev-parse --show-toplevel failed: ${topLevel.stderr.trim() || `rc=${topLevel.status}`}`);
|
|
@@ -4389,13 +4390,13 @@ selfCmd
|
|
|
4389
4390
|
const { remoteCommitSupportsArtifactDir, newRemoteArtifactDir } = await import('./self-implement/gate-artifacts.js');
|
|
4390
4391
|
remoteArtifacts = remoteCommitSupportsArtifactDir(repo) ? { dir: newRemoteArtifactDir() } : { unsupported: true };
|
|
4391
4392
|
}
|
|
4392
|
-
const remoteArtifactArgs = remoteArtifacts && 'dir' in remoteArtifacts ? ['--artifact-dir', remoteArtifacts.dir] : [];
|
|
4393
4393
|
// origin/main is pinned to this machine's origin/main on the host; any other base is passed as its resolved sha.
|
|
4394
|
-
const remoteArgv = (sha: (ref: string) => string) =>
|
|
4395
|
-
|
|
4394
|
+
const remoteArgv = (sha: (ref: string) => string) => remoteSelfGateArgv({
|
|
4395
|
+
changed: opts.changed, base: opts.base, shards, fixedShardDistribution: opts.fixedShardDistribution,
|
|
4396
|
+
includeImporters: opts.includeImporters,
|
|
4396
4397
|
// A measured commit without this option rejects it (exit ≠ 0) — read as not passed, never as a silent measured run.
|
|
4397
|
-
|
|
4398
|
-
|
|
4398
|
+
artifactDir: remoteArtifacts && 'dir' in remoteArtifacts ? remoteArtifacts.dir : undefined,
|
|
4399
|
+
}, sha);
|
|
4399
4400
|
const { gateHostReservationRoot } = await import('./release-loop/gate-host-reservation.js');
|
|
4400
4401
|
process.exitCode = await dispatchHeavyCheck({
|
|
4401
4402
|
tool: 'self-gate', repo, installPwa: true, remoteArgv, refs: opts.base ? [opts.base] : [], reservationRoot: gateHostReservationRoot(),
|
|
@@ -6175,6 +6176,7 @@ async function loadJevAccessOrExit(): Promise<import('./decide/jev.js').JevAcces
|
|
|
6175
6176
|
registerIntakeCommands(program);
|
|
6176
6177
|
|
|
6177
6178
|
registerLogsCommands(program);
|
|
6179
|
+
registerSimCommands(program);
|
|
6178
6180
|
|
|
6179
6181
|
// ── ad (marketing-ad pipeline entrance) ──
|
|
6180
6182
|
/** ⛔ 「있다」와 「읽을 수 있는 파일이다」는 다른 값 — 디렉토리·권한 없는 파일을 실사로 읽지 않는다. */
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
// - ⏹ 정확히 `/stop`·`/esc` 인 글(TUI-STOP-INPUT-BYPASS · #68): 대기열을 «건너뛰고» 지금 턴의 ESC 중단 경로
|
|
16
16
|
// (`requestStop` → 대시보드 escGate)를 바로 부른다. 대기 중인 일반 항목은 그대로·순서대로 남는다.
|
|
17
17
|
// 턴이 없으면 아무것도 안 하고 `tui-stop-no-active-turn`(멈출 길이 없으면 `tui-stop-undelivered`)으로 던져 바깥 호출자(rc≠0)에게 알린다.
|
|
18
|
+
// - ⏹ 원시 pty 길(TUI-STOP-RAW-PTY): `pty text pty_<id> /stop --enter`·tmux 처럼 TUI 를 띄운 «PTY 자체»에 쓰면
|
|
19
|
+
// 이 라우터를 안 지나 stdin 으로 들어오고, 턴 중 typeahead 가 «대기(응답 뒤)»에 쌓았다(10-17 재점검).
|
|
20
|
+
// ⇒ 턴 중 스트리밍 리스너가 stdin «한 청크»가 정확히 `/stop⏎`·`/esc⏎` 이면 `routeRawPtyStop` 으로 같은 `routeStop` 을 부른다.
|
|
21
|
+
// 키보드는 한 키씩 오고 붙여넣기는 봉투에 싸이므로, 한 청크에 온 «글 한 줄 ⊕ Enter» 는 바깥 주입이다.
|
|
22
|
+
// 턴 밖·Enter 없음·그 밖의 글은 종전대로 키로 흐른다(관측 `path: 'raw-pty'` · 제어 IPC 는 `'control-ipc'`).
|
|
18
23
|
//
|
|
19
24
|
// ⚠️ herdr/tmux 처럼 «터미널 자체»에 쓰는 길은 키보드와 구별할 수 없다 — 이 라우터는
|
|
20
25
|
// elanous 제어 IPC(`pty text`) 경로만 가른다.
|
|
@@ -40,11 +45,13 @@ export interface ExternalInputHost {
|
|
|
40
45
|
requestStop?(): boolean;
|
|
41
46
|
/** 외부 stop 의 결과 표지(채팅 로그 한 줄). */
|
|
42
47
|
onExternalStop?(result: 'stopped' | 'no-turn' | 'failed'): void;
|
|
43
|
-
/**
|
|
44
|
-
onQueueChange?(pending: readonly ExternalInputItem[]
|
|
48
|
+
/** 대기열의 현재 상태가 바뀌었다 — 화면 표지를 다시 그린다. */
|
|
49
|
+
onQueueChange?(pending: readonly ExternalInputItem[]): void;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
export type ExternalInputRoute = 'passthrough' | 'submitted' | 'queued' | 'stopped';
|
|
53
|
+
/** 외부 stop 이 들어온 길 — 제어 IPC(`tui:<pid>` · 이 라우터) / 원시 pty(stdin 한 청크). */
|
|
54
|
+
export type ExternalStopPath = 'control-ipc' | 'raw-pty';
|
|
48
55
|
export type ExternalInputDecision = 'passthrough' | 'submit-now' | 'queue' | 'reject';
|
|
49
56
|
|
|
50
57
|
const EXTERNAL_INPUT_SOURCE: InputSourceRef = { kind: 'terminal', provider: 'pty' };
|
|
@@ -133,8 +140,8 @@ export function createExternalInputQueue(deps: ExternalInputQueueDeps = {}) {
|
|
|
133
140
|
// 경고는 머리 항목 «하나»에 한 번 — 머리가 실제로 바뀔 때만 다시 경고할 수 있다(제출 실패로 남은 같은 머리는 유지).
|
|
134
141
|
let staleTurnWarnedHead: ExternalInputItem | null = null;
|
|
135
142
|
|
|
136
|
-
const notify = (
|
|
137
|
-
try { host?.onQueueChange?.(pending
|
|
143
|
+
const notify = (): void => {
|
|
144
|
+
try { host?.onQueueChange?.(pending); } catch { /* fail-soft — 표지 실패가 입력을 막지 않는다 */ }
|
|
138
145
|
};
|
|
139
146
|
const ensurePump = (): void => {
|
|
140
147
|
if (pending.length > 0 && !stopPump) stopPump = schedule(pump, PUMP_MS);
|
|
@@ -163,20 +170,20 @@ export function createExternalInputQueue(deps: ExternalInputQueueDeps = {}) {
|
|
|
163
170
|
if (!ok) { log('deliver-failed', { length: head.text.length, pending: pending.length }); return false; }
|
|
164
171
|
pending = pending.slice(1);
|
|
165
172
|
log('delivered', { length: head.text.length, waitedMs: now() - head.at, pending: pending.length });
|
|
166
|
-
notify(
|
|
173
|
+
notify();
|
|
167
174
|
ensurePump();
|
|
168
175
|
return true;
|
|
169
176
|
}
|
|
170
177
|
|
|
171
178
|
/** ⏹ 외부 stop — 대기열을 건너뛰고 지금 턴의 escGate 로. 대기열은 건드리지 않는다. */
|
|
172
|
-
function routeStop(h: ExternalInputHost, text: string): ExternalInputRoute {
|
|
179
|
+
function routeStop(h: ExternalInputHost, text: string, path: ExternalStopPath): ExternalInputRoute {
|
|
173
180
|
let turnActive = false;
|
|
174
181
|
try { turnActive = h.isTurnActive(); } catch { turnActive = false; }
|
|
175
182
|
let stopped = false;
|
|
176
183
|
if (turnActive && h.requestStop) {
|
|
177
184
|
try { stopped = h.requestStop(); } catch { stopped = false; }
|
|
178
185
|
}
|
|
179
|
-
logInput('external-stop', { hadTurn: turnActive, stopped, command: text, pending: pending.length });
|
|
186
|
+
logInput('external-stop', { hadTurn: turnActive, stopped, command: text, pending: pending.length, path });
|
|
180
187
|
const result = stopped ? 'stopped' : turnActive ? 'failed' : 'no-turn';
|
|
181
188
|
try { h.onExternalStop?.(result); } catch { /* fail-soft */ }
|
|
182
189
|
if (result === 'no-turn') throw new Error('tui-stop-no-active-turn');
|
|
@@ -189,6 +196,7 @@ export function createExternalInputQueue(deps: ExternalInputQueueDeps = {}) {
|
|
|
189
196
|
host = next;
|
|
190
197
|
log('host-registered', { pending: pending.length });
|
|
191
198
|
ensurePump();
|
|
199
|
+
if (pending.length > 0) notify();
|
|
192
200
|
return () => {
|
|
193
201
|
if (host !== next) return;
|
|
194
202
|
host = null;
|
|
@@ -200,7 +208,7 @@ export function createExternalInputQueue(deps: ExternalInputQueueDeps = {}) {
|
|
|
200
208
|
// `pty key`(input-key)는 «키»다 — 공백·`j` 처럼 출력 가능한 키도 종전대로 넣는다.
|
|
201
209
|
if (keyCommandDepth > 0) return 'passthrough';
|
|
202
210
|
const line = parseExternalTextLine(chars);
|
|
203
|
-
if (line && host && isExternalStopCommand(line.text)) return routeStop(host, line.text);
|
|
211
|
+
if (line && host && isExternalStopCommand(line.text)) return routeStop(host, line.text, 'control-ipc');
|
|
204
212
|
let turnActive = false; let composer: string | undefined; let pendingKeys = false;
|
|
205
213
|
if (host && line) {
|
|
206
214
|
try { turnActive = host.isTurnActive(); composer = host.composerText(); pendingKeys = host.hasPendingKeys(); }
|
|
@@ -229,10 +237,26 @@ export function createExternalInputQueue(deps: ExternalInputQueueDeps = {}) {
|
|
|
229
237
|
}
|
|
230
238
|
pending = [...pending, { text: line!.text, at: now() }];
|
|
231
239
|
log('route', { ...base, route: 'queued', pending: pending.length });
|
|
232
|
-
notify(
|
|
240
|
+
notify();
|
|
233
241
|
ensurePump();
|
|
234
242
|
return 'queued';
|
|
235
243
|
},
|
|
244
|
+
/**
|
|
245
|
+
* ⏹ 원시 pty 길 — 턴 중 stdin «한 청크»가 정확히 `/stop⏎`·`/esc⏎` 이면 `routeStop` 으로 보내고 true(키로 흘리지 않는다).
|
|
246
|
+
* 턴 밖·호스트 없음·Enter 없음·그 밖의 글은 false — 호출측이 종전대로 키를 넣는다.
|
|
247
|
+
*/
|
|
248
|
+
routeRawStop(chunk: string): boolean {
|
|
249
|
+
const h = host;
|
|
250
|
+
if (!h) return false;
|
|
251
|
+
const line = parseExternalTextLine(chunk);
|
|
252
|
+
if (!line?.submit || !isExternalStopCommand(line.text)) return false;
|
|
253
|
+
let turnActive = false;
|
|
254
|
+
try { turnActive = h.isTurnActive(); } catch { turnActive = false; }
|
|
255
|
+
if (!turnActive) return false;
|
|
256
|
+
// 'failed'(중단 경로 없음)도 소비한다 — 표지(Esc 로 멈춰 주세요)는 routeStop 이 이미 남겼다.
|
|
257
|
+
try { routeStop(h, line.text, 'raw-pty'); } catch { /* 결과는 이벤트·표지로 남았다 */ }
|
|
258
|
+
return true;
|
|
259
|
+
},
|
|
236
260
|
pump,
|
|
237
261
|
pending: (): readonly ExternalInputItem[] => pending,
|
|
238
262
|
};
|
|
@@ -249,3 +273,4 @@ const shared = createExternalInputQueue();
|
|
|
249
273
|
|
|
250
274
|
export function registerExternalInputHost(host: ExternalInputHost): () => void { return shared.register(host); }
|
|
251
275
|
export function routeExternalInput(chars: string): ExternalInputRoute { return shared.route(chars); }
|
|
276
|
+
export function routeRawPtyStop(chunk: string): boolean { return shared.routeRawStop(chunk); }
|
|
@@ -40,12 +40,48 @@ const lastBySurface = new Map<string, RouteDecision>();
|
|
|
40
40
|
|
|
41
41
|
export function recordCurrentRouteDecision(surface: string, decision: RouteDecision): void {
|
|
42
42
|
lastBySurface.set(surface, decision);
|
|
43
|
+
// 새 턴의 결정이 서면 지난 턴의 «실제 실행»은 더는 이 턴의 것이 아니다.
|
|
44
|
+
executionBySurface.delete(surface);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export function currentRouteDecision(surface: string): RouteDecision | null {
|
|
46
48
|
return lastBySurface.get(surface) ?? null;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
/** 이번 턴에 «실제로» 응답한 공급자·모델(TUI-MODEL-BADGE-ACTUAL · 10-16).
|
|
52
|
+
* 결정(`RouteDecision`)은 턴 «시작»에 고른 값이고, 폴백 체인이 다른 공급자로 넘기면 실제는 달라진다.
|
|
53
|
+
* ACP 사용량 봉투(`routeProvider`·`model`)로 들어온다 — 이 표면이 결정을 세운 in-process 세션만 적는다.
|
|
54
|
+
* ⚠️ 지금 읽는 곳은 완료 배지뿐이다. 상태줄·`/model` «지금:» 의 원천 통일은 후속 칸 TUI-MODEL-BADGE-SOURCE. */
|
|
55
|
+
export interface RouteExecution {
|
|
56
|
+
provider?: string;
|
|
57
|
+
model: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const executionBySurface = new Map<string, RouteExecution>();
|
|
61
|
+
|
|
62
|
+
export function recordRouteExecution(surface: string, execution: RouteExecution): void {
|
|
63
|
+
executionBySurface.set(surface, execution);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function currentRouteExecution(surface: string): RouteExecution | null {
|
|
67
|
+
return executionBySurface.get(surface) ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 표면의 결정·실행을 비운다(세션 핀은 사람의 선택이라 남긴다) — 짝이 안 맞는 턴(원격 등)에서 지난 «A → B» 가
|
|
71
|
+
* 다시 보이지 않게 한다(r5 리뷰). */
|
|
72
|
+
export function clearSurfaceRouteDecision(surface: string): void {
|
|
73
|
+
lastBySurface.delete(surface);
|
|
74
|
+
executionBySurface.delete(surface);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 턴이 고른 모델과 실제로 응답한 모델이 다르면 «처음 → 실제», 아니면 null(꾸미지 않는다). */
|
|
78
|
+
export function routeFallbackLabel(surface: string): string | null {
|
|
79
|
+
const decision = lastBySurface.get(surface);
|
|
80
|
+
const execution = executionBySurface.get(surface);
|
|
81
|
+
if (!decision?.model || !execution?.model || execution.model === decision.model) return null;
|
|
82
|
+
return `${decision.model} → ${execution.model}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
49
85
|
/** A `/model` choice scoped to this process's surface (TUI-MODEL-SESSION-SCOPE · 10-14).
|
|
50
86
|
* It reaches the turn as `explicitModel`, so it wins over the Codex tier policy —
|
|
51
87
|
* a config-only `llm.model` change is ignored by that policy (10-14 OPT: `/model luna`
|
|
@@ -18,6 +18,12 @@ export const REPAIR_PHASE_READ_ONLY_OPS_TOOLS = ['MsgList', 'ChecklistList'] as
|
|
|
18
18
|
* rejects the steer target loops forever (#62 repair · #67 verify — MsgPost).
|
|
19
19
|
* - Agent is allowed in every action phase, so its result (AgentOutput/AgentList) must be
|
|
20
20
|
* too, or a delegated result is dropped and the parent redoes the work (#41).
|
|
21
|
+
* - Read-only observation of the run itself (TUI-OPS-TURN-GUARD gap ②, old-build samples where
|
|
22
|
+
* repair/verify rejected these mid-ops-turn): `logs_query` (logs.db READ-ONLY) and
|
|
23
|
+
* `PtyShellPoll` (drains output of a PTY the turn already started — no input, no spawn).
|
|
24
|
+
* Deliberately NOT here: `schedule_manage` (create/update/delete mutate crontab) and
|
|
25
|
+
* `se_build` (`stop` SIGTERMs a mission) — the allow list is by name, so a tool with any
|
|
26
|
+
* write action stays phase-governed.
|
|
21
27
|
* Source edits stay governed by each phase's own Edit/Write entries — nothing here edits files. */
|
|
22
28
|
export const ACTION_PHASE_OPS_TOOLS = [
|
|
23
29
|
'MsgList',
|
|
@@ -30,4 +36,6 @@ export const ACTION_PHASE_OPS_TOOLS = [
|
|
|
30
36
|
'ChecklistMove',
|
|
31
37
|
'AgentOutput',
|
|
32
38
|
'AgentList',
|
|
39
|
+
'logs_query',
|
|
40
|
+
'PtyShellPoll',
|
|
33
41
|
] as const;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// ── Codex(Responses API) 요청 «모양» 요약 — 4xx 거절 관측용 (TUI-OBS-400-SHAPE · 결함 #69) ──
|
|
2
|
+
//
|
|
3
|
+
// 왜: Codex 백엔드는 400 을 `{"detail":"Bad Request"}` 한 줄로만 돌려준다. 그래서 «요청이 너무 컸나 ·
|
|
4
|
+
// 메시지가 몇 개였나 · tool_call ↔ tool_result 짝이 깨졌나 · 첨부가 섞였나» 를 로그로 가를 수 없었다
|
|
5
|
+
// (10-16 OPT 실측: 같은 문면이 «합쳤을 때만» 400 — 원인 추정 불가).
|
|
6
|
+
//
|
|
7
|
+
// 무엇을 싣나: 수·바이트·종류·짝 깨짐 수 «만». ⛔ 본문·프롬프트·도구 인자·도구 출력·사용자 텍스트·키는
|
|
8
|
+
// 싣지 않는다. 짝 깨진 call_id 는 sha256 앞 8자로만(상한 5개) — 같은 id 가 다음 런에도 보이는지 대조용.
|
|
9
|
+
// 순수 함수 — 호출부(streamCodexResponsesEvents)가 실제 전송 객체와 전송 문자열을 넘긴다.
|
|
10
|
+
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
|
|
13
|
+
const HASH_SAMPLE_CAP = 5;
|
|
14
|
+
|
|
15
|
+
// ⛔ 카운트 «키» 도 요청에서 온 문자열이다 — 알려진 종류만 키로 쓰고 나머지는 `unknown` 으로 접는다
|
|
16
|
+
// (잘못된 type/role 문자열에 내용이 들어 있어도 이벤트에 새지 않게 · 리뷰 r1).
|
|
17
|
+
const KNOWN_ROLES = new Set(['user', 'assistant', 'system', 'developer', 'tool']);
|
|
18
|
+
const KNOWN_ITEM_TYPES = new Set([
|
|
19
|
+
'message', 'function_call', 'function_call_output', 'reasoning', 'custom_tool_call', 'custom_tool_call_output',
|
|
20
|
+
'local_shell_call', 'local_shell_call_output', 'web_search_call', 'image_generation_call', 'item_reference',
|
|
21
|
+
]);
|
|
22
|
+
const KNOWN_CONTENT_TYPES = new Set([
|
|
23
|
+
'input_text', 'output_text', 'input_image', 'input_file', 'input_audio', 'refusal', 'summary_text', 'reasoning_text', 'string',
|
|
24
|
+
]);
|
|
25
|
+
const KNOWN_EFFORTS = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
26
|
+
const KNOWN_SUMMARIES = new Set(['auto', 'concise', 'detailed', 'none']);
|
|
27
|
+
const KNOWN_TOOL_CHOICES = new Set(['auto', 'required', 'none', 'function', 'allowed_tools', 'custom']);
|
|
28
|
+
const MODEL_ID_RE = /^[A-Za-z0-9._:/@+-]{1,80}$/;
|
|
29
|
+
|
|
30
|
+
function known(set: Set<string>, value: unknown): string {
|
|
31
|
+
return typeof value === 'string' && set.has(value) ? value : 'unknown';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hash8(id: string): string {
|
|
35
|
+
return createHash('sha256').update(id).digest('hex').slice(0, 8);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function byteLen(value: unknown): number {
|
|
39
|
+
try {
|
|
40
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value ?? null);
|
|
41
|
+
return Buffer.byteLength(text ?? '', 'utf8');
|
|
42
|
+
} catch {
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function bump(counts: Record<string, number>, key: string): void {
|
|
48
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function countContentBlocks(content: unknown, counts: Record<string, number>, empty: { n: number }): void {
|
|
52
|
+
if (typeof content === 'string') {
|
|
53
|
+
bump(counts, 'string');
|
|
54
|
+
if (content.length === 0) empty.n += 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!Array.isArray(content)) return;
|
|
58
|
+
for (const block of content) {
|
|
59
|
+
if (!block || typeof block !== 'object') { bump(counts, 'unknown'); continue; }
|
|
60
|
+
const b = block as Record<string, unknown>;
|
|
61
|
+
const type = typeof b.type === 'string' ? b.type : '';
|
|
62
|
+
bump(counts, known(KNOWN_CONTENT_TYPES, type));
|
|
63
|
+
if ((type === 'input_text' || type === 'output_text') && typeof b.text === 'string' && b.text.length === 0) empty.n += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CodexRequestShape {
|
|
68
|
+
status: number;
|
|
69
|
+
/** UTF-8 bytes of the exact JSON string sent on the wire. */
|
|
70
|
+
bytes: number;
|
|
71
|
+
instructionsBytes: number;
|
|
72
|
+
inputBytes: number;
|
|
73
|
+
toolsBytes: number;
|
|
74
|
+
/** Largest single input item, bytes — one huge tool output vs. many small items. */
|
|
75
|
+
maxInputItemBytes: number;
|
|
76
|
+
inputItemCount: number;
|
|
77
|
+
/** Items carrying a `role` (user/assistant/system/developer messages). */
|
|
78
|
+
messageCount: number;
|
|
79
|
+
roleCounts: Record<string, number>;
|
|
80
|
+
/** Top-level item kinds: message / function_call / function_call_output / reasoning / … */
|
|
81
|
+
itemTypeCounts: Record<string, number>;
|
|
82
|
+
/** Content block kinds across messages and array-form tool outputs (input_text / input_image / …). */
|
|
83
|
+
contentTypeCounts: Record<string, number>;
|
|
84
|
+
emptyTextBlockCount: number;
|
|
85
|
+
toolCallCount: number;
|
|
86
|
+
toolResultCount: number;
|
|
87
|
+
/** function_call «items» whose call_id has no function_call_output anywhere (counted per item, duplicates included). */
|
|
88
|
+
unpairedToolCallCount: number;
|
|
89
|
+
/** function_call_output «items» whose call_id has no function_call anywhere (counted per item). */
|
|
90
|
+
orphanToolResultCount: number;
|
|
91
|
+
/** function_call_output items whose call_id has a function_call only «after» them. */
|
|
92
|
+
toolResultBeforeCallCount: number;
|
|
93
|
+
/** function_call items beyond the first for the same call_id. */
|
|
94
|
+
duplicateToolCallIdCount: number;
|
|
95
|
+
missingCallIdCount: number;
|
|
96
|
+
unpairedToolCallIdHashes: string[];
|
|
97
|
+
orphanToolResultIdHashes: string[];
|
|
98
|
+
toolDefinitionCount: number;
|
|
99
|
+
model?: string;
|
|
100
|
+
reasoningEffort?: string;
|
|
101
|
+
reasoningSummary?: string;
|
|
102
|
+
store: boolean;
|
|
103
|
+
hasPreviousResponseId: boolean;
|
|
104
|
+
toolChoice?: string;
|
|
105
|
+
hasPromptCacheKey: boolean;
|
|
106
|
+
/** Size of the provider's error body — the body itself is not recorded. */
|
|
107
|
+
errorBodyBytes?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Summarise a Codex Responses request body for a rejected (4xx) call. Never copies text content. */
|
|
111
|
+
export function summarizeCodexRequestShape(
|
|
112
|
+
wireBody: Record<string, unknown>,
|
|
113
|
+
wireJson: string,
|
|
114
|
+
status: number,
|
|
115
|
+
errorBodyText?: string,
|
|
116
|
+
): CodexRequestShape {
|
|
117
|
+
const input = Array.isArray(wireBody.input) ? wireBody.input : [];
|
|
118
|
+
const roleCounts: Record<string, number> = {};
|
|
119
|
+
const itemTypeCounts: Record<string, number> = {};
|
|
120
|
+
const contentTypeCounts: Record<string, number> = {};
|
|
121
|
+
const empty = { n: 0 };
|
|
122
|
+
// 항목별로 센다(리뷰 r2) — 같은 call_id 가 여러 번 나와도 짝 깨짐 수는 «항목 수» 다.
|
|
123
|
+
const calls: Array<{ id: string; index: number }> = [];
|
|
124
|
+
const results: Array<{ id: string; index: number }> = [];
|
|
125
|
+
let toolCallCount = 0;
|
|
126
|
+
let toolResultCount = 0;
|
|
127
|
+
let missingCallIdCount = 0;
|
|
128
|
+
let maxInputItemBytes = 0;
|
|
129
|
+
|
|
130
|
+
input.forEach((raw, index) => {
|
|
131
|
+
maxInputItemBytes = Math.max(maxInputItemBytes, byteLen(raw));
|
|
132
|
+
if (!raw || typeof raw !== 'object') { bump(itemTypeCounts, 'unknown'); return; }
|
|
133
|
+
const item = raw as Record<string, unknown>;
|
|
134
|
+
const type = typeof item.type === 'string' ? item.type : (typeof item.role === 'string' ? 'message' : 'unknown');
|
|
135
|
+
bump(itemTypeCounts, known(KNOWN_ITEM_TYPES, type));
|
|
136
|
+
if (typeof item.role === 'string') {
|
|
137
|
+
bump(roleCounts, known(KNOWN_ROLES, item.role));
|
|
138
|
+
countContentBlocks(item.content, contentTypeCounts, empty);
|
|
139
|
+
}
|
|
140
|
+
if (type === 'function_call') {
|
|
141
|
+
toolCallCount += 1;
|
|
142
|
+
const id = typeof item.call_id === 'string' && item.call_id ? item.call_id : '';
|
|
143
|
+
if (!id) { missingCallIdCount += 1; return; }
|
|
144
|
+
calls.push({ id, index });
|
|
145
|
+
} else if (type === 'function_call_output') {
|
|
146
|
+
toolResultCount += 1;
|
|
147
|
+
countContentBlocks(item.output, contentTypeCounts, empty);
|
|
148
|
+
const id = typeof item.call_id === 'string' && item.call_id ? item.call_id : '';
|
|
149
|
+
if (!id) { missingCallIdCount += 1; return; }
|
|
150
|
+
results.push({ id, index });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const firstCallAt = new Map<string, number>();
|
|
155
|
+
for (const c of calls) if (!firstCallAt.has(c.id)) firstCallAt.set(c.id, c.index);
|
|
156
|
+
const resultIds = new Set(results.map((r) => r.id));
|
|
157
|
+
const unpairedItems = calls.filter((c) => !resultIds.has(c.id));
|
|
158
|
+
const orphanItems = results.filter((r) => !firstCallAt.has(r.id));
|
|
159
|
+
const resultBeforeCall = results.filter((r) => firstCallAt.has(r.id) && r.index < (firstCallAt.get(r.id) as number)).length;
|
|
160
|
+
const duplicateToolCallIdCount = calls.length - firstCallAt.size;
|
|
161
|
+
const unpaired = [...new Set(unpairedItems.map((c) => c.id))];
|
|
162
|
+
const orphan = [...new Set(orphanItems.map((r) => r.id))];
|
|
163
|
+
|
|
164
|
+
const reasoning = wireBody.reasoning && typeof wireBody.reasoning === 'object'
|
|
165
|
+
? wireBody.reasoning as Record<string, unknown> : undefined;
|
|
166
|
+
const toolChoiceRaw = wireBody.tool_choice;
|
|
167
|
+
const toolChoice = toolChoiceRaw === undefined || toolChoiceRaw === null
|
|
168
|
+
? undefined
|
|
169
|
+
: known(KNOWN_TOOL_CHOICES, typeof toolChoiceRaw === 'object' ? (toolChoiceRaw as Record<string, unknown>).type : toolChoiceRaw);
|
|
170
|
+
const tools = Array.isArray(wireBody.tools) ? wireBody.tools : [];
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
status,
|
|
174
|
+
bytes: Buffer.byteLength(wireJson, 'utf8'),
|
|
175
|
+
instructionsBytes: typeof wireBody.instructions === 'string' ? Buffer.byteLength(wireBody.instructions, 'utf8') : 0,
|
|
176
|
+
inputBytes: byteLen(input),
|
|
177
|
+
toolsBytes: byteLen(tools),
|
|
178
|
+
maxInputItemBytes,
|
|
179
|
+
inputItemCount: input.length,
|
|
180
|
+
messageCount: Object.values(roleCounts).reduce((a, b) => a + b, 0),
|
|
181
|
+
roleCounts,
|
|
182
|
+
itemTypeCounts,
|
|
183
|
+
contentTypeCounts,
|
|
184
|
+
emptyTextBlockCount: empty.n,
|
|
185
|
+
toolCallCount,
|
|
186
|
+
toolResultCount,
|
|
187
|
+
unpairedToolCallCount: unpairedItems.length,
|
|
188
|
+
orphanToolResultCount: orphanItems.length,
|
|
189
|
+
toolResultBeforeCallCount: resultBeforeCall,
|
|
190
|
+
duplicateToolCallIdCount,
|
|
191
|
+
missingCallIdCount,
|
|
192
|
+
unpairedToolCallIdHashes: unpaired.slice(0, HASH_SAMPLE_CAP).map(hash8),
|
|
193
|
+
orphanToolResultIdHashes: orphan.slice(0, HASH_SAMPLE_CAP).map(hash8),
|
|
194
|
+
toolDefinitionCount: tools.length,
|
|
195
|
+
...(typeof wireBody.model === 'string' ? { model: MODEL_ID_RE.test(wireBody.model) ? wireBody.model : 'unknown' } : {}),
|
|
196
|
+
...(reasoning?.effort !== undefined ? { reasoningEffort: known(KNOWN_EFFORTS, reasoning.effort) } : {}),
|
|
197
|
+
...(reasoning?.summary !== undefined ? { reasoningSummary: known(KNOWN_SUMMARIES, reasoning.summary) } : {}),
|
|
198
|
+
store: wireBody.store === true,
|
|
199
|
+
hasPreviousResponseId: typeof wireBody.previous_response_id === 'string' && wireBody.previous_response_id.length > 0,
|
|
200
|
+
...(toolChoice ? { toolChoice } : {}),
|
|
201
|
+
hasPromptCacheKey: typeof wireBody.prompt_cache_key === 'string',
|
|
202
|
+
...(typeof errorBodyText === 'string' ? { errorBodyBytes: Buffer.byteLength(errorBodyText, 'utf8') } : {}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** 4xx rejections whose cause can be the request shape. Auth (401/403) and quota (429) are not shape problems. */
|
|
207
|
+
export function isShapeRelevantRejection(status: number): boolean {
|
|
208
|
+
return status >= 400 && status < 500 && status !== 401 && status !== 403 && status !== 429;
|
|
209
|
+
}
|