agentbox-app 0.1.12 → 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/acp.mjs +296 -0
- package/main/agent-homes.mjs +158 -0
- package/main/agent-sessions.mjs +311 -260
- package/main/agent-updates.mjs +9 -22
- package/main/agents.mjs +179 -466
- package/main/analytics.mjs +42 -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-bin.mjs +45 -311
- package/main/claude-input.mjs +57 -7
- package/main/claude-launch-env.mjs +54 -0
- package/main/claude-models.mjs +39 -7
- package/main/claude-plan.mjs +64 -16
- package/main/claude-session-open.mjs +28 -0
- package/main/claude-usage-process.mjs +40 -0
- package/main/claude-usage.mjs +94 -59
- 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-bin.mjs +51 -249
- package/main/codex-models.mjs +20 -2
- package/main/codex-session.mjs +246 -506
- package/main/codex-threads-worker.mjs +12 -0
- package/main/codex-threads.mjs +112 -11
- package/main/codex-usage-file.mjs +5 -3
- package/main/codex-usage.mjs +56 -7
- package/main/codex-watch.mjs +83 -5
- package/main/codex.mjs +95 -363
- package/main/config.mjs +37 -5
- package/main/corner-tag.mjs +810 -0
- package/main/crash-report.mjs +26 -3
- package/main/dev-window.mjs +1 -1
- package/main/engine-bin.mjs +545 -0
- package/main/engine-setup.mjs +25 -24
- package/main/feedback.mjs +14 -31
- 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-stream.mjs +208 -0
- package/main/harnesses/claude.mjs +105 -0
- package/main/harnesses/codex.mjs +72 -0
- package/main/harnesses/common.mjs +78 -0
- package/main/harnesses/instructions-file.mjs +116 -0
- package/main/harnesses/opencode.mjs +234 -0
- package/main/harnesses.mjs +65 -0
- package/main/importable-threads.mjs +96 -0
- package/main/ipc.mjs +520 -135
- package/main/leftovers.mjs +117 -25
- package/main/liquid-glass.mjs +26 -0
- package/main/live-replies.mjs +10 -4
- package/main/local-app-frames.mjs +82 -0
- package/main/main-went-red.mjs +330 -0
- package/main/main.mjs +248 -37
- package/main/memory-gate-history.mjs +85 -20
- package/main/memory-gate-server.mjs +129 -11
- package/main/memory-gate.mjs +62 -8
- package/main/memory-log.mjs +124 -0
- 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 +64 -5
- package/main/opencode-server.mjs +271 -0
- package/main/opencode.mjs +287 -0
- package/main/phone-alerts.mjs +117 -0
- package/main/phone-inbox.mjs +625 -0
- package/main/phone-link.mjs +571 -0
- 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-address.mjs +28 -0
- package/main/relay-client.mjs +314 -0
- package/main/repeats.mjs +10 -4
- package/main/row-label.mjs +18 -47
- package/main/run-change.mjs +13 -0
- package/main/run-ledger.mjs +135 -0
- package/main/runs-on.mjs +32 -35
- package/main/save-attachments.mjs +41 -0
- package/main/serve.mjs +155 -9
- package/main/settings.mjs +364 -50
- package/main/shell-path.mjs +234 -0
- package/main/ship-conflict-brief.mjs +119 -0
- package/main/ship-queue.mjs +134 -20
- package/main/sign-in-files.mjs +3 -7
- package/main/source-updater.mjs +45 -11
- package/main/stage-attachment.mjs +68 -0
- package/main/stall-watch.mjs +50 -0
- package/main/start-hold.mjs +73 -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 +227 -53
- package/main/supervisor.mjs +3865 -699
- package/main/task-commands.mjs +13 -17
- package/main/task-folders-offthread.mjs +6 -1
- package/main/task-folders.mjs +897 -105
- package/main/task-terminals.mjs +147 -41
- 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/updater.mjs +71 -9
- package/main/webview-guard.mjs +45 -0
- package/main/worktree-include.mjs +365 -0
- 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 +30 -5
- package/renderer/dist/assets/abnfDiagram-YUKMZFIV-CoWB6YUK.js +1 -0
- package/renderer/dist/assets/arc-Ch0NrCul.js +1 -0
- package/renderer/dist/assets/architectureDiagram-47P4ROYG-CXz9M5tQ.js +36 -0
- package/renderer/dist/assets/blockDiagram-E7TT5TSB-DchGUuHH.js +140 -0
- package/renderer/dist/assets/c4Diagram-CRA5TL53-Cip-zNyr.js +38 -0
- package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
- package/renderer/dist/assets/chunk-2Q5K7J3B-BoXid_2r.js +1 -0
- package/renderer/dist/assets/chunk-5VM5RSS4-Cpez1rRz.js +15 -0
- package/renderer/dist/assets/chunk-7TKQ45FW-DKA84YkE.js +1 -0
- package/renderer/dist/assets/chunk-AW2ZBBNX-Dg-SumPG.js +88 -0
- package/renderer/dist/assets/chunk-FVRAUYC3-06TCvPm1.js +2 -0
- package/renderer/dist/assets/chunk-HTAEGDNF-CMEBy1Td.js +1 -0
- package/renderer/dist/assets/chunk-JWPE2WC7-BJ2ObmV-.js +1 -0
- package/renderer/dist/assets/chunk-KQW6MTUR-CGWOYXUE.js +1 -0
- package/renderer/dist/assets/chunk-XXDRQBXY-CiZp07XC.js +1 -0
- package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-DJNIX719.js +217 -0
- 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-CbkyZOUH.js +1 -0
- package/renderer/dist/assets/cynefin-EF2NZ3EQ-78iOroZO.js +141 -0
- package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-GcCopMs7.js +62 -0
- package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/renderer/dist/assets/dagre-W4DXFKR2-DHLlXLkz.js +4 -0
- package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/renderer/dist/assets/diagram-2UJZ2QOL-DuZSUHcH.js +200 -0
- package/renderer/dist/assets/diagram-OVF4WLC6-Ckxu3cqH.js +24 -0
- package/renderer/dist/assets/diagram-PFPMY2P6-CsTw9Ycn.js +30 -0
- package/renderer/dist/assets/diagram-UMYRVEAY-CTXd8qVg.js +24 -0
- package/renderer/dist/assets/diagram-YEKJPTXX-CJs1T_9m.js +41 -0
- package/renderer/dist/assets/diagram-ZIFT7M5P-CvjI23mf.js +3 -0
- package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-d_IeAzOJ.js +1 -0
- package/renderer/dist/assets/elk-IJKZMXRS-zo9KFP3N.js +27 -0
- package/renderer/dist/assets/erDiagram-O2IAPWRE-DqfYf5Dt.js +99 -0
- package/renderer/dist/assets/flowDiagram-OXPTDLAJ-DAWsMLmo.js +213 -0
- package/renderer/dist/assets/ganttDiagram-R7TSEDQI-4ml_bhwR.js +292 -0
- package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-B_WcqSId.js +106 -0
- package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
- package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-CI-jYqzc.js +2 -0
- package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
- package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-B56VWRlV.js +70 -0
- package/renderer/dist/assets/journeyDiagram-COXZFDF6-D1-p09Lr.js +139 -0
- package/renderer/dist/assets/kanban-definition-P3RFRI5V-Cv3w7JoN.js +89 -0
- package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
- package/renderer/dist/assets/linear-C35V2Spi.js +1 -0
- package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
- package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
- package/renderer/dist/assets/mermaid.core-BzGT5Vo9.js +314 -0
- package/renderer/dist/assets/mindmap-definition-LZFPQGKD-BZUfuymC.js +96 -0
- package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/renderer/dist/assets/pegDiagram-BGZESJAR-DFh9XNGe.js +1 -0
- package/renderer/dist/assets/pieDiagram-CAPJLFHJ-C65tPVsy.js +39 -0
- package/renderer/dist/assets/product-name-CSNW6d7s.js +40 -0
- package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-Cushj_nk.js +7 -0
- package/renderer/dist/assets/railroadDiagram-SM67HX2B-DZGebGx5.js +1 -0
- package/renderer/dist/assets/requirementDiagram-X7JNWC4A-DpAB0MNo.js +84 -0
- package/renderer/dist/assets/sankeyDiagram-UM26HJRW-D-29rHra.js +40 -0
- package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-FfOQAZol.js +169 -0
- package/renderer/dist/assets/sizeCapture-INFHLROL-C_n6fDBY.js +1 -0
- package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-BZjenzJG.js +291 -0
- package/renderer/dist/assets/swimlanes-N4OXWK64-DxaMU9vX.js +1 -0
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
- package/renderer/dist/assets/timeline-definition-YOQAKGHF-DTg5HJKz.js +120 -0
- package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-Bc3MZDsl.js +370 -0
- package/renderer/dist/assets/vennDiagram-BLWOH2XV-GIVGk9Id.js +34 -0
- package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-NmJnbQOg.js +78 -0
- package/renderer/dist/assets/xychartDiagram-TAQBALBS-p-kfGfnW.js +7 -0
- package/renderer/dist/corner-tag.html +13 -0
- package/renderer/dist/emulator/audio-rules.mjs +74 -0
- package/renderer/dist/emulator/data/compression/README.md +11 -0
- package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
- package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
- package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +22 -0
- package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/package.json +65 -0
- package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
- package/renderer/dist/emulator/data/emulator.css +1614 -0
- package/renderer/dist/emulator/data/loader.js +170 -0
- package/renderer/dist/emulator/data/localization/README.md +63 -0
- package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
- package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
- package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
- package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
- package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
- package/renderer/dist/emulator/data/localization/en-US.json +339 -0
- package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
- package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
- package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
- package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
- package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
- package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
- package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
- package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
- package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
- package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
- package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
- package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
- package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
- package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
- package/renderer/dist/emulator/data/src/GameManager.js +464 -0
- package/renderer/dist/emulator/data/src/compression.js +142 -0
- package/renderer/dist/emulator/data/src/emulator.js +6158 -0
- package/renderer/dist/emulator/data/src/gamepad.js +167 -0
- package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
- package/renderer/dist/emulator/data/src/shaders.js +236 -0
- package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
- package/renderer/dist/emulator/data/src/storage.js +125 -0
- package/renderer/dist/emulator/data/version.json +5 -0
- package/renderer/dist/emulator/host.js +297 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/index.html +16 -3
- package/scripts/after-install.mjs +30 -8
- package/scripts/approval-server.sh +24 -5
- package/scripts/before-publish.mjs +12 -10
- 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 +62 -34
- package/shared/agents.d.mts +1 -0
- 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 +45 -1
- package/shared/claude-code-shape.mjs +6 -0
- package/shared/claude-commands.generated.mjs +6 -4
- package/shared/claude-models.generated.mjs +6 -4
- 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 +36 -0
- package/shared/corner-tag.mjs +396 -0
- package/shared/dead-run-trace.mjs +19 -2
- package/shared/engines.mjs +76 -300
- 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 +111 -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 +31 -0
- package/shared/notify-rules.mjs +55 -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/provider-commands.mjs +10 -2
- package/shared/rank.d.mts +4 -1
- package/shared/rank.mjs +82 -19
- package/shared/relay-address.mjs +6 -0
- package/shared/relay-crypto.mjs +165 -0
- 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 -13
- package/shared/runs-on.mjs +31 -52
- package/shared/shipped-note.d.mts +4 -0
- package/shared/shipped-note.mjs +6 -0
- package/shared/shortcuts.mjs +24 -10
- package/shared/spawn-trouble.d.mts +4 -0
- package/shared/spawn-trouble.mjs +484 -30
- 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/terminal-input.d.mts +1 -0
- package/shared/terminal-input.mjs +30 -0
- package/shared/terminal-keys.d.mts +1 -0
- package/shared/terminal-keys.mjs +16 -0
- package/shared/terminal-size.d.mts +4 -0
- package/shared/terminal-size.mjs +29 -0
- package/shared/thread-cards.mjs +4 -1
- package/shared/usage.d.mts +12 -0
- package/shared/usage.mjs +56 -3
- package/shared/websites.d.mts +15 -0
- package/shared/websites.mjs +61 -0
- package/shared/where-it-runs-from.d.mts +43 -0
- package/shared/where-it-runs-from.mjs +155 -0
- package/shared/work-items.mjs +150 -29
- package/shared/work-lines.mjs +93 -245
- package/shared/xterm-invalid-range.d.mts +2 -0
- package/shared/xterm-invalid-range.mjs +58 -0
- package/renderer/dist/assets/index-ChKsQkRw.js +0 -1169
- package/renderer/dist/assets/index-ClBXnq3B.css +0 -32
|
@@ -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,10 +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';
|
|
31
|
+
import { codexDataDirs } from './agent-homes.mjs';
|
|
30
32
|
import { WAS_SLUGS, nameSlug } from '../shared/product-name.mjs';
|
|
33
|
+
import { howTheChatEnded } from '../shared/agent-import.mjs';
|
|
31
34
|
|
|
32
35
|
export const CODEX_RECENT_DAYS = RECENT_DAYS;
|
|
33
36
|
/** The originator Codex records for a run this app started (CLIENT_INFO.name), now and before. */
|
|
@@ -163,7 +166,25 @@ const partsOf = (payload) => (Array.isArray(payload?.content) ? payload.content
|
|
|
163
166
|
* `message` with a `role` and a `content` array of `input_text`, `input_image`
|
|
164
167
|
* and `output_text` parts. Current Codex threads are paginated, and a build
|
|
165
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
|
+
|
|
166
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) {
|
|
167
188
|
let text = '';
|
|
168
189
|
try {
|
|
169
190
|
const fd = fs.openSync(file, 'r');
|
|
@@ -178,6 +199,8 @@ export function readRollout(file) {
|
|
|
178
199
|
let meta = null;
|
|
179
200
|
let prompt = '';
|
|
180
201
|
let last = '';
|
|
202
|
+
// Whether the person wrote after Codex's last answer, so it is not waiting.
|
|
203
|
+
let herTurnLast = false;
|
|
181
204
|
let turns = 0;
|
|
182
205
|
let images = 0;
|
|
183
206
|
let lastActive = 0;
|
|
@@ -212,11 +235,12 @@ export function readRollout(file) {
|
|
|
212
235
|
if (p.type === 'user_message') {
|
|
213
236
|
const said = cleanPrompt(p.message);
|
|
214
237
|
if (!prompt && said && !isInjected(said)) prompt = said;
|
|
238
|
+
if (said && !isInjected(said)) herTurnLast = true;
|
|
215
239
|
turns += 1;
|
|
216
240
|
sawEventTurns = true;
|
|
217
241
|
} else if (p.type === 'agent_message') {
|
|
218
242
|
const said = cleanAnswer(p.message);
|
|
219
|
-
if (said) last = said;
|
|
243
|
+
if (said) { last = said; herTurnLast = false; }
|
|
220
244
|
}
|
|
221
245
|
continue;
|
|
222
246
|
}
|
|
@@ -230,16 +254,17 @@ export function readRollout(file) {
|
|
|
230
254
|
.filter(Boolean);
|
|
231
255
|
if (!hers.length) continue; // a turn made of nothing but Codex's own preamble
|
|
232
256
|
if (!prompt) prompt = hers.join('\n\n').trim();
|
|
257
|
+
herTurnLast = true;
|
|
233
258
|
if (!sawEventTurns) turns += 1;
|
|
234
259
|
} else if (p.role === 'assistant') {
|
|
235
260
|
const said = cleanAnswer(partsOf(p).filter((c) => c.type === 'output_text').map((c) => c.text ?? '').join('\n'));
|
|
236
|
-
if (said) last = said;
|
|
261
|
+
if (said) { last = said; herTurnLast = false; }
|
|
237
262
|
}
|
|
238
263
|
}
|
|
239
264
|
if (!meta || !meta.id) return null;
|
|
240
265
|
let mtime = 0;
|
|
241
266
|
try { mtime = fs.statSync(file).mtimeMs; } catch { mtime = 0; }
|
|
242
|
-
return { ...meta, prompt, last, turns, images, lastActive, path: file, mtime };
|
|
267
|
+
return { ...meta, prompt, last, herTurnLast, turns, images, lastActive, path: file, mtime };
|
|
243
268
|
}
|
|
244
269
|
|
|
245
270
|
/**
|
|
@@ -252,12 +277,40 @@ export const isLive = (mtime, now = Date.now()) => Number(mtime) > 0 && now - Nu
|
|
|
252
277
|
/**
|
|
253
278
|
* THE USER'S CONVERSATIONS, newest first, in the shape the import card and the
|
|
254
279
|
* import row both read. One entry per thread a person typed into, in a real folder,
|
|
255
|
-
* touched in the last `days` days.
|
|
256
|
-
|
|
257
|
-
|
|
280
|
+
* touched in the last `days` days.
|
|
281
|
+
*
|
|
282
|
+
* EVERY CODEX FOLDER ON THE MACHINE, not only `~/.codex` (w-e46f2e9cea): a
|
|
283
|
+
* second login under `~/.codex-<name>` or a `CODEX_HOME` holds conversations
|
|
284
|
+
* the import never offered. `codexDirs` is the list main/agent-homes.mjs makes;
|
|
285
|
+
* `codexDir` still names exactly one folder for a caller that means one. The
|
|
286
|
+
* same conversation in two folders is one row.
|
|
287
|
+
*
|
|
288
|
+
* WHICH COPY, AND WHICH NAME, decided apart (Codex's review of this change,
|
|
289
|
+
* 2026-10-11). The copy kept is the transcript file written last, judged by
|
|
290
|
+
* the file alone: judging by `when`, which folds in the index's own date, let
|
|
291
|
+
* a stale copy whose folder's index was touched later beat the copy still
|
|
292
|
+
* being written. And the name Codex gave a conversation is read from every
|
|
293
|
+
* folder's index, newest entry winning, so the copy kept still carries a name
|
|
294
|
+
* that only another folder's index knew. */
|
|
295
|
+
export function readCodexThreads({ home = os.homedir(), codexDir = null, codexDirs = null, days = CODEX_RECENT_DAYS, now = Date.now() } = {}) {
|
|
296
|
+
const dirs = codexDirs ?? (codexDir ? [codexDir] : codexDataDirs({ home }));
|
|
258
297
|
const cutoff = now - Math.max(0, Number(days) || 0) * 86400 * 1000;
|
|
259
|
-
const
|
|
260
|
-
const
|
|
298
|
+
const index = new Map();
|
|
299
|
+
for (const dir of dirs) {
|
|
300
|
+
for (const [id, entry] of readCodexIndex(dir)) {
|
|
301
|
+
const had = index.get(id);
|
|
302
|
+
if (!had || (entry.name && (!had.name || entry.updatedAt > had.updatedAt))) index.set(id, entry);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const byId = new Map();
|
|
306
|
+
const skipped = { notHers: 0, scratch: 0, old: 0, empty: 0, unreadable: 0, copy: 0 };
|
|
307
|
+
for (const dir of dirs) readOneCodexHome(dir, { home, days, now, cutoff, index, byId, skipped });
|
|
308
|
+
const out = [...byId.values()].map(({ fileTime, ...t }) => t);
|
|
309
|
+
out.sort((a, b) => b.when - a.when);
|
|
310
|
+
return { threads: out, skipped };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function readOneCodexHome(codexDir, { home, days, now, cutoff, index, byId, skipped }) {
|
|
261
314
|
for (const file of listRolloutFiles(codexDir, { days, since: cutoff })) {
|
|
262
315
|
const r = readRollout(file);
|
|
263
316
|
if (!r) { skipped.unreadable += 1; continue; }
|
|
@@ -268,7 +321,13 @@ export function readCodexThreads({ home = os.homedir(), codexDir = codexHome(hom
|
|
|
268
321
|
if (when < cutoff) { skipped.old += 1; continue; }
|
|
269
322
|
const title = named?.name || threadTitle([r.prompt]);
|
|
270
323
|
if (!title) { skipped.empty += 1; continue; }
|
|
271
|
-
|
|
324
|
+
const had = byId.get(r.id);
|
|
325
|
+
if (had) {
|
|
326
|
+
skipped.copy += 1;
|
|
327
|
+
if (had.fileTime >= r.mtime) continue;
|
|
328
|
+
}
|
|
329
|
+
byId.set(r.id, {
|
|
330
|
+
fileTime: r.mtime,
|
|
272
331
|
id: r.id,
|
|
273
332
|
source: 'codex',
|
|
274
333
|
folder: r.cwd,
|
|
@@ -284,8 +343,50 @@ export function readCodexThreads({ home = os.homedir(), codexDir = codexHome(hom
|
|
|
284
343
|
prompt: r.prompt,
|
|
285
344
|
last: r.last,
|
|
286
345
|
live: isLive(r.mtime, now),
|
|
346
|
+
...(r.herTurnLast ? { waiting: false, ask: '' } : howTheChatEnded(r.last)),
|
|
347
|
+
lastSaid: r.last,
|
|
348
|
+
firstSaid: r.prompt,
|
|
287
349
|
});
|
|
288
350
|
}
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
});
|
|
291
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
|
@@ -10,21 +10,99 @@
|
|
|
10
10
|
// The founder, before launch: "after onboarding for both anthropic and openai
|
|
11
11
|
// harnesses, it should only import on command, not continuously". Importing is
|
|
12
12
|
// now the walk's last card and ⌘K import, for Claude Code and Codex alike.
|
|
13
|
+
//
|
|
14
|
+
// IT IS ALSO WHERE A CODEX AGENT IS NOTICED (w-1116fbb68a, 2026-10-08). It is
|
|
15
|
+
// the one thing in the app that already knows a person's Codex conversations,
|
|
16
|
+
// and it reads them on a timer rather than on the snapshot, which is what makes
|
|
17
|
+
// it the right place: see `noticeCodexAgents` below.
|
|
13
18
|
import { readCodexThreads } from './codex-threads.mjs';
|
|
14
19
|
|
|
15
20
|
export const CODEX_POLL_MS = 60_000;
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* HOW LONG AFTER ITS LAST WRITE A CODEX THREAD IS STILL AN AGENT THAT WAS SEEN.
|
|
24
|
+
*
|
|
25
|
+
* A Claude Code session is a process, so `agent_seen` catches one that has sat
|
|
26
|
+
* open since earlier in the sitting (main/ipc.mjs, liveAgents). A Codex thread
|
|
27
|
+
* leaves no process behind: `isLive` is two minutes of file mtime, so counting
|
|
28
|
+
* only live threads would notice a Codex user ONLY when this watch's minute
|
|
29
|
+
* happened to land while they were typing. An hour is one sitting, which is
|
|
30
|
+
* the span the Claude read already covers without being asked to.
|
|
31
|
+
*
|
|
32
|
+
* AND IT IS NOT A DAY. The reader hands over ten days of conversations, and
|
|
33
|
+
* counting those would say every install with Codex on it had an agent up on
|
|
34
|
+
* every day the app was opened, which is the opposite of the under-count this
|
|
35
|
+
* was added to fix.
|
|
36
|
+
*/
|
|
37
|
+
export const CODEX_SEEN_MS = 60 * 60_000;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* "AN AGENT WAS SEEN", FOR CODEX TOO (w-1116fbb68a, 2026-10-08).
|
|
41
|
+
*
|
|
42
|
+
* `agent_seen` listed Claude Code processes and nothing else, and about a
|
|
43
|
+
* third of the people on this app are on Codex, so whether they had an agent
|
|
44
|
+
* at all was read off proxies: a run this app started, or a reply. The threads
|
|
45
|
+
* are already in hand once a minute for the mirror refresh, so this costs
|
|
46
|
+
* nothing beyond the comparison.
|
|
47
|
+
*
|
|
48
|
+
* NOTHING ABOUT A THREAD IS SENT. Not its folder, not its title, not its id,
|
|
49
|
+
* which is only ever compared inside this process. `engine` is the whole of
|
|
50
|
+
* the payload and it is one of two fixed words.
|
|
51
|
+
*
|
|
52
|
+
* ONCE PER THREAD PER RUN OF THE APP, for the reason the Claude side keeps a
|
|
53
|
+
* set: this reads the same conversations every minute, so without it one
|
|
54
|
+
* conversation would send an event a minute all day.
|
|
55
|
+
*/
|
|
56
|
+
export function noticeCodexAgents(threads, { count = null, seen = null, now = Date.now() } = {}) {
|
|
57
|
+
if (typeof count !== 'function') return 0;
|
|
58
|
+
let sent = 0;
|
|
59
|
+
for (const t of threads ?? []) {
|
|
60
|
+
const id = String(t?.id ?? '');
|
|
61
|
+
if (!id) continue;
|
|
62
|
+
const at = Number(t.lastActive ?? 0);
|
|
63
|
+
if (!t.live && !(at > 0 && now - at < CODEX_SEEN_MS)) continue;
|
|
64
|
+
if (seen) {
|
|
65
|
+
if (seen.has(id)) continue;
|
|
66
|
+
seen.add(id);
|
|
67
|
+
}
|
|
68
|
+
// A count is never worth the watch: the mirror refresh below is what
|
|
69
|
+
// somebody actually sees, and it runs whatever the switch does.
|
|
70
|
+
try { count('agent_seen', { engine: 'codex' }); } catch { /* nothing */ }
|
|
71
|
+
sent += 1;
|
|
72
|
+
}
|
|
73
|
+
return sent;
|
|
74
|
+
}
|
|
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).
|
|
78
|
+
export function scanCodex({ store, readThreads = readCodexThreads, now = Date.now(), count = null, seen = null } = {}) {
|
|
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 }) {
|
|
87
|
+
noticeCodexAgents(threads, { count, seen, now });
|
|
20
88
|
const refreshed = store.refreshCodexMirrors(threads, { now });
|
|
21
89
|
const retold = store.refreshCodexAsks(threads, { now });
|
|
22
90
|
return { refreshed, retold, threads: threads.length };
|
|
23
91
|
}
|
|
24
92
|
|
|
25
|
-
export function startCodexWatch({ store, readThreads = readCodexThreads, pollMs = CODEX_POLL_MS, firstDelayMs = 8_000 } = {}) {
|
|
93
|
+
export function startCodexWatch({ store, readThreads = readCodexThreads, pollMs = CODEX_POLL_MS, firstDelayMs = 8_000, count = null } = {}) {
|
|
94
|
+
// Conversations this run has already counted. In memory on purpose, exactly
|
|
95
|
+
// as the Claude side's set is: a fresh launch seeing the same conversation
|
|
96
|
+
// again is a fresh fact about the app being used.
|
|
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;
|
|
26
100
|
const run = () => {
|
|
27
|
-
|
|
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;
|
|
28
106
|
};
|
|
29
107
|
const first = setTimeout(run, firstDelayMs);
|
|
30
108
|
const timer = setInterval(run, pollMs);
|