agentbox-app 0.1.12 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/bin/agentbox.mjs +23 -1
- package/briefs/message-rules.md +36 -8
- package/briefs/worker.md +46 -23
- package/main/account-discovery.mjs +122 -30
- package/main/account-tooling.mjs +29 -1
- package/main/account-usage.mjs +46 -0
- package/main/acp.mjs +296 -0
- package/main/agent-homes.mjs +158 -0
- package/main/agent-sessions.mjs +311 -260
- package/main/agent-updates.mjs +9 -22
- package/main/agents.mjs +179 -466
- package/main/analytics.mjs +42 -2
- package/main/answer-modes.mjs +103 -37
- package/main/approval-prompt-core.mjs +197 -0
- package/main/approval-prompt-server.mjs +4 -138
- package/main/approvals.mjs +101 -297
- package/main/artifact-path.mjs +47 -18
- package/main/claude-bin.mjs +45 -311
- package/main/claude-input.mjs +57 -7
- package/main/claude-launch-env.mjs +54 -0
- package/main/claude-models.mjs +39 -7
- package/main/claude-plan.mjs +64 -16
- package/main/claude-session-open.mjs +28 -0
- package/main/claude-usage-process.mjs +40 -0
- package/main/claude-usage.mjs +94 -59
- package/main/cloud/account.mjs +179 -0
- package/main/cloud/aws.mjs +127 -0
- package/main/cloud/browser-return.mjs +58 -0
- package/main/cloud/clients.mjs +15 -0
- package/main/cloud/codex-cloud.mjs +53 -0
- package/main/cloud/config.mjs +107 -0
- package/main/cloud/docs.mjs +219 -0
- package/main/cloud/exec.mjs +27 -0
- package/main/cloud/host.mjs +165 -0
- package/main/cloud/kit.mjs +80 -0
- package/main/cloud/logins.mjs +543 -0
- package/main/cloud/managed.mjs +115 -0
- package/main/cloud/marker.mjs +104 -0
- package/main/cloud/org.mjs +53 -0
- package/main/cloud/reuse.mjs +208 -0
- package/main/cloud/run-cli.mjs +177 -0
- package/main/cloud/run.mjs +1934 -0
- package/main/cloud/usage.mjs +76 -0
- package/main/cloud-permission.mjs +51 -0
- package/main/codex-account.mjs +78 -0
- package/main/codex-app-server.mjs +276 -409
- package/main/codex-approvals.mjs +148 -433
- package/main/codex-bin.mjs +51 -249
- package/main/codex-models.mjs +20 -2
- package/main/codex-session.mjs +246 -506
- package/main/codex-threads-worker.mjs +12 -0
- package/main/codex-threads.mjs +112 -11
- package/main/codex-usage-file.mjs +5 -3
- package/main/codex-usage.mjs +56 -7
- package/main/codex-watch.mjs +83 -5
- package/main/codex.mjs +95 -363
- package/main/config.mjs +37 -5
- package/main/corner-tag.mjs +810 -0
- package/main/crash-report.mjs +26 -3
- package/main/dev-window.mjs +1 -1
- package/main/engine-bin.mjs +545 -0
- package/main/engine-setup.mjs +25 -24
- package/main/feedback.mjs +14 -31
- package/main/folder-pictures.mjs +2 -2
- package/main/freeze-recorder-thread.mjs +86 -0
- package/main/freeze-recorder.mjs +146 -0
- package/main/fresh-user.mjs +20 -9
- package/main/git-change-offthread.mjs +17 -3
- package/main/git-change-thread.mjs +5 -2
- package/main/harnesses/claude-stream.mjs +208 -0
- package/main/harnesses/claude.mjs +105 -0
- package/main/harnesses/codex.mjs +72 -0
- package/main/harnesses/common.mjs +78 -0
- package/main/harnesses/instructions-file.mjs +116 -0
- package/main/harnesses/opencode.mjs +234 -0
- package/main/harnesses.mjs +65 -0
- package/main/importable-threads.mjs +96 -0
- package/main/ipc.mjs +520 -135
- package/main/leftovers.mjs +117 -25
- package/main/liquid-glass.mjs +26 -0
- package/main/live-replies.mjs +10 -4
- package/main/local-app-frames.mjs +82 -0
- package/main/main-went-red.mjs +330 -0
- package/main/main.mjs +248 -37
- package/main/memory-gate-history.mjs +85 -20
- package/main/memory-gate-server.mjs +129 -11
- package/main/memory-gate.mjs +62 -8
- package/main/memory-log.mjs +124 -0
- package/main/menu.mjs +4 -2
- package/main/message-priority.mjs +2 -2
- package/main/mic.mjs +38 -0
- package/main/native/liquid-glass/build/liquid-glass.node +0 -0
- package/main/new-chats-watch.mjs +110 -0
- package/main/new-user-window.mjs +25 -0
- package/main/notify.mjs +64 -5
- package/main/opencode-server.mjs +271 -0
- package/main/opencode.mjs +287 -0
- package/main/phone-alerts.mjs +117 -0
- package/main/phone-inbox.mjs +625 -0
- package/main/phone-link.mjs +571 -0
- package/main/phone-uploads.mjs +476 -0
- package/main/picture-shape.mjs +114 -0
- package/main/quit-sequence.mjs +143 -0
- package/main/refresh-agent-models.mjs +15 -2
- package/main/relay-address.mjs +28 -0
- package/main/relay-client.mjs +314 -0
- package/main/repeats.mjs +10 -4
- package/main/row-label.mjs +18 -47
- package/main/run-change.mjs +13 -0
- package/main/run-ledger.mjs +135 -0
- package/main/runs-on.mjs +32 -35
- package/main/save-attachments.mjs +41 -0
- package/main/serve.mjs +155 -9
- package/main/settings.mjs +364 -50
- package/main/shell-path.mjs +234 -0
- package/main/ship-conflict-brief.mjs +119 -0
- package/main/ship-queue.mjs +134 -20
- package/main/sign-in-files.mjs +3 -7
- package/main/source-updater.mjs +45 -11
- package/main/stage-attachment.mjs +68 -0
- package/main/stall-watch.mjs +50 -0
- package/main/start-hold.mjs +73 -0
- package/main/store/home.mjs +87 -232
- package/main/store/paths.mjs +11 -0
- package/main/store/teammates-files.mjs +99 -0
- package/main/store/work-items.mjs +231 -13
- package/main/store.mjs +227 -53
- package/main/supervisor.mjs +3865 -699
- package/main/task-commands.mjs +13 -17
- package/main/task-folders-offthread.mjs +6 -1
- package/main/task-folders.mjs +897 -105
- package/main/task-terminals.mjs +147 -41
- package/main/team/index.mjs +114 -10
- package/main/team/memory-cloud.mjs +183 -1
- package/main/team/room-hours.mjs +48 -0
- package/main/team/room-setup.mjs +31 -0
- package/main/team/rooms-memory.mjs +454 -0
- package/main/team/rooms-shape.mjs +42 -0
- package/main/team/rooms-supabase.mjs +324 -0
- package/main/team/rooms.mjs +575 -0
- package/main/team/session.mjs +96 -26
- package/main/team/supabase-backend.mjs +180 -0
- package/main/team/sync.mjs +109 -1
- package/main/teammates.mjs +580 -0
- package/main/terminal-host.mjs +143 -0
- package/main/terminal-hosts.mjs +261 -0
- package/main/transcript-search.mjs +80 -0
- package/main/updater.mjs +71 -9
- package/main/webview-guard.mjs +45 -0
- package/main/worktree-include.mjs +365 -0
- package/mcp/core/documents.mjs +3 -1
- package/mcp/core/repeats.mjs +88 -0
- package/mcp/core/teammates.mjs +222 -0
- package/mcp/core/work.mjs +76 -4
- package/mcp/stdio.mjs +24 -11
- package/mcp/tools.mjs +116 -8
- package/package.json +30 -5
- package/renderer/dist/assets/abnfDiagram-YUKMZFIV-CoWB6YUK.js +1 -0
- package/renderer/dist/assets/arc-Ch0NrCul.js +1 -0
- package/renderer/dist/assets/architectureDiagram-47P4ROYG-CXz9M5tQ.js +36 -0
- package/renderer/dist/assets/blockDiagram-E7TT5TSB-DchGUuHH.js +140 -0
- package/renderer/dist/assets/c4Diagram-CRA5TL53-Cip-zNyr.js +38 -0
- package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
- package/renderer/dist/assets/chunk-2Q5K7J3B-BoXid_2r.js +1 -0
- package/renderer/dist/assets/chunk-5VM5RSS4-Cpez1rRz.js +15 -0
- package/renderer/dist/assets/chunk-7TKQ45FW-DKA84YkE.js +1 -0
- package/renderer/dist/assets/chunk-AW2ZBBNX-Dg-SumPG.js +88 -0
- package/renderer/dist/assets/chunk-FVRAUYC3-06TCvPm1.js +2 -0
- package/renderer/dist/assets/chunk-HTAEGDNF-CMEBy1Td.js +1 -0
- package/renderer/dist/assets/chunk-JWPE2WC7-BJ2ObmV-.js +1 -0
- package/renderer/dist/assets/chunk-KQW6MTUR-CGWOYXUE.js +1 -0
- package/renderer/dist/assets/chunk-XXDRQBXY-CiZp07XC.js +1 -0
- package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-DJNIX719.js +217 -0
- package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
- package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
- package/renderer/dist/assets/cose-bilkent-JH36ORCC-CbkyZOUH.js +1 -0
- package/renderer/dist/assets/cynefin-EF2NZ3EQ-78iOroZO.js +141 -0
- package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-GcCopMs7.js +62 -0
- package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/renderer/dist/assets/dagre-W4DXFKR2-DHLlXLkz.js +4 -0
- package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/renderer/dist/assets/diagram-2UJZ2QOL-DuZSUHcH.js +200 -0
- package/renderer/dist/assets/diagram-OVF4WLC6-Ckxu3cqH.js +24 -0
- package/renderer/dist/assets/diagram-PFPMY2P6-CsTw9Ycn.js +30 -0
- package/renderer/dist/assets/diagram-UMYRVEAY-CTXd8qVg.js +24 -0
- package/renderer/dist/assets/diagram-YEKJPTXX-CJs1T_9m.js +41 -0
- package/renderer/dist/assets/diagram-ZIFT7M5P-CvjI23mf.js +3 -0
- package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-d_IeAzOJ.js +1 -0
- package/renderer/dist/assets/elk-IJKZMXRS-zo9KFP3N.js +27 -0
- package/renderer/dist/assets/erDiagram-O2IAPWRE-DqfYf5Dt.js +99 -0
- package/renderer/dist/assets/flowDiagram-OXPTDLAJ-DAWsMLmo.js +213 -0
- package/renderer/dist/assets/ganttDiagram-R7TSEDQI-4ml_bhwR.js +292 -0
- package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-B_WcqSId.js +106 -0
- package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
- package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-CI-jYqzc.js +2 -0
- package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
- package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-B56VWRlV.js +70 -0
- package/renderer/dist/assets/journeyDiagram-COXZFDF6-D1-p09Lr.js +139 -0
- package/renderer/dist/assets/kanban-definition-P3RFRI5V-Cv3w7JoN.js +89 -0
- package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
- package/renderer/dist/assets/linear-C35V2Spi.js +1 -0
- package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
- package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
- package/renderer/dist/assets/mermaid.core-BzGT5Vo9.js +314 -0
- package/renderer/dist/assets/mindmap-definition-LZFPQGKD-BZUfuymC.js +96 -0
- package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/renderer/dist/assets/pegDiagram-BGZESJAR-DFh9XNGe.js +1 -0
- package/renderer/dist/assets/pieDiagram-CAPJLFHJ-C65tPVsy.js +39 -0
- package/renderer/dist/assets/product-name-CSNW6d7s.js +40 -0
- package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-Cushj_nk.js +7 -0
- package/renderer/dist/assets/railroadDiagram-SM67HX2B-DZGebGx5.js +1 -0
- package/renderer/dist/assets/requirementDiagram-X7JNWC4A-DpAB0MNo.js +84 -0
- package/renderer/dist/assets/sankeyDiagram-UM26HJRW-D-29rHra.js +40 -0
- package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-FfOQAZol.js +169 -0
- package/renderer/dist/assets/sizeCapture-INFHLROL-C_n6fDBY.js +1 -0
- package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-BZjenzJG.js +291 -0
- package/renderer/dist/assets/swimlanes-N4OXWK64-DxaMU9vX.js +1 -0
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
- package/renderer/dist/assets/timeline-definition-YOQAKGHF-DTg5HJKz.js +120 -0
- package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-Bc3MZDsl.js +370 -0
- package/renderer/dist/assets/vennDiagram-BLWOH2XV-GIVGk9Id.js +34 -0
- package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-NmJnbQOg.js +78 -0
- package/renderer/dist/assets/xychartDiagram-TAQBALBS-p-kfGfnW.js +7 -0
- package/renderer/dist/corner-tag.html +13 -0
- package/renderer/dist/emulator/audio-rules.mjs +74 -0
- package/renderer/dist/emulator/data/compression/README.md +11 -0
- package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
- package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
- package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +22 -0
- package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/package.json +65 -0
- package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
- package/renderer/dist/emulator/data/emulator.css +1614 -0
- package/renderer/dist/emulator/data/loader.js +170 -0
- package/renderer/dist/emulator/data/localization/README.md +63 -0
- package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
- package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
- package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
- package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
- package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
- package/renderer/dist/emulator/data/localization/en-US.json +339 -0
- package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
- package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
- package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
- package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
- package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
- package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
- package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
- package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
- package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
- package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
- package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
- package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
- package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
- package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
- package/renderer/dist/emulator/data/src/GameManager.js +464 -0
- package/renderer/dist/emulator/data/src/compression.js +142 -0
- package/renderer/dist/emulator/data/src/emulator.js +6158 -0
- package/renderer/dist/emulator/data/src/gamepad.js +167 -0
- package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
- package/renderer/dist/emulator/data/src/shaders.js +236 -0
- package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
- package/renderer/dist/emulator/data/src/storage.js +125 -0
- package/renderer/dist/emulator/data/version.json +5 -0
- package/renderer/dist/emulator/host.js +297 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/index.html +16 -3
- package/scripts/after-install.mjs +30 -8
- package/scripts/approval-server.sh +24 -5
- package/scripts/before-publish.mjs +12 -10
- package/scripts/cloud-shim.pl +57 -0
- package/shared/account-names.d.mts +6 -0
- package/shared/account-names.mjs +15 -0
- package/shared/agent-hours.mjs +136 -0
- package/shared/agent-import.mjs +62 -34
- package/shared/agents.d.mts +1 -0
- package/shared/agents.mjs +126 -522
- package/shared/analytics-events.mjs +12 -0
- package/shared/approval-card.mjs +36 -0
- package/shared/bridge-map.mjs +45 -1
- package/shared/claude-code-shape.mjs +6 -0
- package/shared/claude-commands.generated.mjs +6 -4
- package/shared/claude-models.generated.mjs +6 -4
- package/shared/claude-usage.d.mts +1 -0
- package/shared/claude-usage.mjs +42 -1
- package/shared/cloud-hand-back.d.mts +7 -0
- package/shared/cloud-hand-back.mjs +20 -0
- package/shared/cloud-kit.d.mts +219 -0
- package/shared/cloud-kit.mjs +341 -0
- package/shared/cloud-login-name.mjs +4 -0
- package/shared/cloud-start.mjs +17 -0
- package/shared/cloud-versions.mjs +23 -0
- package/shared/codex-import.mjs +2 -2
- package/shared/codex-modes.d.mts +22 -9
- package/shared/codex-modes.mjs +67 -11
- package/shared/codex-usage.mjs +108 -2
- package/shared/corner-tag.d.mts +36 -0
- package/shared/corner-tag.mjs +396 -0
- package/shared/dead-run-trace.mjs +19 -2
- package/shared/engines.mjs +76 -300
- package/shared/feedback.d.mts +7 -0
- package/shared/feedback.mjs +25 -0
- package/shared/first-run-practice.mjs +17 -24
- package/shared/harness-definitions.mjs +111 -0
- package/shared/instruction-defaults.json +2 -2
- package/shared/key-caps.d.mts +14 -0
- package/shared/key-caps.mjs +68 -0
- package/shared/message-edits.d.mts +7 -0
- package/shared/message-edits.mjs +35 -0
- package/shared/move-thread.d.mts +24 -0
- package/shared/move-thread.mjs +112 -0
- package/shared/named-threads.mjs +72 -0
- package/shared/not-shipped.d.mts +22 -0
- package/shared/not-shipped.mjs +89 -0
- package/shared/notify-rules.d.mts +31 -0
- package/shared/notify-rules.mjs +55 -8
- package/shared/phone-files.mjs +28 -0
- package/shared/phone-settings.mjs +118 -0
- package/shared/phone-snapshot.d.mts +16 -0
- package/shared/phone-snapshot.mjs +348 -0
- package/shared/picture-formats.d.mts +1 -0
- package/shared/picture-formats.mjs +13 -0
- package/shared/provider-commands.mjs +10 -2
- package/shared/rank.d.mts +4 -1
- package/shared/rank.mjs +82 -19
- package/shared/relay-address.mjs +6 -0
- package/shared/relay-crypto.mjs +165 -0
- package/shared/repeats.d.mts +8 -0
- package/shared/repeats.mjs +33 -2
- package/shared/room-cards.mjs +167 -0
- package/shared/room-links.d.mts +4 -0
- package/shared/room-links.mjs +21 -0
- package/shared/runs-on.d.mts +3 -13
- package/shared/runs-on.mjs +31 -52
- package/shared/shipped-note.d.mts +4 -0
- package/shared/shipped-note.mjs +6 -0
- package/shared/shortcuts.mjs +24 -10
- package/shared/spawn-trouble.d.mts +4 -0
- package/shared/spawn-trouble.mjs +484 -30
- package/shared/team-rules.mjs +9 -2
- package/shared/teammates.d.mts +19 -0
- package/shared/teammates.mjs +756 -0
- package/shared/terminal-focus.d.mts +1 -0
- package/shared/terminal-focus.mjs +13 -0
- package/shared/terminal-input.d.mts +1 -0
- package/shared/terminal-input.mjs +30 -0
- package/shared/terminal-keys.d.mts +1 -0
- package/shared/terminal-keys.mjs +16 -0
- package/shared/terminal-size.d.mts +4 -0
- package/shared/terminal-size.mjs +29 -0
- package/shared/thread-cards.mjs +4 -1
- package/shared/usage.d.mts +12 -0
- package/shared/usage.mjs +56 -3
- package/shared/websites.d.mts +15 -0
- package/shared/websites.mjs +61 -0
- package/shared/where-it-runs-from.d.mts +43 -0
- package/shared/where-it-runs-from.mjs +155 -0
- package/shared/work-items.mjs +150 -29
- package/shared/work-lines.mjs +93 -245
- package/shared/xterm-invalid-range.d.mts +2 -0
- package/shared/xterm-invalid-range.mjs +58 -0
- package/renderer/dist/assets/index-ChKsQkRw.js +0 -1169
- package/renderer/dist/assets/index-ClBXnq3B.css +0 -32
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// A THREAD WHOSE WORK NEVER REACHED MAIN, read off the app's own notes.
|
|
2
|
+
//
|
|
3
|
+
// The ship queue and the cloud run write these words (main/ship-queue.mjs
|
|
4
|
+
// `failureNote`, main/cloud/run.mjs `park`), so the reader here cannot drift
|
|
5
|
+
// from the writers. The agent's last message was written before either, and
|
|
6
|
+
// usually says the work is shipping, so these notes are what has to be shown.
|
|
7
|
+
|
|
8
|
+
import { isShippedNote } from './shipped-note.mjs';
|
|
9
|
+
|
|
10
|
+
export const SHIP_FAILED = 'It did not ship, and it went back to its agent: ';
|
|
11
|
+
export const KEPT_ASIDE = "This thread's cloud run finished, but its work was not laid into the task folder: ";
|
|
12
|
+
// The sweep's note when it reopens a thread that was closed without being asked.
|
|
13
|
+
export const CLOSED_UNSHIPPED = 'This thread was closed before its work reached main, so it is back. ';
|
|
14
|
+
// Written when the person was asked and closed it anyway; ends the matter.
|
|
15
|
+
export const CLOSED_ANYWAY = 'Closed without its work reaching main.';
|
|
16
|
+
|
|
17
|
+
const at = (item, field) => item?.wrote?.[field]?.ts ?? 0;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `{ why, branch, reopened }` while the latest note says the work did not
|
|
21
|
+
* reach main and the agent has not spoken since, else null.
|
|
22
|
+
*/
|
|
23
|
+
export function notShipped(item) {
|
|
24
|
+
const note = typeof item?.note === 'string' ? item.note : '';
|
|
25
|
+
if (!note || at(item, 'result') > at(item, 'note')) return null;
|
|
26
|
+
let text = note;
|
|
27
|
+
const reopened = text.startsWith(CLOSED_UNSHIPPED);
|
|
28
|
+
if (reopened) text = text.slice(CLOSED_UNSHIPPED.length);
|
|
29
|
+
if (text.startsWith(SHIP_FAILED)) {
|
|
30
|
+
return { why: text.slice(SHIP_FAILED.length).trim(), branch: null, reopened };
|
|
31
|
+
}
|
|
32
|
+
if (text.startsWith(KEPT_ASIDE)) {
|
|
33
|
+
const branch = /on the branch (\S+?)\.(?:\s|$)/.exec(text)?.[1] ?? null;
|
|
34
|
+
return { why: branch ? `the cloud run's work is set aside on ${branch}` : "the cloud run's work is set aside", branch, reopened };
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `{ sha }` while the app's own latest note says it shipped the work and
|
|
41
|
+
* nobody has written on the thread since. An agent saying it shipped does
|
|
42
|
+
* not count: only the app ships.
|
|
43
|
+
*/
|
|
44
|
+
export function shippedAs(item) {
|
|
45
|
+
if (!isShippedNote(item?.note) || item?.wrote?.note?.source !== 'system') return null;
|
|
46
|
+
if (at(item, 'result') > at(item, 'note') || at(item, 'answer') > at(item, 'note')) return null;
|
|
47
|
+
return { sha: / as ([0-9a-f]{7,40})\./.exec(item.note)?.[1] ?? null };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A closed thread whose close was told its work never reached main. */
|
|
51
|
+
export function closedUnshipped(item) {
|
|
52
|
+
return item?.status === 'done' && typeof item?.note === 'string' && item.note.startsWith(CLOSED_ANYWAY);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The row's line: "Not shipped. <why>". */
|
|
56
|
+
export function notShippedLine(item) {
|
|
57
|
+
const ns = notShipped(item);
|
|
58
|
+
return ns ? `Not shipped. ${ns.why.charAt(0).toUpperCase()}${ns.why.slice(1)}` : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** What the agent is asked when the person picks "have the agent ship it". */
|
|
62
|
+
export function shipItAsk(item) {
|
|
63
|
+
const ns = notShipped(item);
|
|
64
|
+
if (!ns) return null;
|
|
65
|
+
return ns.branch
|
|
66
|
+
? `Bring the work on ${ns.branch} into this folder, check it against current main, then ship it.`
|
|
67
|
+
: 'Fix what stopped the ship, then ship it.';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The note a close writes once the person has been asked and said close. */
|
|
71
|
+
export function closedAnywayNote(item) {
|
|
72
|
+
const ns = notShipped(item);
|
|
73
|
+
if (!ns) return null;
|
|
74
|
+
return ns.branch ? `${CLOSED_ANYWAY} It is still on ${ns.branch}.` : CLOSED_ANYWAY;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `{ ok: false, error }` for a close nobody was asked about, else null. */
|
|
78
|
+
export function closeRefusal(item, { closeUnshipped = false } = {}) {
|
|
79
|
+
if (closeUnshipped) return null;
|
|
80
|
+
const line = notShippedLine(item);
|
|
81
|
+
return line ? { ok: false, error: `${line}. Reply to have it shipped, or close it on your computer.` } : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Done, with work that never reached main, and never asked about. */
|
|
85
|
+
export function closedBeforeItShipped(item) {
|
|
86
|
+
if (item?.status !== 'done') return false;
|
|
87
|
+
const ns = notShipped(item);
|
|
88
|
+
return !!ns && !ns.reopened;
|
|
89
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Types for shared/notify-rules.mjs, which is plain ESM so that main (Node)
|
|
2
|
+
// and the screen (Vite) can share one copy of the rule about when Agentbox is
|
|
3
|
+
// allowed to interrupt you. The screen asks only `isNew`, which is the
|
|
4
|
+
// question before "are you away"; the rest is main's.
|
|
5
|
+
|
|
6
|
+
export const AWAY_AFTER_MS: number;
|
|
7
|
+
|
|
8
|
+
export function atTheApp(state?: { focused?: boolean; idleMs?: number }): boolean;
|
|
9
|
+
|
|
10
|
+
export function atTheComputer(state?: { idleMs?: number }): boolean;
|
|
11
|
+
|
|
12
|
+
// `known` is the ids on the snapshot before this one, and null means there has
|
|
13
|
+
// not been one. Empty and never-looked are different facts, and the module
|
|
14
|
+
// says what reading one as the other cost.
|
|
15
|
+
export function isNew(known: ReadonlySet<string> | null | undefined, id: string): boolean;
|
|
16
|
+
|
|
17
|
+
export function clip(text: unknown, max?: number): string;
|
|
18
|
+
|
|
19
|
+
export function shouldSpeak(state?: {
|
|
20
|
+
spoke?: boolean;
|
|
21
|
+
spokeAsk?: boolean;
|
|
22
|
+
waiting?: ReadonlyArray<{ kind?: string } | null | undefined>;
|
|
23
|
+
}): boolean;
|
|
24
|
+
|
|
25
|
+
export function banner(
|
|
26
|
+
waiting: ReadonlyArray<{ id?: string; title?: unknown; kind?: string } | null | undefined> | null | undefined,
|
|
27
|
+
): { title: string; body: string; open: string | null } | null;
|
|
28
|
+
|
|
29
|
+
export function phoneBanner(
|
|
30
|
+
waiting: ReadonlyArray<{ id?: string; title?: unknown; thread?: unknown; kind?: string } | null | undefined> | null | undefined,
|
|
31
|
+
): { title: string; body: string; open: string | null } | null;
|
package/shared/notify-rules.mjs
CHANGED
|
@@ -31,6 +31,34 @@ export function atTheApp({ focused = false, idleMs = 0 } = {}) {
|
|
|
31
31
|
return !!focused && idleMs < AWAY_AFTER_MS;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// THE PHONE'S RULE IS WIDER: she is at the computer, in any app, so the phone
|
|
35
|
+
// says nothing. Someone in a browser on the laptop is not at the app, and the
|
|
36
|
+
// desktop banner above is how she hears; buzzing the phone in her bag as well
|
|
37
|
+
// is the "tons and tons of notifications" she reported (2026-10-13). The
|
|
38
|
+
// phone is for when she has left the machine, which only the idle clock knows.
|
|
39
|
+
export function atTheComputer({ idleMs = 0 } = {}) {
|
|
40
|
+
return idleMs < AWAY_AFTER_MS;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// WHAT COUNTS AS NEWS, which is the question before "is she away".
|
|
44
|
+
//
|
|
45
|
+
// renderer/src/App.tsx owns the definition of the inbox and asks this of every
|
|
46
|
+
// row in it, against the ids it remembered from the snapshot before. `known` is
|
|
47
|
+
// those ids, and `null` means there has not been a snapshot yet: on the app's
|
|
48
|
+
// first look, everything waiting has been waiting, so none of it is a banner.
|
|
49
|
+
//
|
|
50
|
+
// EMPTY AND NEVER-LOOKED ARE DIFFERENT FACTS, and reading one as the other is
|
|
51
|
+
// what sent this feature's best moment into the bin. App.tsx used to decide it
|
|
52
|
+
// had never looked by asking whether the ids it remembered were empty, which is
|
|
53
|
+
// also true of inbox zero — the state the whole product aims at. So the
|
|
54
|
+
// arrival that most deserved to reach her, the first one after she got clear,
|
|
55
|
+
// was the one the app silently dropped before any rule here could see it.
|
|
56
|
+
// tests/the-first-arrival-after-inbox-zero-still-speaks.test.mjs.
|
|
57
|
+
export function isNew(known, id) {
|
|
58
|
+
if (!known) return false;
|
|
59
|
+
return !known.has(id);
|
|
60
|
+
}
|
|
61
|
+
|
|
34
62
|
// A title long enough to be a paragraph is normal here; hers run past a
|
|
35
63
|
// hundred characters. macOS will hard-truncate mid-word, so cut on a space and
|
|
36
64
|
// say that it was cut.
|
|
@@ -59,14 +87,15 @@ export function clip(text, max = 120) {
|
|
|
59
87
|
// main/notify.mjs resets this the moment she has the window, so the next time
|
|
60
88
|
// she walks away she can be told again.
|
|
61
89
|
//
|
|
62
|
-
// THE ONE EXCEPTION, and it is here because an approval
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// first banner of the stretch was a
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
90
|
+
// THE ONE EXCEPTION, and it is here because an approval is the one thing in the
|
|
91
|
+
// inbox that nothing else can move. A finished agent has said its piece and the
|
|
92
|
+
// thread is readable without her. An approval is a worker frozen mid-run with
|
|
93
|
+
// her as the only way through, and it waits exactly that long: no clock answers
|
|
94
|
+
// it (main/codex-approvals.mjs). If the first banner of the stretch was a
|
|
95
|
+
// finished agent, an approval landing twenty minutes later would die unheard
|
|
96
|
+
// under a strict one-and-done rule, and the run would still be standing there
|
|
97
|
+
// when she got back. So an approval gets to speak once more, and that is the
|
|
98
|
+
// cap: at most two in one stretch away, never more.
|
|
70
99
|
export function shouldSpeak({ spoke = false, spokeAsk = false, waiting = [] } = {}) {
|
|
71
100
|
const items = (waiting ?? []).filter(Boolean);
|
|
72
101
|
if (!items.length) return false;
|
|
@@ -118,3 +147,21 @@ export function banner(waiting) {
|
|
|
118
147
|
open: lead.kind === 'approval' ? null : (lead.id ?? null),
|
|
119
148
|
};
|
|
120
149
|
}
|
|
150
|
+
|
|
151
|
+
// THE PHONE'S ALERT IS HEADED BY THE THREAD (2026-10-13). On a phone the
|
|
152
|
+
// banner is read in a stack of others, and she asked for the bold line to be
|
|
153
|
+
// "the specific chat title so I know which thread we're talking about". iOS
|
|
154
|
+
// wraps the body and gives the title a line of its own, so the macOS reason
|
|
155
|
+
// above for keeping a name out of the title does not hold there. The sentence
|
|
156
|
+
// moves under it. An approval names the thread that is asking (`thread`) and
|
|
157
|
+
// what it wants to run; with no thread to name, it is the desktop's words.
|
|
158
|
+
export function phoneBanner(waiting) {
|
|
159
|
+
const say = banner(waiting);
|
|
160
|
+
if (!say) return null;
|
|
161
|
+
const items = waiting.filter(Boolean);
|
|
162
|
+
const asks = items.filter((i) => i.kind === 'approval');
|
|
163
|
+
const lead = asks.length ? asks[0] : items[items.length - 1];
|
|
164
|
+
const name = clip(asks.length ? lead.thread : lead.title, 80);
|
|
165
|
+
if (!name) return say;
|
|
166
|
+
return { title: name, body: asks.length ? `Waiting on you: ${say.body}` : say.title, open: say.open };
|
|
167
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// A FILE HER PHONE SENT WITH A REPLY OR A NEW THREAD: put in her storage, in
|
|
2
|
+
// the folder of the computer the thread is on, named by what is in it
|
|
3
|
+
// (renderer/src/phone-sends-files.ts), and linked in her words. The computer
|
|
4
|
+
// (main/phone-inbox.mjs) and the cloud server (cloud/gateway/phone.mjs) each
|
|
5
|
+
// put it in the project's attachments under the same name, made from what is
|
|
6
|
+
// in it, so a turn the cloud ran and the row the computer keeps link one file.
|
|
7
|
+
|
|
8
|
+
export const PHONE_FILE = /(!?)\[([^\]\n]*)\]\(phone-files\/([0-9a-f]{64}\.[a-z0-9]{1,8})\)/g;
|
|
9
|
+
export const PHONE_FILES_MAX = 10;
|
|
10
|
+
// The bucket's own bound on a file (20261017000000_phone_files.sql).
|
|
11
|
+
export const PHONE_FILE_MAX_BYTES = 25 * 1024 * 1024;
|
|
12
|
+
|
|
13
|
+
export function phoneFilesIn(text) {
|
|
14
|
+
return [...String(text ?? '').matchAll(PHONE_FILE)].map((m) => ({ link: m[0], picture: m[1] === '!', label: m[2], name: m[3] }));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The name it goes under, as main/save-attachments.mjs makes one safe. */
|
|
18
|
+
export const phoneFileSafeName = (f) => String(String(f.label ?? '').trim() || f.name).replace(/[^\w.-]+/g, '-').slice(0, 80);
|
|
19
|
+
|
|
20
|
+
/** Where it goes in the project's documents folder: the same file, the same place. */
|
|
21
|
+
export const phoneAttachmentRel = (f) => `attachments/${String(f.name).slice(0, 16)}-${phoneFileSafeName(f)}`;
|
|
22
|
+
|
|
23
|
+
/** Her words with each file linked where it was put. */
|
|
24
|
+
export function linkPhoneFiles(text, found = phoneFilesIn(text)) {
|
|
25
|
+
let out = String(text ?? '');
|
|
26
|
+
for (const f of found) out = out.replace(f.link, () => `${f.picture ? '!' : ''}[${phoneFileSafeName(f)}](${phoneAttachmentRel(f)})`);
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// YOUR COMPUTER'S SETTINGS ON YOUR PHONE: the copy the Mac sends, and the
|
|
2
|
+
// changes the phone may ask for. The Mac's own handlers (main/settings.mjs)
|
|
3
|
+
// still decide every value. This keeps paths and instruction text out of the
|
|
4
|
+
// cloud, and refuses any key the phone has no page for.
|
|
5
|
+
import { CLAUDE_PERMISSION_MODES } from './work-items.mjs';
|
|
6
|
+
import { CODEX_MODE_ORDER } from './codex-modes.mjs';
|
|
7
|
+
import { AGENT_MODES } from './agents.mjs';
|
|
8
|
+
|
|
9
|
+
export const SETTINGS_VERSION = 1;
|
|
10
|
+
|
|
11
|
+
/** A login by the last part of its folder's name; the folder itself stays on the Mac. */
|
|
12
|
+
export const accountIdOf = (profile) => (profile === 'default' ? 'default' : String(profile ?? '').split(/[\\/]/).filter(Boolean).pop() ?? '');
|
|
13
|
+
|
|
14
|
+
const ACCOUNT_ID = /^[\w.-]{1,80}$/;
|
|
15
|
+
const SLUG = /^[a-z0-9][\w-]{0,119}$/i;
|
|
16
|
+
const bool = (v) => typeof v === 'boolean';
|
|
17
|
+
const oneOf = (list) => (v) => list.includes(v);
|
|
18
|
+
const count = (v) => Number.isInteger(v) && v >= 1 && v <= 64;
|
|
19
|
+
const accountId = (v) => typeof v === 'string' && ACCOUNT_ID.test(v) && v !== '.' && v !== '..';
|
|
20
|
+
|
|
21
|
+
const WORKSPACE = {
|
|
22
|
+
cornerTag: bool,
|
|
23
|
+
diagnostics: bool,
|
|
24
|
+
adhdMode: bool,
|
|
25
|
+
alphaRooms: bool,
|
|
26
|
+
alphaTeammates: bool,
|
|
27
|
+
agentsAuto: bool,
|
|
28
|
+
memoryGate: bool,
|
|
29
|
+
cleanupLeftovers: bool,
|
|
30
|
+
sessionsAtOnce: count,
|
|
31
|
+
memoryGateSlots: (v) => v === null || count(v),
|
|
32
|
+
agentsFeltSlow: oneOf(['more', 'reset']),
|
|
33
|
+
outsideAgents: oneOf(AGENT_MODES),
|
|
34
|
+
permission: oneOf(CLAUDE_PERMISSION_MODES),
|
|
35
|
+
codexMode: oneOf(CODEX_MODE_ORDER),
|
|
36
|
+
phoneProjects: (v) => !!v && bool(v.all) && Array.isArray(v.except) && v.except.length <= 500 && v.except.every((s) => typeof s === 'string' && SLUG.test(s)),
|
|
37
|
+
activeAccount: (v) => !!v && oneOf(['claude', 'codex'])(v.engine) && (v.account === null || accountId(v.account)),
|
|
38
|
+
};
|
|
39
|
+
const PROJECT = {
|
|
40
|
+
autonomous: bool,
|
|
41
|
+
permission: oneOf(['workspace', ...CLAUDE_PERMISSION_MODES]),
|
|
42
|
+
codexMode: oneOf(['workspace', ...CODEX_MODE_ORDER]),
|
|
43
|
+
account: (v) => v === 'any' || accountId(v),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Why the Mac should not carry out this change, or null. */
|
|
47
|
+
export function checkSetting(p) {
|
|
48
|
+
if (!p || typeof p !== 'object') return 'not a setting';
|
|
49
|
+
const rules = p.scope === 'workspace' ? WORKSPACE : p.scope === 'project' ? PROJECT : null;
|
|
50
|
+
if (!rules || !Object.hasOwn(rules, p.key)) return 'not a setting your phone can change';
|
|
51
|
+
if (p.scope === 'project' && !(typeof p.product === 'string' && SLUG.test(p.product))) return 'that setting needs its project';
|
|
52
|
+
return rules[p.key](p.value) ? null : 'not a value that setting takes';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function profileFor(model, engine, id) {
|
|
56
|
+
if (id === null) return null;
|
|
57
|
+
const list = engine === 'codex' ? model?.workspace?.codex?.accounts : model?.workspace?.accounts;
|
|
58
|
+
const hit = (list ?? []).find((a) => accountIdOf(a.profile) === id);
|
|
59
|
+
if (!hit) throw new Error('that account is not on this computer');
|
|
60
|
+
return hit.profile;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The window's own handler for a checked change, and what to hand it. */
|
|
64
|
+
export function settingRoute(p, model) {
|
|
65
|
+
if (p.scope === 'project') {
|
|
66
|
+
const value = p.key === 'account' && p.value !== 'any' ? profileFor(model, 'claude', p.value) : p.value;
|
|
67
|
+
return ['zero:settings-set-project', { product: p.product, key: p.key, value }];
|
|
68
|
+
}
|
|
69
|
+
const value = p.key === 'activeAccount' ? { engine: p.value.engine, profile: profileFor(model, p.value.engine, p.value.account) } : p.value;
|
|
70
|
+
return ['zero:settings-set-workspace', { key: p.key, value }];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* What the phone's Settings pages draw, from the Mac's settings model. Null
|
|
75
|
+
* when the Mac could not read its own settings.
|
|
76
|
+
*/
|
|
77
|
+
export function phoneSettingsOf(model, { version }) {
|
|
78
|
+
if (!model?.ok || !model.workspace) return null;
|
|
79
|
+
const w = model.workspace;
|
|
80
|
+
const gate = w.memoryGate ?? {};
|
|
81
|
+
return {
|
|
82
|
+
v: SETTINGS_VERSION,
|
|
83
|
+
version: String(version ?? ''),
|
|
84
|
+
workspace: {
|
|
85
|
+
cornerTag: !!w.cornerTag,
|
|
86
|
+
diagnostics: !!w.diagnostics,
|
|
87
|
+
adhdMode: !!w.adhdMode,
|
|
88
|
+
alpha: { rooms: !!w.alpha?.rooms },
|
|
89
|
+
outsideAgents: w.outsideAgents,
|
|
90
|
+
permission: w.permission,
|
|
91
|
+
codexMode: w.codexMode ?? null,
|
|
92
|
+
engines: (w.engineChoices ?? []).map((e) => e.id),
|
|
93
|
+
claude: { found: !!w.claudeFound },
|
|
94
|
+
codex: { found: !!w.codex?.found },
|
|
95
|
+
agentsAuto: !!w.agentsAuto,
|
|
96
|
+
sessionsAtOnce: w.sessionsAtOnce,
|
|
97
|
+
agentsTotal: w.agentsTotal,
|
|
98
|
+
agentsAutoTotal: w.agentsAutoTotal,
|
|
99
|
+
agentsNudge: w.agentsNudge ?? 0,
|
|
100
|
+
slotsMax: w.slotsMax,
|
|
101
|
+
slotsSuggested: w.slotsSuggested,
|
|
102
|
+
running: w.running,
|
|
103
|
+
memoryGate: { on: !!gate.on, slots: gate.slots ?? null, slotsAuto: gate.slotsAuto ?? null, slotsMax: gate.slotsMax ?? null, now: gate.now ?? null },
|
|
104
|
+
leftovers: { on: !!w.leftovers?.on, now: w.leftovers?.now ?? null },
|
|
105
|
+
accounts: (w.accounts ?? []).map((a) => ({
|
|
106
|
+
id: accountIdOf(a.profile), email: a.email ?? null, chosen: !!a.chosen, state: a.state,
|
|
107
|
+
note: a.trouble?.note ?? null, plan: a.plan?.label ?? null, running: a.running ?? 0,
|
|
108
|
+
})),
|
|
109
|
+
codexAccounts: (w.codex?.accounts ?? []).map((a) => ({
|
|
110
|
+
id: accountIdOf(a.profile), email: a.email ?? null, name: a.name ?? null, plan: a.plan ?? null, signedIn: !!a.signedIn, chosen: !!a.chosen,
|
|
111
|
+
})),
|
|
112
|
+
},
|
|
113
|
+
projects: (model.projects ?? []).map((p) => ({
|
|
114
|
+
slug: p.slug, name: p.name, onPhone: !!p.onPhone, autonomous: !!p.autonomous,
|
|
115
|
+
permission: p.permission, codexMode: p.codexMode, account: p.account === 'any' ? 'any' : accountIdOf(p.account), running: p.running ?? 0,
|
|
116
|
+
})),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Types for shared/phone-snapshot.mjs, for the phone with the computer closed.
|
|
2
|
+
export const SNAPSHOT_LIMITS: { lines: number; textChars: number; items: number; bytes: number };
|
|
3
|
+
export const PHONE_COMMANDS: string[];
|
|
4
|
+
export function hideSecrets(text: string, opts?: { home?: string | null }): string;
|
|
5
|
+
export function bytesOf(value: unknown): number;
|
|
6
|
+
export function threadSnapshot(item: Record<string, unknown>, history?: unknown[], limits?: typeof SNAPSHOT_LIMITS, opts?: { home?: string | null }): { v: number; item: Record<string, unknown>; history: unknown[]; olderLines: number };
|
|
7
|
+
export function threadsWorthCarrying<T>(items: T[], opts?: { now?: number }): T[];
|
|
8
|
+
export function snapshotKey(snapshot: unknown): string;
|
|
9
|
+
export function madeHere(commandId: string): string;
|
|
10
|
+
export function phoneChoices(kind: string, p: Record<string, unknown> | null | undefined): Record<string, unknown>;
|
|
11
|
+
export function phoneChoiceOf(saved: unknown, personId: string | null | undefined, every?: string[]): { all: boolean; except: string[] };
|
|
12
|
+
export function phoneProjectsOf(saved: unknown, personId: string | null | undefined, every?: string[]): string[];
|
|
13
|
+
export function withPhoneProject(saved: unknown, personId: string | null | undefined, slug: string, on: boolean, every?: string[]): { owner: string; all: boolean; except: string[] };
|
|
14
|
+
export function withPhoneAll(saved: unknown, personId: string | null | undefined, on: boolean, every?: string[]): { owner: string; all: boolean; except: string[] };
|
|
15
|
+
export function checkCommand(cmd: unknown, ctx: { projects: string[]; hasItem: (project: string, id: string) => boolean }): string | null;
|
|
16
|
+
export function phoneEligible(products: { slug?: string }[]): string[];
|