openmausbot 0.1.86 → 0.1.88
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 +4 -3
- package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-qd9VD3GV.js} +1 -1
- package/dist/assets/{arc-DW7mrI4p.js → arc-3RwkUjRe.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-CFKSJS7a.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CG1rEfWD.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-CBW9N664.js} +1 -1
- package/dist/assets/channel-CnRnO3tF.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-2mRfTksB.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-Cv1dpErj.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-B7IACJp-.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-An0hZtYa.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-BcJB2nEm.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-CV_W7TN7.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Bcs_kwnA.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-lrx_DxFP.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-DabHlLGM.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BnHj6P7d.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-CfLp6UDi.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Dm4dbVA1.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-B9mH-PID.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-DDc_NVQJ.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-lwr8Ip8-.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-BLLe6dmI.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-6eUIq4lz.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-O-pRN7VQ.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-BietvGAI.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-Bd2Cl62X.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-B6iDy69W.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-DQVHfKe0.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BWdVp2tQ.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-WLE6rKB4.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-BbjS-4gn.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-Dpy2QSGq.js} +1 -1
- package/dist/assets/index-BIMdIbaw.css +1 -0
- package/dist/assets/index-CHnODCr2.js +583 -0
- package/dist/assets/{index-h9FFNwTu.js → index-CIaHn5NS.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-D_jKWlKe.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-DLJohSuK.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DPOFj0qN.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-BI7v-xpK.js} +1 -1
- package/dist/assets/{linear-3su2TG93.js → linear-C2pWPSxO.js} +1 -1
- package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-CR-j5WQU.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-lz9GvXRh.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-Dl4_ia61.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-DWuT3n88.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-DXWe9Jx7.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-COY1IBVY.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-DnyVuhsQ.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-BSc4NdGK.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-kqEU2EGq.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-BvB4bkWD.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BIrqNv7y.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-CFnalQUE.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C4a8qNGj.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-CsSWJnSQ.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-DkAmJ1EE.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-C3vd9drL.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-j26CM-pi.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-BYu1Q_at.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/companion/src/routes.js +8 -1
- package/dist-server/connector-proxy.js +105 -1
- package/dist-server/container-mcp.js +1032 -165
- package/dist-server/drivers/agents-proxy.js +338 -29
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/index.js +40590 -31811
- package/dist-server/local-computer-proxy.js +5 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/mcp-gate.js +2 -0
- package/dist-server/mcp-server.js +5 -3
- package/dist-server/message-search.worker.js +52 -0
- package/dist-server/openmausbot.js +4457 -1399
- package/dist-server/pair-cli.js +4457 -1399
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/admission.js +81 -0
- package/dist-server/server/aside-queue.js +294 -0
- package/dist-server/server/attachments.js +98 -1
- package/dist-server/server/auto-approve.js +4 -3
- package/dist-server/server/auto-vm-claims.js +1 -0
- package/dist-server/server/bot-attachment.js +107 -0
- package/dist-server/server/bot-overview.js +19 -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-api-setup.js +1 -0
- package/dist-server/server/cli.js +2 -2
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/composio.js +297 -1
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +84 -12
- package/dist-server/server/connector-advertisement.js +165 -0
- package/dist-server/server/connector-proxy.js +14 -2
- package/dist-server/server/connector-verdict.js +168 -0
- package/dist-server/server/container-computer.js +61 -1
- package/dist-server/server/contracts.js +22 -0
- 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 +80 -20
- package/dist-server/server/drivers/acp/cursor.js +3 -7
- package/dist-server/server/drivers/acp/droid.js +4 -7
- package/dist-server/server/drivers/acp/gemini.js +4 -4
- package/dist-server/server/drivers/acp/grok.js +4 -9
- package/dist-server/server/drivers/acp/hermes.js +3 -5
- package/dist-server/server/drivers/acp/opencode-go.js +2 -5
- package/dist-server/server/drivers/acp/qwen.js +9 -15
- package/dist-server/server/drivers/agents-call.js +179 -13
- package/dist-server/server/drivers/agents-catalog.js +134 -10
- package/dist-server/server/drivers/boxagent.js +160 -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 +38 -19
- package/dist-server/server/drivers/codex-catalog.js +14 -6
- package/dist-server/server/drivers/codex-identity.js +2 -1
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-release.js +48 -0
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +102 -55
- package/dist-server/server/drivers/mistral.js +115 -0
- package/dist-server/server/drivers/openai-chat.js +180 -33
- 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 +306 -28
- package/dist-server/server/drivers/prompt-split.js +110 -0
- package/dist-server/server/drivers/session-idle.js +29 -0
- package/dist-server/server/env-path.js +15 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/guarded-requests.js +2 -2
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/harness-capabilities.js +46 -0
- package/dist-server/server/hosted-models.js +1 -1
- package/dist-server/server/index.js +2396 -505
- 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/message-db.js +22 -42
- package/dist-server/server/message-file.js +9 -1
- package/dist-server/server/message-search-query.js +43 -0
- package/dist-server/server/message-search-worker.js +80 -0
- package/dist-server/server/message-search.worker.js +21 -0
- package/dist-server/server/model-requests.js +248 -0
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/options-card.js +21 -0
- package/dist-server/server/org-library.js +1065 -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-delivery.js +60 -0
- package/dist-server/server/peer-roster.js +67 -0
- package/dist-server/server/presets.js +492 -0
- package/dist-server/server/profile-requests.js +66 -39
- package/dist-server/server/profile-revision.js +9 -1
- package/dist-server/server/profile-versions.js +6 -3
- 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 +35 -10
- package/dist-server/server/routines.js +41 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skill-fetch.js +72 -14
- package/dist-server/server/skills.js +35 -61
- package/dist-server/server/steer-queue.js +3 -2
- package/dist-server/server/store.js +291 -17
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +38 -6
- 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 +139 -27
- package/dist-server/server/thread-events.js +20 -2
- package/dist-server/server/tightening-requests.js +405 -0
- package/dist-server/server/tts/chatterbox.js +27 -5
- 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 +19 -2
- 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 +42 -5
- package/dist-server/server/workspace-backup.worker.js +10 -0
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/ask-question.js +207 -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 +6 -2
- package/dist-server/shared/line-diff.js +23 -0
- package/dist-server/shared/model-request.js +1 -0
- package/dist-server/shared/options-card.js +51 -0
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/profile-request.js +5 -4
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +7 -0
- package/dist-server/shared/tightening-request.js +87 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/src/lib/composer-attachments.js +25 -3
- package/dist-server/vps-container-mcp.js +1132 -171
- package/dist-server/workspace-backup.worker.js +19474 -0
- 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
|
@@ -6,6 +6,7 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { decodeInjectId, hostApiKey, localHost, mergeLocalInject } from "../local-inject.js";
|
|
8
8
|
import { createAcpDriver } from "./core.js";
|
|
9
|
+
import { titleCaseModelId } from "../../contracts.js";
|
|
9
10
|
import { execCli } from "../../procs.js";
|
|
10
11
|
const STATIC_MODELS = {
|
|
11
12
|
default: "opencode/x-preview-f-free",
|
|
@@ -21,11 +22,7 @@ let lastSuccessfulCatalog = null;
|
|
|
21
22
|
const MODEL_PROBE_TTL_MS = 30_000;
|
|
22
23
|
const modelProbeCache = new Map();
|
|
23
24
|
function labelForModel(id) {
|
|
24
|
-
return id
|
|
25
|
-
.split(/[-_.]+/g)
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
28
|
-
.join(" ");
|
|
25
|
+
return titleCaseModelId(id, /[-_.]+/);
|
|
29
26
|
}
|
|
30
27
|
function providerLabel(id) {
|
|
31
28
|
if (id === "opencode")
|
|
@@ -3,30 +3,20 @@
|
|
|
3
3
|
// Custom and are written into ~/.qwen/settings.json modelProviders.
|
|
4
4
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
-
import { homedir } from "node:os";
|
|
7
6
|
import { join } from "node:path";
|
|
7
|
+
import { qualifiedModelLabel } from "../../contracts.js";
|
|
8
|
+
import { harnessHome } from "../../env-path.js";
|
|
8
9
|
import { decodeInjectId, hostApiKey, localHost, mergeLocalInject } from "../local-inject.js";
|
|
9
10
|
import { createAcpDriver } from "./core.js";
|
|
10
11
|
const EMPTY = { default: "", options: [] };
|
|
11
|
-
function qwenHome(env) {
|
|
12
|
-
const home = process.platform === "win32"
|
|
13
|
-
? env.USERPROFILE || env.HOME || homedir()
|
|
14
|
-
: env.HOME || env.USERPROFILE || homedir();
|
|
15
|
-
return join(home, ".qwen");
|
|
16
|
-
}
|
|
17
12
|
function isRecord(value) {
|
|
18
13
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19
14
|
}
|
|
20
|
-
function qwenModelLabel(id, name) {
|
|
21
|
-
if (!name || name.toLocaleLowerCase().endsWith(id.toLocaleLowerCase()))
|
|
22
|
-
return id;
|
|
23
|
-
return `${id} — ${name}`;
|
|
24
|
-
}
|
|
25
15
|
const PROTOCOLS = new Set(["openai", "anthropic", "gemini", "vertex-ai"]);
|
|
26
16
|
function readQwenRoutes(env) {
|
|
27
17
|
let settings;
|
|
28
18
|
try {
|
|
29
|
-
settings = JSON.parse(readFileSync(join(
|
|
19
|
+
settings = JSON.parse(readFileSync(join(harnessHome("qwen", env), "settings.json"), "utf8"));
|
|
30
20
|
}
|
|
31
21
|
catch {
|
|
32
22
|
return [];
|
|
@@ -102,7 +92,11 @@ function readQwenRoutes(env) {
|
|
|
102
92
|
/** Public metadata only. Use Qwen's provider-qualified ACP selectors, not raw model IDs. */
|
|
103
93
|
export function readQwenModelCatalog(env = process.env) {
|
|
104
94
|
const options = readQwenRoutes(env).map(({ id, model, label, provider }) => ({
|
|
105
|
-
id,
|
|
95
|
+
id,
|
|
96
|
+
label: qualifiedModelLabel(model, label, {
|
|
97
|
+
redundantWhen: (id, name) => name.toLocaleLowerCase().endsWith(id.toLocaleLowerCase()),
|
|
98
|
+
}),
|
|
99
|
+
custom: true, provider,
|
|
106
100
|
}));
|
|
107
101
|
return { default: options[0]?.id ?? "", options };
|
|
108
102
|
}
|
|
@@ -117,7 +111,7 @@ export function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
117
111
|
const host = localHost(inject.host);
|
|
118
112
|
if (!host)
|
|
119
113
|
return modelId;
|
|
120
|
-
const dir =
|
|
114
|
+
const dir = harnessHome("qwen", env);
|
|
121
115
|
mkdirSync(dir, { recursive: true });
|
|
122
116
|
const path = join(dir, "settings.json");
|
|
123
117
|
let settings = {};
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
// Nothing here reads the environment or holds state of its own. The caller
|
|
8
8
|
// passes a ToolCallContext, and the per-turn counters live on it.
|
|
9
9
|
import { CREDENTIAL_TARGETS, isCredentialTargetId } from "../../shared/credential-request.js";
|
|
10
|
+
import { parseOptionsCardInput, WATCHER_OPTIONS_CARD_BOT_ID } from "../../shared/options-card.js";
|
|
10
11
|
import { normalizeCronSchedule } from "../../shared/routine-schedule.js";
|
|
11
12
|
import { peerName } from "../peer-roster.js";
|
|
13
|
+
import { renderPeerDeliveryReceipts } from "../peer-delivery.js";
|
|
12
14
|
import { catalogProfileFromEnv, SHARED_COMPUTER_TOOL_NAMES, WEEKDAYS } from "./agents-catalog.js";
|
|
13
15
|
import { harnessClientFromEnv } from "./agents-client.js";
|
|
14
16
|
import { boundedAgentResult } from "./agents-result.js";
|
|
@@ -352,12 +354,95 @@ function recallSpeaker(hit) {
|
|
|
352
354
|
return hit.from;
|
|
353
355
|
return hit.role === "user" ? "user" : "you";
|
|
354
356
|
}
|
|
357
|
+
const isPeerDeliveryOutcome = (value) => value === "queued" || value === "injected" || value === "failed";
|
|
358
|
+
/** The delivery receipt a peer-send answer carries, as one prose line.
|
|
359
|
+
* Unvalidated JSON stays out: a malformed receipt renders nothing rather
|
|
360
|
+
* than half a line of somebody else's text. */
|
|
361
|
+
function deliveryNote(r) {
|
|
362
|
+
const receipt = r.receipt;
|
|
363
|
+
if (!receipt || typeof receipt !== "object")
|
|
364
|
+
return "";
|
|
365
|
+
const { botId, botName, outcome, detail, taskId, requestId } = receipt;
|
|
366
|
+
if (typeof botId !== "string" || !botId || typeof detail !== "string" || !detail)
|
|
367
|
+
return "";
|
|
368
|
+
if (!isPeerDeliveryOutcome(outcome))
|
|
369
|
+
return "";
|
|
370
|
+
const parsed = {
|
|
371
|
+
botId,
|
|
372
|
+
...(typeof botName === "string" && botName ? { botName } : {}),
|
|
373
|
+
outcome,
|
|
374
|
+
detail,
|
|
375
|
+
...(typeof taskId === "string" && taskId ? { taskId } : {}),
|
|
376
|
+
...(typeof requestId === "string" && requestId ? { requestId } : {}),
|
|
377
|
+
};
|
|
378
|
+
return renderPeerDeliveryReceipts([parsed]);
|
|
379
|
+
}
|
|
380
|
+
/** When a recalled line was said, on the machine's own clock. `toISOString()`
|
|
381
|
+
* answers in UTC, which disagrees with every other day the bot is shown: a
|
|
382
|
+
* bare `since`/`until` date is read as local midnight (recent-work.ts
|
|
383
|
+
* parseSince), the recent-work brief's times are local (whenLabel), and the
|
|
384
|
+
* daily memory logs are named after the local day. East of UTC a message
|
|
385
|
+
* from this morning was being dated yesterday — so a search for today's work
|
|
386
|
+
* came back stamped with the wrong date. */
|
|
387
|
+
function recallWhen(at, dateOnly) {
|
|
388
|
+
if (typeof at !== "number" || !Number.isFinite(at))
|
|
389
|
+
return "";
|
|
390
|
+
const said = new Date(at);
|
|
391
|
+
if (!Number.isFinite(said.getTime()))
|
|
392
|
+
return "";
|
|
393
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
394
|
+
const day = `${said.getFullYear()}-${pad(said.getMonth() + 1)}-${pad(said.getDate())}`;
|
|
395
|
+
return dateOnly ? day : `${day} ${pad(said.getHours())}:${pad(said.getMinutes())}`;
|
|
396
|
+
}
|
|
355
397
|
export async function callTool(name, args, context) {
|
|
356
398
|
// The names this body has always used, so it reads (and diffs) as it did
|
|
357
399
|
// when these were the proxy's module-level constants.
|
|
358
400
|
const { botId: BOT_ID, threadId: THREAD_ID, depth: DEPTH, externalRuntime: EXTERNAL_RUNTIME, coordinating: COORDINATING, turn } = context;
|
|
359
401
|
const { delegationTaskIdsThisTurn } = turn;
|
|
360
402
|
const { api, apiResponse } = context.client;
|
|
403
|
+
if (name === "vm_exec") {
|
|
404
|
+
const { ok, body } = await apiResponse("/api/internal/vm-exec", {
|
|
405
|
+
method: "POST",
|
|
406
|
+
body: JSON.stringify({ command: args.command, ...(typeof args.timeout_seconds === "number" ? { timeout_seconds: args.timeout_seconds } : {}) }),
|
|
407
|
+
});
|
|
408
|
+
if (!ok)
|
|
409
|
+
return { text: String(body.error ?? "Could not run that command."), isError: true };
|
|
410
|
+
const exitCode = Number(body.exitCode ?? 0);
|
|
411
|
+
const stdout = String(body.stdout ?? "");
|
|
412
|
+
const stderr = String(body.stderr ?? "");
|
|
413
|
+
const lines = [body.timedOut ? "The command was stopped: it ran past its time limit." : `exit code ${exitCode}`];
|
|
414
|
+
if (stdout)
|
|
415
|
+
lines.push("--- stdout ---", stdout.replace(/\s+$/, ""));
|
|
416
|
+
if (stderr)
|
|
417
|
+
lines.push("--- stderr ---", stderr.replace(/\s+$/, ""));
|
|
418
|
+
if (!stdout && !stderr && !body.timedOut)
|
|
419
|
+
lines.push("(no output)");
|
|
420
|
+
return { text: lines.join("\n"), ...(exitCode !== 0 || body.timedOut ? { isError: true } : {}) };
|
|
421
|
+
}
|
|
422
|
+
if (name === "attach_file") {
|
|
423
|
+
const { ok, body } = await apiResponse("/api/internal/attach-file", {
|
|
424
|
+
method: "POST",
|
|
425
|
+
body: JSON.stringify({ path: args.path, ...(typeof args.name === "string" ? { name: args.name } : {}) }),
|
|
426
|
+
});
|
|
427
|
+
if (!ok)
|
|
428
|
+
return { text: String(body.error ?? "Could not attach that file."), isError: true };
|
|
429
|
+
return { text: `Attached ${String(body.name ?? "the file")} (${Number(body.bytes ?? 0)} bytes). It now appears in the chat with a preview.` };
|
|
430
|
+
}
|
|
431
|
+
if (name === "create_options_card") {
|
|
432
|
+
if (BOT_ID !== WATCHER_OPTIONS_CARD_BOT_ID) {
|
|
433
|
+
return { text: "create_options_card is not enabled for this bot.", isError: true };
|
|
434
|
+
}
|
|
435
|
+
const parsed = parseOptionsCardInput(args);
|
|
436
|
+
if (!parsed.ok)
|
|
437
|
+
return { text: parsed.error, isError: true };
|
|
438
|
+
const result = await api("/api/internal/options-card", {
|
|
439
|
+
method: "POST",
|
|
440
|
+
body: JSON.stringify(parsed.value),
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
text: `Rendered the native options card in this Watcher thread (message ${String(result.messageId ?? "created")}). Wait for the person's click or custom response; the card itself authorizes no external action.`,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
361
446
|
// Second lock. With sharing off the tool is not in the catalog, so a front
|
|
362
447
|
// end already refuses the call as an unknown tool — the same answer a build
|
|
363
448
|
// without the feature gives. This keeps the handler itself refusing if that
|
|
@@ -467,6 +552,7 @@ export async function callTool(name, args, context) {
|
|
|
467
552
|
if (name === "post_to_room") {
|
|
468
553
|
const groupId = String(args.group_id ?? "").trim();
|
|
469
554
|
const message = String(args.message ?? "").trim();
|
|
555
|
+
const attachVoiceNote = args.attach_voice_note === true;
|
|
470
556
|
if (!groupId || !message) {
|
|
471
557
|
return { text: "post_to_room needs group_id (from list_rooms) and message.", isError: true };
|
|
472
558
|
}
|
|
@@ -478,13 +564,13 @@ export async function callTool(name, args, context) {
|
|
|
478
564
|
}
|
|
479
565
|
const r = await api("/api/internal/post-to-room", {
|
|
480
566
|
method: "POST",
|
|
481
|
-
body: JSON.stringify({ fromBotId: BOT_ID, fromThreadId: THREAD_ID, groupId, message }),
|
|
567
|
+
body: JSON.stringify({ fromBotId: BOT_ID, fromThreadId: THREAD_ID, groupId, message, attachVoiceNote }),
|
|
482
568
|
});
|
|
483
569
|
if (r.error)
|
|
484
570
|
return { text: String(r.error), isError: true };
|
|
485
571
|
turn.roomPostsThisTurn += 1;
|
|
486
572
|
return {
|
|
487
|
-
text: `Posted in ${r.roomName ?? "the room"}. Nobody's turn was started, so expect no reply — tell the user it is posted.`,
|
|
573
|
+
text: `Posted in ${r.roomName ?? "the room"}${r.attachedVoiceNote ? " with the voice note attached" : ""}. Nobody's turn was started, so expect no reply — tell the user it is posted.`,
|
|
488
574
|
};
|
|
489
575
|
}
|
|
490
576
|
if (name === "ask_bot") {
|
|
@@ -496,6 +582,7 @@ export async function callTool(name, args, context) {
|
|
|
496
582
|
method: "POST",
|
|
497
583
|
body: JSON.stringify({ fromBotId: BOT_ID, fromThreadId: THREAD_ID, toBotId, message, depth: DEPTH }),
|
|
498
584
|
});
|
|
585
|
+
const note = deliveryNote(r);
|
|
499
586
|
if (r.timeout) {
|
|
500
587
|
// The peer's turn outlived the synchronous wait, so the harness
|
|
501
588
|
// converted the ask into a delegation — the reply is not lost.
|
|
@@ -506,10 +593,25 @@ export async function callTool(name, args, context) {
|
|
|
506
593
|
const amount = waitedSeconds < 60 ? waitedSeconds : Math.round(waitedSeconds / 60);
|
|
507
594
|
const unit = waitedSeconds < 60 ? "second" : "minute";
|
|
508
595
|
return {
|
|
509
|
-
text: `${r.toBotName ?? "That bot"} is still working after ${amount} ${unit}${amount === 1 ? "" : "s"} — the ask was converted to a delegation so the reply is not lost. Task id: ${taskId}. ${EXTERNAL_RUNTIME ? EXTERNAL_STATUS_GUIDANCE : "Finish your turn now; the result will be delivered to this conversation automatically. Use check_delegation in a later turn only if the user asks for status."}`,
|
|
596
|
+
text: `${r.toBotName ?? "That bot"} is still working after ${amount} ${unit}${amount === 1 ? "" : "s"} — the ask was converted to a delegation so the reply is not lost. Task id: ${taskId}. ${EXTERNAL_RUNTIME ? EXTERNAL_STATUS_GUIDANCE : "Finish your turn now; the result will be delivered to this conversation automatically. Use check_delegation in a later turn only if the user asks for status."}${note ? `\n\n${note}` : ""}`,
|
|
510
597
|
};
|
|
511
598
|
}
|
|
512
599
|
if (r.busy) {
|
|
600
|
+
// Aside lane: the teammate was mid-turn on a seam-capable engine, so
|
|
601
|
+
// the harness folded the message into their running work as peer
|
|
602
|
+
// context. No new turn started and no reply is coming back through
|
|
603
|
+
// this call — the receipt says whether the words are already in the
|
|
604
|
+
// live turn or waiting for it to settle.
|
|
605
|
+
if (r.aside === "injected") {
|
|
606
|
+
return {
|
|
607
|
+
text: `${r.toBotName ?? "That bot"} is mid-turn; your message was handed to them as an aside — peer context folded into their current work, not a request that interrupts or replies. Finish your turn and treat their eventual output as possibly informed by it.`,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (r.aside === "queued") {
|
|
611
|
+
return {
|
|
612
|
+
text: `${r.toBotName ?? "That bot"} is mid-turn; your aside is queued and will reach them as context when the turn settles. No reply is expected — finish your turn.`,
|
|
613
|
+
};
|
|
614
|
+
}
|
|
513
615
|
// The harness queues the message as a delegation when it can; the
|
|
514
616
|
// task id is the asker's claim ticket for the eventual reply.
|
|
515
617
|
const taskId = String(r.taskId ?? "").trim();
|
|
@@ -517,14 +619,14 @@ export async function callTool(name, args, context) {
|
|
|
517
619
|
if (!EXTERNAL_RUNTIME)
|
|
518
620
|
delegationTaskIdsThisTurn.add(taskId);
|
|
519
621
|
return {
|
|
520
|
-
text: `${r.toBotName ?? "That bot"} is busy right now, so your message was queued as a delegation instead — ${EXTERNAL_RUNTIME ? "it waits for the peer and any required approval" : "it runs after your current turn ends"}. Task id: ${taskId}. ${EXTERNAL_RUNTIME ? EXTERNAL_STATUS_GUIDANCE : "Finish your turn now; the result will be delivered to this conversation automatically. Use check_delegation in a later turn only if the user asks for status."}`,
|
|
622
|
+
text: `${r.toBotName ?? "That bot"} is busy right now, so your message was queued as a delegation instead — ${EXTERNAL_RUNTIME ? "it waits for the peer and any required approval" : "it runs after your current turn ends"}. Task id: ${taskId}. ${EXTERNAL_RUNTIME ? EXTERNAL_STATUS_GUIDANCE : "Finish your turn now; the result will be delivered to this conversation automatically. Use check_delegation in a later turn only if the user asks for status."}${note ? `\n\n${note}` : ""}`,
|
|
521
623
|
};
|
|
522
624
|
}
|
|
523
|
-
return { text: `That bot is busy right now — try again after it finishes
|
|
625
|
+
return { text: `That bot is busy right now — try again after it finishes.${note ? `\n\n${note}` : ""}` };
|
|
524
626
|
}
|
|
525
627
|
if (r.error)
|
|
526
|
-
return { text: `Couldn't reach that bot: ${r.error}`, isError: true };
|
|
527
|
-
return { text: `${r.botName ?? "Bot"} replied:\n${r.text ?? "(no reply)"}` };
|
|
628
|
+
return { text: `Couldn't reach that bot: ${r.error}${note ? `\n\n${note}` : ""}`, isError: true };
|
|
629
|
+
return { text: `${r.botName ?? "Bot"} replied:\n${r.text ?? "(no reply)"}${note ? `\n\n${note}` : ""}` };
|
|
528
630
|
}
|
|
529
631
|
if (name === "delegate_bot") {
|
|
530
632
|
const toBotId = String(args.bot_id ?? "").trim();
|
|
@@ -542,8 +644,9 @@ export async function callTool(name, args, context) {
|
|
|
542
644
|
if (reason)
|
|
543
645
|
body.reason = reason;
|
|
544
646
|
const r = await api(`/api/internal/delegate-bot`, { method: "POST", body: JSON.stringify(body) });
|
|
647
|
+
const delivery = deliveryNote(r);
|
|
545
648
|
if (r.error)
|
|
546
|
-
return { text: `Couldn't queue the delegation: ${r.error}`, isError: true };
|
|
649
|
+
return { text: `Couldn't queue the delegation: ${r.error}${delivery ? `\n\n${delivery}` : ""}`, isError: true };
|
|
547
650
|
// Fire-and-forget by contract: the harness returns immediately, the
|
|
548
651
|
// peer turn runs after our current turn finishes. The task id is the
|
|
549
652
|
// bot's claim ticket for the outcome.
|
|
@@ -554,7 +657,7 @@ export async function callTool(name, args, context) {
|
|
|
554
657
|
const suffix = taskId
|
|
555
658
|
? ` Task id: ${taskId}. ${EXTERNAL_RUNTIME ? EXTERNAL_STATUS_GUIDANCE : "Acknowledge the assignment and finish your turn; the result will be delivered to this conversation automatically. Do not check or wait for it in this turn."}`
|
|
556
659
|
: "";
|
|
557
|
-
return { text: `${note}${suffix}` };
|
|
660
|
+
return { text: `${note}${suffix}${delivery ? `\n\n${delivery}` : ""}` };
|
|
558
661
|
}
|
|
559
662
|
if (name === "check_delegation" || name === "wait_delegation") {
|
|
560
663
|
const taskId = String(args.task_id ?? "").trim();
|
|
@@ -727,11 +830,13 @@ export async function callTool(name, args, context) {
|
|
|
727
830
|
name: botName,
|
|
728
831
|
role,
|
|
729
832
|
instructions,
|
|
833
|
+
...(args.modelSelection !== undefined ? { modelSelection: args.modelSelection } : {}),
|
|
834
|
+
...(typeof args.cwd === "string" ? { cwd: args.cwd.trim() } : {}),
|
|
730
835
|
}),
|
|
731
836
|
});
|
|
732
837
|
turn.createdThisTurn += 1;
|
|
733
838
|
return {
|
|
734
|
-
text: `Created @${r.name ?? botName} in ${r.section ?? "General"} [id: ${r.id}]. Assign work with ${COORDINATING ? "coordinate_bots" : "delegate_bot"}.`,
|
|
839
|
+
text: `Created @${r.name ?? botName} in ${r.section ?? "General"} [id: ${r.id}].${r.modelSelection ? ` Model: ${JSON.stringify(r.modelSelection)}.` : ""} Assign work with ${COORDINATING ? "coordinate_bots" : "delegate_bot"}.`,
|
|
735
840
|
};
|
|
736
841
|
}
|
|
737
842
|
if (name === "create_room") {
|
|
@@ -816,6 +921,27 @@ export async function callTool(name, args, context) {
|
|
|
816
921
|
text: `A secure ${r.label ?? CREDENTIAL_TARGETS[credentialId].label} request is ready. The desktop app and a freshly QR-paired mobile app show its secure entry card; older mobile pairings explain how to pair again or finish on the computer. End this turn; OpenMausBot will resume the task after the user saves or declines. Never ask them to paste the key into chat.`,
|
|
817
922
|
};
|
|
818
923
|
}
|
|
924
|
+
if (name === "send_voice_note") {
|
|
925
|
+
if (typeof args.text !== "string" || !args.text.trim()) {
|
|
926
|
+
return { text: "send_voice_note needs text: the short speakable note, at most 1000 characters.", isError: true };
|
|
927
|
+
}
|
|
928
|
+
const text = args.text.trim();
|
|
929
|
+
if (text.length > 1000) {
|
|
930
|
+
return { text: `send_voice_note is limited to 1000 characters; this one is ${text.length}. Shorten the note.`, isError: true };
|
|
931
|
+
}
|
|
932
|
+
try {
|
|
933
|
+
await api("/api/internal/voice-note", {
|
|
934
|
+
method: "POST",
|
|
935
|
+
body: JSON.stringify({ fromBotId: BOT_ID, fromThreadId: THREAD_ID, text }),
|
|
936
|
+
});
|
|
937
|
+
return { text: "Voice note recorded. It will be attached to this turn's reply when the turn ends; the note text is also the visible caption." };
|
|
938
|
+
}
|
|
939
|
+
catch (error) {
|
|
940
|
+
// A missing voice setup is the user's to fix, not a failed turn: hand
|
|
941
|
+
// the harness's setup guidance straight to the model.
|
|
942
|
+
return { text: `Voice note not sent: ${error instanceof Error ? error.message : String(error)}`, isError: true };
|
|
943
|
+
}
|
|
944
|
+
}
|
|
819
945
|
if (name === "list_routines") {
|
|
820
946
|
const query = new URLSearchParams({ fromBotId: BOT_ID, fromThreadId: THREAD_ID });
|
|
821
947
|
const r = await api(`/api/internal/routines?${query.toString()}`);
|
|
@@ -884,6 +1010,13 @@ export async function callTool(name, args, context) {
|
|
|
884
1010
|
return confirmationResult(r, `${action.replace("_", " ")} on routine ${routineId}`);
|
|
885
1011
|
}
|
|
886
1012
|
if (name === "propose_profile") {
|
|
1013
|
+
// A non-boolean toggle would be dropped silently while any valid half of
|
|
1014
|
+
// the request went through, applying a partial proposal the model never
|
|
1015
|
+
// described. Reject the whole call instead.
|
|
1016
|
+
if ((args.notifications !== undefined && typeof args.notifications !== "boolean")
|
|
1017
|
+
|| (args.speakReplies !== undefined && typeof args.speakReplies !== "boolean")) {
|
|
1018
|
+
return { text: "propose_profile notifications and speakReplies must be true or false.", isError: true };
|
|
1019
|
+
}
|
|
887
1020
|
const changes = {};
|
|
888
1021
|
if (typeof args.name === "string")
|
|
889
1022
|
changes.name = args.name.trim();
|
|
@@ -895,8 +1028,12 @@ export async function callTool(name, args, context) {
|
|
|
895
1028
|
changes.soul = args.soul;
|
|
896
1029
|
if (typeof args.cwd === "string")
|
|
897
1030
|
changes.cwd = args.cwd.trim();
|
|
1031
|
+
if (typeof args.notifications === "boolean")
|
|
1032
|
+
changes.notifications = args.notifications;
|
|
1033
|
+
if (typeof args.speakReplies === "boolean")
|
|
1034
|
+
changes.speakReplies = args.speakReplies;
|
|
898
1035
|
if (!Object.keys(changes).length) {
|
|
899
|
-
return { text: "propose_profile needs at least one of name, title, description, soul, or
|
|
1036
|
+
return { text: "propose_profile needs at least one of name, title, description, soul, cwd, notifications, or speakReplies.", isError: true };
|
|
900
1037
|
}
|
|
901
1038
|
const forBotId = String(args.for_bot_id ?? "").trim();
|
|
902
1039
|
const r = await api("/api/internal/profile-requests", {
|
|
@@ -912,6 +1049,35 @@ export async function callTool(name, args, context) {
|
|
|
912
1049
|
});
|
|
913
1050
|
return confirmationResult(r, "the profile change", "profile");
|
|
914
1051
|
}
|
|
1052
|
+
if (name === "propose_model") {
|
|
1053
|
+
const raw = args.model_selection;
|
|
1054
|
+
const fields = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : null;
|
|
1055
|
+
if (!fields || typeof fields.instanceId !== "string" || !fields.instanceId.trim() || typeof fields.model !== "string" || !fields.model.trim()) {
|
|
1056
|
+
return { text: "propose_model needs model_selection with instanceId and model.", isError: true };
|
|
1057
|
+
}
|
|
1058
|
+
if ((fields.effort !== undefined && (typeof fields.effort !== "string" || !fields.effort.trim()))
|
|
1059
|
+
|| (fields.variant !== undefined && (typeof fields.variant !== "string" || !fields.variant.trim()))) {
|
|
1060
|
+
return { text: "propose_model effort and variant must be non-empty strings when supplied.", isError: true };
|
|
1061
|
+
}
|
|
1062
|
+
const selection = { instanceId: fields.instanceId.trim(), model: fields.model.trim() };
|
|
1063
|
+
if (typeof fields.effort === "string" && fields.effort.trim())
|
|
1064
|
+
selection.effort = fields.effort.trim();
|
|
1065
|
+
if (typeof fields.variant === "string" && fields.variant.trim())
|
|
1066
|
+
selection.variant = fields.variant.trim();
|
|
1067
|
+
const forBotId = String(args.for_bot_id ?? "").trim();
|
|
1068
|
+
const r = await api("/api/internal/model-requests", {
|
|
1069
|
+
method: "POST",
|
|
1070
|
+
body: JSON.stringify({
|
|
1071
|
+
fromBotId: BOT_ID,
|
|
1072
|
+
fromThreadId: THREAD_ID,
|
|
1073
|
+
modelSelection: selection,
|
|
1074
|
+
reason: args.reason,
|
|
1075
|
+
// JSON.stringify drops the key entirely when no target was named
|
|
1076
|
+
forBotId: forBotId || undefined,
|
|
1077
|
+
}),
|
|
1078
|
+
});
|
|
1079
|
+
return confirmationResult(r, "the default model change", "model");
|
|
1080
|
+
}
|
|
915
1081
|
if (name === "memory_update") {
|
|
916
1082
|
if (!["append", "replace", "remove", "supersede"].includes(String(args.action))
|
|
917
1083
|
|| (args.action !== "remove" && (typeof args.text !== "string" || !args.text.trim()))
|
|
@@ -1008,7 +1174,7 @@ export async function callTool(name, args, context) {
|
|
|
1008
1174
|
}
|
|
1009
1175
|
const lines = hits.map((hit) => {
|
|
1010
1176
|
// a listing by time shows the time; a search by words keeps the date
|
|
1011
|
-
const when =
|
|
1177
|
+
const when = recallWhen(hit.at, Boolean(q));
|
|
1012
1178
|
const task = typeof hit.task === "string" && hit.task ? `task "${hit.task}"` : "an earlier task";
|
|
1013
1179
|
const where = hit.current
|
|
1014
1180
|
? "this conversation"
|
|
@@ -1040,7 +1206,7 @@ export async function callTool(name, args, context) {
|
|
|
1040
1206
|
catch (error) {
|
|
1041
1207
|
return { text: `Couldn't read that message: ${error instanceof Error ? error.message : String(error)}. Use ids from a session_search hit.`, isError: true };
|
|
1042
1208
|
}
|
|
1043
|
-
const when =
|
|
1209
|
+
const when = recallWhen(r.at, true);
|
|
1044
1210
|
const readTask = typeof r.task === "string" && r.task ? `task "${r.task}"` : "an earlier task";
|
|
1045
1211
|
const where = threadId === THREAD_ID ? "this conversation" : r.crossed ? `${readTask}, private to this user` : readTask;
|
|
1046
1212
|
const note = r.crossed
|