agentbox-app 0.1.6
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/LICENSE +674 -0
- package/README.md +176 -0
- package/bin/agentbox.mjs +103 -0
- package/briefs/adhd-mode.md +5 -0
- package/briefs/message-rules.md +95 -0
- package/briefs/worker.md +230 -0
- package/briefs/writing-rules.md +336 -0
- package/main/account-discovery.mjs +145 -0
- package/main/account-tooling.mjs +261 -0
- package/main/agent-activity.mjs +56 -0
- package/main/agent-files.mjs +422 -0
- package/main/agent-schedule.mjs +254 -0
- package/main/agent-sessions.mjs +435 -0
- package/main/agents.mjs +1095 -0
- package/main/analytics.mjs +206 -0
- package/main/answer-modes.mjs +206 -0
- package/main/approval-prompt-server.mjs +160 -0
- package/main/approvals.mjs +487 -0
- package/main/artifact-path.mjs +144 -0
- package/main/claude-bin.mjs +311 -0
- package/main/claude-input.mjs +117 -0
- package/main/claude-models.mjs +244 -0
- package/main/claude-plan.mjs +148 -0
- package/main/claude-usage.mjs +149 -0
- package/main/code-change.mjs +383 -0
- package/main/code-file.mjs +116 -0
- package/main/codex-account.mjs +146 -0
- package/main/codex-app-server.mjs +796 -0
- package/main/codex-approvals.mjs +712 -0
- package/main/codex-bin.mjs +261 -0
- package/main/codex-compaction.mjs +49 -0
- package/main/codex-models.mjs +194 -0
- package/main/codex-session.mjs +797 -0
- package/main/codex-threads.mjs +268 -0
- package/main/codex-usage-file.mjs +192 -0
- package/main/codex-usage.mjs +149 -0
- package/main/codex-watch.mjs +35 -0
- package/main/codex.mjs +757 -0
- package/main/config.mjs +345 -0
- package/main/copy-selection.mjs +55 -0
- package/main/crash-report.mjs +158 -0
- package/main/demo.mjs +254 -0
- package/main/dev-window.mjs +103 -0
- package/main/doc-file.mjs +99 -0
- package/main/doc-scheme.mjs +126 -0
- package/main/first-run.mjs +190 -0
- package/main/folders.mjs +91 -0
- package/main/fresh-user.mjs +120 -0
- package/main/git-change.mjs +323 -0
- package/main/img-scheme.mjs +90 -0
- package/main/instruction-settings.mjs +205 -0
- package/main/ipc.mjs +1558 -0
- package/main/live-replies.mjs +77 -0
- package/main/machine.mjs +113 -0
- package/main/main.mjs +972 -0
- package/main/menu.mjs +177 -0
- package/main/notify.mjs +155 -0
- package/main/open-source-file.mjs +8 -0
- package/main/project-identity.mjs +149 -0
- package/main/provider-commands.mjs +32 -0
- package/main/rail-note.mjs +74 -0
- package/main/repeats.mjs +261 -0
- package/main/row-label.mjs +185 -0
- package/main/screen-detail.mjs +44 -0
- package/main/serve.mjs +262 -0
- package/main/settings.mjs +912 -0
- package/main/staleness.mjs +76 -0
- package/main/store/chats.mjs +188 -0
- package/main/store/home.mjs +383 -0
- package/main/store/migrate.mjs +40 -0
- package/main/store/paths.mjs +82 -0
- package/main/store/pending-writes.mjs +122 -0
- package/main/store/project-lock.mjs +200 -0
- package/main/store/project.mjs +523 -0
- package/main/store/queues.mjs +31 -0
- package/main/store/work-items.mjs +299 -0
- package/main/store/write-queue.mjs +14 -0
- package/main/store-modules.mjs +23 -0
- package/main/store.mjs +1269 -0
- package/main/supervisor.mjs +6875 -0
- package/main/task-commands.mjs +161 -0
- package/main/task-folders-offthread.mjs +84 -0
- package/main/task-folders-thread.mjs +13 -0
- package/main/task-folders.mjs +409 -0
- package/main/task-remote-control.mjs +65 -0
- package/main/task-terminals.mjs +169 -0
- package/main/terminal-access.mjs +6 -0
- package/main/updater.mjs +240 -0
- package/main/zoom-keys.mjs +54 -0
- package/main/zoom-readout.mjs +20 -0
- package/mcp/agentbox-mcp.sh +34 -0
- package/mcp/core/account.mjs +77 -0
- package/mcp/core/chat.mjs +61 -0
- package/mcp/core/documents.mjs +238 -0
- package/mcp/core/page.mjs +152 -0
- package/mcp/core/products.mjs +110 -0
- package/mcp/core/skills.mjs +126 -0
- package/mcp/core/work.mjs +377 -0
- package/mcp/stdio.mjs +70 -0
- package/mcp/tools.mjs +257 -0
- package/package.json +186 -0
- package/renderer/dist/assets/Geist-Variable-Bj2R_7yk.woff2 +0 -0
- package/renderer/dist/assets/GeistMono-Variable-Dispecij.woff2 +0 -0
- package/renderer/dist/assets/addon-fit-YJmn1quW.js +12 -0
- package/renderer/dist/assets/addon-serialize-CsFQTvP_.js +13 -0
- 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/gouache-valley-ChSu14-t.webp +0 -0
- package/renderer/dist/assets/gouache-valley-d9qasI6k.webp +0 -0
- package/renderer/dist/assets/gouache-valley-sm-CdLjT5Ec.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-CwiDv5bL.css +32 -0
- package/renderer/dist/assets/index-D9hCAVPl.js +684 -0
- 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/source-sans-3-cyrillic-ext-wght-normal-DzyfIafT.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-cyrillic-wght-normal-BMDVbyM7.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-greek-ext-wght-normal-BWSLJLk6.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-greek-wght-normal-C9H9m1vD.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-latin-ext-wght-normal-C8iNium2.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-latin-wght-normal-BqRLTx4X.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-vietnamese-wght-normal-C1uRvKPU.woff2 +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/assets/xterm-BT7tBf8A.js +27 -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 +299 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/fixtures/onboard/designs/w-n13/the-first-row.html +1 -0
- package/renderer/dist/index.html +13 -0
- package/scripts/after-install.mjs +59 -0
- package/scripts/approval-server.sh +43 -0
- package/scripts/before-publish.mjs +133 -0
- package/shared/agent-import.d.mts +13 -0
- package/shared/agent-import.mjs +252 -0
- package/shared/agents.d.mts +190 -0
- package/shared/agents.mjs +937 -0
- package/shared/analytics-events.mjs +63 -0
- package/shared/answers.d.mts +12 -0
- package/shared/answers.mjs +80 -0
- package/shared/artifact-keys.d.mts +28 -0
- package/shared/artifact-keys.mjs +140 -0
- package/shared/bridge-map.mjs +128 -0
- package/shared/claude-commands.d.mts +20 -0
- package/shared/claude-commands.generated.mjs +149 -0
- package/shared/claude-commands.mjs +131 -0
- package/shared/claude-models.generated.d.mts +15 -0
- package/shared/claude-models.generated.mjs +26 -0
- package/shared/claude-usage.d.mts +16 -0
- package/shared/claude-usage.mjs +172 -0
- package/shared/codex-commands.d.mts +4 -0
- package/shared/codex-commands.mjs +17 -0
- package/shared/codex-import.d.mts +38 -0
- package/shared/codex-import.mjs +232 -0
- package/shared/codex-modes.mjs +159 -0
- package/shared/codex-usage.mjs +180 -0
- package/shared/contracts.mjs +412 -0
- package/shared/crash-scrub.mjs +182 -0
- package/shared/dashboard.mjs +481 -0
- package/shared/dead-run-trace.mjs +127 -0
- package/shared/demo-world.mjs +415 -0
- package/shared/effort-levels.d.mts +15 -0
- package/shared/effort-levels.mjs +76 -0
- package/shared/engines.d.mts +62 -0
- package/shared/engines.mjs +389 -0
- package/shared/first-run-examples.mjs +60 -0
- package/shared/first-run-line.mjs +149 -0
- package/shared/first-run-practice.d.mts +32 -0
- package/shared/first-run-practice.mjs +254 -0
- package/shared/first-run-shape.mjs +100 -0
- package/shared/fresh-user-home.mjs +189 -0
- package/shared/hook-failure.mjs +22 -0
- package/shared/install-id.mjs +39 -0
- package/shared/instruction-defaults.json +5 -0
- package/shared/library-folder.mjs +28 -0
- package/shared/notify-rules.mjs +120 -0
- package/shared/product-name.d.mts +6 -0
- package/shared/product-name.mjs +144 -0
- package/shared/project-folder-check.d.mts +22 -0
- package/shared/project-folder-check.mjs +152 -0
- package/shared/provider-commands.d.mts +8 -0
- package/shared/provider-commands.mjs +58 -0
- package/shared/rank.d.mts +19 -0
- package/shared/rank.mjs +146 -0
- package/shared/recovery.mjs +26 -0
- package/shared/referenced-files.d.mts +11 -0
- package/shared/referenced-files.mjs +122 -0
- package/shared/repeats.d.mts +45 -0
- package/shared/repeats.mjs +193 -0
- package/shared/schemes.mjs +33 -0
- package/shared/settings-terminal.d.mts +1 -0
- package/shared/settings-terminal.mjs +14 -0
- package/shared/side-build.mjs +57 -0
- package/shared/spawn-trouble.d.mts +8 -0
- package/shared/spawn-trouble.mjs +599 -0
- package/shared/terminal-state.d.mts +1 -0
- package/shared/terminal-state.mjs +58 -0
- package/shared/usage.d.mts +53 -0
- package/shared/usage.mjs +319 -0
- package/shared/work-items.mjs +561 -0
- package/shared/work-lines.mjs +360 -0
- package/worker-permissions.json +6 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Send live input in invocation order without waiting for the preceding
|
|
2
|
+
// provider acknowledgement. Claude can batch several queued inputs at its next
|
|
3
|
+
// tool boundary. Serialize acknowledgement + ledger commits separately so
|
|
4
|
+
// neither an out-of-order response nor a rejected input reorders the history.
|
|
5
|
+
import { NAME } from '../shared/product-name.mjs';
|
|
6
|
+
import { TURN_CLOSED } from './claude-input.mjs';
|
|
7
|
+
export function submitReply(supervisor, payload, commit) {
|
|
8
|
+
const { product, id, answer, status, permissionMode } = payload;
|
|
9
|
+
const key = JSON.stringify([product, id]);
|
|
10
|
+
const sends = supervisor._replySends ??= new Map();
|
|
11
|
+
const previous = sends.get(key) ?? Promise.resolve();
|
|
12
|
+
const session = supervisor.sessions?.get(id);
|
|
13
|
+
const live = session?.product === product && typeof session.child?.steer === 'function'
|
|
14
|
+
&& status !== 'done' && typeof answer === 'string' && answer.trim() && answer !== '(withdrawn)';
|
|
15
|
+
// Observe rejections immediately, including when an earlier reply is still
|
|
16
|
+
// waiting. The ordered commit below rethrows them to their own caller.
|
|
17
|
+
const delivery = Promise.resolve().then(() => {
|
|
18
|
+
if (!live) return;
|
|
19
|
+
if (permissionMode != null) throw Error('A running turn keeps its current permissions. Stop it before sending with a different permission mode.');
|
|
20
|
+
if (answer.trimStart().startsWith('/') && !session.remoteIdle) throw Error('Wait for the current turn to finish before running a slash command.');
|
|
21
|
+
return session.child.steer(answer);
|
|
22
|
+
}).then(() => ({ ok: true }), error => ({ ok: false, error }));
|
|
23
|
+
const operation = previous.catch(() => {}).then(async () => {
|
|
24
|
+
const accepted = await delivery;
|
|
25
|
+
// TOO LATE FOR THIS TURN IS NOT A FAILED SEND. The session closed its
|
|
26
|
+
// input between her pressing send and the line reaching it, so the
|
|
27
|
+
// message is saved as an ordinary reply instead: the session's own exit
|
|
28
|
+
// (`deliverMidflightReply`) or the next tick starts the run that carries
|
|
29
|
+
// it. Throwing here put "The turn has ended" in front of her and her words
|
|
30
|
+
// back in the box, on whatever screen she had moved on to (w-12730c6506).
|
|
31
|
+
const late = !accepted.ok && accepted.error?.code === TURN_CLOSED;
|
|
32
|
+
if (!accepted.ok && !late) throw accepted.error;
|
|
33
|
+
const steered = live && !late;
|
|
34
|
+
let saved;
|
|
35
|
+
try { saved = await commit(); }
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (steered) throw Error(`The agent received this message, but ${NAME} could not save its local record. Check the conversation before resending.`, { cause: error });
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
const item = saved ? { ...saved, product, id } : saved;
|
|
41
|
+
if (steered && item) {
|
|
42
|
+
supervisor._handledAnswers.add(supervisor._answerKey(item));
|
|
43
|
+
session.lastLiveReply = item;
|
|
44
|
+
supervisor._saveState();
|
|
45
|
+
if (session.exitFailed) {
|
|
46
|
+
supervisor.redeliverAnswer(item, item.answer);
|
|
47
|
+
if (!session.stoppedByUs) supervisor.deliverMidflightReply({ ...item, answer: null, wrote: {} }, null);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return item;
|
|
51
|
+
});
|
|
52
|
+
sends.set(key, operation);
|
|
53
|
+
void operation.finally(() => { if (sends.get(key) === operation) sends.delete(key); }).catch(() => {});
|
|
54
|
+
return operation;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The ledger preserves every reply even while capacity is full. A resumed
|
|
58
|
+
// provider otherwise receives only its latest-answer field. Read backward to
|
|
59
|
+
// the preceding response/delivery, preserving repeated words and write order.
|
|
60
|
+
export function queuedReplyText(item, history, delivered) {
|
|
61
|
+
const pending=[];
|
|
62
|
+
const latestTs=item.wrote?.answer?.ts;
|
|
63
|
+
for(let i=history.length-1;i>=0;i--){
|
|
64
|
+
const line=history[i];
|
|
65
|
+
if(line.heartbeat||line.release)continue;
|
|
66
|
+
if(line.patch?.result)break;
|
|
67
|
+
const answer=line.patch?.answer;
|
|
68
|
+
if(typeof answer!=='string'||!answer.trim())continue;
|
|
69
|
+
if(answer==='(withdrawn)')break;
|
|
70
|
+
if(line.source!=='founder')continue;
|
|
71
|
+
const candidate={...item,answer,wrote:{...item.wrote,answer:{ts:line.ts}}};
|
|
72
|
+
// The queue marks the newest reply at spawn. It is still part of this input.
|
|
73
|
+
if(line.ts!==latestTs&&delivered(candidate))break;
|
|
74
|
+
pending.unshift(answer);
|
|
75
|
+
}
|
|
76
|
+
return pending.length>1 ? pending.join('\n\n') : item.answer;
|
|
77
|
+
}
|
package/main/machine.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// HOW MANY AGENTS WE SUGGEST RUNNING ON THIS PARTICULAR MAC.
|
|
2
|
+
//
|
|
3
|
+
// IT SUGGESTS AND IT NEVER REFUSES. The stepper goes to `MAX_SLOTS` on every
|
|
4
|
+
// machine, the same on an 8 GB M1 as on a Studio, and the writer clamps to that
|
|
5
|
+
// range and not to anything here. For one round it did clamp, and the founder's
|
|
6
|
+
// answer is why it does not: these are developers using the tool as they want,
|
|
7
|
+
// and somebody who wants twelve agents on a small laptop has decided that.
|
|
8
|
+
//
|
|
9
|
+
// What this number DOES is choose where somebody starts, and say so in a
|
|
10
|
+
// sentence on the Agents row.
|
|
11
|
+
//
|
|
12
|
+
// ═══ WHERE THE NUMBERS COME FROM, AND WHY THEY ARE NOT THE MEASURED ONES ═══
|
|
13
|
+
//
|
|
14
|
+
// FIRST, WHAT WAS MEASURED. On the founder's Mac (Apple M4, 16 GB, 10 cores)
|
|
15
|
+
// with four agents live, 2026-09-22, `ps -Ao pid,ppid,rss` summed over each
|
|
16
|
+
// agent's whole process tree:
|
|
17
|
+
//
|
|
18
|
+
// agent tree 400 MB 675 MB 1353 MB
|
|
19
|
+
// the claude process 311 MB 337 MB 525 MB
|
|
20
|
+
//
|
|
21
|
+
// So an agent is the `claude` process plus whatever it has spawned, and the
|
|
22
|
+
// spread is the spawned half: a session reading files sits near 400 MB and one
|
|
23
|
+
// running a build or a test suite passes a gigabyte. The app itself measured
|
|
24
|
+
// 1.43 GB across all its Electron processes at the same moment.
|
|
25
|
+
//
|
|
26
|
+
// THAT ARITHMETIC SAID 8 ON HER MAC AND IT WAS WRONG. Budgeting 6 GB of
|
|
27
|
+
// headroom and a gigabyte an agent, 16 GB comes out at eight at once.
|
|
28
|
+
//
|
|
29
|
+
// Her reading beats the arithmetic and it is not close. Resident memory is the
|
|
30
|
+
// easiest thing to measure and it is not what runs out first: four agents on a
|
|
31
|
+
// ten core Mac are also four `ripgrep` sweeps, four test suites and four
|
|
32
|
+
// builds, against an editor and a browser she is using at the same time. None
|
|
33
|
+
// of that is in an RSS column. The measurement above is kept because it is
|
|
34
|
+
// real and because it says what an agent costs when you need that; it is just
|
|
35
|
+
// not the thing that decides this.
|
|
36
|
+
//
|
|
37
|
+
// SO THE LADDER IS ANCHORED ON HER TWO POINTS. 2 at 8 GB, which she confirmed,
|
|
38
|
+
// and 4 at 16 GB, which she set. Those two fit ONE AGENT PER 4 GB OF MEMORY
|
|
39
|
+
// exactly, with nothing held back, and that is the whole rule:
|
|
40
|
+
//
|
|
41
|
+
// 8 GB -> 2 32 GB -> 8
|
|
42
|
+
// 16 GB -> 4 36 GB -> 9
|
|
43
|
+
// 24 GB -> 6 64 GB -> 12 (MAX_SLOTS)
|
|
44
|
+
//
|
|
45
|
+
// "For 24 GB, have it scale accordingly" is that line continuing, which is why
|
|
46
|
+
// it is a line and not a table: a machine nobody listed still gets an answer.
|
|
47
|
+
//
|
|
48
|
+
// AND CORES STILL BIND ON AN ODD MACHINE. `cores - 2` leaves the app and the
|
|
49
|
+
// machine's own work a core each. On every Mac Apple actually sells this is
|
|
50
|
+
// slack -- memory decides at 8, 16, 24 and 32 -- but a box with a lot of RAM
|
|
51
|
+
// and few cores exists (a VM, a rented host) and one agent per 4 GB would
|
|
52
|
+
// promise it twelve.
|
|
53
|
+
//
|
|
54
|
+
// IF THE MACHINE CANNOT BE READ, THE ANSWER IS 4.
|
|
55
|
+
|
|
56
|
+
import os from 'node:os';
|
|
57
|
+
|
|
58
|
+
/** The most agents the stepper offers, on any machine. Not derived from
|
|
59
|
+
* anything here: it is the control's own range and it is the same everywhere. */
|
|
60
|
+
export const MAX_SLOTS = 12;
|
|
61
|
+
/** One agent per this many gigabytes of memory. Her two anchors, 2 at 8 GB and
|
|
62
|
+
* 4 at 16 GB, fit this exactly. */
|
|
63
|
+
export const GB_PER_AGENT = 4;
|
|
64
|
+
/** Cores held back for the app and the machine's own work. */
|
|
65
|
+
export const RESERVED_CORES = 2;
|
|
66
|
+
/** What we suggest when the machine cannot be read at all. Her number. */
|
|
67
|
+
export const BASELINE_SLOTS = 4;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* What we suggest for this machine, with the two halves it came from so a
|
|
71
|
+
* screen can say why. `memBytes` and `cores` are arguments rather than reads so
|
|
72
|
+
* a test can lay out an 8 GB M1 without owning one.
|
|
73
|
+
*
|
|
74
|
+
* `known` is false when the machine could not be read and the answer is the
|
|
75
|
+
* baseline rather than a reading, which is what stops the row claiming a Mac
|
|
76
|
+
* has 0 GB.
|
|
77
|
+
*/
|
|
78
|
+
export function machineSlots({ memBytes = os.totalmem(), cores = os.cpus().length } = {}) {
|
|
79
|
+
const memGb = Math.round((Number(memBytes) || 0) / (1024 ** 3));
|
|
80
|
+
const cpu = Math.max(0, Math.round(Number(cores) || 0));
|
|
81
|
+
// NOTHING TO GO ON IS ITS OWN ANSWER, not a small one. `os.totalmem()` coming
|
|
82
|
+
// back as 0, or a core count of nothing, means we did not read the machine;
|
|
83
|
+
// treating that as a tiny Mac would start somebody on one agent because a
|
|
84
|
+
// syscall failed.
|
|
85
|
+
if (memGb <= 0 || cpu <= 0) {
|
|
86
|
+
return { slots: BASELINE_SLOTS, memGb: 0, cores: 0, byMemory: 0, byCores: 0, known: false };
|
|
87
|
+
}
|
|
88
|
+
// Floor, not round: half an agent's worth of memory is not an agent.
|
|
89
|
+
const byMemory = Math.floor(memGb / GB_PER_AGENT);
|
|
90
|
+
const byCores = cpu - RESERVED_CORES;
|
|
91
|
+
// AT LEAST ONE, ALWAYS. A machine too small for this arithmetic still has to
|
|
92
|
+
// run the app, and an app that suggests no agent at all is broken rather than
|
|
93
|
+
// careful. A 4 GB machine gets one at a time and a slow day.
|
|
94
|
+
const slots = Math.max(1, Math.min(MAX_SLOTS, byMemory, byCores));
|
|
95
|
+
return { slots, memGb, cores: cpu, byMemory, byCores, known: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The sentence the Agents row puts under the stepper, in her vocabulary: what
|
|
100
|
+
* this Mac is, and what we would run on it. It SUGGESTS and never refuses, so
|
|
101
|
+
* it says "we suggest" and not "as high as it goes", which is what this line
|
|
102
|
+
* said for one round while the number really was a wall.
|
|
103
|
+
*
|
|
104
|
+
* Null once somebody is already at or under the suggestion, because a sentence
|
|
105
|
+
* recommending a number she has already taken is a line to read past. Null too
|
|
106
|
+
* when the machine could not be read, because the honest version of it would
|
|
107
|
+
* have to say "this Mac has 0 GB".
|
|
108
|
+
*/
|
|
109
|
+
export function machineNote({ slots, memGb, cores, known = true }, at = 0) {
|
|
110
|
+
if (!known) return null;
|
|
111
|
+
if (at && at <= slots) return null;
|
|
112
|
+
return `This Mac has ${memGb} GB and ${cores} cores, so we suggest ${slots} at once. Go higher if you want to.`;
|
|
113
|
+
}
|