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/main.js
CHANGED
|
@@ -273,7 +273,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
|
|
|
273
273
|
function codexAuthFilePath() {
|
|
274
274
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
|
|
275
275
|
}
|
|
276
|
-
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.
|
|
276
|
+
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;
|
|
277
277
|
var init_config = __esm(() => {
|
|
278
278
|
init_config_helpers();
|
|
279
279
|
init_logger();
|
|
@@ -303,6 +303,7 @@ var init_config = __esm(() => {
|
|
|
303
303
|
TSCONFIG_PATH = resolve(PROJECT_ROOT, "tsconfig.json");
|
|
304
304
|
SESSION_COMM_SERVER = resolve(PROJECT_ROOT, "src/mcp/session-comm/server.ts");
|
|
305
305
|
TASK_SERVER = resolve(PROJECT_ROOT, "src/mcp/task-server.ts");
|
|
306
|
+
DECISION_SERVER = resolve(PROJECT_ROOT, "src/mcp/decision-server.ts");
|
|
306
307
|
BROWSER_MCP_SSE_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/browser-sse-proxy-server.ts");
|
|
307
308
|
CANONICAL_MCP_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/canonical-proxy-server.ts");
|
|
308
309
|
WIKI_SERVER = resolve(PROJECT_ROOT, "src/mcp/wiki-server.ts");
|
|
@@ -1868,7 +1869,7 @@ var exports_version = {};
|
|
|
1868
1869
|
__export(exports_version, {
|
|
1869
1870
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
1870
1871
|
});
|
|
1871
|
-
var NEGOTIUM_VERSION = "0.3.
|
|
1872
|
+
var NEGOTIUM_VERSION = "0.3.2";
|
|
1872
1873
|
|
|
1873
1874
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
1874
1875
|
import { spawn } from "child_process";
|
|
@@ -2391,6 +2392,7 @@ var init_runtime_spec = __esm(() => {
|
|
|
2391
2392
|
init_types();
|
|
2392
2393
|
HOSTED_MCP_SURFACES = [
|
|
2393
2394
|
"task",
|
|
2395
|
+
"decision",
|
|
2394
2396
|
"token-stats",
|
|
2395
2397
|
"system-health",
|
|
2396
2398
|
"vault",
|
|
@@ -2661,6 +2663,7 @@ var init_mcp_catalog_policy = __esm(() => {
|
|
|
2661
2663
|
runtime: { scopes: ["forum", "manager", "fork", "cron"], forumRequired: true },
|
|
2662
2664
|
"token-stats": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
2663
2665
|
task: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
2666
|
+
decision: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
2664
2667
|
"session-comm": { scopes: ["forum", "fork", "manager"], forumRequired: true },
|
|
2665
2668
|
wiki: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
2666
2669
|
skills: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
|
|
@@ -2913,7 +2916,7 @@ function getForumMcpServers(opts) {
|
|
|
2913
2916
|
peerBridge
|
|
2914
2917
|
} = opts;
|
|
2915
2918
|
const filter = (name) => {
|
|
2916
|
-
if (silent && name === "task")
|
|
2919
|
+
if (silent && (name === "task" || name === "decision"))
|
|
2917
2920
|
return false;
|
|
2918
2921
|
if (enabled === null)
|
|
2919
2922
|
return true;
|
|
@@ -3131,6 +3134,34 @@ var init_mcp_config = __esm(() => {
|
|
|
3131
3134
|
return buildBuiltinMcpServer("task", ctx, () => buildStdioMcpServer(agent, TASK_SERVER, args));
|
|
3132
3135
|
}
|
|
3133
3136
|
},
|
|
3137
|
+
decision: {
|
|
3138
|
+
...commonRuntimeMcpPolicy("decision"),
|
|
3139
|
+
build(ctx) {
|
|
3140
|
+
const { userId, session, topicId, queryId, agent, model, peerBridge } = ctx;
|
|
3141
|
+
if (peerBridge) {
|
|
3142
|
+
if (!topicId || !queryId)
|
|
3143
|
+
return null;
|
|
3144
|
+
const env = canonicalMcpBridgeEnv({
|
|
3145
|
+
surface: "decision",
|
|
3146
|
+
userId,
|
|
3147
|
+
topicId,
|
|
3148
|
+
queryId,
|
|
3149
|
+
peerBridge
|
|
3150
|
+
});
|
|
3151
|
+
return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=decision"], env) : null;
|
|
3152
|
+
}
|
|
3153
|
+
const args = [
|
|
3154
|
+
`--user-id=${userId}`,
|
|
3155
|
+
`--topic=${session}`,
|
|
3156
|
+
`--agent=${agent ?? FALLBACK_AGENT}`
|
|
3157
|
+
];
|
|
3158
|
+
if (topicId)
|
|
3159
|
+
args.push(`--topic-id=${topicId}`);
|
|
3160
|
+
if (model)
|
|
3161
|
+
args.push(`--model=${model}`);
|
|
3162
|
+
return buildBuiltinMcpServer("decision", ctx, () => buildStdioMcpServer(agent, DECISION_SERVER, args));
|
|
3163
|
+
}
|
|
3164
|
+
},
|
|
3134
3165
|
"session-comm": {
|
|
3135
3166
|
...commonRuntimeMcpPolicy("session-comm"),
|
|
3136
3167
|
build(ctx) {
|
|
@@ -11655,6 +11686,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11655
11686
|
} = opts;
|
|
11656
11687
|
const runtimeNamespace = "mcp__runtime";
|
|
11657
11688
|
const taskNamespace = "mcp__task";
|
|
11689
|
+
const decisionNamespace = "mcp__decision";
|
|
11658
11690
|
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" }.`;
|
|
11659
11691
|
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" }.`;
|
|
11660
11692
|
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.`;
|
|
@@ -11662,6 +11694,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11662
11694
|
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?: "..." }] }.`;
|
|
11663
11695
|
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.`;
|
|
11664
11696
|
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".`;
|
|
11697
|
+
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.`;
|
|
11665
11698
|
const runtimeToolRef = (name) => agentKind === "codex" ? `\`${name}\`` : `"${runtimeNamespace}__${name}"`;
|
|
11666
11699
|
const spawnSubagentToolLine = `Use ${runtimeToolRef("spawn_subagent")} for self-contained parallel or long-running background work; keep quick work inline.`;
|
|
11667
11700
|
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\`.`;
|
|
@@ -11708,6 +11741,9 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11708
11741
|
taskToolLine,
|
|
11709
11742
|
"Use this shared task store for plans, progress, and checklist updates; it is visible across claude/codex/maestro turns.",
|
|
11710
11743
|
nativeTaskPolicyLine,
|
|
11744
|
+
"",
|
|
11745
|
+
"## Shared Decisions",
|
|
11746
|
+
decisionToolLine,
|
|
11711
11747
|
...extensions.render("after-shared-tasks"),
|
|
11712
11748
|
...fileDeliverySection,
|
|
11713
11749
|
...extensions.render("before-session-communication"),
|
|
@@ -17162,6 +17198,10 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
17162
17198
|
.controls button:focus-visible{outline:2px solid var(--celadon);outline-offset:1px}
|
|
17163
17199
|
.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}
|
|
17164
17200
|
.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}
|
|
17201
|
+
.failure{max-width:56ch;margin:0 auto}
|
|
17202
|
+
.failure p{margin:0 0 12px;color:var(--graphite);font:14px/1.6 Geist,system-ui,sans-serif}
|
|
17203
|
+
.failure details{color:var(--graphite);font:12px/1.5 Geist,system-ui,sans-serif}
|
|
17204
|
+
.failure summary{cursor:pointer;padding:4px 0}
|
|
17165
17205
|
@media(max-width:600px){.viewport{padding:54px 14px 18px}.controls{top:10px;right:10px}}
|
|
17166
17206
|
</style>
|
|
17167
17207
|
</head>
|
|
@@ -17176,11 +17216,58 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
17176
17216
|
<script data-otium-mermaid-runtime src="${safeScriptUrl}"></script>
|
|
17177
17217
|
<script>
|
|
17178
17218
|
(async () => {
|
|
17219
|
+
// An unrendered document reports itself in more than one voice: Mermaid's
|
|
17220
|
+
// own 0x0 guard, and the browser refusing geometry on a path that was
|
|
17221
|
+
// never laid out. Both mean the same thing, so both are worth one retry
|
|
17222
|
+
// and, if it still fails, the same explanation.
|
|
17223
|
+
const unrendered = (error) => {
|
|
17224
|
+
const message = String(error && error.message ? error.message : error);
|
|
17225
|
+
return message.indexOf("not in render tree") !== -1 || message.indexOf("path is empty") !== -1;
|
|
17226
|
+
};
|
|
17179
17227
|
try {
|
|
17180
17228
|
const runtime = globalThis.mermaid;
|
|
17181
17229
|
if (!runtime) throw new Error("Mermaid renderer failed to load.");
|
|
17182
17230
|
runtime.initialize({ startOnLoad: false, securityLevel: "strict", theme: ${safeTheme} });
|
|
17183
|
-
|
|
17231
|
+
const host = document.querySelector(".mermaid");
|
|
17232
|
+
// Mermaid sizes every label by appending a probe <svg> to the body and
|
|
17233
|
+
// reading getBBox(), and it throws "svg element not in render tree" the
|
|
17234
|
+
// moment that comes back 0x0. That is what a hidden panel looks like from
|
|
17235
|
+
// in here: the document exists but nothing is in the render tree, so the
|
|
17236
|
+
// measurement has no geometry to report. Ask the same question Mermaid
|
|
17237
|
+
// will ask, and only start once it has an answer.
|
|
17238
|
+
const measurable = () => {
|
|
17239
|
+
const probe = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
17240
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
17241
|
+
text.textContent = "M";
|
|
17242
|
+
probe.appendChild(text);
|
|
17243
|
+
document.body.appendChild(probe);
|
|
17244
|
+
let box = { width: 0, height: 0 };
|
|
17245
|
+
try { box = text.getBBox(); } catch (ignored) {}
|
|
17246
|
+
probe.remove();
|
|
17247
|
+
return box.width > 0 || box.height > 0;
|
|
17248
|
+
};
|
|
17249
|
+
// requestAnimationFrame is the right clock here: a hidden document stops
|
|
17250
|
+
// being animated, so this waits without spinning and resumes on the frame
|
|
17251
|
+
// the panel is shown. The cap counts rendered frames, not wall time.
|
|
17252
|
+
const waitUntilMeasurable = async (maxFrames) => {
|
|
17253
|
+
for (let frame = 0; frame < maxFrames; frame += 1) {
|
|
17254
|
+
if (measurable()) return true;
|
|
17255
|
+
await new Promise((next) => requestAnimationFrame(next));
|
|
17256
|
+
}
|
|
17257
|
+
return measurable();
|
|
17258
|
+
};
|
|
17259
|
+
await waitUntilMeasurable(600);
|
|
17260
|
+
try {
|
|
17261
|
+
await runtime.run({ querySelector: ".mermaid" });
|
|
17262
|
+
} catch (firstAttempt) {
|
|
17263
|
+
if (!unrendered(firstAttempt)) throw firstAttempt;
|
|
17264
|
+
// The panel can be hidden again between the probe and the real measure.
|
|
17265
|
+
// Clear the marker Mermaid leaves behind so the retry is not skipped as
|
|
17266
|
+
// already done, then wait for the render tree once more.
|
|
17267
|
+
host.removeAttribute("data-processed");
|
|
17268
|
+
await waitUntilMeasurable(600);
|
|
17269
|
+
await runtime.run({ querySelector: ".mermaid" });
|
|
17270
|
+
}
|
|
17184
17271
|
const viewport = document.querySelector(".viewport");
|
|
17185
17272
|
const svg = document.querySelector(".mermaid svg");
|
|
17186
17273
|
const value = document.querySelector(".zoom-value");
|
|
@@ -17207,7 +17294,22 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
17207
17294
|
applyScale(scale, false);
|
|
17208
17295
|
} catch (error) {
|
|
17209
17296
|
document.querySelector(".controls")?.remove();
|
|
17210
|
-
|
|
17297
|
+
const raw = String(error && error.message ? error.message : error);
|
|
17298
|
+
const escape = (value) => value.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
|
17299
|
+
// Mermaid aborts the whole render when it cannot place an edge label:
|
|
17300
|
+
// cardinality markers ask for a point a fixed distance along the edge,
|
|
17301
|
+
// and a relation laid out shorter than that walks off the end. Nothing
|
|
17302
|
+
// is wrong with the diagram, so the raw message sends authors looking
|
|
17303
|
+
// for a syntax error that does not exist. Say what actually moves it.
|
|
17304
|
+
const note = raw.indexOf("Could not find a suitable point") !== -1
|
|
17305
|
+
? "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."
|
|
17306
|
+
: unrendered(error)
|
|
17307
|
+
? "The panel stayed hidden long enough that there was never a laid-out page to measure the diagram against. Reopening the panel renders it."
|
|
17308
|
+
: "This diagram could not be rendered.";
|
|
17309
|
+
document.querySelector(".viewport").innerHTML =
|
|
17310
|
+
'<div class="failure"><p>' + escape(note) +
|
|
17311
|
+
'</p><details><summary>Technical detail</summary><pre class="error">' +
|
|
17312
|
+
escape(raw) + '</pre></details></div>';
|
|
17211
17313
|
}
|
|
17212
17314
|
})();
|
|
17213
17315
|
</script>
|
|
@@ -22945,9 +23047,208 @@ var init_conversation_migration = __esm(async () => {
|
|
|
22945
23047
|
init_types();
|
|
22946
23048
|
});
|
|
22947
23049
|
|
|
23050
|
+
// ../../packages/core/src/storage/decisions.ts
|
|
23051
|
+
var exports_decisions = {};
|
|
23052
|
+
__export(exports_decisions, {
|
|
23053
|
+
writeDecisions: () => writeDecisions,
|
|
23054
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
23055
|
+
validateDecisionGraph: () => validateDecisionGraph,
|
|
23056
|
+
updateDecisions: () => updateDecisions,
|
|
23057
|
+
renderDecisionList: () => renderDecisionList,
|
|
23058
|
+
readDecisions: () => readDecisions,
|
|
23059
|
+
getDecisionGraphSvgPath: () => getDecisionGraphSvgPath,
|
|
23060
|
+
getDecisionFilePath: () => getDecisionFilePath,
|
|
23061
|
+
deleteDecisions: () => deleteDecisions,
|
|
23062
|
+
decisionScopeKey: () => decisionScopeKey,
|
|
23063
|
+
createDecisions: () => createDecisions,
|
|
23064
|
+
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
|
|
23065
|
+
});
|
|
23066
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
23067
|
+
import { dirname as dirname16, join as join34 } from "path";
|
|
23068
|
+
function safeDecisionScopeKey(scopeKey) {
|
|
23069
|
+
const safe = sanitizeFileName(scopeKey);
|
|
23070
|
+
if (!safe || safe === "." || safe === "..") {
|
|
23071
|
+
throw new Error(`decisions: refusing unsafe scope key: ${scopeKey}`);
|
|
23072
|
+
}
|
|
23073
|
+
return safe;
|
|
23074
|
+
}
|
|
23075
|
+
function decisionScopeKey(opts) {
|
|
23076
|
+
return opts.topicId?.trim() || opts.session || "default";
|
|
23077
|
+
}
|
|
23078
|
+
function getDecisionFilePath(userId, scopeKey) {
|
|
23079
|
+
return join34(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
|
|
23080
|
+
}
|
|
23081
|
+
function getDecisionGraphSvgPath(userId, scopeKey) {
|
|
23082
|
+
return join34(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
|
|
23083
|
+
}
|
|
23084
|
+
function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
23085
|
+
const path = getDecisionGraphSvgPath(userId, scopeKey);
|
|
23086
|
+
mkdirSync24(dirname16(path), { recursive: true });
|
|
23087
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
23088
|
+
writeFileSync18(tmp, svg, "utf-8");
|
|
23089
|
+
renameSync11(tmp, path);
|
|
23090
|
+
return path;
|
|
23091
|
+
}
|
|
23092
|
+
function readDecisions(userId, scopeKey) {
|
|
23093
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
23094
|
+
if (!existsSync24(path))
|
|
23095
|
+
return [];
|
|
23096
|
+
try {
|
|
23097
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
23098
|
+
return Array.isArray(parsed?.decisions) ? parsed.decisions : [];
|
|
23099
|
+
} catch (error) {
|
|
23100
|
+
logger.warn({ err: error, path }, "decisions: failed to read decision store");
|
|
23101
|
+
return [];
|
|
23102
|
+
}
|
|
23103
|
+
}
|
|
23104
|
+
function writeDecisions(userId, scopeKey, decisions) {
|
|
23105
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
23106
|
+
mkdirSync24(dirname16(path), { recursive: true });
|
|
23107
|
+
const payload = { version: 1, decisions };
|
|
23108
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
23109
|
+
writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
23110
|
+
renameSync11(tmp, path);
|
|
23111
|
+
}
|
|
23112
|
+
function nextDecisionId(decisions) {
|
|
23113
|
+
let max = 0;
|
|
23114
|
+
for (const decision of decisions) {
|
|
23115
|
+
const id = Number(decision.id);
|
|
23116
|
+
if (Number.isInteger(id) && id > max)
|
|
23117
|
+
max = id;
|
|
23118
|
+
}
|
|
23119
|
+
return max + 1;
|
|
23120
|
+
}
|
|
23121
|
+
function normalizedIds(ids) {
|
|
23122
|
+
if (!ids)
|
|
23123
|
+
return;
|
|
23124
|
+
const unique = [...new Set(ids.map((id) => id.trim()).filter(Boolean))];
|
|
23125
|
+
return unique.length > 0 ? unique : undefined;
|
|
23126
|
+
}
|
|
23127
|
+
function validateDecisionGraph(decisions) {
|
|
23128
|
+
const ids = new Set(decisions.map((decision) => decision.id));
|
|
23129
|
+
for (const decision of decisions) {
|
|
23130
|
+
for (const upstream of decision.causedBy ?? []) {
|
|
23131
|
+
if (!ids.has(upstream)) {
|
|
23132
|
+
throw new Error(`Decision #${decision.id} references missing decision #${upstream}.`);
|
|
23133
|
+
}
|
|
23134
|
+
if (upstream === decision.id) {
|
|
23135
|
+
throw new Error(`Decision #${decision.id} cannot cause itself.`);
|
|
23136
|
+
}
|
|
23137
|
+
}
|
|
23138
|
+
}
|
|
23139
|
+
const visiting = new Set;
|
|
23140
|
+
const visited = new Set;
|
|
23141
|
+
const byId = new Map(decisions.map((decision) => [decision.id, decision]));
|
|
23142
|
+
const visit = (id) => {
|
|
23143
|
+
if (visited.has(id))
|
|
23144
|
+
return;
|
|
23145
|
+
if (visiting.has(id))
|
|
23146
|
+
throw new Error(`Decision graph contains a cycle at #${id}.`);
|
|
23147
|
+
visiting.add(id);
|
|
23148
|
+
for (const upstream of byId.get(id)?.causedBy ?? [])
|
|
23149
|
+
visit(upstream);
|
|
23150
|
+
visiting.delete(id);
|
|
23151
|
+
visited.add(id);
|
|
23152
|
+
};
|
|
23153
|
+
for (const id of ids)
|
|
23154
|
+
visit(id);
|
|
23155
|
+
}
|
|
23156
|
+
function createDecisions(decisions, inputs) {
|
|
23157
|
+
const out = [...decisions];
|
|
23158
|
+
const created = [];
|
|
23159
|
+
let id = nextDecisionId(out);
|
|
23160
|
+
for (const input of inputs) {
|
|
23161
|
+
const decision = {
|
|
23162
|
+
id: String(id++),
|
|
23163
|
+
action: input.action.trim(),
|
|
23164
|
+
reasoning: input.reasoning.trim(),
|
|
23165
|
+
agent: input.agent,
|
|
23166
|
+
status: input.status ?? "accepted",
|
|
23167
|
+
timestamp: input.timestamp ?? Date.now(),
|
|
23168
|
+
...input.model ? { model: input.model } : {},
|
|
23169
|
+
...normalizedIds(input.causedBy) ? { causedBy: normalizedIds(input.causedBy) } : {}
|
|
23170
|
+
};
|
|
23171
|
+
out.push(decision);
|
|
23172
|
+
created.push(decision);
|
|
23173
|
+
}
|
|
23174
|
+
validateDecisionGraph(out);
|
|
23175
|
+
return { decisions: out, created };
|
|
23176
|
+
}
|
|
23177
|
+
function updateDecisions(decisions, updates) {
|
|
23178
|
+
const out = decisions.map((decision) => ({
|
|
23179
|
+
...decision,
|
|
23180
|
+
...decision.causedBy ? { causedBy: [...decision.causedBy] } : {}
|
|
23181
|
+
}));
|
|
23182
|
+
const byId = new Map(out.map((decision) => [decision.id, decision]));
|
|
23183
|
+
const missing = [];
|
|
23184
|
+
for (const update of updates) {
|
|
23185
|
+
const decision = byId.get(update.id);
|
|
23186
|
+
if (!decision) {
|
|
23187
|
+
missing.push(update.id);
|
|
23188
|
+
continue;
|
|
23189
|
+
}
|
|
23190
|
+
if (update.action !== undefined)
|
|
23191
|
+
decision.action = update.action.trim();
|
|
23192
|
+
if (update.reasoning !== undefined)
|
|
23193
|
+
decision.reasoning = update.reasoning.trim();
|
|
23194
|
+
if (update.status !== undefined)
|
|
23195
|
+
decision.status = update.status;
|
|
23196
|
+
if (update.causedBy !== undefined) {
|
|
23197
|
+
const ids = normalizedIds(update.causedBy);
|
|
23198
|
+
if (ids)
|
|
23199
|
+
decision.causedBy = ids;
|
|
23200
|
+
else
|
|
23201
|
+
delete decision.causedBy;
|
|
23202
|
+
}
|
|
23203
|
+
}
|
|
23204
|
+
validateDecisionGraph(out);
|
|
23205
|
+
return { decisions: out, missing };
|
|
23206
|
+
}
|
|
23207
|
+
function deleteDecisions(decisions, opts) {
|
|
23208
|
+
if (opts.all)
|
|
23209
|
+
return { decisions: [], removed: decisions.length };
|
|
23210
|
+
const ids = new Set(opts.ids ?? []);
|
|
23211
|
+
const kept = decisions.filter((decision) => !ids.has(decision.id)).map((decision) => {
|
|
23212
|
+
const causedBy = decision.causedBy?.filter((id) => !ids.has(id));
|
|
23213
|
+
const next = { ...decision };
|
|
23214
|
+
if (causedBy && causedBy.length > 0)
|
|
23215
|
+
next.causedBy = causedBy;
|
|
23216
|
+
else
|
|
23217
|
+
delete next.causedBy;
|
|
23218
|
+
return next;
|
|
23219
|
+
});
|
|
23220
|
+
return { decisions: kept, removed: decisions.length - kept.length };
|
|
23221
|
+
}
|
|
23222
|
+
function renderDecisionList(decisions) {
|
|
23223
|
+
if (decisions.length === 0)
|
|
23224
|
+
return "Decisions (0 recorded)";
|
|
23225
|
+
return [
|
|
23226
|
+
`Decisions (${decisions.length} recorded)`,
|
|
23227
|
+
...decisions.map((decision) => {
|
|
23228
|
+
const causes = decision.causedBy?.length ? ` <- ${decision.causedBy.map((id) => `#${id}`).join(", ")}` : "";
|
|
23229
|
+
return `[${decision.status}] #${decision.id} ${decision.action}${causes}
|
|
23230
|
+
${decision.reasoning}`;
|
|
23231
|
+
})
|
|
23232
|
+
].join(`
|
|
23233
|
+
`);
|
|
23234
|
+
}
|
|
23235
|
+
var DECISION_STATUS_VALUES;
|
|
23236
|
+
var init_decisions = __esm(async () => {
|
|
23237
|
+
init_logger();
|
|
23238
|
+
await init_storage_host();
|
|
23239
|
+
DECISION_STATUS_VALUES = [
|
|
23240
|
+
"proposed",
|
|
23241
|
+
"accepted",
|
|
23242
|
+
"executed",
|
|
23243
|
+
"rejected",
|
|
23244
|
+
"superseded"
|
|
23245
|
+
];
|
|
23246
|
+
});
|
|
23247
|
+
|
|
22948
23248
|
// ../../packages/core/src/node-host.ts
|
|
22949
23249
|
var exports_node_host = {};
|
|
22950
23250
|
__export(exports_node_host, {
|
|
23251
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
22951
23252
|
upsertTopic: () => upsertTopic,
|
|
22952
23253
|
topicService: () => topicService,
|
|
22953
23254
|
switchTopicModel: () => switchTopicModel,
|
|
@@ -22972,6 +23273,7 @@ __export(exports_node_host, {
|
|
|
22972
23273
|
runNodeRequestHandlers: () => runNodeRequestHandlers,
|
|
22973
23274
|
reconcilePendingAskUserQuestionGates: () => reconcilePendingAskUserQuestionGates,
|
|
22974
23275
|
reapOrphanBrowsers: () => reapPlaywrightOrphans,
|
|
23276
|
+
readDecisions: () => readDecisions,
|
|
22975
23277
|
onShutdown: () => onShutdown,
|
|
22976
23278
|
nodeRequestHandlerNames: () => nodeRequestHandlerNames,
|
|
22977
23279
|
logger: () => logger,
|
|
@@ -23041,6 +23343,7 @@ var init_node_host = __esm(async () => {
|
|
|
23041
23343
|
await init_turn_runner();
|
|
23042
23344
|
await init_api_messages();
|
|
23043
23345
|
await init_api_topics();
|
|
23346
|
+
await init_decisions();
|
|
23044
23347
|
await init_runtime_events();
|
|
23045
23348
|
await init_runtime_process_leases();
|
|
23046
23349
|
await init_token_stats();
|
|
@@ -23050,7 +23353,7 @@ var init_node_host = __esm(async () => {
|
|
|
23050
23353
|
});
|
|
23051
23354
|
|
|
23052
23355
|
// ../../packages/core/src/agents/mcp-tools/inline-assets.ts
|
|
23053
|
-
import { existsSync as
|
|
23356
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
|
|
23054
23357
|
import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
|
|
23055
23358
|
function isNonLocal(ref) {
|
|
23056
23359
|
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
@@ -23078,7 +23381,7 @@ function inlineLocalAssets(html, options) {
|
|
|
23078
23381
|
const abs = isAbsolute6(relPath) ? relPath : resolve16(baseDir, relPath);
|
|
23079
23382
|
let bytes;
|
|
23080
23383
|
try {
|
|
23081
|
-
if (!
|
|
23384
|
+
if (!existsSync25(abs) || !statSync14(abs).isFile()) {
|
|
23082
23385
|
unresolved.push(ref);
|
|
23083
23386
|
cache.set(ref, null);
|
|
23084
23387
|
return null;
|
|
@@ -23103,7 +23406,7 @@ function inlineLocalAssets(html, options) {
|
|
|
23103
23406
|
let uri;
|
|
23104
23407
|
try {
|
|
23105
23408
|
const mime = MIME_BY_EXT[extname2(abs).toLowerCase()] ?? "application/octet-stream";
|
|
23106
|
-
uri = `data:${mime};base64,${
|
|
23409
|
+
uri = `data:${mime};base64,${readFileSync22(abs).toString("base64")}`;
|
|
23107
23410
|
} catch {
|
|
23108
23411
|
unresolved.push(ref);
|
|
23109
23412
|
cache.set(ref, null);
|
|
@@ -23661,18 +23964,133 @@ var init_task = __esm(async () => {
|
|
|
23661
23964
|
};
|
|
23662
23965
|
});
|
|
23663
23966
|
|
|
23967
|
+
// ../../packages/core/src/mcp/factories/decision.ts
|
|
23968
|
+
var exports_decision = {};
|
|
23969
|
+
__export(exports_decision, {
|
|
23970
|
+
defaultDecisionMcpHost: () => defaultDecisionMcpHost,
|
|
23971
|
+
createDecisionMcpServer: () => createDecisionMcpServer
|
|
23972
|
+
});
|
|
23973
|
+
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23974
|
+
import { z as z8 } from "zod";
|
|
23975
|
+
function createDecisionMcpServer(context, host2 = defaultDecisionMcpHost) {
|
|
23976
|
+
const scopeKey = context.topic ? decisionScopeKey({ topicId: context.topicId, session: context.topic }) : "";
|
|
23977
|
+
const server = new McpServer2({ name: "decision", version: "1.0.0" });
|
|
23978
|
+
const requireContext = () => !context.userId || !scopeKey ? mcpError("Error: missing userId/topic context.") : null;
|
|
23979
|
+
const statusEnum = z8.enum(DECISION_STATUS_VALUES);
|
|
23980
|
+
server.tool("decision_create", "Record one or more durable decisions in this topic. Include the rationale and causal predecessors.", {
|
|
23981
|
+
decisions: z8.array(z8.object({
|
|
23982
|
+
action: z8.string().min(1).describe("Concise statement of what was decided"),
|
|
23983
|
+
reasoning: z8.string().min(1).describe("Why this choice was made"),
|
|
23984
|
+
status: statusEnum.optional().describe("Defaults to accepted"),
|
|
23985
|
+
caused_by: z8.array(z8.string()).optional().describe("Upstream decision ids")
|
|
23986
|
+
})).min(1)
|
|
23987
|
+
}, async ({ decisions: inputs }) => {
|
|
23988
|
+
const guard = requireContext();
|
|
23989
|
+
if (guard)
|
|
23990
|
+
return guard;
|
|
23991
|
+
try {
|
|
23992
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
23993
|
+
const { decisions, created } = createDecisions(current3, inputs.map((input) => ({
|
|
23994
|
+
action: input.action,
|
|
23995
|
+
reasoning: input.reasoning,
|
|
23996
|
+
status: input.status,
|
|
23997
|
+
causedBy: input.caused_by,
|
|
23998
|
+
agent: context.agent,
|
|
23999
|
+
model: context.model
|
|
24000
|
+
})));
|
|
24001
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
24002
|
+
return mcpOk(`${created.length} decision(s) recorded (${created.map((item) => `#${item.id}`).join(", ")})
|
|
24003
|
+
|
|
24004
|
+
${renderDecisionList(decisions)}`);
|
|
24005
|
+
} catch (error) {
|
|
24006
|
+
return mcpError(`decision_create failed: ${errMsg(error)}`);
|
|
24007
|
+
}
|
|
24008
|
+
});
|
|
24009
|
+
server.tool("decision_update", "Update decisions or their causal links in this topic.", {
|
|
24010
|
+
updates: z8.array(z8.object({
|
|
24011
|
+
id: z8.string(),
|
|
24012
|
+
action: z8.string().min(1).optional(),
|
|
24013
|
+
reasoning: z8.string().min(1).optional(),
|
|
24014
|
+
status: statusEnum.optional(),
|
|
24015
|
+
caused_by: z8.array(z8.string()).optional().describe("Replacement upstream decision ids")
|
|
24016
|
+
})).min(1)
|
|
24017
|
+
}, async ({ updates }) => {
|
|
24018
|
+
const guard = requireContext();
|
|
24019
|
+
if (guard)
|
|
24020
|
+
return guard;
|
|
24021
|
+
try {
|
|
24022
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
24023
|
+
const { decisions, missing } = updateDecisions(current3, updates.map((update) => ({
|
|
24024
|
+
...update,
|
|
24025
|
+
causedBy: update.caused_by
|
|
24026
|
+
})));
|
|
24027
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
24028
|
+
const warning = missing.length ? `
|
|
24029
|
+
Missing ids ignored: ${missing.join(", ")}` : "";
|
|
24030
|
+
return mcpOk(`${renderDecisionList(decisions)}${warning}`);
|
|
24031
|
+
} catch (error) {
|
|
24032
|
+
return mcpError(`decision_update failed: ${errMsg(error)}`);
|
|
24033
|
+
}
|
|
24034
|
+
});
|
|
24035
|
+
server.tool("decision_list", "Read this topic's decision graph as a concise list.", {}, async () => {
|
|
24036
|
+
const guard = requireContext();
|
|
24037
|
+
if (guard)
|
|
24038
|
+
return guard;
|
|
24039
|
+
try {
|
|
24040
|
+
return mcpOk(renderDecisionList(host2.readDecisions(context.userId, scopeKey)));
|
|
24041
|
+
} catch (error) {
|
|
24042
|
+
return mcpError(`decision_list failed: ${errMsg(error)}`);
|
|
24043
|
+
}
|
|
24044
|
+
});
|
|
24045
|
+
server.tool("decision_get", "Read one topic decision as JSON.", { id: z8.string() }, async ({ id }) => {
|
|
24046
|
+
const guard = requireContext();
|
|
24047
|
+
if (guard)
|
|
24048
|
+
return guard;
|
|
24049
|
+
try {
|
|
24050
|
+
const decision = host2.readDecisions(context.userId, scopeKey).find((item) => item.id === id);
|
|
24051
|
+
return decision ? mcpOk(JSON.stringify(decision, null, 2)) : mcpError(`Decision #${id} not found.`);
|
|
24052
|
+
} catch (error) {
|
|
24053
|
+
return mcpError(`decision_get failed: ${errMsg(error)}`);
|
|
24054
|
+
}
|
|
24055
|
+
});
|
|
24056
|
+
server.tool("decision_delete", "Delete decisions. Downstream causal references to deleted decisions are removed.", { ids: z8.array(z8.string()).optional(), all: z8.boolean().optional() }, async ({ ids, all }) => {
|
|
24057
|
+
const guard = requireContext();
|
|
24058
|
+
if (guard)
|
|
24059
|
+
return guard;
|
|
24060
|
+
if (!all && (!ids || ids.length === 0))
|
|
24061
|
+
return mcpError("Provide ids or all=true.");
|
|
24062
|
+
try {
|
|
24063
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
24064
|
+
const { decisions, removed } = deleteDecisions(current3, { ids, all });
|
|
24065
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
24066
|
+
return mcpOk(`${removed} decision(s) deleted
|
|
24067
|
+
|
|
24068
|
+
${renderDecisionList(decisions)}`);
|
|
24069
|
+
} catch (error) {
|
|
24070
|
+
return mcpError(`decision_delete failed: ${errMsg(error)}`);
|
|
24071
|
+
}
|
|
24072
|
+
});
|
|
24073
|
+
return server;
|
|
24074
|
+
}
|
|
24075
|
+
var defaultDecisionMcpHost;
|
|
24076
|
+
var init_decision = __esm(async () => {
|
|
24077
|
+
await init_decisions();
|
|
24078
|
+
init_mcp_helpers();
|
|
24079
|
+
defaultDecisionMcpHost = { readDecisions, writeDecisions };
|
|
24080
|
+
});
|
|
24081
|
+
|
|
23664
24082
|
// ../../packages/core/src/mcp/factories/token-stats.ts
|
|
23665
24083
|
var exports_token_stats2 = {};
|
|
23666
24084
|
__export(exports_token_stats2, {
|
|
23667
24085
|
defaultTokenStatsMcpHost: () => defaultTokenStatsMcpHost,
|
|
23668
24086
|
createTokenStatsMcpServer: () => createTokenStatsMcpServer
|
|
23669
24087
|
});
|
|
23670
|
-
import { McpServer as
|
|
23671
|
-
import { z as
|
|
24088
|
+
import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24089
|
+
import { z as z9 } from "zod";
|
|
23672
24090
|
function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
23673
24091
|
if (!context.userId)
|
|
23674
24092
|
throw new Error("token-stats MCP requires userId");
|
|
23675
|
-
const server = new
|
|
24093
|
+
const server = new McpServer3({ name: "token-stats", version: "1.0.0" });
|
|
23676
24094
|
server.tool("get_usage_stats", [
|
|
23677
24095
|
"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.",
|
|
23678
24096
|
"",
|
|
@@ -23684,9 +24102,9 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23684
24102
|
"groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
|
|
23685
24103
|
].join(`
|
|
23686
24104
|
`), {
|
|
23687
|
-
from:
|
|
23688
|
-
to:
|
|
23689
|
-
groupBy:
|
|
24105
|
+
from: z9.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
|
|
24106
|
+
to: z9.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
|
|
24107
|
+
groupBy: z9.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
|
|
23690
24108
|
}, async ({ from, to, groupBy = "hour" }) => {
|
|
23691
24109
|
const snapshot = host2.getStats(context.userId, from, to);
|
|
23692
24110
|
const {
|
|
@@ -23777,7 +24195,7 @@ __export(exports_system_health, {
|
|
|
23777
24195
|
import { execFile as execFile2 } from "child_process";
|
|
23778
24196
|
import { cpus, freemem, loadavg, totalmem } from "os";
|
|
23779
24197
|
import { promisify as promisify2 } from "util";
|
|
23780
|
-
import { McpServer as
|
|
24198
|
+
import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23781
24199
|
function fmtBytes(bytes) {
|
|
23782
24200
|
const gb = bytes / 1024 ** 3;
|
|
23783
24201
|
return gb >= 1 ? `${gb.toFixed(1)}GB` : `${(bytes / 1024 ** 2).toFixed(0)}MB`;
|
|
@@ -23846,7 +24264,7 @@ async function getDisk() {
|
|
|
23846
24264
|
}
|
|
23847
24265
|
}
|
|
23848
24266
|
function createSystemHealthMcpServer(host2 = defaultSystemHealthMcpHost) {
|
|
23849
|
-
const server = new
|
|
24267
|
+
const server = new McpServer4({ name: "system-health", version: "1.0.0" });
|
|
23850
24268
|
server.tool("get_system_health", [
|
|
23851
24269
|
"\uC2DC\uC2A4\uD15C \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
23852
24270
|
"- CPU \uBD80\uD558 (load average 1/5/15\uBD84)",
|
|
@@ -23911,10 +24329,10 @@ __export(exports_agent_health, {
|
|
|
23911
24329
|
createAgentHealthMcpServer: () => createAgentHealthMcpServer
|
|
23912
24330
|
});
|
|
23913
24331
|
import { spawn as spawn6 } from "child_process";
|
|
23914
|
-
import { existsSync as
|
|
23915
|
-
import { join as
|
|
23916
|
-
import { McpServer as
|
|
23917
|
-
import { z as
|
|
24332
|
+
import { existsSync as existsSync26, readdirSync as readdirSync9 } from "fs";
|
|
24333
|
+
import { join as join35 } from "path";
|
|
24334
|
+
import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24335
|
+
import { z as z10 } from "zod";
|
|
23918
24336
|
function signalProcessTree2(child, signal) {
|
|
23919
24337
|
try {
|
|
23920
24338
|
if (child.pid && process.platform !== "win32")
|
|
@@ -24109,7 +24527,7 @@ async function withCheckSlot(operation) {
|
|
|
24109
24527
|
}
|
|
24110
24528
|
}
|
|
24111
24529
|
function createAgentHealthMcpServer(context) {
|
|
24112
|
-
const server = new
|
|
24530
|
+
const server = new McpServer5({ name: "agent-health", version: "1.0.0" });
|
|
24113
24531
|
const abortController = new AbortController;
|
|
24114
24532
|
const originalClose = server.close.bind(server);
|
|
24115
24533
|
server.close = async () => {
|
|
@@ -24117,9 +24535,9 @@ function createAgentHealthMcpServer(context) {
|
|
|
24117
24535
|
await originalClose();
|
|
24118
24536
|
};
|
|
24119
24537
|
server.tool("check_agent", "\uC2E4\uC81C API \uD638\uCD9C\uB85C \uD2B9\uC815 agent+model \uC870\uD569\uC774 \uC815\uC0C1 \uB3D9\uC791\uD558\uB294\uC9C0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", {
|
|
24120
|
-
agent:
|
|
24121
|
-
model:
|
|
24122
|
-
timeoutMs:
|
|
24538
|
+
agent: z10.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
|
|
24539
|
+
model: z10.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
|
|
24540
|
+
timeoutMs: z10.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
|
|
24123
24541
|
}, async ({ agent, model, timeoutMs }) => {
|
|
24124
24542
|
const entry = AGENTS.find((candidate) => candidate.kind === agent);
|
|
24125
24543
|
if (!entry)
|
|
@@ -24135,7 +24553,7 @@ function createAgentHealthMcpServer(context) {
|
|
|
24135
24553
|
latency: ${result.latency}ms
|
|
24136
24554
|
error: ${result.error}`);
|
|
24137
24555
|
});
|
|
24138
|
-
server.tool("check_all", "\uBAA8\uB4E0 agent (claude, codex, maestro)\uB97C \uAE30\uBCF8 \uBAA8\uB378\uB85C \uB3D9\uC2DC\uC5D0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", { timeoutMs:
|
|
24556
|
+
server.tool("check_all", "\uBAA8\uB4E0 agent (claude, codex, maestro)\uB97C \uAE30\uBCF8 \uBAA8\uB378\uB85C \uB3D9\uC2DC\uC5D0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", { timeoutMs: z10.number().default(30000).describe("agent\uBCC4 \uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08") }, async ({ timeoutMs }) => {
|
|
24139
24557
|
const userKey = context.userId ?? "unscoped";
|
|
24140
24558
|
if (activeCheckAllUsers.has(userKey)) {
|
|
24141
24559
|
return mcpOk("FAIL check_all is already running for this user");
|
|
@@ -24156,8 +24574,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24156
24574
|
server.tool("list_active_queries", "\uD604\uC7AC \uC0AC\uC6A9\uC790 \uBC94\uC704\uC5D0\uC11C \uC2E4\uD589 \uC911\uC778 \uD1A0\uD53D \uCFFC\uB9AC \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.", {}, async () => {
|
|
24157
24575
|
if (!context.userId)
|
|
24158
24576
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
|
|
24159
|
-
const stateDir =
|
|
24160
|
-
if (!
|
|
24577
|
+
const stateDir = join35(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24578
|
+
if (!existsSync26(stateDir))
|
|
24161
24579
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
|
|
24162
24580
|
let files;
|
|
24163
24581
|
try {
|
|
@@ -24169,7 +24587,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24169
24587
|
const entries = files.flatMap((file) => {
|
|
24170
24588
|
if (!file.endsWith(".json"))
|
|
24171
24589
|
return [];
|
|
24172
|
-
const state = readJsonFile(
|
|
24590
|
+
const state = readJsonFile(join35(stateDir, file));
|
|
24173
24591
|
if (!state)
|
|
24174
24592
|
return [];
|
|
24175
24593
|
const sinceMs = new Date(state.since).getTime();
|
|
@@ -24245,37 +24663,37 @@ var exports_session_comm = {};
|
|
|
24245
24663
|
__export(exports_session_comm, {
|
|
24246
24664
|
createSessionCommMcpServer: () => createSessionCommMcpServer
|
|
24247
24665
|
});
|
|
24248
|
-
import { McpServer as
|
|
24249
|
-
import { z as
|
|
24666
|
+
import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24667
|
+
import { z as z11 } from "zod";
|
|
24250
24668
|
function createSessionCommMcpServer(context, host2, options = {}) {
|
|
24251
|
-
const server = new
|
|
24669
|
+
const server = new McpServer6({ name: "session-comm", version: "2.0.0" });
|
|
24252
24670
|
const required = options.requiredMcpServers?.join(", ") || "none";
|
|
24253
24671
|
const optional = options.optionalMcpServers?.join(", ") || "none";
|
|
24254
24672
|
const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
|
|
24255
24673
|
server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host2.listSessions(context));
|
|
24256
24674
|
server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
|
|
24257
|
-
enabled:
|
|
24675
|
+
enabled: z11.array(z11.string()).nullable().optional()
|
|
24258
24676
|
}, async ({ enabled }) => host2.configureMcp(context, enabled));
|
|
24259
24677
|
server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host2.getMcpConfig(context));
|
|
24260
24678
|
if (host2.getBrowserProfile) {
|
|
24261
24679
|
server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host2.getBrowserProfile(context));
|
|
24262
24680
|
}
|
|
24263
24681
|
if (host2.setBrowserProfile) {
|
|
24264
|
-
server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile:
|
|
24682
|
+
server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z11.string() }, async ({ profile }) => host2.setBrowserProfile(context, profile));
|
|
24265
24683
|
}
|
|
24266
24684
|
if (host2.deleteBrowserProfile) {
|
|
24267
|
-
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:
|
|
24685
|
+
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: z11.string() }, async ({ profile }) => host2.deleteBrowserProfile(context, profile));
|
|
24268
24686
|
}
|
|
24269
24687
|
server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host2.peekSession(context));
|
|
24270
|
-
server.tool("set_description", "Set the current session description used as a routing hint.", { description:
|
|
24688
|
+
server.tool("set_description", "Set the current session description used as a routing hint.", { description: z11.string() }, async ({ description }) => host2.setDescription(context, description));
|
|
24271
24689
|
if (!context.replyOnly) {
|
|
24272
24690
|
if (!subagentIdentity.restricted) {
|
|
24273
|
-
server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to:
|
|
24691
|
+
server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z11.string(), message: z11.string() }, async (input) => host2.askSession(context, input));
|
|
24274
24692
|
}
|
|
24275
24693
|
if (!subagentIdentity.restricted) {
|
|
24276
|
-
server.tool("abort_session", "Abort the active query in another session.", { to:
|
|
24694
|
+
server.tool("abort_session", "Abort the active query in another session.", { to: z11.string() }, async ({ to }) => host2.abortSession(context, to));
|
|
24277
24695
|
}
|
|
24278
|
-
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to:
|
|
24696
|
+
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z11.string(), message: z11.string() }, async (input) => {
|
|
24279
24697
|
if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
|
|
24280
24698
|
return {
|
|
24281
24699
|
content: [
|
|
@@ -24402,7 +24820,7 @@ var exports_default_host = {};
|
|
|
24402
24820
|
__export(exports_default_host, {
|
|
24403
24821
|
createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
|
|
24404
24822
|
});
|
|
24405
|
-
import { basename as basename6, join as
|
|
24823
|
+
import { basename as basename6, join as join36 } from "path";
|
|
24406
24824
|
function ok2(text2) {
|
|
24407
24825
|
return { content: [{ type: "text", text: text2 }] };
|
|
24408
24826
|
}
|
|
@@ -24449,10 +24867,10 @@ function remoteTarget(context, to) {
|
|
|
24449
24867
|
return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
|
|
24450
24868
|
}
|
|
24451
24869
|
function activeQuery(context, topicId, title) {
|
|
24452
|
-
const dir =
|
|
24453
|
-
const candidates = [
|
|
24870
|
+
const dir = join36(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24871
|
+
const candidates = [join36(dir, `${sanitizeId(topicId)}.json`)];
|
|
24454
24872
|
if (title && basename6(title) === title && title !== "." && title !== "..") {
|
|
24455
|
-
candidates.push(
|
|
24873
|
+
candidates.push(join36(dir, `${title}.json`));
|
|
24456
24874
|
}
|
|
24457
24875
|
for (const path of candidates) {
|
|
24458
24876
|
const state = readJsonFile(path);
|
|
@@ -25028,10 +25446,10 @@ var exports_vault = {};
|
|
|
25028
25446
|
__export(exports_vault, {
|
|
25029
25447
|
createVaultMcpServer: () => createVaultMcpServer
|
|
25030
25448
|
});
|
|
25031
|
-
import { McpServer as
|
|
25032
|
-
import { z as
|
|
25449
|
+
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25450
|
+
import { z as z12 } from "zod";
|
|
25033
25451
|
function createVaultMcpServer(context, host2, executors = {}) {
|
|
25034
|
-
const server = new
|
|
25452
|
+
const server = new McpServer7({ name: "vault", version: "1.0.0" });
|
|
25035
25453
|
const run = executors.run ?? executeVaultRun;
|
|
25036
25454
|
const http = executors.http ?? executeVaultHttpRequest;
|
|
25037
25455
|
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.", {}, () => {
|
|
@@ -25047,9 +25465,9 @@ ${lines.join(`
|
|
|
25047
25465
|
});
|
|
25048
25466
|
if (!context.listOnly && !context.httpOnly) {
|
|
25049
25467
|
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.", {
|
|
25050
|
-
command:
|
|
25051
|
-
timeout_ms:
|
|
25052
|
-
max_output_bytes:
|
|
25468
|
+
command: z12.string().min(1).max(64 * 1024),
|
|
25469
|
+
timeout_ms: z12.number().int().min(1000).max(600000).optional(),
|
|
25470
|
+
max_output_bytes: z12.number().int().min(1024).max(2 * 1024 * 1024).optional()
|
|
25053
25471
|
}, async ({ command, timeout_ms, max_output_bytes }) => {
|
|
25054
25472
|
if (!context.userId)
|
|
25055
25473
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -25064,12 +25482,12 @@ ${lines.join(`
|
|
|
25064
25482
|
}
|
|
25065
25483
|
if (!context.listOnly)
|
|
25066
25484
|
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.", {
|
|
25067
|
-
method:
|
|
25068
|
-
url:
|
|
25069
|
-
headers:
|
|
25070
|
-
body:
|
|
25071
|
-
timeout_ms:
|
|
25072
|
-
max_response_bytes:
|
|
25485
|
+
method: z12.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
25486
|
+
url: z12.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
|
|
25487
|
+
headers: z12.record(z12.string(), z12.string()).optional(),
|
|
25488
|
+
body: z12.string().optional(),
|
|
25489
|
+
timeout_ms: z12.number().int().min(1000).max(120000).optional(),
|
|
25490
|
+
max_response_bytes: z12.number().int().min(1024).max(1024 * 1024).optional()
|
|
25073
25491
|
}, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
|
|
25074
25492
|
if (!context.userId)
|
|
25075
25493
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -25092,7 +25510,7 @@ var init_vault2 = __esm(() => {
|
|
|
25092
25510
|
});
|
|
25093
25511
|
|
|
25094
25512
|
// ../../packages/core/src/mcp/wiki-search-index.ts
|
|
25095
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
25513
|
+
import { chmodSync as chmodSync5, existsSync as existsSync27, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
|
|
25096
25514
|
function documentId(source) {
|
|
25097
25515
|
return `${source.kind}:${source.key}`;
|
|
25098
25516
|
}
|
|
@@ -25176,7 +25594,7 @@ class WikiSearchIndex {
|
|
|
25176
25594
|
continue;
|
|
25177
25595
|
}
|
|
25178
25596
|
try {
|
|
25179
|
-
const text2 =
|
|
25597
|
+
const text2 = readFileSync23(source.path, "utf-8");
|
|
25180
25598
|
changed.push({ source, document: prepare(source, text2), added: !stored });
|
|
25181
25599
|
} catch {}
|
|
25182
25600
|
}
|
|
@@ -25294,7 +25712,7 @@ class WikiSearchIndex {
|
|
|
25294
25712
|
}
|
|
25295
25713
|
function resetCorruptWikiSearchIndex(path) {
|
|
25296
25714
|
for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
|
|
25297
|
-
if (!
|
|
25715
|
+
if (!existsSync27(candidate))
|
|
25298
25716
|
continue;
|
|
25299
25717
|
unlinkSync18(candidate);
|
|
25300
25718
|
}
|
|
@@ -25314,17 +25732,17 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
|
25314
25732
|
import { randomUUID as randomUUID21 } from "crypto";
|
|
25315
25733
|
import {
|
|
25316
25734
|
closeSync as closeSync4,
|
|
25317
|
-
existsSync as
|
|
25318
|
-
mkdirSync as
|
|
25735
|
+
existsSync as existsSync28,
|
|
25736
|
+
mkdirSync as mkdirSync25,
|
|
25319
25737
|
openSync as openSync4,
|
|
25320
25738
|
readdirSync as readdirSync10,
|
|
25321
|
-
readFileSync as
|
|
25322
|
-
renameSync as
|
|
25739
|
+
readFileSync as readFileSync24,
|
|
25740
|
+
renameSync as renameSync12,
|
|
25323
25741
|
statSync as statSync15,
|
|
25324
25742
|
unlinkSync as unlinkSync19,
|
|
25325
|
-
writeFileSync as
|
|
25743
|
+
writeFileSync as writeFileSync19
|
|
25326
25744
|
} from "fs";
|
|
25327
|
-
import { basename as basename7, dirname as
|
|
25745
|
+
import { basename as basename7, dirname as dirname17, join as join37, relative as relative2, resolve as resolve17 } from "path";
|
|
25328
25746
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
25329
25747
|
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
25330
25748
|
import {
|
|
@@ -25350,7 +25768,7 @@ function runtime() {
|
|
|
25350
25768
|
return current3;
|
|
25351
25769
|
}
|
|
25352
25770
|
function ensureDir(dir) {
|
|
25353
|
-
|
|
25771
|
+
mkdirSync25(dir, { recursive: true });
|
|
25354
25772
|
}
|
|
25355
25773
|
function acquireRecoveryGuard(lockPath) {
|
|
25356
25774
|
const guardPath = `${lockPath}.reclaim`;
|
|
@@ -25360,7 +25778,7 @@ function acquireRecoveryGuard(lockPath) {
|
|
|
25360
25778
|
try {
|
|
25361
25779
|
const fd = openSync4(guardPath, "wx");
|
|
25362
25780
|
try {
|
|
25363
|
-
|
|
25781
|
+
writeFileSync19(fd, ownerToken, "utf-8");
|
|
25364
25782
|
} catch (error2) {
|
|
25365
25783
|
closeSync4(fd);
|
|
25366
25784
|
try {
|
|
@@ -25371,7 +25789,7 @@ function acquireRecoveryGuard(lockPath) {
|
|
|
25371
25789
|
return () => {
|
|
25372
25790
|
closeSync4(fd);
|
|
25373
25791
|
try {
|
|
25374
|
-
if (
|
|
25792
|
+
if (readFileSync24(guardPath, "utf-8") === ownerToken)
|
|
25375
25793
|
unlinkSync19(guardPath);
|
|
25376
25794
|
} catch {}
|
|
25377
25795
|
};
|
|
@@ -25393,7 +25811,7 @@ function acquireFileLock(path) {
|
|
|
25393
25811
|
try {
|
|
25394
25812
|
const fd = openSync4(lockPath, "wx");
|
|
25395
25813
|
try {
|
|
25396
|
-
|
|
25814
|
+
writeFileSync19(fd, ownerToken, "utf-8");
|
|
25397
25815
|
} catch (error2) {
|
|
25398
25816
|
closeSync4(fd);
|
|
25399
25817
|
try {
|
|
@@ -25405,7 +25823,7 @@ function acquireFileLock(path) {
|
|
|
25405
25823
|
closeSync4(fd);
|
|
25406
25824
|
const releaseGuard = acquireRecoveryGuard(lockPath);
|
|
25407
25825
|
try {
|
|
25408
|
-
if (
|
|
25826
|
+
if (readFileSync24(lockPath, "utf-8") === ownerToken)
|
|
25409
25827
|
unlinkSync19(lockPath);
|
|
25410
25828
|
} catch {} finally {
|
|
25411
25829
|
releaseGuard();
|
|
@@ -25417,8 +25835,8 @@ function acquireFileLock(path) {
|
|
|
25417
25835
|
const releaseGuard = acquireRecoveryGuard(lockPath);
|
|
25418
25836
|
try {
|
|
25419
25837
|
try {
|
|
25420
|
-
const staleToken =
|
|
25421
|
-
if (Date.now() - statSync15(lockPath).mtimeMs > FILE_LOCK_STALE_MS &&
|
|
25838
|
+
const staleToken = readFileSync24(lockPath, "utf-8");
|
|
25839
|
+
if (Date.now() - statSync15(lockPath).mtimeMs > FILE_LOCK_STALE_MS && readFileSync24(lockPath, "utf-8") === staleToken) {
|
|
25422
25840
|
unlinkSync19(lockPath);
|
|
25423
25841
|
continue;
|
|
25424
25842
|
}
|
|
@@ -25437,9 +25855,9 @@ function acquireFileLock(path) {
|
|
|
25437
25855
|
}
|
|
25438
25856
|
function atomicWriteFile(path, content) {
|
|
25439
25857
|
const temporaryPath = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
25440
|
-
|
|
25858
|
+
writeFileSync19(temporaryPath, content, "utf-8");
|
|
25441
25859
|
try {
|
|
25442
|
-
|
|
25860
|
+
renameSync12(temporaryPath, path);
|
|
25443
25861
|
} catch (error2) {
|
|
25444
25862
|
try {
|
|
25445
25863
|
unlinkSync19(temporaryPath);
|
|
@@ -25628,7 +26046,7 @@ function normalizeWikiSlug(raw) {
|
|
|
25628
26046
|
function parseWikiIndex(path) {
|
|
25629
26047
|
let text2;
|
|
25630
26048
|
try {
|
|
25631
|
-
text2 =
|
|
26049
|
+
text2 = readFileSync24(path, "utf-8");
|
|
25632
26050
|
} catch {
|
|
25633
26051
|
return [];
|
|
25634
26052
|
}
|
|
@@ -25774,15 +26192,15 @@ function wikiQuery(args) {
|
|
|
25774
26192
|
try {
|
|
25775
26193
|
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
25776
26194
|
if (entry.isDirectory()) {
|
|
25777
|
-
const sub =
|
|
26195
|
+
const sub = join37(dir, entry.name);
|
|
25778
26196
|
if (label === "articles" || label === "skills") {
|
|
25779
26197
|
try {
|
|
25780
26198
|
for (const f of readdirSync10(sub, { withFileTypes: true })) {
|
|
25781
26199
|
if (!f.isFile() || !f.name.endsWith(".md"))
|
|
25782
26200
|
continue;
|
|
25783
|
-
const fp =
|
|
26201
|
+
const fp = join37(sub, f.name);
|
|
25784
26202
|
try {
|
|
25785
|
-
const text2 =
|
|
26203
|
+
const text2 = readFileSync24(fp, "utf-8");
|
|
25786
26204
|
const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
|
|
25787
26205
|
scored.push({
|
|
25788
26206
|
kind: label === "articles" ? "article" : "skill",
|
|
@@ -25801,9 +26219,9 @@ function wikiQuery(args) {
|
|
|
25801
26219
|
if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
|
|
25802
26220
|
continue;
|
|
25803
26221
|
}
|
|
25804
|
-
const fp =
|
|
26222
|
+
const fp = join37(dir, entry.name);
|
|
25805
26223
|
try {
|
|
25806
|
-
const text2 =
|
|
26224
|
+
const text2 = readFileSync24(fp, "utf-8");
|
|
25807
26225
|
const key = entry.name.replace(/\.md$/i, "");
|
|
25808
26226
|
const resultKind = label === "articles" ? "article" : label === "topic" ? "topic" : label === "summaries" ? "summary" : "skill";
|
|
25809
26227
|
scored.push({
|
|
@@ -26087,7 +26505,7 @@ function wikiTopicBrief(args) {
|
|
|
26087
26505
|
const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
26088
26506
|
const filePath = resolve17(runtime().topicsDir, `${topicKey}.md`);
|
|
26089
26507
|
try {
|
|
26090
|
-
const content =
|
|
26508
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
26091
26509
|
return {
|
|
26092
26510
|
content: [{ type: "text", text: `# Topic Brief: ${topicKey}
|
|
26093
26511
|
|
|
@@ -26139,7 +26557,7 @@ Adopted topic memory: ${memoryKey}` }]
|
|
|
26139
26557
|
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
26140
26558
|
}
|
|
26141
26559
|
try {
|
|
26142
|
-
const content =
|
|
26560
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
26143
26561
|
return {
|
|
26144
26562
|
content: [{ type: "text", text: `# Wiki ${kind}: ${key}
|
|
26145
26563
|
|
|
@@ -26159,18 +26577,18 @@ function wikiLastConversation(args) {
|
|
|
26159
26577
|
const name = slugify(topicNameFrom(rawTopic));
|
|
26160
26578
|
const archiveDir = resolve17(runtime().archiveDir, name);
|
|
26161
26579
|
try {
|
|
26162
|
-
let files =
|
|
26580
|
+
let files = existsSync28(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26163
26581
|
if (files.length === 0) {
|
|
26164
|
-
files =
|
|
26582
|
+
files = existsSync28(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26165
26583
|
}
|
|
26166
26584
|
if (files.length === 0) {
|
|
26167
26585
|
return {
|
|
26168
26586
|
content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
|
|
26169
26587
|
};
|
|
26170
26588
|
}
|
|
26171
|
-
const actualPath = files[0].includes("/") ? files[0] : resolve17(
|
|
26589
|
+
const actualPath = files[0].includes("/") ? files[0] : resolve17(existsSync28(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
|
|
26172
26590
|
try {
|
|
26173
|
-
const raw =
|
|
26591
|
+
const raw = readFileSync24(actualPath, "utf-8");
|
|
26174
26592
|
const lines = raw.trim().split(`
|
|
26175
26593
|
`).filter(Boolean);
|
|
26176
26594
|
const turns2 = [];
|
|
@@ -26243,7 +26661,7 @@ function skillQuery(args) {
|
|
|
26243
26661
|
continue;
|
|
26244
26662
|
const skillFile = resolve17(runtime().skillsDir, entry.name, "skill.md");
|
|
26245
26663
|
try {
|
|
26246
|
-
const text2 =
|
|
26664
|
+
const text2 = readFileSync24(skillFile, "utf-8");
|
|
26247
26665
|
const lower = text2.toLowerCase();
|
|
26248
26666
|
let score = 0;
|
|
26249
26667
|
for (const w of query2.split(/\s+/).filter(Boolean)) {
|
|
@@ -26314,10 +26732,10 @@ ${fresh.join(`
|
|
|
26314
26732
|
const skillPath = resolve17(skillDir, "skill.md");
|
|
26315
26733
|
let finalContent = content;
|
|
26316
26734
|
try {
|
|
26317
|
-
const existing =
|
|
26735
|
+
const existing = readFileSync24(skillPath, "utf-8");
|
|
26318
26736
|
finalContent = mergeGotchas(content, extractGotchas(existing));
|
|
26319
26737
|
} catch {}
|
|
26320
|
-
|
|
26738
|
+
writeFileSync19(skillPath, finalContent, "utf-8");
|
|
26321
26739
|
return {
|
|
26322
26740
|
content: [
|
|
26323
26741
|
{
|
|
@@ -26336,7 +26754,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26336
26754
|
let counter = 1;
|
|
26337
26755
|
while (true) {
|
|
26338
26756
|
try {
|
|
26339
|
-
|
|
26757
|
+
writeFileSync19(resolve17(runtime().summariesDir, summaryName), content, {
|
|
26340
26758
|
encoding: "utf-8",
|
|
26341
26759
|
flag: "wx"
|
|
26342
26760
|
});
|
|
@@ -26354,7 +26772,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26354
26772
|
if (topicId && setTopicBrief2) {
|
|
26355
26773
|
try {
|
|
26356
26774
|
const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
|
|
26357
|
-
let briefMd =
|
|
26775
|
+
let briefMd = existsSync28(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
|
|
26358
26776
|
if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
|
|
26359
26777
|
const legacy = getTopicBrief2(topicId)?.briefMd;
|
|
26360
26778
|
if (legacy)
|
|
@@ -26374,9 +26792,9 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26374
26792
|
const topicId = runtime().topicId;
|
|
26375
26793
|
const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
|
|
26376
26794
|
const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
|
|
26377
|
-
ensureDir(
|
|
26378
|
-
const existed =
|
|
26379
|
-
|
|
26795
|
+
ensureDir(dirname17(briefPath));
|
|
26796
|
+
const existed = existsSync28(briefPath);
|
|
26797
|
+
writeFileSync19(briefPath, content, "utf-8");
|
|
26380
26798
|
const setTopicBrief2 = runtime().host.setTopicBrief;
|
|
26381
26799
|
let sqliteUpdated = false;
|
|
26382
26800
|
if (topicId && setTopicBrief2) {
|
|
@@ -26389,8 +26807,8 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26389
26807
|
}
|
|
26390
26808
|
function writeArticleDocument(slug, content) {
|
|
26391
26809
|
const articlePath = resolve17(runtime().articlesDir, `${slug}.md`);
|
|
26392
|
-
ensureDir(
|
|
26393
|
-
const existed =
|
|
26810
|
+
ensureDir(dirname17(articlePath));
|
|
26811
|
+
const existed = existsSync28(articlePath);
|
|
26394
26812
|
atomicWriteFile(articlePath, content);
|
|
26395
26813
|
return { existed };
|
|
26396
26814
|
}
|
|
@@ -26522,21 +26940,21 @@ function collectIndexableDocuments() {
|
|
|
26522
26940
|
}
|
|
26523
26941
|
for (const entry of entries) {
|
|
26524
26942
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
26525
|
-
add(kind, entry.name.replace(/\.md$/i, ""),
|
|
26943
|
+
add(kind, entry.name.replace(/\.md$/i, ""), join37(root, entry.name));
|
|
26526
26944
|
continue;
|
|
26527
26945
|
}
|
|
26528
26946
|
if (!entry.isDirectory() || !allowSubdirectories)
|
|
26529
26947
|
continue;
|
|
26530
26948
|
let nested;
|
|
26531
26949
|
try {
|
|
26532
|
-
nested = readdirSync10(
|
|
26950
|
+
nested = readdirSync10(join37(root, entry.name), { withFileTypes: true });
|
|
26533
26951
|
} catch {
|
|
26534
26952
|
continue;
|
|
26535
26953
|
}
|
|
26536
26954
|
for (const file of nested) {
|
|
26537
26955
|
if (!file.isFile() || !file.name.endsWith(".md"))
|
|
26538
26956
|
continue;
|
|
26539
|
-
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`,
|
|
26957
|
+
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join37(root, entry.name, file.name));
|
|
26540
26958
|
}
|
|
26541
26959
|
}
|
|
26542
26960
|
}
|
|
@@ -26593,12 +27011,12 @@ function indexRowOrPartialWrite(label, options) {
|
|
|
26593
27011
|
function upsertIndexRow(options) {
|
|
26594
27012
|
const { kind, slug, description, date, section } = options;
|
|
26595
27013
|
const indexPath = wikiIndexPath(kind);
|
|
26596
|
-
ensureDir(
|
|
27014
|
+
ensureDir(dirname17(indexPath));
|
|
26597
27015
|
const releaseLock = acquireFileLock(indexPath);
|
|
26598
27016
|
try {
|
|
26599
27017
|
let index;
|
|
26600
27018
|
try {
|
|
26601
|
-
index =
|
|
27019
|
+
index = readFileSync24(indexPath, "utf-8");
|
|
26602
27020
|
} catch {
|
|
26603
27021
|
index = "";
|
|
26604
27022
|
}
|
|
@@ -27037,15 +27455,15 @@ __export(exports_activity_log, {
|
|
|
27037
27455
|
readSentFilesForTopic: () => readSentFilesForTopic
|
|
27038
27456
|
});
|
|
27039
27457
|
import {
|
|
27040
|
-
existsSync as
|
|
27041
|
-
mkdirSync as
|
|
27458
|
+
existsSync as existsSync29,
|
|
27459
|
+
mkdirSync as mkdirSync26,
|
|
27042
27460
|
readdirSync as readdirSync11,
|
|
27043
|
-
renameSync as
|
|
27461
|
+
renameSync as renameSync13,
|
|
27044
27462
|
statSync as statSync16,
|
|
27045
27463
|
unlinkSync as unlinkSync20,
|
|
27046
|
-
writeFileSync as
|
|
27464
|
+
writeFileSync as writeFileSync20
|
|
27047
27465
|
} from "fs";
|
|
27048
|
-
import { basename as basename8, join as
|
|
27466
|
+
import { basename as basename8, join as join38 } from "path";
|
|
27049
27467
|
function startLogRotation() {
|
|
27050
27468
|
if (rotationTimer)
|
|
27051
27469
|
return;
|
|
@@ -27056,15 +27474,15 @@ function startLogRotation() {
|
|
|
27056
27474
|
function writeLog(entry) {
|
|
27057
27475
|
startLogRotation();
|
|
27058
27476
|
const logDir = resolveStorageLogDir();
|
|
27059
|
-
|
|
27477
|
+
mkdirSync26(logDir, { recursive: true });
|
|
27060
27478
|
const safeSession = sanitizeTopicName(entry.session);
|
|
27061
27479
|
const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
|
|
27062
|
-
const file =
|
|
27480
|
+
const file = join38(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
|
|
27063
27481
|
try {
|
|
27064
27482
|
const stat = statSync16(file);
|
|
27065
27483
|
if (stat.size >= MAX_FILE_SIZE) {
|
|
27066
27484
|
const rotated = file.replace(/\.jsonl$/, `.${Date.now()}.jsonl`);
|
|
27067
|
-
|
|
27485
|
+
renameSync13(file, rotated);
|
|
27068
27486
|
}
|
|
27069
27487
|
} catch {}
|
|
27070
27488
|
try {
|
|
@@ -27075,11 +27493,11 @@ function writeLog(entry) {
|
|
|
27075
27493
|
}
|
|
27076
27494
|
function rotateOldLogs() {
|
|
27077
27495
|
const logDir = resolveStorageLogDir();
|
|
27078
|
-
|
|
27496
|
+
mkdirSync26(logDir, { recursive: true });
|
|
27079
27497
|
try {
|
|
27080
27498
|
const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
27081
27499
|
try {
|
|
27082
|
-
const stat = statSync16(
|
|
27500
|
+
const stat = statSync16(join38(logDir, f));
|
|
27083
27501
|
return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
27084
27502
|
} catch {
|
|
27085
27503
|
return null;
|
|
@@ -27095,7 +27513,7 @@ function rotateOldLogs() {
|
|
|
27095
27513
|
if (freed >= toFree)
|
|
27096
27514
|
break;
|
|
27097
27515
|
try {
|
|
27098
|
-
unlinkSync20(
|
|
27516
|
+
unlinkSync20(join38(logDir, file.name));
|
|
27099
27517
|
freed += file.size;
|
|
27100
27518
|
logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
|
|
27101
27519
|
} catch (e) {
|
|
@@ -27107,9 +27525,9 @@ function rotateOldLogs() {
|
|
|
27107
27525
|
}
|
|
27108
27526
|
}
|
|
27109
27527
|
function writeSentFileLog(entry) {
|
|
27110
|
-
const dir =
|
|
27111
|
-
|
|
27112
|
-
const file =
|
|
27528
|
+
const dir = join38(resolveStorageUsersLogDir(), String(entry.userId));
|
|
27529
|
+
mkdirSync26(dir, { recursive: true });
|
|
27530
|
+
const file = join38(dir, "sent-files.jsonl");
|
|
27113
27531
|
try {
|
|
27114
27532
|
appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
|
|
27115
27533
|
} catch (e) {
|
|
@@ -27120,8 +27538,8 @@ function entryMatchesTopic(e, topicName) {
|
|
|
27120
27538
|
return e.topicName === topicName;
|
|
27121
27539
|
}
|
|
27122
27540
|
function readSentFilesForTopic(userId, topicName) {
|
|
27123
|
-
const file =
|
|
27124
|
-
if (!
|
|
27541
|
+
const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27542
|
+
if (!existsSync29(file))
|
|
27125
27543
|
return [];
|
|
27126
27544
|
try {
|
|
27127
27545
|
return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
|
|
@@ -27131,8 +27549,8 @@ function readSentFilesForTopic(userId, topicName) {
|
|
|
27131
27549
|
}
|
|
27132
27550
|
}
|
|
27133
27551
|
function removeSentFilesForTopic(userId, topicName) {
|
|
27134
|
-
const file =
|
|
27135
|
-
if (!
|
|
27552
|
+
const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27553
|
+
if (!existsSync29(file))
|
|
27136
27554
|
return;
|
|
27137
27555
|
try {
|
|
27138
27556
|
const remaining = readJsonlLines(file).filter((line) => {
|
|
@@ -27143,7 +27561,7 @@ function removeSentFilesForTopic(userId, topicName) {
|
|
|
27143
27561
|
}
|
|
27144
27562
|
});
|
|
27145
27563
|
if (remaining.length > 0) {
|
|
27146
|
-
|
|
27564
|
+
writeFileSync20(file, `${remaining.join(`
|
|
27147
27565
|
`)}
|
|
27148
27566
|
`);
|
|
27149
27567
|
} else {
|
|
@@ -27640,6 +28058,8 @@ __export(exports_storage_public, {
|
|
|
27640
28058
|
writeTasks: () => writeTasks,
|
|
27641
28059
|
writeSentFileLog: () => writeSentFileLog,
|
|
27642
28060
|
writeLog: () => writeLog,
|
|
28061
|
+
writeDecisions: () => writeDecisions,
|
|
28062
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
27643
28063
|
wikiSummaryStorageSlug: () => wikiSummaryStorageSlug,
|
|
27644
28064
|
wikiSummarySlug: () => wikiSummarySlug,
|
|
27645
28065
|
wikiSummaryNames: () => exports_wiki_summary_names,
|
|
@@ -27648,9 +28068,11 @@ __export(exports_storage_public, {
|
|
|
27648
28068
|
wiki: () => exports_wiki,
|
|
27649
28069
|
waitForRuntimeProcessLease: () => waitForRuntimeProcessLease,
|
|
27650
28070
|
waitForRequiredRuntimeProcessLease: () => waitForRequiredRuntimeProcessLease,
|
|
28071
|
+
validateDecisionGraph: () => validateDecisionGraph,
|
|
27651
28072
|
upsertTopic: () => upsertTopic,
|
|
27652
28073
|
updateTopicThreadId: () => updateTopicThreadId,
|
|
27653
28074
|
updateTasks: () => updateTasks,
|
|
28075
|
+
updateDecisions: () => updateDecisions,
|
|
27654
28076
|
updateApiMessageUsage: () => updateApiMessageUsage,
|
|
27655
28077
|
updateApiMessageText: () => updateApiMessageText,
|
|
27656
28078
|
updateApiMessageSubagentCard: () => updateApiMessageSubagentCard,
|
|
@@ -27699,6 +28121,7 @@ __export(exports_storage_public, {
|
|
|
27699
28121
|
replaceConversationStrict: () => replaceConversationStrict,
|
|
27700
28122
|
reparentTopicChildren: () => reparentTopicChildren,
|
|
27701
28123
|
renderTaskList: () => renderTaskList,
|
|
28124
|
+
renderDecisionList: () => renderDecisionList,
|
|
27702
28125
|
renameTopic: () => renameTopic,
|
|
27703
28126
|
removeTopic: () => removeTopic,
|
|
27704
28127
|
removeSentFilesForTopic: () => removeSentFilesForTopic,
|
|
@@ -27710,6 +28133,7 @@ __export(exports_storage_public, {
|
|
|
27710
28133
|
readTasks: () => readTasks,
|
|
27711
28134
|
readSentFilesForTopic: () => readSentFilesForTopic,
|
|
27712
28135
|
readRawConversation: () => readRawConversation,
|
|
28136
|
+
readDecisions: () => readDecisions,
|
|
27713
28137
|
readConversation: () => readConversation,
|
|
27714
28138
|
quarantineForeignAskUserGates: () => quarantineForeignAskUserGates,
|
|
27715
28139
|
quarantineAskUserGate: () => quarantineAskUserGate,
|
|
@@ -27771,6 +28195,8 @@ __export(exports_storage_public, {
|
|
|
27771
28195
|
getGlobalAiName: () => getGlobalAiName,
|
|
27772
28196
|
getForumTopicByName: () => getTopicByName2,
|
|
27773
28197
|
getDmSessionId: () => getDmSessionId,
|
|
28198
|
+
getDecisionGraphSvgPath: () => getDecisionGraphSvgPath,
|
|
28199
|
+
getDecisionFilePath: () => getDecisionFilePath,
|
|
27774
28200
|
getConversationPath: () => getConversationPath,
|
|
27775
28201
|
getCommunicateThreadId: () => getCommunicateThreadId,
|
|
27776
28202
|
getApiTopicConfig: () => getApiTopicConfig,
|
|
@@ -27794,12 +28220,16 @@ __export(exports_storage_public, {
|
|
|
27794
28220
|
deleteTasks: () => deleteTasks,
|
|
27795
28221
|
deletePendingAsksForTopic: () => deletePendingAsksForTopic,
|
|
27796
28222
|
deleteMessagesForTopic: () => deleteMessagesForTopic,
|
|
28223
|
+
deleteDecisions: () => deleteDecisions,
|
|
27797
28224
|
deleteApiTopicConfig: () => deleteApiTopicConfig,
|
|
27798
28225
|
defaultTopicSurface: () => defaultTopicSurface,
|
|
27799
28226
|
defaultSurfaceScope: () => defaultSurfaceScope,
|
|
28227
|
+
decisions: () => exports_decisions,
|
|
28228
|
+
decisionScopeKey: () => decisionScopeKey,
|
|
27800
28229
|
db: () => db2,
|
|
27801
28230
|
createTasks: () => createTasks,
|
|
27802
28231
|
createPendingAsk: () => createPendingAsk,
|
|
28232
|
+
createDecisions: () => createDecisions,
|
|
27803
28233
|
copyMessagesForTopic: () => copyMessagesForTopic,
|
|
27804
28234
|
copyMessageSnapshotToTopic: () => copyMessageSnapshotToTopic,
|
|
27805
28235
|
conversations: () => exports_conversations,
|
|
@@ -27838,6 +28268,7 @@ __export(exports_storage_public, {
|
|
|
27838
28268
|
PROCESS_LEASE_HEARTBEAT_MS: () => PROCESS_LEASE_HEARTBEAT_MS,
|
|
27839
28269
|
PENDING_ASK_TTL_MS: () => PENDING_ASK_TTL_MS,
|
|
27840
28270
|
DEFAULT_AI_NAME: () => DEFAULT_AI_NAME,
|
|
28271
|
+
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES,
|
|
27841
28272
|
ConversationLogDivergedError: () => ConversationLogDivergedError
|
|
27842
28273
|
});
|
|
27843
28274
|
var init_storage_public = __esm(async () => {
|
|
@@ -27857,6 +28288,8 @@ var init_storage_public = __esm(async () => {
|
|
|
27857
28288
|
init_ask_user_gates();
|
|
27858
28289
|
await init_conversations();
|
|
27859
28290
|
init_conversations();
|
|
28291
|
+
await init_decisions();
|
|
28292
|
+
init_decisions();
|
|
27860
28293
|
await init_forum();
|
|
27861
28294
|
await init_forum();
|
|
27862
28295
|
await init_runtime_process_leases();
|
|
@@ -27895,6 +28328,16 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
27895
28328
|
topicId: context.topicId
|
|
27896
28329
|
});
|
|
27897
28330
|
}
|
|
28331
|
+
case "decision": {
|
|
28332
|
+
const { createDecisionMcpServer: createDecisionMcpServer2 } = await init_decision().then(() => exports_decision);
|
|
28333
|
+
return createDecisionMcpServer2({
|
|
28334
|
+
userId: context.userId,
|
|
28335
|
+
topic: context.topicTitle,
|
|
28336
|
+
topicId: context.topicId,
|
|
28337
|
+
agent: context.agent,
|
|
28338
|
+
model: context.model
|
|
28339
|
+
});
|
|
28340
|
+
}
|
|
27898
28341
|
case "token-stats": {
|
|
27899
28342
|
const { createTokenStatsMcpServer: createTokenStatsMcpServer2 } = await init_token_stats2().then(() => exports_token_stats2);
|
|
27900
28343
|
return createTokenStatsMcpServer2({ userId: context.userId });
|
|
@@ -27979,6 +28422,7 @@ var ACTIVE_HOSTED_MCP_SURFACES;
|
|
|
27979
28422
|
var init_hosted_surfaces = __esm(() => {
|
|
27980
28423
|
ACTIVE_HOSTED_MCP_SURFACES = [
|
|
27981
28424
|
"task",
|
|
28425
|
+
"decision",
|
|
27982
28426
|
"token-stats",
|
|
27983
28427
|
"system-health",
|
|
27984
28428
|
"vault",
|
|
@@ -28098,9 +28542,9 @@ var init_sse_transport = () => {};
|
|
|
28098
28542
|
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
28099
28543
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
28100
28544
|
import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
|
|
28101
|
-
import { McpServer as
|
|
28545
|
+
import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28102
28546
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
28103
|
-
import { z as
|
|
28547
|
+
import { z as z13 } from "zod";
|
|
28104
28548
|
function requireTopicAccess(ctx) {
|
|
28105
28549
|
const topic = getTopic(ctx.topicId);
|
|
28106
28550
|
if (!topic)
|
|
@@ -28219,7 +28663,7 @@ function appendAutoContinue(ctx, field) {
|
|
|
28219
28663
|
});
|
|
28220
28664
|
}
|
|
28221
28665
|
function buildNegotiumMcpServer(ctx) {
|
|
28222
|
-
const server = new
|
|
28666
|
+
const server = new McpServer8({ name: RUNTIME_MCP_KEY, version: "1.0.0" });
|
|
28223
28667
|
if (ctx.visualTools === true) {
|
|
28224
28668
|
for (const def of visualToolDefinitions) {
|
|
28225
28669
|
const handler = ctx.peerBridge && (def.name === "show_html" || def.name === "show_mermaid") ? async () => textResult("Visual queued for ordered display on the canonical hub.") : def.handler;
|
|
@@ -28320,8 +28764,8 @@ function buildNegotiumMcpServer(ctx) {
|
|
|
28320
28764
|
}
|
|
28321
28765
|
}
|
|
28322
28766
|
if (ctx.fileDeliveryTools === true) {
|
|
28323
|
-
server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path:
|
|
28324
|
-
server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths:
|
|
28767
|
+
server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path: z13.string().describe("Absolute path to the file to send") }, async ({ file_path }) => deliverFile(ctx, file_path));
|
|
28768
|
+
server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths: z13.array(z13.string()).describe("Array of absolute file paths to send") }, async ({ file_paths }) => {
|
|
28325
28769
|
const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
|
|
28326
28770
|
const hasError = results.some((result) => ("isError" in result) && result.isError);
|
|
28327
28771
|
return {
|
|
@@ -28619,23 +29063,23 @@ function defaultManifestFile() {
|
|
|
28619
29063
|
var init_paths = () => {};
|
|
28620
29064
|
|
|
28621
29065
|
// ../../packages/mcp-host/src/spec.ts
|
|
28622
|
-
import { z as
|
|
29066
|
+
import { z as z14 } from "zod";
|
|
28623
29067
|
var KEY_PATTERN, portSchema, mcpServerSpecSchema;
|
|
28624
29068
|
var init_spec = __esm(() => {
|
|
28625
29069
|
KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
28626
|
-
portSchema =
|
|
28627
|
-
mcpServerSpecSchema =
|
|
28628
|
-
key:
|
|
28629
|
-
transport:
|
|
28630
|
-
command:
|
|
28631
|
-
args:
|
|
28632
|
-
env:
|
|
28633
|
-
cwd:
|
|
28634
|
-
portRange:
|
|
28635
|
-
scope:
|
|
28636
|
-
readyTimeoutMs:
|
|
28637
|
-
idleEvictMs:
|
|
28638
|
-
healthIntervalMs:
|
|
29070
|
+
portSchema = z14.number().int().min(1).max(65535);
|
|
29071
|
+
mcpServerSpecSchema = z14.object({
|
|
29072
|
+
key: z14.string().min(1).regex(KEY_PATTERN, "key may only contain [a-zA-Z0-9._-]").refine((k) => k !== "." && k !== "..", "key may not be a dot path"),
|
|
29073
|
+
transport: z14.enum(["stdio", "http"]),
|
|
29074
|
+
command: z14.string().min(1),
|
|
29075
|
+
args: z14.array(z14.string()).optional(),
|
|
29076
|
+
env: z14.record(z14.string(), z14.string()).optional(),
|
|
29077
|
+
cwd: z14.string().optional(),
|
|
29078
|
+
portRange: z14.object({ base: portSchema, max: portSchema }).optional(),
|
|
29079
|
+
scope: z14.enum(["node", "instance"]),
|
|
29080
|
+
readyTimeoutMs: z14.number().int().positive().optional(),
|
|
29081
|
+
idleEvictMs: z14.number().int().positive().optional(),
|
|
29082
|
+
healthIntervalMs: z14.number().int().positive().optional()
|
|
28639
29083
|
}).superRefine((spec, ctx) => {
|
|
28640
29084
|
if (spec.transport === "http") {
|
|
28641
29085
|
if (!spec.portRange) {
|
|
@@ -28656,9 +29100,9 @@ var init_spec = __esm(() => {
|
|
|
28656
29100
|
});
|
|
28657
29101
|
|
|
28658
29102
|
// ../../packages/mcp-host/src/manifest.ts
|
|
28659
|
-
import { existsSync as
|
|
28660
|
-
import { dirname as
|
|
28661
|
-
import { z as
|
|
29103
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
29104
|
+
import { dirname as dirname18 } from "path";
|
|
29105
|
+
import { z as z15 } from "zod";
|
|
28662
29106
|
|
|
28663
29107
|
class McpManifest {
|
|
28664
29108
|
file;
|
|
@@ -28701,11 +29145,11 @@ class McpManifest {
|
|
|
28701
29145
|
return this.entries.get(key)?.enabled ?? false;
|
|
28702
29146
|
}
|
|
28703
29147
|
load() {
|
|
28704
|
-
if (!
|
|
29148
|
+
if (!existsSync30(this.file))
|
|
28705
29149
|
return;
|
|
28706
29150
|
let parsed;
|
|
28707
29151
|
try {
|
|
28708
|
-
parsed = manifestFileSchema.parse(JSON.parse(
|
|
29152
|
+
parsed = manifestFileSchema.parse(JSON.parse(readFileSync25(this.file, "utf8")));
|
|
28709
29153
|
} catch (e) {
|
|
28710
29154
|
throw new Error(`Invalid MCP manifest at ${this.file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
28711
29155
|
}
|
|
@@ -28717,28 +29161,28 @@ class McpManifest {
|
|
|
28717
29161
|
}
|
|
28718
29162
|
}
|
|
28719
29163
|
save() {
|
|
28720
|
-
|
|
29164
|
+
mkdirSync27(dirname18(this.file), { recursive: true });
|
|
28721
29165
|
const payload = {
|
|
28722
29166
|
version: 1,
|
|
28723
29167
|
servers: [...this.entries.values()]
|
|
28724
29168
|
};
|
|
28725
29169
|
const tmp = `${this.file}.tmp-${process.pid}`;
|
|
28726
|
-
|
|
29170
|
+
writeFileSync21(tmp, `${JSON.stringify(payload, null, 2)}
|
|
28727
29171
|
`);
|
|
28728
|
-
|
|
29172
|
+
renameSync14(tmp, this.file);
|
|
28729
29173
|
}
|
|
28730
29174
|
}
|
|
28731
29175
|
var manifestEntrySchema, manifestFileSchema;
|
|
28732
29176
|
var init_manifest = __esm(() => {
|
|
28733
29177
|
init_paths();
|
|
28734
29178
|
init_spec();
|
|
28735
|
-
manifestEntrySchema =
|
|
29179
|
+
manifestEntrySchema = z15.object({
|
|
28736
29180
|
spec: mcpServerSpecSchema,
|
|
28737
|
-
enabled:
|
|
29181
|
+
enabled: z15.boolean()
|
|
28738
29182
|
});
|
|
28739
|
-
manifestFileSchema =
|
|
28740
|
-
version:
|
|
28741
|
-
servers:
|
|
29183
|
+
manifestFileSchema = z15.object({
|
|
29184
|
+
version: z15.literal(1),
|
|
29185
|
+
servers: z15.array(manifestEntrySchema)
|
|
28742
29186
|
});
|
|
28743
29187
|
});
|
|
28744
29188
|
|
|
@@ -28753,15 +29197,15 @@ function sanitizePathComponent(name) {
|
|
|
28753
29197
|
// ../../packages/mcp-host/src/manager.ts
|
|
28754
29198
|
import { execFileSync as execFileSync8, spawn as spawn8 } from "child_process";
|
|
28755
29199
|
import {
|
|
28756
|
-
mkdirSync as
|
|
29200
|
+
mkdirSync as mkdirSync28,
|
|
28757
29201
|
readdirSync as readdirSync12,
|
|
28758
|
-
readFileSync as
|
|
28759
|
-
renameSync as
|
|
29202
|
+
readFileSync as readFileSync26,
|
|
29203
|
+
renameSync as renameSync15,
|
|
28760
29204
|
unlinkSync as unlinkSync21,
|
|
28761
|
-
writeFileSync as
|
|
29205
|
+
writeFileSync as writeFileSync22
|
|
28762
29206
|
} from "fs";
|
|
28763
29207
|
import { connect, createServer as createServer3 } from "net";
|
|
28764
|
-
import { join as
|
|
29208
|
+
import { join as join39 } from "path";
|
|
28765
29209
|
function regKey(key, instanceKey) {
|
|
28766
29210
|
return `${key}\x00${instanceKey}`;
|
|
28767
29211
|
}
|
|
@@ -29027,7 +29471,7 @@ class McpHost {
|
|
|
29027
29471
|
const range = spec.portRange;
|
|
29028
29472
|
if (!range)
|
|
29029
29473
|
throw new Error(`MCP spec "${spec.key}" is http but has no portRange`);
|
|
29030
|
-
|
|
29474
|
+
mkdirSync28(this.portsDir, { recursive: true });
|
|
29031
29475
|
const claims = this.readPortClaims();
|
|
29032
29476
|
const own = claims.get(fileName);
|
|
29033
29477
|
if (own !== undefined && own >= range.base && own <= range.max && !this.usedPorts.has(own)) {
|
|
@@ -29063,7 +29507,7 @@ class McpHost {
|
|
|
29063
29507
|
if (file.includes(".tmp-"))
|
|
29064
29508
|
continue;
|
|
29065
29509
|
try {
|
|
29066
|
-
const port = Number.parseInt(
|
|
29510
|
+
const port = Number.parseInt(readFileSync26(join39(this.portsDir, file), "utf8").trim(), 10);
|
|
29067
29511
|
if (!Number.isNaN(port))
|
|
29068
29512
|
claims.set(file, port);
|
|
29069
29513
|
} catch {}
|
|
@@ -29072,18 +29516,18 @@ class McpHost {
|
|
|
29072
29516
|
}
|
|
29073
29517
|
writePortFile(fileName, port) {
|
|
29074
29518
|
try {
|
|
29075
|
-
|
|
29076
|
-
const file =
|
|
29519
|
+
mkdirSync28(this.portsDir, { recursive: true });
|
|
29520
|
+
const file = join39(this.portsDir, fileName);
|
|
29077
29521
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
29078
|
-
|
|
29079
|
-
|
|
29522
|
+
writeFileSync22(tmp, String(port));
|
|
29523
|
+
renameSync15(tmp, file);
|
|
29080
29524
|
} catch (e) {
|
|
29081
29525
|
this.log("warn", "Failed to write MCP port file", { fileName, port, err: String(e) });
|
|
29082
29526
|
}
|
|
29083
29527
|
}
|
|
29084
29528
|
deletePortFile(fileName) {
|
|
29085
29529
|
try {
|
|
29086
|
-
unlinkSync21(
|
|
29530
|
+
unlinkSync21(join39(this.portsDir, fileName));
|
|
29087
29531
|
} catch {}
|
|
29088
29532
|
}
|
|
29089
29533
|
cleanupRunning(rkey, inst) {
|
|
@@ -29188,14 +29632,14 @@ var init_src3 = __esm(() => {
|
|
|
29188
29632
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
29189
29633
|
import {
|
|
29190
29634
|
copyFileSync as copyFileSync4,
|
|
29191
|
-
existsSync as
|
|
29192
|
-
mkdirSync as
|
|
29193
|
-
readFileSync as
|
|
29635
|
+
existsSync as existsSync31,
|
|
29636
|
+
mkdirSync as mkdirSync29,
|
|
29637
|
+
readFileSync as readFileSync27,
|
|
29194
29638
|
rmSync as rmSync9,
|
|
29195
29639
|
statSync as statSync18,
|
|
29196
|
-
writeFileSync as
|
|
29640
|
+
writeFileSync as writeFileSync23
|
|
29197
29641
|
} from "fs";
|
|
29198
|
-
import { basename as basename10, extname as extname4, join as
|
|
29642
|
+
import { basename as basename10, extname as extname4, join as join40 } from "path";
|
|
29199
29643
|
function safeExtension(filename) {
|
|
29200
29644
|
const extension = extname4(basename10(filename));
|
|
29201
29645
|
return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
|
|
@@ -29213,20 +29657,20 @@ function contentDisposition(filename) {
|
|
|
29213
29657
|
|
|
29214
29658
|
class NodeFileStore {
|
|
29215
29659
|
uploadDir;
|
|
29216
|
-
constructor(uploadDir =
|
|
29660
|
+
constructor(uploadDir = join40(DATA_DIR, "uploads")) {
|
|
29217
29661
|
this.uploadDir = uploadDir;
|
|
29218
29662
|
}
|
|
29219
29663
|
#ensureDir() {
|
|
29220
|
-
|
|
29664
|
+
mkdirSync29(this.uploadDir, { recursive: true });
|
|
29221
29665
|
}
|
|
29222
29666
|
#metadataPath(fileId) {
|
|
29223
|
-
return
|
|
29667
|
+
return join40(this.uploadDir, `${fileId}.meta.json`);
|
|
29224
29668
|
}
|
|
29225
29669
|
#metadata(fileId) {
|
|
29226
29670
|
if (!FILE_ID_RE.test(fileId))
|
|
29227
29671
|
return null;
|
|
29228
29672
|
try {
|
|
29229
|
-
const parsed = JSON.parse(
|
|
29673
|
+
const parsed = JSON.parse(readFileSync27(this.#metadataPath(fileId), "utf8"));
|
|
29230
29674
|
if (typeof parsed.filename !== "string" || typeof parsed.mimeType !== "string" || typeof parsed.sizeBytes !== "number" || typeof parsed.savedName !== "string") {
|
|
29231
29675
|
return null;
|
|
29232
29676
|
}
|
|
@@ -29249,7 +29693,7 @@ class NodeFileStore {
|
|
|
29249
29693
|
hooks = {
|
|
29250
29694
|
resolveAttachmentByFileId: (fileId) => {
|
|
29251
29695
|
const metadata = this.#metadata(fileId);
|
|
29252
|
-
if (!metadata || !
|
|
29696
|
+
if (!metadata || !existsSync31(join40(this.uploadDir, metadata.savedName)))
|
|
29253
29697
|
return null;
|
|
29254
29698
|
return this.#attachment(fileId, metadata);
|
|
29255
29699
|
},
|
|
@@ -29257,8 +29701,8 @@ class NodeFileStore {
|
|
|
29257
29701
|
const metadata = this.#metadata(fileId);
|
|
29258
29702
|
if (!metadata)
|
|
29259
29703
|
return null;
|
|
29260
|
-
const path =
|
|
29261
|
-
return
|
|
29704
|
+
const path = join40(this.uploadDir, metadata.savedName);
|
|
29705
|
+
return existsSync31(path) ? path : null;
|
|
29262
29706
|
},
|
|
29263
29707
|
storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
|
|
29264
29708
|
deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
|
|
@@ -29268,7 +29712,7 @@ class NodeFileStore {
|
|
|
29268
29712
|
const fileId = randomUUID24();
|
|
29269
29713
|
const extension = safeExtension(absPath);
|
|
29270
29714
|
const savedName = `${fileId}${extension}`;
|
|
29271
|
-
const savedPath =
|
|
29715
|
+
const savedPath = join40(this.uploadDir, savedName);
|
|
29272
29716
|
try {
|
|
29273
29717
|
const stats = statSync18(absPath);
|
|
29274
29718
|
if (!stats.isFile() || stats.size > MAX_UPLOAD_BYTES)
|
|
@@ -29283,7 +29727,7 @@ class NodeFileStore {
|
|
|
29283
29727
|
...access.visibility ? { visibility: access.visibility } : {}
|
|
29284
29728
|
};
|
|
29285
29729
|
copyFileSync4(absPath, savedPath);
|
|
29286
|
-
|
|
29730
|
+
writeFileSync23(this.#metadataPath(fileId), JSON.stringify(metadata), { mode: 384 });
|
|
29287
29731
|
return this.#attachment(fileId, metadata);
|
|
29288
29732
|
} catch (error2) {
|
|
29289
29733
|
rmSync9(savedPath, { force: true });
|
|
@@ -29300,8 +29744,8 @@ class NodeFileStore {
|
|
|
29300
29744
|
const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
|
|
29301
29745
|
if (!allowed)
|
|
29302
29746
|
return null;
|
|
29303
|
-
const path =
|
|
29304
|
-
if (!
|
|
29747
|
+
const path = join40(this.uploadDir, metadata.savedName);
|
|
29748
|
+
if (!existsSync31(path))
|
|
29305
29749
|
return null;
|
|
29306
29750
|
return new Response(Bun.file(path), {
|
|
29307
29751
|
headers: {
|
|
@@ -29319,7 +29763,7 @@ class NodeFileStore {
|
|
|
29319
29763
|
const metadata = this.#metadata(fileId);
|
|
29320
29764
|
if (metadata?.topicId !== topicId)
|
|
29321
29765
|
continue;
|
|
29322
|
-
rmSync9(
|
|
29766
|
+
rmSync9(join40(this.uploadDir, metadata.savedName), { force: true });
|
|
29323
29767
|
rmSync9(this.#metadataPath(fileId), { force: true });
|
|
29324
29768
|
}
|
|
29325
29769
|
}
|
|
@@ -29471,14 +29915,14 @@ function createPollingSseStream(req, options) {
|
|
|
29471
29915
|
import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
29472
29916
|
import {
|
|
29473
29917
|
chmodSync as chmodSync6,
|
|
29474
|
-
existsSync as
|
|
29475
|
-
mkdirSync as
|
|
29476
|
-
readFileSync as
|
|
29477
|
-
renameSync as
|
|
29918
|
+
existsSync as existsSync32,
|
|
29919
|
+
mkdirSync as mkdirSync30,
|
|
29920
|
+
readFileSync as readFileSync28,
|
|
29921
|
+
renameSync as renameSync16,
|
|
29478
29922
|
unlinkSync as unlinkSync22,
|
|
29479
|
-
writeFileSync as
|
|
29923
|
+
writeFileSync as writeFileSync24
|
|
29480
29924
|
} from "fs";
|
|
29481
|
-
import { dirname as
|
|
29925
|
+
import { dirname as dirname19, resolve as resolve21 } from "path";
|
|
29482
29926
|
function jsonError(status, error2) {
|
|
29483
29927
|
return Response.json({ ok: false, error: error2 }, { status });
|
|
29484
29928
|
}
|
|
@@ -29968,6 +30412,25 @@ function createNodeControlHandler(options) {
|
|
|
29968
30412
|
return jsonError(404, "Topic not found");
|
|
29969
30413
|
return Response.json({ ok: true, usage: getTopicStats(userId, topicId) });
|
|
29970
30414
|
}
|
|
30415
|
+
const decisionsMatch = path.match(/^\/topics\/([^/]+)\/decisions$/);
|
|
30416
|
+
if (decisionsMatch && req.method === "GET") {
|
|
30417
|
+
const topicId = decodeURIComponent(decisionsMatch[1]);
|
|
30418
|
+
const userId = requiredText(url.searchParams.get("user"), "user");
|
|
30419
|
+
if (!topicForUser(topicId, userId))
|
|
30420
|
+
return jsonError(404, "Topic not found");
|
|
30421
|
+
return Response.json({ ok: true, decisions: readDecisions(userId, topicId) });
|
|
30422
|
+
}
|
|
30423
|
+
const decisionGraphMatch = path.match(/^\/topics\/([^/]+)\/decision-graph$/);
|
|
30424
|
+
if (decisionGraphMatch && req.method === "POST") {
|
|
30425
|
+
const topicId = decodeURIComponent(decisionGraphMatch[1]);
|
|
30426
|
+
const body = await bodyRecord(req);
|
|
30427
|
+
const userId = requiredText(body.userId, "userId");
|
|
30428
|
+
const svg = requiredText(body.svg, "svg");
|
|
30429
|
+
if (!topicForUser(topicId, userId))
|
|
30430
|
+
return jsonError(404, "Topic not found");
|
|
30431
|
+
writeDecisionGraphSvg(userId, topicId, svg);
|
|
30432
|
+
return Response.json({ ok: true });
|
|
30433
|
+
}
|
|
29971
30434
|
const modelMatch = path.match(/^\/topics\/([^/]+)\/model$/);
|
|
29972
30435
|
if (modelMatch && req.method === "POST") {
|
|
29973
30436
|
const topicId = decodeURIComponent(modelMatch[1]);
|
|
@@ -30105,20 +30568,20 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30105
30568
|
stateDir: STATE_DIR,
|
|
30106
30569
|
startedAt
|
|
30107
30570
|
};
|
|
30108
|
-
|
|
30571
|
+
mkdirSync30(dirname19(NODE_DAEMON_INFO_PATH), { recursive: true });
|
|
30109
30572
|
const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
|
|
30110
|
-
|
|
30573
|
+
writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
|
|
30111
30574
|
`, { mode: 384 });
|
|
30112
30575
|
chmodSync6(temporary, 384);
|
|
30113
|
-
|
|
30576
|
+
renameSync16(temporary, NODE_DAEMON_INFO_PATH);
|
|
30114
30577
|
chmodSync6(NODE_DAEMON_INFO_PATH, 384);
|
|
30115
30578
|
return info;
|
|
30116
30579
|
}
|
|
30117
30580
|
function readNodeDaemonInfo() {
|
|
30118
|
-
if (!
|
|
30581
|
+
if (!existsSync32(NODE_DAEMON_INFO_PATH))
|
|
30119
30582
|
return null;
|
|
30120
30583
|
try {
|
|
30121
|
-
const value = JSON.parse(
|
|
30584
|
+
const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
|
|
30122
30585
|
if (value?.schemaVersion !== 1 || value.stateDir !== STATE_DIR || !Number.isInteger(value.protocolVersion) || !Number.isInteger(value.pid) || !Number.isInteger(value.port) || value.port <= 0) {
|
|
30123
30586
|
return null;
|
|
30124
30587
|
}
|
|
@@ -30402,7 +30865,7 @@ var init_schedule = __esm(() => {
|
|
|
30402
30865
|
|
|
30403
30866
|
// ../../packages/module-cron/src/scripts.ts
|
|
30404
30867
|
import { spawn as spawn9 } from "child_process";
|
|
30405
|
-
import { existsSync as
|
|
30868
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
|
|
30406
30869
|
import { resolve as resolve22, sep as sep2 } from "path";
|
|
30407
30870
|
function validateCronScriptName(script) {
|
|
30408
30871
|
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
|
|
@@ -30420,12 +30883,12 @@ function resolveCronScriptPath(script) {
|
|
|
30420
30883
|
return path;
|
|
30421
30884
|
}
|
|
30422
30885
|
function listCronScripts() {
|
|
30423
|
-
|
|
30886
|
+
mkdirSync31(CRON_JOBS_DIR, { recursive: true });
|
|
30424
30887
|
return readdirSync13(CRON_JOBS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && validateCronScriptName(entry.name).ok).map((entry) => entry.name).sort();
|
|
30425
30888
|
}
|
|
30426
30889
|
function cronScriptExists(script) {
|
|
30427
30890
|
try {
|
|
30428
|
-
return
|
|
30891
|
+
return existsSync33(resolveCronScriptPath(script));
|
|
30429
30892
|
} catch {
|
|
30430
30893
|
return false;
|
|
30431
30894
|
}
|
|
@@ -30457,7 +30920,7 @@ async function runCronPromptScript(options) {
|
|
|
30457
30920
|
if (options.signal?.aborted)
|
|
30458
30921
|
throw new CronScriptError("script run aborted");
|
|
30459
30922
|
const scriptPath = resolveCronScriptPath(options.script);
|
|
30460
|
-
if (!
|
|
30923
|
+
if (!existsSync33(scriptPath))
|
|
30461
30924
|
throw new CronScriptError(`cron script not found: ${options.script}`);
|
|
30462
30925
|
const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
|
|
30463
30926
|
const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
|
|
@@ -31650,7 +32113,7 @@ var init_scheduler = __esm(async () => {
|
|
|
31650
32113
|
});
|
|
31651
32114
|
|
|
31652
32115
|
// ../../packages/module-cron/src/module.ts
|
|
31653
|
-
import { existsSync as
|
|
32116
|
+
import { existsSync as existsSync34 } from "fs";
|
|
31654
32117
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31655
32118
|
function envMs(name, fallback) {
|
|
31656
32119
|
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
@@ -31681,7 +32144,7 @@ function createCronModule(options = {}) {
|
|
|
31681
32144
|
if (event.type === "topic-deleted")
|
|
31682
32145
|
trackCleanup(resetCronTopicContext(event.topicId));
|
|
31683
32146
|
});
|
|
31684
|
-
const mcpServerFile = options.mcpServerFile ?? (
|
|
32147
|
+
const mcpServerFile = options.mcpServerFile ?? (existsSync34(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
|
|
31685
32148
|
const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
|
|
31686
32149
|
scopes: ["forum", "manager"],
|
|
31687
32150
|
forumRequired: true,
|
|
@@ -32348,6 +32811,73 @@ var init_commands = __esm(() => {
|
|
|
32348
32811
|
];
|
|
32349
32812
|
});
|
|
32350
32813
|
|
|
32814
|
+
// ../../adapters/terminal/src/decision-graph.ts
|
|
32815
|
+
import {
|
|
32816
|
+
layoutGraph,
|
|
32817
|
+
renderSvgGraph,
|
|
32818
|
+
renderTerminalGraph
|
|
32819
|
+
} from "orchgraph";
|
|
32820
|
+
function adjustDecisionGraphSpacing(current3, delta) {
|
|
32821
|
+
return Math.min(MAX_DECISION_GRAPH_SPACING, Math.max(MIN_DECISION_GRAPH_SPACING, current3 + delta));
|
|
32822
|
+
}
|
|
32823
|
+
function decisionNodeState(status) {
|
|
32824
|
+
switch (status) {
|
|
32825
|
+
case "proposed":
|
|
32826
|
+
return "queued";
|
|
32827
|
+
case "executed":
|
|
32828
|
+
return "succeeded";
|
|
32829
|
+
case "rejected":
|
|
32830
|
+
return "failed";
|
|
32831
|
+
case "superseded":
|
|
32832
|
+
return "blocked";
|
|
32833
|
+
default:
|
|
32834
|
+
return "idle";
|
|
32835
|
+
}
|
|
32836
|
+
}
|
|
32837
|
+
function compactDetail(decision) {
|
|
32838
|
+
const rationale = decision.reasoning.replace(/\s+/g, " ").trim();
|
|
32839
|
+
return `${rationale} \xB7 ${decision.agent}${decision.model ? `/${decision.model}` : ""}`;
|
|
32840
|
+
}
|
|
32841
|
+
function buildDecisionGraph(decisions2, topicTitle) {
|
|
32842
|
+
const ids = new Set(decisions2.map((decision) => decision.id));
|
|
32843
|
+
return {
|
|
32844
|
+
id: "decisions",
|
|
32845
|
+
title: topicTitle,
|
|
32846
|
+
direction: "DOWN",
|
|
32847
|
+
nodes: decisions2.map((decision) => ({
|
|
32848
|
+
id: decision.id,
|
|
32849
|
+
label: `#${decision.id} ${decision.action}`,
|
|
32850
|
+
detail: compactDetail(decision),
|
|
32851
|
+
kind: decision.status,
|
|
32852
|
+
state: decisionNodeState(decision.status),
|
|
32853
|
+
metadata: { timestamp: decision.timestamp, agent: decision.agent, model: decision.model }
|
|
32854
|
+
})),
|
|
32855
|
+
edges: decisions2.flatMap((decision) => (decision.causedBy ?? []).filter((upstream) => ids.has(upstream)).map((upstream) => ({
|
|
32856
|
+
id: `caused:${upstream}:${decision.id}`,
|
|
32857
|
+
source: upstream,
|
|
32858
|
+
target: decision.id,
|
|
32859
|
+
label: "caused",
|
|
32860
|
+
kind: "caused",
|
|
32861
|
+
direction: "forward",
|
|
32862
|
+
style: "solid"
|
|
32863
|
+
})))
|
|
32864
|
+
};
|
|
32865
|
+
}
|
|
32866
|
+
async function renderDecisionGraph(graph, spacing = DEFAULT_DECISION_GRAPH_SPACING, signal) {
|
|
32867
|
+
const geometry = await layoutGraph(graph, {
|
|
32868
|
+
direction: "DOWN",
|
|
32869
|
+
spacing,
|
|
32870
|
+
signal,
|
|
32871
|
+
timeoutMs: 15000
|
|
32872
|
+
});
|
|
32873
|
+
return {
|
|
32874
|
+
canvas: { ...renderTerminalGraph(graph, geometry), title: graph.title },
|
|
32875
|
+
svg: renderSvgGraph(graph, geometry, { title: graph.title })
|
|
32876
|
+
};
|
|
32877
|
+
}
|
|
32878
|
+
var DEFAULT_DECISION_GRAPH_SPACING = 4, MIN_DECISION_GRAPH_SPACING = 2, MAX_DECISION_GRAPH_SPACING = 10;
|
|
32879
|
+
var init_decision_graph = () => {};
|
|
32880
|
+
|
|
32351
32881
|
// ../../adapters/terminal/src/subagent-graph.ts
|
|
32352
32882
|
import {
|
|
32353
32883
|
layoutTerminalGraph,
|
|
@@ -32541,6 +33071,9 @@ function createInitialState(userId) {
|
|
|
32541
33071
|
subagentGraphLoading: false,
|
|
32542
33072
|
subagentGraphOffset: { x: 0, y: 0 },
|
|
32543
33073
|
subagentGraphSpacing: DEFAULT_SUBAGENT_GRAPH_SPACING,
|
|
33074
|
+
decisionGraphLoading: false,
|
|
33075
|
+
decisionGraphOffset: { x: 0, y: 0 },
|
|
33076
|
+
decisionGraphSpacing: DEFAULT_DECISION_GRAPH_SPACING,
|
|
32544
33077
|
askChoiceIndex: 0,
|
|
32545
33078
|
taskSidebarEnabled: true,
|
|
32546
33079
|
overlay: null,
|
|
@@ -33104,6 +33637,7 @@ function applyRuntimeEvent(state, event) {
|
|
|
33104
33637
|
var NO_TOPIC_PICKER_SELECTION = -1;
|
|
33105
33638
|
var init_state2 = __esm(async () => {
|
|
33106
33639
|
await init_src();
|
|
33640
|
+
init_decision_graph();
|
|
33107
33641
|
init_subagent_graph();
|
|
33108
33642
|
});
|
|
33109
33643
|
|
|
@@ -33168,9 +33702,9 @@ var init_context_usage = __esm(async () => {
|
|
|
33168
33702
|
|
|
33169
33703
|
// ../../adapters/terminal/src/path-suggest.ts
|
|
33170
33704
|
import { execFile as execFile3 } from "child_process";
|
|
33171
|
-
import { existsSync as
|
|
33705
|
+
import { existsSync as existsSync35, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
33172
33706
|
import { homedir as homedir10 } from "os";
|
|
33173
|
-
import { basename as basename11, dirname as
|
|
33707
|
+
import { basename as basename11, dirname as dirname20, join as join41, sep as sep3 } from "path";
|
|
33174
33708
|
import { promisify as promisify3 } from "util";
|
|
33175
33709
|
function indexRecursivePaths(files) {
|
|
33176
33710
|
const paths = [];
|
|
@@ -33271,15 +33805,15 @@ function resolveFragment(frag) {
|
|
|
33271
33805
|
else if (frag === "~/")
|
|
33272
33806
|
path = home;
|
|
33273
33807
|
else if (frag.startsWith("~/"))
|
|
33274
|
-
path =
|
|
33808
|
+
path = join41(home, frag.slice(2));
|
|
33275
33809
|
else if (frag.startsWith("/"))
|
|
33276
33810
|
path = frag;
|
|
33277
33811
|
else
|
|
33278
|
-
path =
|
|
33812
|
+
path = join41(home, frag);
|
|
33279
33813
|
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
33280
33814
|
return { dir: path, prefix: "" };
|
|
33281
33815
|
}
|
|
33282
|
-
return { dir:
|
|
33816
|
+
return { dir: dirname20(path), prefix: basename11(path) };
|
|
33283
33817
|
}
|
|
33284
33818
|
function toToken(fullPath, isDir) {
|
|
33285
33819
|
const home = homedir10();
|
|
@@ -33304,7 +33838,7 @@ function rankAndSlice(dir, candidates) {
|
|
|
33304
33838
|
});
|
|
33305
33839
|
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
33306
33840
|
label: `${relPath}${isDir ? "/" : ""}`,
|
|
33307
|
-
value: toToken(
|
|
33841
|
+
value: toToken(join41(dir, relPath), isDir),
|
|
33308
33842
|
isDir
|
|
33309
33843
|
}));
|
|
33310
33844
|
}
|
|
@@ -33383,7 +33917,7 @@ function pathSuggestions(lineText, col) {
|
|
|
33383
33917
|
let isDir = entry.isDirectory();
|
|
33384
33918
|
if (!isDir && entry.isSymbolicLink()) {
|
|
33385
33919
|
try {
|
|
33386
|
-
isDir = statSync19(
|
|
33920
|
+
isDir = statSync19(join41(dir, entry.name)).isDirectory();
|
|
33387
33921
|
} catch {}
|
|
33388
33922
|
}
|
|
33389
33923
|
return {
|
|
@@ -33410,14 +33944,14 @@ function fragmentToAbsolutePath(frag) {
|
|
|
33410
33944
|
if (frag === "~" || frag === "~/")
|
|
33411
33945
|
return home;
|
|
33412
33946
|
if (frag.startsWith("~/"))
|
|
33413
|
-
return
|
|
33947
|
+
return join41(home, frag.slice(2));
|
|
33414
33948
|
if (frag.startsWith("/"))
|
|
33415
33949
|
return frag;
|
|
33416
|
-
return
|
|
33950
|
+
return join41(home, frag);
|
|
33417
33951
|
}
|
|
33418
33952
|
function fragmentResolves(frag) {
|
|
33419
33953
|
try {
|
|
33420
|
-
return
|
|
33954
|
+
return existsSync35(fragmentToAbsolutePath(frag));
|
|
33421
33955
|
} catch {
|
|
33422
33956
|
return false;
|
|
33423
33957
|
}
|
|
@@ -34283,7 +34817,7 @@ function helpLines() {
|
|
|
34283
34817
|
line(" Alt-Backspace delete word \xB7 Ctrl-U clear before cursor"),
|
|
34284
34818
|
line(" Cmd-Backspace works when forwarded \xB7 Ctrl-W/K aliases"),
|
|
34285
34819
|
line(" Mouse wheel / PgUp/PgDn scroll \xB7 Ctrl-E load older \xB7 Ctrl-T tasks"),
|
|
34286
|
-
line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-X abort"),
|
|
34820
|
+
line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions \xB7 Ctrl-X abort"),
|
|
34287
34821
|
line(" In topics: type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete"),
|
|
34288
34822
|
line(" Esc/Ctrl-C stop active turn \xB7 Ctrl-C twice when idle to quit"),
|
|
34289
34823
|
line(""),
|
|
@@ -34630,6 +35164,43 @@ function subagentGraphLines(state, width, height, animationFrame) {
|
|
|
34630
35164
|
})
|
|
34631
35165
|
].slice(0, height);
|
|
34632
35166
|
}
|
|
35167
|
+
function decisionGraphLines(state, width, height) {
|
|
35168
|
+
const headerHeight = 5;
|
|
35169
|
+
const canvas = state.decisionGraph;
|
|
35170
|
+
const offset = state.decisionGraphOffset;
|
|
35171
|
+
const viewportWidth = Math.max(1, width - 4);
|
|
35172
|
+
const viewportHeight = Math.max(1, height - headerHeight);
|
|
35173
|
+
const maxX = Math.max(0, (canvas?.width ?? 0) - viewportWidth);
|
|
35174
|
+
const maxY = Math.max(0, (canvas?.height ?? 0) - viewportHeight);
|
|
35175
|
+
const x = Math.min(maxX, offset.x);
|
|
35176
|
+
const y = Math.min(maxY, offset.y);
|
|
35177
|
+
const position = maxX > 0 || maxY > 0 ? ` \xB7 view ${x + 1},${y + 1}/${maxX + 1},${maxY + 1}` : "";
|
|
35178
|
+
const header = [
|
|
35179
|
+
line(" Decision graph", { fg: theme.accent, bold: true }),
|
|
35180
|
+
line(` ${canvas?.title ?? activeTopic(state)?.title ?? "Current topic"}`, {
|
|
35181
|
+
fg: theme.text,
|
|
35182
|
+
bold: true
|
|
35183
|
+
}),
|
|
35184
|
+
line(` [/] spacing ${state.decisionGraphSpacing} \xB7 drag/wheel/arrows/hjkl move \xB7 Ctrl-D/Esc close${position}`, { fg: theme.muted }),
|
|
35185
|
+
line(" \u25CB accepted \xB7 \u25F7 proposed \xB7 \u2713 executed \xB7 \u2717 rejected \xB7 ! superseded", {
|
|
35186
|
+
fg: theme.subtle
|
|
35187
|
+
}),
|
|
35188
|
+
line("")
|
|
35189
|
+
];
|
|
35190
|
+
if (state.decisionGraphLoading) {
|
|
35191
|
+
return [...header, line(" Laying out decisions with Orchgraph\u2026", { fg: theme.cyan })].slice(0, height);
|
|
35192
|
+
}
|
|
35193
|
+
if (!canvas || canvas.lines.length === 0) {
|
|
35194
|
+
return [...header, line(" No decision graph data", { fg: theme.muted })].slice(0, height);
|
|
35195
|
+
}
|
|
35196
|
+
return [
|
|
35197
|
+
...header,
|
|
35198
|
+
...Array.from({ length: viewportHeight }, (_, index) => {
|
|
35199
|
+
const text2 = sliceWidthRange(canvas.lines[y + index] ?? "", x, viewportWidth);
|
|
35200
|
+
return line(` ${text2}`, { fg: /[\u256D\u256E\u2570\u256F]/.test(text2) ? theme.cyan : theme.muted });
|
|
35201
|
+
})
|
|
35202
|
+
].slice(0, height);
|
|
35203
|
+
}
|
|
34633
35204
|
function modelOverlayLines(state, height) {
|
|
34634
35205
|
const currentModel = effectiveTopicModel(activeTopic(state));
|
|
34635
35206
|
const modelColumnWidth = Math.max(...SELECTABLE_MODELS.map(({ model }) => `${model} (current)`.length));
|
|
@@ -34747,6 +35318,8 @@ function conversationLines(state, width, height, animationFrame = 0, nowMs = ter
|
|
|
34747
35318
|
return topicOverlayLines(state, width, height, animationFrame).slice(0, height);
|
|
34748
35319
|
if (state.overlay === "subagents")
|
|
34749
35320
|
return subagentGraphLines(state, width, height, animationFrame);
|
|
35321
|
+
if (state.overlay === "decisions")
|
|
35322
|
+
return decisionGraphLines(state, width, height);
|
|
34750
35323
|
if (state.overlay === "background-session")
|
|
34751
35324
|
return backgroundSessionViewportLines(state, width, height, nowMs);
|
|
34752
35325
|
if (state.overlay === "models")
|
|
@@ -34889,7 +35462,7 @@ function inputVisualLines(state, width) {
|
|
|
34889
35462
|
return { lines: result, cursorLine, cursorColumn };
|
|
34890
35463
|
}
|
|
34891
35464
|
function composerPane(state, width) {
|
|
34892
|
-
const title = state.creatingTopic ? "new topic \xB7 type a name \xB7 Enter create" : state.overlay === "vault" ? state.vaultMode === "list" ? "Vault command \xB7 Enter run \xB7 Esc close" : `${state.vaultMode === "key" ? "key" : state.vaultMode === "value" ? "secret value" : "description"} \xB7 Enter continue \xB7 Esc cancel` : "Ctrl-O topics \xB7 Ctrl-G subagents";
|
|
35465
|
+
const title = state.creatingTopic ? "new topic \xB7 type a name \xB7 Enter create" : state.overlay === "vault" ? state.vaultMode === "list" ? "Vault command \xB7 Enter run \xB7 Esc close" : `${state.vaultMode === "key" ? "key" : state.vaultMode === "value" ? "secret value" : "description"} \xB7 Enter continue \xB7 Esc cancel` : "Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions";
|
|
34893
35466
|
const visual = inputVisualLines(state, width);
|
|
34894
35467
|
let inputStart = Math.max(0, visual.lines.length - 5);
|
|
34895
35468
|
if (visual.cursorLine < inputStart)
|
|
@@ -35051,6 +35624,13 @@ function footerHintText(state) {
|
|
|
35051
35624
|
if (state.overlay === "subagents") {
|
|
35052
35625
|
return ["arrows/hjkl/wheel move \xB7 Esc close ", "arrows move \xB7 Esc close ", "Esc close "];
|
|
35053
35626
|
}
|
|
35627
|
+
if (state.overlay === "decisions") {
|
|
35628
|
+
return [
|
|
35629
|
+
"arrows/hjkl/wheel move \xB7 Ctrl-D/Esc close ",
|
|
35630
|
+
"arrows move \xB7 Esc close ",
|
|
35631
|
+
"Esc close "
|
|
35632
|
+
];
|
|
35633
|
+
}
|
|
35054
35634
|
if (state.overlay === "vault") {
|
|
35055
35635
|
if (state.vaultMode === "list")
|
|
35056
35636
|
return ["Enter run \xB7 Esc close ", "Esc close "];
|
|
@@ -35123,7 +35703,7 @@ function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = termin
|
|
|
35123
35703
|
const height = Math.max(14, rows);
|
|
35124
35704
|
const footer = footerLines(state, width);
|
|
35125
35705
|
const decision = decisionPane(state, width);
|
|
35126
|
-
const hideComposer = state.overlay === "background-session" || state.overlay === "topics" || state.overlay === "subagents" || state.overlay === "vault" && state.vaultMode === "confirm-delete";
|
|
35706
|
+
const hideComposer = state.overlay === "background-session" || state.overlay === "topics" || state.overlay === "subagents" || state.overlay === "decisions" || state.overlay === "vault" && state.vaultMode === "confirm-delete";
|
|
35127
35707
|
const composer = hideComposer ? { lines: [], cursor: null } : composerPane(state, width);
|
|
35128
35708
|
const bodyHeight = Math.max(3, height - footer.length - decision.length - composer.lines.length);
|
|
35129
35709
|
const layout = terminalBodyLayout(state, width);
|
|
@@ -35659,6 +36239,12 @@ class EmbeddedNegotiumClient {
|
|
|
35659
36239
|
listTopicUsage(topicId) {
|
|
35660
36240
|
return getTopicStats(this.#userId, topicId);
|
|
35661
36241
|
}
|
|
36242
|
+
listDecisions(topicId) {
|
|
36243
|
+
return readDecisions(this.#userId, topicId);
|
|
36244
|
+
}
|
|
36245
|
+
saveDecisionGraph(topicId, svg) {
|
|
36246
|
+
writeDecisionGraphSvg(this.#userId, topicId, svg);
|
|
36247
|
+
}
|
|
35662
36248
|
listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
35663
36249
|
const result = listApiMessages(topicId, { cursor, limit });
|
|
35664
36250
|
return { messages: result.page, cursor: result.cursor, hasMore: result.hasMore };
|
|
@@ -35848,6 +36434,16 @@ class RemoteNegotiumClient {
|
|
|
35848
36434
|
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(this.#userId)}`);
|
|
35849
36435
|
return result.usage;
|
|
35850
36436
|
}
|
|
36437
|
+
async listDecisions(topicId) {
|
|
36438
|
+
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/decisions?user=${encodeURIComponent(this.#userId)}`);
|
|
36439
|
+
return result.decisions ?? [];
|
|
36440
|
+
}
|
|
36441
|
+
async saveDecisionGraph(topicId, svg) {
|
|
36442
|
+
await this.#request(`/topics/${encodeURIComponent(topicId)}/decision-graph`, {
|
|
36443
|
+
method: "POST",
|
|
36444
|
+
body: JSON.stringify({ userId: this.#userId, svg })
|
|
36445
|
+
});
|
|
36446
|
+
}
|
|
35851
36447
|
async listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
35852
36448
|
const query2 = new URLSearchParams({ user: this.#userId, limit: String(limit) });
|
|
35853
36449
|
if (cursor)
|
|
@@ -37183,6 +37779,10 @@ class TerminalApp {
|
|
|
37183
37779
|
#subagentGraphSpacingTimer = null;
|
|
37184
37780
|
#subagentGraphCache = null;
|
|
37185
37781
|
#pendingSubagentGraphSpacing = null;
|
|
37782
|
+
#decisionGraphGeneration = 0;
|
|
37783
|
+
#decisionGraphAbortController = null;
|
|
37784
|
+
#decisionGraphSpacingTimer = null;
|
|
37785
|
+
#pendingDecisionGraphSpacing = null;
|
|
37186
37786
|
#running = false;
|
|
37187
37787
|
#stopRequested = false;
|
|
37188
37788
|
#finishRun = null;
|
|
@@ -37540,6 +38140,9 @@ class TerminalApp {
|
|
|
37540
38140
|
const topicId = this.#state.activeTopicId;
|
|
37541
38141
|
if (!topicId)
|
|
37542
38142
|
return;
|
|
38143
|
+
this.#decisionGraphGeneration += 1;
|
|
38144
|
+
this.#decisionGraphAbortController?.abort();
|
|
38145
|
+
this.#decisionGraphAbortController = null;
|
|
37543
38146
|
if (this.#state.overlay === "subagents") {
|
|
37544
38147
|
this.#subagentGraphGeneration += 1;
|
|
37545
38148
|
this.#subagentGraphAbortController?.abort();
|
|
@@ -37592,6 +38195,143 @@ class TerminalApp {
|
|
|
37592
38195
|
}
|
|
37593
38196
|
this.#queueRender();
|
|
37594
38197
|
}
|
|
38198
|
+
async#toggleDecisionGraph() {
|
|
38199
|
+
const topic = activeTopic(this.#state);
|
|
38200
|
+
if (!topic || !this.#client.listDecisions)
|
|
38201
|
+
return;
|
|
38202
|
+
if (this.#state.overlay === "decisions") {
|
|
38203
|
+
this.#decisionGraphGeneration += 1;
|
|
38204
|
+
this.#decisionGraphAbortController?.abort();
|
|
38205
|
+
this.#decisionGraphAbortController = null;
|
|
38206
|
+
this.#subagentGraphDragPoint = null;
|
|
38207
|
+
this.#state = { ...this.#state, overlay: null, decisionGraphLoading: false };
|
|
38208
|
+
this.#queueRender();
|
|
38209
|
+
return;
|
|
38210
|
+
}
|
|
38211
|
+
this.#subagentGraphGeneration += 1;
|
|
38212
|
+
this.#subagentGraphAbortController?.abort();
|
|
38213
|
+
this.#subagentGraphAbortController = null;
|
|
38214
|
+
const generation = ++this.#decisionGraphGeneration;
|
|
38215
|
+
this.#decisionGraphAbortController?.abort();
|
|
38216
|
+
const controller = new AbortController;
|
|
38217
|
+
this.#decisionGraphAbortController = controller;
|
|
38218
|
+
this.#subagentGraphDragPoint = null;
|
|
38219
|
+
this.#state = {
|
|
38220
|
+
...this.#state,
|
|
38221
|
+
overlay: "decisions",
|
|
38222
|
+
decisionGraph: undefined,
|
|
38223
|
+
decisionGraphLoading: true,
|
|
38224
|
+
decisionGraphOffset: { x: 0, y: 0 },
|
|
38225
|
+
notice: undefined,
|
|
38226
|
+
noticeLevel: undefined
|
|
38227
|
+
};
|
|
38228
|
+
this.#queueRender();
|
|
38229
|
+
try {
|
|
38230
|
+
const decisions2 = await this.#client.listDecisions(topic.id);
|
|
38231
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38232
|
+
return;
|
|
38233
|
+
}
|
|
38234
|
+
if (decisions2.length === 0) {
|
|
38235
|
+
this.#decisionGraphAbortController = null;
|
|
38236
|
+
this.#state = {
|
|
38237
|
+
...this.#state,
|
|
38238
|
+
overlay: null,
|
|
38239
|
+
decisionGraphLoading: false,
|
|
38240
|
+
notice: "No decisions recorded in this topic",
|
|
38241
|
+
noticeLevel: "info"
|
|
38242
|
+
};
|
|
38243
|
+
this.#queueRender();
|
|
38244
|
+
return;
|
|
38245
|
+
}
|
|
38246
|
+
const rendered = await renderDecisionGraph(buildDecisionGraph(decisions2, topic.title), this.#state.decisionGraphSpacing, controller.signal);
|
|
38247
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38248
|
+
return;
|
|
38249
|
+
}
|
|
38250
|
+
let saveWarning;
|
|
38251
|
+
try {
|
|
38252
|
+
await this.#client.saveDecisionGraph?.(topic.id, rendered.svg);
|
|
38253
|
+
} catch (error2) {
|
|
38254
|
+
saveWarning = `Decision graph shown but SVG save failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
38255
|
+
}
|
|
38256
|
+
this.#decisionGraphAbortController = null;
|
|
38257
|
+
this.#state = {
|
|
38258
|
+
...this.#state,
|
|
38259
|
+
decisionGraph: rendered.canvas,
|
|
38260
|
+
decisionGraphLoading: false,
|
|
38261
|
+
...saveWarning ? { notice: saveWarning, noticeLevel: "warn" } : {}
|
|
38262
|
+
};
|
|
38263
|
+
} catch (error2) {
|
|
38264
|
+
if (generation !== this.#decisionGraphGeneration)
|
|
38265
|
+
return;
|
|
38266
|
+
this.#decisionGraphAbortController = null;
|
|
38267
|
+
this.#state = {
|
|
38268
|
+
...this.#state,
|
|
38269
|
+
overlay: null,
|
|
38270
|
+
decisionGraphLoading: false,
|
|
38271
|
+
notice: `Decision graph failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
38272
|
+
noticeLevel: "error"
|
|
38273
|
+
};
|
|
38274
|
+
}
|
|
38275
|
+
this.#queueRender();
|
|
38276
|
+
}
|
|
38277
|
+
#changeDecisionGraphSpacing(delta) {
|
|
38278
|
+
const previous = this.#pendingDecisionGraphSpacing ?? this.#state.decisionGraphSpacing;
|
|
38279
|
+
const spacing = adjustDecisionGraphSpacing(previous, delta);
|
|
38280
|
+
if (spacing === previous)
|
|
38281
|
+
return;
|
|
38282
|
+
this.#pendingDecisionGraphSpacing = spacing;
|
|
38283
|
+
if (this.#decisionGraphSpacingTimer)
|
|
38284
|
+
clearTimeout(this.#decisionGraphSpacingTimer);
|
|
38285
|
+
this.#decisionGraphSpacingTimer = setTimeout(() => {
|
|
38286
|
+
this.#decisionGraphSpacingTimer = null;
|
|
38287
|
+
const target = this.#pendingDecisionGraphSpacing;
|
|
38288
|
+
this.#pendingDecisionGraphSpacing = null;
|
|
38289
|
+
if (target !== null)
|
|
38290
|
+
this.#applyDecisionGraphSpacing(target);
|
|
38291
|
+
}, 120);
|
|
38292
|
+
}
|
|
38293
|
+
async#applyDecisionGraphSpacing(spacing) {
|
|
38294
|
+
const topic = activeTopic(this.#state);
|
|
38295
|
+
if (!topic || this.#state.overlay !== "decisions" || !this.#client.listDecisions)
|
|
38296
|
+
return;
|
|
38297
|
+
const generation = ++this.#decisionGraphGeneration;
|
|
38298
|
+
this.#decisionGraphAbortController?.abort();
|
|
38299
|
+
const controller = new AbortController;
|
|
38300
|
+
this.#decisionGraphAbortController = controller;
|
|
38301
|
+
this.#state = { ...this.#state, decisionGraphSpacing: spacing, decisionGraphLoading: true };
|
|
38302
|
+
this.#queueRender();
|
|
38303
|
+
try {
|
|
38304
|
+
const decisions2 = await this.#client.listDecisions(topic.id);
|
|
38305
|
+
const rendered = await renderDecisionGraph(buildDecisionGraph(decisions2, topic.title), spacing, controller.signal);
|
|
38306
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38307
|
+
return;
|
|
38308
|
+
}
|
|
38309
|
+
let saveWarning;
|
|
38310
|
+
try {
|
|
38311
|
+
await this.#client.saveDecisionGraph?.(topic.id, rendered.svg);
|
|
38312
|
+
} catch (error2) {
|
|
38313
|
+
saveWarning = `Decision graph shown but SVG save failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
38314
|
+
}
|
|
38315
|
+
this.#decisionGraphAbortController = null;
|
|
38316
|
+
this.#state = {
|
|
38317
|
+
...this.#state,
|
|
38318
|
+
decisionGraph: rendered.canvas,
|
|
38319
|
+
decisionGraphLoading: false,
|
|
38320
|
+
...saveWarning ? { notice: saveWarning, noticeLevel: "warn" } : {}
|
|
38321
|
+
};
|
|
38322
|
+
} catch (error2) {
|
|
38323
|
+
if (generation !== this.#decisionGraphGeneration)
|
|
38324
|
+
return;
|
|
38325
|
+
this.#decisionGraphAbortController = null;
|
|
38326
|
+
this.#state = {
|
|
38327
|
+
...this.#state,
|
|
38328
|
+
decisionGraphLoading: false,
|
|
38329
|
+
notice: `Decision graph failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
38330
|
+
noticeLevel: "error"
|
|
38331
|
+
};
|
|
38332
|
+
}
|
|
38333
|
+
this.#queueRender();
|
|
38334
|
+
}
|
|
37595
38335
|
#changeSubagentGraphSpacing(delta) {
|
|
37596
38336
|
const previousSpacing = this.#pendingSubagentGraphSpacing ?? this.#state.subagentGraphSpacing;
|
|
37597
38337
|
const spacing = adjustSubagentGraphSpacing(previousSpacing, delta);
|
|
@@ -37772,18 +38512,18 @@ class TerminalApp {
|
|
|
37772
38512
|
}
|
|
37773
38513
|
#handleKeySegment(segment) {
|
|
37774
38514
|
const mouse = consumeMouseInput(segment);
|
|
37775
|
-
if (mouse.horizontalScrollDelta !== 0 && this.#state.overlay === "subagents") {
|
|
38515
|
+
if (mouse.horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
|
|
37776
38516
|
this.#selection = null;
|
|
37777
|
-
this.#
|
|
38517
|
+
this.#panActiveGraph(-mouse.horizontalScrollDelta, 0);
|
|
37778
38518
|
}
|
|
37779
38519
|
if (mouse.scrollDelta !== 0) {
|
|
37780
38520
|
this.#selection = null;
|
|
37781
38521
|
this.#scroll(mouse.scrollDelta);
|
|
37782
38522
|
}
|
|
37783
38523
|
for (const event of mouse.events) {
|
|
37784
|
-
if (this.#state.overlay === "subagents")
|
|
37785
|
-
this.#
|
|
37786
|
-
else
|
|
38524
|
+
if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
|
|
38525
|
+
this.#handleGraphMouse(event);
|
|
38526
|
+
} else
|
|
37787
38527
|
this.#handleMouseSelection(event);
|
|
37788
38528
|
}
|
|
37789
38529
|
const chunk = normalizeKeySequences(mouse.input);
|
|
@@ -37817,6 +38557,12 @@ class TerminalApp {
|
|
|
37817
38557
|
this.#toggleSubagentGraph();
|
|
37818
38558
|
return;
|
|
37819
38559
|
}
|
|
38560
|
+
if (chunk === "\x04" && this.#state.overlay !== "topics") {
|
|
38561
|
+
if (editingVaultSecret)
|
|
38562
|
+
return;
|
|
38563
|
+
this.#toggleDecisionGraph();
|
|
38564
|
+
return;
|
|
38565
|
+
}
|
|
37820
38566
|
if (chunk === "\x14") {
|
|
37821
38567
|
if (editingVaultSecret)
|
|
37822
38568
|
return;
|
|
@@ -37869,6 +38615,38 @@ class TerminalApp {
|
|
|
37869
38615
|
this.#queueRender();
|
|
37870
38616
|
return;
|
|
37871
38617
|
}
|
|
38618
|
+
if (this.#state.overlay === "decisions") {
|
|
38619
|
+
if (chunk === "\x1B") {
|
|
38620
|
+
this.#decisionGraphGeneration += 1;
|
|
38621
|
+
this.#decisionGraphAbortController?.abort();
|
|
38622
|
+
this.#decisionGraphAbortController = null;
|
|
38623
|
+
this.#subagentGraphDragPoint = null;
|
|
38624
|
+
this.#state = { ...this.#state, overlay: null, decisionGraphLoading: false };
|
|
38625
|
+
} else if (chunk === "\x1B[A" || chunk === "k") {
|
|
38626
|
+
this.#panDecisionGraph(0, -2);
|
|
38627
|
+
return;
|
|
38628
|
+
} else if (chunk === "\x1B[B" || chunk === "j") {
|
|
38629
|
+
this.#panDecisionGraph(0, 2);
|
|
38630
|
+
return;
|
|
38631
|
+
} else if (chunk === "\x1B[D" || chunk === "h") {
|
|
38632
|
+
this.#panDecisionGraph(-4, 0);
|
|
38633
|
+
return;
|
|
38634
|
+
} else if (chunk === "\x1B[C" || chunk === "l") {
|
|
38635
|
+
this.#panDecisionGraph(4, 0);
|
|
38636
|
+
return;
|
|
38637
|
+
} else if (chunk === "\x1B[5~") {
|
|
38638
|
+
this.#panDecisionGraph(0, -8);
|
|
38639
|
+
return;
|
|
38640
|
+
} else if (chunk === "\x1B[6~") {
|
|
38641
|
+
this.#panDecisionGraph(0, 8);
|
|
38642
|
+
return;
|
|
38643
|
+
} else if (chunk === "[" || chunk === "]") {
|
|
38644
|
+
this.#changeDecisionGraphSpacing(chunk === "[" ? -1 : 1);
|
|
38645
|
+
return;
|
|
38646
|
+
}
|
|
38647
|
+
this.#queueRender();
|
|
38648
|
+
return;
|
|
38649
|
+
}
|
|
37872
38650
|
if (chunk === "\x01" || chunk === "\x1B[H" || chunk === "\x1B[1~") {
|
|
37873
38651
|
this.#input.moveHome();
|
|
37874
38652
|
this.#syncInput();
|
|
@@ -38809,7 +39587,7 @@ class TerminalApp {
|
|
|
38809
39587
|
this.#copySelection(text2);
|
|
38810
39588
|
this.#queueRender();
|
|
38811
39589
|
}
|
|
38812
|
-
#
|
|
39590
|
+
#handleGraphMouse(event) {
|
|
38813
39591
|
if ((event.button & 3) !== 0 && event.kind !== "release")
|
|
38814
39592
|
return;
|
|
38815
39593
|
const point = { x: event.x, y: event.y };
|
|
@@ -38826,7 +39604,7 @@ class TerminalApp {
|
|
|
38826
39604
|
this.#subagentGraphDragPoint = point;
|
|
38827
39605
|
if (!previous)
|
|
38828
39606
|
return;
|
|
38829
|
-
this.#
|
|
39607
|
+
this.#panActiveGraph(previous.x - point.x, previous.y - point.y);
|
|
38830
39608
|
}
|
|
38831
39609
|
async#copySelection(text2) {
|
|
38832
39610
|
try {
|
|
@@ -38887,8 +39665,8 @@ class TerminalApp {
|
|
|
38887
39665
|
this.#loadOlderMessages(topic.id, currentMax + 8);
|
|
38888
39666
|
}
|
|
38889
39667
|
#scroll(delta) {
|
|
38890
|
-
if (this.#state.overlay === "subagents") {
|
|
38891
|
-
this.#
|
|
39668
|
+
if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
|
|
39669
|
+
this.#panActiveGraph(0, -delta);
|
|
38892
39670
|
return;
|
|
38893
39671
|
}
|
|
38894
39672
|
if (this.#state.overlay === "background-session") {
|
|
@@ -38922,6 +39700,26 @@ class TerminalApp {
|
|
|
38922
39700
|
};
|
|
38923
39701
|
this.#queueRender();
|
|
38924
39702
|
}
|
|
39703
|
+
#panDecisionGraph(deltaX, deltaY) {
|
|
39704
|
+
const canvas = this.#state.decisionGraph;
|
|
39705
|
+
const viewportWidth = Math.max(1, (process.stdout.columns ?? 100) - 4);
|
|
39706
|
+
const viewportHeight = Math.max(1, (process.stdout.rows ?? 30) - 8);
|
|
39707
|
+
const current3 = this.#state.decisionGraphOffset;
|
|
39708
|
+
this.#state = {
|
|
39709
|
+
...this.#state,
|
|
39710
|
+
decisionGraphOffset: {
|
|
39711
|
+
x: Math.min(Math.max(0, (canvas?.width ?? 0) - viewportWidth), Math.max(0, current3.x + deltaX)),
|
|
39712
|
+
y: Math.min(Math.max(0, (canvas?.height ?? 0) - viewportHeight), Math.max(0, current3.y + deltaY))
|
|
39713
|
+
}
|
|
39714
|
+
};
|
|
39715
|
+
this.#queueRender();
|
|
39716
|
+
}
|
|
39717
|
+
#panActiveGraph(deltaX, deltaY) {
|
|
39718
|
+
if (this.#state.overlay === "decisions")
|
|
39719
|
+
this.#panDecisionGraph(deltaX, deltaY);
|
|
39720
|
+
else
|
|
39721
|
+
this.#panSubagentGraph(deltaX, deltaY);
|
|
39722
|
+
}
|
|
38925
39723
|
async#abort() {
|
|
38926
39724
|
const topic = activeTopic(this.#state);
|
|
38927
39725
|
let aborted = false;
|
|
@@ -39005,6 +39803,13 @@ class TerminalApp {
|
|
|
39005
39803
|
this.#subagentGraphGeneration += 1;
|
|
39006
39804
|
this.#subagentGraphAbortController?.abort();
|
|
39007
39805
|
this.#subagentGraphAbortController = null;
|
|
39806
|
+
this.#decisionGraphGeneration += 1;
|
|
39807
|
+
this.#decisionGraphAbortController?.abort();
|
|
39808
|
+
this.#decisionGraphAbortController = null;
|
|
39809
|
+
if (this.#subagentGraphSpacingTimer)
|
|
39810
|
+
clearTimeout(this.#subagentGraphSpacingTimer);
|
|
39811
|
+
if (this.#decisionGraphSpacingTimer)
|
|
39812
|
+
clearTimeout(this.#decisionGraphSpacingTimer);
|
|
39008
39813
|
if (this.#renderTimer)
|
|
39009
39814
|
clearTimeout(this.#renderTimer);
|
|
39010
39815
|
if (this.#inputCarryTimer)
|
|
@@ -39044,6 +39849,7 @@ var init_app = __esm(async () => {
|
|
|
39044
39849
|
init_color_depth();
|
|
39045
39850
|
await init_command_router();
|
|
39046
39851
|
init_commands();
|
|
39852
|
+
init_decision_graph();
|
|
39047
39853
|
init_path_suggest();
|
|
39048
39854
|
await init_render();
|
|
39049
39855
|
init_screen_renderer();
|
|
@@ -39646,8 +40452,8 @@ var init_commands2 = __esm(async () => {
|
|
|
39646
40452
|
|
|
39647
40453
|
// ../../adapters/telegram/src/mapping-store.ts
|
|
39648
40454
|
import { Database as Database2 } from "bun:sqlite";
|
|
39649
|
-
import { mkdirSync as
|
|
39650
|
-
import { dirname as
|
|
40455
|
+
import { mkdirSync as mkdirSync32 } from "fs";
|
|
40456
|
+
import { dirname as dirname21, join as join42 } from "path";
|
|
39651
40457
|
function outboxRowToEntry(row) {
|
|
39652
40458
|
return {
|
|
39653
40459
|
id: row.id,
|
|
@@ -39713,9 +40519,9 @@ function migrateOutboxSchema(db4) {
|
|
|
39713
40519
|
db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
|
|
39714
40520
|
}
|
|
39715
40521
|
function openMappingStore(path) {
|
|
39716
|
-
const dbPath = path ??
|
|
40522
|
+
const dbPath = path ?? join42(DATA_DIR, "adapter-telegram.db");
|
|
39717
40523
|
if (dbPath !== ":memory:")
|
|
39718
|
-
|
|
40524
|
+
mkdirSync32(dirname21(dbPath), { recursive: true });
|
|
39719
40525
|
const db4 = new Database2(dbPath);
|
|
39720
40526
|
migrateLegacySchema(db4);
|
|
39721
40527
|
createTables(db4);
|
|
@@ -40073,7 +40879,7 @@ function renderOutbound(text2, maxLen = 4096) {
|
|
|
40073
40879
|
}
|
|
40074
40880
|
|
|
40075
40881
|
// ../../adapters/telegram/src/adapter.ts
|
|
40076
|
-
import { existsSync as
|
|
40882
|
+
import { existsSync as existsSync36 } from "fs";
|
|
40077
40883
|
import { basename as basename12, extname as extname5 } from "path";
|
|
40078
40884
|
function startTelegramAdapter(opts) {
|
|
40079
40885
|
const { client, forumChatId } = opts;
|
|
@@ -40571,7 +41377,7 @@ function startTelegramAdapter(opts) {
|
|
|
40571
41377
|
logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
|
|
40572
41378
|
return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
|
|
40573
41379
|
}
|
|
40574
|
-
if (!
|
|
41380
|
+
if (!existsSync36(path)) {
|
|
40575
41381
|
const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
|
|
40576
41382
|
const messageId = sentMessageId(sent);
|
|
40577
41383
|
return {
|
|
@@ -41591,11 +42397,11 @@ var exports_join_status = {};
|
|
|
41591
42397
|
__export(exports_join_status, {
|
|
41592
42398
|
hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
|
|
41593
42399
|
});
|
|
41594
|
-
import { existsSync as
|
|
42400
|
+
import { existsSync as existsSync37 } from "fs";
|
|
41595
42401
|
import { resolve as resolve23 } from "path";
|
|
41596
42402
|
function hasConfiguredOtiumJoin() {
|
|
41597
42403
|
const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
|
|
41598
|
-
return envJoin ||
|
|
42404
|
+
return envJoin || existsSync37(resolve23(DATA_DIR, "otium-join.json"));
|
|
41599
42405
|
}
|
|
41600
42406
|
var init_join_status = __esm(() => {
|
|
41601
42407
|
init_config_public();
|
|
@@ -41613,9 +42419,9 @@ function firstCell() {
|
|
|
41613
42419
|
return cell;
|
|
41614
42420
|
return null;
|
|
41615
42421
|
}
|
|
41616
|
-
function attachOtiumCentralCell(
|
|
41617
|
-
cells.set(
|
|
41618
|
-
join:
|
|
42422
|
+
function attachOtiumCentralCell(join43) {
|
|
42423
|
+
cells.set(join43.cellId, {
|
|
42424
|
+
join: join43,
|
|
41619
42425
|
nodesCache: null,
|
|
41620
42426
|
verifyCache: new Map,
|
|
41621
42427
|
tokenCache: new Map
|
|
@@ -41627,10 +42433,10 @@ function detachOtiumCentralCell(cellId) {
|
|
|
41627
42433
|
function attachedOtiumCells() {
|
|
41628
42434
|
return [...cells.values()].map((cell) => cell.join);
|
|
41629
42435
|
}
|
|
41630
|
-
function configureOtiumCentral(
|
|
42436
|
+
function configureOtiumCentral(join43) {
|
|
41631
42437
|
cells.clear();
|
|
41632
|
-
if (
|
|
41633
|
-
attachOtiumCentralCell(
|
|
42438
|
+
if (join43)
|
|
42439
|
+
attachOtiumCentralCell(join43);
|
|
41634
42440
|
}
|
|
41635
42441
|
function isOtiumCentralConfigured() {
|
|
41636
42442
|
return cells.size > 0;
|
|
@@ -41971,6 +42777,13 @@ var init_canonical_mcp_bridge = __esm(async () => {
|
|
|
41971
42777
|
CAPABILITY_TTL_MS = 60 * 60 * 1000;
|
|
41972
42778
|
TOOL_ALLOWLIST = {
|
|
41973
42779
|
task: new Set(["task_create", "task_update", "task_list", "task_get", "task_delete"]),
|
|
42780
|
+
decision: new Set([
|
|
42781
|
+
"decision_create",
|
|
42782
|
+
"decision_update",
|
|
42783
|
+
"decision_list",
|
|
42784
|
+
"decision_get",
|
|
42785
|
+
"decision_delete"
|
|
42786
|
+
]),
|
|
41974
42787
|
wiki: new Set([
|
|
41975
42788
|
"wiki_query",
|
|
41976
42789
|
"wiki_read",
|
|
@@ -42037,20 +42850,20 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
|
|
|
42037
42850
|
import {
|
|
42038
42851
|
chmodSync as chmodSync7,
|
|
42039
42852
|
closeSync as closeSync5,
|
|
42040
|
-
existsSync as
|
|
42853
|
+
existsSync as existsSync38,
|
|
42041
42854
|
fsyncSync as fsyncSync2,
|
|
42042
42855
|
linkSync,
|
|
42043
42856
|
lstatSync,
|
|
42044
|
-
mkdirSync as
|
|
42857
|
+
mkdirSync as mkdirSync33,
|
|
42045
42858
|
openSync as openSync5,
|
|
42046
|
-
readFileSync as
|
|
42047
|
-
renameSync as
|
|
42859
|
+
readFileSync as readFileSync29,
|
|
42860
|
+
renameSync as renameSync17,
|
|
42048
42861
|
rmSync as rmSync10,
|
|
42049
42862
|
statSync as statSync20,
|
|
42050
42863
|
unlinkSync as unlinkSync23,
|
|
42051
|
-
writeFileSync as
|
|
42864
|
+
writeFileSync as writeFileSync25
|
|
42052
42865
|
} from "fs";
|
|
42053
|
-
import { dirname as
|
|
42866
|
+
import { dirname as dirname22, resolve as resolve24 } from "path";
|
|
42054
42867
|
function joinFilePath() {
|
|
42055
42868
|
return resolve24(DATA_DIR, "otium-join.json");
|
|
42056
42869
|
}
|
|
@@ -42123,13 +42936,13 @@ function withJoinCredentialLock(operation) {
|
|
|
42123
42936
|
const lockPath = joinLockPath();
|
|
42124
42937
|
const ownerPath = resolve24(lockPath, "owner.json");
|
|
42125
42938
|
const owner = { pid: process.pid, token: randomUUID28() };
|
|
42126
|
-
|
|
42939
|
+
mkdirSync33(dirname22(lockPath), { recursive: true });
|
|
42127
42940
|
for (let attempt = 0;; attempt += 1) {
|
|
42128
42941
|
let created = false;
|
|
42129
42942
|
try {
|
|
42130
|
-
|
|
42943
|
+
mkdirSync33(lockPath, { mode: 448 });
|
|
42131
42944
|
created = true;
|
|
42132
|
-
|
|
42945
|
+
writeFileSync25(ownerPath, `${JSON.stringify(owner)}
|
|
42133
42946
|
`, { mode: 384 });
|
|
42134
42947
|
const ownerFd = openSync5(ownerPath, "r");
|
|
42135
42948
|
try {
|
|
@@ -42147,7 +42960,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42147
42960
|
throw error2;
|
|
42148
42961
|
let current3 = null;
|
|
42149
42962
|
try {
|
|
42150
|
-
current3 = JSON.parse(
|
|
42963
|
+
current3 = JSON.parse(readFileSync29(ownerPath, "utf8"));
|
|
42151
42964
|
} catch {}
|
|
42152
42965
|
let ageMs;
|
|
42153
42966
|
try {
|
|
@@ -42165,7 +42978,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42165
42978
|
}
|
|
42166
42979
|
const stalePath = `${lockPath}.stale.${process.pid}.${randomUUID28()}`;
|
|
42167
42980
|
try {
|
|
42168
|
-
|
|
42981
|
+
renameSync17(lockPath, stalePath);
|
|
42169
42982
|
rmSync10(stalePath, { recursive: true, force: true });
|
|
42170
42983
|
} catch (staleError) {
|
|
42171
42984
|
if (staleError.code !== "ENOENT")
|
|
@@ -42177,7 +42990,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42177
42990
|
return operation();
|
|
42178
42991
|
} finally {
|
|
42179
42992
|
try {
|
|
42180
|
-
const current3 = JSON.parse(
|
|
42993
|
+
const current3 = JSON.parse(readFileSync29(ownerPath, "utf8"));
|
|
42181
42994
|
if (current3.pid === owner.pid && current3.token === owner.token) {
|
|
42182
42995
|
rmSync10(lockPath, { recursive: true, force: true });
|
|
42183
42996
|
}
|
|
@@ -42187,22 +43000,22 @@ function withJoinCredentialLock(operation) {
|
|
|
42187
43000
|
function joinsEqual(left, right) {
|
|
42188
43001
|
return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
|
|
42189
43002
|
}
|
|
42190
|
-
function normalizedJoin(
|
|
43003
|
+
function normalizedJoin(join43) {
|
|
42191
43004
|
return normalizeJoin({
|
|
42192
|
-
v:
|
|
42193
|
-
central:
|
|
42194
|
-
relay:
|
|
42195
|
-
cellId:
|
|
42196
|
-
secret:
|
|
43005
|
+
v: join43.v,
|
|
43006
|
+
central: join43.central,
|
|
43007
|
+
relay: join43.relay,
|
|
43008
|
+
cellId: join43.cellId,
|
|
43009
|
+
secret: join43.secret
|
|
42197
43010
|
});
|
|
42198
43011
|
}
|
|
42199
|
-
function joinCredentialDigest(
|
|
42200
|
-
return createHash11("sha256").update(JSON.stringify(normalizedJoin(
|
|
43012
|
+
function joinCredentialDigest(join43) {
|
|
43013
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join43))).digest("base64url");
|
|
42201
43014
|
}
|
|
42202
43015
|
function readPersistedJoins(path = joinFilePath()) {
|
|
42203
|
-
if (!
|
|
43016
|
+
if (!existsSync38(path))
|
|
42204
43017
|
return [];
|
|
42205
|
-
const parsed = JSON.parse(
|
|
43018
|
+
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
42206
43019
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
42207
43020
|
throw new Error("persisted join credentials are not a JSON object");
|
|
42208
43021
|
}
|
|
@@ -42216,9 +43029,9 @@ function readPersistedJoins(path = joinFilePath()) {
|
|
|
42216
43029
|
return normalizeJoin(entry);
|
|
42217
43030
|
});
|
|
42218
43031
|
}
|
|
42219
|
-
function isJoinPersisted(
|
|
43032
|
+
function isJoinPersisted(join43) {
|
|
42220
43033
|
try {
|
|
42221
|
-
const normalized = normalizedJoin(
|
|
43034
|
+
const normalized = normalizedJoin(join43);
|
|
42222
43035
|
return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
|
|
42223
43036
|
} catch {
|
|
42224
43037
|
return false;
|
|
@@ -42234,19 +43047,19 @@ function fsyncPath(target) {
|
|
|
42234
43047
|
}
|
|
42235
43048
|
function writeJoins(joins, allowOverwrite) {
|
|
42236
43049
|
const path = joinFilePath();
|
|
42237
|
-
const directory =
|
|
43050
|
+
const directory = dirname22(path);
|
|
42238
43051
|
const temporaryPath = resolve24(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
|
|
42239
43052
|
const payload = { v: JOIN_FILE_VERSION, joins };
|
|
42240
43053
|
let fd;
|
|
42241
43054
|
try {
|
|
42242
43055
|
fd = openSync5(temporaryPath, "wx", 384);
|
|
42243
|
-
|
|
43056
|
+
writeFileSync25(fd, `${JSON.stringify(payload, null, 2)}
|
|
42244
43057
|
`, "utf8");
|
|
42245
43058
|
fsyncSync2(fd);
|
|
42246
43059
|
closeSync5(fd);
|
|
42247
43060
|
fd = undefined;
|
|
42248
43061
|
if (allowOverwrite) {
|
|
42249
|
-
|
|
43062
|
+
renameSync17(temporaryPath, path);
|
|
42250
43063
|
} else {
|
|
42251
43064
|
linkSync(temporaryPath, path);
|
|
42252
43065
|
unlinkSync23(temporaryPath);
|
|
@@ -42256,18 +43069,18 @@ function writeJoins(joins, allowOverwrite) {
|
|
|
42256
43069
|
} catch (error2) {
|
|
42257
43070
|
if (fd !== undefined)
|
|
42258
43071
|
closeSync5(fd);
|
|
42259
|
-
if (
|
|
43072
|
+
if (existsSync38(temporaryPath))
|
|
42260
43073
|
unlinkSync23(temporaryPath);
|
|
42261
43074
|
throw error2;
|
|
42262
43075
|
}
|
|
42263
43076
|
return path;
|
|
42264
43077
|
}
|
|
42265
|
-
function saveJoinWhileLocked(
|
|
43078
|
+
function saveJoinWhileLocked(join43, options = {}) {
|
|
42266
43079
|
const path = joinFilePath();
|
|
42267
|
-
const directory =
|
|
42268
|
-
const normalized = normalizedJoin(
|
|
42269
|
-
|
|
42270
|
-
if (!
|
|
43080
|
+
const directory = dirname22(path);
|
|
43081
|
+
const normalized = normalizedJoin(join43);
|
|
43082
|
+
mkdirSync33(directory, { recursive: true });
|
|
43083
|
+
if (!existsSync38(path))
|
|
42271
43084
|
return writeJoins([normalized], false);
|
|
42272
43085
|
if (lstatSync(path).isSymbolicLink()) {
|
|
42273
43086
|
throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
|
|
@@ -42295,13 +43108,13 @@ function saveJoinWhileLocked(join42, options = {}) {
|
|
|
42295
43108
|
const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
|
|
42296
43109
|
return writeJoins(next, true);
|
|
42297
43110
|
}
|
|
42298
|
-
function saveJoin(
|
|
42299
|
-
return withJoinCredentialLock(() => saveJoinWhileLocked(
|
|
43111
|
+
function saveJoin(join43, options = {}) {
|
|
43112
|
+
return withJoinCredentialLock(() => saveJoinWhileLocked(join43, options));
|
|
42300
43113
|
}
|
|
42301
43114
|
function removeJoin(cellId) {
|
|
42302
43115
|
return withJoinCredentialLock(() => {
|
|
42303
43116
|
const path = joinFilePath();
|
|
42304
|
-
if (!
|
|
43117
|
+
if (!existsSync38(path))
|
|
42305
43118
|
return false;
|
|
42306
43119
|
if (lstatSync(path).isSymbolicLink()) {
|
|
42307
43120
|
throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
|
|
@@ -42309,7 +43122,7 @@ function removeJoin(cellId) {
|
|
|
42309
43122
|
if (cellId) {
|
|
42310
43123
|
let remaining;
|
|
42311
43124
|
try {
|
|
42312
|
-
remaining = readPersistedJoins(path).filter((
|
|
43125
|
+
remaining = readPersistedJoins(path).filter((join43) => join43.cellId !== cellId);
|
|
42313
43126
|
} catch {
|
|
42314
43127
|
return false;
|
|
42315
43128
|
}
|
|
@@ -42321,7 +43134,7 @@ function removeJoin(cellId) {
|
|
|
42321
43134
|
}
|
|
42322
43135
|
}
|
|
42323
43136
|
unlinkSync23(path);
|
|
42324
|
-
fsyncPath(
|
|
43137
|
+
fsyncPath(dirname22(path));
|
|
42325
43138
|
return true;
|
|
42326
43139
|
});
|
|
42327
43140
|
}
|
|
@@ -42342,7 +43155,7 @@ function loadJoins() {
|
|
|
42342
43155
|
logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
|
|
42343
43156
|
}
|
|
42344
43157
|
const path = joinFilePath();
|
|
42345
|
-
if (!
|
|
43158
|
+
if (!existsSync38(path))
|
|
42346
43159
|
return [];
|
|
42347
43160
|
try {
|
|
42348
43161
|
return readPersistedJoins(path);
|
|
@@ -42361,8 +43174,8 @@ var init_join = __esm(async () => {
|
|
|
42361
43174
|
|
|
42362
43175
|
// ../../adapters/otium/src/peer-files.ts
|
|
42363
43176
|
import { randomUUID as randomUUID29 } from "crypto";
|
|
42364
|
-
import { copyFileSync as copyFileSync5, mkdirSync as
|
|
42365
|
-
import { basename as basename13, extname as extname6, join as
|
|
43177
|
+
import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync34, rmSync as rmSync11, statSync as statSync21 } from "fs";
|
|
43178
|
+
import { basename as basename13, extname as extname6, join as join43 } from "path";
|
|
42366
43179
|
function fileType(mimeType) {
|
|
42367
43180
|
if (mimeType.startsWith("image/"))
|
|
42368
43181
|
return "image";
|
|
@@ -42427,8 +43240,8 @@ function recordFile(args) {
|
|
|
42427
43240
|
}
|
|
42428
43241
|
function insertLocalFile(args) {
|
|
42429
43242
|
const id = randomUUID29();
|
|
42430
|
-
|
|
42431
|
-
const path =
|
|
43243
|
+
mkdirSync34(PEER_FILES_DIR, { recursive: true });
|
|
43244
|
+
const path = join43(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
|
|
42432
43245
|
copyFileSync5(args.sourcePath, path);
|
|
42433
43246
|
return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
|
|
42434
43247
|
}
|
|
@@ -42474,7 +43287,7 @@ function installPeerFileHooks() {
|
|
|
42474
43287
|
var PEER_FILES_DIR;
|
|
42475
43288
|
var init_peer_files = __esm(async () => {
|
|
42476
43289
|
await init_src();
|
|
42477
|
-
PEER_FILES_DIR =
|
|
43290
|
+
PEER_FILES_DIR = join43(DATA_DIR, "otium-peer-files");
|
|
42478
43291
|
db.exec(`
|
|
42479
43292
|
CREATE TABLE IF NOT EXISTS otium_peer_files (
|
|
42480
43293
|
id TEXT PRIMARY KEY,
|
|
@@ -42866,8 +43679,8 @@ var init_store2 = __esm(async () => {
|
|
|
42866
43679
|
|
|
42867
43680
|
// ../../adapters/otium/src/workspace-scope.ts
|
|
42868
43681
|
import { createHash as createHash13 } from "crypto";
|
|
42869
|
-
import { mkdirSync as
|
|
42870
|
-
import { dirname as
|
|
43682
|
+
import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
|
|
43683
|
+
import { dirname as dirname23, resolve as resolve25 } from "path";
|
|
42871
43684
|
function surfaceScopeFor(central, workspaceId) {
|
|
42872
43685
|
const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
|
|
42873
43686
|
${workspaceId.trim()}`).digest("hex");
|
|
@@ -42878,7 +43691,7 @@ function scopeCachePath() {
|
|
|
42878
43691
|
}
|
|
42879
43692
|
function readCache() {
|
|
42880
43693
|
try {
|
|
42881
|
-
const parsed = JSON.parse(
|
|
43694
|
+
const parsed = JSON.parse(readFileSync30(scopeCachePath(), "utf-8"));
|
|
42882
43695
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
42883
43696
|
return {};
|
|
42884
43697
|
return parsed;
|
|
@@ -42886,43 +43699,43 @@ function readCache() {
|
|
|
42886
43699
|
return {};
|
|
42887
43700
|
}
|
|
42888
43701
|
}
|
|
42889
|
-
function cachedSurfaceScope(
|
|
42890
|
-
const record = readCache()[
|
|
42891
|
-
if (!record || record.central !==
|
|
43702
|
+
function cachedSurfaceScope(join44) {
|
|
43703
|
+
const record = readCache()[join44.cellId];
|
|
43704
|
+
if (!record || record.central !== join44.central || !record.workspaceId)
|
|
42892
43705
|
return null;
|
|
42893
43706
|
return surfaceScopeFor(record.central, record.workspaceId);
|
|
42894
43707
|
}
|
|
42895
|
-
function cacheSurfaceScope(
|
|
42896
|
-
const scope = surfaceScopeFor(
|
|
43708
|
+
function cacheSurfaceScope(join44, workspaceId) {
|
|
43709
|
+
const scope = surfaceScopeFor(join44.central, workspaceId);
|
|
42897
43710
|
const path = scopeCachePath();
|
|
42898
43711
|
try {
|
|
42899
|
-
|
|
43712
|
+
mkdirSync35(dirname23(path), { recursive: true });
|
|
42900
43713
|
const cache = readCache();
|
|
42901
|
-
cache[
|
|
42902
|
-
|
|
43714
|
+
cache[join44.cellId] = { central: join44.central, workspaceId, scope };
|
|
43715
|
+
writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
|
|
42903
43716
|
`, { mode: 384 });
|
|
42904
43717
|
} catch (err2) {
|
|
42905
43718
|
logger.warn({ err: err2, path }, "otium: could not cache the workspace scope");
|
|
42906
43719
|
}
|
|
42907
43720
|
return scope;
|
|
42908
43721
|
}
|
|
42909
|
-
async function resolveSurfaceScope(
|
|
42910
|
-
const cached = cachedSurfaceScope(
|
|
43722
|
+
async function resolveSurfaceScope(join44) {
|
|
43723
|
+
const cached = cachedSurfaceScope(join44);
|
|
42911
43724
|
if (cached)
|
|
42912
43725
|
return cached;
|
|
42913
43726
|
try {
|
|
42914
|
-
const workspaceId = await peerWorkspaceIdForCell(
|
|
43727
|
+
const workspaceId = await peerWorkspaceIdForCell(join44.cellId);
|
|
42915
43728
|
if (!workspaceId)
|
|
42916
43729
|
return null;
|
|
42917
|
-
return cacheSurfaceScope(
|
|
43730
|
+
return cacheSurfaceScope(join44, workspaceId);
|
|
42918
43731
|
} catch (err2) {
|
|
42919
43732
|
logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
|
|
42920
43733
|
return null;
|
|
42921
43734
|
}
|
|
42922
43735
|
}
|
|
42923
43736
|
function surfaceScopeForCell(cellId) {
|
|
42924
|
-
const
|
|
42925
|
-
return
|
|
43737
|
+
const join44 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
|
|
43738
|
+
return join44 ? cachedSurfaceScope(join44) : null;
|
|
42926
43739
|
}
|
|
42927
43740
|
function unscopedRoomsAddressable() {
|
|
42928
43741
|
return attachedOtiumCells().length < 2;
|
|
@@ -43935,17 +44748,17 @@ import {
|
|
|
43935
44748
|
import {
|
|
43936
44749
|
chmodSync as chmodSync8,
|
|
43937
44750
|
closeSync as closeSync6,
|
|
43938
|
-
existsSync as
|
|
44751
|
+
existsSync as existsSync39,
|
|
43939
44752
|
fsyncSync as fsyncSync3,
|
|
43940
44753
|
linkSync as linkSync2,
|
|
43941
|
-
mkdirSync as
|
|
44754
|
+
mkdirSync as mkdirSync36,
|
|
43942
44755
|
openSync as openSync6,
|
|
43943
|
-
readFileSync as
|
|
43944
|
-
renameSync as
|
|
44756
|
+
readFileSync as readFileSync31,
|
|
44757
|
+
renameSync as renameSync18,
|
|
43945
44758
|
unlinkSync as unlinkSync24,
|
|
43946
|
-
writeFileSync as
|
|
44759
|
+
writeFileSync as writeFileSync27
|
|
43947
44760
|
} from "fs";
|
|
43948
|
-
import { dirname as
|
|
44761
|
+
import { dirname as dirname24, resolve as resolve26 } from "path";
|
|
43949
44762
|
function parseEnrollmentInvite(code) {
|
|
43950
44763
|
let parsed;
|
|
43951
44764
|
try {
|
|
@@ -43970,10 +44783,10 @@ function pendingEnrollmentPath() {
|
|
|
43970
44783
|
}
|
|
43971
44784
|
function isEnrollmentPending(invite) {
|
|
43972
44785
|
const path = pendingEnrollmentPath();
|
|
43973
|
-
if (!
|
|
44786
|
+
if (!existsSync39(path))
|
|
43974
44787
|
return false;
|
|
43975
44788
|
try {
|
|
43976
|
-
const saved = JSON.parse(
|
|
44789
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
43977
44790
|
return saved.central === invite.central && saved.token === invite.token;
|
|
43978
44791
|
} catch {
|
|
43979
44792
|
return false;
|
|
@@ -43981,8 +44794,8 @@ function isEnrollmentPending(invite) {
|
|
|
43981
44794
|
}
|
|
43982
44795
|
function loadOrCreatePending(invite, nodeName) {
|
|
43983
44796
|
const path = pendingEnrollmentPath();
|
|
43984
|
-
if (
|
|
43985
|
-
const saved = JSON.parse(
|
|
44797
|
+
if (existsSync39(path)) {
|
|
44798
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
43986
44799
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
43987
44800
|
return saved;
|
|
43988
44801
|
throw new Error(`another Otium enrollment is pending at ${path}`);
|
|
@@ -43995,12 +44808,12 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
43995
44808
|
publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
|
|
43996
44809
|
...nodeName ? { nodeName } : {}
|
|
43997
44810
|
};
|
|
43998
|
-
|
|
43999
|
-
const temporaryPath = resolve26(
|
|
44811
|
+
mkdirSync36(dirname24(path), { recursive: true });
|
|
44812
|
+
const temporaryPath = resolve26(dirname24(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
44000
44813
|
let fd;
|
|
44001
44814
|
try {
|
|
44002
44815
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
44003
|
-
|
|
44816
|
+
writeFileSync27(fd, `${JSON.stringify(pending2, null, 2)}
|
|
44004
44817
|
`, "utf8");
|
|
44005
44818
|
fsyncSync3(fd);
|
|
44006
44819
|
closeSync6(fd);
|
|
@@ -44008,7 +44821,7 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44008
44821
|
linkSync2(temporaryPath, path);
|
|
44009
44822
|
unlinkSync24(temporaryPath);
|
|
44010
44823
|
chmodSync8(path, 384);
|
|
44011
|
-
const directoryFd = openSync6(
|
|
44824
|
+
const directoryFd = openSync6(dirname24(path), "r");
|
|
44012
44825
|
try {
|
|
44013
44826
|
fsyncSync3(directoryFd);
|
|
44014
44827
|
} finally {
|
|
@@ -44017,10 +44830,10 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44017
44830
|
} catch (error2) {
|
|
44018
44831
|
if (fd !== undefined)
|
|
44019
44832
|
closeSync6(fd);
|
|
44020
|
-
if (
|
|
44833
|
+
if (existsSync39(temporaryPath))
|
|
44021
44834
|
unlinkSync24(temporaryPath);
|
|
44022
|
-
if (error2.code === "EEXIST" &&
|
|
44023
|
-
const saved = JSON.parse(
|
|
44835
|
+
if (error2.code === "EEXIST" && existsSync39(path)) {
|
|
44836
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
44024
44837
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
44025
44838
|
return saved;
|
|
44026
44839
|
}
|
|
@@ -44030,17 +44843,17 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44030
44843
|
}
|
|
44031
44844
|
function replacePendingEnrollment(pending2) {
|
|
44032
44845
|
const path = pendingEnrollmentPath();
|
|
44033
|
-
const directory =
|
|
44846
|
+
const directory = dirname24(path);
|
|
44034
44847
|
const temporaryPath = resolve26(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
44035
44848
|
let fd;
|
|
44036
44849
|
try {
|
|
44037
44850
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
44038
|
-
|
|
44851
|
+
writeFileSync27(fd, `${JSON.stringify(pending2, null, 2)}
|
|
44039
44852
|
`, "utf8");
|
|
44040
44853
|
fsyncSync3(fd);
|
|
44041
44854
|
closeSync6(fd);
|
|
44042
44855
|
fd = undefined;
|
|
44043
|
-
|
|
44856
|
+
renameSync18(temporaryPath, path);
|
|
44044
44857
|
const directoryFd = openSync6(directory, "r");
|
|
44045
44858
|
try {
|
|
44046
44859
|
fsyncSync3(directoryFd);
|
|
@@ -44050,18 +44863,18 @@ function replacePendingEnrollment(pending2) {
|
|
|
44050
44863
|
} catch (error2) {
|
|
44051
44864
|
if (fd !== undefined)
|
|
44052
44865
|
closeSync6(fd);
|
|
44053
|
-
if (
|
|
44866
|
+
if (existsSync39(temporaryPath))
|
|
44054
44867
|
unlinkSync24(temporaryPath);
|
|
44055
44868
|
throw error2;
|
|
44056
44869
|
}
|
|
44057
44870
|
}
|
|
44058
|
-
function recordClaimedCredential(pending2,
|
|
44871
|
+
function recordClaimedCredential(pending2, join44) {
|
|
44059
44872
|
withJoinCredentialLock(() => {
|
|
44060
|
-
const current3 = JSON.parse(
|
|
44873
|
+
const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
|
|
44061
44874
|
if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
|
|
44062
44875
|
throw new Error("pending Otium enrollment changed while its claim was in flight");
|
|
44063
44876
|
}
|
|
44064
|
-
const claimed = { digest: joinCredentialDigest(
|
|
44877
|
+
const claimed = { digest: joinCredentialDigest(join44), cellId: join44.cellId };
|
|
44065
44878
|
if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
|
|
44066
44879
|
throw new Error("central returned different credentials for an idempotent enrollment claim");
|
|
44067
44880
|
}
|
|
@@ -44119,36 +44932,36 @@ async function claimEnrollment(invite, nodeName) {
|
|
|
44119
44932
|
const secret = openCredential(credential, pending2.privateKey);
|
|
44120
44933
|
if (!secret.startsWith("rcs_"))
|
|
44121
44934
|
throw new Error("central returned an invalid runtime credential");
|
|
44122
|
-
const
|
|
44935
|
+
const join44 = {
|
|
44123
44936
|
v: 2,
|
|
44124
44937
|
central: invite.central,
|
|
44125
44938
|
relay: String(response.relayUrl),
|
|
44126
44939
|
cellId: String(response.cell?.id),
|
|
44127
44940
|
secret
|
|
44128
44941
|
};
|
|
44129
|
-
if (!
|
|
44942
|
+
if (!join44.relay || !join44.cellId || join44.cellId === "undefined") {
|
|
44130
44943
|
throw new Error("central returned an incomplete enrollment response");
|
|
44131
44944
|
}
|
|
44132
|
-
assertSecureRelayUrl(
|
|
44133
|
-
recordClaimedCredential(pending2,
|
|
44134
|
-
return
|
|
44945
|
+
assertSecureRelayUrl(join44.relay);
|
|
44946
|
+
recordClaimedCredential(pending2, join44);
|
|
44947
|
+
return join44;
|
|
44135
44948
|
}
|
|
44136
|
-
function commitEnrollment(
|
|
44949
|
+
function commitEnrollment(join44, options = {}) {
|
|
44137
44950
|
return withJoinCredentialLock(() => {
|
|
44138
44951
|
const pendingPath = pendingEnrollmentPath();
|
|
44139
|
-
const pending2 =
|
|
44140
|
-
const digest = joinCredentialDigest(
|
|
44141
|
-
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !==
|
|
44952
|
+
const pending2 = existsSync39(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
|
|
44953
|
+
const digest = joinCredentialDigest(join44);
|
|
44954
|
+
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join44.cellId)) {
|
|
44142
44955
|
throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
|
|
44143
44956
|
}
|
|
44144
|
-
const path = saveJoinWhileLocked(
|
|
44145
|
-
if (!isJoinPersisted(
|
|
44957
|
+
const path = saveJoinWhileLocked(join44, options);
|
|
44958
|
+
if (!isJoinPersisted(join44)) {
|
|
44146
44959
|
throw new Error("Otium join credentials were not durably persisted");
|
|
44147
44960
|
}
|
|
44148
44961
|
if (!pending2)
|
|
44149
44962
|
return path;
|
|
44150
44963
|
unlinkSync24(pendingPath);
|
|
44151
|
-
const directoryFd = openSync6(
|
|
44964
|
+
const directoryFd = openSync6(dirname24(pendingPath), "r");
|
|
44152
44965
|
try {
|
|
44153
44966
|
fsyncSync3(directoryFd);
|
|
44154
44967
|
} finally {
|
|
@@ -44729,24 +45542,24 @@ function refreshDefaultSurfaceScope() {
|
|
|
44729
45542
|
setSurfaceScopeRequired(scopes.length > 1);
|
|
44730
45543
|
}
|
|
44731
45544
|
function startOtiumNodeRuntime(options) {
|
|
44732
|
-
const { join:
|
|
44733
|
-
attachOtiumCentralCell(
|
|
45545
|
+
const { join: join44 } = options;
|
|
45546
|
+
attachOtiumCentralCell(join44);
|
|
44734
45547
|
const releaseGlobals = acquireGlobalOtiumServices();
|
|
44735
45548
|
let stopped = false;
|
|
44736
|
-
logger.info({ central:
|
|
44737
|
-
mountedScopes.set(
|
|
45549
|
+
logger.info({ central: join44.central, cellId: join44.cellId }, "otium: worker mode enabled");
|
|
45550
|
+
mountedScopes.set(join44.cellId, cachedSurfaceScope(join44));
|
|
44738
45551
|
refreshDefaultSurfaceScope();
|
|
44739
|
-
selfPeerNodeForCell(
|
|
45552
|
+
selfPeerNodeForCell(join44.cellId).then((self) => {
|
|
44740
45553
|
if (self) {
|
|
44741
45554
|
logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
|
|
44742
45555
|
}
|
|
44743
45556
|
}).catch((err2) => {
|
|
44744
45557
|
logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
|
|
44745
45558
|
});
|
|
44746
|
-
resolveSurfaceScope(
|
|
45559
|
+
resolveSurfaceScope(join44).then((scope) => {
|
|
44747
45560
|
if (!scope || stopped)
|
|
44748
45561
|
return;
|
|
44749
|
-
mountedScopes.set(
|
|
45562
|
+
mountedScopes.set(join44.cellId, scope);
|
|
44750
45563
|
refreshDefaultSurfaceScope();
|
|
44751
45564
|
if (mountedScopes.size === 1)
|
|
44752
45565
|
stampUnscopedOtiumTopics(scope);
|
|
@@ -44755,14 +45568,14 @@ function startOtiumNodeRuntime(options) {
|
|
|
44755
45568
|
});
|
|
44756
45569
|
return {
|
|
44757
45570
|
name: "otium",
|
|
44758
|
-
join:
|
|
45571
|
+
join: join44,
|
|
44759
45572
|
stop: () => {
|
|
44760
45573
|
if (stopped)
|
|
44761
45574
|
return;
|
|
44762
45575
|
stopped = true;
|
|
44763
|
-
mountedScopes.delete(
|
|
45576
|
+
mountedScopes.delete(join44.cellId);
|
|
44764
45577
|
refreshDefaultSurfaceScope();
|
|
44765
|
-
detachOtiumCentralCell(
|
|
45578
|
+
detachOtiumCentralCell(join44.cellId);
|
|
44766
45579
|
releaseGlobals();
|
|
44767
45580
|
}
|
|
44768
45581
|
};
|
|
@@ -44857,14 +45670,14 @@ __export(exports_node_runtime, {
|
|
|
44857
45670
|
function sameCredentials(left, right) {
|
|
44858
45671
|
return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
|
|
44859
45672
|
}
|
|
44860
|
-
function attachOtiumWorkspace(
|
|
44861
|
-
const current3 = mounted.get(
|
|
45673
|
+
function attachOtiumWorkspace(join44) {
|
|
45674
|
+
const current3 = mounted.get(join44.cellId);
|
|
44862
45675
|
if (current3) {
|
|
44863
|
-
if (sameCredentials(current3.join,
|
|
45676
|
+
if (sameCredentials(current3.join, join44))
|
|
44864
45677
|
return false;
|
|
44865
|
-
detachOtiumWorkspace(
|
|
45678
|
+
detachOtiumWorkspace(join44.cellId);
|
|
44866
45679
|
}
|
|
44867
|
-
mounted.set(
|
|
45680
|
+
mounted.set(join44.cellId, startOtiumNodeRuntime({ join: join44 }));
|
|
44868
45681
|
return true;
|
|
44869
45682
|
}
|
|
44870
45683
|
function detachOtiumWorkspace(cellId) {
|
|
@@ -44879,7 +45692,7 @@ function mountedOtiumWorkspaces() {
|
|
|
44879
45692
|
return [...mounted.values()].map((runtime2) => runtime2.join);
|
|
44880
45693
|
}
|
|
44881
45694
|
function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
44882
|
-
const wanted = new Map(joins.map((
|
|
45695
|
+
const wanted = new Map(joins.map((join44) => [join44.cellId, join44]));
|
|
44883
45696
|
const detached = [];
|
|
44884
45697
|
for (const cellId of [...mounted.keys()]) {
|
|
44885
45698
|
if (wanted.has(cellId))
|
|
@@ -44888,8 +45701,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
|
44888
45701
|
detached.push(cellId);
|
|
44889
45702
|
}
|
|
44890
45703
|
const attached = [];
|
|
44891
|
-
for (const [cellId,
|
|
44892
|
-
if (attachOtiumWorkspace(
|
|
45704
|
+
for (const [cellId, join44] of wanted) {
|
|
45705
|
+
if (attachOtiumWorkspace(join44))
|
|
44893
45706
|
attached.push(cellId);
|
|
44894
45707
|
}
|
|
44895
45708
|
if (attached.length > 0 || detached.length > 0) {
|
|
@@ -44912,9 +45725,9 @@ async function handleOtiumAdapterControlRequest(req) {
|
|
|
44912
45725
|
if (req.method === "GET") {
|
|
44913
45726
|
return Response.json({
|
|
44914
45727
|
ok: true,
|
|
44915
|
-
workspaces: mountedOtiumWorkspaces().map((
|
|
44916
|
-
cellId:
|
|
44917
|
-
central:
|
|
45728
|
+
workspaces: mountedOtiumWorkspaces().map((join44) => ({
|
|
45729
|
+
cellId: join44.cellId,
|
|
45730
|
+
central: join44.central
|
|
44918
45731
|
}))
|
|
44919
45732
|
});
|
|
44920
45733
|
}
|
|
@@ -44935,8 +45748,8 @@ function mountConfiguredOtiumNodeRuntime() {
|
|
|
44935
45748
|
const joins = loadJoins();
|
|
44936
45749
|
if (joins.length === 0)
|
|
44937
45750
|
return null;
|
|
44938
|
-
for (const
|
|
44939
|
-
attachOtiumWorkspace(
|
|
45751
|
+
for (const join44 of joins)
|
|
45752
|
+
attachOtiumWorkspace(join44);
|
|
44940
45753
|
registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
|
|
44941
45754
|
let stopped = false;
|
|
44942
45755
|
return {
|
|
@@ -45027,11 +45840,11 @@ async function joinCommand(args) {
|
|
|
45027
45840
|
process.exitCode = 1;
|
|
45028
45841
|
return;
|
|
45029
45842
|
}
|
|
45030
|
-
let
|
|
45843
|
+
let join44;
|
|
45031
45844
|
let productionEnrollment = false;
|
|
45032
45845
|
if (args.includes("--legacy")) {
|
|
45033
45846
|
try {
|
|
45034
|
-
|
|
45847
|
+
join44 = parseInviteCode(code);
|
|
45035
45848
|
} catch (err2) {
|
|
45036
45849
|
console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
|
|
45037
45850
|
process.exitCode = 1;
|
|
@@ -45058,7 +45871,7 @@ async function joinCommand(args) {
|
|
|
45058
45871
|
return;
|
|
45059
45872
|
}
|
|
45060
45873
|
}
|
|
45061
|
-
|
|
45874
|
+
join44 = await claimEnrollment(invite, nodeName);
|
|
45062
45875
|
productionEnrollment = true;
|
|
45063
45876
|
} catch (err2) {
|
|
45064
45877
|
console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
@@ -45069,16 +45882,16 @@ async function joinCommand(args) {
|
|
|
45069
45882
|
let path;
|
|
45070
45883
|
try {
|
|
45071
45884
|
const saveOptions = { replaceExisting: args.includes("--replace") };
|
|
45072
|
-
path = productionEnrollment ? commitEnrollment(
|
|
45885
|
+
path = productionEnrollment ? commitEnrollment(join44, saveOptions) : saveJoin(join44, saveOptions);
|
|
45073
45886
|
} catch (err2) {
|
|
45074
45887
|
console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
|
|
45075
45888
|
process.exitCode = 1;
|
|
45076
45889
|
return;
|
|
45077
45890
|
}
|
|
45078
45891
|
console.log(`otium join credentials saved to ${path}`);
|
|
45079
|
-
console.log(` central: ${
|
|
45080
|
-
console.log(` cellId: ${
|
|
45081
|
-
configureOtiumCentral(
|
|
45892
|
+
console.log(` central: ${join44.central}`);
|
|
45893
|
+
console.log(` cellId: ${join44.cellId}`);
|
|
45894
|
+
configureOtiumCentral(join44);
|
|
45082
45895
|
try {
|
|
45083
45896
|
const self = await selfPeerNode();
|
|
45084
45897
|
if (self) {
|
|
@@ -45144,8 +45957,8 @@ async function proxyOtiumPeerRequest(req, dependencies = {}) {
|
|
|
45144
45957
|
}
|
|
45145
45958
|
}
|
|
45146
45959
|
async function runOtiumSidecar(options) {
|
|
45147
|
-
const
|
|
45148
|
-
if (!
|
|
45960
|
+
const join44 = loadJoin();
|
|
45961
|
+
if (!join44) {
|
|
45149
45962
|
throw new Error("not joined to an Otium workspace \u2014 run `negotium otium join <code>` first");
|
|
45150
45963
|
}
|
|
45151
45964
|
const initialNode = await inspectNodeDaemon();
|
|
@@ -45177,10 +45990,10 @@ async function runOtiumSidecar(options) {
|
|
|
45177
45990
|
lease.stop();
|
|
45178
45991
|
throw error2;
|
|
45179
45992
|
}
|
|
45180
|
-
const selectedRelay = options.relayUrl?.trim() ||
|
|
45993
|
+
const selectedRelay = options.relayUrl?.trim() || join44.relay || process.env.OTIUM_RELAY_URL?.trim();
|
|
45181
45994
|
const tunnel = selectedRelay ? new TunnelClient({
|
|
45182
45995
|
relayUrl: selectedRelay,
|
|
45183
|
-
token:
|
|
45996
|
+
token: join44.secret,
|
|
45184
45997
|
targetOrigin: `http://127.0.0.1:${server.port}`,
|
|
45185
45998
|
nodeVersion: `negotium@${NEGOTIUM_VERSION}`,
|
|
45186
45999
|
logger
|
|
@@ -45326,9 +46139,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
45326
46139
|
if (joins.length === 0)
|
|
45327
46140
|
throw new Error("not joined to an Otium workspace");
|
|
45328
46141
|
if (!targetCellId && joins.length > 1) {
|
|
45329
|
-
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((
|
|
46142
|
+
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join44) => join44.cellId).join(", ")}`);
|
|
45330
46143
|
}
|
|
45331
|
-
if (targetCellId && !joins.some((
|
|
46144
|
+
if (targetCellId && !joins.some((join44) => join44.cellId === targetCellId)) {
|
|
45332
46145
|
throw new Error(`not joined as ${targetCellId}`);
|
|
45333
46146
|
}
|
|
45334
46147
|
removeJoin2(targetCellId);
|
|
@@ -46066,4 +46879,4 @@ switch (command) {
|
|
|
46066
46879
|
}
|
|
46067
46880
|
}
|
|
46068
46881
|
|
|
46069
|
-
//# debugId=
|
|
46882
|
+
//# debugId=530A2FBC74E2464364756E2164756E21
|