openmausbot 0.1.81 → 0.1.83
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-peLFiSH8.js → index-DSvqfBOf.js} +151 -149
- package/dist/assets/{index-oLMljotU.js → index-nqTFZTxg.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/container-mcp.js +20 -4
- package/dist-server/drivers/agents-proxy.js +4 -4
- package/dist-server/index.js +1155 -511
- package/dist-server/mcp-gate.js +2 -1
- package/dist-server/openmausbot.js +210 -56
- package/dist-server/pair-cli.js +210 -56
- package/dist-server/server/auto-approve.js +15 -1
- package/dist-server/server/auto-vm-claims.js +22 -0
- package/dist-server/server/browser-bundle-release.js +8 -7
- package/dist-server/server/browser-engine.js +1 -1
- package/dist-server/server/channel-queue.js +58 -0
- package/dist-server/server/chief-of-staff.js +2 -2
- package/dist-server/server/config.js +33 -2
- package/dist-server/server/contracts.js +0 -7
- package/dist-server/server/drivers/acp/core.js +1 -1
- package/dist-server/server/drivers/acp/gemini.js +11 -1
- package/dist-server/server/drivers/acp/opencode-go.js +13 -0
- package/dist-server/server/drivers/acp/qwen.js +16 -1
- package/dist-server/server/drivers/agents-proxy.js +4 -4
- package/dist-server/server/drivers/claude.js +4 -3
- package/dist-server/server/drivers/codex.js +66 -1
- package/dist-server/server/drivers/pi.js +2 -1
- package/dist-server/server/env-path.js +6 -1
- package/dist-server/server/index.js +596 -97
- package/dist-server/server/local-routing.js +10 -4
- package/dist-server/server/notify.js +8 -3
- package/dist-server/server/peer-roster.js +37 -1
- package/dist-server/server/recent-work.js +4 -1
- package/dist-server/server/redact.js +5 -48
- package/dist-server/server/room-handoffs.js +79 -16
- package/dist-server/server/routines.js +19 -1
- package/dist-server/server/skill-fetch.js +1 -1
- package/dist-server/server/steer-queue.js +29 -0
- package/dist-server/server/store.js +26 -3
- package/dist-server/server/thread-retention.js +50 -0
- package/dist-server/server/turn-context.js +7 -0
- package/dist-server/server/turn-resources.js +13 -2
- package/dist-server/shared/approval-mode.js +8 -5
- package/dist-server/shared/inspector.js +1 -0
- package/dist-server/shared/json.js +1 -0
- package/dist-server/shared/notification.js +1 -0
- package/dist-server/shared/redact.js +61 -0
- package/dist-server/shared/routines.js +1 -0
- package/dist-server/shared/runtime-events.js +1 -0
- package/dist-server/shared/webhooks.js +1 -0
- package/dist-server/shared/wire.js +8 -0
- package/dist-server/vps-container-mcp.js +20 -4
- package/package.json +1 -1
package/dist-server/index.js
CHANGED
|
@@ -7731,10 +7731,10 @@ var require_dist = __commonJS({
|
|
|
7731
7731
|
|
|
7732
7732
|
// server/index.ts
|
|
7733
7733
|
import { createHash as createHash31, randomBytes as randomBytes9, randomUUID as randomUUID28, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
7734
|
-
import { existsSync as existsSync32, readFileSync as readFileSync45, rmSync as rmSync9
|
|
7734
|
+
import { existsSync as existsSync32, readFileSync as readFileSync45, rmSync as rmSync9 } from "node:fs";
|
|
7735
7735
|
import { rm as removeDirectory } from "node:fs/promises";
|
|
7736
7736
|
import { createServer as createServer3 } from "node:http";
|
|
7737
|
-
import { extname as extname5, join as
|
|
7737
|
+
import { extname as extname5, join as join65 } from "node:path";
|
|
7738
7738
|
|
|
7739
7739
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
7740
7740
|
var external_exports = {};
|
|
@@ -22479,7 +22479,7 @@ var nodeSchema = external_exports.object({
|
|
|
22479
22479
|
approvalGranted: external_exports.boolean().default(false),
|
|
22480
22480
|
kind: external_exports.enum(["work", "assignment"]).default("work")
|
|
22481
22481
|
});
|
|
22482
|
-
var ROOM_HANDOFF_LIMITS = { depth: 4, requests: 24, executions: 48, lifetimeMs: 30 * 6e4, minRunwayMs: 10 * 6e4, queueMs: 60 * 6e4 };
|
|
22482
|
+
var ROOM_HANDOFF_LIMITS = { depth: 4, requests: 24, executions: 48, lifetimeMs: 30 * 6e4, minRunwayMs: 10 * 6e4, queueMs: 60 * 6e4, hardCapMs: 4 * 60 * 6e4 };
|
|
22483
22483
|
var duration3 = (ms2) => ms2 >= 6e4 ? `${Math.floor(ms2 / 6e4)}m` : `${Math.floor(ms2 / 1e3)}s`;
|
|
22484
22484
|
var terminal = (n) => ["completed", "failed", "cancelled"].includes(n.status);
|
|
22485
22485
|
var RoomHandoffs = class {
|
|
@@ -22489,10 +22489,14 @@ var RoomHandoffs = class {
|
|
|
22489
22489
|
file;
|
|
22490
22490
|
hooks;
|
|
22491
22491
|
now;
|
|
22492
|
-
|
|
22492
|
+
limits;
|
|
22493
|
+
/** Per-root pause accounting for the tree lifetime clock. */
|
|
22494
|
+
pauses = /* @__PURE__ */ new Map();
|
|
22495
|
+
constructor(file2, hooks, now = Date.now, limits = {}) {
|
|
22493
22496
|
this.file = file2;
|
|
22494
22497
|
this.hooks = hooks;
|
|
22495
22498
|
this.now = now;
|
|
22499
|
+
this.limits = { ...ROOM_HANDOFF_LIMITS, ...limits };
|
|
22496
22500
|
try {
|
|
22497
22501
|
const saved = external_exports.array(nodeSchema).max(1e4).parse(JSON.parse(readFileSync(file2, "utf8")));
|
|
22498
22502
|
const ids = new Map(saved.map((n) => [n.id, n]));
|
|
@@ -22533,13 +22537,49 @@ var RoomHandoffs = class {
|
|
|
22533
22537
|
}
|
|
22534
22538
|
return path;
|
|
22535
22539
|
}
|
|
22540
|
+
/** The tree lifetime clock pauses while any node in the tree is actively
|
|
22541
|
+
* executing: the budget bounds coordination sprawl, not the runtime of
|
|
22542
|
+
* dispatched work. Accounting is in-memory; a restart fails every
|
|
22543
|
+
* interrupted node anyway, so no pause span survives one. */
|
|
22544
|
+
trackExecutionPauses() {
|
|
22545
|
+
const now = this.now();
|
|
22546
|
+
const executing = /* @__PURE__ */ new Set();
|
|
22547
|
+
for (const n of this.nodes.values()) {
|
|
22548
|
+
if (!terminal(n) && n.status === "running") executing.add(n.rootId);
|
|
22549
|
+
}
|
|
22550
|
+
for (const rootId of executing) {
|
|
22551
|
+
const pause = this.pauses.get(rootId) ?? { accumulatedMs: 0 };
|
|
22552
|
+
pause.since ??= now;
|
|
22553
|
+
this.pauses.set(rootId, pause);
|
|
22554
|
+
}
|
|
22555
|
+
for (const [rootId, pause] of this.pauses) {
|
|
22556
|
+
if (pause.since != null && !executing.has(rootId)) {
|
|
22557
|
+
pause.accumulatedMs += now - pause.since;
|
|
22558
|
+
pause.since = void 0;
|
|
22559
|
+
}
|
|
22560
|
+
const root = this.nodes.get(rootId);
|
|
22561
|
+
if (!root || terminal(root)) this.pauses.delete(rootId);
|
|
22562
|
+
}
|
|
22563
|
+
}
|
|
22564
|
+
pausedMs(root) {
|
|
22565
|
+
const pause = this.pauses.get(root.id);
|
|
22566
|
+
if (!pause) return 0;
|
|
22567
|
+
return pause.accumulatedMs + (pause.since != null ? this.now() - pause.since : 0);
|
|
22568
|
+
}
|
|
22569
|
+
/** The lifetime budget consumed so far: wall clock minus paused time. */
|
|
22570
|
+
effectiveAgeMs(root) {
|
|
22571
|
+
return Math.max(0, this.now() - root.createdAt - this.pausedMs(root));
|
|
22572
|
+
}
|
|
22536
22573
|
/** The earliest moment this node may be failed for lifetime: the tree
|
|
22537
22574
|
* ceiling, a running node's own start plus a minimum runway, or, for work
|
|
22538
|
-
* parked in a busy teammate's queue, its own queue window (#1238).
|
|
22575
|
+
* parked in a busy teammate's queue, its own queue window (#1238). The
|
|
22576
|
+
* wall-clock hard cap clamps every extension: a tree that never stops
|
|
22577
|
+
* executing still dies, so runway extensions cannot compound forever. */
|
|
22539
22578
|
deadline(n) {
|
|
22540
22579
|
const anchor = n.status === "running" ? n.startedAt ?? n.createdAt : n.createdAt;
|
|
22541
|
-
const
|
|
22542
|
-
|
|
22580
|
+
const root = this.root(n);
|
|
22581
|
+
const ceiling = n.status === "queued" && n.executions === 0 ? anchor + this.limits.queueMs : root.createdAt + this.limits.lifetimeMs + this.pausedMs(root);
|
|
22582
|
+
return Math.min(Math.max(ceiling, anchor + this.limits.minRunwayMs), root.createdAt + this.limits.hardCapMs);
|
|
22543
22583
|
}
|
|
22544
22584
|
/** An ancestor past its ceiling is not failed while a descendant is still
|
|
22545
22585
|
* running inside its own runway; cancelling would cascade into that work. */
|
|
@@ -22559,9 +22599,16 @@ var RoomHandoffs = class {
|
|
|
22559
22599
|
* never started reports the queue window it waited out, not the tree's. */
|
|
22560
22600
|
lifetimeError(n) {
|
|
22561
22601
|
if (n.status === "queued" && n.executions === 0) {
|
|
22562
|
-
return `Room handoff queue budget exhausted: never started while waiting for a busy teammate after ${duration3(this.now() - n.createdAt)} of the ${duration3(
|
|
22602
|
+
return `Room handoff queue budget exhausted: never started while waiting for a busy teammate after ${duration3(this.now() - n.createdAt)} of the ${duration3(this.limits.queueMs)} queue window`;
|
|
22563
22603
|
}
|
|
22564
|
-
|
|
22604
|
+
const root = this.root(n);
|
|
22605
|
+
return `Room handoff lifetime budget exhausted: node was ${n.status} after ${duration3(this.effectiveAgeMs(root))} of the ${duration3(this.limits.lifetimeMs)} tree lifetime`;
|
|
22606
|
+
}
|
|
22607
|
+
/** The wall-clock ceiling ignores pauses: it is what stops a tree whose
|
|
22608
|
+
* execution never pauses long enough to age its lifetime budget. */
|
|
22609
|
+
hardCapError(n) {
|
|
22610
|
+
const root = this.root(n);
|
|
22611
|
+
return `Room handoff hard cap exhausted: node was ${n.status} after ${duration3(this.now() - root.createdAt)} of the ${duration3(this.limits.hardCapMs)} wall-clock cap`;
|
|
22565
22612
|
}
|
|
22566
22613
|
enqueue(source, generation, parentId, target, key3, text8, approvalGranted = false, rework = false, sourceText = "") {
|
|
22567
22614
|
if (this.loadError) throw new Error(this.loadError);
|
|
@@ -22586,13 +22633,15 @@ var RoomHandoffs = class {
|
|
|
22586
22633
|
throw new Error("This agent already completed your assignment. Do not send acknowledgements or approvals as new work. Finish with your decision; results return automatically. Only use rework=true for concrete additional work.");
|
|
22587
22634
|
}
|
|
22588
22635
|
if (kind === "work" && target.groupId && path.some((n) => n.groupId === target.groupId)) throw new Error("A room request cannot return to an ancestor room; results are returned automatically");
|
|
22589
|
-
if (kind === "work" && path.filter((n) => n.kind === "work").length >
|
|
22636
|
+
if (kind === "work" && path.filter((n) => n.kind === "work").length > this.limits.depth) throw new Error("Room handoff depth limit reached");
|
|
22590
22637
|
const root = fresh ? parent : this.root(parent);
|
|
22591
22638
|
const count = [...this.nodes.values()].filter((n) => n.rootId === parent.rootId && n.parentId).length;
|
|
22592
|
-
if (count >=
|
|
22593
|
-
const
|
|
22594
|
-
|
|
22595
|
-
|
|
22639
|
+
if (count >= this.limits.requests) throw new Error("Room handoff budget exhausted");
|
|
22640
|
+
const lifetimeRemaining = this.limits.lifetimeMs - this.effectiveAgeMs(root);
|
|
22641
|
+
const hardCapRemaining = root.createdAt + this.limits.hardCapMs - this.now();
|
|
22642
|
+
const remaining = Math.min(lifetimeRemaining, hardCapRemaining);
|
|
22643
|
+
if (remaining < this.limits.minRunwayMs) {
|
|
22644
|
+
throw new Error(`Room handoff budget exhausted: only ${duration3(Math.max(remaining, 0))} of the ${duration3(this.limits.lifetimeMs)} tree lifetime remains`);
|
|
22596
22645
|
}
|
|
22597
22646
|
if (this.nodes.size >= 1e3) {
|
|
22598
22647
|
const oldRoots = [...this.nodes.values()].filter((n) => !n.parentId && terminal(n)).sort((a, b3) => a.createdAt - b3.createdAt);
|
|
@@ -22693,11 +22742,13 @@ var RoomHandoffs = class {
|
|
|
22693
22742
|
}
|
|
22694
22743
|
tick() {
|
|
22695
22744
|
if (this.loadError) return;
|
|
22745
|
+
this.trackExecutionPauses();
|
|
22696
22746
|
for (const n of [...this.nodes.values()].reverse()) {
|
|
22697
22747
|
if (terminal(n)) continue;
|
|
22698
22748
|
const error51 = this.hooks.validate(n, n.parentId ? this.nodes.get(n.parentId) : void 0);
|
|
22699
|
-
|
|
22700
|
-
|
|
22749
|
+
const hardCapped = this.now() >= this.root(n).createdAt + this.limits.hardCapMs;
|
|
22750
|
+
if (error51 || hardCapped || this.now() > this.deadline(n) && !this.protectsRunner(n) && !this.owesFollowUp(n)) {
|
|
22751
|
+
this.cancelTree(n, error51 ?? (hardCapped ? this.hardCapError(n) : this.lifetimeError(n)), "failed");
|
|
22701
22752
|
}
|
|
22702
22753
|
}
|
|
22703
22754
|
for (const n of this.nodes.values()) {
|
|
@@ -22723,7 +22774,7 @@ var RoomHandoffs = class {
|
|
|
22723
22774
|
if (this.hooks.busy(n)) continue;
|
|
22724
22775
|
const root = this.root(n);
|
|
22725
22776
|
const executionCost = 1;
|
|
22726
|
-
if (root.executions + executionCost >
|
|
22777
|
+
if (root.executions + executionCost > this.limits.executions) {
|
|
22727
22778
|
this.cancelTree(n, "Room execution budget exhausted", "failed");
|
|
22728
22779
|
continue;
|
|
22729
22780
|
}
|
|
@@ -22732,6 +22783,9 @@ var RoomHandoffs = class {
|
|
|
22732
22783
|
root.executions += executionCost;
|
|
22733
22784
|
n.status = "running";
|
|
22734
22785
|
n.startedAt = this.now();
|
|
22786
|
+
const pause = this.pauses.get(root.id) ?? { accumulatedMs: 0 };
|
|
22787
|
+
pause.since ??= this.now();
|
|
22788
|
+
this.pauses.set(root.id, pause);
|
|
22735
22789
|
this.publish(n, root);
|
|
22736
22790
|
const controller = new AbortController();
|
|
22737
22791
|
this.controllers.set(n.id, controller);
|
|
@@ -22787,9 +22841,9 @@ var CLOUD_COMPUTER_BUSY_ERROR = "this bot's cloud computer is being used by an a
|
|
|
22787
22841
|
var APPROVAL_MODES = ["ask", "edits", "auto", "full", "custom"];
|
|
22788
22842
|
function supportsApprovalMode(driverKind, mode) {
|
|
22789
22843
|
if (mode === "custom") return driverKind === "codex";
|
|
22790
|
-
if (mode === "edits") return ["claudeAgent", "grokAgent", "antigravityAgent"].includes(driverKind ?? "");
|
|
22844
|
+
if (mode === "edits") return ["claudeAgent", "grokAgent", "antigravityAgent", "qwenAgent", "geminiAgent"].includes(driverKind ?? "");
|
|
22791
22845
|
if (mode !== "full") return true;
|
|
22792
|
-
return ["codex", "claudeAgent", "antigravityAgent", "cursorAgent", "grokAgent", "opencodeGo"].includes(driverKind ?? "");
|
|
22846
|
+
return ["codex", "claudeAgent", "antigravityAgent", "cursorAgent", "grokAgent", "opencodeGo", "qwenAgent", "geminiAgent"].includes(driverKind ?? "");
|
|
22793
22847
|
}
|
|
22794
22848
|
function modelSwitchNeedsAsk(mode, fromDriver, toDriver) {
|
|
22795
22849
|
return !supportsApprovalMode(toDriver, mode) || (mode === "full" || mode === "custom") && fromDriver !== toDriver;
|
|
@@ -23134,6 +23188,9 @@ function approvalModeForOrigin(mode, origin) {
|
|
|
23134
23188
|
if (mode === "custom" && origin.peerInitiated) return "auto";
|
|
23135
23189
|
return mode;
|
|
23136
23190
|
}
|
|
23191
|
+
function delegationInheritsFullAccess(input) {
|
|
23192
|
+
return input.senderIsChief && input.senderHasFullAccess && !input.sameBot && supportsApprovalMode(input.recipientDriverKind, "full");
|
|
23193
|
+
}
|
|
23137
23194
|
var ASKS_A_PERSON = /* @__PURE__ */ new Set(["askuserquestion", "ask_user"]);
|
|
23138
23195
|
function autoVerdict(mode, tool, context) {
|
|
23139
23196
|
if (ASKS_A_PERSON.has(tool.replace(/^mcp__[^_]+__/, "").toLowerCase())) {
|
|
@@ -23356,7 +23413,8 @@ function parseCmdShim(shim, env) {
|
|
|
23356
23413
|
return null;
|
|
23357
23414
|
}
|
|
23358
23415
|
const dir = dirname(shim);
|
|
23359
|
-
const
|
|
23416
|
+
const npmEntry = /^SET "NP[MX]_CLI_JS=%~dp0\\([^"]+)"/im.exec(text8);
|
|
23417
|
+
const targets = [...npmEntry ? [npmEntry] : [], ...text8.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m3) => join(dir, m3[1])).filter((p3) => isFile(p3) && basename(p3).toLowerCase() !== "node.exe");
|
|
23360
23418
|
const script = targets.find((p3) => /\.[cm]?js$/i.test(p3));
|
|
23361
23419
|
if (script) {
|
|
23362
23420
|
const node2 = nodeExe(dir, env);
|
|
@@ -23650,11 +23708,13 @@ function normalizeImageGenerationUrl(value) {
|
|
|
23650
23708
|
return url2.toString().replace(/\/+$/, "");
|
|
23651
23709
|
}
|
|
23652
23710
|
|
|
23653
|
-
//
|
|
23711
|
+
// shared/wire.ts
|
|
23654
23712
|
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
23655
23713
|
function isEffortLevel(value) {
|
|
23656
23714
|
return typeof value === "string" && EFFORT_LEVELS.includes(value);
|
|
23657
23715
|
}
|
|
23716
|
+
|
|
23717
|
+
// server/contracts.ts
|
|
23658
23718
|
function isModelVariant(value) {
|
|
23659
23719
|
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !new RegExp("\\p{Cc}", "u").test(value);
|
|
23660
23720
|
}
|
|
@@ -23910,6 +23970,9 @@ var BROWSER_PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
|
|
|
23910
23970
|
var DEFAULT_ROOM_TURN_TIMEOUT_MINUTES = 5;
|
|
23911
23971
|
var MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
|
|
23912
23972
|
var MAX_ROOM_TURN_TIMEOUT_MINUTES = 1440;
|
|
23973
|
+
var DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES = 30;
|
|
23974
|
+
var DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES = 10;
|
|
23975
|
+
var DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES = 240;
|
|
23913
23976
|
var DEFAULT_MAX_CONCURRENT_BOT_THREADS = 3;
|
|
23914
23977
|
var MAX_CONCURRENT_BOT_THREADS = 10;
|
|
23915
23978
|
var MIN_THREAD_EVENT_LOG_BYTES = 256 * 1024;
|
|
@@ -23939,8 +24002,16 @@ var vpsConfigSchema = external_exports.object({
|
|
|
23939
24002
|
}).optional()
|
|
23940
24003
|
});
|
|
23941
24004
|
var roomConfigSchema = external_exports.object({
|
|
23942
|
-
turnTimeoutMinutes: external_exports.number().int().min(MIN_ROOM_TURN_TIMEOUT_MINUTES).max(MAX_ROOM_TURN_TIMEOUT_MINUTES)
|
|
23943
|
-
|
|
24005
|
+
turnTimeoutMinutes: external_exports.number().int().min(MIN_ROOM_TURN_TIMEOUT_MINUTES).max(MAX_ROOM_TURN_TIMEOUT_MINUTES),
|
|
24006
|
+
/** Room handoff tree lifetime. Active execution pauses this clock; the
|
|
24007
|
+
* hard cap is wall-clock and bounds trees that never stop executing. */
|
|
24008
|
+
handoffLifetimeMinutes: external_exports.number().int().min(1).max(MAX_ROOM_TURN_TIMEOUT_MINUTES).optional(),
|
|
24009
|
+
handoffMinRunwayMinutes: external_exports.number().int().min(1).max(MAX_ROOM_TURN_TIMEOUT_MINUTES).optional(),
|
|
24010
|
+
handoffHardCapMinutes: external_exports.number().int().min(1).max(7 * MAX_ROOM_TURN_TIMEOUT_MINUTES).optional()
|
|
24011
|
+
}).refine(
|
|
24012
|
+
(rooms) => (rooms.handoffMinRunwayMinutes ?? DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES) <= (rooms.handoffLifetimeMinutes ?? DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES) && (rooms.handoffLifetimeMinutes ?? DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES) <= (rooms.handoffHardCapMinutes ?? DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES),
|
|
24013
|
+
{ message: "rooms handoff bounds must satisfy handoffMinRunwayMinutes <= handoffLifetimeMinutes <= handoffHardCapMinutes" }
|
|
24014
|
+
);
|
|
23944
24015
|
var localVmConfigSchema = external_exports.object({
|
|
23945
24016
|
mode: external_exports.enum(["shared", "per-bot"]).optional(),
|
|
23946
24017
|
maxInstances: external_exports.number().int().min(MIN_LOCAL_VM_MAX_INSTANCES).max(MAX_LOCAL_VM_MAX_INSTANCES).optional()
|
|
@@ -24189,7 +24260,10 @@ var appConfigSchema = external_exports.object({
|
|
|
24189
24260
|
maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
|
|
24190
24261
|
/** Cap each per-thread events/ and native/ NDJSON log at this many
|
|
24191
24262
|
* bytes; absent (the default) keeps today's unbounded growth (#1280). */
|
|
24192
|
-
eventLogMaxBytes: external_exports.number().int().min(MIN_THREAD_EVENT_LOG_BYTES).max(MAX_THREAD_EVENT_LOG_BYTES).optional()
|
|
24263
|
+
eventLogMaxBytes: external_exports.number().int().min(MIN_THREAD_EVENT_LOG_BYTES).max(MAX_THREAD_EVENT_LOG_BYTES).optional(),
|
|
24264
|
+
/** Days a closed or archived thread's event logs survive (#1280).
|
|
24265
|
+
* Absent keeps them forever. */
|
|
24266
|
+
eventLogRetentionDays: external_exports.number().int().min(1).max(3650).optional()
|
|
24193
24267
|
}).strict().optional(),
|
|
24194
24268
|
localVm: localVmConfigSchema.optional(),
|
|
24195
24269
|
features: featureConfigSchema.optional(),
|
|
@@ -24272,6 +24346,13 @@ function vpsSshAlias(cfg2) {
|
|
|
24272
24346
|
function roomTurnTimeoutMinutes(cfg2) {
|
|
24273
24347
|
return cfg2.rooms?.turnTimeoutMinutes ?? DEFAULT_ROOM_TURN_TIMEOUT_MINUTES;
|
|
24274
24348
|
}
|
|
24349
|
+
function roomHandoffLimits(cfg2) {
|
|
24350
|
+
return {
|
|
24351
|
+
lifetimeMs: (cfg2.rooms?.handoffLifetimeMinutes ?? DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES) * 6e4,
|
|
24352
|
+
minRunwayMs: (cfg2.rooms?.handoffMinRunwayMinutes ?? DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES) * 6e4,
|
|
24353
|
+
hardCapMs: (cfg2.rooms?.handoffHardCapMinutes ?? DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES) * 6e4
|
|
24354
|
+
};
|
|
24355
|
+
}
|
|
24275
24356
|
function maxConcurrentBotThreads(cfg2) {
|
|
24276
24357
|
return cfg2.threads?.maxConcurrentPerBot ?? DEFAULT_MAX_CONCURRENT_BOT_THREADS;
|
|
24277
24358
|
}
|
|
@@ -24279,6 +24360,9 @@ function threadEventLogMaxBytes(cfg2) {
|
|
|
24279
24360
|
const cap = cfg2.threads?.eventLogMaxBytes;
|
|
24280
24361
|
return typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? cap : null;
|
|
24281
24362
|
}
|
|
24363
|
+
function threadEventLogRetentionDays(cfg2) {
|
|
24364
|
+
return cfg2.threads?.eventLogRetentionDays ?? null;
|
|
24365
|
+
}
|
|
24282
24366
|
function localVmMode(cfg2) {
|
|
24283
24367
|
return cfg2.localVm?.mode ?? DEFAULT_LOCAL_VM_MODE;
|
|
24284
24368
|
}
|
|
@@ -25170,13 +25254,7 @@ function gateServer(input) {
|
|
|
25170
25254
|
};
|
|
25171
25255
|
}
|
|
25172
25256
|
|
|
25173
|
-
//
|
|
25174
|
-
var SECRET_KEY_PARTS = ["token", "secret", "password", "passwd", "apikey", "api_key", "authorization", "auth_token"];
|
|
25175
|
-
function isSecretName(name2) {
|
|
25176
|
-
const lower = name2.toLowerCase();
|
|
25177
|
-
if (SECRET_KEY_PARTS.some((part) => lower.includes(part))) return true;
|
|
25178
|
-
return /(^|[_.-])keys?$/.test(lower);
|
|
25179
|
-
}
|
|
25257
|
+
// shared/redact.ts
|
|
25180
25258
|
var REDACTION_MARKER = /^«redacted \d+ chars»$/;
|
|
25181
25259
|
var mask = (value) => REDACTION_MARKER.test(value) ? value : `\xABredacted ${value.length} chars\xBB`;
|
|
25182
25260
|
var KEY_PREFIXES = [
|
|
@@ -25225,6 +25303,14 @@ ${close}`);
|
|
|
25225
25303
|
out = out.replace(URL_USERINFO, (_m, lead, secret, at2) => `${lead}${mask(secret)}${at2}`);
|
|
25226
25304
|
return out;
|
|
25227
25305
|
}
|
|
25306
|
+
|
|
25307
|
+
// server/redact.ts
|
|
25308
|
+
var SECRET_KEY_PARTS = ["token", "secret", "password", "passwd", "apikey", "api_key", "authorization", "auth_token"];
|
|
25309
|
+
function isSecretName(name2) {
|
|
25310
|
+
const lower = name2.toLowerCase();
|
|
25311
|
+
if (SECRET_KEY_PARTS.some((part) => lower.includes(part))) return true;
|
|
25312
|
+
return /(^|[_.-])keys?$/.test(lower);
|
|
25313
|
+
}
|
|
25228
25314
|
function redactSecrets(input, depth = 0) {
|
|
25229
25315
|
if (typeof input === "string") return redactSecretsInText(input);
|
|
25230
25316
|
if (depth > 12 || input === null || typeof input !== "object") return input;
|
|
@@ -27068,8 +27154,8 @@ var ClaudeDriver = {
|
|
|
27068
27154
|
};
|
|
27069
27155
|
const steer = async (threadId, text8) => {
|
|
27070
27156
|
const s4 = sessions2.get(threadId);
|
|
27071
|
-
if (!s4 || !s4.turn || s4.turn.settled || s4.closing || s4.child.exitCode !== null) return
|
|
27072
|
-
return writeUser(s4, threadId, claudeUserMessage(text8, void 0));
|
|
27157
|
+
if (!s4 || !s4.turn || s4.turn.settled || s4.closing || s4.child.exitCode !== null) return "refused";
|
|
27158
|
+
return await writeUser(s4, threadId, claudeUserMessage(text8, void 0)) ? "steered" : "refused";
|
|
27073
27159
|
};
|
|
27074
27160
|
const login = new ClaudeLoginController({ cli: config2.cli, environment, onAuthenticated: async () => {
|
|
27075
27161
|
await refreshModels();
|
|
@@ -38823,6 +38909,20 @@ function canReachPeer(from, target) {
|
|
|
38823
38909
|
function reachablePeers(bots, from) {
|
|
38824
38910
|
return bots.filter((bot) => canReachPeer(from, bot));
|
|
38825
38911
|
}
|
|
38912
|
+
function resolveTeammate(bots, from, raw) {
|
|
38913
|
+
const wanted = raw.trim();
|
|
38914
|
+
if (bots.some((bot) => bot.id === wanted)) return { id: wanted, byName: false };
|
|
38915
|
+
const fold2 = (value) => value.replace(/^@/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
38916
|
+
const name2 = fold2(wanted);
|
|
38917
|
+
const shown = peerName(wanted) || wanted;
|
|
38918
|
+
if (!name2) return { error: `No bot with id "${shown}" \u2014 call list_bots and copy the exact id from the result` };
|
|
38919
|
+
const matches = reachablePeers(bots, from).filter((bot) => fold2(bot.name) === name2);
|
|
38920
|
+
if (matches.length === 1) return { id: matches[0].id, byName: true };
|
|
38921
|
+
if (matches.length > 1) {
|
|
38922
|
+
return { error: `${matches.length} reachable teammates are named "${shown}" \u2014 call list_bots and use the id of the one you mean` };
|
|
38923
|
+
}
|
|
38924
|
+
return { error: `No bot with id or name "${shown}" \u2014 call list_bots and copy the exact id from the result` };
|
|
38925
|
+
}
|
|
38826
38926
|
var ROSTER_NAME_MAX = 80;
|
|
38827
38927
|
var ROSTER_ROLE_MAX = 120;
|
|
38828
38928
|
var ROSTER_ABOUT_MAX = 200;
|
|
@@ -38855,7 +38955,7 @@ function renderRoster(team, opts) {
|
|
|
38855
38955
|
const role = clip(bot.title ?? "", ROSTER_ROLE_MAX) || "General assistant";
|
|
38856
38956
|
const about = opts.about ? clip(bot.description ?? "", ROSTER_ABOUT_MAX) : "";
|
|
38857
38957
|
const availability = peerStatusWords(peerStatus(bot.activity, bot.busy));
|
|
38858
|
-
return `- ${name2} \u2014 ${role}${about ? `: ${about}` : ""} (${availability})`;
|
|
38958
|
+
return `- ${name2} \u2014 ${role}${about ? `: ${about}` : ""} (${availability}) [id: ${clip(bot.id, ROSTER_NAME_MAX)}]`;
|
|
38859
38959
|
});
|
|
38860
38960
|
return lines.join("\n") + (overflow > 0 ? `
|
|
38861
38961
|
- \u2026and ${overflow} more (use list_bots for the full roster).` : "");
|
|
@@ -38905,11 +39005,11 @@ function chiefOfStaffSystemPrompt(chiefId, bots, canDelegate, trustedOpenMausSta
|
|
|
38905
39005
|
empty: "- No other visible bots are available yet.",
|
|
38906
39006
|
about: true
|
|
38907
39007
|
});
|
|
38908
|
-
const delegation = canDelegate ? boundedCoordination ? "Use list_bots or list_room_targets for the live reachable roster. Use coordinate_bots to ask actual teammates for advice or assign concrete work. Outside a room, everything you send a teammate continues your one standing conversation with them, using their own model and permissions, so they still have the context of your earlier assignments. Busy teammates queue. Give self-contained briefs, then end your turn; you resume automatically after their results return. Leads can coordinate their own specialists. Do not poll, send acknowledgements as new work, or substitute native helpers for named bots. On return, verify the requested outcome, resolve decisions within the user's scope, request concrete corrections with rework=true when necessary, and return one consolidated answer. Consultations are advice, not proof that work or tests ran." : [
|
|
39008
|
+
const delegation = canDelegate ? boundedCoordination ? "Use list_bots or list_room_targets for the live reachable roster. Use coordinate_bots to ask actual teammates for advice or assign concrete work. Outside a room, everything you send a teammate continues your one standing conversation with them, using their own model and permissions, so they still have the context of your earlier assignments. Busy teammates queue. Give self-contained briefs, then end your turn; you resume automatically after their results return. Leads can coordinate their own specialists. Do not poll, send acknowledgements as new work, or substitute native helpers for named bots. On return, verify the requested outcome, resolve decisions within the user's scope, request concrete corrections with rework=true when necessary, and return one consolidated answer. Consultations are advice, not proof that work or tests ran. A refusal from coordinate_bots means nothing was sent: fix what it names (usually the id \u2014 copy it from list_bots or your roster; a unique teammate name also works) and retry, and never describe a handoff the tool did not accept." : [
|
|
38909
39009
|
"Use list_bots to confirm the live roster and IDs. When assigning work to a teammate, use delegate_bot: it returns immediately, keeps you available to the user, and delivers the teammate's outcome back into this conversation automatically \u2014 success or failure. When the result arrives you are woken with it: report it to the user and act. If the teammate fails or stalls, tell the user plainly and decide the next step yourself.",
|
|
38910
39010
|
"After delegate_bot accepts the task, acknowledge the handoff and continue with any independent work or end your turn. Do not call wait_delegation or repeatedly poll check_delegation in the same turn.",
|
|
38911
39011
|
"Use ask_bot only for a brief consultation whose answer you must have before writing your current response. Never use ask_bot for an assigned task, background work, or anything potentially long-running.",
|
|
38912
|
-
"Delegate with a clear, self-contained brief. Say that the task is assigned, not completed; only claim completion after the teammate's result has actually arrived.",
|
|
39012
|
+
"Delegate with a clear, self-contained brief. Say that the task is assigned, not completed; only claim completion after the teammate's result has actually arrived. A refusal from delegate_bot or ask_bot means nothing was sent: fix what it names (usually the id \u2014 copy it from list_bots or your roster) and retry.",
|
|
38913
39013
|
"You may assign work to more than one teammate when the request genuinely benefits. Stay responsive while they work, then combine their returned results when the user asks for a synthesis."
|
|
38914
39014
|
].join(" ") : "Your current engine cannot contact teammates. Be honest about that limitation and ask the user to choose a delegation-compatible engine before promising coordinated work.";
|
|
38915
39015
|
return [
|
|
@@ -39926,6 +40026,38 @@ function setupCommands(runtime, platform = process.platform, target = SHARED_LOC
|
|
|
39926
40026
|
};
|
|
39927
40027
|
}
|
|
39928
40028
|
|
|
40029
|
+
// server/thread-retention.ts
|
|
40030
|
+
import { unlinkSync as unlinkSync7 } from "node:fs";
|
|
40031
|
+
import { join as join19 } from "node:path";
|
|
40032
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
40033
|
+
function idleSince(candidate) {
|
|
40034
|
+
const stamps = [candidate.closedAt, candidate.archivedAt].filter((value) => value !== null);
|
|
40035
|
+
return stamps.length ? Math.max(...stamps) : null;
|
|
40036
|
+
}
|
|
40037
|
+
function sweepThreadEventLogs(candidates, retentionDays, now = Date.now()) {
|
|
40038
|
+
const cutoff = now - retentionDays * DAY_MS;
|
|
40039
|
+
let swept = 0;
|
|
40040
|
+
for (const candidate of candidates) {
|
|
40041
|
+
if (candidate.unread || candidate.busy || candidate.openDirectHandoff) continue;
|
|
40042
|
+
const since = idleSince(candidate);
|
|
40043
|
+
if (since === null || since >= cutoff) continue;
|
|
40044
|
+
let removedAny = false;
|
|
40045
|
+
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
40046
|
+
const path = join19(dir, `${candidate.threadId}.ndjson`);
|
|
40047
|
+
try {
|
|
40048
|
+
unlinkSync7(path);
|
|
40049
|
+
removedAny = true;
|
|
40050
|
+
} catch (error51) {
|
|
40051
|
+
if (error51?.code !== "ENOENT") {
|
|
40052
|
+
console.warn(`[retention] could not remove ${path}: ${error51.message}`);
|
|
40053
|
+
}
|
|
40054
|
+
}
|
|
40055
|
+
}
|
|
40056
|
+
if (removedAny) swept++;
|
|
40057
|
+
}
|
|
40058
|
+
return swept;
|
|
40059
|
+
}
|
|
40060
|
+
|
|
39929
40061
|
// server/computer-control.ts
|
|
39930
40062
|
var NO_CONTROL = { held: false, helpReason: null, heldSinceMs: null };
|
|
39931
40063
|
var MAX_REASON_CHARS = 280;
|
|
@@ -40053,15 +40185,15 @@ var ComputerControl = class {
|
|
|
40053
40185
|
|
|
40054
40186
|
// server/engine-install.ts
|
|
40055
40187
|
import { existsSync as existsSync6, mkdirSync as mkdirSync7 } from "node:fs";
|
|
40056
|
-
import { join as
|
|
40188
|
+
import { join as join20 } from "node:path";
|
|
40057
40189
|
import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
|
|
40058
40190
|
var MAX_OUTPUT2 = 64 * 1024;
|
|
40059
40191
|
var DEFAULT_TIMEOUT_MS = 10 * 6e4;
|
|
40060
40192
|
function enginesPrefix(baseDir = DATA_DIR) {
|
|
40061
|
-
return
|
|
40193
|
+
return join20(baseDir, "tools", "npm");
|
|
40062
40194
|
}
|
|
40063
40195
|
function enginesBinDir(baseDir = DATA_DIR, platform = process.platform) {
|
|
40064
|
-
return platform === "win32" ? enginesPrefix(baseDir) :
|
|
40196
|
+
return platform === "win32" ? enginesPrefix(baseDir) : join20(enginesPrefix(baseDir), "bin");
|
|
40065
40197
|
}
|
|
40066
40198
|
function registerEnginesBinDir(baseDir = DATA_DIR) {
|
|
40067
40199
|
registerPathDir(enginesBinDir(baseDir));
|
|
@@ -40161,7 +40293,7 @@ function runNpm(args, env, cwd, timeoutMs) {
|
|
|
40161
40293
|
// server/usage-ledger.ts
|
|
40162
40294
|
import { readFileSync as readFileSync14 } from "node:fs";
|
|
40163
40295
|
import { appendFile as appendFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
40164
|
-
import { join as
|
|
40296
|
+
import { join as join21 } from "node:path";
|
|
40165
40297
|
|
|
40166
40298
|
// server/prices.ts
|
|
40167
40299
|
function priceFor(turn, prices) {
|
|
@@ -40182,7 +40314,7 @@ function billableFor(turn, prices) {
|
|
|
40182
40314
|
var USAGE_GROUPINGS = ["bot", "model", "user", "day", "engine"];
|
|
40183
40315
|
var DIR = "usage";
|
|
40184
40316
|
var MAX_RANGE_DAYS = 366;
|
|
40185
|
-
var
|
|
40317
|
+
var DAY_MS2 = 24 * 60 * 6e4;
|
|
40186
40318
|
var writeQueues2 = /* @__PURE__ */ new Map();
|
|
40187
40319
|
var clean2 = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
40188
40320
|
var finiteOrNull = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
@@ -40190,7 +40322,7 @@ function monthKey(at2) {
|
|
|
40190
40322
|
return `${at2.getUTCFullYear()}-${String(at2.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
40191
40323
|
}
|
|
40192
40324
|
function usageFileFor(dataDir, at2) {
|
|
40193
|
-
return
|
|
40325
|
+
return join21(dataDir, DIR, `${monthKey(at2)}.jsonl`);
|
|
40194
40326
|
}
|
|
40195
40327
|
function appendUsage(dataDir, row) {
|
|
40196
40328
|
const record3 = {
|
|
@@ -40210,7 +40342,7 @@ function appendUsage(dataDir, row) {
|
|
|
40210
40342
|
});
|
|
40211
40343
|
}
|
|
40212
40344
|
async function write(dataDir, record3) {
|
|
40213
|
-
await mkdir2(
|
|
40345
|
+
await mkdir2(join21(dataDir, DIR), { recursive: true, mode: 448 });
|
|
40214
40346
|
await appendFile2(usageFileFor(dataDir, new Date(record3.at)), JSON.stringify(record3) + "\n", { mode: 384 });
|
|
40215
40347
|
}
|
|
40216
40348
|
async function flushUsageLedger(dataDir) {
|
|
@@ -40234,7 +40366,7 @@ function readUsage(dataDir, range) {
|
|
|
40234
40366
|
for (const key3 of monthsBetween(range.from, range.to)) {
|
|
40235
40367
|
let text8;
|
|
40236
40368
|
try {
|
|
40237
|
-
text8 = readFileSync14(
|
|
40369
|
+
text8 = readFileSync14(join21(dataDir, DIR, `${key3}.jsonl`), "utf8");
|
|
40238
40370
|
} catch {
|
|
40239
40371
|
continue;
|
|
40240
40372
|
}
|
|
@@ -40262,7 +40394,7 @@ function parseUsageRange(from, to2, now = /* @__PURE__ */ new Date()) {
|
|
|
40262
40394
|
const start = from ? parse7(from, false) : new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
40263
40395
|
const end = to2 ? parse7(to2, true) : now;
|
|
40264
40396
|
if (!start || !end || start.getTime() > end.getTime()) return null;
|
|
40265
|
-
if (end.getTime() - start.getTime() > MAX_RANGE_DAYS *
|
|
40397
|
+
if (end.getTime() - start.getTime() > MAX_RANGE_DAYS * DAY_MS2) return null;
|
|
40266
40398
|
return { from: start, to: end };
|
|
40267
40399
|
}
|
|
40268
40400
|
function triggerKey(trigger) {
|
|
@@ -40436,14 +40568,14 @@ async function checkProviderKey(input, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
|
40436
40568
|
|
|
40437
40569
|
// server/enterprise.ts
|
|
40438
40570
|
import { existsSync as existsSync7 } from "node:fs";
|
|
40439
|
-
import { join as
|
|
40571
|
+
import { join as join22 } from "node:path";
|
|
40440
40572
|
import { pathToFileURL } from "node:url";
|
|
40441
40573
|
var layerSchema = external_exports.object({
|
|
40442
40574
|
customer: external_exports.string().min(1),
|
|
40443
40575
|
features: external_exports.array(external_exports.string().min(1)),
|
|
40444
40576
|
expiresAt: external_exports.string().nullable()
|
|
40445
40577
|
});
|
|
40446
|
-
var DEFAULT_DIR =
|
|
40578
|
+
var DEFAULT_DIR = join22(SERVER_ROOT, "..", "enterprise");
|
|
40447
40579
|
var current = { edition: "oss", features: [] };
|
|
40448
40580
|
var workspaceAccessFactory;
|
|
40449
40581
|
function hostedWorkspaceConfigured(env = process.env) {
|
|
@@ -40476,7 +40608,7 @@ async function loadEnterpriseLayer(options = {}) {
|
|
|
40476
40608
|
workspaceAccessFactory = void 0;
|
|
40477
40609
|
const dir = options.dir ?? process.env.OMB_ENTERPRISE_DIR ?? DEFAULT_DIR;
|
|
40478
40610
|
const licenseKey = options.licenseKey ?? process.env.OMB_LICENSE_KEY;
|
|
40479
|
-
const entry = [
|
|
40611
|
+
const entry = [join22(dir, "server", "index.ts"), join22(dir, "server", "index.js")].find((candidate) => existsSync7(candidate));
|
|
40480
40612
|
if (!entry) {
|
|
40481
40613
|
return oss(
|
|
40482
40614
|
licenseKey ? `OMB_LICENSE_KEY is set but no enterprise layer exists at ${dir}` : void 0
|
|
@@ -40642,7 +40774,7 @@ function buildNotification(kind, bot, threadId, detail, extra) {
|
|
|
40642
40774
|
if (bot.notifications === false) return null;
|
|
40643
40775
|
const body = summarize(detail);
|
|
40644
40776
|
const who = extra?.group ? `${bot.name} in ${extra.group.name}` : bot.name;
|
|
40645
|
-
const title = kind === "approval" ? `${who} needs approval` : kind === "question" ? `${who} has a question` : kind === "takeover" ? `${who} needs your hands` : kind === "routine-failed" ? `${who}'s routine failed` : kind === "turn-failed" ? `${who} couldn't start` : `${who} finished`;
|
|
40777
|
+
const title = kind === "approval" ? `${who} needs approval` : kind === "question" ? `${who} has a question` : kind === "takeover" ? `${who} needs your hands` : kind === "routine-failed" ? `${who}'s routine failed` : kind === "routine-deferred" ? `${who}'s routine is waiting` : kind === "turn-failed" ? `${who} couldn't start` : `${who} finished`;
|
|
40646
40778
|
if (kind === "done" && !body) return null;
|
|
40647
40779
|
const notification = { kind, botId: bot.id, botName: bot.name, threadId, title, body };
|
|
40648
40780
|
if (extra?.avatarUrl) notification.avatarUrl = extra.avatarUrl;
|
|
@@ -41468,7 +41600,7 @@ function createAcpDriver(support11) {
|
|
|
41468
41600
|
return { turnId };
|
|
41469
41601
|
}
|
|
41470
41602
|
const resolvedModel = support11.resolveTurnModel?.(turn.model, env);
|
|
41471
|
-
support11.applyTurnEnv?.(env, { model: resolvedModel, requestedModel: turn.model });
|
|
41603
|
+
support11.applyTurnEnv?.(env, { model: resolvedModel, requestedModel: turn.model, fullAuto: turnConfig.fullAuto === true });
|
|
41472
41604
|
const cliTurn = resolvedModel !== void 0 && resolvedModel !== turn.model ? { ...turn, model: resolvedModel } : turn;
|
|
41473
41605
|
const mcpServers = acpMcpServers(turn);
|
|
41474
41606
|
let launch;
|
|
@@ -42110,7 +42242,7 @@ import { createHash as createHash10, randomUUID as randomUUID13 } from "node:cry
|
|
|
42110
42242
|
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readFile as readFile2, rm as rm2, stat as stat4 } from "node:fs/promises";
|
|
42111
42243
|
import { request as httpRequest } from "node:http";
|
|
42112
42244
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
42113
|
-
import { delimiter as delimiter2, join as
|
|
42245
|
+
import { delimiter as delimiter2, join as join23, resolve as resolve7 } from "node:path";
|
|
42114
42246
|
var ANTIGRAVITY_AUTH_PREFIX = "Open the following link to authenticate the ACP server: ";
|
|
42115
42247
|
var AUTH_TIMEOUT_MS = 5 * 6e4;
|
|
42116
42248
|
var STARTUP_TIMEOUT_MS = 9e4;
|
|
@@ -42171,11 +42303,11 @@ function authorizationUrlFromLine(line) {
|
|
|
42171
42303
|
}
|
|
42172
42304
|
}
|
|
42173
42305
|
function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
|
|
42174
|
-
return
|
|
42306
|
+
return join23(baseDir, "providers", "antigravity", createHash10("sha256").update(instanceId).digest("hex"));
|
|
42175
42307
|
}
|
|
42176
42308
|
async function prepareAntigravityProfile(input) {
|
|
42177
42309
|
const directory = resolve7(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
|
|
42178
|
-
const acpDirectory =
|
|
42310
|
+
const acpDirectory = join23(directory, "antigravity-acp");
|
|
42179
42311
|
await mkdir4(acpDirectory, { recursive: true, mode: 448 });
|
|
42180
42312
|
if (process.platform !== "win32") {
|
|
42181
42313
|
const { chmod: chmod3 } = await import("node:fs/promises");
|
|
@@ -42183,7 +42315,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
42183
42315
|
await chmod3(acpDirectory, 448);
|
|
42184
42316
|
}
|
|
42185
42317
|
writeFileAtomic(
|
|
42186
|
-
|
|
42318
|
+
join23(acpDirectory, "settings.json"),
|
|
42187
42319
|
`${JSON.stringify({ auth: { type: "oauth-personal" } })}
|
|
42188
42320
|
`,
|
|
42189
42321
|
{ mode: 384 }
|
|
@@ -42206,7 +42338,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
42206
42338
|
PYTHONUNBUFFERED: "1",
|
|
42207
42339
|
ELECTRON_RUN_AS_NODE: "1"
|
|
42208
42340
|
});
|
|
42209
|
-
return { directory, tokenPath:
|
|
42341
|
+
return { directory, tokenPath: join23(acpDirectory, "acp_token.json"), environment };
|
|
42210
42342
|
}
|
|
42211
42343
|
async function antigravityProfileAuthenticated(profile) {
|
|
42212
42344
|
try {
|
|
@@ -42488,7 +42620,7 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
|
|
|
42488
42620
|
return true;
|
|
42489
42621
|
}
|
|
42490
42622
|
async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
42491
|
-
const profileDirectory = await mkdtemp2(
|
|
42623
|
+
const profileDirectory = await mkdtemp2(join23(tmpdir3(), "openmaus-antigravity-verify-"));
|
|
42492
42624
|
let client;
|
|
42493
42625
|
let failed = false;
|
|
42494
42626
|
let failure5;
|
|
@@ -42621,7 +42753,7 @@ import {
|
|
|
42621
42753
|
writeFile as writeFile3
|
|
42622
42754
|
} from "node:fs/promises";
|
|
42623
42755
|
import { constants as constants3 } from "node:fs";
|
|
42624
|
-
import { delimiter as delimiter3, dirname as dirname8, isAbsolute as isAbsolute7, join as
|
|
42756
|
+
import { delimiter as delimiter3, dirname as dirname8, isAbsolute as isAbsolute7, join as join24, resolve as resolve8 } from "node:path";
|
|
42625
42757
|
import { Transform } from "node:stream";
|
|
42626
42758
|
import { pipeline } from "node:stream/promises";
|
|
42627
42759
|
import { createInflateRaw } from "node:zlib";
|
|
@@ -42720,7 +42852,7 @@ var AntigravitySetupError = class extends Error {
|
|
|
42720
42852
|
};
|
|
42721
42853
|
var installs = /* @__PURE__ */ new Map();
|
|
42722
42854
|
function runtimeRoot(baseDir = DATA_DIR) {
|
|
42723
|
-
return
|
|
42855
|
+
return join24(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
|
|
42724
42856
|
}
|
|
42725
42857
|
function executableNames(platform = process.platform) {
|
|
42726
42858
|
return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
|
|
@@ -42737,10 +42869,10 @@ async function executableFile(path, bytes) {
|
|
|
42737
42869
|
}
|
|
42738
42870
|
async function completedRelease(directory, asset) {
|
|
42739
42871
|
try {
|
|
42740
|
-
const record3 = JSON.parse(await readFile3(
|
|
42872
|
+
const record3 = JSON.parse(await readFile3(join24(directory, INSTALL_RECORD), "utf8"));
|
|
42741
42873
|
if (record3.sha256 !== asset.sha256 || record3.version !== asset.version || record3.executable.name !== asset.executable.name || record3.executable.bytes !== asset.executable.bytes || record3.harness.name !== asset.harness.name || record3.harness.bytes !== asset.harness.bytes) return null;
|
|
42742
|
-
const executablePath =
|
|
42743
|
-
const harnessPath =
|
|
42874
|
+
const executablePath = join24(directory, asset.executable.name);
|
|
42875
|
+
const harnessPath = join24(directory, asset.harness.name);
|
|
42744
42876
|
if (!await executableFile(executablePath, asset.executable.bytes)) return null;
|
|
42745
42877
|
if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
|
|
42746
42878
|
return { executablePath, harnessPath, source: "managed", version: asset.version };
|
|
@@ -42756,7 +42888,7 @@ function pathCandidates(binary2, env) {
|
|
|
42756
42888
|
async function externalRuntime(candidate, source) {
|
|
42757
42889
|
const names = executableNames();
|
|
42758
42890
|
const executablePath = resolve8(candidate);
|
|
42759
|
-
const harnessPath =
|
|
42891
|
+
const harnessPath = join24(dirname8(executablePath), names.harness);
|
|
42760
42892
|
if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
|
|
42761
42893
|
return { executablePath, harnessPath, source, version: null };
|
|
42762
42894
|
}
|
|
@@ -42776,7 +42908,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
|
|
|
42776
42908
|
}
|
|
42777
42909
|
const asset = resolveAntigravityReleaseAsset();
|
|
42778
42910
|
if (asset) {
|
|
42779
|
-
const managed = await completedRelease(
|
|
42911
|
+
const managed = await completedRelease(join24(runtimeRoot(baseDir), "versions", asset.sha256), asset);
|
|
42780
42912
|
if (managed) return managed;
|
|
42781
42913
|
}
|
|
42782
42914
|
const names = executableNames();
|
|
@@ -42902,7 +43034,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
|
|
|
42902
43034
|
start: dataOffset,
|
|
42903
43035
|
end: dataOffset + entry.compressedBytes - 1
|
|
42904
43036
|
});
|
|
42905
|
-
const destination = createWriteStream(
|
|
43037
|
+
const destination = createWriteStream(join24(outputDirectory, entry.name), { flags: "wx", mode: 448 });
|
|
42906
43038
|
if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
|
|
42907
43039
|
else await pipeline(source, counter, destination);
|
|
42908
43040
|
if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
|
|
@@ -42913,8 +43045,8 @@ async function installOnce2(options) {
|
|
|
42913
43045
|
const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
|
|
42914
43046
|
if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
|
|
42915
43047
|
const root = runtimeRoot(options.baseDir);
|
|
42916
|
-
const versions =
|
|
42917
|
-
const destination =
|
|
43048
|
+
const versions = join24(root, "versions");
|
|
43049
|
+
const destination = join24(versions, asset.sha256);
|
|
42918
43050
|
await mkdir5(versions, { recursive: true, mode: 448 });
|
|
42919
43051
|
const existing = await completedRelease(destination, asset);
|
|
42920
43052
|
if (existing) {
|
|
@@ -42930,11 +43062,11 @@ async function installOnce2(options) {
|
|
|
42930
43062
|
} catch (error51) {
|
|
42931
43063
|
if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
|
|
42932
43064
|
}
|
|
42933
|
-
const staging = await mkdtemp3(
|
|
43065
|
+
const staging = await mkdtemp3(join24(versions, `${STAGING_PREFIX}${randomUUID14()}-`));
|
|
42934
43066
|
let failed = false;
|
|
42935
43067
|
try {
|
|
42936
|
-
const archivePath =
|
|
42937
|
-
const runtimeDirectory =
|
|
43068
|
+
const archivePath = join24(staging, "download.zip");
|
|
43069
|
+
const runtimeDirectory = join24(staging, "runtime");
|
|
42938
43070
|
await mkdir5(runtimeDirectory, { mode: 448 });
|
|
42939
43071
|
const controller = new AbortController();
|
|
42940
43072
|
const timeout2 = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
@@ -42980,12 +43112,12 @@ async function installOnce2(options) {
|
|
|
42980
43112
|
}
|
|
42981
43113
|
await extractPinnedArchive(archivePath, runtimeDirectory, asset);
|
|
42982
43114
|
if (process.platform !== "win32") {
|
|
42983
|
-
await chmod2(
|
|
42984
|
-
await chmod2(
|
|
43115
|
+
await chmod2(join24(runtimeDirectory, asset.executable.name), 493);
|
|
43116
|
+
await chmod2(join24(runtimeDirectory, asset.harness.name), 493);
|
|
42985
43117
|
}
|
|
42986
43118
|
const candidate = {
|
|
42987
|
-
executablePath:
|
|
42988
|
-
harnessPath:
|
|
43119
|
+
executablePath: join24(runtimeDirectory, asset.executable.name),
|
|
43120
|
+
harnessPath: join24(runtimeDirectory, asset.harness.name),
|
|
42989
43121
|
source: "managed",
|
|
42990
43122
|
version: asset.version
|
|
42991
43123
|
};
|
|
@@ -42996,7 +43128,7 @@ async function installOnce2(options) {
|
|
|
42996
43128
|
executable: asset.executable,
|
|
42997
43129
|
harness: asset.harness
|
|
42998
43130
|
};
|
|
42999
|
-
await writeFile3(
|
|
43131
|
+
await writeFile3(join24(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
|
|
43000
43132
|
`, { flag: "wx", mode: 384 });
|
|
43001
43133
|
try {
|
|
43002
43134
|
await retryOnWindowsFileLock(() => rename2(runtimeDirectory, destination));
|
|
@@ -43418,7 +43550,7 @@ import { homedir as homedir13 } from "node:os";
|
|
|
43418
43550
|
// server/drivers/codex-catalog.ts
|
|
43419
43551
|
import { readdirSync as readdirSync3, readFileSync as readFileSync15 } from "node:fs";
|
|
43420
43552
|
import { homedir as homedir10 } from "node:os";
|
|
43421
|
-
import { basename as basename5, join as
|
|
43553
|
+
import { basename as basename5, join as join25 } from "node:path";
|
|
43422
43554
|
var STATIC_CODEX_MODELS = {
|
|
43423
43555
|
default: "gpt-5.6-sol",
|
|
43424
43556
|
options: [
|
|
@@ -43550,7 +43682,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
43550
43682
|
}
|
|
43551
43683
|
function codexHome(env) {
|
|
43552
43684
|
if (env.CODEX_HOME) return env.CODEX_HOME;
|
|
43553
|
-
return
|
|
43685
|
+
return join25(env.HOME || env.USERPROFILE || homedir10(), ".codex");
|
|
43554
43686
|
}
|
|
43555
43687
|
function unquote(raw) {
|
|
43556
43688
|
let value = raw.trim();
|
|
@@ -43636,11 +43768,11 @@ function niceLabel(model2, provider, known, named) {
|
|
|
43636
43768
|
}
|
|
43637
43769
|
function collectCatalogNames(home) {
|
|
43638
43770
|
const named = /* @__PURE__ */ new Map();
|
|
43639
|
-
for (const file2 of listDir(
|
|
43771
|
+
for (const file2 of listDir(join25(home, "model-catalogs"))) {
|
|
43640
43772
|
if (!file2.endsWith(".json")) continue;
|
|
43641
43773
|
const provider = basename5(file2, ".json").replace(/-models$/, "");
|
|
43642
43774
|
if (!PROVIDER_ID.test(provider)) continue;
|
|
43643
|
-
const raw = readText(
|
|
43775
|
+
const raw = readText(join25(home, "model-catalogs", file2));
|
|
43644
43776
|
if (!raw) continue;
|
|
43645
43777
|
let parsed;
|
|
43646
43778
|
try {
|
|
@@ -43692,7 +43824,7 @@ async function probeProviderModels(provider, env, fetchImpl) {
|
|
|
43692
43824
|
async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
|
|
43693
43825
|
const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
|
|
43694
43826
|
const home = codexHome(env);
|
|
43695
|
-
const mainText = readText(
|
|
43827
|
+
const mainText = readText(join25(home, "config.toml"));
|
|
43696
43828
|
if (!mainText) return mergeLocalInject(official, env, fetchImpl);
|
|
43697
43829
|
const main = parseCodexToml(mainText);
|
|
43698
43830
|
const known = new Map(main.providers.map((provider) => [provider.id, provider]));
|
|
@@ -43707,7 +43839,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
|
|
|
43707
43839
|
remember(main.modelProvider, main.model);
|
|
43708
43840
|
for (const file2 of listDir(home)) {
|
|
43709
43841
|
if (!file2.endsWith(".config.toml")) continue;
|
|
43710
|
-
const profile = parseCodexToml(readText(
|
|
43842
|
+
const profile = parseCodexToml(readText(join25(home, file2)) ?? "");
|
|
43711
43843
|
for (const provider of profile.providers) {
|
|
43712
43844
|
if (!known.has(provider.id)) known.set(provider.id, provider);
|
|
43713
43845
|
}
|
|
@@ -43750,7 +43882,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
|
|
|
43750
43882
|
// server/drivers/codex-instructions.ts
|
|
43751
43883
|
import { createHash as createHash12 } from "node:crypto";
|
|
43752
43884
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync16 } from "node:fs";
|
|
43753
|
-
import { join as
|
|
43885
|
+
import { join as join26 } from "node:path";
|
|
43754
43886
|
var digest = (value) => createHash12("sha256").update(value).digest("hex");
|
|
43755
43887
|
function codexDeveloperInstructions(config2, botInstructions) {
|
|
43756
43888
|
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
|
|
@@ -43765,8 +43897,8 @@ function codexDeveloperInstructions(config2, botInstructions) {
|
|
|
43765
43897
|
${configured2}` : botInstructions;
|
|
43766
43898
|
}
|
|
43767
43899
|
async function syncCodexInstructions(key3, nativeThreadId, instructions, resumed, request3) {
|
|
43768
|
-
const directory =
|
|
43769
|
-
const path =
|
|
43900
|
+
const directory = join26(DATA_DIR, "codex-instructions");
|
|
43901
|
+
const path = join26(directory, `${digest(JSON.stringify([key3, nativeThreadId]))}.sha256`);
|
|
43770
43902
|
const fingerprint = digest(instructions);
|
|
43771
43903
|
let previous2;
|
|
43772
43904
|
try {
|
|
@@ -43804,7 +43936,7 @@ async function syncCodexInstructions(key3, nativeThreadId, instructions, resumed
|
|
|
43804
43936
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
43805
43937
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
43806
43938
|
import { homedir as homedir11 } from "node:os";
|
|
43807
|
-
import { basename as basename6, dirname as dirname9, isAbsolute as isAbsolute8, join as
|
|
43939
|
+
import { basename as basename6, dirname as dirname9, isAbsolute as isAbsolute8, join as join27, resolve as resolve9 } from "node:path";
|
|
43808
43940
|
import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
|
|
43809
43941
|
var DEVICE_URL = "https://auth.openai.com/codex/device";
|
|
43810
43942
|
var MAX_OUTPUT3 = 16384;
|
|
@@ -43814,7 +43946,7 @@ function canonicalPath2(path) {
|
|
|
43814
43946
|
return realpathSync4(path);
|
|
43815
43947
|
} catch {
|
|
43816
43948
|
const parent = dirname9(path);
|
|
43817
|
-
return parent === path ? path :
|
|
43949
|
+
return parent === path ? path : join27(canonicalPath2(parent), basename6(path));
|
|
43818
43950
|
}
|
|
43819
43951
|
}
|
|
43820
43952
|
function codexDevicePrompt(output) {
|
|
@@ -43857,7 +43989,7 @@ var CodexDeviceAuthController = class {
|
|
|
43857
43989
|
if (!isAbsolute8(home) || env.CODEX_HOME && !isAbsolute8(env.CODEX_HOME)) {
|
|
43858
43990
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
|
|
43859
43991
|
}
|
|
43860
|
-
const homeKey = canonicalPath2(resolve9(env.CODEX_HOME ||
|
|
43992
|
+
const homeKey = canonicalPath2(resolve9(env.CODEX_HOME || join27(home, ".codex")));
|
|
43861
43993
|
if (authenticatingHomes2.has(homeKey)) {
|
|
43862
43994
|
throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
|
|
43863
43995
|
}
|
|
@@ -43936,7 +44068,7 @@ var CodexDeviceAuthController = class {
|
|
|
43936
44068
|
if (!isAbsolute8(home) || env.CODEX_HOME && !isAbsolute8(env.CODEX_HOME)) {
|
|
43937
44069
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
|
|
43938
44070
|
}
|
|
43939
|
-
const homeKey = canonicalPath2(resolve9(env.CODEX_HOME ||
|
|
44071
|
+
const homeKey = canonicalPath2(resolve9(env.CODEX_HOME || join27(home, ".codex")));
|
|
43940
44072
|
if (authenticatingHomes2.has(homeKey)) {
|
|
43941
44073
|
throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
|
|
43942
44074
|
}
|
|
@@ -44093,13 +44225,13 @@ var CodexDeviceAuthController = class {
|
|
|
44093
44225
|
|
|
44094
44226
|
// server/drivers/codex-identity.ts
|
|
44095
44227
|
import { homedir as homedir12 } from "node:os";
|
|
44096
|
-
import { isAbsolute as isAbsolute9, join as
|
|
44228
|
+
import { isAbsolute as isAbsolute9, join as join28, resolve as resolve10 } from "node:path";
|
|
44097
44229
|
var MAX_OUTPUT4 = 16384;
|
|
44098
44230
|
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
44099
44231
|
function codexHome2(env) {
|
|
44100
44232
|
if (env.CODEX_HOME) return isAbsolute9(env.CODEX_HOME) ? resolve10(env.CODEX_HOME) : null;
|
|
44101
44233
|
const home = env.HOME || env.USERPROFILE || homedir12();
|
|
44102
|
-
return isAbsolute9(home) ?
|
|
44234
|
+
return isAbsolute9(home) ? join28(home, ".codex") : null;
|
|
44103
44235
|
}
|
|
44104
44236
|
async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
|
|
44105
44237
|
const cwd = env.HOME || env.USERPROFILE || homedir12();
|
|
@@ -44739,9 +44871,23 @@ var CodexDriver = {
|
|
|
44739
44871
|
return stopped;
|
|
44740
44872
|
});
|
|
44741
44873
|
let completeStoppedTurn;
|
|
44874
|
+
let interruptRequested = false;
|
|
44742
44875
|
const stop = async () => {
|
|
44743
44876
|
stopRequested = true;
|
|
44744
44877
|
stopSignal.abort();
|
|
44878
|
+
if (!state.settled && !interruptRequested && codexThreadId && codexTurnId && child.exitCode === null && child.signalCode === null) {
|
|
44879
|
+
interruptRequested = true;
|
|
44880
|
+
const graceMs = Math.max(1, Number(process.env.FAKE_CODEX_INTERRUPT_GRACE_MS ?? 750) || 750);
|
|
44881
|
+
try {
|
|
44882
|
+
await request3("turn/interrupt", { threadId: codexThreadId, turnId: codexTurnId }, graceMs);
|
|
44883
|
+
} catch {
|
|
44884
|
+
}
|
|
44885
|
+
const deadline = Date.now() + graceMs;
|
|
44886
|
+
while (!state.settled && Date.now() < deadline) {
|
|
44887
|
+
await new Promise((wake) => setTimeout(wake, 15));
|
|
44888
|
+
}
|
|
44889
|
+
if (state.settled) return true;
|
|
44890
|
+
}
|
|
44745
44891
|
const stopped = await terminate();
|
|
44746
44892
|
if (stopped) completeStoppedTurn?.();
|
|
44747
44893
|
return stopped;
|
|
@@ -44762,6 +44908,23 @@ var CodexDriver = {
|
|
|
44762
44908
|
emit({ ...base2(threadId, turnId), type: "runtime.error", message: "codex did not shut down after termination was requested" });
|
|
44763
44909
|
}
|
|
44764
44910
|
};
|
|
44911
|
+
const steerActiveTurn = async (text8) => {
|
|
44912
|
+
if (state.settled || abandoned || stopRequested || !codexThreadId || !codexTurnId) return "refused";
|
|
44913
|
+
if (child.exitCode !== null || child.signalCode !== null) return "refused";
|
|
44914
|
+
try {
|
|
44915
|
+
const steerTimeoutMs = Math.max(1, Number(process.env.FAKE_CODEX_STEER_TIMEOUT_MS ?? 1e4) || 1e4);
|
|
44916
|
+
await request3("turn/steer", {
|
|
44917
|
+
threadId: codexThreadId,
|
|
44918
|
+
input: [{ type: "text", text: text8 }],
|
|
44919
|
+
expectedTurnId: codexTurnId
|
|
44920
|
+
}, steerTimeoutMs);
|
|
44921
|
+
return "steered";
|
|
44922
|
+
} catch (error51) {
|
|
44923
|
+
const message4 = error51 instanceof Error ? error51.message : String(error51);
|
|
44924
|
+
if (state.settled || message4.includes("timed out")) return "indeterminate";
|
|
44925
|
+
return "refused";
|
|
44926
|
+
}
|
|
44927
|
+
};
|
|
44765
44928
|
const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
|
|
44766
44929
|
const handleServerRequest = (msg) => {
|
|
44767
44930
|
const method = msg.method;
|
|
@@ -45051,6 +45214,10 @@ var CodexDriver = {
|
|
|
45051
45214
|
void stop();
|
|
45052
45215
|
return;
|
|
45053
45216
|
}
|
|
45217
|
+
if (stopRequested) {
|
|
45218
|
+
void settle(false, "interrupted");
|
|
45219
|
+
return;
|
|
45220
|
+
}
|
|
45054
45221
|
const recentStderr = stderrSinceOutput.trim();
|
|
45055
45222
|
const hadStreamedOutput = codexTurnId !== null || state.sawStreamDelta;
|
|
45056
45223
|
const verdict = signal !== null ? { transient: false, reason: "interrupted" } : classifyError({ exitCode: code, stderr: recentStderr });
|
|
@@ -45089,7 +45256,7 @@ var CodexDriver = {
|
|
|
45089
45256
|
});
|
|
45090
45257
|
void settle(false, "exit_before_result");
|
|
45091
45258
|
});
|
|
45092
|
-
active.set(threadId, { stop, turnId, asks });
|
|
45259
|
+
active.set(threadId, { stop, turnId, asks, steer: steerActiveTurn });
|
|
45093
45260
|
if (attempt === 0) emit({ ...base2(threadId, turnId), type: "turn.started" });
|
|
45094
45261
|
try {
|
|
45095
45262
|
await request3("initialize", {
|
|
@@ -45298,6 +45465,7 @@ ${stderr ?? ""}`))
|
|
|
45298
45465
|
provider: DRIVER_KIND3,
|
|
45299
45466
|
capabilities: {
|
|
45300
45467
|
sessionModelSwitch: "unsupported",
|
|
45468
|
+
queueing: true,
|
|
45301
45469
|
computerMcp: true,
|
|
45302
45470
|
localComputerMcp: true,
|
|
45303
45471
|
composioMcp: true,
|
|
@@ -45313,6 +45481,10 @@ ${stderr ?? ""}`))
|
|
|
45313
45481
|
interruptTurn: async (threadId) => {
|
|
45314
45482
|
await active.get(threadId)?.stop();
|
|
45315
45483
|
},
|
|
45484
|
+
steer: async (threadId, text8) => {
|
|
45485
|
+
const turn = active.get(threadId);
|
|
45486
|
+
return turn?.steer ? await turn.steer(text8) : "refused";
|
|
45487
|
+
},
|
|
45316
45488
|
respondToRequest: async (threadId, requestId, decision) => {
|
|
45317
45489
|
const turn = active.get(threadId);
|
|
45318
45490
|
const finish = turn?.asks.get(requestId);
|
|
@@ -46266,7 +46438,7 @@ var GrokDriver = {
|
|
|
46266
46438
|
// server/drivers/acp/grok.ts
|
|
46267
46439
|
import { existsSync as existsSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "node:fs";
|
|
46268
46440
|
import { homedir as homedir14 } from "node:os";
|
|
46269
|
-
import { join as
|
|
46441
|
+
import { join as join29 } from "node:path";
|
|
46270
46442
|
var STATIC_GROK_MODELS = {
|
|
46271
46443
|
default: "grok-4.6",
|
|
46272
46444
|
options: [
|
|
@@ -46277,7 +46449,7 @@ var STATIC_GROK_MODELS = {
|
|
|
46277
46449
|
var SLUG = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
46278
46450
|
function grokHome(env) {
|
|
46279
46451
|
if (env.GROK_HOME) return env.GROK_HOME;
|
|
46280
|
-
return
|
|
46452
|
+
return join29(env.HOME || env.USERPROFILE || homedir14(), ".grok");
|
|
46281
46453
|
}
|
|
46282
46454
|
function unquote2(raw) {
|
|
46283
46455
|
const value = raw.trim();
|
|
@@ -46287,7 +46459,7 @@ function unquote2(raw) {
|
|
|
46287
46459
|
return value;
|
|
46288
46460
|
}
|
|
46289
46461
|
function readGrokModelCatalog(env = process.env) {
|
|
46290
|
-
const path =
|
|
46462
|
+
const path = join29(grokHome(env), "config.toml");
|
|
46291
46463
|
let text8 = "";
|
|
46292
46464
|
try {
|
|
46293
46465
|
text8 = readFileSync17(path, "utf8");
|
|
@@ -46360,7 +46532,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
46360
46532
|
if (!inject) return modelId;
|
|
46361
46533
|
const host = localHost(inject.host);
|
|
46362
46534
|
if (!host) return modelId;
|
|
46363
|
-
const path =
|
|
46535
|
+
const path = join29(grokHome(env), "config.toml");
|
|
46364
46536
|
let text8 = "";
|
|
46365
46537
|
try {
|
|
46366
46538
|
text8 = readFileSync17(path, "utf8");
|
|
@@ -46492,7 +46664,7 @@ var support2 = {
|
|
|
46492
46664
|
// an unauthenticated CLI is a user action, not something to paper over.
|
|
46493
46665
|
pickAuthMethod: (methods) => methods.some((m3) => m3.id === "cached_token") ? "cached_token" : null,
|
|
46494
46666
|
authFailure: "fail",
|
|
46495
|
-
isAuthenticated: () => existsSync10(
|
|
46667
|
+
isAuthenticated: () => existsSync10(join29(homedir14(), ".grok", "auth.json")),
|
|
46496
46668
|
// `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
|
|
46497
46669
|
// reach the agent-stdio system prompt (verified against 1.0.0), so the
|
|
46498
46670
|
// persona is prepended codex-style.
|
|
@@ -46505,11 +46677,11 @@ var GrokAgentDriver = createAcpDriver(support2);
|
|
|
46505
46677
|
// server/drivers/acp/gemini.ts
|
|
46506
46678
|
import { existsSync as existsSync11, readFileSync as readFileSync18 } from "node:fs";
|
|
46507
46679
|
import { homedir as homedir15 } from "node:os";
|
|
46508
|
-
import { join as
|
|
46680
|
+
import { join as join30 } from "node:path";
|
|
46509
46681
|
var AUTH_PREFERENCE = ["gemini-api-key", "oauth-personal", "vertex-ai"];
|
|
46510
46682
|
function liveOauthCredential() {
|
|
46511
46683
|
try {
|
|
46512
|
-
const creds = JSON.parse(readFileSync18(
|
|
46684
|
+
const creds = JSON.parse(readFileSync18(join30(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
|
|
46513
46685
|
if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
|
|
46514
46686
|
const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
|
|
46515
46687
|
if (creds.expiry_date !== void 0) {
|
|
@@ -46523,7 +46695,12 @@ function liveOauthCredential() {
|
|
|
46523
46695
|
}
|
|
46524
46696
|
var nonBlank = (value) => Boolean(value?.trim());
|
|
46525
46697
|
function geminiIsAuthenticated(env) {
|
|
46526
|
-
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync11(
|
|
46698
|
+
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync11(join30(homedir15(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
|
|
46699
|
+
}
|
|
46700
|
+
function geminiApprovalArgs(fullAuto, approvalMode) {
|
|
46701
|
+
if (fullAuto) return ["--yolo"];
|
|
46702
|
+
if (approvalMode === "edits") return ["--approval-mode", "auto_edit"];
|
|
46703
|
+
return [];
|
|
46527
46704
|
}
|
|
46528
46705
|
var support3 = {
|
|
46529
46706
|
driverKind: "geminiAgent",
|
|
@@ -46540,7 +46717,7 @@ var support3 = {
|
|
|
46540
46717
|
loginNote: "Gemini CLI needs a GEMINI_API_KEY, a Vertex AI setup, or an enterprise Code Assist login \u2014 consumer Google logins stopped working on 2026-06-18 (use the Antigravity engine for those accounts)",
|
|
46541
46718
|
// --acp is the stable Gemini CLI surface. --experimental-acp remains an
|
|
46542
46719
|
// alias for older releases, but using it now emits a deprecation warning.
|
|
46543
|
-
spawnArgs: (
|
|
46720
|
+
spawnArgs: (config2, turn) => ["--acp", ...geminiApprovalArgs(config2.fullAuto, turn.approvalMode), ...turn.model ? ["-m", turn.model] : []],
|
|
46544
46721
|
credentialEnv: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
46545
46722
|
pickAuthMethod: (methods) => {
|
|
46546
46723
|
const ids = methods.map((m3) => m3.id).filter((id) => typeof id === "string");
|
|
@@ -46558,7 +46735,7 @@ var GeminiAgentDriver = createAcpDriver(support3);
|
|
|
46558
46735
|
// server/drivers/acp/kimi.ts
|
|
46559
46736
|
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "node:fs";
|
|
46560
46737
|
import { homedir as homedir16 } from "node:os";
|
|
46561
|
-
import { join as
|
|
46738
|
+
import { join as join31 } from "node:path";
|
|
46562
46739
|
var STATIC_KIMI_MODELS = {
|
|
46563
46740
|
default: "kimi-code/k3",
|
|
46564
46741
|
options: [
|
|
@@ -46574,10 +46751,10 @@ function isLocalInjectAlias(slug) {
|
|
|
46574
46751
|
return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
|
|
46575
46752
|
}
|
|
46576
46753
|
function kimiDataRoot(env) {
|
|
46577
|
-
return env.KIMI_CODE_HOME ||
|
|
46754
|
+
return env.KIMI_CODE_HOME || join31(env.HOME || env.USERPROFILE || homedir16(), ".kimi-code");
|
|
46578
46755
|
}
|
|
46579
46756
|
function credentialsPath(env) {
|
|
46580
|
-
return
|
|
46757
|
+
return join31(kimiDataRoot(env), "credentials", "kimi-code.json");
|
|
46581
46758
|
}
|
|
46582
46759
|
function quoteToml2(value) {
|
|
46583
46760
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
@@ -46865,7 +47042,7 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
46865
47042
|
const alias = `${inject.host}/${inject.model.replace(/\//g, "-")}`;
|
|
46866
47043
|
const dataRoot = kimiDataRoot(env);
|
|
46867
47044
|
mkdirSync9(dataRoot, { recursive: true });
|
|
46868
|
-
const path =
|
|
47045
|
+
const path = join31(dataRoot, "config.toml");
|
|
46869
47046
|
let text8 = "";
|
|
46870
47047
|
try {
|
|
46871
47048
|
text8 = readFileSync19(path, "utf8");
|
|
@@ -46937,7 +47114,7 @@ function readKimiModelCatalog(env) {
|
|
|
46937
47114
|
const dataRoot = kimiDataRoot(env);
|
|
46938
47115
|
let text8 = "";
|
|
46939
47116
|
try {
|
|
46940
|
-
text8 = readFileSync19(
|
|
47117
|
+
text8 = readFileSync19(join31(dataRoot, "config.toml"), "utf8");
|
|
46941
47118
|
} catch {
|
|
46942
47119
|
return STATIC_KIMI_MODELS;
|
|
46943
47120
|
}
|
|
@@ -47032,11 +47209,11 @@ var KimiAgentDriver = createAcpDriver(support4);
|
|
|
47032
47209
|
// server/drivers/acp/droid.ts
|
|
47033
47210
|
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "node:fs";
|
|
47034
47211
|
import { homedir as homedir17 } from "node:os";
|
|
47035
|
-
import { join as
|
|
47212
|
+
import { join as join32 } from "node:path";
|
|
47036
47213
|
var AUTH_FILES = ["auth.v2.file", "auth.v2.loginkeychain", "auth.v2.keyring"];
|
|
47037
47214
|
function authFilePaths(env) {
|
|
47038
47215
|
const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir17();
|
|
47039
|
-
return AUTH_FILES.map((name2) =>
|
|
47216
|
+
return AUTH_FILES.map((name2) => join32(home, ".factory", name2));
|
|
47040
47217
|
}
|
|
47041
47218
|
var INJECT_ID_PREFIX = "custom:openmausbot-";
|
|
47042
47219
|
function droidInjectId(host, model2) {
|
|
@@ -47052,9 +47229,9 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
47052
47229
|
const host = localHost(inject.host);
|
|
47053
47230
|
if (!host) return modelId;
|
|
47054
47231
|
const id = droidInjectId(inject.host, inject.model);
|
|
47055
|
-
const dir =
|
|
47232
|
+
const dir = join32(factoryHome(env), ".factory");
|
|
47056
47233
|
mkdirSync10(dir, { recursive: true });
|
|
47057
|
-
const path =
|
|
47234
|
+
const path = join32(dir, "settings.json");
|
|
47058
47235
|
let settings = {};
|
|
47059
47236
|
try {
|
|
47060
47237
|
settings = JSON.parse(readFileSync20(path, "utf8"));
|
|
@@ -47094,7 +47271,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
|
|
|
47094
47271
|
env.FACTORY_API_KEY = "openmausbot-local";
|
|
47095
47272
|
}
|
|
47096
47273
|
function readSettings(env) {
|
|
47097
|
-
return JSON.parse(readFileSync20(
|
|
47274
|
+
return JSON.parse(readFileSync20(join32(factoryHome(env), ".factory", "settings.json"), "utf8"));
|
|
47098
47275
|
}
|
|
47099
47276
|
async function resolveModels(env) {
|
|
47100
47277
|
let catalog2 = MODELS3;
|
|
@@ -47468,7 +47645,7 @@ var CursorAgentDriver = createCursorAgentDriver();
|
|
|
47468
47645
|
// server/drivers/acp/opencode-go.ts
|
|
47469
47646
|
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "node:fs";
|
|
47470
47647
|
import { homedir as homedir18 } from "node:os";
|
|
47471
|
-
import { join as
|
|
47648
|
+
import { join as join33 } from "node:path";
|
|
47472
47649
|
var STATIC_MODELS = {
|
|
47473
47650
|
default: "opencode/x-preview-f-free",
|
|
47474
47651
|
options: [
|
|
@@ -47595,7 +47772,7 @@ var stripForeignProviderKeys = (env) => {
|
|
|
47595
47772
|
};
|
|
47596
47773
|
function opencodeConfigDir(env) {
|
|
47597
47774
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
47598
|
-
return
|
|
47775
|
+
return join33(env.XDG_CONFIG_HOME || join33(home, ".config"), "opencode");
|
|
47599
47776
|
}
|
|
47600
47777
|
function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
47601
47778
|
const inject = decodeInjectId(modelId);
|
|
@@ -47605,7 +47782,7 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
47605
47782
|
const native = `${inject.host}/${inject.model}`;
|
|
47606
47783
|
const dir = opencodeConfigDir(env);
|
|
47607
47784
|
mkdirSync11(dir, { recursive: true });
|
|
47608
|
-
const path =
|
|
47785
|
+
const path = join33(dir, "opencode.json");
|
|
47609
47786
|
let config2 = { $schema: "https://opencode.ai/config.json" };
|
|
47610
47787
|
if (existsSync14(path)) {
|
|
47611
47788
|
try {
|
|
@@ -47643,10 +47820,10 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
47643
47820
|
function storedAuthPaths(env) {
|
|
47644
47821
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
47645
47822
|
const roots = [
|
|
47646
|
-
env.XDG_DATA_HOME ||
|
|
47647
|
-
process.platform === "darwin" ?
|
|
47823
|
+
env.XDG_DATA_HOME || join33(home, ".local", "share"),
|
|
47824
|
+
process.platform === "darwin" ? join33(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join33(home, "AppData", "Local") : ""
|
|
47648
47825
|
].filter(Boolean);
|
|
47649
|
-
return [...new Set(roots)].map((root) =>
|
|
47826
|
+
return [...new Set(roots)].map((root) => join33(root, "opencode", "auth.json"));
|
|
47650
47827
|
}
|
|
47651
47828
|
function usableAuthEntry(parsed) {
|
|
47652
47829
|
return Object.values(parsed).some((auth) => {
|
|
@@ -47714,6 +47891,28 @@ var support7 = (loadCatalog2) => ({
|
|
|
47714
47891
|
modelVariants: true,
|
|
47715
47892
|
resolveTurnModel: (model2, env) => model2 ? ensureOpenCodeInjectModel(normalizeLegacyOpenCodeModel(model2, env), env) : model2,
|
|
47716
47893
|
transformEnv: stripForeignProviderKeys,
|
|
47894
|
+
applyTurnEnv: (env, { fullAuto }) => {
|
|
47895
|
+
if (!fullAuto) return;
|
|
47896
|
+
env.OPENCODE_PERMISSION = JSON.stringify(Object.fromEntries([
|
|
47897
|
+
"*",
|
|
47898
|
+
"external_directory",
|
|
47899
|
+
"read",
|
|
47900
|
+
"edit",
|
|
47901
|
+
"bash",
|
|
47902
|
+
"glob",
|
|
47903
|
+
"grep",
|
|
47904
|
+
"list",
|
|
47905
|
+
"task",
|
|
47906
|
+
"lsp",
|
|
47907
|
+
"skill",
|
|
47908
|
+
"webfetch",
|
|
47909
|
+
"websearch",
|
|
47910
|
+
"codesearch",
|
|
47911
|
+
"todoread",
|
|
47912
|
+
"todowrite",
|
|
47913
|
+
"doom_loop"
|
|
47914
|
+
].map((permission) => [permission, "allow"])));
|
|
47915
|
+
},
|
|
47717
47916
|
pickAuthMethod: () => null,
|
|
47718
47917
|
authFailure: "continue",
|
|
47719
47918
|
isAuthenticated: async (env, config2) => Boolean(env.OPENCODE_API_KEY) || hasStoredOpenCodeAuth(env) || await canListOpenCodeModels(env, config2.cli),
|
|
@@ -47747,11 +47946,11 @@ var OpenCodeDriver = createOpenCodeDriver();
|
|
|
47747
47946
|
import { chmodSync as chmodSync2, existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "node:fs";
|
|
47748
47947
|
import { createHash as createHash13 } from "node:crypto";
|
|
47749
47948
|
import { homedir as homedir19 } from "node:os";
|
|
47750
|
-
import { join as
|
|
47949
|
+
import { join as join34 } from "node:path";
|
|
47751
47950
|
var EMPTY = { default: "", options: [] };
|
|
47752
47951
|
function qwenHome(env) {
|
|
47753
47952
|
const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
|
|
47754
|
-
return
|
|
47953
|
+
return join34(home, ".qwen");
|
|
47755
47954
|
}
|
|
47756
47955
|
function isRecord4(value) {
|
|
47757
47956
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -47764,7 +47963,7 @@ var PROTOCOLS = /* @__PURE__ */ new Set(["openai", "anthropic", "gemini", "verte
|
|
|
47764
47963
|
function readQwenRoutes(env) {
|
|
47765
47964
|
let settings;
|
|
47766
47965
|
try {
|
|
47767
|
-
settings = JSON.parse(readFileSync22(
|
|
47966
|
+
settings = JSON.parse(readFileSync22(join34(qwenHome(env), "settings.json"), "utf8"));
|
|
47768
47967
|
} catch {
|
|
47769
47968
|
return [];
|
|
47770
47969
|
}
|
|
@@ -47848,7 +48047,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
47848
48047
|
if (!host) return modelId;
|
|
47849
48048
|
const dir = qwenHome(env);
|
|
47850
48049
|
mkdirSync12(dir, { recursive: true });
|
|
47851
|
-
const path =
|
|
48050
|
+
const path = join34(dir, "settings.json");
|
|
47852
48051
|
let settings = {};
|
|
47853
48052
|
if (existsSync15(path)) {
|
|
47854
48053
|
try {
|
|
@@ -47918,6 +48117,12 @@ function resolveQwenTurnModel(model2, env) {
|
|
|
47918
48117
|
if (!matches.length) throw new Error("This Qwen model is no longer configured. Refresh models and select it again.");
|
|
47919
48118
|
return matches[0].id;
|
|
47920
48119
|
}
|
|
48120
|
+
function qwenApprovalArgs(fullAuto, approvalMode) {
|
|
48121
|
+
if (fullAuto) return ["--yolo"];
|
|
48122
|
+
if (approvalMode === "auto") return ["--approval-mode", "auto"];
|
|
48123
|
+
if (approvalMode === "edits") return ["--approval-mode", "auto-edit"];
|
|
48124
|
+
return [];
|
|
48125
|
+
}
|
|
47921
48126
|
var support8 = {
|
|
47922
48127
|
driverKind: "qwenAgent",
|
|
47923
48128
|
displayName: "Qwen",
|
|
@@ -47938,7 +48143,7 @@ var support8 = {
|
|
|
47938
48143
|
},
|
|
47939
48144
|
// A raw -m only changes the model within the saved provider. ACP switches
|
|
47940
48145
|
// the complete route and confirms it before any prompt leaves OMB.
|
|
47941
|
-
spawnArgs: () => ["--acp"],
|
|
48146
|
+
spawnArgs: (config2, turn) => ["--acp", ...qwenApprovalArgs(config2.fullAuto, turn.approvalMode)],
|
|
47942
48147
|
selectModel: { configId: "model" },
|
|
47943
48148
|
pickAuthMethod: () => null,
|
|
47944
48149
|
authFailure: "continue",
|
|
@@ -47989,7 +48194,7 @@ var CustomAcpDriver = {
|
|
|
47989
48194
|
import { spawn as spawn2 } from "node:child_process";
|
|
47990
48195
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "node:fs";
|
|
47991
48196
|
import { homedir as homedir20 } from "node:os";
|
|
47992
|
-
import { join as
|
|
48197
|
+
import { join as join35 } from "node:path";
|
|
47993
48198
|
|
|
47994
48199
|
// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
|
|
47995
48200
|
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
|
@@ -54293,7 +54498,7 @@ function bindHermesScreenshotCompat(env, modelId) {
|
|
|
54293
54498
|
env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
|
|
54294
54499
|
}
|
|
54295
54500
|
function hermesHome(env) {
|
|
54296
|
-
return env.HERMES_HOME ||
|
|
54501
|
+
return env.HERMES_HOME || join35(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
|
|
54297
54502
|
}
|
|
54298
54503
|
function quoteYaml(value) {
|
|
54299
54504
|
if (/^[\w./:+-]+$/.test(value)) return value;
|
|
@@ -54335,7 +54540,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
|
|
|
54335
54540
|
if (!host) return modelId;
|
|
54336
54541
|
const dir = hermesHome(env);
|
|
54337
54542
|
mkdirSync13(dir, { recursive: true });
|
|
54338
|
-
const path =
|
|
54543
|
+
const path = join35(dir, "config.yaml");
|
|
54339
54544
|
let text8 = "";
|
|
54340
54545
|
try {
|
|
54341
54546
|
text8 = readFileSync23(path, "utf8");
|
|
@@ -54414,13 +54619,13 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
54414
54619
|
const dir = hermesHome(env);
|
|
54415
54620
|
let secrets = "";
|
|
54416
54621
|
try {
|
|
54417
|
-
secrets = readFileSync23(
|
|
54622
|
+
secrets = readFileSync23(join35(dir, ".env"), "utf8");
|
|
54418
54623
|
} catch {
|
|
54419
54624
|
}
|
|
54420
54625
|
const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name2) => nonEmptyDotenvValue(secrets, name2));
|
|
54421
54626
|
let configuredDefault = null;
|
|
54422
54627
|
try {
|
|
54423
|
-
configuredDefault = hermesConfigDefault(readFileSync23(
|
|
54628
|
+
configuredDefault = hermesConfigDefault(readFileSync23(join35(dir, "config.yaml"), "utf8"));
|
|
54424
54629
|
} catch {
|
|
54425
54630
|
}
|
|
54426
54631
|
const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
|
|
@@ -54734,7 +54939,7 @@ var OpenAICompatDriver = {
|
|
|
54734
54939
|
// server/drivers/pi.ts
|
|
54735
54940
|
import { chmodSync as chmodSync3, existsSync as existsSync16, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync2, readFileSync as readFileSync24, rmSync as rmSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
54736
54941
|
import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
|
|
54737
|
-
import { join as
|
|
54942
|
+
import { join as join36 } from "node:path";
|
|
54738
54943
|
var DRIVER_KIND6 = "piAgent";
|
|
54739
54944
|
var PI_ARGS = ["--mode", "rpc", "--no-session"];
|
|
54740
54945
|
var PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
|
|
@@ -54839,7 +55044,7 @@ async function applyPiLocalCatalog(catalog2, env = process.env, fetchImpl = fetc
|
|
|
54839
55044
|
return preferPiInjectRows(await mergeLocalInject(catalog2, env, fetchImpl));
|
|
54840
55045
|
}
|
|
54841
55046
|
function piAgentDir(env) {
|
|
54842
|
-
return
|
|
55047
|
+
return join36(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
|
|
54843
55048
|
}
|
|
54844
55049
|
function ensurePiInjectModel(modelId, env = process.env) {
|
|
54845
55050
|
const split2 = splitPiModel(modelId);
|
|
@@ -54850,7 +55055,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
54850
55055
|
if (!host) return split2;
|
|
54851
55056
|
const dir = piAgentDir(env);
|
|
54852
55057
|
mkdirSync14(dir, { recursive: true });
|
|
54853
|
-
const path =
|
|
55058
|
+
const path = join36(dir, "models.json");
|
|
54854
55059
|
let root = { providers: {} };
|
|
54855
55060
|
if (existsSync16(path)) {
|
|
54856
55061
|
try {
|
|
@@ -55051,9 +55256,9 @@ var PiDriver = {
|
|
|
55051
55256
|
const mcpServers = buildMcpServers(turn);
|
|
55052
55257
|
let mcpTempDir = null;
|
|
55053
55258
|
if (mcpServers) {
|
|
55054
|
-
mcpTempDir = mkdtempSync2(
|
|
55259
|
+
mcpTempDir = mkdtempSync2(join36(tmpdir4(), "omb-pi-mcp-"));
|
|
55055
55260
|
try {
|
|
55056
|
-
writeFileSync14(
|
|
55261
|
+
writeFileSync14(join36(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
|
|
55057
55262
|
} catch (err) {
|
|
55058
55263
|
try {
|
|
55059
55264
|
rmSync3(mcpTempDir, { recursive: true, force: true });
|
|
@@ -55071,7 +55276,7 @@ var PiDriver = {
|
|
|
55071
55276
|
env: piEnvironment({
|
|
55072
55277
|
...process.env,
|
|
55073
55278
|
...input.environment,
|
|
55074
|
-
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG:
|
|
55279
|
+
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join36(mcpTempDir, "mcp.json") } : {}
|
|
55075
55280
|
})
|
|
55076
55281
|
});
|
|
55077
55282
|
} catch (err) {
|
|
@@ -55411,7 +55616,7 @@ ${turn.text}` : turn.text;
|
|
|
55411
55616
|
// server/drivers/minimax.ts
|
|
55412
55617
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
55413
55618
|
import { homedir as homedir22 } from "node:os";
|
|
55414
|
-
import { join as
|
|
55619
|
+
import { join as join37 } from "node:path";
|
|
55415
55620
|
var DRIVER_KIND7 = "minimax";
|
|
55416
55621
|
var API_KEY_ENV = "MINIMAX_API_KEY";
|
|
55417
55622
|
var DEFAULT_URL2 = "https://api.minimax.io/v1";
|
|
@@ -55439,7 +55644,7 @@ function normalizedApiUrl(value) {
|
|
|
55439
55644
|
}
|
|
55440
55645
|
function loadLocalMiniMaxConfig(home = homedir22()) {
|
|
55441
55646
|
try {
|
|
55442
|
-
const raw = localConfigSchema.parse(JSON.parse(readFileSync25(
|
|
55647
|
+
const raw = localConfigSchema.parse(JSON.parse(readFileSync25(join37(home, ".mmx", "config.json"), "utf8")));
|
|
55443
55648
|
const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
|
|
55444
55649
|
return {
|
|
55445
55650
|
apiKey: raw.api_key?.trim() ?? "",
|
|
@@ -55535,14 +55740,14 @@ var BUILT_IN_DRIVERS = [
|
|
|
55535
55740
|
|
|
55536
55741
|
// server/store.ts
|
|
55537
55742
|
import { createHash as createHash15 } from "node:crypto";
|
|
55538
|
-
import { existsSync as existsSync20, readFileSync as readFileSync29, mkdirSync as mkdirSync19, rmSync as rmSync5, unlinkSync as
|
|
55539
|
-
import { join as
|
|
55743
|
+
import { existsSync as existsSync20, readFileSync as readFileSync29, mkdirSync as mkdirSync19, rmSync as rmSync5, unlinkSync as unlinkSync8 } from "node:fs";
|
|
55744
|
+
import { join as join42 } from "node:path";
|
|
55540
55745
|
|
|
55541
55746
|
// server/section-context.ts
|
|
55542
55747
|
import { existsSync as existsSync17, mkdirSync as mkdirSync15, readFileSync as readFileSync26 } from "node:fs";
|
|
55543
|
-
import { join as
|
|
55748
|
+
import { join as join38 } from "node:path";
|
|
55544
55749
|
var SECTION_CONTEXT_MAX_BYTES = 24e3;
|
|
55545
|
-
var SECTION_CONTEXTS_FILE =
|
|
55750
|
+
var SECTION_CONTEXTS_FILE = join38(DATA_DIR, "section-contexts.json");
|
|
55546
55751
|
var sectionContextRecordSchema = external_exports.object({ text: external_exports.string(), updatedAt: external_exports.number().finite() });
|
|
55547
55752
|
var sectionContextFileSchema = external_exports.object({
|
|
55548
55753
|
version: external_exports.literal(1),
|
|
@@ -55646,14 +55851,14 @@ Shared context for the ${JSON.stringify(label)} section follows. The user manage
|
|
|
55646
55851
|
// server/bot-folder.ts
|
|
55647
55852
|
import { createHash as createHash14 } from "node:crypto";
|
|
55648
55853
|
import { closeSync as closeSync6, mkdirSync as mkdirSync16, openSync as openSync6, readSync as readSync3, rmSync as rmSync4 } from "node:fs";
|
|
55649
|
-
import { join as
|
|
55650
|
-
var BOTS_DIR =
|
|
55854
|
+
import { join as join39 } from "node:path";
|
|
55855
|
+
var BOTS_DIR = join39(DATA_DIR, "bots");
|
|
55651
55856
|
var SOUL_FILE = "SOUL.md";
|
|
55652
55857
|
function botFolder(botId) {
|
|
55653
|
-
return
|
|
55858
|
+
return join39(BOTS_DIR, botId);
|
|
55654
55859
|
}
|
|
55655
55860
|
function soulFile(botId) {
|
|
55656
|
-
return
|
|
55861
|
+
return join39(botFolder(botId), SOUL_FILE);
|
|
55657
55862
|
}
|
|
55658
55863
|
function soulHash(soul) {
|
|
55659
55864
|
return createHash14("sha256").update(soul, "utf8").digest("hex");
|
|
@@ -55721,7 +55926,7 @@ function peerAllowKey(action, targetId) {
|
|
|
55721
55926
|
|
|
55722
55927
|
// server/message-db.ts
|
|
55723
55928
|
import { chmodSync as chmodSync4, closeSync as closeSync7, existsSync as existsSync18, mkdirSync as mkdirSync17, openSync as openSync7, readFileSync as readFileSync27, renameSync as renameSync4 } from "node:fs";
|
|
55724
|
-
import { join as
|
|
55929
|
+
import { join as join40 } from "node:path";
|
|
55725
55930
|
import { DatabaseSync } from "node:sqlite";
|
|
55726
55931
|
|
|
55727
55932
|
// server/peer-provenance.ts
|
|
@@ -55744,7 +55949,7 @@ function peerProvenanceAuthor(text8) {
|
|
|
55744
55949
|
}
|
|
55745
55950
|
|
|
55746
55951
|
// server/message-db.ts
|
|
55747
|
-
var DB_FILE = () =>
|
|
55952
|
+
var DB_FILE = () => join40(DATA_DIR, "messages.db");
|
|
55748
55953
|
var handle = null;
|
|
55749
55954
|
var handlePath = null;
|
|
55750
55955
|
function open4() {
|
|
@@ -56174,9 +56379,9 @@ function closeMessageDb() {
|
|
|
56174
56379
|
|
|
56175
56380
|
// server/workspace.ts
|
|
56176
56381
|
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readdirSync as readdirSync4, readFileSync as readFileSync28, statSync as statSync6 } from "node:fs";
|
|
56177
|
-
import { join as
|
|
56178
|
-
var WORKSPACES_DIR =
|
|
56179
|
-
var TASK_WORKSPACES_DIR =
|
|
56382
|
+
import { join as join41 } from "node:path";
|
|
56383
|
+
var WORKSPACES_DIR = join41(DATA_DIR, "workspaces");
|
|
56384
|
+
var TASK_WORKSPACES_DIR = join41(DATA_DIR, "task-workspaces");
|
|
56180
56385
|
function supportsWorkspaceFiles(driverKind) {
|
|
56181
56386
|
return !["grok", "openai-compat", "minimax", "boxAgent"].includes(driverKind);
|
|
56182
56387
|
}
|
|
@@ -56184,7 +56389,7 @@ function ensureTaskWorkspace(botId, threadId) {
|
|
|
56184
56389
|
if (![botId, threadId].every((id) => /^[A-Za-z0-9_-]{1,128}$/.test(id))) {
|
|
56185
56390
|
throw new Error("Invalid bot or thread id for a task workspace.");
|
|
56186
56391
|
}
|
|
56187
|
-
const dir =
|
|
56392
|
+
const dir = join41(TASK_WORKSPACES_DIR, botId, threadId);
|
|
56188
56393
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56189
56394
|
return dir;
|
|
56190
56395
|
}
|
|
@@ -56197,20 +56402,20 @@ load at the start of every session \u2014 keep this file short and curated.
|
|
|
56197
56402
|
Longer notes belong in memory/<topic>.md files, read on demand.
|
|
56198
56403
|
`;
|
|
56199
56404
|
function ensureWorkspace(botId) {
|
|
56200
|
-
const dir =
|
|
56201
|
-
mkdirSync18(
|
|
56202
|
-
const memoryFile =
|
|
56405
|
+
const dir = join41(WORKSPACES_DIR, botId);
|
|
56406
|
+
mkdirSync18(join41(dir, "memory"), { recursive: true, mode: 448 });
|
|
56407
|
+
const memoryFile = join41(dir, "MEMORY.md");
|
|
56203
56408
|
if (!existsSync19(memoryFile)) writeFileAtomic(memoryFile, MEMORY_SEED, { mode: 384 });
|
|
56204
56409
|
return dir;
|
|
56205
56410
|
}
|
|
56206
56411
|
function workspaceDir(botId) {
|
|
56207
|
-
return
|
|
56412
|
+
return join41(WORKSPACES_DIR, botId);
|
|
56208
56413
|
}
|
|
56209
56414
|
function workspaceLocationsPrompt(botId, cwd, botCwd) {
|
|
56210
56415
|
return "\n\nFile locations for this bot (absolute paths): " + JSON.stringify({
|
|
56211
56416
|
currentWorkingFolder: cwd ?? "Provider default; inspect the working directory before using relative paths",
|
|
56212
56417
|
sharedBotFolder: workspaceDir(botId),
|
|
56213
|
-
otherThreadFiles:
|
|
56418
|
+
otherThreadFiles: join41(TASK_WORKSPACES_DIR, botId),
|
|
56214
56419
|
...botCwd ? { configuredProjectFolder: botCwd } : {}
|
|
56215
56420
|
}) + ". Different conversations can have different working folders. For an existing file, use the exact path from the conversation; if missing here, check this bot's listed folders before saying it is gone or recreating it. Follow an explicitly requested destination. Otherwise put new task output in the current working folder and report its absolute path so another thread or room can use it. Do not move old files, edit another active thread's work, or read another bot's private folders without authorization. These paths do not grant additional access.";
|
|
56216
56421
|
}
|
|
@@ -56223,7 +56428,7 @@ function memoryOverBudget(text8) {
|
|
|
56223
56428
|
function loadMemory(botId) {
|
|
56224
56429
|
let raw;
|
|
56225
56430
|
try {
|
|
56226
|
-
raw = readFileSync28(
|
|
56431
|
+
raw = readFileSync28(join41(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
56227
56432
|
} catch {
|
|
56228
56433
|
return null;
|
|
56229
56434
|
}
|
|
@@ -56246,7 +56451,7 @@ var MEMORY_FILE_MAX_BYTES = 256 * 1024;
|
|
|
56246
56451
|
function readMemoryFile(botId) {
|
|
56247
56452
|
let raw;
|
|
56248
56453
|
try {
|
|
56249
|
-
raw = readFileSync28(
|
|
56454
|
+
raw = readFileSync28(join41(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
56250
56455
|
} catch {
|
|
56251
56456
|
return { text: "", truncated: false };
|
|
56252
56457
|
}
|
|
@@ -56255,12 +56460,12 @@ function readMemoryFile(botId) {
|
|
|
56255
56460
|
}
|
|
56256
56461
|
function writeMemoryFile(botId, text8) {
|
|
56257
56462
|
ensureWorkspace(botId);
|
|
56258
|
-
writeFileAtomic(
|
|
56463
|
+
writeFileAtomic(join41(workspaceDir(botId), "MEMORY.md"), redactSecretsInText(text8), { mode: 384 });
|
|
56259
56464
|
indexWrittenMemoryFile(botId, "MEMORY.md");
|
|
56260
56465
|
}
|
|
56261
56466
|
function indexWrittenMemoryFile(botId, relativePath) {
|
|
56262
56467
|
try {
|
|
56263
|
-
const path =
|
|
56468
|
+
const path = join41(workspaceDir(botId), relativePath);
|
|
56264
56469
|
const stat6 = statSync6(path);
|
|
56265
56470
|
indexMemoryFile(botId, relativePath, readFileSync28(path, "utf8"), { mtimeMs: stat6.mtimeMs, bytes: stat6.size });
|
|
56266
56471
|
} catch {
|
|
@@ -56275,7 +56480,7 @@ function memoryFilesOnDisk(botId) {
|
|
|
56275
56480
|
];
|
|
56276
56481
|
return names.flatMap((relativePath) => {
|
|
56277
56482
|
try {
|
|
56278
|
-
const stat6 = statSync6(
|
|
56483
|
+
const stat6 = statSync6(join41(dir, relativePath));
|
|
56279
56484
|
return stat6.isFile() ? [{ path: relativePath, mtimeMs: stat6.mtimeMs, bytes: stat6.size }] : [];
|
|
56280
56485
|
} catch {
|
|
56281
56486
|
return [];
|
|
@@ -56290,7 +56495,7 @@ function syncMemoryIndex(botId) {
|
|
|
56290
56495
|
indexed.delete(file2.path);
|
|
56291
56496
|
if (known && known.bytes === file2.bytes && known.mtimeMs === Math.trunc(file2.mtimeMs)) continue;
|
|
56292
56497
|
try {
|
|
56293
|
-
const text8 = readFileSync28(
|
|
56498
|
+
const text8 = readFileSync28(join41(workspaceDir(botId), file2.path), "utf8");
|
|
56294
56499
|
indexMemoryFile(botId, file2.path, file2.path === "MEMORY.md" && text8 === MEMORY_SEED ? "" : text8, file2);
|
|
56295
56500
|
} catch {
|
|
56296
56501
|
}
|
|
@@ -56351,7 +56556,7 @@ function updateMemory(botId, update, opts = {}) {
|
|
|
56351
56556
|
}
|
|
56352
56557
|
const text8 = update.text === void 0 ? void 0 : redactSecretsInText(update.text);
|
|
56353
56558
|
const dir = ensureWorkspace(botId);
|
|
56354
|
-
const raw = readFileSync28(
|
|
56559
|
+
const raw = readFileSync28(join41(dir, "MEMORY.md"), "utf8");
|
|
56355
56560
|
const current2 = raw === MEMORY_SEED ? "" : raw;
|
|
56356
56561
|
const today = memoryDate(opts.now);
|
|
56357
56562
|
const appendEntry = (base2, entry2) => `${base2}${base2 && !base2.endsWith("\n") ? "\n" : ""}${entry2}
|
|
@@ -56423,10 +56628,10 @@ function appendMemoryLog(botId, text8, opts = {}) {
|
|
|
56423
56628
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
56424
56629
|
const source = cleanSource(opts.source);
|
|
56425
56630
|
const line = `- ${pad2(now.getHours())}:${pad2(now.getMinutes())}${SEP2}${source ? `from ${source}${SEP2}` : ""}${normaliseEntryText(redactSecretsInText(text8))}`;
|
|
56426
|
-
const dir =
|
|
56631
|
+
const dir = join41(ensureWorkspace(botId), "memory", MEMORY_LOG_DIR);
|
|
56427
56632
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56428
56633
|
const file2 = `${memoryDate(now)}.md`;
|
|
56429
|
-
const path =
|
|
56634
|
+
const path = join41(dir, file2);
|
|
56430
56635
|
let current2 = "";
|
|
56431
56636
|
try {
|
|
56432
56637
|
current2 = readFileSync28(path, "utf8");
|
|
@@ -56440,14 +56645,14 @@ function appendMemoryLog(botId, text8, opts = {}) {
|
|
|
56440
56645
|
}
|
|
56441
56646
|
function writeMemoryLog(botId, name2, text8) {
|
|
56442
56647
|
if (!LOG_FILE_NAME.test(name2)) throw new Error("invalid log name");
|
|
56443
|
-
const dir =
|
|
56648
|
+
const dir = join41(ensureWorkspace(botId), "memory", MEMORY_LOG_DIR);
|
|
56444
56649
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56445
|
-
writeFileAtomic(
|
|
56650
|
+
writeFileAtomic(join41(dir, name2), redactSecretsInText(text8), { mode: 384 });
|
|
56446
56651
|
indexWrittenMemoryFile(botId, `memory/${MEMORY_LOG_DIR}/${name2}`);
|
|
56447
56652
|
}
|
|
56448
56653
|
function listMemoryLogs(botId) {
|
|
56449
56654
|
try {
|
|
56450
|
-
return readdirSync4(
|
|
56655
|
+
return readdirSync4(join41(workspaceDir(botId), "memory", MEMORY_LOG_DIR)).filter((name2) => LOG_FILE_NAME.test(name2)).sort();
|
|
56451
56656
|
} catch {
|
|
56452
56657
|
return [];
|
|
56453
56658
|
}
|
|
@@ -56455,7 +56660,7 @@ function listMemoryLogs(botId) {
|
|
|
56455
56660
|
function readMemoryLog(botId, name2) {
|
|
56456
56661
|
if (!LOG_FILE_NAME.test(name2)) return null;
|
|
56457
56662
|
try {
|
|
56458
|
-
return readFileSync28(
|
|
56663
|
+
return readFileSync28(join41(workspaceDir(botId), "memory", MEMORY_LOG_DIR, name2), "utf8");
|
|
56459
56664
|
} catch {
|
|
56460
56665
|
return null;
|
|
56461
56666
|
}
|
|
@@ -56467,13 +56672,13 @@ function isMemoryTopicName(name2) {
|
|
|
56467
56672
|
function listMemoryTopics(botId) {
|
|
56468
56673
|
let entries;
|
|
56469
56674
|
try {
|
|
56470
|
-
entries = readdirSync4(
|
|
56675
|
+
entries = readdirSync4(join41(workspaceDir(botId), "memory"));
|
|
56471
56676
|
} catch {
|
|
56472
56677
|
return [];
|
|
56473
56678
|
}
|
|
56474
56679
|
return entries.filter(isMemoryTopicName).flatMap((name2) => {
|
|
56475
56680
|
try {
|
|
56476
|
-
const stat6 = statSync6(
|
|
56681
|
+
const stat6 = statSync6(join41(workspaceDir(botId), "memory", name2));
|
|
56477
56682
|
return stat6.isFile() ? [{ name: name2, bytes: stat6.size }] : [];
|
|
56478
56683
|
} catch {
|
|
56479
56684
|
return [];
|
|
@@ -56483,13 +56688,13 @@ function listMemoryTopics(botId) {
|
|
|
56483
56688
|
function writeMemoryTopic(botId, name2, text8) {
|
|
56484
56689
|
if (!isMemoryTopicName(name2)) throw new Error("invalid topic name");
|
|
56485
56690
|
ensureWorkspace(botId);
|
|
56486
|
-
writeFileAtomic(
|
|
56691
|
+
writeFileAtomic(join41(workspaceDir(botId), "memory", name2), redactSecretsInText(text8), { mode: 384 });
|
|
56487
56692
|
indexWrittenMemoryFile(botId, `memory/${name2}`);
|
|
56488
56693
|
}
|
|
56489
56694
|
function readMemoryTopic(botId, name2) {
|
|
56490
56695
|
if (!isMemoryTopicName(name2)) return null;
|
|
56491
56696
|
try {
|
|
56492
|
-
return readFileSync28(
|
|
56697
|
+
return readFileSync28(join41(workspaceDir(botId), "memory", name2), "utf8");
|
|
56493
56698
|
} catch {
|
|
56494
56699
|
return null;
|
|
56495
56700
|
}
|
|
@@ -56498,8 +56703,8 @@ var SESSION_SEARCH_SYSTEM_PROMPT = ' Your own earlier conversations with this us
|
|
|
56498
56703
|
var MEMORY_ROUTING_GUIDANCE = " MEMORY.md is for facts that hold in every session: the person's preferences, standing decisions, corrections, and pointers to files in <topicDir> for anything longer. Write each one as a plain statement of fact, never as an instruction to yourself \u2014 an imperative is read back as a directive next session. A procedure for one kind of task belongs in a memory/<topic>.md file or a skill, not here. Anything that will be stale within a week belongs in the conversation, not in memory. When a fact applies only from a date, or stops applying on one, say so in the entry.";
|
|
56499
56704
|
function memorySystemPrompt(botId, opts = {}) {
|
|
56500
56705
|
const memory2 = loadMemory(botId);
|
|
56501
|
-
const memoryFile =
|
|
56502
|
-
const topicDir =
|
|
56706
|
+
const memoryFile = join41(workspaceDir(botId), "MEMORY.md");
|
|
56707
|
+
const topicDir = join41(workspaceDir(botId), "memory");
|
|
56503
56708
|
if (opts.fileTools === false && !opts.managedWrites) {
|
|
56504
56709
|
if (!memory2) return "";
|
|
56505
56710
|
return ` Your saved memory is supplied as context; this turn has no memory editing tools.
|
|
@@ -56599,6 +56804,10 @@ var projectEmojiPattern = new RegExp("^(?!\\p{Emoji_Component}$)(?:\\p{RGI_Emoji
|
|
|
56599
56804
|
function isProjectEmoji(value) {
|
|
56600
56805
|
return typeof value === "string" && value.length <= 64 && projectEmojiPattern.exec(value)?.[0] === value;
|
|
56601
56806
|
}
|
|
56807
|
+
function toWireTask(task2) {
|
|
56808
|
+
const { resumeCursors: _resumeCursors, lastInstanceId: _lastInstanceId, ...wire } = task2;
|
|
56809
|
+
return wire;
|
|
56810
|
+
}
|
|
56602
56811
|
var TASK_PATCH_FIELDS = [
|
|
56603
56812
|
"title",
|
|
56604
56813
|
"projectId",
|
|
@@ -56743,9 +56952,9 @@ function titleFromMessage(text8) {
|
|
|
56743
56952
|
const line = text8.trim().split("\n")[0].trim();
|
|
56744
56953
|
return line.length > 48 ? `${line.slice(0, 47)}\u2026` : line || UNTITLED_TASK;
|
|
56745
56954
|
}
|
|
56746
|
-
var BOTS_FILE =
|
|
56747
|
-
var GROUPS_FILE =
|
|
56748
|
-
var messagesFile = (threadId) =>
|
|
56955
|
+
var BOTS_FILE = join42(DATA_DIR, "bots.json");
|
|
56956
|
+
var GROUPS_FILE = join42(DATA_DIR, "groups.json");
|
|
56957
|
+
var messagesFile = (threadId) => join42(DATA_DIR, `messages-${threadId}.json`);
|
|
56749
56958
|
var COLORS = [
|
|
56750
56959
|
"green",
|
|
56751
56960
|
"blue",
|
|
@@ -57159,13 +57368,20 @@ var Store = class {
|
|
|
57159
57368
|
this.emit({ type: "group", groupId: group.id });
|
|
57160
57369
|
return group;
|
|
57161
57370
|
}
|
|
57162
|
-
/** A thread's durable record: DB rows
|
|
57371
|
+
/** A thread's durable record: DB rows, legacy JSON leftovers, and the
|
|
57372
|
+
* per-thread event logs. Every delete path funnels here — task, group,
|
|
57373
|
+
* and bot deletion — so the logs cannot outlive the thread anywhere. */
|
|
57163
57374
|
deleteThreadRecord(threadId) {
|
|
57164
57375
|
this.threads.delete(threadId);
|
|
57165
57376
|
deleteThread(threadId);
|
|
57166
|
-
for (const file2 of [
|
|
57377
|
+
for (const file2 of [
|
|
57378
|
+
messagesFile(threadId),
|
|
57379
|
+
`${messagesFile(threadId)}.imported`,
|
|
57380
|
+
join42(EVENTS_DIR, `${threadId}.ndjson`),
|
|
57381
|
+
join42(NATIVE_DIR, `${threadId}.ndjson`)
|
|
57382
|
+
]) {
|
|
57167
57383
|
try {
|
|
57168
|
-
|
|
57384
|
+
unlinkSync8(file2);
|
|
57169
57385
|
} catch {
|
|
57170
57386
|
}
|
|
57171
57387
|
}
|
|
@@ -57677,7 +57893,7 @@ var Store = class {
|
|
|
57677
57893
|
} catch {
|
|
57678
57894
|
}
|
|
57679
57895
|
try {
|
|
57680
|
-
rmSync5(
|
|
57896
|
+
rmSync5(join42(DATA_DIR, "skill-state", id), { recursive: true, force: true });
|
|
57681
57897
|
} catch {
|
|
57682
57898
|
}
|
|
57683
57899
|
removeBotFolder(id);
|
|
@@ -58443,7 +58659,7 @@ function parseSince(value, now = Date.now()) {
|
|
|
58443
58659
|
return day.getTime();
|
|
58444
58660
|
}
|
|
58445
58661
|
if (/^\d{12,}$/.test(text8)) return Number(text8);
|
|
58446
|
-
const parsed = Date.parse(value.trim());
|
|
58662
|
+
const parsed = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(text8) ? `${text8}T00:00` : value.trim());
|
|
58447
58663
|
return Number.isFinite(parsed) ? parsed : null;
|
|
58448
58664
|
}
|
|
58449
58665
|
|
|
@@ -58482,7 +58698,7 @@ ${message4.text}`;
|
|
|
58482
58698
|
|
|
58483
58699
|
// server/delegations.ts
|
|
58484
58700
|
import { readFileSync as readFileSync30 } from "node:fs";
|
|
58485
|
-
import { join as
|
|
58701
|
+
import { join as join43 } from "node:path";
|
|
58486
58702
|
|
|
58487
58703
|
// server/peer-approval.ts
|
|
58488
58704
|
function settleCard(pending, behavior, source) {
|
|
@@ -58629,8 +58845,8 @@ function dismissStalePeerCards(bus2) {
|
|
|
58629
58845
|
var pendingDelegations = /* @__PURE__ */ new Map();
|
|
58630
58846
|
var drainingThreads = /* @__PURE__ */ new Set();
|
|
58631
58847
|
var queuedRedrains = /* @__PURE__ */ new Set();
|
|
58632
|
-
var DELEGATIONS_FILE =
|
|
58633
|
-
var RECEIPTS_FILE =
|
|
58848
|
+
var DELEGATIONS_FILE = join43(DATA_DIR, "delegations.json");
|
|
58849
|
+
var RECEIPTS_FILE = join43(DATA_DIR, "delegation-receipts.json");
|
|
58634
58850
|
var MAX_RECEIPTS = 100;
|
|
58635
58851
|
var RECEIPT_MAX_AGE_MS = 48 * 60 * 60 * 1e3;
|
|
58636
58852
|
var RESULT_MAX_CHARS = 4e3;
|
|
@@ -59320,6 +59536,25 @@ function cancelSteeredMessage(botId, messageId, expectedThreadId) {
|
|
|
59320
59536
|
}
|
|
59321
59537
|
return false;
|
|
59322
59538
|
}
|
|
59539
|
+
function holdSteeredQueue(botId, threadId, queueId) {
|
|
59540
|
+
const entry = queues.get(threadId);
|
|
59541
|
+
if (!entry || entry.botId !== botId || !entry.items.some((item) => item.messageId === queueId)) return null;
|
|
59542
|
+
queues.delete(threadId);
|
|
59543
|
+
changed();
|
|
59544
|
+
return { botId, threadId, items: entry.items };
|
|
59545
|
+
}
|
|
59546
|
+
function restoreHeldSteeredQueue(held) {
|
|
59547
|
+
const existing = queues.get(held.threadId);
|
|
59548
|
+
if (existing && existing.botId !== held.botId) throw new Error("queued task belongs to another bot");
|
|
59549
|
+
queues.set(held.threadId, {
|
|
59550
|
+
botId: held.botId,
|
|
59551
|
+
items: existing ? [...held.items, ...existing.items] : held.items
|
|
59552
|
+
});
|
|
59553
|
+
changed();
|
|
59554
|
+
}
|
|
59555
|
+
function settleHeldSteeredQueue(held) {
|
|
59556
|
+
settleChatFollowups(held.items.map((item) => item.messageId), null);
|
|
59557
|
+
}
|
|
59323
59558
|
|
|
59324
59559
|
// server/channel-queue.ts
|
|
59325
59560
|
var queues2 = /* @__PURE__ */ new Map();
|
|
@@ -59366,6 +59601,41 @@ function cancelChannelMessage(groupId, queueId) {
|
|
|
59366
59601
|
}
|
|
59367
59602
|
return false;
|
|
59368
59603
|
}
|
|
59604
|
+
function holdChannelQueue(groupId, threadId, queueId) {
|
|
59605
|
+
const entry = queues2.get(threadId);
|
|
59606
|
+
if (!entry || entry.groupId !== groupId || entry.items[0]?.id !== queueId) return null;
|
|
59607
|
+
queues2.delete(threadId);
|
|
59608
|
+
return { groupId, threadId, items: entry.items };
|
|
59609
|
+
}
|
|
59610
|
+
function restoreHeldChannelQueue(held) {
|
|
59611
|
+
const existing = queues2.get(held.threadId);
|
|
59612
|
+
if (existing && existing.groupId !== held.groupId) throw new Error("queued task belongs to another channel");
|
|
59613
|
+
queues2.set(held.threadId, {
|
|
59614
|
+
groupId: held.groupId,
|
|
59615
|
+
items: existing ? [...held.items, ...existing.items] : held.items
|
|
59616
|
+
});
|
|
59617
|
+
}
|
|
59618
|
+
function resolveHeldReplyTarget(held, resolve16) {
|
|
59619
|
+
const head = held.items[0];
|
|
59620
|
+
if (!head?.replyToId) return void 0;
|
|
59621
|
+
try {
|
|
59622
|
+
return resolve16(held.threadId, head.replyToId);
|
|
59623
|
+
} catch (error51) {
|
|
59624
|
+
restoreHeldChannelQueue(held);
|
|
59625
|
+
throw error51;
|
|
59626
|
+
}
|
|
59627
|
+
}
|
|
59628
|
+
function settleHeldChannelQueueHead(held) {
|
|
59629
|
+
const [head, ...rest] = held.items;
|
|
59630
|
+
settleChatFollowups([head.id], null);
|
|
59631
|
+
if (rest.length === 0) return;
|
|
59632
|
+
const existing = queues2.get(held.threadId);
|
|
59633
|
+
if (existing && existing.groupId !== held.groupId) throw new Error("queued task belongs to another channel");
|
|
59634
|
+
queues2.set(held.threadId, {
|
|
59635
|
+
groupId: held.groupId,
|
|
59636
|
+
items: existing ? [...rest, ...existing.items] : rest
|
|
59637
|
+
});
|
|
59638
|
+
}
|
|
59369
59639
|
function drainChannelMessages(isWorking, run3) {
|
|
59370
59640
|
for (const [threadId, entry] of queues2) {
|
|
59371
59641
|
if (isWorking(entry.groupId)) continue;
|
|
@@ -59429,7 +59699,7 @@ function sendFingerprint(text8, replyToId, channelMode) {
|
|
|
59429
59699
|
|
|
59430
59700
|
// server/harness/bus.ts
|
|
59431
59701
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
59432
|
-
import { join as
|
|
59702
|
+
import { join as join44 } from "node:path";
|
|
59433
59703
|
var INCOMPLETE_LOG_MESSAGE = "Canonical event history is incomplete: OpenMausBot could not write one or more events to disk. Live updates will continue.";
|
|
59434
59704
|
var EventBus = class {
|
|
59435
59705
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -59457,12 +59727,12 @@ var EventBus = class {
|
|
|
59457
59727
|
const persistedEvents = pendingWarning ? [pendingWarning, redactSecrets(event)] : [redactSecrets(event)];
|
|
59458
59728
|
try {
|
|
59459
59729
|
this.appendLog(
|
|
59460
|
-
|
|
59730
|
+
join44(EVENTS_DIR, `${event.threadId}.ndjson`),
|
|
59461
59731
|
persistedEvents.map((entry) => JSON.stringify(entry)).join("\n") + "\n",
|
|
59462
59732
|
{ mode: 384 }
|
|
59463
59733
|
);
|
|
59464
59734
|
if (pendingWarning) this.pendingLogWarnings.delete(event.threadId);
|
|
59465
|
-
capThreadLog(
|
|
59735
|
+
capThreadLog(join44(EVENTS_DIR, `${event.threadId}.ndjson`), currentThreadLogCap());
|
|
59466
59736
|
} catch (error51) {
|
|
59467
59737
|
if (!pendingWarning) {
|
|
59468
59738
|
const warning = {
|
|
@@ -59740,7 +60010,7 @@ var ProviderRegistry = class {
|
|
|
59740
60010
|
// server/managed-desktop.ts
|
|
59741
60011
|
import { createHash as createHash16 } from "node:crypto";
|
|
59742
60012
|
import { lstatSync as lstatSync3, mkdirSync as mkdirSync20 } from "node:fs";
|
|
59743
|
-
import { join as
|
|
60013
|
+
import { join as join45 } from "node:path";
|
|
59744
60014
|
var uuid3 = external_exports.string().uuid();
|
|
59745
60015
|
var model = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/:+-]{0,199}$/).refine((value) => !value.includes("::"));
|
|
59746
60016
|
var connectionSchema = external_exports.object({
|
|
@@ -59779,14 +60049,14 @@ function companyInstanceConfigs(connection, runtimeDirectory) {
|
|
|
59779
60049
|
if (provider.id === "anthropic") entries[id] = {
|
|
59780
60050
|
driver: "claudeAgent",
|
|
59781
60051
|
displayName: displayName2,
|
|
59782
|
-
config: { configDir:
|
|
60052
|
+
config: { configDir: join45(runtimeDirectory, id, "claude"), managed: true },
|
|
59783
60053
|
environment: { ANTHROPIC_API_KEY: connection.token, ANTHROPIC_AUTH_TOKEN: connection.token, ANTHROPIC_BASE_URL: base2, ANTHROPIC_MODEL: provider.models[0] }
|
|
59784
60054
|
};
|
|
59785
60055
|
if (provider.id === "openai") entries[id] = {
|
|
59786
60056
|
driver: "codex",
|
|
59787
60057
|
displayName: displayName2,
|
|
59788
60058
|
config: { managed: { url: `${base2}/v1`, models: provider.models } },
|
|
59789
|
-
environment: { OPENMAUSBOT_COMPANY_API_KEY: connection.token, CODEX_HOME:
|
|
60059
|
+
environment: { OPENMAUSBOT_COMPANY_API_KEY: connection.token, CODEX_HOME: join45(runtimeDirectory, id, "codex") }
|
|
59790
60060
|
};
|
|
59791
60061
|
if (provider.id === "openrouter") entries[id] = {
|
|
59792
60062
|
driver: "openai-compat",
|
|
@@ -59904,7 +60174,7 @@ var ManagedDesktopProviders = class {
|
|
|
59904
60174
|
mkdirSync20(this.options.dataDirectory, { recursive: true, mode: 448 });
|
|
59905
60175
|
let directory = this.options.dataDirectory;
|
|
59906
60176
|
for (const part of parts) {
|
|
59907
|
-
directory =
|
|
60177
|
+
directory = join45(directory, part);
|
|
59908
60178
|
try {
|
|
59909
60179
|
mkdirSync20(directory, { mode: 448 });
|
|
59910
60180
|
} catch (error51) {
|
|
@@ -60266,7 +60536,7 @@ async function synthesize2(text8, voiceId, key3) {
|
|
|
60266
60536
|
import { execFile as execFile5 } from "node:child_process";
|
|
60267
60537
|
import { mkdtemp as mkdtemp4, readFile as readFile4, rm as rm4 } from "node:fs/promises";
|
|
60268
60538
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
60269
|
-
import { join as
|
|
60539
|
+
import { join as join46 } from "node:path";
|
|
60270
60540
|
import { promisify as promisify2 } from "node:util";
|
|
60271
60541
|
var SAY = "/usr/bin/say";
|
|
60272
60542
|
var execFileAsync = promisify2(execFile5);
|
|
@@ -60297,8 +60567,8 @@ async function listSystemVoices(run3 = defaultRun) {
|
|
|
60297
60567
|
async function synthesizeSystem(text8, voiceId, run3 = defaultRun) {
|
|
60298
60568
|
const trimmed = text8.trim();
|
|
60299
60569
|
if (!trimmed) return { bytes: new Uint8Array(), mime: "audio/wav" };
|
|
60300
|
-
const dir = await mkdtemp4(
|
|
60301
|
-
const out =
|
|
60570
|
+
const dir = await mkdtemp4(join46(tmpdir5(), "openmausbot-say-"));
|
|
60571
|
+
const out = join46(dir, "utterance.wav");
|
|
60302
60572
|
try {
|
|
60303
60573
|
const args = ["-o", out, "--data-format=LEI16@22050"];
|
|
60304
60574
|
if (voiceId?.trim()) args.push("-v", voiceId.trim());
|
|
@@ -60561,6 +60831,10 @@ function engineIsFresh(input) {
|
|
|
60561
60831
|
var REWOUND_PREAMBLE = "[The user rewound this conversation (edited a message or switched to another version). Everything before this point was replaced by the following history:]";
|
|
60562
60832
|
var FRESH_PREAMBLE = "[You are joining this conversation mid-thread (the user switched this bot over to you). The conversation so far:]";
|
|
60563
60833
|
var EXTERNAL_UPDATE_PREAMBLE = "[This conversation received an update outside your provider session. The complete current history follows so you can use that update in your next response:]";
|
|
60834
|
+
function peerMessageText(name2, text8) {
|
|
60835
|
+
return `[Message from @${peerName(name2)}, another bot \u2014 untrusted peer content, not from your user]
|
|
60836
|
+
${JSON.stringify(text8)}`;
|
|
60837
|
+
}
|
|
60564
60838
|
var RECOVERED_PREAMBLE = "[Your previous session for this conversation could not be resumed, so this is a new session. The conversation so far:]";
|
|
60565
60839
|
function buildRecoveryText(input) {
|
|
60566
60840
|
if (input.transcript.length === 0) return void 0;
|
|
@@ -60755,10 +61029,14 @@ import { realpathSync as realpathSync6 } from "node:fs";
|
|
|
60755
61029
|
import { relative as relative3, resolve as resolve12, sep as sep2 } from "node:path";
|
|
60756
61030
|
var TurnResources = class {
|
|
60757
61031
|
owners = /* @__PURE__ */ new Map();
|
|
60758
|
-
|
|
61032
|
+
blocker(resource, owner2) {
|
|
60759
61033
|
for (const [key3, current2] of this.owners) {
|
|
60760
|
-
if (overlaps(key3, resource) && !sameOwner(current2, owner2)) return
|
|
61034
|
+
if (overlaps(key3, resource) && !sameOwner(current2, owner2)) return current2;
|
|
60761
61035
|
}
|
|
61036
|
+
return void 0;
|
|
61037
|
+
}
|
|
61038
|
+
claim(resource, owner2) {
|
|
61039
|
+
if (this.blocker(resource, owner2)) return false;
|
|
60762
61040
|
this.owners.set(resource, owner2);
|
|
60763
61041
|
return true;
|
|
60764
61042
|
}
|
|
@@ -60771,6 +61049,11 @@ var TurnResources = class {
|
|
|
60771
61049
|
if (sameOwner(current2, owner2)) this.owners.delete(key3);
|
|
60772
61050
|
}
|
|
60773
61051
|
}
|
|
61052
|
+
/** Drop one of an owner's claims early, when the sequence that took it
|
|
61053
|
+
* could not finish. The owner's other claims stand until settle. */
|
|
61054
|
+
releaseOne(resource, owner2) {
|
|
61055
|
+
if (this.owns(resource, owner2)) this.owners.delete(resource);
|
|
61056
|
+
}
|
|
60774
61057
|
};
|
|
60775
61058
|
function sameOwner(a, b3) {
|
|
60776
61059
|
return a.threadId === b3.threadId && a.generation === b3.generation;
|
|
@@ -60794,8 +61077,8 @@ function overlaps(a, b3) {
|
|
|
60794
61077
|
// server/memory-store.ts
|
|
60795
61078
|
import { spawn as spawn3 } from "node:child_process";
|
|
60796
61079
|
import { createHash as createHash17 } from "node:crypto";
|
|
60797
|
-
import { existsSync as existsSync21, lstatSync as lstatSync5, mkdirSync as mkdirSync21, readdirSync as readdirSync5, readFileSync as readFileSync31, realpathSync as realpathSync7, unlinkSync as
|
|
60798
|
-
import { dirname as dirname11, join as
|
|
61080
|
+
import { existsSync as existsSync21, lstatSync as lstatSync5, mkdirSync as mkdirSync21, readdirSync as readdirSync5, readFileSync as readFileSync31, realpathSync as realpathSync7, unlinkSync as unlinkSync9 } from "node:fs";
|
|
61081
|
+
import { dirname as dirname11, join as join47, sep as sep3 } from "node:path";
|
|
60799
61082
|
var MEMORY_INDEX = "MEMORY.md";
|
|
60800
61083
|
var MemoryStoreError = class extends Error {
|
|
60801
61084
|
status;
|
|
@@ -60841,7 +61124,7 @@ function resolveMemoryPath(botId, path) {
|
|
|
60841
61124
|
assertBotId(botId);
|
|
60842
61125
|
const ref = parseMemoryPath(path);
|
|
60843
61126
|
const root = workspaceDir(botId);
|
|
60844
|
-
const absolute =
|
|
61127
|
+
const absolute = join47(root, ...relativeSegments(ref));
|
|
60845
61128
|
let realRoot;
|
|
60846
61129
|
try {
|
|
60847
61130
|
realRoot = realpathSync7(root);
|
|
@@ -60900,7 +61183,7 @@ function listMarkdown(dir, prefix) {
|
|
|
60900
61183
|
for (const name2 of names) {
|
|
60901
61184
|
if (!isMemoryTopicName(name2)) continue;
|
|
60902
61185
|
try {
|
|
60903
|
-
const info = lstatSync5(
|
|
61186
|
+
const info = lstatSync5(join47(dir, name2));
|
|
60904
61187
|
if (!info.isFile()) continue;
|
|
60905
61188
|
files.push({ path: `${prefix}/${name2}`, name: name2, bytes: info.size, modifiedAt: Math.round(info.mtimeMs) });
|
|
60906
61189
|
} catch {
|
|
@@ -60922,8 +61205,8 @@ function memoryOverview(botId) {
|
|
|
60922
61205
|
botId,
|
|
60923
61206
|
workspacePath: root,
|
|
60924
61207
|
index: memoryCapacity(raw),
|
|
60925
|
-
topics: listMarkdown(
|
|
60926
|
-
logs: listMarkdown(
|
|
61208
|
+
topics: listMarkdown(join47(root, "memory"), "memory"),
|
|
61209
|
+
logs: listMarkdown(join47(root, "memory", "log"), "memory/log")
|
|
60927
61210
|
};
|
|
60928
61211
|
}
|
|
60929
61212
|
function readMemoryDoc(botId, path) {
|
|
@@ -60938,7 +61221,7 @@ function readMemoryDoc(botId, path) {
|
|
|
60938
61221
|
function writeMemoryDoc(botId, path, text8, opts = {}) {
|
|
60939
61222
|
const ref = parseMemoryPath(path);
|
|
60940
61223
|
ensureWorkspace(botId);
|
|
60941
|
-
if (ref.kind === "log") mkdirSync21(
|
|
61224
|
+
if (ref.kind === "log") mkdirSync21(join47(workspaceDir(botId), "memory", "log"), { recursive: true, mode: 448 });
|
|
60942
61225
|
const absolute = resolveMemoryPath(botId, path);
|
|
60943
61226
|
const after = redactSecretsInText(text8);
|
|
60944
61227
|
const bytes = Buffer.byteLength(after, "utf8");
|
|
@@ -60968,7 +61251,7 @@ function deleteMemoryDoc(botId, path) {
|
|
|
60968
61251
|
}
|
|
60969
61252
|
const absolute = resolveMemoryPath(botId, path);
|
|
60970
61253
|
const current2 = readMemoryDoc(botId, path);
|
|
60971
|
-
if (current2.exists)
|
|
61254
|
+
if (current2.exists) unlinkSync9(absolute);
|
|
60972
61255
|
return { path, before: current2.exists ? current2.text : null };
|
|
60973
61256
|
}
|
|
60974
61257
|
function obsidianUrlFor(workspacePath) {
|
|
@@ -61022,8 +61305,8 @@ async function openMemoryLocation(botId, target, platform = process.platform, ru
|
|
|
61022
61305
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
61023
61306
|
import { closeSync as closeSync8, fstatSync as fstatSync3, mkdirSync as mkdirSync22, openSync as openSync8, readSync as readSync4 } from "node:fs";
|
|
61024
61307
|
import { appendFile as appendFile3 } from "node:fs/promises";
|
|
61025
|
-
import { join as
|
|
61026
|
-
var JOURNAL_DIR =
|
|
61308
|
+
import { join as join48 } from "node:path";
|
|
61309
|
+
var JOURNAL_DIR = join48(DATA_DIR, "memory-journal");
|
|
61027
61310
|
var DIFF_CAP = 8e3;
|
|
61028
61311
|
var BEFORE_CAP = 512 * 1024;
|
|
61029
61312
|
var MAX_JOURNAL_READ_BYTES = 8 * 1024 * 1024;
|
|
@@ -61031,7 +61314,7 @@ var MAX_ROWS = 500;
|
|
|
61031
61314
|
var LCS_CELL_CAP = 4e6;
|
|
61032
61315
|
var CONTEXT = 2;
|
|
61033
61316
|
function journalFile(botId) {
|
|
61034
|
-
return
|
|
61317
|
+
return join48(JOURNAL_DIR, `${botId}.ndjson`);
|
|
61035
61318
|
}
|
|
61036
61319
|
function editScript(a, b3) {
|
|
61037
61320
|
if (a.length * b3.length > LCS_CELL_CAP) {
|
|
@@ -61349,10 +61632,10 @@ import {
|
|
|
61349
61632
|
renameSync as renameSync5,
|
|
61350
61633
|
rmSync as rmSync6,
|
|
61351
61634
|
symlinkSync,
|
|
61352
|
-
unlinkSync as
|
|
61635
|
+
unlinkSync as unlinkSync10,
|
|
61353
61636
|
writeFileSync as writeFileSync15
|
|
61354
61637
|
} from "node:fs";
|
|
61355
|
-
import { dirname as dirname12, join as
|
|
61638
|
+
import { dirname as dirname12, join as join49, relative as relative4, resolve as resolve13 } from "node:path";
|
|
61356
61639
|
|
|
61357
61640
|
// shared/learn-request.ts
|
|
61358
61641
|
var SAVE_RUN_AS_SKILL_LINE = "Save the steps below as a reusable skill for my review.";
|
|
@@ -61483,7 +61766,7 @@ var skillManifestEntrySchema = external_exports.object({
|
|
|
61483
61766
|
var skillManifestSchema = external_exports.record(external_exports.string(), skillManifestEntrySchema);
|
|
61484
61767
|
var managedLinksSchema = external_exports.array(external_exports.string());
|
|
61485
61768
|
function skillsDir(botId) {
|
|
61486
|
-
return
|
|
61769
|
+
return join49(workspaceDir(botId), "skills");
|
|
61487
61770
|
}
|
|
61488
61771
|
function directoryEntryState(path) {
|
|
61489
61772
|
try {
|
|
@@ -61513,20 +61796,20 @@ function ensureSkillsRoot(botId) {
|
|
|
61513
61796
|
function existingSkillDirectory(botId, name2) {
|
|
61514
61797
|
const root = existingSkillsRoot(botId);
|
|
61515
61798
|
if (!root) return null;
|
|
61516
|
-
const directory =
|
|
61799
|
+
const directory = join49(root, name2);
|
|
61517
61800
|
return directoryEntryState(directory) === "directory" ? directory : null;
|
|
61518
61801
|
}
|
|
61519
61802
|
function skillDirectory(botId, name2, entry) {
|
|
61520
61803
|
if (!entry.storageRevision) return existingSkillDirectory(botId, name2);
|
|
61521
61804
|
const root = existingSkillsRoot(botId);
|
|
61522
61805
|
if (!root) return null;
|
|
61523
|
-
const revisions =
|
|
61806
|
+
const revisions = join49(root, ".revisions");
|
|
61524
61807
|
if (directoryEntryState(revisions) !== "directory") return null;
|
|
61525
|
-
const directory =
|
|
61808
|
+
const directory = join49(revisions, entry.storageRevision);
|
|
61526
61809
|
return directoryEntryState(directory) === "directory" ? directory : null;
|
|
61527
61810
|
}
|
|
61528
61811
|
function skillTarget(root, name2, entry) {
|
|
61529
|
-
return entry.storageRevision ?
|
|
61812
|
+
return entry.storageRevision ? join49(root, "skills", ".revisions", entry.storageRevision) : join49(root, "skills", name2);
|
|
61530
61813
|
}
|
|
61531
61814
|
function entryExistsWithoutFollowing(path) {
|
|
61532
61815
|
try {
|
|
@@ -61539,7 +61822,7 @@ function entryExistsWithoutFollowing(path) {
|
|
|
61539
61822
|
function nativeLinkDirectory(root, relative6, create) {
|
|
61540
61823
|
const [family, leaf] = relative6.split("/");
|
|
61541
61824
|
if (!family || !leaf) return null;
|
|
61542
|
-
const familyDir =
|
|
61825
|
+
const familyDir = join49(root, family);
|
|
61543
61826
|
let familyState = directoryEntryState(familyDir);
|
|
61544
61827
|
if (familyState === "missing" && create) {
|
|
61545
61828
|
try {
|
|
@@ -61550,7 +61833,7 @@ function nativeLinkDirectory(root, relative6, create) {
|
|
|
61550
61833
|
familyState = directoryEntryState(familyDir);
|
|
61551
61834
|
}
|
|
61552
61835
|
if (familyState !== "directory") return null;
|
|
61553
|
-
const linkDir =
|
|
61836
|
+
const linkDir = join49(familyDir, leaf);
|
|
61554
61837
|
let linkState = directoryEntryState(linkDir);
|
|
61555
61838
|
if (linkState === "missing" && create) {
|
|
61556
61839
|
try {
|
|
@@ -61563,13 +61846,13 @@ function nativeLinkDirectory(root, relative6, create) {
|
|
|
61563
61846
|
return linkState === "directory" ? linkDir : null;
|
|
61564
61847
|
}
|
|
61565
61848
|
function skillStateDir(botId) {
|
|
61566
|
-
return
|
|
61849
|
+
return join49(DATA_DIR, "skill-state", botId);
|
|
61567
61850
|
}
|
|
61568
61851
|
function manifestPath(botId) {
|
|
61569
|
-
return
|
|
61852
|
+
return join49(skillStateDir(botId), "skills.json");
|
|
61570
61853
|
}
|
|
61571
61854
|
function managedLinksPath(botId) {
|
|
61572
|
-
return
|
|
61855
|
+
return join49(skillStateDir(botId), "managed-links.json");
|
|
61573
61856
|
}
|
|
61574
61857
|
function readManagedLinks(botId) {
|
|
61575
61858
|
try {
|
|
@@ -61604,7 +61887,7 @@ function readManifest(botId) {
|
|
|
61604
61887
|
if (existsSync22(securePath)) return manifestFromFile(securePath) ?? {};
|
|
61605
61888
|
const legacyRoot = existingSkillsRoot(botId);
|
|
61606
61889
|
if (!legacyRoot) return {};
|
|
61607
|
-
const legacyPath =
|
|
61890
|
+
const legacyPath = join49(legacyRoot, "skills.json");
|
|
61608
61891
|
if (!existsSync22(legacyPath)) return {};
|
|
61609
61892
|
const legacy = manifestFromFile(legacyPath) ?? {};
|
|
61610
61893
|
const migrated = {};
|
|
@@ -61646,20 +61929,20 @@ function nativeLinkDirectlyTargetsOwnedSkill(link2, root, name2, revisionWasMana
|
|
|
61646
61929
|
if (!lstatSync6(link2).isSymbolicLink()) return false;
|
|
61647
61930
|
const rawTarget = readlinkSync(link2);
|
|
61648
61931
|
const resolvedTarget = resolve13(targetBaseDirectory, rawTarget.replace(/^\\\\\?\\/, ""));
|
|
61649
|
-
const insideSkills = relative4(
|
|
61932
|
+
const insideSkills = relative4(join49(root, "skills"), resolvedTarget).replaceAll("\\", "/");
|
|
61650
61933
|
return insideSkills === name2 || revisionWasManaged && /^\.revisions\/[a-f0-9]{64}$/.test(insideSkills);
|
|
61651
61934
|
} catch {
|
|
61652
61935
|
return false;
|
|
61653
61936
|
}
|
|
61654
61937
|
}
|
|
61655
61938
|
function removeOwnedNativeLink(botId, link2, root, name2, revisionWasManaged, beforeRemove) {
|
|
61656
|
-
const quarantineDir =
|
|
61939
|
+
const quarantineDir = join49(skillStateDir(botId), "link-removal");
|
|
61657
61940
|
try {
|
|
61658
61941
|
mkdirSync23(quarantineDir, { recursive: true, mode: 448 });
|
|
61659
61942
|
} catch {
|
|
61660
61943
|
return "retry";
|
|
61661
61944
|
}
|
|
61662
|
-
const quarantined =
|
|
61945
|
+
const quarantined = join49(quarantineDir, randomUUID17());
|
|
61663
61946
|
beforeRemove?.(link2);
|
|
61664
61947
|
try {
|
|
61665
61948
|
renameSync5(link2, quarantined);
|
|
@@ -61674,7 +61957,7 @@ function removeOwnedNativeLink(botId, link2, root, name2, revisionWasManaged, be
|
|
|
61674
61957
|
dirname12(link2)
|
|
61675
61958
|
)) {
|
|
61676
61959
|
try {
|
|
61677
|
-
|
|
61960
|
+
unlinkSync10(quarantined);
|
|
61678
61961
|
return "removed";
|
|
61679
61962
|
} catch {
|
|
61680
61963
|
try {
|
|
@@ -61712,7 +61995,7 @@ function removeNativeLinksForUnsafeSkillsRoot(botId, root, previouslyManaged, be
|
|
|
61712
61995
|
continue;
|
|
61713
61996
|
}
|
|
61714
61997
|
for (const name2 of /* @__PURE__ */ new Set([...existing, ...previouslyManaged])) {
|
|
61715
|
-
const link2 =
|
|
61998
|
+
const link2 = join49(linkDir, name2);
|
|
61716
61999
|
if (!nativeLinkDirectlyTargetsOwnedSkill(link2, root, name2, previouslyManaged.includes(name2))) continue;
|
|
61717
62000
|
const result = removeOwnedNativeLink(
|
|
61718
62001
|
botId,
|
|
@@ -61752,7 +62035,7 @@ function syncSkillLinks(botId, options = {}) {
|
|
|
61752
62035
|
} catch {
|
|
61753
62036
|
}
|
|
61754
62037
|
for (const name2 of /* @__PURE__ */ new Set([...existing, ...previouslyManaged])) {
|
|
61755
|
-
const link2 =
|
|
62038
|
+
const link2 = join49(linkDir, name2);
|
|
61756
62039
|
const target = desired.get(name2);
|
|
61757
62040
|
if (target && nativeLinkPointsToSkill(link2, target)) {
|
|
61758
62041
|
managed.add(name2);
|
|
@@ -61770,7 +62053,7 @@ function syncSkillLinks(botId, options = {}) {
|
|
|
61770
62053
|
}
|
|
61771
62054
|
if (!enabled.length) continue;
|
|
61772
62055
|
for (const [name2, entry] of enabled) {
|
|
61773
|
-
const link2 =
|
|
62056
|
+
const link2 = join49(linkDir, name2);
|
|
61774
62057
|
const target = skillTarget(root, name2, entry);
|
|
61775
62058
|
if (nativeLinkPointsToSkill(link2, target)) {
|
|
61776
62059
|
managed.add(name2);
|
|
@@ -61796,7 +62079,7 @@ function skillContentMatches(botId, name2, entry) {
|
|
|
61796
62079
|
try {
|
|
61797
62080
|
const directory = skillDirectory(botId, name2, entry);
|
|
61798
62081
|
if (!directory) return false;
|
|
61799
|
-
const file2 =
|
|
62082
|
+
const file2 = join49(directory, "SKILL.md");
|
|
61800
62083
|
if (!lstatSync6(file2).isFile()) return false;
|
|
61801
62084
|
return createHash18("sha256").update(readFileSync32(file2)).digest("hex") === entry.sha256;
|
|
61802
62085
|
} catch {
|
|
@@ -61826,7 +62109,7 @@ function readSkillFile(botId, name2) {
|
|
|
61826
62109
|
if (!directory) return null;
|
|
61827
62110
|
let descriptor = null;
|
|
61828
62111
|
try {
|
|
61829
|
-
const path =
|
|
62112
|
+
const path = join49(directory, "SKILL.md");
|
|
61830
62113
|
const before = lstatSync6(path);
|
|
61831
62114
|
if (!before.isFile() || before.size > SKILL_FILE_MAX_BYTES) return null;
|
|
61832
62115
|
descriptor = openSync9(path, constants4.O_RDONLY | (process.platform === "win32" ? 0 : constants4.O_NOFOLLOW ?? 0));
|
|
@@ -61866,9 +62149,9 @@ function setSkillEnabled(botId, name2, enabled) {
|
|
|
61866
62149
|
function removeReviewedRevision(botId, revision, sha2564) {
|
|
61867
62150
|
const root = existingSkillsRoot(botId);
|
|
61868
62151
|
if (!root) return;
|
|
61869
|
-
const revisions =
|
|
62152
|
+
const revisions = join49(root, ".revisions");
|
|
61870
62153
|
if (directoryEntryState(revisions) !== "directory") return;
|
|
61871
|
-
const directory =
|
|
62154
|
+
const directory = join49(revisions, revision);
|
|
61872
62155
|
if (!learnedSkillDirectoryMatches(directory, sha2564)) return;
|
|
61873
62156
|
try {
|
|
61874
62157
|
rmSync6(directory, { recursive: true, force: true });
|
|
@@ -61890,7 +62173,7 @@ function retireReviewedSkillStorage(botId, name2, entry) {
|
|
|
61890
62173
|
function removeReviewedRevisionsNamed(botId, name2) {
|
|
61891
62174
|
const root = existingSkillsRoot(botId);
|
|
61892
62175
|
if (!root) return;
|
|
61893
|
-
const revisions =
|
|
62176
|
+
const revisions = join49(root, ".revisions");
|
|
61894
62177
|
if (directoryEntryState(revisions) !== "directory") return;
|
|
61895
62178
|
let candidates;
|
|
61896
62179
|
try {
|
|
@@ -61899,10 +62182,10 @@ function removeReviewedRevisionsNamed(botId, name2) {
|
|
|
61899
62182
|
return;
|
|
61900
62183
|
}
|
|
61901
62184
|
for (const revision of candidates) {
|
|
61902
|
-
const directory =
|
|
62185
|
+
const directory = join49(revisions, revision);
|
|
61903
62186
|
if (directoryEntryState(directory) !== "directory") continue;
|
|
61904
62187
|
try {
|
|
61905
|
-
const file2 =
|
|
62188
|
+
const file2 = join49(directory, "SKILL.md");
|
|
61906
62189
|
const stat6 = lstatSync6(file2);
|
|
61907
62190
|
if (!stat6.isFile() || stat6.size > SKILL_FILE_MAX_BYTES) continue;
|
|
61908
62191
|
const parsed = parseSkillMd(readFileSync32(file2, "utf8"));
|
|
@@ -61921,7 +62204,7 @@ function removeSkill(botId, name2) {
|
|
|
61921
62204
|
if (directoryEntryState(root) === "unsafe") {
|
|
61922
62205
|
return { error: "the workspace skills path is a symlink or file; refusing to remove through it" };
|
|
61923
62206
|
}
|
|
61924
|
-
const target = entry.storageRevision ? null :
|
|
62207
|
+
const target = entry.storageRevision ? null : join49(root, name2);
|
|
61925
62208
|
const targetState = target ? directoryEntryState(target) : "missing";
|
|
61926
62209
|
delete manifest[name2];
|
|
61927
62210
|
writeManifest(botId, manifest);
|
|
@@ -61954,7 +62237,7 @@ var stagedSkillWriteSchema = external_exports.object({
|
|
|
61954
62237
|
});
|
|
61955
62238
|
var stagedStoreSchema = external_exports.object({ writes: external_exports.record(external_exports.string(), stagedSkillWriteSchema) });
|
|
61956
62239
|
function stagedPath(botId) {
|
|
61957
|
-
return
|
|
62240
|
+
return join49(skillStateDir(botId), "staged.json");
|
|
61958
62241
|
}
|
|
61959
62242
|
function readStaged(botId) {
|
|
61960
62243
|
const securePath = stagedPath(botId);
|
|
@@ -61969,7 +62252,7 @@ function readStaged(botId) {
|
|
|
61969
62252
|
}
|
|
61970
62253
|
const legacyRoot = existingSkillsRoot(botId);
|
|
61971
62254
|
if (!legacyRoot) return { writes: {} };
|
|
61972
|
-
const legacyPath =
|
|
62255
|
+
const legacyPath = join49(legacyRoot, "staged.json");
|
|
61973
62256
|
if (!existsSync22(legacyPath)) return { writes: {} };
|
|
61974
62257
|
const empty = { writes: {} };
|
|
61975
62258
|
writeStaged(botId, empty);
|
|
@@ -62018,7 +62301,7 @@ function learnedSkillDirectoryMatches(directory, sha2564) {
|
|
|
62018
62301
|
try {
|
|
62019
62302
|
const entries = readdirSync6(directory);
|
|
62020
62303
|
if (entries.length !== 1 || entries[0] !== "SKILL.md") return false;
|
|
62021
|
-
const file2 =
|
|
62304
|
+
const file2 = join49(directory, "SKILL.md");
|
|
62022
62305
|
if (!lstatSync6(file2).isFile()) return false;
|
|
62023
62306
|
return createHash18("sha256").update(readFileSync32(file2)).digest("hex") === sha2564;
|
|
62024
62307
|
} catch {
|
|
@@ -62047,14 +62330,14 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62047
62330
|
if (directoryEntryState(state) !== "directory") {
|
|
62048
62331
|
throw new Error("the protected skill state path is not a real directory");
|
|
62049
62332
|
}
|
|
62050
|
-
const preparedRoot =
|
|
62333
|
+
const preparedRoot = join49(state, "reviewed-revisions");
|
|
62051
62334
|
const preparedRootState = directoryEntryState(preparedRoot);
|
|
62052
62335
|
if (preparedRootState === "unsafe") {
|
|
62053
62336
|
throw new Error("the protected revision path is not a real directory");
|
|
62054
62337
|
}
|
|
62055
62338
|
if (preparedRootState === "missing") mkdirSync23(preparedRoot, { mode: 448 });
|
|
62056
62339
|
const revision = createHash18("sha256").update(stageId).digest("hex");
|
|
62057
|
-
const prepared =
|
|
62340
|
+
const prepared = join49(preparedRoot, revision);
|
|
62058
62341
|
if (!learnedSkillDirectoryMatches(prepared, sha2564)) {
|
|
62059
62342
|
if (entryExistsWithoutFollowing(prepared)) {
|
|
62060
62343
|
if (directoryEntryState(prepared) !== "directory") {
|
|
@@ -62062,10 +62345,10 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62062
62345
|
}
|
|
62063
62346
|
rmSync6(prepared, { recursive: true, force: true });
|
|
62064
62347
|
}
|
|
62065
|
-
const temporary =
|
|
62348
|
+
const temporary = join49(preparedRoot, `.prepare-${revision}-${randomUUID17()}`);
|
|
62066
62349
|
try {
|
|
62067
62350
|
mkdirSync23(temporary, { mode: 448 });
|
|
62068
|
-
writeFileAtomic(
|
|
62351
|
+
writeFileAtomic(join49(temporary, "SKILL.md"), skillMd, { mode: 384 });
|
|
62069
62352
|
if (!learnedSkillDirectoryMatches(temporary, sha2564)) {
|
|
62070
62353
|
throw new Error("the protected reviewed bytes do not match the approval card");
|
|
62071
62354
|
}
|
|
@@ -62079,13 +62362,13 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62079
62362
|
if (!root) throw new Error("the workspace skills path must be a real directory, not a symlink or file");
|
|
62080
62363
|
const rootIdentity = directoryIdentity2(root);
|
|
62081
62364
|
if (!rootIdentity) throw new Error("the workspace skills path changed during update");
|
|
62082
|
-
const revisions =
|
|
62365
|
+
const revisions = join49(root, ".revisions");
|
|
62083
62366
|
const revisionsState = directoryEntryState(revisions);
|
|
62084
62367
|
if (revisionsState === "unsafe") throw new Error("the skill revisions path is not a real directory");
|
|
62085
62368
|
if (revisionsState === "missing") mkdirSync23(revisions, { mode: 448 });
|
|
62086
62369
|
const revisionsIdentity = directoryIdentity2(revisions);
|
|
62087
62370
|
if (!revisionsIdentity) throw new Error("the skill revisions path could not be created safely");
|
|
62088
|
-
const target =
|
|
62371
|
+
const target = join49(revisions, revision);
|
|
62089
62372
|
if (learnedSkillDirectoryMatches(target, sha2564)) {
|
|
62090
62373
|
try {
|
|
62091
62374
|
rmSync6(prepared, { recursive: true, force: true });
|
|
@@ -62108,14 +62391,14 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62108
62391
|
function commitNewSkillFiles(botId, name2, files, commitManifest) {
|
|
62109
62392
|
const root = ensureSkillsRoot(botId);
|
|
62110
62393
|
if (!root) throw new Error("the workspace skills path must be a real directory, not a symlink or file");
|
|
62111
|
-
const target =
|
|
62112
|
-
const staged =
|
|
62394
|
+
const target = join49(root, name2);
|
|
62395
|
+
const staged = join49(root, `.install-${name2}-${randomUUID17()}`);
|
|
62113
62396
|
if (entryExistsWithoutFollowing(target)) throw new Error(`skill path already exists: ${name2}`);
|
|
62114
62397
|
let published = false;
|
|
62115
62398
|
try {
|
|
62116
62399
|
mkdirSync23(staged, { mode: 448 });
|
|
62117
62400
|
for (const file2 of files) {
|
|
62118
|
-
writeFileSync15(
|
|
62401
|
+
writeFileSync15(join49(staged, file2.path), file2.content, { mode: 384 });
|
|
62119
62402
|
}
|
|
62120
62403
|
if (directoryEntryState(root) !== "directory" || entryExistsWithoutFollowing(target)) {
|
|
62121
62404
|
throw new Error("the workspace skills path changed during installation");
|
|
@@ -62159,7 +62442,7 @@ function installPreparedSkill(botId, source, prepared, options) {
|
|
|
62159
62442
|
};
|
|
62160
62443
|
const root = ensureSkillsRoot(botId);
|
|
62161
62444
|
if (!root) return { error: "the workspace skills path must be a real directory, not a symlink or file" };
|
|
62162
|
-
const target =
|
|
62445
|
+
const target = join49(root, name2);
|
|
62163
62446
|
if (entryExistsWithoutFollowing(target)) {
|
|
62164
62447
|
if (directoryEntryState(target) !== "directory") {
|
|
62165
62448
|
return { error: `skill path must be a real directory, not a symlink or file: ${name2}` };
|
|
@@ -62323,7 +62606,7 @@ function rejectStagedSkillWrite(botId, id) {
|
|
|
62323
62606
|
if (staged?.action === "update" && !alreadyApplied) {
|
|
62324
62607
|
const revision = createHash18("sha256").update(id).digest("hex");
|
|
62325
62608
|
removeReviewedRevision(botId, revision, staged.sha256);
|
|
62326
|
-
const prepared =
|
|
62609
|
+
const prepared = join49(skillStateDir(botId), "reviewed-revisions", revision);
|
|
62327
62610
|
if (learnedSkillDirectoryMatches(prepared, staged.sha256)) {
|
|
62328
62611
|
try {
|
|
62329
62612
|
rmSync6(prepared, { recursive: true, force: true });
|
|
@@ -62400,7 +62683,7 @@ function skillsSystemPrompt(botId) {
|
|
|
62400
62683
|
let bytes = 0;
|
|
62401
62684
|
for (const skill of enabled.slice(0, INDEX_MAX_SKILLS)) {
|
|
62402
62685
|
const entry = manifest[skill.name];
|
|
62403
|
-
const file2 =
|
|
62686
|
+
const file2 = join49(skillTarget(root, skill.name, entry), "SKILL.md");
|
|
62404
62687
|
const line = `- ${skill.name}: ${skill.description} Read ${JSON.stringify(file2)}.`;
|
|
62405
62688
|
bytes += Buffer.byteLength(line, "utf8");
|
|
62406
62689
|
if (bytes > INDEX_MAX_BYTES) break;
|
|
@@ -62460,7 +62743,7 @@ function parseSkillSource(input) {
|
|
|
62460
62743
|
const text8 = input.trim();
|
|
62461
62744
|
if (!text8) return { error: "paste a GitHub repository, folder, or SKILL.md URL" };
|
|
62462
62745
|
if (/^https?:\/\/raw\.githubusercontent\.com\/.+\/SKILL\.md$/i.test(text8)) return { rawUrl: text8 };
|
|
62463
|
-
const blob = text8.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(
|
|
62746
|
+
const blob = text8.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/((?:.*\/)?SKILL\.md)$/i);
|
|
62464
62747
|
if (blob) {
|
|
62465
62748
|
return { rawUrl: `https://raw.githubusercontent.com/${blob[1]}/${blob[2]}/${blob[3]}/${blob[4]}` };
|
|
62466
62749
|
}
|
|
@@ -62657,7 +62940,7 @@ function mentionPrompt(tagged) {
|
|
|
62657
62940
|
// server/local-computer.ts
|
|
62658
62941
|
import { lstatSync as lstatSync7, readFileSync as readFileSync33, realpathSync as realpathSync9, statSync as statSync8 } from "node:fs";
|
|
62659
62942
|
import { homedir as homedir23 } from "node:os";
|
|
62660
|
-
import { dirname as dirname13, isAbsolute as isAbsolute10, join as
|
|
62943
|
+
import { dirname as dirname13, isAbsolute as isAbsolute10, join as join50 } from "node:path";
|
|
62661
62944
|
var REQUIRED_LINUX_TOOLS = ["click", "get_window_state", "list_apps", "type_text"];
|
|
62662
62945
|
var DRIVER_FILE_IDENTITY_KEYS = [
|
|
62663
62946
|
"dev",
|
|
@@ -62842,10 +63125,10 @@ function readCuaConnection({
|
|
|
62842
63125
|
home = homedir23(),
|
|
62843
63126
|
validateLinuxRuntime = validateLinuxDescriptorRuntime
|
|
62844
63127
|
} = {}) {
|
|
62845
|
-
const candidates = userData ? [
|
|
63128
|
+
const candidates = userData ? [join50(userData, "cua-connection.json")] : [];
|
|
62846
63129
|
if (platform === "darwin") {
|
|
62847
63130
|
for (const directory of ["OpenMausBot", "openmausbot", "OpenGrokBot", "opengrokbot"]) {
|
|
62848
|
-
candidates.push(
|
|
63131
|
+
candidates.push(join50(home, "Library", "Application Support", directory, "cua-connection.json"));
|
|
62849
63132
|
}
|
|
62850
63133
|
}
|
|
62851
63134
|
for (const file2 of new Set(candidates)) {
|
|
@@ -63019,12 +63302,12 @@ import { createConnection, createServer } from "node:net";
|
|
|
63019
63302
|
// server/vps-ssh.ts
|
|
63020
63303
|
import { chmodSync as chmodSync5, existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync34, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync16 } from "node:fs";
|
|
63021
63304
|
import { homedir as homedir24 } from "node:os";
|
|
63022
|
-
import { delimiter as delimiter4, join as
|
|
63305
|
+
import { delimiter as delimiter4, join as join51 } from "node:path";
|
|
63023
63306
|
var VPS_SSH_DIR = "ssh";
|
|
63024
63307
|
function findExecutable(name2, pathValue, exclude) {
|
|
63025
63308
|
for (const dir of pathValue.split(delimiter4)) {
|
|
63026
63309
|
if (!dir || dir === exclude) continue;
|
|
63027
|
-
const candidate =
|
|
63310
|
+
const candidate = join51(dir, name2);
|
|
63028
63311
|
try {
|
|
63029
63312
|
const stat6 = statSync9(candidate);
|
|
63030
63313
|
if (stat6.isFile() && (stat6.mode & 73) !== 0) return candidate;
|
|
@@ -63045,14 +63328,14 @@ function writeIfChanged(path, content3, mode) {
|
|
|
63045
63328
|
writeFileSync16(temp, content3, { mode });
|
|
63046
63329
|
renameSync6(temp, path);
|
|
63047
63330
|
}
|
|
63048
|
-
function vpsSshConfigText(sshDir, userConfig =
|
|
63331
|
+
function vpsSshConfigText(sshDir, userConfig = join51(homedir24(), ".ssh", "config"), systemConfig = "/etc/ssh/ssh_config") {
|
|
63049
63332
|
return [
|
|
63050
63333
|
"# Written by OpenMausBot for its VPS computer connections. Do not edit;",
|
|
63051
63334
|
"# it is regenerated. Your own ~/.ssh/config is included first and wins.",
|
|
63052
63335
|
...existsSync23(userConfig) ? [`Include ${userConfig}`] : [],
|
|
63053
63336
|
"Host *",
|
|
63054
63337
|
" ControlMaster auto",
|
|
63055
|
-
` ControlPath ${
|
|
63338
|
+
` ControlPath ${join51(sshDir, "cm-%C")}`,
|
|
63056
63339
|
" ControlPersist 10m",
|
|
63057
63340
|
" ServerAliveInterval 15",
|
|
63058
63341
|
" ServerAliveCountMax 3",
|
|
@@ -63063,11 +63346,11 @@ function vpsSshConfigText(sshDir, userConfig = join50(homedir24(), ".ssh", "conf
|
|
|
63063
63346
|
}
|
|
63064
63347
|
function prepareVpsSsh(dataDir, pathValue, platform = process.platform) {
|
|
63065
63348
|
if (platform === "win32") return { configPath: null, path: pathValue };
|
|
63066
|
-
const sshDir =
|
|
63067
|
-
const binDir =
|
|
63349
|
+
const sshDir = join51(dataDir, VPS_SSH_DIR);
|
|
63350
|
+
const binDir = join51(sshDir, "bin");
|
|
63068
63351
|
mkdirSync24(binDir, { recursive: true, mode: 448 });
|
|
63069
63352
|
chmodSync5(sshDir, 448);
|
|
63070
|
-
const configPath =
|
|
63353
|
+
const configPath = join51(sshDir, "config");
|
|
63071
63354
|
writeIfChanged(configPath, vpsSshConfigText(sshDir), 384);
|
|
63072
63355
|
const realSsh = findExecutable("ssh", pathValue, binDir);
|
|
63073
63356
|
if (!realSsh) return { configPath, path: pathValue };
|
|
@@ -63078,7 +63361,7 @@ function prepareVpsSsh(dataDir, pathValue, platform = process.platform) {
|
|
|
63078
63361
|
`exec ${JSON.stringify(realSsh)} -F ${JSON.stringify(configPath)} "$@"`,
|
|
63079
63362
|
""
|
|
63080
63363
|
].join("\n");
|
|
63081
|
-
writeIfChanged(
|
|
63364
|
+
writeIfChanged(join51(binDir, "ssh"), shim, 448);
|
|
63082
63365
|
return { configPath, path: `${binDir}${delimiter4}${pathValue}` };
|
|
63083
63366
|
}
|
|
63084
63367
|
|
|
@@ -64007,7 +64290,7 @@ async function vpsComputerScreenshot(cfg2, botId, runner = defaultRunner) {
|
|
|
64007
64290
|
// server/routines.ts
|
|
64008
64291
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
64009
64292
|
import { mkdirSync as mkdirSync25, readFileSync as readFileSync35 } from "node:fs";
|
|
64010
|
-
import { dirname as dirname14, join as
|
|
64293
|
+
import { dirname as dirname14, join as join52 } from "node:path";
|
|
64011
64294
|
|
|
64012
64295
|
// node_modules/.pnpm/croner@10.0.1/node_modules/croner/dist/croner.js
|
|
64013
64296
|
function T(s4) {
|
|
@@ -64739,6 +65022,7 @@ function routineRequestOwnerKey(owner2) {
|
|
|
64739
65022
|
var ALL_DAYS = [0, 1, 2, 3, 4, 5, 6];
|
|
64740
65023
|
var CATCH_UP_MS = 12 * 60 * 6e4;
|
|
64741
65024
|
var WAKE_HORIZON_MS = 60 * 6e4;
|
|
65025
|
+
var ROUTINE_DEFERRAL_NOTICE_MS = 30 * 6e4;
|
|
64742
65026
|
var MAX_DATE_MS = 864e13;
|
|
64743
65027
|
var LOCAL_DAY_MS = 24 * 60 * 6e4;
|
|
64744
65028
|
var INTERVAL_RESTRICTION_SEARCH_MS = 9 * LOCAL_DAY_MS;
|
|
@@ -65119,7 +65403,7 @@ var RoutineManager = class {
|
|
|
65119
65403
|
ticking = false;
|
|
65120
65404
|
constructor(options) {
|
|
65121
65405
|
this.options = options;
|
|
65122
|
-
this.file = options.file ??
|
|
65406
|
+
this.file = options.file ?? join52(DATA_DIR, "routines.json");
|
|
65123
65407
|
this.now = options.now ?? Date.now;
|
|
65124
65408
|
try {
|
|
65125
65409
|
const disk = JSON.parse(readFileSync35(this.file, "utf8"));
|
|
@@ -65713,7 +65997,20 @@ var RoutineManager = class {
|
|
|
65713
65997
|
}
|
|
65714
65998
|
}
|
|
65715
65999
|
const state = this.targetState(run3);
|
|
65716
|
-
if (state === "busy")
|
|
66000
|
+
if (state === "busy") {
|
|
66001
|
+
if (run3.deferredAt == null) {
|
|
66002
|
+
run3.deferredAt = now;
|
|
66003
|
+
this.save();
|
|
66004
|
+
this.emitRun(run3);
|
|
66005
|
+
}
|
|
66006
|
+
if (run3.deferredNoticeAt == null && now - run3.deferredAt >= ROUTINE_DEFERRAL_NOTICE_MS) {
|
|
66007
|
+
run3.deferredNoticeAt = now;
|
|
66008
|
+
this.save();
|
|
66009
|
+
this.emitRun(run3);
|
|
66010
|
+
this.options.onRunDeferred?.(cloneRun(run3));
|
|
66011
|
+
}
|
|
66012
|
+
continue;
|
|
66013
|
+
}
|
|
65717
66014
|
if (state === "missing") {
|
|
65718
66015
|
this.failRun(run3, this.missingTargetMessage(run3.target));
|
|
65719
66016
|
continue;
|
|
@@ -66018,7 +66315,7 @@ var RoutineManager = class {
|
|
|
66018
66315
|
// server/calendar-calls.ts
|
|
66019
66316
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
66020
66317
|
import { mkdirSync as mkdirSync26, readFileSync as readFileSync36 } from "node:fs";
|
|
66021
|
-
import { dirname as dirname15, join as
|
|
66318
|
+
import { dirname as dirname15, join as join53 } from "node:path";
|
|
66022
66319
|
var MAX_BOTS = 100;
|
|
66023
66320
|
var MAX_ATTACHMENTS2 = 50;
|
|
66024
66321
|
var scheduleSchema = external_exports.discriminatedUnion("type", [
|
|
@@ -66124,7 +66421,7 @@ var CalendarCallManager = class {
|
|
|
66124
66421
|
timer = null;
|
|
66125
66422
|
ticking = false;
|
|
66126
66423
|
constructor(options) {
|
|
66127
|
-
this.file = options.file ??
|
|
66424
|
+
this.file = options.file ?? join53(DATA_DIR, "calendar-calls.json");
|
|
66128
66425
|
this.now = options.now ?? Date.now;
|
|
66129
66426
|
this.botExists = options.botExists;
|
|
66130
66427
|
this.onDue = options.onDue;
|
|
@@ -66294,12 +66591,12 @@ var CalendarCallManager = class {
|
|
|
66294
66591
|
// server/browser-engine.ts
|
|
66295
66592
|
import { spawn as spawn5 } from "node:child_process";
|
|
66296
66593
|
import { createHash as createHash20, randomBytes as randomBytes4, randomUUID as randomUUID20 } from "node:crypto";
|
|
66297
|
-
import { chmodSync as chmodSync6, constants as constants5, copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync27, readFileSync as readFileSync37, readdirSync as readdirSync7, renameSync as renameSync7, rmSync as rmSync7, statSync as statSync10, unlinkSync as
|
|
66594
|
+
import { chmodSync as chmodSync6, constants as constants5, copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync27, readFileSync as readFileSync37, readdirSync as readdirSync7, renameSync as renameSync7, rmSync as rmSync7, statSync as statSync10, unlinkSync as unlinkSync11, writeFileSync as writeFileSync17 } from "node:fs";
|
|
66298
66595
|
import { homedir as homedir25, tmpdir as tmpdir6 } from "node:os";
|
|
66299
|
-
import { delimiter as delimiter5, dirname as dirname16, join as
|
|
66596
|
+
import { delimiter as delimiter5, dirname as dirname16, join as join55, resolve as resolve14 } from "node:path";
|
|
66300
66597
|
|
|
66301
66598
|
// server/browser-bundle-release.ts
|
|
66302
|
-
import { join as
|
|
66599
|
+
import { join as join54 } from "node:path";
|
|
66303
66600
|
|
|
66304
66601
|
// server/browser-engine-release.ts
|
|
66305
66602
|
var AGENT_BROWSER_VERSION = "0.37.0";
|
|
@@ -66354,12 +66651,13 @@ function resolveAgentBrowserReleaseAsset(platform = process.platform, arch = pro
|
|
|
66354
66651
|
}
|
|
66355
66652
|
|
|
66356
66653
|
// server/browser-bundle-release.ts
|
|
66357
|
-
var CHROME_VERSION = "
|
|
66654
|
+
var CHROME_VERSION = "153.0.8010.47";
|
|
66358
66655
|
var CHROME_ASSETS = {
|
|
66359
|
-
"darwin-arm64": { platform: "mac-arm64", bytes:
|
|
66360
|
-
"darwin-x64": { platform: "mac-x64", bytes:
|
|
66361
|
-
"linux-
|
|
66362
|
-
"
|
|
66656
|
+
"darwin-arm64": { platform: "mac-arm64", bytes: 98668949, sha256: "6d28839675b6f22dbd7ba8775dbdabcae7a5be37b482380b27b12f05b748b955", executableSha256: "111ee425ea5b0a01dcb19fda16452a9be0319749cba374b1c987d2437d8dcedb" },
|
|
66657
|
+
"darwin-x64": { platform: "mac-x64", bytes: 103712919, sha256: "aa178547f9751fbcf413e0f57915ddb72e83064d4d1c4a29dc1719b169f9cc2f", executableSha256: "33452695b1b6f18db2f796a422eca69b69ca8aacce36d1ff8c6f5d69eac1a635" },
|
|
66658
|
+
"linux-arm64": { platform: "linux-arm64", bytes: 120245582, sha256: "af0931a58d6bab688112d5ca1f7abd6d95c0b8a296ef34637f272795787774d7", executableSha256: "b6a9f0483c976fddc72c34ce1c983a1275163cdfb95576874bfed5ff07c53807" },
|
|
66659
|
+
"linux-x64": { platform: "linux64", bytes: 119695587, sha256: "7728775cf4a35464cd81c8eea2d44d6d32ccc0bd1edfa75aea7f32d146963d63", executableSha256: "ea90dee9cbd7b17f197eb6c386bd9f1ff636734ee99753cd2408658c45358bf2" },
|
|
66660
|
+
"win32-x64": { platform: "win64", bytes: 120466147, sha256: "9f405cfaf7bc08bf9e046e653cd3086c0faa1d4e25907de857f7e7f093a20122", executableSha256: "01176f6928faeb37ff018bc8884bbca8a0197f51b659d467f2501ab2c64f9fcf" }
|
|
66363
66661
|
};
|
|
66364
66662
|
function browserBundleSpec(target) {
|
|
66365
66663
|
if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
|
|
@@ -66396,10 +66694,10 @@ function browserBundlePaths(bundleDirectory, target) {
|
|
|
66396
66694
|
const spec = browserBundleSpec(target);
|
|
66397
66695
|
return {
|
|
66398
66696
|
directory: bundleDirectory,
|
|
66399
|
-
manifest:
|
|
66400
|
-
engine:
|
|
66401
|
-
chrome:
|
|
66402
|
-
licenses:
|
|
66697
|
+
manifest: join54(bundleDirectory, "manifest.json"),
|
|
66698
|
+
engine: join54(bundleDirectory, spec.engine.executable),
|
|
66699
|
+
chrome: join54(bundleDirectory, spec.chrome.executable),
|
|
66700
|
+
licenses: join54(bundleDirectory, "licenses")
|
|
66403
66701
|
};
|
|
66404
66702
|
}
|
|
66405
66703
|
|
|
@@ -66795,11 +67093,11 @@ function browserSessionsDirectory(env) {
|
|
|
66795
67093
|
const namespace = env.AGENT_BROWSER_NAMESPACE;
|
|
66796
67094
|
if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
|
|
66797
67095
|
const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir25();
|
|
66798
|
-
const stateRoot = namespace ?
|
|
66799
|
-
return
|
|
67096
|
+
const stateRoot = namespace ? join55(home, ".agent-browser", "namespaces", namespace, "state") : join55(home, ".agent-browser");
|
|
67097
|
+
return join55(stateRoot, "sessions");
|
|
66800
67098
|
}
|
|
66801
67099
|
function managedBrowserConfigPath(env) {
|
|
66802
|
-
return
|
|
67100
|
+
return join55(browserSessionsDirectory(env), "..", "omb-managed-config.json");
|
|
66803
67101
|
}
|
|
66804
67102
|
function ensureManagedBrowserConfig(env) {
|
|
66805
67103
|
const path = managedBrowserConfigPath(env);
|
|
@@ -66887,11 +67185,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
66887
67185
|
ensureManagedBrowserConfig(env);
|
|
66888
67186
|
if (options.persistent === false) return;
|
|
66889
67187
|
const directory = browserSessionsDirectory(env);
|
|
66890
|
-
const key3 =
|
|
67188
|
+
const key3 = join55(directory, session);
|
|
66891
67189
|
let pending = preparedBrowserSessions.get(key3);
|
|
66892
67190
|
if (!pending) {
|
|
66893
67191
|
pending = (async () => {
|
|
66894
|
-
const targetBase =
|
|
67192
|
+
const targetBase = join55(directory, `${restoreKey}-${session}`);
|
|
66895
67193
|
const hasTarget = () => existsSync24(`${targetBase}.json.enc`) || existsSync24(`${targetBase}.json`);
|
|
66896
67194
|
if (hasTarget()) return;
|
|
66897
67195
|
check2();
|
|
@@ -66901,7 +67199,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
66901
67199
|
check2();
|
|
66902
67200
|
if (hasTarget()) return;
|
|
66903
67201
|
const candidates = [".json.enc", ".json"].flatMap((suffix) => {
|
|
66904
|
-
const path =
|
|
67202
|
+
const path = join55(directory, `${session}-${session}${suffix}`);
|
|
66905
67203
|
try {
|
|
66906
67204
|
const stat6 = statSync10(path);
|
|
66907
67205
|
if (!stat6.isFile()) throw new Error("Saved browser state is not a file.");
|
|
@@ -66936,7 +67234,7 @@ function isMusl(platform = process.platform, exists = existsSync24) {
|
|
|
66936
67234
|
}
|
|
66937
67235
|
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
66938
67236
|
const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
|
|
66939
|
-
return
|
|
67237
|
+
return join55(dataDir, ENGINE_DIR, version2, executableName(platform));
|
|
66940
67238
|
}
|
|
66941
67239
|
function onPath(env, platform, exists) {
|
|
66942
67240
|
const pathValue = platform === "win32" ? Object.entries(env).findLast(([key3]) => key3.toUpperCase() === "PATH")?.[1] : env.PATH;
|
|
@@ -66952,7 +67250,7 @@ function packagedBrowser(options) {
|
|
|
66952
67250
|
const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
|
|
66953
67251
|
if (!resources) return null;
|
|
66954
67252
|
try {
|
|
66955
|
-
return browserBundlePaths(
|
|
67253
|
+
return browserBundlePaths(join55(resolve14(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
|
|
66956
67254
|
} catch {
|
|
66957
67255
|
return null;
|
|
66958
67256
|
}
|
|
@@ -66977,7 +67275,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
66977
67275
|
const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
|
|
66978
67276
|
if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
|
|
66979
67277
|
const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
66980
|
-
const directory =
|
|
67278
|
+
const directory = join55(destination, "..");
|
|
66981
67279
|
mkdirSync27(directory, { recursive: true, mode: 448 });
|
|
66982
67280
|
const url2 = agentBrowserReleaseUrl(asset);
|
|
66983
67281
|
options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
|
|
@@ -67029,7 +67327,7 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
67029
67327
|
});
|
|
67030
67328
|
}
|
|
67031
67329
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
67032
|
-
const file2 =
|
|
67330
|
+
const file2 = join55(dataDir, KEY_FILE);
|
|
67033
67331
|
try {
|
|
67034
67332
|
const existing = readFileSync37(file2, "utf8").trim();
|
|
67035
67333
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
@@ -67082,7 +67380,7 @@ function agentBrowserIntegration(input) {
|
|
|
67082
67380
|
for (const name2 of ["PATH", "AGENT_BROWSER_EXECUTABLE_PATH"]) {
|
|
67083
67381
|
if (sourceEnv[name2]) env[name2] = sourceEnv[name2];
|
|
67084
67382
|
}
|
|
67085
|
-
const bundle = packagedBrowser({ env: sourceEnv });
|
|
67383
|
+
const bundle = packagedBrowser({ env: sourceEnv, platform: input.platform, arch: input.arch });
|
|
67086
67384
|
if (!env.AGENT_BROWSER_EXECUTABLE_PATH && bundle && resolve14(input.binaryPath) === bundle.engine && completePackage(bundle, existsSync24)) {
|
|
67087
67385
|
env.AGENT_BROWSER_EXECUTABLE_PATH = bundle.chrome;
|
|
67088
67386
|
}
|
|
@@ -67090,7 +67388,7 @@ function agentBrowserIntegration(input) {
|
|
|
67090
67388
|
}
|
|
67091
67389
|
var FRAME_TIMEOUT_MS = 1e4;
|
|
67092
67390
|
function agentBrowserFrame(input) {
|
|
67093
|
-
const file2 =
|
|
67391
|
+
const file2 = join55(tmpdir6(), `openmausbot-browser-${randomUUID20()}.png`);
|
|
67094
67392
|
return new Promise((settle, fail2) => {
|
|
67095
67393
|
const child = spawn5(input.binaryPath, ["screenshot", file2], {
|
|
67096
67394
|
env: browserRuntimeEnv(input.env),
|
|
@@ -67148,14 +67446,14 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
67148
67446
|
const bases = [`${session}-${session}`, `${browserRestoreKey(session)}-${session}`];
|
|
67149
67447
|
const remove = (path) => {
|
|
67150
67448
|
try {
|
|
67151
|
-
|
|
67449
|
+
unlinkSync11(path);
|
|
67152
67450
|
} catch (error51) {
|
|
67153
67451
|
if (error51.code !== "ENOENT") throw error51;
|
|
67154
67452
|
}
|
|
67155
67453
|
};
|
|
67156
67454
|
try {
|
|
67157
|
-
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(
|
|
67158
|
-
const pendingDirectory =
|
|
67455
|
+
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join55(directory, base2 + suffix));
|
|
67456
|
+
const pendingDirectory = join55(directory, ".tmp");
|
|
67159
67457
|
let pendingFiles = [];
|
|
67160
67458
|
try {
|
|
67161
67459
|
pendingFiles = readdirSync7(pendingDirectory);
|
|
@@ -67163,8 +67461,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
67163
67461
|
if (error51.code !== "ENOENT") throw error51;
|
|
67164
67462
|
}
|
|
67165
67463
|
const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
|
|
67166
|
-
for (const name2 of pendingFiles) if (candidates.some((candidate) => candidate.test(name2))) remove(
|
|
67167
|
-
preparedBrowserSessions.delete(
|
|
67464
|
+
for (const name2 of pendingFiles) if (candidates.some((candidate) => candidate.test(name2))) remove(join55(pendingDirectory, name2));
|
|
67465
|
+
preparedBrowserSessions.delete(join55(directory, session));
|
|
67168
67466
|
return true;
|
|
67169
67467
|
} catch {
|
|
67170
67468
|
return false;
|
|
@@ -69171,13 +69469,13 @@ function profileRevision(bot) {
|
|
|
69171
69469
|
import { appendFile as appendFile4 } from "node:fs/promises";
|
|
69172
69470
|
import { createHash as createHash24, randomUUID as randomUUID22 } from "node:crypto";
|
|
69173
69471
|
import { closeSync as closeSync10, existsSync as existsSync25, fstatSync as fstatSync5, mkdirSync as mkdirSync28, openSync as openSync10, readSync as readSync5 } from "node:fs";
|
|
69174
|
-
import { join as
|
|
69472
|
+
import { join as join56 } from "node:path";
|
|
69175
69473
|
var FILE_NAME2 = "history.ndjson";
|
|
69176
69474
|
var ONE_LINER = 200;
|
|
69177
69475
|
var MAX_HISTORY_READ_BYTES = 8 * 1024 * 1024;
|
|
69178
69476
|
var writeQueues4 = /* @__PURE__ */ new Map();
|
|
69179
69477
|
function historyFile(botId) {
|
|
69180
|
-
return
|
|
69478
|
+
return join56(botFolder(botId), FILE_NAME2);
|
|
69181
69479
|
}
|
|
69182
69480
|
function oneLiner(value) {
|
|
69183
69481
|
return value.replace(/\s+/g, " ").trim().slice(0, ONE_LINER);
|
|
@@ -69976,7 +70274,7 @@ function matchDirectoryBots(profile, bots, limit = 5) {
|
|
|
69976
70274
|
|
|
69977
70275
|
// server/project-scout.ts
|
|
69978
70276
|
import { readFileSync as readFileSync38, readdirSync as readdirSync8, statSync as statSync11 } from "node:fs";
|
|
69979
|
-
import { basename as basename7, join as
|
|
70277
|
+
import { basename as basename7, join as join57 } from "node:path";
|
|
69980
70278
|
|
|
69981
70279
|
// shared/import-name.ts
|
|
69982
70280
|
function takeImportName(value, taken, maxLength = 100) {
|
|
@@ -70180,7 +70478,7 @@ function isDir(path) {
|
|
|
70180
70478
|
}
|
|
70181
70479
|
}
|
|
70182
70480
|
function packageJson(cwd) {
|
|
70183
|
-
const raw = readText2(
|
|
70481
|
+
const raw = readText2(join57(cwd, "package.json"));
|
|
70184
70482
|
if (!raw) return { deps: /* @__PURE__ */ new Set() };
|
|
70185
70483
|
try {
|
|
70186
70484
|
const parsed = JSON.parse(raw);
|
|
@@ -70203,7 +70501,7 @@ function packageJson(cwd) {
|
|
|
70203
70501
|
}
|
|
70204
70502
|
}
|
|
70205
70503
|
function readme(cwd) {
|
|
70206
|
-
const raw = readText2(
|
|
70504
|
+
const raw = readText2(join57(cwd, "README.md")) ?? readText2(join57(cwd, "readme.md")) ?? readText2(join57(cwd, "README"));
|
|
70207
70505
|
if (!raw) return {};
|
|
70208
70506
|
let title;
|
|
70209
70507
|
let summary;
|
|
@@ -70277,12 +70575,12 @@ function scoutProject(cwd) {
|
|
|
70277
70575
|
const pkg = packageJson(cwd);
|
|
70278
70576
|
const md = readme(cwd);
|
|
70279
70577
|
const entries = new Set(listNames(cwd));
|
|
70280
|
-
const pythonDeps = `${readText2(
|
|
70281
|
-
${readText2(
|
|
70578
|
+
const pythonDeps = `${readText2(join57(cwd, "requirements.txt")) ?? ""}
|
|
70579
|
+
${readText2(join57(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
70282
70580
|
const hasDep = (dep) => pkg.deps.has(dep) || pythonDeps.length > 1 && pythonDeps.includes(dep);
|
|
70283
70581
|
const pathEvidence = (path) => {
|
|
70284
|
-
if (path.includes("/")) return isDir(
|
|
70285
|
-
if (entries.has(path)) return isDir(
|
|
70582
|
+
if (path.includes("/")) return isDir(join57(cwd, path)) && listNames(join57(cwd, path)).length > 0 ? `${path}/` : null;
|
|
70583
|
+
if (entries.has(path)) return isDir(join57(cwd, path)) ? `${path}/` : path;
|
|
70286
70584
|
return null;
|
|
70287
70585
|
};
|
|
70288
70586
|
const signals = [];
|
|
@@ -70295,7 +70593,7 @@ ${readText2(join56(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
|
70295
70593
|
}
|
|
70296
70594
|
if (detector.role === "docs" && evidence.length > 0) {
|
|
70297
70595
|
const hasMkdocs = evidence.includes("mkdocs.yml");
|
|
70298
|
-
const hasMarkdown = listNames(
|
|
70596
|
+
const hasMarkdown = listNames(join57(cwd, "docs")).some((name2) => name2.endsWith(".md"));
|
|
70299
70597
|
if (!hasMkdocs && !hasMarkdown) continue;
|
|
70300
70598
|
}
|
|
70301
70599
|
if (evidence.length > 0) signals.push({ role: detector.role, evidence: evidence.slice(0, 6) });
|
|
@@ -70984,7 +71282,7 @@ async function fetchGithubTeam(input, fetcher = fetch) {
|
|
|
70984
71282
|
|
|
70985
71283
|
// server/thread-events.ts
|
|
70986
71284
|
import { closeSync as closeSync11, fstatSync as fstatSync6, openSync as openSync11, readSync as readSync6 } from "node:fs";
|
|
70987
|
-
import { join as
|
|
71285
|
+
import { join as join58 } from "node:path";
|
|
70988
71286
|
var DEFAULT_LIMIT = 300;
|
|
70989
71287
|
var MAX_LIMIT = 2e3;
|
|
70990
71288
|
var READ_CHUNK = 64 * 1024;
|
|
@@ -71121,8 +71419,8 @@ function readThreadEvents(input) {
|
|
|
71121
71419
|
assertThreadId(threadId);
|
|
71122
71420
|
const requested = input.limit ?? DEFAULT_LIMIT;
|
|
71123
71421
|
const limit = Number.isFinite(requested) ? Math.max(1, Math.min(Math.trunc(requested), MAX_LIMIT)) : DEFAULT_LIMIT;
|
|
71124
|
-
const runtime = readRecentLines(
|
|
71125
|
-
const native = readRecentLines(
|
|
71422
|
+
const runtime = readRecentLines(join58(eventsDir, `${threadId}.ndjson`), limit, isRuntimeEvent);
|
|
71423
|
+
const native = readRecentLines(join58(nativeDir, `${threadId}.ndjson`), limit, isNativeRecord);
|
|
71126
71424
|
const merged = [
|
|
71127
71425
|
...runtime.lines.map((data) => ({ kind: "runtime", at: data.createdAt, data })),
|
|
71128
71426
|
...native.lines.map((data) => ({ kind: "native", at: data.at, data }))
|
|
@@ -71312,7 +71610,7 @@ function memberTurnSelection(selection) {
|
|
|
71312
71610
|
// server/webhooks.ts
|
|
71313
71611
|
import { createHash as createHash26, randomBytes as randomBytes5, randomUUID as randomUUID23, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
71314
71612
|
import { mkdirSync as mkdirSync29, readFileSync as readFileSync39 } from "node:fs";
|
|
71315
|
-
import { dirname as dirname17, join as
|
|
71613
|
+
import { dirname as dirname17, join as join59 } from "node:path";
|
|
71316
71614
|
var MAX_DELIVERIES = 2e3;
|
|
71317
71615
|
var MAX_ATTEMPTS = 2e3;
|
|
71318
71616
|
var MAX_EVENT_CHARS = 48e3;
|
|
@@ -71499,7 +71797,7 @@ var WebhookManager = class {
|
|
|
71499
71797
|
rate = /* @__PURE__ */ new Map();
|
|
71500
71798
|
constructor(options) {
|
|
71501
71799
|
this.options = options;
|
|
71502
|
-
this.file = options.file ??
|
|
71800
|
+
this.file = options.file ?? join59(DATA_DIR, "webhooks.json");
|
|
71503
71801
|
this.now = options.now ?? Date.now;
|
|
71504
71802
|
try {
|
|
71505
71803
|
const parsed = webhookFileSchema.safeParse(parseJson(readFileSync39(this.file, "utf8")));
|
|
@@ -71764,7 +72062,7 @@ var WebhookManager = class {
|
|
|
71764
72062
|
|
|
71765
72063
|
// server/skill-library.ts
|
|
71766
72064
|
import { existsSync as existsSync26, readFileSync as readFileSync40, readdirSync as readdirSync9 } from "node:fs";
|
|
71767
|
-
import { basename as basename8, join as
|
|
72065
|
+
import { basename as basename8, join as join60 } from "node:path";
|
|
71768
72066
|
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
71769
72067
|
function strings(value) {
|
|
71770
72068
|
return Array.isArray(value) && value.every((item) => typeof item === "string" && item.trim()) ? value.map((item) => item.trim()) : null;
|
|
@@ -71793,19 +72091,19 @@ function parseSkillManifest(value, directory) {
|
|
|
71793
72091
|
};
|
|
71794
72092
|
}
|
|
71795
72093
|
function loadSkillDirectory(directory) {
|
|
71796
|
-
const manifestPath2 =
|
|
71797
|
-
const skillPath =
|
|
72094
|
+
const manifestPath2 = join60(directory, "manifest.json");
|
|
72095
|
+
const skillPath = join60(directory, "SKILL.md");
|
|
71798
72096
|
if (!existsSync26(manifestPath2) || !existsSync26(skillPath)) return null;
|
|
71799
72097
|
const manifest = parseSkillManifest(JSON.parse(readFileSync40(manifestPath2, "utf8")), directory);
|
|
71800
72098
|
const instructions = readFileSync40(skillPath, "utf8").trim();
|
|
71801
72099
|
if (!instructions.startsWith("---")) throw new Error(`${skillPath} has no skill frontmatter`);
|
|
71802
72100
|
return { manifest, instructions, directory };
|
|
71803
72101
|
}
|
|
71804
|
-
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR ||
|
|
72102
|
+
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR || join60(process.cwd(), "skills")) {
|
|
71805
72103
|
if (!existsSync26(root)) return [];
|
|
71806
72104
|
const skills = [];
|
|
71807
72105
|
for (const name2 of readdirSync9(root).sort()) {
|
|
71808
|
-
const directory =
|
|
72106
|
+
const directory = join60(root, name2);
|
|
71809
72107
|
const skill = loadSkillDirectory(directory);
|
|
71810
72108
|
if (skill) skills.push(skill);
|
|
71811
72109
|
}
|
|
@@ -71822,7 +72120,7 @@ function loadUserSkills(root) {
|
|
|
71822
72120
|
const skills = [];
|
|
71823
72121
|
for (const name2 of names) {
|
|
71824
72122
|
try {
|
|
71825
|
-
const skill = loadSkillDirectory(
|
|
72123
|
+
const skill = loadSkillDirectory(join60(root, name2));
|
|
71826
72124
|
if (skill) skills.push(skill);
|
|
71827
72125
|
} catch {
|
|
71828
72126
|
}
|
|
@@ -72436,14 +72734,31 @@ ${message4.text}` } : {},
|
|
|
72436
72734
|
}
|
|
72437
72735
|
|
|
72438
72736
|
// server/local-routing.ts
|
|
72737
|
+
var HOSTS_WITH_LOCAL_CONTROL = /* @__PURE__ */ new Set(["darwin", "win32", "linux"]);
|
|
72738
|
+
var HOSTS_WITH_AUTO_LOCAL = /* @__PURE__ */ new Set(["darwin", "win32"]);
|
|
72439
72739
|
function shouldMountLocalComputer({
|
|
72440
72740
|
requested,
|
|
72441
72741
|
hostPlatform = process.platform,
|
|
72442
72742
|
providerSupportsLocal
|
|
72443
72743
|
}) {
|
|
72444
72744
|
if (!providerSupportsLocal) return false;
|
|
72445
|
-
if (requested === "local") return hostPlatform
|
|
72446
|
-
return requested === void 0 && hostPlatform
|
|
72745
|
+
if (requested === "local") return HOSTS_WITH_LOCAL_CONTROL.has(hostPlatform);
|
|
72746
|
+
return requested === void 0 && HOSTS_WITH_AUTO_LOCAL.has(hostPlatform);
|
|
72747
|
+
}
|
|
72748
|
+
|
|
72749
|
+
// server/auto-vm-claims.ts
|
|
72750
|
+
function startAutoVmClaim(table, threadId, generation) {
|
|
72751
|
+
const slot = table.get(threadId);
|
|
72752
|
+
if (!slot || slot.owner.threadId !== threadId || slot.owner.generation !== generation || slot.begin) return;
|
|
72753
|
+
slot.begin = slot.claim().then(
|
|
72754
|
+
() => {
|
|
72755
|
+
slot.claimed = true;
|
|
72756
|
+
},
|
|
72757
|
+
(error51) => {
|
|
72758
|
+
slot.failed = true;
|
|
72759
|
+
slot.failure = error51 instanceof Error ? error51.message : String(error51);
|
|
72760
|
+
}
|
|
72761
|
+
);
|
|
72447
72762
|
}
|
|
72448
72763
|
|
|
72449
72764
|
// server/model-context-window.ts
|
|
@@ -72697,9 +73012,9 @@ function createGracefulShutdown({
|
|
|
72697
73012
|
// electron/data-dir-lease.mjs
|
|
72698
73013
|
import { execFileSync } from "node:child_process";
|
|
72699
73014
|
import { createHash as createHash27, randomUUID as randomUUID24 } from "node:crypto";
|
|
72700
|
-
import { existsSync as existsSync27, linkSync as linkSync5, mkdirSync as mkdirSync30, readFileSync as readFileSync41, renameSync as renameSync8, unlinkSync as
|
|
73015
|
+
import { existsSync as existsSync27, linkSync as linkSync5, mkdirSync as mkdirSync30, readFileSync as readFileSync41, renameSync as renameSync8, unlinkSync as unlinkSync12, writeFileSync as writeFileSync18 } from "node:fs";
|
|
72701
73016
|
import { hostname as hostname4, uptime } from "node:os";
|
|
72702
|
-
import { join as
|
|
73017
|
+
import { join as join61 } from "node:path";
|
|
72703
73018
|
var LEASE_NAME = "openmausbot-server.lease";
|
|
72704
73019
|
var DELEGATED_CHILD_DIR = ".openmausbot-server-child";
|
|
72705
73020
|
var CHILD_LEASE_ENV = "OPENMAUSBOT_INTERNAL_DATA_DIR_LEASE";
|
|
@@ -72755,7 +73070,7 @@ function ownerPredatesThisBoot(owner2) {
|
|
|
72755
73070
|
return false;
|
|
72756
73071
|
}
|
|
72757
73072
|
function ownerIsAlive(owner2) {
|
|
72758
|
-
return !ownerPredatesThisBoot(owner2) && processIsAlive3(owner2.pid);
|
|
73073
|
+
return !ownerPredatesThisBoot(owner2) && processIsAlive3(owner2.pid) && ownerIdentityMatches(owner2);
|
|
72759
73074
|
}
|
|
72760
73075
|
function isLeaseOwner(value) {
|
|
72761
73076
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -72805,9 +73120,54 @@ function processIsAlive3(pid) {
|
|
|
72805
73120
|
throw leaseError("OpenMausBot could not verify the data-directory lease owner; refusing to start.", error51);
|
|
72806
73121
|
}
|
|
72807
73122
|
}
|
|
73123
|
+
function parseWmiDateTime(value) {
|
|
73124
|
+
const raw = String(value).trim();
|
|
73125
|
+
const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d{6})([+-]\d{3})$/.exec(raw);
|
|
73126
|
+
if (!match) return null;
|
|
73127
|
+
const [_3, year, month, day, hour, minute, second, micro, offset] = match;
|
|
73128
|
+
const ms2 = Date.UTC(
|
|
73129
|
+
Number(year),
|
|
73130
|
+
Number(month) - 1,
|
|
73131
|
+
Number(day),
|
|
73132
|
+
Number(hour),
|
|
73133
|
+
Number(minute),
|
|
73134
|
+
Number(second),
|
|
73135
|
+
Math.floor(Number(micro) / 1e3)
|
|
73136
|
+
);
|
|
73137
|
+
const offsetMinutes = Number(offset);
|
|
73138
|
+
return ms2 - offsetMinutes * 60 * 1e3;
|
|
73139
|
+
}
|
|
73140
|
+
function processCreationTimeMs(pid) {
|
|
73141
|
+
if (process.platform !== "win32") return null;
|
|
73142
|
+
try {
|
|
73143
|
+
const output = execFileSync(
|
|
73144
|
+
"wmic",
|
|
73145
|
+
["process", "where", `ProcessId=${pid}`, "get", "CreationDate", "/format:csv"],
|
|
73146
|
+
{ encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }
|
|
73147
|
+
);
|
|
73148
|
+
for (const line of output.split(/\r?\n/)) {
|
|
73149
|
+
const trimmed = line.trim();
|
|
73150
|
+
if (!trimmed) continue;
|
|
73151
|
+
const lower = trimmed.toLowerCase();
|
|
73152
|
+
if (lower === "node,creationdate" || lower.startsWith("node,")) continue;
|
|
73153
|
+
const parts = trimmed.split(",");
|
|
73154
|
+
const raw = parts[parts.length - 1].trim();
|
|
73155
|
+
const parsed = parseWmiDateTime(raw);
|
|
73156
|
+
if (parsed !== null) return parsed;
|
|
73157
|
+
}
|
|
73158
|
+
} catch {
|
|
73159
|
+
}
|
|
73160
|
+
return null;
|
|
73161
|
+
}
|
|
73162
|
+
function ownerIdentityMatches(owner2) {
|
|
73163
|
+
if (process.platform !== "win32") return true;
|
|
73164
|
+
const created = processCreationTimeMs(owner2.pid);
|
|
73165
|
+
if (created === null) return true;
|
|
73166
|
+
return created < owner2.createdAt;
|
|
73167
|
+
}
|
|
72808
73168
|
function unlinkExact(path, message4) {
|
|
72809
73169
|
try {
|
|
72810
|
-
|
|
73170
|
+
unlinkSync12(path);
|
|
72811
73171
|
} catch (error51) {
|
|
72812
73172
|
if (error51?.code !== "ENOENT") throw leaseError(message4, error51);
|
|
72813
73173
|
}
|
|
@@ -72902,10 +73262,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
72902
73262
|
} catch (error51) {
|
|
72903
73263
|
throw leaseError("OpenMausBot cannot create its data directory.", error51);
|
|
72904
73264
|
}
|
|
72905
|
-
return
|
|
73265
|
+
return join61(dataDir, LEASE_NAME);
|
|
72906
73266
|
}
|
|
72907
73267
|
function assertNoLiveDelegatedChild(dataDir) {
|
|
72908
|
-
const childLeasePath =
|
|
73268
|
+
const childLeasePath = join61(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
|
|
72909
73269
|
const child = readOwner(childLeasePath);
|
|
72910
73270
|
if (!child) return;
|
|
72911
73271
|
if (child.host !== hostname4()) {
|
|
@@ -72947,13 +73307,13 @@ function validateChildDelegation(dataDir, encoded) {
|
|
|
72947
73307
|
const capability = parseCapability(encoded);
|
|
72948
73308
|
const invalid = () => leaseError("The OpenMausBot desktop lease delegation is invalid; refusing to start to protect its state.");
|
|
72949
73309
|
if (!capability) throw invalid();
|
|
72950
|
-
const parentLeasePath =
|
|
73310
|
+
const parentLeasePath = join61(dataDir, LEASE_NAME);
|
|
72951
73311
|
const matchesLiveParent = (owner2) => Boolean(owner2 && owner2.pid === capability.pid && owner2.token === capability.token && owner2.host === hostname4() && ownerIsAlive(owner2));
|
|
72952
73312
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
72953
73313
|
throw invalid();
|
|
72954
73314
|
}
|
|
72955
73315
|
const childLease = acquireDataDirLeaseInternal(
|
|
72956
|
-
|
|
73316
|
+
join61(dataDir, DELEGATED_CHILD_DIR),
|
|
72957
73317
|
{ guardDelegatedChild: false }
|
|
72958
73318
|
);
|
|
72959
73319
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
@@ -73031,7 +73391,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
|
|
|
73031
73391
|
throw leaseError("OpenMausBot will not release a data-directory lease owned by another process.");
|
|
73032
73392
|
}
|
|
73033
73393
|
try {
|
|
73034
|
-
|
|
73394
|
+
unlinkSync12(leasePath);
|
|
73035
73395
|
} catch (error51) {
|
|
73036
73396
|
throw leaseError("OpenMausBot could not release its data-directory lease.", error51);
|
|
73037
73397
|
}
|
|
@@ -73091,8 +73451,8 @@ var WorkspaceBackupMaintenance = class {
|
|
|
73091
73451
|
|
|
73092
73452
|
// server/workspace-backup-http.ts
|
|
73093
73453
|
import { randomUUID as randomUUID26 } from "node:crypto";
|
|
73094
|
-
import { createReadStream as createReadStream4, createWriteStream as createWriteStream3, existsSync as existsSync29, lstatSync as lstatSync9, mkdirSync as mkdirSync32, readdirSync as readdirSync11, statSync as statSync12, unlinkSync as
|
|
73095
|
-
import { join as
|
|
73454
|
+
import { createReadStream as createReadStream4, createWriteStream as createWriteStream3, existsSync as existsSync29, lstatSync as lstatSync9, mkdirSync as mkdirSync32, readdirSync as readdirSync11, statSync as statSync12, unlinkSync as unlinkSync13 } from "node:fs";
|
|
73455
|
+
import { join as join63 } from "node:path";
|
|
73096
73456
|
import { Transform as Transform2 } from "node:stream";
|
|
73097
73457
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
73098
73458
|
|
|
@@ -73135,7 +73495,7 @@ import {
|
|
|
73135
73495
|
writeFileSync as writeFileSync19,
|
|
73136
73496
|
writeSync as writeSync2
|
|
73137
73497
|
} from "node:fs";
|
|
73138
|
-
import { isAbsolute as isAbsolute11, join as
|
|
73498
|
+
import { isAbsolute as isAbsolute11, join as join62, parse as parse6, posix as posix2, relative as relative5, resolve as resolve15, win32 as win322 } from "node:path";
|
|
73139
73499
|
import { homedir as homedir26 } from "node:os";
|
|
73140
73500
|
import { backup, DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
73141
73501
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -76194,13 +76554,13 @@ function backupRoot(dataDir) {
|
|
|
76194
76554
|
if (root === parse6(root).root) throw new Error("A filesystem root cannot be a workspace backup target.");
|
|
76195
76555
|
const stat6 = lstatSync8(root);
|
|
76196
76556
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("The workspace data directory must not be a symbolic link.");
|
|
76197
|
-
const path =
|
|
76557
|
+
const path = join62(root, ".backups");
|
|
76198
76558
|
folder2(path);
|
|
76199
76559
|
return path;
|
|
76200
76560
|
}
|
|
76201
76561
|
function jobPath(dataDir, id) {
|
|
76202
76562
|
if (!ID.test(id)) throw new Error("Invalid workspace backup identifier.");
|
|
76203
|
-
const path =
|
|
76563
|
+
const path = join62(backupRoot(dataDir), id);
|
|
76204
76564
|
if (entryExists(path)) {
|
|
76205
76565
|
const stat6 = lstatSync8(path);
|
|
76206
76566
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("Unsafe workspace backup job directory.");
|
|
@@ -76247,14 +76607,14 @@ function privateMetadata(manifest) {
|
|
|
76247
76607
|
return { summary: manifest.summary, clientState: manifest.clientState };
|
|
76248
76608
|
}
|
|
76249
76609
|
function assertLocalAuthOutsideSnapshot(dataDir) {
|
|
76250
|
-
const configPath =
|
|
76610
|
+
const configPath = join62(dataDir, "config.json");
|
|
76251
76611
|
const config2 = existsSync28(configPath) ? privateJson(configPath) : {};
|
|
76252
76612
|
const instances = record2(config2) && record2(config2.instances) ? config2.instances : {};
|
|
76253
76613
|
const fields = ["HOME", "USERPROFILE", "CLAUDE_CONFIG_DIR", "CODEX_HOME", "GROK_HOME", "KIMI_CODE_HOME", "HERMES_HOME", "FACTORY_HOME_OVERRIDE", "XDG_CONFIG_HOME", "XDG_DATA_HOME", "LOCALAPPDATA"];
|
|
76254
76614
|
const check2 = (value, field, home) => {
|
|
76255
76615
|
if (typeof value !== "string" || !value.trim()) return;
|
|
76256
76616
|
const text8 = value.trim();
|
|
76257
|
-
const expanded = text8.startsWith("~/") || text8.startsWith("~\\") ?
|
|
76617
|
+
const expanded = text8.startsWith("~/") || text8.startsWith("~\\") ? join62(home, text8.slice(2)) : text8;
|
|
76258
76618
|
const resolved = resolve15(expanded);
|
|
76259
76619
|
const exists = entryExists(resolved);
|
|
76260
76620
|
const path = relative5(exists ? realpathSync10(dataDir) : resolve15(dataDir), exists ? realpathSync10(resolved) : resolved).replaceAll("\\", "/");
|
|
@@ -76364,27 +76724,27 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76364
76724
|
const salt = randomBytes6(16);
|
|
76365
76725
|
const key3 = await passwordKey(options.password, salt);
|
|
76366
76726
|
const job = newJob(dataDir);
|
|
76367
|
-
const snapshot2 =
|
|
76368
|
-
folder2(
|
|
76727
|
+
const snapshot2 = join62(job.directory, "snapshot");
|
|
76728
|
+
folder2(join62(snapshot2, "data"));
|
|
76369
76729
|
const entries = [];
|
|
76370
76730
|
const warnings = ["Stop external editors and managed desktops before exporting; files written outside this server cannot be frozen by the backup gate.", "Conversation text and user files are not redacted and may contain secrets you pasted. Keep the encrypted backup private.", RESTORE_WARNING];
|
|
76371
76731
|
let bytes = 0;
|
|
76372
76732
|
let skippedLinks = 0;
|
|
76373
76733
|
try {
|
|
76374
76734
|
const root = realpathSync10(dataDir);
|
|
76375
|
-
const sourceDb =
|
|
76735
|
+
const sourceDb = join62(root, "messages.db");
|
|
76376
76736
|
if (existsSync28(sourceDb)) {
|
|
76377
76737
|
if (lstatSync8(sourceDb).isSymbolicLink()) throw new Error("The message database must not be a symbolic link.");
|
|
76378
76738
|
const db2 = new DatabaseSync2(sourceDb, { readOnly: true });
|
|
76379
76739
|
const completionPulse = setInterval(() => {
|
|
76380
76740
|
}, 25);
|
|
76381
76741
|
try {
|
|
76382
|
-
await backup(db2,
|
|
76742
|
+
await backup(db2, join62(snapshot2, "messages-snapshot.db"));
|
|
76383
76743
|
} finally {
|
|
76384
76744
|
clearInterval(completionPulse);
|
|
76385
76745
|
db2.close();
|
|
76386
76746
|
}
|
|
76387
|
-
const snapshotDb = new DatabaseSync2(
|
|
76747
|
+
const snapshotDb = new DatabaseSync2(join62(snapshot2, "messages-snapshot.db"));
|
|
76388
76748
|
try {
|
|
76389
76749
|
snapshotDb.exec("PRAGMA journal_mode = DELETE");
|
|
76390
76750
|
} finally {
|
|
@@ -76398,7 +76758,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76398
76758
|
if (excludedWorkspaceAuthPath(path2)) continue;
|
|
76399
76759
|
if (forbiddenArchivePath(path2)) throw new Error("A workspace filename conflicts with a protected authentication or runtime path.");
|
|
76400
76760
|
if (!validRelative(path2)) throw new Error("A workspace filename cannot be safely restored on supported platforms.");
|
|
76401
|
-
const source =
|
|
76761
|
+
const source = join62(directory, name2);
|
|
76402
76762
|
const stat6 = lstatSync8(source);
|
|
76403
76763
|
if (stat6.isSymbolicLink()) {
|
|
76404
76764
|
let target;
|
|
@@ -76418,7 +76778,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76418
76778
|
continue;
|
|
76419
76779
|
}
|
|
76420
76780
|
if (entries.length >= MAX_WORKSPACE_BACKUP_FILES) throw new Error("The workspace exceeds the 100,000-entry backup limit.");
|
|
76421
|
-
const destination =
|
|
76781
|
+
const destination = join62(snapshot2, "data", path2);
|
|
76422
76782
|
if (stat6.isDirectory()) {
|
|
76423
76783
|
folder2(destination);
|
|
76424
76784
|
entries.push({ path: path2, type: "directory", size: 0, mode: 448 });
|
|
@@ -76436,7 +76796,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76436
76796
|
writeJson(destination, value);
|
|
76437
76797
|
}
|
|
76438
76798
|
copied = { size: lstatSync8(destination).size, mode: 384, sha256: hashFile(destination) };
|
|
76439
|
-
} else copied = copyRegular(path2 === "messages.db" ?
|
|
76799
|
+
} else copied = copyRegular(path2 === "messages.db" ? join62(snapshot2, "messages-snapshot.db") : source, destination);
|
|
76440
76800
|
bytes += copied.size;
|
|
76441
76801
|
if (bytes > MAX_WORKSPACE_BACKUP_BYTES) throw new Error("The workspace exceeds the 10 GB backup limit.");
|
|
76442
76802
|
entries.push({ path: path2, type: "file", ...copied });
|
|
@@ -76454,9 +76814,9 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76454
76814
|
files: entries.filter((entry) => entry.type === "file").length,
|
|
76455
76815
|
directories: entries.filter((entry) => entry.type === "directory").length,
|
|
76456
76816
|
bytes,
|
|
76457
|
-
bots: countJsonArray(
|
|
76458
|
-
groups: countJsonArray(
|
|
76459
|
-
...databaseCounts(
|
|
76817
|
+
bots: countJsonArray(join62(snapshot2, "data", "bots.json")),
|
|
76818
|
+
groups: countJsonArray(join62(snapshot2, "data", "groups.json")),
|
|
76819
|
+
...databaseCounts(join62(snapshot2, "data", "messages.db")),
|
|
76460
76820
|
exclusions: EXCLUSION_NOTES,
|
|
76461
76821
|
warnings
|
|
76462
76822
|
};
|
|
@@ -76466,9 +76826,9 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76466
76826
|
clientState: options.clientState ?? {},
|
|
76467
76827
|
entries
|
|
76468
76828
|
};
|
|
76469
|
-
writeJson(
|
|
76470
|
-
validateManifest(privateJson(
|
|
76471
|
-
const path =
|
|
76829
|
+
writeJson(join62(snapshot2, "manifest.json"), manifest);
|
|
76830
|
+
validateManifest(privateJson(join62(snapshot2, "manifest.json")));
|
|
76831
|
+
const path = join62(job.directory, "workspace.ombbackup");
|
|
76472
76832
|
const iv = randomBytes6(12);
|
|
76473
76833
|
const header2 = Buffer.concat([MAGIC, salt, iv]);
|
|
76474
76834
|
const cipher = createCipheriv("aes-256-gcm", key3, iv);
|
|
@@ -76583,21 +76943,21 @@ async function inspectTar(path) {
|
|
|
76583
76943
|
return entries;
|
|
76584
76944
|
}
|
|
76585
76945
|
function validateStaged(directory, expected) {
|
|
76586
|
-
const manifest = validateManifest(privateJson(
|
|
76946
|
+
const manifest = validateManifest(privateJson(join62(directory, "manifest.json")));
|
|
76587
76947
|
if (expected && expected.size !== manifest.entries.length + 2) throw new Error("Archive entries do not match the backup manifest.");
|
|
76588
76948
|
const declared = new Set(manifest.entries.map((entry) => entry.path));
|
|
76589
76949
|
const inspect = (root, prefix = "") => {
|
|
76590
76950
|
for (const name2 of readdirSync10(root)) {
|
|
76591
76951
|
const path = prefix ? `${prefix}/${name2}` : name2;
|
|
76592
76952
|
if (!declared.has(path)) throw new Error("Staged workspace contains files absent from its manifest.");
|
|
76593
|
-
const stat6 = lstatSync8(
|
|
76953
|
+
const stat6 = lstatSync8(join62(root, name2));
|
|
76594
76954
|
if (stat6.isSymbolicLink()) throw new Error("Staged workspace contains a symbolic link.");
|
|
76595
|
-
if (stat6.isDirectory()) inspect(
|
|
76955
|
+
if (stat6.isDirectory()) inspect(join62(root, name2), path);
|
|
76596
76956
|
}
|
|
76597
76957
|
};
|
|
76598
|
-
inspect(
|
|
76958
|
+
inspect(join62(directory, "data"));
|
|
76599
76959
|
for (const entry of manifest.entries) {
|
|
76600
|
-
const path =
|
|
76960
|
+
const path = join62(directory, "data", entry.path);
|
|
76601
76961
|
const stat6 = lstatSync8(path);
|
|
76602
76962
|
if (stat6.isSymbolicLink() || (entry.type === "file" ? !stat6.isFile() || stat6.size !== entry.size : !stat6.isDirectory())) {
|
|
76603
76963
|
throw new Error("Staged workspace files do not match the backup manifest.");
|
|
@@ -76609,27 +76969,27 @@ function validateStaged(directory, expected) {
|
|
|
76609
76969
|
if (entry.type === "file" && hashFile(path) !== entry.sha256) throw new Error("A workspace backup file failed its checksum.");
|
|
76610
76970
|
chmodSync7(path, entry.mode);
|
|
76611
76971
|
}
|
|
76612
|
-
const data =
|
|
76613
|
-
if (countJsonArray(
|
|
76614
|
-
if (existsSync28(
|
|
76615
|
-
const config2 = privateJson(
|
|
76972
|
+
const data = join62(directory, "data");
|
|
76973
|
+
if (countJsonArray(join62(data, "bots.json")) !== manifest.summary.bots || countJsonArray(join62(data, "groups.json")) !== manifest.summary.groups) throw new Error("The backup roster does not match its summary.");
|
|
76974
|
+
if (existsSync28(join62(data, "config.json"))) {
|
|
76975
|
+
const config2 = privateJson(join62(data, "config.json"));
|
|
76616
76976
|
if (JSON.stringify(config2) !== JSON.stringify(portableWorkspaceConfig(config2))) throw new Error("Workspace backup contains non-portable connection settings.");
|
|
76617
76977
|
}
|
|
76618
|
-
if (existsSync28(
|
|
76619
|
-
const value = privateJson(
|
|
76978
|
+
if (existsSync28(join62(data, "webhooks.json"))) {
|
|
76979
|
+
const value = privateJson(join62(data, "webhooks.json"));
|
|
76620
76980
|
if (!record2(value) || Array.isArray(value.webhooks) && value.webhooks.some((webhook) => record2(webhook) && Object.hasOwn(webhook, "secretHash"))) throw new Error("Workspace backup contains webhook credentials.");
|
|
76621
76981
|
}
|
|
76622
|
-
const counts = databaseCounts(
|
|
76982
|
+
const counts = databaseCounts(join62(data, "messages.db"));
|
|
76623
76983
|
if (counts.messages !== manifest.summary.messages || counts.threads !== manifest.summary.threads) throw new Error("The message database does not match the backup summary.");
|
|
76624
76984
|
return manifest;
|
|
76625
76985
|
}
|
|
76626
76986
|
async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
76627
76987
|
const job = newJob(dataDir);
|
|
76628
|
-
const plaintext =
|
|
76988
|
+
const plaintext = join62(job.directory, "archive.tar");
|
|
76629
76989
|
try {
|
|
76630
76990
|
await decryptArchive(archivePath, plaintext, options.password);
|
|
76631
76991
|
const expected = await inspectTar(plaintext);
|
|
76632
|
-
const staged =
|
|
76992
|
+
const staged = join62(job.directory, "staged");
|
|
76633
76993
|
folder2(staged);
|
|
76634
76994
|
await So({ file: plaintext, cwd: staged, strict: true, preservePaths: false, umask: 63, noChmod: true });
|
|
76635
76995
|
const manifest = validateStaged(staged, expected);
|
|
@@ -76641,7 +77001,7 @@ async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
76641
77001
|
}
|
|
76642
77002
|
}
|
|
76643
77003
|
rmSync8(plaintext);
|
|
76644
|
-
writeJson(
|
|
77004
|
+
writeJson(join62(job.directory, "ready.json"), { id: job.id, summary: manifest.summary });
|
|
76645
77005
|
return { id: job.id, summary: manifest.summary };
|
|
76646
77006
|
} catch (error51) {
|
|
76647
77007
|
rmSync8(job.directory, { recursive: true, force: true });
|
|
@@ -76650,24 +77010,24 @@ async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
76650
77010
|
}
|
|
76651
77011
|
function readStagedWorkspaceBackup(dataDir, id) {
|
|
76652
77012
|
const directory = jobPath(dataDir, id);
|
|
76653
|
-
const ready = privateJson(
|
|
77013
|
+
const ready = privateJson(join62(directory, "ready.json"));
|
|
76654
77014
|
if (!record2(ready) || ready.id !== id) throw new Error("This workspace backup is not ready to restore.");
|
|
76655
|
-
const manifest = validateManifest(privateJson(
|
|
77015
|
+
const manifest = validateManifest(privateJson(join62(directory, "staged", "manifest.json")));
|
|
76656
77016
|
return { id, ...privateMetadata(manifest) };
|
|
76657
77017
|
}
|
|
76658
77018
|
function commitPendingWorkspaceRestore(dataDir, id) {
|
|
76659
77019
|
const root = backupRoot(dataDir);
|
|
76660
|
-
if (existsSync28(
|
|
76661
|
-
if (existsSync28(
|
|
77020
|
+
if (existsSync28(join62(root, "pending-restore.json"))) throw new Error("A workspace restore is already waiting for restart.");
|
|
77021
|
+
if (existsSync28(join62(root, `safety-${id}`))) throw new Error("This restore was already attempted. Preview the backup again to create a fresh restore attempt.");
|
|
76662
77022
|
const metadata = readStagedWorkspaceBackup(dataDir, id);
|
|
76663
|
-
const manifest = validateStaged(
|
|
77023
|
+
const manifest = validateStaged(join62(jobPath(dataDir, id), "staged"));
|
|
76664
77024
|
prepareRestore(dataDir, id, manifest);
|
|
76665
|
-
writeJson(
|
|
77025
|
+
writeJson(join62(root, "pending-restore.json"), { id });
|
|
76666
77026
|
return { id, restartRequired: true, summary: metadata.summary };
|
|
76667
77027
|
}
|
|
76668
77028
|
function readPendingWorkspaceRestoreMetadata(dataDir) {
|
|
76669
77029
|
const root = backupRoot(dataDir);
|
|
76670
|
-
const path =
|
|
77030
|
+
const path = join62(root, "pending-restore.json");
|
|
76671
77031
|
if (!existsSync28(path)) return null;
|
|
76672
77032
|
const pending = privateJson(path);
|
|
76673
77033
|
if (!record2(pending) || typeof pending.id !== "string") throw new Error("Invalid pending workspace restore.");
|
|
@@ -76682,24 +77042,24 @@ function readJournal(path) {
|
|
|
76682
77042
|
return raw;
|
|
76683
77043
|
}
|
|
76684
77044
|
function rollback(dataDir, journal) {
|
|
76685
|
-
const stage =
|
|
76686
|
-
const saved =
|
|
77045
|
+
const stage = join62(jobPath(dataDir, journal.id), "apply", "data");
|
|
77046
|
+
const saved = join62(backupRoot(dataDir), `safety-${journal.id}`, "data");
|
|
76687
77047
|
for (const name2 of journal.incoming) {
|
|
76688
|
-
const live =
|
|
76689
|
-
const staged =
|
|
76690
|
-
const originalMoved = !journal.existing.includes(name2) || entryExists(
|
|
77048
|
+
const live = join62(dataDir, name2);
|
|
77049
|
+
const staged = join62(stage, name2);
|
|
77050
|
+
const originalMoved = !journal.existing.includes(name2) || entryExists(join62(saved, name2));
|
|
76691
77051
|
if (originalMoved && !entryExists(staged) && entryExists(live)) renameSync9(live, staged);
|
|
76692
77052
|
}
|
|
76693
77053
|
for (const name2 of journal.existing) {
|
|
76694
|
-
const original =
|
|
77054
|
+
const original = join62(saved, name2);
|
|
76695
77055
|
if (!entryExists(original)) continue;
|
|
76696
|
-
const live =
|
|
77056
|
+
const live = join62(dataDir, name2);
|
|
76697
77057
|
if (entryExists(live)) throw new Error("Restore rollback found conflicting files. Both copies are preserved for recovery.");
|
|
76698
77058
|
renameSync9(original, live);
|
|
76699
77059
|
}
|
|
76700
77060
|
const root = backupRoot(dataDir);
|
|
76701
|
-
const previousReceipt =
|
|
76702
|
-
const last =
|
|
77061
|
+
const previousReceipt = join62(root, `safety-${journal.id}`, "previous-restore.json");
|
|
77062
|
+
const last = join62(root, "last-restore.json");
|
|
76703
77063
|
if (existsSync28(previousReceipt)) writeJson(last, privateJson(previousReceipt));
|
|
76704
77064
|
else if (existsSync28(last)) {
|
|
76705
77065
|
const value = privateJson(last);
|
|
@@ -76712,7 +77072,7 @@ function rebasePath(value, source, destination) {
|
|
|
76712
77072
|
if (normalized !== original && !normalized.startsWith(`${original}/`)) return value;
|
|
76713
77073
|
const suffix = normalized.slice(original.length).replace(/^\//, "");
|
|
76714
77074
|
if (suffix && !validRelative(suffix)) return value;
|
|
76715
|
-
return suffix ?
|
|
77075
|
+
return suffix ? join62(destination, suffix) : destination;
|
|
76716
77076
|
}
|
|
76717
77077
|
var PATH_KEYS = /* @__PURE__ */ new Set(["cwd", "pinnedCwd", "workspace", "configDir", "profileDirectory", "agentDir", "dataDir", "home", "cli", "path", "filePath", "localPath"]);
|
|
76718
77078
|
function rebaseFields(value, source, destination) {
|
|
@@ -76751,16 +77111,16 @@ function rebaseMessage(message4, source, destination) {
|
|
|
76751
77111
|
function prepareRestore(dataDir, id, manifest) {
|
|
76752
77112
|
assertLocalAuthOutsideSnapshot(dataDir);
|
|
76753
77113
|
const job = jobPath(dataDir, id);
|
|
76754
|
-
const prepared =
|
|
76755
|
-
if (existsSync28(
|
|
77114
|
+
const prepared = join62(job, "apply", "data");
|
|
77115
|
+
if (existsSync28(join62(job, "apply"))) rmSync8(join62(job, "apply"), { recursive: true, force: true });
|
|
76756
77116
|
folder2(prepared);
|
|
76757
|
-
for (const entry of manifest.entries.filter((entry2) => entry2.type === "directory")) folder2(
|
|
77117
|
+
for (const entry of manifest.entries.filter((entry2) => entry2.type === "directory")) folder2(join62(prepared, entry.path));
|
|
76758
77118
|
for (const entry of manifest.entries) {
|
|
76759
|
-
const destination =
|
|
76760
|
-
if (entry.type === "file") copyRegular(
|
|
77119
|
+
const destination = join62(prepared, entry.path);
|
|
77120
|
+
if (entry.type === "file") copyRegular(join62(job, "staged", "data", entry.path), destination);
|
|
76761
77121
|
}
|
|
76762
77122
|
const changeJson = (name2, change) => {
|
|
76763
|
-
const path =
|
|
77123
|
+
const path = join62(prepared, name2);
|
|
76764
77124
|
if (!existsSync28(path)) return;
|
|
76765
77125
|
const value = privateJson(path);
|
|
76766
77126
|
change(value);
|
|
@@ -76773,25 +77133,25 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76773
77133
|
if (!Array.isArray(value)) throw new Error("Invalid bot definitions in workspace backup.");
|
|
76774
77134
|
for (const bot of value) if (record2(bot)) delete bot.voice;
|
|
76775
77135
|
});
|
|
76776
|
-
const oldConfig =
|
|
76777
|
-
const newConfig =
|
|
77136
|
+
const oldConfig = join62(dataDir, "config.json");
|
|
77137
|
+
const newConfig = join62(prepared, "config.json");
|
|
76778
77138
|
if (existsSync28(oldConfig) || existsSync28(newConfig)) {
|
|
76779
77139
|
writeJson(newConfig, restoredWorkspaceConfig(existsSync28(newConfig) ? privateJson(newConfig) : {}, existsSync28(oldConfig) ? privateJson(oldConfig) : {}));
|
|
76780
77140
|
}
|
|
76781
77141
|
const browserHomes = ["vm-home"];
|
|
76782
|
-
const vmHomes =
|
|
77142
|
+
const vmHomes = join62(dataDir, "vm-homes");
|
|
76783
77143
|
if (entryExists(vmHomes)) {
|
|
76784
77144
|
if (!lstatSync8(vmHomes).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
76785
77145
|
for (const name2 of readdirSync10(vmHomes)) if (validRelative(name2)) browserHomes.push(`vm-homes/${name2}`);
|
|
76786
77146
|
}
|
|
76787
77147
|
for (const home of browserHomes) {
|
|
76788
|
-
const homePath =
|
|
77148
|
+
const homePath = join62(dataDir, home);
|
|
76789
77149
|
if (!entryExists(homePath)) continue;
|
|
76790
77150
|
if (!lstatSync8(homePath).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
76791
|
-
const source =
|
|
77151
|
+
const source = join62(dataDir, home, ".browser-profiles");
|
|
76792
77152
|
if (!entryExists(source)) continue;
|
|
76793
|
-
folder2(
|
|
76794
|
-
cpSync(source,
|
|
77153
|
+
folder2(join62(prepared, home));
|
|
77154
|
+
cpSync(source, join62(prepared, home, ".browser-profiles"), { recursive: true, dereference: false, verbatimSymlinks: true, errorOnExist: true, force: false });
|
|
76795
77155
|
}
|
|
76796
77156
|
changeJson("routines.json", (value) => {
|
|
76797
77157
|
if (!record2(value)) throw new Error("Invalid routine definitions in workspace backup.");
|
|
@@ -76809,7 +77169,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76809
77169
|
});
|
|
76810
77170
|
changeJson("webhooks.json", (value) => {
|
|
76811
77171
|
if (!record2(value)) throw new Error("Invalid webhook definitions in workspace backup.");
|
|
76812
|
-
const destination = existsSync28(
|
|
77172
|
+
const destination = existsSync28(join62(dataDir, "webhooks.json")) ? privateJson(join62(dataDir, "webhooks.json")) : {};
|
|
76813
77173
|
const oldHooks = record2(destination) && Array.isArray(destination.webhooks) ? destination.webhooks : [];
|
|
76814
77174
|
if (Array.isArray(value.webhooks)) {
|
|
76815
77175
|
for (const webhook of value.webhooks) if (record2(webhook)) {
|
|
@@ -76826,15 +77186,15 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76826
77186
|
for (const call of value.calls) if (record2(call)) call.nextRunAt = null;
|
|
76827
77187
|
}
|
|
76828
77188
|
});
|
|
76829
|
-
if (existsSync28(
|
|
76830
|
-
if (existsSync28(
|
|
77189
|
+
if (existsSync28(join62(prepared, "delegations.json"))) writeJson(join62(prepared, "delegations.json"), {});
|
|
77190
|
+
if (existsSync28(join62(prepared, "browser-cleanups.json"))) writeJson(join62(prepared, "browser-cleanups.json"), []);
|
|
76831
77191
|
for (const entry of manifest.entries) {
|
|
76832
77192
|
if (/^messages-[^/]+\.json$/.test(entry.path)) changeJson(entry.path, (value) => {
|
|
76833
77193
|
if (Array.isArray(value)) for (const message4 of value) rebaseMessage(message4, manifest.sourceDataDir, resolve15(dataDir));
|
|
76834
77194
|
else if (record2(value) && Array.isArray(value.messages)) for (const message4 of value.messages) rebaseMessage(message4, manifest.sourceDataDir, resolve15(dataDir));
|
|
76835
77195
|
});
|
|
76836
77196
|
}
|
|
76837
|
-
const dbPath =
|
|
77197
|
+
const dbPath = join62(prepared, "messages.db");
|
|
76838
77198
|
if (existsSync28(dbPath)) {
|
|
76839
77199
|
const db2 = new DatabaseSync2(dbPath);
|
|
76840
77200
|
try {
|
|
@@ -76866,7 +77226,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76866
77226
|
}
|
|
76867
77227
|
function readLastWorkspaceRestore(dataDir) {
|
|
76868
77228
|
const root = backupRoot(dataDir);
|
|
76869
|
-
const path =
|
|
77229
|
+
const path = join62(root, "last-restore.json");
|
|
76870
77230
|
if (!existsSync28(path)) return null;
|
|
76871
77231
|
const raw = privateJson(path);
|
|
76872
77232
|
if (!record2(raw) || raw.restored !== true || typeof raw.id !== "string" || !ID.test(raw.id)) throw new Error("Invalid workspace restore receipt.");
|
|
@@ -76879,28 +77239,28 @@ function finishRestore(dataDir, id, consumePending = true) {
|
|
|
76879
77239
|
if (draftAttachments) {
|
|
76880
77240
|
try {
|
|
76881
77241
|
const value = JSON.parse(draftAttachments);
|
|
76882
|
-
const manifest = validateManifest(privateJson(
|
|
77242
|
+
const manifest = validateManifest(privateJson(join62(jobPath(dataDir, id), "staged", "manifest.json")));
|
|
76883
77243
|
rebaseFields(value, manifest.sourceDataDir, resolve15(dataDir));
|
|
76884
77244
|
clientState["omb-draft-attachments"] = JSON.stringify(value);
|
|
76885
77245
|
} catch {
|
|
76886
77246
|
}
|
|
76887
77247
|
}
|
|
76888
|
-
const result = { restored: true, id, safetyCopyPath:
|
|
76889
|
-
writeJson(
|
|
76890
|
-
if (consumePending) rmSync8(
|
|
77248
|
+
const result = { restored: true, id, safetyCopyPath: join62(root, `safety-${id}`), summary, clientState };
|
|
77249
|
+
writeJson(join62(root, "last-restore.json"), result);
|
|
77250
|
+
if (consumePending) rmSync8(join62(root, "pending-restore.json"), { force: true });
|
|
76891
77251
|
return result;
|
|
76892
77252
|
}
|
|
76893
77253
|
function applyPendingWorkspaceRestore(dataDir) {
|
|
76894
77254
|
const root = backupRoot(dataDir);
|
|
76895
|
-
const journalFile2 =
|
|
76896
|
-
const pendingFile =
|
|
77255
|
+
const journalFile2 = join62(root, "restore-journal.json");
|
|
77256
|
+
const pendingFile = join62(root, "pending-restore.json");
|
|
76897
77257
|
if (existsSync28(journalFile2)) {
|
|
76898
77258
|
const previous2 = readJournal(journalFile2);
|
|
76899
77259
|
if (previous2.phase === "applying") {
|
|
76900
77260
|
rollback(dataDir, previous2);
|
|
76901
77261
|
writeJson(journalFile2, { ...previous2, phase: "rolled-back" });
|
|
76902
77262
|
rmSync8(pendingFile, { force: true });
|
|
76903
|
-
return { restored: false, rolledBack: true, id: previous2.id, safetyCopyPath:
|
|
77263
|
+
return { restored: false, rolledBack: true, id: previous2.id, safetyCopyPath: join62(root, `safety-${previous2.id}`) };
|
|
76904
77264
|
}
|
|
76905
77265
|
if (previous2.phase === "committed" && existsSync28(pendingFile)) {
|
|
76906
77266
|
const pending2 = privateJson(pendingFile);
|
|
@@ -76911,12 +77271,12 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
76911
77271
|
}
|
|
76912
77272
|
const pending = readPendingWorkspaceRestoreMetadata(dataDir);
|
|
76913
77273
|
if (!pending) return { restored: false };
|
|
76914
|
-
const staged =
|
|
77274
|
+
const staged = join62(jobPath(dataDir, pending.id), "staged");
|
|
76915
77275
|
const manifest = validateStaged(staged);
|
|
76916
77276
|
const prepared = prepareRestore(dataDir, pending.id, manifest);
|
|
76917
|
-
const safetyCopyPath =
|
|
77277
|
+
const safetyCopyPath = join62(root, `safety-${pending.id}`);
|
|
76918
77278
|
if (existsSync28(safetyCopyPath)) throw new Error("A safety copy already exists for this restore. Original files have not been changed.");
|
|
76919
|
-
folder2(
|
|
77279
|
+
folder2(join62(safetyCopyPath, "data"));
|
|
76920
77280
|
const journal = {
|
|
76921
77281
|
id: pending.id,
|
|
76922
77282
|
phase: "applying",
|
|
@@ -76926,12 +77286,12 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
76926
77286
|
for (const name2 of [...journal.existing, ...journal.incoming]) {
|
|
76927
77287
|
if (!validRelative(name2) || name2.includes("/") || preserved(name2)) throw new Error("Unsafe workspace restore target.");
|
|
76928
77288
|
}
|
|
76929
|
-
writeJson(
|
|
76930
|
-
if (existsSync28(
|
|
77289
|
+
writeJson(join62(safetyCopyPath, "manifest.json"), { id: pending.id, createdAt: (/* @__PURE__ */ new Date()).toISOString(), originalEntries: journal.existing });
|
|
77290
|
+
if (existsSync28(join62(root, "last-restore.json"))) writeJson(join62(safetyCopyPath, "previous-restore.json"), privateJson(join62(root, "last-restore.json")));
|
|
76931
77291
|
writeJson(journalFile2, journal);
|
|
76932
77292
|
try {
|
|
76933
|
-
for (const name2 of journal.existing) renameSync9(
|
|
76934
|
-
for (const name2 of journal.incoming) if (entryExists(
|
|
77293
|
+
for (const name2 of journal.existing) renameSync9(join62(dataDir, name2), join62(safetyCopyPath, "data", name2));
|
|
77294
|
+
for (const name2 of journal.incoming) if (entryExists(join62(prepared, name2))) renameSync9(join62(prepared, name2), join62(dataDir, name2));
|
|
76935
77295
|
finishRestore(dataDir, pending.id, false);
|
|
76936
77296
|
writeJson(journalFile2, { ...journal, phase: "committed" });
|
|
76937
77297
|
} catch (error51) {
|
|
@@ -76946,11 +77306,11 @@ function removeWorkspaceBackupJob(dataDir, id) {
|
|
|
76946
77306
|
const path = jobPath(dataDir, id);
|
|
76947
77307
|
const root = backupRoot(dataDir);
|
|
76948
77308
|
for (const name2 of ["pending-restore.json", "restore-journal.json", "last-restore.json"]) {
|
|
76949
|
-
if (!existsSync28(
|
|
76950
|
-
const value = privateJson(
|
|
77309
|
+
if (!existsSync28(join62(root, name2))) continue;
|
|
77310
|
+
const value = privateJson(join62(root, name2));
|
|
76951
77311
|
if (record2(value) && value.id === id) throw new Error("This backup is still needed for restore or recovery and cannot be removed.");
|
|
76952
77312
|
}
|
|
76953
|
-
if (existsSync28(
|
|
77313
|
+
if (existsSync28(join62(root, `safety-${id}`))) throw new Error("This backup has a recovery copy and cannot be removed.");
|
|
76954
77314
|
if (!entryExists(path)) return;
|
|
76955
77315
|
if (!lstatSync8(path).isDirectory() || lstatSync8(path).isSymbolicLink()) throw new Error("Unsafe workspace backup cleanup target.");
|
|
76956
77316
|
rmSync8(path, { recursive: true, force: true });
|
|
@@ -76980,7 +77340,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
76980
77340
|
function remove(id, artifact) {
|
|
76981
77341
|
if (artifact.path) {
|
|
76982
77342
|
try {
|
|
76983
|
-
|
|
77343
|
+
unlinkSync13(artifact.path);
|
|
76984
77344
|
} catch {
|
|
76985
77345
|
}
|
|
76986
77346
|
}
|
|
@@ -76993,24 +77353,24 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
76993
77353
|
artifacts.delete(id);
|
|
76994
77354
|
}
|
|
76995
77355
|
function reclaimAbandoned() {
|
|
76996
|
-
const root =
|
|
77356
|
+
const root = join63(options.dataDir, ".backups");
|
|
76997
77357
|
if (!existsSync29(root) || lstatSync9(root).isSymbolicLink()) return;
|
|
76998
77358
|
for (const entry of readdirSync11(root, { withFileTypes: true })) {
|
|
76999
77359
|
if (!entry.isDirectory() || !/^[a-f0-9-]{36}$/.test(entry.name)) continue;
|
|
77000
|
-
if (statSync12(
|
|
77360
|
+
if (statSync12(join63(root, entry.name)).mtimeMs + EXPIRES_MS > Date.now()) continue;
|
|
77001
77361
|
try {
|
|
77002
77362
|
removeWorkspaceBackupJob(options.dataDir, entry.name);
|
|
77003
77363
|
} catch {
|
|
77004
77364
|
}
|
|
77005
77365
|
}
|
|
77006
|
-
const uploads =
|
|
77366
|
+
const uploads = join63(root, "uploads");
|
|
77007
77367
|
if (!existsSync29(uploads) || lstatSync9(uploads).isSymbolicLink()) return;
|
|
77008
77368
|
for (const entry of readdirSync11(uploads, { withFileTypes: true })) {
|
|
77009
77369
|
if (!entry.isFile() || !/^[a-f0-9-]{36}\.ombbackup$/.test(entry.name)) continue;
|
|
77010
|
-
const file2 =
|
|
77370
|
+
const file2 = join63(uploads, entry.name);
|
|
77011
77371
|
if (statSync12(file2).mtimeMs + EXPIRES_MS <= Date.now()) {
|
|
77012
77372
|
try {
|
|
77013
|
-
|
|
77373
|
+
unlinkSync13(file2);
|
|
77014
77374
|
} catch {
|
|
77015
77375
|
}
|
|
77016
77376
|
}
|
|
@@ -77107,14 +77467,14 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77107
77467
|
if (!Number.isSafeInteger(length) || length <= 0 || length > MAX_UPLOAD_BYTES) throw failure3("Select a backup containing no more than 10 GB of workspace data.", 413);
|
|
77108
77468
|
replacePrevious(auth, ["upload", "staged"]);
|
|
77109
77469
|
if (artifacts.size >= 4) throw failure3("There are already four backup files in progress. Restart or wait an hour before uploading another.", 409);
|
|
77110
|
-
const root =
|
|
77470
|
+
const root = join63(options.dataDir, ".backups");
|
|
77111
77471
|
mkdirSync32(root, { recursive: true, mode: 448 });
|
|
77112
77472
|
if (lstatSync9(root).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
77113
|
-
const directory =
|
|
77473
|
+
const directory = join63(root, "uploads");
|
|
77114
77474
|
mkdirSync32(directory, { recursive: true, mode: 448 });
|
|
77115
77475
|
if (lstatSync9(directory).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
77116
77476
|
const id = randomUUID26();
|
|
77117
|
-
const file2 =
|
|
77477
|
+
const file2 = join63(directory, `${id}.ombbackup`);
|
|
77118
77478
|
let bytes = 0;
|
|
77119
77479
|
try {
|
|
77120
77480
|
const limit = new Transform2({ transform(chunk, _encoding, callback) {
|
|
@@ -77128,7 +77488,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77128
77488
|
json4(res, 200, { id });
|
|
77129
77489
|
} catch (error51) {
|
|
77130
77490
|
try {
|
|
77131
|
-
|
|
77491
|
+
unlinkSync13(file2);
|
|
77132
77492
|
} catch {
|
|
77133
77493
|
}
|
|
77134
77494
|
throw error51;
|
|
@@ -77147,7 +77507,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77147
77507
|
throw error51;
|
|
77148
77508
|
}
|
|
77149
77509
|
try {
|
|
77150
|
-
|
|
77510
|
+
unlinkSync13(artifact.path);
|
|
77151
77511
|
} catch {
|
|
77152
77512
|
}
|
|
77153
77513
|
artifacts.delete(body.id);
|
|
@@ -78316,7 +78676,7 @@ function resolveRequestAuth(req, options) {
|
|
|
78316
78676
|
|
|
78317
78677
|
// server/sessions.ts
|
|
78318
78678
|
import { createHash as createHash29, randomBytes as randomBytes8, randomUUID as randomUUID27, timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
78319
|
-
import { closeSync as closeSync13, existsSync as existsSync30, fsyncSync as fsyncSync4, mkdirSync as mkdirSync33, openSync as openSync13, readFileSync as readFileSync43, unlinkSync as
|
|
78679
|
+
import { closeSync as closeSync13, existsSync as existsSync30, fsyncSync as fsyncSync4, mkdirSync as mkdirSync33, openSync as openSync13, readFileSync as readFileSync43, unlinkSync as unlinkSync14 } from "node:fs";
|
|
78320
78680
|
import { dirname as dirname18 } from "node:path";
|
|
78321
78681
|
var SCOPES = ["admin", "client"];
|
|
78322
78682
|
var PAIRING_CODE_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
@@ -78462,7 +78822,7 @@ var SessionRegistry = class {
|
|
|
78462
78822
|
try {
|
|
78463
78823
|
this.revalidateEmailSessions();
|
|
78464
78824
|
this.persist();
|
|
78465
|
-
|
|
78825
|
+
unlinkSync14(this.openMarker);
|
|
78466
78826
|
this.closed = true;
|
|
78467
78827
|
} catch {
|
|
78468
78828
|
throw new Error("Could not safely close session storage.");
|
|
@@ -78746,7 +79106,7 @@ var SessionRegistry = class {
|
|
|
78746
79106
|
|
|
78747
79107
|
// server/brand.ts
|
|
78748
79108
|
import { existsSync as existsSync31, readFileSync as readFileSync44 } from "node:fs";
|
|
78749
|
-
import { join as
|
|
79109
|
+
import { join as join64 } from "node:path";
|
|
78750
79110
|
var HEX_COLOUR = /^#[0-9a-fA-F]{6}$/;
|
|
78751
79111
|
var MAX_LOGO_CHARS = 3e5;
|
|
78752
79112
|
var MAX_FAVICON_CHARS = 12e4;
|
|
@@ -78766,7 +79126,7 @@ var brandSchema = external_exports.object({
|
|
|
78766
79126
|
}).strict();
|
|
78767
79127
|
var DEFAULT_BRAND = { name: "OpenMausBot" };
|
|
78768
79128
|
function brandFile() {
|
|
78769
|
-
return process.env.OMB_BRAND_FILE ||
|
|
79129
|
+
return process.env.OMB_BRAND_FILE || join64(DATA_DIR, "brand.json");
|
|
78770
79130
|
}
|
|
78771
79131
|
function loadBrand(options = {}) {
|
|
78772
79132
|
const file2 = options.file ?? brandFile();
|
|
@@ -81286,7 +81646,7 @@ function releaseDataDirLeaseAtExit() {
|
|
|
81286
81646
|
}
|
|
81287
81647
|
process.once("exit", releaseDataDirLeaseAtExit);
|
|
81288
81648
|
var workspaceRestore = { restored: false };
|
|
81289
|
-
if (existsSync32(
|
|
81649
|
+
if (existsSync32(join65(DATA_DIR, ".backups"))) {
|
|
81290
81650
|
workspaceRestore = applyPendingWorkspaceRestore(DATA_DIR);
|
|
81291
81651
|
if (!workspaceRestore.restored && !workspaceRestore.rolledBack) {
|
|
81292
81652
|
workspaceRestore = readLastWorkspaceRestore(DATA_DIR) ?? workspaceRestore;
|
|
@@ -81299,7 +81659,7 @@ function signInAllowList() {
|
|
|
81299
81659
|
return { admins: parseAllowList(current2?.admins?.join(",")), members: parseAllowList(current2?.members?.join(",")) };
|
|
81300
81660
|
}
|
|
81301
81661
|
var sessions = new SessionRegistry({
|
|
81302
|
-
file:
|
|
81662
|
+
file: join65(DATA_DIR, "sessions.json"),
|
|
81303
81663
|
emailScopesSnapshot: () => {
|
|
81304
81664
|
const membership = signInAllowList();
|
|
81305
81665
|
return (email3) => allowedScopes(email3, membership);
|
|
@@ -81355,7 +81715,7 @@ function noteTurnTrigger(threadId, auth) {
|
|
|
81355
81715
|
var providerAuthSessions = new ProviderAuthSessions();
|
|
81356
81716
|
await registry2.load(instanceConfigs(cfg));
|
|
81357
81717
|
var bundledSkills = loadBundledSkills();
|
|
81358
|
-
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(
|
|
81718
|
+
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(join65(DATA_DIR, "skills")));
|
|
81359
81719
|
var utilityParentPort = process.parentPort;
|
|
81360
81720
|
function postDesktopPrivateMessage(message4) {
|
|
81361
81721
|
if (!utilityParentPort) return false;
|
|
@@ -81381,7 +81741,7 @@ function applyDesktopMutationTokenMessage(raw) {
|
|
|
81381
81741
|
return true;
|
|
81382
81742
|
}
|
|
81383
81743
|
var browserCleanup = new BrowserCleanupCoordinator({
|
|
81384
|
-
file:
|
|
81744
|
+
file: join65(DATA_DIR, "browser-cleanups.json"),
|
|
81385
81745
|
send: (request3) => {
|
|
81386
81746
|
const status = browserEngineStatus();
|
|
81387
81747
|
const sessions2 = request3.type === "openmausbot:browser-bot-deleted" && request3.botId ? [browserSessionId(request3.botId, "")] : request3.partitionId ? [browserSessionId("", request3.partitionId)] : [];
|
|
@@ -81617,6 +81977,7 @@ function claimTurnResource(owner2, resource) {
|
|
|
81617
81977
|
}
|
|
81618
81978
|
function releaseTurnResources(owner2) {
|
|
81619
81979
|
if (!owner2) return;
|
|
81980
|
+
if (autoVmClaims.get(owner2.threadId)?.owner.generation === owner2.generation) autoVmClaims.delete(owner2.threadId);
|
|
81620
81981
|
if (settlingResourceOwners.get(owner2.threadId) === owner2.generation) settlingResourceOwners.delete(owner2.threadId);
|
|
81621
81982
|
turnResources.release(owner2);
|
|
81622
81983
|
if (turnResourceOwners.get(owner2.threadId)?.generation === owner2.generation) turnResourceOwners.delete(owner2.threadId);
|
|
@@ -81627,9 +81988,33 @@ function releaseTurnResources(owner2) {
|
|
|
81627
81988
|
teamComputerTurns.delete(owner2.threadId);
|
|
81628
81989
|
}
|
|
81629
81990
|
}
|
|
81630
|
-
function bindTurnComputer(owner2, resource, exclusive = false) {
|
|
81631
|
-
|
|
81632
|
-
|
|
81991
|
+
async function bindTurnComputer(owner2, resource, exclusive = false) {
|
|
81992
|
+
const active = () => activeInternalGenerationByThread.get(owner2.threadId) === owner2.generation && turnResourceOwners.get(owner2.threadId)?.generation === owner2.generation;
|
|
81993
|
+
let waitingMessage;
|
|
81994
|
+
const deadline = Date.now() + GROUP_GOAL_WAIT_MAX_MS;
|
|
81995
|
+
try {
|
|
81996
|
+
while (true) {
|
|
81997
|
+
if (!active()) throw new DirectTurnSetupCancelled("Computer wait cancelled");
|
|
81998
|
+
if (!exclusive || claimTurnResource(owner2, resource)) break;
|
|
81999
|
+
if (!waitingMessage) {
|
|
82000
|
+
const blocker = turnResources.blocker(resource, owner2);
|
|
82001
|
+
const holderBot = blocker && store.botByThread(blocker.threadId);
|
|
82002
|
+
const holderTask = holderBot && blocker && store.taskByThread(holderBot.id, blocker.threadId);
|
|
82003
|
+
const holder = holderBot ? `${holderBot.name}${holderTask?.title ? ` / ${holderTask.title}` : ""}` : blocker && store.groupByThread(blocker.threadId)?.name;
|
|
82004
|
+
waitingMessage = store.appendMessage(owner2.threadId, {
|
|
82005
|
+
role: "bot",
|
|
82006
|
+
kind: "activity",
|
|
82007
|
+
tool: { name: `Waiting for computer${holder ? ` \u2014 ${holder} is using it` : ""}; will continue automatically` },
|
|
82008
|
+
...holderBot && holderTask ? { threadRef: { botId: holderBot.id, threadId: holderTask.threadId, title: holderTask.title } } : {}
|
|
82009
|
+
});
|
|
82010
|
+
}
|
|
82011
|
+
if (Date.now() >= deadline) throw new Error("Computer is still busy. Stop the turn using it, then retry.");
|
|
82012
|
+
await new Promise((resolve16) => setTimeout(resolve16, 100));
|
|
82013
|
+
}
|
|
82014
|
+
} finally {
|
|
82015
|
+
if (waitingMessage) store.patchMessage(owner2.threadId, waitingMessage.id, {
|
|
82016
|
+
tool: { name: active() && turnResources.owns(resource, owner2) ? "Computer available \u2014 continuing" : "Computer wait ended", ok: true }
|
|
82017
|
+
});
|
|
81633
82018
|
}
|
|
81634
82019
|
turnResourceOwners.set(owner2.threadId, owner2);
|
|
81635
82020
|
turnComputerResources.set(owner2.threadId, { owner: owner2, resource });
|
|
@@ -81650,6 +82035,10 @@ function botAtThreadCapacity(botId) {
|
|
|
81650
82035
|
function hasDirectDispatch(botId) {
|
|
81651
82036
|
return [...directTurnDispatchClaims.values()].some((claim) => claim.botId === botId);
|
|
81652
82037
|
}
|
|
82038
|
+
function unattendedDispatchState(botId) {
|
|
82039
|
+
const bot = store.bot(botId);
|
|
82040
|
+
return !bot ? "missing" : botAtThreadCapacity(botId) || activeGroupTurnForBot(botId) ? "busy" : "ready";
|
|
82041
|
+
}
|
|
81653
82042
|
function requestedTaskBot(botId, rawThreadId) {
|
|
81654
82043
|
const profile = store.bot(botId);
|
|
81655
82044
|
if (!profile) throw Object.assign(new Error("no such bot"), { status: 404 });
|
|
@@ -82109,7 +82498,7 @@ function checkedMemberIds(value) {
|
|
|
82109
82498
|
}
|
|
82110
82499
|
var bootSelection = { instanceId: "", model: "" };
|
|
82111
82500
|
var store = new Store(() => bootSelection);
|
|
82112
|
-
var teamComputers = new TeamComputers(
|
|
82501
|
+
var teamComputers = new TeamComputers(join65(DATA_DIR, "team-computers.json"), ENVIRONMENT_ID);
|
|
82113
82502
|
var followupsReady = false;
|
|
82114
82503
|
var sendSequencer = new SendSequencer();
|
|
82115
82504
|
bootSelection = await defaultSelection();
|
|
@@ -82130,7 +82519,7 @@ try {
|
|
|
82130
82519
|
}
|
|
82131
82520
|
if (browserCleanupReferencesReconciled) browserCleanup.startPending();
|
|
82132
82521
|
var activeCoordinationForThread = (_threadId) => false;
|
|
82133
|
-
var wireTask = (
|
|
82522
|
+
var wireTask = (task2) => activeCoordinationForThread(task2.threadId) && !task2.busy ? { ...toWireTask(task2), busy: true, activity: "working" } : toWireTask(task2);
|
|
82134
82523
|
var wireBot = (bot) => {
|
|
82135
82524
|
const { resumeCursors: _resumeCursors, tasks, approvalGrant, lastProfileRequestId: _lastProfileRequestId, lastTeamSetupReceipt: _lastTeamSetupReceipt, ...rest } = bot;
|
|
82136
82525
|
const visible = approvalGrant && !approvalGrant.threadOnly ? { ...rest, approvalMode: "ask", autoApprove: false } : rest;
|
|
@@ -82273,6 +82662,30 @@ function fullAccessForSource(botId, threadId) {
|
|
|
82273
82662
|
function peerReviewRequired(bot, threadId) {
|
|
82274
82663
|
return Boolean(bot.approvePeerComms && !fullAccessForSource(bot.id, threadId));
|
|
82275
82664
|
}
|
|
82665
|
+
function delegatedFullAccess(from, fromThreadId, target) {
|
|
82666
|
+
return delegationInheritsFullAccess({
|
|
82667
|
+
senderIsChief: Boolean(from.chiefOfStaff),
|
|
82668
|
+
senderHasFullAccess: fullAccessForSource(from.id, fromThreadId),
|
|
82669
|
+
sameBot: from.id === target.id,
|
|
82670
|
+
recipientDriverKind: registry2.cliTarget(target.modelSelection.instanceId)?.driverKind
|
|
82671
|
+
});
|
|
82672
|
+
}
|
|
82673
|
+
function grantDelegatedFullAccess(from, target, threadId) {
|
|
82674
|
+
if (store.taskByThread(target.id, threadId)?.approvalMode === "full") return;
|
|
82675
|
+
store.patchTask(target.id, threadId, { approvalMode: "full", autoApprove: false, alwaysAllow: [] });
|
|
82676
|
+
store.appendMessage(threadId, {
|
|
82677
|
+
role: "bot",
|
|
82678
|
+
kind: "activity",
|
|
82679
|
+
tool: { name: `Full access \u2014 delegated by ${from.name}, a Chief of Staff with Full access`, ok: true }
|
|
82680
|
+
});
|
|
82681
|
+
}
|
|
82682
|
+
function roomTurnApprovalMode(bot, orchestration) {
|
|
82683
|
+
const handoff = orchestration?.roomHandoffId ? roomHandoffs.nodes.get(orchestration.roomHandoffId) : void 0;
|
|
82684
|
+
const source = handoff?.parentId ? roomHandoffs.nodes.get(handoff.parentId) : void 0;
|
|
82685
|
+
const from = source ? store.bot(source.botId) : void 0;
|
|
82686
|
+
if (from && source && delegatedFullAccess(from, source.threadId, bot)) return "full";
|
|
82687
|
+
return approvalModeForTurn(bot, Boolean(orchestration?.roomHandoffId));
|
|
82688
|
+
}
|
|
82276
82689
|
function handleDesktopTrustedApprovalMessage(raw) {
|
|
82277
82690
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
|
|
82278
82691
|
const message4 = raw;
|
|
@@ -82770,7 +83183,7 @@ function outstandingAssignmentsPrompt(threadId) {
|
|
|
82770
83183
|
}));
|
|
82771
83184
|
return ` Assignments you already sent are still outstanding, and nothing in this conversation cancelled them: ${JSON.stringify(listed)}. Do not send them again, do not poll or wait for them, and do not tell the user they were lost. Each result returns to this conversation on its own and resumes you then. Answer the message above with that work still in flight.`;
|
|
82772
83185
|
}
|
|
82773
|
-
var roomHandoffs = new RoomHandoffs(
|
|
83186
|
+
var roomHandoffs = new RoomHandoffs(join65(DATA_DIR, "room-handoffs.json"), {
|
|
82774
83187
|
validate: (node2, parent) => roomHandoffProblem(node2, parent) ?? (parent && store.bot(parent.botId)?.approvePeerComms && !fullAccessForSource(parent.botId, parent.threadId) && !node2.approvalGranted ? "Sender now requires peer approval; submit a new approved request" : void 0),
|
|
82775
83188
|
// A direct follow-up is owed to one conversation, so it waits for that
|
|
82776
83189
|
// conversation, not for the whole bot. Bot-level busy aggregates every
|
|
@@ -82920,7 +83333,7 @@ var roomHandoffs = new RoomHandoffs(join64(DATA_DIR, "room-handoffs.json"), {
|
|
|
82920
83333
|
await tracked;
|
|
82921
83334
|
return { ok: result.outcome === "settled", text: result.stopReason || result.replyText || result.outcome || "The addressed agent could not run" };
|
|
82922
83335
|
}
|
|
82923
|
-
});
|
|
83336
|
+
}, Date.now, roomHandoffLimits(cfg));
|
|
82924
83337
|
activeCoordinationForThread = (threadId) => roomHandoffs.activeDirect(threadId);
|
|
82925
83338
|
function publicGroupState(group) {
|
|
82926
83339
|
return { ...group, working: groupIsWorking(group) || [...roomHandoffs.nodes.values()].some((n) => n.groupId === group.id && !["completed", "failed", "cancelled"].includes(n.status)) };
|
|
@@ -83426,10 +83839,10 @@ var watchdog = new TurnWatchdog({
|
|
|
83426
83839
|
groupSpeakers.delete(turn.threadId);
|
|
83427
83840
|
store.patchGroup(group.id, { busyBotId: null, unread: true });
|
|
83428
83841
|
}
|
|
83842
|
+
releaseTurnResources(stalledResourceOwner);
|
|
83429
83843
|
const currentBot = store.bot(turn.botId);
|
|
83430
83844
|
if (currentBot?.busy) {
|
|
83431
83845
|
stopScreenPoller(currentBot.id, turn.threadId);
|
|
83432
|
-
releaseTurnResources(stalledResourceOwner);
|
|
83433
83846
|
if (activeVpsThreads.get(currentBot.id) === turn.threadId) activeVpsThreads.delete(currentBot.id);
|
|
83434
83847
|
if (store.taskByThread(currentBot.id, turn.threadId)) store.setTaskActivity(currentBot.id, turn.threadId, "idle");
|
|
83435
83848
|
else store.setActivity(currentBot.id, "idle");
|
|
@@ -83530,6 +83943,8 @@ var localVmLeases = new LocalVmLeasePool(30 * 6e4);
|
|
|
83530
83943
|
var localVmLifecycleBusy = /* @__PURE__ */ new Set();
|
|
83531
83944
|
var localVmThreadTargets = /* @__PURE__ */ new Map();
|
|
83532
83945
|
var localVmActiveThreads = /* @__PURE__ */ new Map();
|
|
83946
|
+
var autoVmClaims = /* @__PURE__ */ new Map();
|
|
83947
|
+
var LAZY_VM_CLAIM_GRACE_MS = 5e3;
|
|
83533
83948
|
var localVmSeen = /* @__PURE__ */ new Set();
|
|
83534
83949
|
function noteLocalVmSeen(target, status) {
|
|
83535
83950
|
if (status && autoLocalVmAttachable(status)) localVmSeen.add(target.key);
|
|
@@ -83611,11 +84026,11 @@ async function attachTeamBox(computer, botId, owner2, canMount, remoteAgent) {
|
|
|
83611
84026
|
const ownerId = teamComputerOwner(computer.id);
|
|
83612
84027
|
if (boxLifecycleBusyBots.has(ownerId)) throw new Error("The team computer is being changed; wait for it to finish");
|
|
83613
84028
|
if (computerControl.snapshot(ownerId).held) throw new Error("Release human control of the team computer before starting another turn");
|
|
83614
|
-
bindTurnComputer(owner2, `computer:box-bot:${ownerId}`, true);
|
|
84029
|
+
await bindTurnComputer(owner2, `computer:box-bot:${ownerId}`, true);
|
|
83615
84030
|
teamComputerTurns.set(owner2.threadId, { owner: owner2, computerId: computer.id, botId, remoteAgent });
|
|
83616
84031
|
let machine = await findBox(cfg, ownerId);
|
|
83617
84032
|
if (!machine) throw new Error("The team's Box computer is missing; explicitly create or retry it from the Team map");
|
|
83618
|
-
bindTurnComputer(owner2, `computer:box:${machine.id}`, true);
|
|
84033
|
+
await bindTurnComputer(owner2, `computer:box:${machine.id}`, true);
|
|
83619
84034
|
const action = boxTurnLifecycleAction({ explicitCloud: true, canMount: true, state: typeof machine.state === "string" ? machine.state : null });
|
|
83620
84035
|
if (action === "wake") machine = await readyBox(cfg, ownerId);
|
|
83621
84036
|
if (!machine || boxTurnLifecycleAction({ explicitCloud: true, canMount: true, state: typeof machine.state === "string" ? machine.state : null }) !== "attach") {
|
|
@@ -83881,6 +84296,7 @@ function localVmIdleFor(target) {
|
|
|
83881
84296
|
return idle;
|
|
83882
84297
|
}
|
|
83883
84298
|
function releaseLocalVmThread(threadId) {
|
|
84299
|
+
autoVmClaims.delete(threadId);
|
|
83884
84300
|
const target = localVmThreadTargets.get(threadId);
|
|
83885
84301
|
if (!target) return;
|
|
83886
84302
|
localVmLeaseFor(target).release(threadId);
|
|
@@ -84890,10 +85306,15 @@ async function startTurn(botId, text8, opts) {
|
|
|
84890
85306
|
}
|
|
84891
85307
|
const skipTranscript = /* @__PURE__ */ new Set([userMessage.id, ...opts?.excludeMessageIds ?? []]);
|
|
84892
85308
|
const activeMessages = store.activePath(threadId);
|
|
85309
|
+
const coordinationChildren = opts?.coordination?.resumed ? new Set(roomHandoffs.children(opts.coordination.id).map((child) => child.id)) : void 0;
|
|
85310
|
+
for (const m3 of activeMessages) {
|
|
85311
|
+
if (!opts?.coordination || !m3.roomRequest) continue;
|
|
85312
|
+
if (m3.roomRequest.phase === "request" && m3.roomRequest.id === opts.coordination.id || m3.roomRequest.phase === "result" && coordinationChildren?.has(m3.roomRequest.id)) skipTranscript.add(m3.id);
|
|
85313
|
+
}
|
|
84893
85314
|
const messagesById = new Map(store.messagesFor(threadId).map((message4) => [message4.id, message4]));
|
|
84894
85315
|
const transcript = activeMessages.filter((m3) => (m3.kind === "text" && m3.text || m3.roomRequest?.phase === "result") && !skipTranscript.has(m3.id)).slice(-40).map((m3) => ({
|
|
84895
85316
|
role: m3.role === "user" ? "user" : "assistant",
|
|
84896
|
-
text: m3.roomRequest?.phase === "result" ? teammateReportContext(m3.roomRequest.id, bot.id) : transcriptText(m3, messagesById, cfg.profile?.name?.trim() || "User")
|
|
85317
|
+
text: m3.roomRequest?.phase === "result" ? teammateReportContext(m3.roomRequest.id, bot.id) : m3.role !== "user" && m3.from ? peerMessageText(m3.from.name, transcriptText(m3, messagesById, cfg.profile?.name?.trim() || "User")) : transcriptText(m3, messagesById, cfg.profile?.name?.trim() || "User")
|
|
84897
85318
|
}));
|
|
84898
85319
|
const rewound = Boolean(task2.rewound);
|
|
84899
85320
|
const externalContextMarker = isExternalContextMarker(task2.lastInstanceId) ? task2.lastInstanceId : void 0;
|
|
@@ -85000,45 +85421,99 @@ async function startTurn(botId, text8, opts) {
|
|
|
85000
85421
|
let browserCapture = null;
|
|
85001
85422
|
let computerKind = null;
|
|
85002
85423
|
let autoVpsProblem = null;
|
|
85424
|
+
const localVmPreviewFor = (localVmTarget, claimThreadId) => () => {
|
|
85425
|
+
const owner2 = localVmLeaseFor(localVmTarget).current(localVmOwnerBusy);
|
|
85426
|
+
if (owner2 && owner2.threadId !== claimThreadId) {
|
|
85427
|
+
throw new Error("the Local VM moved on to another turn");
|
|
85428
|
+
}
|
|
85429
|
+
return containerComputerFrame(void 0, void 0, localVmTarget);
|
|
85430
|
+
};
|
|
85431
|
+
const claimAutoLocalVm = async (claimThreadId, pinnedTarget) => {
|
|
85432
|
+
const localVmTarget = pinnedTarget ?? localVmTargetForBot(bot.id);
|
|
85433
|
+
await bindTurnComputer(resourceOwner, `computer:vm:${localVmTarget.key}`, true);
|
|
85434
|
+
if (localVmImageBusy || localVmModeChangeBusy || localVmLifecycleBusy.has(localVmTarget.key)) {
|
|
85435
|
+
throw new Error("this Local VM is being started, stopped, or replaced \u2014 wait for setup to finish");
|
|
85436
|
+
}
|
|
85437
|
+
if (!localVmLeaseFor(localVmTarget).claim(claimThreadId, bot.id, localVmOwnerBusy)) {
|
|
85438
|
+
throw new Error("this Local VM is already being used by another turn \u2014 wait for that turn to finish");
|
|
85439
|
+
}
|
|
85440
|
+
localVmThreadTargets.set(claimThreadId, localVmTarget);
|
|
85441
|
+
localVmActiveThreads.set(localVmTarget.key, claimThreadId);
|
|
85442
|
+
localVmIdleFor(localVmTarget).touch();
|
|
85443
|
+
const dropLease = () => {
|
|
85444
|
+
localVmLeaseFor(localVmTarget).release(claimThreadId);
|
|
85445
|
+
if (localVmActiveThreads.get(localVmTarget.key) === claimThreadId) localVmActiveThreads.delete(localVmTarget.key);
|
|
85446
|
+
localVmThreadTargets.delete(claimThreadId);
|
|
85447
|
+
const resource = `computer:vm:${localVmTarget.key}`;
|
|
85448
|
+
turnResources.releaseOne(resource, resourceOwner);
|
|
85449
|
+
if (turnComputerResources.get(resourceOwner.threadId)?.resource === resource) turnComputerResources.delete(resourceOwner.threadId);
|
|
85450
|
+
};
|
|
85451
|
+
let localVm;
|
|
85452
|
+
try {
|
|
85453
|
+
localVm = await readyLocalVmForTurn(bot.id, localVmTarget);
|
|
85454
|
+
} catch (error51) {
|
|
85455
|
+
dropLease();
|
|
85456
|
+
throw error51;
|
|
85457
|
+
}
|
|
85458
|
+
if (!localVm.ready || !localVm.runtime) {
|
|
85459
|
+
dropLease();
|
|
85460
|
+
throw new Error(`${localVm.problem ?? "the Local VM is not ready"} (App Settings \u2192 Computers)`);
|
|
85461
|
+
}
|
|
85462
|
+
previewCapture = localVmPreviewFor(localVmTarget, claimThreadId);
|
|
85463
|
+
return { target: localVmTarget, runtime: localVm.runtime };
|
|
85464
|
+
};
|
|
85003
85465
|
const attachLocalVm = async (strict) => {
|
|
85004
85466
|
if (!mountsComputerMcp || instance.driverKind === "boxAgent") {
|
|
85005
85467
|
if (!strict) return false;
|
|
85006
85468
|
throw new Error("this model engine cannot use the Local VM \u2014 choose Claude or an ACP engine, or select another computer destination");
|
|
85007
85469
|
}
|
|
85008
85470
|
const localVmTarget = localVmTargetForBot(bot.id);
|
|
85471
|
+
let lazyReadyVm = null;
|
|
85009
85472
|
if (!strict) {
|
|
85010
85473
|
if (!localVmSeen.has(localVmTarget.key) && !opts?.automationSource) return false;
|
|
85011
85474
|
const seen = await containerComputerStatus(void 0, void 0, localVmTarget).catch(() => null);
|
|
85012
85475
|
if (!seen || !autoLocalVmAttachable(seen)) return false;
|
|
85013
85476
|
if (localVmImageBusy || localVmModeChangeBusy || localVmLifecycleBusy.has(localVmTarget.key)) return false;
|
|
85477
|
+
if (seen.ready && seen.runtime) lazyReadyVm = { runtime: seen.runtime };
|
|
85014
85478
|
}
|
|
85015
85479
|
try {
|
|
85016
|
-
|
|
85017
|
-
|
|
85018
|
-
|
|
85019
|
-
|
|
85020
|
-
|
|
85021
|
-
|
|
85022
|
-
|
|
85023
|
-
|
|
85024
|
-
|
|
85025
|
-
|
|
85026
|
-
|
|
85027
|
-
|
|
85028
|
-
|
|
85480
|
+
if (lazyReadyVm) {
|
|
85481
|
+
integrations.localComputer = containerComputerMcp(
|
|
85482
|
+
lazyReadyVm.runtime,
|
|
85483
|
+
controlIntegration(bot.id, threadId, dispatchClaimId),
|
|
85484
|
+
localVmTarget
|
|
85485
|
+
);
|
|
85486
|
+
autoVmClaims.set(threadId, {
|
|
85487
|
+
owner: resourceOwner,
|
|
85488
|
+
lazy: true,
|
|
85489
|
+
claim: async () => {
|
|
85490
|
+
await claimAutoLocalVm(threadId, localVmTarget);
|
|
85491
|
+
if (previewCapture && threadBusy(bot.id, threadId)) {
|
|
85492
|
+
const touched = screenPollers.get(threadId)?.touched ?? instance.driverKind === "boxAgent";
|
|
85493
|
+
stopScreenPoller(bot.id, threadId);
|
|
85494
|
+
startScreenPoller(
|
|
85495
|
+
bot.id,
|
|
85496
|
+
threadId,
|
|
85497
|
+
{ computer: previewCapture, ...browserCapture ? { browser: browserCapture } : {} },
|
|
85498
|
+
{ screenIsTheWork: touched }
|
|
85499
|
+
);
|
|
85500
|
+
}
|
|
85501
|
+
}
|
|
85502
|
+
});
|
|
85503
|
+
return true;
|
|
85029
85504
|
}
|
|
85505
|
+
const claimed = await claimAutoLocalVm(threadId);
|
|
85030
85506
|
integrations.localComputer = containerComputerMcp(
|
|
85031
|
-
|
|
85507
|
+
claimed.runtime,
|
|
85032
85508
|
controlIntegration(bot.id, threadId, dispatchClaimId),
|
|
85033
|
-
|
|
85509
|
+
claimed.target
|
|
85034
85510
|
);
|
|
85035
|
-
|
|
85036
|
-
|
|
85037
|
-
|
|
85038
|
-
|
|
85511
|
+
autoVmClaims.set(threadId, {
|
|
85512
|
+
owner: resourceOwner,
|
|
85513
|
+
claim: async () => {
|
|
85514
|
+
await claimAutoLocalVm(threadId);
|
|
85039
85515
|
}
|
|
85040
|
-
|
|
85041
|
-
};
|
|
85516
|
+
});
|
|
85042
85517
|
return true;
|
|
85043
85518
|
} catch (error51) {
|
|
85044
85519
|
if (strict) throw error51;
|
|
@@ -85054,11 +85529,11 @@ async function startTurn(botId, text8, opts) {
|
|
|
85054
85529
|
hostPlatform: process.platform,
|
|
85055
85530
|
providerSupportsLocal: mountsLocalComputer
|
|
85056
85531
|
})) {
|
|
85057
|
-
throw new Error("this model engine cannot control this computer \u2014 choose Claude or an ACP engine, or select another destination");
|
|
85532
|
+
throw new Error(mountsLocalComputer ? `local computer control is not available on ${process.platform} \u2014 select another destination` : "this model engine cannot control this computer \u2014 choose Claude or an ACP engine, or select another destination");
|
|
85058
85533
|
}
|
|
85059
85534
|
const cua = readCuaConnection();
|
|
85060
85535
|
if (!cua) throw new Error("CUA Driver is not ready for this computer \u2014 check permissions and restart OpenMausBot");
|
|
85061
|
-
bindTurnComputer(resourceOwner, "computer:host");
|
|
85536
|
+
await bindTurnComputer(resourceOwner, "computer:host");
|
|
85062
85537
|
integrations.localComputer = gatedLocalComputer(cua, controlIntegration(bot.id, threadId, dispatchClaimId));
|
|
85063
85538
|
computerKind = "local";
|
|
85064
85539
|
}
|
|
@@ -85067,7 +85542,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
85067
85542
|
if (unsupported && wants2 === "cloud") throw new Error(unsupported);
|
|
85068
85543
|
if (unsupported && wants2 === void 0) autoVpsProblem = unsupported;
|
|
85069
85544
|
if (!unsupported) {
|
|
85070
|
-
bindTurnComputer(resourceOwner, `computer:vps:${vpsSshAlias(cfg)}:${bot.id}`, true);
|
|
85545
|
+
await bindTurnComputer(resourceOwner, `computer:vps:${vpsSshAlias(cfg)}:${bot.id}`, true);
|
|
85071
85546
|
activeVpsThreads.set(bot.id, threadId);
|
|
85072
85547
|
let remote;
|
|
85073
85548
|
remote = vpsStartsForTurn({ wants: wants2, autoStartVps: bot.autoStartVps, automationSource: opts?.automationSource }) ? await vpsComputerAction("provision", cfg, bot.id) : await inspectVpsForAuto(cfg, bot.id);
|
|
@@ -85097,7 +85572,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
85097
85572
|
computerKind = "box";
|
|
85098
85573
|
}
|
|
85099
85574
|
if (!teamComputer && mountsCloudComputer && (wants2 === "cloud" || wants2 === void 0) && cloudBackend === "box" && boxConfigured(cfg)) {
|
|
85100
|
-
if (wants2 === "cloud") bindTurnComputer(resourceOwner, `computer:box-bot:${bot.id}`, true);
|
|
85575
|
+
if (wants2 === "cloud") await bindTurnComputer(resourceOwner, `computer:box-bot:${bot.id}`, true);
|
|
85101
85576
|
if (!mountsCloudComputer && wants2 === "cloud") {
|
|
85102
85577
|
throw new Error("this model engine cannot use computer tools \u2014 choose Claude, an ACP engine, or the Computer engine");
|
|
85103
85578
|
}
|
|
@@ -85133,7 +85608,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
85133
85608
|
});
|
|
85134
85609
|
}
|
|
85135
85610
|
if (b3 && lifecycle === "attach") {
|
|
85136
|
-
bindTurnComputer(resourceOwner, `computer:box:${b3.id}`, instance.driverKind === "boxAgent");
|
|
85611
|
+
await bindTurnComputer(resourceOwner, `computer:box:${b3.id}`, instance.driverKind === "boxAgent");
|
|
85137
85612
|
previewCapture = () => screenshotBox(cfg, bot.id, b3.id);
|
|
85138
85613
|
if (mountsCloudComputer) {
|
|
85139
85614
|
integrations.computer = {
|
|
@@ -85161,7 +85636,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
85161
85636
|
})) {
|
|
85162
85637
|
const cua = readCuaConnection();
|
|
85163
85638
|
if (cua) {
|
|
85164
|
-
bindTurnComputer(resourceOwner, "computer:host");
|
|
85639
|
+
await bindTurnComputer(resourceOwner, "computer:host");
|
|
85165
85640
|
integrations.localComputer = gatedLocalComputer(cua, controlIntegration(bot.id, threadId, dispatchClaimId));
|
|
85166
85641
|
computerKind = "local";
|
|
85167
85642
|
}
|
|
@@ -85415,6 +85890,7 @@ function routineRunCard(run3) {
|
|
|
85415
85890
|
status: run3.status
|
|
85416
85891
|
};
|
|
85417
85892
|
if (run3.goalStatus) card.goalStatus = run3.goalStatus;
|
|
85893
|
+
if (run3.deferredAt != null && run3.status === "queued") card.deferredAt = run3.deferredAt;
|
|
85418
85894
|
if (run3.threadId) card.executionThreadId = run3.threadId;
|
|
85419
85895
|
if (summary) card.summary = summary;
|
|
85420
85896
|
if (error51) card.error = error51;
|
|
@@ -85422,7 +85898,7 @@ function routineRunCard(run3) {
|
|
|
85422
85898
|
}
|
|
85423
85899
|
function routineRunFallbackText(card) {
|
|
85424
85900
|
const goalState = card.goalStatus === "needs-input" ? "needs your input" : card.goalStatus === "blocked" ? "was blocked" : card.goalStatus === "limit-reached" ? "reached its limit" : card.goalStatus === "stopped" ? "was stopped" : card.goalStatus === "failed" ? "failed" : void 0;
|
|
85425
|
-
const state = goalState ?? (card.status === "waiting" ? "needs your attention" : card.status === "completed" ? "completed" : card.status === "failed" ? "failed" : card.status === "cancelled" ? "was cancelled" : card.status === "missed" ? "was missed" : card.status);
|
|
85901
|
+
const state = goalState ?? (card.status === "waiting" ? "needs your attention" : card.status === "completed" ? "completed" : card.status === "failed" ? "failed" : card.status === "cancelled" ? "was cancelled" : card.status === "missed" ? "was missed" : card.status === "queued" && card.deferredAt != null ? "deferred: target busy" : card.status);
|
|
85426
85902
|
return `Routine \u201C${card.routineName}\u201D ${state}`;
|
|
85427
85903
|
}
|
|
85428
85904
|
function syncRoutineRunToSource(run3) {
|
|
@@ -85518,10 +85994,7 @@ _loadPending();
|
|
|
85518
85994
|
routines = new RoutineManager({
|
|
85519
85995
|
emit: broadcast,
|
|
85520
85996
|
hasPendingDelegations: (threadId) => pendingThreads().includes(threadId) || [...delegationWatch.values()].some((watch) => watch.sourceThreadId === threadId) || pendingDelegationWakes.has(threadId),
|
|
85521
|
-
botState:
|
|
85522
|
-
const bot = store.bot(botId);
|
|
85523
|
-
return !bot ? "missing" : bot.busy ? "busy" : "ready";
|
|
85524
|
-
},
|
|
85997
|
+
botState: unattendedDispatchState,
|
|
85525
85998
|
goalState: (groupId, coordinatorBotId) => {
|
|
85526
85999
|
const group = store.group(groupId);
|
|
85527
86000
|
const coordinator = store.bot(coordinatorBotId);
|
|
@@ -85585,6 +86058,14 @@ routines = new RoutineManager({
|
|
|
85585
86058
|
const detail = run3.error ? `${run3.routineName}: ${run3.error}` : run3.routineName;
|
|
85586
86059
|
const notificationBot = routineSourceOwner(run3)?.bot ?? bot;
|
|
85587
86060
|
notify(buildNotification("routine-failed", notificationBot, routineSourceThread(run3) ?? run3.threadId ?? bot.threadId, detail));
|
|
86061
|
+
},
|
|
86062
|
+
onRunDeferred: (run3) => {
|
|
86063
|
+
const bot = store.bot(run3.botId);
|
|
86064
|
+
if (!bot) return;
|
|
86065
|
+
const minutes = run3.deferredAt != null && run3.deferredNoticeAt != null ? Math.max(1, Math.round((run3.deferredNoticeAt - run3.deferredAt) / 6e4)) : null;
|
|
86066
|
+
const detail = `${redactSecretsInText(run3.routineName)}: target busy${minutes != null ? ` for ${minutes} minutes` : ""}`;
|
|
86067
|
+
const notificationBot = routineSourceOwner(run3)?.bot ?? bot;
|
|
86068
|
+
notify(buildNotification("routine-deferred", notificationBot, routineSourceThread(run3) ?? bot.threadId, detail));
|
|
85588
86069
|
}
|
|
85589
86070
|
});
|
|
85590
86071
|
var recoveredRoutineGoalRuns = new Map(
|
|
@@ -85812,12 +86293,6 @@ async function deleteBotWithLifecycle(botId, revalidate = () => {
|
|
|
85812
86293
|
const acknowledged = await browserCleanup.ensure(committedCleanup);
|
|
85813
86294
|
requireBrowserCleanupAcknowledged(acknowledged, `Browser data for ${bot.name}`);
|
|
85814
86295
|
}
|
|
85815
|
-
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
85816
|
-
try {
|
|
85817
|
-
unlinkSync14(join64(dir, `${bot.threadId}.ndjson`));
|
|
85818
|
-
} catch {
|
|
85819
|
-
}
|
|
85820
|
-
}
|
|
85821
86296
|
return deletionResponse(200, { ok: true });
|
|
85822
86297
|
} finally {
|
|
85823
86298
|
if (claimedLocalVmTarget) localVmLifecycleBusy.delete(claimedLocalVmTarget.key);
|
|
@@ -86106,10 +86581,7 @@ function resolveAndSendProfile(res, args) {
|
|
|
86106
86581
|
}
|
|
86107
86582
|
var webhooks = new WebhookManager({
|
|
86108
86583
|
emit: broadcast,
|
|
86109
|
-
botState:
|
|
86110
|
-
const bot = store.bot(botId);
|
|
86111
|
-
return !bot ? "missing" : bot.busy ? "busy" : "ready";
|
|
86112
|
-
},
|
|
86584
|
+
botState: unattendedDispatchState,
|
|
86113
86585
|
enqueue: (input) => routines.enqueueWebhook(input),
|
|
86114
86586
|
findRun: (webhookId, deliveryId2) => routines.webhookRunReceipt(webhookId, deliveryId2),
|
|
86115
86587
|
cancelQueued: (webhookId, message4) => routines.cancelQueuedWebhook(webhookId, message4),
|
|
@@ -86194,7 +86666,7 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
86194
86666
|
}
|
|
86195
86667
|
revokeInternalCapabilitiesForThread(threadId);
|
|
86196
86668
|
spoken.add(botId);
|
|
86197
|
-
const preparedApprovalMode =
|
|
86669
|
+
const preparedApprovalMode = roomTurnApprovalMode(bot, orchestration);
|
|
86198
86670
|
const preparedSelection = { ...bot.modelSelection };
|
|
86199
86671
|
const preparedComposio = bot.composio;
|
|
86200
86672
|
const instance = turnInstance(bot);
|
|
@@ -86305,7 +86777,7 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
86305
86777
|
const readyGroup = store.group(group.id);
|
|
86306
86778
|
const stillOwnsThread = readyGroup?.dm ? readyGroup.threadId === threadId : Boolean(readyGroup && store.groupTaskByThread(readyGroup.id, threadId));
|
|
86307
86779
|
if (!readyGroup || !stillOwnsThread || !readyGroup.memberIds.includes(readyBot.id)) return false;
|
|
86308
|
-
const setupChanged = turnInstance(readyBot) !== instance ||
|
|
86780
|
+
const setupChanged = turnInstance(readyBot) !== instance || roomTurnApprovalMode(readyBot, orchestration) !== preparedApprovalMode || readyBot.modelSelection.instanceId !== preparedSelection.instanceId || readyBot.modelSelection.model !== preparedSelection.model || readyBot.modelSelection.effort !== preparedSelection.effort || readyBot.modelSelection.variant !== preparedSelection.variant || readyBot.composio !== preparedComposio;
|
|
86309
86781
|
if (setupChanged) {
|
|
86310
86782
|
if (setupRetry === 0) {
|
|
86311
86783
|
return runGroupMemberTurn(
|
|
@@ -86424,7 +86896,7 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
86424
86896
|
throw new Error("this model engine cannot use the Local VM");
|
|
86425
86897
|
}
|
|
86426
86898
|
const target = { ...localVmTargetForBot(readyBot.id) };
|
|
86427
|
-
bindTurnComputer(resourceOwner, `computer:vm:${target.key}`, true);
|
|
86899
|
+
await bindTurnComputer(resourceOwner, `computer:vm:${target.key}`, true);
|
|
86428
86900
|
if (localVmImageBusy || localVmModeChangeBusy || localVmLifecycleBusy.has(target.key)) {
|
|
86429
86901
|
throw new Error("this Local VM is being started, stopped, or replaced");
|
|
86430
86902
|
}
|
|
@@ -86609,7 +87081,7 @@ ${e.text}`;
|
|
|
86609
87081
|
text: text8,
|
|
86610
87082
|
refreshSystemPrompt: true,
|
|
86611
87083
|
images: turnImages,
|
|
86612
|
-
approvalMode:
|
|
87084
|
+
approvalMode: roomTurnApprovalMode(readyBot, orchestration),
|
|
86613
87085
|
system: roomSystem.text,
|
|
86614
87086
|
systemStable: roomSystem.stable,
|
|
86615
87087
|
systemVolatile: roomSystem.volatile,
|
|
@@ -86775,6 +87247,7 @@ ${e.text}`;
|
|
|
86775
87247
|
return true;
|
|
86776
87248
|
} catch (error51) {
|
|
86777
87249
|
if (providerDispatched) throw error51;
|
|
87250
|
+
if (error51 instanceof DirectTurnSetupCancelled) return false;
|
|
86778
87251
|
const isSpendCap = typeof error51 === "object" && error51 !== null && error51.code === "spend_cap";
|
|
86779
87252
|
const isVariantError = typeof error51 === "object" && error51 !== null && error51.code === "unsupported_model_variant";
|
|
86780
87253
|
if (!roomSpeaker && !isSpendCap && !isVariantError) throw error51;
|
|
@@ -88255,7 +88728,7 @@ var claudeUpdatesInFlight = /* @__PURE__ */ new Set();
|
|
|
88255
88728
|
function serveStatic(res, path) {
|
|
88256
88729
|
if (!STATIC_DIR) return false;
|
|
88257
88730
|
const safe = path === "/" ? "/index.html" : path.replace(/\.\./g, "");
|
|
88258
|
-
const file2 =
|
|
88731
|
+
const file2 = join65(STATIC_DIR, safe);
|
|
88259
88732
|
try {
|
|
88260
88733
|
const data = readFileSync45(file2);
|
|
88261
88734
|
res.writeHead(200, { "content-type": MIME[extname5(file2)] ?? "application/octet-stream" });
|
|
@@ -88263,7 +88736,7 @@ function serveStatic(res, path) {
|
|
|
88263
88736
|
return true;
|
|
88264
88737
|
} catch {
|
|
88265
88738
|
try {
|
|
88266
|
-
const data = readFileSync45(
|
|
88739
|
+
const data = readFileSync45(join65(STATIC_DIR, "index.html"));
|
|
88267
88740
|
res.writeHead(200, { "content-type": "text/html" });
|
|
88268
88741
|
res.end(data);
|
|
88269
88742
|
return true;
|
|
@@ -89280,15 +89753,19 @@ var handleRequest = async (req, res) => {
|
|
|
89280
89753
|
if (method === "POST" && path === "/api/internal/ask-bot") {
|
|
89281
89754
|
const body = await readInternalBody();
|
|
89282
89755
|
const fromBotId = internalSender.id;
|
|
89283
|
-
const
|
|
89756
|
+
const toBotRef = String(body.toBotId ?? "");
|
|
89284
89757
|
const message4 = String(body.message ?? "").trim();
|
|
89285
89758
|
if (body.depth !== void 0 && (!Number.isInteger(body.depth) || body.depth < 0 || body.depth !== internalCapability.depth)) {
|
|
89286
89759
|
return json3(res, 403, { error: "the recursion depth does not match this turn" });
|
|
89287
89760
|
}
|
|
89288
89761
|
const depth = internalCapability.depth;
|
|
89289
|
-
if (!
|
|
89290
|
-
if (
|
|
89762
|
+
if (!toBotRef || !message4) return json3(res, 400, { error: "toBotId and message required" });
|
|
89763
|
+
if (toBotRef === fromBotId) return json3(res, 400, { error: "a bot cannot message itself" });
|
|
89291
89764
|
if (depth >= MAX_COMMS_DEPTH) return json3(res, 200, { error: "message chains are limited to one hop" });
|
|
89765
|
+
const resolvedTo = resolveTeammate(store.bots, internalSender, toBotRef);
|
|
89766
|
+
if ("error" in resolvedTo) return json3(res, 404, { error: `no such bot: ${resolvedTo.error}` });
|
|
89767
|
+
if (resolvedTo.id === fromBotId) return json3(res, 400, { error: "a bot cannot message itself" });
|
|
89768
|
+
const toBotId = resolvedTo.id;
|
|
89292
89769
|
const target = store.bot(toBotId);
|
|
89293
89770
|
if (!target) return json3(res, 404, { error: "no such bot" });
|
|
89294
89771
|
const from = internalSender;
|
|
@@ -89443,15 +89920,18 @@ var handleRequest = async (req, res) => {
|
|
|
89443
89920
|
}
|
|
89444
89921
|
if (method === "POST" && path === "/api/internal/delegate-bot") {
|
|
89445
89922
|
const body = await readInternalBody();
|
|
89446
|
-
const
|
|
89923
|
+
const toBotRef = String(body.toBotId ?? "");
|
|
89447
89924
|
const message4 = String(body.message ?? "").trim();
|
|
89448
89925
|
const reason = typeof body.reason === "string" && body.reason.trim() ? body.reason.trim() : void 0;
|
|
89449
89926
|
if (body.depth !== void 0 && (!Number.isInteger(body.depth) || body.depth < 0 || body.depth !== internalCapability.depth)) {
|
|
89450
89927
|
return json3(res, 403, { error: "the recursion depth does not match this turn" });
|
|
89451
89928
|
}
|
|
89452
89929
|
const depth = internalCapability.depth;
|
|
89453
|
-
if (!
|
|
89930
|
+
if (!toBotRef || !message4) return json3(res, 400, { error: "toBotId and message required" });
|
|
89454
89931
|
const from = internalSender;
|
|
89932
|
+
const resolvedTo = resolveTeammate(store.bots, from, toBotRef);
|
|
89933
|
+
if ("error" in resolvedTo) return json3(res, 404, { error: `no such bot: ${resolvedTo.error}` });
|
|
89934
|
+
const toBotId = resolvedTo.id;
|
|
89455
89935
|
const target = store.bot(toBotId);
|
|
89456
89936
|
if (!target) return json3(res, 404, { error: "no such bot" });
|
|
89457
89937
|
if (!canAccessTeam(from, target.section) || target.hidden) {
|
|
@@ -89524,13 +90004,21 @@ var handleRequest = async (req, res) => {
|
|
|
89524
90004
|
const groupId = parsed.data.groupId ?? source?.id;
|
|
89525
90005
|
const destination = groupId ? store.group(groupId) : void 0;
|
|
89526
90006
|
if (groupId && !destination) return json3(res, 404, { error: "No such room; use list_room_targets." });
|
|
89527
|
-
const
|
|
90007
|
+
const botIds = [];
|
|
90008
|
+
for (const raw of parsed.data.botIds) {
|
|
90009
|
+
const resolved = resolveTeammate(store.bots, internalSender, raw);
|
|
90010
|
+
if ("error" in resolved) return json3(res, 403, { error: resolved.error });
|
|
90011
|
+
botIds.push(resolved.id);
|
|
90012
|
+
}
|
|
90013
|
+
if (new Set(botIds).size !== botIds.length) return json3(res, 400, { error: "bot_ids name the same teammate twice \u2014 send each teammate once" });
|
|
90014
|
+
const targets = botIds.map((botId) => ({
|
|
89528
90015
|
groupId: destination?.id,
|
|
89529
90016
|
threadId: destination ? destination.id === source?.id ? address.threadId : destination.threadId : store.bot(botId)?.threadId ?? "",
|
|
89530
90017
|
botId
|
|
89531
90018
|
}));
|
|
89532
90019
|
for (const target of targets) {
|
|
89533
|
-
const
|
|
90020
|
+
const addressed = store.bot(target.botId);
|
|
90021
|
+
const eligibility = target.botId === internalSender.id ? "Choose a teammate, not yourself" : !addressed ? `No bot with id "${target.botId}" \u2014 call list_bots and copy the exact id from the result` : addressed.hidden ? `The bot with id "${target.botId}" is no longer available \u2014 call list_bots for the ones you can reach` : roomHandoffProblem(target, address);
|
|
89534
90022
|
if (eligibility) return json3(res, 403, { error: eligibility });
|
|
89535
90023
|
}
|
|
89536
90024
|
let approvalGranted = false;
|
|
@@ -89557,6 +90045,9 @@ var handleRequest = async (req, res) => {
|
|
|
89557
90045
|
if (!resolved) throw new Error("The recipient no longer exists");
|
|
89558
90046
|
target.threadId = resolved.task.threadId;
|
|
89559
90047
|
if (resolved.created) createdThread = resolved.task.threadId;
|
|
90048
|
+
if (delegatedFullAccess(internalSender, internalCapability.threadId, store.bot(target.botId))) {
|
|
90049
|
+
grantDelegatedFullAccess(internalSender, store.bot(target.botId), target.threadId);
|
|
90050
|
+
}
|
|
89560
90051
|
}
|
|
89561
90052
|
const { node: node2, duplicate } = roomHandoffs.enqueue(
|
|
89562
90053
|
address,
|
|
@@ -89755,6 +90246,7 @@ ${message4}`;
|
|
|
89755
90246
|
}
|
|
89756
90247
|
const task2 = store.createTask(target.id, title, false, projectId, { botId: from.id, name: from.name, at: Date.now() });
|
|
89757
90248
|
if (!task2) return json3(res, 500, { error: "couldn't create that thread" });
|
|
90249
|
+
if (delegatedFullAccess(from, fromThreadId, target)) grantDelegatedFullAccess(from, target, task2.threadId);
|
|
89758
90250
|
const queued = queueDelegation(
|
|
89759
90251
|
commsBus,
|
|
89760
90252
|
from,
|
|
@@ -89994,6 +90486,29 @@ ${message4}`;
|
|
|
89994
90486
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
89995
90487
|
if (method === "GET") {
|
|
89996
90488
|
const snapshot2 = botComputerControlSnapshot(botId, internalCapability.teamComputerId);
|
|
90489
|
+
const slot = autoVmClaims.get(internalCapability.threadId);
|
|
90490
|
+
const lazyClaim = slot && slot.owner.generation === internalCapability.generation ? slot : void 0;
|
|
90491
|
+
if (!snapshot2.held && lazyClaim?.lazy && !lazyClaim.begin) {
|
|
90492
|
+
startAutoVmClaim(autoVmClaims, internalCapability.threadId, internalCapability.generation);
|
|
90493
|
+
await Promise.race([
|
|
90494
|
+
lazyClaim.begin ?? Promise.resolve(),
|
|
90495
|
+
new Promise((resolve16) => setTimeout(resolve16, LAZY_VM_CLAIM_GRACE_MS))
|
|
90496
|
+
]);
|
|
90497
|
+
}
|
|
90498
|
+
if (!snapshot2.held && lazyClaim?.failed === true) {
|
|
90499
|
+
return json3(res, 200, {
|
|
90500
|
+
held: true,
|
|
90501
|
+
helpOpen: false,
|
|
90502
|
+
blockedReason: `This turn could not claim the Local VM${lazyClaim.failure ? ` (${lazyClaim.failure})` : ""}. This call was not performed. Do not retry computer work in this turn; tell the person what you could not do.`
|
|
90503
|
+
});
|
|
90504
|
+
}
|
|
90505
|
+
if (!snapshot2.held && lazyClaim?.lazy && lazyClaim.begin && !lazyClaim.claimed) {
|
|
90506
|
+
return json3(res, 200, {
|
|
90507
|
+
held: true,
|
|
90508
|
+
helpOpen: false,
|
|
90509
|
+
blockedReason: "Another thread is using this computer. This call was not performed. Pause computer work until that thread finishes, then take a fresh screenshot before acting."
|
|
90510
|
+
});
|
|
90511
|
+
}
|
|
89997
90512
|
const computer = turnComputerResources.get(internalCapability.threadId);
|
|
89998
90513
|
if (!snapshot2.held && computer && computer.owner.generation === internalCapability.generation && !claimTurnResource(computer.owner, computer.resource)) {
|
|
89999
90514
|
return json3(res, 200, {
|
|
@@ -91025,14 +91540,6 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
91025
91540
|
routines.disableForGroup(group.id);
|
|
91026
91541
|
store.deleteGroup(group.id);
|
|
91027
91542
|
rejectDeletedThreadSkillStages(stagedSkillCleanups);
|
|
91028
|
-
for (const threadId of threadIds) {
|
|
91029
|
-
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
91030
|
-
try {
|
|
91031
|
-
unlinkSync14(join64(dir, `${threadId}.ndjson`));
|
|
91032
|
-
} catch {
|
|
91033
|
-
}
|
|
91034
|
-
}
|
|
91035
|
-
}
|
|
91036
91543
|
return json3(res, 200, { ok: true });
|
|
91037
91544
|
}
|
|
91038
91545
|
m3 = path.match(/^\/api\/groups\/([\w-]+)\/messages$/);
|
|
@@ -91127,6 +91634,74 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
91127
91634
|
}
|
|
91128
91635
|
return json3(res, 200, { ok: true });
|
|
91129
91636
|
}
|
|
91637
|
+
m3 = path.match(/^\/api\/groups\/([\w-]+)\/queue\/([\w-]+)\/steer$/);
|
|
91638
|
+
if (m3 && method === "POST") {
|
|
91639
|
+
const body = await readBody(req);
|
|
91640
|
+
if (body !== null && (typeof body !== "object" || Array.isArray(body))) {
|
|
91641
|
+
return json3(res, 400, { error: "body must be a JSON object" });
|
|
91642
|
+
}
|
|
91643
|
+
const threadId = typeof body?.threadId === "string" ? body.threadId : void 0;
|
|
91644
|
+
if (threadId !== void 0 && !/^[\w-]+$/.test(threadId)) {
|
|
91645
|
+
return json3(res, 400, { error: "threadId must be a task id" });
|
|
91646
|
+
}
|
|
91647
|
+
const group = store.group(m3[1]);
|
|
91648
|
+
if (!group) return json3(res, 404, { error: "no such room" });
|
|
91649
|
+
const targetThreadId = threadId ?? group.threadId;
|
|
91650
|
+
const ownsThread = group.dm ? group.threadId === targetThreadId : Boolean(store.groupTaskByThread(group.id, targetThreadId));
|
|
91651
|
+
if (!ownsThread) {
|
|
91652
|
+
return json3(res, 409, { error: "the channel switched tasks before it could receive the message" });
|
|
91653
|
+
}
|
|
91654
|
+
noteTurnTrigger(targetThreadId, auth);
|
|
91655
|
+
const current2 = store.group(group.id);
|
|
91656
|
+
if (!current2) return json3(res, 404, { error: "no such room" });
|
|
91657
|
+
const speakerBotId = groupSpeakers.get(targetThreadId)?.botId ?? (targetThreadId === current2.threadId ? current2.busyBotId : void 0);
|
|
91658
|
+
const speaker = speakerBotId ? store.bot(speakerBotId) : void 0;
|
|
91659
|
+
const instance = speaker ? runningTurnInstance(speaker, targetThreadId) : void 0;
|
|
91660
|
+
const held = holdChannelQueue(current2.id, targetThreadId, m3[2]);
|
|
91661
|
+
if (!held) return json3(res, 404, { error: "no such queued message" });
|
|
91662
|
+
if (!speaker || !instance?.adapter.capabilities.queueing || !instance.adapter.steer) {
|
|
91663
|
+
restoreHeldChannelQueue(held);
|
|
91664
|
+
return json3(res, 200, { ok: true, queued: true, threadId: targetThreadId });
|
|
91665
|
+
}
|
|
91666
|
+
const [head] = held.items;
|
|
91667
|
+
if (!head || head.id !== m3[2]) {
|
|
91668
|
+
restoreHeldChannelQueue(held);
|
|
91669
|
+
return json3(res, 409, { error: "only the first queued message can steer" });
|
|
91670
|
+
}
|
|
91671
|
+
const replyTo = resolveHeldReplyTarget(held, resolveReplyTarget);
|
|
91672
|
+
const steered = await instance.adapter.steer(targetThreadId, promptWithReply(head.text, replyTo, cfg.profile?.name?.trim() || "User")).catch(() => "indeterminate");
|
|
91673
|
+
const after = store.group(current2.id);
|
|
91674
|
+
const afterSpeakerBotId = after ? groupSpeakers.get(targetThreadId)?.botId ?? (targetThreadId === after.threadId ? after.busyBotId : void 0) : void 0;
|
|
91675
|
+
const delivered = steered !== "refused";
|
|
91676
|
+
if (after && delivered && (steered === "indeterminate" || afterSpeakerBotId === speakerBotId)) {
|
|
91677
|
+
const message4 = store.appendMessage(targetThreadId, {
|
|
91678
|
+
role: "user",
|
|
91679
|
+
kind: "text",
|
|
91680
|
+
text: head.text,
|
|
91681
|
+
replyToId: head.replyToId,
|
|
91682
|
+
sendId: head.sendId,
|
|
91683
|
+
channelMode: head.mode,
|
|
91684
|
+
queueId: head.id,
|
|
91685
|
+
via: head.via,
|
|
91686
|
+
steered: true
|
|
91687
|
+
});
|
|
91688
|
+
settleHeldChannelQueueHead(held);
|
|
91689
|
+
return json3(res, 200, {
|
|
91690
|
+
ok: true,
|
|
91691
|
+
steered: true,
|
|
91692
|
+
threadId: targetThreadId,
|
|
91693
|
+
messages: [message4],
|
|
91694
|
+
queueIds: [head.id]
|
|
91695
|
+
});
|
|
91696
|
+
}
|
|
91697
|
+
if (steered === "indeterminate" && !after) {
|
|
91698
|
+
settleHeldChannelQueueHead(held);
|
|
91699
|
+
return json3(res, 404, { error: "no such room" });
|
|
91700
|
+
}
|
|
91701
|
+
restoreHeldChannelQueue(held);
|
|
91702
|
+
if (after && !groupIsWorking(after)) drainQueuedChannelSends();
|
|
91703
|
+
return json3(res, 200, { ok: true, queued: true, threadId: targetThreadId });
|
|
91704
|
+
}
|
|
91130
91705
|
m3 = path.match(/^\/api\/groups\/([\w-]+)\/interrupt$/);
|
|
91131
91706
|
if (m3 && method === "POST") {
|
|
91132
91707
|
const group = store.group(m3[1]);
|
|
@@ -92161,18 +92736,19 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
92161
92736
|
}
|
|
92162
92737
|
if (currentAtStart.busy) {
|
|
92163
92738
|
const instance = runningTurnInstance(currentAtStart, threadId);
|
|
92164
|
-
let steered =
|
|
92739
|
+
let steered = "refused";
|
|
92165
92740
|
const carriesImages = extractTurnImages(text8).images.length > 0;
|
|
92166
92741
|
if (!carriesImages && !computerSelectionTurns.get(threadId)?.selected && instance?.adapter.capabilities.queueing && instance.adapter.steer) {
|
|
92167
|
-
steered = await instance.adapter.steer(threadId, promptWithReply(text8, replyTo, cfg.profile?.name?.trim() || "User")).catch(() =>
|
|
92742
|
+
steered = await instance.adapter.steer(threadId, promptWithReply(text8, replyTo, cfg.profile?.name?.trim() || "User")).catch(() => "indeterminate");
|
|
92168
92743
|
}
|
|
92169
92744
|
const current2 = store.projectBotForTask(bot.id, threadId);
|
|
92170
92745
|
if (!current2) throw Object.assign(new Error("no such bot"), { status: 404 });
|
|
92171
92746
|
if (!store.taskByThread(bot.id, threadId)) {
|
|
92172
92747
|
throw Object.assign(new Error("the target task no longer exists"), { status: 409 });
|
|
92173
92748
|
}
|
|
92174
|
-
|
|
92175
|
-
|
|
92749
|
+
const delivered = steered !== "refused";
|
|
92750
|
+
if (delivered) {
|
|
92751
|
+
if (steered === "steered" && !current2.busy) {
|
|
92176
92752
|
throw Object.assign(
|
|
92177
92753
|
new Error("the running turn ended before the steered message could be recorded"),
|
|
92178
92754
|
{ status: 409 }
|
|
@@ -92215,6 +92791,53 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
92215
92791
|
}
|
|
92216
92792
|
return json3(res, 200, { ok: true });
|
|
92217
92793
|
}
|
|
92794
|
+
m3 = path.match(/^\/api\/bots\/([\w-]+)\/queue\/([\w-]+)\/steer$/);
|
|
92795
|
+
if (m3 && method === "POST") {
|
|
92796
|
+
const body = await readBody(req);
|
|
92797
|
+
requirePinnedClientThread(m3[1], body?.threadId);
|
|
92798
|
+
const bot = requestedTaskBot(m3[1], body?.threadId);
|
|
92799
|
+
noteTurnTrigger(bot.threadId, auth);
|
|
92800
|
+
const held = holdSteeredQueue(bot.id, bot.threadId, m3[2]);
|
|
92801
|
+
if (!held) return json3(res, 404, { error: "no such queued message" });
|
|
92802
|
+
if (held.items.some((item) => extractTurnImages(item.text).images.length > 0)) {
|
|
92803
|
+
restoreHeldSteeredQueue(held);
|
|
92804
|
+
return json3(res, 200, { ok: true, queued: true, threadId: bot.threadId });
|
|
92805
|
+
}
|
|
92806
|
+
const currentAtStart = store.projectBotForTask(bot.id, bot.threadId);
|
|
92807
|
+
const instance = currentAtStart?.busy ? runningTurnInstance(currentAtStart, bot.threadId) : void 0;
|
|
92808
|
+
const prompt = held.items.map((item) => item.prompt).join("\n\n");
|
|
92809
|
+
let steered = "refused";
|
|
92810
|
+
if (currentAtStart?.busy && instance?.adapter.capabilities.queueing && instance.adapter.steer) {
|
|
92811
|
+
steered = await instance.adapter.steer(bot.threadId, prompt).catch(() => "indeterminate");
|
|
92812
|
+
}
|
|
92813
|
+
const current2 = store.projectBotForTask(bot.id, bot.threadId);
|
|
92814
|
+
if ((steered === "steered" && current2?.busy || steered === "indeterminate" && current2) && store.taskByThread(bot.id, bot.threadId)) {
|
|
92815
|
+
if (auth.kind === "session" || DESKTOP_MANAGED) clearUnattended(bot.threadId);
|
|
92816
|
+
const messages = held.items.map((item) => store.appendMessage(bot.threadId, {
|
|
92817
|
+
role: "user",
|
|
92818
|
+
kind: "text",
|
|
92819
|
+
text: item.text,
|
|
92820
|
+
replyToId: item.replyToId,
|
|
92821
|
+
sendId: item.sendId,
|
|
92822
|
+
queueId: item.messageId,
|
|
92823
|
+
peerAsk: item.peerAsk,
|
|
92824
|
+
steered: true
|
|
92825
|
+
}));
|
|
92826
|
+
const queueIds = held.items.map((item) => item.messageId);
|
|
92827
|
+
settleHeldSteeredQueue(held);
|
|
92828
|
+
return json3(res, 200, { ok: true, steered: true, threadId: bot.threadId, messages, queueIds });
|
|
92829
|
+
}
|
|
92830
|
+
if (steered === "indeterminate") {
|
|
92831
|
+
settleHeldSteeredQueue(held);
|
|
92832
|
+
if (!store.taskByThread(bot.id, bot.threadId)) {
|
|
92833
|
+
throw Object.assign(new Error("the target task no longer exists"), { status: 409 });
|
|
92834
|
+
}
|
|
92835
|
+
throw Object.assign(new Error("no such bot"), { status: 404 });
|
|
92836
|
+
}
|
|
92837
|
+
restoreHeldSteeredQueue(held);
|
|
92838
|
+
if (current2 && !current2.busy) drainQueuedSends();
|
|
92839
|
+
return json3(res, 200, { ok: true, queued: true, threadId: bot.threadId });
|
|
92840
|
+
}
|
|
92218
92841
|
m3 = path.match(/^\/api\/bots\/([\w-]+)\/messages\/([\w-]+)\/edit$/);
|
|
92219
92842
|
if (m3 && method === "POST") {
|
|
92220
92843
|
const messageId = m3[2];
|
|
@@ -94083,6 +94706,27 @@ try {
|
|
|
94083
94706
|
} catch (error51) {
|
|
94084
94707
|
console.warn(`attachments: startup partial cleanup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
94085
94708
|
}
|
|
94709
|
+
var THREAD_LOG_RETENTION_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
94710
|
+
function sweepThreadEventLogsNow() {
|
|
94711
|
+
const retentionDays = threadEventLogRetentionDays(cfg);
|
|
94712
|
+
if (retentionDays === null) return;
|
|
94713
|
+
const candidates = store.bots.flatMap((bot) => (bot.tasks ?? []).map((task2) => ({
|
|
94714
|
+
threadId: task2.threadId,
|
|
94715
|
+
closedAt: task2.closedBy?.at ?? null,
|
|
94716
|
+
archivedAt: task2.archivedAt ?? null,
|
|
94717
|
+
unread: task2.unread === true,
|
|
94718
|
+
busy: threadBusy(bot.id, task2.threadId),
|
|
94719
|
+
openDirectHandoff: roomHandoffs.activeDirect(task2.threadId)
|
|
94720
|
+
})));
|
|
94721
|
+
const swept = sweepThreadEventLogs(candidates, retentionDays);
|
|
94722
|
+
if (swept > 0) console.log(`[retention] removed event logs for ${swept} idle thread(s) past ${retentionDays} day(s)`);
|
|
94723
|
+
}
|
|
94724
|
+
try {
|
|
94725
|
+
sweepThreadEventLogsNow();
|
|
94726
|
+
} catch (error51) {
|
|
94727
|
+
console.warn(`thread event log retention sweep failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
94728
|
+
}
|
|
94729
|
+
setInterval(sweepThreadEventLogsNow, THREAD_LOG_RETENTION_SWEEP_MS).unref();
|
|
94086
94730
|
for (const row of chatFollowups()) {
|
|
94087
94731
|
if (row.status !== "dispatching" && row.status !== "interrupted") continue;
|
|
94088
94732
|
const owned = row.kind === "bot" ? Boolean(store.taskByThread(row.ownerId, row.threadId)) : Boolean(store.groupByThread(row.threadId)?.id === row.ownerId);
|