negotium 0.2.33 → 0.3.1
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 +57 -5
- package/dist/agent-helpers.js.map +12 -12
- 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 +1135 -370
- package/dist/main.js.map +27 -24
- package/dist/mcp-catalog.js +2 -1
- package/dist/mcp-catalog.js.map +3 -3
- package/dist/mcp-factories.js +585 -255
- package/dist/mcp-factories.js.map +16 -14
- 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/cron/mcp-server.ts +2 -2
- 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/mcp/session-comm/topic-config.ts +7 -1
- 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/inbox.ts +6 -4
- package/dist/runtime/src/runtime/visual-html.ts +18 -1
- package/dist/runtime/src/storage/api-topics.ts +30 -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 +21 -3
- package/dist/runtime-helpers.js.map +5 -5
- package/dist/storage.js +263 -57
- package/dist/storage.js.map +6 -5
- 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/api-topics.d.ts +29 -4
- 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.
|
|
1872
|
+
var NEGOTIUM_VERSION = "0.3.1";
|
|
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) {
|
|
@@ -8185,7 +8216,8 @@ function setTopicSurfaces(topicIds, surface) {
|
|
|
8185
8216
|
})();
|
|
8186
8217
|
return changed;
|
|
8187
8218
|
}
|
|
8188
|
-
function getTopicByNameForUser(title, userId,
|
|
8219
|
+
function getTopicByNameForUser(title, userId, lookupScope) {
|
|
8220
|
+
const opts = "scope" in lookupScope ? {} : lookupScope;
|
|
8189
8221
|
const trimmed = title.trim();
|
|
8190
8222
|
const qualified = /^(agent|channel|manager):(.+)$/i.exec(trimmed);
|
|
8191
8223
|
const requestedKind = qualified ? normalizeTopicKind(qualified[1]?.toLowerCase()) : null;
|
|
@@ -11654,6 +11686,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11654
11686
|
} = opts;
|
|
11655
11687
|
const runtimeNamespace = "mcp__runtime";
|
|
11656
11688
|
const taskNamespace = "mcp__task";
|
|
11689
|
+
const decisionNamespace = "mcp__decision";
|
|
11657
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" }.`;
|
|
11658
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" }.`;
|
|
11659
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.`;
|
|
@@ -11661,6 +11694,7 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11661
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?: "..." }] }.`;
|
|
11662
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.`;
|
|
11663
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.`;
|
|
11664
11698
|
const runtimeToolRef = (name) => agentKind === "codex" ? `\`${name}\`` : `"${runtimeNamespace}__${name}"`;
|
|
11665
11699
|
const spawnSubagentToolLine = `Use ${runtimeToolRef("spawn_subagent")} for self-contained parallel or long-running background work; keep quick work inline.`;
|
|
11666
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\`.`;
|
|
@@ -11707,6 +11741,9 @@ function buildRuntimeToolSection(opts, extensions) {
|
|
|
11707
11741
|
taskToolLine,
|
|
11708
11742
|
"Use this shared task store for plans, progress, and checklist updates; it is visible across claude/codex/maestro turns.",
|
|
11709
11743
|
nativeTaskPolicyLine,
|
|
11744
|
+
"",
|
|
11745
|
+
"## Shared Decisions",
|
|
11746
|
+
decisionToolLine,
|
|
11710
11747
|
...extensions.render("after-shared-tasks"),
|
|
11711
11748
|
...fileDeliverySection,
|
|
11712
11749
|
...extensions.render("before-session-communication"),
|
|
@@ -17161,6 +17198,10 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
17161
17198
|
.controls button:focus-visible{outline:2px solid var(--celadon);outline-offset:1px}
|
|
17162
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}
|
|
17163
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}
|
|
17164
17205
|
@media(max-width:600px){.viewport{padding:54px 14px 18px}.controls{top:10px;right:10px}}
|
|
17165
17206
|
</style>
|
|
17166
17207
|
</head>
|
|
@@ -17206,7 +17247,20 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
|
|
|
17206
17247
|
applyScale(scale, false);
|
|
17207
17248
|
} catch (error) {
|
|
17208
17249
|
document.querySelector(".controls")?.remove();
|
|
17209
|
-
|
|
17250
|
+
const raw = String(error && error.message ? error.message : error);
|
|
17251
|
+
const escape = (value) => value.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
|
17252
|
+
// Mermaid aborts the whole render when it cannot place an edge label:
|
|
17253
|
+
// cardinality markers ask for a point a fixed distance along the edge,
|
|
17254
|
+
// and a relation laid out shorter than that walks off the end. Nothing
|
|
17255
|
+
// is wrong with the diagram, so the raw message sends authors looking
|
|
17256
|
+
// for a syntax error that does not exist. Say what actually moves it.
|
|
17257
|
+
const note = raw.indexOf("Could not find a suitable point") !== -1
|
|
17258
|
+
? "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."
|
|
17259
|
+
: "This diagram could not be rendered.";
|
|
17260
|
+
document.querySelector(".viewport").innerHTML =
|
|
17261
|
+
'<div class="failure"><p>' + escape(note) +
|
|
17262
|
+
'</p><details><summary>Technical detail</summary><pre class="error">' +
|
|
17263
|
+
escape(raw) + '</pre></details></div>';
|
|
17210
17264
|
}
|
|
17211
17265
|
})();
|
|
17212
17266
|
</script>
|
|
@@ -21917,7 +21971,7 @@ function callerScope(entry, getTopic2) {
|
|
|
21917
21971
|
function scopeToLookupOpts(scope) {
|
|
21918
21972
|
if (scope.kind === "deny")
|
|
21919
21973
|
return null;
|
|
21920
|
-
return scope.kind === "surface" ? { surface: scope.on } : {};
|
|
21974
|
+
return scope.kind === "surface" ? { surface: scope.on } : { scope: "all" };
|
|
21921
21975
|
}
|
|
21922
21976
|
async function resolveEntryCallerTopic(scope, entry) {
|
|
21923
21977
|
const { getTopic: getTopic2, getTopicByNameForUser: getTopicByNameForUser2 } = await init_api_topics().then(() => exports_api_topics);
|
|
@@ -22240,7 +22294,7 @@ async function processTopicInbox(args) {
|
|
|
22240
22294
|
async function handleAbortEntry(userId, topicName, topicId, issuedAt) {
|
|
22241
22295
|
const { getTopic: getTopic2, getTopicByNameForUser: getTopicByNameForUser2 } = await init_api_topics().then(() => exports_api_topics);
|
|
22242
22296
|
const byId = topicId ? getTopic2(topicId) : null;
|
|
22243
|
-
const topic = topicId ? byId?.participants.some((participant) => participant.userId === userId) ? byId : null : getTopicByNameForUser2(topicName, userId);
|
|
22297
|
+
const topic = topicId ? byId?.participants.some((participant) => participant.userId === userId) ? byId : null : getTopicByNameForUser2(topicName, userId, { scope: "all" });
|
|
22244
22298
|
if (!topic) {
|
|
22245
22299
|
logger.warn({ topicName }, "session-inbox: abort for unknown topic, skipping");
|
|
22246
22300
|
return;
|
|
@@ -22944,9 +22998,208 @@ var init_conversation_migration = __esm(async () => {
|
|
|
22944
22998
|
init_types();
|
|
22945
22999
|
});
|
|
22946
23000
|
|
|
23001
|
+
// ../../packages/core/src/storage/decisions.ts
|
|
23002
|
+
var exports_decisions = {};
|
|
23003
|
+
__export(exports_decisions, {
|
|
23004
|
+
writeDecisions: () => writeDecisions,
|
|
23005
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
23006
|
+
validateDecisionGraph: () => validateDecisionGraph,
|
|
23007
|
+
updateDecisions: () => updateDecisions,
|
|
23008
|
+
renderDecisionList: () => renderDecisionList,
|
|
23009
|
+
readDecisions: () => readDecisions,
|
|
23010
|
+
getDecisionGraphSvgPath: () => getDecisionGraphSvgPath,
|
|
23011
|
+
getDecisionFilePath: () => getDecisionFilePath,
|
|
23012
|
+
deleteDecisions: () => deleteDecisions,
|
|
23013
|
+
decisionScopeKey: () => decisionScopeKey,
|
|
23014
|
+
createDecisions: () => createDecisions,
|
|
23015
|
+
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
|
|
23016
|
+
});
|
|
23017
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
23018
|
+
import { dirname as dirname16, join as join34 } from "path";
|
|
23019
|
+
function safeDecisionScopeKey(scopeKey) {
|
|
23020
|
+
const safe = sanitizeFileName(scopeKey);
|
|
23021
|
+
if (!safe || safe === "." || safe === "..") {
|
|
23022
|
+
throw new Error(`decisions: refusing unsafe scope key: ${scopeKey}`);
|
|
23023
|
+
}
|
|
23024
|
+
return safe;
|
|
23025
|
+
}
|
|
23026
|
+
function decisionScopeKey(opts) {
|
|
23027
|
+
return opts.topicId?.trim() || opts.session || "default";
|
|
23028
|
+
}
|
|
23029
|
+
function getDecisionFilePath(userId, scopeKey) {
|
|
23030
|
+
return join34(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
|
|
23031
|
+
}
|
|
23032
|
+
function getDecisionGraphSvgPath(userId, scopeKey) {
|
|
23033
|
+
return join34(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
|
|
23034
|
+
}
|
|
23035
|
+
function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
23036
|
+
const path = getDecisionGraphSvgPath(userId, scopeKey);
|
|
23037
|
+
mkdirSync24(dirname16(path), { recursive: true });
|
|
23038
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
23039
|
+
writeFileSync18(tmp, svg, "utf-8");
|
|
23040
|
+
renameSync11(tmp, path);
|
|
23041
|
+
return path;
|
|
23042
|
+
}
|
|
23043
|
+
function readDecisions(userId, scopeKey) {
|
|
23044
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
23045
|
+
if (!existsSync24(path))
|
|
23046
|
+
return [];
|
|
23047
|
+
try {
|
|
23048
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
23049
|
+
return Array.isArray(parsed?.decisions) ? parsed.decisions : [];
|
|
23050
|
+
} catch (error) {
|
|
23051
|
+
logger.warn({ err: error, path }, "decisions: failed to read decision store");
|
|
23052
|
+
return [];
|
|
23053
|
+
}
|
|
23054
|
+
}
|
|
23055
|
+
function writeDecisions(userId, scopeKey, decisions) {
|
|
23056
|
+
const path = getDecisionFilePath(userId, scopeKey);
|
|
23057
|
+
mkdirSync24(dirname16(path), { recursive: true });
|
|
23058
|
+
const payload = { version: 1, decisions };
|
|
23059
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
23060
|
+
writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
23061
|
+
renameSync11(tmp, path);
|
|
23062
|
+
}
|
|
23063
|
+
function nextDecisionId(decisions) {
|
|
23064
|
+
let max = 0;
|
|
23065
|
+
for (const decision of decisions) {
|
|
23066
|
+
const id = Number(decision.id);
|
|
23067
|
+
if (Number.isInteger(id) && id > max)
|
|
23068
|
+
max = id;
|
|
23069
|
+
}
|
|
23070
|
+
return max + 1;
|
|
23071
|
+
}
|
|
23072
|
+
function normalizedIds(ids) {
|
|
23073
|
+
if (!ids)
|
|
23074
|
+
return;
|
|
23075
|
+
const unique = [...new Set(ids.map((id) => id.trim()).filter(Boolean))];
|
|
23076
|
+
return unique.length > 0 ? unique : undefined;
|
|
23077
|
+
}
|
|
23078
|
+
function validateDecisionGraph(decisions) {
|
|
23079
|
+
const ids = new Set(decisions.map((decision) => decision.id));
|
|
23080
|
+
for (const decision of decisions) {
|
|
23081
|
+
for (const upstream of decision.causedBy ?? []) {
|
|
23082
|
+
if (!ids.has(upstream)) {
|
|
23083
|
+
throw new Error(`Decision #${decision.id} references missing decision #${upstream}.`);
|
|
23084
|
+
}
|
|
23085
|
+
if (upstream === decision.id) {
|
|
23086
|
+
throw new Error(`Decision #${decision.id} cannot cause itself.`);
|
|
23087
|
+
}
|
|
23088
|
+
}
|
|
23089
|
+
}
|
|
23090
|
+
const visiting = new Set;
|
|
23091
|
+
const visited = new Set;
|
|
23092
|
+
const byId = new Map(decisions.map((decision) => [decision.id, decision]));
|
|
23093
|
+
const visit = (id) => {
|
|
23094
|
+
if (visited.has(id))
|
|
23095
|
+
return;
|
|
23096
|
+
if (visiting.has(id))
|
|
23097
|
+
throw new Error(`Decision graph contains a cycle at #${id}.`);
|
|
23098
|
+
visiting.add(id);
|
|
23099
|
+
for (const upstream of byId.get(id)?.causedBy ?? [])
|
|
23100
|
+
visit(upstream);
|
|
23101
|
+
visiting.delete(id);
|
|
23102
|
+
visited.add(id);
|
|
23103
|
+
};
|
|
23104
|
+
for (const id of ids)
|
|
23105
|
+
visit(id);
|
|
23106
|
+
}
|
|
23107
|
+
function createDecisions(decisions, inputs) {
|
|
23108
|
+
const out = [...decisions];
|
|
23109
|
+
const created = [];
|
|
23110
|
+
let id = nextDecisionId(out);
|
|
23111
|
+
for (const input of inputs) {
|
|
23112
|
+
const decision = {
|
|
23113
|
+
id: String(id++),
|
|
23114
|
+
action: input.action.trim(),
|
|
23115
|
+
reasoning: input.reasoning.trim(),
|
|
23116
|
+
agent: input.agent,
|
|
23117
|
+
status: input.status ?? "accepted",
|
|
23118
|
+
timestamp: input.timestamp ?? Date.now(),
|
|
23119
|
+
...input.model ? { model: input.model } : {},
|
|
23120
|
+
...normalizedIds(input.causedBy) ? { causedBy: normalizedIds(input.causedBy) } : {}
|
|
23121
|
+
};
|
|
23122
|
+
out.push(decision);
|
|
23123
|
+
created.push(decision);
|
|
23124
|
+
}
|
|
23125
|
+
validateDecisionGraph(out);
|
|
23126
|
+
return { decisions: out, created };
|
|
23127
|
+
}
|
|
23128
|
+
function updateDecisions(decisions, updates) {
|
|
23129
|
+
const out = decisions.map((decision) => ({
|
|
23130
|
+
...decision,
|
|
23131
|
+
...decision.causedBy ? { causedBy: [...decision.causedBy] } : {}
|
|
23132
|
+
}));
|
|
23133
|
+
const byId = new Map(out.map((decision) => [decision.id, decision]));
|
|
23134
|
+
const missing = [];
|
|
23135
|
+
for (const update of updates) {
|
|
23136
|
+
const decision = byId.get(update.id);
|
|
23137
|
+
if (!decision) {
|
|
23138
|
+
missing.push(update.id);
|
|
23139
|
+
continue;
|
|
23140
|
+
}
|
|
23141
|
+
if (update.action !== undefined)
|
|
23142
|
+
decision.action = update.action.trim();
|
|
23143
|
+
if (update.reasoning !== undefined)
|
|
23144
|
+
decision.reasoning = update.reasoning.trim();
|
|
23145
|
+
if (update.status !== undefined)
|
|
23146
|
+
decision.status = update.status;
|
|
23147
|
+
if (update.causedBy !== undefined) {
|
|
23148
|
+
const ids = normalizedIds(update.causedBy);
|
|
23149
|
+
if (ids)
|
|
23150
|
+
decision.causedBy = ids;
|
|
23151
|
+
else
|
|
23152
|
+
delete decision.causedBy;
|
|
23153
|
+
}
|
|
23154
|
+
}
|
|
23155
|
+
validateDecisionGraph(out);
|
|
23156
|
+
return { decisions: out, missing };
|
|
23157
|
+
}
|
|
23158
|
+
function deleteDecisions(decisions, opts) {
|
|
23159
|
+
if (opts.all)
|
|
23160
|
+
return { decisions: [], removed: decisions.length };
|
|
23161
|
+
const ids = new Set(opts.ids ?? []);
|
|
23162
|
+
const kept = decisions.filter((decision) => !ids.has(decision.id)).map((decision) => {
|
|
23163
|
+
const causedBy = decision.causedBy?.filter((id) => !ids.has(id));
|
|
23164
|
+
const next = { ...decision };
|
|
23165
|
+
if (causedBy && causedBy.length > 0)
|
|
23166
|
+
next.causedBy = causedBy;
|
|
23167
|
+
else
|
|
23168
|
+
delete next.causedBy;
|
|
23169
|
+
return next;
|
|
23170
|
+
});
|
|
23171
|
+
return { decisions: kept, removed: decisions.length - kept.length };
|
|
23172
|
+
}
|
|
23173
|
+
function renderDecisionList(decisions) {
|
|
23174
|
+
if (decisions.length === 0)
|
|
23175
|
+
return "Decisions (0 recorded)";
|
|
23176
|
+
return [
|
|
23177
|
+
`Decisions (${decisions.length} recorded)`,
|
|
23178
|
+
...decisions.map((decision) => {
|
|
23179
|
+
const causes = decision.causedBy?.length ? ` <- ${decision.causedBy.map((id) => `#${id}`).join(", ")}` : "";
|
|
23180
|
+
return `[${decision.status}] #${decision.id} ${decision.action}${causes}
|
|
23181
|
+
${decision.reasoning}`;
|
|
23182
|
+
})
|
|
23183
|
+
].join(`
|
|
23184
|
+
`);
|
|
23185
|
+
}
|
|
23186
|
+
var DECISION_STATUS_VALUES;
|
|
23187
|
+
var init_decisions = __esm(async () => {
|
|
23188
|
+
init_logger();
|
|
23189
|
+
await init_storage_host();
|
|
23190
|
+
DECISION_STATUS_VALUES = [
|
|
23191
|
+
"proposed",
|
|
23192
|
+
"accepted",
|
|
23193
|
+
"executed",
|
|
23194
|
+
"rejected",
|
|
23195
|
+
"superseded"
|
|
23196
|
+
];
|
|
23197
|
+
});
|
|
23198
|
+
|
|
22947
23199
|
// ../../packages/core/src/node-host.ts
|
|
22948
23200
|
var exports_node_host = {};
|
|
22949
23201
|
__export(exports_node_host, {
|
|
23202
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
22950
23203
|
upsertTopic: () => upsertTopic,
|
|
22951
23204
|
topicService: () => topicService,
|
|
22952
23205
|
switchTopicModel: () => switchTopicModel,
|
|
@@ -22971,6 +23224,7 @@ __export(exports_node_host, {
|
|
|
22971
23224
|
runNodeRequestHandlers: () => runNodeRequestHandlers,
|
|
22972
23225
|
reconcilePendingAskUserQuestionGates: () => reconcilePendingAskUserQuestionGates,
|
|
22973
23226
|
reapOrphanBrowsers: () => reapPlaywrightOrphans,
|
|
23227
|
+
readDecisions: () => readDecisions,
|
|
22974
23228
|
onShutdown: () => onShutdown,
|
|
22975
23229
|
nodeRequestHandlerNames: () => nodeRequestHandlerNames,
|
|
22976
23230
|
logger: () => logger,
|
|
@@ -23040,6 +23294,7 @@ var init_node_host = __esm(async () => {
|
|
|
23040
23294
|
await init_turn_runner();
|
|
23041
23295
|
await init_api_messages();
|
|
23042
23296
|
await init_api_topics();
|
|
23297
|
+
await init_decisions();
|
|
23043
23298
|
await init_runtime_events();
|
|
23044
23299
|
await init_runtime_process_leases();
|
|
23045
23300
|
await init_token_stats();
|
|
@@ -23049,7 +23304,7 @@ var init_node_host = __esm(async () => {
|
|
|
23049
23304
|
});
|
|
23050
23305
|
|
|
23051
23306
|
// ../../packages/core/src/agents/mcp-tools/inline-assets.ts
|
|
23052
|
-
import { existsSync as
|
|
23307
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
|
|
23053
23308
|
import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
|
|
23054
23309
|
function isNonLocal(ref) {
|
|
23055
23310
|
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
@@ -23077,7 +23332,7 @@ function inlineLocalAssets(html, options) {
|
|
|
23077
23332
|
const abs = isAbsolute6(relPath) ? relPath : resolve16(baseDir, relPath);
|
|
23078
23333
|
let bytes;
|
|
23079
23334
|
try {
|
|
23080
|
-
if (!
|
|
23335
|
+
if (!existsSync25(abs) || !statSync14(abs).isFile()) {
|
|
23081
23336
|
unresolved.push(ref);
|
|
23082
23337
|
cache.set(ref, null);
|
|
23083
23338
|
return null;
|
|
@@ -23102,7 +23357,7 @@ function inlineLocalAssets(html, options) {
|
|
|
23102
23357
|
let uri;
|
|
23103
23358
|
try {
|
|
23104
23359
|
const mime = MIME_BY_EXT[extname2(abs).toLowerCase()] ?? "application/octet-stream";
|
|
23105
|
-
uri = `data:${mime};base64,${
|
|
23360
|
+
uri = `data:${mime};base64,${readFileSync22(abs).toString("base64")}`;
|
|
23106
23361
|
} catch {
|
|
23107
23362
|
unresolved.push(ref);
|
|
23108
23363
|
cache.set(ref, null);
|
|
@@ -23660,18 +23915,133 @@ var init_task = __esm(async () => {
|
|
|
23660
23915
|
};
|
|
23661
23916
|
});
|
|
23662
23917
|
|
|
23918
|
+
// ../../packages/core/src/mcp/factories/decision.ts
|
|
23919
|
+
var exports_decision = {};
|
|
23920
|
+
__export(exports_decision, {
|
|
23921
|
+
defaultDecisionMcpHost: () => defaultDecisionMcpHost,
|
|
23922
|
+
createDecisionMcpServer: () => createDecisionMcpServer
|
|
23923
|
+
});
|
|
23924
|
+
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23925
|
+
import { z as z8 } from "zod";
|
|
23926
|
+
function createDecisionMcpServer(context, host2 = defaultDecisionMcpHost) {
|
|
23927
|
+
const scopeKey = context.topic ? decisionScopeKey({ topicId: context.topicId, session: context.topic }) : "";
|
|
23928
|
+
const server = new McpServer2({ name: "decision", version: "1.0.0" });
|
|
23929
|
+
const requireContext = () => !context.userId || !scopeKey ? mcpError("Error: missing userId/topic context.") : null;
|
|
23930
|
+
const statusEnum = z8.enum(DECISION_STATUS_VALUES);
|
|
23931
|
+
server.tool("decision_create", "Record one or more durable decisions in this topic. Include the rationale and causal predecessors.", {
|
|
23932
|
+
decisions: z8.array(z8.object({
|
|
23933
|
+
action: z8.string().min(1).describe("Concise statement of what was decided"),
|
|
23934
|
+
reasoning: z8.string().min(1).describe("Why this choice was made"),
|
|
23935
|
+
status: statusEnum.optional().describe("Defaults to accepted"),
|
|
23936
|
+
caused_by: z8.array(z8.string()).optional().describe("Upstream decision ids")
|
|
23937
|
+
})).min(1)
|
|
23938
|
+
}, async ({ decisions: inputs }) => {
|
|
23939
|
+
const guard = requireContext();
|
|
23940
|
+
if (guard)
|
|
23941
|
+
return guard;
|
|
23942
|
+
try {
|
|
23943
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
23944
|
+
const { decisions, created } = createDecisions(current3, inputs.map((input) => ({
|
|
23945
|
+
action: input.action,
|
|
23946
|
+
reasoning: input.reasoning,
|
|
23947
|
+
status: input.status,
|
|
23948
|
+
causedBy: input.caused_by,
|
|
23949
|
+
agent: context.agent,
|
|
23950
|
+
model: context.model
|
|
23951
|
+
})));
|
|
23952
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
23953
|
+
return mcpOk(`${created.length} decision(s) recorded (${created.map((item) => `#${item.id}`).join(", ")})
|
|
23954
|
+
|
|
23955
|
+
${renderDecisionList(decisions)}`);
|
|
23956
|
+
} catch (error) {
|
|
23957
|
+
return mcpError(`decision_create failed: ${errMsg(error)}`);
|
|
23958
|
+
}
|
|
23959
|
+
});
|
|
23960
|
+
server.tool("decision_update", "Update decisions or their causal links in this topic.", {
|
|
23961
|
+
updates: z8.array(z8.object({
|
|
23962
|
+
id: z8.string(),
|
|
23963
|
+
action: z8.string().min(1).optional(),
|
|
23964
|
+
reasoning: z8.string().min(1).optional(),
|
|
23965
|
+
status: statusEnum.optional(),
|
|
23966
|
+
caused_by: z8.array(z8.string()).optional().describe("Replacement upstream decision ids")
|
|
23967
|
+
})).min(1)
|
|
23968
|
+
}, async ({ updates }) => {
|
|
23969
|
+
const guard = requireContext();
|
|
23970
|
+
if (guard)
|
|
23971
|
+
return guard;
|
|
23972
|
+
try {
|
|
23973
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
23974
|
+
const { decisions, missing } = updateDecisions(current3, updates.map((update) => ({
|
|
23975
|
+
...update,
|
|
23976
|
+
causedBy: update.caused_by
|
|
23977
|
+
})));
|
|
23978
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
23979
|
+
const warning = missing.length ? `
|
|
23980
|
+
Missing ids ignored: ${missing.join(", ")}` : "";
|
|
23981
|
+
return mcpOk(`${renderDecisionList(decisions)}${warning}`);
|
|
23982
|
+
} catch (error) {
|
|
23983
|
+
return mcpError(`decision_update failed: ${errMsg(error)}`);
|
|
23984
|
+
}
|
|
23985
|
+
});
|
|
23986
|
+
server.tool("decision_list", "Read this topic's decision graph as a concise list.", {}, async () => {
|
|
23987
|
+
const guard = requireContext();
|
|
23988
|
+
if (guard)
|
|
23989
|
+
return guard;
|
|
23990
|
+
try {
|
|
23991
|
+
return mcpOk(renderDecisionList(host2.readDecisions(context.userId, scopeKey)));
|
|
23992
|
+
} catch (error) {
|
|
23993
|
+
return mcpError(`decision_list failed: ${errMsg(error)}`);
|
|
23994
|
+
}
|
|
23995
|
+
});
|
|
23996
|
+
server.tool("decision_get", "Read one topic decision as JSON.", { id: z8.string() }, async ({ id }) => {
|
|
23997
|
+
const guard = requireContext();
|
|
23998
|
+
if (guard)
|
|
23999
|
+
return guard;
|
|
24000
|
+
try {
|
|
24001
|
+
const decision = host2.readDecisions(context.userId, scopeKey).find((item) => item.id === id);
|
|
24002
|
+
return decision ? mcpOk(JSON.stringify(decision, null, 2)) : mcpError(`Decision #${id} not found.`);
|
|
24003
|
+
} catch (error) {
|
|
24004
|
+
return mcpError(`decision_get failed: ${errMsg(error)}`);
|
|
24005
|
+
}
|
|
24006
|
+
});
|
|
24007
|
+
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 }) => {
|
|
24008
|
+
const guard = requireContext();
|
|
24009
|
+
if (guard)
|
|
24010
|
+
return guard;
|
|
24011
|
+
if (!all && (!ids || ids.length === 0))
|
|
24012
|
+
return mcpError("Provide ids or all=true.");
|
|
24013
|
+
try {
|
|
24014
|
+
const current3 = host2.readDecisions(context.userId, scopeKey);
|
|
24015
|
+
const { decisions, removed } = deleteDecisions(current3, { ids, all });
|
|
24016
|
+
host2.writeDecisions(context.userId, scopeKey, decisions);
|
|
24017
|
+
return mcpOk(`${removed} decision(s) deleted
|
|
24018
|
+
|
|
24019
|
+
${renderDecisionList(decisions)}`);
|
|
24020
|
+
} catch (error) {
|
|
24021
|
+
return mcpError(`decision_delete failed: ${errMsg(error)}`);
|
|
24022
|
+
}
|
|
24023
|
+
});
|
|
24024
|
+
return server;
|
|
24025
|
+
}
|
|
24026
|
+
var defaultDecisionMcpHost;
|
|
24027
|
+
var init_decision = __esm(async () => {
|
|
24028
|
+
await init_decisions();
|
|
24029
|
+
init_mcp_helpers();
|
|
24030
|
+
defaultDecisionMcpHost = { readDecisions, writeDecisions };
|
|
24031
|
+
});
|
|
24032
|
+
|
|
23663
24033
|
// ../../packages/core/src/mcp/factories/token-stats.ts
|
|
23664
24034
|
var exports_token_stats2 = {};
|
|
23665
24035
|
__export(exports_token_stats2, {
|
|
23666
24036
|
defaultTokenStatsMcpHost: () => defaultTokenStatsMcpHost,
|
|
23667
24037
|
createTokenStatsMcpServer: () => createTokenStatsMcpServer
|
|
23668
24038
|
});
|
|
23669
|
-
import { McpServer as
|
|
23670
|
-
import { z as
|
|
24039
|
+
import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24040
|
+
import { z as z9 } from "zod";
|
|
23671
24041
|
function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
23672
24042
|
if (!context.userId)
|
|
23673
24043
|
throw new Error("token-stats MCP requires userId");
|
|
23674
|
-
const server = new
|
|
24044
|
+
const server = new McpServer3({ name: "token-stats", version: "1.0.0" });
|
|
23675
24045
|
server.tool("get_usage_stats", [
|
|
23676
24046
|
"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.",
|
|
23677
24047
|
"",
|
|
@@ -23683,9 +24053,9 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23683
24053
|
"groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
|
|
23684
24054
|
].join(`
|
|
23685
24055
|
`), {
|
|
23686
|
-
from:
|
|
23687
|
-
to:
|
|
23688
|
-
groupBy:
|
|
24056
|
+
from: z9.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
|
|
24057
|
+
to: z9.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
|
|
24058
|
+
groupBy: z9.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
|
|
23689
24059
|
}, async ({ from, to, groupBy = "hour" }) => {
|
|
23690
24060
|
const snapshot = host2.getStats(context.userId, from, to);
|
|
23691
24061
|
const {
|
|
@@ -23776,7 +24146,7 @@ __export(exports_system_health, {
|
|
|
23776
24146
|
import { execFile as execFile2 } from "child_process";
|
|
23777
24147
|
import { cpus, freemem, loadavg, totalmem } from "os";
|
|
23778
24148
|
import { promisify as promisify2 } from "util";
|
|
23779
|
-
import { McpServer as
|
|
24149
|
+
import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23780
24150
|
function fmtBytes(bytes) {
|
|
23781
24151
|
const gb = bytes / 1024 ** 3;
|
|
23782
24152
|
return gb >= 1 ? `${gb.toFixed(1)}GB` : `${(bytes / 1024 ** 2).toFixed(0)}MB`;
|
|
@@ -23845,7 +24215,7 @@ async function getDisk() {
|
|
|
23845
24215
|
}
|
|
23846
24216
|
}
|
|
23847
24217
|
function createSystemHealthMcpServer(host2 = defaultSystemHealthMcpHost) {
|
|
23848
|
-
const server = new
|
|
24218
|
+
const server = new McpServer4({ name: "system-health", version: "1.0.0" });
|
|
23849
24219
|
server.tool("get_system_health", [
|
|
23850
24220
|
"\uC2DC\uC2A4\uD15C \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
23851
24221
|
"- CPU \uBD80\uD558 (load average 1/5/15\uBD84)",
|
|
@@ -23910,10 +24280,10 @@ __export(exports_agent_health, {
|
|
|
23910
24280
|
createAgentHealthMcpServer: () => createAgentHealthMcpServer
|
|
23911
24281
|
});
|
|
23912
24282
|
import { spawn as spawn6 } from "child_process";
|
|
23913
|
-
import { existsSync as
|
|
23914
|
-
import { join as
|
|
23915
|
-
import { McpServer as
|
|
23916
|
-
import { z as
|
|
24283
|
+
import { existsSync as existsSync26, readdirSync as readdirSync9 } from "fs";
|
|
24284
|
+
import { join as join35 } from "path";
|
|
24285
|
+
import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24286
|
+
import { z as z10 } from "zod";
|
|
23917
24287
|
function signalProcessTree2(child, signal) {
|
|
23918
24288
|
try {
|
|
23919
24289
|
if (child.pid && process.platform !== "win32")
|
|
@@ -24108,7 +24478,7 @@ async function withCheckSlot(operation) {
|
|
|
24108
24478
|
}
|
|
24109
24479
|
}
|
|
24110
24480
|
function createAgentHealthMcpServer(context) {
|
|
24111
|
-
const server = new
|
|
24481
|
+
const server = new McpServer5({ name: "agent-health", version: "1.0.0" });
|
|
24112
24482
|
const abortController = new AbortController;
|
|
24113
24483
|
const originalClose = server.close.bind(server);
|
|
24114
24484
|
server.close = async () => {
|
|
@@ -24116,9 +24486,9 @@ function createAgentHealthMcpServer(context) {
|
|
|
24116
24486
|
await originalClose();
|
|
24117
24487
|
};
|
|
24118
24488
|
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.", {
|
|
24119
|
-
agent:
|
|
24120
|
-
model:
|
|
24121
|
-
timeoutMs:
|
|
24489
|
+
agent: z10.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
|
|
24490
|
+
model: z10.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
|
|
24491
|
+
timeoutMs: z10.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
|
|
24122
24492
|
}, async ({ agent, model, timeoutMs }) => {
|
|
24123
24493
|
const entry = AGENTS.find((candidate) => candidate.kind === agent);
|
|
24124
24494
|
if (!entry)
|
|
@@ -24134,7 +24504,7 @@ function createAgentHealthMcpServer(context) {
|
|
|
24134
24504
|
latency: ${result.latency}ms
|
|
24135
24505
|
error: ${result.error}`);
|
|
24136
24506
|
});
|
|
24137
|
-
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:
|
|
24507
|
+
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 }) => {
|
|
24138
24508
|
const userKey = context.userId ?? "unscoped";
|
|
24139
24509
|
if (activeCheckAllUsers.has(userKey)) {
|
|
24140
24510
|
return mcpOk("FAIL check_all is already running for this user");
|
|
@@ -24155,8 +24525,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24155
24525
|
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 () => {
|
|
24156
24526
|
if (!context.userId)
|
|
24157
24527
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
|
|
24158
|
-
const stateDir =
|
|
24159
|
-
if (!
|
|
24528
|
+
const stateDir = join35(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24529
|
+
if (!existsSync26(stateDir))
|
|
24160
24530
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
|
|
24161
24531
|
let files;
|
|
24162
24532
|
try {
|
|
@@ -24168,7 +24538,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24168
24538
|
const entries = files.flatMap((file) => {
|
|
24169
24539
|
if (!file.endsWith(".json"))
|
|
24170
24540
|
return [];
|
|
24171
|
-
const state = readJsonFile(
|
|
24541
|
+
const state = readJsonFile(join35(stateDir, file));
|
|
24172
24542
|
if (!state)
|
|
24173
24543
|
return [];
|
|
24174
24544
|
const sinceMs = new Date(state.since).getTime();
|
|
@@ -24244,37 +24614,37 @@ var exports_session_comm = {};
|
|
|
24244
24614
|
__export(exports_session_comm, {
|
|
24245
24615
|
createSessionCommMcpServer: () => createSessionCommMcpServer
|
|
24246
24616
|
});
|
|
24247
|
-
import { McpServer as
|
|
24248
|
-
import { z as
|
|
24617
|
+
import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24618
|
+
import { z as z11 } from "zod";
|
|
24249
24619
|
function createSessionCommMcpServer(context, host2, options = {}) {
|
|
24250
|
-
const server = new
|
|
24620
|
+
const server = new McpServer6({ name: "session-comm", version: "2.0.0" });
|
|
24251
24621
|
const required = options.requiredMcpServers?.join(", ") || "none";
|
|
24252
24622
|
const optional = options.optionalMcpServers?.join(", ") || "none";
|
|
24253
24623
|
const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
|
|
24254
24624
|
server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host2.listSessions(context));
|
|
24255
24625
|
server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
|
|
24256
|
-
enabled:
|
|
24626
|
+
enabled: z11.array(z11.string()).nullable().optional()
|
|
24257
24627
|
}, async ({ enabled }) => host2.configureMcp(context, enabled));
|
|
24258
24628
|
server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host2.getMcpConfig(context));
|
|
24259
24629
|
if (host2.getBrowserProfile) {
|
|
24260
24630
|
server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host2.getBrowserProfile(context));
|
|
24261
24631
|
}
|
|
24262
24632
|
if (host2.setBrowserProfile) {
|
|
24263
|
-
server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile:
|
|
24633
|
+
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));
|
|
24264
24634
|
}
|
|
24265
24635
|
if (host2.deleteBrowserProfile) {
|
|
24266
|
-
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:
|
|
24636
|
+
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));
|
|
24267
24637
|
}
|
|
24268
24638
|
server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host2.peekSession(context));
|
|
24269
|
-
server.tool("set_description", "Set the current session description used as a routing hint.", { description:
|
|
24639
|
+
server.tool("set_description", "Set the current session description used as a routing hint.", { description: z11.string() }, async ({ description }) => host2.setDescription(context, description));
|
|
24270
24640
|
if (!context.replyOnly) {
|
|
24271
24641
|
if (!subagentIdentity.restricted) {
|
|
24272
|
-
server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to:
|
|
24642
|
+
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));
|
|
24273
24643
|
}
|
|
24274
24644
|
if (!subagentIdentity.restricted) {
|
|
24275
|
-
server.tool("abort_session", "Abort the active query in another session.", { to:
|
|
24645
|
+
server.tool("abort_session", "Abort the active query in another session.", { to: z11.string() }, async ({ to }) => host2.abortSession(context, to));
|
|
24276
24646
|
}
|
|
24277
|
-
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to:
|
|
24647
|
+
server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z11.string(), message: z11.string() }, async (input) => {
|
|
24278
24648
|
if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
|
|
24279
24649
|
return {
|
|
24280
24650
|
content: [
|
|
@@ -24401,7 +24771,7 @@ var exports_default_host = {};
|
|
|
24401
24771
|
__export(exports_default_host, {
|
|
24402
24772
|
createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
|
|
24403
24773
|
});
|
|
24404
|
-
import { basename as basename6, join as
|
|
24774
|
+
import { basename as basename6, join as join36 } from "path";
|
|
24405
24775
|
function ok2(text2) {
|
|
24406
24776
|
return { content: [{ type: "text", text: text2 }] };
|
|
24407
24777
|
}
|
|
@@ -24448,10 +24818,10 @@ function remoteTarget(context, to) {
|
|
|
24448
24818
|
return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
|
|
24449
24819
|
}
|
|
24450
24820
|
function activeQuery(context, topicId, title) {
|
|
24451
|
-
const dir =
|
|
24452
|
-
const candidates = [
|
|
24821
|
+
const dir = join36(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24822
|
+
const candidates = [join36(dir, `${sanitizeId(topicId)}.json`)];
|
|
24453
24823
|
if (title && basename6(title) === title && title !== "." && title !== "..") {
|
|
24454
|
-
candidates.push(
|
|
24824
|
+
candidates.push(join36(dir, `${title}.json`));
|
|
24455
24825
|
}
|
|
24456
24826
|
for (const path of candidates) {
|
|
24457
24827
|
const state = readJsonFile(path);
|
|
@@ -25027,10 +25397,10 @@ var exports_vault = {};
|
|
|
25027
25397
|
__export(exports_vault, {
|
|
25028
25398
|
createVaultMcpServer: () => createVaultMcpServer
|
|
25029
25399
|
});
|
|
25030
|
-
import { McpServer as
|
|
25031
|
-
import { z as
|
|
25400
|
+
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25401
|
+
import { z as z12 } from "zod";
|
|
25032
25402
|
function createVaultMcpServer(context, host2, executors = {}) {
|
|
25033
|
-
const server = new
|
|
25403
|
+
const server = new McpServer7({ name: "vault", version: "1.0.0" });
|
|
25034
25404
|
const run = executors.run ?? executeVaultRun;
|
|
25035
25405
|
const http = executors.http ?? executeVaultHttpRequest;
|
|
25036
25406
|
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.", {}, () => {
|
|
@@ -25046,9 +25416,9 @@ ${lines.join(`
|
|
|
25046
25416
|
});
|
|
25047
25417
|
if (!context.listOnly && !context.httpOnly) {
|
|
25048
25418
|
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.", {
|
|
25049
|
-
command:
|
|
25050
|
-
timeout_ms:
|
|
25051
|
-
max_output_bytes:
|
|
25419
|
+
command: z12.string().min(1).max(64 * 1024),
|
|
25420
|
+
timeout_ms: z12.number().int().min(1000).max(600000).optional(),
|
|
25421
|
+
max_output_bytes: z12.number().int().min(1024).max(2 * 1024 * 1024).optional()
|
|
25052
25422
|
}, async ({ command, timeout_ms, max_output_bytes }) => {
|
|
25053
25423
|
if (!context.userId)
|
|
25054
25424
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -25063,12 +25433,12 @@ ${lines.join(`
|
|
|
25063
25433
|
}
|
|
25064
25434
|
if (!context.listOnly)
|
|
25065
25435
|
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.", {
|
|
25066
|
-
method:
|
|
25067
|
-
url:
|
|
25068
|
-
headers:
|
|
25069
|
-
body:
|
|
25070
|
-
timeout_ms:
|
|
25071
|
-
max_response_bytes:
|
|
25436
|
+
method: z12.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
25437
|
+
url: z12.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
|
|
25438
|
+
headers: z12.record(z12.string(), z12.string()).optional(),
|
|
25439
|
+
body: z12.string().optional(),
|
|
25440
|
+
timeout_ms: z12.number().int().min(1000).max(120000).optional(),
|
|
25441
|
+
max_response_bytes: z12.number().int().min(1024).max(1024 * 1024).optional()
|
|
25072
25442
|
}, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
|
|
25073
25443
|
if (!context.userId)
|
|
25074
25444
|
return mcpError("Vault unavailable: no user context");
|
|
@@ -25091,7 +25461,7 @@ var init_vault2 = __esm(() => {
|
|
|
25091
25461
|
});
|
|
25092
25462
|
|
|
25093
25463
|
// ../../packages/core/src/mcp/wiki-search-index.ts
|
|
25094
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
25464
|
+
import { chmodSync as chmodSync5, existsSync as existsSync27, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
|
|
25095
25465
|
function documentId(source) {
|
|
25096
25466
|
return `${source.kind}:${source.key}`;
|
|
25097
25467
|
}
|
|
@@ -25175,7 +25545,7 @@ class WikiSearchIndex {
|
|
|
25175
25545
|
continue;
|
|
25176
25546
|
}
|
|
25177
25547
|
try {
|
|
25178
|
-
const text2 =
|
|
25548
|
+
const text2 = readFileSync23(source.path, "utf-8");
|
|
25179
25549
|
changed.push({ source, document: prepare(source, text2), added: !stored });
|
|
25180
25550
|
} catch {}
|
|
25181
25551
|
}
|
|
@@ -25293,7 +25663,7 @@ class WikiSearchIndex {
|
|
|
25293
25663
|
}
|
|
25294
25664
|
function resetCorruptWikiSearchIndex(path) {
|
|
25295
25665
|
for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
|
|
25296
|
-
if (!
|
|
25666
|
+
if (!existsSync27(candidate))
|
|
25297
25667
|
continue;
|
|
25298
25668
|
unlinkSync18(candidate);
|
|
25299
25669
|
}
|
|
@@ -25313,17 +25683,17 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
|
25313
25683
|
import { randomUUID as randomUUID21 } from "crypto";
|
|
25314
25684
|
import {
|
|
25315
25685
|
closeSync as closeSync4,
|
|
25316
|
-
existsSync as
|
|
25317
|
-
mkdirSync as
|
|
25686
|
+
existsSync as existsSync28,
|
|
25687
|
+
mkdirSync as mkdirSync25,
|
|
25318
25688
|
openSync as openSync4,
|
|
25319
25689
|
readdirSync as readdirSync10,
|
|
25320
|
-
readFileSync as
|
|
25321
|
-
renameSync as
|
|
25690
|
+
readFileSync as readFileSync24,
|
|
25691
|
+
renameSync as renameSync12,
|
|
25322
25692
|
statSync as statSync15,
|
|
25323
25693
|
unlinkSync as unlinkSync19,
|
|
25324
|
-
writeFileSync as
|
|
25694
|
+
writeFileSync as writeFileSync19
|
|
25325
25695
|
} from "fs";
|
|
25326
|
-
import { basename as basename7, dirname as
|
|
25696
|
+
import { basename as basename7, dirname as dirname17, join as join37, relative as relative2, resolve as resolve17 } from "path";
|
|
25327
25697
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
25328
25698
|
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
25329
25699
|
import {
|
|
@@ -25349,7 +25719,7 @@ function runtime() {
|
|
|
25349
25719
|
return current3;
|
|
25350
25720
|
}
|
|
25351
25721
|
function ensureDir(dir) {
|
|
25352
|
-
|
|
25722
|
+
mkdirSync25(dir, { recursive: true });
|
|
25353
25723
|
}
|
|
25354
25724
|
function acquireRecoveryGuard(lockPath) {
|
|
25355
25725
|
const guardPath = `${lockPath}.reclaim`;
|
|
@@ -25359,7 +25729,7 @@ function acquireRecoveryGuard(lockPath) {
|
|
|
25359
25729
|
try {
|
|
25360
25730
|
const fd = openSync4(guardPath, "wx");
|
|
25361
25731
|
try {
|
|
25362
|
-
|
|
25732
|
+
writeFileSync19(fd, ownerToken, "utf-8");
|
|
25363
25733
|
} catch (error2) {
|
|
25364
25734
|
closeSync4(fd);
|
|
25365
25735
|
try {
|
|
@@ -25370,7 +25740,7 @@ function acquireRecoveryGuard(lockPath) {
|
|
|
25370
25740
|
return () => {
|
|
25371
25741
|
closeSync4(fd);
|
|
25372
25742
|
try {
|
|
25373
|
-
if (
|
|
25743
|
+
if (readFileSync24(guardPath, "utf-8") === ownerToken)
|
|
25374
25744
|
unlinkSync19(guardPath);
|
|
25375
25745
|
} catch {}
|
|
25376
25746
|
};
|
|
@@ -25392,7 +25762,7 @@ function acquireFileLock(path) {
|
|
|
25392
25762
|
try {
|
|
25393
25763
|
const fd = openSync4(lockPath, "wx");
|
|
25394
25764
|
try {
|
|
25395
|
-
|
|
25765
|
+
writeFileSync19(fd, ownerToken, "utf-8");
|
|
25396
25766
|
} catch (error2) {
|
|
25397
25767
|
closeSync4(fd);
|
|
25398
25768
|
try {
|
|
@@ -25404,7 +25774,7 @@ function acquireFileLock(path) {
|
|
|
25404
25774
|
closeSync4(fd);
|
|
25405
25775
|
const releaseGuard = acquireRecoveryGuard(lockPath);
|
|
25406
25776
|
try {
|
|
25407
|
-
if (
|
|
25777
|
+
if (readFileSync24(lockPath, "utf-8") === ownerToken)
|
|
25408
25778
|
unlinkSync19(lockPath);
|
|
25409
25779
|
} catch {} finally {
|
|
25410
25780
|
releaseGuard();
|
|
@@ -25416,8 +25786,8 @@ function acquireFileLock(path) {
|
|
|
25416
25786
|
const releaseGuard = acquireRecoveryGuard(lockPath);
|
|
25417
25787
|
try {
|
|
25418
25788
|
try {
|
|
25419
|
-
const staleToken =
|
|
25420
|
-
if (Date.now() - statSync15(lockPath).mtimeMs > FILE_LOCK_STALE_MS &&
|
|
25789
|
+
const staleToken = readFileSync24(lockPath, "utf-8");
|
|
25790
|
+
if (Date.now() - statSync15(lockPath).mtimeMs > FILE_LOCK_STALE_MS && readFileSync24(lockPath, "utf-8") === staleToken) {
|
|
25421
25791
|
unlinkSync19(lockPath);
|
|
25422
25792
|
continue;
|
|
25423
25793
|
}
|
|
@@ -25436,9 +25806,9 @@ function acquireFileLock(path) {
|
|
|
25436
25806
|
}
|
|
25437
25807
|
function atomicWriteFile(path, content) {
|
|
25438
25808
|
const temporaryPath = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
25439
|
-
|
|
25809
|
+
writeFileSync19(temporaryPath, content, "utf-8");
|
|
25440
25810
|
try {
|
|
25441
|
-
|
|
25811
|
+
renameSync12(temporaryPath, path);
|
|
25442
25812
|
} catch (error2) {
|
|
25443
25813
|
try {
|
|
25444
25814
|
unlinkSync19(temporaryPath);
|
|
@@ -25627,7 +25997,7 @@ function normalizeWikiSlug(raw) {
|
|
|
25627
25997
|
function parseWikiIndex(path) {
|
|
25628
25998
|
let text2;
|
|
25629
25999
|
try {
|
|
25630
|
-
text2 =
|
|
26000
|
+
text2 = readFileSync24(path, "utf-8");
|
|
25631
26001
|
} catch {
|
|
25632
26002
|
return [];
|
|
25633
26003
|
}
|
|
@@ -25773,15 +26143,15 @@ function wikiQuery(args) {
|
|
|
25773
26143
|
try {
|
|
25774
26144
|
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
25775
26145
|
if (entry.isDirectory()) {
|
|
25776
|
-
const sub =
|
|
26146
|
+
const sub = join37(dir, entry.name);
|
|
25777
26147
|
if (label === "articles" || label === "skills") {
|
|
25778
26148
|
try {
|
|
25779
26149
|
for (const f of readdirSync10(sub, { withFileTypes: true })) {
|
|
25780
26150
|
if (!f.isFile() || !f.name.endsWith(".md"))
|
|
25781
26151
|
continue;
|
|
25782
|
-
const fp =
|
|
26152
|
+
const fp = join37(sub, f.name);
|
|
25783
26153
|
try {
|
|
25784
|
-
const text2 =
|
|
26154
|
+
const text2 = readFileSync24(fp, "utf-8");
|
|
25785
26155
|
const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
|
|
25786
26156
|
scored.push({
|
|
25787
26157
|
kind: label === "articles" ? "article" : "skill",
|
|
@@ -25800,9 +26170,9 @@ function wikiQuery(args) {
|
|
|
25800
26170
|
if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
|
|
25801
26171
|
continue;
|
|
25802
26172
|
}
|
|
25803
|
-
const fp =
|
|
26173
|
+
const fp = join37(dir, entry.name);
|
|
25804
26174
|
try {
|
|
25805
|
-
const text2 =
|
|
26175
|
+
const text2 = readFileSync24(fp, "utf-8");
|
|
25806
26176
|
const key = entry.name.replace(/\.md$/i, "");
|
|
25807
26177
|
const resultKind = label === "articles" ? "article" : label === "topic" ? "topic" : label === "summaries" ? "summary" : "skill";
|
|
25808
26178
|
scored.push({
|
|
@@ -26086,7 +26456,7 @@ function wikiTopicBrief(args) {
|
|
|
26086
26456
|
const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
26087
26457
|
const filePath = resolve17(runtime().topicsDir, `${topicKey}.md`);
|
|
26088
26458
|
try {
|
|
26089
|
-
const content =
|
|
26459
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
26090
26460
|
return {
|
|
26091
26461
|
content: [{ type: "text", text: `# Topic Brief: ${topicKey}
|
|
26092
26462
|
|
|
@@ -26138,7 +26508,7 @@ Adopted topic memory: ${memoryKey}` }]
|
|
|
26138
26508
|
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
26139
26509
|
}
|
|
26140
26510
|
try {
|
|
26141
|
-
const content =
|
|
26511
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
26142
26512
|
return {
|
|
26143
26513
|
content: [{ type: "text", text: `# Wiki ${kind}: ${key}
|
|
26144
26514
|
|
|
@@ -26158,18 +26528,18 @@ function wikiLastConversation(args) {
|
|
|
26158
26528
|
const name = slugify(topicNameFrom(rawTopic));
|
|
26159
26529
|
const archiveDir = resolve17(runtime().archiveDir, name);
|
|
26160
26530
|
try {
|
|
26161
|
-
let files =
|
|
26531
|
+
let files = existsSync28(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26162
26532
|
if (files.length === 0) {
|
|
26163
|
-
files =
|
|
26533
|
+
files = existsSync28(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26164
26534
|
}
|
|
26165
26535
|
if (files.length === 0) {
|
|
26166
26536
|
return {
|
|
26167
26537
|
content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
|
|
26168
26538
|
};
|
|
26169
26539
|
}
|
|
26170
|
-
const actualPath = files[0].includes("/") ? files[0] : resolve17(
|
|
26540
|
+
const actualPath = files[0].includes("/") ? files[0] : resolve17(existsSync28(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
|
|
26171
26541
|
try {
|
|
26172
|
-
const raw =
|
|
26542
|
+
const raw = readFileSync24(actualPath, "utf-8");
|
|
26173
26543
|
const lines = raw.trim().split(`
|
|
26174
26544
|
`).filter(Boolean);
|
|
26175
26545
|
const turns2 = [];
|
|
@@ -26242,7 +26612,7 @@ function skillQuery(args) {
|
|
|
26242
26612
|
continue;
|
|
26243
26613
|
const skillFile = resolve17(runtime().skillsDir, entry.name, "skill.md");
|
|
26244
26614
|
try {
|
|
26245
|
-
const text2 =
|
|
26615
|
+
const text2 = readFileSync24(skillFile, "utf-8");
|
|
26246
26616
|
const lower = text2.toLowerCase();
|
|
26247
26617
|
let score = 0;
|
|
26248
26618
|
for (const w of query2.split(/\s+/).filter(Boolean)) {
|
|
@@ -26313,10 +26683,10 @@ ${fresh.join(`
|
|
|
26313
26683
|
const skillPath = resolve17(skillDir, "skill.md");
|
|
26314
26684
|
let finalContent = content;
|
|
26315
26685
|
try {
|
|
26316
|
-
const existing =
|
|
26686
|
+
const existing = readFileSync24(skillPath, "utf-8");
|
|
26317
26687
|
finalContent = mergeGotchas(content, extractGotchas(existing));
|
|
26318
26688
|
} catch {}
|
|
26319
|
-
|
|
26689
|
+
writeFileSync19(skillPath, finalContent, "utf-8");
|
|
26320
26690
|
return {
|
|
26321
26691
|
content: [
|
|
26322
26692
|
{
|
|
@@ -26335,7 +26705,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26335
26705
|
let counter = 1;
|
|
26336
26706
|
while (true) {
|
|
26337
26707
|
try {
|
|
26338
|
-
|
|
26708
|
+
writeFileSync19(resolve17(runtime().summariesDir, summaryName), content, {
|
|
26339
26709
|
encoding: "utf-8",
|
|
26340
26710
|
flag: "wx"
|
|
26341
26711
|
});
|
|
@@ -26353,7 +26723,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26353
26723
|
if (topicId && setTopicBrief2) {
|
|
26354
26724
|
try {
|
|
26355
26725
|
const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
|
|
26356
|
-
let briefMd =
|
|
26726
|
+
let briefMd = existsSync28(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
|
|
26357
26727
|
if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
|
|
26358
26728
|
const legacy = getTopicBrief2(topicId)?.briefMd;
|
|
26359
26729
|
if (legacy)
|
|
@@ -26373,9 +26743,9 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26373
26743
|
const topicId = runtime().topicId;
|
|
26374
26744
|
const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
|
|
26375
26745
|
const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
|
|
26376
|
-
ensureDir(
|
|
26377
|
-
const existed =
|
|
26378
|
-
|
|
26746
|
+
ensureDir(dirname17(briefPath));
|
|
26747
|
+
const existed = existsSync28(briefPath);
|
|
26748
|
+
writeFileSync19(briefPath, content, "utf-8");
|
|
26379
26749
|
const setTopicBrief2 = runtime().host.setTopicBrief;
|
|
26380
26750
|
let sqliteUpdated = false;
|
|
26381
26751
|
if (topicId && setTopicBrief2) {
|
|
@@ -26388,8 +26758,8 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26388
26758
|
}
|
|
26389
26759
|
function writeArticleDocument(slug, content) {
|
|
26390
26760
|
const articlePath = resolve17(runtime().articlesDir, `${slug}.md`);
|
|
26391
|
-
ensureDir(
|
|
26392
|
-
const existed =
|
|
26761
|
+
ensureDir(dirname17(articlePath));
|
|
26762
|
+
const existed = existsSync28(articlePath);
|
|
26393
26763
|
atomicWriteFile(articlePath, content);
|
|
26394
26764
|
return { existed };
|
|
26395
26765
|
}
|
|
@@ -26521,21 +26891,21 @@ function collectIndexableDocuments() {
|
|
|
26521
26891
|
}
|
|
26522
26892
|
for (const entry of entries) {
|
|
26523
26893
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
26524
|
-
add(kind, entry.name.replace(/\.md$/i, ""),
|
|
26894
|
+
add(kind, entry.name.replace(/\.md$/i, ""), join37(root, entry.name));
|
|
26525
26895
|
continue;
|
|
26526
26896
|
}
|
|
26527
26897
|
if (!entry.isDirectory() || !allowSubdirectories)
|
|
26528
26898
|
continue;
|
|
26529
26899
|
let nested;
|
|
26530
26900
|
try {
|
|
26531
|
-
nested = readdirSync10(
|
|
26901
|
+
nested = readdirSync10(join37(root, entry.name), { withFileTypes: true });
|
|
26532
26902
|
} catch {
|
|
26533
26903
|
continue;
|
|
26534
26904
|
}
|
|
26535
26905
|
for (const file of nested) {
|
|
26536
26906
|
if (!file.isFile() || !file.name.endsWith(".md"))
|
|
26537
26907
|
continue;
|
|
26538
|
-
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`,
|
|
26908
|
+
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join37(root, entry.name, file.name));
|
|
26539
26909
|
}
|
|
26540
26910
|
}
|
|
26541
26911
|
}
|
|
@@ -26592,12 +26962,12 @@ function indexRowOrPartialWrite(label, options) {
|
|
|
26592
26962
|
function upsertIndexRow(options) {
|
|
26593
26963
|
const { kind, slug, description, date, section } = options;
|
|
26594
26964
|
const indexPath = wikiIndexPath(kind);
|
|
26595
|
-
ensureDir(
|
|
26965
|
+
ensureDir(dirname17(indexPath));
|
|
26596
26966
|
const releaseLock = acquireFileLock(indexPath);
|
|
26597
26967
|
try {
|
|
26598
26968
|
let index;
|
|
26599
26969
|
try {
|
|
26600
|
-
index =
|
|
26970
|
+
index = readFileSync24(indexPath, "utf-8");
|
|
26601
26971
|
} catch {
|
|
26602
26972
|
index = "";
|
|
26603
26973
|
}
|
|
@@ -27036,15 +27406,15 @@ __export(exports_activity_log, {
|
|
|
27036
27406
|
readSentFilesForTopic: () => readSentFilesForTopic
|
|
27037
27407
|
});
|
|
27038
27408
|
import {
|
|
27039
|
-
existsSync as
|
|
27040
|
-
mkdirSync as
|
|
27409
|
+
existsSync as existsSync29,
|
|
27410
|
+
mkdirSync as mkdirSync26,
|
|
27041
27411
|
readdirSync as readdirSync11,
|
|
27042
|
-
renameSync as
|
|
27412
|
+
renameSync as renameSync13,
|
|
27043
27413
|
statSync as statSync16,
|
|
27044
27414
|
unlinkSync as unlinkSync20,
|
|
27045
|
-
writeFileSync as
|
|
27415
|
+
writeFileSync as writeFileSync20
|
|
27046
27416
|
} from "fs";
|
|
27047
|
-
import { basename as basename8, join as
|
|
27417
|
+
import { basename as basename8, join as join38 } from "path";
|
|
27048
27418
|
function startLogRotation() {
|
|
27049
27419
|
if (rotationTimer)
|
|
27050
27420
|
return;
|
|
@@ -27055,15 +27425,15 @@ function startLogRotation() {
|
|
|
27055
27425
|
function writeLog(entry) {
|
|
27056
27426
|
startLogRotation();
|
|
27057
27427
|
const logDir = resolveStorageLogDir();
|
|
27058
|
-
|
|
27428
|
+
mkdirSync26(logDir, { recursive: true });
|
|
27059
27429
|
const safeSession = sanitizeTopicName(entry.session);
|
|
27060
27430
|
const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
|
|
27061
|
-
const file =
|
|
27431
|
+
const file = join38(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
|
|
27062
27432
|
try {
|
|
27063
27433
|
const stat = statSync16(file);
|
|
27064
27434
|
if (stat.size >= MAX_FILE_SIZE) {
|
|
27065
27435
|
const rotated = file.replace(/\.jsonl$/, `.${Date.now()}.jsonl`);
|
|
27066
|
-
|
|
27436
|
+
renameSync13(file, rotated);
|
|
27067
27437
|
}
|
|
27068
27438
|
} catch {}
|
|
27069
27439
|
try {
|
|
@@ -27074,11 +27444,11 @@ function writeLog(entry) {
|
|
|
27074
27444
|
}
|
|
27075
27445
|
function rotateOldLogs() {
|
|
27076
27446
|
const logDir = resolveStorageLogDir();
|
|
27077
|
-
|
|
27447
|
+
mkdirSync26(logDir, { recursive: true });
|
|
27078
27448
|
try {
|
|
27079
27449
|
const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
27080
27450
|
try {
|
|
27081
|
-
const stat = statSync16(
|
|
27451
|
+
const stat = statSync16(join38(logDir, f));
|
|
27082
27452
|
return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
27083
27453
|
} catch {
|
|
27084
27454
|
return null;
|
|
@@ -27094,7 +27464,7 @@ function rotateOldLogs() {
|
|
|
27094
27464
|
if (freed >= toFree)
|
|
27095
27465
|
break;
|
|
27096
27466
|
try {
|
|
27097
|
-
unlinkSync20(
|
|
27467
|
+
unlinkSync20(join38(logDir, file.name));
|
|
27098
27468
|
freed += file.size;
|
|
27099
27469
|
logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
|
|
27100
27470
|
} catch (e) {
|
|
@@ -27106,9 +27476,9 @@ function rotateOldLogs() {
|
|
|
27106
27476
|
}
|
|
27107
27477
|
}
|
|
27108
27478
|
function writeSentFileLog(entry) {
|
|
27109
|
-
const dir =
|
|
27110
|
-
|
|
27111
|
-
const file =
|
|
27479
|
+
const dir = join38(resolveStorageUsersLogDir(), String(entry.userId));
|
|
27480
|
+
mkdirSync26(dir, { recursive: true });
|
|
27481
|
+
const file = join38(dir, "sent-files.jsonl");
|
|
27112
27482
|
try {
|
|
27113
27483
|
appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
|
|
27114
27484
|
} catch (e) {
|
|
@@ -27119,8 +27489,8 @@ function entryMatchesTopic(e, topicName) {
|
|
|
27119
27489
|
return e.topicName === topicName;
|
|
27120
27490
|
}
|
|
27121
27491
|
function readSentFilesForTopic(userId, topicName) {
|
|
27122
|
-
const file =
|
|
27123
|
-
if (!
|
|
27492
|
+
const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27493
|
+
if (!existsSync29(file))
|
|
27124
27494
|
return [];
|
|
27125
27495
|
try {
|
|
27126
27496
|
return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
|
|
@@ -27130,8 +27500,8 @@ function readSentFilesForTopic(userId, topicName) {
|
|
|
27130
27500
|
}
|
|
27131
27501
|
}
|
|
27132
27502
|
function removeSentFilesForTopic(userId, topicName) {
|
|
27133
|
-
const file =
|
|
27134
|
-
if (!
|
|
27503
|
+
const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27504
|
+
if (!existsSync29(file))
|
|
27135
27505
|
return;
|
|
27136
27506
|
try {
|
|
27137
27507
|
const remaining = readJsonlLines(file).filter((line) => {
|
|
@@ -27142,7 +27512,7 @@ function removeSentFilesForTopic(userId, topicName) {
|
|
|
27142
27512
|
}
|
|
27143
27513
|
});
|
|
27144
27514
|
if (remaining.length > 0) {
|
|
27145
|
-
|
|
27515
|
+
writeFileSync20(file, `${remaining.join(`
|
|
27146
27516
|
`)}
|
|
27147
27517
|
`);
|
|
27148
27518
|
} else {
|
|
@@ -27639,6 +28009,8 @@ __export(exports_storage_public, {
|
|
|
27639
28009
|
writeTasks: () => writeTasks,
|
|
27640
28010
|
writeSentFileLog: () => writeSentFileLog,
|
|
27641
28011
|
writeLog: () => writeLog,
|
|
28012
|
+
writeDecisions: () => writeDecisions,
|
|
28013
|
+
writeDecisionGraphSvg: () => writeDecisionGraphSvg,
|
|
27642
28014
|
wikiSummaryStorageSlug: () => wikiSummaryStorageSlug,
|
|
27643
28015
|
wikiSummarySlug: () => wikiSummarySlug,
|
|
27644
28016
|
wikiSummaryNames: () => exports_wiki_summary_names,
|
|
@@ -27647,9 +28019,11 @@ __export(exports_storage_public, {
|
|
|
27647
28019
|
wiki: () => exports_wiki,
|
|
27648
28020
|
waitForRuntimeProcessLease: () => waitForRuntimeProcessLease,
|
|
27649
28021
|
waitForRequiredRuntimeProcessLease: () => waitForRequiredRuntimeProcessLease,
|
|
28022
|
+
validateDecisionGraph: () => validateDecisionGraph,
|
|
27650
28023
|
upsertTopic: () => upsertTopic,
|
|
27651
28024
|
updateTopicThreadId: () => updateTopicThreadId,
|
|
27652
28025
|
updateTasks: () => updateTasks,
|
|
28026
|
+
updateDecisions: () => updateDecisions,
|
|
27653
28027
|
updateApiMessageUsage: () => updateApiMessageUsage,
|
|
27654
28028
|
updateApiMessageText: () => updateApiMessageText,
|
|
27655
28029
|
updateApiMessageSubagentCard: () => updateApiMessageSubagentCard,
|
|
@@ -27698,6 +28072,7 @@ __export(exports_storage_public, {
|
|
|
27698
28072
|
replaceConversationStrict: () => replaceConversationStrict,
|
|
27699
28073
|
reparentTopicChildren: () => reparentTopicChildren,
|
|
27700
28074
|
renderTaskList: () => renderTaskList,
|
|
28075
|
+
renderDecisionList: () => renderDecisionList,
|
|
27701
28076
|
renameTopic: () => renameTopic,
|
|
27702
28077
|
removeTopic: () => removeTopic,
|
|
27703
28078
|
removeSentFilesForTopic: () => removeSentFilesForTopic,
|
|
@@ -27709,6 +28084,7 @@ __export(exports_storage_public, {
|
|
|
27709
28084
|
readTasks: () => readTasks,
|
|
27710
28085
|
readSentFilesForTopic: () => readSentFilesForTopic,
|
|
27711
28086
|
readRawConversation: () => readRawConversation,
|
|
28087
|
+
readDecisions: () => readDecisions,
|
|
27712
28088
|
readConversation: () => readConversation,
|
|
27713
28089
|
quarantineForeignAskUserGates: () => quarantineForeignAskUserGates,
|
|
27714
28090
|
quarantineAskUserGate: () => quarantineAskUserGate,
|
|
@@ -27770,6 +28146,8 @@ __export(exports_storage_public, {
|
|
|
27770
28146
|
getGlobalAiName: () => getGlobalAiName,
|
|
27771
28147
|
getForumTopicByName: () => getTopicByName2,
|
|
27772
28148
|
getDmSessionId: () => getDmSessionId,
|
|
28149
|
+
getDecisionGraphSvgPath: () => getDecisionGraphSvgPath,
|
|
28150
|
+
getDecisionFilePath: () => getDecisionFilePath,
|
|
27773
28151
|
getConversationPath: () => getConversationPath,
|
|
27774
28152
|
getCommunicateThreadId: () => getCommunicateThreadId,
|
|
27775
28153
|
getApiTopicConfig: () => getApiTopicConfig,
|
|
@@ -27793,12 +28171,16 @@ __export(exports_storage_public, {
|
|
|
27793
28171
|
deleteTasks: () => deleteTasks,
|
|
27794
28172
|
deletePendingAsksForTopic: () => deletePendingAsksForTopic,
|
|
27795
28173
|
deleteMessagesForTopic: () => deleteMessagesForTopic,
|
|
28174
|
+
deleteDecisions: () => deleteDecisions,
|
|
27796
28175
|
deleteApiTopicConfig: () => deleteApiTopicConfig,
|
|
27797
28176
|
defaultTopicSurface: () => defaultTopicSurface,
|
|
27798
28177
|
defaultSurfaceScope: () => defaultSurfaceScope,
|
|
28178
|
+
decisions: () => exports_decisions,
|
|
28179
|
+
decisionScopeKey: () => decisionScopeKey,
|
|
27799
28180
|
db: () => db2,
|
|
27800
28181
|
createTasks: () => createTasks,
|
|
27801
28182
|
createPendingAsk: () => createPendingAsk,
|
|
28183
|
+
createDecisions: () => createDecisions,
|
|
27802
28184
|
copyMessagesForTopic: () => copyMessagesForTopic,
|
|
27803
28185
|
copyMessageSnapshotToTopic: () => copyMessageSnapshotToTopic,
|
|
27804
28186
|
conversations: () => exports_conversations,
|
|
@@ -27837,6 +28219,7 @@ __export(exports_storage_public, {
|
|
|
27837
28219
|
PROCESS_LEASE_HEARTBEAT_MS: () => PROCESS_LEASE_HEARTBEAT_MS,
|
|
27838
28220
|
PENDING_ASK_TTL_MS: () => PENDING_ASK_TTL_MS,
|
|
27839
28221
|
DEFAULT_AI_NAME: () => DEFAULT_AI_NAME,
|
|
28222
|
+
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES,
|
|
27840
28223
|
ConversationLogDivergedError: () => ConversationLogDivergedError
|
|
27841
28224
|
});
|
|
27842
28225
|
var init_storage_public = __esm(async () => {
|
|
@@ -27856,6 +28239,8 @@ var init_storage_public = __esm(async () => {
|
|
|
27856
28239
|
init_ask_user_gates();
|
|
27857
28240
|
await init_conversations();
|
|
27858
28241
|
init_conversations();
|
|
28242
|
+
await init_decisions();
|
|
28243
|
+
init_decisions();
|
|
27859
28244
|
await init_forum();
|
|
27860
28245
|
await init_forum();
|
|
27861
28246
|
await init_runtime_process_leases();
|
|
@@ -27894,6 +28279,16 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
27894
28279
|
topicId: context.topicId
|
|
27895
28280
|
});
|
|
27896
28281
|
}
|
|
28282
|
+
case "decision": {
|
|
28283
|
+
const { createDecisionMcpServer: createDecisionMcpServer2 } = await init_decision().then(() => exports_decision);
|
|
28284
|
+
return createDecisionMcpServer2({
|
|
28285
|
+
userId: context.userId,
|
|
28286
|
+
topic: context.topicTitle,
|
|
28287
|
+
topicId: context.topicId,
|
|
28288
|
+
agent: context.agent,
|
|
28289
|
+
model: context.model
|
|
28290
|
+
});
|
|
28291
|
+
}
|
|
27897
28292
|
case "token-stats": {
|
|
27898
28293
|
const { createTokenStatsMcpServer: createTokenStatsMcpServer2 } = await init_token_stats2().then(() => exports_token_stats2);
|
|
27899
28294
|
return createTokenStatsMcpServer2({ userId: context.userId });
|
|
@@ -27978,6 +28373,7 @@ var ACTIVE_HOSTED_MCP_SURFACES;
|
|
|
27978
28373
|
var init_hosted_surfaces = __esm(() => {
|
|
27979
28374
|
ACTIVE_HOSTED_MCP_SURFACES = [
|
|
27980
28375
|
"task",
|
|
28376
|
+
"decision",
|
|
27981
28377
|
"token-stats",
|
|
27982
28378
|
"system-health",
|
|
27983
28379
|
"vault",
|
|
@@ -28097,9 +28493,9 @@ var init_sse_transport = () => {};
|
|
|
28097
28493
|
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
28098
28494
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
28099
28495
|
import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
|
|
28100
|
-
import { McpServer as
|
|
28496
|
+
import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28101
28497
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
28102
|
-
import { z as
|
|
28498
|
+
import { z as z13 } from "zod";
|
|
28103
28499
|
function requireTopicAccess(ctx) {
|
|
28104
28500
|
const topic = getTopic(ctx.topicId);
|
|
28105
28501
|
if (!topic)
|
|
@@ -28218,7 +28614,7 @@ function appendAutoContinue(ctx, field) {
|
|
|
28218
28614
|
});
|
|
28219
28615
|
}
|
|
28220
28616
|
function buildNegotiumMcpServer(ctx) {
|
|
28221
|
-
const server = new
|
|
28617
|
+
const server = new McpServer8({ name: RUNTIME_MCP_KEY, version: "1.0.0" });
|
|
28222
28618
|
if (ctx.visualTools === true) {
|
|
28223
28619
|
for (const def of visualToolDefinitions) {
|
|
28224
28620
|
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;
|
|
@@ -28319,8 +28715,8 @@ function buildNegotiumMcpServer(ctx) {
|
|
|
28319
28715
|
}
|
|
28320
28716
|
}
|
|
28321
28717
|
if (ctx.fileDeliveryTools === true) {
|
|
28322
|
-
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:
|
|
28323
|
-
server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths:
|
|
28718
|
+
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));
|
|
28719
|
+
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 }) => {
|
|
28324
28720
|
const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
|
|
28325
28721
|
const hasError = results.some((result) => ("isError" in result) && result.isError);
|
|
28326
28722
|
return {
|
|
@@ -28618,23 +29014,23 @@ function defaultManifestFile() {
|
|
|
28618
29014
|
var init_paths = () => {};
|
|
28619
29015
|
|
|
28620
29016
|
// ../../packages/mcp-host/src/spec.ts
|
|
28621
|
-
import { z as
|
|
29017
|
+
import { z as z14 } from "zod";
|
|
28622
29018
|
var KEY_PATTERN, portSchema, mcpServerSpecSchema;
|
|
28623
29019
|
var init_spec = __esm(() => {
|
|
28624
29020
|
KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
28625
|
-
portSchema =
|
|
28626
|
-
mcpServerSpecSchema =
|
|
28627
|
-
key:
|
|
28628
|
-
transport:
|
|
28629
|
-
command:
|
|
28630
|
-
args:
|
|
28631
|
-
env:
|
|
28632
|
-
cwd:
|
|
28633
|
-
portRange:
|
|
28634
|
-
scope:
|
|
28635
|
-
readyTimeoutMs:
|
|
28636
|
-
idleEvictMs:
|
|
28637
|
-
healthIntervalMs:
|
|
29021
|
+
portSchema = z14.number().int().min(1).max(65535);
|
|
29022
|
+
mcpServerSpecSchema = z14.object({
|
|
29023
|
+
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"),
|
|
29024
|
+
transport: z14.enum(["stdio", "http"]),
|
|
29025
|
+
command: z14.string().min(1),
|
|
29026
|
+
args: z14.array(z14.string()).optional(),
|
|
29027
|
+
env: z14.record(z14.string(), z14.string()).optional(),
|
|
29028
|
+
cwd: z14.string().optional(),
|
|
29029
|
+
portRange: z14.object({ base: portSchema, max: portSchema }).optional(),
|
|
29030
|
+
scope: z14.enum(["node", "instance"]),
|
|
29031
|
+
readyTimeoutMs: z14.number().int().positive().optional(),
|
|
29032
|
+
idleEvictMs: z14.number().int().positive().optional(),
|
|
29033
|
+
healthIntervalMs: z14.number().int().positive().optional()
|
|
28638
29034
|
}).superRefine((spec, ctx) => {
|
|
28639
29035
|
if (spec.transport === "http") {
|
|
28640
29036
|
if (!spec.portRange) {
|
|
@@ -28655,9 +29051,9 @@ var init_spec = __esm(() => {
|
|
|
28655
29051
|
});
|
|
28656
29052
|
|
|
28657
29053
|
// ../../packages/mcp-host/src/manifest.ts
|
|
28658
|
-
import { existsSync as
|
|
28659
|
-
import { dirname as
|
|
28660
|
-
import { z as
|
|
29054
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
29055
|
+
import { dirname as dirname18 } from "path";
|
|
29056
|
+
import { z as z15 } from "zod";
|
|
28661
29057
|
|
|
28662
29058
|
class McpManifest {
|
|
28663
29059
|
file;
|
|
@@ -28700,11 +29096,11 @@ class McpManifest {
|
|
|
28700
29096
|
return this.entries.get(key)?.enabled ?? false;
|
|
28701
29097
|
}
|
|
28702
29098
|
load() {
|
|
28703
|
-
if (!
|
|
29099
|
+
if (!existsSync30(this.file))
|
|
28704
29100
|
return;
|
|
28705
29101
|
let parsed;
|
|
28706
29102
|
try {
|
|
28707
|
-
parsed = manifestFileSchema.parse(JSON.parse(
|
|
29103
|
+
parsed = manifestFileSchema.parse(JSON.parse(readFileSync25(this.file, "utf8")));
|
|
28708
29104
|
} catch (e) {
|
|
28709
29105
|
throw new Error(`Invalid MCP manifest at ${this.file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
28710
29106
|
}
|
|
@@ -28716,28 +29112,28 @@ class McpManifest {
|
|
|
28716
29112
|
}
|
|
28717
29113
|
}
|
|
28718
29114
|
save() {
|
|
28719
|
-
|
|
29115
|
+
mkdirSync27(dirname18(this.file), { recursive: true });
|
|
28720
29116
|
const payload = {
|
|
28721
29117
|
version: 1,
|
|
28722
29118
|
servers: [...this.entries.values()]
|
|
28723
29119
|
};
|
|
28724
29120
|
const tmp = `${this.file}.tmp-${process.pid}`;
|
|
28725
|
-
|
|
29121
|
+
writeFileSync21(tmp, `${JSON.stringify(payload, null, 2)}
|
|
28726
29122
|
`);
|
|
28727
|
-
|
|
29123
|
+
renameSync14(tmp, this.file);
|
|
28728
29124
|
}
|
|
28729
29125
|
}
|
|
28730
29126
|
var manifestEntrySchema, manifestFileSchema;
|
|
28731
29127
|
var init_manifest = __esm(() => {
|
|
28732
29128
|
init_paths();
|
|
28733
29129
|
init_spec();
|
|
28734
|
-
manifestEntrySchema =
|
|
29130
|
+
manifestEntrySchema = z15.object({
|
|
28735
29131
|
spec: mcpServerSpecSchema,
|
|
28736
|
-
enabled:
|
|
29132
|
+
enabled: z15.boolean()
|
|
28737
29133
|
});
|
|
28738
|
-
manifestFileSchema =
|
|
28739
|
-
version:
|
|
28740
|
-
servers:
|
|
29134
|
+
manifestFileSchema = z15.object({
|
|
29135
|
+
version: z15.literal(1),
|
|
29136
|
+
servers: z15.array(manifestEntrySchema)
|
|
28741
29137
|
});
|
|
28742
29138
|
});
|
|
28743
29139
|
|
|
@@ -28752,15 +29148,15 @@ function sanitizePathComponent(name) {
|
|
|
28752
29148
|
// ../../packages/mcp-host/src/manager.ts
|
|
28753
29149
|
import { execFileSync as execFileSync8, spawn as spawn8 } from "child_process";
|
|
28754
29150
|
import {
|
|
28755
|
-
mkdirSync as
|
|
29151
|
+
mkdirSync as mkdirSync28,
|
|
28756
29152
|
readdirSync as readdirSync12,
|
|
28757
|
-
readFileSync as
|
|
28758
|
-
renameSync as
|
|
29153
|
+
readFileSync as readFileSync26,
|
|
29154
|
+
renameSync as renameSync15,
|
|
28759
29155
|
unlinkSync as unlinkSync21,
|
|
28760
|
-
writeFileSync as
|
|
29156
|
+
writeFileSync as writeFileSync22
|
|
28761
29157
|
} from "fs";
|
|
28762
29158
|
import { connect, createServer as createServer3 } from "net";
|
|
28763
|
-
import { join as
|
|
29159
|
+
import { join as join39 } from "path";
|
|
28764
29160
|
function regKey(key, instanceKey) {
|
|
28765
29161
|
return `${key}\x00${instanceKey}`;
|
|
28766
29162
|
}
|
|
@@ -29026,7 +29422,7 @@ class McpHost {
|
|
|
29026
29422
|
const range = spec.portRange;
|
|
29027
29423
|
if (!range)
|
|
29028
29424
|
throw new Error(`MCP spec "${spec.key}" is http but has no portRange`);
|
|
29029
|
-
|
|
29425
|
+
mkdirSync28(this.portsDir, { recursive: true });
|
|
29030
29426
|
const claims = this.readPortClaims();
|
|
29031
29427
|
const own = claims.get(fileName);
|
|
29032
29428
|
if (own !== undefined && own >= range.base && own <= range.max && !this.usedPorts.has(own)) {
|
|
@@ -29062,7 +29458,7 @@ class McpHost {
|
|
|
29062
29458
|
if (file.includes(".tmp-"))
|
|
29063
29459
|
continue;
|
|
29064
29460
|
try {
|
|
29065
|
-
const port = Number.parseInt(
|
|
29461
|
+
const port = Number.parseInt(readFileSync26(join39(this.portsDir, file), "utf8").trim(), 10);
|
|
29066
29462
|
if (!Number.isNaN(port))
|
|
29067
29463
|
claims.set(file, port);
|
|
29068
29464
|
} catch {}
|
|
@@ -29071,18 +29467,18 @@ class McpHost {
|
|
|
29071
29467
|
}
|
|
29072
29468
|
writePortFile(fileName, port) {
|
|
29073
29469
|
try {
|
|
29074
|
-
|
|
29075
|
-
const file =
|
|
29470
|
+
mkdirSync28(this.portsDir, { recursive: true });
|
|
29471
|
+
const file = join39(this.portsDir, fileName);
|
|
29076
29472
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
29077
|
-
|
|
29078
|
-
|
|
29473
|
+
writeFileSync22(tmp, String(port));
|
|
29474
|
+
renameSync15(tmp, file);
|
|
29079
29475
|
} catch (e) {
|
|
29080
29476
|
this.log("warn", "Failed to write MCP port file", { fileName, port, err: String(e) });
|
|
29081
29477
|
}
|
|
29082
29478
|
}
|
|
29083
29479
|
deletePortFile(fileName) {
|
|
29084
29480
|
try {
|
|
29085
|
-
unlinkSync21(
|
|
29481
|
+
unlinkSync21(join39(this.portsDir, fileName));
|
|
29086
29482
|
} catch {}
|
|
29087
29483
|
}
|
|
29088
29484
|
cleanupRunning(rkey, inst) {
|
|
@@ -29187,14 +29583,14 @@ var init_src3 = __esm(() => {
|
|
|
29187
29583
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
29188
29584
|
import {
|
|
29189
29585
|
copyFileSync as copyFileSync4,
|
|
29190
|
-
existsSync as
|
|
29191
|
-
mkdirSync as
|
|
29192
|
-
readFileSync as
|
|
29586
|
+
existsSync as existsSync31,
|
|
29587
|
+
mkdirSync as mkdirSync29,
|
|
29588
|
+
readFileSync as readFileSync27,
|
|
29193
29589
|
rmSync as rmSync9,
|
|
29194
29590
|
statSync as statSync18,
|
|
29195
|
-
writeFileSync as
|
|
29591
|
+
writeFileSync as writeFileSync23
|
|
29196
29592
|
} from "fs";
|
|
29197
|
-
import { basename as basename10, extname as extname4, join as
|
|
29593
|
+
import { basename as basename10, extname as extname4, join as join40 } from "path";
|
|
29198
29594
|
function safeExtension(filename) {
|
|
29199
29595
|
const extension = extname4(basename10(filename));
|
|
29200
29596
|
return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
|
|
@@ -29212,20 +29608,20 @@ function contentDisposition(filename) {
|
|
|
29212
29608
|
|
|
29213
29609
|
class NodeFileStore {
|
|
29214
29610
|
uploadDir;
|
|
29215
|
-
constructor(uploadDir =
|
|
29611
|
+
constructor(uploadDir = join40(DATA_DIR, "uploads")) {
|
|
29216
29612
|
this.uploadDir = uploadDir;
|
|
29217
29613
|
}
|
|
29218
29614
|
#ensureDir() {
|
|
29219
|
-
|
|
29615
|
+
mkdirSync29(this.uploadDir, { recursive: true });
|
|
29220
29616
|
}
|
|
29221
29617
|
#metadataPath(fileId) {
|
|
29222
|
-
return
|
|
29618
|
+
return join40(this.uploadDir, `${fileId}.meta.json`);
|
|
29223
29619
|
}
|
|
29224
29620
|
#metadata(fileId) {
|
|
29225
29621
|
if (!FILE_ID_RE.test(fileId))
|
|
29226
29622
|
return null;
|
|
29227
29623
|
try {
|
|
29228
|
-
const parsed = JSON.parse(
|
|
29624
|
+
const parsed = JSON.parse(readFileSync27(this.#metadataPath(fileId), "utf8"));
|
|
29229
29625
|
if (typeof parsed.filename !== "string" || typeof parsed.mimeType !== "string" || typeof parsed.sizeBytes !== "number" || typeof parsed.savedName !== "string") {
|
|
29230
29626
|
return null;
|
|
29231
29627
|
}
|
|
@@ -29248,7 +29644,7 @@ class NodeFileStore {
|
|
|
29248
29644
|
hooks = {
|
|
29249
29645
|
resolveAttachmentByFileId: (fileId) => {
|
|
29250
29646
|
const metadata = this.#metadata(fileId);
|
|
29251
|
-
if (!metadata || !
|
|
29647
|
+
if (!metadata || !existsSync31(join40(this.uploadDir, metadata.savedName)))
|
|
29252
29648
|
return null;
|
|
29253
29649
|
return this.#attachment(fileId, metadata);
|
|
29254
29650
|
},
|
|
@@ -29256,8 +29652,8 @@ class NodeFileStore {
|
|
|
29256
29652
|
const metadata = this.#metadata(fileId);
|
|
29257
29653
|
if (!metadata)
|
|
29258
29654
|
return null;
|
|
29259
|
-
const path =
|
|
29260
|
-
return
|
|
29655
|
+
const path = join40(this.uploadDir, metadata.savedName);
|
|
29656
|
+
return existsSync31(path) ? path : null;
|
|
29261
29657
|
},
|
|
29262
29658
|
storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
|
|
29263
29659
|
deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
|
|
@@ -29267,7 +29663,7 @@ class NodeFileStore {
|
|
|
29267
29663
|
const fileId = randomUUID24();
|
|
29268
29664
|
const extension = safeExtension(absPath);
|
|
29269
29665
|
const savedName = `${fileId}${extension}`;
|
|
29270
|
-
const savedPath =
|
|
29666
|
+
const savedPath = join40(this.uploadDir, savedName);
|
|
29271
29667
|
try {
|
|
29272
29668
|
const stats = statSync18(absPath);
|
|
29273
29669
|
if (!stats.isFile() || stats.size > MAX_UPLOAD_BYTES)
|
|
@@ -29282,7 +29678,7 @@ class NodeFileStore {
|
|
|
29282
29678
|
...access.visibility ? { visibility: access.visibility } : {}
|
|
29283
29679
|
};
|
|
29284
29680
|
copyFileSync4(absPath, savedPath);
|
|
29285
|
-
|
|
29681
|
+
writeFileSync23(this.#metadataPath(fileId), JSON.stringify(metadata), { mode: 384 });
|
|
29286
29682
|
return this.#attachment(fileId, metadata);
|
|
29287
29683
|
} catch (error2) {
|
|
29288
29684
|
rmSync9(savedPath, { force: true });
|
|
@@ -29299,8 +29695,8 @@ class NodeFileStore {
|
|
|
29299
29695
|
const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
|
|
29300
29696
|
if (!allowed)
|
|
29301
29697
|
return null;
|
|
29302
|
-
const path =
|
|
29303
|
-
if (!
|
|
29698
|
+
const path = join40(this.uploadDir, metadata.savedName);
|
|
29699
|
+
if (!existsSync31(path))
|
|
29304
29700
|
return null;
|
|
29305
29701
|
return new Response(Bun.file(path), {
|
|
29306
29702
|
headers: {
|
|
@@ -29318,7 +29714,7 @@ class NodeFileStore {
|
|
|
29318
29714
|
const metadata = this.#metadata(fileId);
|
|
29319
29715
|
if (metadata?.topicId !== topicId)
|
|
29320
29716
|
continue;
|
|
29321
|
-
rmSync9(
|
|
29717
|
+
rmSync9(join40(this.uploadDir, metadata.savedName), { force: true });
|
|
29322
29718
|
rmSync9(this.#metadataPath(fileId), { force: true });
|
|
29323
29719
|
}
|
|
29324
29720
|
}
|
|
@@ -29470,14 +29866,14 @@ function createPollingSseStream(req, options) {
|
|
|
29470
29866
|
import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
29471
29867
|
import {
|
|
29472
29868
|
chmodSync as chmodSync6,
|
|
29473
|
-
existsSync as
|
|
29474
|
-
mkdirSync as
|
|
29475
|
-
readFileSync as
|
|
29476
|
-
renameSync as
|
|
29869
|
+
existsSync as existsSync32,
|
|
29870
|
+
mkdirSync as mkdirSync30,
|
|
29871
|
+
readFileSync as readFileSync28,
|
|
29872
|
+
renameSync as renameSync16,
|
|
29477
29873
|
unlinkSync as unlinkSync22,
|
|
29478
|
-
writeFileSync as
|
|
29874
|
+
writeFileSync as writeFileSync24
|
|
29479
29875
|
} from "fs";
|
|
29480
|
-
import { dirname as
|
|
29876
|
+
import { dirname as dirname19, resolve as resolve21 } from "path";
|
|
29481
29877
|
function jsonError(status, error2) {
|
|
29482
29878
|
return Response.json({ ok: false, error: error2 }, { status });
|
|
29483
29879
|
}
|
|
@@ -29967,6 +30363,25 @@ function createNodeControlHandler(options) {
|
|
|
29967
30363
|
return jsonError(404, "Topic not found");
|
|
29968
30364
|
return Response.json({ ok: true, usage: getTopicStats(userId, topicId) });
|
|
29969
30365
|
}
|
|
30366
|
+
const decisionsMatch = path.match(/^\/topics\/([^/]+)\/decisions$/);
|
|
30367
|
+
if (decisionsMatch && req.method === "GET") {
|
|
30368
|
+
const topicId = decodeURIComponent(decisionsMatch[1]);
|
|
30369
|
+
const userId = requiredText(url.searchParams.get("user"), "user");
|
|
30370
|
+
if (!topicForUser(topicId, userId))
|
|
30371
|
+
return jsonError(404, "Topic not found");
|
|
30372
|
+
return Response.json({ ok: true, decisions: readDecisions(userId, topicId) });
|
|
30373
|
+
}
|
|
30374
|
+
const decisionGraphMatch = path.match(/^\/topics\/([^/]+)\/decision-graph$/);
|
|
30375
|
+
if (decisionGraphMatch && req.method === "POST") {
|
|
30376
|
+
const topicId = decodeURIComponent(decisionGraphMatch[1]);
|
|
30377
|
+
const body = await bodyRecord(req);
|
|
30378
|
+
const userId = requiredText(body.userId, "userId");
|
|
30379
|
+
const svg = requiredText(body.svg, "svg");
|
|
30380
|
+
if (!topicForUser(topicId, userId))
|
|
30381
|
+
return jsonError(404, "Topic not found");
|
|
30382
|
+
writeDecisionGraphSvg(userId, topicId, svg);
|
|
30383
|
+
return Response.json({ ok: true });
|
|
30384
|
+
}
|
|
29970
30385
|
const modelMatch = path.match(/^\/topics\/([^/]+)\/model$/);
|
|
29971
30386
|
if (modelMatch && req.method === "POST") {
|
|
29972
30387
|
const topicId = decodeURIComponent(modelMatch[1]);
|
|
@@ -30104,20 +30519,20 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30104
30519
|
stateDir: STATE_DIR,
|
|
30105
30520
|
startedAt
|
|
30106
30521
|
};
|
|
30107
|
-
|
|
30522
|
+
mkdirSync30(dirname19(NODE_DAEMON_INFO_PATH), { recursive: true });
|
|
30108
30523
|
const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
|
|
30109
|
-
|
|
30524
|
+
writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
|
|
30110
30525
|
`, { mode: 384 });
|
|
30111
30526
|
chmodSync6(temporary, 384);
|
|
30112
|
-
|
|
30527
|
+
renameSync16(temporary, NODE_DAEMON_INFO_PATH);
|
|
30113
30528
|
chmodSync6(NODE_DAEMON_INFO_PATH, 384);
|
|
30114
30529
|
return info;
|
|
30115
30530
|
}
|
|
30116
30531
|
function readNodeDaemonInfo() {
|
|
30117
|
-
if (!
|
|
30532
|
+
if (!existsSync32(NODE_DAEMON_INFO_PATH))
|
|
30118
30533
|
return null;
|
|
30119
30534
|
try {
|
|
30120
|
-
const value = JSON.parse(
|
|
30535
|
+
const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
|
|
30121
30536
|
if (value?.schemaVersion !== 1 || value.stateDir !== STATE_DIR || !Number.isInteger(value.protocolVersion) || !Number.isInteger(value.pid) || !Number.isInteger(value.port) || value.port <= 0) {
|
|
30122
30537
|
return null;
|
|
30123
30538
|
}
|
|
@@ -30401,7 +30816,7 @@ var init_schedule = __esm(() => {
|
|
|
30401
30816
|
|
|
30402
30817
|
// ../../packages/module-cron/src/scripts.ts
|
|
30403
30818
|
import { spawn as spawn9 } from "child_process";
|
|
30404
|
-
import { existsSync as
|
|
30819
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
|
|
30405
30820
|
import { resolve as resolve22, sep as sep2 } from "path";
|
|
30406
30821
|
function validateCronScriptName(script) {
|
|
30407
30822
|
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
|
|
@@ -30419,12 +30834,12 @@ function resolveCronScriptPath(script) {
|
|
|
30419
30834
|
return path;
|
|
30420
30835
|
}
|
|
30421
30836
|
function listCronScripts() {
|
|
30422
|
-
|
|
30837
|
+
mkdirSync31(CRON_JOBS_DIR, { recursive: true });
|
|
30423
30838
|
return readdirSync13(CRON_JOBS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && validateCronScriptName(entry.name).ok).map((entry) => entry.name).sort();
|
|
30424
30839
|
}
|
|
30425
30840
|
function cronScriptExists(script) {
|
|
30426
30841
|
try {
|
|
30427
|
-
return
|
|
30842
|
+
return existsSync33(resolveCronScriptPath(script));
|
|
30428
30843
|
} catch {
|
|
30429
30844
|
return false;
|
|
30430
30845
|
}
|
|
@@ -30456,7 +30871,7 @@ async function runCronPromptScript(options) {
|
|
|
30456
30871
|
if (options.signal?.aborted)
|
|
30457
30872
|
throw new CronScriptError("script run aborted");
|
|
30458
30873
|
const scriptPath = resolveCronScriptPath(options.script);
|
|
30459
|
-
if (!
|
|
30874
|
+
if (!existsSync33(scriptPath))
|
|
30460
30875
|
throw new CronScriptError(`cron script not found: ${options.script}`);
|
|
30461
30876
|
const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
|
|
30462
30877
|
const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
|
|
@@ -31649,7 +32064,7 @@ var init_scheduler = __esm(async () => {
|
|
|
31649
32064
|
});
|
|
31650
32065
|
|
|
31651
32066
|
// ../../packages/module-cron/src/module.ts
|
|
31652
|
-
import { existsSync as
|
|
32067
|
+
import { existsSync as existsSync34 } from "fs";
|
|
31653
32068
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31654
32069
|
function envMs(name, fallback) {
|
|
31655
32070
|
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
@@ -31680,7 +32095,7 @@ function createCronModule(options = {}) {
|
|
|
31680
32095
|
if (event.type === "topic-deleted")
|
|
31681
32096
|
trackCleanup(resetCronTopicContext(event.topicId));
|
|
31682
32097
|
});
|
|
31683
|
-
const mcpServerFile = options.mcpServerFile ?? (
|
|
32098
|
+
const mcpServerFile = options.mcpServerFile ?? (existsSync34(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
|
|
31684
32099
|
const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
|
|
31685
32100
|
scopes: ["forum", "manager"],
|
|
31686
32101
|
forumRequired: true,
|
|
@@ -32347,6 +32762,73 @@ var init_commands = __esm(() => {
|
|
|
32347
32762
|
];
|
|
32348
32763
|
});
|
|
32349
32764
|
|
|
32765
|
+
// ../../adapters/terminal/src/decision-graph.ts
|
|
32766
|
+
import {
|
|
32767
|
+
layoutGraph,
|
|
32768
|
+
renderSvgGraph,
|
|
32769
|
+
renderTerminalGraph
|
|
32770
|
+
} from "orchgraph";
|
|
32771
|
+
function adjustDecisionGraphSpacing(current3, delta) {
|
|
32772
|
+
return Math.min(MAX_DECISION_GRAPH_SPACING, Math.max(MIN_DECISION_GRAPH_SPACING, current3 + delta));
|
|
32773
|
+
}
|
|
32774
|
+
function decisionNodeState(status) {
|
|
32775
|
+
switch (status) {
|
|
32776
|
+
case "proposed":
|
|
32777
|
+
return "queued";
|
|
32778
|
+
case "executed":
|
|
32779
|
+
return "succeeded";
|
|
32780
|
+
case "rejected":
|
|
32781
|
+
return "failed";
|
|
32782
|
+
case "superseded":
|
|
32783
|
+
return "blocked";
|
|
32784
|
+
default:
|
|
32785
|
+
return "idle";
|
|
32786
|
+
}
|
|
32787
|
+
}
|
|
32788
|
+
function compactDetail(decision) {
|
|
32789
|
+
const rationale = decision.reasoning.replace(/\s+/g, " ").trim();
|
|
32790
|
+
return `${rationale} \xB7 ${decision.agent}${decision.model ? `/${decision.model}` : ""}`;
|
|
32791
|
+
}
|
|
32792
|
+
function buildDecisionGraph(decisions2, topicTitle) {
|
|
32793
|
+
const ids = new Set(decisions2.map((decision) => decision.id));
|
|
32794
|
+
return {
|
|
32795
|
+
id: "decisions",
|
|
32796
|
+
title: topicTitle,
|
|
32797
|
+
direction: "DOWN",
|
|
32798
|
+
nodes: decisions2.map((decision) => ({
|
|
32799
|
+
id: decision.id,
|
|
32800
|
+
label: `#${decision.id} ${decision.action}`,
|
|
32801
|
+
detail: compactDetail(decision),
|
|
32802
|
+
kind: decision.status,
|
|
32803
|
+
state: decisionNodeState(decision.status),
|
|
32804
|
+
metadata: { timestamp: decision.timestamp, agent: decision.agent, model: decision.model }
|
|
32805
|
+
})),
|
|
32806
|
+
edges: decisions2.flatMap((decision) => (decision.causedBy ?? []).filter((upstream) => ids.has(upstream)).map((upstream) => ({
|
|
32807
|
+
id: `caused:${upstream}:${decision.id}`,
|
|
32808
|
+
source: upstream,
|
|
32809
|
+
target: decision.id,
|
|
32810
|
+
label: "caused",
|
|
32811
|
+
kind: "caused",
|
|
32812
|
+
direction: "forward",
|
|
32813
|
+
style: "solid"
|
|
32814
|
+
})))
|
|
32815
|
+
};
|
|
32816
|
+
}
|
|
32817
|
+
async function renderDecisionGraph(graph, spacing = DEFAULT_DECISION_GRAPH_SPACING, signal) {
|
|
32818
|
+
const geometry = await layoutGraph(graph, {
|
|
32819
|
+
direction: "DOWN",
|
|
32820
|
+
spacing,
|
|
32821
|
+
signal,
|
|
32822
|
+
timeoutMs: 15000
|
|
32823
|
+
});
|
|
32824
|
+
return {
|
|
32825
|
+
canvas: { ...renderTerminalGraph(graph, geometry), title: graph.title },
|
|
32826
|
+
svg: renderSvgGraph(graph, geometry, { title: graph.title })
|
|
32827
|
+
};
|
|
32828
|
+
}
|
|
32829
|
+
var DEFAULT_DECISION_GRAPH_SPACING = 4, MIN_DECISION_GRAPH_SPACING = 2, MAX_DECISION_GRAPH_SPACING = 10;
|
|
32830
|
+
var init_decision_graph = () => {};
|
|
32831
|
+
|
|
32350
32832
|
// ../../adapters/terminal/src/subagent-graph.ts
|
|
32351
32833
|
import {
|
|
32352
32834
|
layoutTerminalGraph,
|
|
@@ -32540,6 +33022,9 @@ function createInitialState(userId) {
|
|
|
32540
33022
|
subagentGraphLoading: false,
|
|
32541
33023
|
subagentGraphOffset: { x: 0, y: 0 },
|
|
32542
33024
|
subagentGraphSpacing: DEFAULT_SUBAGENT_GRAPH_SPACING,
|
|
33025
|
+
decisionGraphLoading: false,
|
|
33026
|
+
decisionGraphOffset: { x: 0, y: 0 },
|
|
33027
|
+
decisionGraphSpacing: DEFAULT_DECISION_GRAPH_SPACING,
|
|
32543
33028
|
askChoiceIndex: 0,
|
|
32544
33029
|
taskSidebarEnabled: true,
|
|
32545
33030
|
overlay: null,
|
|
@@ -33103,6 +33588,7 @@ function applyRuntimeEvent(state, event) {
|
|
|
33103
33588
|
var NO_TOPIC_PICKER_SELECTION = -1;
|
|
33104
33589
|
var init_state2 = __esm(async () => {
|
|
33105
33590
|
await init_src();
|
|
33591
|
+
init_decision_graph();
|
|
33106
33592
|
init_subagent_graph();
|
|
33107
33593
|
});
|
|
33108
33594
|
|
|
@@ -33167,9 +33653,9 @@ var init_context_usage = __esm(async () => {
|
|
|
33167
33653
|
|
|
33168
33654
|
// ../../adapters/terminal/src/path-suggest.ts
|
|
33169
33655
|
import { execFile as execFile3 } from "child_process";
|
|
33170
|
-
import { existsSync as
|
|
33656
|
+
import { existsSync as existsSync35, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
33171
33657
|
import { homedir as homedir10 } from "os";
|
|
33172
|
-
import { basename as basename11, dirname as
|
|
33658
|
+
import { basename as basename11, dirname as dirname20, join as join41, sep as sep3 } from "path";
|
|
33173
33659
|
import { promisify as promisify3 } from "util";
|
|
33174
33660
|
function indexRecursivePaths(files) {
|
|
33175
33661
|
const paths = [];
|
|
@@ -33270,15 +33756,15 @@ function resolveFragment(frag) {
|
|
|
33270
33756
|
else if (frag === "~/")
|
|
33271
33757
|
path = home;
|
|
33272
33758
|
else if (frag.startsWith("~/"))
|
|
33273
|
-
path =
|
|
33759
|
+
path = join41(home, frag.slice(2));
|
|
33274
33760
|
else if (frag.startsWith("/"))
|
|
33275
33761
|
path = frag;
|
|
33276
33762
|
else
|
|
33277
|
-
path =
|
|
33763
|
+
path = join41(home, frag);
|
|
33278
33764
|
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
33279
33765
|
return { dir: path, prefix: "" };
|
|
33280
33766
|
}
|
|
33281
|
-
return { dir:
|
|
33767
|
+
return { dir: dirname20(path), prefix: basename11(path) };
|
|
33282
33768
|
}
|
|
33283
33769
|
function toToken(fullPath, isDir) {
|
|
33284
33770
|
const home = homedir10();
|
|
@@ -33303,7 +33789,7 @@ function rankAndSlice(dir, candidates) {
|
|
|
33303
33789
|
});
|
|
33304
33790
|
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
33305
33791
|
label: `${relPath}${isDir ? "/" : ""}`,
|
|
33306
|
-
value: toToken(
|
|
33792
|
+
value: toToken(join41(dir, relPath), isDir),
|
|
33307
33793
|
isDir
|
|
33308
33794
|
}));
|
|
33309
33795
|
}
|
|
@@ -33382,7 +33868,7 @@ function pathSuggestions(lineText, col) {
|
|
|
33382
33868
|
let isDir = entry.isDirectory();
|
|
33383
33869
|
if (!isDir && entry.isSymbolicLink()) {
|
|
33384
33870
|
try {
|
|
33385
|
-
isDir = statSync19(
|
|
33871
|
+
isDir = statSync19(join41(dir, entry.name)).isDirectory();
|
|
33386
33872
|
} catch {}
|
|
33387
33873
|
}
|
|
33388
33874
|
return {
|
|
@@ -33409,14 +33895,14 @@ function fragmentToAbsolutePath(frag) {
|
|
|
33409
33895
|
if (frag === "~" || frag === "~/")
|
|
33410
33896
|
return home;
|
|
33411
33897
|
if (frag.startsWith("~/"))
|
|
33412
|
-
return
|
|
33898
|
+
return join41(home, frag.slice(2));
|
|
33413
33899
|
if (frag.startsWith("/"))
|
|
33414
33900
|
return frag;
|
|
33415
|
-
return
|
|
33901
|
+
return join41(home, frag);
|
|
33416
33902
|
}
|
|
33417
33903
|
function fragmentResolves(frag) {
|
|
33418
33904
|
try {
|
|
33419
|
-
return
|
|
33905
|
+
return existsSync35(fragmentToAbsolutePath(frag));
|
|
33420
33906
|
} catch {
|
|
33421
33907
|
return false;
|
|
33422
33908
|
}
|
|
@@ -34282,7 +34768,7 @@ function helpLines() {
|
|
|
34282
34768
|
line(" Alt-Backspace delete word \xB7 Ctrl-U clear before cursor"),
|
|
34283
34769
|
line(" Cmd-Backspace works when forwarded \xB7 Ctrl-W/K aliases"),
|
|
34284
34770
|
line(" Mouse wheel / PgUp/PgDn scroll \xB7 Ctrl-E load older \xB7 Ctrl-T tasks"),
|
|
34285
|
-
line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-X abort"),
|
|
34771
|
+
line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions \xB7 Ctrl-X abort"),
|
|
34286
34772
|
line(" In topics: type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete"),
|
|
34287
34773
|
line(" Esc/Ctrl-C stop active turn \xB7 Ctrl-C twice when idle to quit"),
|
|
34288
34774
|
line(""),
|
|
@@ -34629,6 +35115,43 @@ function subagentGraphLines(state, width, height, animationFrame) {
|
|
|
34629
35115
|
})
|
|
34630
35116
|
].slice(0, height);
|
|
34631
35117
|
}
|
|
35118
|
+
function decisionGraphLines(state, width, height) {
|
|
35119
|
+
const headerHeight = 5;
|
|
35120
|
+
const canvas = state.decisionGraph;
|
|
35121
|
+
const offset = state.decisionGraphOffset;
|
|
35122
|
+
const viewportWidth = Math.max(1, width - 4);
|
|
35123
|
+
const viewportHeight = Math.max(1, height - headerHeight);
|
|
35124
|
+
const maxX = Math.max(0, (canvas?.width ?? 0) - viewportWidth);
|
|
35125
|
+
const maxY = Math.max(0, (canvas?.height ?? 0) - viewportHeight);
|
|
35126
|
+
const x = Math.min(maxX, offset.x);
|
|
35127
|
+
const y = Math.min(maxY, offset.y);
|
|
35128
|
+
const position = maxX > 0 || maxY > 0 ? ` \xB7 view ${x + 1},${y + 1}/${maxX + 1},${maxY + 1}` : "";
|
|
35129
|
+
const header = [
|
|
35130
|
+
line(" Decision graph", { fg: theme.accent, bold: true }),
|
|
35131
|
+
line(` ${canvas?.title ?? activeTopic(state)?.title ?? "Current topic"}`, {
|
|
35132
|
+
fg: theme.text,
|
|
35133
|
+
bold: true
|
|
35134
|
+
}),
|
|
35135
|
+
line(` [/] spacing ${state.decisionGraphSpacing} \xB7 drag/wheel/arrows/hjkl move \xB7 Ctrl-D/Esc close${position}`, { fg: theme.muted }),
|
|
35136
|
+
line(" \u25CB accepted \xB7 \u25F7 proposed \xB7 \u2713 executed \xB7 \u2717 rejected \xB7 ! superseded", {
|
|
35137
|
+
fg: theme.subtle
|
|
35138
|
+
}),
|
|
35139
|
+
line("")
|
|
35140
|
+
];
|
|
35141
|
+
if (state.decisionGraphLoading) {
|
|
35142
|
+
return [...header, line(" Laying out decisions with Orchgraph\u2026", { fg: theme.cyan })].slice(0, height);
|
|
35143
|
+
}
|
|
35144
|
+
if (!canvas || canvas.lines.length === 0) {
|
|
35145
|
+
return [...header, line(" No decision graph data", { fg: theme.muted })].slice(0, height);
|
|
35146
|
+
}
|
|
35147
|
+
return [
|
|
35148
|
+
...header,
|
|
35149
|
+
...Array.from({ length: viewportHeight }, (_, index) => {
|
|
35150
|
+
const text2 = sliceWidthRange(canvas.lines[y + index] ?? "", x, viewportWidth);
|
|
35151
|
+
return line(` ${text2}`, { fg: /[\u256D\u256E\u2570\u256F]/.test(text2) ? theme.cyan : theme.muted });
|
|
35152
|
+
})
|
|
35153
|
+
].slice(0, height);
|
|
35154
|
+
}
|
|
34632
35155
|
function modelOverlayLines(state, height) {
|
|
34633
35156
|
const currentModel = effectiveTopicModel(activeTopic(state));
|
|
34634
35157
|
const modelColumnWidth = Math.max(...SELECTABLE_MODELS.map(({ model }) => `${model} (current)`.length));
|
|
@@ -34746,6 +35269,8 @@ function conversationLines(state, width, height, animationFrame = 0, nowMs = ter
|
|
|
34746
35269
|
return topicOverlayLines(state, width, height, animationFrame).slice(0, height);
|
|
34747
35270
|
if (state.overlay === "subagents")
|
|
34748
35271
|
return subagentGraphLines(state, width, height, animationFrame);
|
|
35272
|
+
if (state.overlay === "decisions")
|
|
35273
|
+
return decisionGraphLines(state, width, height);
|
|
34749
35274
|
if (state.overlay === "background-session")
|
|
34750
35275
|
return backgroundSessionViewportLines(state, width, height, nowMs);
|
|
34751
35276
|
if (state.overlay === "models")
|
|
@@ -34888,7 +35413,7 @@ function inputVisualLines(state, width) {
|
|
|
34888
35413
|
return { lines: result, cursorLine, cursorColumn };
|
|
34889
35414
|
}
|
|
34890
35415
|
function composerPane(state, width) {
|
|
34891
|
-
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";
|
|
35416
|
+
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";
|
|
34892
35417
|
const visual = inputVisualLines(state, width);
|
|
34893
35418
|
let inputStart = Math.max(0, visual.lines.length - 5);
|
|
34894
35419
|
if (visual.cursorLine < inputStart)
|
|
@@ -35050,6 +35575,13 @@ function footerHintText(state) {
|
|
|
35050
35575
|
if (state.overlay === "subagents") {
|
|
35051
35576
|
return ["arrows/hjkl/wheel move \xB7 Esc close ", "arrows move \xB7 Esc close ", "Esc close "];
|
|
35052
35577
|
}
|
|
35578
|
+
if (state.overlay === "decisions") {
|
|
35579
|
+
return [
|
|
35580
|
+
"arrows/hjkl/wheel move \xB7 Ctrl-D/Esc close ",
|
|
35581
|
+
"arrows move \xB7 Esc close ",
|
|
35582
|
+
"Esc close "
|
|
35583
|
+
];
|
|
35584
|
+
}
|
|
35053
35585
|
if (state.overlay === "vault") {
|
|
35054
35586
|
if (state.vaultMode === "list")
|
|
35055
35587
|
return ["Enter run \xB7 Esc close ", "Esc close "];
|
|
@@ -35122,7 +35654,7 @@ function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = termin
|
|
|
35122
35654
|
const height = Math.max(14, rows);
|
|
35123
35655
|
const footer = footerLines(state, width);
|
|
35124
35656
|
const decision = decisionPane(state, width);
|
|
35125
|
-
const hideComposer = state.overlay === "background-session" || state.overlay === "topics" || state.overlay === "subagents" || state.overlay === "vault" && state.vaultMode === "confirm-delete";
|
|
35657
|
+
const hideComposer = state.overlay === "background-session" || state.overlay === "topics" || state.overlay === "subagents" || state.overlay === "decisions" || state.overlay === "vault" && state.vaultMode === "confirm-delete";
|
|
35126
35658
|
const composer = hideComposer ? { lines: [], cursor: null } : composerPane(state, width);
|
|
35127
35659
|
const bodyHeight = Math.max(3, height - footer.length - decision.length - composer.lines.length);
|
|
35128
35660
|
const layout = terminalBodyLayout(state, width);
|
|
@@ -35658,6 +36190,12 @@ class EmbeddedNegotiumClient {
|
|
|
35658
36190
|
listTopicUsage(topicId) {
|
|
35659
36191
|
return getTopicStats(this.#userId, topicId);
|
|
35660
36192
|
}
|
|
36193
|
+
listDecisions(topicId) {
|
|
36194
|
+
return readDecisions(this.#userId, topicId);
|
|
36195
|
+
}
|
|
36196
|
+
saveDecisionGraph(topicId, svg) {
|
|
36197
|
+
writeDecisionGraphSvg(this.#userId, topicId, svg);
|
|
36198
|
+
}
|
|
35661
36199
|
listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
35662
36200
|
const result = listApiMessages(topicId, { cursor, limit });
|
|
35663
36201
|
return { messages: result.page, cursor: result.cursor, hasMore: result.hasMore };
|
|
@@ -35847,6 +36385,16 @@ class RemoteNegotiumClient {
|
|
|
35847
36385
|
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(this.#userId)}`);
|
|
35848
36386
|
return result.usage;
|
|
35849
36387
|
}
|
|
36388
|
+
async listDecisions(topicId) {
|
|
36389
|
+
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/decisions?user=${encodeURIComponent(this.#userId)}`);
|
|
36390
|
+
return result.decisions ?? [];
|
|
36391
|
+
}
|
|
36392
|
+
async saveDecisionGraph(topicId, svg) {
|
|
36393
|
+
await this.#request(`/topics/${encodeURIComponent(topicId)}/decision-graph`, {
|
|
36394
|
+
method: "POST",
|
|
36395
|
+
body: JSON.stringify({ userId: this.#userId, svg })
|
|
36396
|
+
});
|
|
36397
|
+
}
|
|
35850
36398
|
async listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
35851
36399
|
const query2 = new URLSearchParams({ user: this.#userId, limit: String(limit) });
|
|
35852
36400
|
if (cursor)
|
|
@@ -37182,6 +37730,10 @@ class TerminalApp {
|
|
|
37182
37730
|
#subagentGraphSpacingTimer = null;
|
|
37183
37731
|
#subagentGraphCache = null;
|
|
37184
37732
|
#pendingSubagentGraphSpacing = null;
|
|
37733
|
+
#decisionGraphGeneration = 0;
|
|
37734
|
+
#decisionGraphAbortController = null;
|
|
37735
|
+
#decisionGraphSpacingTimer = null;
|
|
37736
|
+
#pendingDecisionGraphSpacing = null;
|
|
37185
37737
|
#running = false;
|
|
37186
37738
|
#stopRequested = false;
|
|
37187
37739
|
#finishRun = null;
|
|
@@ -37539,6 +38091,9 @@ class TerminalApp {
|
|
|
37539
38091
|
const topicId = this.#state.activeTopicId;
|
|
37540
38092
|
if (!topicId)
|
|
37541
38093
|
return;
|
|
38094
|
+
this.#decisionGraphGeneration += 1;
|
|
38095
|
+
this.#decisionGraphAbortController?.abort();
|
|
38096
|
+
this.#decisionGraphAbortController = null;
|
|
37542
38097
|
if (this.#state.overlay === "subagents") {
|
|
37543
38098
|
this.#subagentGraphGeneration += 1;
|
|
37544
38099
|
this.#subagentGraphAbortController?.abort();
|
|
@@ -37591,6 +38146,143 @@ class TerminalApp {
|
|
|
37591
38146
|
}
|
|
37592
38147
|
this.#queueRender();
|
|
37593
38148
|
}
|
|
38149
|
+
async#toggleDecisionGraph() {
|
|
38150
|
+
const topic = activeTopic(this.#state);
|
|
38151
|
+
if (!topic || !this.#client.listDecisions)
|
|
38152
|
+
return;
|
|
38153
|
+
if (this.#state.overlay === "decisions") {
|
|
38154
|
+
this.#decisionGraphGeneration += 1;
|
|
38155
|
+
this.#decisionGraphAbortController?.abort();
|
|
38156
|
+
this.#decisionGraphAbortController = null;
|
|
38157
|
+
this.#subagentGraphDragPoint = null;
|
|
38158
|
+
this.#state = { ...this.#state, overlay: null, decisionGraphLoading: false };
|
|
38159
|
+
this.#queueRender();
|
|
38160
|
+
return;
|
|
38161
|
+
}
|
|
38162
|
+
this.#subagentGraphGeneration += 1;
|
|
38163
|
+
this.#subagentGraphAbortController?.abort();
|
|
38164
|
+
this.#subagentGraphAbortController = null;
|
|
38165
|
+
const generation = ++this.#decisionGraphGeneration;
|
|
38166
|
+
this.#decisionGraphAbortController?.abort();
|
|
38167
|
+
const controller = new AbortController;
|
|
38168
|
+
this.#decisionGraphAbortController = controller;
|
|
38169
|
+
this.#subagentGraphDragPoint = null;
|
|
38170
|
+
this.#state = {
|
|
38171
|
+
...this.#state,
|
|
38172
|
+
overlay: "decisions",
|
|
38173
|
+
decisionGraph: undefined,
|
|
38174
|
+
decisionGraphLoading: true,
|
|
38175
|
+
decisionGraphOffset: { x: 0, y: 0 },
|
|
38176
|
+
notice: undefined,
|
|
38177
|
+
noticeLevel: undefined
|
|
38178
|
+
};
|
|
38179
|
+
this.#queueRender();
|
|
38180
|
+
try {
|
|
38181
|
+
const decisions2 = await this.#client.listDecisions(topic.id);
|
|
38182
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38183
|
+
return;
|
|
38184
|
+
}
|
|
38185
|
+
if (decisions2.length === 0) {
|
|
38186
|
+
this.#decisionGraphAbortController = null;
|
|
38187
|
+
this.#state = {
|
|
38188
|
+
...this.#state,
|
|
38189
|
+
overlay: null,
|
|
38190
|
+
decisionGraphLoading: false,
|
|
38191
|
+
notice: "No decisions recorded in this topic",
|
|
38192
|
+
noticeLevel: "info"
|
|
38193
|
+
};
|
|
38194
|
+
this.#queueRender();
|
|
38195
|
+
return;
|
|
38196
|
+
}
|
|
38197
|
+
const rendered = await renderDecisionGraph(buildDecisionGraph(decisions2, topic.title), this.#state.decisionGraphSpacing, controller.signal);
|
|
38198
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38199
|
+
return;
|
|
38200
|
+
}
|
|
38201
|
+
let saveWarning;
|
|
38202
|
+
try {
|
|
38203
|
+
await this.#client.saveDecisionGraph?.(topic.id, rendered.svg);
|
|
38204
|
+
} catch (error2) {
|
|
38205
|
+
saveWarning = `Decision graph shown but SVG save failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
38206
|
+
}
|
|
38207
|
+
this.#decisionGraphAbortController = null;
|
|
38208
|
+
this.#state = {
|
|
38209
|
+
...this.#state,
|
|
38210
|
+
decisionGraph: rendered.canvas,
|
|
38211
|
+
decisionGraphLoading: false,
|
|
38212
|
+
...saveWarning ? { notice: saveWarning, noticeLevel: "warn" } : {}
|
|
38213
|
+
};
|
|
38214
|
+
} catch (error2) {
|
|
38215
|
+
if (generation !== this.#decisionGraphGeneration)
|
|
38216
|
+
return;
|
|
38217
|
+
this.#decisionGraphAbortController = null;
|
|
38218
|
+
this.#state = {
|
|
38219
|
+
...this.#state,
|
|
38220
|
+
overlay: null,
|
|
38221
|
+
decisionGraphLoading: false,
|
|
38222
|
+
notice: `Decision graph failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
38223
|
+
noticeLevel: "error"
|
|
38224
|
+
};
|
|
38225
|
+
}
|
|
38226
|
+
this.#queueRender();
|
|
38227
|
+
}
|
|
38228
|
+
#changeDecisionGraphSpacing(delta) {
|
|
38229
|
+
const previous = this.#pendingDecisionGraphSpacing ?? this.#state.decisionGraphSpacing;
|
|
38230
|
+
const spacing = adjustDecisionGraphSpacing(previous, delta);
|
|
38231
|
+
if (spacing === previous)
|
|
38232
|
+
return;
|
|
38233
|
+
this.#pendingDecisionGraphSpacing = spacing;
|
|
38234
|
+
if (this.#decisionGraphSpacingTimer)
|
|
38235
|
+
clearTimeout(this.#decisionGraphSpacingTimer);
|
|
38236
|
+
this.#decisionGraphSpacingTimer = setTimeout(() => {
|
|
38237
|
+
this.#decisionGraphSpacingTimer = null;
|
|
38238
|
+
const target = this.#pendingDecisionGraphSpacing;
|
|
38239
|
+
this.#pendingDecisionGraphSpacing = null;
|
|
38240
|
+
if (target !== null)
|
|
38241
|
+
this.#applyDecisionGraphSpacing(target);
|
|
38242
|
+
}, 120);
|
|
38243
|
+
}
|
|
38244
|
+
async#applyDecisionGraphSpacing(spacing) {
|
|
38245
|
+
const topic = activeTopic(this.#state);
|
|
38246
|
+
if (!topic || this.#state.overlay !== "decisions" || !this.#client.listDecisions)
|
|
38247
|
+
return;
|
|
38248
|
+
const generation = ++this.#decisionGraphGeneration;
|
|
38249
|
+
this.#decisionGraphAbortController?.abort();
|
|
38250
|
+
const controller = new AbortController;
|
|
38251
|
+
this.#decisionGraphAbortController = controller;
|
|
38252
|
+
this.#state = { ...this.#state, decisionGraphSpacing: spacing, decisionGraphLoading: true };
|
|
38253
|
+
this.#queueRender();
|
|
38254
|
+
try {
|
|
38255
|
+
const decisions2 = await this.#client.listDecisions(topic.id);
|
|
38256
|
+
const rendered = await renderDecisionGraph(buildDecisionGraph(decisions2, topic.title), spacing, controller.signal);
|
|
38257
|
+
if (generation !== this.#decisionGraphGeneration || this.#state.overlay !== "decisions") {
|
|
38258
|
+
return;
|
|
38259
|
+
}
|
|
38260
|
+
let saveWarning;
|
|
38261
|
+
try {
|
|
38262
|
+
await this.#client.saveDecisionGraph?.(topic.id, rendered.svg);
|
|
38263
|
+
} catch (error2) {
|
|
38264
|
+
saveWarning = `Decision graph shown but SVG save failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
38265
|
+
}
|
|
38266
|
+
this.#decisionGraphAbortController = null;
|
|
38267
|
+
this.#state = {
|
|
38268
|
+
...this.#state,
|
|
38269
|
+
decisionGraph: rendered.canvas,
|
|
38270
|
+
decisionGraphLoading: false,
|
|
38271
|
+
...saveWarning ? { notice: saveWarning, noticeLevel: "warn" } : {}
|
|
38272
|
+
};
|
|
38273
|
+
} catch (error2) {
|
|
38274
|
+
if (generation !== this.#decisionGraphGeneration)
|
|
38275
|
+
return;
|
|
38276
|
+
this.#decisionGraphAbortController = null;
|
|
38277
|
+
this.#state = {
|
|
38278
|
+
...this.#state,
|
|
38279
|
+
decisionGraphLoading: false,
|
|
38280
|
+
notice: `Decision graph failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
38281
|
+
noticeLevel: "error"
|
|
38282
|
+
};
|
|
38283
|
+
}
|
|
38284
|
+
this.#queueRender();
|
|
38285
|
+
}
|
|
37594
38286
|
#changeSubagentGraphSpacing(delta) {
|
|
37595
38287
|
const previousSpacing = this.#pendingSubagentGraphSpacing ?? this.#state.subagentGraphSpacing;
|
|
37596
38288
|
const spacing = adjustSubagentGraphSpacing(previousSpacing, delta);
|
|
@@ -37771,18 +38463,18 @@ class TerminalApp {
|
|
|
37771
38463
|
}
|
|
37772
38464
|
#handleKeySegment(segment) {
|
|
37773
38465
|
const mouse = consumeMouseInput(segment);
|
|
37774
|
-
if (mouse.horizontalScrollDelta !== 0 && this.#state.overlay === "subagents") {
|
|
38466
|
+
if (mouse.horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
|
|
37775
38467
|
this.#selection = null;
|
|
37776
|
-
this.#
|
|
38468
|
+
this.#panActiveGraph(-mouse.horizontalScrollDelta, 0);
|
|
37777
38469
|
}
|
|
37778
38470
|
if (mouse.scrollDelta !== 0) {
|
|
37779
38471
|
this.#selection = null;
|
|
37780
38472
|
this.#scroll(mouse.scrollDelta);
|
|
37781
38473
|
}
|
|
37782
38474
|
for (const event of mouse.events) {
|
|
37783
|
-
if (this.#state.overlay === "subagents")
|
|
37784
|
-
this.#
|
|
37785
|
-
else
|
|
38475
|
+
if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
|
|
38476
|
+
this.#handleGraphMouse(event);
|
|
38477
|
+
} else
|
|
37786
38478
|
this.#handleMouseSelection(event);
|
|
37787
38479
|
}
|
|
37788
38480
|
const chunk = normalizeKeySequences(mouse.input);
|
|
@@ -37816,6 +38508,12 @@ class TerminalApp {
|
|
|
37816
38508
|
this.#toggleSubagentGraph();
|
|
37817
38509
|
return;
|
|
37818
38510
|
}
|
|
38511
|
+
if (chunk === "\x04" && this.#state.overlay !== "topics") {
|
|
38512
|
+
if (editingVaultSecret)
|
|
38513
|
+
return;
|
|
38514
|
+
this.#toggleDecisionGraph();
|
|
38515
|
+
return;
|
|
38516
|
+
}
|
|
37819
38517
|
if (chunk === "\x14") {
|
|
37820
38518
|
if (editingVaultSecret)
|
|
37821
38519
|
return;
|
|
@@ -37868,6 +38566,38 @@ class TerminalApp {
|
|
|
37868
38566
|
this.#queueRender();
|
|
37869
38567
|
return;
|
|
37870
38568
|
}
|
|
38569
|
+
if (this.#state.overlay === "decisions") {
|
|
38570
|
+
if (chunk === "\x1B") {
|
|
38571
|
+
this.#decisionGraphGeneration += 1;
|
|
38572
|
+
this.#decisionGraphAbortController?.abort();
|
|
38573
|
+
this.#decisionGraphAbortController = null;
|
|
38574
|
+
this.#subagentGraphDragPoint = null;
|
|
38575
|
+
this.#state = { ...this.#state, overlay: null, decisionGraphLoading: false };
|
|
38576
|
+
} else if (chunk === "\x1B[A" || chunk === "k") {
|
|
38577
|
+
this.#panDecisionGraph(0, -2);
|
|
38578
|
+
return;
|
|
38579
|
+
} else if (chunk === "\x1B[B" || chunk === "j") {
|
|
38580
|
+
this.#panDecisionGraph(0, 2);
|
|
38581
|
+
return;
|
|
38582
|
+
} else if (chunk === "\x1B[D" || chunk === "h") {
|
|
38583
|
+
this.#panDecisionGraph(-4, 0);
|
|
38584
|
+
return;
|
|
38585
|
+
} else if (chunk === "\x1B[C" || chunk === "l") {
|
|
38586
|
+
this.#panDecisionGraph(4, 0);
|
|
38587
|
+
return;
|
|
38588
|
+
} else if (chunk === "\x1B[5~") {
|
|
38589
|
+
this.#panDecisionGraph(0, -8);
|
|
38590
|
+
return;
|
|
38591
|
+
} else if (chunk === "\x1B[6~") {
|
|
38592
|
+
this.#panDecisionGraph(0, 8);
|
|
38593
|
+
return;
|
|
38594
|
+
} else if (chunk === "[" || chunk === "]") {
|
|
38595
|
+
this.#changeDecisionGraphSpacing(chunk === "[" ? -1 : 1);
|
|
38596
|
+
return;
|
|
38597
|
+
}
|
|
38598
|
+
this.#queueRender();
|
|
38599
|
+
return;
|
|
38600
|
+
}
|
|
37871
38601
|
if (chunk === "\x01" || chunk === "\x1B[H" || chunk === "\x1B[1~") {
|
|
37872
38602
|
this.#input.moveHome();
|
|
37873
38603
|
this.#syncInput();
|
|
@@ -38808,7 +39538,7 @@ class TerminalApp {
|
|
|
38808
39538
|
this.#copySelection(text2);
|
|
38809
39539
|
this.#queueRender();
|
|
38810
39540
|
}
|
|
38811
|
-
#
|
|
39541
|
+
#handleGraphMouse(event) {
|
|
38812
39542
|
if ((event.button & 3) !== 0 && event.kind !== "release")
|
|
38813
39543
|
return;
|
|
38814
39544
|
const point = { x: event.x, y: event.y };
|
|
@@ -38825,7 +39555,7 @@ class TerminalApp {
|
|
|
38825
39555
|
this.#subagentGraphDragPoint = point;
|
|
38826
39556
|
if (!previous)
|
|
38827
39557
|
return;
|
|
38828
|
-
this.#
|
|
39558
|
+
this.#panActiveGraph(previous.x - point.x, previous.y - point.y);
|
|
38829
39559
|
}
|
|
38830
39560
|
async#copySelection(text2) {
|
|
38831
39561
|
try {
|
|
@@ -38886,8 +39616,8 @@ class TerminalApp {
|
|
|
38886
39616
|
this.#loadOlderMessages(topic.id, currentMax + 8);
|
|
38887
39617
|
}
|
|
38888
39618
|
#scroll(delta) {
|
|
38889
|
-
if (this.#state.overlay === "subagents") {
|
|
38890
|
-
this.#
|
|
39619
|
+
if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
|
|
39620
|
+
this.#panActiveGraph(0, -delta);
|
|
38891
39621
|
return;
|
|
38892
39622
|
}
|
|
38893
39623
|
if (this.#state.overlay === "background-session") {
|
|
@@ -38921,6 +39651,26 @@ class TerminalApp {
|
|
|
38921
39651
|
};
|
|
38922
39652
|
this.#queueRender();
|
|
38923
39653
|
}
|
|
39654
|
+
#panDecisionGraph(deltaX, deltaY) {
|
|
39655
|
+
const canvas = this.#state.decisionGraph;
|
|
39656
|
+
const viewportWidth = Math.max(1, (process.stdout.columns ?? 100) - 4);
|
|
39657
|
+
const viewportHeight = Math.max(1, (process.stdout.rows ?? 30) - 8);
|
|
39658
|
+
const current3 = this.#state.decisionGraphOffset;
|
|
39659
|
+
this.#state = {
|
|
39660
|
+
...this.#state,
|
|
39661
|
+
decisionGraphOffset: {
|
|
39662
|
+
x: Math.min(Math.max(0, (canvas?.width ?? 0) - viewportWidth), Math.max(0, current3.x + deltaX)),
|
|
39663
|
+
y: Math.min(Math.max(0, (canvas?.height ?? 0) - viewportHeight), Math.max(0, current3.y + deltaY))
|
|
39664
|
+
}
|
|
39665
|
+
};
|
|
39666
|
+
this.#queueRender();
|
|
39667
|
+
}
|
|
39668
|
+
#panActiveGraph(deltaX, deltaY) {
|
|
39669
|
+
if (this.#state.overlay === "decisions")
|
|
39670
|
+
this.#panDecisionGraph(deltaX, deltaY);
|
|
39671
|
+
else
|
|
39672
|
+
this.#panSubagentGraph(deltaX, deltaY);
|
|
39673
|
+
}
|
|
38924
39674
|
async#abort() {
|
|
38925
39675
|
const topic = activeTopic(this.#state);
|
|
38926
39676
|
let aborted = false;
|
|
@@ -39004,6 +39754,13 @@ class TerminalApp {
|
|
|
39004
39754
|
this.#subagentGraphGeneration += 1;
|
|
39005
39755
|
this.#subagentGraphAbortController?.abort();
|
|
39006
39756
|
this.#subagentGraphAbortController = null;
|
|
39757
|
+
this.#decisionGraphGeneration += 1;
|
|
39758
|
+
this.#decisionGraphAbortController?.abort();
|
|
39759
|
+
this.#decisionGraphAbortController = null;
|
|
39760
|
+
if (this.#subagentGraphSpacingTimer)
|
|
39761
|
+
clearTimeout(this.#subagentGraphSpacingTimer);
|
|
39762
|
+
if (this.#decisionGraphSpacingTimer)
|
|
39763
|
+
clearTimeout(this.#decisionGraphSpacingTimer);
|
|
39007
39764
|
if (this.#renderTimer)
|
|
39008
39765
|
clearTimeout(this.#renderTimer);
|
|
39009
39766
|
if (this.#inputCarryTimer)
|
|
@@ -39043,6 +39800,7 @@ var init_app = __esm(async () => {
|
|
|
39043
39800
|
init_color_depth();
|
|
39044
39801
|
await init_command_router();
|
|
39045
39802
|
init_commands();
|
|
39803
|
+
init_decision_graph();
|
|
39046
39804
|
init_path_suggest();
|
|
39047
39805
|
await init_render();
|
|
39048
39806
|
init_screen_renderer();
|
|
@@ -39645,8 +40403,8 @@ var init_commands2 = __esm(async () => {
|
|
|
39645
40403
|
|
|
39646
40404
|
// ../../adapters/telegram/src/mapping-store.ts
|
|
39647
40405
|
import { Database as Database2 } from "bun:sqlite";
|
|
39648
|
-
import { mkdirSync as
|
|
39649
|
-
import { dirname as
|
|
40406
|
+
import { mkdirSync as mkdirSync32 } from "fs";
|
|
40407
|
+
import { dirname as dirname21, join as join42 } from "path";
|
|
39650
40408
|
function outboxRowToEntry(row) {
|
|
39651
40409
|
return {
|
|
39652
40410
|
id: row.id,
|
|
@@ -39712,9 +40470,9 @@ function migrateOutboxSchema(db4) {
|
|
|
39712
40470
|
db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
|
|
39713
40471
|
}
|
|
39714
40472
|
function openMappingStore(path) {
|
|
39715
|
-
const dbPath = path ??
|
|
40473
|
+
const dbPath = path ?? join42(DATA_DIR, "adapter-telegram.db");
|
|
39716
40474
|
if (dbPath !== ":memory:")
|
|
39717
|
-
|
|
40475
|
+
mkdirSync32(dirname21(dbPath), { recursive: true });
|
|
39718
40476
|
const db4 = new Database2(dbPath);
|
|
39719
40477
|
migrateLegacySchema(db4);
|
|
39720
40478
|
createTables(db4);
|
|
@@ -40072,7 +40830,7 @@ function renderOutbound(text2, maxLen = 4096) {
|
|
|
40072
40830
|
}
|
|
40073
40831
|
|
|
40074
40832
|
// ../../adapters/telegram/src/adapter.ts
|
|
40075
|
-
import { existsSync as
|
|
40833
|
+
import { existsSync as existsSync36 } from "fs";
|
|
40076
40834
|
import { basename as basename12, extname as extname5 } from "path";
|
|
40077
40835
|
function startTelegramAdapter(opts) {
|
|
40078
40836
|
const { client, forumChatId } = opts;
|
|
@@ -40570,7 +41328,7 @@ function startTelegramAdapter(opts) {
|
|
|
40570
41328
|
logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
|
|
40571
41329
|
return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
|
|
40572
41330
|
}
|
|
40573
|
-
if (!
|
|
41331
|
+
if (!existsSync36(path)) {
|
|
40574
41332
|
const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
|
|
40575
41333
|
const messageId = sentMessageId(sent);
|
|
40576
41334
|
return {
|
|
@@ -41590,11 +42348,11 @@ var exports_join_status = {};
|
|
|
41590
42348
|
__export(exports_join_status, {
|
|
41591
42349
|
hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
|
|
41592
42350
|
});
|
|
41593
|
-
import { existsSync as
|
|
42351
|
+
import { existsSync as existsSync37 } from "fs";
|
|
41594
42352
|
import { resolve as resolve23 } from "path";
|
|
41595
42353
|
function hasConfiguredOtiumJoin() {
|
|
41596
42354
|
const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
|
|
41597
|
-
return envJoin ||
|
|
42355
|
+
return envJoin || existsSync37(resolve23(DATA_DIR, "otium-join.json"));
|
|
41598
42356
|
}
|
|
41599
42357
|
var init_join_status = __esm(() => {
|
|
41600
42358
|
init_config_public();
|
|
@@ -41612,9 +42370,9 @@ function firstCell() {
|
|
|
41612
42370
|
return cell;
|
|
41613
42371
|
return null;
|
|
41614
42372
|
}
|
|
41615
|
-
function attachOtiumCentralCell(
|
|
41616
|
-
cells.set(
|
|
41617
|
-
join:
|
|
42373
|
+
function attachOtiumCentralCell(join43) {
|
|
42374
|
+
cells.set(join43.cellId, {
|
|
42375
|
+
join: join43,
|
|
41618
42376
|
nodesCache: null,
|
|
41619
42377
|
verifyCache: new Map,
|
|
41620
42378
|
tokenCache: new Map
|
|
@@ -41626,10 +42384,10 @@ function detachOtiumCentralCell(cellId) {
|
|
|
41626
42384
|
function attachedOtiumCells() {
|
|
41627
42385
|
return [...cells.values()].map((cell) => cell.join);
|
|
41628
42386
|
}
|
|
41629
|
-
function configureOtiumCentral(
|
|
42387
|
+
function configureOtiumCentral(join43) {
|
|
41630
42388
|
cells.clear();
|
|
41631
|
-
if (
|
|
41632
|
-
attachOtiumCentralCell(
|
|
42389
|
+
if (join43)
|
|
42390
|
+
attachOtiumCentralCell(join43);
|
|
41633
42391
|
}
|
|
41634
42392
|
function isOtiumCentralConfigured() {
|
|
41635
42393
|
return cells.size > 0;
|
|
@@ -41970,6 +42728,13 @@ var init_canonical_mcp_bridge = __esm(async () => {
|
|
|
41970
42728
|
CAPABILITY_TTL_MS = 60 * 60 * 1000;
|
|
41971
42729
|
TOOL_ALLOWLIST = {
|
|
41972
42730
|
task: new Set(["task_create", "task_update", "task_list", "task_get", "task_delete"]),
|
|
42731
|
+
decision: new Set([
|
|
42732
|
+
"decision_create",
|
|
42733
|
+
"decision_update",
|
|
42734
|
+
"decision_list",
|
|
42735
|
+
"decision_get",
|
|
42736
|
+
"decision_delete"
|
|
42737
|
+
]),
|
|
41973
42738
|
wiki: new Set([
|
|
41974
42739
|
"wiki_query",
|
|
41975
42740
|
"wiki_read",
|
|
@@ -42036,20 +42801,20 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
|
|
|
42036
42801
|
import {
|
|
42037
42802
|
chmodSync as chmodSync7,
|
|
42038
42803
|
closeSync as closeSync5,
|
|
42039
|
-
existsSync as
|
|
42804
|
+
existsSync as existsSync38,
|
|
42040
42805
|
fsyncSync as fsyncSync2,
|
|
42041
42806
|
linkSync,
|
|
42042
42807
|
lstatSync,
|
|
42043
|
-
mkdirSync as
|
|
42808
|
+
mkdirSync as mkdirSync33,
|
|
42044
42809
|
openSync as openSync5,
|
|
42045
|
-
readFileSync as
|
|
42046
|
-
renameSync as
|
|
42810
|
+
readFileSync as readFileSync29,
|
|
42811
|
+
renameSync as renameSync17,
|
|
42047
42812
|
rmSync as rmSync10,
|
|
42048
42813
|
statSync as statSync20,
|
|
42049
42814
|
unlinkSync as unlinkSync23,
|
|
42050
|
-
writeFileSync as
|
|
42815
|
+
writeFileSync as writeFileSync25
|
|
42051
42816
|
} from "fs";
|
|
42052
|
-
import { dirname as
|
|
42817
|
+
import { dirname as dirname22, resolve as resolve24 } from "path";
|
|
42053
42818
|
function joinFilePath() {
|
|
42054
42819
|
return resolve24(DATA_DIR, "otium-join.json");
|
|
42055
42820
|
}
|
|
@@ -42122,13 +42887,13 @@ function withJoinCredentialLock(operation) {
|
|
|
42122
42887
|
const lockPath = joinLockPath();
|
|
42123
42888
|
const ownerPath = resolve24(lockPath, "owner.json");
|
|
42124
42889
|
const owner = { pid: process.pid, token: randomUUID28() };
|
|
42125
|
-
|
|
42890
|
+
mkdirSync33(dirname22(lockPath), { recursive: true });
|
|
42126
42891
|
for (let attempt = 0;; attempt += 1) {
|
|
42127
42892
|
let created = false;
|
|
42128
42893
|
try {
|
|
42129
|
-
|
|
42894
|
+
mkdirSync33(lockPath, { mode: 448 });
|
|
42130
42895
|
created = true;
|
|
42131
|
-
|
|
42896
|
+
writeFileSync25(ownerPath, `${JSON.stringify(owner)}
|
|
42132
42897
|
`, { mode: 384 });
|
|
42133
42898
|
const ownerFd = openSync5(ownerPath, "r");
|
|
42134
42899
|
try {
|
|
@@ -42146,7 +42911,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42146
42911
|
throw error2;
|
|
42147
42912
|
let current3 = null;
|
|
42148
42913
|
try {
|
|
42149
|
-
current3 = JSON.parse(
|
|
42914
|
+
current3 = JSON.parse(readFileSync29(ownerPath, "utf8"));
|
|
42150
42915
|
} catch {}
|
|
42151
42916
|
let ageMs;
|
|
42152
42917
|
try {
|
|
@@ -42164,7 +42929,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42164
42929
|
}
|
|
42165
42930
|
const stalePath = `${lockPath}.stale.${process.pid}.${randomUUID28()}`;
|
|
42166
42931
|
try {
|
|
42167
|
-
|
|
42932
|
+
renameSync17(lockPath, stalePath);
|
|
42168
42933
|
rmSync10(stalePath, { recursive: true, force: true });
|
|
42169
42934
|
} catch (staleError) {
|
|
42170
42935
|
if (staleError.code !== "ENOENT")
|
|
@@ -42176,7 +42941,7 @@ function withJoinCredentialLock(operation) {
|
|
|
42176
42941
|
return operation();
|
|
42177
42942
|
} finally {
|
|
42178
42943
|
try {
|
|
42179
|
-
const current3 = JSON.parse(
|
|
42944
|
+
const current3 = JSON.parse(readFileSync29(ownerPath, "utf8"));
|
|
42180
42945
|
if (current3.pid === owner.pid && current3.token === owner.token) {
|
|
42181
42946
|
rmSync10(lockPath, { recursive: true, force: true });
|
|
42182
42947
|
}
|
|
@@ -42186,22 +42951,22 @@ function withJoinCredentialLock(operation) {
|
|
|
42186
42951
|
function joinsEqual(left, right) {
|
|
42187
42952
|
return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
|
|
42188
42953
|
}
|
|
42189
|
-
function normalizedJoin(
|
|
42954
|
+
function normalizedJoin(join43) {
|
|
42190
42955
|
return normalizeJoin({
|
|
42191
|
-
v:
|
|
42192
|
-
central:
|
|
42193
|
-
relay:
|
|
42194
|
-
cellId:
|
|
42195
|
-
secret:
|
|
42956
|
+
v: join43.v,
|
|
42957
|
+
central: join43.central,
|
|
42958
|
+
relay: join43.relay,
|
|
42959
|
+
cellId: join43.cellId,
|
|
42960
|
+
secret: join43.secret
|
|
42196
42961
|
});
|
|
42197
42962
|
}
|
|
42198
|
-
function joinCredentialDigest(
|
|
42199
|
-
return createHash11("sha256").update(JSON.stringify(normalizedJoin(
|
|
42963
|
+
function joinCredentialDigest(join43) {
|
|
42964
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join43))).digest("base64url");
|
|
42200
42965
|
}
|
|
42201
42966
|
function readPersistedJoins(path = joinFilePath()) {
|
|
42202
|
-
if (!
|
|
42967
|
+
if (!existsSync38(path))
|
|
42203
42968
|
return [];
|
|
42204
|
-
const parsed = JSON.parse(
|
|
42969
|
+
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
42205
42970
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
42206
42971
|
throw new Error("persisted join credentials are not a JSON object");
|
|
42207
42972
|
}
|
|
@@ -42215,9 +42980,9 @@ function readPersistedJoins(path = joinFilePath()) {
|
|
|
42215
42980
|
return normalizeJoin(entry);
|
|
42216
42981
|
});
|
|
42217
42982
|
}
|
|
42218
|
-
function isJoinPersisted(
|
|
42983
|
+
function isJoinPersisted(join43) {
|
|
42219
42984
|
try {
|
|
42220
|
-
const normalized = normalizedJoin(
|
|
42985
|
+
const normalized = normalizedJoin(join43);
|
|
42221
42986
|
return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
|
|
42222
42987
|
} catch {
|
|
42223
42988
|
return false;
|
|
@@ -42233,19 +42998,19 @@ function fsyncPath(target) {
|
|
|
42233
42998
|
}
|
|
42234
42999
|
function writeJoins(joins, allowOverwrite) {
|
|
42235
43000
|
const path = joinFilePath();
|
|
42236
|
-
const directory =
|
|
43001
|
+
const directory = dirname22(path);
|
|
42237
43002
|
const temporaryPath = resolve24(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
|
|
42238
43003
|
const payload = { v: JOIN_FILE_VERSION, joins };
|
|
42239
43004
|
let fd;
|
|
42240
43005
|
try {
|
|
42241
43006
|
fd = openSync5(temporaryPath, "wx", 384);
|
|
42242
|
-
|
|
43007
|
+
writeFileSync25(fd, `${JSON.stringify(payload, null, 2)}
|
|
42243
43008
|
`, "utf8");
|
|
42244
43009
|
fsyncSync2(fd);
|
|
42245
43010
|
closeSync5(fd);
|
|
42246
43011
|
fd = undefined;
|
|
42247
43012
|
if (allowOverwrite) {
|
|
42248
|
-
|
|
43013
|
+
renameSync17(temporaryPath, path);
|
|
42249
43014
|
} else {
|
|
42250
43015
|
linkSync(temporaryPath, path);
|
|
42251
43016
|
unlinkSync23(temporaryPath);
|
|
@@ -42255,18 +43020,18 @@ function writeJoins(joins, allowOverwrite) {
|
|
|
42255
43020
|
} catch (error2) {
|
|
42256
43021
|
if (fd !== undefined)
|
|
42257
43022
|
closeSync5(fd);
|
|
42258
|
-
if (
|
|
43023
|
+
if (existsSync38(temporaryPath))
|
|
42259
43024
|
unlinkSync23(temporaryPath);
|
|
42260
43025
|
throw error2;
|
|
42261
43026
|
}
|
|
42262
43027
|
return path;
|
|
42263
43028
|
}
|
|
42264
|
-
function saveJoinWhileLocked(
|
|
43029
|
+
function saveJoinWhileLocked(join43, options = {}) {
|
|
42265
43030
|
const path = joinFilePath();
|
|
42266
|
-
const directory =
|
|
42267
|
-
const normalized = normalizedJoin(
|
|
42268
|
-
|
|
42269
|
-
if (!
|
|
43031
|
+
const directory = dirname22(path);
|
|
43032
|
+
const normalized = normalizedJoin(join43);
|
|
43033
|
+
mkdirSync33(directory, { recursive: true });
|
|
43034
|
+
if (!existsSync38(path))
|
|
42270
43035
|
return writeJoins([normalized], false);
|
|
42271
43036
|
if (lstatSync(path).isSymbolicLink()) {
|
|
42272
43037
|
throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
|
|
@@ -42294,13 +43059,13 @@ function saveJoinWhileLocked(join42, options = {}) {
|
|
|
42294
43059
|
const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
|
|
42295
43060
|
return writeJoins(next, true);
|
|
42296
43061
|
}
|
|
42297
|
-
function saveJoin(
|
|
42298
|
-
return withJoinCredentialLock(() => saveJoinWhileLocked(
|
|
43062
|
+
function saveJoin(join43, options = {}) {
|
|
43063
|
+
return withJoinCredentialLock(() => saveJoinWhileLocked(join43, options));
|
|
42299
43064
|
}
|
|
42300
43065
|
function removeJoin(cellId) {
|
|
42301
43066
|
return withJoinCredentialLock(() => {
|
|
42302
43067
|
const path = joinFilePath();
|
|
42303
|
-
if (!
|
|
43068
|
+
if (!existsSync38(path))
|
|
42304
43069
|
return false;
|
|
42305
43070
|
if (lstatSync(path).isSymbolicLink()) {
|
|
42306
43071
|
throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
|
|
@@ -42308,7 +43073,7 @@ function removeJoin(cellId) {
|
|
|
42308
43073
|
if (cellId) {
|
|
42309
43074
|
let remaining;
|
|
42310
43075
|
try {
|
|
42311
|
-
remaining = readPersistedJoins(path).filter((
|
|
43076
|
+
remaining = readPersistedJoins(path).filter((join43) => join43.cellId !== cellId);
|
|
42312
43077
|
} catch {
|
|
42313
43078
|
return false;
|
|
42314
43079
|
}
|
|
@@ -42320,7 +43085,7 @@ function removeJoin(cellId) {
|
|
|
42320
43085
|
}
|
|
42321
43086
|
}
|
|
42322
43087
|
unlinkSync23(path);
|
|
42323
|
-
fsyncPath(
|
|
43088
|
+
fsyncPath(dirname22(path));
|
|
42324
43089
|
return true;
|
|
42325
43090
|
});
|
|
42326
43091
|
}
|
|
@@ -42341,7 +43106,7 @@ function loadJoins() {
|
|
|
42341
43106
|
logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
|
|
42342
43107
|
}
|
|
42343
43108
|
const path = joinFilePath();
|
|
42344
|
-
if (!
|
|
43109
|
+
if (!existsSync38(path))
|
|
42345
43110
|
return [];
|
|
42346
43111
|
try {
|
|
42347
43112
|
return readPersistedJoins(path);
|
|
@@ -42360,8 +43125,8 @@ var init_join = __esm(async () => {
|
|
|
42360
43125
|
|
|
42361
43126
|
// ../../adapters/otium/src/peer-files.ts
|
|
42362
43127
|
import { randomUUID as randomUUID29 } from "crypto";
|
|
42363
|
-
import { copyFileSync as copyFileSync5, mkdirSync as
|
|
42364
|
-
import { basename as basename13, extname as extname6, join as
|
|
43128
|
+
import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync34, rmSync as rmSync11, statSync as statSync21 } from "fs";
|
|
43129
|
+
import { basename as basename13, extname as extname6, join as join43 } from "path";
|
|
42365
43130
|
function fileType(mimeType) {
|
|
42366
43131
|
if (mimeType.startsWith("image/"))
|
|
42367
43132
|
return "image";
|
|
@@ -42426,8 +43191,8 @@ function recordFile(args) {
|
|
|
42426
43191
|
}
|
|
42427
43192
|
function insertLocalFile(args) {
|
|
42428
43193
|
const id = randomUUID29();
|
|
42429
|
-
|
|
42430
|
-
const path =
|
|
43194
|
+
mkdirSync34(PEER_FILES_DIR, { recursive: true });
|
|
43195
|
+
const path = join43(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
|
|
42431
43196
|
copyFileSync5(args.sourcePath, path);
|
|
42432
43197
|
return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
|
|
42433
43198
|
}
|
|
@@ -42473,7 +43238,7 @@ function installPeerFileHooks() {
|
|
|
42473
43238
|
var PEER_FILES_DIR;
|
|
42474
43239
|
var init_peer_files = __esm(async () => {
|
|
42475
43240
|
await init_src();
|
|
42476
|
-
PEER_FILES_DIR =
|
|
43241
|
+
PEER_FILES_DIR = join43(DATA_DIR, "otium-peer-files");
|
|
42477
43242
|
db.exec(`
|
|
42478
43243
|
CREATE TABLE IF NOT EXISTS otium_peer_files (
|
|
42479
43244
|
id TEXT PRIMARY KEY,
|
|
@@ -42865,8 +43630,8 @@ var init_store2 = __esm(async () => {
|
|
|
42865
43630
|
|
|
42866
43631
|
// ../../adapters/otium/src/workspace-scope.ts
|
|
42867
43632
|
import { createHash as createHash13 } from "crypto";
|
|
42868
|
-
import { mkdirSync as
|
|
42869
|
-
import { dirname as
|
|
43633
|
+
import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
|
|
43634
|
+
import { dirname as dirname23, resolve as resolve25 } from "path";
|
|
42870
43635
|
function surfaceScopeFor(central, workspaceId) {
|
|
42871
43636
|
const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
|
|
42872
43637
|
${workspaceId.trim()}`).digest("hex");
|
|
@@ -42877,7 +43642,7 @@ function scopeCachePath() {
|
|
|
42877
43642
|
}
|
|
42878
43643
|
function readCache() {
|
|
42879
43644
|
try {
|
|
42880
|
-
const parsed = JSON.parse(
|
|
43645
|
+
const parsed = JSON.parse(readFileSync30(scopeCachePath(), "utf-8"));
|
|
42881
43646
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
42882
43647
|
return {};
|
|
42883
43648
|
return parsed;
|
|
@@ -42885,43 +43650,43 @@ function readCache() {
|
|
|
42885
43650
|
return {};
|
|
42886
43651
|
}
|
|
42887
43652
|
}
|
|
42888
|
-
function cachedSurfaceScope(
|
|
42889
|
-
const record = readCache()[
|
|
42890
|
-
if (!record || record.central !==
|
|
43653
|
+
function cachedSurfaceScope(join44) {
|
|
43654
|
+
const record = readCache()[join44.cellId];
|
|
43655
|
+
if (!record || record.central !== join44.central || !record.workspaceId)
|
|
42891
43656
|
return null;
|
|
42892
43657
|
return surfaceScopeFor(record.central, record.workspaceId);
|
|
42893
43658
|
}
|
|
42894
|
-
function cacheSurfaceScope(
|
|
42895
|
-
const scope = surfaceScopeFor(
|
|
43659
|
+
function cacheSurfaceScope(join44, workspaceId) {
|
|
43660
|
+
const scope = surfaceScopeFor(join44.central, workspaceId);
|
|
42896
43661
|
const path = scopeCachePath();
|
|
42897
43662
|
try {
|
|
42898
|
-
|
|
43663
|
+
mkdirSync35(dirname23(path), { recursive: true });
|
|
42899
43664
|
const cache = readCache();
|
|
42900
|
-
cache[
|
|
42901
|
-
|
|
43665
|
+
cache[join44.cellId] = { central: join44.central, workspaceId, scope };
|
|
43666
|
+
writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
|
|
42902
43667
|
`, { mode: 384 });
|
|
42903
43668
|
} catch (err2) {
|
|
42904
43669
|
logger.warn({ err: err2, path }, "otium: could not cache the workspace scope");
|
|
42905
43670
|
}
|
|
42906
43671
|
return scope;
|
|
42907
43672
|
}
|
|
42908
|
-
async function resolveSurfaceScope(
|
|
42909
|
-
const cached = cachedSurfaceScope(
|
|
43673
|
+
async function resolveSurfaceScope(join44) {
|
|
43674
|
+
const cached = cachedSurfaceScope(join44);
|
|
42910
43675
|
if (cached)
|
|
42911
43676
|
return cached;
|
|
42912
43677
|
try {
|
|
42913
|
-
const workspaceId = await peerWorkspaceIdForCell(
|
|
43678
|
+
const workspaceId = await peerWorkspaceIdForCell(join44.cellId);
|
|
42914
43679
|
if (!workspaceId)
|
|
42915
43680
|
return null;
|
|
42916
|
-
return cacheSurfaceScope(
|
|
43681
|
+
return cacheSurfaceScope(join44, workspaceId);
|
|
42917
43682
|
} catch (err2) {
|
|
42918
43683
|
logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
|
|
42919
43684
|
return null;
|
|
42920
43685
|
}
|
|
42921
43686
|
}
|
|
42922
43687
|
function surfaceScopeForCell(cellId) {
|
|
42923
|
-
const
|
|
42924
|
-
return
|
|
43688
|
+
const join44 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
|
|
43689
|
+
return join44 ? cachedSurfaceScope(join44) : null;
|
|
42925
43690
|
}
|
|
42926
43691
|
function unscopedRoomsAddressable() {
|
|
42927
43692
|
return attachedOtiumCells().length < 2;
|
|
@@ -43934,17 +44699,17 @@ import {
|
|
|
43934
44699
|
import {
|
|
43935
44700
|
chmodSync as chmodSync8,
|
|
43936
44701
|
closeSync as closeSync6,
|
|
43937
|
-
existsSync as
|
|
44702
|
+
existsSync as existsSync39,
|
|
43938
44703
|
fsyncSync as fsyncSync3,
|
|
43939
44704
|
linkSync as linkSync2,
|
|
43940
|
-
mkdirSync as
|
|
44705
|
+
mkdirSync as mkdirSync36,
|
|
43941
44706
|
openSync as openSync6,
|
|
43942
|
-
readFileSync as
|
|
43943
|
-
renameSync as
|
|
44707
|
+
readFileSync as readFileSync31,
|
|
44708
|
+
renameSync as renameSync18,
|
|
43944
44709
|
unlinkSync as unlinkSync24,
|
|
43945
|
-
writeFileSync as
|
|
44710
|
+
writeFileSync as writeFileSync27
|
|
43946
44711
|
} from "fs";
|
|
43947
|
-
import { dirname as
|
|
44712
|
+
import { dirname as dirname24, resolve as resolve26 } from "path";
|
|
43948
44713
|
function parseEnrollmentInvite(code) {
|
|
43949
44714
|
let parsed;
|
|
43950
44715
|
try {
|
|
@@ -43969,10 +44734,10 @@ function pendingEnrollmentPath() {
|
|
|
43969
44734
|
}
|
|
43970
44735
|
function isEnrollmentPending(invite) {
|
|
43971
44736
|
const path = pendingEnrollmentPath();
|
|
43972
|
-
if (!
|
|
44737
|
+
if (!existsSync39(path))
|
|
43973
44738
|
return false;
|
|
43974
44739
|
try {
|
|
43975
|
-
const saved = JSON.parse(
|
|
44740
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
43976
44741
|
return saved.central === invite.central && saved.token === invite.token;
|
|
43977
44742
|
} catch {
|
|
43978
44743
|
return false;
|
|
@@ -43980,8 +44745,8 @@ function isEnrollmentPending(invite) {
|
|
|
43980
44745
|
}
|
|
43981
44746
|
function loadOrCreatePending(invite, nodeName) {
|
|
43982
44747
|
const path = pendingEnrollmentPath();
|
|
43983
|
-
if (
|
|
43984
|
-
const saved = JSON.parse(
|
|
44748
|
+
if (existsSync39(path)) {
|
|
44749
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
43985
44750
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
43986
44751
|
return saved;
|
|
43987
44752
|
throw new Error(`another Otium enrollment is pending at ${path}`);
|
|
@@ -43994,12 +44759,12 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
43994
44759
|
publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
|
|
43995
44760
|
...nodeName ? { nodeName } : {}
|
|
43996
44761
|
};
|
|
43997
|
-
|
|
43998
|
-
const temporaryPath = resolve26(
|
|
44762
|
+
mkdirSync36(dirname24(path), { recursive: true });
|
|
44763
|
+
const temporaryPath = resolve26(dirname24(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
43999
44764
|
let fd;
|
|
44000
44765
|
try {
|
|
44001
44766
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
44002
|
-
|
|
44767
|
+
writeFileSync27(fd, `${JSON.stringify(pending2, null, 2)}
|
|
44003
44768
|
`, "utf8");
|
|
44004
44769
|
fsyncSync3(fd);
|
|
44005
44770
|
closeSync6(fd);
|
|
@@ -44007,7 +44772,7 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44007
44772
|
linkSync2(temporaryPath, path);
|
|
44008
44773
|
unlinkSync24(temporaryPath);
|
|
44009
44774
|
chmodSync8(path, 384);
|
|
44010
|
-
const directoryFd = openSync6(
|
|
44775
|
+
const directoryFd = openSync6(dirname24(path), "r");
|
|
44011
44776
|
try {
|
|
44012
44777
|
fsyncSync3(directoryFd);
|
|
44013
44778
|
} finally {
|
|
@@ -44016,10 +44781,10 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44016
44781
|
} catch (error2) {
|
|
44017
44782
|
if (fd !== undefined)
|
|
44018
44783
|
closeSync6(fd);
|
|
44019
|
-
if (
|
|
44784
|
+
if (existsSync39(temporaryPath))
|
|
44020
44785
|
unlinkSync24(temporaryPath);
|
|
44021
|
-
if (error2.code === "EEXIST" &&
|
|
44022
|
-
const saved = JSON.parse(
|
|
44786
|
+
if (error2.code === "EEXIST" && existsSync39(path)) {
|
|
44787
|
+
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
44023
44788
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
44024
44789
|
return saved;
|
|
44025
44790
|
}
|
|
@@ -44029,17 +44794,17 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
44029
44794
|
}
|
|
44030
44795
|
function replacePendingEnrollment(pending2) {
|
|
44031
44796
|
const path = pendingEnrollmentPath();
|
|
44032
|
-
const directory =
|
|
44797
|
+
const directory = dirname24(path);
|
|
44033
44798
|
const temporaryPath = resolve26(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
44034
44799
|
let fd;
|
|
44035
44800
|
try {
|
|
44036
44801
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
44037
|
-
|
|
44802
|
+
writeFileSync27(fd, `${JSON.stringify(pending2, null, 2)}
|
|
44038
44803
|
`, "utf8");
|
|
44039
44804
|
fsyncSync3(fd);
|
|
44040
44805
|
closeSync6(fd);
|
|
44041
44806
|
fd = undefined;
|
|
44042
|
-
|
|
44807
|
+
renameSync18(temporaryPath, path);
|
|
44043
44808
|
const directoryFd = openSync6(directory, "r");
|
|
44044
44809
|
try {
|
|
44045
44810
|
fsyncSync3(directoryFd);
|
|
@@ -44049,18 +44814,18 @@ function replacePendingEnrollment(pending2) {
|
|
|
44049
44814
|
} catch (error2) {
|
|
44050
44815
|
if (fd !== undefined)
|
|
44051
44816
|
closeSync6(fd);
|
|
44052
|
-
if (
|
|
44817
|
+
if (existsSync39(temporaryPath))
|
|
44053
44818
|
unlinkSync24(temporaryPath);
|
|
44054
44819
|
throw error2;
|
|
44055
44820
|
}
|
|
44056
44821
|
}
|
|
44057
|
-
function recordClaimedCredential(pending2,
|
|
44822
|
+
function recordClaimedCredential(pending2, join44) {
|
|
44058
44823
|
withJoinCredentialLock(() => {
|
|
44059
|
-
const current3 = JSON.parse(
|
|
44824
|
+
const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
|
|
44060
44825
|
if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
|
|
44061
44826
|
throw new Error("pending Otium enrollment changed while its claim was in flight");
|
|
44062
44827
|
}
|
|
44063
|
-
const claimed = { digest: joinCredentialDigest(
|
|
44828
|
+
const claimed = { digest: joinCredentialDigest(join44), cellId: join44.cellId };
|
|
44064
44829
|
if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
|
|
44065
44830
|
throw new Error("central returned different credentials for an idempotent enrollment claim");
|
|
44066
44831
|
}
|
|
@@ -44118,36 +44883,36 @@ async function claimEnrollment(invite, nodeName) {
|
|
|
44118
44883
|
const secret = openCredential(credential, pending2.privateKey);
|
|
44119
44884
|
if (!secret.startsWith("rcs_"))
|
|
44120
44885
|
throw new Error("central returned an invalid runtime credential");
|
|
44121
|
-
const
|
|
44886
|
+
const join44 = {
|
|
44122
44887
|
v: 2,
|
|
44123
44888
|
central: invite.central,
|
|
44124
44889
|
relay: String(response.relayUrl),
|
|
44125
44890
|
cellId: String(response.cell?.id),
|
|
44126
44891
|
secret
|
|
44127
44892
|
};
|
|
44128
|
-
if (!
|
|
44893
|
+
if (!join44.relay || !join44.cellId || join44.cellId === "undefined") {
|
|
44129
44894
|
throw new Error("central returned an incomplete enrollment response");
|
|
44130
44895
|
}
|
|
44131
|
-
assertSecureRelayUrl(
|
|
44132
|
-
recordClaimedCredential(pending2,
|
|
44133
|
-
return
|
|
44896
|
+
assertSecureRelayUrl(join44.relay);
|
|
44897
|
+
recordClaimedCredential(pending2, join44);
|
|
44898
|
+
return join44;
|
|
44134
44899
|
}
|
|
44135
|
-
function commitEnrollment(
|
|
44900
|
+
function commitEnrollment(join44, options = {}) {
|
|
44136
44901
|
return withJoinCredentialLock(() => {
|
|
44137
44902
|
const pendingPath = pendingEnrollmentPath();
|
|
44138
|
-
const pending2 =
|
|
44139
|
-
const digest = joinCredentialDigest(
|
|
44140
|
-
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !==
|
|
44903
|
+
const pending2 = existsSync39(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
|
|
44904
|
+
const digest = joinCredentialDigest(join44);
|
|
44905
|
+
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join44.cellId)) {
|
|
44141
44906
|
throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
|
|
44142
44907
|
}
|
|
44143
|
-
const path = saveJoinWhileLocked(
|
|
44144
|
-
if (!isJoinPersisted(
|
|
44908
|
+
const path = saveJoinWhileLocked(join44, options);
|
|
44909
|
+
if (!isJoinPersisted(join44)) {
|
|
44145
44910
|
throw new Error("Otium join credentials were not durably persisted");
|
|
44146
44911
|
}
|
|
44147
44912
|
if (!pending2)
|
|
44148
44913
|
return path;
|
|
44149
44914
|
unlinkSync24(pendingPath);
|
|
44150
|
-
const directoryFd = openSync6(
|
|
44915
|
+
const directoryFd = openSync6(dirname24(pendingPath), "r");
|
|
44151
44916
|
try {
|
|
44152
44917
|
fsyncSync3(directoryFd);
|
|
44153
44918
|
} finally {
|
|
@@ -44728,24 +45493,24 @@ function refreshDefaultSurfaceScope() {
|
|
|
44728
45493
|
setSurfaceScopeRequired(scopes.length > 1);
|
|
44729
45494
|
}
|
|
44730
45495
|
function startOtiumNodeRuntime(options) {
|
|
44731
|
-
const { join:
|
|
44732
|
-
attachOtiumCentralCell(
|
|
45496
|
+
const { join: join44 } = options;
|
|
45497
|
+
attachOtiumCentralCell(join44);
|
|
44733
45498
|
const releaseGlobals = acquireGlobalOtiumServices();
|
|
44734
45499
|
let stopped = false;
|
|
44735
|
-
logger.info({ central:
|
|
44736
|
-
mountedScopes.set(
|
|
45500
|
+
logger.info({ central: join44.central, cellId: join44.cellId }, "otium: worker mode enabled");
|
|
45501
|
+
mountedScopes.set(join44.cellId, cachedSurfaceScope(join44));
|
|
44737
45502
|
refreshDefaultSurfaceScope();
|
|
44738
|
-
selfPeerNodeForCell(
|
|
45503
|
+
selfPeerNodeForCell(join44.cellId).then((self) => {
|
|
44739
45504
|
if (self) {
|
|
44740
45505
|
logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
|
|
44741
45506
|
}
|
|
44742
45507
|
}).catch((err2) => {
|
|
44743
45508
|
logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
|
|
44744
45509
|
});
|
|
44745
|
-
resolveSurfaceScope(
|
|
45510
|
+
resolveSurfaceScope(join44).then((scope) => {
|
|
44746
45511
|
if (!scope || stopped)
|
|
44747
45512
|
return;
|
|
44748
|
-
mountedScopes.set(
|
|
45513
|
+
mountedScopes.set(join44.cellId, scope);
|
|
44749
45514
|
refreshDefaultSurfaceScope();
|
|
44750
45515
|
if (mountedScopes.size === 1)
|
|
44751
45516
|
stampUnscopedOtiumTopics(scope);
|
|
@@ -44754,14 +45519,14 @@ function startOtiumNodeRuntime(options) {
|
|
|
44754
45519
|
});
|
|
44755
45520
|
return {
|
|
44756
45521
|
name: "otium",
|
|
44757
|
-
join:
|
|
45522
|
+
join: join44,
|
|
44758
45523
|
stop: () => {
|
|
44759
45524
|
if (stopped)
|
|
44760
45525
|
return;
|
|
44761
45526
|
stopped = true;
|
|
44762
|
-
mountedScopes.delete(
|
|
45527
|
+
mountedScopes.delete(join44.cellId);
|
|
44763
45528
|
refreshDefaultSurfaceScope();
|
|
44764
|
-
detachOtiumCentralCell(
|
|
45529
|
+
detachOtiumCentralCell(join44.cellId);
|
|
44765
45530
|
releaseGlobals();
|
|
44766
45531
|
}
|
|
44767
45532
|
};
|
|
@@ -44856,14 +45621,14 @@ __export(exports_node_runtime, {
|
|
|
44856
45621
|
function sameCredentials(left, right) {
|
|
44857
45622
|
return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
|
|
44858
45623
|
}
|
|
44859
|
-
function attachOtiumWorkspace(
|
|
44860
|
-
const current3 = mounted.get(
|
|
45624
|
+
function attachOtiumWorkspace(join44) {
|
|
45625
|
+
const current3 = mounted.get(join44.cellId);
|
|
44861
45626
|
if (current3) {
|
|
44862
|
-
if (sameCredentials(current3.join,
|
|
45627
|
+
if (sameCredentials(current3.join, join44))
|
|
44863
45628
|
return false;
|
|
44864
|
-
detachOtiumWorkspace(
|
|
45629
|
+
detachOtiumWorkspace(join44.cellId);
|
|
44865
45630
|
}
|
|
44866
|
-
mounted.set(
|
|
45631
|
+
mounted.set(join44.cellId, startOtiumNodeRuntime({ join: join44 }));
|
|
44867
45632
|
return true;
|
|
44868
45633
|
}
|
|
44869
45634
|
function detachOtiumWorkspace(cellId) {
|
|
@@ -44878,7 +45643,7 @@ function mountedOtiumWorkspaces() {
|
|
|
44878
45643
|
return [...mounted.values()].map((runtime2) => runtime2.join);
|
|
44879
45644
|
}
|
|
44880
45645
|
function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
44881
|
-
const wanted = new Map(joins.map((
|
|
45646
|
+
const wanted = new Map(joins.map((join44) => [join44.cellId, join44]));
|
|
44882
45647
|
const detached = [];
|
|
44883
45648
|
for (const cellId of [...mounted.keys()]) {
|
|
44884
45649
|
if (wanted.has(cellId))
|
|
@@ -44887,8 +45652,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
|
44887
45652
|
detached.push(cellId);
|
|
44888
45653
|
}
|
|
44889
45654
|
const attached = [];
|
|
44890
|
-
for (const [cellId,
|
|
44891
|
-
if (attachOtiumWorkspace(
|
|
45655
|
+
for (const [cellId, join44] of wanted) {
|
|
45656
|
+
if (attachOtiumWorkspace(join44))
|
|
44892
45657
|
attached.push(cellId);
|
|
44893
45658
|
}
|
|
44894
45659
|
if (attached.length > 0 || detached.length > 0) {
|
|
@@ -44911,9 +45676,9 @@ async function handleOtiumAdapterControlRequest(req) {
|
|
|
44911
45676
|
if (req.method === "GET") {
|
|
44912
45677
|
return Response.json({
|
|
44913
45678
|
ok: true,
|
|
44914
|
-
workspaces: mountedOtiumWorkspaces().map((
|
|
44915
|
-
cellId:
|
|
44916
|
-
central:
|
|
45679
|
+
workspaces: mountedOtiumWorkspaces().map((join44) => ({
|
|
45680
|
+
cellId: join44.cellId,
|
|
45681
|
+
central: join44.central
|
|
44917
45682
|
}))
|
|
44918
45683
|
});
|
|
44919
45684
|
}
|
|
@@ -44934,8 +45699,8 @@ function mountConfiguredOtiumNodeRuntime() {
|
|
|
44934
45699
|
const joins = loadJoins();
|
|
44935
45700
|
if (joins.length === 0)
|
|
44936
45701
|
return null;
|
|
44937
|
-
for (const
|
|
44938
|
-
attachOtiumWorkspace(
|
|
45702
|
+
for (const join44 of joins)
|
|
45703
|
+
attachOtiumWorkspace(join44);
|
|
44939
45704
|
registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
|
|
44940
45705
|
let stopped = false;
|
|
44941
45706
|
return {
|
|
@@ -45026,11 +45791,11 @@ async function joinCommand(args) {
|
|
|
45026
45791
|
process.exitCode = 1;
|
|
45027
45792
|
return;
|
|
45028
45793
|
}
|
|
45029
|
-
let
|
|
45794
|
+
let join44;
|
|
45030
45795
|
let productionEnrollment = false;
|
|
45031
45796
|
if (args.includes("--legacy")) {
|
|
45032
45797
|
try {
|
|
45033
|
-
|
|
45798
|
+
join44 = parseInviteCode(code);
|
|
45034
45799
|
} catch (err2) {
|
|
45035
45800
|
console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
|
|
45036
45801
|
process.exitCode = 1;
|
|
@@ -45057,7 +45822,7 @@ async function joinCommand(args) {
|
|
|
45057
45822
|
return;
|
|
45058
45823
|
}
|
|
45059
45824
|
}
|
|
45060
|
-
|
|
45825
|
+
join44 = await claimEnrollment(invite, nodeName);
|
|
45061
45826
|
productionEnrollment = true;
|
|
45062
45827
|
} catch (err2) {
|
|
45063
45828
|
console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
@@ -45068,16 +45833,16 @@ async function joinCommand(args) {
|
|
|
45068
45833
|
let path;
|
|
45069
45834
|
try {
|
|
45070
45835
|
const saveOptions = { replaceExisting: args.includes("--replace") };
|
|
45071
|
-
path = productionEnrollment ? commitEnrollment(
|
|
45836
|
+
path = productionEnrollment ? commitEnrollment(join44, saveOptions) : saveJoin(join44, saveOptions);
|
|
45072
45837
|
} catch (err2) {
|
|
45073
45838
|
console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
|
|
45074
45839
|
process.exitCode = 1;
|
|
45075
45840
|
return;
|
|
45076
45841
|
}
|
|
45077
45842
|
console.log(`otium join credentials saved to ${path}`);
|
|
45078
|
-
console.log(` central: ${
|
|
45079
|
-
console.log(` cellId: ${
|
|
45080
|
-
configureOtiumCentral(
|
|
45843
|
+
console.log(` central: ${join44.central}`);
|
|
45844
|
+
console.log(` cellId: ${join44.cellId}`);
|
|
45845
|
+
configureOtiumCentral(join44);
|
|
45081
45846
|
try {
|
|
45082
45847
|
const self = await selfPeerNode();
|
|
45083
45848
|
if (self) {
|
|
@@ -45143,8 +45908,8 @@ async function proxyOtiumPeerRequest(req, dependencies = {}) {
|
|
|
45143
45908
|
}
|
|
45144
45909
|
}
|
|
45145
45910
|
async function runOtiumSidecar(options) {
|
|
45146
|
-
const
|
|
45147
|
-
if (!
|
|
45911
|
+
const join44 = loadJoin();
|
|
45912
|
+
if (!join44) {
|
|
45148
45913
|
throw new Error("not joined to an Otium workspace \u2014 run `negotium otium join <code>` first");
|
|
45149
45914
|
}
|
|
45150
45915
|
const initialNode = await inspectNodeDaemon();
|
|
@@ -45176,10 +45941,10 @@ async function runOtiumSidecar(options) {
|
|
|
45176
45941
|
lease.stop();
|
|
45177
45942
|
throw error2;
|
|
45178
45943
|
}
|
|
45179
|
-
const selectedRelay = options.relayUrl?.trim() ||
|
|
45944
|
+
const selectedRelay = options.relayUrl?.trim() || join44.relay || process.env.OTIUM_RELAY_URL?.trim();
|
|
45180
45945
|
const tunnel = selectedRelay ? new TunnelClient({
|
|
45181
45946
|
relayUrl: selectedRelay,
|
|
45182
|
-
token:
|
|
45947
|
+
token: join44.secret,
|
|
45183
45948
|
targetOrigin: `http://127.0.0.1:${server.port}`,
|
|
45184
45949
|
nodeVersion: `negotium@${NEGOTIUM_VERSION}`,
|
|
45185
45950
|
logger
|
|
@@ -45325,9 +46090,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
45325
46090
|
if (joins.length === 0)
|
|
45326
46091
|
throw new Error("not joined to an Otium workspace");
|
|
45327
46092
|
if (!targetCellId && joins.length > 1) {
|
|
45328
|
-
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((
|
|
46093
|
+
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join44) => join44.cellId).join(", ")}`);
|
|
45329
46094
|
}
|
|
45330
|
-
if (targetCellId && !joins.some((
|
|
46095
|
+
if (targetCellId && !joins.some((join44) => join44.cellId === targetCellId)) {
|
|
45331
46096
|
throw new Error(`not joined as ${targetCellId}`);
|
|
45332
46097
|
}
|
|
45333
46098
|
removeJoin2(targetCellId);
|
|
@@ -45633,7 +46398,7 @@ async function cronCommand(args) {
|
|
|
45633
46398
|
}
|
|
45634
46399
|
if (script && !cronScriptExists(script))
|
|
45635
46400
|
throw new Error(`cron script not found: ${script}`);
|
|
45636
|
-
const topic = getTopicByNameForUser(topicName, USER_ID);
|
|
46401
|
+
const topic = getTopicByNameForUser(topicName, USER_ID, { scope: "all" });
|
|
45637
46402
|
if (!topic?.agent)
|
|
45638
46403
|
throw new Error(`topic not found or has no agent: ${topicName}`);
|
|
45639
46404
|
const agentRaw = flag(rest, "agent");
|
|
@@ -45680,7 +46445,7 @@ async function cronCommand(args) {
|
|
|
45680
46445
|
if (!job || job.ownerUserId !== USER_ID)
|
|
45681
46446
|
throw new Error(`cron job not found: ${ref}`);
|
|
45682
46447
|
const topicName = flag(rest, "topic");
|
|
45683
|
-
const topic = topicName ? getTopicByNameForUser(topicName, USER_ID) : null;
|
|
46448
|
+
const topic = topicName ? getTopicByNameForUser(topicName, USER_ID, { scope: "all" }) : null;
|
|
45684
46449
|
if (topicName && !topic?.agent)
|
|
45685
46450
|
throw new Error(`topic not found or has no agent: ${topicName}`);
|
|
45686
46451
|
const scriptTouched = hasFlag2(rest, "script");
|
|
@@ -46065,4 +46830,4 @@ switch (command) {
|
|
|
46065
46830
|
}
|
|
46066
46831
|
}
|
|
46067
46832
|
|
|
46068
|
-
//# debugId=
|
|
46833
|
+
//# debugId=65E123B94EDD2F5C64756E2164756E21
|