negotium 0.3.0 → 0.3.2
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 +107 -6
- package/dist/agent-helpers.js.map +11 -11
- package/dist/background-bash.js +3 -2
- package/dist/background-bash.js.map +4 -4
- package/dist/browser-runtime.js +3 -2
- package/dist/browser-runtime.js.map +4 -4
- package/dist/chunk-s2gez3wg.js.map +1 -1
- package/dist/{chunk-1y2xw0xe.js → chunk-vvfwqxnh.js} +33 -3
- package/dist/{chunk-1y2xw0xe.js.map → chunk-vvfwqxnh.js.map} +8 -8
- package/dist/hosted-agent.js +34 -4
- package/dist/hosted-agent.js.map +8 -8
- package/dist/main.js +1179 -366
- package/dist/main.js.map +24 -21
- package/dist/mcp-catalog.js +2 -1
- package/dist/mcp-catalog.js.map +3 -3
- package/dist/mcp-factories.js +635 -256
- package/dist/mcp-factories.js.map +15 -13
- package/dist/mcp-servers.js +3 -1
- package/dist/mcp-servers.js.map +3 -3
- package/dist/prompts.js +8 -2
- package/dist/prompts.js.map +5 -5
- package/dist/query-runtime.js +3 -2
- package/dist/query-runtime.js.map +4 -4
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/mcp/canonical-bridge-config.ts +1 -1
- package/dist/runtime/src/mcp/canonical-proxy-server.ts +74 -1
- package/dist/runtime/src/mcp/decision-server.ts +21 -0
- package/dist/runtime/src/mcp/factories/decision.ts +178 -0
- package/dist/runtime/src/mcp/factories/index.ts +6 -0
- package/dist/runtime/src/mcp/runtime-spec.ts +1 -0
- package/dist/runtime/src/node-host.ts +1 -0
- package/dist/runtime/src/platform/config.ts +2 -1
- package/dist/runtime/src/platform/mcp-catalog-policy.ts +1 -0
- package/dist/runtime/src/platform/mcp-config.ts +31 -1
- package/dist/runtime/src/prompts/builders.ts +5 -0
- package/dist/runtime/src/runtime/visual-html.ts +68 -2
- package/dist/runtime/src/storage/decisions.ts +231 -0
- package/dist/runtime/src/storage/storage-public.ts +2 -0
- package/dist/runtime/src/types.ts +13 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +71 -4
- package/dist/runtime-helpers.js.map +5 -5
- package/dist/storage.js +261 -56
- package/dist/storage.js.map +5 -4
- package/dist/types/apps/negotium/src/mcp-servers.d.ts +1 -1
- package/dist/types/packages/core/src/mcp/canonical-bridge-config.d.ts +1 -1
- package/dist/types/packages/core/src/mcp/factories/decision.d.ts +16 -0
- package/dist/types/packages/core/src/mcp/factories/index.d.ts +1 -0
- package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +1 -1
- package/dist/types/packages/core/src/platform/config.d.ts +2 -1
- package/dist/types/packages/core/src/platform/mcp-catalog-policy.d.ts +4 -0
- package/dist/types/packages/core/src/storage/decisions.d.ts +45 -0
- package/dist/types/packages/core/src/storage/storage-public.d.ts +2 -0
- package/dist/types/packages/core/src/types.d.ts +12 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +3 -2
- package/dist/vault.js.map +4 -4
- package/install-browser-rs.mjs +3 -3
- package/package.json +2 -2
package/dist/mcp-factories.js
CHANGED
|
@@ -313,7 +313,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
|
|
|
313
313
|
function codexAuthFilePath() {
|
|
314
314
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
|
|
315
315
|
}
|
|
316
|
-
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.
|
|
316
|
+
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.21", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000, SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.5", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, DECISION_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, NODE_ID, 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, BASHRS_SPILL_ROOT, 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;
|
|
317
317
|
var init_config = __esm(() => {
|
|
318
318
|
init_config_helpers();
|
|
319
319
|
init_logger();
|
|
@@ -343,6 +343,7 @@ var init_config = __esm(() => {
|
|
|
343
343
|
TSCONFIG_PATH = resolve(PROJECT_ROOT, "tsconfig.json");
|
|
344
344
|
SESSION_COMM_SERVER = resolve(PROJECT_ROOT, "src/mcp/session-comm/server.ts");
|
|
345
345
|
TASK_SERVER = resolve(PROJECT_ROOT, "src/mcp/task-server.ts");
|
|
346
|
+
DECISION_SERVER = resolve(PROJECT_ROOT, "src/mcp/decision-server.ts");
|
|
346
347
|
BROWSER_MCP_SSE_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/browser-sse-proxy-server.ts");
|
|
347
348
|
CANONICAL_MCP_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/canonical-proxy-server.ts");
|
|
348
349
|
WIKI_SERVER = resolve(PROJECT_ROOT, "src/mcp/wiki-server.ts");
|
|
@@ -1016,7 +1017,7 @@ var init_claude_registry = __esm(() => {
|
|
|
1016
1017
|
});
|
|
1017
1018
|
|
|
1018
1019
|
// ../../packages/core/src/version.ts
|
|
1019
|
-
var NEGOTIUM_VERSION = "0.3.
|
|
1020
|
+
var NEGOTIUM_VERSION = "0.3.2";
|
|
1020
1021
|
|
|
1021
1022
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
1022
1023
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -1707,6 +1708,7 @@ var init_mcp_catalog_policy = __esm(() => {
|
|
|
1707
1708
|
runtime: { scopes: ["forum", "manager", "fork", "cron"], forumRequired: true },
|
|
1708
1709
|
"token-stats": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
1709
1710
|
task: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
1711
|
+
decision: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
1710
1712
|
"session-comm": { scopes: ["forum", "fork", "manager"], forumRequired: true },
|
|
1711
1713
|
wiki: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
1712
1714
|
skills: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
@@ -1930,7 +1932,7 @@ function getForumMcpServers(opts) {
|
|
|
1930
1932
|
peerBridge
|
|
1931
1933
|
} = opts;
|
|
1932
1934
|
const filter = (name) => {
|
|
1933
|
-
if (silent && name === "task")
|
|
1935
|
+
if (silent && (name === "task" || name === "decision"))
|
|
1934
1936
|
return false;
|
|
1935
1937
|
if (enabled === null)
|
|
1936
1938
|
return true;
|
|
@@ -2148,6 +2150,34 @@ var init_mcp_config = __esm(() => {
|
|
|
2148
2150
|
return buildBuiltinMcpServer("task", ctx, () => buildStdioMcpServer(agent, TASK_SERVER, args));
|
|
2149
2151
|
}
|
|
2150
2152
|
},
|
|
2153
|
+
decision: {
|
|
2154
|
+
...commonRuntimeMcpPolicy("decision"),
|
|
2155
|
+
build(ctx) {
|
|
2156
|
+
const { userId, session, topicId, queryId, agent, model, peerBridge } = ctx;
|
|
2157
|
+
if (peerBridge) {
|
|
2158
|
+
if (!topicId || !queryId)
|
|
2159
|
+
return null;
|
|
2160
|
+
const env = canonicalMcpBridgeEnv({
|
|
2161
|
+
surface: "decision",
|
|
2162
|
+
userId,
|
|
2163
|
+
topicId,
|
|
2164
|
+
queryId,
|
|
2165
|
+
peerBridge
|
|
2166
|
+
});
|
|
2167
|
+
return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=decision"], env) : null;
|
|
2168
|
+
}
|
|
2169
|
+
const args = [
|
|
2170
|
+
`--user-id=${userId}`,
|
|
2171
|
+
`--topic=${session}`,
|
|
2172
|
+
`--agent=${agent ?? FALLBACK_AGENT}`
|
|
2173
|
+
];
|
|
2174
|
+
if (topicId)
|
|
2175
|
+
args.push(`--topic-id=${topicId}`);
|
|
2176
|
+
if (model)
|
|
2177
|
+
args.push(`--model=${model}`);
|
|
2178
|
+
return buildBuiltinMcpServer("decision", ctx, () => buildStdioMcpServer(agent, DECISION_SERVER, args));
|
|
2179
|
+
}
|
|
2180
|
+
},
|
|
2151
2181
|
"session-comm": {
|
|
2152
2182
|
...commonRuntimeMcpPolicy("session-comm"),
|
|
2153
2183
|
build(ctx) {
|
|
@@ -3549,6 +3579,21 @@ function mcpError(text) {
|
|
|
3549
3579
|
}
|
|
3550
3580
|
var init_mcp_helpers = () => {};
|
|
3551
3581
|
|
|
3582
|
+
// ../../packages/core/src/security/sanitize.ts
|
|
3583
|
+
function sanitizeTopicName(name, lowercase = false) {
|
|
3584
|
+
const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
|
|
3585
|
+
return lowercase ? safe.toLowerCase() : safe;
|
|
3586
|
+
}
|
|
3587
|
+
function sanitizeFileName(name) {
|
|
3588
|
+
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
|
|
3589
|
+
if (safe === "." || safe === "..")
|
|
3590
|
+
return "_";
|
|
3591
|
+
return safe;
|
|
3592
|
+
}
|
|
3593
|
+
function sanitizeId(id) {
|
|
3594
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, "_") || "_";
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3552
3597
|
// ../../packages/core/src/agents/mcp-tools/common.ts
|
|
3553
3598
|
function textResult(text) {
|
|
3554
3599
|
return mcpOk(text);
|
|
@@ -5227,7 +5272,7 @@ var init_registry = __esm(async () => {
|
|
|
5227
5272
|
});
|
|
5228
5273
|
|
|
5229
5274
|
// ../../packages/core/src/agents/fork.ts
|
|
5230
|
-
import { existsSync as
|
|
5275
|
+
import { existsSync as existsSync12, unlinkSync as unlinkSync9 } from "fs";
|
|
5231
5276
|
function createAgentForkHelpers(host) {
|
|
5232
5277
|
return {
|
|
5233
5278
|
async forkAgentSession(options) {
|
|
@@ -5264,7 +5309,7 @@ var init_fork = __esm(async () => {
|
|
|
5264
5309
|
init_logger();
|
|
5265
5310
|
defaultForkHelpers = createAgentForkHelpers({
|
|
5266
5311
|
forkSession: (agent, options) => getRegistryOperations(agent).forkSession(options),
|
|
5267
|
-
exists:
|
|
5312
|
+
exists: existsSync12,
|
|
5268
5313
|
unlink: unlinkSync9,
|
|
5269
5314
|
warn: (details, message) => logger.warn(details, message)
|
|
5270
5315
|
});
|
|
@@ -5935,7 +5980,7 @@ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from
|
|
|
5935
5980
|
import { chmodSync as chmodSync5 } from "fs";
|
|
5936
5981
|
import { createServer as createServer2 } from "net";
|
|
5937
5982
|
import { tmpdir as tmpdir2 } from "os";
|
|
5938
|
-
import { join as
|
|
5983
|
+
import { join as join13 } from "path";
|
|
5939
5984
|
function deepMapStrings(value2, transform) {
|
|
5940
5985
|
if (typeof value2 === "string")
|
|
5941
5986
|
return transform(value2);
|
|
@@ -6022,7 +6067,7 @@ function authorized(actual, expected) {
|
|
|
6022
6067
|
}
|
|
6023
6068
|
async function createBrowserVaultBroker(userId) {
|
|
6024
6069
|
const token = randomBytes5(32).toString("hex");
|
|
6025
|
-
const socketPath =
|
|
6070
|
+
const socketPath = join13(process.platform === "win32" ? tmpdir2() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes5(8).toString("hex")}.sock`);
|
|
6026
6071
|
const retainedForms = new Map;
|
|
6027
6072
|
const leases = new Map;
|
|
6028
6073
|
const sockets = new Set;
|
|
@@ -6159,21 +6204,6 @@ var init_vault_broker = __esm(async () => {
|
|
|
6159
6204
|
};
|
|
6160
6205
|
});
|
|
6161
6206
|
|
|
6162
|
-
// ../../packages/core/src/security/sanitize.ts
|
|
6163
|
-
function sanitizeTopicName(name, lowercase = false) {
|
|
6164
|
-
const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
|
|
6165
|
-
return lowercase ? safe.toLowerCase() : safe;
|
|
6166
|
-
}
|
|
6167
|
-
function sanitizeFileName(name) {
|
|
6168
|
-
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
|
|
6169
|
-
if (safe === "." || safe === "..")
|
|
6170
|
-
return "_";
|
|
6171
|
-
return safe;
|
|
6172
|
-
}
|
|
6173
|
-
function sanitizeId(id) {
|
|
6174
|
-
return id.replace(/[^a-zA-Z0-9_-]/g, "_") || "_";
|
|
6175
|
-
}
|
|
6176
|
-
|
|
6177
6207
|
// ../../packages/core/src/storage/browser-profiles.ts
|
|
6178
6208
|
function normalizeBrowserProfileName(name) {
|
|
6179
6209
|
const value2 = name.trim().toLowerCase();
|
|
@@ -6239,21 +6269,21 @@ import { execFileSync as execFileSync4, spawn as spawn6 } from "child_process";
|
|
|
6239
6269
|
import { createHash as createHash3, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
6240
6270
|
import {
|
|
6241
6271
|
cpSync,
|
|
6242
|
-
existsSync as
|
|
6243
|
-
mkdirSync as
|
|
6244
|
-
readFileSync as
|
|
6272
|
+
existsSync as existsSync13,
|
|
6273
|
+
mkdirSync as mkdirSync10,
|
|
6274
|
+
readFileSync as readFileSync11,
|
|
6245
6275
|
rmSync as rmSync2,
|
|
6246
6276
|
unlinkSync as unlinkSync11,
|
|
6247
|
-
writeFileSync as
|
|
6277
|
+
writeFileSync as writeFileSync8
|
|
6248
6278
|
} from "fs";
|
|
6249
|
-
import { dirname as
|
|
6279
|
+
import { dirname as dirname9, join as join14, resolve as resolve10 } from "path";
|
|
6250
6280
|
function removeDefaultProfileDataDir(userDataDir) {
|
|
6251
6281
|
const root = resolve10(BROWSER_PROFILES_DIR);
|
|
6252
6282
|
const target = resolve10(userDataDir);
|
|
6253
|
-
if (
|
|
6283
|
+
if (dirname9(target) !== root) {
|
|
6254
6284
|
throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
|
|
6255
6285
|
}
|
|
6256
|
-
const existed =
|
|
6286
|
+
const existed = existsSync13(target);
|
|
6257
6287
|
rmSync2(target, { recursive: true, force: true });
|
|
6258
6288
|
return existed;
|
|
6259
6289
|
}
|
|
@@ -6335,15 +6365,15 @@ function portFileName(instanceKey) {
|
|
|
6335
6365
|
}
|
|
6336
6366
|
function writePortFile(instanceKey, port) {
|
|
6337
6367
|
try {
|
|
6338
|
-
|
|
6339
|
-
|
|
6368
|
+
mkdirSync10(managerHost.portsDir, { recursive: true });
|
|
6369
|
+
writeFileSync8(join14(managerHost.portsDir, portFileName(instanceKey)), String(port));
|
|
6340
6370
|
} catch (e) {
|
|
6341
6371
|
logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
|
|
6342
6372
|
}
|
|
6343
6373
|
}
|
|
6344
6374
|
function deletePortFile(instanceKey) {
|
|
6345
6375
|
try {
|
|
6346
|
-
unlinkSync11(
|
|
6376
|
+
unlinkSync11(join14(managerHost.portsDir, portFileName(instanceKey)));
|
|
6347
6377
|
} catch (e) {
|
|
6348
6378
|
if (e.code === "ENOENT")
|
|
6349
6379
|
return;
|
|
@@ -6544,7 +6574,7 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
|
|
|
6544
6574
|
}
|
|
6545
6575
|
const userDataDir = resolveUserDataDir(instanceKey);
|
|
6546
6576
|
const port = reservedPort ?? await allocatePort();
|
|
6547
|
-
|
|
6577
|
+
mkdirSync10(userDataDir, { recursive: true });
|
|
6548
6578
|
const mcpArgs = [
|
|
6549
6579
|
"--port",
|
|
6550
6580
|
String(port),
|
|
@@ -6828,14 +6858,14 @@ async function cloneProfileForChild(opts) {
|
|
|
6828
6858
|
killInstance(dstKey);
|
|
6829
6859
|
await delay(500);
|
|
6830
6860
|
}
|
|
6831
|
-
if (!
|
|
6861
|
+
if (!existsSync13(srcDir)) {
|
|
6832
6862
|
return { copied: false, srcDir, dstDir, reason: "src-missing" };
|
|
6833
6863
|
}
|
|
6834
6864
|
try {
|
|
6835
|
-
if (
|
|
6865
|
+
if (existsSync13(dstDir)) {
|
|
6836
6866
|
rmSync2(dstDir, { recursive: true, force: true });
|
|
6837
6867
|
}
|
|
6838
|
-
|
|
6868
|
+
mkdirSync10(dirname9(dstDir), { recursive: true });
|
|
6839
6869
|
if (process.platform === "darwin") {
|
|
6840
6870
|
try {
|
|
6841
6871
|
execFileSync4("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
|
|
@@ -7397,30 +7427,30 @@ var init_api_topic_config = __esm(async () => {
|
|
|
7397
7427
|
|
|
7398
7428
|
// ../../packages/core/src/storage/conversations.ts
|
|
7399
7429
|
import {
|
|
7400
|
-
existsSync as
|
|
7401
|
-
mkdirSync as
|
|
7402
|
-
readFileSync as
|
|
7403
|
-
renameSync as
|
|
7430
|
+
existsSync as existsSync14,
|
|
7431
|
+
mkdirSync as mkdirSync11,
|
|
7432
|
+
readFileSync as readFileSync12,
|
|
7433
|
+
renameSync as renameSync6,
|
|
7404
7434
|
unlinkSync as unlinkSync12,
|
|
7405
|
-
writeFileSync as
|
|
7435
|
+
writeFileSync as writeFileSync9
|
|
7406
7436
|
} from "fs";
|
|
7407
|
-
import { dirname as
|
|
7437
|
+
import { dirname as dirname10, join as join15 } from "path";
|
|
7408
7438
|
function conversationDir(_userId) {
|
|
7409
|
-
return
|
|
7439
|
+
return join15(resolveStorageDataDir(), "conversations");
|
|
7410
7440
|
}
|
|
7411
7441
|
function topicFilename(topicName) {
|
|
7412
7442
|
const t = sanitizeTopicName(topicName, true);
|
|
7413
7443
|
return `${t}.jsonl`;
|
|
7414
7444
|
}
|
|
7415
7445
|
function getConversationPath(userId, topicName) {
|
|
7416
|
-
return
|
|
7446
|
+
return join15(conversationDir(userId), topicFilename(topicName));
|
|
7417
7447
|
}
|
|
7418
7448
|
function getActiveConversationPath(userId, topicName) {
|
|
7419
7449
|
const rawPath = getConversationPath(userId, topicName);
|
|
7420
7450
|
return rawPath.endsWith(".jsonl") ? `${rawPath.slice(0, -".jsonl".length)}.active.jsonl` : `${rawPath}.active`;
|
|
7421
7451
|
}
|
|
7422
7452
|
function hasActiveConversation(userId, topicName) {
|
|
7423
|
-
return
|
|
7453
|
+
return existsSync14(getActiveConversationPath(userId, topicName));
|
|
7424
7454
|
}
|
|
7425
7455
|
function appendConversationEvent(userId, topicName, agent, event) {
|
|
7426
7456
|
try {
|
|
@@ -7439,10 +7469,10 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
|
|
|
7439
7469
|
event
|
|
7440
7470
|
};
|
|
7441
7471
|
const line = JSON.stringify(entry);
|
|
7442
|
-
|
|
7472
|
+
mkdirSync11(dirname10(path), { recursive: true });
|
|
7443
7473
|
appendJsonlLine(path, line);
|
|
7444
7474
|
const activePath = getActiveConversationPath(userId, topicName);
|
|
7445
|
-
if (
|
|
7475
|
+
if (existsSync14(activePath)) {
|
|
7446
7476
|
try {
|
|
7447
7477
|
appendJsonlLine(activePath, line);
|
|
7448
7478
|
} catch (cause) {
|
|
@@ -7452,11 +7482,11 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
|
|
|
7452
7482
|
}
|
|
7453
7483
|
function readConversationPath(path) {
|
|
7454
7484
|
const out = [];
|
|
7455
|
-
if (!
|
|
7485
|
+
if (!existsSync14(path))
|
|
7456
7486
|
return out;
|
|
7457
7487
|
let raw;
|
|
7458
7488
|
try {
|
|
7459
|
-
raw =
|
|
7489
|
+
raw = readFileSync12(path, "utf8");
|
|
7460
7490
|
} catch (err) {
|
|
7461
7491
|
logger.warn({ err, path }, "readConversation: read failed");
|
|
7462
7492
|
return out;
|
|
@@ -7475,7 +7505,7 @@ function readConversationPath(path) {
|
|
|
7475
7505
|
}
|
|
7476
7506
|
function readConversation(userId, topicName) {
|
|
7477
7507
|
const activePath = getActiveConversationPath(userId, topicName);
|
|
7478
|
-
return readConversationPath(
|
|
7508
|
+
return readConversationPath(existsSync14(activePath) ? activePath : getConversationPath(userId, topicName));
|
|
7479
7509
|
}
|
|
7480
7510
|
function readRawConversation(userId, topicName) {
|
|
7481
7511
|
return readConversationPath(getConversationPath(userId, topicName));
|
|
@@ -7488,12 +7518,12 @@ function replaceRawConversationStrict(userId, topicName, entries) {
|
|
|
7488
7518
|
}
|
|
7489
7519
|
function replaceConversationPathStrict(path, entries) {
|
|
7490
7520
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
7491
|
-
|
|
7521
|
+
mkdirSync11(dirname10(path), { recursive: true });
|
|
7492
7522
|
try {
|
|
7493
|
-
|
|
7523
|
+
writeFileSync9(tempPath, entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(`
|
|
7494
7524
|
`)}
|
|
7495
7525
|
` : "", { flag: "wx" });
|
|
7496
|
-
|
|
7526
|
+
renameSync6(tempPath, path);
|
|
7497
7527
|
} catch (error) {
|
|
7498
7528
|
try {
|
|
7499
7529
|
unlinkSync12(tempPath);
|
|
@@ -7635,8 +7665,8 @@ RULES:
|
|
|
7635
7665
|
- Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
|
|
7636
7666
|
|
|
7637
7667
|
// ../../packages/core/src/storage/tasks.ts
|
|
7638
|
-
import { existsSync as
|
|
7639
|
-
import { dirname as
|
|
7668
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync13, renameSync as renameSync7, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
7669
|
+
import { dirname as dirname11, join as join16 } from "path";
|
|
7640
7670
|
function safeTaskScopeKey(scopeKey) {
|
|
7641
7671
|
const safe = sanitizeFileName(scopeKey);
|
|
7642
7672
|
if (!safe || safe === "." || safe === "..") {
|
|
@@ -7648,14 +7678,14 @@ function taskScopeKey(opts) {
|
|
|
7648
7678
|
return opts.topicId?.trim() || opts.session || "default";
|
|
7649
7679
|
}
|
|
7650
7680
|
function getTaskFilePath(userId, scopeKey) {
|
|
7651
|
-
return
|
|
7681
|
+
return join16(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
|
|
7652
7682
|
}
|
|
7653
7683
|
function readTasks(userId, scopeKey) {
|
|
7654
7684
|
const path = getTaskFilePath(userId, scopeKey);
|
|
7655
|
-
if (!
|
|
7685
|
+
if (!existsSync15(path))
|
|
7656
7686
|
return [];
|
|
7657
7687
|
try {
|
|
7658
|
-
const parsed = JSON.parse(
|
|
7688
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7659
7689
|
return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
|
|
7660
7690
|
} catch (e) {
|
|
7661
7691
|
logger.warn({ err: e, path }, "tasks: failed to read task store");
|
|
@@ -7664,11 +7694,11 @@ function readTasks(userId, scopeKey) {
|
|
|
7664
7694
|
}
|
|
7665
7695
|
function writeTasks(userId, scopeKey, tasks) {
|
|
7666
7696
|
const path = getTaskFilePath(userId, scopeKey);
|
|
7667
|
-
|
|
7697
|
+
mkdirSync12(dirname11(path), { recursive: true });
|
|
7668
7698
|
const payload = { version: 1, tasks };
|
|
7669
7699
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
7670
|
-
|
|
7671
|
-
|
|
7700
|
+
writeFileSync10(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
7701
|
+
renameSync7(tmp, path);
|
|
7672
7702
|
}
|
|
7673
7703
|
function taskFileMtimeNs(userId, scopeKey) {
|
|
7674
7704
|
try {
|
|
@@ -7910,7 +7940,7 @@ __export(exports_claude_provider, {
|
|
|
7910
7940
|
buildClaudeDisallowedTools: () => buildClaudeDisallowedTools
|
|
7911
7941
|
});
|
|
7912
7942
|
import { spawn as spawn7 } from "child_process";
|
|
7913
|
-
import { existsSync as
|
|
7943
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
7914
7944
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
7915
7945
|
function claudeBuiltInTools(opts) {
|
|
7916
7946
|
return opts.toolPolicy ? [] : undefined;
|
|
@@ -7963,7 +7993,7 @@ function buildClaudePrompt(opts) {
|
|
|
7963
7993
|
source: {
|
|
7964
7994
|
type: "base64",
|
|
7965
7995
|
media_type: attachment.mimeType,
|
|
7966
|
-
data:
|
|
7996
|
+
data: readFileSync14(attachment.path).toString("base64")
|
|
7967
7997
|
}
|
|
7968
7998
|
});
|
|
7969
7999
|
} catch (err) {
|
|
@@ -8091,7 +8121,7 @@ function isAbortError(err) {
|
|
|
8091
8121
|
}
|
|
8092
8122
|
async function* claudeProvider(opts) {
|
|
8093
8123
|
const claudeExecutable = hostedClaudeCodeExecutablePath();
|
|
8094
|
-
if (claudeExecutable && !
|
|
8124
|
+
if (claudeExecutable && !existsSync16(claudeExecutable)) {
|
|
8095
8125
|
yield {
|
|
8096
8126
|
type: "error",
|
|
8097
8127
|
content: `Claude CLI not found at ${claudeExecutable}. Install Claude Code or configure the executable path.`
|
|
@@ -8611,7 +8641,7 @@ var init_codex_tree_kill = __esm(() => {
|
|
|
8611
8641
|
});
|
|
8612
8642
|
|
|
8613
8643
|
// ../../packages/core/src/agents/tool-format.ts
|
|
8614
|
-
import { readFileSync as
|
|
8644
|
+
import { readFileSync as readFileSync15, statSync as statSync5 } from "fs";
|
|
8615
8645
|
import { isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
|
|
8616
8646
|
import { diffLines as computeLineDiff } from "diff";
|
|
8617
8647
|
function summarizeDisplayText(value2) {
|
|
@@ -8940,7 +8970,7 @@ function sourceStartLine(input, before, after, cwd) {
|
|
|
8940
8970
|
try {
|
|
8941
8971
|
if (statSync5(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
|
|
8942
8972
|
return 1;
|
|
8943
|
-
const source =
|
|
8973
|
+
const source = readFileSync15(path, "utf8").replace(/\r\n?/g, `
|
|
8944
8974
|
`);
|
|
8945
8975
|
for (const candidate of [before, after].filter(Boolean)) {
|
|
8946
8976
|
const index = source.indexOf(candidate);
|
|
@@ -9134,9 +9164,9 @@ __export(exports_codex_provider, {
|
|
|
9134
9164
|
codexProvider: () => codexProvider
|
|
9135
9165
|
});
|
|
9136
9166
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9137
|
-
import { existsSync as
|
|
9167
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
|
|
9138
9168
|
import { homedir as homedir6 } from "os";
|
|
9139
|
-
import { dirname as
|
|
9169
|
+
import { dirname as dirname12, isAbsolute as isAbsolute3, join as join17, relative as relative2, resolve as resolve12 } from "path";
|
|
9140
9170
|
import { Codex } from "@openai/codex-sdk";
|
|
9141
9171
|
function sameCodexUsage(usage, total) {
|
|
9142
9172
|
return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
|
|
@@ -9164,12 +9194,12 @@ function codexMcpServerName(name) {
|
|
|
9164
9194
|
return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
|
|
9165
9195
|
}
|
|
9166
9196
|
function globalCodexMcpServerNames(authFilePath) {
|
|
9167
|
-
const configPath =
|
|
9168
|
-
if (!
|
|
9197
|
+
const configPath = join17(dirname12(authFilePath), "config.toml");
|
|
9198
|
+
if (!existsSync17(configPath))
|
|
9169
9199
|
return [];
|
|
9170
9200
|
try {
|
|
9171
9201
|
const names = new Set;
|
|
9172
|
-
for (const line of
|
|
9202
|
+
for (const line of readFileSync16(configPath, "utf8").split(`
|
|
9173
9203
|
`)) {
|
|
9174
9204
|
const match = line.match(/^\s*\[\s*mcp_servers\.((?:"(?:\\.|[^"])*")|(?:'[^']*')|[^.\]\s]+)(?:[.\]])/);
|
|
9175
9205
|
const raw = match?.[1];
|
|
@@ -9266,13 +9296,13 @@ function canonicalPath(path) {
|
|
|
9266
9296
|
}
|
|
9267
9297
|
}
|
|
9268
9298
|
function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
|
|
9269
|
-
if (!
|
|
9299
|
+
if (!existsSync17(path))
|
|
9270
9300
|
return null;
|
|
9271
9301
|
try {
|
|
9272
9302
|
const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
|
|
9273
9303
|
if (statSync6(path).size > byteLimit)
|
|
9274
9304
|
return;
|
|
9275
|
-
const content =
|
|
9305
|
+
const content = readFileSync16(path);
|
|
9276
9306
|
if (content.byteLength > byteLimit || content.includes(0))
|
|
9277
9307
|
return;
|
|
9278
9308
|
return content.toString("utf8");
|
|
@@ -9478,7 +9508,7 @@ async function* codexProvider(opts) {
|
|
|
9478
9508
|
const filePreviews = new CodexFilePreviewTracker(opts.cwd);
|
|
9479
9509
|
const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
|
|
9480
9510
|
const codexAuthPath = hostedCodexAuthFilePath();
|
|
9481
|
-
if (!
|
|
9511
|
+
if (!existsSync17(codexAuthPath)) {
|
|
9482
9512
|
yield {
|
|
9483
9513
|
type: "error",
|
|
9484
9514
|
content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
|
|
@@ -9517,12 +9547,12 @@ async function* codexProvider(opts) {
|
|
|
9517
9547
|
model: opts.model ?? "(sdk default)",
|
|
9518
9548
|
effort: opts.effort ?? "(off)",
|
|
9519
9549
|
cwd: opts.cwd,
|
|
9520
|
-
cwdExists:
|
|
9550
|
+
cwdExists: existsSync17(opts.cwd),
|
|
9521
9551
|
resume: Boolean(opts.sessionId),
|
|
9522
9552
|
mcpServerCount: Object.keys(codexMcpServers).length
|
|
9523
9553
|
}, "codexProvider: starting turn");
|
|
9524
9554
|
const hostedCodexHome = hostedCodexHomePath();
|
|
9525
|
-
const inheritedCodexHome = process.env.CODEX_HOME ||
|
|
9555
|
+
const inheritedCodexHome = process.env.CODEX_HOME || join17(homedir6(), ".codex");
|
|
9526
9556
|
const codexEnvironment = scopedBrowserCapability || resolve12(hostedCodexHome) !== resolve12(inheritedCodexHome) ? {
|
|
9527
9557
|
...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
|
|
9528
9558
|
CODEX_HOME: hostedCodexHome,
|
|
@@ -9965,9 +9995,9 @@ var init_maestro_provider = __esm(async () => {
|
|
|
9965
9995
|
});
|
|
9966
9996
|
|
|
9967
9997
|
// ../../packages/core/src/agents/index.ts
|
|
9968
|
-
import { existsSync as
|
|
9998
|
+
import { existsSync as existsSync18 } from "fs";
|
|
9969
9999
|
import { homedir as homedir7 } from "os";
|
|
9970
|
-
import { join as
|
|
10000
|
+
import { join as join18 } from "path";
|
|
9971
10001
|
async function* dispatchAgent(opts) {
|
|
9972
10002
|
switch (opts.agent) {
|
|
9973
10003
|
case "claude": {
|
|
@@ -10001,11 +10031,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
10001
10031
|
switch (agent) {
|
|
10002
10032
|
case "claude": {
|
|
10003
10033
|
const encodedCwd = encodeClaudeCwd(cwd);
|
|
10004
|
-
const path =
|
|
10005
|
-
return !
|
|
10034
|
+
const path = join18(homedir7(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
|
|
10035
|
+
return !existsSync18(path);
|
|
10006
10036
|
}
|
|
10007
10037
|
case "codex": {
|
|
10008
|
-
const sessionsDir =
|
|
10038
|
+
const sessionsDir = join18(hostedCodexHomePath(), "sessions");
|
|
10009
10039
|
const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
|
|
10010
10040
|
for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
|
|
10011
10041
|
return false;
|
|
@@ -10014,7 +10044,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
10014
10044
|
}
|
|
10015
10045
|
case "maestro": {
|
|
10016
10046
|
const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
|
|
10017
|
-
return !
|
|
10047
|
+
return !existsSync18(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
|
|
10018
10048
|
}
|
|
10019
10049
|
default: {
|
|
10020
10050
|
const _exhaustive = agent;
|
|
@@ -10085,10 +10115,10 @@ var init_agents = __esm(async () => {
|
|
|
10085
10115
|
});
|
|
10086
10116
|
|
|
10087
10117
|
// ../../packages/core/src/prompts/builders.ts
|
|
10088
|
-
import { readFileSync as
|
|
10118
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
10089
10119
|
import { resolve as resolve14 } from "path";
|
|
10090
10120
|
function loadPrompt(filename, dir = SESSIONS_DIR) {
|
|
10091
|
-
const raw =
|
|
10121
|
+
const raw = readFileSync17(resolve14(dir, filename), "utf-8");
|
|
10092
10122
|
return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
|
|
10093
10123
|
}
|
|
10094
10124
|
function replaceVars(template, vars) {
|
|
@@ -10137,7 +10167,7 @@ function visualDesignGuide() {
|
|
|
10137
10167
|
return _visualDesignGuide;
|
|
10138
10168
|
}
|
|
10139
10169
|
function loadAgentPrompt(filename) {
|
|
10140
|
-
const raw =
|
|
10170
|
+
const raw = readFileSync17(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
|
|
10141
10171
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
10142
10172
|
if (!match)
|
|
10143
10173
|
throw new Error(`Agent prompt ${filename} is missing frontmatter`);
|
|
@@ -10183,6 +10213,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
10183
10213
|
} = opts;
|
|
10184
10214
|
const runtimeNamespace = "mcp__runtime";
|
|
10185
10215
|
const taskNamespace = "mcp__task";
|
|
10216
|
+
const decisionNamespace = "mcp__decision";
|
|
10186
10217
|
const visualToolLine = agentKind === "codex" ? `To display charts, tables, or interactive HTML results to the user, call the \`show_html\` function in the \`${runtimeNamespace}\` namespace with { html: "<complete HTML string>", title?: "optional title" }.` : `To display charts, tables, or interactive HTML results to the user, call the MCP tool "${runtimeNamespace}__show_html" with { html: "<complete HTML string>", title?: "optional title" }.`;
|
|
10187
10218
|
const mermaidToolLine = agentKind === "codex" ? `For diagrams that Mermaid supports, prefer the \`show_mermaid\` function in the \`${runtimeNamespace}\` namespace with { code: "<Mermaid DSL without markdown fences>", title?: "...", theme?: "neutral" }.` : `For diagrams that Mermaid supports, prefer the MCP tool "${runtimeNamespace}__show_mermaid" with { code: "<Mermaid DSL without markdown fences>", title?: "...", theme?: "neutral" }.`;
|
|
10188
10219
|
const mediaToolLine = agentKind === "codex" ? `To display an existing image or video in the visual panel, use \`show_image\` or \`show_video\` in the \`${runtimeNamespace}\` namespace with either { file_path: "...", title?: "..." } for a topic-workspace file or { file_id: "...", title?: "..." } for an uploaded file already attached in this topic.` : `To display an existing image or video in the visual panel, use MCP tool "${runtimeNamespace}__show_image" or "${runtimeNamespace}__show_video" with either { file_path: "...", title?: "..." } for a topic-workspace file or { file_id: "...", title?: "..." } for an uploaded file already attached in this topic.`;
|
|
@@ -10190,6 +10221,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
10190
10221
|
const askUserToolLine = agentKind === "codex" ? `When you need a blocking user choice, call the \`ask_user_question\` function in the \`${runtimeNamespace}\` namespace with { question: "...", choices: [{ label: "...", description?: "..." }] }.` : `When you need a blocking user choice, call the MCP tool "${runtimeNamespace}__ask_user_question" with { question: "...", choices: [{ label: "...", description?: "..." }] }.`;
|
|
10191
10222
|
const scheduleSelfToolLine = agentKind === "codex" ? `For a one-shot delayed continuation within 24 hours, call the \`schedule_self\` function in the \`${runtimeNamespace}\` namespace with { delay_seconds: number, message: "self-contained future instruction" }. Only one pending self-schedule is allowed per topic; use \`get_self_schedule\`, \`update_self_schedule\`, or \`cancel_self_schedule\` in that namespace to manage it. Use cron-manager for recurring schedules.` : `For a one-shot delayed continuation within 24 hours, call the MCP tool "${runtimeNamespace}__schedule_self" with { delay_seconds: number, message: "self-contained future instruction" }. Only one pending self-schedule is allowed per topic; manage it with "${runtimeNamespace}__get_self_schedule", "${runtimeNamespace}__update_self_schedule", or "${runtimeNamespace}__cancel_self_schedule". Use cron-manager for recurring schedules.`;
|
|
10192
10223
|
const taskToolLine = agentKind === "codex" ? `For task tracking, use \`task_create\`, \`task_update\`, \`task_list\`, \`task_get\`, and \`task_delete\` functions in the \`${taskNamespace}\` namespace.` : `For task tracking, use MCP tools "${taskNamespace}__task_create", "${taskNamespace}__task_update", "${taskNamespace}__task_list", "${taskNamespace}__task_get", and "${taskNamespace}__task_delete".`;
|
|
10224
|
+
const decisionToolLine = `Use the shared Decision tools in the \`${decisionNamespace}\` namespace when an architectural, product, or operational choice establishes or changes a durable direction or constraint. Do not record routine task progress or temporary implementation details; link causal predecessors when relevant.`;
|
|
10193
10225
|
const runtimeToolRef = (name) => agentKind === "codex" ? `\`${name}\`` : `"${runtimeNamespace}__${name}"`;
|
|
10194
10226
|
const spawnSubagentToolLine = `Use ${runtimeToolRef("spawn_subagent")} for self-contained parallel or long-running background work; keep quick work inline.`;
|
|
10195
10227
|
const lifecycleToolLine = `For staged work, call ${runtimeToolRef("create_subagent")} then ${runtimeToolRef("start_subagent")}. Create fixes \`task\` and \`report_mode\`; start takes only the room ID, so create after inputs are known unless preparing a \`tell_session\` receiver. Manage descendants with ${runtimeToolRef("list_subagents")} and ${runtimeToolRef("delete_subagent")}, and non-parent tell routes with ${runtimeToolRef("grant_subagent_tell")} and ${runtimeToolRef("revoke_subagent_tell")}. Direct-parent reporting needs no grant. Use ${runtimeToolRef("list_memory_topics")} to select \`memory_topic\`.`;
|
|
@@ -10236,6 +10268,9 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
10236
10268
|
taskToolLine,
|
|
10237
10269
|
"Use this shared task store for plans, progress, and checklist updates; it is visible across claude/codex/maestro turns.",
|
|
10238
10270
|
nativeTaskPolicyLine,
|
|
10271
|
+
"",
|
|
10272
|
+
"## Shared Decisions",
|
|
10273
|
+
decisionToolLine,
|
|
10239
10274
|
...extensions.render("after-shared-tasks"),
|
|
10240
10275
|
...fileDeliverySection,
|
|
10241
10276
|
...extensions.render("before-session-communication"),
|
|
@@ -10524,9 +10559,9 @@ var init_api_topic_brief = __esm(async () => {
|
|
|
10524
10559
|
});
|
|
10525
10560
|
|
|
10526
10561
|
// ../../packages/core/src/storage/wiki.ts
|
|
10527
|
-
import { basename as basename3, dirname as
|
|
10562
|
+
import { basename as basename3, dirname as dirname13, join as join19 } from "path";
|
|
10528
10563
|
function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
|
|
10529
|
-
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() :
|
|
10564
|
+
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join19(workspaceDir, "wiki");
|
|
10530
10565
|
}
|
|
10531
10566
|
var init_wiki = __esm(async () => {
|
|
10532
10567
|
await init_storage_host();
|
|
@@ -10534,8 +10569,8 @@ var init_wiki = __esm(async () => {
|
|
|
10534
10569
|
|
|
10535
10570
|
// ../../packages/core/src/agents/archiver.ts
|
|
10536
10571
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
10537
|
-
import { existsSync as
|
|
10538
|
-
import { join as
|
|
10572
|
+
import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync7 } from "fs";
|
|
10573
|
+
import { join as join20 } from "path";
|
|
10539
10574
|
function resolveMemoryLanguage() {
|
|
10540
10575
|
const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
|
|
10541
10576
|
return override && override.length > 0 ? override : resolveOutputLanguage();
|
|
@@ -10786,10 +10821,10 @@ function distillOneLine(summaryMd) {
|
|
|
10786
10821
|
return "";
|
|
10787
10822
|
}
|
|
10788
10823
|
function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
10789
|
-
const dir =
|
|
10824
|
+
const dir = join20(storage.getWikiDir(), "summaries");
|
|
10790
10825
|
if (!storage.fileExists(dir))
|
|
10791
10826
|
return null;
|
|
10792
|
-
const predicted =
|
|
10827
|
+
const predicted = join20(dir, wikiSummaryFilename(date, topicTitle, topicId));
|
|
10793
10828
|
if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
|
|
10794
10829
|
return predicted;
|
|
10795
10830
|
let best = null;
|
|
@@ -10797,7 +10832,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
|
10797
10832
|
if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
|
|
10798
10833
|
continue;
|
|
10799
10834
|
}
|
|
10800
|
-
const p =
|
|
10835
|
+
const p = join20(dir, f);
|
|
10801
10836
|
try {
|
|
10802
10837
|
const m = storage.fileModifiedAt(p);
|
|
10803
10838
|
if (m >= sinceMs && (!best || m > best.mtime))
|
|
@@ -10878,9 +10913,9 @@ var init_archiver = __esm(async () => {
|
|
|
10878
10913
|
defaultArchiverRuntime = createArchiverRuntime({
|
|
10879
10914
|
storage: {
|
|
10880
10915
|
getWikiDir: getSharedWikiDir,
|
|
10881
|
-
fileExists:
|
|
10916
|
+
fileExists: existsSync19,
|
|
10882
10917
|
listDirectory: readdirSync5,
|
|
10883
|
-
readTextFile: (path) =>
|
|
10918
|
+
readTextFile: (path) => readFileSync18(path, "utf-8"),
|
|
10884
10919
|
fileSize: (path) => statSync7(path).size,
|
|
10885
10920
|
fileModifiedAt: (path) => statSync7(path).mtimeMs,
|
|
10886
10921
|
getGeneralTopicId: (userId) => ensurePersonalGeneral(userId).id,
|
|
@@ -11000,15 +11035,15 @@ function formatTopicArchiveTranscriptRecord(row, topicTitle, index) {
|
|
|
11000
11035
|
}
|
|
11001
11036
|
|
|
11002
11037
|
// ../../packages/core/src/storage/topic-archive.ts
|
|
11003
|
-
import { mkdirSync as
|
|
11004
|
-
import { join as
|
|
11038
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
11039
|
+
import { join as join21 } from "path";
|
|
11005
11040
|
function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
11006
11041
|
const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
|
|
11007
11042
|
if (rows.length === 0)
|
|
11008
11043
|
return null;
|
|
11009
11044
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
11010
|
-
const archiveDir =
|
|
11011
|
-
|
|
11045
|
+
const archiveDir = join21(getSharedWikiDir(), "archive");
|
|
11046
|
+
mkdirSync13(archiveDir, { recursive: true });
|
|
11012
11047
|
const date = new Date().toISOString().slice(0, 10);
|
|
11013
11048
|
const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
|
|
11014
11049
|
let filename;
|
|
@@ -11020,9 +11055,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
11020
11055
|
let path;
|
|
11021
11056
|
while (true) {
|
|
11022
11057
|
filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
|
|
11023
|
-
path =
|
|
11058
|
+
path = join21(archiveDir, filename);
|
|
11024
11059
|
try {
|
|
11025
|
-
|
|
11060
|
+
writeFileSync11(path, body, { flag: "wx" });
|
|
11026
11061
|
break;
|
|
11027
11062
|
} catch (err) {
|
|
11028
11063
|
if (err.code !== "EEXIST")
|
|
@@ -11065,8 +11100,8 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
11065
11100
|
const entries = readRawConversation(userId, topicTitle);
|
|
11066
11101
|
if (entries.length === 0)
|
|
11067
11102
|
return null;
|
|
11068
|
-
const archiveDir =
|
|
11069
|
-
|
|
11103
|
+
const archiveDir = join21(getSharedWikiDir(), "archive");
|
|
11104
|
+
mkdirSync13(archiveDir, { recursive: true });
|
|
11070
11105
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
11071
11106
|
const date = new Date().toISOString().slice(0, 10);
|
|
11072
11107
|
const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
|
|
@@ -11093,9 +11128,9 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
11093
11128
|
let counter = 1;
|
|
11094
11129
|
while (true) {
|
|
11095
11130
|
const suffix = counter === 1 ? "" : `_${counter}`;
|
|
11096
|
-
const path =
|
|
11131
|
+
const path = join21(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
|
|
11097
11132
|
try {
|
|
11098
|
-
|
|
11133
|
+
writeFileSync11(path, body, { flag: "wx" });
|
|
11099
11134
|
logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
|
|
11100
11135
|
return { path, eventCount: entries.length };
|
|
11101
11136
|
} catch (error) {
|
|
@@ -11411,8 +11446,8 @@ var init_idle_archiver = __esm(async () => {
|
|
|
11411
11446
|
});
|
|
11412
11447
|
|
|
11413
11448
|
// ../../packages/core/src/agents/topic-cleanup.ts
|
|
11414
|
-
import { mkdirSync as
|
|
11415
|
-
import { dirname as
|
|
11449
|
+
import { mkdirSync as mkdirSync14, renameSync as renameSync8, unlinkSync as unlinkSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
11450
|
+
import { dirname as dirname14 } from "path";
|
|
11416
11451
|
function collectSessionIdsByAgent(entries, extraSessions = []) {
|
|
11417
11452
|
const out = new Map;
|
|
11418
11453
|
for (const e of entries) {
|
|
@@ -11478,11 +11513,11 @@ function createTopicLogMaintenance(host) {
|
|
|
11478
11513
|
const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
|
|
11479
11514
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
11480
11515
|
try {
|
|
11481
|
-
|
|
11482
|
-
|
|
11516
|
+
mkdirSync14(dirname14(path), { recursive: true });
|
|
11517
|
+
writeFileSync12(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
|
|
11483
11518
|
`)}
|
|
11484
11519
|
` : "", { flag: "wx" });
|
|
11485
|
-
|
|
11520
|
+
renameSync8(tempPath, path);
|
|
11486
11521
|
} catch (e) {
|
|
11487
11522
|
try {
|
|
11488
11523
|
unlinkSync13(tempPath);
|
|
@@ -11640,9 +11675,9 @@ var init_usage_alert = __esm(() => {
|
|
|
11640
11675
|
|
|
11641
11676
|
// ../../packages/core/src/topics/session.ts
|
|
11642
11677
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
11643
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as
|
|
11678
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync13 } from "fs";
|
|
11644
11679
|
import { tmpdir as tmpdir3 } from "os";
|
|
11645
|
-
import { join as
|
|
11680
|
+
import { join as join22 } from "path";
|
|
11646
11681
|
function previousCompactedSummary(entries) {
|
|
11647
11682
|
for (let index = entries.length - 2;index >= 0; index -= 1) {
|
|
11648
11683
|
const request = entries[index]?.event;
|
|
@@ -11772,7 +11807,7 @@ function formatCompactElapsed(startedAt) {
|
|
|
11772
11807
|
async function summarizeTopicContext(request) {
|
|
11773
11808
|
const startedAt = Date.now();
|
|
11774
11809
|
const sessionIds = [];
|
|
11775
|
-
const compactCwd = mkdtempSync2(
|
|
11810
|
+
const compactCwd = mkdtempSync2(join22(tmpdir3(), "negotium-compact-"));
|
|
11776
11811
|
const abortController = new AbortController;
|
|
11777
11812
|
const relayAbort = () => abortController.abort(request.signal?.reason);
|
|
11778
11813
|
if (request.signal?.aborted)
|
|
@@ -11799,7 +11834,7 @@ async function summarizeTopicContext(request) {
|
|
|
11799
11834
|
let error = "";
|
|
11800
11835
|
let toolViolation = false;
|
|
11801
11836
|
let compactionLogCalls = 0;
|
|
11802
|
-
const compactionLogPath =
|
|
11837
|
+
const compactionLogPath = join22(compactCwd, "conversation.log");
|
|
11803
11838
|
try {
|
|
11804
11839
|
const compactionMcp = useCompactionLog ? {
|
|
11805
11840
|
compact_log: {
|
|
@@ -11813,7 +11848,7 @@ async function summarizeTopicContext(request) {
|
|
|
11813
11848
|
}
|
|
11814
11849
|
} : undefined;
|
|
11815
11850
|
if (useCompactionLog)
|
|
11816
|
-
|
|
11851
|
+
writeFileSync13(compactionLogPath, request.source, { mode: 384 });
|
|
11817
11852
|
for await (const event of runAgent({
|
|
11818
11853
|
agent: request.agent,
|
|
11819
11854
|
prompt: useCompactionLog ? [
|
|
@@ -12062,7 +12097,7 @@ __export(exports_derive, {
|
|
|
12062
12097
|
TopicDeriveBusyError: () => TopicDeriveBusyError
|
|
12063
12098
|
});
|
|
12064
12099
|
import { createHash as createHash4, randomUUID as randomUUID11 } from "crypto";
|
|
12065
|
-
import { mkdirSync as
|
|
12100
|
+
import { mkdirSync as mkdirSync15, rmSync as rmSync5, unlinkSync as unlinkSync14 } from "fs";
|
|
12066
12101
|
function getTopics(opts = {}) {
|
|
12067
12102
|
return listTopics(opts).filter((topic) => !isLegacySharedGeneral(topic.id));
|
|
12068
12103
|
}
|
|
@@ -12195,7 +12230,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
|
|
|
12195
12230
|
try {
|
|
12196
12231
|
if (agent) {
|
|
12197
12232
|
const cwd = derivedWorkspace;
|
|
12198
|
-
|
|
12233
|
+
mkdirSync15(cwd, { recursive: true });
|
|
12199
12234
|
const registry = getRegistry(agent);
|
|
12200
12235
|
const requestedRolloutModel = subagentModel ?? (subagent?.agent ? undefined : sourceConfig?.model) ?? derived.defaultModel;
|
|
12201
12236
|
const rolloutModel = resolveModelForAgent(agent, requestedRolloutModel, registry);
|
|
@@ -12418,14 +12453,14 @@ var init_derive = __esm(async () => {
|
|
|
12418
12453
|
});
|
|
12419
12454
|
|
|
12420
12455
|
// ../../packages/core/src/query/session-inbox-path.ts
|
|
12421
|
-
import { join as
|
|
12456
|
+
import { join as join23 } from "path";
|
|
12422
12457
|
function sessionInboxPath(userId, topicId) {
|
|
12423
12458
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
12424
|
-
return
|
|
12459
|
+
return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
|
|
12425
12460
|
}
|
|
12426
12461
|
function scheduledSessionInboxPath(userId, topicId) {
|
|
12427
12462
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
12428
|
-
return
|
|
12463
|
+
return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
|
|
12429
12464
|
}
|
|
12430
12465
|
var TOPIC_ID_FILE_PREFIX = "topic-id-", JSONL_SUFFIX = ".jsonl", SCHEDULE_SUFFIX = ".schedule";
|
|
12431
12466
|
var init_session_inbox_path = __esm(() => {
|
|
@@ -12434,13 +12469,13 @@ var init_session_inbox_path = __esm(() => {
|
|
|
12434
12469
|
|
|
12435
12470
|
// ../../packages/core/src/query/session-inbox-cleanup.ts
|
|
12436
12471
|
import { unlinkSync as unlinkSync15 } from "fs";
|
|
12437
|
-
import { basename as basename4, join as
|
|
12472
|
+
import { basename as basename4, join as join24 } from "path";
|
|
12438
12473
|
function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
|
|
12439
12474
|
const live = sessionInboxPath(userId, topicId);
|
|
12440
12475
|
const scheduled = scheduledSessionInboxPath(userId, topicId);
|
|
12441
12476
|
const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
|
|
12442
12477
|
if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename4(legacyTopicTitle) === legacyTopicTitle) {
|
|
12443
|
-
const legacyBase =
|
|
12478
|
+
const legacyBase = join24(SESSION_INBOX_DIR, userId, legacyTopicTitle);
|
|
12444
12479
|
for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
|
|
12445
12480
|
candidates.add(`${legacyBase}${suffix}`);
|
|
12446
12481
|
}
|
|
@@ -12465,29 +12500,29 @@ var init_session_inbox_cleanup = __esm(() => {
|
|
|
12465
12500
|
});
|
|
12466
12501
|
|
|
12467
12502
|
// ../../packages/core/src/query/state.ts
|
|
12468
|
-
import { mkdirSync as
|
|
12469
|
-
import { basename as basename5, join as
|
|
12503
|
+
import { mkdirSync as mkdirSync16, renameSync as renameSync9, unlinkSync as unlinkSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
12504
|
+
import { basename as basename5, join as join25 } from "path";
|
|
12470
12505
|
function createQueryStateStore(options) {
|
|
12471
12506
|
const sanitize = options.sanitizeTopicId ?? sanitizeId;
|
|
12472
|
-
const queryStateDirPath = (userId) =>
|
|
12473
|
-
const queryStateFile = (userId, topicId) =>
|
|
12507
|
+
const queryStateDirPath = (userId) => join25(options.usersLogDir, String(userId), "active-queries");
|
|
12508
|
+
const queryStateFile = (userId, topicId) => join25(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
|
|
12474
12509
|
const legacyQueryStateFile = (userId, topicName) => {
|
|
12475
12510
|
if (!topicName || topicName === "." || topicName === ".." || basename5(topicName) !== topicName) {
|
|
12476
12511
|
return null;
|
|
12477
12512
|
}
|
|
12478
|
-
return
|
|
12513
|
+
return join25(queryStateDirPath(userId), `${topicName}.json`);
|
|
12479
12514
|
};
|
|
12480
12515
|
return {
|
|
12481
12516
|
write(userId, topicId, topicName, task) {
|
|
12482
12517
|
const dir = queryStateDirPath(userId);
|
|
12483
|
-
|
|
12518
|
+
mkdirSync16(dir, { recursive: true });
|
|
12484
12519
|
const state = { topicId, topicName, since: new Date().toISOString() };
|
|
12485
12520
|
if (task)
|
|
12486
12521
|
state.task = [...task.replace(/\n+/g, " ").trim()].slice(0, 100).join("");
|
|
12487
12522
|
const target = queryStateFile(userId, topicId);
|
|
12488
12523
|
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
12489
|
-
|
|
12490
|
-
|
|
12524
|
+
writeFileSync14(tmp, JSON.stringify(state));
|
|
12525
|
+
renameSync9(tmp, target);
|
|
12491
12526
|
},
|
|
12492
12527
|
clear(userId, topicId, legacyTopicName) {
|
|
12493
12528
|
const paths = [
|
|
@@ -12628,38 +12663,38 @@ __export(exports_session_asks, {
|
|
|
12628
12663
|
import { createHash as createHash5 } from "crypto";
|
|
12629
12664
|
import {
|
|
12630
12665
|
closeSync as closeSync3,
|
|
12631
|
-
mkdirSync as
|
|
12666
|
+
mkdirSync as mkdirSync17,
|
|
12632
12667
|
openSync as openSync3,
|
|
12633
12668
|
readdirSync as readdirSync6,
|
|
12634
|
-
readFileSync as
|
|
12669
|
+
readFileSync as readFileSync19,
|
|
12635
12670
|
statSync as statSync8,
|
|
12636
12671
|
unlinkSync as unlinkSync17,
|
|
12637
|
-
writeFileSync as
|
|
12672
|
+
writeFileSync as writeFileSync15
|
|
12638
12673
|
} from "fs";
|
|
12639
|
-
import { dirname as
|
|
12674
|
+
import { dirname as dirname15, join as join26 } from "path";
|
|
12640
12675
|
function pendingAskDir(userId) {
|
|
12641
12676
|
const rawUserId = String(userId);
|
|
12642
12677
|
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash5("sha256").update(rawUserId).digest("hex")}`;
|
|
12643
|
-
return
|
|
12678
|
+
return join26(resolveStorageSessionAsksDir(), safeUserId);
|
|
12644
12679
|
}
|
|
12645
12680
|
function encodeAskKey(key) {
|
|
12646
12681
|
return JSON.stringify([key.from, key.to]);
|
|
12647
12682
|
}
|
|
12648
12683
|
function pendingAskPath(key) {
|
|
12649
12684
|
const digest = createHash5("sha256").update(encodeAskKey(key)).digest("hex");
|
|
12650
|
-
return
|
|
12685
|
+
return join26(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
|
|
12651
12686
|
}
|
|
12652
12687
|
function v2PendingAskPath(key) {
|
|
12653
12688
|
const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
|
|
12654
|
-
return
|
|
12689
|
+
return join26(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
|
|
12655
12690
|
}
|
|
12656
12691
|
function legacyPendingAskPath(key) {
|
|
12657
12692
|
if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
|
|
12658
12693
|
return null;
|
|
12659
12694
|
}
|
|
12660
12695
|
const dir = pendingAskDir(key.userId);
|
|
12661
|
-
const candidate =
|
|
12662
|
-
return
|
|
12696
|
+
const candidate = join26(dir, `${key.from}___${key.to}.pending`);
|
|
12697
|
+
return dirname15(candidate) === dir ? candidate : null;
|
|
12663
12698
|
}
|
|
12664
12699
|
function parsePendingAskFilename(fileName) {
|
|
12665
12700
|
if (!fileName.endsWith(".pending"))
|
|
@@ -12680,7 +12715,7 @@ function parsePendingAskFilename(fileName) {
|
|
|
12680
12715
|
}
|
|
12681
12716
|
function readPendingAskFile(path, fallback) {
|
|
12682
12717
|
try {
|
|
12683
|
-
const raw =
|
|
12718
|
+
const raw = readFileSync19(path, "utf8").trim();
|
|
12684
12719
|
if (!raw)
|
|
12685
12720
|
return null;
|
|
12686
12721
|
if (!raw.startsWith("{")) {
|
|
@@ -12731,17 +12766,17 @@ function isStale(record, path) {
|
|
|
12731
12766
|
}
|
|
12732
12767
|
function writePendingAsk(record) {
|
|
12733
12768
|
const path = pendingAskPath(record);
|
|
12734
|
-
|
|
12735
|
-
|
|
12769
|
+
mkdirSync17(pendingAskDir(record.userId), { recursive: true });
|
|
12770
|
+
writeFileSync15(path, `${JSON.stringify(record)}
|
|
12736
12771
|
`);
|
|
12737
12772
|
}
|
|
12738
12773
|
function writePendingAskIfAbsent(record) {
|
|
12739
12774
|
const path = pendingAskPath(record);
|
|
12740
|
-
|
|
12775
|
+
mkdirSync17(pendingAskDir(record.userId), { recursive: true });
|
|
12741
12776
|
let fd = null;
|
|
12742
12777
|
try {
|
|
12743
12778
|
fd = openSync3(path, "wx");
|
|
12744
|
-
|
|
12779
|
+
writeFileSync15(fd, `${JSON.stringify(record)}
|
|
12745
12780
|
`);
|
|
12746
12781
|
return true;
|
|
12747
12782
|
} catch (error) {
|
|
@@ -12801,12 +12836,12 @@ function createPendingAsk(args) {
|
|
|
12801
12836
|
createdAt: now,
|
|
12802
12837
|
updatedAt: now
|
|
12803
12838
|
};
|
|
12804
|
-
|
|
12839
|
+
mkdirSync17(pendingAskDir(args.userId), { recursive: true });
|
|
12805
12840
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
12806
12841
|
let fd = null;
|
|
12807
12842
|
try {
|
|
12808
12843
|
fd = openSync3(path, "wx");
|
|
12809
|
-
|
|
12844
|
+
writeFileSync15(fd, `${JSON.stringify(record)}
|
|
12810
12845
|
`);
|
|
12811
12846
|
return { ok: true, record };
|
|
12812
12847
|
} catch (err) {
|
|
@@ -12899,7 +12934,7 @@ function listPendingAsksForCaller(args) {
|
|
|
12899
12934
|
const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
|
|
12900
12935
|
if (!parsed)
|
|
12901
12936
|
continue;
|
|
12902
|
-
const path =
|
|
12937
|
+
const path = join26(dir, fileName);
|
|
12903
12938
|
const record = readPendingAskFile(path, {
|
|
12904
12939
|
userId: args.userId,
|
|
12905
12940
|
from: parsed.from,
|
|
@@ -12941,7 +12976,7 @@ function deletePendingAsksForTopic(args) {
|
|
|
12941
12976
|
}
|
|
12942
12977
|
let deleted = 0;
|
|
12943
12978
|
for (const fileName of files) {
|
|
12944
|
-
const path =
|
|
12979
|
+
const path = join26(dir, fileName);
|
|
12945
12980
|
const parsed = parsePendingAskFilename(fileName);
|
|
12946
12981
|
const record = readPendingAskFile(path, {
|
|
12947
12982
|
userId: args.userId,
|
|
@@ -13378,8 +13413,8 @@ var init_self_schedules = __esm(async () => {
|
|
|
13378
13413
|
|
|
13379
13414
|
// ../../packages/core/src/storage/token-stats.ts
|
|
13380
13415
|
import { createHash as createHash6 } from "crypto";
|
|
13381
|
-
import { mkdirSync as
|
|
13382
|
-
import { join as
|
|
13416
|
+
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
13417
|
+
import { join as join27 } from "path";
|
|
13383
13418
|
function emptyBucket() {
|
|
13384
13419
|
return {
|
|
13385
13420
|
inputTokens: 0,
|
|
@@ -13397,8 +13432,8 @@ function tokenStatsFileId(userId) {
|
|
|
13397
13432
|
function queriesPath(userId) {
|
|
13398
13433
|
const fileId = tokenStatsFileId(userId);
|
|
13399
13434
|
const logDir = resolveStorageLogDir();
|
|
13400
|
-
|
|
13401
|
-
return
|
|
13435
|
+
mkdirSync18(logDir, { recursive: true });
|
|
13436
|
+
return join27(logDir, `token-queries-${fileId}.jsonl`);
|
|
13402
13437
|
}
|
|
13403
13438
|
function loadRecords(userId) {
|
|
13404
13439
|
try {
|
|
@@ -13467,7 +13502,7 @@ function deleteTopicStats(userId, topicId) {
|
|
|
13467
13502
|
return true;
|
|
13468
13503
|
}
|
|
13469
13504
|
});
|
|
13470
|
-
|
|
13505
|
+
writeFileSync16(path, kept.length > 0 ? `${kept.join(`
|
|
13471
13506
|
`)}
|
|
13472
13507
|
` : "", "utf-8");
|
|
13473
13508
|
} catch (e) {
|
|
@@ -13799,8 +13834,8 @@ var init_lifecycle = __esm(async () => {
|
|
|
13799
13834
|
|
|
13800
13835
|
// ../../packages/core/src/runtime/attachments.ts
|
|
13801
13836
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
13802
|
-
import { copyFileSync as copyFileSync2, mkdirSync as
|
|
13803
|
-
import { basename as basename6, join as
|
|
13837
|
+
import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync19, writeFileSync as writeFileSync17 } from "fs";
|
|
13838
|
+
import { basename as basename6, join as join28 } from "path";
|
|
13804
13839
|
function workspaceCwdFor(topicId) {
|
|
13805
13840
|
return resolveTopicWorkspaceDir(topicId);
|
|
13806
13841
|
}
|
|
@@ -13813,7 +13848,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
13813
13848
|
if (!attachmentIds?.length)
|
|
13814
13849
|
return [];
|
|
13815
13850
|
const out = [];
|
|
13816
|
-
const destDir =
|
|
13851
|
+
const destDir = join28(workspaceCwdFor(topicId), "attachments", queryId);
|
|
13817
13852
|
for (const rawId of attachmentIds) {
|
|
13818
13853
|
if (typeof rawId !== "string")
|
|
13819
13854
|
continue;
|
|
@@ -13827,10 +13862,10 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
13827
13862
|
continue;
|
|
13828
13863
|
}
|
|
13829
13864
|
try {
|
|
13830
|
-
|
|
13865
|
+
mkdirSync19(destDir, { recursive: true });
|
|
13831
13866
|
const index = String(out.length + 1).padStart(2, "0");
|
|
13832
13867
|
const safeName = safeAttachmentFilename(attachment.filename, fileId);
|
|
13833
|
-
const destPath =
|
|
13868
|
+
const destPath = join28(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
|
|
13834
13869
|
copyFileSync2(sourcePath, destPath);
|
|
13835
13870
|
out.push({
|
|
13836
13871
|
id: attachment.id,
|
|
@@ -13860,14 +13895,14 @@ function promptWithAttachments(prompt, attachments) {
|
|
|
13860
13895
|
return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
|
|
13861
13896
|
}
|
|
13862
13897
|
function ingestAttachment(args) {
|
|
13863
|
-
const destDir =
|
|
13864
|
-
|
|
13898
|
+
const destDir = join28(UPLOADS_DIR, args.topicId);
|
|
13899
|
+
mkdirSync19(destDir, { recursive: true });
|
|
13865
13900
|
const safeName = safeAttachmentFilename(args.filename, "upload");
|
|
13866
|
-
const destPath =
|
|
13901
|
+
const destPath = join28(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
|
|
13867
13902
|
if (args.sourcePath !== undefined) {
|
|
13868
13903
|
copyFileSync2(args.sourcePath, destPath);
|
|
13869
13904
|
} else if (args.bytes !== undefined) {
|
|
13870
|
-
|
|
13905
|
+
writeFileSync17(destPath, args.bytes);
|
|
13871
13906
|
} else {
|
|
13872
13907
|
throw new Error("ingestAttachment: provide sourcePath or bytes");
|
|
13873
13908
|
}
|
|
@@ -14336,7 +14371,7 @@ var init_ask_user_gates = __esm(async () => {
|
|
|
14336
14371
|
|
|
14337
14372
|
// ../../packages/core/src/agents/mcp-tools/ask-user.ts
|
|
14338
14373
|
import { createHash as createHash7, randomUUID as randomUUID14 } from "crypto";
|
|
14339
|
-
import { z as
|
|
14374
|
+
import { z as z4 } from "zod";
|
|
14340
14375
|
function normalizeAskText(value2, maxChars) {
|
|
14341
14376
|
if (typeof value2 !== "string")
|
|
14342
14377
|
return;
|
|
@@ -14609,12 +14644,12 @@ function createAskUserRuntime(host) {
|
|
|
14609
14644
|
name: "ask_user_question",
|
|
14610
14645
|
description: "Ask the user a blocking multiple-choice question in the chat and wait for their selection. Use this instead of provider built-in AskUserQuestion. Use only when you cannot proceed safely without the user's choice.",
|
|
14611
14646
|
schema: {
|
|
14612
|
-
question:
|
|
14613
|
-
choices:
|
|
14614
|
-
label:
|
|
14615
|
-
description:
|
|
14647
|
+
question: z4.string().describe("The concise question to show to the user."),
|
|
14648
|
+
choices: z4.array(z4.object({
|
|
14649
|
+
label: z4.string().describe("Choice label shown on the button."),
|
|
14650
|
+
description: z4.string().optional().describe("Optional one-sentence consequence.")
|
|
14616
14651
|
})).min(1).max(MAX_CHOICES).describe("Choices the user can select."),
|
|
14617
|
-
idempotency_key:
|
|
14652
|
+
idempotency_key: z4.string().max(MAX_IDEMPOTENCY_KEY_CHARS).optional().describe("Optional retry key. Reusing it with the same question replays the existing result; reusing it with different content fails.")
|
|
14618
14653
|
},
|
|
14619
14654
|
async handler(input) {
|
|
14620
14655
|
return askUserQuestion(ctx, input);
|
|
@@ -14784,6 +14819,10 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
14784
14819
|
.controls button:focus-visible{outline:2px solid var(--celadon);outline-offset:1px}
|
|
14785
14820
|
.zoom-value{min-width:46px;color:var(--graphite);font:500 11px/1 Geist,system-ui,sans-serif;text-align:center;font-variant-numeric:tabular-nums}
|
|
14786
14821
|
.error{margin:0;white-space:pre-wrap;color:#7D2E2E;background:#FFF5F2;border:1px solid #E4B9B1;border-radius:6px;padding:14px;font:13px ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
14822
|
+
.failure{max-width:56ch;margin:0 auto}
|
|
14823
|
+
.failure p{margin:0 0 12px;color:var(--graphite);font:14px/1.6 Geist,system-ui,sans-serif}
|
|
14824
|
+
.failure details{color:var(--graphite);font:12px/1.5 Geist,system-ui,sans-serif}
|
|
14825
|
+
.failure summary{cursor:pointer;padding:4px 0}
|
|
14787
14826
|
@media(max-width:600px){.viewport{padding:54px 14px 18px}.controls{top:10px;right:10px}}
|
|
14788
14827
|
</style>
|
|
14789
14828
|
</head>
|
|
@@ -14798,11 +14837,58 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
14798
14837
|
<script data-otium-mermaid-runtime src="${safeScriptUrl}"></script>
|
|
14799
14838
|
<script>
|
|
14800
14839
|
(async () => {
|
|
14840
|
+
// An unrendered document reports itself in more than one voice: Mermaid's
|
|
14841
|
+
// own 0x0 guard, and the browser refusing geometry on a path that was
|
|
14842
|
+
// never laid out. Both mean the same thing, so both are worth one retry
|
|
14843
|
+
// and, if it still fails, the same explanation.
|
|
14844
|
+
const unrendered = (error) => {
|
|
14845
|
+
const message = String(error && error.message ? error.message : error);
|
|
14846
|
+
return message.indexOf("not in render tree") !== -1 || message.indexOf("path is empty") !== -1;
|
|
14847
|
+
};
|
|
14801
14848
|
try {
|
|
14802
14849
|
const runtime = globalThis.mermaid;
|
|
14803
14850
|
if (!runtime) throw new Error("Mermaid renderer failed to load.");
|
|
14804
14851
|
runtime.initialize({ startOnLoad: false, securityLevel: "strict", theme: ${safeTheme} });
|
|
14805
|
-
|
|
14852
|
+
const host = document.querySelector(".mermaid");
|
|
14853
|
+
// Mermaid sizes every label by appending a probe <svg> to the body and
|
|
14854
|
+
// reading getBBox(), and it throws "svg element not in render tree" the
|
|
14855
|
+
// moment that comes back 0x0. That is what a hidden panel looks like from
|
|
14856
|
+
// in here: the document exists but nothing is in the render tree, so the
|
|
14857
|
+
// measurement has no geometry to report. Ask the same question Mermaid
|
|
14858
|
+
// will ask, and only start once it has an answer.
|
|
14859
|
+
const measurable = () => {
|
|
14860
|
+
const probe = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
14861
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
14862
|
+
text.textContent = "M";
|
|
14863
|
+
probe.appendChild(text);
|
|
14864
|
+
document.body.appendChild(probe);
|
|
14865
|
+
let box = { width: 0, height: 0 };
|
|
14866
|
+
try { box = text.getBBox(); } catch (ignored) {}
|
|
14867
|
+
probe.remove();
|
|
14868
|
+
return box.width > 0 || box.height > 0;
|
|
14869
|
+
};
|
|
14870
|
+
// requestAnimationFrame is the right clock here: a hidden document stops
|
|
14871
|
+
// being animated, so this waits without spinning and resumes on the frame
|
|
14872
|
+
// the panel is shown. The cap counts rendered frames, not wall time.
|
|
14873
|
+
const waitUntilMeasurable = async (maxFrames) => {
|
|
14874
|
+
for (let frame = 0; frame < maxFrames; frame += 1) {
|
|
14875
|
+
if (measurable()) return true;
|
|
14876
|
+
await new Promise((next) => requestAnimationFrame(next));
|
|
14877
|
+
}
|
|
14878
|
+
return measurable();
|
|
14879
|
+
};
|
|
14880
|
+
await waitUntilMeasurable(600);
|
|
14881
|
+
try {
|
|
14882
|
+
await runtime.run({ querySelector: ".mermaid" });
|
|
14883
|
+
} catch (firstAttempt) {
|
|
14884
|
+
if (!unrendered(firstAttempt)) throw firstAttempt;
|
|
14885
|
+
// The panel can be hidden again between the probe and the real measure.
|
|
14886
|
+
// Clear the marker Mermaid leaves behind so the retry is not skipped as
|
|
14887
|
+
// already done, then wait for the render tree once more.
|
|
14888
|
+
host.removeAttribute("data-processed");
|
|
14889
|
+
await waitUntilMeasurable(600);
|
|
14890
|
+
await runtime.run({ querySelector: ".mermaid" });
|
|
14891
|
+
}
|
|
14806
14892
|
const viewport = document.querySelector(".viewport");
|
|
14807
14893
|
const svg = document.querySelector(".mermaid svg");
|
|
14808
14894
|
const value = document.querySelector(".zoom-value");
|
|
@@ -14829,7 +14915,22 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
14829
14915
|
applyScale(scale, false);
|
|
14830
14916
|
} catch (error) {
|
|
14831
14917
|
document.querySelector(".controls")?.remove();
|
|
14832
|
-
|
|
14918
|
+
const raw = String(error && error.message ? error.message : error);
|
|
14919
|
+
const escape = (value) => value.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
|
14920
|
+
// Mermaid aborts the whole render when it cannot place an edge label:
|
|
14921
|
+
// cardinality markers ask for a point a fixed distance along the edge,
|
|
14922
|
+
// and a relation laid out shorter than that walks off the end. Nothing
|
|
14923
|
+
// is wrong with the diagram, so the raw message sends authors looking
|
|
14924
|
+
// for a syntax error that does not exist. Say what actually moves it.
|
|
14925
|
+
const note = raw.indexOf("Could not find a suitable point") !== -1
|
|
14926
|
+
? "Two nodes ended up too close together for Mermaid to fit a label on the edge between them. Renaming a node, adding another, or setting an explicit direction usually spreads the layout enough to render."
|
|
14927
|
+
: unrendered(error)
|
|
14928
|
+
? "The panel stayed hidden long enough that there was never a laid-out page to measure the diagram against. Reopening the panel renders it."
|
|
14929
|
+
: "This diagram could not be rendered.";
|
|
14930
|
+
document.querySelector(".viewport").innerHTML =
|
|
14931
|
+
'<div class="failure"><p>' + escape(note) +
|
|
14932
|
+
'</p><details><summary>Technical detail</summary><pre class="error">' +
|
|
14933
|
+
escape(raw) + '</pre></details></div>';
|
|
14833
14934
|
}
|
|
14834
14935
|
})();
|
|
14835
14936
|
</script>
|
|
@@ -15631,10 +15732,10 @@ var init_turn_session = __esm(async () => {
|
|
|
15631
15732
|
});
|
|
15632
15733
|
|
|
15633
15734
|
// ../../packages/core/src/storage/app-settings.ts
|
|
15634
|
-
import { existsSync as
|
|
15635
|
-
import { dirname as
|
|
15735
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "fs";
|
|
15736
|
+
import { dirname as dirname16, join as join29 } from "path";
|
|
15636
15737
|
function settingsFile() {
|
|
15637
|
-
return
|
|
15738
|
+
return join29(resolveStorageDataDir(), "otium-settings.json");
|
|
15638
15739
|
}
|
|
15639
15740
|
function ensureSettingsLoaded() {
|
|
15640
15741
|
const path = settingsFile();
|
|
@@ -15643,8 +15744,8 @@ function ensureSettingsLoaded() {
|
|
|
15643
15744
|
loadedSettingsFile = path;
|
|
15644
15745
|
aiName = DEFAULT_AI_NAME;
|
|
15645
15746
|
try {
|
|
15646
|
-
if (
|
|
15647
|
-
const data = JSON.parse(
|
|
15747
|
+
if (existsSync20(path)) {
|
|
15748
|
+
const data = JSON.parse(readFileSync20(path, "utf8"));
|
|
15648
15749
|
if (typeof data.aiName === "string" && data.aiName.trim()) {
|
|
15649
15750
|
aiName = data.aiName.trim();
|
|
15650
15751
|
}
|
|
@@ -15737,8 +15838,8 @@ __export(exports_turn_runner, {
|
|
|
15737
15838
|
AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
|
|
15738
15839
|
});
|
|
15739
15840
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
15740
|
-
import { existsSync as
|
|
15741
|
-
import { join as
|
|
15841
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync21, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
|
|
15842
|
+
import { join as join30 } from "path";
|
|
15742
15843
|
function withDefaultPlaywright(configuredMcp, isManager) {
|
|
15743
15844
|
if (isManager)
|
|
15744
15845
|
return configuredMcp;
|
|
@@ -15793,8 +15894,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
|
|
|
15793
15894
|
return message;
|
|
15794
15895
|
}
|
|
15795
15896
|
function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
|
|
15796
|
-
const preferred =
|
|
15797
|
-
if (preferExact &&
|
|
15897
|
+
const preferred = join30(directory, preferredFilename);
|
|
15898
|
+
if (preferExact && existsSync21(preferred))
|
|
15798
15899
|
return preferred;
|
|
15799
15900
|
let newestLegacyId = null;
|
|
15800
15901
|
let newestTitle = null;
|
|
@@ -15804,7 +15905,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
15804
15905
|
const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
|
|
15805
15906
|
if (!titleMatch && !legacyIdMatch)
|
|
15806
15907
|
continue;
|
|
15807
|
-
const path =
|
|
15908
|
+
const path = join30(directory, filename);
|
|
15808
15909
|
const mtimeMs = statSync10(path).mtimeMs;
|
|
15809
15910
|
if (titleMatch) {
|
|
15810
15911
|
if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
|
|
@@ -15818,10 +15919,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
15818
15919
|
return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
|
|
15819
15920
|
}
|
|
15820
15921
|
function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
|
|
15821
|
-
const briefFile = resolveWikiMirrorPath(
|
|
15822
|
-
const hasBriefFile =
|
|
15823
|
-
const latestSummaryCandidate = resolveWikiMirrorPath(
|
|
15824
|
-
const latestSummaryFile =
|
|
15922
|
+
const briefFile = resolveWikiMirrorPath(join30(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
|
|
15923
|
+
const hasBriefFile = existsSync21(briefFile) && statSync10(briefFile).isFile();
|
|
15924
|
+
const latestSummaryCandidate = resolveWikiMirrorPath(join30(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
|
|
15925
|
+
const latestSummaryFile = existsSync21(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
|
|
15825
15926
|
return { briefFile, hasBriefFile, latestSummaryFile };
|
|
15826
15927
|
}
|
|
15827
15928
|
async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
|
|
@@ -16496,7 +16597,7 @@ function startAiTurn(params) {
|
|
|
16496
16597
|
const resolvedModel = execution.model;
|
|
16497
16598
|
const resolvedEffort = execution.effort;
|
|
16498
16599
|
const workspaceCwd = cwd ?? workspaceCwdFor(topicId);
|
|
16499
|
-
|
|
16600
|
+
mkdirSync21(workspaceCwd, { recursive: true });
|
|
16500
16601
|
let historyBridgeError;
|
|
16501
16602
|
if (bridgeSessionFromHistory && !sessionId) {
|
|
16502
16603
|
const bridge = tryReconstructTopicRollout({
|
|
@@ -17017,9 +17118,9 @@ __export(exports_auth_check, {
|
|
|
17017
17118
|
checkAgentAuth: () => checkAgentAuth
|
|
17018
17119
|
});
|
|
17019
17120
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
17020
|
-
import { existsSync as
|
|
17121
|
+
import { existsSync as existsSync22 } from "fs";
|
|
17021
17122
|
import { homedir as homedir8, platform } from "os";
|
|
17022
|
-
import { join as
|
|
17123
|
+
import { join as join31 } from "path";
|
|
17023
17124
|
function hasMaestroCredential(host, key, userId) {
|
|
17024
17125
|
if (userId && host.getVaultValue(userId, key)?.trim())
|
|
17025
17126
|
return true;
|
|
@@ -17039,7 +17140,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
|
|
|
17039
17140
|
case "claude": {
|
|
17040
17141
|
if (host.environment.ANTHROPIC_API_KEY)
|
|
17041
17142
|
return { ok: true };
|
|
17042
|
-
const path =
|
|
17143
|
+
const path = join31(host.homeDirectory(), ".claude", ".credentials.json");
|
|
17043
17144
|
if (host.operatingSystem() === "darwin") {
|
|
17044
17145
|
if (host.hasMacOsCredential("Claude Code-credentials") || host.exists(path)) {
|
|
17045
17146
|
return { ok: true };
|
|
@@ -17090,7 +17191,7 @@ var init_auth_check = __esm(async () => {
|
|
|
17090
17191
|
await init_vault();
|
|
17091
17192
|
defaultAgentAuthHost = {
|
|
17092
17193
|
codexAuthFilePath,
|
|
17093
|
-
exists:
|
|
17194
|
+
exists: existsSync22,
|
|
17094
17195
|
environment: process.env,
|
|
17095
17196
|
homeDirectory: homedir8,
|
|
17096
17197
|
operatingSystem: platform,
|
|
@@ -17107,7 +17208,7 @@ var init_auth_check = __esm(async () => {
|
|
|
17107
17208
|
});
|
|
17108
17209
|
|
|
17109
17210
|
// ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
|
|
17110
|
-
import { z as
|
|
17211
|
+
import { z as z5 } from "zod";
|
|
17111
17212
|
function createSubagentLifecycle(host) {
|
|
17112
17213
|
const runtimeOwnerId = host.runtime.ownerId;
|
|
17113
17214
|
const limits = Object.freeze({
|
|
@@ -17642,7 +17743,7 @@ ${card.task}${reportInstruction}`;
|
|
|
17642
17743
|
name: "start_subagent",
|
|
17643
17744
|
description: "Start a prepared descendant subagent task.",
|
|
17644
17745
|
schema: {
|
|
17645
|
-
topic_id:
|
|
17746
|
+
topic_id: z5.string().describe("Prepared subagent room id.")
|
|
17646
17747
|
},
|
|
17647
17748
|
async handler(input) {
|
|
17648
17749
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17662,7 +17763,7 @@ ${card.task}${reportInstruction}`;
|
|
|
17662
17763
|
name: "delete_subagent",
|
|
17663
17764
|
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.",
|
|
17664
17765
|
schema: {
|
|
17665
|
-
topic_id:
|
|
17766
|
+
topic_id: z5.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
|
|
17666
17767
|
},
|
|
17667
17768
|
async handler(input) {
|
|
17668
17769
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17686,8 +17787,8 @@ ${card.task}${reportInstruction}`;
|
|
|
17686
17787
|
name: "grant_subagent_tell",
|
|
17687
17788
|
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.",
|
|
17688
17789
|
schema: {
|
|
17689
|
-
subagent_topic_id:
|
|
17690
|
-
target_topic_id:
|
|
17790
|
+
subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
|
|
17791
|
+
target_topic_id: z5.string().describe("Target topic id: this manager room or another descendant in its tree.")
|
|
17691
17792
|
},
|
|
17692
17793
|
async handler(input) {
|
|
17693
17794
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17713,8 +17814,8 @@ ${card.task}${reportInstruction}`;
|
|
|
17713
17814
|
name: "revoke_subagent_tell",
|
|
17714
17815
|
description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
|
|
17715
17816
|
schema: {
|
|
17716
|
-
subagent_topic_id:
|
|
17717
|
-
target_topic_id:
|
|
17817
|
+
subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
|
|
17818
|
+
target_topic_id: z5.string().describe("Granted target topic id.")
|
|
17718
17819
|
},
|
|
17719
17820
|
async handler(input) {
|
|
17720
17821
|
const result = ownedSubagentTree(ctx);
|
|
@@ -17740,12 +17841,12 @@ ${card.task}${reportInstruction}`;
|
|
|
17740
17841
|
name: "spawn_subagent",
|
|
17741
17842
|
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.",
|
|
17742
17843
|
schema: {
|
|
17743
|
-
task:
|
|
17744
|
-
name:
|
|
17745
|
-
agent:
|
|
17746
|
-
model:
|
|
17747
|
-
memory_topic:
|
|
17748
|
-
report_mode:
|
|
17844
|
+
task: z5.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
|
|
17845
|
+
name: z5.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
|
|
17846
|
+
agent: z5.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
|
|
17847
|
+
model: z5.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.`),
|
|
17848
|
+
memory_topic: z5.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
|
|
17849
|
+
report_mode: z5.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.")
|
|
17749
17850
|
},
|
|
17750
17851
|
async handler(input) {
|
|
17751
17852
|
return createSubagent(ctx, input, true);
|
|
@@ -17757,12 +17858,12 @@ ${card.task}${reportInstruction}`;
|
|
|
17757
17858
|
name: "create_subagent",
|
|
17758
17859
|
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.",
|
|
17759
17860
|
schema: {
|
|
17760
|
-
task:
|
|
17761
|
-
name:
|
|
17762
|
-
agent:
|
|
17763
|
-
model:
|
|
17764
|
-
memory_topic:
|
|
17765
|
-
report_mode:
|
|
17861
|
+
task: z5.string().describe("Self-contained task brief for the subagent."),
|
|
17862
|
+
name: z5.string().optional().describe("Short room name. Auto-generated when omitted."),
|
|
17863
|
+
agent: z5.enum(["claude", "codex", "maestro"]).optional(),
|
|
17864
|
+
model: z5.string().optional(),
|
|
17865
|
+
memory_topic: z5.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
|
|
17866
|
+
report_mode: z5.enum(["auto", "tell", "status-only"]).optional()
|
|
17766
17867
|
},
|
|
17767
17868
|
async handler(input) {
|
|
17768
17869
|
return createSubagent(ctx, input, false);
|
|
@@ -20637,9 +20738,285 @@ function createCompactionLogMcpServer(context) {
|
|
|
20637
20738
|
});
|
|
20638
20739
|
return server;
|
|
20639
20740
|
}
|
|
20640
|
-
// ../../packages/core/src/mcp/factories/
|
|
20741
|
+
// ../../packages/core/src/mcp/factories/decision.ts
|
|
20641
20742
|
import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20642
|
-
import { z as
|
|
20743
|
+
import { z as z3 } from "zod";
|
|
20744
|
+
|
|
20745
|
+
// ../../packages/core/src/storage/decisions.ts
|
|
20746
|
+
init_logger();
|
|
20747
|
+
await init_storage_host();
|
|
20748
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
20749
|
+
import { dirname as dirname8, join as join12 } from "path";
|
|
20750
|
+
var DECISION_STATUS_VALUES = [
|
|
20751
|
+
"proposed",
|
|
20752
|
+
"accepted",
|
|
20753
|
+
"executed",
|
|
20754
|
+
"rejected",
|
|
20755
|
+
"superseded"
|
|
20756
|
+
];
|
|
20757
|
+
function safeDecisionScopeKey(scopeKey) {
|
|
20758
|
+
const safe = sanitizeFileName(scopeKey);
|
|
20759
|
+
if (!safe || safe === "." || safe === "..") {
|
|
20760
|
+
throw new Error(`decisions: refusing unsafe scope key: ${scopeKey}`);
|
|
20761
|
+
}
|
|
20762
|
+
return safe;
|
|
20763
|
+
}
|
|
20764
|
+
function decisionScopeKey(opts) {
|
|
20765
|
+
return opts.topicId?.trim() || opts.session || "default";
|
|
20766
|
+
}
|
|
20767
|
+
function getDecisionFilePath(userId, scopeKey) {
|
|
20768
|
+
return join12(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
|
|
20769
|
+
}
|
|
20770
|
+
function readDecisions(userId, scopeKey) {
|
|
20771
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
20772
|
+
if (!existsSync11(path))
|
|
20773
|
+
return [];
|
|
20774
|
+
try {
|
|
20775
|
+
const parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
20776
|
+
return Array.isArray(parsed?.decisions) ? parsed.decisions : [];
|
|
20777
|
+
} catch (error) {
|
|
20778
|
+
logger.warn({ err: error, path }, "decisions: failed to read decision store");
|
|
20779
|
+
return [];
|
|
20780
|
+
}
|
|
20781
|
+
}
|
|
20782
|
+
function writeDecisions(userId, scopeKey, decisions) {
|
|
20783
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
20784
|
+
mkdirSync9(dirname8(path), { recursive: true });
|
|
20785
|
+
const payload = { version: 1, decisions };
|
|
20786
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
20787
|
+
writeFileSync7(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
20788
|
+
renameSync5(tmp, path);
|
|
20789
|
+
}
|
|
20790
|
+
function nextDecisionId(decisions) {
|
|
20791
|
+
let max = 0;
|
|
20792
|
+
for (const decision of decisions) {
|
|
20793
|
+
const id = Number(decision.id);
|
|
20794
|
+
if (Number.isInteger(id) && id > max)
|
|
20795
|
+
max = id;
|
|
20796
|
+
}
|
|
20797
|
+
return max + 1;
|
|
20798
|
+
}
|
|
20799
|
+
function normalizedIds(ids) {
|
|
20800
|
+
if (!ids)
|
|
20801
|
+
return;
|
|
20802
|
+
const unique = [...new Set(ids.map((id) => id.trim()).filter(Boolean))];
|
|
20803
|
+
return unique.length > 0 ? unique : undefined;
|
|
20804
|
+
}
|
|
20805
|
+
function validateDecisionGraph(decisions) {
|
|
20806
|
+
const ids = new Set(decisions.map((decision) => decision.id));
|
|
20807
|
+
for (const decision of decisions) {
|
|
20808
|
+
for (const upstream of decision.causedBy ?? []) {
|
|
20809
|
+
if (!ids.has(upstream)) {
|
|
20810
|
+
throw new Error(`Decision #${decision.id} references missing decision #${upstream}.`);
|
|
20811
|
+
}
|
|
20812
|
+
if (upstream === decision.id) {
|
|
20813
|
+
throw new Error(`Decision #${decision.id} cannot cause itself.`);
|
|
20814
|
+
}
|
|
20815
|
+
}
|
|
20816
|
+
}
|
|
20817
|
+
const visiting = new Set;
|
|
20818
|
+
const visited = new Set;
|
|
20819
|
+
const byId = new Map(decisions.map((decision) => [decision.id, decision]));
|
|
20820
|
+
const visit = (id) => {
|
|
20821
|
+
if (visited.has(id))
|
|
20822
|
+
return;
|
|
20823
|
+
if (visiting.has(id))
|
|
20824
|
+
throw new Error(`Decision graph contains a cycle at #${id}.`);
|
|
20825
|
+
visiting.add(id);
|
|
20826
|
+
for (const upstream of byId.get(id)?.causedBy ?? [])
|
|
20827
|
+
visit(upstream);
|
|
20828
|
+
visiting.delete(id);
|
|
20829
|
+
visited.add(id);
|
|
20830
|
+
};
|
|
20831
|
+
for (const id of ids)
|
|
20832
|
+
visit(id);
|
|
20833
|
+
}
|
|
20834
|
+
function createDecisions(decisions, inputs) {
|
|
20835
|
+
const out = [...decisions];
|
|
20836
|
+
const created = [];
|
|
20837
|
+
let id = nextDecisionId(out);
|
|
20838
|
+
for (const input of inputs) {
|
|
20839
|
+
const decision = {
|
|
20840
|
+
id: String(id++),
|
|
20841
|
+
action: input.action.trim(),
|
|
20842
|
+
reasoning: input.reasoning.trim(),
|
|
20843
|
+
agent: input.agent,
|
|
20844
|
+
status: input.status ?? "accepted",
|
|
20845
|
+
timestamp: input.timestamp ?? Date.now(),
|
|
20846
|
+
...input.model ? { model: input.model } : {},
|
|
20847
|
+
...normalizedIds(input.causedBy) ? { causedBy: normalizedIds(input.causedBy) } : {}
|
|
20848
|
+
};
|
|
20849
|
+
out.push(decision);
|
|
20850
|
+
created.push(decision);
|
|
20851
|
+
}
|
|
20852
|
+
validateDecisionGraph(out);
|
|
20853
|
+
return { decisions: out, created };
|
|
20854
|
+
}
|
|
20855
|
+
function updateDecisions(decisions, updates) {
|
|
20856
|
+
const out = decisions.map((decision) => ({
|
|
20857
|
+
...decision,
|
|
20858
|
+
...decision.causedBy ? { causedBy: [...decision.causedBy] } : {}
|
|
20859
|
+
}));
|
|
20860
|
+
const byId = new Map(out.map((decision) => [decision.id, decision]));
|
|
20861
|
+
const missing = [];
|
|
20862
|
+
for (const update of updates) {
|
|
20863
|
+
const decision = byId.get(update.id);
|
|
20864
|
+
if (!decision) {
|
|
20865
|
+
missing.push(update.id);
|
|
20866
|
+
continue;
|
|
20867
|
+
}
|
|
20868
|
+
if (update.action !== undefined)
|
|
20869
|
+
decision.action = update.action.trim();
|
|
20870
|
+
if (update.reasoning !== undefined)
|
|
20871
|
+
decision.reasoning = update.reasoning.trim();
|
|
20872
|
+
if (update.status !== undefined)
|
|
20873
|
+
decision.status = update.status;
|
|
20874
|
+
if (update.causedBy !== undefined) {
|
|
20875
|
+
const ids = normalizedIds(update.causedBy);
|
|
20876
|
+
if (ids)
|
|
20877
|
+
decision.causedBy = ids;
|
|
20878
|
+
else
|
|
20879
|
+
delete decision.causedBy;
|
|
20880
|
+
}
|
|
20881
|
+
}
|
|
20882
|
+
validateDecisionGraph(out);
|
|
20883
|
+
return { decisions: out, missing };
|
|
20884
|
+
}
|
|
20885
|
+
function deleteDecisions(decisions, opts) {
|
|
20886
|
+
if (opts.all)
|
|
20887
|
+
return { decisions: [], removed: decisions.length };
|
|
20888
|
+
const ids = new Set(opts.ids ?? []);
|
|
20889
|
+
const kept = decisions.filter((decision) => !ids.has(decision.id)).map((decision) => {
|
|
20890
|
+
const causedBy = decision.causedBy?.filter((id) => !ids.has(id));
|
|
20891
|
+
const next = { ...decision };
|
|
20892
|
+
if (causedBy && causedBy.length > 0)
|
|
20893
|
+
next.causedBy = causedBy;
|
|
20894
|
+
else
|
|
20895
|
+
delete next.causedBy;
|
|
20896
|
+
return next;
|
|
20897
|
+
});
|
|
20898
|
+
return { decisions: kept, removed: decisions.length - kept.length };
|
|
20899
|
+
}
|
|
20900
|
+
function renderDecisionList(decisions) {
|
|
20901
|
+
if (decisions.length === 0)
|
|
20902
|
+
return "Decisions (0 recorded)";
|
|
20903
|
+
return [
|
|
20904
|
+
`Decisions (${decisions.length} recorded)`,
|
|
20905
|
+
...decisions.map((decision) => {
|
|
20906
|
+
const causes = decision.causedBy?.length ? ` <- ${decision.causedBy.map((id) => `#${id}`).join(", ")}` : "";
|
|
20907
|
+
return `[${decision.status}] #${decision.id} ${decision.action}${causes}
|
|
20908
|
+
${decision.reasoning}`;
|
|
20909
|
+
})
|
|
20910
|
+
].join(`
|
|
20911
|
+
`);
|
|
20912
|
+
}
|
|
20913
|
+
|
|
20914
|
+
// ../../packages/core/src/mcp/factories/decision.ts
|
|
20915
|
+
init_mcp_helpers();
|
|
20916
|
+
var defaultDecisionMcpHost = { readDecisions, writeDecisions };
|
|
20917
|
+
function createDecisionMcpServer(context, host = defaultDecisionMcpHost) {
|
|
20918
|
+
const scopeKey = context.topic ? decisionScopeKey({ topicId: context.topicId, session: context.topic }) : "";
|
|
20919
|
+
const server = new McpServer3({ name: "decision", version: "1.0.0" });
|
|
20920
|
+
const requireContext = () => !context.userId || !scopeKey ? mcpError("Error: missing userId/topic context.") : null;
|
|
20921
|
+
const statusEnum = z3.enum(DECISION_STATUS_VALUES);
|
|
20922
|
+
server.tool("decision_create", "Record one or more durable decisions in this topic. Include the rationale and causal predecessors.", {
|
|
20923
|
+
decisions: z3.array(z3.object({
|
|
20924
|
+
action: z3.string().min(1).describe("Concise statement of what was decided"),
|
|
20925
|
+
reasoning: z3.string().min(1).describe("Why this choice was made"),
|
|
20926
|
+
status: statusEnum.optional().describe("Defaults to accepted"),
|
|
20927
|
+
caused_by: z3.array(z3.string()).optional().describe("Upstream decision ids")
|
|
20928
|
+
})).min(1)
|
|
20929
|
+
}, async ({ decisions: inputs }) => {
|
|
20930
|
+
const guard = requireContext();
|
|
20931
|
+
if (guard)
|
|
20932
|
+
return guard;
|
|
20933
|
+
try {
|
|
20934
|
+
const current = host.readDecisions(context.userId, scopeKey);
|
|
20935
|
+
const { decisions, created } = createDecisions(current, inputs.map((input) => ({
|
|
20936
|
+
action: input.action,
|
|
20937
|
+
reasoning: input.reasoning,
|
|
20938
|
+
status: input.status,
|
|
20939
|
+
causedBy: input.caused_by,
|
|
20940
|
+
agent: context.agent,
|
|
20941
|
+
model: context.model
|
|
20942
|
+
})));
|
|
20943
|
+
host.writeDecisions(context.userId, scopeKey, decisions);
|
|
20944
|
+
return mcpOk(`${created.length} decision(s) recorded (${created.map((item) => `#${item.id}`).join(", ")})
|
|
20945
|
+
|
|
20946
|
+
${renderDecisionList(decisions)}`);
|
|
20947
|
+
} catch (error) {
|
|
20948
|
+
return mcpError(`decision_create failed: ${errMsg(error)}`);
|
|
20949
|
+
}
|
|
20950
|
+
});
|
|
20951
|
+
server.tool("decision_update", "Update decisions or their causal links in this topic.", {
|
|
20952
|
+
updates: z3.array(z3.object({
|
|
20953
|
+
id: z3.string(),
|
|
20954
|
+
action: z3.string().min(1).optional(),
|
|
20955
|
+
reasoning: z3.string().min(1).optional(),
|
|
20956
|
+
status: statusEnum.optional(),
|
|
20957
|
+
caused_by: z3.array(z3.string()).optional().describe("Replacement upstream decision ids")
|
|
20958
|
+
})).min(1)
|
|
20959
|
+
}, async ({ updates }) => {
|
|
20960
|
+
const guard = requireContext();
|
|
20961
|
+
if (guard)
|
|
20962
|
+
return guard;
|
|
20963
|
+
try {
|
|
20964
|
+
const current = host.readDecisions(context.userId, scopeKey);
|
|
20965
|
+
const { decisions, missing } = updateDecisions(current, updates.map((update) => ({
|
|
20966
|
+
...update,
|
|
20967
|
+
causedBy: update.caused_by
|
|
20968
|
+
})));
|
|
20969
|
+
host.writeDecisions(context.userId, scopeKey, decisions);
|
|
20970
|
+
const warning = missing.length ? `
|
|
20971
|
+
Missing ids ignored: ${missing.join(", ")}` : "";
|
|
20972
|
+
return mcpOk(`${renderDecisionList(decisions)}${warning}`);
|
|
20973
|
+
} catch (error) {
|
|
20974
|
+
return mcpError(`decision_update failed: ${errMsg(error)}`);
|
|
20975
|
+
}
|
|
20976
|
+
});
|
|
20977
|
+
server.tool("decision_list", "Read this topic's decision graph as a concise list.", {}, async () => {
|
|
20978
|
+
const guard = requireContext();
|
|
20979
|
+
if (guard)
|
|
20980
|
+
return guard;
|
|
20981
|
+
try {
|
|
20982
|
+
return mcpOk(renderDecisionList(host.readDecisions(context.userId, scopeKey)));
|
|
20983
|
+
} catch (error) {
|
|
20984
|
+
return mcpError(`decision_list failed: ${errMsg(error)}`);
|
|
20985
|
+
}
|
|
20986
|
+
});
|
|
20987
|
+
server.tool("decision_get", "Read one topic decision as JSON.", { id: z3.string() }, async ({ id }) => {
|
|
20988
|
+
const guard = requireContext();
|
|
20989
|
+
if (guard)
|
|
20990
|
+
return guard;
|
|
20991
|
+
try {
|
|
20992
|
+
const decision = host.readDecisions(context.userId, scopeKey).find((item) => item.id === id);
|
|
20993
|
+
return decision ? mcpOk(JSON.stringify(decision, null, 2)) : mcpError(`Decision #${id} not found.`);
|
|
20994
|
+
} catch (error) {
|
|
20995
|
+
return mcpError(`decision_get failed: ${errMsg(error)}`);
|
|
20996
|
+
}
|
|
20997
|
+
});
|
|
20998
|
+
server.tool("decision_delete", "Delete decisions. Downstream causal references to deleted decisions are removed.", { ids: z3.array(z3.string()).optional(), all: z3.boolean().optional() }, async ({ ids, all }) => {
|
|
20999
|
+
const guard = requireContext();
|
|
21000
|
+
if (guard)
|
|
21001
|
+
return guard;
|
|
21002
|
+
if (!all && (!ids || ids.length === 0))
|
|
21003
|
+
return mcpError("Provide ids or all=true.");
|
|
21004
|
+
try {
|
|
21005
|
+
const current = host.readDecisions(context.userId, scopeKey);
|
|
21006
|
+
const { decisions, removed } = deleteDecisions(current, { ids, all });
|
|
21007
|
+
host.writeDecisions(context.userId, scopeKey, decisions);
|
|
21008
|
+
return mcpOk(`${removed} decision(s) deleted
|
|
21009
|
+
|
|
21010
|
+
${renderDecisionList(decisions)}`);
|
|
21011
|
+
} catch (error) {
|
|
21012
|
+
return mcpError(`decision_delete failed: ${errMsg(error)}`);
|
|
21013
|
+
}
|
|
21014
|
+
});
|
|
21015
|
+
return server;
|
|
21016
|
+
}
|
|
21017
|
+
// ../../packages/core/src/mcp/factories/session-comm.ts
|
|
21018
|
+
import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21019
|
+
import { z as z6 } from "zod";
|
|
20643
21020
|
|
|
20644
21021
|
// ../../packages/core/src/mcp/session-comm/tell-permissions.ts
|
|
20645
21022
|
await init_spawn_subagent();
|
|
@@ -20672,34 +21049,34 @@ function canSubagentTellTargetByName(identity, target) {
|
|
|
20672
21049
|
|
|
20673
21050
|
// ../../packages/core/src/mcp/factories/session-comm.ts
|
|
20674
21051
|
function createSessionCommMcpServer(context, host, options = {}) {
|
|
20675
|
-
const server = new
|
|
21052
|
+
const server = new McpServer4({ name: "session-comm", version: "2.0.0" });
|
|
20676
21053
|
const required = options.requiredMcpServers?.join(", ") || "none";
|
|
20677
21054
|
const optional = options.optionalMcpServers?.join(", ") || "none";
|
|
20678
21055
|
const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
|
|
20679
21056
|
server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host.listSessions(context));
|
|
20680
21057
|
server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
|
|
20681
|
-
enabled:
|
|
21058
|
+
enabled: z6.array(z6.string()).nullable().optional()
|
|
20682
21059
|
}, async ({ enabled }) => host.configureMcp(context, enabled));
|
|
20683
21060
|
server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host.getMcpConfig(context));
|
|
20684
21061
|
if (host.getBrowserProfile) {
|
|
20685
21062
|
server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host.getBrowserProfile(context));
|
|
20686
21063
|
}
|
|
20687
21064
|
if (host.setBrowserProfile) {
|
|
20688
|
-
server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile:
|
|
21065
|
+
server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z6.string() }, async ({ profile }) => host.setBrowserProfile(context, profile));
|
|
20689
21066
|
}
|
|
20690
21067
|
if (host.deleteBrowserProfile) {
|
|
20691
|
-
server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile:
|
|
21068
|
+
server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile: z6.string() }, async ({ profile }) => host.deleteBrowserProfile(context, profile));
|
|
20692
21069
|
}
|
|
20693
21070
|
server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host.peekSession(context));
|
|
20694
|
-
server.tool("set_description", "Set the current session description used as a routing hint.", { description:
|
|
21071
|
+
server.tool("set_description", "Set the current session description used as a routing hint.", { description: z6.string() }, async ({ description }) => host.setDescription(context, description));
|
|
20695
21072
|
if (!context.replyOnly) {
|
|
20696
21073
|
if (!subagentIdentity.restricted) {
|
|
20697
|
-
server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to:
|
|
21074
|
+
server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z6.string(), message: z6.string() }, async (input) => host.askSession(context, input));
|
|
20698
21075
|
}
|
|
20699
21076
|
if (!subagentIdentity.restricted) {
|
|
20700
|
-
server.tool("abort_session", "Abort the active query in another session.", { to:
|
|
21077
|
+
server.tool("abort_session", "Abort the active query in another session.", { to: z6.string() }, async ({ to }) => host.abortSession(context, to));
|
|
20701
21078
|
}
|
|
20702
|
-
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to:
|
|
21079
|
+
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z6.string(), message: z6.string() }, async (input) => {
|
|
20703
21080
|
if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
|
|
20704
21081
|
return {
|
|
20705
21082
|
content: [
|
|
@@ -20737,7 +21114,7 @@ init_mcp_helpers();
|
|
|
20737
21114
|
import { execFile } from "child_process";
|
|
20738
21115
|
import { cpus, freemem, loadavg, totalmem } from "os";
|
|
20739
21116
|
import { promisify } from "util";
|
|
20740
|
-
import { McpServer as
|
|
21117
|
+
import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20741
21118
|
var execFileAsync = promisify(execFile);
|
|
20742
21119
|
function fmtBytes(bytes) {
|
|
20743
21120
|
const gb = bytes / 1024 ** 3;
|
|
@@ -20830,7 +21207,7 @@ var defaultSystemHealthMcpHost = {
|
|
|
20830
21207
|
}
|
|
20831
21208
|
};
|
|
20832
21209
|
function createSystemHealthMcpServer(host = defaultSystemHealthMcpHost) {
|
|
20833
|
-
const server = new
|
|
21210
|
+
const server = new McpServer5({ name: "system-health", version: "1.0.0" });
|
|
20834
21211
|
server.tool("get_system_health", [
|
|
20835
21212
|
"\uC2DC\uC2A4\uD15C \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
20836
21213
|
"- CPU \uBD80\uD558 (load average 1/5/15\uBD84)",
|
|
@@ -20863,28 +21240,28 @@ function createSystemHealthMcpServer(host = defaultSystemHealthMcpHost) {
|
|
|
20863
21240
|
// ../../packages/core/src/mcp/factories/task.ts
|
|
20864
21241
|
await init_tasks();
|
|
20865
21242
|
init_mcp_helpers();
|
|
20866
|
-
import { McpServer as
|
|
20867
|
-
import { z as
|
|
21243
|
+
import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21244
|
+
import { z as z7 } from "zod";
|
|
20868
21245
|
var defaultTaskMcpHost = {
|
|
20869
21246
|
readTasks,
|
|
20870
21247
|
writeTasks
|
|
20871
21248
|
};
|
|
20872
21249
|
function createTaskMcpServer(context, host = defaultTaskMcpHost) {
|
|
20873
21250
|
const scopeKey = context.topic ? taskScopeKey({ topicId: context.topicId, session: context.topic }) : "";
|
|
20874
|
-
const server = new
|
|
21251
|
+
const server = new McpServer6({ name: "task", version: "1.0.0" });
|
|
20875
21252
|
const requireContext = () => {
|
|
20876
21253
|
if (!context.userId || !scopeKey) {
|
|
20877
21254
|
return mcpError("Error: missing userId/topic context.");
|
|
20878
21255
|
}
|
|
20879
21256
|
return null;
|
|
20880
21257
|
};
|
|
20881
|
-
const statusEnum =
|
|
21258
|
+
const statusEnum = z7.enum(TASK_STATUS_VALUES);
|
|
20882
21259
|
server.tool("task_create", "Add one or more tasks to this topic's shared Otium task list. Use this instead of provider-native todo/task tools.", {
|
|
20883
|
-
tasks:
|
|
20884
|
-
subject:
|
|
20885
|
-
active_form:
|
|
20886
|
-
blocked_by:
|
|
20887
|
-
owner:
|
|
21260
|
+
tasks: z7.array(z7.object({
|
|
21261
|
+
subject: z7.string().min(1).describe("Short imperative task description"),
|
|
21262
|
+
active_form: z7.string().optional().describe("Label to show while the task is in progress"),
|
|
21263
|
+
blocked_by: z7.array(z7.string()).optional().describe("Task ids blocking this task"),
|
|
21264
|
+
owner: z7.string().optional().describe("Owner/agent label for multi-agent work")
|
|
20888
21265
|
})).min(1)
|
|
20889
21266
|
}, async ({ tasks: inputs }) => {
|
|
20890
21267
|
const guard = requireContext();
|
|
@@ -20908,13 +21285,13 @@ ${renderTaskList(tasks2)}`);
|
|
|
20908
21285
|
}
|
|
20909
21286
|
});
|
|
20910
21287
|
server.tool("task_update", "Update task status/content in this topic's shared Otium task list. Batch related status changes in one call.", {
|
|
20911
|
-
updates:
|
|
20912
|
-
id:
|
|
21288
|
+
updates: z7.array(z7.object({
|
|
21289
|
+
id: z7.string().describe("Target task id"),
|
|
20913
21290
|
status: statusEnum.optional().describe("New status"),
|
|
20914
|
-
subject:
|
|
20915
|
-
active_form:
|
|
20916
|
-
blocked_by:
|
|
20917
|
-
owner:
|
|
21291
|
+
subject: z7.string().min(1).optional().describe("Replacement task description"),
|
|
21292
|
+
active_form: z7.string().optional().describe("Replacement in-progress label; empty string clears it"),
|
|
21293
|
+
blocked_by: z7.array(z7.string()).optional().describe("Replacement blocker id list"),
|
|
21294
|
+
owner: z7.string().optional().describe("Replacement owner; empty string clears it")
|
|
20918
21295
|
})).min(1)
|
|
20919
21296
|
}, async ({ updates }) => {
|
|
20920
21297
|
const guard = requireContext();
|
|
@@ -20948,7 +21325,7 @@ Missing ids ignored: ${missing.join(", ")}` : "";
|
|
|
20948
21325
|
return mcpError(`task_list failed: ${errMsg(error)}`);
|
|
20949
21326
|
}
|
|
20950
21327
|
});
|
|
20951
|
-
server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id:
|
|
21328
|
+
server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z7.string().describe("Task id") }, async ({ id }) => {
|
|
20952
21329
|
const guard = requireContext();
|
|
20953
21330
|
if (guard)
|
|
20954
21331
|
return guard;
|
|
@@ -20962,8 +21339,8 @@ Missing ids ignored: ${missing.join(", ")}` : "";
|
|
|
20962
21339
|
}
|
|
20963
21340
|
});
|
|
20964
21341
|
server.tool("task_delete", "Delete tasks from this topic's shared Otium task list. Use all=true only when starting a new plan.", {
|
|
20965
|
-
ids:
|
|
20966
|
-
all:
|
|
21342
|
+
ids: z7.array(z7.string()).optional().describe("Task ids to delete"),
|
|
21343
|
+
all: z7.boolean().optional().describe("Delete the whole list")
|
|
20967
21344
|
}, async ({ ids, all }) => {
|
|
20968
21345
|
const guard = requireContext();
|
|
20969
21346
|
if (guard)
|
|
@@ -20985,8 +21362,8 @@ ${renderTaskList(tasks2)}`);
|
|
|
20985
21362
|
}
|
|
20986
21363
|
// ../../packages/core/src/mcp/factories/token-stats.ts
|
|
20987
21364
|
await init_token_stats();
|
|
20988
|
-
import { McpServer as
|
|
20989
|
-
import { z as
|
|
21365
|
+
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21366
|
+
import { z as z8 } from "zod";
|
|
20990
21367
|
var defaultTokenStatsMcpHost = {
|
|
20991
21368
|
getStats,
|
|
20992
21369
|
calcCost
|
|
@@ -20994,7 +21371,7 @@ var defaultTokenStatsMcpHost = {
|
|
|
20994
21371
|
function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
|
|
20995
21372
|
if (!context.userId)
|
|
20996
21373
|
throw new Error("token-stats MCP requires userId");
|
|
20997
|
-
const server = new
|
|
21374
|
+
const server = new McpServer7({ name: "token-stats", version: "1.0.0" });
|
|
20998
21375
|
server.tool("get_usage_stats", [
|
|
20999
21376
|
"Claude/Codex/Maestro \uC0AC\uC6A9\uB7C9\uACFC provider session\uC758 \uCD5C\uC2E0 context \uC810\uC720\uC728\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uD604\uC7AC \uC2E4\uD589 \uC911\uC778 turn\uC740 \uC644\uB8CC \uD6C4 \uC9D1\uACC4\uB429\uB2C8\uB2E4.",
|
|
21000
21377
|
"",
|
|
@@ -21006,9 +21383,9 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
|
|
|
21006
21383
|
"groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
|
|
21007
21384
|
].join(`
|
|
21008
21385
|
`), {
|
|
21009
|
-
from:
|
|
21010
|
-
to:
|
|
21011
|
-
groupBy:
|
|
21386
|
+
from: z8.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
|
|
21387
|
+
to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
|
|
21388
|
+
groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
|
|
21012
21389
|
}, async ({ from, to, groupBy = "hour" }) => {
|
|
21013
21390
|
const snapshot = host.getStats(context.userId, from, to);
|
|
21014
21391
|
const {
|
|
@@ -21082,11 +21459,11 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
|
|
|
21082
21459
|
return server;
|
|
21083
21460
|
}
|
|
21084
21461
|
// ../../packages/core/src/mcp/factories/vault.ts
|
|
21085
|
-
import { McpServer as
|
|
21086
|
-
import { z as
|
|
21462
|
+
import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21463
|
+
import { z as z9 } from "zod";
|
|
21087
21464
|
init_mcp_helpers();
|
|
21088
21465
|
function createVaultMcpServer(context, host, executors = {}) {
|
|
21089
|
-
const server = new
|
|
21466
|
+
const server = new McpServer8({ name: "vault", version: "1.0.0" });
|
|
21090
21467
|
const run = executors.run ?? executeVaultRun;
|
|
21091
21468
|
const http = executors.http ?? executeVaultHttpRequest;
|
|
21092
21469
|
server.tool("vault_list", context.httpOnly ? "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for HTTPS APIs that need a credential." : "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for APIs and vault_run for shell/CLI work that needs a credential.", {}, () => {
|
|
@@ -21102,9 +21479,9 @@ ${lines.join(`
|
|
|
21102
21479
|
});
|
|
21103
21480
|
if (!context.listOnly && !context.httpOnly) {
|
|
21104
21481
|
server.tool("vault_run", "Run a shell command containing {{KEY}} references inside Otium's credential broker. Expanded command input never reaches the model/provider, and stdout/stderr are redacted before return. Prefer vault_http_request for HTTP APIs.", {
|
|
21105
|
-
command:
|
|
21106
|
-
timeout_ms:
|
|
21107
|
-
max_output_bytes:
|
|
21482
|
+
command: z9.string().min(1).max(64 * 1024),
|
|
21483
|
+
timeout_ms: z9.number().int().min(1000).max(600000).optional(),
|
|
21484
|
+
max_output_bytes: z9.number().int().min(1024).max(2 * 1024 * 1024).optional()
|
|
21108
21485
|
}, async ({ command, timeout_ms, max_output_bytes }) => {
|
|
21109
21486
|
if (!context.userId)
|
|
21110
21487
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -21119,12 +21496,12 @@ ${lines.join(`
|
|
|
21119
21496
|
}
|
|
21120
21497
|
if (!context.listOnly)
|
|
21121
21498
|
server.tool("vault_http_request", "Make an HTTPS request with {{KEY}} references resolved inside Otium. Put secrets in headers or body, never in the URL. The expanded request is not returned; the response is redacted before the model sees it.", {
|
|
21122
|
-
method:
|
|
21123
|
-
url:
|
|
21124
|
-
headers:
|
|
21125
|
-
body:
|
|
21126
|
-
timeout_ms:
|
|
21127
|
-
max_response_bytes:
|
|
21499
|
+
method: z9.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
21500
|
+
url: z9.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
|
|
21501
|
+
headers: z9.record(z9.string(), z9.string()).optional(),
|
|
21502
|
+
body: z9.string().optional(),
|
|
21503
|
+
timeout_ms: z9.number().int().min(1000).max(120000).optional(),
|
|
21504
|
+
max_response_bytes: z9.number().int().min(1024).max(1024 * 1024).optional()
|
|
21128
21505
|
}, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
|
|
21129
21506
|
if (!context.userId)
|
|
21130
21507
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -21148,6 +21525,7 @@ export {
|
|
|
21148
21525
|
defaultTokenStatsMcpHost,
|
|
21149
21526
|
defaultTaskMcpHost,
|
|
21150
21527
|
defaultSystemHealthMcpHost,
|
|
21528
|
+
defaultDecisionMcpHost,
|
|
21151
21529
|
createWikiMcpServer,
|
|
21152
21530
|
createVaultMcpServer,
|
|
21153
21531
|
createTokenStatsMcpServer,
|
|
@@ -21155,8 +21533,9 @@ export {
|
|
|
21155
21533
|
createSystemHealthMcpServer,
|
|
21156
21534
|
createSessionTargetCatalog,
|
|
21157
21535
|
createSessionCommMcpServer,
|
|
21536
|
+
createDecisionMcpServer,
|
|
21158
21537
|
createCompactionLogMcpServer,
|
|
21159
21538
|
createAgentHealthMcpServer
|
|
21160
21539
|
};
|
|
21161
21540
|
|
|
21162
|
-
//# debugId=
|
|
21541
|
+
//# debugId=A4B482066B2CF06D64756E2164756E21
|