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,159 @@
|
|
|
1
|
+
// WHAT A CODEX WORKER MAY DO, SAID ONCE, IN CODEX'S OWN TERMS.
|
|
2
|
+
//
|
|
3
|
+
// The sister of renderer/src/modes.ts, which does this for Claude Code. That
|
|
4
|
+
// file's rule holds here too: every value below is a value Codex really takes,
|
|
5
|
+
// and nothing is invented to make a tidier row.
|
|
6
|
+
//
|
|
7
|
+
// WHY THERE ARE THREE AND NOT SIX. Claude Code has six permission modes and we
|
|
8
|
+
// print all six because they are its own list. Codex does not have six of
|
|
9
|
+
// anything. It has a SANDBOX with three settings and an APPROVAL POLICY that
|
|
10
|
+
// sits on top of it, and the two are not independent in any way a person would
|
|
11
|
+
// want to choose between: sixteen combinations, of which three are the ones
|
|
12
|
+
// anybody means. Read off `codex app-server generate-json-schema` on
|
|
13
|
+
// codex-cli 0.153.4, 2026-09-23:
|
|
14
|
+
//
|
|
15
|
+
// SandboxMode read-only | workspace-write | danger-full-access
|
|
16
|
+
// AskForApproval untrusted | on-request | never | {granular:{...}}
|
|
17
|
+
//
|
|
18
|
+
// So each row here is a PAIR, and the pair is the thing she picks.
|
|
19
|
+
//
|
|
20
|
+
// `untrusted` IS DELIBERATELY NOT OFFERED. It is the one Agentbox used to run
|
|
21
|
+
// and it is why Codex was unusable here: it cards every command before it runs,
|
|
22
|
+
// and it adds no containment the sandbox was not already providing. The
|
|
23
|
+
// measurements are in main/codex-session.mjs. Leaving it off the list is not
|
|
24
|
+
// hiding a choice, it is declining to offer a broken one. `granular` is not
|
|
25
|
+
// offered either: it is an object of five switches, which is a settings screen
|
|
26
|
+
// of its own and not a mode.
|
|
27
|
+
//
|
|
28
|
+
// THE LABELS ARE OURS, unlike the Claude Code file's, because Codex's own
|
|
29
|
+
// surfaces disagree with each other about what to call these. Its CLI says
|
|
30
|
+
// `--sandbox read-only`; its TUI says "Read Only"; its docs say "Auto". Since
|
|
31
|
+
// there is no single authority to copy, these are written for the person
|
|
32
|
+
// reading them, in the words the rest of this app uses.
|
|
33
|
+
|
|
34
|
+
/** Her three, in order of what they let an agent do. */
|
|
35
|
+
export const CODEX_MODE_ORDER = ['read-only', 'auto', 'full-access'];
|
|
36
|
+
|
|
37
|
+
/** The one a worker runs in when nothing has been picked. */
|
|
38
|
+
export const CODEX_DEFAULT_MODE = 'auto';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* THE PAIR EACH MODE REALLY IS. `sandbox` and `approvalPolicy` go straight into
|
|
42
|
+
* `thread/start`, so this table is the whole of the mapping and there is no
|
|
43
|
+
* second copy of it anywhere.
|
|
44
|
+
*
|
|
45
|
+
* `approvalPolicy` is `on-request` on the two sandboxed rows: the sandbox does
|
|
46
|
+
* the refusing, and `on-request` leaves the agent able to ASK to step outside
|
|
47
|
+
* rather than simply failing. On `full-access` there is nothing left to ask
|
|
48
|
+
* about, so it is `never`.
|
|
49
|
+
*/
|
|
50
|
+
export const CODEX_MODES = {
|
|
51
|
+
'read-only': {
|
|
52
|
+
label: 'Read only',
|
|
53
|
+
sandbox: 'read-only',
|
|
54
|
+
approvalPolicy: 'on-request',
|
|
55
|
+
// What runs without asking, in the same voice as the Claude Code list.
|
|
56
|
+
what: 'Reads and searches. Writes nothing and runs nothing that changes anything.',
|
|
57
|
+
},
|
|
58
|
+
auto: {
|
|
59
|
+
label: 'Auto',
|
|
60
|
+
sandbox: 'workspace-write',
|
|
61
|
+
approvalPolicy: 'on-request',
|
|
62
|
+
// WHO ANSWERS THOSE ASKS, SAID HERE RATHER THAN LEFT TO A FILE. This used
|
|
63
|
+
// to come only from `approvals_reviewer` in her `~/.codex/config.toml`, so
|
|
64
|
+
// a second login's home, which starts with no config at all, sent every ask
|
|
65
|
+
// to her inbox instead (2026-09-24, w-ca48e69535). One mode, one behaviour,
|
|
66
|
+
// whichever account the run is on.
|
|
67
|
+
approvalsReviewer: 'auto_review',
|
|
68
|
+
what: 'Works inside the project folder. Asks before going outside it or onto the network.',
|
|
69
|
+
},
|
|
70
|
+
'full-access': {
|
|
71
|
+
label: 'Full access',
|
|
72
|
+
sandbox: 'danger-full-access',
|
|
73
|
+
approvalPolicy: 'never',
|
|
74
|
+
what: 'Anything, anywhere on this Mac, without asking.',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Whether a word is one of the three. */
|
|
79
|
+
export function isCodexMode(word) {
|
|
80
|
+
return typeof word === 'string' && Object.hasOwn(CODEX_MODES, word);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The pair for a mode, or the default's pair when the word is not one of ours.
|
|
85
|
+
* FALLING BACK RATHER THAN THROWING IS DELIBERATE at this level: a stale word
|
|
86
|
+
* in her config should start a worker in the safe-ish default, not refuse to
|
|
87
|
+
* start one. The writers (main/settings.mjs) validate on the way IN, which is
|
|
88
|
+
* where a typo can still be reported to the person who made it.
|
|
89
|
+
*/
|
|
90
|
+
export function codexPosture(mode) {
|
|
91
|
+
const row = CODEX_MODES[mode] ?? CODEX_MODES[CODEX_DEFAULT_MODE];
|
|
92
|
+
const posture = { sandbox: row.sandbox, approvalPolicy: row.approvalPolicy };
|
|
93
|
+
if (row.approvalsReviewer) posture.approvalsReviewer = row.approvalsReviewer;
|
|
94
|
+
return posture;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The label for a mode, for any surface that has to name one. */
|
|
98
|
+
export function codexModeWords(mode) {
|
|
99
|
+
return (CODEX_MODES[mode] ?? CODEX_MODES[CODEX_DEFAULT_MODE]).label;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* THE WORD SHE TYPES AFTER THE SLASH, one per mode.
|
|
104
|
+
*
|
|
105
|
+
* Deliberately NOT the same words as Claude Code's six. `/auto` exists on both
|
|
106
|
+
* engines, which is fine and is the one overlap: a row has one engine, so the
|
|
107
|
+
* menu she is looking at only ever offers one of the two, and both mean "the
|
|
108
|
+
* sensible default" on their own engine.
|
|
109
|
+
*/
|
|
110
|
+
export const CODEX_MODE_COMMAND = {
|
|
111
|
+
'read-only': '/read-only',
|
|
112
|
+
auto: '/auto',
|
|
113
|
+
'full-access': '/full-access',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** One sentence per row of the menu, in the same voice as the Claude Code list. */
|
|
117
|
+
export const CODEX_MODE_HINT = {
|
|
118
|
+
'read-only': 'reads and searches, writes nothing',
|
|
119
|
+
auto: 'works in the project folder, asks to leave it',
|
|
120
|
+
'full-access': 'anything, anywhere, without asking',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The rows the menu draws for what has been typed. Mirrors `menuRowsFor` on the
|
|
125
|
+
* Claude Code side, including the way back when a mode is already set.
|
|
126
|
+
*/
|
|
127
|
+
export function codexMenuRowsFor(query, set) {
|
|
128
|
+
if (query === null) return [];
|
|
129
|
+
const rows = CODEX_MODE_ORDER.filter((id) => CODEX_MODE_COMMAND[id].slice(1).startsWith(query.toLowerCase()));
|
|
130
|
+
// `/clear` puts it back to whatever the project is already set to, and is
|
|
131
|
+
// only offered when there is something to clear.
|
|
132
|
+
const back = set && 'clear'.startsWith(query.toLowerCase()) ? [null] : [];
|
|
133
|
+
return [...rows, ...back];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* WHAT THE CHIP AND THE TOAST SAY while a mode is on. Same shape as Claude
|
|
138
|
+
* Code's `MODE_STATUS`, which is that engine's own status line with " on"
|
|
139
|
+
* after it. Codex prints no such line, so these are ours, kept in the same
|
|
140
|
+
* rhythm so the two engines read alike on one screen.
|
|
141
|
+
*/
|
|
142
|
+
export const CODEX_MODE_STATUS = {
|
|
143
|
+
'read-only': 'read only on',
|
|
144
|
+
auto: 'auto on',
|
|
145
|
+
'full-access': 'full access on',
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** The next one round the loop, for Shift+Tab. */
|
|
149
|
+
export function nextCodexMode(mode) {
|
|
150
|
+
const at = CODEX_MODE_ORDER.indexOf(mode);
|
|
151
|
+
return CODEX_MODE_ORDER[(at + 1) % CODEX_MODE_ORDER.length];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The mode a typed `/word ` means, or null when it means nothing. */
|
|
155
|
+
export function exactCodexMode(word) {
|
|
156
|
+
if (!word) return null;
|
|
157
|
+
const hit = CODEX_MODE_ORDER.find((id) => CODEX_MODE_COMMAND[id] === `/${word}`);
|
|
158
|
+
return hit ?? null;
|
|
159
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// WHAT IS LEFT OF HER LIMIT, OUT OF `codex app-server`'S OWN RATE LIMITS.
|
|
2
|
+
//
|
|
3
|
+
// The pure half, and the mirror of shared/claude-usage.mjs: the payload in, the
|
|
4
|
+
// limits out. Everything after that -- the countdown, the rows, the sentence --
|
|
5
|
+
// is shared/usage.mjs, because a limit is a limit whichever agent reported it.
|
|
6
|
+
// Asking for the payload is main/codex-usage.mjs.
|
|
7
|
+
//
|
|
8
|
+
// NOTHING IS SPAWNED TO DRAW THIS AND NOTHING IS ASKED FOR IT, which is the
|
|
9
|
+
// whole reason this reader looks nothing like the other one. `claude -p /usage`
|
|
10
|
+
// costs a child process and twenty-five seconds, which is what shaped every line
|
|
11
|
+
// of main/claude-usage.mjs. Codex's figures ride the app-server the fleet is
|
|
12
|
+
// ALREADY running and arrive unprompted as `account/rateLimits/updated` during a
|
|
13
|
+
// turn. That push is the only source: the explicit read that once sat beside it
|
|
14
|
+
// turned out to be a request to OpenAI's backend rather than a local lookup, and
|
|
15
|
+
// main/codex-usage.mjs holds the measurement and the reason it is deliberately
|
|
16
|
+
// gone. So there is no 25-second cost here to design around, and there is no
|
|
17
|
+
// reading at all until a turn reports one -- which is honest, and is better than
|
|
18
|
+
// paying for a picture.
|
|
19
|
+
//
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// MEASURED ON THIS MAC, 2026-09-05, codex-cli 0.148.0, by running a real
|
|
22
|
+
// app-server against a scratch CODEX_HOME and asking it once BY HAND, which is
|
|
23
|
+
// the only thing that has ever asked -- Agentbox does not, per the paragraph
|
|
24
|
+
// above. Verbatim, at now(s) = 1788565827:
|
|
25
|
+
//
|
|
26
|
+
// "rateLimits": { "limitId": "codex", "limitName": null,
|
|
27
|
+
// "primary": { "usedPercent": 33, "windowDurationMins": 300, "resetsAt": 1788573509 },
|
|
28
|
+
// "secondary": { "usedPercent": 63, "windowDurationMins": 10080, "resetsAt": 1788798916 } }
|
|
29
|
+
//
|
|
30
|
+
// PRIMARY IS THE FIVE HOUR WINDOW AND SECONDARY IS THE WEEK, and that is read
|
|
31
|
+
// off `windowDurationMins` rather than assumed: 300 minutes is five hours and
|
|
32
|
+
// 10080 is seven days. So they take her own names for the same two spans --
|
|
33
|
+
// "This session" and "This week" -- and no new vocabulary is invented for a
|
|
34
|
+
// fact she already has a word for.
|
|
35
|
+
//
|
|
36
|
+
// `resetsAt` IS UNIX SECONDS. Two proofs off that one capture, because a unit
|
|
37
|
+
// read wrong by a thousand is the confident wrong number this whole surface
|
|
38
|
+
// exists to avoid. The `base_model_inference` bucket on the same response read
|
|
39
|
+
// `{"usedPercent":0,"windowDurationMins":10080,"resetsAt":1789170627}`, and
|
|
40
|
+
// 1789170627 - 1788565827 is 604800 exactly, which is those 10080 minutes to the
|
|
41
|
+
// second. And the schema's neighbouring `RateLimitResetCredit.expiresAt` is
|
|
42
|
+
// documented "Unix timestamp in seconds". It is multiplied by 1000 once, below,
|
|
43
|
+
// and nowhere else.
|
|
44
|
+
//
|
|
45
|
+
// A WINDOW THAT IS NOT THERE IS ORDINARY, NOT AN ERROR. Off
|
|
46
|
+
// `codex app-server generate-json-schema` on the same machine: in
|
|
47
|
+
// `RateLimitSnapshot` both `primary` and `secondary` are
|
|
48
|
+
// `anyOf [RateLimitWindow, null]` and the object has NO required field at all;
|
|
49
|
+
// `usedPercent` is the one required field of a window, and `resetsAt` and
|
|
50
|
+
// `windowDurationMins` are both nullable. The live read proved it too --
|
|
51
|
+
// `base_model_inference` came back with `"secondary": null`.
|
|
52
|
+
//
|
|
53
|
+
// SO AN ABSENT WINDOW IS ABSENT AND NEVER A ZERO. That is the dashboard law:
|
|
54
|
+
// absence of data and a real zero are different facts and must never look the
|
|
55
|
+
// same. A window that really reported 0% keeps its row and its empty bar; one
|
|
56
|
+
// that is not there has no row.
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// AND `primary` IS NOT ALWAYS THE FIVE HOUR WINDOW. The block above read one
|
|
60
|
+
// scratch account on 2026-09-05 and took the pairing it happened to see as the
|
|
61
|
+
// shape. Measured again on HER OWN account, 2026-09-18, across the 1,140
|
|
62
|
+
// rate-limit records in her twenty-five most recent Codex sessions: EVERY ONE of
|
|
63
|
+
// them had `secondary: null` and a `primary` whose window was 10080 minutes. Not
|
|
64
|
+
// one five hour window exists on her plan.
|
|
65
|
+
//
|
|
66
|
+
// So the old table would have drawn her SEVEN DAY limit under the words "This
|
|
67
|
+
// session", which is a confident wrong number about her own subscription, and
|
|
68
|
+
// the corner would have counted a week down as though it were an afternoon.
|
|
69
|
+
//
|
|
70
|
+
// THE WINDOW'S OWN LENGTH DECIDES WHAT IT IS CALLED, and the slot it arrived in
|
|
71
|
+
// decides nothing. 300 minutes is her session, 10080 is her week, and an account
|
|
72
|
+
// reporting only one of them gets only that one row. The slot order is still the
|
|
73
|
+
// tie-breaker when a payload names no duration at all, because that is the one
|
|
74
|
+
// case where there is nothing better to go on.
|
|
75
|
+
//
|
|
76
|
+
// TWO SPELLINGS REACH HERE, and both are real. The app-server's push is
|
|
77
|
+
// camelCase (`usedPercent`, `windowDurationMins`, `resetsAt`); the session log on
|
|
78
|
+
// disk, which main/codex-usage-file.mjs reads when no push has arrived, is
|
|
79
|
+
// snake_case (`used_percent`, `window_minutes`, `resets_at`). They are the same
|
|
80
|
+
// numbers from the same service and neither is converted upstream, so both are
|
|
81
|
+
// accepted right here rather than in two places that could drift apart.
|
|
82
|
+
|
|
83
|
+
const plain = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The order the panel draws them in, and the fallback when nothing says how
|
|
87
|
+
* long a window is. */
|
|
88
|
+
const WINDOWS = ['primary', 'secondary'];
|
|
89
|
+
|
|
90
|
+
const FALLBACK = { primary: ['session', 'This session'], secondary: ['week', 'This week'] };
|
|
91
|
+
|
|
92
|
+
/** Either spelling of the same field, because two sources feed this. */
|
|
93
|
+
const field = (window, camel, snake) => {
|
|
94
|
+
const value = window?.[camel] ?? window?.[snake];
|
|
95
|
+
return Number.isFinite(value) ? value : null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* WHAT TO CALL A WINDOW OF THIS MANY MINUTES, in the words she already uses.
|
|
100
|
+
*
|
|
101
|
+
* Her two names came off and no new vocabulary is invented for a span she has a
|
|
102
|
+
* word for. Anything that is neither of those two is named after its own length
|
|
103
|
+
* rather than guessed at, because a limit drawn under the wrong span is worse
|
|
104
|
+
* than one drawn under a plain one.
|
|
105
|
+
*/
|
|
106
|
+
export function windowSpan(minutes) {
|
|
107
|
+
if (!Number.isFinite(minutes) || minutes <= 0) return null;
|
|
108
|
+
if (minutes <= 60 * 12) return ['session', 'This session'];
|
|
109
|
+
if (minutes >= 60 * 24 * 6 && minutes <= 60 * 24 * 8) return ['week', 'This week'];
|
|
110
|
+
if (minutes >= 60 * 24 * 27 && minutes <= 60 * 24 * 32) return ['month', 'This month'];
|
|
111
|
+
const days = Math.round(minutes / (60 * 24));
|
|
112
|
+
if (days >= 2) return ['window', `These ${days} days`];
|
|
113
|
+
const hours = Math.round(minutes / 60);
|
|
114
|
+
return ['window', `These ${hours} hours`];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* EVERY WINDOW THE SNAPSHOT REALLY CARRIES, in the shape shared/usage.mjs draws.
|
|
119
|
+
*
|
|
120
|
+
* `qualifier` is null and stays null: it is Claude Code's own word for which
|
|
121
|
+
* model a weekly line is about, and nothing in this payload is about a model.
|
|
122
|
+
* `resetsText`, `resetsOn` and `zone` are null for the same kind of reason --
|
|
123
|
+
* Codex prints no clock time and names no timezone, it hands over the instant
|
|
124
|
+
* itself, so the panel counts down to it rather than re-deriving a wall time we
|
|
125
|
+
* were never given.
|
|
126
|
+
*/
|
|
127
|
+
export function codexLimits(snapshot) {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const key of WINDOWS) {
|
|
130
|
+
const window = snapshot?.[key];
|
|
131
|
+
if (!plain(window)) continue;
|
|
132
|
+
const percent = field(window, 'usedPercent', 'used_percent');
|
|
133
|
+
if (percent === null) continue;
|
|
134
|
+
const minutes = field(window, 'windowDurationMins', 'window_minutes');
|
|
135
|
+
const [span, name] = windowSpan(minutes) ?? FALLBACK[key];
|
|
136
|
+
const resets = field(window, 'resetsAt', 'resets_at');
|
|
137
|
+
out.push({
|
|
138
|
+
span,
|
|
139
|
+
qualifier: null,
|
|
140
|
+
name,
|
|
141
|
+
percent: Math.max(0, Math.min(100, Math.round(percent))),
|
|
142
|
+
resetsText: null,
|
|
143
|
+
resetsOn: null,
|
|
144
|
+
resetsAt: resets === null ? null : resets * 1000,
|
|
145
|
+
zone: null,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* FOLD A ROLLING UPDATE INTO WHAT WE ALREADY HAD, WHICH IS THE PROTOCOL'S OWN
|
|
153
|
+
* INSTRUCTION AND NOT A KINDNESS.
|
|
154
|
+
*
|
|
155
|
+
* A CLIENT THAT REPLACED WOULD EMPTY THE PANEL AT RANDOM. Every notification
|
|
156
|
+
* carrying only the five hour window would take the week away, and one carrying
|
|
157
|
+
* neither would take both -- and this repo already puts exactly that on the wire
|
|
158
|
+
* (`{ rateLimits: {} }`, in
|
|
159
|
+
* tests/one-codex-process-carries-many-threads-and-a-card-nobody-answers-denies.test.mjs).
|
|
160
|
+
* The failure would look like the corner going quiet for no reason, which is the
|
|
161
|
+
* complaint this whole surface started from.
|
|
162
|
+
*
|
|
163
|
+
* AND FOLDING IS THE WHOLE OF IT, because the push is the whole of the input.
|
|
164
|
+
* `previous` is null exactly once, on the first update ever seen: there is no
|
|
165
|
+
* read response to start from, deliberately (main/codex-usage.mjs). The schema's
|
|
166
|
+
* other instruction -- "or refetch that snapshot" -- is the branch this app does
|
|
167
|
+
* not take, and the reason is written down there rather than repeated here.
|
|
168
|
+
*
|
|
169
|
+
* Only the two windows are carried. Nothing else in the payload is drawn, and a
|
|
170
|
+
* merge that kept fields nobody reads would be inviting somebody to read them.
|
|
171
|
+
*/
|
|
172
|
+
export function mergeRateLimits(previous, update) {
|
|
173
|
+
const out = {};
|
|
174
|
+
for (const key of WINDOWS) {
|
|
175
|
+
if (plain(update?.[key])) out[key] = update[key];
|
|
176
|
+
else if (plain(previous?.[key])) out[key] = previous[key];
|
|
177
|
+
else out[key] = null;
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|