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
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Reads Codex transcripts for the watch, off the app's main process
|
|
2
|
+
// (codex-threads.mjs readCodexThreadsOffThread).
|
|
3
|
+
import { parentPort } from 'node:worker_threads';
|
|
4
|
+
import { readCodexThreads } from './codex-threads.mjs';
|
|
5
|
+
|
|
6
|
+
parentPort.on('message', ({ id, opts }) => {
|
|
7
|
+
try {
|
|
8
|
+
parentPort.postMessage({ id, ok: true, value: readCodexThreads(opts) });
|
|
9
|
+
} catch (err) {
|
|
10
|
+
parentPort.postMessage({ id, ok: false, error: String(err?.message ?? err) });
|
|
11
|
+
}
|
|
12
|
+
});
|
package/main/codex-threads.mjs
CHANGED
|
@@ -24,11 +24,13 @@
|
|
|
24
24
|
// table. It is not read: Electron ships no sqlite, the file is written live
|
|
25
25
|
// under WAL, and the two plain files carry what the row needs.
|
|
26
26
|
import fs from 'node:fs';
|
|
27
|
+
import { Worker } from 'node:worker_threads';
|
|
27
28
|
import os from 'node:os';
|
|
28
29
|
import path from 'node:path';
|
|
29
30
|
import { RECENT_DAYS, isScratchFolder, shortFolder, threadTitle } from './agent-sessions.mjs';
|
|
30
31
|
import { codexDataDirs } from './agent-homes.mjs';
|
|
31
32
|
import { WAS_SLUGS, nameSlug } from '../shared/product-name.mjs';
|
|
33
|
+
import { howTheChatEnded } from '../shared/agent-import.mjs';
|
|
32
34
|
|
|
33
35
|
export const CODEX_RECENT_DAYS = RECENT_DAYS;
|
|
34
36
|
/** The originator Codex records for a run this app started (CLIENT_INFO.name), now and before. */
|
|
@@ -164,7 +166,25 @@ const partsOf = (payload) => (Array.isArray(payload?.content) ? payload.content
|
|
|
164
166
|
* `message` with a `role` and a `content` array of `input_text`, `input_image`
|
|
165
167
|
* and `output_text` parts. Current Codex threads are paginated, and a build
|
|
166
168
|
* that read only legacy showed "Codex has not answered yet." on every row. */
|
|
169
|
+
// Each transcript as last read, kept while its size and time are unchanged:
|
|
170
|
+
// her Codex folder can hold thousands of them, and reading every one again
|
|
171
|
+
// each minute held the app for seconds.
|
|
172
|
+
const ROLLOUTS_KEPT = 5000;
|
|
173
|
+
const rollouts = new Map();
|
|
174
|
+
|
|
167
175
|
export function readRollout(file) {
|
|
176
|
+
let st = null;
|
|
177
|
+
try { st = fs.statSync(file); } catch { return null; }
|
|
178
|
+
const kept = rollouts.get(file);
|
|
179
|
+
if (kept && kept.size === st.size && kept.mtimeMs === st.mtimeMs) return kept.value;
|
|
180
|
+
const value = readRolloutAfresh(file);
|
|
181
|
+
rollouts.delete(file);
|
|
182
|
+
rollouts.set(file, { size: st.size, mtimeMs: st.mtimeMs, value });
|
|
183
|
+
if (rollouts.size > ROLLOUTS_KEPT) rollouts.delete(rollouts.keys().next().value);
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function readRolloutAfresh(file) {
|
|
168
188
|
let text = '';
|
|
169
189
|
try {
|
|
170
190
|
const fd = fs.openSync(file, 'r');
|
|
@@ -179,6 +199,8 @@ export function readRollout(file) {
|
|
|
179
199
|
let meta = null;
|
|
180
200
|
let prompt = '';
|
|
181
201
|
let last = '';
|
|
202
|
+
// Whether the person wrote after Codex's last answer, so it is not waiting.
|
|
203
|
+
let herTurnLast = false;
|
|
182
204
|
let turns = 0;
|
|
183
205
|
let images = 0;
|
|
184
206
|
let lastActive = 0;
|
|
@@ -213,11 +235,12 @@ export function readRollout(file) {
|
|
|
213
235
|
if (p.type === 'user_message') {
|
|
214
236
|
const said = cleanPrompt(p.message);
|
|
215
237
|
if (!prompt && said && !isInjected(said)) prompt = said;
|
|
238
|
+
if (said && !isInjected(said)) herTurnLast = true;
|
|
216
239
|
turns += 1;
|
|
217
240
|
sawEventTurns = true;
|
|
218
241
|
} else if (p.type === 'agent_message') {
|
|
219
242
|
const said = cleanAnswer(p.message);
|
|
220
|
-
if (said) last = said;
|
|
243
|
+
if (said) { last = said; herTurnLast = false; }
|
|
221
244
|
}
|
|
222
245
|
continue;
|
|
223
246
|
}
|
|
@@ -231,16 +254,17 @@ export function readRollout(file) {
|
|
|
231
254
|
.filter(Boolean);
|
|
232
255
|
if (!hers.length) continue; // a turn made of nothing but Codex's own preamble
|
|
233
256
|
if (!prompt) prompt = hers.join('\n\n').trim();
|
|
257
|
+
herTurnLast = true;
|
|
234
258
|
if (!sawEventTurns) turns += 1;
|
|
235
259
|
} else if (p.role === 'assistant') {
|
|
236
260
|
const said = cleanAnswer(partsOf(p).filter((c) => c.type === 'output_text').map((c) => c.text ?? '').join('\n'));
|
|
237
|
-
if (said) last = said;
|
|
261
|
+
if (said) { last = said; herTurnLast = false; }
|
|
238
262
|
}
|
|
239
263
|
}
|
|
240
264
|
if (!meta || !meta.id) return null;
|
|
241
265
|
let mtime = 0;
|
|
242
266
|
try { mtime = fs.statSync(file).mtimeMs; } catch { mtime = 0; }
|
|
243
|
-
return { ...meta, prompt, last, turns, images, lastActive, path: file, mtime };
|
|
267
|
+
return { ...meta, prompt, last, herTurnLast, turns, images, lastActive, path: file, mtime };
|
|
244
268
|
}
|
|
245
269
|
|
|
246
270
|
/**
|
|
@@ -319,6 +343,50 @@ function readOneCodexHome(codexDir, { home, days, now, cutoff, index, byId, skip
|
|
|
319
343
|
prompt: r.prompt,
|
|
320
344
|
last: r.last,
|
|
321
345
|
live: isLive(r.mtime, now),
|
|
346
|
+
...(r.herTurnLast ? { waiting: false, ask: '' } : howTheChatEnded(r.last)),
|
|
347
|
+
lastSaid: r.last,
|
|
348
|
+
firstSaid: r.prompt,
|
|
322
349
|
});
|
|
323
350
|
}
|
|
324
351
|
}
|
|
352
|
+
|
|
353
|
+
// THE SAME READ IN A THREAD OF ITS OWN, for the watch that runs every minute:
|
|
354
|
+
// a first read of a large Codex folder takes seconds, and on the main process
|
|
355
|
+
// that is seconds of the app not answering. One worker, kept for the life of
|
|
356
|
+
// the app, so its transcripts stay remembered between reads. Where a worker
|
|
357
|
+
// cannot start, the read runs here as before.
|
|
358
|
+
const OFF_THREAD_DEADLINE_MS = 5 * 60_000;
|
|
359
|
+
let worker = null;
|
|
360
|
+
let asked = 0;
|
|
361
|
+
const waiting = new Map();
|
|
362
|
+
export function readCodexThreadsOffThread(opts = {}) {
|
|
363
|
+
try {
|
|
364
|
+
if (!worker) {
|
|
365
|
+
worker = new Worker(new URL('./codex-threads-worker.mjs', import.meta.url));
|
|
366
|
+
worker.unref();
|
|
367
|
+
worker.on('message', ({ id, ok, value, error }) => {
|
|
368
|
+
const w = waiting.get(id);
|
|
369
|
+
if (!w) return;
|
|
370
|
+
waiting.delete(id);
|
|
371
|
+
clearTimeout(w.timer);
|
|
372
|
+
if (ok) w.resolve(value); else w.reject(new Error(error));
|
|
373
|
+
});
|
|
374
|
+
const fail = (err) => {
|
|
375
|
+
for (const w of waiting.values()) { clearTimeout(w.timer); w.reject(err); }
|
|
376
|
+
waiting.clear();
|
|
377
|
+
worker = null;
|
|
378
|
+
};
|
|
379
|
+
worker.on('error', fail);
|
|
380
|
+
worker.on('exit', () => fail(new Error('the Codex reader stopped')));
|
|
381
|
+
}
|
|
382
|
+
} catch {
|
|
383
|
+
return Promise.resolve().then(() => readCodexThreads(opts));
|
|
384
|
+
}
|
|
385
|
+
const id = (asked += 1);
|
|
386
|
+
return new Promise((resolve, reject) => {
|
|
387
|
+
const timer = setTimeout(() => { waiting.delete(id); reject(new Error('the Codex reader did not answer in time')); }, OFF_THREAD_DEADLINE_MS);
|
|
388
|
+
timer.unref?.();
|
|
389
|
+
waiting.set(id, { resolve, reject, timer });
|
|
390
|
+
worker.postMessage({ id, opts });
|
|
391
|
+
});
|
|
392
|
+
}
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
// such records across its most recent sessions, the newest written within
|
|
17
17
|
// the minute.
|
|
18
18
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
19
|
+
// A LIVE PUSH STILL WINS, AND A QUIET ONE NO LONGER DOES (w-7b7182ef61). A
|
|
20
|
+
// notification that arrived a moment ago is this second's truth and nothing on
|
|
21
|
+
// disk can be newer, so main/codex-usage.mjs does not open a file while one is
|
|
22
|
+
// fresh. Past that the two are compared and the newer is read, because an hours
|
|
23
|
+
// old push was being drawn over a newer line in this log.
|
|
22
24
|
//
|
|
23
25
|
// IT IS THE SAME ACCOUNT OR IT IS NOT READ. One CODEX_HOME is one login, and the
|
|
24
26
|
// log lives inside the home, so a reading taken from `~/.codex/sessions` is a
|
package/main/codex-usage.mjs
CHANGED
|
@@ -62,8 +62,38 @@
|
|
|
62
62
|
// level down.
|
|
63
63
|
|
|
64
64
|
import { codexLimits, mergeRateLimits } from '../shared/codex-usage.mjs';
|
|
65
|
+
import { limitsStillRunning } from '../shared/usage.mjs';
|
|
65
66
|
import { loggedCodexLimits } from './codex-usage-file.mjs';
|
|
66
67
|
|
|
68
|
+
const LOG_EVERY_MS = 30_000;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* HOW LONG A PUSH IS THIS SECOND'S TRUTH (w-7b7182ef61).
|
|
72
|
+
*
|
|
73
|
+
* "The push still wins" holds while pushes are arriving, which is every few
|
|
74
|
+
* seconds during a turn; nothing on disk can be newer than one of those, so the
|
|
75
|
+
* busy case never opens a file. A push from four hours ago is just the oldest
|
|
76
|
+
* thing anybody knows, so past this it is compared against the log instead.
|
|
77
|
+
*/
|
|
78
|
+
const PUSH_FRESH_MS = 60_000;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Every Codex login's own reading, for the Settings page: each out of the
|
|
82
|
+
* session logs under its own home, and the one in use from `current`, which
|
|
83
|
+
* the push keeps newer while it works. A login that has never run here gets
|
|
84
|
+
* no limits rather than zeroes, and a window that has reset since is dropped,
|
|
85
|
+
* because a login not in use can go days without a new reading.
|
|
86
|
+
*/
|
|
87
|
+
export function codexAccountReadings({ profiles, homeOf, current = null, currentHome = null, now = Date.now(), readLog = loggedCodexLimits }) {
|
|
88
|
+
return profiles.map((profile) => {
|
|
89
|
+
const home = homeOf(profile);
|
|
90
|
+
const live = !!current?.limits?.length && home === currentHome;
|
|
91
|
+
const reading = live ? current : readLog(home, { now });
|
|
92
|
+
const limits = limitsStillRunning(live ? current.limits : codexLimits(reading?.snapshot), now);
|
|
93
|
+
return limits.length ? { profile, limits, at: reading.at } : { profile };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
export class CodexUsage {
|
|
68
98
|
/**
|
|
69
99
|
* @param {object} how @param { => string|null} how.home Which CODEX_HOME the
|
|
@@ -94,23 +124,42 @@ export class CodexUsage {
|
|
|
94
124
|
read() {
|
|
95
125
|
if (this._takenUnder !== null && this._takenUnder !== this._home()) this._forget();
|
|
96
126
|
const limits = codexLimits(this._snapshot);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
//
|
|
127
|
+
const pushed = limits.length ? { limits, at: this._at } : null;
|
|
128
|
+
if (pushed && this._now() - pushed.at < PUSH_FRESH_MS) return pushed;
|
|
129
|
+
// NOTHING HAS PUSHED LATELY, SO ASK WHAT CODEX ALREADY WROTE DOWN. This is
|
|
130
|
+
// the whole of: the push only ever arrives for work the app itself ran, and
|
|
131
|
+
// a person who runs Codex in its own app would see the panel stay empty
|
|
101
132
|
// while their weekly limit was full. main/codex-usage-file.mjs is a read of a file
|
|
102
133
|
// under this same CODEX_HOME. It starts no process and sends nothing
|
|
103
134
|
// anywhere, so neither rule above is bent.
|
|
104
|
-
|
|
135
|
+
const logged = this._logged();
|
|
136
|
+
if (!pushed) return logged;
|
|
137
|
+
return logged && logged.at > pushed.at ? logged : pushed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* `read()` with the log cache dropped, for a person who pressed Refresh:
|
|
142
|
+
* inside `LOG_EVERY_MS` a press would otherwise change nothing. It asks
|
|
143
|
+
* nobody anything, so both rules at the top of this file still hold. */
|
|
144
|
+
refreshNow() {
|
|
145
|
+
this._loggedRead = null;
|
|
146
|
+
return this.read();
|
|
105
147
|
}
|
|
106
148
|
|
|
107
149
|
/**
|
|
108
150
|
* The last figure in Codex's own session log under this home, shaped exactly
|
|
109
151
|
* like a pushed one. Null on a Mac where Codex has never run. */
|
|
110
152
|
_logged() {
|
|
111
|
-
|
|
153
|
+
// Asked on every request the window makes; the log is walked at most
|
|
154
|
+
// every half minute.
|
|
155
|
+
const home = this._home();
|
|
156
|
+
const now = this._now();
|
|
157
|
+
if (this._loggedRead && this._loggedRead.home === home && now - this._loggedRead.at < LOG_EVERY_MS) return this._loggedRead.value;
|
|
158
|
+
const reading = this._readLog(home, { now });
|
|
112
159
|
const limits = codexLimits(reading?.snapshot);
|
|
113
|
-
|
|
160
|
+
const value = limits.length ? { limits, at: reading.at } : null;
|
|
161
|
+
this._loggedRead = { home, at: now, value };
|
|
162
|
+
return value;
|
|
114
163
|
}
|
|
115
164
|
|
|
116
165
|
/**
|
package/main/codex-watch.mjs
CHANGED
|
@@ -73,9 +73,17 @@ export function noticeCodexAgents(threads, { count = null, seen = null, now = Da
|
|
|
73
73
|
return sent;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// Answers at once for a reader that answers at once, and as a promise for one
|
|
77
|
+
// that reads off the main process (readCodexThreadsOffThread).
|
|
76
78
|
export function scanCodex({ store, readThreads = readCodexThreads, now = Date.now(), count = null, seen = null } = {}) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
const none = { refreshed: 0, retold: 0, threads: 0 };
|
|
80
|
+
let read;
|
|
81
|
+
try { read = readThreads({ now }); } catch { return none; }
|
|
82
|
+
if (typeof read?.then === 'function') return read.then(({ threads }) => applyCodexThreads(threads, { store, now, count, seen }), () => none);
|
|
83
|
+
return applyCodexThreads(read?.threads ?? [], { store, now, count, seen });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function applyCodexThreads(threads, { store, now, count, seen }) {
|
|
79
87
|
noticeCodexAgents(threads, { count, seen, now });
|
|
80
88
|
const refreshed = store.refreshCodexMirrors(threads, { now });
|
|
81
89
|
const retold = store.refreshCodexAsks(threads, { now });
|
|
@@ -87,8 +95,14 @@ export function startCodexWatch({ store, readThreads = readCodexThreads, pollMs
|
|
|
87
95
|
// as the Claude side's set is: a fresh launch seeing the same conversation
|
|
88
96
|
// again is a fresh fact about the app being used.
|
|
89
97
|
const seen = new Set();
|
|
98
|
+
// One scan at a time: a read slower than the minute must not stack up.
|
|
99
|
+
let scanning = null;
|
|
90
100
|
const run = () => {
|
|
91
|
-
|
|
101
|
+
if (scanning) return scanning;
|
|
102
|
+
scanning = Promise.resolve().then(() => scanCodex({ store, readThreads, count, seen }))
|
|
103
|
+
.catch((e) => console.warn('codex watch:', e?.message ?? e))
|
|
104
|
+
.finally(() => { scanning = null; });
|
|
105
|
+
return scanning;
|
|
92
106
|
};
|
|
93
107
|
const first = setTimeout(run, firstDelayMs);
|
|
94
108
|
const timer = setInterval(run, pollMs);
|