openmausbot 0.1.86 → 0.1.87
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 +2 -2
- package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-BTiuL79F.js} +1 -1
- package/dist/assets/{arc-DW7mrI4p.js → arc-C88Blc2v.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-BZsJIZrb.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CVeMe9Ah.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-Cedk3Bhx.js} +1 -1
- package/dist/assets/channel-taCNRlUU.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-D-VRqzTB.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-BChov01Y.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-C-TfwvHy.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-Db7CGj5Y.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-CNvIfqKT.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-C_bEp5tq.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Coi4BOls.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-CzylEuL3.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-m7p7ztaD.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BMy6o9x2.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-BDQ__0_J.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Djv4ghUb.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-BULrlGYH.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-D-jrT_hU.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-p2UhQmfL.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-DHvGWZ7q.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-ChhKDot-.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-B-T7A43R.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-CJm8ENNY.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-510tlza8.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-PkvRPHel.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-BVBVPoX3.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BgYMVm7L.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-BGqsnc_3.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-D_-UeReH.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-D0uooONx.js} +1 -1
- package/dist/assets/index-BwdMAQ6p.css +1 -0
- package/dist/assets/index-ByJennr7.js +581 -0
- package/dist/assets/{index-h9FFNwTu.js → index-zRsJERGJ.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-B9e1z77f.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-BB4vNwvD.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DpkosOTB.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-DbDP4kUu.js} +1 -1
- package/dist/assets/{linear-3su2TG93.js → linear-DBJGPmjT.js} +1 -1
- package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-DWPRi-ue.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-nszBbJmx.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-CWykJWu_.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-g3Da-Vnv.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-Wzrlpzgh.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-moHTFMC0.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-CwfXdFH8.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-3ZROYvnq.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-E1TBebP8.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-DHKEU5pu.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BguEj09D.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-C7IDYnhd.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-DCW-F5PW.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-xBMFtWYV.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-B5nIkuH8.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-DYFAhdbr.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-DeXwX6Cz.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/container-mcp.js +1029 -164
- package/dist-server/drivers/agents-proxy.js +64 -10
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/index.js +14577 -9001
- package/dist-server/local-computer-proxy.js +3 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/openmausbot.js +3981 -1165
- package/dist-server/pair-cli.js +3981 -1165
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/attachments.js +55 -0
- package/dist-server/server/auto-approve.js +4 -3
- package/dist-server/server/auto-vm-claims.js +1 -0
- package/dist-server/server/bot-package.js +18 -292
- package/dist-server/server/bot-profile.js +5 -17
- package/dist-server/server/box-create-idempotency.js +3 -3
- package/dist-server/server/box.js +57 -55
- package/dist-server/server/browser-proxy.js +3 -2
- package/dist-server/server/browser-runtime.js +37 -1
- package/dist-server/server/cli.js +2 -2
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +59 -9
- package/dist-server/server/connector-verdict.js +141 -0
- package/dist-server/server/container-computer.js +1 -1
- package/dist-server/server/default-model-selection.js +9 -0
- package/dist-server/server/delegations.js +85 -34
- package/dist-server/server/drivers/acp/core.js +57 -15
- package/dist-server/server/drivers/agents-call.js +42 -3
- package/dist-server/server/drivers/agents-catalog.js +34 -4
- package/dist-server/server/drivers/boxagent.js +174 -36
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/chat-box-tools.js +90 -0
- package/dist-server/server/drivers/chat-images.js +54 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +38 -8
- package/dist-server/server/drivers/chat-tool-approval.js +70 -18
- package/dist-server/server/drivers/claude.js +7 -2
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +95 -30
- package/dist-server/server/drivers/mistral.js +115 -0
- package/dist-server/server/drivers/openai-chat.js +155 -32
- package/dist-server/server/drivers/openai-compat.js +1 -0
- package/dist-server/server/drivers/permission-command.js +35 -0
- package/dist-server/server/drivers/phone-proxy.js +47 -2
- package/dist-server/server/drivers/pi.js +75 -4
- package/dist-server/server/drivers/prompt-split.js +87 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/hosted-models.js +1 -1
- package/dist-server/server/index.js +1385 -322
- package/dist-server/server/local-computer.js +72 -23
- package/dist-server/server/managed-desktop.js +1 -1
- package/dist-server/server/mcp-bridge.js +6 -6
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/org-library.js +1051 -0
- package/dist-server/server/package-export.js +627 -39
- package/dist-server/server/package-import.js +458 -0
- package/dist-server/server/package-parts.js +87 -0
- package/dist-server/server/peer-approval.js +16 -7
- package/dist-server/server/peer-roster.js +10 -0
- package/dist-server/server/presets.js +492 -0
- package/dist-server/server/provider-key-check.js +3 -2
- package/dist-server/server/published-teams.js +80 -0
- package/dist-server/server/room-handoffs.js +19 -3
- package/dist-server/server/routes/bot-presets.js +25 -0
- package/dist-server/server/routine-requests.js +6 -3
- package/dist-server/server/routines.js +41 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skills.js +34 -61
- package/dist-server/server/steer-queue.js +3 -2
- package/dist-server/server/store.js +259 -15
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +12 -4
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-computers.js +10 -0
- package/dist-server/server/team-library.js +6 -2
- package/dist-server/server/team-manifest.js +4 -3
- package/dist-server/server/team-setup-requests.js +16 -5
- package/dist-server/server/thread-events.js +17 -1
- package/dist-server/server/turn-context.js +2 -0
- package/dist-server/server/turn-watchdog.js +12 -3
- package/dist-server/server/usage-ledger.js +12 -1
- package/dist-server/server/vps-computer.js +139 -51
- package/dist-server/server/vps-container-mcp.js +7 -0
- package/dist-server/server/vps-ssh.js +36 -7
- package/dist-server/server/workspace-backup-policy.js +12 -0
- package/dist-server/server/workspace-backup.js +2 -2
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/ask-question.js +188 -12
- package/dist-server/shared/bot-profile.js +15 -0
- package/dist-server/shared/command-allowlist.js +1 -0
- package/dist-server/shared/credential-request.js +1 -1
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +7 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/vps-container-mcp.js +1129 -170
- package/enterprise/README.md +1 -1
- package/package.json +1 -1
- package/dist/assets/channel-BXFkgus9.js +0 -1
- package/dist/assets/index-BGgqlWPJ.css +0 -1
- package/dist/assets/index-BtHo8ZKl.js +0 -577
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BWrvB2ak.js +0 -8
|
@@ -7,8 +7,17 @@
|
|
|
7
7
|
// that, name the holder as a bot running a thread, and keep the same words
|
|
8
8
|
// on every surface (chat chip, room chip, the gate's refusal) so a person
|
|
9
9
|
// who has read it once recognises it everywhere.
|
|
10
|
-
/** Whole minutes, or seconds under one minute,
|
|
11
|
-
const minutes = (ms) =>
|
|
10
|
+
/** Whole minutes, or seconds under one minute, singular at exactly one. */
|
|
11
|
+
const minutes = (ms) => {
|
|
12
|
+
const value = ms >= 60_000 ? Math.round(ms / 60_000) : Math.round(ms / 1000);
|
|
13
|
+
const unit = ms >= 60_000 ? "minute" : "second";
|
|
14
|
+
return `${value} ${unit}${value === 1 ? "" : "s"}`;
|
|
15
|
+
};
|
|
16
|
+
/** How long a wait lasted, for the resolution line: waits under a second
|
|
17
|
+
* are honest about being over in a blink instead of claiming "0 seconds". */
|
|
18
|
+
export function computerWaitDuration(ms) {
|
|
19
|
+
return ms < 1_000 ? "under a second" : minutes(ms);
|
|
20
|
+
}
|
|
12
21
|
const holderPhrase = (holder) => holder.task ? `${holder.name} is running ${holder.task}` : `${holder.name} is using it`;
|
|
13
22
|
/** The chip a turn shows while it is queued behind another turn's desktop. */
|
|
14
23
|
export function computerWaitingText(holder) {
|
|
@@ -16,14 +25,18 @@ export function computerWaitingText(holder) {
|
|
|
16
25
|
return "Waiting for its turn on this computer. Starts automatically when it is free.";
|
|
17
26
|
return `Waiting for its turn on this computer — ${holderPhrase(holder)}. Starts automatically when that finishes.`;
|
|
18
27
|
}
|
|
19
|
-
/** The
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
/** The resolution appended once the wait landed and this turn holds the
|
|
29
|
+
* desktop: how long it waited, and who held it. The waiting chip stays as
|
|
30
|
+
* written — this line is the history beside it, not its replacement. */
|
|
31
|
+
export function computerFreeAfterText(holder, waitedMs) {
|
|
32
|
+
const who = holder ? ` (${holder.task ? `${holder.name} · ${holder.task}` : holder.name} held it)` : "";
|
|
33
|
+
return `Computer free — continuing after waiting ${computerWaitDuration(waitedMs)}${who}`;
|
|
22
34
|
}
|
|
23
|
-
/** The
|
|
24
|
-
*
|
|
25
|
-
export function
|
|
26
|
-
|
|
35
|
+
/** The resolution appended when the wait ended because the turn was stopped
|
|
36
|
+
* or its claim was cancelled. */
|
|
37
|
+
export function computerStoppedWaitingText(holder, waitedMs) {
|
|
38
|
+
const who = holder ? ` — ${holderPhrase(holder)}` : "";
|
|
39
|
+
return `Stopped waiting for the computer after ${computerWaitDuration(waitedMs)}${who}.`;
|
|
27
40
|
}
|
|
28
41
|
/** The error after the wait ceiling: still names who holds it, and says
|
|
29
42
|
* what a person can do — stop that turn, or move this one. */
|
|
@@ -7,6 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { normalizeImageGenerationUrl } from "../shared/image-generation.js";
|
|
9
9
|
import { writeFileAtomic } from "./atomic.js";
|
|
10
|
+
import { newBotDefaultsSchema } from "./new-bot-defaults.js";
|
|
10
11
|
import { EFFORT_LEVELS } from "../shared/wire.js";
|
|
11
12
|
import { isModelVariant } from "./contracts.js";
|
|
12
13
|
import { PROVIDER_ICON_PRESETS, providerIconError } from "../shared/provider-icon.js";
|
|
@@ -289,8 +290,8 @@ const instanceConfigSchema = z.object({
|
|
|
289
290
|
});
|
|
290
291
|
const instanceConfigMapSchema = z.record(z.string(), instanceConfigSchema);
|
|
291
292
|
const defaultModelSelectionSchema = z.object({
|
|
292
|
-
instanceId: z.string().trim().min(1),
|
|
293
|
-
model: z.string().trim().min(1),
|
|
293
|
+
instanceId: z.string().trim().min(1).max(200),
|
|
294
|
+
model: z.string().trim().min(1).max(500),
|
|
294
295
|
effort: z.enum(EFFORT_LEVELS).optional(),
|
|
295
296
|
variant: z.string().refine(isModelVariant, "invalid model variant").optional(),
|
|
296
297
|
}).refine((selection) => selection.variant === undefined || selection.effort === undefined, "choose either a model variant or an effort level");
|
|
@@ -303,6 +304,15 @@ const threadsConfigSchema = z.object({
|
|
|
303
304
|
* Absent keeps them forever. */
|
|
304
305
|
eventLogRetentionDays: z.number().int().min(1).max(3650).optional(),
|
|
305
306
|
}).strict();
|
|
307
|
+
/** Workspace-wide defaults every new bot starts with (Store.createBot). */
|
|
308
|
+
const newBotsConfigSchema = z.object({
|
|
309
|
+
/** Effort for a new bot whose model selection names none. */
|
|
310
|
+
effort: z.enum(EFFORT_LEVELS).optional(),
|
|
311
|
+
}).strict();
|
|
312
|
+
/** PATCH newBots: null clears a default back to absent (no level is sent). */
|
|
313
|
+
const newBotsPatchSchema = z.object({
|
|
314
|
+
effort: newBotsConfigSchema.shape.effort.nullable(),
|
|
315
|
+
}).strict();
|
|
306
316
|
/** PATCH threads: every knob is independently patchable, and null clears an
|
|
307
317
|
* event-log knob back to its absent (off) default. */
|
|
308
318
|
const threadsPatchSchema = threadsConfigSchema.extend({
|
|
@@ -317,12 +327,15 @@ const appConfigSchema = z.object({
|
|
|
317
327
|
* addresses or `@domain` entries; admins get every scope, members chat only. */
|
|
318
328
|
signIn: z.object({ admins: z.array(z.string().max(320)).max(500).optional(), members: z.array(z.string().max(320)).max(5000).optional() }).optional(),
|
|
319
329
|
defaultModelSelection: defaultModelSelectionSchema.optional(),
|
|
330
|
+
newBotDefaults: newBotDefaultsSchema.optional(),
|
|
331
|
+
newBots: newBotsConfigSchema.optional(),
|
|
320
332
|
/** CLI-only launch preferences. Never enable remote access implicitly. */
|
|
321
333
|
cliStartup: z.object({
|
|
322
334
|
access: z.enum(["local", "tunnel", "tailscale", "public-url"]),
|
|
323
335
|
publicUrl: z.string().url().optional(),
|
|
324
336
|
phone: z.enum(["ios", "android"]).optional(),
|
|
325
337
|
}).optional(),
|
|
338
|
+
mistral: z.object({ key: optionalText }).optional(),
|
|
326
339
|
xai: z.object({ key: optionalText, url: optionalText }).optional(),
|
|
327
340
|
/** Anthropic API key for Claude Code billed per token, handed only to
|
|
328
341
|
* Claude instances; `url` only for a proxy or a test double. Never a
|
|
@@ -400,8 +413,8 @@ const appConfigSchema = z.object({
|
|
|
400
413
|
}).optional(),
|
|
401
414
|
customModel: z.string().trim().max(200).refine((value) => !["\r", "\n", "\0"].some((character) => value.includes(character)), "Use a model ID without control characters").optional(),
|
|
402
415
|
}).optional(),
|
|
403
|
-
/** Non-secret profile details
|
|
404
|
-
profile: z.object({ name: optionalText, email: optionalText }).optional(),
|
|
416
|
+
/** Non-secret profile details; aboutMe is shared with every bot. */
|
|
417
|
+
profile: z.object({ name: optionalText, email: optionalText, aboutMe: z.string().max(24_000).optional() }).optional(),
|
|
405
418
|
/** UI language override (BCP-47, lowercase). Empty/absent = follow the
|
|
406
419
|
* system language. Unknown tags degrade to English in the renderer. */
|
|
407
420
|
language: optionalText,
|
|
@@ -432,7 +445,7 @@ const storedAppConfigSchema = appConfigSchema.extend({
|
|
|
432
445
|
browserProfiles: storedBrowserProfilesSchema.optional(),
|
|
433
446
|
});
|
|
434
447
|
const appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true })
|
|
435
|
-
.extend({ threads: threadsPatchSchema.optional() });
|
|
448
|
+
.extend({ threads: threadsPatchSchema.optional(), newBots: newBotsPatchSchema.optional() });
|
|
436
449
|
const jsonObjectSchema = z.record(z.string(), z.json());
|
|
437
450
|
/** Resolve a canonical profile record to its exact durable Electron
|
|
438
451
|
* partition identity. Callers must never substitute the display/API id. */
|
|
@@ -685,6 +698,9 @@ export function loadConfig() {
|
|
|
685
698
|
// Anything that saves a credential mid-session must keep process.env in
|
|
686
699
|
// step (syncCredentialEnv below), or the value injected at boot would
|
|
687
700
|
// shadow the save until the next launch.
|
|
701
|
+
cfg.mistral = { ...cfg.mistral };
|
|
702
|
+
if (process.env.MISTRAL_API_KEY !== undefined)
|
|
703
|
+
cfg.mistral.key = process.env.MISTRAL_API_KEY;
|
|
688
704
|
cfg.xai = { ...cfg.xai };
|
|
689
705
|
if (process.env.XAI_API_KEY !== undefined)
|
|
690
706
|
cfg.xai.key = process.env.XAI_API_KEY;
|
|
@@ -746,6 +762,7 @@ export function loadConfig() {
|
|
|
746
762
|
export function syncCredentialEnv(patch) {
|
|
747
763
|
const secrets = [
|
|
748
764
|
[patch.xai?.key, "XAI_API_KEY"],
|
|
765
|
+
[patch.mistral?.key, "MISTRAL_API_KEY"],
|
|
749
766
|
[patch.anthropic?.key, "OMB_ANTHROPIC_API_KEY"],
|
|
750
767
|
[patch.openaiCompat?.key, "OPENAI_COMPAT_API_KEY"],
|
|
751
768
|
[patch.composio?.apiKey, "COMPOSIO_API_KEY"],
|
|
@@ -788,6 +805,7 @@ export function syncCredentialEnv(patch) {
|
|
|
788
805
|
* child these are someone else's keys riding along in `...process.env`. */
|
|
789
806
|
export const WORKSPACE_CREDENTIAL_ENV = [
|
|
790
807
|
"XAI_API_KEY",
|
|
808
|
+
"MISTRAL_API_KEY",
|
|
791
809
|
"OMB_ANTHROPIC_API_KEY",
|
|
792
810
|
"OMB_ANTHROPIC_API_URL",
|
|
793
811
|
"OMB_HOSTED_MODEL_TOKEN",
|
|
@@ -850,6 +868,7 @@ export const PROVIDER_CREDENTIAL_ENV = [
|
|
|
850
868
|
"OPENAI_API_KEY",
|
|
851
869
|
"OPENCODE_API_KEY",
|
|
852
870
|
"XAI_API_KEY",
|
|
871
|
+
"MISTRAL_API_KEY",
|
|
853
872
|
"CURSOR_API_KEY",
|
|
854
873
|
"CURSOR_AUTH_TOKEN",
|
|
855
874
|
];
|
|
@@ -874,7 +893,7 @@ export function saveConfig(patch, options = {}) {
|
|
|
874
893
|
catch {
|
|
875
894
|
/* first write */
|
|
876
895
|
}
|
|
877
|
-
const checkedPatch = appConfigSchema.partial().extend({ threads: threadsPatchSchema.optional() }).parse(patch);
|
|
896
|
+
const checkedPatch = appConfigSchema.partial().extend({ threads: threadsPatchSchema.optional(), newBots: newBotsPatchSchema.optional() }).parse(patch);
|
|
878
897
|
const before = configSaveListeners.size ? structuredClone(disk) : disk;
|
|
879
898
|
// A write is the durable migration point. Preserve every other raw key in
|
|
880
899
|
// config.json, but never write #567's mixed-case or duplicate profile ids
|
|
@@ -882,7 +901,7 @@ export function saveConfig(patch, options = {}) {
|
|
|
882
901
|
const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
|
|
883
902
|
if (storedProfiles.success)
|
|
884
903
|
disk.browserProfiles = storedProfiles.data;
|
|
885
|
-
for (const key of ["xai", "anthropic", "openaiCompat", "composio", "box", "opencodeGo", "tts", "imageGen", "profile", "rooms", "threads", "context", "localVm", "features", "budgets", "billing", "decisions", "onboarding", "browserEngine"]) {
|
|
904
|
+
for (const key of ["xai", "anthropic", "mistral", "openaiCompat", "composio", "box", "opencodeGo", "tts", "imageGen", "profile", "rooms", "threads", "context", "localVm", "features", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
|
|
886
905
|
const section = checkedPatch[key];
|
|
887
906
|
if (!section)
|
|
888
907
|
continue;
|
|
@@ -894,8 +913,9 @@ export function saveConfig(patch, options = {}) {
|
|
|
894
913
|
if (key === "threads" && !current.success)
|
|
895
914
|
merged.maxConcurrentPerBot = DEFAULT_MAX_CONCURRENT_BOT_THREADS;
|
|
896
915
|
Object.assign(merged, section);
|
|
897
|
-
// null is the patch's explicit "remove this key" marker (
|
|
898
|
-
//
|
|
916
|
+
// null is the patch's explicit "remove this key" marker (the threads
|
|
917
|
+
// event-log knobs and newBots.effort use it); a key the patch omits
|
|
918
|
+
// keeps its value.
|
|
899
919
|
for (const [sectionKey, sectionValue] of Object.entries(section)) {
|
|
900
920
|
if (sectionValue === null)
|
|
901
921
|
delete merged[sectionKey];
|
|
@@ -915,6 +935,24 @@ export function saveConfig(patch, options = {}) {
|
|
|
915
935
|
// an effort level omitted from the new selection.
|
|
916
936
|
if (checkedPatch.defaultModelSelection !== undefined) {
|
|
917
937
|
disk.defaultModelSelection = checkedPatch.defaultModelSelection;
|
|
938
|
+
if (disk.newBotDefaults) {
|
|
939
|
+
const previousDefaults = newBotDefaultsSchema.parse(disk.newBotDefaults);
|
|
940
|
+
disk.newBotDefaults = newBotDefaultsSchema.parse({
|
|
941
|
+
...previousDefaults,
|
|
942
|
+
profile: { ...previousDefaults.profile, modelSelection: checkedPatch.defaultModelSelection },
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
// Replace the complete template so clearing a field, file or routine
|
|
947
|
+
// cannot revive an old value through the general section merge above.
|
|
948
|
+
if (checkedPatch.newBotDefaults !== undefined) {
|
|
949
|
+
disk.newBotDefaults = checkedPatch.newBotDefaults;
|
|
950
|
+
if (checkedPatch.newBotDefaults.profile.modelSelection) {
|
|
951
|
+
disk.defaultModelSelection = checkedPatch.newBotDefaults.profile.modelSelection;
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
delete disk.defaultModelSelection;
|
|
955
|
+
}
|
|
918
956
|
}
|
|
919
957
|
if (checkedPatch.cliStartup !== undefined)
|
|
920
958
|
disk.cliStartup = checkedPatch.cliStartup;
|
|
@@ -938,6 +976,14 @@ export function saveConfig(patch, options = {}) {
|
|
|
938
976
|
if (routingConflict)
|
|
939
977
|
throw Object.assign(new Error(routingConflict), { status: 409 });
|
|
940
978
|
disk.browserProfiles = nextProfiles;
|
|
979
|
+
if (disk.newBotDefaults) {
|
|
980
|
+
const defaults = newBotDefaultsSchema.parse(disk.newBotDefaults);
|
|
981
|
+
const profileId = defaults.profile.browserProfile;
|
|
982
|
+
if (profileId && profileId !== "guest" && !nextProfiles.some(profile => profile.id === profileId)) {
|
|
983
|
+
delete defaults.profile.browserProfile;
|
|
984
|
+
disk.newBotDefaults = defaults;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
941
987
|
}
|
|
942
988
|
if (checkedPatch.instances) {
|
|
943
989
|
const currentInstances = jsonObjectSchema.safeParse(disk.instances);
|
|
@@ -1046,6 +1092,8 @@ export function persistableInstanceConfigs(cfg) {
|
|
|
1046
1092
|
* environment of an unrelated child process. */
|
|
1047
1093
|
function injectedEnvironment(cfg, driver) {
|
|
1048
1094
|
const environment = new Map();
|
|
1095
|
+
if (driver === "mistral" && cfg.mistral?.key)
|
|
1096
|
+
environment.set("MISTRAL_API_KEY", cfg.mistral.key);
|
|
1049
1097
|
if (driver === "grok" && cfg.xai?.key)
|
|
1050
1098
|
environment.set("XAI_API_KEY", cfg.xai.key);
|
|
1051
1099
|
// The workspace Anthropic key reaches Claude Code as the variable it
|
|
@@ -1096,6 +1144,7 @@ export function instanceConfigs(cfg) {
|
|
|
1096
1144
|
opencodeGo: { driver: "opencodeGo" },
|
|
1097
1145
|
computer: { driver: "boxAgent" },
|
|
1098
1146
|
openaiCompat: { driver: "openai-compat" },
|
|
1147
|
+
mistral: { driver: "mistral" },
|
|
1099
1148
|
qwen: { driver: "qwenAgent" },
|
|
1100
1149
|
hermes: { driver: "hermesAgent" },
|
|
1101
1150
|
pi: { driver: "piAgent" },
|
|
@@ -1111,6 +1160,7 @@ export function instanceConfigs(cfg) {
|
|
|
1111
1160
|
const PRODUCT_FLEET_ADDITIONS = {
|
|
1112
1161
|
cursor: { driver: "cursorAgent" },
|
|
1113
1162
|
openaiCompat: { driver: "openai-compat" },
|
|
1163
|
+
mistral: { driver: "mistral" },
|
|
1114
1164
|
...CUSTOM_ONLY,
|
|
1115
1165
|
};
|
|
1116
1166
|
const configured = cfg.instances && Object.keys(cfg.instances).length ? cfg.instances : null;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// The harness-side verdict for connected-app tool calls (issue #1736, part
|
|
2
|
+
// of the per-bot Composio tool grants umbrella #1734).
|
|
3
|
+
//
|
|
4
|
+
// Every Composio tool call crosses POST /api/internal/connectors/mcp: the
|
|
5
|
+
// provider CLIs talk to the stdio bridge, and the bridge forwards each
|
|
6
|
+
// JSON-RPC frame there. So the grants verdict lives on the harness side,
|
|
7
|
+
// where the loopback token already names the bot and the decision log is
|
|
8
|
+
// native — the bridge never sees a key it could have to enforce with.
|
|
9
|
+
//
|
|
10
|
+
// Three shapes reach this module:
|
|
11
|
+
//
|
|
12
|
+
// * a direct per-toolkit call — params.name is the tool itself, e.g.
|
|
13
|
+
// GMAIL_SEND_EMAIL;
|
|
14
|
+
// * the executor meta-tool — COMPOSIO_MULTI_EXECUTE_TOOL, whose
|
|
15
|
+
// arguments name one or many target tools;
|
|
16
|
+
// * everything else — connection cards, discovery, notifications —
|
|
17
|
+
// which passes through untouched (advertisement filtering is slice 3).
|
|
18
|
+
//
|
|
19
|
+
// Unrecognized argument shapes are denied by default: a call whose target
|
|
20
|
+
// tools cannot be read is a call whose tools cannot be checked, and the
|
|
21
|
+
// grants promise — "this bot may call exactly these tools" — must hold
|
|
22
|
+
// even against a model that garbles the protocol.
|
|
23
|
+
import { CONNECTOR_TOOL_NAME_PATTERN } from "../shared/wire.js";
|
|
24
|
+
/** The executor meta-tool: the only meta-tool whose arguments name target
|
|
25
|
+
* tools. Discovery and connection meta-tools keep their existing flows. */
|
|
26
|
+
export const COMPOSIO_MULTI_EXECUTE_TOOL = "COMPOSIO_MULTI_EXECUTE_TOOL";
|
|
27
|
+
/** The service a Composio tool name belongs to: the upper-snake prefix
|
|
28
|
+
* before the first underscore, lowercased (GMAIL_SEND_EMAIL to gmail). A
|
|
29
|
+
* name with no underscore names no service, so no grant can cover it. */
|
|
30
|
+
export function serviceSlugFor(tool) {
|
|
31
|
+
const underscore = tool.indexOf("_");
|
|
32
|
+
if (underscore <= 0)
|
|
33
|
+
return null;
|
|
34
|
+
return tool.slice(0, underscore).toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
/** Classify one relayed JSON-RPC frame. Anything that is not a tools/call,
|
|
37
|
+
* or is a discovery/connection/platform meta-tool, passes through. */
|
|
38
|
+
export function connectorCallFromFrame(payload) {
|
|
39
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
40
|
+
return { kind: "passthrough" };
|
|
41
|
+
const frame = payload;
|
|
42
|
+
if (frame.method !== "tools/call")
|
|
43
|
+
return { kind: "passthrough" };
|
|
44
|
+
const params = frame.params;
|
|
45
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
46
|
+
return { kind: "unrecognized", invoked: "tools/call", reason: "the call carried no params object" };
|
|
47
|
+
}
|
|
48
|
+
const name = params.name;
|
|
49
|
+
if (typeof name !== "string" || !name) {
|
|
50
|
+
return { kind: "unrecognized", invoked: "tools/call", reason: "the call named no tool" };
|
|
51
|
+
}
|
|
52
|
+
if (name === COMPOSIO_MULTI_EXECUTE_TOOL) {
|
|
53
|
+
return multiExecuteCall(name, params.arguments);
|
|
54
|
+
}
|
|
55
|
+
// Platform meta-tools (search, schemas, remote workbench) are not
|
|
56
|
+
// connected-app tools; grants are keyed by service slug, and slice 3
|
|
57
|
+
// owns what the model is even shown.
|
|
58
|
+
if (name.startsWith("COMPOSIO_"))
|
|
59
|
+
return { kind: "passthrough" };
|
|
60
|
+
// Per-service connection cards keep their card flow in any spelling.
|
|
61
|
+
if (name.endsWith("_MANAGE_CONNECTIONS") || name.endsWith("_WAIT_FOR_CONNECTIONS")) {
|
|
62
|
+
return { kind: "passthrough" };
|
|
63
|
+
}
|
|
64
|
+
if (!CONNECTOR_TOOL_NAME_PATTERN.test(name)) {
|
|
65
|
+
return { kind: "unrecognized", invoked: name, reason: "the tool name is not a Composio tool name" };
|
|
66
|
+
}
|
|
67
|
+
return { kind: "tools", invoked: name, names: [name] };
|
|
68
|
+
}
|
|
69
|
+
/** Read the target tools out of COMPOSIO_MULTI_EXECUTE_TOOL arguments:
|
|
70
|
+
* the upstream schema is a 1-50 item tools array of { tool_slug,
|
|
71
|
+
* arguments }; a legacy single { tool_slug } object is accepted too.
|
|
72
|
+
* Anything else cannot be checked, so it is denied. */
|
|
73
|
+
function multiExecuteCall(invoked, args) {
|
|
74
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
75
|
+
return { kind: "unrecognized", invoked, reason: "the arguments were not an object" };
|
|
76
|
+
}
|
|
77
|
+
const tools = args.tools;
|
|
78
|
+
const singleSlug = args.tool_slug;
|
|
79
|
+
if (Array.isArray(tools)) {
|
|
80
|
+
if (tools.length === 0) {
|
|
81
|
+
return { kind: "unrecognized", invoked, reason: "the tools list was empty" };
|
|
82
|
+
}
|
|
83
|
+
const names = [];
|
|
84
|
+
for (const item of tools) {
|
|
85
|
+
const slug = item && typeof item === "object" && !Array.isArray(item)
|
|
86
|
+
? item.tool_slug
|
|
87
|
+
: undefined;
|
|
88
|
+
if (typeof slug !== "string" || !slug) {
|
|
89
|
+
return { kind: "unrecognized", invoked, reason: "an entry in the tools list named no tool_slug" };
|
|
90
|
+
}
|
|
91
|
+
names.push(slug);
|
|
92
|
+
}
|
|
93
|
+
return { kind: "tools", invoked, names };
|
|
94
|
+
}
|
|
95
|
+
if (typeof singleSlug === "string" && singleSlug) {
|
|
96
|
+
return { kind: "tools", invoked, names: [singleSlug] };
|
|
97
|
+
}
|
|
98
|
+
return { kind: "unrecognized", invoked, reason: "the arguments named no tools to execute" };
|
|
99
|
+
}
|
|
100
|
+
/** Judge distinct target names against a bot's grants. grants undefined
|
|
101
|
+
* is the legacy all-tools bot and passes everything; an explicit record —
|
|
102
|
+
* including the empty one — allows only what it names. */
|
|
103
|
+
export function evaluateConnectorTools(names, grants) {
|
|
104
|
+
if (grants === undefined) {
|
|
105
|
+
return { allowed: true, legacy: true, denials: [], rule: "composio" };
|
|
106
|
+
}
|
|
107
|
+
const denials = [];
|
|
108
|
+
let rule = "";
|
|
109
|
+
for (const tool of new Set(names)) {
|
|
110
|
+
const service = serviceSlugFor(tool);
|
|
111
|
+
const grant = service === null ? undefined : grants[service];
|
|
112
|
+
if (grant && (grant.tools === "*" || grant.tools.includes(tool))) {
|
|
113
|
+
if (!rule)
|
|
114
|
+
rule = "connectorTools." + service;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
denials.push({ tool, service, onGrantedService: Boolean(grant) });
|
|
118
|
+
}
|
|
119
|
+
return { allowed: denials.length === 0, legacy: false, denials, rule };
|
|
120
|
+
}
|
|
121
|
+
/** Safe to hand straight to the model: names the refused tool and says who
|
|
122
|
+
* can change it. It never lists what the bot could have called instead —
|
|
123
|
+
* a refusal that enumerates adjacent grants teaches the model to probe. */
|
|
124
|
+
export function connectorRefusalText(denials) {
|
|
125
|
+
const named = denials.map((denial) => '"' + denial.tool + '"').join(", ");
|
|
126
|
+
const one = denials.length === 1;
|
|
127
|
+
return [
|
|
128
|
+
named + " " + (one ? "is" : "are") + " not granted to this bot.",
|
|
129
|
+
"This call was not performed.",
|
|
130
|
+
"Ask the person to grant " + (one ? "it" : "these tools") + " in OpenMausBot if they want " + (one ? "it" : "them") + " run.",
|
|
131
|
+
].join(" ");
|
|
132
|
+
}
|
|
133
|
+
/** The unrecognized-shape refusal: names the meta-tool and what could not
|
|
134
|
+
* be read, and still points at the person rather than at any tool list. */
|
|
135
|
+
export function connectorUnrecognizedText(invoked, reason) {
|
|
136
|
+
return [
|
|
137
|
+
"A " + invoked + " call arrived in a shape OpenMausBot could not read (" + reason + "), so it was not performed.",
|
|
138
|
+
"Send a well-formed call that names the tools to execute.",
|
|
139
|
+
"Ask the person to grant any tool this bot needs.",
|
|
140
|
+
].join(" ");
|
|
141
|
+
}
|
|
@@ -310,7 +310,7 @@ function statusProblem(status) {
|
|
|
310
310
|
if (status.security === "unsafe")
|
|
311
311
|
return "The existing Local VM is missing safety limits; recreate it";
|
|
312
312
|
if (status.persistence === "unsafe")
|
|
313
|
-
return "The existing Local VM is missing its durable
|
|
313
|
+
return "The existing Local VM is missing its durable folder; recreate it";
|
|
314
314
|
if (status.container === "stopped")
|
|
315
315
|
return "This desktop image cannot safely resume; recreate the Local VM";
|
|
316
316
|
if (status.desktop_error)
|
|
@@ -37,3 +37,12 @@ export function selectDefaultModelSelection(instances, preferred, context = {})
|
|
|
37
37
|
}
|
|
38
38
|
return { instanceId: pick?.instanceId ?? "", model: pick?.models.default ?? "" };
|
|
39
39
|
}
|
|
40
|
+
/** Complete a new bot's selection with the workspace's new-bot effort. An
|
|
41
|
+
* explicit effort or model variant is the caller's choice and wins; an engine
|
|
42
|
+
* that does not offer the level keeps sending none rather than failing turn 1. */
|
|
43
|
+
export function withNewBotEffort(selection, effort, offered) {
|
|
44
|
+
if (!effort || selection.effort !== undefined || selection.variant !== undefined || !offered?.includes(effort)) {
|
|
45
|
+
return selection;
|
|
46
|
+
}
|
|
47
|
+
return { ...selection, effort };
|
|
48
|
+
}
|