openmausbot 0.1.96 → 0.1.98
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/dist/assets/ActivityPanel-H6D3SD39.js +1 -0
- package/dist/assets/BotSettingsDialog-Dqz_AiBZ.js +7 -0
- package/dist/assets/CloudBackendPicker-iezpJFpF.js +1 -0
- package/dist/assets/ComputerPanel-DZPj-IWu.js +1 -0
- package/dist/assets/DesktopViewer-utONms1p.js +5 -0
- package/dist/assets/InspectorPanel-OVaBVrzC.js +5 -0
- package/dist/assets/KeyboardShortcutsModal-gk9mvHj_.js +1 -0
- package/dist/assets/LocalVmWorkspace-DLIvd2kC.js +1 -0
- package/dist/assets/MacLocalControl-bJv8yiPB.js +1 -0
- package/dist/assets/NewBotDialog-1qGOAdUM.js +2 -0
- package/dist/assets/RemoteAgentSettingsPanel-DqKr0HkO.js +1 -0
- package/dist/assets/RoutinesPage-BM2D4qGi.js +1 -0
- package/dist/assets/RoutinesSection-Dny9ra-c.js +1 -0
- package/dist/assets/SettingsModal-gC6XJJmt.js +11 -0
- package/dist/assets/TeamMapPage-B6fuu4r_.js +8 -0
- package/dist/assets/TriggersPanel-j0qTP2CO.js +1 -0
- package/dist/assets/VisibilitySection-DT0FqPvu.js +12 -0
- package/dist/assets/WebhooksPanel--YQyCjKO.js +3 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-Dkbd2hcj.js → abnfDiagram-O67JEVCF-BsjcFX0-.js} +1 -1
- package/dist/assets/activity-BxOkayvD.js +1 -0
- package/dist/assets/{arc-Duj5Coy2.js → arc-CJNA94c6.js} +1 -1
- package/dist/assets/architectureDiagram-NJMV4G6O-dz2ud7lP.js +36 -0
- package/dist/assets/{blockDiagram-BEXU5L5S-CWDlTpXe.js → blockDiagram-BEXU5L5S-ChBEALoR.js} +1 -1
- package/dist/assets/brain-CFZ7PEXc.js +1 -0
- package/dist/assets/{c4Diagram-YGBWAQC7-B0t15PDc.js → c4Diagram-YGBWAQC7-KZNRKDQe.js} +1 -1
- package/dist/assets/channel-WhFyujhZ.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BJSCL-HV.js → chunk-2Q5K7J3B-BLLwmCET.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-D3_ILccd.js → chunk-3FUC2YCW-CQxyreBZ.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-D3dmwT_S.js → chunk-5VM5RSS4-D6sFMW0u.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Br73HFPD.js → chunk-GTNCS2PH-BaZ1YgLF.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-uSsjxubC.js → chunk-GWA4HPMP-CcalphmC.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-C40VWFyr.js → chunk-JWPE2WC7-CSk8R3ve.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-D6WNNYwU.js → chunk-NETBCI7D-CTmLjA57.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-DOEhq64P.js → chunk-WEXAMYUT-CM9Z77Ne.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-BqJEi6Pi.js → chunk-XXDRQBXY-B1fxpMS2.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-ByU_GnxT.js → classDiagram-v2-NBCMYWYE-DCCyoLpj.js} +1 -1
- package/dist/assets/computer-control-NLov352p.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-KyImBKvE.js +1 -0
- package/dist/assets/{cynefin-OW5HDTMX-CZWO7aYj.js → cynefin-OW5HDTMX-fsZc6MGa.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-C70-w0la.js → cynefinDiagram-VND7K2PF-CGmHsShB.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-CnhjhExB.js → dagre-6A5THRUB-Dah1h-CH.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-bnjWCNb4.js → diagram-22UHCM2B-BIu71OyV.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-A4uTWoFV.js → diagram-3UASUU5V-gxhUB_Mu.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Zi6m3NUl.js → diagram-ATOU4E4O-CBhusUBn.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DtCcmd5k.js → diagram-CDSNMT55-BdPrc86x.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-DofuETS3.js → diagram-MLGK6HIB-Clp110R_.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-Dw2T9PuU.js → diagram-MPIPVDR6-DJKfbugw.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-DbrwPiem.js → ebnfDiagram-ZINNZB2B-DnsltoBu.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-BvCt1dCb.js → elk-276RUBZZ-jWdq6gqH.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-Y-QKqPnU.js → erDiagram-OPXOYQCR-DArPVf8u.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-CDkL1B5w.js → flowDiagram-KWPJA3E3-D2HZ-2f7.js} +1 -1
- package/dist/assets/ganttDiagram-FUAMR5RP--o0KIxEo.js +292 -0
- package/dist/assets/{gitGraphDiagram-X574FWY7-nL9iFTCv.js → gitGraphDiagram-X574FWY7-CsoXr2D7.js} +1 -1
- package/dist/assets/image-plus-BbVjGHin.js +1 -0
- package/dist/assets/index-BrPW5HDO.css +1 -0
- package/dist/assets/index-C3NqfdVU.js +585 -0
- package/dist/assets/{index-DreDnjOu.js → index-CJTp9JEp.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-C7aHjzsf.js → infoDiagram-VRGFBTTK-g-6lNp7r.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-KUZsNBTh.js → ishikawaDiagram-OU5B5YK6-B1d6qVTV.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-CGUuodFl.js → journeyDiagram-ZHPQQLJL-BTPuiU6T.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-Cy6r868Z.js → kanban-definition-PNTS6WVX-bDDuRJvP.js} +1 -1
- package/dist/assets/{linear-Bl669148.js → linear-BVVKbEMy.js} +1 -1
- package/dist/assets/{mermaid.core-86Xqz5Mr.js → mermaid.core-JJdoKyKB.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-G4DzHLrd.js → mindmap-definition-NLK3R4M7-B4aE3T0q.js} +1 -1
- package/dist/assets/module-DCUsy2BQ.js +3 -0
- package/dist/assets/page-visible-BBo_Zm8g.js +1 -0
- package/dist/assets/{pegDiagram-GJSIUBJH-K4eruzY0.js → pegDiagram-GJSIUBJH-DFKQWpR7.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DYdR9PPq.js → pieDiagram-5QR66LMP-Iv7OKVJi.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-BCAh0nEy.js → quadrantDiagram-O4NWA36T-U2YnfwBk.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-p0dGRc4g.js → railroadDiagram-XR7U4H2S-CuPy6RqM.js} +1 -1
- package/dist/assets/remote-desktop-panel-BNEj7tUx.js +1 -0
- package/dist/assets/{requirementDiagram-PLB6GJNP-tLeBG0aL.js → requirementDiagram-PLB6GJNP-bMI5AK7T.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-QRuN16rV.js → sankeyDiagram-IPEJSGJF-BKcFE2WQ.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-ueT1nVoa.js → sequenceDiagram-PO4LG4MO-Bn9q88kl.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-wRS2zWg2.js → sizeCapture-INFHLROL-Cvmdq9HD.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-CYzA_hE1.js → stateDiagram-v2-GCMORJYK-JJQYw3eQ.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-kDnv6LMc.js → swimlanes-2SLR337P-BkSCBEwp.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-CmTirbVT.js +8 -0
- package/dist/assets/table-file.worker-KkMx-SNx.js +10 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-D1y2UbbH.js → timeline-definition-EJHVYXUP-DsaM4GHr.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-IqB4v9HX.js → usecaseDiagram-POWQR4AR-3FOwLq7B.js} +1 -1
- package/dist/assets/user-DcZBnAuv.js +1 -0
- package/dist/assets/{vennDiagram-UO4OBE2U-BEi7KYVF.js → vennDiagram-UO4OBE2U-DyNjJsKt.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-Dkybo-Yf.js → wardleyDiagram-VNRHLVJA-Y4ZMcnBC.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-BAPFJLpg.js → xychartDiagram-PMCCYNJV-BXZpg955.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +20 -9
- package/dist-server/container-mcp.js +8 -0
- package/dist-server/desktop-entry.mjs +54 -0
- package/dist-server/drivers/agents-proxy.js +26 -9
- package/dist-server/index.js +3734 -2360
- package/dist-server/openmausbot.js +1664 -1553
- package/dist-server/pair-cli.js +1664 -1553
- package/dist-server/permission-proxy.js +2 -4
- package/dist-server/server/atomic.js +70 -2
- package/dist-server/server/auto-approve.js +1 -1
- package/dist-server/server/auto-vm-claims.js +13 -1
- package/dist-server/server/automatic-recovery.js +1 -2
- package/dist-server/server/boat.js +68 -84
- package/dist-server/server/bot-overview.js +1 -1
- package/dist-server/server/browser-engine.js +38 -1
- package/dist-server/server/browser-live.js +112 -25
- package/dist-server/server/browser-runtime.js +31 -1
- package/dist-server/server/checkpoints.js +275 -44
- package/dist-server/server/cli-setup.js +3 -3
- package/dist-server/server/cloud-computer-tools.js +11 -4
- package/dist-server/server/cloud-home-start.js +4 -4
- package/dist-server/server/cloud-home.js +3 -9
- package/dist-server/server/cloud-owner.js +1 -1
- package/dist-server/server/computer-engine-removal.js +70 -0
- package/dist-server/server/computer-selection.js +18 -0
- package/dist-server/server/config.js +29 -12
- package/dist-server/server/connector-card-text.js +15 -0
- package/dist-server/server/connector-scope-hint.js +83 -0
- package/dist-server/server/default-model-selection.js +23 -14
- package/dist-server/server/delegations.js +11 -5
- package/dist-server/server/delta-context.js +6 -1
- package/dist-server/server/drivers/acp/core.js +11 -1
- package/dist-server/server/drivers/acp/grok.js +47 -5
- package/dist-server/server/drivers/acp/opencode-go.js +16 -21
- package/dist-server/server/drivers/agents-call.js +1 -1
- package/dist-server/server/drivers/agents-catalog.js +27 -7
- package/dist-server/server/drivers/agents-proxy.js +1 -0
- package/dist-server/server/drivers/builtIn.js +0 -2
- package/dist-server/server/drivers/chat-mcp-tools.js +5 -3
- package/dist-server/server/drivers/claude-login-auth.js +1 -1
- package/dist-server/server/drivers/claude.js +84 -41
- package/dist-server/server/drivers/codex-device-auth.js +27 -319
- package/dist-server/server/drivers/codex.js +2 -2
- package/dist-server/server/drivers/device-auth.js +342 -0
- package/dist-server/server/drivers/openai-chat.js +110 -10
- package/dist-server/server/drivers/pi.js +184 -49
- package/dist-server/server/fleet-agent.js +18 -5
- package/dist-server/server/harness/http.js +5 -2
- package/dist-server/server/harness/registry.js +19 -0
- package/dist-server/server/harness-capabilities.js +7 -4
- package/dist-server/server/hosted-models.js +3 -1
- package/dist-server/server/index.js +823 -333
- package/dist-server/server/key-rejections.js +57 -0
- package/dist-server/server/live-call.js +11 -6
- package/dist-server/server/managed-policy.js +1 -1
- package/dist-server/server/message-file.js +7 -4
- package/dist-server/server/model-catalog/presets.js +8 -0
- package/dist-server/server/peer-roster.js +10 -2
- package/dist-server/server/permission-proxy.js +4 -0
- package/dist-server/server/provider-key-check.js +5 -1
- package/dist-server/server/request-auth.js +1 -1
- package/dist-server/server/room-turn-end.js +15 -0
- package/dist-server/server/room-turn-timeout.js +28 -0
- package/dist-server/server/routes/live.js +3 -3
- package/dist-server/server/routes/thread-models.js +36 -0
- package/dist-server/server/routine-requests.js +1 -1
- package/dist-server/server/routines.js +3 -3
- package/dist-server/server/screen-frame-gate.js +1 -3
- package/dist-server/server/skills.js +3 -2
- package/dist-server/server/store.js +273 -61
- package/dist-server/server/surface.js +29 -49
- package/dist-server/server/system-prompt.js +10 -10
- package/dist-server/server/team-backup.js +19 -4
- package/dist-server/server/thread-model.js +25 -0
- package/dist-server/server/turn-continuation.js +168 -0
- package/dist-server/server/webhook-ingress.js +8 -3
- package/dist-server/server/webhooks.js +40 -5
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/ask-question.js +0 -70
- package/dist-server/shared/cloud-computer.js +4 -6
- package/dist-server/shared/credential-request.js +1 -1
- package/dist-server/shared/device-sign-in.js +57 -0
- package/dist-server/shared/failed-turn.js +1 -4
- package/dist-server/shared/place-view.js +409 -0
- package/dist-server/shared/team-backup.js +2 -0
- package/dist-server/shared/thread-model.js +13 -0
- package/dist-server/shared/tool-scope-support.js +1 -2
- package/dist-server/vps-container-mcp.js +8 -0
- package/dist-server/workspace-backup.worker.js +41 -22
- package/package.json +1 -1
- package/dist/assets/DesktopViewer-DsnA23KM.js +0 -5
- package/dist/assets/architectureDiagram-NJMV4G6O-uYUsDawP.js +0 -36
- package/dist/assets/channel-BZxwC-Gw.js +0 -1
- package/dist/assets/cose-bilkent-JH36ORCC-D-Uglxqq.js +0 -1
- package/dist/assets/ganttDiagram-FUAMR5RP-CKV5D2F6.js +0 -292
- package/dist/assets/index-Du7o6Dsf.js +0 -610
- package/dist/assets/index-KxG4bWSr.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C7voMDle.js +0 -8
- package/dist-server/server/drivers/boatagent.js +0 -443
|
@@ -432,8 +432,6 @@ export function resetOpenCodeModelCache() {
|
|
|
432
432
|
/** Compatibility export for older tests/imports while the product migrates
|
|
433
433
|
* from the Go-only name. */
|
|
434
434
|
export const resetOpenCodeGoModelCache = resetOpenCodeModelCache;
|
|
435
|
-
/** The names live in config.ts, beside the keys the owner saves for OpenCode. */
|
|
436
|
-
export { OPENCODE_PROVIDER_ENV };
|
|
437
435
|
/** Whether OpenCode may read provider keys from the server's own
|
|
438
436
|
* environment. Only when that environment is the person's own shell: not on a
|
|
439
437
|
* Cloud home, a hosted team workspace or an organisation-managed desktop, and
|
|
@@ -453,23 +451,16 @@ let providerKeysAllowed = () => openCodeProviderKeysAllowed({
|
|
|
453
451
|
export function setOpenCodeProviderKeyPolicy(allowed) {
|
|
454
452
|
providerKeysAllowed = allowed;
|
|
455
453
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
*
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
function withholdProviderKeysWhenManaged(env) {
|
|
454
|
+
/** Where OpenCode may not read the server's own environment, a provider key
|
|
455
|
+
* reaches it only through its instance environment: a key the owner saved
|
|
456
|
+
* in Settings (config.ts injectedEnvironment), which is theirs on any
|
|
457
|
+
* server. The same name riding along in the server's env stays out. */
|
|
458
|
+
function withholdProviderKeysWhenManaged(env, _config, _instanceId, instanceEnvironment) {
|
|
463
459
|
if (providerKeysAllowed())
|
|
464
460
|
return;
|
|
465
|
-
const own = ownProviderKeys();
|
|
466
461
|
for (const key of OPENCODE_PROVIDER_ENV) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// name still stays out.
|
|
470
|
-
if (own[key] !== undefined && env[key] === own[key])
|
|
471
|
-
continue;
|
|
472
|
-
delete env[key];
|
|
462
|
+
if (!Object.hasOwn(instanceEnvironment, key))
|
|
463
|
+
delete env[key];
|
|
473
464
|
}
|
|
474
465
|
}
|
|
475
466
|
function opencodeConfigDir(env) {
|
|
@@ -766,8 +757,10 @@ function allowOwnedDirectories(env, botId, cwd) {
|
|
|
766
757
|
/** Account failures in plain words, with the fix, naming the provider that
|
|
767
758
|
* refused: with provider keys and `opencode auth login` a model may be
|
|
768
759
|
* OpenRouter's or Anthropic's, not Zen's. Each stays under the 160
|
|
769
|
-
* characters a chat error row shows.
|
|
770
|
-
|
|
760
|
+
* characters a chat error row shows. Zen and Go take the key saved in
|
|
761
|
+
* Settings. On a Cloud the owner has no terminal for `opencode auth login`:
|
|
762
|
+
* another provider's key is saved under Keys for other OpenCode providers. */
|
|
763
|
+
export function describeOpenCodeAccountError(code, model, where = { cloudHome: cloudHomeConfigured() }) {
|
|
771
764
|
const provider = model && model.includes("/") ? model.slice(0, model.indexOf("/")) : "";
|
|
772
765
|
const zen = provider === "opencode";
|
|
773
766
|
const go = provider === "opencode-go";
|
|
@@ -776,8 +769,10 @@ export function describeOpenCodeAccountError(code, model) {
|
|
|
776
769
|
switch (code) {
|
|
777
770
|
case "invalid_credentials":
|
|
778
771
|
return zen || go || !provider
|
|
779
|
-
? "OpenCode rejected its key, or has none for this model.
|
|
780
|
-
:
|
|
772
|
+
? "OpenCode rejected its key, or has none for this model. Add or replace the OpenCode key in Settings → API keys."
|
|
773
|
+
: where.cloudHome
|
|
774
|
+
? `OpenCode's ${name} key for this model is missing or was rejected. Save it under Keys for other OpenCode providers in Settings → API keys.`
|
|
775
|
+
: `OpenCode's ${name} key for this model is missing or was rejected. Fix it with \`opencode auth login\`, or choose another model.`;
|
|
781
776
|
case "insufficient_funds":
|
|
782
777
|
return zen || !provider
|
|
783
778
|
? "Your OpenCode Zen balance has run out. Add credit at opencode.ai, or choose one of Zen's free models for this bot."
|
|
@@ -812,7 +807,7 @@ const support = (loadCatalog) => ({
|
|
|
812
807
|
models: NO_MODELS,
|
|
813
808
|
defaultCli: "opencode",
|
|
814
809
|
nativeSource: "opencode.acp",
|
|
815
|
-
loginNote: "OpenCode has no usable models
|
|
810
|
+
loginNote: "OpenCode has no usable models. Add an OpenCode key in Settings → API keys.",
|
|
816
811
|
install: {
|
|
817
812
|
command: {
|
|
818
813
|
darwin: "npm install -g opencode-ai",
|
|
@@ -259,7 +259,7 @@ function routineFields(args) {
|
|
|
259
259
|
const runOn = destination(args.run_on ?? args.runOn);
|
|
260
260
|
const timeoutMinutes = args.timeout_minutes ?? args.timeoutMinutes;
|
|
261
261
|
if (runOn != null && runOn !== "maus" && runOn !== "cloud") {
|
|
262
|
-
return { fields, error: 'Use run_on="maus" for the bot’s current model and configured computer (including VPS), or run_on="box" only for the bot’s
|
|
262
|
+
return { fields, error: 'Use run_on="maus" for the bot’s current model and configured computer (including VPS), or run_on="box" only for the bot’s cloud computer. Legacy "cloud" also means box.' };
|
|
263
263
|
}
|
|
264
264
|
if (timeoutMinutes != null && (typeof timeoutMinutes !== "number" || !Number.isInteger(timeoutMinutes) || timeoutMinutes < 5 || timeoutMinutes > 240)) {
|
|
265
265
|
return { fields, error: "timeout_minutes must be a whole number from 5 to 240. Use clear_timeout to remove a limit." };
|
|
@@ -26,6 +26,7 @@ export function catalogProfileFromEnv(env) {
|
|
|
26
26
|
voiceNotes: env.OMB_VOICE_NOTES === "1",
|
|
27
27
|
cloudHome: env.OMB_CLOUD_HOME === "1",
|
|
28
28
|
memoryEnabled: env.OMB_MEMORY_ENABLED !== "0",
|
|
29
|
+
chief: env.OMB_CHIEF_OF_STAFF === "1",
|
|
29
30
|
botId: env.OMB_BOT_ID ?? "",
|
|
30
31
|
};
|
|
31
32
|
}
|
|
@@ -132,7 +133,7 @@ const ROUTINE_FIELDS_SCHEMA = {
|
|
|
132
133
|
type: "string",
|
|
133
134
|
// "box" is Boat's historical run_on destination id (agents wire contract).
|
|
134
135
|
enum: ["maus", "box"],
|
|
135
|
-
description: "Default maus keeps the bot's selected model and configured computer, INCLUDING a self-hosted VPS. Omit this field for normal schedules. box runs on the bot's
|
|
136
|
+
description: "Default maus keeps the bot's selected model and configured computer, INCLUDING a self-hosted VPS. Omit this field for normal schedules. box runs on the bot's cloud computer, same model; it needs cloud computers set up and is not the generic cloud/VPS option. Legacy cloud values from list_routines mean box, not VPS.",
|
|
136
137
|
},
|
|
137
138
|
timeout_minutes: {
|
|
138
139
|
type: "integer",
|
|
@@ -146,7 +147,7 @@ const ROUTINE_FIELDS_SCHEMA = {
|
|
|
146
147
|
},
|
|
147
148
|
continuity: {
|
|
148
149
|
type: "boolean",
|
|
149
|
-
description: "Opt in to using the latest completed run's bounded report as historical context. Defaults to false; set false in an update to start fresh again. Included in the applied result or pending confirmation.",
|
|
150
|
+
description: "Opt in to using the latest completed run's bounded report as historical context. Use it for recurring work that builds on last time, such as QA passes, monitoring or follow-ups. Defaults to false; set false in an update to start fresh again. Included in the applied result or pending confirmation.",
|
|
150
151
|
},
|
|
151
152
|
overlap: {
|
|
152
153
|
type: "string",
|
|
@@ -154,6 +155,9 @@ const ROUTINE_FIELDS_SCHEMA = {
|
|
|
154
155
|
description: "While this routine is still working, skip scheduled occurrences (default) or queue at most one run. Queue skips further occurrences until the pending run starts; it never builds an unlimited backlog. Manual and webhook requests are separate.",
|
|
155
156
|
},
|
|
156
157
|
};
|
|
158
|
+
// propose_profile's one sentence about for_bot_id, which a bot that is not a
|
|
159
|
+
// Chief is not shown (catalogTools), along with the parameter itself.
|
|
160
|
+
const CHIEF_PROFILE_TARGET = " A Chief of Staff may pass for_bot_id (from list_bots) for a requested change to another bot in its section.";
|
|
157
161
|
const PROPOSAL_OUTCOME = " Read the result: granted Full Access may apply the change immediately. If applied, continue the requested work without another confirmation. Only a pending result requires ending the turn and waiting for the in-app decision. Never claim success from the permission mode alone; report failed or cancelled results honestly. This does not elevate another bot's execution permissions.";
|
|
158
162
|
/** Every tool, in the order it is listed. Four peer tools are worded
|
|
159
163
|
* differently for an external runtime, which may poll inside one process. */
|
|
@@ -303,7 +307,7 @@ const toolDefinitions = (externalRuntime) => [
|
|
|
303
307
|
description: "Choose where this conversation does computer work. Call with no arguments to inspect actual available choices and the current place. For a task needing computer interaction, select the requested place, or auto to choose a suitable configured computer without asking the user to use menus. OpenMausBot reuses an existing computer first; with a configured provider it can start or provision one when needed. Do not provision for ordinary chat or just to inspect availability. A pending result means end this turn immediately: OpenMausBot updates the conversation selector and resumes the original request with that computer's real tools. Do not use the old tools after requesting a switch, repeat the task, or claim the action is done. This cannot change permissions, override Off, or switch a teammate/routine/channel.",
|
|
304
308
|
inputSchema: { type: "object", additionalProperties: false, properties: {
|
|
305
309
|
surface: { type: "string", enum: ["auto", "cloud", "vm", "local", "browser"],
|
|
306
|
-
description: "auto = suitable configured computer, cloud =
|
|
310
|
+
description: "auto = suitable configured computer, cloud = the bot's cloud computer or self-hosted VPS, vm = isolated Local VM, local = user's own desktop, browser = built-in browser. Omit to list." },
|
|
307
311
|
} },
|
|
308
312
|
},
|
|
309
313
|
{
|
|
@@ -652,7 +656,7 @@ const toolDefinitions = (externalRuntime) => [
|
|
|
652
656
|
},
|
|
653
657
|
{
|
|
654
658
|
name: "propose_profile",
|
|
655
|
-
description: "Submit user-requested changes to your own name, title, description, standing instructions (SOUL.md), working folder (cwd), or your alert and voice toggles (notifications, speakReplies). Keep SOUL.md short — who you are and the rules you never break; put step-by-step procedure into a skill instead.
|
|
659
|
+
description: "Submit user-requested changes to your own name, title, description, standing instructions (SOUL.md), working folder (cwd), or your alert and voice toggles (notifications, speakReplies). Keep SOUL.md short — who you are and the rules you never break; put step-by-step procedure into a skill instead." + CHIEF_PROFILE_TARGET + PROPOSAL_OUTCOME,
|
|
656
660
|
inputSchema: {
|
|
657
661
|
type: "object",
|
|
658
662
|
additionalProperties: false,
|
|
@@ -842,6 +846,14 @@ export const SHARED_COMPUTER_TOOL_NAMES = new Set(["list_shared_computers", "sha
|
|
|
842
846
|
// tool whose every call would end in a setup error. The route behind it
|
|
843
847
|
// refuses regardless; this keeps the catalog honest about what can work.
|
|
844
848
|
const VOICE_TOOL_NAMES = new Set(["send_voice_note"]);
|
|
849
|
+
// And for a role: every route behind these refuses a bot that is not its
|
|
850
|
+
// section's Chief of Staff, as it does a for_bot_id naming another bot on
|
|
851
|
+
// propose_profile or propose_model. (A routine's for_bot_id is open to any
|
|
852
|
+
// bot that can reach that peer, so it stays.)
|
|
853
|
+
const CHIEF_ONLY_TOOL_NAMES = new Set([
|
|
854
|
+
"create_bot", "list_team_setup", "propose_team_setup", "propose_bot_deletion", "create_room", "manage_room", "retry_thread",
|
|
855
|
+
]);
|
|
856
|
+
const CHIEF_TARGET_TOOL_NAMES = new Set(["propose_profile", "propose_model"]);
|
|
845
857
|
// One teamwork path in room turns; keep all unrelated integrations available.
|
|
846
858
|
// Ordinary direct chats use this same bounded coordinator. Goal-owned turns
|
|
847
859
|
// retain their independent loop and cannot start a second coordinator.
|
|
@@ -854,7 +866,7 @@ const WATCHER_TOOL_NAMES = new Set(["create_options_card"]);
|
|
|
854
866
|
const LOCAL_VM_TOOL_NAMES = new Set(["vm_exec"]);
|
|
855
867
|
const CLOUD_HOME_SURFACE = {
|
|
856
868
|
type: "string", enum: ["auto", "cloud", "browser"],
|
|
857
|
-
description: "auto = suitable configured computer, cloud =
|
|
869
|
+
description: "auto = suitable configured computer, cloud = the bot's cloud computer, browser = built-in browser. Omit to list. This server runs in the cloud: the user's own computer and a Local VM are not places here.",
|
|
858
870
|
};
|
|
859
871
|
/** The tools one turn is shown, exactly as tools/list serializes them. */
|
|
860
872
|
export function availableTools(profile) {
|
|
@@ -878,10 +890,18 @@ function catalogTools(profile) {
|
|
|
878
890
|
const VOICE_READY_TOOLS = profile.voiceNotes
|
|
879
891
|
? SHAREABLE_TOOLS
|
|
880
892
|
: SHAREABLE_TOOLS.filter((tool) => !VOICE_TOOL_NAMES.has(tool.name));
|
|
893
|
+
const ROLE_TOOLS = profile.chief
|
|
894
|
+
? VOICE_READY_TOOLS
|
|
895
|
+
: VOICE_READY_TOOLS.filter((tool) => !CHIEF_ONLY_TOOL_NAMES.has(tool.name)).map((tool) => {
|
|
896
|
+
if (!CHIEF_TARGET_TOOL_NAMES.has(tool.name))
|
|
897
|
+
return tool;
|
|
898
|
+
const properties = Object.fromEntries(Object.entries(tool.inputSchema.properties).filter(([key]) => key !== "for_bot_id"));
|
|
899
|
+
return { ...tool, description: tool.description.replace(CHIEF_PROFILE_TARGET, ""), inputSchema: { ...tool.inputSchema, properties } };
|
|
900
|
+
});
|
|
881
901
|
return profile.externalRuntime
|
|
882
902
|
? BOT_SCOPED_TOOLS.filter(tool => EXTERNAL_TOOL_NAMES.has(tool.name))
|
|
883
903
|
: profile.coordinating
|
|
884
|
-
?
|
|
904
|
+
? ROLE_TOOLS.filter(tool => !ROOM_REPLACED_TOOLS.has(tool.name) || (tool.name === "start_thread" && profile.ownThreadCreation))
|
|
885
905
|
.map(tool => tool.name === "start_thread" ? {
|
|
886
906
|
...tool,
|
|
887
907
|
description: "Open a separate job on yourself with its own history and run, without switching the person's selected conversation. Use only when the user requests independent jobs (for example one review per pull request). Give a short specific title and complete instructions; you can open at most five per turn. This is not a teammate handoff: use coordinate_bots for teammates and their automatic replies. Self-opened jobs cannot recursively open more jobs. If refused, do not retry; explain what remains.",
|
|
@@ -889,5 +909,5 @@ function catalogTools(profile) {
|
|
|
889
909
|
bot_id: { type: "string", enum: [profile.botId], description: "Leave out, or use your own bot ID. For teammates use coordinate_bots." },
|
|
890
910
|
} },
|
|
891
911
|
} : tool)
|
|
892
|
-
:
|
|
912
|
+
: ROLE_TOOLS.filter(tool => !ROOM_ONLY_TOOLS.has(tool.name));
|
|
893
913
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// OMB_COMMS_TOKEN shared secret for the localhost-only internal endpoints
|
|
13
13
|
// OMB_TURN_DEPTH this turn's comms depth (the harness refuses recursion)
|
|
14
14
|
// OMB_EXTERNAL_RUNTIME "1" for a standing process: peer tools and polling only
|
|
15
|
+
// OMB_CHIEF_OF_STAFF "1" for a Chief of Staff, the only bot shown the Chief-only tools
|
|
15
16
|
//
|
|
16
17
|
// This file is the stdio front end only. What the tools are and which a turn
|
|
17
18
|
// sees: agents-catalog.ts. What a call does: agents-call.ts. How the harness
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AntigravityDriver } from "./antigravity.js";
|
|
2
|
-
import { BoatAgentDriver } from "./boatagent.js";
|
|
3
2
|
import { ClaudeDriver } from "./claude.js";
|
|
4
3
|
import { CodexDriver } from "./codex.js";
|
|
5
4
|
import { GrokDriver } from "./grok.js";
|
|
@@ -33,7 +32,6 @@ export const BUILT_IN_DRIVERS = [
|
|
|
33
32
|
ClaudeDriver,
|
|
34
33
|
CodexDriver,
|
|
35
34
|
AntigravityDriver,
|
|
36
|
-
BoatAgentDriver,
|
|
37
35
|
MinimaxDriver,
|
|
38
36
|
MistralDriver,
|
|
39
37
|
CerebrasDriver,
|
|
@@ -14,7 +14,9 @@ import { allowsTool, canUseMcpServer, parseToolScope } from "../../shared/tool-s
|
|
|
14
14
|
export class ChatToolSessionError extends Error {
|
|
15
15
|
}
|
|
16
16
|
const STARTUP_MS = 8_000;
|
|
17
|
-
|
|
17
|
+
/** Historic per-call ceiling for a bot's MCP tools; a turn can override it
|
|
18
|
+
* with SendTurnInput.mcpCallTimeoutMs (server config `mcp.callTimeoutMinutes`). */
|
|
19
|
+
const DEFAULT_CALL_MS = 10 * 60_000;
|
|
18
20
|
const FRAME_BYTES = 2 * 1024 * 1024;
|
|
19
21
|
const OUTPUT_BYTES = 50 * 1024;
|
|
20
22
|
const TOOL_COUNT = 128;
|
|
@@ -283,7 +285,7 @@ function omitBlankDefaults(builtInBrowser, tool, args) {
|
|
|
283
285
|
delete args[field];
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
|
-
export async function mountChatTools(integrations, signal, computerUse = false, toolScope) {
|
|
288
|
+
export async function mountChatTools(integrations, signal, computerUse = false, toolScope, callTimeoutMs = DEFAULT_CALL_MS) {
|
|
287
289
|
const parsed = parseToolScope(toolScope);
|
|
288
290
|
if (!parsed.ok)
|
|
289
291
|
throw new Error(parsed.error);
|
|
@@ -411,7 +413,7 @@ export async function mountChatTools(integrations, signal, computerUse = false,
|
|
|
411
413
|
}
|
|
412
414
|
const tool = registered.get(name);
|
|
413
415
|
try {
|
|
414
|
-
const result = await tool.client.call("tools/call", { name: tool.name, arguments: args }, AbortSignal.any([signal, callSignal]),
|
|
416
|
+
const result = await tool.client.call("tools/call", { name: tool.name, arguments: args }, AbortSignal.any([signal, callSignal]), callTimeoutMs);
|
|
415
417
|
if (signal.aborted || callSignal.aborted)
|
|
416
418
|
throw aborted();
|
|
417
419
|
if (!object(result) || !Array.isArray(result.content) || (result.isError !== undefined && typeof result.isError !== "boolean"))
|
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { stripVTControlCharacters } from "node:util";
|
|
5
5
|
import { killCliTree, spawnCli } from "../procs.js";
|
|
6
|
-
import { canonicalPath } from "./
|
|
6
|
+
import { canonicalPath } from "./device-auth.js";
|
|
7
7
|
const MAX_OUTPUT = 32_768;
|
|
8
8
|
/** Where Anthropic's sign-in lives. Anything else in the CLI's output is not a link we show. */
|
|
9
9
|
const SIGN_IN_HOSTS = ["claude.com", "claude.ai", "console.anthropic.com", "platform.claude.com"];
|
|
@@ -15,7 +15,7 @@ import { createServer as createNetServer } from "node:net";
|
|
|
15
15
|
import { homedir, tmpdir } from "node:os";
|
|
16
16
|
import { join, dirname, isAbsolute, normalize } from "node:path";
|
|
17
17
|
import { DATA_DIR, stripWorkspaceCredentialEnv } from "../config.js";
|
|
18
|
-
import { writeFileAtomic } from "../atomic.js";
|
|
18
|
+
import { writeFileAtomic, writeFileAtomicIfChanged } from "../atomic.js";
|
|
19
19
|
import { augmentedPath } from "../env-path.js";
|
|
20
20
|
import { brokerSocketPath, describeSpawnFailure, execCli, killCliTree, spawnCli } from "../procs.js";
|
|
21
21
|
import { classifyResumeFailure, mayReplay, recoveryPromptFor } from "../resume-recovery.js";
|
|
@@ -599,6 +599,9 @@ export async function createPermissionBroker(opts) {
|
|
|
599
599
|
let boundPath = opts.socketPaths[0] ?? "";
|
|
600
600
|
const connectionHandler = (conn) => {
|
|
601
601
|
conn.on("error", () => { });
|
|
602
|
+
// An ask carries the whole tool input (a Write's file content), so one
|
|
603
|
+
// line spans many reads. Decode as a stream so no character is split.
|
|
604
|
+
conn.setEncoding("utf8");
|
|
602
605
|
let buf = "";
|
|
603
606
|
conn.on("data", (chunk) => {
|
|
604
607
|
buf += chunk;
|
|
@@ -932,8 +935,10 @@ function recordCostState(sessionId, state) {
|
|
|
932
935
|
const ids = Object.keys(history);
|
|
933
936
|
for (const id of ids.slice(0, Math.max(0, ids.length - COST_HISTORY_SESSIONS)))
|
|
934
937
|
delete history[id];
|
|
938
|
+
// Written after every turn, so not fsynced: what a power cut could lose is
|
|
939
|
+
// the same thing a failed write already gives up (below).
|
|
935
940
|
try {
|
|
936
|
-
writeFileAtomic(COST_HISTORY_FILE, JSON.stringify(history), { mode: 0o600 });
|
|
941
|
+
writeFileAtomic(COST_HISTORY_FILE, JSON.stringify(history), { mode: 0o600, durable: false });
|
|
937
942
|
}
|
|
938
943
|
catch {
|
|
939
944
|
// a lost state only means a later resume keeps its whole figure
|
|
@@ -1051,6 +1056,26 @@ export const ClaudeDriver = {
|
|
|
1051
1056
|
const readCliVersion = (env) => new Promise((resolve) => {
|
|
1052
1057
|
execCli(config.cli, ["--version"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout.trim() || null));
|
|
1053
1058
|
});
|
|
1059
|
+
// The --help read while it runs: snapshots that overlap (the server's
|
|
1060
|
+
// read at start and the app's first one) share it.
|
|
1061
|
+
let helpRead = null;
|
|
1062
|
+
const readCliHelp = (version, env) => {
|
|
1063
|
+
if (helpRead?.version === version)
|
|
1064
|
+
return helpRead.done;
|
|
1065
|
+
const read = {
|
|
1066
|
+
version,
|
|
1067
|
+
done: new Promise((resolve) => {
|
|
1068
|
+
execCli(config.cli, ["--help"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout));
|
|
1069
|
+
}).then((help) => {
|
|
1070
|
+
cliHasAutocompact = help === null ? null : /^\s*--autocompact\b/m.test(help);
|
|
1071
|
+
cliHelpVersion = version;
|
|
1072
|
+
if (helpRead === read)
|
|
1073
|
+
helpRead = null;
|
|
1074
|
+
}),
|
|
1075
|
+
};
|
|
1076
|
+
helpRead = read;
|
|
1077
|
+
return read.done;
|
|
1078
|
+
};
|
|
1054
1079
|
const listeners = new Set();
|
|
1055
1080
|
// one active turn per thread; a second send while busy is a caller bug
|
|
1056
1081
|
const active = new Map();
|
|
@@ -1145,8 +1170,20 @@ export const ClaudeDriver = {
|
|
|
1145
1170
|
// logical turn's stop handle in `active` while it sets up, so Stop is
|
|
1146
1171
|
// never a silent no-op between two CLI processes of the same turn.
|
|
1147
1172
|
const relaunch = logicalTurnId !== undefined;
|
|
1148
|
-
if (
|
|
1149
|
-
|
|
1173
|
+
if (!relaunch) {
|
|
1174
|
+
// Stop is acknowledged at once and the process tree reaped after it
|
|
1175
|
+
// (taskkill is asynchronous on Windows); the stopped turn leaves
|
|
1176
|
+
// `active` only when its close is handled. A Stop that lands while
|
|
1177
|
+
// a turn is still starting lets the harness send the next message
|
|
1178
|
+
// before then. That turn waits for the stopped process to be gone,
|
|
1179
|
+
// never overlapping its helpers, instead of being refused.
|
|
1180
|
+
const stopped = sessions.get(threadId);
|
|
1181
|
+
if (stopped?.turn?.stopRequested && active.get(threadId)?.turnId === stopped.turn.turnId &&
|
|
1182
|
+
await killCliTree(stopped.child))
|
|
1183
|
+
await stopped.closed;
|
|
1184
|
+
if (active.has(threadId))
|
|
1185
|
+
throw new Error("a turn is already running on this thread");
|
|
1186
|
+
}
|
|
1150
1187
|
// A bot-level mode is authoritative for this turn. In particular, an
|
|
1151
1188
|
// old provider instance may still be configured with
|
|
1152
1189
|
// `bypassPermissions`; Ask/Auto must restore Claude's interactive
|
|
@@ -1399,14 +1436,18 @@ export const ClaudeDriver = {
|
|
|
1399
1436
|
? readClaudeAuthSettings(env, input.environment) : {};
|
|
1400
1437
|
// Harness hooks (item 0.2): one helper command for the events the
|
|
1401
1438
|
// harness observes. The helper reads its bearer from a per-thread file
|
|
1402
|
-
// the harness
|
|
1439
|
+
// the harness refreshes every turn, so a long-lived CLI process never
|
|
1403
1440
|
// presents a stale token. Registered through the same private
|
|
1404
1441
|
// --settings file as the auth override; both are 0600 and per launch.
|
|
1405
1442
|
const hooks = turn.integrations?.hooks;
|
|
1406
1443
|
const hookTokenPath = hooks ? hookTokenFile(threadId, botId) : null;
|
|
1407
1444
|
if (hooks && hookTokenPath) {
|
|
1408
1445
|
mkdirSync(dirname(hookTokenPath), { recursive: true, mode: 0o700 });
|
|
1409
|
-
|
|
1446
|
+
// The bearer is usually the same as last turn, and it only lives in
|
|
1447
|
+
// this process's memory, so a restart invalidates the file anyway:
|
|
1448
|
+
// skip identical bytes and the fsync. A rotated bearer differs from
|
|
1449
|
+
// what is on disk, so it is always written before the CLI launches.
|
|
1450
|
+
writeFileAtomicIfChanged(hookTokenPath, hooks.token, { mode: 0o600, durable: false });
|
|
1410
1451
|
env.OMB_HOOK_URL = hooks.url;
|
|
1411
1452
|
env.OMB_HOOK_TOKEN_FILE = hookTokenPath;
|
|
1412
1453
|
env.OMB_HOOK_NODE = process.execPath;
|
|
@@ -1459,7 +1500,7 @@ export const ClaudeDriver = {
|
|
|
1459
1500
|
if (!turn.sessionReset && live && !live.turn && !live.closing && live.child.exitCode === null && live.argsKey === argsKey && (!sessionId || sessionId === live.sessionId)) {
|
|
1460
1501
|
if (live.idleTimer)
|
|
1461
1502
|
clearTimeout(live.idleTimer);
|
|
1462
|
-
live.turn = { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false, pendingSteers: new Set(), deferred: null, continuationGrace: null, continuationSilence: null };
|
|
1503
|
+
live.turn = { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false, awaitingInit: true, pendingSteers: new Set(), deferred: null, continuationGrace: null, continuationSilence: null };
|
|
1463
1504
|
active.set(threadId, { stop: () => {
|
|
1464
1505
|
if (live.turn)
|
|
1465
1506
|
live.turn.stopRequested = true;
|
|
@@ -1475,20 +1516,13 @@ export const ClaudeDriver = {
|
|
|
1475
1516
|
: claudeUserMessage(withVolatileNote(turn.text, volatile), turn.images);
|
|
1476
1517
|
live.volatile = volatile;
|
|
1477
1518
|
const running = live.turn;
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1519
|
+
// A warm process can end between turns just as the next one is
|
|
1520
|
+
// written: this driver learns of an exit only when the event loop
|
|
1521
|
+
// gets to it (later still on Windows), so the check above can find it
|
|
1522
|
+
// live and the write then fail. Nothing was submitted; its close
|
|
1523
|
+
// resumes this same turn on a fresh process (see `awaitingInit`).
|
|
1524
|
+
if (!(await writeUser(live, threadId, message)) && !running?.stopRequested)
|
|
1482
1525
|
closeSession(threadId, "stdin write failed");
|
|
1483
|
-
retryState.delete(threadId);
|
|
1484
|
-
if (mcpConfigPath) {
|
|
1485
|
-
try {
|
|
1486
|
-
rmSync(dirname(mcpConfigPath), { recursive: true, force: true });
|
|
1487
|
-
}
|
|
1488
|
-
catch { }
|
|
1489
|
-
}
|
|
1490
|
-
throw new Error("claude session stdin is not writable");
|
|
1491
|
-
}
|
|
1492
1526
|
// the MCP config was for the first spawn; nothing to clean here
|
|
1493
1527
|
if (mcpConfigPath) {
|
|
1494
1528
|
try {
|
|
@@ -1630,7 +1664,9 @@ export const ClaudeDriver = {
|
|
|
1630
1664
|
cleanupUnownedLaunch();
|
|
1631
1665
|
throw error;
|
|
1632
1666
|
}
|
|
1667
|
+
let markClosed;
|
|
1633
1668
|
const session = {
|
|
1669
|
+
closed: new Promise((resolve) => { markClosed = resolve; }),
|
|
1634
1670
|
child,
|
|
1635
1671
|
broker,
|
|
1636
1672
|
mcpConfigPath,
|
|
@@ -1749,6 +1785,8 @@ export const ClaudeDriver = {
|
|
|
1749
1785
|
break;
|
|
1750
1786
|
}
|
|
1751
1787
|
session.sawInit = true;
|
|
1788
|
+
if (session.turn)
|
|
1789
|
+
session.turn.awaitingInit = false;
|
|
1752
1790
|
session.nativePermissionMode = typeof o.permissionMode === "string" ? o.permissionMode : null;
|
|
1753
1791
|
if (typeof o.session_id === "string")
|
|
1754
1792
|
session.sessionId = o.session_id;
|
|
@@ -2051,11 +2089,18 @@ export const ClaudeDriver = {
|
|
|
2051
2089
|
const retry = retryState.get(threadId) ?? { attempt: 0, cancelled: false };
|
|
2052
2090
|
const message = `claude exited ${code} before result${session.stderr ? `: ${session.stderr.trim().slice(-300)}` : ""}`;
|
|
2053
2091
|
const verdict = classifyError({ exitCode: code, stderr: message });
|
|
2092
|
+
// A process kept warm from an earlier turn ended before it
|
|
2093
|
+
// announced this one: it never took the prompt. That is a session
|
|
2094
|
+
// ending between turns, not a failed turn, so the same turn
|
|
2095
|
+
// resumes the session on a fresh process at once — no retry row,
|
|
2096
|
+
// no retry budget spent. (A fresh process is never retained, so
|
|
2097
|
+
// this happens at most once per turn.)
|
|
2098
|
+
const endedBeforeTurn = session.turn.awaitingInit === true;
|
|
2054
2099
|
if (!retry.cancelled &&
|
|
2055
|
-
code !== 0 &&
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2100
|
+
(endedBeforeTurn || (code !== 0 &&
|
|
2101
|
+
verdict.transient &&
|
|
2102
|
+
!session.turn.sawStreamDelta &&
|
|
2103
|
+
retry.attempt < RETRY_MAX_ATTEMPTS - 1))) {
|
|
2059
2104
|
// the CLI is gone but the TURN continues: keep the thread busy,
|
|
2060
2105
|
// emit no terminal event, and relaunch after the backoff. The
|
|
2061
2106
|
// `active` entry STAYS — it is what makes an interrupt during
|
|
@@ -2077,15 +2122,18 @@ export const ClaudeDriver = {
|
|
|
2077
2122
|
}
|
|
2078
2123
|
sessions.delete(threadId);
|
|
2079
2124
|
session.turn = null;
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2125
|
+
let delayMs = 0;
|
|
2126
|
+
if (!endedBeforeTurn) {
|
|
2127
|
+
retry.attempt++;
|
|
2128
|
+
delayMs = computeBackoff(retry.attempt - 1);
|
|
2129
|
+
emit({
|
|
2130
|
+
...base(threadId, turnId),
|
|
2131
|
+
type: "turn.retrying",
|
|
2132
|
+
attempt: retry.attempt,
|
|
2133
|
+
delayMs,
|
|
2134
|
+
reason: verdict.reason,
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2089
2137
|
void (async () => {
|
|
2090
2138
|
const wait = interruptibleDelay(delayMs * retryScale, retryAbort.signal);
|
|
2091
2139
|
await wait.promise;
|
|
@@ -2225,7 +2273,7 @@ export const ClaudeDriver = {
|
|
|
2225
2273
|
};
|
|
2226
2274
|
child.on("close", (code) => {
|
|
2227
2275
|
session.finishClose = () => finalizeClose(code);
|
|
2228
|
-
void session.finishClose();
|
|
2276
|
+
void session.finishClose().finally(markClosed);
|
|
2229
2277
|
});
|
|
2230
2278
|
const stop = () => {
|
|
2231
2279
|
if (session.turn)
|
|
@@ -2284,13 +2332,8 @@ export const ClaudeDriver = {
|
|
|
2284
2332
|
return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` };
|
|
2285
2333
|
cliVersion = parseClaudeCliVersion(version);
|
|
2286
2334
|
cliVersionChecked = true;
|
|
2287
|
-
if (version !== cliHelpVersion)
|
|
2288
|
-
|
|
2289
|
-
execCli(config.cli, ["--help"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout));
|
|
2290
|
-
});
|
|
2291
|
-
cliHasAutocompact = help === null ? null : /^\s*--autocompact\b/m.test(help);
|
|
2292
|
-
cliHelpVersion = version;
|
|
2293
|
-
}
|
|
2335
|
+
if (version !== cliHelpVersion)
|
|
2336
|
+
await readCliHelp(version, env);
|
|
2294
2337
|
const update = claudeCliUpdate(version, config.cli);
|
|
2295
2338
|
const warning = claudeInheritWarning(env);
|
|
2296
2339
|
if (config.requireApiKey) {
|