negotium 0.2.9 → 0.2.11
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/agent-helpers.js +336 -92
- package/dist/agent-helpers.js.map +8 -6
- package/dist/background-bash.js +2 -1
- package/dist/background-bash.js.map +3 -3
- package/dist/browser-runtime.js +2 -1
- package/dist/browser-runtime.js.map +3 -3
- package/dist/{chunk-knv045mn.js → chunk-dcfmsrnb.js} +2 -1
- package/dist/{chunk-knv045mn.js.map → chunk-dcfmsrnb.js.map} +3 -3
- package/dist/hosted-agent.js +3 -2
- package/dist/hosted-agent.js.map +4 -4
- package/dist/main.js +505 -252
- package/dist/main.js.map +9 -7
- package/dist/mcp-factories.js +4 -3
- package/dist/mcp-factories.js.map +4 -4
- package/dist/prompts.js +2 -1
- package/dist/prompts.js.map +3 -3
- package/dist/query-runtime.js +2 -1
- package/dist/query-runtime.js.map +3 -3
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/mcp-tools/inline-assets.ts +193 -0
- package/dist/runtime/src/agents/mcp-tools/publish-html.ts +183 -0
- package/dist/runtime/src/agents/public-helpers.ts +4 -0
- package/dist/runtime/src/mcp-runtime-host.ts +1 -0
- package/dist/runtime/src/platform/config.ts +12 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +2 -1
- package/dist/runtime-helpers.js.map +3 -3
- package/dist/types/packages/core/src/agents/mcp-tools/inline-assets.d.ts +24 -0
- package/dist/types/packages/core/src/agents/mcp-tools/publish-html.d.ts +18 -0
- package/dist/types/packages/core/src/agents/public-helpers.d.ts +1 -0
- package/dist/types/packages/core/src/platform/config.d.ts +7 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +2 -1
- package/dist/vault.js.map +3 -3
- package/package.json +1 -1
package/dist/agent-helpers.js
CHANGED
|
@@ -331,7 +331,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
|
|
|
331
331
|
function codexAuthFilePath() {
|
|
332
332
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
|
|
333
333
|
}
|
|
334
|
-
var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.17", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
|
|
334
|
+
var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.17", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", SNIPPETS_API_URL, BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
|
|
335
335
|
var init_config = __esm(() => {
|
|
336
336
|
init_config_helpers();
|
|
337
337
|
init_logger();
|
|
@@ -352,6 +352,7 @@ var init_config = __esm(() => {
|
|
|
352
352
|
SESSION_WORKSPACE_DIR = resolve2(STATE_DIR, "data", "sessions");
|
|
353
353
|
CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
354
354
|
CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve2(CLAUDE_EXECUTABLE_ENV) : undefined;
|
|
355
|
+
SNIPPETS_API_URL = (envText("NEGOTIUM_SNIPPETS_API_URL") ?? envText("SNIPPETS_API_URL") ?? "").replace(/\/+$/, "");
|
|
355
356
|
BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
|
|
356
357
|
PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
|
|
357
358
|
TSX_BIN = resolveDependencyBin("tsx");
|
|
@@ -2147,7 +2148,7 @@ var init_claude_registry = __esm(() => {
|
|
|
2147
2148
|
});
|
|
2148
2149
|
|
|
2149
2150
|
// ../../packages/core/src/version.ts
|
|
2150
|
-
var NEGOTIUM_VERSION = "0.2.
|
|
2151
|
+
var NEGOTIUM_VERSION = "0.2.11";
|
|
2151
2152
|
|
|
2152
2153
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
2153
2154
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -10227,7 +10228,7 @@ var init_manager_utils = () => {};
|
|
|
10227
10228
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
10228
10229
|
import { readdirSync as readdirSync3, unlinkSync as unlinkSync10 } from "fs";
|
|
10229
10230
|
import { createServer } from "net";
|
|
10230
|
-
import { resolve as
|
|
10231
|
+
import { resolve as resolve12, sep } from "path";
|
|
10231
10232
|
function isPortInUse(port) {
|
|
10232
10233
|
return new Promise((resolveProbe) => {
|
|
10233
10234
|
const server = createServer();
|
|
@@ -10260,8 +10261,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
|
|
|
10260
10261
|
return null;
|
|
10261
10262
|
}
|
|
10262
10263
|
function killBrowserProcsForUserDataDir(userDataDir) {
|
|
10263
|
-
const target =
|
|
10264
|
-
const profileRoot =
|
|
10264
|
+
const target = resolve12(userDataDir);
|
|
10265
|
+
const profileRoot = resolve12(BROWSER_PROFILES_DIR);
|
|
10265
10266
|
if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
|
|
10266
10267
|
return;
|
|
10267
10268
|
let pids;
|
|
@@ -10282,7 +10283,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
10282
10283
|
stdio: "pipe"
|
|
10283
10284
|
}).toString().trim();
|
|
10284
10285
|
const argDir = extractUserDataDirArg(cmdline);
|
|
10285
|
-
if (!argDir ||
|
|
10286
|
+
if (!argDir || resolve12(argDir) !== target)
|
|
10286
10287
|
continue;
|
|
10287
10288
|
killProcessTreeChildren(pidNum);
|
|
10288
10289
|
process.kill(pidNum, "SIGKILL");
|
|
@@ -10293,13 +10294,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
10293
10294
|
}
|
|
10294
10295
|
}
|
|
10295
10296
|
function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
|
|
10296
|
-
const root =
|
|
10297
|
-
const live = new Set([...liveUserDataDirs].map((d) =>
|
|
10297
|
+
const root = resolve12(profileRoot);
|
|
10298
|
+
const live = new Set([...liveUserDataDirs].map((d) => resolve12(d)));
|
|
10298
10299
|
const out = [];
|
|
10299
10300
|
for (const { pid, userDataDir } of procs) {
|
|
10300
10301
|
if (pid === selfPid || !userDataDir)
|
|
10301
10302
|
continue;
|
|
10302
|
-
const dir =
|
|
10303
|
+
const dir = resolve12(userDataDir);
|
|
10303
10304
|
if (dir !== root && !dir.startsWith(`${root}${sep}`))
|
|
10304
10305
|
continue;
|
|
10305
10306
|
if (live.has(dir))
|
|
@@ -10315,7 +10316,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
|
|
|
10315
10316
|
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
10316
10317
|
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
|
|
10317
10318
|
return;
|
|
10318
|
-
const profileRoot =
|
|
10319
|
+
const profileRoot = resolve12(BROWSER_PROFILES_DIR);
|
|
10319
10320
|
let pids;
|
|
10320
10321
|
try {
|
|
10321
10322
|
pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
|
|
@@ -10354,7 +10355,7 @@ function cleanSingletonFiles(userDataDir) {
|
|
|
10354
10355
|
for (const f of files) {
|
|
10355
10356
|
if (f.startsWith("Singleton")) {
|
|
10356
10357
|
try {
|
|
10357
|
-
unlinkSync10(
|
|
10358
|
+
unlinkSync10(resolve12(userDataDir, f));
|
|
10358
10359
|
logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
|
|
10359
10360
|
} catch (e) {
|
|
10360
10361
|
logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
|
|
@@ -10394,9 +10395,9 @@ var init_browser_processes = __esm(async () => {
|
|
|
10394
10395
|
|
|
10395
10396
|
// ../../packages/core/src/platform/playwright/headed-launch.ts
|
|
10396
10397
|
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
10397
|
-
import { delimiter, isAbsolute as
|
|
10398
|
+
import { delimiter, isAbsolute as isAbsolute4, resolve as resolve13 } from "path";
|
|
10398
10399
|
function findExecutableOnPath(command, environment = process.env) {
|
|
10399
|
-
const candidates =
|
|
10400
|
+
const candidates = isAbsolute4(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve13(directory, command));
|
|
10400
10401
|
for (const candidate of candidates) {
|
|
10401
10402
|
try {
|
|
10402
10403
|
accessSync2(candidate, constants2.X_OK);
|
|
@@ -10784,21 +10785,21 @@ import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
|
|
|
10784
10785
|
import { createHash as createHash3, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
10785
10786
|
import {
|
|
10786
10787
|
cpSync,
|
|
10787
|
-
existsSync as
|
|
10788
|
+
existsSync as existsSync17,
|
|
10788
10789
|
mkdirSync as mkdirSync11,
|
|
10789
|
-
readFileSync as
|
|
10790
|
+
readFileSync as readFileSync15,
|
|
10790
10791
|
rmSync as rmSync3,
|
|
10791
10792
|
unlinkSync as unlinkSync11,
|
|
10792
10793
|
writeFileSync as writeFileSync9
|
|
10793
10794
|
} from "fs";
|
|
10794
|
-
import { dirname as dirname11, join as join19, resolve as
|
|
10795
|
+
import { dirname as dirname11, join as join19, resolve as resolve14 } from "path";
|
|
10795
10796
|
function removeDefaultProfileDataDir(userDataDir) {
|
|
10796
|
-
const root =
|
|
10797
|
-
const target =
|
|
10797
|
+
const root = resolve14(BROWSER_PROFILES_DIR);
|
|
10798
|
+
const target = resolve14(userDataDir);
|
|
10798
10799
|
if (dirname11(target) !== root) {
|
|
10799
10800
|
throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
|
|
10800
10801
|
}
|
|
10801
|
-
const existed =
|
|
10802
|
+
const existed = existsSync17(target);
|
|
10802
10803
|
rmSync3(target, { recursive: true, force: true });
|
|
10803
10804
|
return existed;
|
|
10804
10805
|
}
|
|
@@ -11012,7 +11013,7 @@ function releasePort(port) {
|
|
|
11012
11013
|
usedPorts.delete(port);
|
|
11013
11014
|
}
|
|
11014
11015
|
function defaultProfileDir(_ownerId, profile) {
|
|
11015
|
-
return
|
|
11016
|
+
return resolve14(BROWSER_PROFILES_DIR, profile);
|
|
11016
11017
|
}
|
|
11017
11018
|
function resolveUserDataDir(instanceKey) {
|
|
11018
11019
|
return managerHost.resolveInstanceDataDir(instanceKey);
|
|
@@ -11373,11 +11374,11 @@ async function cloneProfileForChild(opts) {
|
|
|
11373
11374
|
killInstance(dstKey);
|
|
11374
11375
|
await delay(500);
|
|
11375
11376
|
}
|
|
11376
|
-
if (!
|
|
11377
|
+
if (!existsSync17(srcDir)) {
|
|
11377
11378
|
return { copied: false, srcDir, dstDir, reason: "src-missing" };
|
|
11378
11379
|
}
|
|
11379
11380
|
try {
|
|
11380
|
-
if (
|
|
11381
|
+
if (existsSync17(dstDir)) {
|
|
11381
11382
|
rmSync3(dstDir, { recursive: true, force: true });
|
|
11382
11383
|
}
|
|
11383
11384
|
mkdirSync11(dirname11(dstDir), { recursive: true });
|
|
@@ -11398,7 +11399,7 @@ async function cloneProfileForChild(opts) {
|
|
|
11398
11399
|
cleanSingletonFiles(dstDir);
|
|
11399
11400
|
for (const f of ["DevToolsActivePort", "LOCK"]) {
|
|
11400
11401
|
try {
|
|
11401
|
-
unlinkSync11(
|
|
11402
|
+
unlinkSync11(resolve14(dstDir, f));
|
|
11402
11403
|
} catch {}
|
|
11403
11404
|
}
|
|
11404
11405
|
logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
|
|
@@ -13034,8 +13035,8 @@ import {
|
|
|
13034
13035
|
mkdirSync as mkdirSync15,
|
|
13035
13036
|
openSync as openSync2,
|
|
13036
13037
|
readdirSync as readdirSync4,
|
|
13037
|
-
readFileSync as
|
|
13038
|
-
statSync as
|
|
13038
|
+
readFileSync as readFileSync16,
|
|
13039
|
+
statSync as statSync8,
|
|
13039
13040
|
unlinkSync as unlinkSync16,
|
|
13040
13041
|
writeFileSync as writeFileSync13
|
|
13041
13042
|
} from "fs";
|
|
@@ -13083,11 +13084,11 @@ function parsePendingAskFilename(fileName) {
|
|
|
13083
13084
|
}
|
|
13084
13085
|
function readPendingAskFile(path, fallback) {
|
|
13085
13086
|
try {
|
|
13086
|
-
const raw =
|
|
13087
|
+
const raw = readFileSync16(path, "utf8").trim();
|
|
13087
13088
|
if (!raw)
|
|
13088
13089
|
return null;
|
|
13089
13090
|
if (!raw.startsWith("{")) {
|
|
13090
|
-
const ts = new Date(
|
|
13091
|
+
const ts = new Date(statSync8(path).mtimeMs).toISOString();
|
|
13091
13092
|
return {
|
|
13092
13093
|
userId: fallback.userId,
|
|
13093
13094
|
from: fallback.from,
|
|
@@ -13127,7 +13128,7 @@ function isStale(record, path) {
|
|
|
13127
13128
|
if (Number.isFinite(ts))
|
|
13128
13129
|
return Date.now() - ts > PENDING_ASK_TTL_MS;
|
|
13129
13130
|
try {
|
|
13130
|
-
return Date.now() -
|
|
13131
|
+
return Date.now() - statSync8(path).mtimeMs > PENDING_ASK_TTL_MS;
|
|
13131
13132
|
} catch {
|
|
13132
13133
|
return false;
|
|
13133
13134
|
}
|
|
@@ -14214,12 +14215,12 @@ var init_errors = __esm(() => {
|
|
|
14214
14215
|
|
|
14215
14216
|
// ../../packages/core/src/runtime/event-heartbeat.ts
|
|
14216
14217
|
function nextOrHeartbeat(pending, intervalMs) {
|
|
14217
|
-
return new Promise((
|
|
14218
|
-
const timer = setTimeout(() =>
|
|
14218
|
+
return new Promise((resolve15, reject) => {
|
|
14219
|
+
const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
|
|
14219
14220
|
timer.unref?.();
|
|
14220
14221
|
pending.then((result) => {
|
|
14221
14222
|
clearTimeout(timer);
|
|
14222
|
-
|
|
14223
|
+
resolve15({ kind: "event", result });
|
|
14223
14224
|
}, (error) => {
|
|
14224
14225
|
clearTimeout(timer);
|
|
14225
14226
|
reject(error);
|
|
@@ -14476,7 +14477,7 @@ var init_visual_html = __esm(() => {
|
|
|
14476
14477
|
|
|
14477
14478
|
// ../../packages/core/src/runtime/visuals.ts
|
|
14478
14479
|
import { realpathSync as realpathSync4 } from "fs";
|
|
14479
|
-
import { isAbsolute as
|
|
14480
|
+
import { isAbsolute as isAbsolute5, resolve as resolve15 } from "path";
|
|
14480
14481
|
function activeVisualHtmlForPrompt(html) {
|
|
14481
14482
|
if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
|
|
14482
14483
|
return { html, omittedChars: 0 };
|
|
@@ -14527,8 +14528,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
|
|
|
14527
14528
|
return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
|
|
14528
14529
|
}
|
|
14529
14530
|
function isPathInside(baseDir, filePath) {
|
|
14530
|
-
const base =
|
|
14531
|
-
const normalized =
|
|
14531
|
+
const base = resolve15(baseDir);
|
|
14532
|
+
const normalized = resolve15(filePath);
|
|
14532
14533
|
try {
|
|
14533
14534
|
const realBase = realpathSync4(base);
|
|
14534
14535
|
const real = realpathSync4(normalized);
|
|
@@ -14590,7 +14591,7 @@ function resolveVisualMediaInput(topicId, input) {
|
|
|
14590
14591
|
}
|
|
14591
14592
|
const rawPath = input.file_path.trim();
|
|
14592
14593
|
const cwd = workspaceCwdFor(topicId);
|
|
14593
|
-
const candidate =
|
|
14594
|
+
const candidate = isAbsolute5(rawPath) ? rawPath : resolve15(cwd, rawPath);
|
|
14594
14595
|
if (!isPathInside(cwd, candidate)) {
|
|
14595
14596
|
return { error: "file_path must be inside the topic workspace" };
|
|
14596
14597
|
}
|
|
@@ -14664,8 +14665,8 @@ var init_token_stats = __esm(async () => {
|
|
|
14664
14665
|
|
|
14665
14666
|
// ../../packages/core/src/runtime/turn-event-stream.ts
|
|
14666
14667
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
14667
|
-
import { realpathSync as realpathSync5, statSync as
|
|
14668
|
-
import { isAbsolute as
|
|
14668
|
+
import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
|
|
14669
|
+
import { isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
|
|
14669
14670
|
function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
|
|
14670
14671
|
if (getRoomQuery(topicId)?.queryId !== queryId)
|
|
14671
14672
|
return false;
|
|
@@ -14980,7 +14981,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
14980
14981
|
case "file":
|
|
14981
14982
|
if (!silent && peerBridge) {
|
|
14982
14983
|
const cwd = workspaceCwdFor(topicId);
|
|
14983
|
-
const path =
|
|
14984
|
+
const path = isAbsolute6(event.path) ? event.path : resolve16(cwd, event.path);
|
|
14984
14985
|
if (!isPathInside(cwd, path)) {
|
|
14985
14986
|
logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
|
|
14986
14987
|
break;
|
|
@@ -14988,7 +14989,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
14988
14989
|
let safePath;
|
|
14989
14990
|
try {
|
|
14990
14991
|
safePath = realpathSync5(path);
|
|
14991
|
-
if (!
|
|
14992
|
+
if (!statSync9(safePath).isFile()) {
|
|
14992
14993
|
logger.warn({ topicId, path }, "peer output path is not a regular file");
|
|
14993
14994
|
break;
|
|
14994
14995
|
}
|
|
@@ -15264,7 +15265,7 @@ var init_turn_session = __esm(async () => {
|
|
|
15264
15265
|
});
|
|
15265
15266
|
|
|
15266
15267
|
// ../../packages/core/src/storage/app-settings.ts
|
|
15267
|
-
import { existsSync as
|
|
15268
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "fs";
|
|
15268
15269
|
import { dirname as dirname14, join as join27 } from "path";
|
|
15269
15270
|
function settingsFile() {
|
|
15270
15271
|
return join27(resolveStorageDataDir(), "otium-settings.json");
|
|
@@ -15276,8 +15277,8 @@ function ensureSettingsLoaded() {
|
|
|
15276
15277
|
loadedSettingsFile = path;
|
|
15277
15278
|
aiName = DEFAULT_AI_NAME;
|
|
15278
15279
|
try {
|
|
15279
|
-
if (
|
|
15280
|
-
const data = JSON.parse(
|
|
15280
|
+
if (existsSync18(path)) {
|
|
15281
|
+
const data = JSON.parse(readFileSync17(path, "utf8"));
|
|
15281
15282
|
if (typeof data.aiName === "string" && data.aiName.trim()) {
|
|
15282
15283
|
aiName = data.aiName.trim();
|
|
15283
15284
|
}
|
|
@@ -15369,7 +15370,7 @@ __export(exports_turn_runner, {
|
|
|
15369
15370
|
AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
|
|
15370
15371
|
});
|
|
15371
15372
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
15372
|
-
import { existsSync as
|
|
15373
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync19, readdirSync as readdirSync5, statSync as statSync10 } from "fs";
|
|
15373
15374
|
import { join as join28 } from "path";
|
|
15374
15375
|
function withDefaultPlaywright(configuredMcp, isManager) {
|
|
15375
15376
|
if (isManager)
|
|
@@ -15414,7 +15415,7 @@ function appendAskReplyMessage(topicId, text2, agentType) {
|
|
|
15414
15415
|
}
|
|
15415
15416
|
function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
|
|
15416
15417
|
const preferred = join28(directory, preferredFilename);
|
|
15417
|
-
if (preferExact &&
|
|
15418
|
+
if (preferExact && existsSync19(preferred))
|
|
15418
15419
|
return preferred;
|
|
15419
15420
|
let newestLegacyId = null;
|
|
15420
15421
|
let newestTitle = null;
|
|
@@ -15425,7 +15426,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
15425
15426
|
if (!titleMatch && !legacyIdMatch)
|
|
15426
15427
|
continue;
|
|
15427
15428
|
const path = join28(directory, filename);
|
|
15428
|
-
const mtimeMs =
|
|
15429
|
+
const mtimeMs = statSync10(path).mtimeMs;
|
|
15429
15430
|
if (titleMatch) {
|
|
15430
15431
|
if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
|
|
15431
15432
|
newestTitle = { path, mtimeMs };
|
|
@@ -15439,9 +15440,9 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
15439
15440
|
}
|
|
15440
15441
|
function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
|
|
15441
15442
|
const briefFile = resolveWikiMirrorPath(join28(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
|
|
15442
|
-
const hasBriefFile =
|
|
15443
|
+
const hasBriefFile = existsSync19(briefFile) && statSync10(briefFile).isFile();
|
|
15443
15444
|
const latestSummaryCandidate = resolveWikiMirrorPath(join28(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
|
|
15444
|
-
const latestSummaryFile =
|
|
15445
|
+
const latestSummaryFile = existsSync19(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
|
|
15445
15446
|
return { briefFile, hasBriefFile, latestSummaryFile };
|
|
15446
15447
|
}
|
|
15447
15448
|
async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
|
|
@@ -16635,7 +16636,7 @@ var init_turn_runner = __esm(async () => {
|
|
|
16635
16636
|
});
|
|
16636
16637
|
|
|
16637
16638
|
// ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
|
|
16638
|
-
import { z as
|
|
16639
|
+
import { z as z4 } from "zod";
|
|
16639
16640
|
function createSubagentLifecycle(host) {
|
|
16640
16641
|
const runtimeOwnerId = host.runtime.ownerId;
|
|
16641
16642
|
const limits = Object.freeze({
|
|
@@ -17170,7 +17171,7 @@ ${card.task}${reportInstruction}`;
|
|
|
17170
17171
|
name: "start_subagent",
|
|
17171
17172
|
description: "Start a prepared descendant subagent task.",
|
|
17172
17173
|
schema: {
|
|
17173
|
-
topic_id:
|
|
17174
|
+
topic_id: z4.string().describe("Prepared subagent room id.")
|
|
17174
17175
|
},
|
|
17175
17176
|
async handler(input) {
|
|
17176
17177
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17190,7 +17191,7 @@ ${card.task}${reportInstruction}`;
|
|
|
17190
17191
|
name: "delete_subagent",
|
|
17191
17192
|
description: "Permanently delete one descendant subagent room managed by the current room. " + "This removes its conversation, workspace, runtime state, and topic. Keep it when follow-up work is likely.",
|
|
17192
17193
|
schema: {
|
|
17193
|
-
topic_id:
|
|
17194
|
+
topic_id: z4.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
|
|
17194
17195
|
},
|
|
17195
17196
|
async handler(input) {
|
|
17196
17197
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17214,8 +17215,8 @@ ${card.task}${reportInstruction}`;
|
|
|
17214
17215
|
name: "grant_subagent_tell",
|
|
17215
17216
|
description: "Allow one descendant subagent to tell_session another topic in this managed subagent tree. " + "Direct-parent reporting is always allowed and does not need a grant.",
|
|
17216
17217
|
schema: {
|
|
17217
|
-
subagent_topic_id:
|
|
17218
|
-
target_topic_id:
|
|
17218
|
+
subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
|
|
17219
|
+
target_topic_id: z4.string().describe("Target topic id: this manager room or another descendant in its tree.")
|
|
17219
17220
|
},
|
|
17220
17221
|
async handler(input) {
|
|
17221
17222
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17241,8 +17242,8 @@ ${card.task}${reportInstruction}`;
|
|
|
17241
17242
|
name: "revoke_subagent_tell",
|
|
17242
17243
|
description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
|
|
17243
17244
|
schema: {
|
|
17244
|
-
subagent_topic_id:
|
|
17245
|
-
target_topic_id:
|
|
17245
|
+
subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
|
|
17246
|
+
target_topic_id: z4.string().describe("Granted target topic id.")
|
|
17246
17247
|
},
|
|
17247
17248
|
async handler(input) {
|
|
17248
17249
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17268,12 +17269,12 @@ ${card.task}${reportInstruction}`;
|
|
|
17268
17269
|
name: "spawn_subagent",
|
|
17269
17270
|
description: "Delegate a self-contained task to a subagent that works in its own new agent room. " + "Returns immediately; the subagent runs in the background and reports according to report_mode. " + "The fresh room has no parent conversation history; it receives the task plus the selected or inherited topic memory. " + "Use for parallelizable or long-running side work. Do not use provider built-in Task/Agent subagents.",
|
|
17270
17271
|
schema: {
|
|
17271
|
-
task:
|
|
17272
|
-
name:
|
|
17273
|
-
agent:
|
|
17274
|
-
model:
|
|
17275
|
-
memory_topic:
|
|
17276
|
-
report_mode:
|
|
17272
|
+
task: z4.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
|
|
17273
|
+
name: z4.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
|
|
17274
|
+
agent: z4.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
|
|
17275
|
+
model: z4.string().optional().describe(`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`),
|
|
17276
|
+
memory_topic: z4.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
|
|
17277
|
+
report_mode: z4.enum(["auto", "tell", "status-only"]).optional().describe("Completion reporting: auto injects the final result, tell requires tell_session, status-only updates only the lifecycle card.")
|
|
17277
17278
|
},
|
|
17278
17279
|
async handler(input) {
|
|
17279
17280
|
return createSubagent(ctx, input, true);
|
|
@@ -17285,12 +17286,12 @@ ${card.task}${reportInstruction}`;
|
|
|
17285
17286
|
name: "create_subagent",
|
|
17286
17287
|
description: "Create a prepared subagent room and task definition without starting its AI turn. " + "Use start_subagent when the team is ready to run it.",
|
|
17287
17288
|
schema: {
|
|
17288
|
-
task:
|
|
17289
|
-
name:
|
|
17290
|
-
agent:
|
|
17291
|
-
model:
|
|
17292
|
-
memory_topic:
|
|
17293
|
-
report_mode:
|
|
17289
|
+
task: z4.string().describe("Self-contained task brief for the subagent."),
|
|
17290
|
+
name: z4.string().optional().describe("Short room name. Auto-generated when omitted."),
|
|
17291
|
+
agent: z4.enum(["claude", "codex", "maestro"]).optional(),
|
|
17292
|
+
model: z4.string().optional(),
|
|
17293
|
+
memory_topic: z4.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
|
|
17294
|
+
report_mode: z4.enum(["auto", "tell", "status-only"]).optional()
|
|
17294
17295
|
},
|
|
17295
17296
|
async handler(input) {
|
|
17296
17297
|
return createSubagent(ctx, input, false);
|
|
@@ -17478,10 +17479,252 @@ await __promiseAll([
|
|
|
17478
17479
|
init_ask_user()
|
|
17479
17480
|
]);
|
|
17480
17481
|
|
|
17481
|
-
// ../../packages/core/src/agents/mcp-tools/
|
|
17482
|
+
// ../../packages/core/src/agents/mcp-tools/publish-html.ts
|
|
17482
17483
|
init_common();
|
|
17483
17484
|
import { z as z2 } from "zod";
|
|
17484
17485
|
|
|
17486
|
+
// ../../packages/core/src/agents/mcp-tools/inline-assets.ts
|
|
17487
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
17488
|
+
import { extname, isAbsolute as isAbsolute3, resolve as resolve11 } from "path";
|
|
17489
|
+
var MIME_BY_EXT = {
|
|
17490
|
+
".apng": "image/apng",
|
|
17491
|
+
".avif": "image/avif",
|
|
17492
|
+
".bmp": "image/bmp",
|
|
17493
|
+
".css": "text/css",
|
|
17494
|
+
".gif": "image/gif",
|
|
17495
|
+
".ico": "image/x-icon",
|
|
17496
|
+
".jpeg": "image/jpeg",
|
|
17497
|
+
".jpg": "image/jpeg",
|
|
17498
|
+
".mp3": "audio/mpeg",
|
|
17499
|
+
".mp4": "video/mp4",
|
|
17500
|
+
".otf": "font/otf",
|
|
17501
|
+
".png": "image/png",
|
|
17502
|
+
".svg": "image/svg+xml",
|
|
17503
|
+
".ttf": "font/ttf",
|
|
17504
|
+
".wav": "audio/wav",
|
|
17505
|
+
".webm": "video/webm",
|
|
17506
|
+
".webp": "image/webp",
|
|
17507
|
+
".woff": "font/woff",
|
|
17508
|
+
".woff2": "font/woff2"
|
|
17509
|
+
};
|
|
17510
|
+
function isNonLocal(ref) {
|
|
17511
|
+
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
17512
|
+
}
|
|
17513
|
+
function stripSuffix(ref) {
|
|
17514
|
+
return ref.replace(/[?#].*$/, "");
|
|
17515
|
+
}
|
|
17516
|
+
function inlineLocalAssets(html, options) {
|
|
17517
|
+
const { baseDir, maxAssetBytes, maxTotalBytes } = options;
|
|
17518
|
+
const inlined = [];
|
|
17519
|
+
const unresolved = [];
|
|
17520
|
+
const skipped = [];
|
|
17521
|
+
const cache = new Map;
|
|
17522
|
+
let budget = maxTotalBytes - Buffer.byteLength(html, "utf-8");
|
|
17523
|
+
function toDataUri(rawRef) {
|
|
17524
|
+
const ref = rawRef.trim();
|
|
17525
|
+
if (!ref || isNonLocal(ref))
|
|
17526
|
+
return null;
|
|
17527
|
+
const cached = cache.get(ref);
|
|
17528
|
+
if (cached !== undefined)
|
|
17529
|
+
return cached;
|
|
17530
|
+
const relPath = stripSuffix(ref);
|
|
17531
|
+
if (!relPath)
|
|
17532
|
+
return null;
|
|
17533
|
+
const abs = isAbsolute3(relPath) ? relPath : resolve11(baseDir, relPath);
|
|
17534
|
+
let bytes;
|
|
17535
|
+
try {
|
|
17536
|
+
if (!existsSync16(abs) || !statSync7(abs).isFile()) {
|
|
17537
|
+
unresolved.push(ref);
|
|
17538
|
+
cache.set(ref, null);
|
|
17539
|
+
return null;
|
|
17540
|
+
}
|
|
17541
|
+
bytes = statSync7(abs).size;
|
|
17542
|
+
} catch {
|
|
17543
|
+
unresolved.push(ref);
|
|
17544
|
+
cache.set(ref, null);
|
|
17545
|
+
return null;
|
|
17546
|
+
}
|
|
17547
|
+
if (bytes > maxAssetBytes) {
|
|
17548
|
+
skipped.push({ ref, bytes, reason: "asset-too-large" });
|
|
17549
|
+
cache.set(ref, null);
|
|
17550
|
+
return null;
|
|
17551
|
+
}
|
|
17552
|
+
const encodedCost = Math.ceil(bytes / 3) * 4;
|
|
17553
|
+
if (encodedCost > budget) {
|
|
17554
|
+
skipped.push({ ref, bytes, reason: "document-too-large" });
|
|
17555
|
+
cache.set(ref, null);
|
|
17556
|
+
return null;
|
|
17557
|
+
}
|
|
17558
|
+
let uri;
|
|
17559
|
+
try {
|
|
17560
|
+
const mime = MIME_BY_EXT[extname(abs).toLowerCase()] ?? "application/octet-stream";
|
|
17561
|
+
uri = `data:${mime};base64,${readFileSync14(abs).toString("base64")}`;
|
|
17562
|
+
} catch {
|
|
17563
|
+
unresolved.push(ref);
|
|
17564
|
+
cache.set(ref, null);
|
|
17565
|
+
return null;
|
|
17566
|
+
}
|
|
17567
|
+
budget -= uri.length;
|
|
17568
|
+
inlined.push({ ref, bytes });
|
|
17569
|
+
cache.set(ref, uri);
|
|
17570
|
+
return uri;
|
|
17571
|
+
}
|
|
17572
|
+
function rewriteSrcset(value) {
|
|
17573
|
+
return value.split(",").map((candidate) => {
|
|
17574
|
+
const trimmed = candidate.trim();
|
|
17575
|
+
if (!trimmed)
|
|
17576
|
+
return candidate;
|
|
17577
|
+
const [url, ...descriptor] = trimmed.split(/\s+/);
|
|
17578
|
+
const uri = url ? toDataUri(url) : null;
|
|
17579
|
+
if (!uri)
|
|
17580
|
+
return candidate;
|
|
17581
|
+
return [uri, ...descriptor].join(" ");
|
|
17582
|
+
}).join(", ");
|
|
17583
|
+
}
|
|
17584
|
+
let out = html;
|
|
17585
|
+
out = out.replace(/(\b(?:src|poster)\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, ref) => {
|
|
17586
|
+
const uri = toDataUri(ref);
|
|
17587
|
+
return uri ? `${prefix}${quote}${uri}${quote}` : match;
|
|
17588
|
+
});
|
|
17589
|
+
out = out.replace(/(\bsrcset\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, value) => {
|
|
17590
|
+
const rewritten = rewriteSrcset(value);
|
|
17591
|
+
return rewritten === value ? match : `${prefix}${quote}${rewritten}${quote}`;
|
|
17592
|
+
});
|
|
17593
|
+
out = out.replace(/<link\b[^>]*>/gi, (tag) => tag.replace(/(\bhref\s*=\s*)(["'])([^"']*)\2/i, (match, prefix, quote, ref) => {
|
|
17594
|
+
const uri = toDataUri(ref);
|
|
17595
|
+
return uri ? `${prefix}${quote}${uri}${quote}` : match;
|
|
17596
|
+
}));
|
|
17597
|
+
out = out.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/gi, (match, quote, ref) => {
|
|
17598
|
+
const uri = toDataUri(ref);
|
|
17599
|
+
return uri ? `url(${quote}${uri}${quote})` : match;
|
|
17600
|
+
});
|
|
17601
|
+
return { html: out, inlined, unresolved, skipped };
|
|
17602
|
+
}
|
|
17603
|
+
|
|
17604
|
+
// ../../packages/core/src/agents/mcp-tools/publish-html.ts
|
|
17605
|
+
init_config();
|
|
17606
|
+
var REQUEST_TIMEOUT_MS = 30000;
|
|
17607
|
+
var MAX_PUBLISHED_BYTES = 10 * 1024 * 1024;
|
|
17608
|
+
var MAX_PUBLISHED_ASSET_BYTES = 5 * 1024 * 1024;
|
|
17609
|
+
function formatPublishBytes(bytes) {
|
|
17610
|
+
if (bytes < 1024)
|
|
17611
|
+
return `${bytes}B`;
|
|
17612
|
+
if (bytes < 1024 * 1024)
|
|
17613
|
+
return `${(bytes / 1024).toFixed(0)}KB`;
|
|
17614
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
17615
|
+
}
|
|
17616
|
+
function parseSnippetId(input) {
|
|
17617
|
+
const trimmed = input.trim();
|
|
17618
|
+
const fromUrl = trimmed.match(/\/snippets\/([A-Za-z0-9-]+)/)?.[1];
|
|
17619
|
+
const id = fromUrl ?? trimmed;
|
|
17620
|
+
return /^[A-Za-z0-9-]+$/.test(id) ? id : null;
|
|
17621
|
+
}
|
|
17622
|
+
async function snippetRequest(baseUrl, path, init = {}) {
|
|
17623
|
+
try {
|
|
17624
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
17625
|
+
...init,
|
|
17626
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
17627
|
+
});
|
|
17628
|
+
const body = await response.json().catch(() => null);
|
|
17629
|
+
if (body)
|
|
17630
|
+
return body;
|
|
17631
|
+
return { ok: false, error: `snippet service returned ${response.status}` };
|
|
17632
|
+
} catch (error) {
|
|
17633
|
+
const message = error instanceof Error ? error.message : "snippet service unreachable";
|
|
17634
|
+
return { ok: false, error: message };
|
|
17635
|
+
}
|
|
17636
|
+
}
|
|
17637
|
+
function createPublishHtmlToolDefinitions(ctx, options = {}) {
|
|
17638
|
+
const apiUrl = (options.apiUrl ?? SNIPPETS_API_URL)?.replace(/\/+$/, "");
|
|
17639
|
+
if (!apiUrl)
|
|
17640
|
+
return [];
|
|
17641
|
+
const getCtx = typeof ctx === "function" ? ctx : () => ctx;
|
|
17642
|
+
return [
|
|
17643
|
+
{
|
|
17644
|
+
name: "publish_html",
|
|
17645
|
+
description: [
|
|
17646
|
+
"Publish an HTML document and return a public, shareable https link that",
|
|
17647
|
+
"stays live for 72 hours. Use this when the user should be able to open,",
|
|
17648
|
+
"keep, or send the result \u2014 a report, dashboard, or styled table \u2014 rather",
|
|
17649
|
+
"than only glance at it in the side panel.",
|
|
17650
|
+
"Pass a complete standalone document and inline the CSS and JS.",
|
|
17651
|
+
"For charts and diagrams prefer inline <svg>: it stays sharp at any zoom",
|
|
17652
|
+
"and is far smaller than an image file.",
|
|
17653
|
+
"Images may be remote URLs, data: URIs, or paths to local files you",
|
|
17654
|
+
"created \u2014 local files are embedded automatically, so the page keeps",
|
|
17655
|
+
"working after your workspace is gone."
|
|
17656
|
+
].join(" "),
|
|
17657
|
+
schema: {
|
|
17658
|
+
html: z2.string().describe("Complete standalone HTML document to publish. Inline the CSS and JS."),
|
|
17659
|
+
base_dir: z2.string().optional().describe("Directory that relative image paths resolve against. Defaults to the working directory.")
|
|
17660
|
+
},
|
|
17661
|
+
async handler(input) {
|
|
17662
|
+
const html = input?.html ?? "";
|
|
17663
|
+
if (!html.trim())
|
|
17664
|
+
return errorResult("html is empty \u2014 provide an HTML document.");
|
|
17665
|
+
const baseDir = input.base_dir?.trim() || getCtx().cwd || process.cwd();
|
|
17666
|
+
const report = inlineLocalAssets(html, {
|
|
17667
|
+
baseDir,
|
|
17668
|
+
maxAssetBytes: MAX_PUBLISHED_ASSET_BYTES,
|
|
17669
|
+
maxTotalBytes: MAX_PUBLISHED_BYTES
|
|
17670
|
+
});
|
|
17671
|
+
const bytes = Buffer.byteLength(report.html, "utf-8");
|
|
17672
|
+
if (bytes > MAX_PUBLISHED_BYTES) {
|
|
17673
|
+
return errorResult(`Document is ${formatPublishBytes(bytes)} after embedding images, over the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit. Use fewer or smaller images, or switch charts to inline <svg>.`);
|
|
17674
|
+
}
|
|
17675
|
+
const result = await snippetRequest(apiUrl, "/snippets", {
|
|
17676
|
+
method: "POST",
|
|
17677
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
17678
|
+
body: report.html
|
|
17679
|
+
});
|
|
17680
|
+
if (!result.ok || !result.url) {
|
|
17681
|
+
return errorResult(result.error ?? "Failed to publish.");
|
|
17682
|
+
}
|
|
17683
|
+
const notes = [];
|
|
17684
|
+
if (report.inlined.length > 0) {
|
|
17685
|
+
const total = report.inlined.reduce((sum, a) => sum + a.bytes, 0);
|
|
17686
|
+
notes.push(`Embedded ${report.inlined.length} local file(s), ${formatPublishBytes(total)}.`);
|
|
17687
|
+
}
|
|
17688
|
+
if (report.unresolved.length > 0) {
|
|
17689
|
+
notes.push(`WARNING \u2014 these look like local files but were not found, so they will render broken: ${report.unresolved.join(", ")}. Check the paths (relative to ${baseDir}) and republish.`);
|
|
17690
|
+
}
|
|
17691
|
+
for (const s of report.skipped) {
|
|
17692
|
+
notes.push(s.reason === "asset-too-large" ? `WARNING \u2014 ${s.ref} is ${formatPublishBytes(s.bytes)}, over the ${formatPublishBytes(MAX_PUBLISHED_ASSET_BYTES)} per-file limit, and was left as a broken link.` : `WARNING \u2014 ${s.ref} was left out because the document hit the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit.`);
|
|
17693
|
+
}
|
|
17694
|
+
const payload = {
|
|
17695
|
+
url: result.url,
|
|
17696
|
+
snippetId: result.snippetId,
|
|
17697
|
+
expiresAt: result.expiresAt,
|
|
17698
|
+
sizeBytes: bytes,
|
|
17699
|
+
...report.inlined.length > 0 ? { embeddedFiles: report.inlined.length } : {}
|
|
17700
|
+
};
|
|
17701
|
+
return textResult([JSON.stringify(payload, null, 2), ...notes].join(`
|
|
17702
|
+
|
|
17703
|
+
`));
|
|
17704
|
+
}
|
|
17705
|
+
},
|
|
17706
|
+
{
|
|
17707
|
+
name: "unpublish_html",
|
|
17708
|
+
description: "Delete a previously published HTML document before its 72-hour expiry. Accepts the URL returned by publish_html, or its id.",
|
|
17709
|
+
schema: {
|
|
17710
|
+
snippet: z2.string().describe("URL returned by publish_html, or the bare id.")
|
|
17711
|
+
},
|
|
17712
|
+
async handler(input) {
|
|
17713
|
+
const id = parseSnippetId(input?.snippet ?? "");
|
|
17714
|
+
if (!id)
|
|
17715
|
+
return errorResult("Could not read a published document id from that value.");
|
|
17716
|
+
const result = await snippetRequest(apiUrl, `/snippets/${id}`, { method: "DELETE" });
|
|
17717
|
+
if (!result.ok)
|
|
17718
|
+
return errorResult(result.error ?? "Failed to delete.");
|
|
17719
|
+
return textResult(`Published document ${id} deleted.`);
|
|
17720
|
+
}
|
|
17721
|
+
}
|
|
17722
|
+
];
|
|
17723
|
+
}
|
|
17724
|
+
// ../../packages/core/src/agents/mcp-tools/self-config.ts
|
|
17725
|
+
init_common();
|
|
17726
|
+
import { z as z3 } from "zod";
|
|
17727
|
+
|
|
17485
17728
|
// ../../packages/core/src/agents/api-topic-agent-switch.ts
|
|
17486
17729
|
init_model_catalog();
|
|
17487
17730
|
init_logger();
|
|
@@ -18027,7 +18270,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18027
18270
|
{
|
|
18028
18271
|
name: "set_model",
|
|
18029
18272
|
description: toolDescription(core, "set_model", "Set the model for THIS topic; it persists and applies from the NEXT turn. Use a model from the system prompt's same-agent catalog. Cross-agent changes require an explicit user-requested set_agent first. Fails if the user locked this setting. NEVER use 'fable' unless explicitly requested."),
|
|
18030
|
-
schema: { model:
|
|
18273
|
+
schema: { model: z3.string().describe("Model id valid for the topic's current agent.") },
|
|
18031
18274
|
async handler({ model }) {
|
|
18032
18275
|
return mcpResult(core.setModel(getCtx(), model));
|
|
18033
18276
|
}
|
|
@@ -18043,7 +18286,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18043
18286
|
{
|
|
18044
18287
|
name: "set_agent",
|
|
18045
18288
|
description: toolDescription(core, "set_agent", "Switch the agent backend for THIS topic between 'claude', 'codex', and 'maestro'. Clears the topic's model/effort override so the new agent starts at its defaults. Fails if the user locked this setting. Use only when the user explicitly asks to switch the runtime itself."),
|
|
18046
|
-
schema: { agent:
|
|
18289
|
+
schema: { agent: z3.enum(AGENT_VALUES) },
|
|
18047
18290
|
async handler({ agent }) {
|
|
18048
18291
|
return mcpResult(core.setAgent(getCtx(), agent));
|
|
18049
18292
|
}
|
|
@@ -18060,7 +18303,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18060
18303
|
name: "set_effort",
|
|
18061
18304
|
description: toolDescription(core, "set_effort", "Set the reasoning effort for THIS topic. Claude: low/medium/high/xhigh/max. Codex: low/medium/high/xhigh. Maestro: low/medium/high/xhigh/max. Higher effort costs more and is slower. Fails if the user locked this setting."),
|
|
18062
18305
|
schema: {
|
|
18063
|
-
effort:
|
|
18306
|
+
effort: z3.enum(EFFORT_VALUES).describe("Reasoning effort level valid for the topic's current agent.")
|
|
18064
18307
|
},
|
|
18065
18308
|
async handler({ effort }) {
|
|
18066
18309
|
return mcpResult(core.setEffort(getCtx(), effort));
|
|
@@ -18080,8 +18323,8 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18080
18323
|
name: "schedule_self",
|
|
18081
18324
|
description: toolDescription(core, "schedule_self", "Create the one pending durable delayed continuation allowed for THIS topic without blocking the current turn. Use it to check a long-running operation or resume work later. The future message must be self-contained. If one already exists, use get_self_schedule then update_self_schedule or cancel_self_schedule. For recurring or longer-lived schedules, use cron-manager instead."),
|
|
18082
18325
|
schema: {
|
|
18083
|
-
delay_seconds:
|
|
18084
|
-
message:
|
|
18326
|
+
delay_seconds: z3.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).describe(`Seconds before this topic resumes, from 1 through ${core.product.scheduleMaxDelaySeconds}${core.product.scheduleMaxDelaySeconds === 86400 ? " (24 hours)" : ""}.`),
|
|
18327
|
+
message: z3.string().min(1).max(core.product.scheduleMaxMessageLength).describe("Self-contained instruction delivered to your future turn.")
|
|
18085
18328
|
},
|
|
18086
18329
|
async handler({ delay_seconds, message }) {
|
|
18087
18330
|
return mcpResult(core.scheduleContinue(getCtx(), delay_seconds, message));
|
|
@@ -18099,9 +18342,9 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18099
18342
|
name: "update_self_schedule",
|
|
18100
18343
|
description: toolDescription(core, "update_self_schedule", "Edit THIS topic's pending self-schedule. Provide its schedule_id from schedule_self/get_self_schedule and change delay_seconds, message, or both. delay_seconds is measured from this update. A schedule that already started cannot be edited."),
|
|
18101
18344
|
schema: {
|
|
18102
|
-
schedule_id:
|
|
18103
|
-
delay_seconds:
|
|
18104
|
-
message:
|
|
18345
|
+
schedule_id: z3.string().min(1).describe("Exact ID of the pending self-schedule."),
|
|
18346
|
+
delay_seconds: z3.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).optional().describe(`New delay from now, from 1 through ${core.product.scheduleMaxDelaySeconds} seconds.`),
|
|
18347
|
+
message: z3.string().min(1).max(core.product.scheduleMaxMessageLength).optional().describe("Replacement self-contained future instruction.")
|
|
18105
18348
|
},
|
|
18106
18349
|
async handler({
|
|
18107
18350
|
schedule_id,
|
|
@@ -18118,7 +18361,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18118
18361
|
name: "cancel_self_schedule",
|
|
18119
18362
|
description: toolDescription(core, "cancel_self_schedule", "Cancel THIS topic's pending self-schedule by its exact schedule_id. A schedule that already started cannot be cancelled with this tool."),
|
|
18120
18363
|
schema: {
|
|
18121
|
-
schedule_id:
|
|
18364
|
+
schedule_id: z3.string().min(1).describe("Exact ID of the pending self-schedule.")
|
|
18122
18365
|
},
|
|
18123
18366
|
async handler({ schedule_id }) {
|
|
18124
18367
|
return mcpResult(core.cancelSchedule(getCtx(), schedule_id));
|
|
@@ -18130,7 +18373,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18130
18373
|
name: "spawn_topic",
|
|
18131
18374
|
description: toolDescription(core, "spawn_topic", `Create a new forum topic that inherits all settings from THIS topic but starts with a fresh session. Limited to ${core.product.derivedTopicLimit} calls per conversation turn.`),
|
|
18132
18375
|
schema: {
|
|
18133
|
-
name:
|
|
18376
|
+
name: z3.string().optional().describe("New topic name. Auto-generated if omitted.")
|
|
18134
18377
|
},
|
|
18135
18378
|
async handler({ name }) {
|
|
18136
18379
|
if (derivedCount >= core.product.derivedTopicLimit) {
|
|
@@ -18147,7 +18390,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
|
|
|
18147
18390
|
name: "fork_topic",
|
|
18148
18391
|
description: toolDescription(core, "fork_topic", `Fork THIS topic by creating a new topic that inherits both settings AND conversation history. Limited to ${core.product.derivedTopicLimit} calls per conversation turn.`),
|
|
18149
18392
|
schema: {
|
|
18150
|
-
name:
|
|
18393
|
+
name: z3.string().optional().describe("New topic name. Auto-generated if omitted.")
|
|
18151
18394
|
},
|
|
18152
18395
|
async handler({ name }) {
|
|
18153
18396
|
if (derivedCount >= core.product.derivedTopicLimit) {
|
|
@@ -18169,14 +18412,14 @@ await init_spawn_subagent();
|
|
|
18169
18412
|
|
|
18170
18413
|
// ../../packages/core/src/agents/mcp-tools/visuals.ts
|
|
18171
18414
|
init_common();
|
|
18172
|
-
import { z as
|
|
18415
|
+
import { z as z5 } from "zod";
|
|
18173
18416
|
var VISUALS_MCP_KEY = "visuals";
|
|
18174
18417
|
var showHtmlTool = {
|
|
18175
18418
|
name: "show_html",
|
|
18176
18419
|
description: "Display charts, tables, or interactive HTML to the user in a sandboxed WebView side panel. Pass a complete, self-contained HTML string with inline CSS/JS. Local DOM interactions are supported. Links (<a href> to http/https URLs) are safe to include: a click opens the URL in the user's system browser. Network requests, external scripts, form posts, popups, and parent-window access are blocked.",
|
|
18177
18420
|
schema: {
|
|
18178
|
-
html:
|
|
18179
|
-
title:
|
|
18421
|
+
html: z5.string().describe("Complete, self-contained HTML document or fragment to render."),
|
|
18422
|
+
title: z5.string().optional().describe("Optional title shown above the rendered card.")
|
|
18180
18423
|
},
|
|
18181
18424
|
async handler() {
|
|
18182
18425
|
return textResult("HTML card displayed to the user.");
|
|
@@ -18186,9 +18429,9 @@ var showMermaidTool = {
|
|
|
18186
18429
|
name: "show_mermaid",
|
|
18187
18430
|
description: "Render a Mermaid diagram in the user's visual side panel. Pass Mermaid DSL only, not markdown fences. Use this for flowcharts, sequence diagrams, class diagrams, state diagrams, ER diagrams, timelines, and architecture diagrams.",
|
|
18188
18431
|
schema: {
|
|
18189
|
-
code:
|
|
18190
|
-
title:
|
|
18191
|
-
theme:
|
|
18432
|
+
code: z5.string().describe("Mermaid diagram source without markdown code fences."),
|
|
18433
|
+
title: z5.string().optional().describe("Optional title shown above the rendered card."),
|
|
18434
|
+
theme: z5.enum(["default", "neutral", "dark", "forest"]).optional().describe("Optional Mermaid theme. Defaults to neutral.")
|
|
18192
18435
|
},
|
|
18193
18436
|
async handler() {
|
|
18194
18437
|
return textResult("Mermaid diagram displayed to the user.");
|
|
@@ -18198,10 +18441,10 @@ var showImageTool = {
|
|
|
18198
18441
|
name: "show_image",
|
|
18199
18442
|
description: "Display an image in the user's visual side panel. Provide either file_path for an image in the topic workspace, or file_id for an uploaded file already attached in this topic.",
|
|
18200
18443
|
schema: {
|
|
18201
|
-
file_path:
|
|
18202
|
-
file_id:
|
|
18203
|
-
title:
|
|
18204
|
-
alt:
|
|
18444
|
+
file_path: z5.string().optional().describe("Absolute or workspace-relative path to an image file."),
|
|
18445
|
+
file_id: z5.string().optional().describe("Full Otium uploaded file UUID for an image attached in this topic."),
|
|
18446
|
+
title: z5.string().optional().describe("Optional title shown above the rendered card."),
|
|
18447
|
+
alt: z5.string().optional().describe("Optional alt text for the image.")
|
|
18205
18448
|
},
|
|
18206
18449
|
async handler(input) {
|
|
18207
18450
|
if (!input?.file_path && !input?.file_id) {
|
|
@@ -18214,9 +18457,9 @@ var showVideoTool = {
|
|
|
18214
18457
|
name: "show_video",
|
|
18215
18458
|
description: "Display a playable video in the user's visual side panel. Provide either file_path for a video in the topic workspace, or file_id for an uploaded file already attached in this topic.",
|
|
18216
18459
|
schema: {
|
|
18217
|
-
file_path:
|
|
18218
|
-
file_id:
|
|
18219
|
-
title:
|
|
18460
|
+
file_path: z5.string().optional().describe("Absolute or workspace-relative path to a video file."),
|
|
18461
|
+
file_id: z5.string().optional().describe("Full Otium uploaded file UUID for a video attached in this topic."),
|
|
18462
|
+
title: z5.string().optional().describe("Optional title shown above the rendered card.")
|
|
18220
18463
|
},
|
|
18221
18464
|
async handler(input) {
|
|
18222
18465
|
if (!input?.file_path && !input?.file_id) {
|
|
@@ -18290,6 +18533,7 @@ export {
|
|
|
18290
18533
|
createSelfConfigToolDefinitionsForCore,
|
|
18291
18534
|
createSelfConfigRuntime,
|
|
18292
18535
|
createSelfConfigCore,
|
|
18536
|
+
createPublishHtmlToolDefinitions,
|
|
18293
18537
|
createCodexTreeManager,
|
|
18294
18538
|
createAskUserRuntime,
|
|
18295
18539
|
createArchiverRuntime,
|
|
@@ -18309,4 +18553,4 @@ export {
|
|
|
18309
18553
|
DEFAULT_SELF_CONFIG_PRODUCT
|
|
18310
18554
|
};
|
|
18311
18555
|
|
|
18312
|
-
//# debugId=
|
|
18556
|
+
//# debugId=893EA9DA75C8A56D64756E2164756E21
|