openmausbot 0.1.82 → 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-BLLsDK2F.js → index-DSvqfBOf.js} +71 -71
- package/dist/assets/{index-5HVwp5m2.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 +1063 -493
- package/dist-server/mcp-gate.js +2 -1
- package/dist-server/openmausbot.js +187 -55
- package/dist-server/pair-cli.js +187 -55
- 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/gemini.js +11 -1
- 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 +534 -84
- package/dist-server/server/local-routing.js +10 -4
- 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/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 +6 -0
- 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
|
|
@@ -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) => {
|
|
@@ -47769,11 +47946,11 @@ var OpenCodeDriver = createOpenCodeDriver();
|
|
|
47769
47946
|
import { chmodSync as chmodSync2, existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "node:fs";
|
|
47770
47947
|
import { createHash as createHash13 } from "node:crypto";
|
|
47771
47948
|
import { homedir as homedir19 } from "node:os";
|
|
47772
|
-
import { join as
|
|
47949
|
+
import { join as join34 } from "node:path";
|
|
47773
47950
|
var EMPTY = { default: "", options: [] };
|
|
47774
47951
|
function qwenHome(env) {
|
|
47775
47952
|
const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
|
|
47776
|
-
return
|
|
47953
|
+
return join34(home, ".qwen");
|
|
47777
47954
|
}
|
|
47778
47955
|
function isRecord4(value) {
|
|
47779
47956
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -47786,7 +47963,7 @@ var PROTOCOLS = /* @__PURE__ */ new Set(["openai", "anthropic", "gemini", "verte
|
|
|
47786
47963
|
function readQwenRoutes(env) {
|
|
47787
47964
|
let settings;
|
|
47788
47965
|
try {
|
|
47789
|
-
settings = JSON.parse(readFileSync22(
|
|
47966
|
+
settings = JSON.parse(readFileSync22(join34(qwenHome(env), "settings.json"), "utf8"));
|
|
47790
47967
|
} catch {
|
|
47791
47968
|
return [];
|
|
47792
47969
|
}
|
|
@@ -47870,7 +48047,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
47870
48047
|
if (!host) return modelId;
|
|
47871
48048
|
const dir = qwenHome(env);
|
|
47872
48049
|
mkdirSync12(dir, { recursive: true });
|
|
47873
|
-
const path =
|
|
48050
|
+
const path = join34(dir, "settings.json");
|
|
47874
48051
|
let settings = {};
|
|
47875
48052
|
if (existsSync15(path)) {
|
|
47876
48053
|
try {
|
|
@@ -47940,6 +48117,12 @@ function resolveQwenTurnModel(model2, env) {
|
|
|
47940
48117
|
if (!matches.length) throw new Error("This Qwen model is no longer configured. Refresh models and select it again.");
|
|
47941
48118
|
return matches[0].id;
|
|
47942
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
|
+
}
|
|
47943
48126
|
var support8 = {
|
|
47944
48127
|
driverKind: "qwenAgent",
|
|
47945
48128
|
displayName: "Qwen",
|
|
@@ -47960,7 +48143,7 @@ var support8 = {
|
|
|
47960
48143
|
},
|
|
47961
48144
|
// A raw -m only changes the model within the saved provider. ACP switches
|
|
47962
48145
|
// the complete route and confirms it before any prompt leaves OMB.
|
|
47963
|
-
spawnArgs: () => ["--acp"],
|
|
48146
|
+
spawnArgs: (config2, turn) => ["--acp", ...qwenApprovalArgs(config2.fullAuto, turn.approvalMode)],
|
|
47964
48147
|
selectModel: { configId: "model" },
|
|
47965
48148
|
pickAuthMethod: () => null,
|
|
47966
48149
|
authFailure: "continue",
|
|
@@ -48011,7 +48194,7 @@ var CustomAcpDriver = {
|
|
|
48011
48194
|
import { spawn as spawn2 } from "node:child_process";
|
|
48012
48195
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "node:fs";
|
|
48013
48196
|
import { homedir as homedir20 } from "node:os";
|
|
48014
|
-
import { join as
|
|
48197
|
+
import { join as join35 } from "node:path";
|
|
48015
48198
|
|
|
48016
48199
|
// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
|
|
48017
48200
|
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
|
@@ -54315,7 +54498,7 @@ function bindHermesScreenshotCompat(env, modelId) {
|
|
|
54315
54498
|
env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
|
|
54316
54499
|
}
|
|
54317
54500
|
function hermesHome(env) {
|
|
54318
|
-
return env.HERMES_HOME ||
|
|
54501
|
+
return env.HERMES_HOME || join35(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
|
|
54319
54502
|
}
|
|
54320
54503
|
function quoteYaml(value) {
|
|
54321
54504
|
if (/^[\w./:+-]+$/.test(value)) return value;
|
|
@@ -54357,7 +54540,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
|
|
|
54357
54540
|
if (!host) return modelId;
|
|
54358
54541
|
const dir = hermesHome(env);
|
|
54359
54542
|
mkdirSync13(dir, { recursive: true });
|
|
54360
|
-
const path =
|
|
54543
|
+
const path = join35(dir, "config.yaml");
|
|
54361
54544
|
let text8 = "";
|
|
54362
54545
|
try {
|
|
54363
54546
|
text8 = readFileSync23(path, "utf8");
|
|
@@ -54436,13 +54619,13 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
54436
54619
|
const dir = hermesHome(env);
|
|
54437
54620
|
let secrets = "";
|
|
54438
54621
|
try {
|
|
54439
|
-
secrets = readFileSync23(
|
|
54622
|
+
secrets = readFileSync23(join35(dir, ".env"), "utf8");
|
|
54440
54623
|
} catch {
|
|
54441
54624
|
}
|
|
54442
54625
|
const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name2) => nonEmptyDotenvValue(secrets, name2));
|
|
54443
54626
|
let configuredDefault = null;
|
|
54444
54627
|
try {
|
|
54445
|
-
configuredDefault = hermesConfigDefault(readFileSync23(
|
|
54628
|
+
configuredDefault = hermesConfigDefault(readFileSync23(join35(dir, "config.yaml"), "utf8"));
|
|
54446
54629
|
} catch {
|
|
54447
54630
|
}
|
|
54448
54631
|
const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
|
|
@@ -54756,7 +54939,7 @@ var OpenAICompatDriver = {
|
|
|
54756
54939
|
// server/drivers/pi.ts
|
|
54757
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";
|
|
54758
54941
|
import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
|
|
54759
|
-
import { join as
|
|
54942
|
+
import { join as join36 } from "node:path";
|
|
54760
54943
|
var DRIVER_KIND6 = "piAgent";
|
|
54761
54944
|
var PI_ARGS = ["--mode", "rpc", "--no-session"];
|
|
54762
54945
|
var PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
|
|
@@ -54861,7 +55044,7 @@ async function applyPiLocalCatalog(catalog2, env = process.env, fetchImpl = fetc
|
|
|
54861
55044
|
return preferPiInjectRows(await mergeLocalInject(catalog2, env, fetchImpl));
|
|
54862
55045
|
}
|
|
54863
55046
|
function piAgentDir(env) {
|
|
54864
|
-
return
|
|
55047
|
+
return join36(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
|
|
54865
55048
|
}
|
|
54866
55049
|
function ensurePiInjectModel(modelId, env = process.env) {
|
|
54867
55050
|
const split2 = splitPiModel(modelId);
|
|
@@ -54872,7 +55055,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
54872
55055
|
if (!host) return split2;
|
|
54873
55056
|
const dir = piAgentDir(env);
|
|
54874
55057
|
mkdirSync14(dir, { recursive: true });
|
|
54875
|
-
const path =
|
|
55058
|
+
const path = join36(dir, "models.json");
|
|
54876
55059
|
let root = { providers: {} };
|
|
54877
55060
|
if (existsSync16(path)) {
|
|
54878
55061
|
try {
|
|
@@ -55073,9 +55256,9 @@ var PiDriver = {
|
|
|
55073
55256
|
const mcpServers = buildMcpServers(turn);
|
|
55074
55257
|
let mcpTempDir = null;
|
|
55075
55258
|
if (mcpServers) {
|
|
55076
|
-
mcpTempDir = mkdtempSync2(
|
|
55259
|
+
mcpTempDir = mkdtempSync2(join36(tmpdir4(), "omb-pi-mcp-"));
|
|
55077
55260
|
try {
|
|
55078
|
-
writeFileSync14(
|
|
55261
|
+
writeFileSync14(join36(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
|
|
55079
55262
|
} catch (err) {
|
|
55080
55263
|
try {
|
|
55081
55264
|
rmSync3(mcpTempDir, { recursive: true, force: true });
|
|
@@ -55093,7 +55276,7 @@ var PiDriver = {
|
|
|
55093
55276
|
env: piEnvironment({
|
|
55094
55277
|
...process.env,
|
|
55095
55278
|
...input.environment,
|
|
55096
|
-
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG:
|
|
55279
|
+
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join36(mcpTempDir, "mcp.json") } : {}
|
|
55097
55280
|
})
|
|
55098
55281
|
});
|
|
55099
55282
|
} catch (err) {
|
|
@@ -55433,7 +55616,7 @@ ${turn.text}` : turn.text;
|
|
|
55433
55616
|
// server/drivers/minimax.ts
|
|
55434
55617
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
55435
55618
|
import { homedir as homedir22 } from "node:os";
|
|
55436
|
-
import { join as
|
|
55619
|
+
import { join as join37 } from "node:path";
|
|
55437
55620
|
var DRIVER_KIND7 = "minimax";
|
|
55438
55621
|
var API_KEY_ENV = "MINIMAX_API_KEY";
|
|
55439
55622
|
var DEFAULT_URL2 = "https://api.minimax.io/v1";
|
|
@@ -55461,7 +55644,7 @@ function normalizedApiUrl(value) {
|
|
|
55461
55644
|
}
|
|
55462
55645
|
function loadLocalMiniMaxConfig(home = homedir22()) {
|
|
55463
55646
|
try {
|
|
55464
|
-
const raw = localConfigSchema.parse(JSON.parse(readFileSync25(
|
|
55647
|
+
const raw = localConfigSchema.parse(JSON.parse(readFileSync25(join37(home, ".mmx", "config.json"), "utf8")));
|
|
55465
55648
|
const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
|
|
55466
55649
|
return {
|
|
55467
55650
|
apiKey: raw.api_key?.trim() ?? "",
|
|
@@ -55557,14 +55740,14 @@ var BUILT_IN_DRIVERS = [
|
|
|
55557
55740
|
|
|
55558
55741
|
// server/store.ts
|
|
55559
55742
|
import { createHash as createHash15 } from "node:crypto";
|
|
55560
|
-
import { existsSync as existsSync20, readFileSync as readFileSync29, mkdirSync as mkdirSync19, rmSync as rmSync5, unlinkSync as
|
|
55561
|
-
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";
|
|
55562
55745
|
|
|
55563
55746
|
// server/section-context.ts
|
|
55564
55747
|
import { existsSync as existsSync17, mkdirSync as mkdirSync15, readFileSync as readFileSync26 } from "node:fs";
|
|
55565
|
-
import { join as
|
|
55748
|
+
import { join as join38 } from "node:path";
|
|
55566
55749
|
var SECTION_CONTEXT_MAX_BYTES = 24e3;
|
|
55567
|
-
var SECTION_CONTEXTS_FILE =
|
|
55750
|
+
var SECTION_CONTEXTS_FILE = join38(DATA_DIR, "section-contexts.json");
|
|
55568
55751
|
var sectionContextRecordSchema = external_exports.object({ text: external_exports.string(), updatedAt: external_exports.number().finite() });
|
|
55569
55752
|
var sectionContextFileSchema = external_exports.object({
|
|
55570
55753
|
version: external_exports.literal(1),
|
|
@@ -55668,14 +55851,14 @@ Shared context for the ${JSON.stringify(label)} section follows. The user manage
|
|
|
55668
55851
|
// server/bot-folder.ts
|
|
55669
55852
|
import { createHash as createHash14 } from "node:crypto";
|
|
55670
55853
|
import { closeSync as closeSync6, mkdirSync as mkdirSync16, openSync as openSync6, readSync as readSync3, rmSync as rmSync4 } from "node:fs";
|
|
55671
|
-
import { join as
|
|
55672
|
-
var BOTS_DIR =
|
|
55854
|
+
import { join as join39 } from "node:path";
|
|
55855
|
+
var BOTS_DIR = join39(DATA_DIR, "bots");
|
|
55673
55856
|
var SOUL_FILE = "SOUL.md";
|
|
55674
55857
|
function botFolder(botId) {
|
|
55675
|
-
return
|
|
55858
|
+
return join39(BOTS_DIR, botId);
|
|
55676
55859
|
}
|
|
55677
55860
|
function soulFile(botId) {
|
|
55678
|
-
return
|
|
55861
|
+
return join39(botFolder(botId), SOUL_FILE);
|
|
55679
55862
|
}
|
|
55680
55863
|
function soulHash(soul) {
|
|
55681
55864
|
return createHash14("sha256").update(soul, "utf8").digest("hex");
|
|
@@ -55743,7 +55926,7 @@ function peerAllowKey(action, targetId) {
|
|
|
55743
55926
|
|
|
55744
55927
|
// server/message-db.ts
|
|
55745
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";
|
|
55746
|
-
import { join as
|
|
55929
|
+
import { join as join40 } from "node:path";
|
|
55747
55930
|
import { DatabaseSync } from "node:sqlite";
|
|
55748
55931
|
|
|
55749
55932
|
// server/peer-provenance.ts
|
|
@@ -55766,7 +55949,7 @@ function peerProvenanceAuthor(text8) {
|
|
|
55766
55949
|
}
|
|
55767
55950
|
|
|
55768
55951
|
// server/message-db.ts
|
|
55769
|
-
var DB_FILE = () =>
|
|
55952
|
+
var DB_FILE = () => join40(DATA_DIR, "messages.db");
|
|
55770
55953
|
var handle = null;
|
|
55771
55954
|
var handlePath = null;
|
|
55772
55955
|
function open4() {
|
|
@@ -56196,9 +56379,9 @@ function closeMessageDb() {
|
|
|
56196
56379
|
|
|
56197
56380
|
// server/workspace.ts
|
|
56198
56381
|
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readdirSync as readdirSync4, readFileSync as readFileSync28, statSync as statSync6 } from "node:fs";
|
|
56199
|
-
import { join as
|
|
56200
|
-
var WORKSPACES_DIR =
|
|
56201
|
-
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");
|
|
56202
56385
|
function supportsWorkspaceFiles(driverKind) {
|
|
56203
56386
|
return !["grok", "openai-compat", "minimax", "boxAgent"].includes(driverKind);
|
|
56204
56387
|
}
|
|
@@ -56206,7 +56389,7 @@ function ensureTaskWorkspace(botId, threadId) {
|
|
|
56206
56389
|
if (![botId, threadId].every((id) => /^[A-Za-z0-9_-]{1,128}$/.test(id))) {
|
|
56207
56390
|
throw new Error("Invalid bot or thread id for a task workspace.");
|
|
56208
56391
|
}
|
|
56209
|
-
const dir =
|
|
56392
|
+
const dir = join41(TASK_WORKSPACES_DIR, botId, threadId);
|
|
56210
56393
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56211
56394
|
return dir;
|
|
56212
56395
|
}
|
|
@@ -56219,20 +56402,20 @@ load at the start of every session \u2014 keep this file short and curated.
|
|
|
56219
56402
|
Longer notes belong in memory/<topic>.md files, read on demand.
|
|
56220
56403
|
`;
|
|
56221
56404
|
function ensureWorkspace(botId) {
|
|
56222
|
-
const dir =
|
|
56223
|
-
mkdirSync18(
|
|
56224
|
-
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");
|
|
56225
56408
|
if (!existsSync19(memoryFile)) writeFileAtomic(memoryFile, MEMORY_SEED, { mode: 384 });
|
|
56226
56409
|
return dir;
|
|
56227
56410
|
}
|
|
56228
56411
|
function workspaceDir(botId) {
|
|
56229
|
-
return
|
|
56412
|
+
return join41(WORKSPACES_DIR, botId);
|
|
56230
56413
|
}
|
|
56231
56414
|
function workspaceLocationsPrompt(botId, cwd, botCwd) {
|
|
56232
56415
|
return "\n\nFile locations for this bot (absolute paths): " + JSON.stringify({
|
|
56233
56416
|
currentWorkingFolder: cwd ?? "Provider default; inspect the working directory before using relative paths",
|
|
56234
56417
|
sharedBotFolder: workspaceDir(botId),
|
|
56235
|
-
otherThreadFiles:
|
|
56418
|
+
otherThreadFiles: join41(TASK_WORKSPACES_DIR, botId),
|
|
56236
56419
|
...botCwd ? { configuredProjectFolder: botCwd } : {}
|
|
56237
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.";
|
|
56238
56421
|
}
|
|
@@ -56245,7 +56428,7 @@ function memoryOverBudget(text8) {
|
|
|
56245
56428
|
function loadMemory(botId) {
|
|
56246
56429
|
let raw;
|
|
56247
56430
|
try {
|
|
56248
|
-
raw = readFileSync28(
|
|
56431
|
+
raw = readFileSync28(join41(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
56249
56432
|
} catch {
|
|
56250
56433
|
return null;
|
|
56251
56434
|
}
|
|
@@ -56268,7 +56451,7 @@ var MEMORY_FILE_MAX_BYTES = 256 * 1024;
|
|
|
56268
56451
|
function readMemoryFile(botId) {
|
|
56269
56452
|
let raw;
|
|
56270
56453
|
try {
|
|
56271
|
-
raw = readFileSync28(
|
|
56454
|
+
raw = readFileSync28(join41(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
56272
56455
|
} catch {
|
|
56273
56456
|
return { text: "", truncated: false };
|
|
56274
56457
|
}
|
|
@@ -56277,12 +56460,12 @@ function readMemoryFile(botId) {
|
|
|
56277
56460
|
}
|
|
56278
56461
|
function writeMemoryFile(botId, text8) {
|
|
56279
56462
|
ensureWorkspace(botId);
|
|
56280
|
-
writeFileAtomic(
|
|
56463
|
+
writeFileAtomic(join41(workspaceDir(botId), "MEMORY.md"), redactSecretsInText(text8), { mode: 384 });
|
|
56281
56464
|
indexWrittenMemoryFile(botId, "MEMORY.md");
|
|
56282
56465
|
}
|
|
56283
56466
|
function indexWrittenMemoryFile(botId, relativePath) {
|
|
56284
56467
|
try {
|
|
56285
|
-
const path =
|
|
56468
|
+
const path = join41(workspaceDir(botId), relativePath);
|
|
56286
56469
|
const stat6 = statSync6(path);
|
|
56287
56470
|
indexMemoryFile(botId, relativePath, readFileSync28(path, "utf8"), { mtimeMs: stat6.mtimeMs, bytes: stat6.size });
|
|
56288
56471
|
} catch {
|
|
@@ -56297,7 +56480,7 @@ function memoryFilesOnDisk(botId) {
|
|
|
56297
56480
|
];
|
|
56298
56481
|
return names.flatMap((relativePath) => {
|
|
56299
56482
|
try {
|
|
56300
|
-
const stat6 = statSync6(
|
|
56483
|
+
const stat6 = statSync6(join41(dir, relativePath));
|
|
56301
56484
|
return stat6.isFile() ? [{ path: relativePath, mtimeMs: stat6.mtimeMs, bytes: stat6.size }] : [];
|
|
56302
56485
|
} catch {
|
|
56303
56486
|
return [];
|
|
@@ -56312,7 +56495,7 @@ function syncMemoryIndex(botId) {
|
|
|
56312
56495
|
indexed.delete(file2.path);
|
|
56313
56496
|
if (known && known.bytes === file2.bytes && known.mtimeMs === Math.trunc(file2.mtimeMs)) continue;
|
|
56314
56497
|
try {
|
|
56315
|
-
const text8 = readFileSync28(
|
|
56498
|
+
const text8 = readFileSync28(join41(workspaceDir(botId), file2.path), "utf8");
|
|
56316
56499
|
indexMemoryFile(botId, file2.path, file2.path === "MEMORY.md" && text8 === MEMORY_SEED ? "" : text8, file2);
|
|
56317
56500
|
} catch {
|
|
56318
56501
|
}
|
|
@@ -56373,7 +56556,7 @@ function updateMemory(botId, update, opts = {}) {
|
|
|
56373
56556
|
}
|
|
56374
56557
|
const text8 = update.text === void 0 ? void 0 : redactSecretsInText(update.text);
|
|
56375
56558
|
const dir = ensureWorkspace(botId);
|
|
56376
|
-
const raw = readFileSync28(
|
|
56559
|
+
const raw = readFileSync28(join41(dir, "MEMORY.md"), "utf8");
|
|
56377
56560
|
const current2 = raw === MEMORY_SEED ? "" : raw;
|
|
56378
56561
|
const today = memoryDate(opts.now);
|
|
56379
56562
|
const appendEntry = (base2, entry2) => `${base2}${base2 && !base2.endsWith("\n") ? "\n" : ""}${entry2}
|
|
@@ -56445,10 +56628,10 @@ function appendMemoryLog(botId, text8, opts = {}) {
|
|
|
56445
56628
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
56446
56629
|
const source = cleanSource(opts.source);
|
|
56447
56630
|
const line = `- ${pad2(now.getHours())}:${pad2(now.getMinutes())}${SEP2}${source ? `from ${source}${SEP2}` : ""}${normaliseEntryText(redactSecretsInText(text8))}`;
|
|
56448
|
-
const dir =
|
|
56631
|
+
const dir = join41(ensureWorkspace(botId), "memory", MEMORY_LOG_DIR);
|
|
56449
56632
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56450
56633
|
const file2 = `${memoryDate(now)}.md`;
|
|
56451
|
-
const path =
|
|
56634
|
+
const path = join41(dir, file2);
|
|
56452
56635
|
let current2 = "";
|
|
56453
56636
|
try {
|
|
56454
56637
|
current2 = readFileSync28(path, "utf8");
|
|
@@ -56462,14 +56645,14 @@ function appendMemoryLog(botId, text8, opts = {}) {
|
|
|
56462
56645
|
}
|
|
56463
56646
|
function writeMemoryLog(botId, name2, text8) {
|
|
56464
56647
|
if (!LOG_FILE_NAME.test(name2)) throw new Error("invalid log name");
|
|
56465
|
-
const dir =
|
|
56648
|
+
const dir = join41(ensureWorkspace(botId), "memory", MEMORY_LOG_DIR);
|
|
56466
56649
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
56467
|
-
writeFileAtomic(
|
|
56650
|
+
writeFileAtomic(join41(dir, name2), redactSecretsInText(text8), { mode: 384 });
|
|
56468
56651
|
indexWrittenMemoryFile(botId, `memory/${MEMORY_LOG_DIR}/${name2}`);
|
|
56469
56652
|
}
|
|
56470
56653
|
function listMemoryLogs(botId) {
|
|
56471
56654
|
try {
|
|
56472
|
-
return readdirSync4(
|
|
56655
|
+
return readdirSync4(join41(workspaceDir(botId), "memory", MEMORY_LOG_DIR)).filter((name2) => LOG_FILE_NAME.test(name2)).sort();
|
|
56473
56656
|
} catch {
|
|
56474
56657
|
return [];
|
|
56475
56658
|
}
|
|
@@ -56477,7 +56660,7 @@ function listMemoryLogs(botId) {
|
|
|
56477
56660
|
function readMemoryLog(botId, name2) {
|
|
56478
56661
|
if (!LOG_FILE_NAME.test(name2)) return null;
|
|
56479
56662
|
try {
|
|
56480
|
-
return readFileSync28(
|
|
56663
|
+
return readFileSync28(join41(workspaceDir(botId), "memory", MEMORY_LOG_DIR, name2), "utf8");
|
|
56481
56664
|
} catch {
|
|
56482
56665
|
return null;
|
|
56483
56666
|
}
|
|
@@ -56489,13 +56672,13 @@ function isMemoryTopicName(name2) {
|
|
|
56489
56672
|
function listMemoryTopics(botId) {
|
|
56490
56673
|
let entries;
|
|
56491
56674
|
try {
|
|
56492
|
-
entries = readdirSync4(
|
|
56675
|
+
entries = readdirSync4(join41(workspaceDir(botId), "memory"));
|
|
56493
56676
|
} catch {
|
|
56494
56677
|
return [];
|
|
56495
56678
|
}
|
|
56496
56679
|
return entries.filter(isMemoryTopicName).flatMap((name2) => {
|
|
56497
56680
|
try {
|
|
56498
|
-
const stat6 = statSync6(
|
|
56681
|
+
const stat6 = statSync6(join41(workspaceDir(botId), "memory", name2));
|
|
56499
56682
|
return stat6.isFile() ? [{ name: name2, bytes: stat6.size }] : [];
|
|
56500
56683
|
} catch {
|
|
56501
56684
|
return [];
|
|
@@ -56505,13 +56688,13 @@ function listMemoryTopics(botId) {
|
|
|
56505
56688
|
function writeMemoryTopic(botId, name2, text8) {
|
|
56506
56689
|
if (!isMemoryTopicName(name2)) throw new Error("invalid topic name");
|
|
56507
56690
|
ensureWorkspace(botId);
|
|
56508
|
-
writeFileAtomic(
|
|
56691
|
+
writeFileAtomic(join41(workspaceDir(botId), "memory", name2), redactSecretsInText(text8), { mode: 384 });
|
|
56509
56692
|
indexWrittenMemoryFile(botId, `memory/${name2}`);
|
|
56510
56693
|
}
|
|
56511
56694
|
function readMemoryTopic(botId, name2) {
|
|
56512
56695
|
if (!isMemoryTopicName(name2)) return null;
|
|
56513
56696
|
try {
|
|
56514
|
-
return readFileSync28(
|
|
56697
|
+
return readFileSync28(join41(workspaceDir(botId), "memory", name2), "utf8");
|
|
56515
56698
|
} catch {
|
|
56516
56699
|
return null;
|
|
56517
56700
|
}
|
|
@@ -56520,8 +56703,8 @@ var SESSION_SEARCH_SYSTEM_PROMPT = ' Your own earlier conversations with this us
|
|
|
56520
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.";
|
|
56521
56704
|
function memorySystemPrompt(botId, opts = {}) {
|
|
56522
56705
|
const memory2 = loadMemory(botId);
|
|
56523
|
-
const memoryFile =
|
|
56524
|
-
const topicDir =
|
|
56706
|
+
const memoryFile = join41(workspaceDir(botId), "MEMORY.md");
|
|
56707
|
+
const topicDir = join41(workspaceDir(botId), "memory");
|
|
56525
56708
|
if (opts.fileTools === false && !opts.managedWrites) {
|
|
56526
56709
|
if (!memory2) return "";
|
|
56527
56710
|
return ` Your saved memory is supplied as context; this turn has no memory editing tools.
|
|
@@ -56621,6 +56804,10 @@ var projectEmojiPattern = new RegExp("^(?!\\p{Emoji_Component}$)(?:\\p{RGI_Emoji
|
|
|
56621
56804
|
function isProjectEmoji(value) {
|
|
56622
56805
|
return typeof value === "string" && value.length <= 64 && projectEmojiPattern.exec(value)?.[0] === value;
|
|
56623
56806
|
}
|
|
56807
|
+
function toWireTask(task2) {
|
|
56808
|
+
const { resumeCursors: _resumeCursors, lastInstanceId: _lastInstanceId, ...wire } = task2;
|
|
56809
|
+
return wire;
|
|
56810
|
+
}
|
|
56624
56811
|
var TASK_PATCH_FIELDS = [
|
|
56625
56812
|
"title",
|
|
56626
56813
|
"projectId",
|
|
@@ -56765,9 +56952,9 @@ function titleFromMessage(text8) {
|
|
|
56765
56952
|
const line = text8.trim().split("\n")[0].trim();
|
|
56766
56953
|
return line.length > 48 ? `${line.slice(0, 47)}\u2026` : line || UNTITLED_TASK;
|
|
56767
56954
|
}
|
|
56768
|
-
var BOTS_FILE =
|
|
56769
|
-
var GROUPS_FILE =
|
|
56770
|
-
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`);
|
|
56771
56958
|
var COLORS = [
|
|
56772
56959
|
"green",
|
|
56773
56960
|
"blue",
|
|
@@ -57181,13 +57368,20 @@ var Store = class {
|
|
|
57181
57368
|
this.emit({ type: "group", groupId: group.id });
|
|
57182
57369
|
return group;
|
|
57183
57370
|
}
|
|
57184
|
-
/** 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. */
|
|
57185
57374
|
deleteThreadRecord(threadId) {
|
|
57186
57375
|
this.threads.delete(threadId);
|
|
57187
57376
|
deleteThread(threadId);
|
|
57188
|
-
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
|
+
]) {
|
|
57189
57383
|
try {
|
|
57190
|
-
|
|
57384
|
+
unlinkSync8(file2);
|
|
57191
57385
|
} catch {
|
|
57192
57386
|
}
|
|
57193
57387
|
}
|
|
@@ -57699,7 +57893,7 @@ var Store = class {
|
|
|
57699
57893
|
} catch {
|
|
57700
57894
|
}
|
|
57701
57895
|
try {
|
|
57702
|
-
rmSync5(
|
|
57896
|
+
rmSync5(join42(DATA_DIR, "skill-state", id), { recursive: true, force: true });
|
|
57703
57897
|
} catch {
|
|
57704
57898
|
}
|
|
57705
57899
|
removeBotFolder(id);
|
|
@@ -58465,7 +58659,7 @@ function parseSince(value, now = Date.now()) {
|
|
|
58465
58659
|
return day.getTime();
|
|
58466
58660
|
}
|
|
58467
58661
|
if (/^\d{12,}$/.test(text8)) return Number(text8);
|
|
58468
|
-
const parsed = Date.parse(value.trim());
|
|
58662
|
+
const parsed = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(text8) ? `${text8}T00:00` : value.trim());
|
|
58469
58663
|
return Number.isFinite(parsed) ? parsed : null;
|
|
58470
58664
|
}
|
|
58471
58665
|
|
|
@@ -58504,7 +58698,7 @@ ${message4.text}`;
|
|
|
58504
58698
|
|
|
58505
58699
|
// server/delegations.ts
|
|
58506
58700
|
import { readFileSync as readFileSync30 } from "node:fs";
|
|
58507
|
-
import { join as
|
|
58701
|
+
import { join as join43 } from "node:path";
|
|
58508
58702
|
|
|
58509
58703
|
// server/peer-approval.ts
|
|
58510
58704
|
function settleCard(pending, behavior, source) {
|
|
@@ -58651,8 +58845,8 @@ function dismissStalePeerCards(bus2) {
|
|
|
58651
58845
|
var pendingDelegations = /* @__PURE__ */ new Map();
|
|
58652
58846
|
var drainingThreads = /* @__PURE__ */ new Set();
|
|
58653
58847
|
var queuedRedrains = /* @__PURE__ */ new Set();
|
|
58654
|
-
var DELEGATIONS_FILE =
|
|
58655
|
-
var RECEIPTS_FILE =
|
|
58848
|
+
var DELEGATIONS_FILE = join43(DATA_DIR, "delegations.json");
|
|
58849
|
+
var RECEIPTS_FILE = join43(DATA_DIR, "delegation-receipts.json");
|
|
58656
58850
|
var MAX_RECEIPTS = 100;
|
|
58657
58851
|
var RECEIPT_MAX_AGE_MS = 48 * 60 * 60 * 1e3;
|
|
58658
58852
|
var RESULT_MAX_CHARS = 4e3;
|
|
@@ -59342,6 +59536,25 @@ function cancelSteeredMessage(botId, messageId, expectedThreadId) {
|
|
|
59342
59536
|
}
|
|
59343
59537
|
return false;
|
|
59344
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
|
+
}
|
|
59345
59558
|
|
|
59346
59559
|
// server/channel-queue.ts
|
|
59347
59560
|
var queues2 = /* @__PURE__ */ new Map();
|
|
@@ -59388,6 +59601,41 @@ function cancelChannelMessage(groupId, queueId) {
|
|
|
59388
59601
|
}
|
|
59389
59602
|
return false;
|
|
59390
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
|
+
}
|
|
59391
59639
|
function drainChannelMessages(isWorking, run3) {
|
|
59392
59640
|
for (const [threadId, entry] of queues2) {
|
|
59393
59641
|
if (isWorking(entry.groupId)) continue;
|
|
@@ -59451,7 +59699,7 @@ function sendFingerprint(text8, replyToId, channelMode) {
|
|
|
59451
59699
|
|
|
59452
59700
|
// server/harness/bus.ts
|
|
59453
59701
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
59454
|
-
import { join as
|
|
59702
|
+
import { join as join44 } from "node:path";
|
|
59455
59703
|
var INCOMPLETE_LOG_MESSAGE = "Canonical event history is incomplete: OpenMausBot could not write one or more events to disk. Live updates will continue.";
|
|
59456
59704
|
var EventBus = class {
|
|
59457
59705
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -59479,12 +59727,12 @@ var EventBus = class {
|
|
|
59479
59727
|
const persistedEvents = pendingWarning ? [pendingWarning, redactSecrets(event)] : [redactSecrets(event)];
|
|
59480
59728
|
try {
|
|
59481
59729
|
this.appendLog(
|
|
59482
|
-
|
|
59730
|
+
join44(EVENTS_DIR, `${event.threadId}.ndjson`),
|
|
59483
59731
|
persistedEvents.map((entry) => JSON.stringify(entry)).join("\n") + "\n",
|
|
59484
59732
|
{ mode: 384 }
|
|
59485
59733
|
);
|
|
59486
59734
|
if (pendingWarning) this.pendingLogWarnings.delete(event.threadId);
|
|
59487
|
-
capThreadLog(
|
|
59735
|
+
capThreadLog(join44(EVENTS_DIR, `${event.threadId}.ndjson`), currentThreadLogCap());
|
|
59488
59736
|
} catch (error51) {
|
|
59489
59737
|
if (!pendingWarning) {
|
|
59490
59738
|
const warning = {
|
|
@@ -59762,7 +60010,7 @@ var ProviderRegistry = class {
|
|
|
59762
60010
|
// server/managed-desktop.ts
|
|
59763
60011
|
import { createHash as createHash16 } from "node:crypto";
|
|
59764
60012
|
import { lstatSync as lstatSync3, mkdirSync as mkdirSync20 } from "node:fs";
|
|
59765
|
-
import { join as
|
|
60013
|
+
import { join as join45 } from "node:path";
|
|
59766
60014
|
var uuid3 = external_exports.string().uuid();
|
|
59767
60015
|
var model = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/:+-]{0,199}$/).refine((value) => !value.includes("::"));
|
|
59768
60016
|
var connectionSchema = external_exports.object({
|
|
@@ -59801,14 +60049,14 @@ function companyInstanceConfigs(connection, runtimeDirectory) {
|
|
|
59801
60049
|
if (provider.id === "anthropic") entries[id] = {
|
|
59802
60050
|
driver: "claudeAgent",
|
|
59803
60051
|
displayName: displayName2,
|
|
59804
|
-
config: { configDir:
|
|
60052
|
+
config: { configDir: join45(runtimeDirectory, id, "claude"), managed: true },
|
|
59805
60053
|
environment: { ANTHROPIC_API_KEY: connection.token, ANTHROPIC_AUTH_TOKEN: connection.token, ANTHROPIC_BASE_URL: base2, ANTHROPIC_MODEL: provider.models[0] }
|
|
59806
60054
|
};
|
|
59807
60055
|
if (provider.id === "openai") entries[id] = {
|
|
59808
60056
|
driver: "codex",
|
|
59809
60057
|
displayName: displayName2,
|
|
59810
60058
|
config: { managed: { url: `${base2}/v1`, models: provider.models } },
|
|
59811
|
-
environment: { OPENMAUSBOT_COMPANY_API_KEY: connection.token, CODEX_HOME:
|
|
60059
|
+
environment: { OPENMAUSBOT_COMPANY_API_KEY: connection.token, CODEX_HOME: join45(runtimeDirectory, id, "codex") }
|
|
59812
60060
|
};
|
|
59813
60061
|
if (provider.id === "openrouter") entries[id] = {
|
|
59814
60062
|
driver: "openai-compat",
|
|
@@ -59926,7 +60174,7 @@ var ManagedDesktopProviders = class {
|
|
|
59926
60174
|
mkdirSync20(this.options.dataDirectory, { recursive: true, mode: 448 });
|
|
59927
60175
|
let directory = this.options.dataDirectory;
|
|
59928
60176
|
for (const part of parts) {
|
|
59929
|
-
directory =
|
|
60177
|
+
directory = join45(directory, part);
|
|
59930
60178
|
try {
|
|
59931
60179
|
mkdirSync20(directory, { mode: 448 });
|
|
59932
60180
|
} catch (error51) {
|
|
@@ -60288,7 +60536,7 @@ async function synthesize2(text8, voiceId, key3) {
|
|
|
60288
60536
|
import { execFile as execFile5 } from "node:child_process";
|
|
60289
60537
|
import { mkdtemp as mkdtemp4, readFile as readFile4, rm as rm4 } from "node:fs/promises";
|
|
60290
60538
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
60291
|
-
import { join as
|
|
60539
|
+
import { join as join46 } from "node:path";
|
|
60292
60540
|
import { promisify as promisify2 } from "node:util";
|
|
60293
60541
|
var SAY = "/usr/bin/say";
|
|
60294
60542
|
var execFileAsync = promisify2(execFile5);
|
|
@@ -60319,8 +60567,8 @@ async function listSystemVoices(run3 = defaultRun) {
|
|
|
60319
60567
|
async function synthesizeSystem(text8, voiceId, run3 = defaultRun) {
|
|
60320
60568
|
const trimmed = text8.trim();
|
|
60321
60569
|
if (!trimmed) return { bytes: new Uint8Array(), mime: "audio/wav" };
|
|
60322
|
-
const dir = await mkdtemp4(
|
|
60323
|
-
const out =
|
|
60570
|
+
const dir = await mkdtemp4(join46(tmpdir5(), "openmausbot-say-"));
|
|
60571
|
+
const out = join46(dir, "utterance.wav");
|
|
60324
60572
|
try {
|
|
60325
60573
|
const args = ["-o", out, "--data-format=LEI16@22050"];
|
|
60326
60574
|
if (voiceId?.trim()) args.push("-v", voiceId.trim());
|
|
@@ -60583,6 +60831,10 @@ function engineIsFresh(input) {
|
|
|
60583
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:]";
|
|
60584
60832
|
var FRESH_PREAMBLE = "[You are joining this conversation mid-thread (the user switched this bot over to you). The conversation so far:]";
|
|
60585
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
|
+
}
|
|
60586
60838
|
var RECOVERED_PREAMBLE = "[Your previous session for this conversation could not be resumed, so this is a new session. The conversation so far:]";
|
|
60587
60839
|
function buildRecoveryText(input) {
|
|
60588
60840
|
if (input.transcript.length === 0) return void 0;
|
|
@@ -60797,6 +61049,11 @@ var TurnResources = class {
|
|
|
60797
61049
|
if (sameOwner(current2, owner2)) this.owners.delete(key3);
|
|
60798
61050
|
}
|
|
60799
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
|
+
}
|
|
60800
61057
|
};
|
|
60801
61058
|
function sameOwner(a, b3) {
|
|
60802
61059
|
return a.threadId === b3.threadId && a.generation === b3.generation;
|
|
@@ -60820,8 +61077,8 @@ function overlaps(a, b3) {
|
|
|
60820
61077
|
// server/memory-store.ts
|
|
60821
61078
|
import { spawn as spawn3 } from "node:child_process";
|
|
60822
61079
|
import { createHash as createHash17 } from "node:crypto";
|
|
60823
|
-
import { existsSync as existsSync21, lstatSync as lstatSync5, mkdirSync as mkdirSync21, readdirSync as readdirSync5, readFileSync as readFileSync31, realpathSync as realpathSync7, unlinkSync as
|
|
60824
|
-
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";
|
|
60825
61082
|
var MEMORY_INDEX = "MEMORY.md";
|
|
60826
61083
|
var MemoryStoreError = class extends Error {
|
|
60827
61084
|
status;
|
|
@@ -60867,7 +61124,7 @@ function resolveMemoryPath(botId, path) {
|
|
|
60867
61124
|
assertBotId(botId);
|
|
60868
61125
|
const ref = parseMemoryPath(path);
|
|
60869
61126
|
const root = workspaceDir(botId);
|
|
60870
|
-
const absolute =
|
|
61127
|
+
const absolute = join47(root, ...relativeSegments(ref));
|
|
60871
61128
|
let realRoot;
|
|
60872
61129
|
try {
|
|
60873
61130
|
realRoot = realpathSync7(root);
|
|
@@ -60926,7 +61183,7 @@ function listMarkdown(dir, prefix) {
|
|
|
60926
61183
|
for (const name2 of names) {
|
|
60927
61184
|
if (!isMemoryTopicName(name2)) continue;
|
|
60928
61185
|
try {
|
|
60929
|
-
const info = lstatSync5(
|
|
61186
|
+
const info = lstatSync5(join47(dir, name2));
|
|
60930
61187
|
if (!info.isFile()) continue;
|
|
60931
61188
|
files.push({ path: `${prefix}/${name2}`, name: name2, bytes: info.size, modifiedAt: Math.round(info.mtimeMs) });
|
|
60932
61189
|
} catch {
|
|
@@ -60948,8 +61205,8 @@ function memoryOverview(botId) {
|
|
|
60948
61205
|
botId,
|
|
60949
61206
|
workspacePath: root,
|
|
60950
61207
|
index: memoryCapacity(raw),
|
|
60951
|
-
topics: listMarkdown(
|
|
60952
|
-
logs: listMarkdown(
|
|
61208
|
+
topics: listMarkdown(join47(root, "memory"), "memory"),
|
|
61209
|
+
logs: listMarkdown(join47(root, "memory", "log"), "memory/log")
|
|
60953
61210
|
};
|
|
60954
61211
|
}
|
|
60955
61212
|
function readMemoryDoc(botId, path) {
|
|
@@ -60964,7 +61221,7 @@ function readMemoryDoc(botId, path) {
|
|
|
60964
61221
|
function writeMemoryDoc(botId, path, text8, opts = {}) {
|
|
60965
61222
|
const ref = parseMemoryPath(path);
|
|
60966
61223
|
ensureWorkspace(botId);
|
|
60967
|
-
if (ref.kind === "log") mkdirSync21(
|
|
61224
|
+
if (ref.kind === "log") mkdirSync21(join47(workspaceDir(botId), "memory", "log"), { recursive: true, mode: 448 });
|
|
60968
61225
|
const absolute = resolveMemoryPath(botId, path);
|
|
60969
61226
|
const after = redactSecretsInText(text8);
|
|
60970
61227
|
const bytes = Buffer.byteLength(after, "utf8");
|
|
@@ -60994,7 +61251,7 @@ function deleteMemoryDoc(botId, path) {
|
|
|
60994
61251
|
}
|
|
60995
61252
|
const absolute = resolveMemoryPath(botId, path);
|
|
60996
61253
|
const current2 = readMemoryDoc(botId, path);
|
|
60997
|
-
if (current2.exists)
|
|
61254
|
+
if (current2.exists) unlinkSync9(absolute);
|
|
60998
61255
|
return { path, before: current2.exists ? current2.text : null };
|
|
60999
61256
|
}
|
|
61000
61257
|
function obsidianUrlFor(workspacePath) {
|
|
@@ -61048,8 +61305,8 @@ async function openMemoryLocation(botId, target, platform = process.platform, ru
|
|
|
61048
61305
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
61049
61306
|
import { closeSync as closeSync8, fstatSync as fstatSync3, mkdirSync as mkdirSync22, openSync as openSync8, readSync as readSync4 } from "node:fs";
|
|
61050
61307
|
import { appendFile as appendFile3 } from "node:fs/promises";
|
|
61051
|
-
import { join as
|
|
61052
|
-
var JOURNAL_DIR =
|
|
61308
|
+
import { join as join48 } from "node:path";
|
|
61309
|
+
var JOURNAL_DIR = join48(DATA_DIR, "memory-journal");
|
|
61053
61310
|
var DIFF_CAP = 8e3;
|
|
61054
61311
|
var BEFORE_CAP = 512 * 1024;
|
|
61055
61312
|
var MAX_JOURNAL_READ_BYTES = 8 * 1024 * 1024;
|
|
@@ -61057,7 +61314,7 @@ var MAX_ROWS = 500;
|
|
|
61057
61314
|
var LCS_CELL_CAP = 4e6;
|
|
61058
61315
|
var CONTEXT = 2;
|
|
61059
61316
|
function journalFile(botId) {
|
|
61060
|
-
return
|
|
61317
|
+
return join48(JOURNAL_DIR, `${botId}.ndjson`);
|
|
61061
61318
|
}
|
|
61062
61319
|
function editScript(a, b3) {
|
|
61063
61320
|
if (a.length * b3.length > LCS_CELL_CAP) {
|
|
@@ -61375,10 +61632,10 @@ import {
|
|
|
61375
61632
|
renameSync as renameSync5,
|
|
61376
61633
|
rmSync as rmSync6,
|
|
61377
61634
|
symlinkSync,
|
|
61378
|
-
unlinkSync as
|
|
61635
|
+
unlinkSync as unlinkSync10,
|
|
61379
61636
|
writeFileSync as writeFileSync15
|
|
61380
61637
|
} from "node:fs";
|
|
61381
|
-
import { dirname as dirname12, join as
|
|
61638
|
+
import { dirname as dirname12, join as join49, relative as relative4, resolve as resolve13 } from "node:path";
|
|
61382
61639
|
|
|
61383
61640
|
// shared/learn-request.ts
|
|
61384
61641
|
var SAVE_RUN_AS_SKILL_LINE = "Save the steps below as a reusable skill for my review.";
|
|
@@ -61509,7 +61766,7 @@ var skillManifestEntrySchema = external_exports.object({
|
|
|
61509
61766
|
var skillManifestSchema = external_exports.record(external_exports.string(), skillManifestEntrySchema);
|
|
61510
61767
|
var managedLinksSchema = external_exports.array(external_exports.string());
|
|
61511
61768
|
function skillsDir(botId) {
|
|
61512
|
-
return
|
|
61769
|
+
return join49(workspaceDir(botId), "skills");
|
|
61513
61770
|
}
|
|
61514
61771
|
function directoryEntryState(path) {
|
|
61515
61772
|
try {
|
|
@@ -61539,20 +61796,20 @@ function ensureSkillsRoot(botId) {
|
|
|
61539
61796
|
function existingSkillDirectory(botId, name2) {
|
|
61540
61797
|
const root = existingSkillsRoot(botId);
|
|
61541
61798
|
if (!root) return null;
|
|
61542
|
-
const directory =
|
|
61799
|
+
const directory = join49(root, name2);
|
|
61543
61800
|
return directoryEntryState(directory) === "directory" ? directory : null;
|
|
61544
61801
|
}
|
|
61545
61802
|
function skillDirectory(botId, name2, entry) {
|
|
61546
61803
|
if (!entry.storageRevision) return existingSkillDirectory(botId, name2);
|
|
61547
61804
|
const root = existingSkillsRoot(botId);
|
|
61548
61805
|
if (!root) return null;
|
|
61549
|
-
const revisions =
|
|
61806
|
+
const revisions = join49(root, ".revisions");
|
|
61550
61807
|
if (directoryEntryState(revisions) !== "directory") return null;
|
|
61551
|
-
const directory =
|
|
61808
|
+
const directory = join49(revisions, entry.storageRevision);
|
|
61552
61809
|
return directoryEntryState(directory) === "directory" ? directory : null;
|
|
61553
61810
|
}
|
|
61554
61811
|
function skillTarget(root, name2, entry) {
|
|
61555
|
-
return entry.storageRevision ?
|
|
61812
|
+
return entry.storageRevision ? join49(root, "skills", ".revisions", entry.storageRevision) : join49(root, "skills", name2);
|
|
61556
61813
|
}
|
|
61557
61814
|
function entryExistsWithoutFollowing(path) {
|
|
61558
61815
|
try {
|
|
@@ -61565,7 +61822,7 @@ function entryExistsWithoutFollowing(path) {
|
|
|
61565
61822
|
function nativeLinkDirectory(root, relative6, create) {
|
|
61566
61823
|
const [family, leaf] = relative6.split("/");
|
|
61567
61824
|
if (!family || !leaf) return null;
|
|
61568
|
-
const familyDir =
|
|
61825
|
+
const familyDir = join49(root, family);
|
|
61569
61826
|
let familyState = directoryEntryState(familyDir);
|
|
61570
61827
|
if (familyState === "missing" && create) {
|
|
61571
61828
|
try {
|
|
@@ -61576,7 +61833,7 @@ function nativeLinkDirectory(root, relative6, create) {
|
|
|
61576
61833
|
familyState = directoryEntryState(familyDir);
|
|
61577
61834
|
}
|
|
61578
61835
|
if (familyState !== "directory") return null;
|
|
61579
|
-
const linkDir =
|
|
61836
|
+
const linkDir = join49(familyDir, leaf);
|
|
61580
61837
|
let linkState = directoryEntryState(linkDir);
|
|
61581
61838
|
if (linkState === "missing" && create) {
|
|
61582
61839
|
try {
|
|
@@ -61589,13 +61846,13 @@ function nativeLinkDirectory(root, relative6, create) {
|
|
|
61589
61846
|
return linkState === "directory" ? linkDir : null;
|
|
61590
61847
|
}
|
|
61591
61848
|
function skillStateDir(botId) {
|
|
61592
|
-
return
|
|
61849
|
+
return join49(DATA_DIR, "skill-state", botId);
|
|
61593
61850
|
}
|
|
61594
61851
|
function manifestPath(botId) {
|
|
61595
|
-
return
|
|
61852
|
+
return join49(skillStateDir(botId), "skills.json");
|
|
61596
61853
|
}
|
|
61597
61854
|
function managedLinksPath(botId) {
|
|
61598
|
-
return
|
|
61855
|
+
return join49(skillStateDir(botId), "managed-links.json");
|
|
61599
61856
|
}
|
|
61600
61857
|
function readManagedLinks(botId) {
|
|
61601
61858
|
try {
|
|
@@ -61630,7 +61887,7 @@ function readManifest(botId) {
|
|
|
61630
61887
|
if (existsSync22(securePath)) return manifestFromFile(securePath) ?? {};
|
|
61631
61888
|
const legacyRoot = existingSkillsRoot(botId);
|
|
61632
61889
|
if (!legacyRoot) return {};
|
|
61633
|
-
const legacyPath =
|
|
61890
|
+
const legacyPath = join49(legacyRoot, "skills.json");
|
|
61634
61891
|
if (!existsSync22(legacyPath)) return {};
|
|
61635
61892
|
const legacy = manifestFromFile(legacyPath) ?? {};
|
|
61636
61893
|
const migrated = {};
|
|
@@ -61672,20 +61929,20 @@ function nativeLinkDirectlyTargetsOwnedSkill(link2, root, name2, revisionWasMana
|
|
|
61672
61929
|
if (!lstatSync6(link2).isSymbolicLink()) return false;
|
|
61673
61930
|
const rawTarget = readlinkSync(link2);
|
|
61674
61931
|
const resolvedTarget = resolve13(targetBaseDirectory, rawTarget.replace(/^\\\\\?\\/, ""));
|
|
61675
|
-
const insideSkills = relative4(
|
|
61932
|
+
const insideSkills = relative4(join49(root, "skills"), resolvedTarget).replaceAll("\\", "/");
|
|
61676
61933
|
return insideSkills === name2 || revisionWasManaged && /^\.revisions\/[a-f0-9]{64}$/.test(insideSkills);
|
|
61677
61934
|
} catch {
|
|
61678
61935
|
return false;
|
|
61679
61936
|
}
|
|
61680
61937
|
}
|
|
61681
61938
|
function removeOwnedNativeLink(botId, link2, root, name2, revisionWasManaged, beforeRemove) {
|
|
61682
|
-
const quarantineDir =
|
|
61939
|
+
const quarantineDir = join49(skillStateDir(botId), "link-removal");
|
|
61683
61940
|
try {
|
|
61684
61941
|
mkdirSync23(quarantineDir, { recursive: true, mode: 448 });
|
|
61685
61942
|
} catch {
|
|
61686
61943
|
return "retry";
|
|
61687
61944
|
}
|
|
61688
|
-
const quarantined =
|
|
61945
|
+
const quarantined = join49(quarantineDir, randomUUID17());
|
|
61689
61946
|
beforeRemove?.(link2);
|
|
61690
61947
|
try {
|
|
61691
61948
|
renameSync5(link2, quarantined);
|
|
@@ -61700,7 +61957,7 @@ function removeOwnedNativeLink(botId, link2, root, name2, revisionWasManaged, be
|
|
|
61700
61957
|
dirname12(link2)
|
|
61701
61958
|
)) {
|
|
61702
61959
|
try {
|
|
61703
|
-
|
|
61960
|
+
unlinkSync10(quarantined);
|
|
61704
61961
|
return "removed";
|
|
61705
61962
|
} catch {
|
|
61706
61963
|
try {
|
|
@@ -61738,7 +61995,7 @@ function removeNativeLinksForUnsafeSkillsRoot(botId, root, previouslyManaged, be
|
|
|
61738
61995
|
continue;
|
|
61739
61996
|
}
|
|
61740
61997
|
for (const name2 of /* @__PURE__ */ new Set([...existing, ...previouslyManaged])) {
|
|
61741
|
-
const link2 =
|
|
61998
|
+
const link2 = join49(linkDir, name2);
|
|
61742
61999
|
if (!nativeLinkDirectlyTargetsOwnedSkill(link2, root, name2, previouslyManaged.includes(name2))) continue;
|
|
61743
62000
|
const result = removeOwnedNativeLink(
|
|
61744
62001
|
botId,
|
|
@@ -61778,7 +62035,7 @@ function syncSkillLinks(botId, options = {}) {
|
|
|
61778
62035
|
} catch {
|
|
61779
62036
|
}
|
|
61780
62037
|
for (const name2 of /* @__PURE__ */ new Set([...existing, ...previouslyManaged])) {
|
|
61781
|
-
const link2 =
|
|
62038
|
+
const link2 = join49(linkDir, name2);
|
|
61782
62039
|
const target = desired.get(name2);
|
|
61783
62040
|
if (target && nativeLinkPointsToSkill(link2, target)) {
|
|
61784
62041
|
managed.add(name2);
|
|
@@ -61796,7 +62053,7 @@ function syncSkillLinks(botId, options = {}) {
|
|
|
61796
62053
|
}
|
|
61797
62054
|
if (!enabled.length) continue;
|
|
61798
62055
|
for (const [name2, entry] of enabled) {
|
|
61799
|
-
const link2 =
|
|
62056
|
+
const link2 = join49(linkDir, name2);
|
|
61800
62057
|
const target = skillTarget(root, name2, entry);
|
|
61801
62058
|
if (nativeLinkPointsToSkill(link2, target)) {
|
|
61802
62059
|
managed.add(name2);
|
|
@@ -61822,7 +62079,7 @@ function skillContentMatches(botId, name2, entry) {
|
|
|
61822
62079
|
try {
|
|
61823
62080
|
const directory = skillDirectory(botId, name2, entry);
|
|
61824
62081
|
if (!directory) return false;
|
|
61825
|
-
const file2 =
|
|
62082
|
+
const file2 = join49(directory, "SKILL.md");
|
|
61826
62083
|
if (!lstatSync6(file2).isFile()) return false;
|
|
61827
62084
|
return createHash18("sha256").update(readFileSync32(file2)).digest("hex") === entry.sha256;
|
|
61828
62085
|
} catch {
|
|
@@ -61852,7 +62109,7 @@ function readSkillFile(botId, name2) {
|
|
|
61852
62109
|
if (!directory) return null;
|
|
61853
62110
|
let descriptor = null;
|
|
61854
62111
|
try {
|
|
61855
|
-
const path =
|
|
62112
|
+
const path = join49(directory, "SKILL.md");
|
|
61856
62113
|
const before = lstatSync6(path);
|
|
61857
62114
|
if (!before.isFile() || before.size > SKILL_FILE_MAX_BYTES) return null;
|
|
61858
62115
|
descriptor = openSync9(path, constants4.O_RDONLY | (process.platform === "win32" ? 0 : constants4.O_NOFOLLOW ?? 0));
|
|
@@ -61892,9 +62149,9 @@ function setSkillEnabled(botId, name2, enabled) {
|
|
|
61892
62149
|
function removeReviewedRevision(botId, revision, sha2564) {
|
|
61893
62150
|
const root = existingSkillsRoot(botId);
|
|
61894
62151
|
if (!root) return;
|
|
61895
|
-
const revisions =
|
|
62152
|
+
const revisions = join49(root, ".revisions");
|
|
61896
62153
|
if (directoryEntryState(revisions) !== "directory") return;
|
|
61897
|
-
const directory =
|
|
62154
|
+
const directory = join49(revisions, revision);
|
|
61898
62155
|
if (!learnedSkillDirectoryMatches(directory, sha2564)) return;
|
|
61899
62156
|
try {
|
|
61900
62157
|
rmSync6(directory, { recursive: true, force: true });
|
|
@@ -61916,7 +62173,7 @@ function retireReviewedSkillStorage(botId, name2, entry) {
|
|
|
61916
62173
|
function removeReviewedRevisionsNamed(botId, name2) {
|
|
61917
62174
|
const root = existingSkillsRoot(botId);
|
|
61918
62175
|
if (!root) return;
|
|
61919
|
-
const revisions =
|
|
62176
|
+
const revisions = join49(root, ".revisions");
|
|
61920
62177
|
if (directoryEntryState(revisions) !== "directory") return;
|
|
61921
62178
|
let candidates;
|
|
61922
62179
|
try {
|
|
@@ -61925,10 +62182,10 @@ function removeReviewedRevisionsNamed(botId, name2) {
|
|
|
61925
62182
|
return;
|
|
61926
62183
|
}
|
|
61927
62184
|
for (const revision of candidates) {
|
|
61928
|
-
const directory =
|
|
62185
|
+
const directory = join49(revisions, revision);
|
|
61929
62186
|
if (directoryEntryState(directory) !== "directory") continue;
|
|
61930
62187
|
try {
|
|
61931
|
-
const file2 =
|
|
62188
|
+
const file2 = join49(directory, "SKILL.md");
|
|
61932
62189
|
const stat6 = lstatSync6(file2);
|
|
61933
62190
|
if (!stat6.isFile() || stat6.size > SKILL_FILE_MAX_BYTES) continue;
|
|
61934
62191
|
const parsed = parseSkillMd(readFileSync32(file2, "utf8"));
|
|
@@ -61947,7 +62204,7 @@ function removeSkill(botId, name2) {
|
|
|
61947
62204
|
if (directoryEntryState(root) === "unsafe") {
|
|
61948
62205
|
return { error: "the workspace skills path is a symlink or file; refusing to remove through it" };
|
|
61949
62206
|
}
|
|
61950
|
-
const target = entry.storageRevision ? null :
|
|
62207
|
+
const target = entry.storageRevision ? null : join49(root, name2);
|
|
61951
62208
|
const targetState = target ? directoryEntryState(target) : "missing";
|
|
61952
62209
|
delete manifest[name2];
|
|
61953
62210
|
writeManifest(botId, manifest);
|
|
@@ -61980,7 +62237,7 @@ var stagedSkillWriteSchema = external_exports.object({
|
|
|
61980
62237
|
});
|
|
61981
62238
|
var stagedStoreSchema = external_exports.object({ writes: external_exports.record(external_exports.string(), stagedSkillWriteSchema) });
|
|
61982
62239
|
function stagedPath(botId) {
|
|
61983
|
-
return
|
|
62240
|
+
return join49(skillStateDir(botId), "staged.json");
|
|
61984
62241
|
}
|
|
61985
62242
|
function readStaged(botId) {
|
|
61986
62243
|
const securePath = stagedPath(botId);
|
|
@@ -61995,7 +62252,7 @@ function readStaged(botId) {
|
|
|
61995
62252
|
}
|
|
61996
62253
|
const legacyRoot = existingSkillsRoot(botId);
|
|
61997
62254
|
if (!legacyRoot) return { writes: {} };
|
|
61998
|
-
const legacyPath =
|
|
62255
|
+
const legacyPath = join49(legacyRoot, "staged.json");
|
|
61999
62256
|
if (!existsSync22(legacyPath)) return { writes: {} };
|
|
62000
62257
|
const empty = { writes: {} };
|
|
62001
62258
|
writeStaged(botId, empty);
|
|
@@ -62044,7 +62301,7 @@ function learnedSkillDirectoryMatches(directory, sha2564) {
|
|
|
62044
62301
|
try {
|
|
62045
62302
|
const entries = readdirSync6(directory);
|
|
62046
62303
|
if (entries.length !== 1 || entries[0] !== "SKILL.md") return false;
|
|
62047
|
-
const file2 =
|
|
62304
|
+
const file2 = join49(directory, "SKILL.md");
|
|
62048
62305
|
if (!lstatSync6(file2).isFile()) return false;
|
|
62049
62306
|
return createHash18("sha256").update(readFileSync32(file2)).digest("hex") === sha2564;
|
|
62050
62307
|
} catch {
|
|
@@ -62073,14 +62330,14 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62073
62330
|
if (directoryEntryState(state) !== "directory") {
|
|
62074
62331
|
throw new Error("the protected skill state path is not a real directory");
|
|
62075
62332
|
}
|
|
62076
|
-
const preparedRoot =
|
|
62333
|
+
const preparedRoot = join49(state, "reviewed-revisions");
|
|
62077
62334
|
const preparedRootState = directoryEntryState(preparedRoot);
|
|
62078
62335
|
if (preparedRootState === "unsafe") {
|
|
62079
62336
|
throw new Error("the protected revision path is not a real directory");
|
|
62080
62337
|
}
|
|
62081
62338
|
if (preparedRootState === "missing") mkdirSync23(preparedRoot, { mode: 448 });
|
|
62082
62339
|
const revision = createHash18("sha256").update(stageId).digest("hex");
|
|
62083
|
-
const prepared =
|
|
62340
|
+
const prepared = join49(preparedRoot, revision);
|
|
62084
62341
|
if (!learnedSkillDirectoryMatches(prepared, sha2564)) {
|
|
62085
62342
|
if (entryExistsWithoutFollowing(prepared)) {
|
|
62086
62343
|
if (directoryEntryState(prepared) !== "directory") {
|
|
@@ -62088,10 +62345,10 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62088
62345
|
}
|
|
62089
62346
|
rmSync6(prepared, { recursive: true, force: true });
|
|
62090
62347
|
}
|
|
62091
|
-
const temporary =
|
|
62348
|
+
const temporary = join49(preparedRoot, `.prepare-${revision}-${randomUUID17()}`);
|
|
62092
62349
|
try {
|
|
62093
62350
|
mkdirSync23(temporary, { mode: 448 });
|
|
62094
|
-
writeFileAtomic(
|
|
62351
|
+
writeFileAtomic(join49(temporary, "SKILL.md"), skillMd, { mode: 384 });
|
|
62095
62352
|
if (!learnedSkillDirectoryMatches(temporary, sha2564)) {
|
|
62096
62353
|
throw new Error("the protected reviewed bytes do not match the approval card");
|
|
62097
62354
|
}
|
|
@@ -62105,13 +62362,13 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62105
62362
|
if (!root) throw new Error("the workspace skills path must be a real directory, not a symlink or file");
|
|
62106
62363
|
const rootIdentity = directoryIdentity2(root);
|
|
62107
62364
|
if (!rootIdentity) throw new Error("the workspace skills path changed during update");
|
|
62108
|
-
const revisions =
|
|
62365
|
+
const revisions = join49(root, ".revisions");
|
|
62109
62366
|
const revisionsState = directoryEntryState(revisions);
|
|
62110
62367
|
if (revisionsState === "unsafe") throw new Error("the skill revisions path is not a real directory");
|
|
62111
62368
|
if (revisionsState === "missing") mkdirSync23(revisions, { mode: 448 });
|
|
62112
62369
|
const revisionsIdentity = directoryIdentity2(revisions);
|
|
62113
62370
|
if (!revisionsIdentity) throw new Error("the skill revisions path could not be created safely");
|
|
62114
|
-
const target =
|
|
62371
|
+
const target = join49(revisions, revision);
|
|
62115
62372
|
if (learnedSkillDirectoryMatches(target, sha2564)) {
|
|
62116
62373
|
try {
|
|
62117
62374
|
rmSync6(prepared, { recursive: true, force: true });
|
|
@@ -62134,14 +62391,14 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
62134
62391
|
function commitNewSkillFiles(botId, name2, files, commitManifest) {
|
|
62135
62392
|
const root = ensureSkillsRoot(botId);
|
|
62136
62393
|
if (!root) throw new Error("the workspace skills path must be a real directory, not a symlink or file");
|
|
62137
|
-
const target =
|
|
62138
|
-
const staged =
|
|
62394
|
+
const target = join49(root, name2);
|
|
62395
|
+
const staged = join49(root, `.install-${name2}-${randomUUID17()}`);
|
|
62139
62396
|
if (entryExistsWithoutFollowing(target)) throw new Error(`skill path already exists: ${name2}`);
|
|
62140
62397
|
let published = false;
|
|
62141
62398
|
try {
|
|
62142
62399
|
mkdirSync23(staged, { mode: 448 });
|
|
62143
62400
|
for (const file2 of files) {
|
|
62144
|
-
writeFileSync15(
|
|
62401
|
+
writeFileSync15(join49(staged, file2.path), file2.content, { mode: 384 });
|
|
62145
62402
|
}
|
|
62146
62403
|
if (directoryEntryState(root) !== "directory" || entryExistsWithoutFollowing(target)) {
|
|
62147
62404
|
throw new Error("the workspace skills path changed during installation");
|
|
@@ -62185,7 +62442,7 @@ function installPreparedSkill(botId, source, prepared, options) {
|
|
|
62185
62442
|
};
|
|
62186
62443
|
const root = ensureSkillsRoot(botId);
|
|
62187
62444
|
if (!root) return { error: "the workspace skills path must be a real directory, not a symlink or file" };
|
|
62188
|
-
const target =
|
|
62445
|
+
const target = join49(root, name2);
|
|
62189
62446
|
if (entryExistsWithoutFollowing(target)) {
|
|
62190
62447
|
if (directoryEntryState(target) !== "directory") {
|
|
62191
62448
|
return { error: `skill path must be a real directory, not a symlink or file: ${name2}` };
|
|
@@ -62349,7 +62606,7 @@ function rejectStagedSkillWrite(botId, id) {
|
|
|
62349
62606
|
if (staged?.action === "update" && !alreadyApplied) {
|
|
62350
62607
|
const revision = createHash18("sha256").update(id).digest("hex");
|
|
62351
62608
|
removeReviewedRevision(botId, revision, staged.sha256);
|
|
62352
|
-
const prepared =
|
|
62609
|
+
const prepared = join49(skillStateDir(botId), "reviewed-revisions", revision);
|
|
62353
62610
|
if (learnedSkillDirectoryMatches(prepared, staged.sha256)) {
|
|
62354
62611
|
try {
|
|
62355
62612
|
rmSync6(prepared, { recursive: true, force: true });
|
|
@@ -62426,7 +62683,7 @@ function skillsSystemPrompt(botId) {
|
|
|
62426
62683
|
let bytes = 0;
|
|
62427
62684
|
for (const skill of enabled.slice(0, INDEX_MAX_SKILLS)) {
|
|
62428
62685
|
const entry = manifest[skill.name];
|
|
62429
|
-
const file2 =
|
|
62686
|
+
const file2 = join49(skillTarget(root, skill.name, entry), "SKILL.md");
|
|
62430
62687
|
const line = `- ${skill.name}: ${skill.description} Read ${JSON.stringify(file2)}.`;
|
|
62431
62688
|
bytes += Buffer.byteLength(line, "utf8");
|
|
62432
62689
|
if (bytes > INDEX_MAX_BYTES) break;
|
|
@@ -62486,7 +62743,7 @@ function parseSkillSource(input) {
|
|
|
62486
62743
|
const text8 = input.trim();
|
|
62487
62744
|
if (!text8) return { error: "paste a GitHub repository, folder, or SKILL.md URL" };
|
|
62488
62745
|
if (/^https?:\/\/raw\.githubusercontent\.com\/.+\/SKILL\.md$/i.test(text8)) return { rawUrl: text8 };
|
|
62489
|
-
const blob = text8.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(
|
|
62746
|
+
const blob = text8.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/((?:.*\/)?SKILL\.md)$/i);
|
|
62490
62747
|
if (blob) {
|
|
62491
62748
|
return { rawUrl: `https://raw.githubusercontent.com/${blob[1]}/${blob[2]}/${blob[3]}/${blob[4]}` };
|
|
62492
62749
|
}
|
|
@@ -62683,7 +62940,7 @@ function mentionPrompt(tagged) {
|
|
|
62683
62940
|
// server/local-computer.ts
|
|
62684
62941
|
import { lstatSync as lstatSync7, readFileSync as readFileSync33, realpathSync as realpathSync9, statSync as statSync8 } from "node:fs";
|
|
62685
62942
|
import { homedir as homedir23 } from "node:os";
|
|
62686
|
-
import { dirname as dirname13, isAbsolute as isAbsolute10, join as
|
|
62943
|
+
import { dirname as dirname13, isAbsolute as isAbsolute10, join as join50 } from "node:path";
|
|
62687
62944
|
var REQUIRED_LINUX_TOOLS = ["click", "get_window_state", "list_apps", "type_text"];
|
|
62688
62945
|
var DRIVER_FILE_IDENTITY_KEYS = [
|
|
62689
62946
|
"dev",
|
|
@@ -62868,10 +63125,10 @@ function readCuaConnection({
|
|
|
62868
63125
|
home = homedir23(),
|
|
62869
63126
|
validateLinuxRuntime = validateLinuxDescriptorRuntime
|
|
62870
63127
|
} = {}) {
|
|
62871
|
-
const candidates = userData ? [
|
|
63128
|
+
const candidates = userData ? [join50(userData, "cua-connection.json")] : [];
|
|
62872
63129
|
if (platform === "darwin") {
|
|
62873
63130
|
for (const directory of ["OpenMausBot", "openmausbot", "OpenGrokBot", "opengrokbot"]) {
|
|
62874
|
-
candidates.push(
|
|
63131
|
+
candidates.push(join50(home, "Library", "Application Support", directory, "cua-connection.json"));
|
|
62875
63132
|
}
|
|
62876
63133
|
}
|
|
62877
63134
|
for (const file2 of new Set(candidates)) {
|
|
@@ -63045,12 +63302,12 @@ import { createConnection, createServer } from "node:net";
|
|
|
63045
63302
|
// server/vps-ssh.ts
|
|
63046
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";
|
|
63047
63304
|
import { homedir as homedir24 } from "node:os";
|
|
63048
|
-
import { delimiter as delimiter4, join as
|
|
63305
|
+
import { delimiter as delimiter4, join as join51 } from "node:path";
|
|
63049
63306
|
var VPS_SSH_DIR = "ssh";
|
|
63050
63307
|
function findExecutable(name2, pathValue, exclude) {
|
|
63051
63308
|
for (const dir of pathValue.split(delimiter4)) {
|
|
63052
63309
|
if (!dir || dir === exclude) continue;
|
|
63053
|
-
const candidate =
|
|
63310
|
+
const candidate = join51(dir, name2);
|
|
63054
63311
|
try {
|
|
63055
63312
|
const stat6 = statSync9(candidate);
|
|
63056
63313
|
if (stat6.isFile() && (stat6.mode & 73) !== 0) return candidate;
|
|
@@ -63071,14 +63328,14 @@ function writeIfChanged(path, content3, mode) {
|
|
|
63071
63328
|
writeFileSync16(temp, content3, { mode });
|
|
63072
63329
|
renameSync6(temp, path);
|
|
63073
63330
|
}
|
|
63074
|
-
function vpsSshConfigText(sshDir, userConfig =
|
|
63331
|
+
function vpsSshConfigText(sshDir, userConfig = join51(homedir24(), ".ssh", "config"), systemConfig = "/etc/ssh/ssh_config") {
|
|
63075
63332
|
return [
|
|
63076
63333
|
"# Written by OpenMausBot for its VPS computer connections. Do not edit;",
|
|
63077
63334
|
"# it is regenerated. Your own ~/.ssh/config is included first and wins.",
|
|
63078
63335
|
...existsSync23(userConfig) ? [`Include ${userConfig}`] : [],
|
|
63079
63336
|
"Host *",
|
|
63080
63337
|
" ControlMaster auto",
|
|
63081
|
-
` ControlPath ${
|
|
63338
|
+
` ControlPath ${join51(sshDir, "cm-%C")}`,
|
|
63082
63339
|
" ControlPersist 10m",
|
|
63083
63340
|
" ServerAliveInterval 15",
|
|
63084
63341
|
" ServerAliveCountMax 3",
|
|
@@ -63089,11 +63346,11 @@ function vpsSshConfigText(sshDir, userConfig = join50(homedir24(), ".ssh", "conf
|
|
|
63089
63346
|
}
|
|
63090
63347
|
function prepareVpsSsh(dataDir, pathValue, platform = process.platform) {
|
|
63091
63348
|
if (platform === "win32") return { configPath: null, path: pathValue };
|
|
63092
|
-
const sshDir =
|
|
63093
|
-
const binDir =
|
|
63349
|
+
const sshDir = join51(dataDir, VPS_SSH_DIR);
|
|
63350
|
+
const binDir = join51(sshDir, "bin");
|
|
63094
63351
|
mkdirSync24(binDir, { recursive: true, mode: 448 });
|
|
63095
63352
|
chmodSync5(sshDir, 448);
|
|
63096
|
-
const configPath =
|
|
63353
|
+
const configPath = join51(sshDir, "config");
|
|
63097
63354
|
writeIfChanged(configPath, vpsSshConfigText(sshDir), 384);
|
|
63098
63355
|
const realSsh = findExecutable("ssh", pathValue, binDir);
|
|
63099
63356
|
if (!realSsh) return { configPath, path: pathValue };
|
|
@@ -63104,7 +63361,7 @@ function prepareVpsSsh(dataDir, pathValue, platform = process.platform) {
|
|
|
63104
63361
|
`exec ${JSON.stringify(realSsh)} -F ${JSON.stringify(configPath)} "$@"`,
|
|
63105
63362
|
""
|
|
63106
63363
|
].join("\n");
|
|
63107
|
-
writeIfChanged(
|
|
63364
|
+
writeIfChanged(join51(binDir, "ssh"), shim, 448);
|
|
63108
63365
|
return { configPath, path: `${binDir}${delimiter4}${pathValue}` };
|
|
63109
63366
|
}
|
|
63110
63367
|
|
|
@@ -64033,7 +64290,7 @@ async function vpsComputerScreenshot(cfg2, botId, runner = defaultRunner) {
|
|
|
64033
64290
|
// server/routines.ts
|
|
64034
64291
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
64035
64292
|
import { mkdirSync as mkdirSync25, readFileSync as readFileSync35 } from "node:fs";
|
|
64036
|
-
import { dirname as dirname14, join as
|
|
64293
|
+
import { dirname as dirname14, join as join52 } from "node:path";
|
|
64037
64294
|
|
|
64038
64295
|
// node_modules/.pnpm/croner@10.0.1/node_modules/croner/dist/croner.js
|
|
64039
64296
|
function T(s4) {
|
|
@@ -65146,7 +65403,7 @@ var RoutineManager = class {
|
|
|
65146
65403
|
ticking = false;
|
|
65147
65404
|
constructor(options) {
|
|
65148
65405
|
this.options = options;
|
|
65149
|
-
this.file = options.file ??
|
|
65406
|
+
this.file = options.file ?? join52(DATA_DIR, "routines.json");
|
|
65150
65407
|
this.now = options.now ?? Date.now;
|
|
65151
65408
|
try {
|
|
65152
65409
|
const disk = JSON.parse(readFileSync35(this.file, "utf8"));
|
|
@@ -66058,7 +66315,7 @@ var RoutineManager = class {
|
|
|
66058
66315
|
// server/calendar-calls.ts
|
|
66059
66316
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
66060
66317
|
import { mkdirSync as mkdirSync26, readFileSync as readFileSync36 } from "node:fs";
|
|
66061
|
-
import { dirname as dirname15, join as
|
|
66318
|
+
import { dirname as dirname15, join as join53 } from "node:path";
|
|
66062
66319
|
var MAX_BOTS = 100;
|
|
66063
66320
|
var MAX_ATTACHMENTS2 = 50;
|
|
66064
66321
|
var scheduleSchema = external_exports.discriminatedUnion("type", [
|
|
@@ -66164,7 +66421,7 @@ var CalendarCallManager = class {
|
|
|
66164
66421
|
timer = null;
|
|
66165
66422
|
ticking = false;
|
|
66166
66423
|
constructor(options) {
|
|
66167
|
-
this.file = options.file ??
|
|
66424
|
+
this.file = options.file ?? join53(DATA_DIR, "calendar-calls.json");
|
|
66168
66425
|
this.now = options.now ?? Date.now;
|
|
66169
66426
|
this.botExists = options.botExists;
|
|
66170
66427
|
this.onDue = options.onDue;
|
|
@@ -66334,12 +66591,12 @@ var CalendarCallManager = class {
|
|
|
66334
66591
|
// server/browser-engine.ts
|
|
66335
66592
|
import { spawn as spawn5 } from "node:child_process";
|
|
66336
66593
|
import { createHash as createHash20, randomBytes as randomBytes4, randomUUID as randomUUID20 } from "node:crypto";
|
|
66337
|
-
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";
|
|
66338
66595
|
import { homedir as homedir25, tmpdir as tmpdir6 } from "node:os";
|
|
66339
|
-
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";
|
|
66340
66597
|
|
|
66341
66598
|
// server/browser-bundle-release.ts
|
|
66342
|
-
import { join as
|
|
66599
|
+
import { join as join54 } from "node:path";
|
|
66343
66600
|
|
|
66344
66601
|
// server/browser-engine-release.ts
|
|
66345
66602
|
var AGENT_BROWSER_VERSION = "0.37.0";
|
|
@@ -66394,12 +66651,13 @@ function resolveAgentBrowserReleaseAsset(platform = process.platform, arch = pro
|
|
|
66394
66651
|
}
|
|
66395
66652
|
|
|
66396
66653
|
// server/browser-bundle-release.ts
|
|
66397
|
-
var CHROME_VERSION = "
|
|
66654
|
+
var CHROME_VERSION = "153.0.8010.47";
|
|
66398
66655
|
var CHROME_ASSETS = {
|
|
66399
|
-
"darwin-arm64": { platform: "mac-arm64", bytes:
|
|
66400
|
-
"darwin-x64": { platform: "mac-x64", bytes:
|
|
66401
|
-
"linux-
|
|
66402
|
-
"
|
|
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" }
|
|
66403
66661
|
};
|
|
66404
66662
|
function browserBundleSpec(target) {
|
|
66405
66663
|
if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
|
|
@@ -66436,10 +66694,10 @@ function browserBundlePaths(bundleDirectory, target) {
|
|
|
66436
66694
|
const spec = browserBundleSpec(target);
|
|
66437
66695
|
return {
|
|
66438
66696
|
directory: bundleDirectory,
|
|
66439
|
-
manifest:
|
|
66440
|
-
engine:
|
|
66441
|
-
chrome:
|
|
66442
|
-
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")
|
|
66443
66701
|
};
|
|
66444
66702
|
}
|
|
66445
66703
|
|
|
@@ -66835,11 +67093,11 @@ function browserSessionsDirectory(env) {
|
|
|
66835
67093
|
const namespace = env.AGENT_BROWSER_NAMESPACE;
|
|
66836
67094
|
if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
|
|
66837
67095
|
const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir25();
|
|
66838
|
-
const stateRoot = namespace ?
|
|
66839
|
-
return
|
|
67096
|
+
const stateRoot = namespace ? join55(home, ".agent-browser", "namespaces", namespace, "state") : join55(home, ".agent-browser");
|
|
67097
|
+
return join55(stateRoot, "sessions");
|
|
66840
67098
|
}
|
|
66841
67099
|
function managedBrowserConfigPath(env) {
|
|
66842
|
-
return
|
|
67100
|
+
return join55(browserSessionsDirectory(env), "..", "omb-managed-config.json");
|
|
66843
67101
|
}
|
|
66844
67102
|
function ensureManagedBrowserConfig(env) {
|
|
66845
67103
|
const path = managedBrowserConfigPath(env);
|
|
@@ -66927,11 +67185,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
66927
67185
|
ensureManagedBrowserConfig(env);
|
|
66928
67186
|
if (options.persistent === false) return;
|
|
66929
67187
|
const directory = browserSessionsDirectory(env);
|
|
66930
|
-
const key3 =
|
|
67188
|
+
const key3 = join55(directory, session);
|
|
66931
67189
|
let pending = preparedBrowserSessions.get(key3);
|
|
66932
67190
|
if (!pending) {
|
|
66933
67191
|
pending = (async () => {
|
|
66934
|
-
const targetBase =
|
|
67192
|
+
const targetBase = join55(directory, `${restoreKey}-${session}`);
|
|
66935
67193
|
const hasTarget = () => existsSync24(`${targetBase}.json.enc`) || existsSync24(`${targetBase}.json`);
|
|
66936
67194
|
if (hasTarget()) return;
|
|
66937
67195
|
check2();
|
|
@@ -66941,7 +67199,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
66941
67199
|
check2();
|
|
66942
67200
|
if (hasTarget()) return;
|
|
66943
67201
|
const candidates = [".json.enc", ".json"].flatMap((suffix) => {
|
|
66944
|
-
const path =
|
|
67202
|
+
const path = join55(directory, `${session}-${session}${suffix}`);
|
|
66945
67203
|
try {
|
|
66946
67204
|
const stat6 = statSync10(path);
|
|
66947
67205
|
if (!stat6.isFile()) throw new Error("Saved browser state is not a file.");
|
|
@@ -66976,7 +67234,7 @@ function isMusl(platform = process.platform, exists = existsSync24) {
|
|
|
66976
67234
|
}
|
|
66977
67235
|
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
66978
67236
|
const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
|
|
66979
|
-
return
|
|
67237
|
+
return join55(dataDir, ENGINE_DIR, version2, executableName(platform));
|
|
66980
67238
|
}
|
|
66981
67239
|
function onPath(env, platform, exists) {
|
|
66982
67240
|
const pathValue = platform === "win32" ? Object.entries(env).findLast(([key3]) => key3.toUpperCase() === "PATH")?.[1] : env.PATH;
|
|
@@ -66992,7 +67250,7 @@ function packagedBrowser(options) {
|
|
|
66992
67250
|
const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
|
|
66993
67251
|
if (!resources) return null;
|
|
66994
67252
|
try {
|
|
66995
|
-
return browserBundlePaths(
|
|
67253
|
+
return browserBundlePaths(join55(resolve14(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
|
|
66996
67254
|
} catch {
|
|
66997
67255
|
return null;
|
|
66998
67256
|
}
|
|
@@ -67017,7 +67275,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
67017
67275
|
const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
|
|
67018
67276
|
if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
|
|
67019
67277
|
const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
67020
|
-
const directory =
|
|
67278
|
+
const directory = join55(destination, "..");
|
|
67021
67279
|
mkdirSync27(directory, { recursive: true, mode: 448 });
|
|
67022
67280
|
const url2 = agentBrowserReleaseUrl(asset);
|
|
67023
67281
|
options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
|
|
@@ -67069,7 +67327,7 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
67069
67327
|
});
|
|
67070
67328
|
}
|
|
67071
67329
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
67072
|
-
const file2 =
|
|
67330
|
+
const file2 = join55(dataDir, KEY_FILE);
|
|
67073
67331
|
try {
|
|
67074
67332
|
const existing = readFileSync37(file2, "utf8").trim();
|
|
67075
67333
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
@@ -67122,7 +67380,7 @@ function agentBrowserIntegration(input) {
|
|
|
67122
67380
|
for (const name2 of ["PATH", "AGENT_BROWSER_EXECUTABLE_PATH"]) {
|
|
67123
67381
|
if (sourceEnv[name2]) env[name2] = sourceEnv[name2];
|
|
67124
67382
|
}
|
|
67125
|
-
const bundle = packagedBrowser({ env: sourceEnv });
|
|
67383
|
+
const bundle = packagedBrowser({ env: sourceEnv, platform: input.platform, arch: input.arch });
|
|
67126
67384
|
if (!env.AGENT_BROWSER_EXECUTABLE_PATH && bundle && resolve14(input.binaryPath) === bundle.engine && completePackage(bundle, existsSync24)) {
|
|
67127
67385
|
env.AGENT_BROWSER_EXECUTABLE_PATH = bundle.chrome;
|
|
67128
67386
|
}
|
|
@@ -67130,7 +67388,7 @@ function agentBrowserIntegration(input) {
|
|
|
67130
67388
|
}
|
|
67131
67389
|
var FRAME_TIMEOUT_MS = 1e4;
|
|
67132
67390
|
function agentBrowserFrame(input) {
|
|
67133
|
-
const file2 =
|
|
67391
|
+
const file2 = join55(tmpdir6(), `openmausbot-browser-${randomUUID20()}.png`);
|
|
67134
67392
|
return new Promise((settle, fail2) => {
|
|
67135
67393
|
const child = spawn5(input.binaryPath, ["screenshot", file2], {
|
|
67136
67394
|
env: browserRuntimeEnv(input.env),
|
|
@@ -67188,14 +67446,14 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
67188
67446
|
const bases = [`${session}-${session}`, `${browserRestoreKey(session)}-${session}`];
|
|
67189
67447
|
const remove = (path) => {
|
|
67190
67448
|
try {
|
|
67191
|
-
|
|
67449
|
+
unlinkSync11(path);
|
|
67192
67450
|
} catch (error51) {
|
|
67193
67451
|
if (error51.code !== "ENOENT") throw error51;
|
|
67194
67452
|
}
|
|
67195
67453
|
};
|
|
67196
67454
|
try {
|
|
67197
|
-
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(
|
|
67198
|
-
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");
|
|
67199
67457
|
let pendingFiles = [];
|
|
67200
67458
|
try {
|
|
67201
67459
|
pendingFiles = readdirSync7(pendingDirectory);
|
|
@@ -67203,8 +67461,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
67203
67461
|
if (error51.code !== "ENOENT") throw error51;
|
|
67204
67462
|
}
|
|
67205
67463
|
const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
|
|
67206
|
-
for (const name2 of pendingFiles) if (candidates.some((candidate) => candidate.test(name2))) remove(
|
|
67207
|
-
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));
|
|
67208
67466
|
return true;
|
|
67209
67467
|
} catch {
|
|
67210
67468
|
return false;
|
|
@@ -69211,13 +69469,13 @@ function profileRevision(bot) {
|
|
|
69211
69469
|
import { appendFile as appendFile4 } from "node:fs/promises";
|
|
69212
69470
|
import { createHash as createHash24, randomUUID as randomUUID22 } from "node:crypto";
|
|
69213
69471
|
import { closeSync as closeSync10, existsSync as existsSync25, fstatSync as fstatSync5, mkdirSync as mkdirSync28, openSync as openSync10, readSync as readSync5 } from "node:fs";
|
|
69214
|
-
import { join as
|
|
69472
|
+
import { join as join56 } from "node:path";
|
|
69215
69473
|
var FILE_NAME2 = "history.ndjson";
|
|
69216
69474
|
var ONE_LINER = 200;
|
|
69217
69475
|
var MAX_HISTORY_READ_BYTES = 8 * 1024 * 1024;
|
|
69218
69476
|
var writeQueues4 = /* @__PURE__ */ new Map();
|
|
69219
69477
|
function historyFile(botId) {
|
|
69220
|
-
return
|
|
69478
|
+
return join56(botFolder(botId), FILE_NAME2);
|
|
69221
69479
|
}
|
|
69222
69480
|
function oneLiner(value) {
|
|
69223
69481
|
return value.replace(/\s+/g, " ").trim().slice(0, ONE_LINER);
|
|
@@ -70016,7 +70274,7 @@ function matchDirectoryBots(profile, bots, limit = 5) {
|
|
|
70016
70274
|
|
|
70017
70275
|
// server/project-scout.ts
|
|
70018
70276
|
import { readFileSync as readFileSync38, readdirSync as readdirSync8, statSync as statSync11 } from "node:fs";
|
|
70019
|
-
import { basename as basename7, join as
|
|
70277
|
+
import { basename as basename7, join as join57 } from "node:path";
|
|
70020
70278
|
|
|
70021
70279
|
// shared/import-name.ts
|
|
70022
70280
|
function takeImportName(value, taken, maxLength = 100) {
|
|
@@ -70220,7 +70478,7 @@ function isDir(path) {
|
|
|
70220
70478
|
}
|
|
70221
70479
|
}
|
|
70222
70480
|
function packageJson(cwd) {
|
|
70223
|
-
const raw = readText2(
|
|
70481
|
+
const raw = readText2(join57(cwd, "package.json"));
|
|
70224
70482
|
if (!raw) return { deps: /* @__PURE__ */ new Set() };
|
|
70225
70483
|
try {
|
|
70226
70484
|
const parsed = JSON.parse(raw);
|
|
@@ -70243,7 +70501,7 @@ function packageJson(cwd) {
|
|
|
70243
70501
|
}
|
|
70244
70502
|
}
|
|
70245
70503
|
function readme(cwd) {
|
|
70246
|
-
const raw = readText2(
|
|
70504
|
+
const raw = readText2(join57(cwd, "README.md")) ?? readText2(join57(cwd, "readme.md")) ?? readText2(join57(cwd, "README"));
|
|
70247
70505
|
if (!raw) return {};
|
|
70248
70506
|
let title;
|
|
70249
70507
|
let summary;
|
|
@@ -70317,12 +70575,12 @@ function scoutProject(cwd) {
|
|
|
70317
70575
|
const pkg = packageJson(cwd);
|
|
70318
70576
|
const md = readme(cwd);
|
|
70319
70577
|
const entries = new Set(listNames(cwd));
|
|
70320
|
-
const pythonDeps = `${readText2(
|
|
70321
|
-
${readText2(
|
|
70578
|
+
const pythonDeps = `${readText2(join57(cwd, "requirements.txt")) ?? ""}
|
|
70579
|
+
${readText2(join57(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
70322
70580
|
const hasDep = (dep) => pkg.deps.has(dep) || pythonDeps.length > 1 && pythonDeps.includes(dep);
|
|
70323
70581
|
const pathEvidence = (path) => {
|
|
70324
|
-
if (path.includes("/")) return isDir(
|
|
70325
|
-
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;
|
|
70326
70584
|
return null;
|
|
70327
70585
|
};
|
|
70328
70586
|
const signals = [];
|
|
@@ -70335,7 +70593,7 @@ ${readText2(join56(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
|
70335
70593
|
}
|
|
70336
70594
|
if (detector.role === "docs" && evidence.length > 0) {
|
|
70337
70595
|
const hasMkdocs = evidence.includes("mkdocs.yml");
|
|
70338
|
-
const hasMarkdown = listNames(
|
|
70596
|
+
const hasMarkdown = listNames(join57(cwd, "docs")).some((name2) => name2.endsWith(".md"));
|
|
70339
70597
|
if (!hasMkdocs && !hasMarkdown) continue;
|
|
70340
70598
|
}
|
|
70341
70599
|
if (evidence.length > 0) signals.push({ role: detector.role, evidence: evidence.slice(0, 6) });
|
|
@@ -71024,7 +71282,7 @@ async function fetchGithubTeam(input, fetcher = fetch) {
|
|
|
71024
71282
|
|
|
71025
71283
|
// server/thread-events.ts
|
|
71026
71284
|
import { closeSync as closeSync11, fstatSync as fstatSync6, openSync as openSync11, readSync as readSync6 } from "node:fs";
|
|
71027
|
-
import { join as
|
|
71285
|
+
import { join as join58 } from "node:path";
|
|
71028
71286
|
var DEFAULT_LIMIT = 300;
|
|
71029
71287
|
var MAX_LIMIT = 2e3;
|
|
71030
71288
|
var READ_CHUNK = 64 * 1024;
|
|
@@ -71161,8 +71419,8 @@ function readThreadEvents(input) {
|
|
|
71161
71419
|
assertThreadId(threadId);
|
|
71162
71420
|
const requested = input.limit ?? DEFAULT_LIMIT;
|
|
71163
71421
|
const limit = Number.isFinite(requested) ? Math.max(1, Math.min(Math.trunc(requested), MAX_LIMIT)) : DEFAULT_LIMIT;
|
|
71164
|
-
const runtime = readRecentLines(
|
|
71165
|
-
const native = readRecentLines(
|
|
71422
|
+
const runtime = readRecentLines(join58(eventsDir, `${threadId}.ndjson`), limit, isRuntimeEvent);
|
|
71423
|
+
const native = readRecentLines(join58(nativeDir, `${threadId}.ndjson`), limit, isNativeRecord);
|
|
71166
71424
|
const merged = [
|
|
71167
71425
|
...runtime.lines.map((data) => ({ kind: "runtime", at: data.createdAt, data })),
|
|
71168
71426
|
...native.lines.map((data) => ({ kind: "native", at: data.at, data }))
|
|
@@ -71352,7 +71610,7 @@ function memberTurnSelection(selection) {
|
|
|
71352
71610
|
// server/webhooks.ts
|
|
71353
71611
|
import { createHash as createHash26, randomBytes as randomBytes5, randomUUID as randomUUID23, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
71354
71612
|
import { mkdirSync as mkdirSync29, readFileSync as readFileSync39 } from "node:fs";
|
|
71355
|
-
import { dirname as dirname17, join as
|
|
71613
|
+
import { dirname as dirname17, join as join59 } from "node:path";
|
|
71356
71614
|
var MAX_DELIVERIES = 2e3;
|
|
71357
71615
|
var MAX_ATTEMPTS = 2e3;
|
|
71358
71616
|
var MAX_EVENT_CHARS = 48e3;
|
|
@@ -71539,7 +71797,7 @@ var WebhookManager = class {
|
|
|
71539
71797
|
rate = /* @__PURE__ */ new Map();
|
|
71540
71798
|
constructor(options) {
|
|
71541
71799
|
this.options = options;
|
|
71542
|
-
this.file = options.file ??
|
|
71800
|
+
this.file = options.file ?? join59(DATA_DIR, "webhooks.json");
|
|
71543
71801
|
this.now = options.now ?? Date.now;
|
|
71544
71802
|
try {
|
|
71545
71803
|
const parsed = webhookFileSchema.safeParse(parseJson(readFileSync39(this.file, "utf8")));
|
|
@@ -71804,7 +72062,7 @@ var WebhookManager = class {
|
|
|
71804
72062
|
|
|
71805
72063
|
// server/skill-library.ts
|
|
71806
72064
|
import { existsSync as existsSync26, readFileSync as readFileSync40, readdirSync as readdirSync9 } from "node:fs";
|
|
71807
|
-
import { basename as basename8, join as
|
|
72065
|
+
import { basename as basename8, join as join60 } from "node:path";
|
|
71808
72066
|
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
71809
72067
|
function strings(value) {
|
|
71810
72068
|
return Array.isArray(value) && value.every((item) => typeof item === "string" && item.trim()) ? value.map((item) => item.trim()) : null;
|
|
@@ -71833,19 +72091,19 @@ function parseSkillManifest(value, directory) {
|
|
|
71833
72091
|
};
|
|
71834
72092
|
}
|
|
71835
72093
|
function loadSkillDirectory(directory) {
|
|
71836
|
-
const manifestPath2 =
|
|
71837
|
-
const skillPath =
|
|
72094
|
+
const manifestPath2 = join60(directory, "manifest.json");
|
|
72095
|
+
const skillPath = join60(directory, "SKILL.md");
|
|
71838
72096
|
if (!existsSync26(manifestPath2) || !existsSync26(skillPath)) return null;
|
|
71839
72097
|
const manifest = parseSkillManifest(JSON.parse(readFileSync40(manifestPath2, "utf8")), directory);
|
|
71840
72098
|
const instructions = readFileSync40(skillPath, "utf8").trim();
|
|
71841
72099
|
if (!instructions.startsWith("---")) throw new Error(`${skillPath} has no skill frontmatter`);
|
|
71842
72100
|
return { manifest, instructions, directory };
|
|
71843
72101
|
}
|
|
71844
|
-
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR ||
|
|
72102
|
+
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR || join60(process.cwd(), "skills")) {
|
|
71845
72103
|
if (!existsSync26(root)) return [];
|
|
71846
72104
|
const skills = [];
|
|
71847
72105
|
for (const name2 of readdirSync9(root).sort()) {
|
|
71848
|
-
const directory =
|
|
72106
|
+
const directory = join60(root, name2);
|
|
71849
72107
|
const skill = loadSkillDirectory(directory);
|
|
71850
72108
|
if (skill) skills.push(skill);
|
|
71851
72109
|
}
|
|
@@ -71862,7 +72120,7 @@ function loadUserSkills(root) {
|
|
|
71862
72120
|
const skills = [];
|
|
71863
72121
|
for (const name2 of names) {
|
|
71864
72122
|
try {
|
|
71865
|
-
const skill = loadSkillDirectory(
|
|
72123
|
+
const skill = loadSkillDirectory(join60(root, name2));
|
|
71866
72124
|
if (skill) skills.push(skill);
|
|
71867
72125
|
} catch {
|
|
71868
72126
|
}
|
|
@@ -72476,14 +72734,31 @@ ${message4.text}` } : {},
|
|
|
72476
72734
|
}
|
|
72477
72735
|
|
|
72478
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"]);
|
|
72479
72739
|
function shouldMountLocalComputer({
|
|
72480
72740
|
requested,
|
|
72481
72741
|
hostPlatform = process.platform,
|
|
72482
72742
|
providerSupportsLocal
|
|
72483
72743
|
}) {
|
|
72484
72744
|
if (!providerSupportsLocal) return false;
|
|
72485
|
-
if (requested === "local") return hostPlatform
|
|
72486
|
-
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
|
+
);
|
|
72487
72762
|
}
|
|
72488
72763
|
|
|
72489
72764
|
// server/model-context-window.ts
|
|
@@ -72737,9 +73012,9 @@ function createGracefulShutdown({
|
|
|
72737
73012
|
// electron/data-dir-lease.mjs
|
|
72738
73013
|
import { execFileSync } from "node:child_process";
|
|
72739
73014
|
import { createHash as createHash27, randomUUID as randomUUID24 } from "node:crypto";
|
|
72740
|
-
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";
|
|
72741
73016
|
import { hostname as hostname4, uptime } from "node:os";
|
|
72742
|
-
import { join as
|
|
73017
|
+
import { join as join61 } from "node:path";
|
|
72743
73018
|
var LEASE_NAME = "openmausbot-server.lease";
|
|
72744
73019
|
var DELEGATED_CHILD_DIR = ".openmausbot-server-child";
|
|
72745
73020
|
var CHILD_LEASE_ENV = "OPENMAUSBOT_INTERNAL_DATA_DIR_LEASE";
|
|
@@ -72795,7 +73070,7 @@ function ownerPredatesThisBoot(owner2) {
|
|
|
72795
73070
|
return false;
|
|
72796
73071
|
}
|
|
72797
73072
|
function ownerIsAlive(owner2) {
|
|
72798
|
-
return !ownerPredatesThisBoot(owner2) && processIsAlive3(owner2.pid);
|
|
73073
|
+
return !ownerPredatesThisBoot(owner2) && processIsAlive3(owner2.pid) && ownerIdentityMatches(owner2);
|
|
72799
73074
|
}
|
|
72800
73075
|
function isLeaseOwner(value) {
|
|
72801
73076
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -72845,9 +73120,54 @@ function processIsAlive3(pid) {
|
|
|
72845
73120
|
throw leaseError("OpenMausBot could not verify the data-directory lease owner; refusing to start.", error51);
|
|
72846
73121
|
}
|
|
72847
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
|
+
}
|
|
72848
73168
|
function unlinkExact(path, message4) {
|
|
72849
73169
|
try {
|
|
72850
|
-
|
|
73170
|
+
unlinkSync12(path);
|
|
72851
73171
|
} catch (error51) {
|
|
72852
73172
|
if (error51?.code !== "ENOENT") throw leaseError(message4, error51);
|
|
72853
73173
|
}
|
|
@@ -72942,10 +73262,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
72942
73262
|
} catch (error51) {
|
|
72943
73263
|
throw leaseError("OpenMausBot cannot create its data directory.", error51);
|
|
72944
73264
|
}
|
|
72945
|
-
return
|
|
73265
|
+
return join61(dataDir, LEASE_NAME);
|
|
72946
73266
|
}
|
|
72947
73267
|
function assertNoLiveDelegatedChild(dataDir) {
|
|
72948
|
-
const childLeasePath =
|
|
73268
|
+
const childLeasePath = join61(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
|
|
72949
73269
|
const child = readOwner(childLeasePath);
|
|
72950
73270
|
if (!child) return;
|
|
72951
73271
|
if (child.host !== hostname4()) {
|
|
@@ -72987,13 +73307,13 @@ function validateChildDelegation(dataDir, encoded) {
|
|
|
72987
73307
|
const capability = parseCapability(encoded);
|
|
72988
73308
|
const invalid = () => leaseError("The OpenMausBot desktop lease delegation is invalid; refusing to start to protect its state.");
|
|
72989
73309
|
if (!capability) throw invalid();
|
|
72990
|
-
const parentLeasePath =
|
|
73310
|
+
const parentLeasePath = join61(dataDir, LEASE_NAME);
|
|
72991
73311
|
const matchesLiveParent = (owner2) => Boolean(owner2 && owner2.pid === capability.pid && owner2.token === capability.token && owner2.host === hostname4() && ownerIsAlive(owner2));
|
|
72992
73312
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
72993
73313
|
throw invalid();
|
|
72994
73314
|
}
|
|
72995
73315
|
const childLease = acquireDataDirLeaseInternal(
|
|
72996
|
-
|
|
73316
|
+
join61(dataDir, DELEGATED_CHILD_DIR),
|
|
72997
73317
|
{ guardDelegatedChild: false }
|
|
72998
73318
|
);
|
|
72999
73319
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
@@ -73071,7 +73391,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
|
|
|
73071
73391
|
throw leaseError("OpenMausBot will not release a data-directory lease owned by another process.");
|
|
73072
73392
|
}
|
|
73073
73393
|
try {
|
|
73074
|
-
|
|
73394
|
+
unlinkSync12(leasePath);
|
|
73075
73395
|
} catch (error51) {
|
|
73076
73396
|
throw leaseError("OpenMausBot could not release its data-directory lease.", error51);
|
|
73077
73397
|
}
|
|
@@ -73131,8 +73451,8 @@ var WorkspaceBackupMaintenance = class {
|
|
|
73131
73451
|
|
|
73132
73452
|
// server/workspace-backup-http.ts
|
|
73133
73453
|
import { randomUUID as randomUUID26 } from "node:crypto";
|
|
73134
|
-
import { createReadStream as createReadStream4, createWriteStream as createWriteStream3, existsSync as existsSync29, lstatSync as lstatSync9, mkdirSync as mkdirSync32, readdirSync as readdirSync11, statSync as statSync12, unlinkSync as
|
|
73135
|
-
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";
|
|
73136
73456
|
import { Transform as Transform2 } from "node:stream";
|
|
73137
73457
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
73138
73458
|
|
|
@@ -73175,7 +73495,7 @@ import {
|
|
|
73175
73495
|
writeFileSync as writeFileSync19,
|
|
73176
73496
|
writeSync as writeSync2
|
|
73177
73497
|
} from "node:fs";
|
|
73178
|
-
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";
|
|
73179
73499
|
import { homedir as homedir26 } from "node:os";
|
|
73180
73500
|
import { backup, DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
73181
73501
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -76234,13 +76554,13 @@ function backupRoot(dataDir) {
|
|
|
76234
76554
|
if (root === parse6(root).root) throw new Error("A filesystem root cannot be a workspace backup target.");
|
|
76235
76555
|
const stat6 = lstatSync8(root);
|
|
76236
76556
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("The workspace data directory must not be a symbolic link.");
|
|
76237
|
-
const path =
|
|
76557
|
+
const path = join62(root, ".backups");
|
|
76238
76558
|
folder2(path);
|
|
76239
76559
|
return path;
|
|
76240
76560
|
}
|
|
76241
76561
|
function jobPath(dataDir, id) {
|
|
76242
76562
|
if (!ID.test(id)) throw new Error("Invalid workspace backup identifier.");
|
|
76243
|
-
const path =
|
|
76563
|
+
const path = join62(backupRoot(dataDir), id);
|
|
76244
76564
|
if (entryExists(path)) {
|
|
76245
76565
|
const stat6 = lstatSync8(path);
|
|
76246
76566
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("Unsafe workspace backup job directory.");
|
|
@@ -76287,14 +76607,14 @@ function privateMetadata(manifest) {
|
|
|
76287
76607
|
return { summary: manifest.summary, clientState: manifest.clientState };
|
|
76288
76608
|
}
|
|
76289
76609
|
function assertLocalAuthOutsideSnapshot(dataDir) {
|
|
76290
|
-
const configPath =
|
|
76610
|
+
const configPath = join62(dataDir, "config.json");
|
|
76291
76611
|
const config2 = existsSync28(configPath) ? privateJson(configPath) : {};
|
|
76292
76612
|
const instances = record2(config2) && record2(config2.instances) ? config2.instances : {};
|
|
76293
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"];
|
|
76294
76614
|
const check2 = (value, field, home) => {
|
|
76295
76615
|
if (typeof value !== "string" || !value.trim()) return;
|
|
76296
76616
|
const text8 = value.trim();
|
|
76297
|
-
const expanded = text8.startsWith("~/") || text8.startsWith("~\\") ?
|
|
76617
|
+
const expanded = text8.startsWith("~/") || text8.startsWith("~\\") ? join62(home, text8.slice(2)) : text8;
|
|
76298
76618
|
const resolved = resolve15(expanded);
|
|
76299
76619
|
const exists = entryExists(resolved);
|
|
76300
76620
|
const path = relative5(exists ? realpathSync10(dataDir) : resolve15(dataDir), exists ? realpathSync10(resolved) : resolved).replaceAll("\\", "/");
|
|
@@ -76404,27 +76724,27 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76404
76724
|
const salt = randomBytes6(16);
|
|
76405
76725
|
const key3 = await passwordKey(options.password, salt);
|
|
76406
76726
|
const job = newJob(dataDir);
|
|
76407
|
-
const snapshot2 =
|
|
76408
|
-
folder2(
|
|
76727
|
+
const snapshot2 = join62(job.directory, "snapshot");
|
|
76728
|
+
folder2(join62(snapshot2, "data"));
|
|
76409
76729
|
const entries = [];
|
|
76410
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];
|
|
76411
76731
|
let bytes = 0;
|
|
76412
76732
|
let skippedLinks = 0;
|
|
76413
76733
|
try {
|
|
76414
76734
|
const root = realpathSync10(dataDir);
|
|
76415
|
-
const sourceDb =
|
|
76735
|
+
const sourceDb = join62(root, "messages.db");
|
|
76416
76736
|
if (existsSync28(sourceDb)) {
|
|
76417
76737
|
if (lstatSync8(sourceDb).isSymbolicLink()) throw new Error("The message database must not be a symbolic link.");
|
|
76418
76738
|
const db2 = new DatabaseSync2(sourceDb, { readOnly: true });
|
|
76419
76739
|
const completionPulse = setInterval(() => {
|
|
76420
76740
|
}, 25);
|
|
76421
76741
|
try {
|
|
76422
|
-
await backup(db2,
|
|
76742
|
+
await backup(db2, join62(snapshot2, "messages-snapshot.db"));
|
|
76423
76743
|
} finally {
|
|
76424
76744
|
clearInterval(completionPulse);
|
|
76425
76745
|
db2.close();
|
|
76426
76746
|
}
|
|
76427
|
-
const snapshotDb = new DatabaseSync2(
|
|
76747
|
+
const snapshotDb = new DatabaseSync2(join62(snapshot2, "messages-snapshot.db"));
|
|
76428
76748
|
try {
|
|
76429
76749
|
snapshotDb.exec("PRAGMA journal_mode = DELETE");
|
|
76430
76750
|
} finally {
|
|
@@ -76438,7 +76758,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76438
76758
|
if (excludedWorkspaceAuthPath(path2)) continue;
|
|
76439
76759
|
if (forbiddenArchivePath(path2)) throw new Error("A workspace filename conflicts with a protected authentication or runtime path.");
|
|
76440
76760
|
if (!validRelative(path2)) throw new Error("A workspace filename cannot be safely restored on supported platforms.");
|
|
76441
|
-
const source =
|
|
76761
|
+
const source = join62(directory, name2);
|
|
76442
76762
|
const stat6 = lstatSync8(source);
|
|
76443
76763
|
if (stat6.isSymbolicLink()) {
|
|
76444
76764
|
let target;
|
|
@@ -76458,7 +76778,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76458
76778
|
continue;
|
|
76459
76779
|
}
|
|
76460
76780
|
if (entries.length >= MAX_WORKSPACE_BACKUP_FILES) throw new Error("The workspace exceeds the 100,000-entry backup limit.");
|
|
76461
|
-
const destination =
|
|
76781
|
+
const destination = join62(snapshot2, "data", path2);
|
|
76462
76782
|
if (stat6.isDirectory()) {
|
|
76463
76783
|
folder2(destination);
|
|
76464
76784
|
entries.push({ path: path2, type: "directory", size: 0, mode: 448 });
|
|
@@ -76476,7 +76796,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76476
76796
|
writeJson(destination, value);
|
|
76477
76797
|
}
|
|
76478
76798
|
copied = { size: lstatSync8(destination).size, mode: 384, sha256: hashFile(destination) };
|
|
76479
|
-
} else copied = copyRegular(path2 === "messages.db" ?
|
|
76799
|
+
} else copied = copyRegular(path2 === "messages.db" ? join62(snapshot2, "messages-snapshot.db") : source, destination);
|
|
76480
76800
|
bytes += copied.size;
|
|
76481
76801
|
if (bytes > MAX_WORKSPACE_BACKUP_BYTES) throw new Error("The workspace exceeds the 10 GB backup limit.");
|
|
76482
76802
|
entries.push({ path: path2, type: "file", ...copied });
|
|
@@ -76494,9 +76814,9 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76494
76814
|
files: entries.filter((entry) => entry.type === "file").length,
|
|
76495
76815
|
directories: entries.filter((entry) => entry.type === "directory").length,
|
|
76496
76816
|
bytes,
|
|
76497
|
-
bots: countJsonArray(
|
|
76498
|
-
groups: countJsonArray(
|
|
76499
|
-
...databaseCounts(
|
|
76817
|
+
bots: countJsonArray(join62(snapshot2, "data", "bots.json")),
|
|
76818
|
+
groups: countJsonArray(join62(snapshot2, "data", "groups.json")),
|
|
76819
|
+
...databaseCounts(join62(snapshot2, "data", "messages.db")),
|
|
76500
76820
|
exclusions: EXCLUSION_NOTES,
|
|
76501
76821
|
warnings
|
|
76502
76822
|
};
|
|
@@ -76506,9 +76826,9 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
76506
76826
|
clientState: options.clientState ?? {},
|
|
76507
76827
|
entries
|
|
76508
76828
|
};
|
|
76509
|
-
writeJson(
|
|
76510
|
-
validateManifest(privateJson(
|
|
76511
|
-
const path =
|
|
76829
|
+
writeJson(join62(snapshot2, "manifest.json"), manifest);
|
|
76830
|
+
validateManifest(privateJson(join62(snapshot2, "manifest.json")));
|
|
76831
|
+
const path = join62(job.directory, "workspace.ombbackup");
|
|
76512
76832
|
const iv = randomBytes6(12);
|
|
76513
76833
|
const header2 = Buffer.concat([MAGIC, salt, iv]);
|
|
76514
76834
|
const cipher = createCipheriv("aes-256-gcm", key3, iv);
|
|
@@ -76623,21 +76943,21 @@ async function inspectTar(path) {
|
|
|
76623
76943
|
return entries;
|
|
76624
76944
|
}
|
|
76625
76945
|
function validateStaged(directory, expected) {
|
|
76626
|
-
const manifest = validateManifest(privateJson(
|
|
76946
|
+
const manifest = validateManifest(privateJson(join62(directory, "manifest.json")));
|
|
76627
76947
|
if (expected && expected.size !== manifest.entries.length + 2) throw new Error("Archive entries do not match the backup manifest.");
|
|
76628
76948
|
const declared = new Set(manifest.entries.map((entry) => entry.path));
|
|
76629
76949
|
const inspect = (root, prefix = "") => {
|
|
76630
76950
|
for (const name2 of readdirSync10(root)) {
|
|
76631
76951
|
const path = prefix ? `${prefix}/${name2}` : name2;
|
|
76632
76952
|
if (!declared.has(path)) throw new Error("Staged workspace contains files absent from its manifest.");
|
|
76633
|
-
const stat6 = lstatSync8(
|
|
76953
|
+
const stat6 = lstatSync8(join62(root, name2));
|
|
76634
76954
|
if (stat6.isSymbolicLink()) throw new Error("Staged workspace contains a symbolic link.");
|
|
76635
|
-
if (stat6.isDirectory()) inspect(
|
|
76955
|
+
if (stat6.isDirectory()) inspect(join62(root, name2), path);
|
|
76636
76956
|
}
|
|
76637
76957
|
};
|
|
76638
|
-
inspect(
|
|
76958
|
+
inspect(join62(directory, "data"));
|
|
76639
76959
|
for (const entry of manifest.entries) {
|
|
76640
|
-
const path =
|
|
76960
|
+
const path = join62(directory, "data", entry.path);
|
|
76641
76961
|
const stat6 = lstatSync8(path);
|
|
76642
76962
|
if (stat6.isSymbolicLink() || (entry.type === "file" ? !stat6.isFile() || stat6.size !== entry.size : !stat6.isDirectory())) {
|
|
76643
76963
|
throw new Error("Staged workspace files do not match the backup manifest.");
|
|
@@ -76649,27 +76969,27 @@ function validateStaged(directory, expected) {
|
|
|
76649
76969
|
if (entry.type === "file" && hashFile(path) !== entry.sha256) throw new Error("A workspace backup file failed its checksum.");
|
|
76650
76970
|
chmodSync7(path, entry.mode);
|
|
76651
76971
|
}
|
|
76652
|
-
const data =
|
|
76653
|
-
if (countJsonArray(
|
|
76654
|
-
if (existsSync28(
|
|
76655
|
-
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"));
|
|
76656
76976
|
if (JSON.stringify(config2) !== JSON.stringify(portableWorkspaceConfig(config2))) throw new Error("Workspace backup contains non-portable connection settings.");
|
|
76657
76977
|
}
|
|
76658
|
-
if (existsSync28(
|
|
76659
|
-
const value = privateJson(
|
|
76978
|
+
if (existsSync28(join62(data, "webhooks.json"))) {
|
|
76979
|
+
const value = privateJson(join62(data, "webhooks.json"));
|
|
76660
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.");
|
|
76661
76981
|
}
|
|
76662
|
-
const counts = databaseCounts(
|
|
76982
|
+
const counts = databaseCounts(join62(data, "messages.db"));
|
|
76663
76983
|
if (counts.messages !== manifest.summary.messages || counts.threads !== manifest.summary.threads) throw new Error("The message database does not match the backup summary.");
|
|
76664
76984
|
return manifest;
|
|
76665
76985
|
}
|
|
76666
76986
|
async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
76667
76987
|
const job = newJob(dataDir);
|
|
76668
|
-
const plaintext =
|
|
76988
|
+
const plaintext = join62(job.directory, "archive.tar");
|
|
76669
76989
|
try {
|
|
76670
76990
|
await decryptArchive(archivePath, plaintext, options.password);
|
|
76671
76991
|
const expected = await inspectTar(plaintext);
|
|
76672
|
-
const staged =
|
|
76992
|
+
const staged = join62(job.directory, "staged");
|
|
76673
76993
|
folder2(staged);
|
|
76674
76994
|
await So({ file: plaintext, cwd: staged, strict: true, preservePaths: false, umask: 63, noChmod: true });
|
|
76675
76995
|
const manifest = validateStaged(staged, expected);
|
|
@@ -76681,7 +77001,7 @@ async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
76681
77001
|
}
|
|
76682
77002
|
}
|
|
76683
77003
|
rmSync8(plaintext);
|
|
76684
|
-
writeJson(
|
|
77004
|
+
writeJson(join62(job.directory, "ready.json"), { id: job.id, summary: manifest.summary });
|
|
76685
77005
|
return { id: job.id, summary: manifest.summary };
|
|
76686
77006
|
} catch (error51) {
|
|
76687
77007
|
rmSync8(job.directory, { recursive: true, force: true });
|
|
@@ -76690,24 +77010,24 @@ async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
76690
77010
|
}
|
|
76691
77011
|
function readStagedWorkspaceBackup(dataDir, id) {
|
|
76692
77012
|
const directory = jobPath(dataDir, id);
|
|
76693
|
-
const ready = privateJson(
|
|
77013
|
+
const ready = privateJson(join62(directory, "ready.json"));
|
|
76694
77014
|
if (!record2(ready) || ready.id !== id) throw new Error("This workspace backup is not ready to restore.");
|
|
76695
|
-
const manifest = validateManifest(privateJson(
|
|
77015
|
+
const manifest = validateManifest(privateJson(join62(directory, "staged", "manifest.json")));
|
|
76696
77016
|
return { id, ...privateMetadata(manifest) };
|
|
76697
77017
|
}
|
|
76698
77018
|
function commitPendingWorkspaceRestore(dataDir, id) {
|
|
76699
77019
|
const root = backupRoot(dataDir);
|
|
76700
|
-
if (existsSync28(
|
|
76701
|
-
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.");
|
|
76702
77022
|
const metadata = readStagedWorkspaceBackup(dataDir, id);
|
|
76703
|
-
const manifest = validateStaged(
|
|
77023
|
+
const manifest = validateStaged(join62(jobPath(dataDir, id), "staged"));
|
|
76704
77024
|
prepareRestore(dataDir, id, manifest);
|
|
76705
|
-
writeJson(
|
|
77025
|
+
writeJson(join62(root, "pending-restore.json"), { id });
|
|
76706
77026
|
return { id, restartRequired: true, summary: metadata.summary };
|
|
76707
77027
|
}
|
|
76708
77028
|
function readPendingWorkspaceRestoreMetadata(dataDir) {
|
|
76709
77029
|
const root = backupRoot(dataDir);
|
|
76710
|
-
const path =
|
|
77030
|
+
const path = join62(root, "pending-restore.json");
|
|
76711
77031
|
if (!existsSync28(path)) return null;
|
|
76712
77032
|
const pending = privateJson(path);
|
|
76713
77033
|
if (!record2(pending) || typeof pending.id !== "string") throw new Error("Invalid pending workspace restore.");
|
|
@@ -76722,24 +77042,24 @@ function readJournal(path) {
|
|
|
76722
77042
|
return raw;
|
|
76723
77043
|
}
|
|
76724
77044
|
function rollback(dataDir, journal) {
|
|
76725
|
-
const stage =
|
|
76726
|
-
const saved =
|
|
77045
|
+
const stage = join62(jobPath(dataDir, journal.id), "apply", "data");
|
|
77046
|
+
const saved = join62(backupRoot(dataDir), `safety-${journal.id}`, "data");
|
|
76727
77047
|
for (const name2 of journal.incoming) {
|
|
76728
|
-
const live =
|
|
76729
|
-
const staged =
|
|
76730
|
-
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));
|
|
76731
77051
|
if (originalMoved && !entryExists(staged) && entryExists(live)) renameSync9(live, staged);
|
|
76732
77052
|
}
|
|
76733
77053
|
for (const name2 of journal.existing) {
|
|
76734
|
-
const original =
|
|
77054
|
+
const original = join62(saved, name2);
|
|
76735
77055
|
if (!entryExists(original)) continue;
|
|
76736
|
-
const live =
|
|
77056
|
+
const live = join62(dataDir, name2);
|
|
76737
77057
|
if (entryExists(live)) throw new Error("Restore rollback found conflicting files. Both copies are preserved for recovery.");
|
|
76738
77058
|
renameSync9(original, live);
|
|
76739
77059
|
}
|
|
76740
77060
|
const root = backupRoot(dataDir);
|
|
76741
|
-
const previousReceipt =
|
|
76742
|
-
const last =
|
|
77061
|
+
const previousReceipt = join62(root, `safety-${journal.id}`, "previous-restore.json");
|
|
77062
|
+
const last = join62(root, "last-restore.json");
|
|
76743
77063
|
if (existsSync28(previousReceipt)) writeJson(last, privateJson(previousReceipt));
|
|
76744
77064
|
else if (existsSync28(last)) {
|
|
76745
77065
|
const value = privateJson(last);
|
|
@@ -76752,7 +77072,7 @@ function rebasePath(value, source, destination) {
|
|
|
76752
77072
|
if (normalized !== original && !normalized.startsWith(`${original}/`)) return value;
|
|
76753
77073
|
const suffix = normalized.slice(original.length).replace(/^\//, "");
|
|
76754
77074
|
if (suffix && !validRelative(suffix)) return value;
|
|
76755
|
-
return suffix ?
|
|
77075
|
+
return suffix ? join62(destination, suffix) : destination;
|
|
76756
77076
|
}
|
|
76757
77077
|
var PATH_KEYS = /* @__PURE__ */ new Set(["cwd", "pinnedCwd", "workspace", "configDir", "profileDirectory", "agentDir", "dataDir", "home", "cli", "path", "filePath", "localPath"]);
|
|
76758
77078
|
function rebaseFields(value, source, destination) {
|
|
@@ -76791,16 +77111,16 @@ function rebaseMessage(message4, source, destination) {
|
|
|
76791
77111
|
function prepareRestore(dataDir, id, manifest) {
|
|
76792
77112
|
assertLocalAuthOutsideSnapshot(dataDir);
|
|
76793
77113
|
const job = jobPath(dataDir, id);
|
|
76794
|
-
const prepared =
|
|
76795
|
-
if (existsSync28(
|
|
77114
|
+
const prepared = join62(job, "apply", "data");
|
|
77115
|
+
if (existsSync28(join62(job, "apply"))) rmSync8(join62(job, "apply"), { recursive: true, force: true });
|
|
76796
77116
|
folder2(prepared);
|
|
76797
|
-
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));
|
|
76798
77118
|
for (const entry of manifest.entries) {
|
|
76799
|
-
const destination =
|
|
76800
|
-
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);
|
|
76801
77121
|
}
|
|
76802
77122
|
const changeJson = (name2, change) => {
|
|
76803
|
-
const path =
|
|
77123
|
+
const path = join62(prepared, name2);
|
|
76804
77124
|
if (!existsSync28(path)) return;
|
|
76805
77125
|
const value = privateJson(path);
|
|
76806
77126
|
change(value);
|
|
@@ -76813,25 +77133,25 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76813
77133
|
if (!Array.isArray(value)) throw new Error("Invalid bot definitions in workspace backup.");
|
|
76814
77134
|
for (const bot of value) if (record2(bot)) delete bot.voice;
|
|
76815
77135
|
});
|
|
76816
|
-
const oldConfig =
|
|
76817
|
-
const newConfig =
|
|
77136
|
+
const oldConfig = join62(dataDir, "config.json");
|
|
77137
|
+
const newConfig = join62(prepared, "config.json");
|
|
76818
77138
|
if (existsSync28(oldConfig) || existsSync28(newConfig)) {
|
|
76819
77139
|
writeJson(newConfig, restoredWorkspaceConfig(existsSync28(newConfig) ? privateJson(newConfig) : {}, existsSync28(oldConfig) ? privateJson(oldConfig) : {}));
|
|
76820
77140
|
}
|
|
76821
77141
|
const browserHomes = ["vm-home"];
|
|
76822
|
-
const vmHomes =
|
|
77142
|
+
const vmHomes = join62(dataDir, "vm-homes");
|
|
76823
77143
|
if (entryExists(vmHomes)) {
|
|
76824
77144
|
if (!lstatSync8(vmHomes).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
76825
77145
|
for (const name2 of readdirSync10(vmHomes)) if (validRelative(name2)) browserHomes.push(`vm-homes/${name2}`);
|
|
76826
77146
|
}
|
|
76827
77147
|
for (const home of browserHomes) {
|
|
76828
|
-
const homePath =
|
|
77148
|
+
const homePath = join62(dataDir, home);
|
|
76829
77149
|
if (!entryExists(homePath)) continue;
|
|
76830
77150
|
if (!lstatSync8(homePath).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
76831
|
-
const source =
|
|
77151
|
+
const source = join62(dataDir, home, ".browser-profiles");
|
|
76832
77152
|
if (!entryExists(source)) continue;
|
|
76833
|
-
folder2(
|
|
76834
|
-
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 });
|
|
76835
77155
|
}
|
|
76836
77156
|
changeJson("routines.json", (value) => {
|
|
76837
77157
|
if (!record2(value)) throw new Error("Invalid routine definitions in workspace backup.");
|
|
@@ -76849,7 +77169,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76849
77169
|
});
|
|
76850
77170
|
changeJson("webhooks.json", (value) => {
|
|
76851
77171
|
if (!record2(value)) throw new Error("Invalid webhook definitions in workspace backup.");
|
|
76852
|
-
const destination = existsSync28(
|
|
77172
|
+
const destination = existsSync28(join62(dataDir, "webhooks.json")) ? privateJson(join62(dataDir, "webhooks.json")) : {};
|
|
76853
77173
|
const oldHooks = record2(destination) && Array.isArray(destination.webhooks) ? destination.webhooks : [];
|
|
76854
77174
|
if (Array.isArray(value.webhooks)) {
|
|
76855
77175
|
for (const webhook of value.webhooks) if (record2(webhook)) {
|
|
@@ -76866,15 +77186,15 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76866
77186
|
for (const call of value.calls) if (record2(call)) call.nextRunAt = null;
|
|
76867
77187
|
}
|
|
76868
77188
|
});
|
|
76869
|
-
if (existsSync28(
|
|
76870
|
-
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"), []);
|
|
76871
77191
|
for (const entry of manifest.entries) {
|
|
76872
77192
|
if (/^messages-[^/]+\.json$/.test(entry.path)) changeJson(entry.path, (value) => {
|
|
76873
77193
|
if (Array.isArray(value)) for (const message4 of value) rebaseMessage(message4, manifest.sourceDataDir, resolve15(dataDir));
|
|
76874
77194
|
else if (record2(value) && Array.isArray(value.messages)) for (const message4 of value.messages) rebaseMessage(message4, manifest.sourceDataDir, resolve15(dataDir));
|
|
76875
77195
|
});
|
|
76876
77196
|
}
|
|
76877
|
-
const dbPath =
|
|
77197
|
+
const dbPath = join62(prepared, "messages.db");
|
|
76878
77198
|
if (existsSync28(dbPath)) {
|
|
76879
77199
|
const db2 = new DatabaseSync2(dbPath);
|
|
76880
77200
|
try {
|
|
@@ -76906,7 +77226,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
76906
77226
|
}
|
|
76907
77227
|
function readLastWorkspaceRestore(dataDir) {
|
|
76908
77228
|
const root = backupRoot(dataDir);
|
|
76909
|
-
const path =
|
|
77229
|
+
const path = join62(root, "last-restore.json");
|
|
76910
77230
|
if (!existsSync28(path)) return null;
|
|
76911
77231
|
const raw = privateJson(path);
|
|
76912
77232
|
if (!record2(raw) || raw.restored !== true || typeof raw.id !== "string" || !ID.test(raw.id)) throw new Error("Invalid workspace restore receipt.");
|
|
@@ -76919,28 +77239,28 @@ function finishRestore(dataDir, id, consumePending = true) {
|
|
|
76919
77239
|
if (draftAttachments) {
|
|
76920
77240
|
try {
|
|
76921
77241
|
const value = JSON.parse(draftAttachments);
|
|
76922
|
-
const manifest = validateManifest(privateJson(
|
|
77242
|
+
const manifest = validateManifest(privateJson(join62(jobPath(dataDir, id), "staged", "manifest.json")));
|
|
76923
77243
|
rebaseFields(value, manifest.sourceDataDir, resolve15(dataDir));
|
|
76924
77244
|
clientState["omb-draft-attachments"] = JSON.stringify(value);
|
|
76925
77245
|
} catch {
|
|
76926
77246
|
}
|
|
76927
77247
|
}
|
|
76928
|
-
const result = { restored: true, id, safetyCopyPath:
|
|
76929
|
-
writeJson(
|
|
76930
|
-
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 });
|
|
76931
77251
|
return result;
|
|
76932
77252
|
}
|
|
76933
77253
|
function applyPendingWorkspaceRestore(dataDir) {
|
|
76934
77254
|
const root = backupRoot(dataDir);
|
|
76935
|
-
const journalFile2 =
|
|
76936
|
-
const pendingFile =
|
|
77255
|
+
const journalFile2 = join62(root, "restore-journal.json");
|
|
77256
|
+
const pendingFile = join62(root, "pending-restore.json");
|
|
76937
77257
|
if (existsSync28(journalFile2)) {
|
|
76938
77258
|
const previous2 = readJournal(journalFile2);
|
|
76939
77259
|
if (previous2.phase === "applying") {
|
|
76940
77260
|
rollback(dataDir, previous2);
|
|
76941
77261
|
writeJson(journalFile2, { ...previous2, phase: "rolled-back" });
|
|
76942
77262
|
rmSync8(pendingFile, { force: true });
|
|
76943
|
-
return { restored: false, rolledBack: true, id: previous2.id, safetyCopyPath:
|
|
77263
|
+
return { restored: false, rolledBack: true, id: previous2.id, safetyCopyPath: join62(root, `safety-${previous2.id}`) };
|
|
76944
77264
|
}
|
|
76945
77265
|
if (previous2.phase === "committed" && existsSync28(pendingFile)) {
|
|
76946
77266
|
const pending2 = privateJson(pendingFile);
|
|
@@ -76951,12 +77271,12 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
76951
77271
|
}
|
|
76952
77272
|
const pending = readPendingWorkspaceRestoreMetadata(dataDir);
|
|
76953
77273
|
if (!pending) return { restored: false };
|
|
76954
|
-
const staged =
|
|
77274
|
+
const staged = join62(jobPath(dataDir, pending.id), "staged");
|
|
76955
77275
|
const manifest = validateStaged(staged);
|
|
76956
77276
|
const prepared = prepareRestore(dataDir, pending.id, manifest);
|
|
76957
|
-
const safetyCopyPath =
|
|
77277
|
+
const safetyCopyPath = join62(root, `safety-${pending.id}`);
|
|
76958
77278
|
if (existsSync28(safetyCopyPath)) throw new Error("A safety copy already exists for this restore. Original files have not been changed.");
|
|
76959
|
-
folder2(
|
|
77279
|
+
folder2(join62(safetyCopyPath, "data"));
|
|
76960
77280
|
const journal = {
|
|
76961
77281
|
id: pending.id,
|
|
76962
77282
|
phase: "applying",
|
|
@@ -76966,12 +77286,12 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
76966
77286
|
for (const name2 of [...journal.existing, ...journal.incoming]) {
|
|
76967
77287
|
if (!validRelative(name2) || name2.includes("/") || preserved(name2)) throw new Error("Unsafe workspace restore target.");
|
|
76968
77288
|
}
|
|
76969
|
-
writeJson(
|
|
76970
|
-
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")));
|
|
76971
77291
|
writeJson(journalFile2, journal);
|
|
76972
77292
|
try {
|
|
76973
|
-
for (const name2 of journal.existing) renameSync9(
|
|
76974
|
-
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));
|
|
76975
77295
|
finishRestore(dataDir, pending.id, false);
|
|
76976
77296
|
writeJson(journalFile2, { ...journal, phase: "committed" });
|
|
76977
77297
|
} catch (error51) {
|
|
@@ -76986,11 +77306,11 @@ function removeWorkspaceBackupJob(dataDir, id) {
|
|
|
76986
77306
|
const path = jobPath(dataDir, id);
|
|
76987
77307
|
const root = backupRoot(dataDir);
|
|
76988
77308
|
for (const name2 of ["pending-restore.json", "restore-journal.json", "last-restore.json"]) {
|
|
76989
|
-
if (!existsSync28(
|
|
76990
|
-
const value = privateJson(
|
|
77309
|
+
if (!existsSync28(join62(root, name2))) continue;
|
|
77310
|
+
const value = privateJson(join62(root, name2));
|
|
76991
77311
|
if (record2(value) && value.id === id) throw new Error("This backup is still needed for restore or recovery and cannot be removed.");
|
|
76992
77312
|
}
|
|
76993
|
-
if (existsSync28(
|
|
77313
|
+
if (existsSync28(join62(root, `safety-${id}`))) throw new Error("This backup has a recovery copy and cannot be removed.");
|
|
76994
77314
|
if (!entryExists(path)) return;
|
|
76995
77315
|
if (!lstatSync8(path).isDirectory() || lstatSync8(path).isSymbolicLink()) throw new Error("Unsafe workspace backup cleanup target.");
|
|
76996
77316
|
rmSync8(path, { recursive: true, force: true });
|
|
@@ -77020,7 +77340,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77020
77340
|
function remove(id, artifact) {
|
|
77021
77341
|
if (artifact.path) {
|
|
77022
77342
|
try {
|
|
77023
|
-
|
|
77343
|
+
unlinkSync13(artifact.path);
|
|
77024
77344
|
} catch {
|
|
77025
77345
|
}
|
|
77026
77346
|
}
|
|
@@ -77033,24 +77353,24 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77033
77353
|
artifacts.delete(id);
|
|
77034
77354
|
}
|
|
77035
77355
|
function reclaimAbandoned() {
|
|
77036
|
-
const root =
|
|
77356
|
+
const root = join63(options.dataDir, ".backups");
|
|
77037
77357
|
if (!existsSync29(root) || lstatSync9(root).isSymbolicLink()) return;
|
|
77038
77358
|
for (const entry of readdirSync11(root, { withFileTypes: true })) {
|
|
77039
77359
|
if (!entry.isDirectory() || !/^[a-f0-9-]{36}$/.test(entry.name)) continue;
|
|
77040
|
-
if (statSync12(
|
|
77360
|
+
if (statSync12(join63(root, entry.name)).mtimeMs + EXPIRES_MS > Date.now()) continue;
|
|
77041
77361
|
try {
|
|
77042
77362
|
removeWorkspaceBackupJob(options.dataDir, entry.name);
|
|
77043
77363
|
} catch {
|
|
77044
77364
|
}
|
|
77045
77365
|
}
|
|
77046
|
-
const uploads =
|
|
77366
|
+
const uploads = join63(root, "uploads");
|
|
77047
77367
|
if (!existsSync29(uploads) || lstatSync9(uploads).isSymbolicLink()) return;
|
|
77048
77368
|
for (const entry of readdirSync11(uploads, { withFileTypes: true })) {
|
|
77049
77369
|
if (!entry.isFile() || !/^[a-f0-9-]{36}\.ombbackup$/.test(entry.name)) continue;
|
|
77050
|
-
const file2 =
|
|
77370
|
+
const file2 = join63(uploads, entry.name);
|
|
77051
77371
|
if (statSync12(file2).mtimeMs + EXPIRES_MS <= Date.now()) {
|
|
77052
77372
|
try {
|
|
77053
|
-
|
|
77373
|
+
unlinkSync13(file2);
|
|
77054
77374
|
} catch {
|
|
77055
77375
|
}
|
|
77056
77376
|
}
|
|
@@ -77147,14 +77467,14 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77147
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);
|
|
77148
77468
|
replacePrevious(auth, ["upload", "staged"]);
|
|
77149
77469
|
if (artifacts.size >= 4) throw failure3("There are already four backup files in progress. Restart or wait an hour before uploading another.", 409);
|
|
77150
|
-
const root =
|
|
77470
|
+
const root = join63(options.dataDir, ".backups");
|
|
77151
77471
|
mkdirSync32(root, { recursive: true, mode: 448 });
|
|
77152
77472
|
if (lstatSync9(root).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
77153
|
-
const directory =
|
|
77473
|
+
const directory = join63(root, "uploads");
|
|
77154
77474
|
mkdirSync32(directory, { recursive: true, mode: 448 });
|
|
77155
77475
|
if (lstatSync9(directory).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
77156
77476
|
const id = randomUUID26();
|
|
77157
|
-
const file2 =
|
|
77477
|
+
const file2 = join63(directory, `${id}.ombbackup`);
|
|
77158
77478
|
let bytes = 0;
|
|
77159
77479
|
try {
|
|
77160
77480
|
const limit = new Transform2({ transform(chunk, _encoding, callback) {
|
|
@@ -77168,7 +77488,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77168
77488
|
json4(res, 200, { id });
|
|
77169
77489
|
} catch (error51) {
|
|
77170
77490
|
try {
|
|
77171
|
-
|
|
77491
|
+
unlinkSync13(file2);
|
|
77172
77492
|
} catch {
|
|
77173
77493
|
}
|
|
77174
77494
|
throw error51;
|
|
@@ -77187,7 +77507,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
77187
77507
|
throw error51;
|
|
77188
77508
|
}
|
|
77189
77509
|
try {
|
|
77190
|
-
|
|
77510
|
+
unlinkSync13(artifact.path);
|
|
77191
77511
|
} catch {
|
|
77192
77512
|
}
|
|
77193
77513
|
artifacts.delete(body.id);
|
|
@@ -78356,7 +78676,7 @@ function resolveRequestAuth(req, options) {
|
|
|
78356
78676
|
|
|
78357
78677
|
// server/sessions.ts
|
|
78358
78678
|
import { createHash as createHash29, randomBytes as randomBytes8, randomUUID as randomUUID27, timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
78359
|
-
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";
|
|
78360
78680
|
import { dirname as dirname18 } from "node:path";
|
|
78361
78681
|
var SCOPES = ["admin", "client"];
|
|
78362
78682
|
var PAIRING_CODE_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
@@ -78502,7 +78822,7 @@ var SessionRegistry = class {
|
|
|
78502
78822
|
try {
|
|
78503
78823
|
this.revalidateEmailSessions();
|
|
78504
78824
|
this.persist();
|
|
78505
|
-
|
|
78825
|
+
unlinkSync14(this.openMarker);
|
|
78506
78826
|
this.closed = true;
|
|
78507
78827
|
} catch {
|
|
78508
78828
|
throw new Error("Could not safely close session storage.");
|
|
@@ -78786,7 +79106,7 @@ var SessionRegistry = class {
|
|
|
78786
79106
|
|
|
78787
79107
|
// server/brand.ts
|
|
78788
79108
|
import { existsSync as existsSync31, readFileSync as readFileSync44 } from "node:fs";
|
|
78789
|
-
import { join as
|
|
79109
|
+
import { join as join64 } from "node:path";
|
|
78790
79110
|
var HEX_COLOUR = /^#[0-9a-fA-F]{6}$/;
|
|
78791
79111
|
var MAX_LOGO_CHARS = 3e5;
|
|
78792
79112
|
var MAX_FAVICON_CHARS = 12e4;
|
|
@@ -78806,7 +79126,7 @@ var brandSchema = external_exports.object({
|
|
|
78806
79126
|
}).strict();
|
|
78807
79127
|
var DEFAULT_BRAND = { name: "OpenMausBot" };
|
|
78808
79128
|
function brandFile() {
|
|
78809
|
-
return process.env.OMB_BRAND_FILE ||
|
|
79129
|
+
return process.env.OMB_BRAND_FILE || join64(DATA_DIR, "brand.json");
|
|
78810
79130
|
}
|
|
78811
79131
|
function loadBrand(options = {}) {
|
|
78812
79132
|
const file2 = options.file ?? brandFile();
|
|
@@ -81326,7 +81646,7 @@ function releaseDataDirLeaseAtExit() {
|
|
|
81326
81646
|
}
|
|
81327
81647
|
process.once("exit", releaseDataDirLeaseAtExit);
|
|
81328
81648
|
var workspaceRestore = { restored: false };
|
|
81329
|
-
if (existsSync32(
|
|
81649
|
+
if (existsSync32(join65(DATA_DIR, ".backups"))) {
|
|
81330
81650
|
workspaceRestore = applyPendingWorkspaceRestore(DATA_DIR);
|
|
81331
81651
|
if (!workspaceRestore.restored && !workspaceRestore.rolledBack) {
|
|
81332
81652
|
workspaceRestore = readLastWorkspaceRestore(DATA_DIR) ?? workspaceRestore;
|
|
@@ -81339,7 +81659,7 @@ function signInAllowList() {
|
|
|
81339
81659
|
return { admins: parseAllowList(current2?.admins?.join(",")), members: parseAllowList(current2?.members?.join(",")) };
|
|
81340
81660
|
}
|
|
81341
81661
|
var sessions = new SessionRegistry({
|
|
81342
|
-
file:
|
|
81662
|
+
file: join65(DATA_DIR, "sessions.json"),
|
|
81343
81663
|
emailScopesSnapshot: () => {
|
|
81344
81664
|
const membership = signInAllowList();
|
|
81345
81665
|
return (email3) => allowedScopes(email3, membership);
|
|
@@ -81395,7 +81715,7 @@ function noteTurnTrigger(threadId, auth) {
|
|
|
81395
81715
|
var providerAuthSessions = new ProviderAuthSessions();
|
|
81396
81716
|
await registry2.load(instanceConfigs(cfg));
|
|
81397
81717
|
var bundledSkills = loadBundledSkills();
|
|
81398
|
-
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(
|
|
81718
|
+
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(join65(DATA_DIR, "skills")));
|
|
81399
81719
|
var utilityParentPort = process.parentPort;
|
|
81400
81720
|
function postDesktopPrivateMessage(message4) {
|
|
81401
81721
|
if (!utilityParentPort) return false;
|
|
@@ -81421,7 +81741,7 @@ function applyDesktopMutationTokenMessage(raw) {
|
|
|
81421
81741
|
return true;
|
|
81422
81742
|
}
|
|
81423
81743
|
var browserCleanup = new BrowserCleanupCoordinator({
|
|
81424
|
-
file:
|
|
81744
|
+
file: join65(DATA_DIR, "browser-cleanups.json"),
|
|
81425
81745
|
send: (request3) => {
|
|
81426
81746
|
const status = browserEngineStatus();
|
|
81427
81747
|
const sessions2 = request3.type === "openmausbot:browser-bot-deleted" && request3.botId ? [browserSessionId(request3.botId, "")] : request3.partitionId ? [browserSessionId("", request3.partitionId)] : [];
|
|
@@ -81657,6 +81977,7 @@ function claimTurnResource(owner2, resource) {
|
|
|
81657
81977
|
}
|
|
81658
81978
|
function releaseTurnResources(owner2) {
|
|
81659
81979
|
if (!owner2) return;
|
|
81980
|
+
if (autoVmClaims.get(owner2.threadId)?.owner.generation === owner2.generation) autoVmClaims.delete(owner2.threadId);
|
|
81660
81981
|
if (settlingResourceOwners.get(owner2.threadId) === owner2.generation) settlingResourceOwners.delete(owner2.threadId);
|
|
81661
81982
|
turnResources.release(owner2);
|
|
81662
81983
|
if (turnResourceOwners.get(owner2.threadId)?.generation === owner2.generation) turnResourceOwners.delete(owner2.threadId);
|
|
@@ -81714,6 +82035,10 @@ function botAtThreadCapacity(botId) {
|
|
|
81714
82035
|
function hasDirectDispatch(botId) {
|
|
81715
82036
|
return [...directTurnDispatchClaims.values()].some((claim) => claim.botId === botId);
|
|
81716
82037
|
}
|
|
82038
|
+
function unattendedDispatchState(botId) {
|
|
82039
|
+
const bot = store.bot(botId);
|
|
82040
|
+
return !bot ? "missing" : botAtThreadCapacity(botId) || activeGroupTurnForBot(botId) ? "busy" : "ready";
|
|
82041
|
+
}
|
|
81717
82042
|
function requestedTaskBot(botId, rawThreadId) {
|
|
81718
82043
|
const profile = store.bot(botId);
|
|
81719
82044
|
if (!profile) throw Object.assign(new Error("no such bot"), { status: 404 });
|
|
@@ -82173,7 +82498,7 @@ function checkedMemberIds(value) {
|
|
|
82173
82498
|
}
|
|
82174
82499
|
var bootSelection = { instanceId: "", model: "" };
|
|
82175
82500
|
var store = new Store(() => bootSelection);
|
|
82176
|
-
var teamComputers = new TeamComputers(
|
|
82501
|
+
var teamComputers = new TeamComputers(join65(DATA_DIR, "team-computers.json"), ENVIRONMENT_ID);
|
|
82177
82502
|
var followupsReady = false;
|
|
82178
82503
|
var sendSequencer = new SendSequencer();
|
|
82179
82504
|
bootSelection = await defaultSelection();
|
|
@@ -82194,7 +82519,7 @@ try {
|
|
|
82194
82519
|
}
|
|
82195
82520
|
if (browserCleanupReferencesReconciled) browserCleanup.startPending();
|
|
82196
82521
|
var activeCoordinationForThread = (_threadId) => false;
|
|
82197
|
-
var wireTask = (
|
|
82522
|
+
var wireTask = (task2) => activeCoordinationForThread(task2.threadId) && !task2.busy ? { ...toWireTask(task2), busy: true, activity: "working" } : toWireTask(task2);
|
|
82198
82523
|
var wireBot = (bot) => {
|
|
82199
82524
|
const { resumeCursors: _resumeCursors, tasks, approvalGrant, lastProfileRequestId: _lastProfileRequestId, lastTeamSetupReceipt: _lastTeamSetupReceipt, ...rest } = bot;
|
|
82200
82525
|
const visible = approvalGrant && !approvalGrant.threadOnly ? { ...rest, approvalMode: "ask", autoApprove: false } : rest;
|
|
@@ -82337,6 +82662,30 @@ function fullAccessForSource(botId, threadId) {
|
|
|
82337
82662
|
function peerReviewRequired(bot, threadId) {
|
|
82338
82663
|
return Boolean(bot.approvePeerComms && !fullAccessForSource(bot.id, threadId));
|
|
82339
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
|
+
}
|
|
82340
82689
|
function handleDesktopTrustedApprovalMessage(raw) {
|
|
82341
82690
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
|
|
82342
82691
|
const message4 = raw;
|
|
@@ -82834,7 +83183,7 @@ function outstandingAssignmentsPrompt(threadId) {
|
|
|
82834
83183
|
}));
|
|
82835
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.`;
|
|
82836
83185
|
}
|
|
82837
|
-
var roomHandoffs = new RoomHandoffs(
|
|
83186
|
+
var roomHandoffs = new RoomHandoffs(join65(DATA_DIR, "room-handoffs.json"), {
|
|
82838
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),
|
|
82839
83188
|
// A direct follow-up is owed to one conversation, so it waits for that
|
|
82840
83189
|
// conversation, not for the whole bot. Bot-level busy aggregates every
|
|
@@ -82984,7 +83333,7 @@ var roomHandoffs = new RoomHandoffs(join64(DATA_DIR, "room-handoffs.json"), {
|
|
|
82984
83333
|
await tracked;
|
|
82985
83334
|
return { ok: result.outcome === "settled", text: result.stopReason || result.replyText || result.outcome || "The addressed agent could not run" };
|
|
82986
83335
|
}
|
|
82987
|
-
});
|
|
83336
|
+
}, Date.now, roomHandoffLimits(cfg));
|
|
82988
83337
|
activeCoordinationForThread = (threadId) => roomHandoffs.activeDirect(threadId);
|
|
82989
83338
|
function publicGroupState(group) {
|
|
82990
83339
|
return { ...group, working: groupIsWorking(group) || [...roomHandoffs.nodes.values()].some((n) => n.groupId === group.id && !["completed", "failed", "cancelled"].includes(n.status)) };
|
|
@@ -83594,6 +83943,8 @@ var localVmLeases = new LocalVmLeasePool(30 * 6e4);
|
|
|
83594
83943
|
var localVmLifecycleBusy = /* @__PURE__ */ new Set();
|
|
83595
83944
|
var localVmThreadTargets = /* @__PURE__ */ new Map();
|
|
83596
83945
|
var localVmActiveThreads = /* @__PURE__ */ new Map();
|
|
83946
|
+
var autoVmClaims = /* @__PURE__ */ new Map();
|
|
83947
|
+
var LAZY_VM_CLAIM_GRACE_MS = 5e3;
|
|
83597
83948
|
var localVmSeen = /* @__PURE__ */ new Set();
|
|
83598
83949
|
function noteLocalVmSeen(target, status) {
|
|
83599
83950
|
if (status && autoLocalVmAttachable(status)) localVmSeen.add(target.key);
|
|
@@ -83945,6 +84296,7 @@ function localVmIdleFor(target) {
|
|
|
83945
84296
|
return idle;
|
|
83946
84297
|
}
|
|
83947
84298
|
function releaseLocalVmThread(threadId) {
|
|
84299
|
+
autoVmClaims.delete(threadId);
|
|
83948
84300
|
const target = localVmThreadTargets.get(threadId);
|
|
83949
84301
|
if (!target) return;
|
|
83950
84302
|
localVmLeaseFor(target).release(threadId);
|
|
@@ -84954,10 +85306,15 @@ async function startTurn(botId, text8, opts) {
|
|
|
84954
85306
|
}
|
|
84955
85307
|
const skipTranscript = /* @__PURE__ */ new Set([userMessage.id, ...opts?.excludeMessageIds ?? []]);
|
|
84956
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
|
+
}
|
|
84957
85314
|
const messagesById = new Map(store.messagesFor(threadId).map((message4) => [message4.id, message4]));
|
|
84958
85315
|
const transcript = activeMessages.filter((m3) => (m3.kind === "text" && m3.text || m3.roomRequest?.phase === "result") && !skipTranscript.has(m3.id)).slice(-40).map((m3) => ({
|
|
84959
85316
|
role: m3.role === "user" ? "user" : "assistant",
|
|
84960
|
-
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")
|
|
84961
85318
|
}));
|
|
84962
85319
|
const rewound = Boolean(task2.rewound);
|
|
84963
85320
|
const externalContextMarker = isExternalContextMarker(task2.lastInstanceId) ? task2.lastInstanceId : void 0;
|
|
@@ -85064,45 +85421,99 @@ async function startTurn(botId, text8, opts) {
|
|
|
85064
85421
|
let browserCapture = null;
|
|
85065
85422
|
let computerKind = null;
|
|
85066
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
|
+
};
|
|
85067
85465
|
const attachLocalVm = async (strict) => {
|
|
85068
85466
|
if (!mountsComputerMcp || instance.driverKind === "boxAgent") {
|
|
85069
85467
|
if (!strict) return false;
|
|
85070
85468
|
throw new Error("this model engine cannot use the Local VM \u2014 choose Claude or an ACP engine, or select another computer destination");
|
|
85071
85469
|
}
|
|
85072
85470
|
const localVmTarget = localVmTargetForBot(bot.id);
|
|
85471
|
+
let lazyReadyVm = null;
|
|
85073
85472
|
if (!strict) {
|
|
85074
85473
|
if (!localVmSeen.has(localVmTarget.key) && !opts?.automationSource) return false;
|
|
85075
85474
|
const seen = await containerComputerStatus(void 0, void 0, localVmTarget).catch(() => null);
|
|
85076
85475
|
if (!seen || !autoLocalVmAttachable(seen)) return false;
|
|
85077
85476
|
if (localVmImageBusy || localVmModeChangeBusy || localVmLifecycleBusy.has(localVmTarget.key)) return false;
|
|
85477
|
+
if (seen.ready && seen.runtime) lazyReadyVm = { runtime: seen.runtime };
|
|
85078
85478
|
}
|
|
85079
85479
|
try {
|
|
85080
|
-
|
|
85081
|
-
|
|
85082
|
-
|
|
85083
|
-
|
|
85084
|
-
|
|
85085
|
-
|
|
85086
|
-
|
|
85087
|
-
|
|
85088
|
-
|
|
85089
|
-
|
|
85090
|
-
|
|
85091
|
-
|
|
85092
|
-
|
|
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;
|
|
85093
85504
|
}
|
|
85505
|
+
const claimed = await claimAutoLocalVm(threadId);
|
|
85094
85506
|
integrations.localComputer = containerComputerMcp(
|
|
85095
|
-
|
|
85507
|
+
claimed.runtime,
|
|
85096
85508
|
controlIntegration(bot.id, threadId, dispatchClaimId),
|
|
85097
|
-
|
|
85509
|
+
claimed.target
|
|
85098
85510
|
);
|
|
85099
|
-
|
|
85100
|
-
|
|
85101
|
-
|
|
85102
|
-
|
|
85511
|
+
autoVmClaims.set(threadId, {
|
|
85512
|
+
owner: resourceOwner,
|
|
85513
|
+
claim: async () => {
|
|
85514
|
+
await claimAutoLocalVm(threadId);
|
|
85103
85515
|
}
|
|
85104
|
-
|
|
85105
|
-
};
|
|
85516
|
+
});
|
|
85106
85517
|
return true;
|
|
85107
85518
|
} catch (error51) {
|
|
85108
85519
|
if (strict) throw error51;
|
|
@@ -85118,7 +85529,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
85118
85529
|
hostPlatform: process.platform,
|
|
85119
85530
|
providerSupportsLocal: mountsLocalComputer
|
|
85120
85531
|
})) {
|
|
85121
|
-
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");
|
|
85122
85533
|
}
|
|
85123
85534
|
const cua = readCuaConnection();
|
|
85124
85535
|
if (!cua) throw new Error("CUA Driver is not ready for this computer \u2014 check permissions and restart OpenMausBot");
|
|
@@ -85583,10 +85994,7 @@ _loadPending();
|
|
|
85583
85994
|
routines = new RoutineManager({
|
|
85584
85995
|
emit: broadcast,
|
|
85585
85996
|
hasPendingDelegations: (threadId) => pendingThreads().includes(threadId) || [...delegationWatch.values()].some((watch) => watch.sourceThreadId === threadId) || pendingDelegationWakes.has(threadId),
|
|
85586
|
-
botState:
|
|
85587
|
-
const bot = store.bot(botId);
|
|
85588
|
-
return !bot ? "missing" : bot.busy ? "busy" : "ready";
|
|
85589
|
-
},
|
|
85997
|
+
botState: unattendedDispatchState,
|
|
85590
85998
|
goalState: (groupId, coordinatorBotId) => {
|
|
85591
85999
|
const group = store.group(groupId);
|
|
85592
86000
|
const coordinator = store.bot(coordinatorBotId);
|
|
@@ -85885,12 +86293,6 @@ async function deleteBotWithLifecycle(botId, revalidate = () => {
|
|
|
85885
86293
|
const acknowledged = await browserCleanup.ensure(committedCleanup);
|
|
85886
86294
|
requireBrowserCleanupAcknowledged(acknowledged, `Browser data for ${bot.name}`);
|
|
85887
86295
|
}
|
|
85888
|
-
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
85889
|
-
try {
|
|
85890
|
-
unlinkSync14(join64(dir, `${bot.threadId}.ndjson`));
|
|
85891
|
-
} catch {
|
|
85892
|
-
}
|
|
85893
|
-
}
|
|
85894
86296
|
return deletionResponse(200, { ok: true });
|
|
85895
86297
|
} finally {
|
|
85896
86298
|
if (claimedLocalVmTarget) localVmLifecycleBusy.delete(claimedLocalVmTarget.key);
|
|
@@ -86179,10 +86581,7 @@ function resolveAndSendProfile(res, args) {
|
|
|
86179
86581
|
}
|
|
86180
86582
|
var webhooks = new WebhookManager({
|
|
86181
86583
|
emit: broadcast,
|
|
86182
|
-
botState:
|
|
86183
|
-
const bot = store.bot(botId);
|
|
86184
|
-
return !bot ? "missing" : bot.busy ? "busy" : "ready";
|
|
86185
|
-
},
|
|
86584
|
+
botState: unattendedDispatchState,
|
|
86186
86585
|
enqueue: (input) => routines.enqueueWebhook(input),
|
|
86187
86586
|
findRun: (webhookId, deliveryId2) => routines.webhookRunReceipt(webhookId, deliveryId2),
|
|
86188
86587
|
cancelQueued: (webhookId, message4) => routines.cancelQueuedWebhook(webhookId, message4),
|
|
@@ -86267,7 +86666,7 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
86267
86666
|
}
|
|
86268
86667
|
revokeInternalCapabilitiesForThread(threadId);
|
|
86269
86668
|
spoken.add(botId);
|
|
86270
|
-
const preparedApprovalMode =
|
|
86669
|
+
const preparedApprovalMode = roomTurnApprovalMode(bot, orchestration);
|
|
86271
86670
|
const preparedSelection = { ...bot.modelSelection };
|
|
86272
86671
|
const preparedComposio = bot.composio;
|
|
86273
86672
|
const instance = turnInstance(bot);
|
|
@@ -86378,7 +86777,7 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
86378
86777
|
const readyGroup = store.group(group.id);
|
|
86379
86778
|
const stillOwnsThread = readyGroup?.dm ? readyGroup.threadId === threadId : Boolean(readyGroup && store.groupTaskByThread(readyGroup.id, threadId));
|
|
86380
86779
|
if (!readyGroup || !stillOwnsThread || !readyGroup.memberIds.includes(readyBot.id)) return false;
|
|
86381
|
-
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;
|
|
86382
86781
|
if (setupChanged) {
|
|
86383
86782
|
if (setupRetry === 0) {
|
|
86384
86783
|
return runGroupMemberTurn(
|
|
@@ -86682,7 +87081,7 @@ ${e.text}`;
|
|
|
86682
87081
|
text: text8,
|
|
86683
87082
|
refreshSystemPrompt: true,
|
|
86684
87083
|
images: turnImages,
|
|
86685
|
-
approvalMode:
|
|
87084
|
+
approvalMode: roomTurnApprovalMode(readyBot, orchestration),
|
|
86686
87085
|
system: roomSystem.text,
|
|
86687
87086
|
systemStable: roomSystem.stable,
|
|
86688
87087
|
systemVolatile: roomSystem.volatile,
|
|
@@ -88329,7 +88728,7 @@ var claudeUpdatesInFlight = /* @__PURE__ */ new Set();
|
|
|
88329
88728
|
function serveStatic(res, path) {
|
|
88330
88729
|
if (!STATIC_DIR) return false;
|
|
88331
88730
|
const safe = path === "/" ? "/index.html" : path.replace(/\.\./g, "");
|
|
88332
|
-
const file2 =
|
|
88731
|
+
const file2 = join65(STATIC_DIR, safe);
|
|
88333
88732
|
try {
|
|
88334
88733
|
const data = readFileSync45(file2);
|
|
88335
88734
|
res.writeHead(200, { "content-type": MIME[extname5(file2)] ?? "application/octet-stream" });
|
|
@@ -88337,7 +88736,7 @@ function serveStatic(res, path) {
|
|
|
88337
88736
|
return true;
|
|
88338
88737
|
} catch {
|
|
88339
88738
|
try {
|
|
88340
|
-
const data = readFileSync45(
|
|
88739
|
+
const data = readFileSync45(join65(STATIC_DIR, "index.html"));
|
|
88341
88740
|
res.writeHead(200, { "content-type": "text/html" });
|
|
88342
88741
|
res.end(data);
|
|
88343
88742
|
return true;
|
|
@@ -89354,15 +89753,19 @@ var handleRequest = async (req, res) => {
|
|
|
89354
89753
|
if (method === "POST" && path === "/api/internal/ask-bot") {
|
|
89355
89754
|
const body = await readInternalBody();
|
|
89356
89755
|
const fromBotId = internalSender.id;
|
|
89357
|
-
const
|
|
89756
|
+
const toBotRef = String(body.toBotId ?? "");
|
|
89358
89757
|
const message4 = String(body.message ?? "").trim();
|
|
89359
89758
|
if (body.depth !== void 0 && (!Number.isInteger(body.depth) || body.depth < 0 || body.depth !== internalCapability.depth)) {
|
|
89360
89759
|
return json3(res, 403, { error: "the recursion depth does not match this turn" });
|
|
89361
89760
|
}
|
|
89362
89761
|
const depth = internalCapability.depth;
|
|
89363
|
-
if (!
|
|
89364
|
-
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" });
|
|
89365
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;
|
|
89366
89769
|
const target = store.bot(toBotId);
|
|
89367
89770
|
if (!target) return json3(res, 404, { error: "no such bot" });
|
|
89368
89771
|
const from = internalSender;
|
|
@@ -89517,15 +89920,18 @@ var handleRequest = async (req, res) => {
|
|
|
89517
89920
|
}
|
|
89518
89921
|
if (method === "POST" && path === "/api/internal/delegate-bot") {
|
|
89519
89922
|
const body = await readInternalBody();
|
|
89520
|
-
const
|
|
89923
|
+
const toBotRef = String(body.toBotId ?? "");
|
|
89521
89924
|
const message4 = String(body.message ?? "").trim();
|
|
89522
89925
|
const reason = typeof body.reason === "string" && body.reason.trim() ? body.reason.trim() : void 0;
|
|
89523
89926
|
if (body.depth !== void 0 && (!Number.isInteger(body.depth) || body.depth < 0 || body.depth !== internalCapability.depth)) {
|
|
89524
89927
|
return json3(res, 403, { error: "the recursion depth does not match this turn" });
|
|
89525
89928
|
}
|
|
89526
89929
|
const depth = internalCapability.depth;
|
|
89527
|
-
if (!
|
|
89930
|
+
if (!toBotRef || !message4) return json3(res, 400, { error: "toBotId and message required" });
|
|
89528
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;
|
|
89529
89935
|
const target = store.bot(toBotId);
|
|
89530
89936
|
if (!target) return json3(res, 404, { error: "no such bot" });
|
|
89531
89937
|
if (!canAccessTeam(from, target.section) || target.hidden) {
|
|
@@ -89598,13 +90004,21 @@ var handleRequest = async (req, res) => {
|
|
|
89598
90004
|
const groupId = parsed.data.groupId ?? source?.id;
|
|
89599
90005
|
const destination = groupId ? store.group(groupId) : void 0;
|
|
89600
90006
|
if (groupId && !destination) return json3(res, 404, { error: "No such room; use list_room_targets." });
|
|
89601
|
-
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) => ({
|
|
89602
90015
|
groupId: destination?.id,
|
|
89603
90016
|
threadId: destination ? destination.id === source?.id ? address.threadId : destination.threadId : store.bot(botId)?.threadId ?? "",
|
|
89604
90017
|
botId
|
|
89605
90018
|
}));
|
|
89606
90019
|
for (const target of targets) {
|
|
89607
|
-
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);
|
|
89608
90022
|
if (eligibility) return json3(res, 403, { error: eligibility });
|
|
89609
90023
|
}
|
|
89610
90024
|
let approvalGranted = false;
|
|
@@ -89631,6 +90045,9 @@ var handleRequest = async (req, res) => {
|
|
|
89631
90045
|
if (!resolved) throw new Error("The recipient no longer exists");
|
|
89632
90046
|
target.threadId = resolved.task.threadId;
|
|
89633
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
|
+
}
|
|
89634
90051
|
}
|
|
89635
90052
|
const { node: node2, duplicate } = roomHandoffs.enqueue(
|
|
89636
90053
|
address,
|
|
@@ -89829,6 +90246,7 @@ ${message4}`;
|
|
|
89829
90246
|
}
|
|
89830
90247
|
const task2 = store.createTask(target.id, title, false, projectId, { botId: from.id, name: from.name, at: Date.now() });
|
|
89831
90248
|
if (!task2) return json3(res, 500, { error: "couldn't create that thread" });
|
|
90249
|
+
if (delegatedFullAccess(from, fromThreadId, target)) grantDelegatedFullAccess(from, target, task2.threadId);
|
|
89832
90250
|
const queued = queueDelegation(
|
|
89833
90251
|
commsBus,
|
|
89834
90252
|
from,
|
|
@@ -90068,6 +90486,29 @@ ${message4}`;
|
|
|
90068
90486
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
90069
90487
|
if (method === "GET") {
|
|
90070
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
|
+
}
|
|
90071
90512
|
const computer = turnComputerResources.get(internalCapability.threadId);
|
|
90072
90513
|
if (!snapshot2.held && computer && computer.owner.generation === internalCapability.generation && !claimTurnResource(computer.owner, computer.resource)) {
|
|
90073
90514
|
return json3(res, 200, {
|
|
@@ -91099,14 +91540,6 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
91099
91540
|
routines.disableForGroup(group.id);
|
|
91100
91541
|
store.deleteGroup(group.id);
|
|
91101
91542
|
rejectDeletedThreadSkillStages(stagedSkillCleanups);
|
|
91102
|
-
for (const threadId of threadIds) {
|
|
91103
|
-
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
91104
|
-
try {
|
|
91105
|
-
unlinkSync14(join64(dir, `${threadId}.ndjson`));
|
|
91106
|
-
} catch {
|
|
91107
|
-
}
|
|
91108
|
-
}
|
|
91109
|
-
}
|
|
91110
91543
|
return json3(res, 200, { ok: true });
|
|
91111
91544
|
}
|
|
91112
91545
|
m3 = path.match(/^\/api\/groups\/([\w-]+)\/messages$/);
|
|
@@ -91201,6 +91634,74 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
91201
91634
|
}
|
|
91202
91635
|
return json3(res, 200, { ok: true });
|
|
91203
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
|
+
}
|
|
91204
91705
|
m3 = path.match(/^\/api\/groups\/([\w-]+)\/interrupt$/);
|
|
91205
91706
|
if (m3 && method === "POST") {
|
|
91206
91707
|
const group = store.group(m3[1]);
|
|
@@ -92235,18 +92736,19 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
92235
92736
|
}
|
|
92236
92737
|
if (currentAtStart.busy) {
|
|
92237
92738
|
const instance = runningTurnInstance(currentAtStart, threadId);
|
|
92238
|
-
let steered =
|
|
92739
|
+
let steered = "refused";
|
|
92239
92740
|
const carriesImages = extractTurnImages(text8).images.length > 0;
|
|
92240
92741
|
if (!carriesImages && !computerSelectionTurns.get(threadId)?.selected && instance?.adapter.capabilities.queueing && instance.adapter.steer) {
|
|
92241
|
-
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");
|
|
92242
92743
|
}
|
|
92243
92744
|
const current2 = store.projectBotForTask(bot.id, threadId);
|
|
92244
92745
|
if (!current2) throw Object.assign(new Error("no such bot"), { status: 404 });
|
|
92245
92746
|
if (!store.taskByThread(bot.id, threadId)) {
|
|
92246
92747
|
throw Object.assign(new Error("the target task no longer exists"), { status: 409 });
|
|
92247
92748
|
}
|
|
92248
|
-
|
|
92249
|
-
|
|
92749
|
+
const delivered = steered !== "refused";
|
|
92750
|
+
if (delivered) {
|
|
92751
|
+
if (steered === "steered" && !current2.busy) {
|
|
92250
92752
|
throw Object.assign(
|
|
92251
92753
|
new Error("the running turn ended before the steered message could be recorded"),
|
|
92252
92754
|
{ status: 409 }
|
|
@@ -92289,6 +92791,53 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
92289
92791
|
}
|
|
92290
92792
|
return json3(res, 200, { ok: true });
|
|
92291
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
|
+
}
|
|
92292
92841
|
m3 = path.match(/^\/api\/bots\/([\w-]+)\/messages\/([\w-]+)\/edit$/);
|
|
92293
92842
|
if (m3 && method === "POST") {
|
|
92294
92843
|
const messageId = m3[2];
|
|
@@ -94157,6 +94706,27 @@ try {
|
|
|
94157
94706
|
} catch (error51) {
|
|
94158
94707
|
console.warn(`attachments: startup partial cleanup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
94159
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();
|
|
94160
94730
|
for (const row of chatFollowups()) {
|
|
94161
94731
|
if (row.status !== "dispatching" && row.status !== "interrupted") continue;
|
|
94162
94732
|
const owned = row.kind === "bot" ? Boolean(store.taskByThread(row.ownerId, row.threadId)) : Boolean(store.groupByThread(row.threadId)?.id === row.ownerId);
|