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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// WHAT THE AGENTS ARE RUNNING ON, IN WORDS.
|
|
2
|
+
//
|
|
3
|
+
// From a session with a new user, 2026-10-07: "He didn't realize it
|
|
4
|
+
// auto-connected to Claude/Codex; he wasn't sure how it was even running. Would
|
|
5
|
+
// be nice to have some small indication."
|
|
6
|
+
//
|
|
7
|
+
// He was right that nothing said. The app finds whichever coding agent is
|
|
8
|
+
// already signed in on the Mac and runs every worker on that subscription
|
|
9
|
+
// (main/claude-plan.mjs, main/codex-account.mjs), and the walk is SILENT about
|
|
10
|
+
// it by design: `needsPlan` in renderer/src/plan-setup.ts skips the plan
|
|
11
|
+
// question entirely when one tool is found and signed in, which is the right
|
|
12
|
+
// call and leaves nobody told. The only screen that has ever named the account
|
|
13
|
+
// is Settings, two clicks in, and you have to suspect there is something to
|
|
14
|
+
// look for before you go.
|
|
15
|
+
//
|
|
16
|
+
// THE PLAN'S VOCABULARY, NOT THE TOOL'S. "Claude" and "ChatGPT" here, never
|
|
17
|
+
// "Claude Code" and "Codex", which is the rule the walk already follows
|
|
18
|
+
// (renderer/src/plan-setup.ts: words people know, "never the names of the tools
|
|
19
|
+
// underneath"). The fact being shown is WHOSE SUBSCRIPTION IS BEING SPENT, and
|
|
20
|
+
// nobody pays for a CLI. The tool names stay where they belong: on a byline
|
|
21
|
+
// about which agent is working a row (`engineWordFor`, renderer/src/byline.ts),
|
|
22
|
+
// which is a different question with a different answer.
|
|
23
|
+
//
|
|
24
|
+
// NOTHING IS SAID UNTIL SOMETHING IS KNOWN. Every function here answers null on
|
|
25
|
+
// an engine it cannot name, and the screens draw nothing at all for null rather
|
|
26
|
+
// than "checking…" or a dash. A corner that says it does not know is worse than
|
|
27
|
+
// a corner that says nothing: it spends the glance and answers no question.
|
|
28
|
+
|
|
29
|
+
/** The word for the plan behind an engine. Null for an engine we cannot name. */
|
|
30
|
+
export function runsOnName(engine) {
|
|
31
|
+
if (engine === 'claude') return 'Claude';
|
|
32
|
+
if (engine === 'codex') return 'ChatGPT';
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* THE LINE ON A GLANCE SURFACE. "Claude · Max 20x", or just "Claude" where the
|
|
38
|
+
* plan cannot be read.
|
|
39
|
+
*
|
|
40
|
+
* The plan is the raw label the tool itself reports, already turned into words
|
|
41
|
+
* by whoever read it (`planLabel` in main/claude-plan.mjs for Claude, the
|
|
42
|
+
* named-plan table in main/codex-account.mjs for ChatGPT). It is never
|
|
43
|
+
* invented here: a plan nobody measured, printed confidently, is the app
|
|
44
|
+
* lying about what somebody is paying for.
|
|
45
|
+
*/
|
|
46
|
+
export function runsOnLine({ engine = null, plan = null } = {}) {
|
|
47
|
+
const name = runsOnName(engine);
|
|
48
|
+
if (!name) return null;
|
|
49
|
+
const words = String(plan ?? '').trim();
|
|
50
|
+
return words ? `${name} · ${words}` : name;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* THE WHOLE SENTENCE, for a hover and for whatever reads the screen aloud. The
|
|
55
|
+
* glance surface carries two words and a plan; this is where the rest of the
|
|
56
|
+
* answer lives, and it is the half that tells a new person what they are
|
|
57
|
+
* looking at.
|
|
58
|
+
*/
|
|
59
|
+
export function runsOnTitle({ engine = null, plan = null } = {}) {
|
|
60
|
+
const name = runsOnName(engine);
|
|
61
|
+
if (!name) return null;
|
|
62
|
+
const words = String(plan ?? '').trim();
|
|
63
|
+
const plansentence = words ? ` Your ${words} plan.` : '';
|
|
64
|
+
return `Your agents run on the ${name} account already signed in on this Mac.${plansentence}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* THE ONE LINE DURING THE WALK, on the Mac where there was nothing to ask.
|
|
69
|
+
*
|
|
70
|
+
* It replaces silence, not a question: somebody with no coding agent set up
|
|
71
|
+
* still gets the plan question and the setup card, and never sees this.
|
|
72
|
+
*/
|
|
73
|
+
export function foundOnThisMac({ engine = null, plan = null } = {}) {
|
|
74
|
+
const name = runsOnName(engine);
|
|
75
|
+
if (!name) return null;
|
|
76
|
+
const words = String(plan ?? '').trim();
|
|
77
|
+
if (words) return `Your agents will run on the ${name} ${words} plan already signed in on this Mac.`;
|
|
78
|
+
return `Your agents will run on the ${name} account already signed in on this Mac.`;
|
|
79
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Types for shared/schemes.mjs, for the window side.
|
|
2
|
+
//
|
|
3
|
+
// The lists are built from the app's past names at load, so their length is
|
|
4
|
+
// not a constant and nothing here pretends otherwise. `isScheme` takes the
|
|
5
|
+
// protocol as `URL` hands it over, with its colon on the end.
|
|
6
|
+
export declare const DOC_SCHEME: string;
|
|
7
|
+
export declare const IMG_SCHEME: string;
|
|
8
|
+
export declare const DOC_SCHEMES: readonly string[];
|
|
9
|
+
export declare const IMG_SCHEMES: readonly string[];
|
|
10
|
+
export declare const ALL_SCHEMES: readonly string[];
|
|
11
|
+
export declare function isScheme(schemes: readonly string[], protocol: string): boolean;
|
package/shared/schemes.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// of these schemes is STORED: a product's logo is kept in the store as
|
|
11
11
|
// `<scheme>://file/…` and a message she has already been sent holds picture
|
|
12
12
|
// urls in its body. Dropping the old scheme would not degrade those, it would
|
|
13
|
-
// break them, and the symptom is a broken image in a row
|
|
13
|
+
// break them, and the symptom is a broken image in a row written months ago.
|
|
14
14
|
// So the current name is what new urls are built with, and every name this app
|
|
15
15
|
// has had is still registered and still answered.
|
|
16
16
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Types for shared/shortcuts.mjs, which is plain ESM so that the Settings page
|
|
2
|
+
// (Vite) and the supervisor (Node) read one list of keys. See the module for
|
|
3
|
+
// why each row reads the way it does.
|
|
4
|
+
|
|
5
|
+
export interface Shortcut {
|
|
6
|
+
/**
|
|
7
|
+
* The caps, drawn in order. A chord is ONE cap ('⌘K', '⌘↵'), the way the
|
|
8
|
+
* walk already draws them, because ⌘ and K are not two keys to a reader.
|
|
9
|
+
*
|
|
10
|
+
* AND THE HOVER HINT NO LONGER AGREES WITH THAT. ⌘J was drawn both ways on
|
|
11
|
+
* w-2f7fac6027, one cap per key won, because a single ⌘J cap looked
|
|
12
|
+
* condensed next to the usual way shortcuts are drawn. So
|
|
13
|
+
* `capsFor` in hint-plate.ts splits a chord into one cap per glyph, and this
|
|
14
|
+
* page does not. That is a real disagreement between two surfaces and it is
|
|
15
|
+
* left standing rather than quietly resolved: this is a reference page where
|
|
16
|
+
* a chord is read as one thing to press, and the hint is a label on a
|
|
17
|
+
* control she is pointing at. If the page ever looks condensed too,
|
|
18
|
+
* the fix is to take `capsFor` here, not to put one cap back there. */
|
|
19
|
+
keys: string[];
|
|
20
|
+
/**
|
|
21
|
+
* The word set between two caps. Absent means they simply sit side by side,
|
|
22
|
+
* which is what ↑ ↓ means: both of them, either way. */
|
|
23
|
+
join?: 'or' | 'to';
|
|
24
|
+
/** What it does, in plain English. ONE sentence, under seventy characters. */
|
|
25
|
+
what: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ShortcutGroup {
|
|
29
|
+
/** THE HEADING SAYS WHEN THE KEYS WORK, LITERALLY, AND IT IS THE ONLY PROSE
|
|
30
|
+
* A GROUP GETS. There is deliberately no note field: see the module. */
|
|
31
|
+
label: string;
|
|
32
|
+
keys: Shortcut[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const SHORTCUTS: ShortcutGroup[];
|
|
36
|
+
|
|
37
|
+
/** Every cap drawn on the page, flattened. The test walks this. */
|
|
38
|
+
export function everyKey(groups?: ShortcutGroup[]): string[];
|
|
39
|
+
|
|
40
|
+
/** The keys as one block of plain text, for an agent's system prompt. */
|
|
41
|
+
export function keysForAgents(groups?: ShortcutGroup[]): string;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// PURE. EVERY KEY AGENTBOX HAS, IN THE ORDER A PERSON MEETS THEM.
|
|
2
|
+
//
|
|
3
|
+
// A shortcuts page in settings, because onboarding a non-technical tester
|
|
4
|
+
// meant saying each key out loud, one at a time: the app had a switch for
|
|
5
|
+
// whether it WHISPERS its keys and no page anywhere that says what they are.
|
|
6
|
+
//
|
|
7
|
+
// WHY THIS IS A FILE AND NOT MARKUP IN THE COMPONENT. A page that prints a key
|
|
8
|
+
// the handler does not have is worse than no page at all — that is the rule
|
|
9
|
+
// `list-rules.ts` already keeps for the hint on a row, and the reason it holds
|
|
10
|
+
// even harder here is that a hint is read for a second and a reference page is
|
|
11
|
+
// believed. So the list is a value, and
|
|
12
|
+
// `tests/the-shortcuts-page-lists-keys-that-work.test.mjs` reads every entry
|
|
13
|
+
// below against the branch in App.tsx (or main/main.mjs, or Compose.tsx) that
|
|
14
|
+
// actually runs it. A key added here without a handler fails the suite.
|
|
15
|
+
//
|
|
16
|
+
// WHY IT IS IN shared/ AND PLAIN ESM (w-7ec8553e23, 2026-10-04). It lived in
|
|
17
|
+
// renderer/src as TypeScript until an agent told the user to "Press S on this
|
|
18
|
+
// task" three days after S stopped scheduling: nothing a session is handed
|
|
19
|
+
// said what the keys are, so it went by an old memory. The supervisor now
|
|
20
|
+
// hands every run `keysForAgents()`, read off this same list, and main cannot
|
|
21
|
+
// import TypeScript. Types are in shortcuts.d.mts beside it.
|
|
22
|
+
//
|
|
23
|
+
// ONE SENTENCE A ROW, AND IT IS SHORT (w-1bc916a880). Every
|
|
24
|
+
// sentence here is now under seventy characters and the test enforces it. A row
|
|
25
|
+
// that wants a second idea becomes its own row, or it does not go on the page.
|
|
26
|
+
//
|
|
27
|
+
// AND NO KEY HIDES INSIDE A SENTENCE.
|
|
28
|
+
//
|
|
29
|
+
// HOW IT IS GROUPED, and it is not how the code is arranged. The code has one
|
|
30
|
+
// switch for the list, one branch for an open task, a chord block above both
|
|
31
|
+
// and a main-process handler under all of it. Nobody thinks in those. Four
|
|
32
|
+
// headings, and every key sits under the one that matches when she would want
|
|
33
|
+
// it: in your inbox, in a task you have opened, closing and scheduling and
|
|
34
|
+
// undoing, anywhere in the app.
|
|
35
|
+
//
|
|
36
|
+
// EVERY HEADING IS LITERAL AND THERE IS NO SUBHEADING AT ALL (w-1bc916a880,
|
|
37
|
+
// second round).
|
|
38
|
+
//
|
|
39
|
+
// The plainest point first: that heading named one of the two
|
|
40
|
+
// directions its own rows draw. Three of the four were figures of speech, and a
|
|
41
|
+
// figure of speech is a second thing to decode on a page whose only job is to
|
|
42
|
+
// be looked up. So a heading now says WHEN the keys under it work, in the words
|
|
43
|
+
// the app itself uses for those places, and the one group that cannot be named
|
|
44
|
+
// that way is named by what its keys do instead, because all four of its keys
|
|
45
|
+
// work in both places.
|
|
46
|
+
//
|
|
47
|
+
// What the three of them carried, and where it went:
|
|
48
|
+
//
|
|
49
|
+
// "In Scheduled, E brings a task back instead of closing it." This was the
|
|
50
|
+
// most confusing one. It is also the same class of thing as G, B and
|
|
51
|
+
// the import letters below: a behaviour that only happens in one view. The
|
|
52
|
+
// page's existing rule is that those stay off it, so this now obeys the rule
|
|
53
|
+
// the rest of the file already kept.
|
|
54
|
+
//
|
|
55
|
+
// The two ↵ rows say what ↵ does when it does something.
|
|
56
|
+
//
|
|
57
|
+
// "Anything without a key of its own is in ⌘K by name." The lede at the top
|
|
58
|
+
// of the page already says this, so it was the same sentence twice.
|
|
59
|
+
//
|
|
60
|
+
// WHAT IS DELIBERATELY NOT HERE, so nobody adds it back as an oversight:
|
|
61
|
+
//
|
|
62
|
+
// ⌘1..4, for PRIORITY or for anything else. Priority was removed deliberately
|
|
63
|
+
// and that handler no longer fires. The chord then went to a section of the
|
|
64
|
+
// sidebar until 2026-10-02, and that is gone as well: Tab moves along the tabs
|
|
65
|
+
// now and is listed above.
|
|
66
|
+
//
|
|
67
|
+
// Y, N and I on a row in the list. Real since w-9741ed0e0b, but they answer
|
|
68
|
+
// a Codex conversation's import row and are dead on every other row in the
|
|
69
|
+
// app. A reference page that lists a key which does nothing on nineteen
|
|
70
|
+
// screens out of twenty teaches somebody that the app ignores them.
|
|
71
|
+
//
|
|
72
|
+
// ⇥ inside the new task card, which changes the project it goes to. It is
|
|
73
|
+
// gated to that one card, the card draws its projects where she can see
|
|
74
|
+
// them, and it used to ride along inside the C row's sentence, which is the
|
|
75
|
+
// fault the NO KEY HIDES INSIDE A SENTENCE block above is about.
|
|
76
|
+
//
|
|
77
|
+
// ⇧⇥ in the reply box, which cycles Claude Code's permission modes. Real,
|
|
78
|
+
// and it belongs to the reply box rather than to the app; it is also the one
|
|
79
|
+
// key here that cannot be said without the word "permissions", and Settings
|
|
80
|
+
// already has a whole group explaining those.
|
|
81
|
+
//
|
|
82
|
+
// ⌘R, ⌘=, ⌘- and ⌘0. The Mac's own chords, answered in main.mjs so they work
|
|
83
|
+
// over the game. Nobody needs to be taught reload and zoom.
|
|
84
|
+
//
|
|
85
|
+
// EVERY SENTENCE IS PLAIN. A tester does not know what a modal, a pane, a
|
|
86
|
+
// binary or a view is. No row uses a word the app does not print on screen
|
|
87
|
+
// somewhere she can see it. THE SIDEBAR ROW IS WHY THAT NOW MATTERS TWICE: it
|
|
88
|
+
// used to name the sections, "Inbox, Scheduled, In progress, Closed", and
|
|
89
|
+
// that went wrong in both halves. The real order is Inbox, In
|
|
90
|
+
// progress, Scheduled, Closed (`workspaceDestinations`), and Scheduled is
|
|
91
|
+
// drawn only when something is actually scheduled. A reference
|
|
92
|
+
// page that recites a list the sidebar can change goes stale without anybody
|
|
93
|
+
// touching it, so the row names no sections at all.
|
|
94
|
+
|
|
95
|
+
import { NAME } from './product-name.mjs';
|
|
96
|
+
|
|
97
|
+
/** @type {import('./shortcuts.d.mts').ShortcutGroup[]} */
|
|
98
|
+
export const SHORTCUTS = [
|
|
99
|
+
{
|
|
100
|
+
// App.tsx, the `switch (e.key)` at the foot of the key handler: this is the
|
|
101
|
+
// list with nothing open, which is where anybody starts.
|
|
102
|
+
//
|
|
103
|
+
// "IN YOUR INBOX", BECAUSE INBOX IS A WORD THE APP PRINTS AND TASK LIST IS
|
|
104
|
+
// NOT (w-1bc916a880, third round).
|
|
105
|
+
//
|
|
106
|
+
// The mistake is worth naming because it is the same one
|
|
107
|
+
// the round before was meant to fix. "Task list" was reached for to cover
|
|
108
|
+
// the fact that these keys work in all four sidebar sections rather than
|
|
109
|
+
// only the first, which is an accurate thing about the code and a phrase
|
|
110
|
+
// nobody sees on a screen. Inbox is the first row of the
|
|
111
|
+
// sidebar. The heading claims these keys work there; it does not claim they
|
|
112
|
+
// work ONLY there, and one of its rows moves between the sections anyway.
|
|
113
|
+
label: 'In your inbox',
|
|
114
|
+
keys: [
|
|
115
|
+
// case 'j': case 'J': case 'ArrowDown' → selected + 1
|
|
116
|
+
{ keys: ['J', '↓'], join: 'or', what: 'Move down the list.' },
|
|
117
|
+
// case 'k': case 'K': case 'ArrowUp' → selected - 1
|
|
118
|
+
{ keys: ['K', '↑'], join: 'or', what: 'Move up the list.' },
|
|
119
|
+
// case 'ArrowLeft': case 'ArrowRight' → boardSideways (w-23fc91bff5).
|
|
120
|
+
// "Board" is the word the View and filters menu prints.
|
|
121
|
+
{ keys: ['←', '→'], what: 'Move to the next column on the board.' },
|
|
122
|
+
// case 'Enter' → setFocused(pointed)
|
|
123
|
+
{ keys: ['↵'], what: 'Open the task you are on.' },
|
|
124
|
+
// App.tsx, `if (e.key === 'Tab')`: nextTab(stateTabOrder, view, shiftKey)
|
|
125
|
+
// walks the tabs along the top of the inbox, wrapping. It replaced ⌘1 to
|
|
126
|
+
// ⌘4 on 2026-10-02 (w-914b16eab6), which jumped through the sidebar's
|
|
127
|
+
// sections before those became these tabs. Shift-Tab goes back and is
|
|
128
|
+
// left unlisted on her word: "just show the Next tab".
|
|
129
|
+
{ keys: ['⇥'], what: 'Move to the next tab along the top.' },
|
|
130
|
+
// case 'b': case 'B' → setInboxDisplay(flipView(inboxDisplay)), asked
|
|
131
|
+
// for by name (w-58c8f466e7). "List" and "Board" are the words the View
|
|
132
|
+
// and filters menu prints, so the sentence uses those.
|
|
133
|
+
{ keys: ['B'], what: 'Switch between the list and the board.' },
|
|
134
|
+
// Escape in the focused branch: doc → task → list. In the list switch it
|
|
135
|
+
// clears a selection and closes search.
|
|
136
|
+
{ keys: ['esc'], what: 'Go back one step: a file, then the task, then the list.' },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
// App.tsx, the `if (focused)` branch: a task open in front of her. NOT
|
|
141
|
+
// "answering an agent" any more, because ⌘J opens its terminal and that is
|
|
142
|
+
// not an answer; the heading says when the keys work, which is the scheme.
|
|
143
|
+
label: 'In a task you have opened',
|
|
144
|
+
keys: [
|
|
145
|
+
// focused branch: 'r' → setModal('reply'); list switch: 'r' in the inbox
|
|
146
|
+
{ keys: ['R'], what: 'Write back to the agent.' },
|
|
147
|
+
// Compose.tsx onKeyDown: (meta||ctrl) && Enter → send; Focus.tsx the same
|
|
148
|
+
{ keys: ['⌘↵'], what: 'Send what you have typed.' },
|
|
149
|
+
// focused branch: ArrowDown / ArrowUp walk opts
|
|
150
|
+
{ keys: ['↑', '↓'], what: 'Move through the options an agent has offered.' },
|
|
151
|
+
// focused branch: Enter sends optionSel. Its own row rather than a clause
|
|
152
|
+
// at the end of the one above, so somebody scanning for ↵ finds it.
|
|
153
|
+
{ keys: ['↵'], what: 'Send the option you have selected.' },
|
|
154
|
+
// focused branch: /^[1-9]$/ → pickOption(focused, Number(e.key))
|
|
155
|
+
{ keys: ['1', '9'], join: 'to', what: 'Pick an option by its number.' },
|
|
156
|
+
// ⌘J, above the inInput gate in App.tsx: dispatches task-terminal-toggle,
|
|
157
|
+
// and toasts "Open a task to use its terminal" when there is none
|
|
158
|
+
// (w-ef92663cb1). It landed after this list last changed, so it was
|
|
159
|
+
// missing here at first.
|
|
160
|
+
{ keys: ['⌘J'], what: 'Show or hide this task’s terminal.' },
|
|
161
|
+
// threads/Summary.tsx, `useSummaryShortcut`: it listens in the capture
|
|
162
|
+
// phase and stops the key there, so S belongs to the summary on a thread
|
|
163
|
+
// that has one. It is under THIS heading and not the one below because it
|
|
164
|
+
// is the one key in the app that works only inside an open thread.
|
|
165
|
+
{ keys: ['S'], what: 'Show or hide the summary.' },
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
// The one group that cannot be named by WHERE she is, because all four of
|
|
170
|
+
// its keys work both in the list and inside an open task. So it is named by
|
|
171
|
+
// what the keys do, literally and in the order they are drawn.
|
|
172
|
+
label: 'Closing, scheduling and undoing',
|
|
173
|
+
keys: [
|
|
174
|
+
// focused branch: 'e' → markDone. List switch: inbox → markDone,
|
|
175
|
+
// snoozed → unsnooze. It never approves anything.
|
|
176
|
+
{ keys: ['E'], what: 'Close the task and take it out of your inbox.' },
|
|
177
|
+
// focused branch and list switch: 'l' → openSnooze. IT WAS S UNTIL
|
|
178
|
+
// 2026-10-01 AND THAT IS THE WHOLE OF WHY IT MOVED. S had come to mean
|
|
179
|
+
// the summary inside a thread and still meant the schedule picker in the
|
|
180
|
+
// list, so this page taught one letter for two different things and an
|
|
181
|
+
// office manager testing the app met both inside a minute. One meaning
|
|
182
|
+
// per letter: S is the summary, L is later.
|
|
183
|
+
{ keys: ['L'], what: 'Put the task off until a time you pick.' },
|
|
184
|
+
// focused branch and list switch: 'z' → undo
|
|
185
|
+
{ keys: ['Z'], what: 'Undo the last thing you did.' },
|
|
186
|
+
// (meta||ctrl) && 'a' && !inInput && !modal → tick the whole list
|
|
187
|
+
{ keys: ['⌘A'], what: 'Tick every task in the list. Press it again to untick.' },
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
label: 'Anywhere in the app',
|
|
192
|
+
keys: [
|
|
193
|
+
// (meta||ctrl) && 'k' → the palette, above every other branch, and
|
|
194
|
+
// forwarded from inside an open file by main.mjs as well.
|
|
195
|
+
{ keys: ['⌘K', 'Ctrl+K'], join: 'or', what: `The command bar: everything ${NAME} can do, by name.` },
|
|
196
|
+
// 'n' in both branches → setModal('compose'). C does the same and is
|
|
197
|
+
// left off: N is the default (w-fb9051e597).
|
|
198
|
+
// "Threads", not tasks, since w-ec62ab6b38.
|
|
199
|
+
{ keys: ['N'], what: 'Start a new thread.' },
|
|
200
|
+
// '/' above the focused branch → openSearch
|
|
201
|
+
{ keys: ['/'], what: 'Search your threads.' },
|
|
202
|
+
// '\\' above the focused branch → togglePanel
|
|
203
|
+
{ keys: ['\\'], what: 'Show or hide the left sidebar.' },
|
|
204
|
+
// main/main.mjs before-input-event: ⌘Y / ⌘N answer the oldest approval.
|
|
205
|
+
// Its own card prints "⌘Y / ⌘N" beside Allow and Deny, so the words here
|
|
206
|
+
// are the card's words. It lives under this heading rather than under an
|
|
207
|
+
// open task because it is answered from anywhere at all.
|
|
208
|
+
{ keys: ['⌘Y', '⌘N'], join: 'or', what: 'Allow or deny a card asking to run something.' },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
/** Every cap drawn on the page, flattened. The test walks this. */
|
|
214
|
+
export function everyKey(groups = SHORTCUTS) {
|
|
215
|
+
return groups.flatMap((g) => g.keys.flatMap((k) => k.keys));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// WHAT AN AGENT IS TOLD, and it is this page and nothing else, so a key that
|
|
219
|
+
// moves here has moved for every agent on its next run. The closing line is
|
|
220
|
+
// the point: an agent's own saved memory is read before anything we hand it,
|
|
221
|
+
// and on 2026-10-04 one said "press S" three days after S stopped scheduling.
|
|
222
|
+
/** The keys as one block of plain text, for an agent's system prompt. */
|
|
223
|
+
export function keysForAgents(groups = SHORTCUTS) {
|
|
224
|
+
const lines = [
|
|
225
|
+
`The keys in ${NAME}, the app this person reads you in, as they are today.`,
|
|
226
|
+
'When you tell them to press something, use these. Keys have moved before,',
|
|
227
|
+
'so a key you remember from an earlier session, a memory or a note that',
|
|
228
|
+
'disagrees with this list is out of date. Anything without a key is in ⌘K',
|
|
229
|
+
'by name.',
|
|
230
|
+
];
|
|
231
|
+
for (const g of groups) {
|
|
232
|
+
lines.push('', `${g.label}:`);
|
|
233
|
+
for (const k of g.keys) lines.push(`- ${k.keys.join(k.join ? ` ${k.join} ` : ' ')}: ${k.what}`);
|
|
234
|
+
}
|
|
235
|
+
return lines.join('\n');
|
|
236
|
+
}
|
|
@@ -6,3 +6,5 @@
|
|
|
6
6
|
// main and by the tests, which are both plain JS and need nothing here.
|
|
7
7
|
|
|
8
8
|
export function strandedTitle(count: number): string;
|
|
9
|
+
/** The line in a thread where a run stopped, in our words; `raw` is the tool's own refusal. */
|
|
10
|
+
export function runStoppedLine(raw: string): string;
|
package/shared/spawn-trouble.mjs
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// across the top of her inbox. Her three complaints were all correct. There is
|
|
7
7
|
// no OAuth in Agentbox, so the sentence names a thing that does not exist here.
|
|
8
8
|
// It is ugly. And it was on screen while agents were working perfectly well,
|
|
9
|
-
// because it was about one
|
|
9
|
+
// because it was about one Claude account among several and not about the app.
|
|
10
10
|
//
|
|
11
11
|
// So the raw text never reaches the renderer again. It goes in the session's
|
|
12
12
|
// own trace log, which is where a person debugging wants it, and what she is
|
|
13
|
-
// shown is one sentence of ours written in
|
|
13
|
+
// shown is one sentence of ours written in plain words.
|
|
14
14
|
//
|
|
15
15
|
// Every sentence here has to survive the same test: read it out loud to
|
|
16
16
|
// somebody who has never heard of a subprocess, and they know what happened
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// CODEX. It is right about Codex anyway, and that is measured rather than
|
|
25
25
|
// assumed. On 2026-09-05 `codex app-server` (codex-cli 0.148.0) was driven the
|
|
26
26
|
// way main/codex-app-server.mjs drives it, with CODEX_HOME pointed at an empty
|
|
27
|
-
// directory so there was no auth.json and
|
|
27
|
+
// directory so there was no auth.json and the real ~/.codex login was neither
|
|
28
28
|
// read nor written. The turn retried ten times and failed with
|
|
29
29
|
//
|
|
30
30
|
// unexpected status 401 Unauthorized: Missing bearer or basic authentication in
|
|
@@ -41,18 +41,18 @@
|
|
|
41
41
|
|
|
42
42
|
// AN ACCOUNT THAT IS SIGNED IN AND NOT ALLOWED TO RUN.
|
|
43
43
|
//
|
|
44
|
-
//
|
|
45
|
-
// time
|
|
46
|
-
// off. Run by hand
|
|
44
|
+
// It happens. A login can be intact, with the browser sign-in succeeding every
|
|
45
|
+
// time, while the WORKSPACE that account belongs to has Claude Code turned
|
|
46
|
+
// off. Run by hand, this is the whole of what the CLI says:
|
|
47
47
|
//
|
|
48
48
|
// Your organization has disabled Claude subscription access for Claude Code ·
|
|
49
49
|
// Use an Anthropic API key instead, or ask your admin to enable access
|
|
50
50
|
//
|
|
51
51
|
// "subscription access" is in the list below, so every one of those was filed
|
|
52
|
-
// as a login that had run out and
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
52
|
+
// as a login that had run out and the person was told to type /login. Doing
|
|
53
|
+
// it again and again changes nothing, because no login can change an admin's
|
|
54
|
+
// policy, and every dead run on the row carried "Sign that account back in
|
|
55
|
+
// and it picks straight up."
|
|
56
56
|
//
|
|
57
57
|
// TESTED BEFORE SIGNED_OUT, which is the whole of the fix: these lines say
|
|
58
58
|
// "subscription" and "access" and mean something else entirely.
|
|
@@ -188,9 +188,8 @@ export function troubleCause(raw) {
|
|
|
188
188
|
//
|
|
189
189
|
// carries the one thing she actually wants to know, and we were throwing it
|
|
190
190
|
// away and then telling her the vaguer "when the limit resets". The timezone
|
|
191
|
-
// is dropped rather than shown:
|
|
192
|
-
//
|
|
193
|
-
// add a word she has to read. Null when there is no time to quote, and then
|
|
191
|
+
// is dropped rather than shown: the limit line prints the zone the Mac is
|
|
192
|
+
// already set to, so naming it would only add a word she has to read. Null when there is no time to quote, and then
|
|
194
193
|
// the sentence falls back to the wording that has always been there.
|
|
195
194
|
export function limitResetsAt(raw) {
|
|
196
195
|
// Both wordings are real: the CLI's own line is "resets 6pm" and the API's
|
|
@@ -202,14 +201,14 @@ export function limitResetsAt(raw) {
|
|
|
202
201
|
const MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
|
203
202
|
|
|
204
203
|
// THE SAME HOUR AS A MOMENT, so the fleet can wait exactly until it
|
|
205
|
-
// (
|
|
204
|
+
// (a limit that reset at, say, 6:50pm could hold every agent until 7:03pm,
|
|
206
205
|
// because the app waited a fixed thirty minutes instead of reading this line).
|
|
207
206
|
// The next local occurrence of that clock time at or after `from`; null when
|
|
208
207
|
// the line names no hour.
|
|
209
208
|
export function limitResetMoment(raw, from = Date.now()) {
|
|
210
209
|
// CODEX NAMES A DAY, NOT AN HOUR: "try again at Sep 28th, 2026 1:05 PM"
|
|
211
210
|
// (codex-cli 0.153.4, 2026-09-24). A weekly limit read as "thirty minutes"
|
|
212
|
-
// put the capped login back in the pool every half hour for
|
|
211
|
+
// put the capped login back in the pool every half hour for days.
|
|
213
212
|
const dated = /try again at ([a-z]{3,9})\.? (\d{1,2})(?:st|nd|rd|th)?,? (\d{4}),? (\d{1,2}):(\d{2})\s*(am|pm)/i.exec(String(raw ?? ''));
|
|
214
213
|
if (dated) {
|
|
215
214
|
const month = MONTHS.indexOf(dated[1].slice(0, 3).toLowerCase());
|
|
@@ -250,7 +249,7 @@ export function troubleSentence(cause) {
|
|
|
250
249
|
if (cause === 'at-limit') return 'No agents can start. Your Claude plan is at its limit.';
|
|
251
250
|
if (cause === 'workspace') return 'No agents can start. Claude will not run in this folder.';
|
|
252
251
|
if (cause === 'interrupted') return 'No agents can start. The connection to Claude keeps dropping.';
|
|
253
|
-
return 'No agents can start on this
|
|
252
|
+
return 'No agents can start on this computer right now.';
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
// THE SECOND LINE: what happens next, so the first line is never a dead end.
|
|
@@ -343,8 +342,8 @@ export function engineTroubleNote(cause, engineWord = 'Codex') {
|
|
|
343
342
|
// The count is the point, and it is why this is a different sentence from
|
|
344
343
|
// `troubleSentence` above rather than a rewording of it. That one is about the
|
|
345
344
|
// FLEET, and it can only ever be true when every account on the Mac is down at
|
|
346
|
-
// once. That is why
|
|
347
|
-
// signed-out login
|
|
345
|
+
// once. That is why the screen could stay quiet: Codex tasks and tasks on a
|
|
346
|
+
// signed-out login could sit there all day while the other agents worked, so the
|
|
348
347
|
// fleet was never down and the one line the app owns was structurally unable to
|
|
349
348
|
// speak. The subject here is her TASKS, which is the thing she went looking for
|
|
350
349
|
// and the thing that was actually stuck.
|
|
@@ -443,6 +442,28 @@ const DEAD_RUN_OPENERS = {
|
|
|
443
442
|
unknown: 'stopped before it did anything, so this task has not been started.',
|
|
444
443
|
};
|
|
445
444
|
|
|
445
|
+
/**
|
|
446
|
+
* THE LINE IN A THREAD WHERE A RUN STOPPED, in our words.
|
|
447
|
+
*
|
|
448
|
+
* The CLI types its own refusal into the run as if the agent had said it
|
|
449
|
+
* ("Failed to authenticate: OAuth session expired..."), and the thread drew it
|
|
450
|
+
* as the agent's message. This is about one run, not the task: a run that
|
|
451
|
+
* stops mid-thread has usually done plenty before it, so it says nothing about
|
|
452
|
+
* what has or has not been done.
|
|
453
|
+
*/
|
|
454
|
+
const RUN_STOPPED = {
|
|
455
|
+
'signed-out': 'This run stopped because the account was signed out.',
|
|
456
|
+
'org-blocked': 'This run stopped because that account is switched off by its organization.',
|
|
457
|
+
'at-limit': 'This run stopped because the account hit its usage limit.',
|
|
458
|
+
workspace: "This run stopped because it would not start in this project's folder.",
|
|
459
|
+
interrupted: 'This run was cut off in the middle.',
|
|
460
|
+
unknown: 'This run stopped before it finished.',
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
export function runStoppedLine(raw) {
|
|
464
|
+
return RUN_STOPPED[troubleCause(raw)] ?? RUN_STOPPED.unknown;
|
|
465
|
+
}
|
|
466
|
+
|
|
446
467
|
export function deadRunSentence({ engineWord = 'The agent', cause = 'unknown', runs = 1, resetsAt = null } = {}) {
|
|
447
468
|
const opener = `${engineWord} ${DEAD_RUN_OPENERS[cause] ?? DEAD_RUN_OPENERS.unknown}`;
|
|
448
469
|
|
|
@@ -468,7 +489,7 @@ export function deadRunSentence({ engineWord = 'The agent', cause = 'unknown', r
|
|
|
468
489
|
// WHAT GOES ON HER ROW WHEN THE RUN WAS FINE AND HER ANSWER STILL WENT NOWHERE.
|
|
469
490
|
//
|
|
470
491
|
// `settleDelivery` is two independent tests joined with an AND: did the process
|
|
471
|
-
// exit cleanly, and did an agent leave a word on the row since
|
|
492
|
+
// exit cleanly, and did an agent leave a word on the row since the user wrote. A run
|
|
472
493
|
// can pass the first and fail the second, and several ordinary ones do -- a
|
|
473
494
|
// Codex turn that talks in its thread and never calls a store tool, a worker
|
|
474
495
|
// that claims a row and finishes without writing, any session on an install
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Types for shared/team-rules.mjs, read by the renderer.
|
|
2
|
+
type Row = { createdBy?: string | null; assignee?: string; runner?: string; people?: string[]; status?: string };
|
|
3
|
+
type ProductLike = { team?: { projectId?: string; sharedBy?: string | null } | null } | null | undefined;
|
|
4
|
+
|
|
5
|
+
export function isShared(product: ProductLike): boolean;
|
|
6
|
+
export function runnerOf(item: Row, product: ProductLike): string | null;
|
|
7
|
+
export function heldByAPerson(item: Row): boolean;
|
|
8
|
+
export function mayRunHere(item: Row, product: ProductLike, me: string | null): boolean;
|
|
9
|
+
export function inMyInbox(item: Row, product: ProductLike, me: string | null): boolean;
|
|
10
|
+
export function lastSpeaker(item: Row): string | null;
|
|
11
|
+
export function iSpokeLast(item: Row, product: ProductLike, me: string | null): boolean;
|
|
12
|
+
export function handedOnByReply(item: Row, product: ProductLike, me: string | null): string | null;
|
|
13
|
+
/** One thread you are in where somebody spoke after you (w-920461cbe6). */
|
|
14
|
+
export interface ThreadWaiting { uid: string; mine: boolean; fresh: number; people: string[]; text: string; last: string; lastBy: string }
|
|
15
|
+
/** What in a conversation waits on you; null on a row from before threads. */
|
|
16
|
+
export interface Waits { chat: number; threads: ThreadWaiting[] }
|
|
17
|
+
export function whatWaits(item: Row & { talk?: unknown }, me: string | null): Waits | null;
|