openmausbot 0.1.83 → 0.1.85
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/index-Ba9G44HI.js +310 -0
- package/dist/assets/{index-nqTFZTxg.js → index-CTNNoqSJ.js} +1 -1
- package/dist/assets/index-CkRNp7wX.css +1 -0
- package/dist/index.html +2 -2
- package/dist-server/companion/src/routes.js +1 -0
- package/dist-server/container-mcp.js +212 -7
- package/dist-server/drivers/agents-proxy.js +161 -12
- package/dist-server/enterprise/server/index.js +1 -0
- package/dist-server/hooks/omb-hook.js +74 -0
- package/dist-server/index.js +8025 -5661
- package/dist-server/local-computer-proxy.js +83 -1
- package/dist-server/local-computer.js +14 -2
- package/dist-server/openmausbot.js +796 -309
- package/dist-server/pair-cli.js +796 -309
- package/dist-server/proxy-paths.js +1 -0
- package/dist-server/server/agent-tool-policy.js +1 -0
- package/dist-server/server/bot-package.js +2 -0
- package/dist-server/server/box.js +23 -4
- package/dist-server/server/browser-engine.js +87 -10
- package/dist-server/server/browser-live.js +7 -5
- package/dist-server/server/browser-runtime.js +65 -10
- package/dist-server/server/checkpoints.js +71 -23
- package/dist-server/server/chief-of-staff.js +3 -0
- package/dist-server/server/cli-prompts.js +3 -1
- package/dist-server/server/commands.js +27 -0
- package/dist-server/server/compaction-summary.js +78 -0
- package/dist-server/server/computer-wait.js +33 -0
- package/dist-server/server/config.js +84 -3
- package/dist-server/server/context-budget.js +29 -0
- package/dist-server/server/context-rebuild.js +72 -0
- package/dist-server/server/delta-context.js +258 -0
- package/dist-server/server/digest.js +130 -0
- package/dist-server/server/drivers/acp/core.js +540 -229
- package/dist-server/server/drivers/agents-proxy.js +79 -12
- package/dist-server/server/drivers/agents-result.js +23 -0
- package/dist-server/server/drivers/claude.js +84 -15
- package/dist-server/server/drivers/codex.js +19 -5
- package/dist-server/server/drivers/openai-chat.js +66 -45
- package/dist-server/server/drivers/openai-compat.js +1 -0
- package/dist-server/server/hooks/omb-hook.js +103 -0
- package/dist-server/server/incidents.js +104 -0
- package/dist-server/server/index.js +1382 -318
- package/dist-server/server/local-computer.js +21 -2
- package/dist-server/server/mcp-bridge.js +10 -2
- package/dist-server/server/mcp-tool-images.js +37 -0
- package/dist-server/server/mcp-tool-schema.js +108 -0
- package/dist-server/server/message-db.js +80 -21
- package/dist-server/server/message-file.js +3 -2
- package/dist-server/server/notify.js +3 -1
- package/dist-server/server/package-export.js +1 -0
- package/dist-server/server/peer-roster.js +4 -2
- package/dist-server/server/provider-icon.js +20 -0
- package/dist-server/server/proxy-paths.js +1 -0
- package/dist-server/server/request-auth.js +1 -0
- package/dist-server/server/room-handoffs.js +16 -2
- package/dist-server/server/routine-requests.js +39 -1
- package/dist-server/server/routines.js +43 -9
- package/dist-server/server/shared-computer-control.js +31 -4
- package/dist-server/server/steer-queue.js +6 -0
- package/dist-server/server/store.js +111 -14
- package/dist-server/server/system-prompt.js +4 -4
- package/dist-server/server/team-backup.js +9 -1
- package/dist-server/server/tool-results.js +72 -0
- package/dist-server/server/tts/grok.js +74 -0
- package/dist-server/server/tts/index.js +19 -1
- package/dist-server/server/webhooks.js +28 -0
- package/dist-server/shared/approval-mode.js +7 -1
- package/dist-server/shared/digest.js +1 -0
- package/dist-server/shared/markdown-windows-paths.js +36 -0
- package/dist-server/shared/provider-icon.js +110 -0
- package/dist-server/shared/team-backup.js +3 -0
- package/dist-server/vps-container-mcp.js +217 -12
- package/enterprise/server/index.js +1 -0
- package/package.json +1 -1
- package/dist/assets/index-CKysBq-V.css +0 -1
- package/dist/assets/index-DSvqfBOf.js +0 -309
|
@@ -3157,13 +3157,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
3157
3157
|
}
|
|
3158
3158
|
return propValues;
|
|
3159
3159
|
});
|
|
3160
|
-
const
|
|
3160
|
+
const isObject3 = isObject;
|
|
3161
3161
|
const catchall = def.catchall;
|
|
3162
3162
|
let value;
|
|
3163
3163
|
inst._zod.parse = (payload, ctx) => {
|
|
3164
3164
|
value ?? (value = _normalized.value);
|
|
3165
3165
|
const input = payload.value;
|
|
3166
|
-
if (!
|
|
3166
|
+
if (!isObject3(input)) {
|
|
3167
3167
|
payload.issues.push({
|
|
3168
3168
|
expected: "object",
|
|
3169
3169
|
code: "invalid_type",
|
|
@@ -3290,7 +3290,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3290
3290
|
return (payload, ctx) => fn(shape, payload, ctx);
|
|
3291
3291
|
};
|
|
3292
3292
|
let fastpass;
|
|
3293
|
-
const
|
|
3293
|
+
const isObject3 = isObject;
|
|
3294
3294
|
const jit = !globalConfig.jitless;
|
|
3295
3295
|
const allowsEval2 = allowsEval;
|
|
3296
3296
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -3299,7 +3299,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3299
3299
|
inst._zod.parse = (payload, ctx) => {
|
|
3300
3300
|
value ?? (value = _normalized.value);
|
|
3301
3301
|
const input = payload.value;
|
|
3302
|
-
if (!
|
|
3302
|
+
if (!isObject3(input)) {
|
|
3303
3303
|
payload.issues.push({
|
|
3304
3304
|
expected: "object",
|
|
3305
3305
|
code: "invalid_type",
|
|
@@ -14660,6 +14660,97 @@ function isModelVariant(value) {
|
|
|
14660
14660
|
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !new RegExp("\\p{Cc}", "u").test(value);
|
|
14661
14661
|
}
|
|
14662
14662
|
|
|
14663
|
+
// shared/provider-icon.ts
|
|
14664
|
+
var PROVIDER_ICON_PRESETS = [
|
|
14665
|
+
"openai",
|
|
14666
|
+
"anthropic",
|
|
14667
|
+
"google",
|
|
14668
|
+
"azure",
|
|
14669
|
+
"aws",
|
|
14670
|
+
"xai",
|
|
14671
|
+
"deepseek",
|
|
14672
|
+
"meta",
|
|
14673
|
+
"mistral",
|
|
14674
|
+
"qwen",
|
|
14675
|
+
"moonshot",
|
|
14676
|
+
"cohere",
|
|
14677
|
+
"openrouter"
|
|
14678
|
+
];
|
|
14679
|
+
var PROVIDER_ICON_MAX_BYTES = 128 * 1024;
|
|
14680
|
+
var PROVIDER_ICON_MAX_DIMENSION = 1024;
|
|
14681
|
+
function decodedBase64Bytes(value) {
|
|
14682
|
+
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
14683
|
+
return Math.floor(value.length * 3 / 4) - padding;
|
|
14684
|
+
}
|
|
14685
|
+
function imageDimensions(bytes, mime) {
|
|
14686
|
+
const u16be = (offset) => bytes[offset] * 256 + bytes[offset + 1];
|
|
14687
|
+
const u16le = (offset) => bytes[offset] + bytes[offset + 1] * 256;
|
|
14688
|
+
const u24le = (offset) => bytes[offset] + bytes[offset + 1] * 256 + bytes[offset + 2] * 65536;
|
|
14689
|
+
const u32le = (offset) => bytes[offset] + bytes[offset + 1] * 256 + bytes[offset + 2] * 65536 + bytes[offset + 3] * 16777216;
|
|
14690
|
+
const u32be = (offset) => bytes[offset] * 16777216 + bytes[offset + 1] * 65536 + bytes[offset + 2] * 256 + bytes[offset + 3];
|
|
14691
|
+
if (mime === "image/png") {
|
|
14692
|
+
if (bytes.length < 33 || bytes.slice(0, 8).join(",") !== "137,80,78,71,13,10,26,10" || String.fromCharCode(...bytes.slice(12, 16)) !== "IHDR" || bytes.slice(-8).join(",") !== "73,69,78,68,174,66,96,130") return null;
|
|
14693
|
+
return { width: u32be(16), height: u32be(20) };
|
|
14694
|
+
}
|
|
14695
|
+
if (mime === "image/jpeg") {
|
|
14696
|
+
if (bytes.length < 12 || bytes[0] !== 255 || bytes[1] !== 216 || bytes.at(-2) !== 255 || bytes.at(-1) !== 217) return null;
|
|
14697
|
+
for (let offset = 2; offset + 8 < bytes.length; ) {
|
|
14698
|
+
if (bytes[offset] !== 255) {
|
|
14699
|
+
offset += 1;
|
|
14700
|
+
continue;
|
|
14701
|
+
}
|
|
14702
|
+
const marker = bytes[offset + 1];
|
|
14703
|
+
if ([192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207].includes(marker)) {
|
|
14704
|
+
return { height: u16be(offset + 5), width: u16be(offset + 7) };
|
|
14705
|
+
}
|
|
14706
|
+
if (marker === 216 || marker === 217 || marker === 1 || marker >= 208 && marker <= 215) {
|
|
14707
|
+
offset += 2;
|
|
14708
|
+
continue;
|
|
14709
|
+
}
|
|
14710
|
+
const length = u16be(offset + 2);
|
|
14711
|
+
if (length < 2) return null;
|
|
14712
|
+
offset += length + 2;
|
|
14713
|
+
}
|
|
14714
|
+
return null;
|
|
14715
|
+
}
|
|
14716
|
+
if (bytes.length < 30 || String.fromCharCode(...bytes.slice(0, 4)) !== "RIFF" || String.fromCharCode(...bytes.slice(8, 12)) !== "WEBP" || u32le(4) + 8 !== bytes.length) return null;
|
|
14717
|
+
const format = String.fromCharCode(...bytes.slice(12, 16));
|
|
14718
|
+
if (format === "VP8X") return { width: u24le(24) + 1, height: u24le(27) + 1 };
|
|
14719
|
+
if (format === "VP8L" && bytes[20] === 47) {
|
|
14720
|
+
const bits = bytes[21] | bytes[22] << 8 | bytes[23] << 16 | bytes[24] << 24;
|
|
14721
|
+
return { width: (bits & 16383) + 1, height: (bits >>> 14 & 16383) + 1 };
|
|
14722
|
+
}
|
|
14723
|
+
if (format === "VP8 " && bytes[23] === 157 && bytes[24] === 1 && bytes[25] === 42) {
|
|
14724
|
+
return { width: u16le(26) & 16383, height: u16le(28) & 16383 };
|
|
14725
|
+
}
|
|
14726
|
+
return null;
|
|
14727
|
+
}
|
|
14728
|
+
function providerIconError(icon) {
|
|
14729
|
+
if (icon.kind === "preset") {
|
|
14730
|
+
return PROVIDER_ICON_PRESETS.includes(icon.preset) ? null : "Choose a supported provider icon.";
|
|
14731
|
+
}
|
|
14732
|
+
const match = /^data:(image\/(?:png|jpeg|webp));base64,([A-Za-z0-9+/]+={0,2})$/.exec(icon.dataUrl);
|
|
14733
|
+
if (!match) return "Upload a PNG, JPEG, or WebP image.";
|
|
14734
|
+
const byteLength = decodedBase64Bytes(match[2]);
|
|
14735
|
+
if (byteLength > PROVIDER_ICON_MAX_BYTES) {
|
|
14736
|
+
return "Provider icons must be 128 KB or smaller.";
|
|
14737
|
+
}
|
|
14738
|
+
let decoded;
|
|
14739
|
+
try {
|
|
14740
|
+
decoded = atob(match[2]);
|
|
14741
|
+
} catch {
|
|
14742
|
+
return "The uploaded image is not valid base64.";
|
|
14743
|
+
}
|
|
14744
|
+
if (decoded.length !== byteLength) return "The uploaded image is not valid base64.";
|
|
14745
|
+
const bytes = Uint8Array.from(decoded, (value) => value.charCodeAt(0));
|
|
14746
|
+
const dimensions = imageDimensions(bytes, match[1]);
|
|
14747
|
+
if (!dimensions) return "The uploaded image is incomplete or does not match its file type.";
|
|
14748
|
+
if (dimensions.width < 1 || dimensions.height < 1 || dimensions.width > PROVIDER_ICON_MAX_DIMENSION || dimensions.height > PROVIDER_ICON_MAX_DIMENSION || dimensions.width * dimensions.height > PROVIDER_ICON_MAX_DIMENSION ** 2) {
|
|
14749
|
+
return `Provider icons must be at most ${PROVIDER_ICON_MAX_DIMENSION} \xD7 ${PROVIDER_ICON_MAX_DIMENSION} pixels.`;
|
|
14750
|
+
}
|
|
14751
|
+
return null;
|
|
14752
|
+
}
|
|
14753
|
+
|
|
14663
14754
|
// server/mcp-registry.ts
|
|
14664
14755
|
var MAX_ARGS = 64;
|
|
14665
14756
|
var stdioEntrySchema = external_exports.object({
|
|
@@ -14700,11 +14791,26 @@ var MAX_LOCAL_VM_MAX_INSTANCES = 4;
|
|
|
14700
14791
|
function isValidSshAlias(value) {
|
|
14701
14792
|
return typeof value === "string" && SSH_ALIAS.test(value);
|
|
14702
14793
|
}
|
|
14794
|
+
var CDP_PORT = /^[0-9]{1,5}$/;
|
|
14795
|
+
var CDP_URL = /^(https?|wss?):\/\/\S+$/i;
|
|
14796
|
+
function isValidCdpTarget(value) {
|
|
14797
|
+
if (typeof value !== "string" || value === "") return false;
|
|
14798
|
+
if (CDP_PORT.test(value)) {
|
|
14799
|
+
const port = Number(value);
|
|
14800
|
+
return port >= 1 && port <= 65535;
|
|
14801
|
+
}
|
|
14802
|
+
return CDP_URL.test(value);
|
|
14803
|
+
}
|
|
14703
14804
|
var vpsConfigSchema = external_exports.object({
|
|
14704
14805
|
sshAlias: external_exports.string().refine((value) => value === "" || isValidSshAlias(value), {
|
|
14705
14806
|
message: "must be a simple SSH config alias"
|
|
14706
14807
|
}).optional()
|
|
14707
14808
|
});
|
|
14809
|
+
var browserEngineConfigSchema = external_exports.object({
|
|
14810
|
+
attachCdpUrl: external_exports.string().trim().max(2048).refine((value) => value === "" || isValidCdpTarget(value), {
|
|
14811
|
+
message: "browserEngine.attachCdpUrl must be a CDP port (1-65535) or an http(s)/ws(s) URL"
|
|
14812
|
+
}).optional()
|
|
14813
|
+
});
|
|
14708
14814
|
var roomConfigSchema = external_exports.object({
|
|
14709
14815
|
turnTimeoutMinutes: external_exports.number().int().min(MIN_ROOM_TURN_TIMEOUT_MINUTES).max(MAX_ROOM_TURN_TIMEOUT_MINUTES),
|
|
14710
14816
|
/** Room handoff tree lifetime. Active execution pauses this clock; the
|
|
@@ -14846,7 +14952,11 @@ var featureConfigSchema = external_exports.object({
|
|
|
14846
14952
|
/** Claude bots also load the MCP servers and connectors from this
|
|
14847
14953
|
* machine's own Claude Code setup (Plugins → MCP servers switch). Off by
|
|
14848
14954
|
* default: each extra tool costs tokens on every model call. */
|
|
14849
|
-
claudeUserMcp: external_exports.boolean().optional()
|
|
14955
|
+
claudeUserMcp: external_exports.boolean().optional(),
|
|
14956
|
+
/** LLM-generated titles for new bot threads. Off until explicitly
|
|
14957
|
+
* enabled; a one-shot that fails or answers junk leaves the first-message
|
|
14958
|
+
* snippet in place — see llmThreadTitlesEnabled. */
|
|
14959
|
+
llmThreadTitles: external_exports.boolean().optional()
|
|
14850
14960
|
});
|
|
14851
14961
|
var onboardingConfigSchema = external_exports.object({
|
|
14852
14962
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -14860,6 +14970,10 @@ var instanceConfigSchema = external_exports.object({
|
|
|
14860
14970
|
driver: external_exports.string().min(1),
|
|
14861
14971
|
displayName: optionalText,
|
|
14862
14972
|
accentColor: optionalText,
|
|
14973
|
+
icon: external_exports.discriminatedUnion("kind", [
|
|
14974
|
+
external_exports.object({ kind: external_exports.literal("preset"), preset: external_exports.enum(PROVIDER_ICON_PRESETS) }).strict(),
|
|
14975
|
+
external_exports.object({ kind: external_exports.literal("custom"), dataUrl: external_exports.string() }).strict().refine((icon) => providerIconError(icon) === null, { message: "Invalid provider icon" })
|
|
14976
|
+
]).optional(),
|
|
14863
14977
|
environment: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
14864
14978
|
enabled: external_exports.boolean().optional(),
|
|
14865
14979
|
config: external_exports.json().optional()
|
|
@@ -14924,6 +15038,7 @@ var appConfigSchema = external_exports.object({
|
|
|
14924
15038
|
/** Voice settings and the selected voice id. `provider` picks the
|
|
14925
15039
|
* engine: "elevenlabs" (default; needs `key`), "fish" (needs its own
|
|
14926
15040
|
* `fishKey`), "system" (the Mac's built-in voices, no key), or
|
|
15041
|
+
* "xai" (Grok TTS, reusing `xai.key`), or
|
|
14927
15042
|
* "chatterbox" (a local OpenAI-compatible Chatterbox server; `baseUrl`
|
|
14928
15043
|
* and `model` are settings, not secrets). Cloud keys stay separate so
|
|
14929
15044
|
* switching providers never overwrites or misuses the other key. */
|
|
@@ -14931,7 +15046,7 @@ var appConfigSchema = external_exports.object({
|
|
|
14931
15046
|
key: optionalText,
|
|
14932
15047
|
fishKey: optionalText,
|
|
14933
15048
|
voice: optionalText,
|
|
14934
|
-
provider: external_exports.enum(["elevenlabs", "fish", "system", "chatterbox"]).optional(),
|
|
15049
|
+
provider: external_exports.enum(["elevenlabs", "fish", "system", "chatterbox", "xai"]).optional(),
|
|
14935
15050
|
baseUrl: external_exports.string().trim().max(2048).refine((value) => !value || /^https?:\/\//i.test(value), "the Chatterbox server address must start with http:// or https://").optional(),
|
|
14936
15051
|
model: optionalText
|
|
14937
15052
|
}).optional(),
|
|
@@ -14960,6 +15075,11 @@ var appConfigSchema = external_exports.object({
|
|
|
14960
15075
|
* system language. Unknown tags degrade to English in the renderer. */
|
|
14961
15076
|
language: optionalText,
|
|
14962
15077
|
rooms: roomConfigSchema.optional(),
|
|
15078
|
+
context: external_exports.object({
|
|
15079
|
+
rebuildBytes: external_exports.number().int().min(1024).max(1e6).optional(),
|
|
15080
|
+
compactAt: external_exports.number().positive().max(1e7).optional(),
|
|
15081
|
+
autoCompact: external_exports.boolean().optional()
|
|
15082
|
+
}).optional(),
|
|
14963
15083
|
threads: external_exports.object({
|
|
14964
15084
|
maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
|
|
14965
15085
|
/** Cap each per-thread events/ and native/ NDJSON log at this many
|
|
@@ -14972,6 +15092,8 @@ var appConfigSchema = external_exports.object({
|
|
|
14972
15092
|
localVm: localVmConfigSchema.optional(),
|
|
14973
15093
|
features: featureConfigSchema.optional(),
|
|
14974
15094
|
onboarding: onboardingConfigSchema.optional(),
|
|
15095
|
+
/** CDP attach target for a bot's browser; see browserEngineConfigSchema. */
|
|
15096
|
+
browserEngine: browserEngineConfigSchema.optional(),
|
|
14975
15097
|
browserProfiles: browserProfilesSchema.optional(),
|
|
14976
15098
|
instances: instanceConfigMapSchema.optional(),
|
|
14977
15099
|
/** User-configured MCP servers, mounted into every capable engine. Kept
|
|
@@ -15010,6 +15132,7 @@ var SPAWNED_PROXIES = {
|
|
|
15010
15132
|
connectors: resolveProxy("connector-proxy"),
|
|
15011
15133
|
mcpGate: resolveProxy("mcp-gate"),
|
|
15012
15134
|
phone: resolveProxy("drivers/phone-proxy"),
|
|
15135
|
+
hook: resolveProxy("hooks/omb-hook"),
|
|
15013
15136
|
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
15014
15137
|
// resolved through the same single source of truth so the packaged layout
|
|
15015
15138
|
// check can assert it ships.
|
|
@@ -15125,6 +15248,86 @@ function createControlClient(options) {
|
|
|
15125
15248
|
}
|
|
15126
15249
|
var CONTROL_REFUSAL_PLAIN = "A person has taken control of this computer, so this call was NOT performed. Do not retry it \u2014 the screen is changing under their hands. Pause this task, tell the person you are waiting for them to hand control back, and take a fresh screenshot before your next action once they have.";
|
|
15127
15250
|
|
|
15251
|
+
// server/mcp-tool-schema.ts
|
|
15252
|
+
var ROOT_COMBINATORS = ["anyOf", "oneOf", "allOf", "not"];
|
|
15253
|
+
function isObject2(value) {
|
|
15254
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
15255
|
+
}
|
|
15256
|
+
function branches(value) {
|
|
15257
|
+
return Array.isArray(value) ? value.filter(isObject2) : [];
|
|
15258
|
+
}
|
|
15259
|
+
function objectShape(schema) {
|
|
15260
|
+
const properties = isObject2(schema.properties) ? { ...schema.properties } : {};
|
|
15261
|
+
const required2 = new Set(Array.isArray(schema.required) ? schema.required.filter((key) => typeof key === "string") : []);
|
|
15262
|
+
const adopt = (shape) => {
|
|
15263
|
+
for (const [key, value] of Object.entries(shape.properties)) if (!(key in properties)) properties[key] = value;
|
|
15264
|
+
};
|
|
15265
|
+
for (const branch of branches(schema.allOf)) {
|
|
15266
|
+
const shape = objectShape(branch);
|
|
15267
|
+
adopt(shape);
|
|
15268
|
+
for (const key of shape.required) required2.add(key);
|
|
15269
|
+
}
|
|
15270
|
+
for (const keyword of ["anyOf", "oneOf"]) {
|
|
15271
|
+
const choices = branches(schema[keyword]).map(objectShape);
|
|
15272
|
+
if (choices.length === 0) continue;
|
|
15273
|
+
for (const choice of choices) adopt(choice);
|
|
15274
|
+
for (const key of choices[0].required) {
|
|
15275
|
+
if (choices.every((choice) => choice.required.includes(key))) required2.add(key);
|
|
15276
|
+
}
|
|
15277
|
+
}
|
|
15278
|
+
return { properties, required: [...required2] };
|
|
15279
|
+
}
|
|
15280
|
+
function describesObject(type) {
|
|
15281
|
+
return type === void 0 || type === "object" || Array.isArray(type) && type.includes("object");
|
|
15282
|
+
}
|
|
15283
|
+
function providerSafeInputSchema(schema) {
|
|
15284
|
+
if (!isObject2(schema) || !describesObject(schema.type)) return { type: "object", properties: {} };
|
|
15285
|
+
if (schema.type === "object" && ROOT_COMBINATORS.every((keyword) => !(keyword in schema))) return schema;
|
|
15286
|
+
const { properties, required: required2 } = objectShape(schema);
|
|
15287
|
+
const safe = { ...schema, type: "object", properties };
|
|
15288
|
+
for (const keyword of ROOT_COMBINATORS) delete safe[keyword];
|
|
15289
|
+
if (required2.length > 0) safe.required = required2;
|
|
15290
|
+
else delete safe.required;
|
|
15291
|
+
return safe;
|
|
15292
|
+
}
|
|
15293
|
+
function createToolListNormalizer() {
|
|
15294
|
+
const pending = /* @__PURE__ */ new Set();
|
|
15295
|
+
const idKey = (id) => `${typeof id}:${String(id)}`;
|
|
15296
|
+
return {
|
|
15297
|
+
observeRequest(line) {
|
|
15298
|
+
let frame;
|
|
15299
|
+
try {
|
|
15300
|
+
frame = JSON.parse(line);
|
|
15301
|
+
} catch {
|
|
15302
|
+
return;
|
|
15303
|
+
}
|
|
15304
|
+
if (isObject2(frame) && frame.method === "tools/list" && (typeof frame.id === "number" || typeof frame.id === "string")) {
|
|
15305
|
+
pending.add(idKey(frame.id));
|
|
15306
|
+
}
|
|
15307
|
+
},
|
|
15308
|
+
rewriteResponse(line) {
|
|
15309
|
+
if (pending.size === 0) return line;
|
|
15310
|
+
let frame;
|
|
15311
|
+
try {
|
|
15312
|
+
frame = JSON.parse(line);
|
|
15313
|
+
} catch {
|
|
15314
|
+
return line;
|
|
15315
|
+
}
|
|
15316
|
+
if (!isObject2(frame) || "method" in frame || !pending.delete(idKey(frame.id))) return line;
|
|
15317
|
+
if (!isObject2(frame.result) || !Array.isArray(frame.result.tools)) return line;
|
|
15318
|
+
let changed = false;
|
|
15319
|
+
const tools = frame.result.tools.map((tool) => {
|
|
15320
|
+
if (!isObject2(tool)) return tool;
|
|
15321
|
+
const inputSchema = providerSafeInputSchema(tool.inputSchema);
|
|
15322
|
+
if (inputSchema === tool.inputSchema) return tool;
|
|
15323
|
+
changed = true;
|
|
15324
|
+
return { ...tool, inputSchema };
|
|
15325
|
+
});
|
|
15326
|
+
return changed ? JSON.stringify({ ...frame, result: { ...frame.result, tools } }) : line;
|
|
15327
|
+
}
|
|
15328
|
+
};
|
|
15329
|
+
}
|
|
15330
|
+
|
|
15128
15331
|
// server/mcp-bridge.ts
|
|
15129
15332
|
var BRIDGE_INACTIVITY_MS = 45e3;
|
|
15130
15333
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -15294,8 +15497,10 @@ function runMcpBridge(options) {
|
|
|
15294
15497
|
}
|
|
15295
15498
|
} : {}
|
|
15296
15499
|
});
|
|
15500
|
+
const toolLists = createToolListNormalizer();
|
|
15297
15501
|
let pendingInput = Promise.resolve();
|
|
15298
15502
|
const inbound = createLineSplitter((line) => {
|
|
15503
|
+
toolLists.observeRequest(line);
|
|
15299
15504
|
const completion = intercept(line);
|
|
15300
15505
|
if (completion) pendingInput = completion;
|
|
15301
15506
|
});
|
|
@@ -15305,7 +15510,7 @@ function runMcpBridge(options) {
|
|
|
15305
15510
|
inbound.flush();
|
|
15306
15511
|
void pendingInput.finally(() => child.stdin.end());
|
|
15307
15512
|
});
|
|
15308
|
-
const outbound = createLineSplitter((line) => process.stdout.write(line + "\n"));
|
|
15513
|
+
const outbound = createLineSplitter((line) => process.stdout.write(toolLists.rewriteResponse(line) + "\n"));
|
|
15309
15514
|
child.stdout.on("data", (chunk) => outbound.push(chunk));
|
|
15310
15515
|
child.stdout.on("end", () => outbound.flush());
|
|
15311
15516
|
const detach = () => {
|
|
@@ -763,6 +763,7 @@ var READ_ONLY_AGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
763
763
|
"wait_delegation",
|
|
764
764
|
"session_search",
|
|
765
765
|
"session_read",
|
|
766
|
+
"tool_result_read",
|
|
766
767
|
"list_routines",
|
|
767
768
|
"skills_list"
|
|
768
769
|
]);
|
|
@@ -798,6 +799,88 @@ function peerName(value) {
|
|
|
798
799
|
return clip(value.replace(/[[\]]/g, " "), ROSTER_NAME_MAX);
|
|
799
800
|
}
|
|
800
801
|
|
|
802
|
+
// shared/redact.ts
|
|
803
|
+
var REDACTION_MARKER = /^«redacted \d+ chars»$/;
|
|
804
|
+
var mask = (value) => REDACTION_MARKER.test(value) ? value : `\xABredacted ${value.length} chars\xBB`;
|
|
805
|
+
var KEY_PREFIXES = [
|
|
806
|
+
/\bom[dg]_[A-Za-z0-9_-]{43}(?![A-Za-z0-9_-])/g,
|
|
807
|
+
// desktop device and model-only credentials
|
|
808
|
+
/\bsk-(?:ant-|proj-|live-|test-)?[A-Za-z0-9_-]{16,}/g,
|
|
809
|
+
// anthropic / openai / stripe
|
|
810
|
+
/\bxai-[A-Za-z0-9_-]{20,}/g,
|
|
811
|
+
// xai (grok)
|
|
812
|
+
/\bgsk_[A-Za-z0-9]{40,}/g,
|
|
813
|
+
// groq
|
|
814
|
+
/\bhf_[A-Za-z0-9]{30,}/g,
|
|
815
|
+
// hugging face
|
|
816
|
+
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g,
|
|
817
|
+
// github classic
|
|
818
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/g,
|
|
819
|
+
// github fine-grained
|
|
820
|
+
/\bxox[abposr]-[A-Za-z0-9-]{20,}/g,
|
|
821
|
+
// slack
|
|
822
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
823
|
+
// aws access key id
|
|
824
|
+
/\bAIza[0-9A-Za-z_-]{30,}/g,
|
|
825
|
+
// google api key
|
|
826
|
+
/\bnpm_[A-Za-z0-9]{20,}/g,
|
|
827
|
+
// npm
|
|
828
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
|
|
829
|
+
// jwt
|
|
830
|
+
];
|
|
831
|
+
var BEARER = /(\bBearer\s+)([A-Za-z0-9._~+/=-]{12,})/g;
|
|
832
|
+
var PEM_BLOCK = /(-----BEGIN [A-Z ]*PRIVATE KEY-----)([\s\S]*?)(-----END [A-Z ]*PRIVATE KEY-----)/g;
|
|
833
|
+
var KEY_VALUE = /\b((?:[A-Za-z0-9_-]*_)?(?:api[_-]?key|apikey|secret|token|password|passwd|authorization|auth[_-]?token|access[_-]?key|private[_-]?key)s?)(["']?\s*[=:]\s*)(["']?)([A-Za-z0-9._~+/=-]{8,})\3/gi;
|
|
834
|
+
var KEY_SUFFIX_ASSIGNMENT = /\b([A-Za-z][A-Za-z0-9_-]*[_-]key)s?(=)(["']?)([A-Za-z0-9._~+/=-]+)\3/gi;
|
|
835
|
+
var SECRET_FLAG = /(--(?:token|password|passwd|api-key|apikey|secret|access-key|auth-token)(?:=|\s+))(["']?)(?!-)([A-Za-z0-9._~+/=-]+)\2/gi;
|
|
836
|
+
var URL_USERINFO = /(\b[a-z][a-z0-9+.-]*:\/\/[^\s/:@'"]+:)([^\s/@'"«»]+)(@)/gi;
|
|
837
|
+
function redactSecretsInText(text) {
|
|
838
|
+
if (!text || text.length < 8) return text;
|
|
839
|
+
let out = text;
|
|
840
|
+
out = out.replace(PEM_BLOCK, (_m, open, body, close) => `${open}
|
|
841
|
+
${mask(body.trim())}
|
|
842
|
+
${close}`);
|
|
843
|
+
for (const re of KEY_PREFIXES) out = out.replace(re, (m2) => mask(m2));
|
|
844
|
+
out = out.replace(BEARER, (_m, lead, tok) => `${lead}${mask(tok)}`);
|
|
845
|
+
out = out.replace(KEY_VALUE, (_m, key, sep, quote, value) => `${key}${sep}${quote}${mask(value)}${quote}`);
|
|
846
|
+
out = out.replace(KEY_SUFFIX_ASSIGNMENT, (_m, key, sep, quote, value) => `${key}${sep}${quote}${mask(value)}${quote}`);
|
|
847
|
+
out = out.replace(SECRET_FLAG, (_m, flag, quote, value) => `${flag}${quote}${mask(value)}${quote}`);
|
|
848
|
+
out = out.replace(URL_USERINFO, (_m, lead, secret, at) => `${lead}${mask(secret)}${at}`);
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// server/tool-results.ts
|
|
853
|
+
var TOOL_RESULT_PREVIEW_CHARS = 16e3;
|
|
854
|
+
var TOOL_RESULT_MAX_CHARS = 128 * 1024;
|
|
855
|
+
var TOOL_RESULT_TTL_MS = 60 * 6e4;
|
|
856
|
+
var MAX_BYTES = 16 * 1024 * 1024;
|
|
857
|
+
var MAX_OWNER_BYTES = 2 * 1024 * 1024;
|
|
858
|
+
function toolResultPrefix(text, chars) {
|
|
859
|
+
const prefix = text.slice(0, chars);
|
|
860
|
+
const last = prefix.charCodeAt(prefix.length - 1);
|
|
861
|
+
return last >= 55296 && last <= 56319 ? prefix.slice(0, -1) : prefix;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// server/drivers/agents-result.ts
|
|
865
|
+
async function boundedAgentResult(text, save) {
|
|
866
|
+
if (text.length <= 24e3) return text;
|
|
867
|
+
const redacted = redactSecretsInText(text);
|
|
868
|
+
const prefix = toolResultPrefix(redacted, TOOL_RESULT_PREVIEW_CHARS);
|
|
869
|
+
const retained = toolResultPrefix(redacted, TOOL_RESULT_MAX_CHARS);
|
|
870
|
+
const truncated = retained.length < redacted.length;
|
|
871
|
+
try {
|
|
872
|
+
const saved = await save(retained, truncated);
|
|
873
|
+
if (!saved || typeof saved.id !== "string" || !/^r-[0-9a-f-]{36}$/.test(saved.id)) throw new Error("Invalid saved result");
|
|
874
|
+
return `${prefix}
|
|
875
|
+
|
|
876
|
+
[Large tool result: showing the first ${prefix.length} characters. ${truncated || saved.truncated ? "Only a bounded portion was retained; the remaining tail was omitted." : "The remaining redacted result is temporarily saved."} If a missing detail is needed, call tool_result_read with id "${saved.id}" and offset ${prefix.length}. Saved results expire after one hour, on app restart, or under cache pressure. Do not repeat an action just to retrieve its output.]`;
|
|
877
|
+
} catch {
|
|
878
|
+
return `${prefix}
|
|
879
|
+
|
|
880
|
+
[Large tool result: showing the first ${prefix.length} characters. The remaining output could not be saved. The original operation was not retried. Do not repeat an action just to retrieve its output.]`;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
801
884
|
// server/drivers/agents-proxy.ts
|
|
802
885
|
var HARNESS = process.env.OMB_HARNESS_URL ?? "http://127.0.0.1:8799";
|
|
803
886
|
var BOT_ID = process.env.OMB_BOT_ID ?? "";
|
|
@@ -1047,8 +1130,8 @@ var ROUTINE_FIELDS_SCHEMA = {
|
|
|
1047
1130
|
schedule: ROUTINE_SCHEDULE_SCHEMA,
|
|
1048
1131
|
run_on: {
|
|
1049
1132
|
type: "string",
|
|
1050
|
-
enum: ["maus", "
|
|
1051
|
-
description: "
|
|
1133
|
+
enum: ["maus", "box"],
|
|
1134
|
+
description: "Default maus keeps the bot's selected model and configured computer, INCLUDING a self-hosted VPS. Omit this field for normal schedules. box explicitly switches the agent to the Box-hosted runner; it requires Box setup and is not the generic cloud/VPS option. Legacy cloud values from list_routines mean box, not VPS."
|
|
1052
1135
|
},
|
|
1053
1136
|
timeout_minutes: {
|
|
1054
1137
|
type: "integer",
|
|
@@ -1063,10 +1146,28 @@ var ROUTINE_FIELDS_SCHEMA = {
|
|
|
1063
1146
|
continuity: {
|
|
1064
1147
|
type: "boolean",
|
|
1065
1148
|
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."
|
|
1149
|
+
},
|
|
1150
|
+
overlap: {
|
|
1151
|
+
type: "string",
|
|
1152
|
+
enum: ["skip", "queue"],
|
|
1153
|
+
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."
|
|
1066
1154
|
}
|
|
1067
1155
|
};
|
|
1068
1156
|
var 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.";
|
|
1069
1157
|
var TOOLS = [
|
|
1158
|
+
{
|
|
1159
|
+
name: "tool_result_read",
|
|
1160
|
+
description: "Read a missing portion of an oversized agents-tool result using the saved id and next offset from its notice. Returns at most 16,000 characters, only from this bot in this conversation. Use only when the preview is insufficient; do not load every page by default. Results expire after one hour, on app restart, or under cache pressure. This never reruns the original action.",
|
|
1161
|
+
inputSchema: {
|
|
1162
|
+
type: "object",
|
|
1163
|
+
additionalProperties: false,
|
|
1164
|
+
properties: {
|
|
1165
|
+
id: { type: "string", description: "Saved result id copied from the truncation notice." },
|
|
1166
|
+
offset: { type: "integer", minimum: 0, description: "Character offset copied from the previous result's notice. Defaults to 0." }
|
|
1167
|
+
},
|
|
1168
|
+
required: ["id"]
|
|
1169
|
+
}
|
|
1170
|
+
},
|
|
1070
1171
|
{
|
|
1071
1172
|
name: "list_shared_computers",
|
|
1072
1173
|
description: "List online desktop computers explicitly shared with this workspace, and their allowed folders/capabilities. These are the user's computers, not this server. An offline or unshared computer cannot be accessed. Folder paths use opaque folder IDs and relative paths.",
|
|
@@ -1117,7 +1218,7 @@ var TOOLS = [
|
|
|
1117
1218
|
},
|
|
1118
1219
|
{
|
|
1119
1220
|
name: "ask_bot",
|
|
1120
|
-
description: "
|
|
1221
|
+
description: "Brief synchronous consultation: send a short question to another bot. Quick replies return inline; slow replies become asynchronous delegations and return automatically after you finish your turn. Use only when that reply is required to write your current response. Do not use for assigning work, background tasks, or potentially long work; use delegate_bot for those. Returns promptly with a note if that bot is busy.",
|
|
1121
1222
|
inputSchema: {
|
|
1122
1223
|
type: "object",
|
|
1123
1224
|
properties: {
|
|
@@ -1359,6 +1460,20 @@ var TOOLS = [
|
|
|
1359
1460
|
required: ["action"]
|
|
1360
1461
|
}
|
|
1361
1462
|
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "retry_thread",
|
|
1465
|
+
description: "Chief of Staff only. Resume a teammate's thread whose last run failed, stalled or could not start \u2014 the one an incident report named \u2014 exactly where it stopped, keeping its conversation and files. The teammate gets a line saying you asked for the retry and why. Use it when the cause looks transient (a crash, a timeout, a busy service). Use delegate_bot with a corrected brief instead when the request itself needs to change, and tell the person instead when only they can fix the cause (a sign-in, a missing credential, an unanswered question). Never retry the same thread more than twice.",
|
|
1466
|
+
inputSchema: {
|
|
1467
|
+
type: "object",
|
|
1468
|
+
additionalProperties: false,
|
|
1469
|
+
properties: {
|
|
1470
|
+
bot_id: { type: "string", description: "The teammate's id, from the incident report or list_bots." },
|
|
1471
|
+
thread_id: { type: "string", description: "The failed thread's id, from the incident report." },
|
|
1472
|
+
note: { type: "string", description: "Optional: one sentence for the teammate about what to watch for this time." }
|
|
1473
|
+
},
|
|
1474
|
+
required: ["bot_id", "thread_id"]
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1362
1477
|
{
|
|
1363
1478
|
name: "memory_log",
|
|
1364
1479
|
description: "Write one line to today's log file, memory/log/YYYY-MM-DD.md, stamped with the time and this conversation: what happened, not what is true. Use it for events worth a trace \u2014 a deploy went out, a person decided something, a check failed \u2014 that should not shape future sessions. Logs are never loaded into your prompt; the person can read them, and session_search finds them later. A fact that should hold in every session goes to memory_update instead.",
|
|
@@ -1544,6 +1659,11 @@ async function api(path, init) {
|
|
|
1544
1659
|
if (!ok2) throw new Error(String(body.error ?? `HTTP ${status}`));
|
|
1545
1660
|
return body;
|
|
1546
1661
|
}
|
|
1662
|
+
var capResult = (text) => boundedAgentResult(text, (retained, truncated) => api("/api/internal/tool-result", {
|
|
1663
|
+
method: "POST",
|
|
1664
|
+
signal: AbortSignal.timeout(3e3),
|
|
1665
|
+
body: JSON.stringify({ text: retained, truncated })
|
|
1666
|
+
}));
|
|
1547
1667
|
async function apiResponse(path, init) {
|
|
1548
1668
|
const res = await fetch(HARNESS + path, {
|
|
1549
1669
|
...init,
|
|
@@ -1566,16 +1686,17 @@ function routineAction(value) {
|
|
|
1566
1686
|
}
|
|
1567
1687
|
function routineFields(args) {
|
|
1568
1688
|
const fields = {};
|
|
1569
|
-
|
|
1689
|
+
const destination = (value) => value === "box" ? "cloud" : value;
|
|
1690
|
+
if (args.run_on != null && args.runOn != null && destination(args.run_on) !== destination(args.runOn)) {
|
|
1570
1691
|
return { fields, error: "Choose one run_on destination; run_on and runOn disagree." };
|
|
1571
1692
|
}
|
|
1572
1693
|
if (args.timeout_minutes != null && args.timeoutMinutes != null && args.timeout_minutes !== args.timeoutMinutes) {
|
|
1573
1694
|
return { fields, error: "Choose one timeout_minutes limit; timeout_minutes and timeoutMinutes disagree." };
|
|
1574
1695
|
}
|
|
1575
|
-
const runOn = args.run_on ?? args.runOn;
|
|
1696
|
+
const runOn = destination(args.run_on ?? args.runOn);
|
|
1576
1697
|
const timeoutMinutes = args.timeout_minutes ?? args.timeoutMinutes;
|
|
1577
1698
|
if (runOn != null && runOn !== "maus" && runOn !== "cloud") {
|
|
1578
|
-
return { fields, error: 'run_on
|
|
1699
|
+
return { fields, error: 'Use run_on="maus" for the bot\u2019s current model and configured computer (including VPS), or run_on="box" only for the Box-hosted agent. Legacy "cloud" also means Box.' };
|
|
1579
1700
|
}
|
|
1580
1701
|
if (timeoutMinutes != null && (typeof timeoutMinutes !== "number" || !Number.isInteger(timeoutMinutes) || timeoutMinutes < 5 || timeoutMinutes > 240)) {
|
|
1581
1702
|
return { fields, error: "timeout_minutes must be a whole number from 5 to 240. Use clear_timeout to remove a limit." };
|
|
@@ -1583,6 +1704,9 @@ function routineFields(args) {
|
|
|
1583
1704
|
if (args.continuity != null && typeof args.continuity !== "boolean") {
|
|
1584
1705
|
return { fields, error: "continuity must be true or false." };
|
|
1585
1706
|
}
|
|
1707
|
+
if (args.overlap !== void 0 && args.overlap !== "skip" && args.overlap !== "queue") {
|
|
1708
|
+
return { fields, error: "overlap must be skip or queue." };
|
|
1709
|
+
}
|
|
1586
1710
|
if (args.clear_timeout != null && typeof args.clear_timeout !== "boolean") {
|
|
1587
1711
|
return { fields, error: "clear_timeout must be true or false." };
|
|
1588
1712
|
}
|
|
@@ -1600,6 +1724,7 @@ function routineFields(args) {
|
|
|
1600
1724
|
if (args.clear_timeout === true) fields.timeoutMinutes = null;
|
|
1601
1725
|
else if (timeoutMinutes != null) fields.timeoutMinutes = timeoutMinutes;
|
|
1602
1726
|
if (typeof args.continuity === "boolean") fields.continuity = args.continuity;
|
|
1727
|
+
if (args.overlap !== void 0) fields.overlap = args.overlap;
|
|
1603
1728
|
return { fields };
|
|
1604
1729
|
}
|
|
1605
1730
|
function completedProposalResult(r, subject) {
|
|
@@ -1643,6 +1768,16 @@ function recallSpeaker(hit) {
|
|
|
1643
1768
|
return hit.role === "user" ? "user" : "you";
|
|
1644
1769
|
}
|
|
1645
1770
|
async function callTool(name, args) {
|
|
1771
|
+
if (name === "tool_result_read") {
|
|
1772
|
+
if (typeof args.id !== "string" || !/^r-[0-9a-f-]{36}$/.test(args.id) || args.offset !== void 0 && (!Number.isSafeInteger(args.offset) || Number(args.offset) < 0)) {
|
|
1773
|
+
return { text: "Use the saved result id and a non-negative integer offset from its notice.", isError: true };
|
|
1774
|
+
}
|
|
1775
|
+
const r = await api(`/api/internal/tool-result?id=${encodeURIComponent(args.id)}&offset=${args.offset ?? 0}`, { signal: AbortSignal.timeout(3e3) });
|
|
1776
|
+
const text = String(r.text ?? "");
|
|
1777
|
+
return { text: `${text}
|
|
1778
|
+
|
|
1779
|
+
[${Number(r.nextOffset) < Number(r.length) ? `Read more with tool_result_read id "${args.id}" and offset ${r.nextOffset}.` : `End of retained result.${r.truncated ? " The original tail exceeded the storage limit and was omitted." : ""}`}]` };
|
|
1780
|
+
}
|
|
1646
1781
|
if (name === "list_room_targets") {
|
|
1647
1782
|
const r = await api("/api/internal/room-targets");
|
|
1648
1783
|
return { text: JSON.stringify(r), ...r.error ? { isError: true } : {} };
|
|
@@ -1747,9 +1882,11 @@ Use post_to_room with one of these ids. A post adds one message to the room; it
|
|
|
1747
1882
|
if (r.timeout) {
|
|
1748
1883
|
const taskId = String(r.taskId ?? "").trim();
|
|
1749
1884
|
if (taskId) delegationTaskIdsThisTurn.add(taskId);
|
|
1750
|
-
const
|
|
1885
|
+
const waitedSeconds = Math.max(1, Math.round((Number(r.waitedMs) || 0) / 1e3));
|
|
1886
|
+
const amount = waitedSeconds < 60 ? waitedSeconds : Math.round(waitedSeconds / 60);
|
|
1887
|
+
const unit = waitedSeconds < 60 ? "second" : "minute";
|
|
1751
1888
|
return {
|
|
1752
|
-
text: `${r.toBotName ?? "That bot"} is still working after ${
|
|
1889
|
+
text: `${r.toBotName ?? "That bot"} is still working after ${amount} ${unit}${amount === 1 ? "" : "s"} \u2014 the ask was converted to a delegation so the reply is not lost. Task id: ${taskId}. 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.`
|
|
1753
1890
|
};
|
|
1754
1891
|
}
|
|
1755
1892
|
if (r.busy) {
|
|
@@ -2141,6 +2278,18 @@ ${recent.join("\n")}` : "";
|
|
|
2141
2278
|
const entry = typeof r.entry === "string" && r.entry ? ` Entry: ${r.entry}` : "";
|
|
2142
2279
|
return { text: `Memory updated.${entry}${r.truncated ? " MEMORY.md exceeds the prompt load budget; keep it short and curated." : ""}` };
|
|
2143
2280
|
}
|
|
2281
|
+
if (name === "retry_thread") {
|
|
2282
|
+
const botId = String(args.bot_id ?? "").trim();
|
|
2283
|
+
const threadId = String(args.thread_id ?? "").trim();
|
|
2284
|
+
const note = typeof args.note === "string" ? args.note.trim() : "";
|
|
2285
|
+
if (!botId || !threadId) return { text: "retry_thread needs bot_id and thread_id \u2014 both are in the incident report.", isError: true };
|
|
2286
|
+
const r = await api("/api/internal/retry-thread", {
|
|
2287
|
+
method: "POST",
|
|
2288
|
+
body: JSON.stringify({ fromBotId: BOT_ID, fromThreadId: THREAD_ID, toBotId: botId, toThreadId: threadId, ...note ? { note } : {} })
|
|
2289
|
+
});
|
|
2290
|
+
if (r.error) return { text: `Couldn't retry that thread: ${String(r.error)}`, isError: true };
|
|
2291
|
+
return { text: typeof r.message === "string" ? r.message : "The thread is running again. Its result stays in that thread; you are not woken for it \u2014 check it later with session_search or list_threads if you need to." };
|
|
2292
|
+
}
|
|
2144
2293
|
if (name === "memory_log") {
|
|
2145
2294
|
if (typeof args.text !== "string" || !args.text.trim()) {
|
|
2146
2295
|
return { text: "memory_log needs text: one line about what happened.", isError: true };
|
|
@@ -2316,20 +2465,20 @@ async function handle(msg) {
|
|
|
2316
2465
|
return;
|
|
2317
2466
|
}
|
|
2318
2467
|
if (name === "list_shared_computers") {
|
|
2319
|
-
textResult(id, JSON.stringify(await api("/api/internal/shared-computers")));
|
|
2468
|
+
textResult(id, await capResult(JSON.stringify(await api("/api/internal/shared-computers"))));
|
|
2320
2469
|
return;
|
|
2321
2470
|
}
|
|
2322
2471
|
if (name === "shared_computer") {
|
|
2323
2472
|
const response = await api("/api/internal/shared-computers", { method: "POST", body: JSON.stringify(params.arguments ?? {}) });
|
|
2324
2473
|
const result = response.result;
|
|
2325
2474
|
if (Array.isArray(result?.content)) ok(id, result);
|
|
2326
|
-
else textResult(id, JSON.stringify(result));
|
|
2475
|
+
else textResult(id, await capResult(JSON.stringify(result)));
|
|
2327
2476
|
return;
|
|
2328
2477
|
}
|
|
2329
2478
|
const { text, isError } = await callTool(name, params.arguments ?? {});
|
|
2330
|
-
textResult(id, text, isError);
|
|
2479
|
+
textResult(id, name === "tool_result_read" ? text : await capResult(text), isError);
|
|
2331
2480
|
} catch (e) {
|
|
2332
|
-
textResult(id, e.message, true);
|
|
2481
|
+
textResult(id, await capResult(e.message), true);
|
|
2333
2482
|
}
|
|
2334
2483
|
return;
|
|
2335
2484
|
}
|
|
@@ -14602,6 +14602,7 @@ var SPAWNED_PROXIES = {
|
|
|
14602
14602
|
connectors: resolveProxy("connector-proxy"),
|
|
14603
14603
|
mcpGate: resolveProxy("mcp-gate"),
|
|
14604
14604
|
phone: resolveProxy("drivers/phone-proxy"),
|
|
14605
|
+
hook: resolveProxy("hooks/omb-hook"),
|
|
14605
14606
|
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
14606
14607
|
// resolved through the same single source of truth so the packaged layout
|
|
14607
14608
|
// check can assert it ships.
|