@agentproto/runtime 3.5.0 → 3.6.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/config.d.ts +23 -1
- package/dist/config.mjs.map +1 -1
- package/dist/index.mjs +452 -225
- package/dist/index.mjs.map +1 -1
- package/dist/resume-strategies.d.ts +4 -3
- package/dist/resume-strategies.mjs +16 -7
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/session-presence.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -21,18 +21,18 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
|
|
|
21
21
|
import { createServer as createServer$1 } from 'net';
|
|
22
22
|
import { defineTool, catchErrors, ToolError, pageParamsShape, paginated, paginate, toolText } from '@agentproto/tool';
|
|
23
23
|
import { CatalogProviderSchema, getModelsByProvider, getStaticModelProvider } from '@agentproto/model-catalog';
|
|
24
|
+
import { APP_UI_DISCOVERY_TOOLS, RUNNER_SELECT_SCRIPT } from '@agentproto/app-client/runner-select';
|
|
25
|
+
import { SESSION_CHAT_APP_ID, liveSessionApp, makeLiveSessionApp, sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, workBoardApp, makeSessionsPanelApp, makeAgentsOverviewApp, makeBureauSessionsApp, makeSessionStoryPanelApp, makeSessionChatApp, makeWorkBoardApp, sessionChatApp } from '@agentproto/apps';
|
|
24
26
|
import { inferLegacyModeKind, parseModelSwitchCommand, isModelSwitchAcknowledgement } from '@agentproto/driver-agent-cli';
|
|
25
27
|
import { loadSandboxConfig, resolveCommandSandbox, COMMAND_SANDBOX_MODE_ENV } from '@agentproto/command-sandbox';
|
|
26
28
|
import { createBrainManager, parseKnowledgeConfig } from '@agentproto/workspace-brain';
|
|
27
29
|
import { defineHttpDriver } from '@agentproto/driver-http';
|
|
28
|
-
import { liveSessionApp, makeLiveSessionApp, sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, workBoardApp, makeSessionsPanelApp, makeAgentsOverviewApp, makeBureauSessionsApp, makeSessionStoryPanelApp, makeSessionChatApp, makeWorkBoardApp, SESSION_CHAT_APP_ID, sessionChatApp } from '@agentproto/apps';
|
|
29
30
|
import matter2 from 'gray-matter';
|
|
30
31
|
import { createServer } from 'http';
|
|
31
32
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
32
33
|
import { WebSocketServer } from 'ws';
|
|
33
34
|
import { loadAppHandle } from '@agentproto/app-kit';
|
|
34
35
|
import { loadAgent } from '@agentproto/agent';
|
|
35
|
-
import { APP_UI_DISCOVERY_TOOLS, RUNNER_SELECT_SCRIPT } from '@agentproto/app-client/runner-select';
|
|
36
36
|
import { normalizeToolId } from '@agentproto/driver';
|
|
37
37
|
import { createIngestionClient } from '@agentproto/telemetry-langfuse';
|
|
38
38
|
import { resolveRedactor } from '@agentproto/redaction';
|
|
@@ -1666,8 +1666,8 @@ async function discoverOpenCodeSessions(cwd, since, expectedId) {
|
|
|
1666
1666
|
const sinceMs = since ? Date.parse(since) : NaN;
|
|
1667
1667
|
return rows.filter((r) => {
|
|
1668
1668
|
if (!Number.isFinite(sinceMs)) return true;
|
|
1669
|
-
|
|
1670
|
-
return
|
|
1669
|
+
if (r.time_created === void 0) return false;
|
|
1670
|
+
return r.time_created >= sinceMs - 1e3;
|
|
1671
1671
|
}).map(openCodeRowToCandidate);
|
|
1672
1672
|
} finally {
|
|
1673
1673
|
db.close();
|
|
@@ -2098,6 +2098,9 @@ function extractFirstText(content) {
|
|
|
2098
2098
|
}
|
|
2099
2099
|
return void 0;
|
|
2100
2100
|
}
|
|
2101
|
+
function isClaudeControlText(text10) {
|
|
2102
|
+
return /^<(?:local-command-caveat|local-command-stdout|command-name|task-notification)>/i.test(text10);
|
|
2103
|
+
}
|
|
2101
2104
|
async function scanClaudeJsonl(filePath) {
|
|
2102
2105
|
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
2103
2106
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
@@ -2126,7 +2129,7 @@ async function scanClaudeJsonl(filePath) {
|
|
|
2126
2129
|
messageCount += 1;
|
|
2127
2130
|
if (preview2 === void 0 && entry.type === "user") {
|
|
2128
2131
|
const text10 = extractFirstText(entry.message?.content);
|
|
2129
|
-
if (text10 !== void 0) {
|
|
2132
|
+
if (text10 !== void 0 && !isClaudeControlText(text10)) {
|
|
2130
2133
|
preview2 = text10.length > 120 ? text10.slice(0, 120) : text10;
|
|
2131
2134
|
}
|
|
2132
2135
|
}
|
|
@@ -2243,6 +2246,9 @@ function conversationTerminalSlugFor(subject) {
|
|
|
2243
2246
|
const argv = subject.argv;
|
|
2244
2247
|
if (!argv || argv.length === 0 || !argv[0]) return void 0;
|
|
2245
2248
|
const bin = argvBasename(argv[0]);
|
|
2249
|
+
for (const [slug, bins] of Object.entries(BARE_LAUNCH_BINS)) {
|
|
2250
|
+
if (bins.some((b) => argvBasename(b) === bin)) return slug;
|
|
2251
|
+
}
|
|
2246
2252
|
for (const [slug, launch] of Object.entries(NATIVE_LAUNCH_ARGV)) {
|
|
2247
2253
|
const launchBin = launch[0];
|
|
2248
2254
|
if (!launchBin || argvBasename(launchBin) !== bin) continue;
|
|
@@ -2265,7 +2271,7 @@ function isConversationTerminal(subject) {
|
|
|
2265
2271
|
}
|
|
2266
2272
|
return false;
|
|
2267
2273
|
}
|
|
2268
|
-
var CONVERSATION_STORES, NATIVE_LAUNCH_ARGV, CONVERSATION_RESUME_KEYS;
|
|
2274
|
+
var CONVERSATION_STORES, NATIVE_LAUNCH_ARGV, BARE_LAUNCH_BINS, CONVERSATION_RESUME_KEYS;
|
|
2269
2275
|
var init_conversation_store = __esm({
|
|
2270
2276
|
"src/conversation-store.ts"() {
|
|
2271
2277
|
CONVERSATION_STORES = {
|
|
@@ -2300,6 +2306,9 @@ var init_conversation_store = __esm({
|
|
|
2300
2306
|
},
|
|
2301
2307
|
opencode: {
|
|
2302
2308
|
storeAs: "openCodeResumeId",
|
|
2309
|
+
// Match the adapter's portable npx installation: a direct `opencode`
|
|
2310
|
+
// binary is not guaranteed to be on the daemon's PATH on every host.
|
|
2311
|
+
attachArgv: (conversationId) => ["npx", "-y", "opencode-ai", "-s", conversationId],
|
|
2303
2312
|
discover: discoverOpenCode,
|
|
2304
2313
|
read: readOpenCode
|
|
2305
2314
|
},
|
|
@@ -2337,6 +2346,10 @@ var init_conversation_store = __esm({
|
|
|
2337
2346
|
"mistral-vibe": ["vibe"],
|
|
2338
2347
|
"kimi-cli": ["kimi"]
|
|
2339
2348
|
};
|
|
2349
|
+
BARE_LAUNCH_BINS = {
|
|
2350
|
+
opencode: ["opencode"],
|
|
2351
|
+
mastracode: ["mastracode"]
|
|
2352
|
+
};
|
|
2340
2353
|
CONVERSATION_RESUME_KEYS = new Set(
|
|
2341
2354
|
Object.values(CONVERSATION_STORES).map((s) => s.storeAs)
|
|
2342
2355
|
);
|
|
@@ -2589,7 +2602,8 @@ var RESUME_STRATEGIES = Object.fromEntries(
|
|
|
2589
2602
|
);
|
|
2590
2603
|
function decideRestartStrategy(prev, opts = {}) {
|
|
2591
2604
|
const mayPreferNative = prev.pty === true || opts.preferNativeTerminal === true;
|
|
2592
|
-
|
|
2605
|
+
const hasNativeCapability = prev.nativeTerminalResume === true || prev.pty === true && prev.nativeTerminalResume === void 0;
|
|
2606
|
+
if (prev.adapterSlug && hasNativeCapability && mayPreferNative) {
|
|
2593
2607
|
const strategy = RESUME_STRATEGIES[prev.adapterSlug];
|
|
2594
2608
|
const id = strategy?.storeAs ? prev.resumeMetadata?.[strategy.storeAs] : void 0;
|
|
2595
2609
|
if (strategy?.spawnArgs && id) {
|
|
@@ -2661,11 +2675,13 @@ async function probeNativeTranscript(prev) {
|
|
|
2661
2675
|
}
|
|
2662
2676
|
function describeResumePath(prev, opts = {}) {
|
|
2663
2677
|
const mayPreferNative = prev.pty === true || opts.preferNativeTerminal === true;
|
|
2664
|
-
|
|
2678
|
+
const hasNativeCapability = prev.nativeTerminalResume === true || prev.pty === true && prev.nativeTerminalResume === void 0;
|
|
2679
|
+
if (prev.adapterSlug && hasNativeCapability && mayPreferNative) {
|
|
2665
2680
|
const s = RESUME_STRATEGIES[prev.adapterSlug];
|
|
2666
2681
|
if (s?.spawnArgs && s.storeAs && prev.resumeMetadata?.[s.storeAs]) {
|
|
2667
|
-
const sample = s.spawnArgs("\u2026")
|
|
2668
|
-
|
|
2682
|
+
const sample = s.spawnArgs("\u2026");
|
|
2683
|
+
const idIndex = sample.indexOf("\u2026");
|
|
2684
|
+
return `resumed via ${idIndex > 0 ? sample.slice(0, idIndex).join(" ") : sample[0] ?? prev.adapterSlug}`;
|
|
2669
2685
|
}
|
|
2670
2686
|
}
|
|
2671
2687
|
if (prev.adapterSlug && prev.adapterSessionId) {
|
|
@@ -5359,6 +5375,9 @@ async function resolveWorkspaceRules(slug, deps2 = {}) {
|
|
|
5359
5375
|
|
|
5360
5376
|
// src/session-title.ts
|
|
5361
5377
|
var MAX_LENGTH = 72;
|
|
5378
|
+
function isInternalDerivedTitle(title) {
|
|
5379
|
+
return /^<(?:local-command-caveat|local-command-stdout|command-name|task-notification)>/i.test(title);
|
|
5380
|
+
}
|
|
5362
5381
|
function isRecord3(value) {
|
|
5363
5382
|
return typeof value === "object" && value !== null;
|
|
5364
5383
|
}
|
|
@@ -5385,6 +5404,7 @@ function deriveSessionTitle(message) {
|
|
|
5385
5404
|
if (raw === void 0) return void 0;
|
|
5386
5405
|
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
5387
5406
|
if (collapsed === "") return void 0;
|
|
5407
|
+
if (isInternalDerivedTitle(collapsed)) return void 0;
|
|
5388
5408
|
const sentence = collapsed.replace(/[.?!](\s.*)?$/, "").trim();
|
|
5389
5409
|
if (sentence === "" || !/[\p{L}\p{N}]/u.test(sentence)) return void 0;
|
|
5390
5410
|
return Array.from(sentence).length > MAX_LENGTH ? truncate2(sentence, MAX_LENGTH) : sentence;
|
|
@@ -8284,8 +8304,175 @@ var sandboxSpecWithReuseSchema = z.object({
|
|
|
8284
8304
|
"Reconnect to an existing box instead of booting a new one. Accepts a prior session's exact `sandboxId`, a ledger `label` (exact match), or an unambiguous prefix of a ledger sandboxId \u2014 resolved against the local sandbox ledger (`~/.agentproto/sandboxes.json`, see `agentproto sandbox list`). A prefix matching more than one ledger entry fails with `sandbox_reuse_ambiguous` listing the candidates. Requires the provider to support reconnect (e.g. e2b); omit to boot fresh (default)."
|
|
8285
8305
|
)
|
|
8286
8306
|
}).strict();
|
|
8287
|
-
|
|
8288
|
-
|
|
8307
|
+
function appUiToolId(appId) {
|
|
8308
|
+
const slug = appId.replace(/^@[^/]+\//, "").replace(/[^a-z0-9]/g, "_");
|
|
8309
|
+
return `app_ui_${slug}`;
|
|
8310
|
+
}
|
|
8311
|
+
var MCP_APP_BRIDGE_SCRIPT = `<script>
|
|
8312
|
+
(function () {
|
|
8313
|
+
window.__AGENTPROTO_UI_TRANSPORT__ = "mcp";
|
|
8314
|
+
if (window.McpApp) return;
|
|
8315
|
+
var nextId = 1, pending = {}, teardownCbs = [];
|
|
8316
|
+
function post(m) { window.parent.postMessage(m, "*"); }
|
|
8317
|
+
window.addEventListener("message", function (e) {
|
|
8318
|
+
var m = e.data;
|
|
8319
|
+
if (typeof m === "string") { try { m = JSON.parse(m); } catch (_) { return; } }
|
|
8320
|
+
if (!m || m.jsonrpc !== "2.0") return;
|
|
8321
|
+
if (m.method === "ui/resource-teardown") {
|
|
8322
|
+
for (var i = 0; i < teardownCbs.length; i++) teardownCbs[i]();
|
|
8323
|
+
post({ jsonrpc: "2.0", id: m.id, result: {} });
|
|
8324
|
+
return;
|
|
8325
|
+
}
|
|
8326
|
+
if (m.id != null && pending[m.id]) { var cb = pending[m.id]; delete pending[m.id]; cb(m.result, m.error); }
|
|
8327
|
+
});
|
|
8328
|
+
function request(method, params) {
|
|
8329
|
+
return new Promise(function (resolve, reject) {
|
|
8330
|
+
var id = nextId++;
|
|
8331
|
+
pending[id] = function (r, err) { if (err) reject(new Error((err && err.message) || String(err))); else resolve(r); };
|
|
8332
|
+
post({ jsonrpc: "2.0", id: id, method: method, params: params });
|
|
8333
|
+
});
|
|
8334
|
+
}
|
|
8335
|
+
window.McpApp = {
|
|
8336
|
+
connect: function () {
|
|
8337
|
+
if (window.parent === window) return Promise.reject(new Error("no host (standalone)"));
|
|
8338
|
+
return request("ui/initialize", {
|
|
8339
|
+
appInfo: { name: "agentproto-app", version: "1.0" },
|
|
8340
|
+
appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
|
|
8341
|
+
protocolVersion: "2026-01-26"
|
|
8342
|
+
}).then(function () {
|
|
8343
|
+
post({ jsonrpc: "2.0", method: "ui/notifications/initialized", params: {} });
|
|
8344
|
+
return {
|
|
8345
|
+
callTool: function (name, args) { return request("tools/call", { name: name, arguments: args || {} }); },
|
|
8346
|
+
sendMessage: function (p) {
|
|
8347
|
+
var params = typeof p === "string" ? { content: p } : (p || {});
|
|
8348
|
+
var content = typeof params.content === "string" ? [{ type: "text", text: params.content }] : (params.content || []);
|
|
8349
|
+
return request("ui/message", { role: "user", content: content });
|
|
8350
|
+
},
|
|
8351
|
+
updateModelContext: function (p) { return request("ui/update-model-context", p || {}); },
|
|
8352
|
+
openLink: function (url) { return request("ui/open-link", { url: url }); },
|
|
8353
|
+
onTeardown: function (cb) { teardownCbs.push(cb); }
|
|
8354
|
+
};
|
|
8355
|
+
});
|
|
8356
|
+
}
|
|
8357
|
+
};
|
|
8358
|
+
})();
|
|
8359
|
+
</script>
|
|
8360
|
+
`;
|
|
8361
|
+
function injectAfterStructuralTag(html, script) {
|
|
8362
|
+
for (const tag of ["head", "body", "html"]) {
|
|
8363
|
+
const match = html.match(new RegExp(`<${tag}[^>]*>`, "i"));
|
|
8364
|
+
if (match?.index !== void 0) {
|
|
8365
|
+
const insertAt = match.index + match[0].length;
|
|
8366
|
+
return html.slice(0, insertAt) + script + html.slice(insertAt);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
return script + html;
|
|
8370
|
+
}
|
|
8371
|
+
function injectMcpAppBridge(html) {
|
|
8372
|
+
const hasBridge = /window\.McpApp\s*=/.test(html);
|
|
8373
|
+
const hasRunnerSelect = /window\.AgentprotoUI\s*=/.test(html);
|
|
8374
|
+
if (hasBridge && hasRunnerSelect) return html;
|
|
8375
|
+
const script = (hasBridge ? "" : MCP_APP_BRIDGE_SCRIPT) + (hasRunnerSelect ? "" : RUNNER_SELECT_SCRIPT);
|
|
8376
|
+
return injectAfterStructuralTag(html, script);
|
|
8377
|
+
}
|
|
8378
|
+
var STANDALONE_REST_BRIDGE_SCRIPT = `<script>
|
|
8379
|
+
(function () {
|
|
8380
|
+
window.__AGENTPROTO_UI_TRANSPORT__ = "http";
|
|
8381
|
+
if (window.McpApp) return;
|
|
8382
|
+
window.McpApp = {
|
|
8383
|
+
connect: function () {
|
|
8384
|
+
return Promise.resolve({
|
|
8385
|
+
callTool: function (name, args) {
|
|
8386
|
+
return fetch("./tool-call", {
|
|
8387
|
+
method: "POST",
|
|
8388
|
+
headers: { "content-type": "application/json" },
|
|
8389
|
+
body: JSON.stringify({ tool: name, args: args || {} })
|
|
8390
|
+
}).then(function (res) {
|
|
8391
|
+
return res.json().catch(function () {
|
|
8392
|
+
throw new Error("tool-call failed: HTTP " + res.status);
|
|
8393
|
+
}).then(function (body) {
|
|
8394
|
+
if (!res.ok) throw new Error((body && (body.message || body.error)) || ("tool-call failed: HTTP " + res.status));
|
|
8395
|
+
return body;
|
|
8396
|
+
});
|
|
8397
|
+
});
|
|
8398
|
+
},
|
|
8399
|
+
sendMessage: function () {
|
|
8400
|
+
return Promise.reject(new Error("sendMessage: no chat host (standalone mode)"));
|
|
8401
|
+
},
|
|
8402
|
+
updateModelContext: function () {
|
|
8403
|
+
return Promise.reject(new Error("updateModelContext: no host (standalone mode)"));
|
|
8404
|
+
},
|
|
8405
|
+
openLink: function (url) {
|
|
8406
|
+
window.open(url, "_blank");
|
|
8407
|
+
return Promise.resolve({});
|
|
8408
|
+
},
|
|
8409
|
+
onTeardown: function () {}
|
|
8410
|
+
});
|
|
8411
|
+
}
|
|
8412
|
+
};
|
|
8413
|
+
})();
|
|
8414
|
+
</script>
|
|
8415
|
+
`;
|
|
8416
|
+
function injectStandaloneAppBridge(html, baseUrl) {
|
|
8417
|
+
const hasRunnerSelect = /window\.AgentprotoUI\s*=/.test(html);
|
|
8418
|
+
const baseUrlScript = baseUrl ? `<script>window.__AGENTPROTO_BASEURL__=${JSON.stringify(baseUrl).replace(/</g, "\\u003c")};</script>` : "";
|
|
8419
|
+
const script = baseUrlScript + STANDALONE_REST_BRIDGE_SCRIPT + (hasRunnerSelect ? "" : RUNNER_SELECT_SCRIPT);
|
|
8420
|
+
return injectAfterStructuralTag(html, script);
|
|
8421
|
+
}
|
|
8422
|
+
function createUiHtmlCache() {
|
|
8423
|
+
const cache = /* @__PURE__ */ new Map();
|
|
8424
|
+
return {
|
|
8425
|
+
async get(path, version) {
|
|
8426
|
+
const cached = cache.get(path);
|
|
8427
|
+
if (cached && cached.version === version) return cached.html;
|
|
8428
|
+
const raw = await readFile(path, "utf8");
|
|
8429
|
+
const html = injectMcpAppBridge(raw);
|
|
8430
|
+
cache.set(path, { version, html });
|
|
8431
|
+
return html;
|
|
8432
|
+
}
|
|
8433
|
+
};
|
|
8434
|
+
}
|
|
8435
|
+
async function makeInstalledAppUiApps(appRegistry, cache, existingToolNames) {
|
|
8436
|
+
const apps = [];
|
|
8437
|
+
const seen = new Set(existingToolNames);
|
|
8438
|
+
for (const app of appRegistry.listApps()) {
|
|
8439
|
+
const ui = app.ui;
|
|
8440
|
+
if (!ui) continue;
|
|
8441
|
+
const toolId = appUiToolId(app.appId);
|
|
8442
|
+
if (seen.has(toolId)) {
|
|
8443
|
+
console.warn(
|
|
8444
|
+
`[app-ui-apps] skipping UI panel for app "${app.appId}": tool id "${toolId}" collides with an existing tool or another installed app's panel.`
|
|
8445
|
+
);
|
|
8446
|
+
continue;
|
|
8447
|
+
}
|
|
8448
|
+
let html;
|
|
8449
|
+
try {
|
|
8450
|
+
html = await cache.get(ui.path, app.updatedAt);
|
|
8451
|
+
} catch (err) {
|
|
8452
|
+
console.warn(
|
|
8453
|
+
`[app-ui-apps] skipping UI panel for app "${app.appId}": could not read "${ui.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
8454
|
+
);
|
|
8455
|
+
continue;
|
|
8456
|
+
}
|
|
8457
|
+
seen.add(toolId);
|
|
8458
|
+
apps.push({
|
|
8459
|
+
id: toolId,
|
|
8460
|
+
title: ui.title ?? app.name ?? app.appId,
|
|
8461
|
+
...ui.description ? { description: ui.description } : {},
|
|
8462
|
+
inputSchema: z.object({}),
|
|
8463
|
+
execute: async () => ({ appId: app.appId, tools: ui.tools ?? [] }),
|
|
8464
|
+
html,
|
|
8465
|
+
...ui.csp ? {
|
|
8466
|
+
csp: {
|
|
8467
|
+
...ui.csp.connectDomains ? { connectDomains: [...ui.csp.connectDomains] } : {},
|
|
8468
|
+
...ui.csp.resourceDomains ? { resourceDomains: [...ui.csp.resourceDomains] } : {},
|
|
8469
|
+
...ui.csp.frameDomains ? { frameDomains: [...ui.csp.frameDomains] } : {}
|
|
8470
|
+
}
|
|
8471
|
+
} : {}
|
|
8472
|
+
});
|
|
8473
|
+
}
|
|
8474
|
+
return apps;
|
|
8475
|
+
}
|
|
8289
8476
|
function stripAnsi(s) {
|
|
8290
8477
|
let result = s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
|
|
8291
8478
|
result = result.replace(/\r\n/g, "\n");
|
|
@@ -8341,9 +8528,11 @@ function registerAgentTools(server, opts) {
|
|
|
8341
8528
|
provisionWorktree,
|
|
8342
8529
|
resolveWorktreeIsolation,
|
|
8343
8530
|
supervisor,
|
|
8344
|
-
defaultAgentPromptInterrupt
|
|
8531
|
+
defaultAgentPromptInterrupt,
|
|
8532
|
+
isSessionChatInstalled
|
|
8345
8533
|
} = opts;
|
|
8346
8534
|
const interruptDefault = defaultAgentPromptInterrupt ?? false;
|
|
8535
|
+
const sessionChatResourceUri = isSessionChatInstalled?.() ? `ui://${appUiToolId(SESSION_CHAT_APP_ID)}/view` : "ui://agentproto_session_chat/view";
|
|
8347
8536
|
server.registerTool(
|
|
8348
8537
|
"agent_start",
|
|
8349
8538
|
{
|
|
@@ -8589,10 +8778,15 @@ function registerAgentTools(server, opts) {
|
|
|
8589
8778
|
// `ui://live_session/view` resource stays registered for its own
|
|
8590
8779
|
// `live_session` tool and other consumers — only this binding moved.
|
|
8591
8780
|
// `visibility:["model","app"]` keeps agent_start fully usable by the
|
|
8592
|
-
// model AND lets the widget re-call it if needed.
|
|
8781
|
+
// model AND lets the widget re-call it if needed. `resourceUri` binds
|
|
8782
|
+
// to the native `app_ui_session_chat` MCP Apps tool once
|
|
8783
|
+
// `@agentik/session-chat` is installed (`isSessionChatInstalled`
|
|
8784
|
+
// above) — the loopback-HTTP `agentproto_session_chat` launcher this
|
|
8785
|
+
// otherwise binds to is unreachable under a strict-CSP host (Codex),
|
|
8786
|
+
// and builtin-apps.ts stops mounting it once the native tool exists.
|
|
8593
8787
|
_meta: {
|
|
8594
8788
|
ui: {
|
|
8595
|
-
resourceUri:
|
|
8789
|
+
resourceUri: sessionChatResourceUri,
|
|
8596
8790
|
visibility: ["model", "app"]
|
|
8597
8791
|
}
|
|
8598
8792
|
}
|
|
@@ -10432,6 +10626,149 @@ function filterSessionObserver(inner, shouldObserve) {
|
|
|
10432
10626
|
init_tool_presenter();
|
|
10433
10627
|
init_transcript_writer();
|
|
10434
10628
|
|
|
10629
|
+
// src/session-titler.ts
|
|
10630
|
+
init_config();
|
|
10631
|
+
init_transcript_writer();
|
|
10632
|
+
var DEFAULT_TITLER_MODEL = "z-ai/glm-5.2@openrouter";
|
|
10633
|
+
var DEFAULT_LABELS = /* @__PURE__ */ new Set(["chat-starter", "chat-starter-autoprompt", "agentproto"]);
|
|
10634
|
+
var MIN_TURN_CHARS = 200;
|
|
10635
|
+
var titledSessions = /* @__PURE__ */ new Set();
|
|
10636
|
+
async function resolveTitlerConfig() {
|
|
10637
|
+
const cfg = await loadConfig();
|
|
10638
|
+
const t = cfg.titler;
|
|
10639
|
+
return { enabled: t?.enabled === true, model: t?.model ?? DEFAULT_TITLER_MODEL };
|
|
10640
|
+
}
|
|
10641
|
+
function isDefaultLabel(desc) {
|
|
10642
|
+
const label = desc.label?.trim() ?? "";
|
|
10643
|
+
if (label === "") return true;
|
|
10644
|
+
if (desc.title !== void 0 && label === desc.title) return true;
|
|
10645
|
+
return DEFAULT_LABELS.has(label);
|
|
10646
|
+
}
|
|
10647
|
+
function fallbackTitle(userText) {
|
|
10648
|
+
const firstLine = userText.split(/\r?\n/).find((l) => l.trim().length > 0)?.trim() ?? "";
|
|
10649
|
+
return firstLine.split(/\s+/).filter(Boolean).slice(0, 6).join(" ");
|
|
10650
|
+
}
|
|
10651
|
+
function sanitizeTitle(raw) {
|
|
10652
|
+
if (!raw) return void 0;
|
|
10653
|
+
const cleaned = raw.replace(/[`*_"“”'']/g, "").split(/\r?\n/)[0]?.trim();
|
|
10654
|
+
if (!cleaned) return void 0;
|
|
10655
|
+
const words = cleaned.split(/\s+/).map((w) => w.replace(/[.,;:!?]+$/u, "")).filter((w) => /[\p{L}\p{N}]/u.test(w)).slice(0, 8);
|
|
10656
|
+
const title = words.join(" ");
|
|
10657
|
+
return title.length > 0 ? title : void 0;
|
|
10658
|
+
}
|
|
10659
|
+
async function readFirstTurn(sessionId) {
|
|
10660
|
+
let stream;
|
|
10661
|
+
try {
|
|
10662
|
+
stream = createReadStream(sessionEventsPath(sessionId), { encoding: "utf8" });
|
|
10663
|
+
await new Promise((resolve31, reject) => {
|
|
10664
|
+
stream.once("error", reject);
|
|
10665
|
+
stream.once("open", resolve31);
|
|
10666
|
+
});
|
|
10667
|
+
} catch {
|
|
10668
|
+
return null;
|
|
10669
|
+
}
|
|
10670
|
+
let userText = "";
|
|
10671
|
+
let assistantText = "";
|
|
10672
|
+
let sawUserPrompt = false;
|
|
10673
|
+
try {
|
|
10674
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
10675
|
+
for await (const line of rl) {
|
|
10676
|
+
const trimmed = line.trim();
|
|
10677
|
+
if (!trimmed) continue;
|
|
10678
|
+
let rec;
|
|
10679
|
+
try {
|
|
10680
|
+
rec = JSON.parse(trimmed);
|
|
10681
|
+
} catch {
|
|
10682
|
+
continue;
|
|
10683
|
+
}
|
|
10684
|
+
if (rec.kind === "user-prompt" && typeof rec.text === "string") {
|
|
10685
|
+
userText = rec.text;
|
|
10686
|
+
sawUserPrompt = true;
|
|
10687
|
+
} else if (rec.kind === "text-delta" && typeof rec.text === "string") {
|
|
10688
|
+
assistantText += rec.text;
|
|
10689
|
+
} else if (rec.kind === "turn-end") {
|
|
10690
|
+
break;
|
|
10691
|
+
}
|
|
10692
|
+
}
|
|
10693
|
+
} catch {
|
|
10694
|
+
return null;
|
|
10695
|
+
}
|
|
10696
|
+
if (!sawUserPrompt) return null;
|
|
10697
|
+
return { userText, assistantText };
|
|
10698
|
+
}
|
|
10699
|
+
async function generateTitleViaLlm(model, input) {
|
|
10700
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
10701
|
+
if (!apiKey) return void 0;
|
|
10702
|
+
const excerpt = (s, n) => s.length <= n ? s : `${s.slice(0, n)}\u2026`;
|
|
10703
|
+
const body = {
|
|
10704
|
+
model,
|
|
10705
|
+
max_tokens: 64,
|
|
10706
|
+
messages: [
|
|
10707
|
+
{
|
|
10708
|
+
role: "system",
|
|
10709
|
+
content: "You write short prosaic titles for agent sessions. Reply with ONLY the title: 4-8 plain words, no quotes, no punctuation at the end, describing what the session is doing."
|
|
10710
|
+
},
|
|
10711
|
+
{
|
|
10712
|
+
role: "user",
|
|
10713
|
+
content: `User asked: ${excerpt(input.userText, 2e3)}
|
|
10714
|
+
Assistant worked on: ${excerpt(input.assistantText, 2e3)}
|
|
10715
|
+
Title:`
|
|
10716
|
+
}
|
|
10717
|
+
]
|
|
10718
|
+
};
|
|
10719
|
+
try {
|
|
10720
|
+
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
10721
|
+
method: "POST",
|
|
10722
|
+
headers: {
|
|
10723
|
+
"content-type": "application/json",
|
|
10724
|
+
authorization: `Bearer ${apiKey}`
|
|
10725
|
+
},
|
|
10726
|
+
body: JSON.stringify(body),
|
|
10727
|
+
signal: AbortSignal.timeout(15e3)
|
|
10728
|
+
});
|
|
10729
|
+
if (!res.ok) return void 0;
|
|
10730
|
+
const parsed = await res.json();
|
|
10731
|
+
const content = parsed.choices?.[0]?.message?.content;
|
|
10732
|
+
return typeof content === "string" ? content : void 0;
|
|
10733
|
+
} catch {
|
|
10734
|
+
return void 0;
|
|
10735
|
+
}
|
|
10736
|
+
}
|
|
10737
|
+
async function maybeTitleSession(registry, sessionId, opts = {}) {
|
|
10738
|
+
try {
|
|
10739
|
+
if (titledSessions.has(sessionId)) return null;
|
|
10740
|
+
titledSessions.add(sessionId);
|
|
10741
|
+
const desc = registry.get(sessionId);
|
|
10742
|
+
if (!desc || desc.kind !== "agent-cli") return null;
|
|
10743
|
+
if (desc.renamedByUser === true) return null;
|
|
10744
|
+
if (!isDefaultLabel(desc)) return null;
|
|
10745
|
+
const generate = opts.generate ?? (async (t) => {
|
|
10746
|
+
const { enabled, model } = await resolveTitlerConfig();
|
|
10747
|
+
if (!enabled) return void 0;
|
|
10748
|
+
return generateTitleViaLlm(model, t);
|
|
10749
|
+
});
|
|
10750
|
+
if (!opts.generate) {
|
|
10751
|
+
const { enabled } = await resolveTitlerConfig();
|
|
10752
|
+
if (!enabled) return null;
|
|
10753
|
+
}
|
|
10754
|
+
const read = opts.readFirstTurn ?? readFirstTurn;
|
|
10755
|
+
const turn = await read(sessionId);
|
|
10756
|
+
if (!turn) return null;
|
|
10757
|
+
const minChars = opts.minChars ?? MIN_TURN_CHARS;
|
|
10758
|
+
if (turn.userText.length + turn.assistantText.length < minChars) return null;
|
|
10759
|
+
const generated = sanitizeTitle(await generate(turn));
|
|
10760
|
+
const title = generated ?? fallbackTitle(turn.userText);
|
|
10761
|
+
if (!title) return null;
|
|
10762
|
+
registry.renameSession(sessionId, { label: title });
|
|
10763
|
+
return title;
|
|
10764
|
+
} catch (err) {
|
|
10765
|
+
console.warn(
|
|
10766
|
+
`[session-titler] maybeTitleSession(${sessionId}) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
10767
|
+
);
|
|
10768
|
+
return null;
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
|
|
10435
10772
|
// src/resume-context-digest.ts
|
|
10436
10773
|
init_transcript_export();
|
|
10437
10774
|
var DIGEST_CHAR_BUDGET = 7e3;
|
|
@@ -12712,6 +13049,9 @@ ${message}`;
|
|
|
12712
13049
|
});
|
|
12713
13050
|
}
|
|
12714
13051
|
if (overBudget) emitExited(rt);
|
|
13052
|
+
if ((rt.desc.turnsCompleted ?? 0) === 1) {
|
|
13053
|
+
void maybeTitleSession(registry, rt.desc.id);
|
|
13054
|
+
}
|
|
12715
13055
|
} else {
|
|
12716
13056
|
rt.desc.turnsCompleted = (rt.desc.turnsCompleted ?? 0) + 1;
|
|
12717
13057
|
if (rt.desc.lastTurnEmpty !== void 0) delete rt.desc.lastTurnEmpty;
|
|
@@ -13221,7 +13561,7 @@ ${message}`;
|
|
|
13221
13561
|
const resumeArgvId = (() => {
|
|
13222
13562
|
if (!conversationStore) return void 0;
|
|
13223
13563
|
for (let i = 0; i < input.argv.length - 1; i++) {
|
|
13224
|
-
if (input.argv[i] !== "--resume" && input.argv[i] !== "-r") continue;
|
|
13564
|
+
if (input.argv[i] !== "--resume" && input.argv[i] !== "-r" && !(conversationSlug === "opencode" && (input.argv[i] === "--session" || input.argv[i] === "-s"))) continue;
|
|
13225
13565
|
const raw = input.argv[i + 1];
|
|
13226
13566
|
const base = raw.slice(raw.lastIndexOf("/") + 1);
|
|
13227
13567
|
return base.endsWith(".jsonl") ? base.slice(0, -".jsonl".length) : raw;
|
|
@@ -16671,6 +17011,10 @@ var mcpBool2 = z.preprocess(
|
|
|
16671
17011
|
(v) => v === "true" ? true : v === "false" ? false : v,
|
|
16672
17012
|
z.boolean()
|
|
16673
17013
|
);
|
|
17014
|
+
var publicSessionDescriptor = (session) => {
|
|
17015
|
+
const { ptyResumeEnv: _privateResumeEnv, ...publicDescriptor } = session;
|
|
17016
|
+
return publicDescriptor;
|
|
17017
|
+
};
|
|
16674
17018
|
var compactSessionItem = (s) => ({
|
|
16675
17019
|
id: s.id,
|
|
16676
17020
|
kind: s.kind,
|
|
@@ -16807,7 +17151,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
16807
17151
|
(s) => s.status === "running" || s.status === "starting"
|
|
16808
17152
|
);
|
|
16809
17153
|
}
|
|
16810
|
-
return rows;
|
|
17154
|
+
return rows.map(publicSessionDescriptor);
|
|
16811
17155
|
},
|
|
16812
17156
|
transformers: [
|
|
16813
17157
|
paginated({
|
|
@@ -17264,7 +17608,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
17264
17608
|
(s) => s.status === "running" || s.status === "starting"
|
|
17265
17609
|
);
|
|
17266
17610
|
}
|
|
17267
|
-
return rows;
|
|
17611
|
+
return rows.map(publicSessionDescriptor);
|
|
17268
17612
|
},
|
|
17269
17613
|
project: compactSessionItemWithProvenance,
|
|
17270
17614
|
keyOf: (s) => s.id,
|
|
@@ -17299,7 +17643,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
17299
17643
|
(s) => s.status === "running" || s.status === "starting"
|
|
17300
17644
|
);
|
|
17301
17645
|
}
|
|
17302
|
-
return rows;
|
|
17646
|
+
return rows.map(publicSessionDescriptor);
|
|
17303
17647
|
},
|
|
17304
17648
|
project: compactSessionItemWithProvenance,
|
|
17305
17649
|
keyOf: (s) => s.id,
|
|
@@ -18213,7 +18557,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18213
18557
|
content: [
|
|
18214
18558
|
{
|
|
18215
18559
|
type: "text",
|
|
18216
|
-
text: JSON.stringify(desc)
|
|
18560
|
+
text: JSON.stringify(publicSessionDescriptor(desc))
|
|
18217
18561
|
}
|
|
18218
18562
|
]
|
|
18219
18563
|
};
|
|
@@ -18258,7 +18602,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18258
18602
|
type: "text",
|
|
18259
18603
|
text: JSON.stringify(
|
|
18260
18604
|
{
|
|
18261
|
-
...restarted.desc,
|
|
18605
|
+
...publicSessionDescriptor(restarted.desc),
|
|
18262
18606
|
resumedFrom: restarted.resumedFrom,
|
|
18263
18607
|
resumeVia: restarted.resumeVia,
|
|
18264
18608
|
...restarted.resumeFallback ? { resumeFallback: true } : {},
|
|
@@ -18329,7 +18673,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18329
18673
|
try {
|
|
18330
18674
|
const desc = registry.archiveSession(prev.id);
|
|
18331
18675
|
return {
|
|
18332
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
18676
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18333
18677
|
};
|
|
18334
18678
|
} catch (err) {
|
|
18335
18679
|
return {
|
|
@@ -18411,7 +18755,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18411
18755
|
try {
|
|
18412
18756
|
const desc = registry.unarchiveSession(prev.id);
|
|
18413
18757
|
return {
|
|
18414
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
18758
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18415
18759
|
};
|
|
18416
18760
|
} catch (err) {
|
|
18417
18761
|
return {
|
|
@@ -18496,7 +18840,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18496
18840
|
reason: input.reason
|
|
18497
18841
|
});
|
|
18498
18842
|
return {
|
|
18499
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
18843
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18500
18844
|
};
|
|
18501
18845
|
} catch (err) {
|
|
18502
18846
|
return {
|
|
@@ -18580,7 +18924,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18580
18924
|
...input.label !== void 0 ? { label: input.label } : {}
|
|
18581
18925
|
});
|
|
18582
18926
|
return {
|
|
18583
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
18927
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18584
18928
|
};
|
|
18585
18929
|
} catch (err) {
|
|
18586
18930
|
return {
|
|
@@ -18642,7 +18986,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18642
18986
|
try {
|
|
18643
18987
|
const desc = registry.setKeepAlive(prev.id, input.keepAlive);
|
|
18644
18988
|
return {
|
|
18645
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
18989
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18646
18990
|
};
|
|
18647
18991
|
} catch (err) {
|
|
18648
18992
|
return {
|
|
@@ -18704,7 +19048,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18704
19048
|
try {
|
|
18705
19049
|
const desc = registry.setPinned(prev.id, input.pinned);
|
|
18706
19050
|
return {
|
|
18707
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
19051
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18708
19052
|
};
|
|
18709
19053
|
} catch (err) {
|
|
18710
19054
|
return {
|
|
@@ -18820,7 +19164,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
18820
19164
|
} : {}
|
|
18821
19165
|
});
|
|
18822
19166
|
return {
|
|
18823
|
-
content: [{ type: "text", text: JSON.stringify(desc) }]
|
|
19167
|
+
content: [{ type: "text", text: JSON.stringify(publicSessionDescriptor(desc)) }]
|
|
18824
19168
|
};
|
|
18825
19169
|
} catch (err) {
|
|
18826
19170
|
return {
|
|
@@ -20640,6 +20984,7 @@ function registerMcpApps(server, apps) {
|
|
|
20640
20984
|
}
|
|
20641
20985
|
}
|
|
20642
20986
|
function makeBuiltinPanelApps(ops) {
|
|
20987
|
+
const sessionChatNativeInstalled = ops.isSessionChatInstalled();
|
|
20643
20988
|
return [
|
|
20644
20989
|
makeSessionsPanelApp({ listSessions: ops.listSessions }),
|
|
20645
20990
|
makeAgentsOverviewApp({ listSessions: ops.listSessions }),
|
|
@@ -20649,17 +20994,26 @@ function makeBuiltinPanelApps(ops) {
|
|
|
20649
20994
|
// `agent_start` via _meta.ui.resourceUri (agent-tools.ts) so a launch
|
|
20650
20995
|
// auto-renders it.
|
|
20651
20996
|
makeLiveSessionApp({ httpBaseUrl: ops.httpBaseUrl }),
|
|
20652
|
-
// Session-chat widget — thin launcher for the installed
|
|
20997
|
+
// Session-chat widget — thin loopback-HTTP launcher for the installed
|
|
20653
20998
|
// `@agentik/session-chat` app's standalone UI (deep-linked iframe when
|
|
20654
20999
|
// installed, install notice otherwise; see apps/src/session-chat).
|
|
20655
|
-
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
|
|
20662
|
-
|
|
21000
|
+
// Mounted ONLY when the native app isn't installed: once it is, its own
|
|
21001
|
+
// MCP Apps tool (`app_ui_session_chat`, app-ui-apps.ts) is the one and
|
|
21002
|
+
// only launch path — a strict-CSP host (Codex) can't fetch this
|
|
21003
|
+
// widget's loopback HTTP calls, so offering both would just add a
|
|
21004
|
+
// broken option next to the working one. `agent_start`'s launch-card
|
|
21005
|
+
// binding (agent-tools.ts) makes the matching choice.
|
|
21006
|
+
...sessionChatNativeInstalled ? [] : [
|
|
21007
|
+
makeSessionChatApp({
|
|
21008
|
+
httpBaseUrl: ops.httpBaseUrl,
|
|
21009
|
+
isSessionChatInstalled: ops.isSessionChatInstalled,
|
|
21010
|
+
// Hand every result a live embed token so a host-cached widget
|
|
21011
|
+
// (Claude Desktop's per-conversation srcdoc) re-arms itself
|
|
21012
|
+
// after a daemon restart instead of 403ing on its dead baked
|
|
21013
|
+
// token.
|
|
21014
|
+
mintEmbedToken: () => stableAppEmbedToken("agentproto_session_chat")
|
|
21015
|
+
})
|
|
21016
|
+
],
|
|
20663
21017
|
// Work-board widget — kanban over the Task ledger (see apps/src/
|
|
20664
21018
|
// work-board). Read path only; writes go through task_claim/
|
|
20665
21019
|
// task_update/task_create over the bridge, same as every other caller.
|
|
@@ -20712,6 +21066,39 @@ function builtinPanelCatalogEntries() {
|
|
|
20712
21066
|
};
|
|
20713
21067
|
});
|
|
20714
21068
|
}
|
|
21069
|
+
|
|
21070
|
+
// src/public-origins.ts
|
|
21071
|
+
var HTTP_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
21072
|
+
var WS_PROTOCOLS = /* @__PURE__ */ new Set(["ws:", "wss:"]);
|
|
21073
|
+
function normalizeOrigin(value, protocols) {
|
|
21074
|
+
const candidate = value?.trim().replace(/\/+$/, "");
|
|
21075
|
+
if (!candidate) return void 0;
|
|
21076
|
+
try {
|
|
21077
|
+
const url = new URL(candidate);
|
|
21078
|
+
if (!protocols.has(url.protocol)) return void 0;
|
|
21079
|
+
if (url.username || url.password || url.search || url.hash) return void 0;
|
|
21080
|
+
if (url.pathname !== "/") return void 0;
|
|
21081
|
+
return url.origin;
|
|
21082
|
+
} catch {
|
|
21083
|
+
return void 0;
|
|
21084
|
+
}
|
|
21085
|
+
}
|
|
21086
|
+
function resolveRequestHttpBaseUrl(headers, configuredOrigin = process.env.AGENTPROTO_PUBLIC_HTTP_ORIGIN) {
|
|
21087
|
+
const configured = normalizeOrigin(configuredOrigin, HTTP_PROTOCOLS);
|
|
21088
|
+
if (configured) return configured;
|
|
21089
|
+
const forwarded = headers["x-forwarded-proto"];
|
|
21090
|
+
const firstForwarded = (Array.isArray(forwarded) ? forwarded[0] : forwarded)?.split(",")[0]?.trim().toLowerCase();
|
|
21091
|
+
const protocol = firstForwarded === "https" ? "https" : "http";
|
|
21092
|
+
return normalizeOrigin(`${protocol}://${headers.host ?? "127.0.0.1"}`, HTTP_PROTOCOLS) ?? `${protocol}://127.0.0.1`;
|
|
21093
|
+
}
|
|
21094
|
+
function resolvePublicAppOrigins(port, configuredHttpOrigin = process.env.AGENTPROTO_PUBLIC_HTTP_ORIGIN, configuredWsOrigin = process.env.AGENTPROTO_PUBLIC_WS_ORIGIN) {
|
|
21095
|
+
const httpOrigin = normalizeOrigin(configuredHttpOrigin, HTTP_PROTOCOLS) ?? `http://127.0.0.1:${port}`;
|
|
21096
|
+
const configuredWs = normalizeOrigin(configuredWsOrigin, WS_PROTOCOLS);
|
|
21097
|
+
if (configuredWs) return { httpOrigin, wsOrigin: configuredWs };
|
|
21098
|
+
const http = new URL(httpOrigin);
|
|
21099
|
+
http.protocol = http.protocol === "https:" ? "wss:" : "ws:";
|
|
21100
|
+
return { httpOrigin, wsOrigin: http.origin };
|
|
21101
|
+
}
|
|
20715
21102
|
function summarizeSession(desc, lines, nowMs) {
|
|
20716
21103
|
return {
|
|
20717
21104
|
sessionId: desc.id,
|
|
@@ -23138,172 +23525,6 @@ function registerAppTools(server, opts) {
|
|
|
23138
23525
|
}
|
|
23139
23526
|
);
|
|
23140
23527
|
}
|
|
23141
|
-
function appUiToolId(appId) {
|
|
23142
|
-
const slug = appId.replace(/^@[^/]+\//, "").replace(/[^a-z0-9]/g, "_");
|
|
23143
|
-
return `app_ui_${slug}`;
|
|
23144
|
-
}
|
|
23145
|
-
var MCP_APP_BRIDGE_SCRIPT = `<script>
|
|
23146
|
-
(function () {
|
|
23147
|
-
if (window.McpApp) return;
|
|
23148
|
-
var nextId = 1, pending = {}, teardownCbs = [];
|
|
23149
|
-
function post(m) { window.parent.postMessage(m, "*"); }
|
|
23150
|
-
window.addEventListener("message", function (e) {
|
|
23151
|
-
var m = e.data;
|
|
23152
|
-
if (typeof m === "string") { try { m = JSON.parse(m); } catch (_) { return; } }
|
|
23153
|
-
if (!m || m.jsonrpc !== "2.0") return;
|
|
23154
|
-
if (m.method === "ui/resource-teardown") {
|
|
23155
|
-
for (var i = 0; i < teardownCbs.length; i++) teardownCbs[i]();
|
|
23156
|
-
post({ jsonrpc: "2.0", id: m.id, result: {} });
|
|
23157
|
-
return;
|
|
23158
|
-
}
|
|
23159
|
-
if (m.id != null && pending[m.id]) { var cb = pending[m.id]; delete pending[m.id]; cb(m.result, m.error); }
|
|
23160
|
-
});
|
|
23161
|
-
function request(method, params) {
|
|
23162
|
-
return new Promise(function (resolve, reject) {
|
|
23163
|
-
var id = nextId++;
|
|
23164
|
-
pending[id] = function (r, err) { if (err) reject(new Error((err && err.message) || String(err))); else resolve(r); };
|
|
23165
|
-
post({ jsonrpc: "2.0", id: id, method: method, params: params });
|
|
23166
|
-
});
|
|
23167
|
-
}
|
|
23168
|
-
window.McpApp = {
|
|
23169
|
-
connect: function () {
|
|
23170
|
-
if (window.parent === window) return Promise.reject(new Error("no host (standalone)"));
|
|
23171
|
-
return request("ui/initialize", {
|
|
23172
|
-
appInfo: { name: "agentproto-app", version: "1.0" },
|
|
23173
|
-
appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
|
|
23174
|
-
protocolVersion: "2026-01-26"
|
|
23175
|
-
}).then(function () {
|
|
23176
|
-
post({ jsonrpc: "2.0", method: "ui/notifications/initialized", params: {} });
|
|
23177
|
-
return {
|
|
23178
|
-
callTool: function (name, args) { return request("tools/call", { name: name, arguments: args || {} }); },
|
|
23179
|
-
sendMessage: function (p) {
|
|
23180
|
-
var params = typeof p === "string" ? { content: p } : (p || {});
|
|
23181
|
-
var content = typeof params.content === "string" ? [{ type: "text", text: params.content }] : (params.content || []);
|
|
23182
|
-
return request("ui/message", { role: "user", content: content });
|
|
23183
|
-
},
|
|
23184
|
-
updateModelContext: function (p) { return request("ui/update-model-context", p || {}); },
|
|
23185
|
-
openLink: function (url) { return request("ui/open-link", { url: url }); },
|
|
23186
|
-
onTeardown: function (cb) { teardownCbs.push(cb); }
|
|
23187
|
-
};
|
|
23188
|
-
});
|
|
23189
|
-
}
|
|
23190
|
-
};
|
|
23191
|
-
})();
|
|
23192
|
-
</script>
|
|
23193
|
-
`;
|
|
23194
|
-
function injectAfterStructuralTag(html, script) {
|
|
23195
|
-
for (const tag of ["head", "body", "html"]) {
|
|
23196
|
-
const match = html.match(new RegExp(`<${tag}[^>]*>`, "i"));
|
|
23197
|
-
if (match?.index !== void 0) {
|
|
23198
|
-
const insertAt = match.index + match[0].length;
|
|
23199
|
-
return html.slice(0, insertAt) + script + html.slice(insertAt);
|
|
23200
|
-
}
|
|
23201
|
-
}
|
|
23202
|
-
return script + html;
|
|
23203
|
-
}
|
|
23204
|
-
function injectMcpAppBridge(html) {
|
|
23205
|
-
const hasBridge = /window\.McpApp\s*=/.test(html);
|
|
23206
|
-
const hasRunnerSelect = /window\.AgentprotoUI\s*=/.test(html);
|
|
23207
|
-
if (hasBridge && hasRunnerSelect) return html;
|
|
23208
|
-
const script = (hasBridge ? "" : MCP_APP_BRIDGE_SCRIPT) + (hasRunnerSelect ? "" : RUNNER_SELECT_SCRIPT);
|
|
23209
|
-
return injectAfterStructuralTag(html, script);
|
|
23210
|
-
}
|
|
23211
|
-
var STANDALONE_REST_BRIDGE_SCRIPT = `<script>
|
|
23212
|
-
(function () {
|
|
23213
|
-
if (window.McpApp) return;
|
|
23214
|
-
window.McpApp = {
|
|
23215
|
-
connect: function () {
|
|
23216
|
-
return Promise.resolve({
|
|
23217
|
-
callTool: function (name, args) {
|
|
23218
|
-
return fetch("./tool-call", {
|
|
23219
|
-
method: "POST",
|
|
23220
|
-
headers: { "content-type": "application/json" },
|
|
23221
|
-
body: JSON.stringify({ tool: name, args: args || {} })
|
|
23222
|
-
}).then(function (res) {
|
|
23223
|
-
return res.json().catch(function () {
|
|
23224
|
-
throw new Error("tool-call failed: HTTP " + res.status);
|
|
23225
|
-
}).then(function (body) {
|
|
23226
|
-
if (!res.ok) throw new Error((body && (body.message || body.error)) || ("tool-call failed: HTTP " + res.status));
|
|
23227
|
-
return body;
|
|
23228
|
-
});
|
|
23229
|
-
});
|
|
23230
|
-
},
|
|
23231
|
-
sendMessage: function () {
|
|
23232
|
-
return Promise.reject(new Error("sendMessage: no chat host (standalone mode)"));
|
|
23233
|
-
},
|
|
23234
|
-
updateModelContext: function () {
|
|
23235
|
-
return Promise.reject(new Error("updateModelContext: no host (standalone mode)"));
|
|
23236
|
-
},
|
|
23237
|
-
openLink: function (url) {
|
|
23238
|
-
window.open(url, "_blank");
|
|
23239
|
-
return Promise.resolve({});
|
|
23240
|
-
},
|
|
23241
|
-
onTeardown: function () {}
|
|
23242
|
-
});
|
|
23243
|
-
}
|
|
23244
|
-
};
|
|
23245
|
-
})();
|
|
23246
|
-
</script>
|
|
23247
|
-
`;
|
|
23248
|
-
function injectStandaloneAppBridge(html) {
|
|
23249
|
-
const hasRunnerSelect = /window\.AgentprotoUI\s*=/.test(html);
|
|
23250
|
-
const script = STANDALONE_REST_BRIDGE_SCRIPT + (hasRunnerSelect ? "" : RUNNER_SELECT_SCRIPT);
|
|
23251
|
-
return injectAfterStructuralTag(html, script);
|
|
23252
|
-
}
|
|
23253
|
-
function createUiHtmlCache() {
|
|
23254
|
-
const cache = /* @__PURE__ */ new Map();
|
|
23255
|
-
return {
|
|
23256
|
-
async get(path, version) {
|
|
23257
|
-
const cached = cache.get(path);
|
|
23258
|
-
if (cached && cached.version === version) return cached.html;
|
|
23259
|
-
const raw = await readFile(path, "utf8");
|
|
23260
|
-
const html = injectMcpAppBridge(raw);
|
|
23261
|
-
cache.set(path, { version, html });
|
|
23262
|
-
return html;
|
|
23263
|
-
}
|
|
23264
|
-
};
|
|
23265
|
-
}
|
|
23266
|
-
async function makeInstalledAppUiApps(appRegistry, cache, existingToolNames) {
|
|
23267
|
-
const apps = [];
|
|
23268
|
-
const seen = new Set(existingToolNames);
|
|
23269
|
-
for (const app of appRegistry.listApps()) {
|
|
23270
|
-
const ui = app.ui;
|
|
23271
|
-
if (!ui) continue;
|
|
23272
|
-
const toolId = appUiToolId(app.appId);
|
|
23273
|
-
if (seen.has(toolId)) {
|
|
23274
|
-
console.warn(
|
|
23275
|
-
`[app-ui-apps] skipping UI panel for app "${app.appId}": tool id "${toolId}" collides with an existing tool or another installed app's panel.`
|
|
23276
|
-
);
|
|
23277
|
-
continue;
|
|
23278
|
-
}
|
|
23279
|
-
let html;
|
|
23280
|
-
try {
|
|
23281
|
-
html = await cache.get(ui.path, app.updatedAt);
|
|
23282
|
-
} catch (err) {
|
|
23283
|
-
console.warn(
|
|
23284
|
-
`[app-ui-apps] skipping UI panel for app "${app.appId}": could not read "${ui.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
23285
|
-
);
|
|
23286
|
-
continue;
|
|
23287
|
-
}
|
|
23288
|
-
seen.add(toolId);
|
|
23289
|
-
apps.push({
|
|
23290
|
-
id: toolId,
|
|
23291
|
-
title: ui.title ?? app.name ?? app.appId,
|
|
23292
|
-
...ui.description ? { description: ui.description } : {},
|
|
23293
|
-
inputSchema: z.object({}),
|
|
23294
|
-
execute: async () => ({ appId: app.appId, tools: ui.tools ?? [] }),
|
|
23295
|
-
html,
|
|
23296
|
-
...ui.csp ? {
|
|
23297
|
-
csp: {
|
|
23298
|
-
...ui.csp.connectDomains ? { connectDomains: [...ui.csp.connectDomains] } : {},
|
|
23299
|
-
...ui.csp.resourceDomains ? { resourceDomains: [...ui.csp.resourceDomains] } : {},
|
|
23300
|
-
...ui.csp.frameDomains ? { frameDomains: [...ui.csp.frameDomains] } : {}
|
|
23301
|
-
}
|
|
23302
|
-
} : {}
|
|
23303
|
-
});
|
|
23304
|
-
}
|
|
23305
|
-
return apps;
|
|
23306
|
-
}
|
|
23307
23528
|
function paginatedLegacyList3(opts) {
|
|
23308
23529
|
return {
|
|
23309
23530
|
name: "paginatedLegacyList",
|
|
@@ -27070,6 +27291,10 @@ function registerPresetTools(server, opts = {}) {
|
|
|
27070
27291
|
lister: async () => listPresets(process.env, adapterPresets)
|
|
27071
27292
|
});
|
|
27072
27293
|
}
|
|
27294
|
+
function sessionDescriptorForHttp(session) {
|
|
27295
|
+
const { ptyResumeEnv: _privateResumeEnv, ...publicDescriptor } = session;
|
|
27296
|
+
return publicDescriptor;
|
|
27297
|
+
}
|
|
27073
27298
|
var DEFAULT_ALLOWED_ORIGINS = [
|
|
27074
27299
|
"http://localhost:*",
|
|
27075
27300
|
"http://127.0.0.1:*",
|
|
@@ -29197,7 +29422,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29197
29422
|
} else if (!includeCommands) {
|
|
29198
29423
|
rows = rows.filter((s) => s.kind !== "command");
|
|
29199
29424
|
}
|
|
29200
|
-
json(200, { sessions: rows });
|
|
29425
|
+
json(200, { sessions: rows.map(sessionDescriptorForHttp) });
|
|
29201
29426
|
return true;
|
|
29202
29427
|
}
|
|
29203
29428
|
if (path === "/sessions/summaries" && req.method === "GET") {
|
|
@@ -29287,7 +29512,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29287
29512
|
return true;
|
|
29288
29513
|
}
|
|
29289
29514
|
json(201, {
|
|
29290
|
-
...result.descriptor,
|
|
29515
|
+
...sessionDescriptorForHttp(result.descriptor),
|
|
29291
29516
|
...result.warnings ? { warnings: result.warnings } : {},
|
|
29292
29517
|
...result.deduped ? { deduped: true } : {},
|
|
29293
29518
|
...result.dedupeSource ? { dedupeSource: result.dedupeSource } : {}
|
|
@@ -29503,7 +29728,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29503
29728
|
...typeof b.name === "string" ? { name: b.name } : {},
|
|
29504
29729
|
...typeof b.label === "string" ? { label: b.label } : {}
|
|
29505
29730
|
});
|
|
29506
|
-
json(201, desc);
|
|
29731
|
+
json(201, sessionDescriptorForHttp(desc));
|
|
29507
29732
|
} catch (err) {
|
|
29508
29733
|
const msg = err instanceof Error ? err.message : String(err);
|
|
29509
29734
|
const status = msg.includes("already in use") ? 409 : msg.includes("no PTY factory") ? 501 : 500;
|
|
@@ -29742,7 +29967,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29742
29967
|
...listCatalogModels ? { listCatalogModels } : {}
|
|
29743
29968
|
});
|
|
29744
29969
|
json(200, {
|
|
29745
|
-
...restarted.desc,
|
|
29970
|
+
...sessionDescriptorForHttp(restarted.desc),
|
|
29746
29971
|
resumedFrom: restarted.resumedFrom,
|
|
29747
29972
|
resumeVia: restarted.resumeVia,
|
|
29748
29973
|
...restarted.resumeFallback ? { resumeFallback: true } : {}
|
|
@@ -29817,7 +30042,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29817
30042
|
};
|
|
29818
30043
|
try {
|
|
29819
30044
|
const desc = registry.renameSession(resolved.id, patch);
|
|
29820
|
-
json(200, desc);
|
|
30045
|
+
json(200, sessionDescriptorForHttp(desc));
|
|
29821
30046
|
} catch (err) {
|
|
29822
30047
|
const msg = err instanceof Error ? err.message : String(err);
|
|
29823
30048
|
json(msg.includes("no session") ? 404 : 500, { error: "rename_failed", message: msg });
|
|
@@ -29868,7 +30093,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
29868
30093
|
env: b.env && typeof b.env === "object" ? b.env : void 0,
|
|
29869
30094
|
label: typeof b.label === "string" ? b.label : void 0
|
|
29870
30095
|
});
|
|
29871
|
-
json(201, desc);
|
|
30096
|
+
json(201, sessionDescriptorForHttp(desc));
|
|
29872
30097
|
} catch (err) {
|
|
29873
30098
|
json(500, {
|
|
29874
30099
|
error: "spawn_failed",
|
|
@@ -30251,7 +30476,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
30251
30476
|
json(404, { error: "session_not_found", id: rawIdOrName });
|
|
30252
30477
|
return true;
|
|
30253
30478
|
}
|
|
30254
|
-
json(200, resolvedDesc);
|
|
30479
|
+
json(200, sessionDescriptorForHttp(resolvedDesc));
|
|
30255
30480
|
return true;
|
|
30256
30481
|
}
|
|
30257
30482
|
if (!suffix && req.method === "DELETE") {
|
|
@@ -31163,7 +31388,7 @@ async function handleProviderInbound(req, res, slug, deps2) {
|
|
|
31163
31388
|
res.end(JSON.stringify(result));
|
|
31164
31389
|
}
|
|
31165
31390
|
function requestHttpBaseUrl(req) {
|
|
31166
|
-
return
|
|
31391
|
+
return resolveRequestHttpBaseUrl(req.headers);
|
|
31167
31392
|
}
|
|
31168
31393
|
async function handleAppUiPage(req, res, appId, appRegistry, frameAncestors) {
|
|
31169
31394
|
const app = appRegistry.getApp(appId);
|
|
@@ -31211,7 +31436,7 @@ async function handleAppUiPage(req, res, appId, appRegistry, frameAncestors) {
|
|
|
31211
31436
|
}
|
|
31212
31437
|
}
|
|
31213
31438
|
res.writeHead(200, headers);
|
|
31214
|
-
res.end(injectStandaloneAppBridge(raw));
|
|
31439
|
+
res.end(injectStandaloneAppBridge(raw, requestHttpBaseUrl(req)));
|
|
31215
31440
|
}
|
|
31216
31441
|
function iframeEmbedAllowed(req, app) {
|
|
31217
31442
|
const secFetchDest = req.headers["sec-fetch-dest"];
|
|
@@ -38326,7 +38551,7 @@ async function createGateway(opts) {
|
|
|
38326
38551
|
const port = opts.port ?? 18790;
|
|
38327
38552
|
const daemonMcpUrl = `http://127.0.0.1:${port}/mcp`;
|
|
38328
38553
|
const resolveSandboxProviderResolved = opts.resolveSandboxProvider ?? makeSandboxResolver(makeSandboxCredsStore());
|
|
38329
|
-
const ptyWsBaseUrl =
|
|
38554
|
+
const { httpOrigin: publicHttpOrigin, wsOrigin: ptyWsBaseUrl } = resolvePublicAppOrigins(port);
|
|
38330
38555
|
const events = createRuntimeEvents();
|
|
38331
38556
|
const conversations = fileConversationStore({ workspace });
|
|
38332
38557
|
const workspaceFs = createWorkspaceFs({ workspace });
|
|
@@ -38516,6 +38741,13 @@ async function createGateway(opts) {
|
|
|
38516
38741
|
dispatchTool
|
|
38517
38742
|
});
|
|
38518
38743
|
const appRegistry = createAppRegistry({ persist });
|
|
38744
|
+
const isSessionChatInstalled = () => {
|
|
38745
|
+
try {
|
|
38746
|
+
return appRegistry.getApp(SESSION_CHAT_APP_ID)?.ui != null;
|
|
38747
|
+
} catch {
|
|
38748
|
+
return false;
|
|
38749
|
+
}
|
|
38750
|
+
};
|
|
38519
38751
|
const appUiHtmlCache = createUiHtmlCache();
|
|
38520
38752
|
const workflowRunner = opts.resolveAgentAdapter ? createWorkflowRunner({
|
|
38521
38753
|
registry: sessions,
|
|
@@ -38717,7 +38949,8 @@ async function createGateway(opts) {
|
|
|
38717
38949
|
...opts.installAgentAdapter ? { installAgentAdapter: opts.installAgentAdapter } : {},
|
|
38718
38950
|
...opts.listCatalogModels ? { listCatalogModels: opts.listCatalogModels } : {},
|
|
38719
38951
|
...opts.listWorktreeStatuses ? { listWorktreeStatuses: opts.listWorktreeStatuses } : {},
|
|
38720
|
-
...opts.runWorktreeGc ? { runWorktreeGc: opts.runWorktreeGc } : {}
|
|
38952
|
+
...opts.runWorktreeGc ? { runWorktreeGc: opts.runWorktreeGc } : {},
|
|
38953
|
+
isSessionChatInstalled
|
|
38721
38954
|
});
|
|
38722
38955
|
registerBrainTools(server, {
|
|
38723
38956
|
brains: workspaceBrains,
|
|
@@ -38774,18 +39007,12 @@ async function createGateway(opts) {
|
|
|
38774
39007
|
listSessions: listSessionsFiltered,
|
|
38775
39008
|
// httpBaseUrl = this daemon's own origin (SSE stream + bridge
|
|
38776
39009
|
// fallback for the live-session widget).
|
|
38777
|
-
httpBaseUrl:
|
|
39010
|
+
httpBaseUrl: publicHttpOrigin,
|
|
38778
39011
|
// The session-chat widget is a thin launcher for the installed
|
|
38779
|
-
// `@agentik/session-chat` studio app —
|
|
38780
|
-
//
|
|
38781
|
-
// `
|
|
38782
|
-
isSessionChatInstalled
|
|
38783
|
-
try {
|
|
38784
|
-
return appRegistry.getApp(SESSION_CHAT_APP_ID)?.ui != null;
|
|
38785
|
-
} catch {
|
|
38786
|
-
return false;
|
|
38787
|
-
}
|
|
38788
|
-
},
|
|
39012
|
+
// `@agentik/session-chat` studio app — same call-time-resolved check
|
|
39013
|
+
// `registerSessionTools` above gets, so the mount decision here and
|
|
39014
|
+
// `agent_start`'s launch-card binding never disagree.
|
|
39015
|
+
isSessionChatInstalled,
|
|
38789
39016
|
// Work-board widget's read path — the root `/mcp` endpoint has no
|
|
38790
39017
|
// scope, so this mount is always the operator caller (default
|
|
38791
39018
|
// board `ws:<slug>`); `canAccessBoard` lets the operator read any
|