agentbox-app 0.1.13 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/bin/agentbox.mjs +23 -1
- package/briefs/message-rules.md +36 -8
- package/briefs/worker.md +46 -23
- package/main/account-discovery.mjs +122 -30
- package/main/account-tooling.mjs +29 -1
- package/main/account-usage.mjs +46 -0
- package/main/agent-sessions.mjs +282 -268
- package/main/agents.mjs +179 -466
- package/main/analytics.mjs +12 -2
- package/main/answer-modes.mjs +103 -37
- package/main/approval-prompt-core.mjs +197 -0
- package/main/approval-prompt-server.mjs +4 -138
- package/main/approvals.mjs +101 -297
- package/main/artifact-path.mjs +47 -18
- package/main/claude-input.mjs +48 -4
- package/main/claude-plan.mjs +48 -0
- package/main/claude-session-open.mjs +28 -0
- package/main/claude-usage-process.mjs +40 -0
- package/main/claude-usage.mjs +18 -40
- package/main/cloud/account.mjs +179 -0
- package/main/cloud/aws.mjs +127 -0
- package/main/cloud/browser-return.mjs +58 -0
- package/main/cloud/clients.mjs +15 -0
- package/main/cloud/codex-cloud.mjs +53 -0
- package/main/cloud/config.mjs +107 -0
- package/main/cloud/docs.mjs +219 -0
- package/main/cloud/exec.mjs +27 -0
- package/main/cloud/host.mjs +165 -0
- package/main/cloud/kit.mjs +80 -0
- package/main/cloud/logins.mjs +543 -0
- package/main/cloud/managed.mjs +115 -0
- package/main/cloud/marker.mjs +104 -0
- package/main/cloud/org.mjs +53 -0
- package/main/cloud/reuse.mjs +208 -0
- package/main/cloud/run-cli.mjs +177 -0
- package/main/cloud/run.mjs +1934 -0
- package/main/cloud/usage.mjs +76 -0
- package/main/cloud-permission.mjs +51 -0
- package/main/codex-account.mjs +78 -0
- package/main/codex-app-server.mjs +276 -409
- package/main/codex-approvals.mjs +148 -433
- package/main/codex-models.mjs +20 -2
- package/main/codex-session.mjs +212 -507
- package/main/codex-threads-worker.mjs +12 -0
- package/main/codex-threads.mjs +71 -3
- package/main/codex-usage-file.mjs +5 -3
- package/main/codex-usage.mjs +56 -7
- package/main/codex-watch.mjs +17 -3
- package/main/codex.mjs +95 -363
- package/main/config.mjs +11 -1
- package/main/corner-tag.mjs +340 -19
- package/main/dev-window.mjs +1 -1
- package/main/engine-bin.mjs +232 -282
- package/main/engine-setup.mjs +21 -9
- package/main/feedback.mjs +3 -25
- package/main/folder-pictures.mjs +2 -2
- package/main/freeze-recorder-thread.mjs +86 -0
- package/main/freeze-recorder.mjs +146 -0
- package/main/fresh-user.mjs +20 -9
- package/main/git-change-offthread.mjs +17 -3
- package/main/git-change-thread.mjs +5 -2
- package/main/harnesses/claude.mjs +32 -11
- package/main/harnesses/codex.mjs +9 -2
- package/main/importable-threads.mjs +64 -6
- package/main/ipc.mjs +362 -111
- package/main/leftovers.mjs +117 -25
- package/main/liquid-glass.mjs +26 -0
- package/main/live-replies.mjs +10 -4
- package/main/main-went-red.mjs +330 -0
- package/main/main.mjs +199 -84
- package/main/memory-gate-history.mjs +36 -10
- package/main/memory-gate-server.mjs +62 -0
- package/main/memory-gate.mjs +14 -1
- package/main/menu.mjs +4 -2
- package/main/message-priority.mjs +2 -2
- package/main/mic.mjs +38 -0
- package/main/native/liquid-glass/build/liquid-glass.node +0 -0
- package/main/new-chats-watch.mjs +110 -0
- package/main/new-user-window.mjs +25 -0
- package/main/notify.mjs +24 -8
- package/main/phone-alerts.mjs +117 -0
- package/main/phone-inbox.mjs +625 -0
- package/main/phone-link.mjs +29 -9
- package/main/phone-uploads.mjs +476 -0
- package/main/picture-shape.mjs +114 -0
- package/main/quit-sequence.mjs +143 -0
- package/main/refresh-agent-models.mjs +15 -2
- package/main/relay-client.mjs +69 -1
- package/main/repeats.mjs +10 -4
- package/main/row-label.mjs +12 -41
- package/main/run-change.mjs +13 -0
- package/main/run-ledger.mjs +135 -0
- package/main/save-attachments.mjs +41 -0
- package/main/serve.mjs +45 -3
- package/main/settings.mjs +220 -44
- package/main/shell-path.mjs +185 -37
- package/main/ship-conflict-brief.mjs +119 -0
- package/main/ship-queue.mjs +115 -19
- package/main/source-updater.mjs +18 -10
- package/main/stage-attachment.mjs +68 -0
- package/main/stall-watch.mjs +50 -0
- package/main/store/home.mjs +87 -232
- package/main/store/paths.mjs +11 -0
- package/main/store/teammates-files.mjs +99 -0
- package/main/store/work-items.mjs +231 -13
- package/main/store.mjs +226 -52
- package/main/supervisor.mjs +3103 -341
- package/main/task-folders.mjs +430 -30
- package/main/task-terminals.mjs +110 -34
- package/main/team/index.mjs +114 -10
- package/main/team/memory-cloud.mjs +183 -1
- package/main/team/room-hours.mjs +48 -0
- package/main/team/room-setup.mjs +31 -0
- package/main/team/rooms-memory.mjs +454 -0
- package/main/team/rooms-shape.mjs +42 -0
- package/main/team/rooms-supabase.mjs +324 -0
- package/main/team/rooms.mjs +575 -0
- package/main/team/session.mjs +96 -26
- package/main/team/supabase-backend.mjs +180 -0
- package/main/team/sync.mjs +109 -1
- package/main/teammates.mjs +580 -0
- package/main/terminal-host.mjs +143 -0
- package/main/terminal-hosts.mjs +261 -0
- package/main/transcript-search.mjs +80 -0
- package/main/webview-guard.mjs +45 -0
- package/main/worktree-include.mjs +90 -219
- package/mcp/core/documents.mjs +3 -1
- package/mcp/core/repeats.mjs +88 -0
- package/mcp/core/teammates.mjs +222 -0
- package/mcp/core/work.mjs +76 -4
- package/mcp/stdio.mjs +24 -11
- package/mcp/tools.mjs +116 -8
- package/package.json +21 -5
- package/renderer/dist/assets/{abnfDiagram-YUKMZFIV-DPPuMWt-.js → abnfDiagram-YUKMZFIV-CoWB6YUK.js} +1 -1
- package/renderer/dist/assets/{arc-HmFfdseH.js → arc-Ch0NrCul.js} +1 -1
- package/renderer/dist/assets/{architectureDiagram-47P4ROYG-DxJ3zHrH.js → architectureDiagram-47P4ROYG-CXz9M5tQ.js} +1 -1
- package/renderer/dist/assets/{blockDiagram-E7TT5TSB-CbTXzOqQ.js → blockDiagram-E7TT5TSB-DchGUuHH.js} +1 -1
- package/renderer/dist/assets/{c4Diagram-CRA5TL53-X5ItFyLn.js → c4Diagram-CRA5TL53-Cip-zNyr.js} +1 -1
- package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
- package/renderer/dist/assets/{chunk-2Q5K7J3B-DC12Ho_L.js → chunk-2Q5K7J3B-BoXid_2r.js} +1 -1
- package/renderer/dist/assets/{chunk-5VM5RSS4-ClVtfPd0.js → chunk-5VM5RSS4-Cpez1rRz.js} +1 -1
- package/renderer/dist/assets/{chunk-7TKQ45FW-DgcOm0HT.js → chunk-7TKQ45FW-DKA84YkE.js} +1 -1
- package/renderer/dist/assets/{chunk-AW2ZBBNX-C01s4yoF.js → chunk-AW2ZBBNX-Dg-SumPG.js} +1 -1
- package/renderer/dist/assets/{chunk-FVRAUYC3-Bj0zxhR4.js → chunk-FVRAUYC3-06TCvPm1.js} +2 -2
- package/renderer/dist/assets/{chunk-HTAEGDNF-DWDeEhLU.js → chunk-HTAEGDNF-CMEBy1Td.js} +1 -1
- package/renderer/dist/assets/{chunk-JWPE2WC7-Dsqwv3eW.js → chunk-JWPE2WC7-BJ2ObmV-.js} +1 -1
- package/renderer/dist/assets/{chunk-KQW6MTUR-CE86ANL2.js → chunk-KQW6MTUR-CGWOYXUE.js} +1 -1
- package/renderer/dist/assets/{chunk-XXDRQBXY-CoNTASfs.js → chunk-XXDRQBXY-CiZp07XC.js} +1 -1
- package/renderer/dist/assets/{classDiagram-v2-K4WV4PDN-2wprLqtw.js → classDiagram-v2-K4WV4PDN-DJNIX719.js} +1 -1
- package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
- package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
- package/renderer/dist/assets/{cose-bilkent-JH36ORCC-enqMiYbn.js → cose-bilkent-JH36ORCC-CbkyZOUH.js} +1 -1
- package/renderer/dist/assets/{cynefin-EF2NZ3EQ-DHngLxvQ.js → cynefin-EF2NZ3EQ-78iOroZO.js} +1 -1
- package/renderer/dist/assets/{cynefinDiagram-3GCD6N5R-C0rZZIl3.js → cynefinDiagram-3GCD6N5R-GcCopMs7.js} +1 -1
- package/renderer/dist/assets/{dagre-W4DXFKR2-DQnaEWO7.js → dagre-W4DXFKR2-DHLlXLkz.js} +1 -1
- package/renderer/dist/assets/{diagram-2UJZ2QOL-CS8AlGPx.js → diagram-2UJZ2QOL-DuZSUHcH.js} +1 -1
- package/renderer/dist/assets/{diagram-OVF4WLC6-yiDMHIkY.js → diagram-OVF4WLC6-Ckxu3cqH.js} +1 -1
- package/renderer/dist/assets/{diagram-PFPMY2P6-XbQKzbBA.js → diagram-PFPMY2P6-CsTw9Ycn.js} +1 -1
- package/renderer/dist/assets/{diagram-UMYRVEAY-dhVvlfl_.js → diagram-UMYRVEAY-CTXd8qVg.js} +1 -1
- package/renderer/dist/assets/{diagram-YEKJPTXX-DZHe8SCf.js → diagram-YEKJPTXX-CJs1T_9m.js} +1 -1
- package/renderer/dist/assets/{diagram-ZIFT7M5P-B5h6aXVR.js → diagram-ZIFT7M5P-CvjI23mf.js} +1 -1
- package/renderer/dist/assets/{ebnfDiagram-VR2GEFS7-PR9ScDco.js → ebnfDiagram-VR2GEFS7-d_IeAzOJ.js} +1 -1
- package/renderer/dist/assets/{elk-IJKZMXRS-Cg-yQXPd.js → elk-IJKZMXRS-zo9KFP3N.js} +1 -1
- package/renderer/dist/assets/{erDiagram-O2IAPWRE-omOrJMxu.js → erDiagram-O2IAPWRE-DqfYf5Dt.js} +1 -1
- package/renderer/dist/assets/{flowDiagram-OXPTDLAJ-aOX2hzvT.js → flowDiagram-OXPTDLAJ-DAWsMLmo.js} +1 -1
- package/renderer/dist/assets/{ganttDiagram-R7TSEDQI-B1H3pWyI.js → ganttDiagram-R7TSEDQI-4ml_bhwR.js} +1 -1
- package/renderer/dist/assets/{gitGraphDiagram-XJZIOB7I-Cvw304HC.js → gitGraphDiagram-XJZIOB7I-B_WcqSId.js} +1 -1
- package/renderer/dist/assets/{infoDiagram-5W2HQ5XZ-_ZXcNViH.js → infoDiagram-5W2HQ5XZ-CI-jYqzc.js} +1 -1
- package/renderer/dist/assets/{ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js → ishikawaDiagram-K3B6WC7H-B56VWRlV.js} +1 -1
- package/renderer/dist/assets/{journeyDiagram-COXZFDF6-fr62QoAt.js → journeyDiagram-COXZFDF6-D1-p09Lr.js} +1 -1
- package/renderer/dist/assets/{kanban-definition-P3RFRI5V-J8zsabCe.js → kanban-definition-P3RFRI5V-Cv3w7JoN.js} +1 -1
- package/renderer/dist/assets/{linear-BmNdEtIi.js → linear-C35V2Spi.js} +1 -1
- package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
- package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
- package/renderer/dist/assets/{mermaid.core-CnYzvFkz.js → mermaid.core-BzGT5Vo9.js} +4 -4
- package/renderer/dist/assets/{mindmap-definition-LZFPQGKD-Bfcxc7PM.js → mindmap-definition-LZFPQGKD-BZUfuymC.js} +1 -1
- package/renderer/dist/assets/{pegDiagram-BGZESJAR-CwUJOq6X.js → pegDiagram-BGZESJAR-DFh9XNGe.js} +1 -1
- package/renderer/dist/assets/{pieDiagram-CAPJLFHJ-BrwMmqfC.js → pieDiagram-CAPJLFHJ-C65tPVsy.js} +1 -1
- package/renderer/dist/assets/{product-name-CTKLYNWY.js → product-name-CSNW6d7s.js} +1 -1
- package/renderer/dist/assets/{quadrantDiagram-GDMTTRAM-BN4Stoj-.js → quadrantDiagram-GDMTTRAM-Cushj_nk.js} +1 -1
- package/renderer/dist/assets/{railroadDiagram-SM67HX2B-CPZ5bbeR.js → railroadDiagram-SM67HX2B-DZGebGx5.js} +1 -1
- package/renderer/dist/assets/{requirementDiagram-X7JNWC4A-CgRnimVe.js → requirementDiagram-X7JNWC4A-DpAB0MNo.js} +1 -1
- package/renderer/dist/assets/{sankeyDiagram-UM26HJRW-Bt2rej__.js → sankeyDiagram-UM26HJRW-D-29rHra.js} +1 -1
- package/renderer/dist/assets/{sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js → sequenceDiagram-ZO4K6R2Y-FfOQAZol.js} +1 -1
- package/renderer/dist/assets/{sizeCapture-INFHLROL-usyNyXyh.js → sizeCapture-INFHLROL-C_n6fDBY.js} +1 -1
- package/renderer/dist/assets/{stateDiagram-v2-TBUQTH76-CRzUBb0u.js → stateDiagram-v2-TBUQTH76-BZjenzJG.js} +1 -1
- package/renderer/dist/assets/{swimlanes-N4OXWK64-C1GJvMS0.js → swimlanes-N4OXWK64-DxaMU9vX.js} +1 -1
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
- package/renderer/dist/assets/{timeline-definition-YOQAKGHF-caeyPlcc.js → timeline-definition-YOQAKGHF-DTg5HJKz.js} +1 -1
- package/renderer/dist/assets/{usecaseDiagram-VIAY4XPW-BN_SV6PC.js → usecaseDiagram-VIAY4XPW-Bc3MZDsl.js} +1 -1
- package/renderer/dist/assets/{vennDiagram-BLWOH2XV-BLGuaUX-.js → vennDiagram-BLWOH2XV-GIVGk9Id.js} +1 -1
- package/renderer/dist/assets/{wardleyDiagram-YMQ3BMBF-Da0cJeNc.js → wardleyDiagram-YMQ3BMBF-NmJnbQOg.js} +1 -1
- package/renderer/dist/assets/{xychartDiagram-TAQBALBS-cGfUffMk.js → xychartDiagram-TAQBALBS-p-kfGfnW.js} +1 -1
- package/renderer/dist/corner-tag.html +3 -3
- package/renderer/dist/index.html +3 -3
- package/scripts/after-install.mjs +30 -8
- package/scripts/cloud-shim.pl +57 -0
- package/shared/account-names.d.mts +6 -0
- package/shared/account-names.mjs +15 -0
- package/shared/agent-hours.mjs +136 -0
- package/shared/agent-import.mjs +60 -34
- package/shared/agents.mjs +126 -522
- package/shared/analytics-events.mjs +12 -0
- package/shared/approval-card.mjs +36 -0
- package/shared/bridge-map.mjs +27 -1
- package/shared/claude-code-shape.mjs +6 -0
- package/shared/claude-usage.d.mts +1 -0
- package/shared/claude-usage.mjs +42 -1
- package/shared/cloud-hand-back.d.mts +7 -0
- package/shared/cloud-hand-back.mjs +20 -0
- package/shared/cloud-kit.d.mts +219 -0
- package/shared/cloud-kit.mjs +341 -0
- package/shared/cloud-login-name.mjs +4 -0
- package/shared/cloud-start.mjs +17 -0
- package/shared/cloud-versions.mjs +23 -0
- package/shared/codex-import.mjs +2 -2
- package/shared/codex-modes.d.mts +22 -9
- package/shared/codex-modes.mjs +67 -11
- package/shared/codex-usage.mjs +108 -2
- package/shared/corner-tag.d.mts +23 -1
- package/shared/corner-tag.mjs +284 -29
- package/shared/dead-run-trace.mjs +19 -2
- package/shared/engines.mjs +70 -309
- package/shared/feedback.d.mts +7 -0
- package/shared/feedback.mjs +25 -0
- package/shared/first-run-practice.mjs +17 -24
- package/shared/harness-definitions.mjs +8 -0
- package/shared/instruction-defaults.json +2 -2
- package/shared/key-caps.d.mts +14 -0
- package/shared/key-caps.mjs +68 -0
- package/shared/message-edits.d.mts +7 -0
- package/shared/message-edits.mjs +35 -0
- package/shared/move-thread.d.mts +24 -0
- package/shared/move-thread.mjs +112 -0
- package/shared/named-threads.mjs +72 -0
- package/shared/not-shipped.d.mts +22 -0
- package/shared/not-shipped.mjs +89 -0
- package/shared/notify-rules.d.mts +6 -0
- package/shared/notify-rules.mjs +36 -8
- package/shared/phone-files.mjs +28 -0
- package/shared/phone-settings.mjs +118 -0
- package/shared/phone-snapshot.d.mts +16 -0
- package/shared/phone-snapshot.mjs +348 -0
- package/shared/picture-formats.d.mts +1 -0
- package/shared/picture-formats.mjs +13 -0
- package/shared/rank.d.mts +2 -1
- package/shared/rank.mjs +32 -19
- package/shared/repeats.d.mts +8 -0
- package/shared/repeats.mjs +33 -2
- package/shared/room-cards.mjs +167 -0
- package/shared/room-links.d.mts +4 -0
- package/shared/room-links.mjs +21 -0
- package/shared/runs-on.d.mts +3 -3
- package/shared/runs-on.mjs +8 -5
- package/shared/shipped-note.d.mts +4 -0
- package/shared/shipped-note.mjs +6 -0
- package/shared/shortcuts.mjs +14 -6
- package/shared/spawn-trouble.d.mts +4 -0
- package/shared/spawn-trouble.mjs +443 -40
- package/shared/team-rules.mjs +9 -2
- package/shared/teammates.d.mts +19 -0
- package/shared/teammates.mjs +756 -0
- package/shared/terminal-focus.d.mts +1 -0
- package/shared/terminal-focus.mjs +13 -0
- package/shared/thread-cards.mjs +4 -1
- package/shared/usage.d.mts +9 -0
- package/shared/usage.mjs +49 -3
- package/shared/websites.d.mts +15 -0
- package/shared/websites.mjs +61 -0
- package/shared/work-items.mjs +127 -30
- package/shared/work-lines.mjs +92 -244
- package/renderer/dist/assets/channel-BlT2g9Sz.js +0 -1
- package/renderer/dist/assets/cornerTag-BLrxiDE5.css +0 -1
- package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +0 -1
- package/renderer/dist/assets/main-ChNdnUvt.js +0 -1132
- package/renderer/dist/assets/main-nj0QR5kg.css +0 -32
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +0 -8
package/main/codex.mjs
CHANGED
|
@@ -1,93 +1,26 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Readers that turn Codex app-server notifications into the same session
|
|
2
|
+
// fields, trace lines, tail and code artifact that the Claude Code readers
|
|
3
|
+
// produce, so nothing downstream needs to know which engine ran. They take
|
|
4
|
+
// `(method, params)` because the transport (main/codex-app-server.mjs) has
|
|
5
|
+
// already parsed each event. Each reader catches its own errors, because one
|
|
6
|
+
// that throws inside the supervisor's stream loop goes silent for the rest of
|
|
7
|
+
// the session.
|
|
2
8
|
//
|
|
3
|
-
//
|
|
4
|
-
// newline-delimited JSON-RPC 2.0, approvals answered or refused, threads
|
|
5
|
-
// routed. It hands its caller notifications and nothing else. THIS file is the
|
|
6
|
-
// layer above it, and its whole job is that a Codex run reaches her page
|
|
7
|
-
// looking like every other run.
|
|
9
|
+
// Facts about the Codex protocol that the code depends on:
|
|
8
10
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
11
|
+
// - A `fileChange` `diff` is a unified diff for an `update` but raw file
|
|
12
|
+
// content for an `add`, so `hunksFor` checks the string, not the kind.
|
|
13
|
+
// - There is no `turn/failed` notification. A failure is `turn/completed` with
|
|
14
|
+
// `turn.status: "failed"` and `turn.error`. `turn/failed` is still handled in
|
|
15
|
+
// case a release adds it.
|
|
16
|
+
// - `TurnStatus` is completed, interrupted, failed or inProgress. An
|
|
17
|
+
// interrupted turn has no answer, the same as a failed one.
|
|
18
|
+
// - `move_path` exists only on the `update` kind, so a rename is an update
|
|
19
|
+
// that moved.
|
|
20
|
+
// - `reasoning` items arrive empty, so they are not traced.
|
|
12
21
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// traceStreamLine -> the persisted trace, "HH:MM:SS [Bash] <cmd>"
|
|
16
|
-
// summarizeStreamLine -> the short In Progress tail
|
|
17
|
-
// changeFromTranscript-> the artifact she opens, files and hunks and rows
|
|
18
|
-
//
|
|
19
|
-
// Each has a twin below producing THE SAME OUTPUT SHAPE, so a later slice can
|
|
20
|
-
// swap readers per engine and nothing downstream — the thread, the byline, the
|
|
21
|
-
// file chips, the diff pane, the header arithmetic — needs to know which engine
|
|
22
|
-
// ran. That is not a tidiness preference.
|
|
23
|
-
//
|
|
24
|
-
// ONE STRUCTURAL DIFFERENCE, AND IT IS IN EVERY SIGNATURE HERE. Claude Code's
|
|
25
|
-
// readers take a raw LINE and parse it themselves, five times over, because
|
|
26
|
-
// stdout is where the events arrive. app-server's events arrive already parsed:
|
|
27
|
-
// the transport does the framing, the JSON and the routing, and calls
|
|
28
|
-
// `onNotification(method, params)`. So every reader below takes `(method,
|
|
29
|
-
// params)` instead of a line, and none of them contains a `JSON.parse`. The
|
|
30
|
-
// try/catch is still there, because a reader that throws inside the
|
|
31
|
-
// supervisor's stdout loop takes the reader out for the rest of the session,
|
|
32
|
-
// which is a worker that keeps running and reports nothing — the exact silent
|
|
33
|
-
// shape CLAUDE.md files under "A WORKER THAT DID NOT FINISH DELIVERED NOTHING".
|
|
34
|
-
//
|
|
35
|
-
// `git show 258d71d^:main/codex.mjs` is the 08-25 original, worth reading for
|
|
36
|
-
// intent. ITS EVENT PARSING IS OBSOLETE TWICE OVER: it targeted `codex exec
|
|
37
|
-
// --json`, whose item types are snake_case (`agent_message`, `file_change`) and
|
|
38
|
-
// which carries neither `phase` nor a real diff. One thing is carried over
|
|
39
|
-
// deliberately and credited here rather than rediscovered — see the trace.
|
|
40
|
-
//
|
|
41
|
-
// ------------------------------------------------------------------------
|
|
42
|
-
// MEASURED ON THIS MAC 2026-09-04 AGAINST codex-cli 0.148.0, over a 223-frame
|
|
43
|
-
// capture of one real turn, plus the CLI's own
|
|
44
|
-
// `codex app-server generate-json-schema` dump for the cases that turn did not
|
|
45
|
-
// produce. Six things differ from the written brief, and each is a whole
|
|
46
|
-
// feature that fails silently rather than loudly:
|
|
47
|
-
//
|
|
48
|
-
// 1. A `fileChange` OF KIND `add` IS NOT A DIFF. The `update` entry read
|
|
49
|
-
// "@@ -1 +1 @@\n-...\n+...\n"; the `add` entry in the same event was the
|
|
50
|
-
// string "hello\n" — raw file content, no `@@` header. A reader that
|
|
51
|
-
// assumes a unified diff per file finds zero hunks in every file a run
|
|
52
|
-
// creates. `hunksFor` therefore branches on the EVIDENCE IN THE STRING, not
|
|
53
|
-
// on the kind word, which also means a `delete` (which that run did not
|
|
54
|
-
// produce) is read correctly whichever of the two shapes it turns out to be.
|
|
55
|
-
//
|
|
56
|
-
// 2. THERE IS NO `turn/failed` NOTIFICATION. `ServerNotification` lists
|
|
57
|
-
// `turn/started`, `turn/completed` and `turn/moderationMetadata`, and the
|
|
58
|
-
// string "turn/failed" appears in none of the schema files. Failure arrives
|
|
59
|
-
// as `turn/completed` with `turn.status: "failed"` and a populated
|
|
60
|
-
// `turn.error` — "Only populated when the Turn's status is failed". A reader
|
|
61
|
-
// waiting for `turn/failed` marks every failed run a success. It is still
|
|
62
|
-
// ANSWERED below, because a notification we drop is a run that reads as
|
|
63
|
-
// unfinished forever, and being wrong in that direction costs nothing.
|
|
64
|
-
//
|
|
65
|
-
// 3. `TurnStatus` HAS FOUR VALUES: completed | interrupted | failed |
|
|
66
|
-
// inProgress. `interrupted` is what `turn/interrupt` leaves behind and it is a
|
|
67
|
-
// run with no answer exactly as much as `failed` is.
|
|
68
|
-
//
|
|
69
|
-
// 4. A COMMAND IN FLIGHT IS `inProgress`, camelCase, not `in_progress`.
|
|
70
|
-
// `CommandExecutionStatus` = inProgress | completed | failed | declined, and
|
|
71
|
-
// `declined` is the status an approval this app refuses produces.
|
|
72
|
-
//
|
|
73
|
-
// 5. `reasoning` ITEMS WERE EMPTY — all five of them, `summary: []` and
|
|
74
|
-
// `content: []`. See the trace for what that settles.
|
|
75
|
-
//
|
|
76
|
-
// 6. `move_path` EXISTS ONLY ON THE `update` KIND. `PatchChangeKind` is a
|
|
77
|
-
// three-way union and only `UpdatePatchChangeKind` carries the field. A
|
|
78
|
-
// rename is an update that moved; `add` and `delete` have nothing but
|
|
79
|
-
// `type`.
|
|
80
|
-
//
|
|
81
|
-
// WHAT THIS FILE DELIBERATELY DOES NOT READ, so the next slice does not have to
|
|
82
|
-
// re-derive that it is missing rather than broken. 0.148.0's `ThreadItem` union
|
|
83
|
-
// has eighteen members; five are handled here and the rest are ignored. The one
|
|
84
|
-
// that will matter first is `mcpToolCall` (`{server, tool, arguments, status,
|
|
85
|
-
// result, error, durationMs}`), because the store is an MCP server and a
|
|
86
|
-
// worker's claim/update/finish calls are therefore invisible in a Codex trace
|
|
87
|
-
// today where they are `[mcp__<store>__update_item]` lines in a Claude one.
|
|
88
|
-
// It is left out because no measured run has produced one and this repo's rule
|
|
89
|
-
// is that a mapping is verified by running it; `plan`, `webSearch`,
|
|
90
|
-
// `subAgentActivity` and `contextCompaction` are the same. None of them throws.
|
|
22
|
+
// Item types not handled here, such as `mcpToolCall`, `plan` and `webSearch`,
|
|
23
|
+
// are ignored.
|
|
91
24
|
|
|
92
25
|
import fs from 'node:fs';
|
|
93
26
|
import path from 'node:path';
|
|
@@ -95,85 +28,36 @@ import { shortPath } from './code-change.mjs';
|
|
|
95
28
|
import { filesFromPatch } from './git-change.mjs';
|
|
96
29
|
|
|
97
30
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* dropped and the trace is left to deliver it whole, which costs her the live
|
|
102
|
-
* view of one paragraph and cannot cost the process anything.
|
|
103
|
-
*
|
|
104
|
-
* AND IT IS NOW THE ONLY COPY. It was written twice for one slice, because the
|
|
105
|
-
* supervisor kept a module-private `const` that the reader slice was not
|
|
106
|
-
* allowed to edit. Two copies of a rule become two different rules within a
|
|
107
|
-
* week, and this pair drifting apart would be a live sentence one engine drops
|
|
108
|
-
* at 20,000 characters and the other keeps, sitting under its own traced copy
|
|
109
|
-
* on her screen forever. main/supervisor.mjs imports this one.
|
|
31
|
+
* The longest live sentence kept in memory. Past it the live text is dropped
|
|
32
|
+
* and the trace delivers the block whole. This is the only copy; the Claude
|
|
33
|
+
* Code stream reader (main/harnesses/claude-stream.mjs) imports it.
|
|
110
34
|
*/
|
|
111
35
|
export const SAYING_CAP = 20_000;
|
|
112
36
|
|
|
113
37
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* which is module-private there and which this slice may not modify. CLAUDE.md
|
|
118
|
-
* is right that two copies of a rule become two different rules within a week,
|
|
119
|
-
* so the copies are EXPORTED and
|
|
120
|
-
* tests/a-codex-turn-reads-like-every-other-run-on-her-page.test.mjs asserts
|
|
121
|
-
* they still match the source text of the original. Edit either and the other
|
|
122
|
-
* goes red naming the file it has to move with.
|
|
123
|
-
*
|
|
124
|
-
* They matter more here than they do there. Codex reports an absolute path for
|
|
125
|
-
* every file it touches, including its own /tmp scratch, so these are the
|
|
126
|
-
* common case rather than the corner one.
|
|
38
|
+
* Paths that never belong in a run's code artifact. Copied from main/code-change.mjs;
|
|
39
|
+
* tests/a-codex-turn-reads-like-every-other-run-on-her-page.test.mjs checks the
|
|
40
|
+
* two copies still match.
|
|
127
41
|
*/
|
|
128
42
|
export const NOT_CODE = /(^|\/)(node_modules|\.git)\//;
|
|
129
43
|
export const SCRATCH = /^\/(private\/)?(tmp|var\/folders)\//;
|
|
130
44
|
|
|
131
|
-
/* =============================== the answer =============================== */
|
|
132
|
-
|
|
133
45
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* one real turn, five agent messages, four of them `commentary` and EXACTLY ONE
|
|
142
|
-
* `final_answer`.
|
|
143
|
-
*
|
|
144
|
-
* So the answer is the final_answer message and nothing else. The 08-25 build
|
|
145
|
-
* took the LAST message it saw, because `codex exec` gave it no phase to read;
|
|
146
|
-
* doing that here puts a progress note on her row as the worker's finished
|
|
147
|
-
* word, and `awaitingHer` (supervisor.mjs) then reports a row as waiting on her
|
|
148
|
-
* when the work never finished. That is her 2026-08-06 incident — a `done` she
|
|
149
|
-
* never saw — arriving through a new door.
|
|
150
|
-
*
|
|
151
|
-
* LAST-WINS SURVIVES AS THE COMPATIBILITY PATH AND ONLY THAT. The schema says
|
|
152
|
-
* plainly that "providers do not emit this consistently, so callers must treat
|
|
153
|
-
* None as 'phase unknown' and keep compatibility behavior for legacy models".
|
|
154
|
-
* An unphased message therefore sets the answer — but never over a real
|
|
155
|
-
* final_answer, and never after one has arrived.
|
|
156
|
-
*
|
|
157
|
-
* A TURN THAT ENDS WITH NO FINAL ANSWER GETS NO RESULT, ON PURPOSE. That is
|
|
158
|
-
* what `saidNothingSheCanUse` is for: `result == null` is the first of the
|
|
159
|
-
* three shapes CLAUDE.md names, "said nothing at all -> the supervisor speaks
|
|
160
|
-
* for it", and there is already a writer for it in OUR words. Inventing a
|
|
161
|
-
* result here would take that row away from the writer that exists and hand her
|
|
162
|
-
* a guess in the agent's voice. The one thing worth doing is looking once more
|
|
163
|
-
* where the answer might still be: `turn/completed` carries `turn.items`, and
|
|
164
|
-
* in the measured run that array held exactly the final agentMessage even
|
|
165
|
-
* though `itemsView` was "summary". So a run whose item events were missed
|
|
166
|
-
* still has its answer in the last frame, for free.
|
|
46
|
+
* Sets the session's id and its final answer. app-server has no `result`
|
|
47
|
+
* event, so the answer is the `agentMessage` with `phase: "final_answer"`.
|
|
48
|
+
* A `commentary` message is never the answer, or a progress note would read as
|
|
49
|
+
* the finished word. A message with no phase still counts, because Codex's
|
|
50
|
+
* schema says older models may not send one, but never over a final answer.
|
|
51
|
+
* A turn that ends with no final answer gets no result, so the supervisor's own
|
|
52
|
+
* "said nothing" handling takes over.
|
|
167
53
|
*/
|
|
168
54
|
export function captureCodexEvent(session, method, params) {
|
|
169
55
|
try {
|
|
170
56
|
if (!session || typeof method !== 'string') return;
|
|
171
57
|
const p = params ?? {};
|
|
172
58
|
|
|
173
|
-
// The id `thread/resume` takes
|
|
174
|
-
// no `params.threadId
|
|
175
|
-
// exist leaves every session unresumable with no symptom until she types a
|
|
176
|
-
// reply on a finished row.
|
|
59
|
+
// The id `thread/resume` takes is at `params.thread.id`. This notification
|
|
60
|
+
// has no `params.threadId`.
|
|
177
61
|
if (method === 'thread/started') {
|
|
178
62
|
const id = p.thread?.id;
|
|
179
63
|
if (id) session.sessionId = String(id);
|
|
@@ -211,21 +95,18 @@ export function captureCodexEvent(session, method, params) {
|
|
|
211
95
|
session.codexFinalAnswer = true;
|
|
212
96
|
return;
|
|
213
97
|
}
|
|
214
|
-
// A turn that failed after
|
|
215
|
-
// must not read as answered because a message got through first.
|
|
98
|
+
// A turn that failed after a message got through still failed.
|
|
216
99
|
session.result = turnTrouble(turn, status);
|
|
217
100
|
session.resultIsError = true;
|
|
218
101
|
}
|
|
219
|
-
} catch { /*
|
|
102
|
+
} catch { /* see the note at the top of this file */ }
|
|
220
103
|
}
|
|
221
104
|
|
|
222
|
-
/** completed | interrupted | failed | inProgress, defaulting to the method. */
|
|
223
105
|
function turnStatus(method, turn) {
|
|
224
106
|
if (typeof turn?.status === 'string') return turn.status;
|
|
225
107
|
return method === 'turn/failed' ? 'failed' : 'completed';
|
|
226
108
|
}
|
|
227
109
|
|
|
228
|
-
/** Why a turn did not finish, in whatever words the server gave us. */
|
|
229
110
|
function turnTrouble(turn, status) {
|
|
230
111
|
const why = String(turn?.error?.message ?? '').trim();
|
|
231
112
|
const more = String(turn?.error?.additionalDetails ?? '').trim();
|
|
@@ -234,11 +115,8 @@ function turnTrouble(turn, status) {
|
|
|
234
115
|
}
|
|
235
116
|
|
|
236
117
|
/**
|
|
237
|
-
* The final answer
|
|
238
|
-
*
|
|
239
|
-
* ONLY a `final_answer` counts, never the last message in the list. The list
|
|
240
|
-
* arrives as a summary view and a summary of a turn that narrated and then
|
|
241
|
-
* died is narration.
|
|
118
|
+
* The final answer in `turn.items`, which still holds it when the item events
|
|
119
|
+
* were missed. Only a `final_answer` counts, never the last message.
|
|
242
120
|
*/
|
|
243
121
|
function finalAnswerIn(items) {
|
|
244
122
|
if (!Array.isArray(items)) return null;
|
|
@@ -250,30 +128,16 @@ function finalAnswerIn(items) {
|
|
|
250
128
|
return null;
|
|
251
129
|
}
|
|
252
130
|
|
|
253
|
-
/* ============================ the live sentence =========================== */
|
|
254
|
-
|
|
255
131
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* Claude Code's half of that is `streamingText` reading
|
|
259
|
-
* `--include-partial-messages` frames; `item/agentMessage/delta` is the same
|
|
260
|
-
* channel here, 157 frames in the measured turn, with the text at
|
|
261
|
-
* `params.delta`.
|
|
262
|
-
*
|
|
263
|
-
* The three rules are the supervisor's, unchanged, and each of them is load
|
|
264
|
-
* bearing (see supervisor.mjs:4131 for the long form):
|
|
265
|
-
*
|
|
266
|
-
* - ONE BLOCK AT A TIME. An `item/started` for an agentMessage starts the next
|
|
267
|
-
* one from empty, so the live string and the string the trace will carry are
|
|
268
|
-
* the same string, and the thread can drop the live copy the moment the
|
|
269
|
-
* traced one arrives rather than guessing.
|
|
270
|
-
* - NOT CLEARED WHEN THE MESSAGE FINISHES. The trace is written on one channel
|
|
271
|
-
* and read on another; clearing here blinks the sentence off her screen for
|
|
272
|
-
* however long the read takes and then puts it back.
|
|
273
|
-
* - DROPPED AT THE CAP, NEVER CUT. A cut string never matches the traced one
|
|
274
|
-
* and would sit under it on her screen forever.
|
|
132
|
+
* The sentence the agent is typing now, built from `item/agentMessage/delta`.
|
|
133
|
+
* Returns true when `session.saying` changed.
|
|
275
134
|
*
|
|
276
|
-
*
|
|
135
|
+
* - Each agentMessage starts from empty, so the live text matches the traced
|
|
136
|
+
* block and the thread can drop it when the trace arrives.
|
|
137
|
+
* - It is not cleared when the message finishes, or it would blink off screen
|
|
138
|
+
* until the trace is read.
|
|
139
|
+
* - Past `SAYING_CAP` it is dropped, never cut, because a cut string never
|
|
140
|
+
* matches the traced one.
|
|
277
141
|
*/
|
|
278
142
|
export function codexStreamingText(session, method, params) {
|
|
279
143
|
try {
|
|
@@ -281,9 +145,8 @@ export function codexStreamingText(session, method, params) {
|
|
|
281
145
|
const p = params ?? {};
|
|
282
146
|
|
|
283
147
|
if (method === 'item/started') {
|
|
284
|
-
// Only
|
|
285
|
-
//
|
|
286
|
-
// stays on screen until the trace delivers the same words.
|
|
148
|
+
// Only a new message resets it. A command or patch starting leaves the
|
|
149
|
+
// typed block on screen until the trace delivers it.
|
|
287
150
|
if (p.item?.type !== 'agentMessage') return false;
|
|
288
151
|
session.saying = '';
|
|
289
152
|
session.sayingAt = Date.now();
|
|
@@ -302,44 +165,14 @@ export function codexStreamingText(session, method, params) {
|
|
|
302
165
|
}
|
|
303
166
|
}
|
|
304
167
|
|
|
305
|
-
/* ================================ the trace =============================== */
|
|
306
|
-
|
|
307
168
|
/**
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* after it. Fuller than the tail, because "tool: Bash" answers nothing when the
|
|
311
|
-
* founder asks what a session actually did.
|
|
312
|
-
*
|
|
313
|
-
* ONLY `item/completed` IS TRACED, AND THE 08-25 BUILD ALREADY KNEW WHY: the
|
|
314
|
-
* same item arrives on `item/started` with an empty output and again on
|
|
315
|
-
* `item/completed` with the real one. Tracing both puts every command on her
|
|
316
|
-
* page twice — three commands became six lines in the measured turn.
|
|
317
|
-
*
|
|
318
|
-
* A READ DONE WITH `sed` IS TRACED AS THE SHELL COMMAND IT WAS. Codex reads and
|
|
319
|
-
* searches by running shell commands rather than by calling tools named Read
|
|
320
|
-
* and Grep, so its work lines really are `[Bash] sed -n '1,200p' math.js` where
|
|
321
|
-
* Claude Code's would be `[Read] math.js`. Rewriting one into the other is
|
|
322
|
-
* Agentbox claiming a tool call Codex never made — and the command is also the
|
|
323
|
-
* thing an approval card is raised about, so the honest spelling is the only
|
|
324
|
-
* one that stays true next to the card. Her peers instruction cuts this way as
|
|
325
|
-
* well as the other: equivalent does not mean identical.
|
|
169
|
+
* One persisted trace line, in the format renderer/src/run-files.ts parses:
|
|
170
|
+
* `HH:MM:SS [Tool] argument`.
|
|
326
171
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* `/^(Write|Edit|NotebookEdit)$/` on it to find the files a run made for her.
|
|
332
|
-
* A Codex worker that wrote her an .html through its patch tool would lose the
|
|
333
|
-
* chip on its card under any other word. THAT is what marking Codex as lesser
|
|
334
|
-
* would actually look like in the product. `Delete` and `Move` are new words
|
|
335
|
-
* because Claude Code has no tool that does either, so there is nothing to
|
|
336
|
-
* borrow and nothing to misrepresent.
|
|
337
|
-
*
|
|
338
|
-
* NOTHING IS WRITTEN FOR A `reasoning` ITEM. Claude Code's trace carries no
|
|
339
|
-
* thinking, one surface should not grow two vocabularies, and the measurement
|
|
340
|
-
* settles it besides: all five reasoning items in the captured turn had
|
|
341
|
-
* `summary: []` and `content: []`, so a reasoning line would have been five
|
|
342
|
-
* bare timestamps in a twenty-eight second run.
|
|
172
|
+
* Only `item/completed` is traced, because `item/started` carries the same item
|
|
173
|
+
* with empty output. Codex reads files with shell commands, and they are traced
|
|
174
|
+
* as `[Bash]`, the command it really ran. File changes use `Write` and `Edit`,
|
|
175
|
+
* because run-files.ts finds a run's files by those words.
|
|
343
176
|
*/
|
|
344
177
|
export function traceCodexEvent(method, params) {
|
|
345
178
|
try {
|
|
@@ -379,10 +212,8 @@ export function traceCodexEvent(method, params) {
|
|
|
379
212
|
if (!turn || typeof turn !== 'object') return null;
|
|
380
213
|
const status = turnStatus(method, turn);
|
|
381
214
|
const bad = status !== 'completed';
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
// duration goes in its place, which is the fact the turn actually
|
|
385
|
-
// carries (`durationMs`, 27746 in the measured run).
|
|
215
|
+
// A Codex run is always one turn, so the footer shows the duration where
|
|
216
|
+
// Claude Code's shows a turn count.
|
|
386
217
|
const span = Number.isFinite(turn.durationMs) ? `${(turn.durationMs / 1000).toFixed(1)}s` : '?s';
|
|
387
218
|
const said = bad ? turnTrouble(turn, status) : (finalAnswerIn(turn.items) ?? '');
|
|
388
219
|
return `\n${at} == RESULT (${status}${bad ? ' ERROR' : ''} · ${span}) ==\n${said.slice(0, 4000)}`;
|
|
@@ -394,21 +225,14 @@ export function traceCodexEvent(method, params) {
|
|
|
394
225
|
}
|
|
395
226
|
}
|
|
396
227
|
|
|
397
|
-
/** The changes on a fileChange item, always an array. */
|
|
398
228
|
function changesOf(item) {
|
|
399
229
|
return Array.isArray(item?.changes) ? item.changes : [];
|
|
400
230
|
}
|
|
401
231
|
|
|
402
232
|
/**
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* Exported because main/codex-approvals.mjs needs the same four words on the
|
|
408
|
-
* approval card she reads BEFORE a patch is applied. A second copy of it would
|
|
409
|
-
* be one edit away from a trace line saying `[Write]` under a card that said
|
|
410
|
-
* `Edit` about the same change. `move_path` lives only on the `update` kind
|
|
411
|
-
* (measurement 6 at the top of this file), so a rename is an update that moved.
|
|
233
|
+
* What one file change did, as Write, Delete, Move or Edit, or null when it
|
|
234
|
+
* names no file. The approval card in main/codex-approvals.mjs uses the same
|
|
235
|
+
* function, so the card and the trace use the same word.
|
|
412
236
|
*/
|
|
413
237
|
export function fileVerbWord(change) {
|
|
414
238
|
const from = String(change?.path ?? '').trim();
|
|
@@ -422,20 +246,15 @@ export function fileVerbWord(change) {
|
|
|
422
246
|
return null;
|
|
423
247
|
}
|
|
424
248
|
|
|
425
|
-
/** One file change as its bracket and its path. Null when there is no path. */
|
|
426
249
|
function fileVerb(change) {
|
|
427
250
|
const word = fileVerbWord(change);
|
|
428
251
|
if (!word) return null;
|
|
429
252
|
const from = String(change.path).trim().slice(0, 200);
|
|
430
|
-
// Both ends of a move, because the path she would go looking for is the one
|
|
431
|
-
// it is at now and the one above it is the one it left.
|
|
432
253
|
if (word === 'Move') return `[Move] ${from} -> ${String(change.kind.move_path).trim().slice(0, 200)}`;
|
|
433
254
|
return `[${word}] ${from}`;
|
|
434
255
|
}
|
|
435
256
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
/** The In Progress tail wants the gist, in the same words Claude Code's does. */
|
|
257
|
+
/** The short In Progress tail, in the same words the Claude Code reader uses. */
|
|
439
258
|
export function summarizeCodexEvent(method, params) {
|
|
440
259
|
try {
|
|
441
260
|
const p = params ?? {};
|
|
@@ -471,59 +290,25 @@ export function summarizeCodexEvent(method, params) {
|
|
|
471
290
|
}
|
|
472
291
|
}
|
|
473
292
|
|
|
474
|
-
/* =============================== the change =============================== */
|
|
475
|
-
|
|
476
293
|
/**
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
* app-server notification is delivered once and gone, so the only place the
|
|
482
|
-
* conversation half of a Codex artifact can be assembled from is memory.
|
|
483
|
-
*
|
|
484
|
-
* The measured turn was 223 frames end to end and left five of the two kinds
|
|
485
|
-
* this keeps, so five thousand is more than twenty times a whole real run's
|
|
486
|
-
* frames and a thousand times its edits. Past it nothing further is kept and
|
|
487
|
-
* the artifact is short by whatever came after -- which is a partial card and
|
|
488
|
-
* not a broken one, because the DISK half is read independently and wins
|
|
489
|
-
* wherever the two disagree (`mergeChange`, main/code-change.mjs). Growing
|
|
490
|
-
* without a bound is the alternative, and a run that edits in a loop would take
|
|
491
|
-
* her app down with it.
|
|
294
|
+
* The most frames one run keeps for its code artifact. app-server events are
|
|
295
|
+
* not saved to disk, so this half of the artifact can only be built from
|
|
296
|
+
* memory. Past the cap the artifact is partial, and the disk half read by
|
|
297
|
+
* `mergeChange` (main/code-change.mjs) still wins where the two disagree.
|
|
492
298
|
*/
|
|
493
299
|
export const CHANGE_FRAME_CAP = 5_000;
|
|
494
300
|
|
|
495
301
|
/**
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
* big each one is, and each one may carry up to the transport's own frame limit
|
|
500
|
-
* (MAX_FRAME_LENGTH, 16 MiB). Five thousand of those is eighty gigabytes, which
|
|
501
|
-
* is not a bound at all: it is her app dying on the exact run this cap was
|
|
502
|
-
* written to survive, the one that edits in a loop.
|
|
503
|
-
*
|
|
504
|
-
* Sixteen mebibytes, which is one whole frame's worth of everything. The
|
|
505
|
-
* measured turn's five kept frames were a few kilobytes between them, so this
|
|
506
|
-
* is thousands of times a real run and still a number the process can hold. Past
|
|
507
|
-
* it nothing further is kept and the artifact is short by whatever came after --
|
|
508
|
-
* a partial card and not a broken one, because the DISK half is read
|
|
509
|
-
* independently and wins wherever the two disagree (`mergeChange`,
|
|
510
|
-
* main/code-change.mjs).
|
|
302
|
+
* The most characters of diff and text one run keeps, because a frame can be
|
|
303
|
+
* as large as the transport's 16 MiB frame limit, so a frame count alone does
|
|
304
|
+
* not bound memory.
|
|
511
305
|
*/
|
|
512
306
|
export const CHANGE_BYTE_CAP = 16 * 1024 * 1024;
|
|
513
307
|
|
|
514
308
|
/**
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* are the edits themselves, and the `agentMessage` items, which are where the
|
|
519
|
-
* sentence beside a hunk comes from. Their ORDER is the whole of how the second
|
|
520
|
-
* attaches to the first (`changeFromCodexTurn` takes the nearest preceding
|
|
521
|
-
* message), so they are kept in one list rather than two.
|
|
522
|
-
*
|
|
523
|
-
* Called on the session's own event path, so it is written to be unable to
|
|
524
|
-
* throw there: a reader that throws inside the supervisor's stream loop takes
|
|
525
|
-
* itself out for the rest of the session, which is a worker that keeps running
|
|
526
|
-
* and reports nothing.
|
|
309
|
+
* Keeps the `fileChange` and `agentMessage` frames the artifact needs, in one
|
|
310
|
+
* list, because `changeFromCodexTurn` pairs each change with the message just
|
|
311
|
+
* before it.
|
|
527
312
|
*/
|
|
528
313
|
export function rememberCodexChange(session, method, params) {
|
|
529
314
|
try {
|
|
@@ -532,28 +317,17 @@ export function rememberCodexChange(session, method, params) {
|
|
|
532
317
|
if (type !== 'fileChange' && type !== 'agentMessage') return;
|
|
533
318
|
const kept = session.codexChange ?? (session.codexChange = []);
|
|
534
319
|
if (kept.length >= CHANGE_FRAME_CAP) return;
|
|
535
|
-
// AND THE SIZE OF WHAT IS ALREADY HELD, WHICH THE COUNT DOES NOT BOUND.
|
|
536
|
-
// A fileChange frame carries the whole per-file diff, so five thousand of
|
|
537
|
-
// them is only a bound if each one is small, and the one run this cap was
|
|
538
|
-
// written for -- the one that edits in a loop -- is exactly the run where
|
|
539
|
-
// they are not. Measured off the frame itself rather than off a guess: a
|
|
540
|
-
// diff is a string, and its length is the number that matters.
|
|
541
320
|
const size = frameSize(params);
|
|
542
321
|
const held = session.codexChangeBytes ?? 0;
|
|
543
322
|
if (held + size > CHANGE_BYTE_CAP) return;
|
|
544
323
|
session.codexChangeBytes = held + size;
|
|
545
324
|
kept.push({ method, params });
|
|
546
|
-
} catch { /*
|
|
325
|
+
} catch { /* see the note at the top of this file */ }
|
|
547
326
|
}
|
|
548
327
|
|
|
549
328
|
/**
|
|
550
|
-
* Roughly how
|
|
551
|
-
*
|
|
552
|
-
* The two fields that actually carry weight are the per-file `diff` and the
|
|
553
|
-
* agent's `text`; everything else on a `ThreadItem` is ids and enums. Counted
|
|
554
|
-
* rather than JSON.stringify'd because this runs on the session's own event
|
|
555
|
-
* path, once per item, and serialising a ten-megabyte diff to measure it is the
|
|
556
|
-
* cost the cap exists to avoid.
|
|
329
|
+
* Roughly how big one frame is: the length of its `text` and each `diff`.
|
|
330
|
+
* Not JSON.stringify, which would cost the very memory the cap guards.
|
|
557
331
|
*/
|
|
558
332
|
function frameSize(params) {
|
|
559
333
|
const item = params?.item ?? {};
|
|
@@ -563,34 +337,17 @@ function frameSize(params) {
|
|
|
563
337
|
}
|
|
564
338
|
|
|
565
339
|
/**
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
* arithmetic under it (renderer/src/code-artifact.ts) and the chip on the card
|
|
570
|
-
* (renderer/src/run-files.ts) are untouched by this slice, so matching the
|
|
571
|
-
* shape exactly IS the deliverable: `{files:[{path, abs, hunks:[{rows, plus,
|
|
572
|
-
* minus, said?, at?}], plus, minus, at}], roots, plus, minus, editCount,
|
|
573
|
-
* withProse, startedAt, endedAt}`.
|
|
340
|
+
* What the run changed, in exactly the shape main/code-change.mjs produces:
|
|
341
|
+
* `{files:[{path, abs, hunks:[{rows, plus, minus, said?, at?}], plus, minus,
|
|
342
|
+
* at}], roots, plus, minus, editCount, withProse, startedAt, endedAt}`.
|
|
574
343
|
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
* `codex exec`'s patch text. `item/completed` now carries a real per-file diff,
|
|
578
|
-
* so the rows come out of the same unified-diff reader git's own changes go
|
|
579
|
-
* through (`filesFromPatch`, main/git-change.mjs) rather than a second copy of
|
|
580
|
-
* one. The only thing synthesized is the two-line file header that reader
|
|
581
|
-
* expects; the hunk bodies are the server's own bytes.
|
|
582
|
-
*
|
|
583
|
-
* `events` are the notifications as the transport delivered them, `{method,
|
|
584
|
-
* params}`, in order. `roots` are the checkouts this run's work happened in and
|
|
585
|
-
* `since` is the moment to start from, both exactly as code-change.mjs means
|
|
586
|
-
* them. `rememberCodexChange` below is what collects them.
|
|
344
|
+
* `events` are the `{method, params}` frames `rememberCodexChange` kept, in
|
|
345
|
+
* order. `roots` and `since` mean what they mean in code-change.mjs.
|
|
587
346
|
*/
|
|
588
347
|
export function changeFromCodexTurn(events, { roots = [], since = 0 } = {}) {
|
|
589
348
|
const into = collector({ roots });
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
// separate agentMessage just before it applies the patch, so the nearest
|
|
593
|
-
// preceding message is the same sentence.
|
|
349
|
+
// Codex narrates in a separate agentMessage just before it applies a patch,
|
|
350
|
+
// so the sentence beside a hunk is the nearest preceding message.
|
|
594
351
|
let said = '';
|
|
595
352
|
|
|
596
353
|
for (const event of Array.isArray(events) ? events : []) {
|
|
@@ -617,13 +374,7 @@ export function changeFromCodexTurn(events, { roots = [], since = 0 } = {}) {
|
|
|
617
374
|
return into.done();
|
|
618
375
|
}
|
|
619
376
|
|
|
620
|
-
/**
|
|
621
|
-
* Where the file IS once the change has landed.
|
|
622
|
-
*
|
|
623
|
-
* A rename is filed under the path it moved to, which is the rule
|
|
624
|
-
* main/git-change.mjs already states for git's own renames. A change she
|
|
625
|
-
* opens has to lead to a file that exists.
|
|
626
|
-
*/
|
|
377
|
+
/** Where the file is after the change. A rename is filed under its new path, as in main/git-change.mjs. */
|
|
627
378
|
function destinationOf(change) {
|
|
628
379
|
const to = typeof change?.kind?.move_path === 'string' ? change.kind.move_path.trim() : '';
|
|
629
380
|
const from = String(change?.path ?? '').trim();
|
|
@@ -631,23 +382,16 @@ function destinationOf(change) {
|
|
|
631
382
|
}
|
|
632
383
|
|
|
633
384
|
/**
|
|
634
|
-
* One file change as hunks of rows.
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
* from the top of this file: an `update` arrived as "@@ -1 +1 @@\n-...\n+...",
|
|
638
|
-
* an `add` in the SAME event arrived as "hello\n". A delete did not occur in
|
|
639
|
-
* that run at all, so guessing which shape it uses would be exactly the kind of
|
|
640
|
-
* unverified mapping this repo refuses; asking the string instead answers for
|
|
641
|
-
* every kind, including ones this CLI has not shipped yet.
|
|
385
|
+
* One file change as hunks of rows. It checks whether the string is a unified
|
|
386
|
+
* diff rather than trusting the kind, because an add arrives as raw content and
|
|
387
|
+
* the shape of a delete has not been observed.
|
|
642
388
|
*/
|
|
643
389
|
function hunksFor(change) {
|
|
644
390
|
const diff = String(change?.diff ?? '');
|
|
645
391
|
if (!diff) return [];
|
|
646
392
|
if (diff.startsWith('@@')) {
|
|
647
|
-
//
|
|
648
|
-
//
|
|
649
|
-
// prefixes and trims, and a real path with a space in it would come back
|
|
650
|
-
// wrong; the path is already known here and is never read back out of this.
|
|
393
|
+
// `filesFromPatch` needs a file header. A placeholder name is used because
|
|
394
|
+
// that reader trims names, and the real path is never read back from it.
|
|
651
395
|
return filesFromPatch(`--- a/f\n+++ b/f\n${diff}`)[0]?.hunks ?? [];
|
|
652
396
|
}
|
|
653
397
|
const mark = change?.kind?.type === 'delete' ? '-' : '+';
|
|
@@ -662,7 +406,7 @@ function splitLines(text) {
|
|
|
662
406
|
return out;
|
|
663
407
|
}
|
|
664
408
|
|
|
665
|
-
/**
|
|
409
|
+
/** The last non-empty line of what the agent said, as code-change.mjs keeps it. */
|
|
666
410
|
function proseFrom(text) {
|
|
667
411
|
const said = String(text ?? '').trim();
|
|
668
412
|
if (!said) return '';
|
|
@@ -671,18 +415,10 @@ function proseFrom(text) {
|
|
|
671
415
|
}
|
|
672
416
|
|
|
673
417
|
/**
|
|
674
|
-
* Each root
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
* `/private/tmp/proof/widget-repo/math.js` matched no root and was dropped, so
|
|
679
|
-
* a run that really did change her code finished with an artifact that did not
|
|
680
|
-
* mention it. Codex reports the RESOLVED spelling for every path it touches, so
|
|
681
|
-
* for this engine that is the ordinary case rather than the corner one.
|
|
682
|
-
*
|
|
683
|
-
* Resolved off the deepest ancestor that still exists rather than off the root
|
|
684
|
-
* itself, because this runs after the work is over and CLAUDE.md's process
|
|
685
|
-
* etiquette asks a session to remove its worktree as the last thing it does.
|
|
418
|
+
* Each root as given and as its real path. Codex reports resolved paths, such
|
|
419
|
+
* as /private/tmp for /tmp on macOS, which would otherwise match no root. The
|
|
420
|
+
* real path is taken from the deepest ancestor that still exists, because the
|
|
421
|
+
* worktree may already be removed when this runs.
|
|
686
422
|
*/
|
|
687
423
|
function rootSpellings(roots) {
|
|
688
424
|
const out = new Set();
|
|
@@ -706,11 +442,7 @@ function rootSpellings(roots) {
|
|
|
706
442
|
return [...out];
|
|
707
443
|
}
|
|
708
444
|
|
|
709
|
-
/**
|
|
710
|
-
* The tally, and the two rules that decide whether a file belongs in her
|
|
711
|
-
* artifact at all. The same collector code-change.mjs keeps private, over
|
|
712
|
-
* absolute paths that arrived in an event instead of in a transcript.
|
|
713
|
-
*/
|
|
445
|
+
/** The same tally as the private collector in code-change.mjs, over absolute paths from events. */
|
|
714
446
|
function collector({ roots = [] } = {}) {
|
|
715
447
|
const files = new Map();
|
|
716
448
|
const totals = { plus: 0, minus: 0, editCount: 0, withProse: 0, startedAt: 0, endedAt: 0 };
|
|
@@ -721,8 +453,8 @@ function collector({ roots = [] } = {}) {
|
|
|
721
453
|
abs = String(abs ?? '').trim();
|
|
722
454
|
if (!abs || NOT_CODE.test(abs)) return;
|
|
723
455
|
const rel = shortPath(abs, spellings);
|
|
724
|
-
//
|
|
725
|
-
//
|
|
456
|
+
// A path inside a root always counts, even under /tmp. Scratch outside
|
|
457
|
+
// every root is dropped.
|
|
726
458
|
if (rel === null && SCRATCH.test(abs)) return;
|
|
727
459
|
if (!rows.length) return;
|
|
728
460
|
const p = rows.filter((r) => r[0] === '+').length;
|