agentbox-app 0.1.11 → 0.1.13
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 -16
- package/bin/agentbox.mjs +18 -9
- package/briefs/message-rules.md +15 -4
- package/main/account-tooling.mjs +2 -1
- package/main/acp.mjs +296 -0
- package/main/agent-homes.mjs +158 -0
- package/main/agent-sessions.mjs +57 -20
- package/main/agent-updates.mjs +9 -20
- package/main/analytics.mjs +30 -0
- package/main/approvals.mjs +14 -5
- package/main/claude-bin.mjs +45 -310
- package/main/claude-input.mjs +9 -3
- package/main/claude-launch-env.mjs +54 -0
- package/main/claude-models.mjs +39 -7
- package/main/claude-plan.mjs +14 -2
- package/main/claude-usage.mjs +82 -25
- package/main/codex-bin.mjs +51 -248
- package/main/codex-launch-env.mjs +31 -0
- package/main/codex-session.mjs +38 -3
- package/main/codex-threads.mjs +41 -8
- package/main/codex-watch.mjs +67 -3
- package/main/config.mjs +40 -13
- package/main/corner-tag.mjs +489 -0
- package/main/crash-report.mjs +26 -3
- package/main/demo.mjs +17 -14
- package/main/engine-bin.mjs +595 -0
- package/main/engine-setup.mjs +27 -27
- package/main/feedback.mjs +11 -6
- package/main/fresh-user.mjs +1 -1
- package/main/git-change.mjs +8 -2
- package/main/harnesses/claude-stream.mjs +208 -0
- package/main/harnesses/claude.mjs +84 -0
- package/main/harnesses/codex.mjs +65 -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 +38 -0
- package/main/instruction-settings.mjs +47 -11
- package/main/ipc.mjs +175 -22
- package/main/local-app-frames.mjs +82 -0
- package/main/machine.mjs +1 -1
- package/main/main.mjs +120 -5
- package/main/memory-gate-history.mjs +51 -12
- package/main/memory-gate-server.mjs +67 -11
- package/main/memory-gate.mjs +49 -8
- package/main/memory-log.mjs +124 -0
- package/main/notify.mjs +48 -5
- package/main/opencode-server.mjs +271 -0
- package/main/opencode.mjs +287 -0
- package/main/phone-link.mjs +551 -0
- package/main/refresh-agent-models.mjs +2 -1
- package/main/relay-address.mjs +28 -0
- package/main/relay-client.mjs +246 -0
- package/main/row-label.mjs +8 -7
- package/main/runs-on.mjs +90 -0
- package/main/serve.mjs +124 -10
- package/main/settings.mjs +171 -11
- package/main/shell-path.mjs +86 -0
- package/main/ship-queue.mjs +25 -7
- package/main/sign-in-files.mjs +3 -7
- package/main/source-updater.mjs +27 -1
- package/main/start-hold.mjs +73 -0
- package/main/store/home.mjs +37 -5
- package/main/store/project.mjs +15 -19
- package/main/store.mjs +10 -4
- package/main/supervisor.mjs +1030 -447
- package/main/task-commands.mjs +13 -17
- package/main/task-folders-offthread.mjs +6 -1
- package/main/task-folders.mjs +491 -82
- package/main/task-terminals.mjs +49 -8
- package/main/team/index.mjs +2 -2
- package/main/team/projects.mjs +1 -1
- package/main/updater.mjs +71 -9
- package/main/worktree-include.mjs +494 -0
- package/mcp/core/page.mjs +1 -1
- package/mcp/core/work.mjs +34 -3
- package/mcp/tools.mjs +2 -1
- package/package.json +10 -1
- package/renderer/dist/assets/abnfDiagram-YUKMZFIV-DPPuMWt-.js +1 -0
- package/renderer/dist/assets/arc-HmFfdseH.js +1 -0
- package/renderer/dist/assets/architectureDiagram-47P4ROYG-DxJ3zHrH.js +36 -0
- package/renderer/dist/assets/blockDiagram-E7TT5TSB-CbTXzOqQ.js +140 -0
- package/renderer/dist/assets/c4Diagram-CRA5TL53-X5ItFyLn.js +38 -0
- package/renderer/dist/assets/channel-BlT2g9Sz.js +1 -0
- package/renderer/dist/assets/chunk-2Q5K7J3B-DC12Ho_L.js +1 -0
- package/renderer/dist/assets/chunk-5VM5RSS4-ClVtfPd0.js +15 -0
- package/renderer/dist/assets/chunk-7TKQ45FW-DgcOm0HT.js +1 -0
- package/renderer/dist/assets/chunk-AW2ZBBNX-C01s4yoF.js +88 -0
- package/renderer/dist/assets/chunk-FVRAUYC3-Bj0zxhR4.js +2 -0
- package/renderer/dist/assets/chunk-HTAEGDNF-DWDeEhLU.js +1 -0
- package/renderer/dist/assets/chunk-JWPE2WC7-Dsqwv3eW.js +1 -0
- package/renderer/dist/assets/chunk-KQW6MTUR-CE86ANL2.js +1 -0
- package/renderer/dist/assets/chunk-XXDRQBXY-CoNTASfs.js +1 -0
- package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-2wprLqtw.js +217 -0
- package/renderer/dist/assets/cornerTag-BLrxiDE5.css +1 -0
- package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +1 -0
- package/renderer/dist/assets/cose-bilkent-JH36ORCC-enqMiYbn.js +1 -0
- package/renderer/dist/assets/cynefin-EF2NZ3EQ-DHngLxvQ.js +141 -0
- package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-C0rZZIl3.js +62 -0
- package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/renderer/dist/assets/dagre-W4DXFKR2-DQnaEWO7.js +4 -0
- package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/renderer/dist/assets/diagram-2UJZ2QOL-CS8AlGPx.js +200 -0
- package/renderer/dist/assets/diagram-OVF4WLC6-yiDMHIkY.js +24 -0
- package/renderer/dist/assets/diagram-PFPMY2P6-XbQKzbBA.js +30 -0
- package/renderer/dist/assets/diagram-UMYRVEAY-dhVvlfl_.js +24 -0
- package/renderer/dist/assets/diagram-YEKJPTXX-DZHe8SCf.js +41 -0
- package/renderer/dist/assets/diagram-ZIFT7M5P-B5h6aXVR.js +3 -0
- package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-PR9ScDco.js +1 -0
- package/renderer/dist/assets/elk-IJKZMXRS-Cg-yQXPd.js +27 -0
- package/renderer/dist/assets/erDiagram-O2IAPWRE-omOrJMxu.js +99 -0
- package/renderer/dist/assets/flowDiagram-OXPTDLAJ-aOX2hzvT.js +213 -0
- package/renderer/dist/assets/ganttDiagram-R7TSEDQI-B1H3pWyI.js +292 -0
- package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-Cvw304HC.js +106 -0
- package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
- package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-_ZXcNViH.js +2 -0
- package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
- package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js +70 -0
- package/renderer/dist/assets/journeyDiagram-COXZFDF6-fr62QoAt.js +139 -0
- package/renderer/dist/assets/kanban-definition-P3RFRI5V-J8zsabCe.js +89 -0
- package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
- package/renderer/dist/assets/linear-BmNdEtIi.js +1 -0
- package/renderer/dist/assets/main-ChNdnUvt.js +1132 -0
- package/renderer/dist/assets/main-nj0QR5kg.css +32 -0
- package/renderer/dist/assets/mermaid.core-CnYzvFkz.js +314 -0
- package/renderer/dist/assets/mindmap-definition-LZFPQGKD-Bfcxc7PM.js +96 -0
- package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/renderer/dist/assets/pegDiagram-BGZESJAR-CwUJOq6X.js +1 -0
- package/renderer/dist/assets/pieDiagram-CAPJLFHJ-BrwMmqfC.js +39 -0
- package/renderer/dist/assets/product-name-CTKLYNWY.js +40 -0
- package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-BN4Stoj-.js +7 -0
- package/renderer/dist/assets/railroadDiagram-SM67HX2B-CPZ5bbeR.js +1 -0
- package/renderer/dist/assets/requirementDiagram-X7JNWC4A-CgRnimVe.js +84 -0
- package/renderer/dist/assets/sankeyDiagram-UM26HJRW-Bt2rej__.js +40 -0
- package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js +169 -0
- package/renderer/dist/assets/sizeCapture-INFHLROL-usyNyXyh.js +1 -0
- package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-CRzUBb0u.js +291 -0
- package/renderer/dist/assets/swimlanes-N4OXWK64-C1GJvMS0.js +1 -0
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +8 -0
- package/renderer/dist/assets/timeline-definition-YOQAKGHF-caeyPlcc.js +120 -0
- package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-BN_SV6PC.js +370 -0
- package/renderer/dist/assets/vennDiagram-BLWOH2XV-BLGuaUX-.js +34 -0
- package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-Da0cJeNc.js +78 -0
- package/renderer/dist/assets/xychartDiagram-TAQBALBS-cGfUffMk.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 +14 -0
- package/scripts/approval-server.sh +31 -7
- package/scripts/before-publish.mjs +12 -10
- package/shared/agent-import.mjs +3 -1
- package/shared/agents.d.mts +1 -0
- package/shared/bridge-map.mjs +19 -0
- package/shared/claude-commands.generated.mjs +8 -6
- package/shared/claude-models.generated.mjs +10 -8
- package/shared/codex-modes.mjs +1 -1
- package/shared/corner-tag.d.mts +14 -0
- package/shared/corner-tag.mjs +141 -0
- package/shared/demo-world.mjs +1 -1
- package/shared/engines.mjs +20 -5
- package/shared/fresh-user-home.mjs +12 -1
- package/shared/harness-definitions.mjs +103 -0
- package/shared/instruction-defaults.json +1 -1
- package/shared/notify-rules.d.mts +25 -0
- package/shared/notify-rules.mjs +19 -0
- package/shared/project-folder-check.mjs +3 -3
- package/shared/provider-commands.mjs +10 -2
- package/shared/rank.d.mts +2 -0
- package/shared/rank.mjs +50 -0
- package/shared/relay-address.mjs +6 -0
- package/shared/relay-crypto.mjs +165 -0
- package/shared/runs-on.d.mts +10 -0
- package/shared/runs-on.mjs +55 -0
- package/shared/shortcuts.mjs +11 -5
- package/shared/spawn-trouble.mjs +58 -7
- 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/usage.d.mts +3 -0
- package/shared/usage.mjs +7 -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 +28 -2
- package/shared/work-lines.mjs +1 -1
- package/shared/xterm-invalid-range.d.mts +2 -0
- package/shared/xterm-invalid-range.mjs +58 -0
- package/renderer/dist/assets/index-B-2Az952.css +0 -32
- package/renderer/dist/assets/index-D6qncgai.js +0 -1168
package/main/codex-threads.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import fs from 'node:fs';
|
|
|
27
27
|
import os from 'node:os';
|
|
28
28
|
import path from 'node:path';
|
|
29
29
|
import { RECENT_DAYS, isScratchFolder, shortFolder, threadTitle } from './agent-sessions.mjs';
|
|
30
|
+
import { codexDataDirs } from './agent-homes.mjs';
|
|
30
31
|
import { WAS_SLUGS, nameSlug } from '../shared/product-name.mjs';
|
|
31
32
|
|
|
32
33
|
export const CODEX_RECENT_DAYS = RECENT_DAYS;
|
|
@@ -252,12 +253,40 @@ export const isLive = (mtime, now = Date.now()) => Number(mtime) > 0 && now - Nu
|
|
|
252
253
|
/**
|
|
253
254
|
* THE USER'S CONVERSATIONS, newest first, in the shape the import card and the
|
|
254
255
|
* 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
|
-
|
|
256
|
+
* touched in the last `days` days.
|
|
257
|
+
*
|
|
258
|
+
* EVERY CODEX FOLDER ON THE MACHINE, not only `~/.codex` (w-e46f2e9cea): a
|
|
259
|
+
* second login under `~/.codex-<name>` or a `CODEX_HOME` holds conversations
|
|
260
|
+
* the import never offered. `codexDirs` is the list main/agent-homes.mjs makes;
|
|
261
|
+
* `codexDir` still names exactly one folder for a caller that means one. The
|
|
262
|
+
* same conversation in two folders is one row.
|
|
263
|
+
*
|
|
264
|
+
* WHICH COPY, AND WHICH NAME, decided apart (Codex's review of this change,
|
|
265
|
+
* 2026-10-11). The copy kept is the transcript file written last, judged by
|
|
266
|
+
* the file alone: judging by `when`, which folds in the index's own date, let
|
|
267
|
+
* a stale copy whose folder's index was touched later beat the copy still
|
|
268
|
+
* being written. And the name Codex gave a conversation is read from every
|
|
269
|
+
* folder's index, newest entry winning, so the copy kept still carries a name
|
|
270
|
+
* that only another folder's index knew. */
|
|
271
|
+
export function readCodexThreads({ home = os.homedir(), codexDir = null, codexDirs = null, days = CODEX_RECENT_DAYS, now = Date.now() } = {}) {
|
|
272
|
+
const dirs = codexDirs ?? (codexDir ? [codexDir] : codexDataDirs({ home }));
|
|
258
273
|
const cutoff = now - Math.max(0, Number(days) || 0) * 86400 * 1000;
|
|
259
|
-
const
|
|
260
|
-
const
|
|
274
|
+
const index = new Map();
|
|
275
|
+
for (const dir of dirs) {
|
|
276
|
+
for (const [id, entry] of readCodexIndex(dir)) {
|
|
277
|
+
const had = index.get(id);
|
|
278
|
+
if (!had || (entry.name && (!had.name || entry.updatedAt > had.updatedAt))) index.set(id, entry);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const byId = new Map();
|
|
282
|
+
const skipped = { notHers: 0, scratch: 0, old: 0, empty: 0, unreadable: 0, copy: 0 };
|
|
283
|
+
for (const dir of dirs) readOneCodexHome(dir, { home, days, now, cutoff, index, byId, skipped });
|
|
284
|
+
const out = [...byId.values()].map(({ fileTime, ...t }) => t);
|
|
285
|
+
out.sort((a, b) => b.when - a.when);
|
|
286
|
+
return { threads: out, skipped };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function readOneCodexHome(codexDir, { home, days, now, cutoff, index, byId, skipped }) {
|
|
261
290
|
for (const file of listRolloutFiles(codexDir, { days, since: cutoff })) {
|
|
262
291
|
const r = readRollout(file);
|
|
263
292
|
if (!r) { skipped.unreadable += 1; continue; }
|
|
@@ -268,7 +297,13 @@ export function readCodexThreads({ home = os.homedir(), codexDir = codexHome(hom
|
|
|
268
297
|
if (when < cutoff) { skipped.old += 1; continue; }
|
|
269
298
|
const title = named?.name || threadTitle([r.prompt]);
|
|
270
299
|
if (!title) { skipped.empty += 1; continue; }
|
|
271
|
-
|
|
300
|
+
const had = byId.get(r.id);
|
|
301
|
+
if (had) {
|
|
302
|
+
skipped.copy += 1;
|
|
303
|
+
if (had.fileTime >= r.mtime) continue;
|
|
304
|
+
}
|
|
305
|
+
byId.set(r.id, {
|
|
306
|
+
fileTime: r.mtime,
|
|
272
307
|
id: r.id,
|
|
273
308
|
source: 'codex',
|
|
274
309
|
folder: r.cwd,
|
|
@@ -286,6 +321,4 @@ export function readCodexThreads({ home = os.homedir(), codexDir = codexHome(hom
|
|
|
286
321
|
live: isLive(r.mtime, now),
|
|
287
322
|
});
|
|
288
323
|
}
|
|
289
|
-
out.sort((a, b) => b.when - a.when);
|
|
290
|
-
return { threads: out, skipped };
|
|
291
324
|
}
|
package/main/codex-watch.mjs
CHANGED
|
@@ -10,21 +10,85 @@
|
|
|
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
|
-
|
|
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
|
+
export function scanCodex({ store, readThreads = readCodexThreads, now = Date.now(), count = null, seen = null } = {}) {
|
|
18
77
|
let threads = [];
|
|
19
78
|
try { ({ threads } = readThreads({ now })); } catch { return { refreshed: 0, retold: 0, threads: 0 }; }
|
|
79
|
+
noticeCodexAgents(threads, { count, seen, now });
|
|
20
80
|
const refreshed = store.refreshCodexMirrors(threads, { now });
|
|
21
81
|
const retold = store.refreshCodexAsks(threads, { now });
|
|
22
82
|
return { refreshed, retold, threads: threads.length };
|
|
23
83
|
}
|
|
24
84
|
|
|
25
|
-
export function startCodexWatch({ store, readThreads = readCodexThreads, pollMs = CODEX_POLL_MS, firstDelayMs = 8_000 } = {}) {
|
|
85
|
+
export function startCodexWatch({ store, readThreads = readCodexThreads, pollMs = CODEX_POLL_MS, firstDelayMs = 8_000, count = null } = {}) {
|
|
86
|
+
// Conversations this run has already counted. In memory on purpose, exactly
|
|
87
|
+
// as the Claude side's set is: a fresh launch seeing the same conversation
|
|
88
|
+
// again is a fresh fact about the app being used.
|
|
89
|
+
const seen = new Set();
|
|
26
90
|
const run = () => {
|
|
27
|
-
try { scanCodex({ store, readThreads }); } catch (e) { console.warn('codex watch:', e?.message ?? e); }
|
|
91
|
+
try { scanCodex({ store, readThreads, count, seen }); } catch (e) { console.warn('codex watch:', e?.message ?? e); }
|
|
28
92
|
};
|
|
29
93
|
const first = setTimeout(run, firstDelayMs);
|
|
30
94
|
const timer = setInterval(run, pollMs);
|
package/main/config.mjs
CHANGED
|
@@ -10,8 +10,9 @@ import path from 'node:path';
|
|
|
10
10
|
import os from 'node:os';
|
|
11
11
|
import crypto from 'node:crypto';
|
|
12
12
|
import { resolveClaudeBin, candidatePaths } from './claude-bin.mjs';
|
|
13
|
+
import { warmShellAnswers } from './engine-bin.mjs';
|
|
13
14
|
import { resolveCodexBin } from './codex-bin.mjs';
|
|
14
|
-
import {
|
|
15
|
+
import { defaultStoreRoot } from './store/home.mjs';
|
|
15
16
|
import { readPlans, slotsForPlans } from './claude-plan.mjs';
|
|
16
17
|
import { machineSlots } from './machine.mjs';
|
|
17
18
|
import { effectiveProfiles } from './account-discovery.mjs';
|
|
@@ -34,14 +35,6 @@ const CONFIG_NAME = 'zero.config.json';
|
|
|
34
35
|
export const DEFAULT_SESSIONS_AT_ONCE = 3;
|
|
35
36
|
|
|
36
37
|
const DEFAULTS = {
|
|
37
|
-
// Where the inbox's own files live. WHICH FOLDER THIS SHOULD BE ON A
|
|
38
|
-
// STRANGER'S MAC IS NOT DECIDED HERE: it is the open question on.
|
|
39
|
-
//
|
|
40
|
-
// IT IS THE APP'S OWN NAME SINCE 2026-09-22, where it used to be an older
|
|
41
|
-
// product's. A real store is always named in the config, so this default is
|
|
42
|
-
// only ever the answer for an install carrying none, and renaming it moved
|
|
43
|
-
// no files: the folder it used to point at is untouched and still on disk.
|
|
44
|
-
storeRoot: path.join(os.homedir(), Name),
|
|
45
38
|
// ONE ACCOUNT FOLDER PER INSTALL, AND NOBODY ELSE'S.
|
|
46
39
|
//
|
|
47
40
|
// This was a literal id for most of the app's life, which meant every copy
|
|
@@ -71,6 +64,11 @@ const DEFAULTS = {
|
|
|
71
64
|
// many times "Something felt slow" has been pressed, and only ever subtracts.
|
|
72
65
|
agentsAuto: true,
|
|
73
66
|
agentsNudge: 0,
|
|
67
|
+
// THE NUMBER PICKED ON THE MENU, FOR THE WHOLE COMPUTER (w-ec27ff1a51). Null
|
|
68
|
+
// until somebody picks one. It replaced `maxConcurrentSessions` as the thing
|
|
69
|
+
// the menu writes, because that one was per login: two logins turned a 7 into
|
|
70
|
+
// 14 agents on a 16 GB Mac, which then ran out of memory.
|
|
71
|
+
agentsAtOnce: null,
|
|
74
72
|
// HOLD HEAVY WORK WHEN MEMORY IS SHORT (w-3958c3753d). Off until somebody
|
|
75
73
|
// turns it on from the Agents page. With it on, every shell command a worker
|
|
76
74
|
// runs asks main/memory-gate-server.mjs first, and heavy ones wait their turn
|
|
@@ -160,6 +158,19 @@ export function loadConfig(appDir, { home = os.homedir() } = {}) {
|
|
|
160
158
|
else console.warn(`zero: could not read ${file}: ${err.message}`);
|
|
161
159
|
}
|
|
162
160
|
const config = { ...DEFAULTS, ...overrides, freshInstall: fresh };
|
|
161
|
+
// Where the inbox's own files live, when the file names no store. A path in
|
|
162
|
+
// the file always wins, including on Linux. The default is computed here, not
|
|
163
|
+
// at import: on Linux it is the XDG data directory, and on macOS a folder of
|
|
164
|
+
// the app's name. An import-time path cannot see the `home` a test passes,
|
|
165
|
+
// and it cannot see a blanked XDG_DATA_HOME.
|
|
166
|
+
//
|
|
167
|
+
// A stand-in home does not inherit this machine's XDG_DATA_HOME. That
|
|
168
|
+
// variable is an absolute path in the real home, and a throwaway asked with
|
|
169
|
+
// `{ home }` would otherwise open the real store.
|
|
170
|
+
if (typeof overrides.storeRoot !== 'string' || !overrides.storeRoot.trim()) {
|
|
171
|
+
const real = path.resolve(home) === path.resolve(os.homedir());
|
|
172
|
+
config.storeRoot = defaultStoreRoot({ home, env: real ? process.env : {} });
|
|
173
|
+
}
|
|
163
174
|
// An older alias for `storeRoot` was read here until 2026-09-22. A config
|
|
164
175
|
// still carrying only the old key falls through to the default now, which is
|
|
165
176
|
// why the key was renamed in zero.config.json in the same step and not after
|
|
@@ -200,6 +211,14 @@ export function loadConfig(appDir, { home = os.homedir() } = {}) {
|
|
|
200
211
|
config.claudeBinConfigured = typeof overrides.claudeBin === 'string' && overrides.claudeBin
|
|
201
212
|
? overrides.claudeBin
|
|
202
213
|
: null;
|
|
214
|
+
// ONE LOGIN SHELL FOR BOTH ENGINES, ASKED BEFORE EITHER SEARCH RUNS. This is
|
|
215
|
+
// on the boot path -- main/main.mjs calls loadConfig inside createWindow and
|
|
216
|
+
// before the window exists -- and a login shell costs about 0.7 s to start
|
|
217
|
+
// whether it is asked about one program or six. Measured on this Mac
|
|
218
|
+
// 2026-10-11, the two searches together took 1.4 s cold and the shell was all
|
|
219
|
+
// of it. It seeds only what it positively found, so a shell that will not
|
|
220
|
+
// start leaves both searches exactly as they were (main/engine-bin.mjs).
|
|
221
|
+
warmShellAnswers();
|
|
203
222
|
const claude = resolveClaudeBin(config.claudeBinConfigured);
|
|
204
223
|
config.claudeBin = claude.path ?? candidatePaths()[0];
|
|
205
224
|
config.claudeFound = claude.found;
|
|
@@ -276,10 +295,18 @@ export function loadConfig(appDir, { home = os.homedir() } = {}) {
|
|
|
276
295
|
// same thing said out loud and kept up to date. An explicit `agentsAuto`
|
|
277
296
|
// outranks the guess, because turning Automatic back on after setting a number
|
|
278
297
|
// has to be possible.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
//
|
|
299
|
+
// ONLY A NUMBER FOR THE WHOLE COMPUTER IS A CHOICE (w-ec27ff1a51). A
|
|
300
|
+
// `maxConcurrentSessions` in the file was written per login, by the stepper
|
|
301
|
+
// that was here before 5 Oct, and reading it as "you set this" is what ran
|
|
302
|
+
// 14 agents on a Mac Automatic would have run 6 on, with nobody having picked
|
|
303
|
+
// 14. So that install goes back to Automatic, even with `agentsAuto: false`
|
|
304
|
+
// written beside it, and a number picked on today's menu (`agentsAtOnce`)
|
|
305
|
+
// is kept as picked unless Automatic was chosen after it.
|
|
306
|
+
config.agentsAuto = typeof overrides.agentsAtOnce !== 'number' || overrides.agentsAuto === true;
|
|
307
|
+
// And the plan is read whenever the number is Automatic's, old per-login
|
|
308
|
+
// number or not, so a plan that allows one at a time still says so.
|
|
309
|
+
if (typeof overrides.agentsAtOnce !== 'number') {
|
|
283
310
|
const plans = readPlans(effectiveProfiles(config.authProfiles, { home }), { home });
|
|
284
311
|
const slots = slotsForPlans(plans, config.machineSlots);
|
|
285
312
|
config.maxConcurrentSessions = slots;
|