agentbox-app 0.1.7 → 0.1.12
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 +220 -58
- package/bin/agentbox.mjs +18 -3
- package/briefs/message-rules.md +24 -3
- package/briefs/worker.md +63 -49
- package/briefs/writing-rules.md +90 -131
- package/main/account-discovery.mjs +21 -20
- package/main/account-tooling.mjs +11 -11
- package/main/agent-activity.mjs +45 -5
- package/main/agent-schedule.mjs +14 -14
- package/main/agent-sessions.mjs +94 -10
- package/main/agent-updates.mjs +116 -0
- package/main/agents.mjs +52 -46
- package/main/analytics.mjs +27 -0
- package/main/approval-prompt-server.mjs +2 -2
- package/main/approvals.mjs +14 -5
- package/main/artifact-path.mjs +11 -0
- package/main/claude-bin.mjs +32 -5
- package/main/claude-input.mjs +45 -3
- package/main/claude-plan.mjs +25 -13
- package/main/code-change.mjs +3 -3
- package/main/codex-account.mjs +1 -1
- package/main/codex-bin.mjs +18 -0
- package/main/codex-launch-env.mjs +31 -0
- package/main/codex-memory-gate.mjs +260 -0
- package/main/codex-models.mjs +12 -7
- package/main/codex-threads.mjs +46 -23
- package/main/codex-usage-file.mjs +7 -7
- package/main/codex-usage.mjs +4 -4
- package/main/codex-watch.mjs +12 -12
- package/main/config.mjs +82 -17
- package/main/demo.mjs +17 -14
- package/main/dev-window.mjs +9 -9
- package/main/doc-scheme.mjs +2 -0
- package/main/engine-setup.mjs +204 -0
- package/main/feedback.mjs +55 -0
- package/main/folder-pictures.mjs +48 -0
- package/main/frame-scrollbars.mjs +90 -0
- package/main/fresh-user.mjs +7 -4
- package/main/git-change-offthread.mjs +70 -0
- package/main/git-change-thread.mjs +11 -0
- package/main/git-change.mjs +9 -3
- package/main/img-scheme.mjs +70 -1
- package/main/instruction-settings.mjs +134 -60
- package/main/ipc.mjs +335 -42
- package/main/leftovers.mjs +518 -0
- package/main/live-replies.mjs +43 -5
- package/main/machine.mjs +48 -1
- package/main/main.mjs +138 -15
- package/main/memory-gate-history.mjs +254 -0
- package/main/memory-gate-server.mjs +461 -0
- package/main/memory-gate.mjs +206 -0
- package/main/message-priority.mjs +87 -0
- package/main/notify.mjs +1 -1
- package/main/rail-note.mjs +2 -2
- package/main/refresh-agent-models.mjs +21 -0
- package/main/repeats.mjs +4 -4
- package/main/row-label.mjs +115 -51
- package/main/runs-on.mjs +93 -0
- package/main/serve.mjs +77 -5
- package/main/settings.mjs +245 -26
- package/main/ship-queue.mjs +279 -0
- package/main/sign-in-files.mjs +39 -0
- package/main/source-updater.mjs +298 -0
- package/main/store/home.mjs +84 -7
- package/main/store/project.mjs +15 -19
- package/main/store/work-items.mjs +150 -6
- package/main/store.mjs +245 -13
- package/main/supervisor.mjs +1634 -417
- package/main/task-commands.mjs +8 -10
- package/main/task-folders.mjs +94 -16
- package/main/task-remote-control.mjs +3 -3
- package/main/task-terminals.mjs +14 -2
- package/main/team/index.mjs +440 -0
- package/main/team/memory-cloud.mjs +247 -0
- package/main/team/projects.mjs +162 -0
- package/main/team/session.mjs +225 -0
- package/main/team/sign-in.mjs +106 -0
- package/main/team/supabase-backend.mjs +231 -0
- package/main/team/sync.mjs +163 -0
- package/main/updater.mjs +15 -5
- package/main/write-before-reload.mjs +50 -0
- package/mcp/core/page.mjs +10 -12
- package/mcp/core/work.mjs +123 -26
- package/mcp/tools.mjs +13 -6
- package/package.json +21 -9
- package/renderer/dist/assets/agentbox-icon-F1oN9nj_.svg +15 -0
- package/renderer/dist/assets/index-ChKsQkRw.js +1169 -0
- package/renderer/dist/assets/index-ClBXnq3B.css +32 -0
- package/renderer/dist/index.html +2 -2
- package/scripts/after-install.mjs +14 -0
- package/scripts/approval-server.sh +7 -2
- package/scripts/memory-gate-hook.sh +59 -0
- package/shared/agent-import.mjs +39 -16
- package/shared/agents.d.mts +26 -8
- package/shared/agents.mjs +100 -108
- package/shared/analytics-events.mjs +19 -2
- package/shared/answers.mjs +12 -0
- package/shared/bridge-map.d.mts +12 -0
- package/shared/bridge-map.mjs +38 -1
- package/shared/claude-commands.generated.mjs +2 -2
- package/shared/claude-models.generated.mjs +6 -6
- package/shared/claude-usage.mjs +6 -6
- package/shared/codex-import.mjs +7 -7
- package/shared/codex-modes.d.mts +50 -0
- package/shared/codex-modes.mjs +1 -1
- package/shared/codex-usage.mjs +7 -8
- package/shared/demo-world.mjs +6 -6
- package/shared/engines.mjs +27 -10
- package/shared/feedback.d.mts +14 -0
- package/shared/feedback.mjs +44 -0
- package/shared/first-run-examples.mjs +4 -4
- package/shared/first-run-practice.d.mts +16 -0
- package/shared/first-run-practice.mjs +176 -70
- package/shared/fresh-user-home.mjs +34 -4
- package/shared/instruction-defaults.json +2 -2
- package/shared/product-name.mjs +4 -2
- package/shared/project-folder-check.mjs +9 -3
- package/shared/rank.d.mts +1 -0
- package/shared/rank.mjs +22 -0
- package/shared/referenced-files.mjs +1 -1
- package/shared/runs-on.d.mts +20 -0
- package/shared/runs-on.mjs +79 -0
- package/shared/schemes.d.mts +11 -0
- package/shared/schemes.mjs +1 -1
- package/shared/shortcuts.d.mts +41 -0
- package/shared/shortcuts.mjs +236 -0
- package/shared/spawn-trouble.d.mts +2 -0
- package/shared/spawn-trouble.mjs +40 -19
- package/shared/team-rules.d.mts +17 -0
- package/shared/team-rules.mjs +230 -0
- package/shared/team-status.d.mts +11 -0
- package/shared/team-status.mjs +90 -0
- package/shared/terminal-links.d.mts +2 -0
- package/shared/terminal-links.mjs +55 -0
- package/shared/terminal-state.d.mts +1 -0
- package/shared/terminal-state.mjs +16 -0
- package/shared/thread-cards.d.mts +19 -0
- package/shared/thread-cards.mjs +254 -0
- package/shared/usage.d.mts +1 -1
- package/shared/usage.mjs +6 -7
- package/shared/work-items.mjs +258 -28
- package/shared/work-lines.mjs +393 -16
- package/worker-permissions.json +1 -1
- package/renderer/dist/assets/cel-dusk-CS5I7kJS.webp +0 -0
- package/renderer/dist/assets/cel-dusk-DUslqS6B.webp +0 -0
- package/renderer/dist/assets/cel-dusk-Jt_xVGxp.webp +0 -0
- package/renderer/dist/assets/cel-dusk-sm-BBkV-tWT.webp +0 -0
- package/renderer/dist/assets/frost-haze-CxPSOBEg.webp +0 -0
- package/renderer/dist/assets/frost-haze-sm-C_w21qgd.webp +0 -0
- package/renderer/dist/assets/ice-BC8luINc.webp +0 -0
- package/renderer/dist/assets/ice-CP9PrKHo.webp +0 -0
- package/renderer/dist/assets/ice-river-D6ziOhLY.webp +0 -0
- package/renderer/dist/assets/ice-river-UVM2-JY_.webp +0 -0
- package/renderer/dist/assets/ice-river-bErJwYWW.webp +0 -0
- package/renderer/dist/assets/ice-river-sm-Bg1U-SIj.webp +0 -0
- package/renderer/dist/assets/ice-sm-CbVcg3_D.webp +0 -0
- package/renderer/dist/assets/index-B7hhfHrT.js +0 -684
- package/renderer/dist/assets/index-CwiDv5bL.css +0 -32
- package/renderer/dist/assets/lake-C0WaW-6E.webp +0 -0
- package/renderer/dist/assets/lake-CSKjLC5K.webp +0 -0
- package/renderer/dist/assets/lake-ETGcOb25.webp +0 -0
- package/renderer/dist/assets/lake-harbour-BCQRbrHb.webp +0 -0
- package/renderer/dist/assets/lake-harbour-B_wfXTmE.webp +0 -0
- package/renderer/dist/assets/lake-harbour-Te1pEvBh.webp +0 -0
- package/renderer/dist/assets/lake-harbour-sm-Cme_ShEg.webp +0 -0
- package/renderer/dist/assets/lake-sm-CbG72Hbv.webp +0 -0
- package/renderer/dist/assets/lino-coast-CUZHcucI.webp +0 -0
- package/renderer/dist/assets/lino-coast-DP4J7cgd.webp +0 -0
- package/renderer/dist/assets/lino-coast-kOIAffr_.webp +0 -0
- package/renderer/dist/assets/lino-coast-sm-C1Mdbgit.webp +0 -0
- package/renderer/dist/assets/moon-pines-BskgnnXR.webp +0 -0
- package/renderer/dist/assets/moon-pines-CFSIIwt0.webp +0 -0
- package/renderer/dist/assets/moon-pines-PNhdOYww.webp +0 -0
- package/renderer/dist/assets/moon-pines-sm-C6K1NomK.webp +0 -0
- package/renderer/dist/assets/orbit-rings-2XV36Ch3.webp +0 -0
- package/renderer/dist/assets/orbit-rings-C4NrcYGQ.webp +0 -0
- package/renderer/dist/assets/orbit-rings-DGLk07dT.webp +0 -0
- package/renderer/dist/assets/orbit-rings-sm-Dky3YdvL.webp +0 -0
- package/renderer/dist/assets/peach-haze-2-C6f7xnWw.webp +0 -0
- package/renderer/dist/assets/peach-haze-2-sm-Dj_Ez_Sb.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-DQjqMROg.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-Dw71H_bc.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-kOZfm-GM.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-sm-Dturjuky.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-C296JVMO.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-C4oMRDSx.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-CqVmQ4eh.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-sm-CHagC_nO.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-C6LYu6uE.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-CIUCEESs.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-sm-BnE0F_w-.webp +0 -0
- package/renderer/dist/assets/riso-hills-7heKVtry.webp +0 -0
- package/renderer/dist/assets/riso-hills-CIjAL3J6.webp +0 -0
- package/renderer/dist/assets/riso-hills-sm-DHsnbCfP.webp +0 -0
- package/renderer/dist/assets/slate-haze-BPWdkNsw.webp +0 -0
- package/renderer/dist/assets/slate-haze-sm-B0Cb5a2W.webp +0 -0
- package/renderer/dist/assets/valley-haze-BDHPqoJy.webp +0 -0
- package/renderer/dist/assets/valley-haze-sm-CTyjmPAG.webp +0 -0
- package/renderer/dist/assets/vector-dunes-DzzWLdKo.webp +0 -0
- package/renderer/dist/assets/vector-dunes-Yr9NBQUy.webp +0 -0
- package/renderer/dist/assets/vector-dunes-sm-D8MYY6ms.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-BCGKN4u4.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-BF-jk5MC.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-sm-Ds1xzTw9.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-B1um67Z9.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-DKQdYs32.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-sm-BJzNq8xh.webp +0 -0
- package/renderer/dist/emulator/audio-rules.mjs +0 -74
- package/renderer/dist/emulator/data/compression/README.md +0 -11
- package/renderer/dist/emulator/data/compression/extract7z.js +0 -1
- package/renderer/dist/emulator/data/compression/extractzip.js +0 -1
- package/renderer/dist/emulator/data/compression/libunrar.js +0 -519
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +0 -22
- 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 +0 -65
- package/renderer/dist/emulator/data/cores/reports/mgba.json +0 -1
- package/renderer/dist/emulator/data/emulator.css +0 -1614
- package/renderer/dist/emulator/data/loader.js +0 -170
- package/renderer/dist/emulator/data/localization/README.md +0 -63
- package/renderer/dist/emulator/data/localization/af-FR.json +0 -339
- package/renderer/dist/emulator/data/localization/ar-AR.json +0 -301
- package/renderer/dist/emulator/data/localization/ben-BEN.json +0 -301
- package/renderer/dist/emulator/data/localization/de-GER.json +0 -301
- package/renderer/dist/emulator/data/localization/el-GR.json +0 -301
- package/renderer/dist/emulator/data/localization/en-US.json +0 -339
- package/renderer/dist/emulator/data/localization/es-ES.json +0 -310
- package/renderer/dist/emulator/data/localization/fa-AF.json +0 -310
- package/renderer/dist/emulator/data/localization/hi-HI.json +0 -301
- package/renderer/dist/emulator/data/localization/it-IT.json +0 -302
- package/renderer/dist/emulator/data/localization/ja-JA.json +0 -301
- package/renderer/dist/emulator/data/localization/jv-JV.json +0 -301
- package/renderer/dist/emulator/data/localization/ko-KO.json +0 -336
- package/renderer/dist/emulator/data/localization/pt-BR.json +0 -361
- package/renderer/dist/emulator/data/localization/retroarch.json +0 -617
- package/renderer/dist/emulator/data/localization/ro-RO.json +0 -310
- package/renderer/dist/emulator/data/localization/ru-RU.json +0 -301
- package/renderer/dist/emulator/data/localization/tr-TR.json +0 -310
- package/renderer/dist/emulator/data/localization/vi-VN.json +0 -302
- package/renderer/dist/emulator/data/localization/zh-CN.json +0 -351
- package/renderer/dist/emulator/data/src/GameManager.js +0 -464
- package/renderer/dist/emulator/data/src/compression.js +0 -142
- package/renderer/dist/emulator/data/src/emulator.js +0 -6158
- package/renderer/dist/emulator/data/src/gamepad.js +0 -167
- package/renderer/dist/emulator/data/src/nipplejs.js +0 -1
- package/renderer/dist/emulator/data/src/shaders.js +0 -236
- package/renderer/dist/emulator/data/src/socket.io.min.js +0 -7
- package/renderer/dist/emulator/data/src/storage.js +0 -125
- package/renderer/dist/emulator/data/version.json +0 -5
- package/renderer/dist/emulator/host.js +0 -299
- package/renderer/dist/emulator/index.html +0 -28
- package/renderer/dist/emulator/provenance.json +0 -6
package/main/settings.mjs
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import fs from 'node:fs';
|
|
16
16
|
import os from 'node:os';
|
|
17
17
|
import path from 'node:path';
|
|
18
|
-
import { saveConfig } from './config.mjs';
|
|
18
|
+
import { codexChoiceToOpen, saveConfig } from './config.mjs';
|
|
19
19
|
import { resolveClaudeBin, forgetClaudeBin, INSTALL_URL } from './claude-bin.mjs';
|
|
20
20
|
import { AGENT_MODES } from '../shared/agents.mjs';
|
|
21
21
|
import { analyticsKey, diagnosticsOn } from './analytics.mjs';
|
|
@@ -23,7 +23,9 @@ import { CLAUDE_PERMISSION_MODES } from '../shared/work-items.mjs';
|
|
|
23
23
|
import { accountSentence, engineTroubleNote } from '../shared/spawn-trouble.mjs';
|
|
24
24
|
import { readPlan } from './claude-plan.mjs';
|
|
25
25
|
import { effectiveProfiles } from './account-discovery.mjs';
|
|
26
|
-
import { machineSlots, machineNote, MAX_SLOTS } from './machine.mjs';
|
|
26
|
+
import { machineSlots, machineNote, autoAgents, MAX_SLOTS } from './machine.mjs';
|
|
27
|
+
import { NAME } from '../shared/product-name.mjs';
|
|
28
|
+
import { autoSlots } from './memory-gate.mjs';
|
|
27
29
|
import { accountIdentity, claudeLoginCommand, duplicateAccountNote, linkAccountTooling, makeClaudeHome } from './account-tooling.mjs';
|
|
28
30
|
import { isEngine } from '../shared/engines.mjs';
|
|
29
31
|
import { codexAccount, codexLoginCommand, makeCodexHome } from './codex-account.mjs';
|
|
@@ -270,6 +272,10 @@ export function recheckClaude(config) {
|
|
|
270
272
|
// has just found Claude Code updates it rather than leaving the rest of the
|
|
271
273
|
// app pointing at where it used to not be.
|
|
272
274
|
if (state.claudeFound && state.claudeBin) config.claudeBin = state.claudeBin;
|
|
275
|
+
// And whether it is here at all, which is what decides that a Mac with only
|
|
276
|
+
// Codex runs on Codex (`Supervisor#_enginesFound`). Only a certain answer
|
|
277
|
+
// moves it, so an unsure search never takes Claude Code away from anybody.
|
|
278
|
+
if (state.claudeFound || state.claudeCertain) config.claudeFound = state.claudeFound;
|
|
273
279
|
return state;
|
|
274
280
|
}
|
|
275
281
|
|
|
@@ -319,6 +325,12 @@ export function codexState(config, where = {}) {
|
|
|
319
325
|
};
|
|
320
326
|
}
|
|
321
327
|
|
|
328
|
+
/** `codexState` under the names the walk's last card reads beside Claude's. */
|
|
329
|
+
export function codexFoundState(config, where = {}) {
|
|
330
|
+
const state = codexState(config, where);
|
|
331
|
+
return { codexFound: state.found, codexCertain: state.certain, codexInstallUrl: state.url };
|
|
332
|
+
}
|
|
333
|
+
|
|
322
334
|
/**
|
|
323
335
|
* LOOK AGAIN, PROPERLY, for the second engine. The twin of `recheckClaude`
|
|
324
336
|
* above, and it earns its place for a sharper reason: finding Codex here is
|
|
@@ -329,6 +341,10 @@ export function recheckCodex(config) {
|
|
|
329
341
|
forgetCodexBin();
|
|
330
342
|
const state = codexState(config);
|
|
331
343
|
config.codexBin = state.found ? state.bin : null;
|
|
344
|
+
// Found while the app is open, by this button or the plan setup: the choice
|
|
345
|
+
// opens now, as it would have at the next launch (main/config.mjs).
|
|
346
|
+
const opened = codexChoiceToOpen(config, state.found);
|
|
347
|
+
if (opened && config.appDir) saveConfig(config, { engineChoice: opened });
|
|
332
348
|
return state;
|
|
333
349
|
}
|
|
334
350
|
|
|
@@ -366,9 +382,9 @@ export function readSettings({ config, supervisor, store }) {
|
|
|
366
382
|
const trouble = troubles[profile] ?? null;
|
|
367
383
|
const resting = (supervisor._profileCooldown?.[profile] ?? 0) >= now;
|
|
368
384
|
// WHOSE SUBSCRIPTION THIS ROW IS, not merely which folder it lives in.
|
|
369
|
-
// The page named its rows after folders, so nothing on it could tell
|
|
370
|
-
// that
|
|
371
|
-
// main/account-tooling.mjs has
|
|
385
|
+
// The page named its rows after folders, so nothing on it could tell a
|
|
386
|
+
// person that one of their accounts had been replaced by the other;
|
|
387
|
+
// main/account-tooling.mjs has how that happens. Null when the
|
|
372
388
|
// folder holds no login, and then the row reads as it always did.
|
|
373
389
|
const identity = accountIdentity(profile === 'default' ? null : profile);
|
|
374
390
|
return {
|
|
@@ -421,9 +437,8 @@ export function readSettings({ config, supervisor, store }) {
|
|
|
421
437
|
// THE MARK, already a drawable url or null, resolved once by listProducts.
|
|
422
438
|
logo: product.logo,
|
|
423
439
|
// ONE SWITCH A PROJECT STILL HAS (w-d19d6d387c, 2026-09-22). It had five
|
|
424
|
-
// controls
|
|
425
|
-
//
|
|
426
|
-
// lean towards just having project-level instructions." The drive, the
|
|
440
|
+
// controls, and what they did was not clear even after reading their
|
|
441
|
+
// labels, so project-level instructions replace them. The drive, the
|
|
427
442
|
// per-project pause and personal mode are gone from the app entirely.
|
|
428
443
|
// `permission` stays readable because `projectSessionArgs` is still
|
|
429
444
|
// honoured for anyone who sets one in the config by hand, and the page
|
|
@@ -434,15 +449,40 @@ export function readSettings({ config, supervisor, store }) {
|
|
|
434
449
|
permission: override ? permissionMode(override) : 'workspace',
|
|
435
450
|
permissionArgs: override ?? null,
|
|
436
451
|
codexMode: config.projectCodexMode?.[product.slug] ?? 'workspace',
|
|
452
|
+
// THE SUPERVISOR IS ASKED RATHER THAN THE CONFIG FILE, so the page shows
|
|
453
|
+
// what will actually happen: a tie naming a login that has been signed out
|
|
454
|
+
// is ignored when work is handed out, and reads 'any' here too.
|
|
455
|
+
account: supervisor._projectProfile?.(product.slug) ?? 'any',
|
|
437
456
|
instructions,
|
|
438
457
|
running: sessions.filter((s) => s.product === product.slug).length,
|
|
439
458
|
};
|
|
440
459
|
});
|
|
441
460
|
|
|
461
|
+
// ARCHIVED PROJECTS, only so the Projects page can bring one back. Name and
|
|
462
|
+
// folder, the two things that tell three projects called Daydream apart.
|
|
463
|
+
let archivedProjects = [];
|
|
464
|
+
try {
|
|
465
|
+
archivedProjects = store.listProducts({ includeArchived: true })
|
|
466
|
+
.filter((p) => p.archived === true)
|
|
467
|
+
.map((p) => ({ slug: p.slug, name: p.name, dir: p.dir }));
|
|
468
|
+
} catch {}
|
|
469
|
+
|
|
442
470
|
return {
|
|
471
|
+
archivedProjects,
|
|
443
472
|
workspace: {
|
|
444
473
|
agentsRunning: !supervisor.paused,
|
|
445
474
|
sessionsAtOnce: config.maxConcurrentSessions,
|
|
475
|
+
// AUTOMATIC, AND WHAT IT WORKS OUT TO (w-e5225b62ba). `agentsAuto` is who
|
|
476
|
+
// is deciding; `agentsTotal` is what the whole Mac is running at once,
|
|
477
|
+
// which is the number the page shows, because the per-account one is
|
|
478
|
+
// arithmetic nobody should have to do. `agentsNudge` is how many steps
|
|
479
|
+
// down "Something felt slow" has taken it.
|
|
480
|
+
agentsAuto: !!config.agentsAuto,
|
|
481
|
+
agentsTotal: status.capacity,
|
|
482
|
+
agentsNudge: Math.max(0, Math.round(Number(config.agentsNudge) || 0)),
|
|
483
|
+
// What Automatic would say right now, so a manual number can be compared
|
|
484
|
+
// with it without turning Automatic back on to find out.
|
|
485
|
+
agentsAutoTotal: autoAgents({ gated: !!config.memoryGate, nudge: config.agentsNudge }),
|
|
446
486
|
// THE PLAN THAT SET THE NUMBER ABOVE, when a plan set it. Null in the
|
|
447
487
|
// ordinary case: she picked the number herself, or it is the three every
|
|
448
488
|
// install has always had. It is only ever filled in when somebody would
|
|
@@ -457,6 +497,8 @@ export function readSettings({ config, supervisor, store }) {
|
|
|
457
497
|
slotsSuggested: slotsHere.slots,
|
|
458
498
|
slotsMax: MAX_SLOTS,
|
|
459
499
|
machineNote: machineNote(slotsHere, config.maxConcurrentSessions),
|
|
500
|
+
memoryGate: memoryGateSettings({ config, supervisor }),
|
|
501
|
+
leftovers: leftoverSettings({ config, supervisor }),
|
|
460
502
|
capacity: status.capacity,
|
|
461
503
|
running: sessions.length,
|
|
462
504
|
model: parseSessionArgs(workspaceArgs).model,
|
|
@@ -509,6 +551,10 @@ export function readSettings({ config, supervisor, store }) {
|
|
|
509
551
|
// Someone who reads "install it, then open this screen again" and does
|
|
510
552
|
// exactly that has to see it turn found, without restarting the app.
|
|
511
553
|
...claudeState(config),
|
|
554
|
+
// AND WHETHER CODEX IS, because the app needs one of the two and not
|
|
555
|
+
// both: the last card of the walk only shuts the inbox when neither is
|
|
556
|
+
// here (renderer/src/App.tsx).
|
|
557
|
+
...codexFoundState(config),
|
|
512
558
|
// AND WHAT THIS MAC'S CLAUDE CODE CALLS ITS MODELS.
|
|
513
559
|
//
|
|
514
560
|
// In the same trip, and for the same reason the line above is looked up
|
|
@@ -577,21 +623,26 @@ function engineSettings(supervisor, config) {
|
|
|
577
623
|
// and that stopped being true somewhere between codex-cli versions.
|
|
578
624
|
// main/codex-account.mjs holds the measurement. Null when nobody is signed
|
|
579
625
|
// in, and the card then reads exactly as it always did.
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
626
|
+
// A Mac that runs on Codex alone has asked about it by having nothing else.
|
|
627
|
+
//
|
|
628
|
+
// AND THE PAGE IS DRAWN ON EVERY MAC NOW (w-d5d632e503). A tester who only
|
|
629
|
+
// uses Codex looked for it in Settings, found nothing, and had no way to
|
|
630
|
+
// tell "not installed" from "not offered". Not found, the page says so and
|
|
631
|
+
// links the install, which is the question that person had.
|
|
632
|
+
const codexIsHome = engine === 'codex';
|
|
633
|
+
const codex = {
|
|
634
|
+
...codexState(config),
|
|
635
|
+
trouble: engineTroubleFor(supervisor, 'codex'),
|
|
636
|
+
account: codexAccount(supervisor._codexHome()),
|
|
637
|
+
// EVERY CODEX LOGIN ON THE MAC, NOT JUST THE FIRST. The supervisor has
|
|
638
|
+
// run one app-server per CODEX_HOME since a second login became real;
|
|
639
|
+
// what never existed was a screen that could see them. One entry per
|
|
640
|
+
// `codexProfiles` word, resolved through the supervisor's own mapping so
|
|
641
|
+
// the card and the fleet cannot disagree about which folder a name
|
|
642
|
+
// means.
|
|
643
|
+
accounts: codexAccountRows(supervisor),
|
|
644
|
+
};
|
|
645
|
+
if (choices.length < 2 && !codexIsHome) {
|
|
595
646
|
return { engine, engineChoices: choices, codex, codexModels: [], codexModelDefault: null, codexModel: null };
|
|
596
647
|
}
|
|
597
648
|
const home = supervisor._codexHome();
|
|
@@ -680,6 +731,24 @@ export function setProjectSetting({ config, supervisor }, { product, key, value
|
|
|
680
731
|
saveConfig(config, { projectCodexMode: Object.keys(map).length ? map : undefined });
|
|
681
732
|
break;
|
|
682
733
|
}
|
|
734
|
+
// WHICH CLAUDE LOGIN THIS PROJECT'S AGENTS RUN ON. 'any' is the absence of
|
|
735
|
+
// an opinion, like 'workspace' above, and it deletes the entry rather than
|
|
736
|
+
// writing a word: a config file with no `projectAccounts` is the file every
|
|
737
|
+
// Mac had before this existed.
|
|
738
|
+
//
|
|
739
|
+
// AND A NAME THIS MAC CANNOT HONOUR IS REFUSED AT THE DOOR rather than
|
|
740
|
+
// written and ignored later. The supervisor does ignore one (`_projectProfile`),
|
|
741
|
+
// because an account can be signed out after the fact, but a pick made
|
|
742
|
+
// against a list we just read has no excuse to be wrong.
|
|
743
|
+
case 'account': {
|
|
744
|
+
const known = effectiveProfiles(config.authProfiles, { home: config.home });
|
|
745
|
+
if (value !== 'any' && !known.includes(value)) throw new Error(`unknown Claude account: ${value}`);
|
|
746
|
+
const map = { ...(config.projectAccounts ?? {}) };
|
|
747
|
+
if (value === 'any') delete map[product];
|
|
748
|
+
else map[product] = value;
|
|
749
|
+
saveConfig(config, { projectAccounts: Object.keys(map).length ? map : undefined });
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
683
752
|
default:
|
|
684
753
|
throw new Error(`unknown project setting: ${key}`);
|
|
685
754
|
}
|
|
@@ -754,8 +823,8 @@ export function addCodexAccount({ config, supervisor }) {
|
|
|
754
823
|
* accounting while this was a product we sold. She withdrew that on
|
|
755
824
|
* w-3498e0cad2 ("we're no longer offering this as a commercially viable
|
|
756
825
|
* product... the rules have changed so I think you should allow
|
|
757
|
-
* multi-accounting"), and this is the door that decision asked for.
|
|
758
|
-
* for wanting it
|
|
826
|
+
* multi-accounting"), and this is the door that decision asked for. The
|
|
827
|
+
* ordinary reason for wanting it: a work login and a personal login, both
|
|
759
828
|
* live, on one Mac.
|
|
760
829
|
*
|
|
761
830
|
* Everything else is the Codex flow exactly. Main makes the folder and
|
|
@@ -781,19 +850,143 @@ export function addClaudeAccount({ config, supervisor }) {
|
|
|
781
850
|
return { home: made.home, profile: made.profile, command: claudeLoginCommand(made.home) };
|
|
782
851
|
}
|
|
783
852
|
|
|
853
|
+
/**
|
|
854
|
+
* The Agents page's memory rows: the switch, the number of heavy commands at
|
|
855
|
+
* once (null is Auto), what Auto works out to here, and one sentence about
|
|
856
|
+
* right now while it is on.
|
|
857
|
+
*/
|
|
858
|
+
export function memoryGateSettings({ config, supervisor }) {
|
|
859
|
+
const on = !!config.memoryGate;
|
|
860
|
+
const slots = Number.isFinite(config.memoryGateSlots) ? config.memoryGateSlots : null;
|
|
861
|
+
// What Auto works out to on this Mac, whether or not somebody picked.
|
|
862
|
+
const slotsAuto = autoSlots(os.totalmem());
|
|
863
|
+
let now = null;
|
|
864
|
+
if (on) {
|
|
865
|
+
let s = null;
|
|
866
|
+
try { s = supervisor.memoryGateStatus?.() ?? null; } catch {}
|
|
867
|
+
if (s?.role === 'standby') now = `Another ${NAME} on this computer is coordinating.`;
|
|
868
|
+
else if (s) {
|
|
869
|
+
const heavy = (s.running ?? []).filter((r) => r.holdsSlot).length;
|
|
870
|
+
const waiting = (s.waiting ?? []).length;
|
|
871
|
+
const mem = s.pressure === 'critical' ? 'Memory is very short.' : s.pressure === 'tight' ? 'Memory is tight.' : 'Memory is fine.';
|
|
872
|
+
const run = heavy ? `${heavy} heavy command${heavy === 1 ? '' : 's'} running` : 'Nothing heavy running';
|
|
873
|
+
now = `${mem} ${run}${waiting ? `, ${waiting} waiting` : ''}.`;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return { on, slots, slotsAuto, slotsMax: MAX_SLOTS, now, reading: memoryReading({ config, supervisor }) };
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* THE FIGURES THE RUNNING PAGE LEADS WITH (w-e5225b62ba), rather than the same
|
|
881
|
+
* facts tacked onto the end of three grey paragraphs: "6 running now", "Holding
|
|
882
|
+
* 3 commands now", "Nothing running now".
|
|
883
|
+
*
|
|
884
|
+
* Every field is null when it is not known, and the page leaves out what is
|
|
885
|
+
* null. A figure invented to fill a slot is worse than a gap: this card's whole
|
|
886
|
+
* job is to be the one place on the screen that is measured.
|
|
887
|
+
*
|
|
888
|
+
* `usedPct` is the kernel's own `kern.memorystatus_level` turned round, which is
|
|
889
|
+
* what the gate already polls, so the bar and the word above it cannot disagree.
|
|
890
|
+
*/
|
|
891
|
+
export function memoryReading({ config, supervisor }) {
|
|
892
|
+
const out = { heavy: null, waiting: null, pressure: null, usedPct: null, asked: null, waited: null, refused: null };
|
|
893
|
+
if (!config.memoryGate) return out;
|
|
894
|
+
let s = null;
|
|
895
|
+
try { s = supervisor.memoryGateStatus?.() ?? null; } catch { return out; }
|
|
896
|
+
if (!s || s.role === 'standby') return out;
|
|
897
|
+
out.heavy = (s.running ?? []).filter((r) => r.holdsSlot).length;
|
|
898
|
+
out.waiting = (s.waiting ?? []).length;
|
|
899
|
+
out.pressure = s.pressure ?? null;
|
|
900
|
+
if (Number.isFinite(s.freePct)) out.usedPct = Math.max(0, Math.min(100, 100 - s.freePct));
|
|
901
|
+
const c = s.counts ?? {};
|
|
902
|
+
if (Number.isFinite(c.asked)) out.asked = c.asked;
|
|
903
|
+
if (Number.isFinite(c.waited)) out.waited = c.waited;
|
|
904
|
+
if (Number.isFinite(c.refused)) out.refused = c.refused;
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* The Agents page's leftovers row: the switch, and one sentence about what
|
|
910
|
+
* finished agents have left running while it is on.
|
|
911
|
+
*/
|
|
912
|
+
export function leftoverSettings({ config, supervisor }) {
|
|
913
|
+
const on = !!config.cleanupLeftovers;
|
|
914
|
+
let now = null;
|
|
915
|
+
let s = null;
|
|
916
|
+
try { s = supervisor.leftoverStatus?.() ?? null; } catch {}
|
|
917
|
+
const list = s?.leftovers ?? [];
|
|
918
|
+
const stoppable = list.reduce((n, l) => n + (l.programs ?? 0), 0);
|
|
919
|
+
const kept = list.reduce((n, l) => n + (l.kept ?? 0), 0);
|
|
920
|
+
const total = stoppable + kept;
|
|
921
|
+
// OFF, IT SAYS WHAT TURNING IT ON WOULD STOP, before anybody does: the
|
|
922
|
+
// consent is to these programs too, not only to future ones (agreed with
|
|
923
|
+
// Codex, 2026-10-05).
|
|
924
|
+
if (!on) {
|
|
925
|
+
if (total && stoppable) {
|
|
926
|
+
now = `Right now finished agents have left ${total} program${total === 1 ? '' : 's'} running; turning this on stops ${stoppable === total ? (total === 1 ? 'it' : 'them') : `${stoppable} of them`}, two hours from then.`;
|
|
927
|
+
}
|
|
928
|
+
return { on, now };
|
|
929
|
+
}
|
|
930
|
+
{
|
|
931
|
+
if (!total) now = 'Nothing left running by finished agents.';
|
|
932
|
+
else {
|
|
933
|
+
const next = list.filter((l) => l.programs && l.stopsAt).map((l) => l.stopsAt).sort((a, b) => a - b)[0];
|
|
934
|
+
const when = (ms) => {
|
|
935
|
+
const m = Math.max(1, Math.round((ms - Date.now()) / 60_000));
|
|
936
|
+
return m >= 60 ? `${Math.floor(m / 60)} h${m % 60 ? ` ${m % 60} m` : ''}` : `${m} m`;
|
|
937
|
+
};
|
|
938
|
+
now = `Finished agents have left ${total} program${total === 1 ? '' : 's'} running.`;
|
|
939
|
+
if (stoppable) now += ` ${stoppable} ${next && next > Date.now() ? `stop in ${when(next)}` : 'are stopping'}`;
|
|
940
|
+
if (kept) now += `${stoppable ? ';' : ''} ${kept} ${kept === 1 ? 'is' : 'are'} kept`;
|
|
941
|
+
now += '.';
|
|
942
|
+
const survivors = s?.survivors?.length ?? 0;
|
|
943
|
+
if (survivors) now += ` ${survivors} did not stop when asked.`;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return { on, now };
|
|
947
|
+
}
|
|
948
|
+
|
|
784
949
|
export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
|
|
785
950
|
switch (key) {
|
|
786
951
|
case 'agentsRunning':
|
|
787
952
|
supervisor.paused = !value;
|
|
788
953
|
supervisor.onChange?.();
|
|
789
954
|
break;
|
|
955
|
+
// WHO DECIDES THE NUMBER (w-e5225b62ba). Turning Automatic on does not
|
|
956
|
+
// throw away the number somebody set: it stays in the file, unread, so
|
|
957
|
+
// turning Automatic off again gives them their own number back rather than
|
|
958
|
+
// a fresh guess.
|
|
959
|
+
case 'agentsAuto':
|
|
960
|
+
saveConfig(config, { agentsAuto: !!value });
|
|
961
|
+
supervisor.wake?.();
|
|
962
|
+
supervisor.onChange?.();
|
|
963
|
+
break;
|
|
964
|
+
// SOMETHING FELT SLOW. The one lever a person has that no reading can
|
|
965
|
+
// replace: they are sitting in front of the Mac and the app is not. It only
|
|
966
|
+
// ever subtracts, and main/machine.mjs refuses to take it below one.
|
|
967
|
+
case 'agentsFeltSlow': {
|
|
968
|
+
const now = Math.max(0, Math.round(Number(config.agentsNudge) || 0));
|
|
969
|
+
saveConfig(config, { agentsNudge: value === 'reset' ? 0 : now + 1 });
|
|
970
|
+
supervisor.wake?.();
|
|
971
|
+
supervisor.onChange?.();
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
790
974
|
case 'sessionsAtOnce': {
|
|
791
975
|
// THE MACHINE DOES NOT CLAMP THIS, and for one round it did. These are
|
|
792
976
|
// developers using the tool as they want, so the range is the control's
|
|
793
977
|
// own range, the same on every Mac. What the hardware does is SUGGEST a
|
|
794
978
|
// starting number, one row up.
|
|
795
979
|
const n = Math.max(1, Math.min(MAX_SLOTS, Math.round(Number(value) || 1)));
|
|
796
|
-
|
|
980
|
+
// Setting a number by hand IS taking the decision back, so the page never
|
|
981
|
+
// shows a stepper that the supervisor is quietly ignoring.
|
|
982
|
+
saveConfig(config, { maxConcurrentSessions: n, agentsAuto: false });
|
|
983
|
+
// An explicit choice replaces the startup plan suggestion for BOTH
|
|
984
|
+
// engines. Leaving this marker set makes Codex ignore the new number
|
|
985
|
+
// until loadConfig runs again at restart. It is derived, not persisted.
|
|
986
|
+
config.planSlotsFrom = null;
|
|
987
|
+
// Fill newly available slots through the usual queue and its guards.
|
|
988
|
+
// Lowering the cap leaves existing work running and gates new starts.
|
|
989
|
+
supervisor.wake?.();
|
|
797
990
|
break;
|
|
798
991
|
}
|
|
799
992
|
// Written as an opt-OUT (`diagnostics: false`) so that on is what a config
|
|
@@ -801,6 +994,32 @@ export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
|
|
|
801
994
|
// see in her own file. `saveConfig` mutates the live config too, and
|
|
802
995
|
// main/analytics.mjs reads the switch at every send, so off takes effect
|
|
803
996
|
// from the moment she moves it and not at the next launch.
|
|
997
|
+
// HOLD HEAVY WORK WHEN MEMORY IS SHORT (w-3958c3753d). Takes effect now:
|
|
998
|
+
// the coordinator starts or stops this moment, and workers spawned from
|
|
999
|
+
// here on carry the check or do not. A worker already running keeps the
|
|
1000
|
+
// hooks it was started with; with the switch off its hook finds no socket
|
|
1001
|
+
// and lets every command straight through.
|
|
1002
|
+
case 'memoryGate':
|
|
1003
|
+
saveConfig(config, { memoryGate: !!value });
|
|
1004
|
+
supervisor.setMemoryGate?.(!!value)?.catch?.((e) => console.warn('zero: memory gate:', e.message));
|
|
1005
|
+
supervisor.onChange?.();
|
|
1006
|
+
break;
|
|
1007
|
+
// STOP WHAT FINISHED AGENTS LEAVE RUNNING (main/leftovers.mjs). Turning it
|
|
1008
|
+
// on records the moment, which is the earliest the clock may start from.
|
|
1009
|
+
case 'cleanupLeftovers':
|
|
1010
|
+
saveConfig(config, value ? { cleanupLeftovers: true, cleanupLeftoversSince: Date.now() } : { cleanupLeftovers: false });
|
|
1011
|
+
supervisor.setLeftoverCleanup?.(!!value)?.catch?.((e) => console.warn('zero: leftovers:', e.message));
|
|
1012
|
+
supervisor.onChange?.();
|
|
1013
|
+
break;
|
|
1014
|
+
case 'memoryGateSlots': {
|
|
1015
|
+
const n = value === null || value === undefined || value === 'auto'
|
|
1016
|
+
? null
|
|
1017
|
+
: Math.max(1, Math.min(MAX_SLOTS, Math.round(Number(value) || 1)));
|
|
1018
|
+
saveConfig(config, { memoryGateSlots: n });
|
|
1019
|
+
if (config.memoryGate) supervisor.setMemoryGate?.(true)?.catch?.(() => {});
|
|
1020
|
+
supervisor.onChange?.();
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
804
1023
|
case 'diagnostics':
|
|
805
1024
|
saveConfig(config, { diagnostics: !!value });
|
|
806
1025
|
break;
|