@agentproto/runtime 5.13.0 → 5.15.0
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/app-mcp-server.d.ts +69 -0
- package/dist/app-mcp-server.mjs +206 -0
- package/dist/app-mcp-server.mjs.map +1 -0
- package/dist/config-schema.mjs +12 -8
- package/dist/config-schema.mjs.map +1 -1
- package/dist/config.mjs +12 -8
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +533 -225
- package/dist/index.mjs +1849 -390
- package/dist/index.mjs.map +1 -1
- package/dist/resume-strategies.mjs +12 -8
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/session-presence.mjs +12 -8
- package/dist/session-presence.mjs.map +1 -1
- package/dist/{sessions-DtkidPeI.d.ts → sessions-Dq4mAphj.d.ts} +2 -0
- package/dist/user-presets.d.ts +1 -1
- package/docs/mcp-tools/agent_start.md +11 -0
- package/docs/mcp-tools/session_list.md +66 -0
- package/package.json +30 -25
package/dist/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { ensureChromeDevtoolsMcp, resolveChrome, buildHeadlessBrowserMcpEntry, h
|
|
|
7
7
|
import { selectPricingTier, getModelProvider, resolveContextWindow, formatTokens, splitContextWindowHint, resolvePricing, LLM_PRICING_CATALOG, resolvePricingExact, MODEL_ALIASES, isKnownLlmId } from '@agentproto/model-catalog/llm';
|
|
8
8
|
import { resolveCustomRoute, registerCustomRoute, tryParseModelRef, resolveLlmModelRoute, formatModelRef, stripRouteSuffix, stripFixedNativeVendor } from '@agentproto/model-catalog/route-identity';
|
|
9
9
|
import { randomBytes, createHmac, randomUUID, timingSafeEqual, createHash } from 'crypto';
|
|
10
|
-
import { createReadStream, promises, readFileSync, mkdirSync, writeFileSync, renameSync, readdirSync, openSync, fstatSync, readSync, closeSync, existsSync, statSync, mkdtempSync, chmodSync, realpathSync, cpSync, appendFileSync, createWriteStream } from 'fs';
|
|
10
|
+
import { createReadStream, promises, readFileSync, mkdirSync, writeFileSync, renameSync, readdirSync, openSync, fstatSync, readSync, closeSync, existsSync, statSync, mkdtempSync, chmodSync, writeSync, fsyncSync, unlinkSync, realpathSync, cpSync, appendFileSync, createWriteStream } from 'fs';
|
|
11
11
|
import { createInterface } from 'readline';
|
|
12
12
|
import * as providers_store_star from '@agentproto/providers-store';
|
|
13
13
|
import { findAnthropicGatewayPreset, anthropicGatewayPresetList, getAnthropicGatewayPreset } from '@agentproto/provider-presets';
|
|
@@ -16,7 +16,7 @@ import { z, ZodError } from 'zod';
|
|
|
16
16
|
import { listAuthProfiles, eligibleProfiles, getAuthProfile, createAuthProfile, AuthProfileValidationError, deleteAuthProfile, updateAuthProfile, KeychainStore, removeAuthProfile, addAuthProfile, setAuthProfileEnabled, setAuthProfileModels, refreshAuthProfileModels, credentialIdentity } from '@agentproto/auth';
|
|
17
17
|
import { CatalogProviderSchema, getModelsByProvider, getStaticModelProvider } from '@agentproto/model-catalog';
|
|
18
18
|
import { resolvePairPageUrl, DEFAULT_PAIR_PAGE } from '@agentproto/secrets/pairing/browser';
|
|
19
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, SUPPORTED_PROTOCOL_VERSIONS, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, McpError, ErrorCode, SUPPORTED_PROTOCOL_VERSIONS, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
20
20
|
import { makeCredsStore, makeAdapterResolver, makeAdapterLister, discoverAdapterPackages, makeSetupLedger, makeListTool, makeSetupTool } from '@agentproto/provider-kit';
|
|
21
21
|
import { syncPiModels, getConfiguredEndpoints, checkHarnessFit, connectorById } from '@agentproto/llm-endpoint';
|
|
22
22
|
import { createServer, request as request$1 } from 'http';
|
|
@@ -38,6 +38,7 @@ import { createBrainManager, parseKnowledgeConfig } from '@agentproto/workspace-
|
|
|
38
38
|
import { browserAdapters, getBrowserAdapter } from '@agentproto/adapter-browser';
|
|
39
39
|
import { defineHttpDriver } from '@agentproto/driver-http';
|
|
40
40
|
import matter2 from 'gray-matter';
|
|
41
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
41
42
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
42
43
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
43
44
|
import { WebSocketServer } from 'ws';
|
|
@@ -48,8 +49,8 @@ import { createReconnectLogGate } from '@agentproto/pairing-host';
|
|
|
48
49
|
export { createReconnectLogGate } from '@agentproto/pairing-host';
|
|
49
50
|
import { loadAppBundledTools, loadAppHandle, peekAppUi, unpackApp, safeId, versionSatisfies } from '@agentproto/app-kit';
|
|
50
51
|
import { loadAgent } from '@agentproto/agent';
|
|
51
|
-
import { normalizeToolId } from '@agentproto/driver';
|
|
52
|
-
import { compileWorkflow, runWorkflow, isCompilableJsonSchema, validateAgainstJsonSchema, validateWorkflowInput, buildAgentStep, StepOutcomeError, MissingArtifactError } from '@agentproto/workflow-runtime';
|
|
52
|
+
import { normalizeToolId, runTool } from '@agentproto/driver';
|
|
53
|
+
import { compileWorkflow, runWorkflow, isCompilableJsonSchema, validateAgainstJsonSchema, validateWorkflowInput, buildAgentStep, AgentSessionLostError, StepOutcomeError, MissingArtifactError } from '@agentproto/workflow-runtime';
|
|
53
54
|
import { loadWorkflowHandle } from '@agentproto/workflow-loader';
|
|
54
55
|
import { Cron } from 'croner';
|
|
55
56
|
import { brotliCompressSync, constants, gzipSync } from 'zlib';
|
|
@@ -145,15 +146,15 @@ function hasMarker(command, marker) {
|
|
|
145
146
|
return false;
|
|
146
147
|
}
|
|
147
148
|
function listProcesses() {
|
|
148
|
-
return new Promise((
|
|
149
|
+
return new Promise((resolve47) => {
|
|
149
150
|
execFile("ps", ["-axo", "pid=,command="], { maxBuffer: 32 * 1024 * 1024 }, (err, stdout) => {
|
|
150
|
-
if (err) return
|
|
151
|
+
if (err) return resolve47([]);
|
|
151
152
|
const out = [];
|
|
152
153
|
for (const line of stdout.split("\n")) {
|
|
153
154
|
const m = line.match(/^\s*(\d+)\s+(.*)$/);
|
|
154
155
|
if (m) out.push({ pid: Number(m[1]), command: m[2] ?? "" });
|
|
155
156
|
}
|
|
156
|
-
|
|
157
|
+
resolve47(out);
|
|
157
158
|
});
|
|
158
159
|
});
|
|
159
160
|
}
|
|
@@ -523,7 +524,7 @@ function resolveSessionPricing(model) {
|
|
|
523
524
|
function tokenCost(tokens, pricePer1M) {
|
|
524
525
|
return tokens === void 0 ? 0 : tokens * pricePer1M / 1e6;
|
|
525
526
|
}
|
|
526
|
-
function deriveSessionUsage(input,
|
|
527
|
+
function deriveSessionUsage(input, resolve47 = defaultResolver) {
|
|
527
528
|
const contextUsed = plausibleContextUsed(input.contextSize, input.contextUsed);
|
|
528
529
|
const base = {
|
|
529
530
|
...input.model !== void 0 ? { model: input.model } : {},
|
|
@@ -538,7 +539,7 @@ function deriveSessionUsage(input, resolve45 = defaultResolver) {
|
|
|
538
539
|
}
|
|
539
540
|
const hasTokens = input.tokensIn !== void 0 || input.tokensOut !== void 0 || input.cacheReadTokens !== void 0 || input.cacheWriteTokens !== void 0;
|
|
540
541
|
if (hasTokens) {
|
|
541
|
-
const basePricing = input.model !== void 0 ?
|
|
542
|
+
const basePricing = input.model !== void 0 ? resolve47(input.model) : void 0;
|
|
542
543
|
if (!basePricing) {
|
|
543
544
|
return { ...base, source: "no-pricing" };
|
|
544
545
|
}
|
|
@@ -1045,8 +1046,8 @@ function createTranscriptWriter(opts) {
|
|
|
1045
1046
|
const state = states.get(sessionId);
|
|
1046
1047
|
if (!state) return Promise.resolve();
|
|
1047
1048
|
flushBuffers(sessionId, state);
|
|
1048
|
-
return new Promise((
|
|
1049
|
-
state.stream.write("", () =>
|
|
1049
|
+
return new Promise((resolve47) => {
|
|
1050
|
+
state.stream.write("", () => resolve47());
|
|
1050
1051
|
});
|
|
1051
1052
|
},
|
|
1052
1053
|
close(sessionId) {
|
|
@@ -1054,8 +1055,8 @@ function createTranscriptWriter(opts) {
|
|
|
1054
1055
|
if (!state) return Promise.resolve();
|
|
1055
1056
|
flushBuffers(sessionId, state);
|
|
1056
1057
|
states.delete(sessionId);
|
|
1057
|
-
return new Promise((
|
|
1058
|
-
state.stream.end(() =>
|
|
1058
|
+
return new Promise((resolve47) => {
|
|
1059
|
+
state.stream.end(() => resolve47());
|
|
1059
1060
|
});
|
|
1060
1061
|
},
|
|
1061
1062
|
closeAll() {
|
|
@@ -1064,7 +1065,7 @@ function createTranscriptWriter(opts) {
|
|
|
1064
1065
|
const state = states.get(sessionId);
|
|
1065
1066
|
if (!state) continue;
|
|
1066
1067
|
flushBuffers(sessionId, state);
|
|
1067
|
-
closings.push(new Promise((
|
|
1068
|
+
closings.push(new Promise((resolve47) => state.stream.end(() => resolve47())));
|
|
1068
1069
|
}
|
|
1069
1070
|
states.clear();
|
|
1070
1071
|
return Promise.all(closings).then(() => void 0);
|
|
@@ -1225,9 +1226,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
|
|
|
1225
1226
|
let stream;
|
|
1226
1227
|
try {
|
|
1227
1228
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
1228
|
-
await new Promise((
|
|
1229
|
+
await new Promise((resolve47, reject) => {
|
|
1229
1230
|
stream.once("error", reject);
|
|
1230
|
-
stream.once("open",
|
|
1231
|
+
stream.once("open", resolve47);
|
|
1231
1232
|
});
|
|
1232
1233
|
} catch (err) {
|
|
1233
1234
|
const code = err.code;
|
|
@@ -1501,7 +1502,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
|
|
|
1501
1502
|
}
|
|
1502
1503
|
async function defaultHermesRunner(adapterSessionId) {
|
|
1503
1504
|
const { spawn: spawn8 } = await import('child_process');
|
|
1504
|
-
return new Promise((
|
|
1505
|
+
return new Promise((resolve47, reject) => {
|
|
1505
1506
|
const chunks = [];
|
|
1506
1507
|
const errChunks = [];
|
|
1507
1508
|
const proc = spawn8(
|
|
@@ -1520,7 +1521,7 @@ async function defaultHermesRunner(adapterSessionId) {
|
|
|
1520
1521
|
)
|
|
1521
1522
|
);
|
|
1522
1523
|
} else {
|
|
1523
|
-
|
|
1524
|
+
resolve47(Buffer.concat(chunks).toString("utf8"));
|
|
1524
1525
|
}
|
|
1525
1526
|
});
|
|
1526
1527
|
});
|
|
@@ -1557,9 +1558,9 @@ async function exportDaemonEventsSession(sessionId, desc, baseDir) {
|
|
|
1557
1558
|
let stream;
|
|
1558
1559
|
try {
|
|
1559
1560
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
1560
|
-
await new Promise((
|
|
1561
|
+
await new Promise((resolve47, reject) => {
|
|
1561
1562
|
stream.once("error", reject);
|
|
1562
|
-
stream.once("open",
|
|
1563
|
+
stream.once("open", resolve47);
|
|
1563
1564
|
});
|
|
1564
1565
|
} catch (err) {
|
|
1565
1566
|
const code = err.code;
|
|
@@ -1781,7 +1782,11 @@ async function readFirstJsonLine(path) {
|
|
|
1781
1782
|
function codexHome() {
|
|
1782
1783
|
return process.env.CODEX_HOME ?? join(homedir(), ".codex");
|
|
1783
1784
|
}
|
|
1784
|
-
function codexSessionsDir() {
|
|
1785
|
+
function codexSessionsDir(configDir) {
|
|
1786
|
+
if (configDir) {
|
|
1787
|
+
const isolated = join(configDir, "sessions");
|
|
1788
|
+
if (existsSync(isolated)) return isolated;
|
|
1789
|
+
}
|
|
1785
1790
|
return join(codexHome(), "sessions");
|
|
1786
1791
|
}
|
|
1787
1792
|
async function walkCodexRollouts(dir) {
|
|
@@ -1816,8 +1821,8 @@ function codexBlocksText(content) {
|
|
|
1816
1821
|
}
|
|
1817
1822
|
return acc;
|
|
1818
1823
|
}
|
|
1819
|
-
async function findCodexRolloutFile(conversationId) {
|
|
1820
|
-
const files = await walkCodexRollouts(codexSessionsDir());
|
|
1824
|
+
async function findCodexRolloutFile(conversationId, configDir) {
|
|
1825
|
+
const files = await walkCodexRollouts(codexSessionsDir(configDir));
|
|
1821
1826
|
const byName = files.find((f) => f.endsWith(`-${conversationId}.jsonl`));
|
|
1822
1827
|
if (byName) return byName;
|
|
1823
1828
|
for (const f of files) {
|
|
@@ -1834,11 +1839,11 @@ async function findCodexRolloutFile(conversationId) {
|
|
|
1834
1839
|
}
|
|
1835
1840
|
return void 0;
|
|
1836
1841
|
}
|
|
1837
|
-
async function exportCodexSession(conversationId) {
|
|
1838
|
-
const file = await findCodexRolloutFile(conversationId);
|
|
1842
|
+
async function exportCodexSession(conversationId, configDir) {
|
|
1843
|
+
const file = await findCodexRolloutFile(conversationId, configDir);
|
|
1839
1844
|
if (!file) {
|
|
1840
1845
|
throw new Error(
|
|
1841
|
-
`codex: no rollout file for conversation "${conversationId}" under ${codexSessionsDir()}.
|
|
1846
|
+
`codex: no rollout file for conversation "${conversationId}" under ${codexSessionsDir(configDir)}.
|
|
1842
1847
|
The session may predate persistence, or CODEX_HOME points elsewhere.`
|
|
1843
1848
|
);
|
|
1844
1849
|
}
|
|
@@ -2488,7 +2493,7 @@ var init_transcript_export = __esm({
|
|
|
2488
2493
|
exportSession: (id) => exportHermesSession(id)
|
|
2489
2494
|
},
|
|
2490
2495
|
codex: {
|
|
2491
|
-
exportSession: (id) => exportCodexSession(id)
|
|
2496
|
+
exportSession: (id, _cwd, configDir) => exportCodexSession(id, configDir)
|
|
2492
2497
|
},
|
|
2493
2498
|
opencode: {
|
|
2494
2499
|
exportSession: (id) => exportOpenCodeSession(id)
|
|
@@ -3907,8 +3912,8 @@ async function enrichWithRemainingQuota(rollup, opts) {
|
|
|
3907
3912
|
})
|
|
3908
3913
|
);
|
|
3909
3914
|
const capMs = opts.timeoutMs ?? DEFAULT_ENRICH_CAP_MS;
|
|
3910
|
-
const cap = new Promise((
|
|
3911
|
-
setTimeout(() =>
|
|
3915
|
+
const cap = new Promise((resolve47) => {
|
|
3916
|
+
setTimeout(() => resolve47(null), capMs);
|
|
3912
3917
|
});
|
|
3913
3918
|
const settled = await Promise.race([work, cap]);
|
|
3914
3919
|
if (settled === null) return rollup;
|
|
@@ -3972,8 +3977,8 @@ async function enrichWithAccountCredits(rollup, opts) {
|
|
|
3972
3977
|
})
|
|
3973
3978
|
);
|
|
3974
3979
|
const capMs = opts.timeoutMs ?? DEFAULT_ENRICH_CAP_MS;
|
|
3975
|
-
const cap = new Promise((
|
|
3976
|
-
setTimeout(() =>
|
|
3980
|
+
const cap = new Promise((resolve47) => {
|
|
3981
|
+
setTimeout(() => resolve47(null), capMs);
|
|
3977
3982
|
});
|
|
3978
3983
|
const settled = await Promise.race([work, cap]);
|
|
3979
3984
|
if (settled === null) return rollup;
|
|
@@ -4228,14 +4233,14 @@ function parseProcStat(statLine, cmdline, uid, ctx) {
|
|
|
4228
4233
|
};
|
|
4229
4234
|
}
|
|
4230
4235
|
function execPs(platform, timeoutMs) {
|
|
4231
|
-
return new Promise((
|
|
4236
|
+
return new Promise((resolve47, reject) => {
|
|
4232
4237
|
execFile(
|
|
4233
4238
|
"ps",
|
|
4234
4239
|
psArgs(platform),
|
|
4235
4240
|
// LC_ALL=C pins the decimal separator of `pcpu` to `.` regardless of the
|
|
4236
4241
|
// operator's locale.
|
|
4237
4242
|
{ maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs, env: { ...process.env, LC_ALL: "C" } },
|
|
4238
|
-
(err, stdout) => err ? reject(new Error(`ps failed${err.killed ? " (timed out, host overloaded?)" : ""}: ${err.message.trim()}`)) :
|
|
4243
|
+
(err, stdout) => err ? reject(new Error(`ps failed${err.killed ? " (timed out, host overloaded?)" : ""}: ${err.message.trim()}`)) : resolve47(stdout)
|
|
4239
4244
|
);
|
|
4240
4245
|
});
|
|
4241
4246
|
}
|
|
@@ -4539,8 +4544,8 @@ function parseVmStatAvailable(text13) {
|
|
|
4539
4544
|
return total * Number(size[1]);
|
|
4540
4545
|
}
|
|
4541
4546
|
function execText(cmd, args) {
|
|
4542
|
-
return new Promise((
|
|
4543
|
-
execFile(cmd, args, { timeout: 3e3 }, (err, stdout) => err ? reject(err) :
|
|
4547
|
+
return new Promise((resolve47, reject) => {
|
|
4548
|
+
execFile(cmd, args, { timeout: 3e3 }, (err, stdout) => err ? reject(err) : resolve47(stdout));
|
|
4544
4549
|
});
|
|
4545
4550
|
}
|
|
4546
4551
|
function createProcessStatsService(opts = {}) {
|
|
@@ -4566,14 +4571,14 @@ function createProcessStatsService(opts = {}) {
|
|
|
4566
4571
|
nowMs: now()
|
|
4567
4572
|
}).claimedPids;
|
|
4568
4573
|
const byPid = new Map(rows.map((r) => [r.pid, r]));
|
|
4569
|
-
const
|
|
4574
|
+
const keyOf3 = (pid) => `${pid}:${Math.floor((now() - (byPid.get(pid)?.elapsedSec ?? 0) * 1e3) / 2e3)}`;
|
|
4570
4575
|
const candidates = strayEnvCandidates(rows, claimed, uid);
|
|
4571
|
-
const unread = candidates.filter((p) => !envMemo.has(
|
|
4576
|
+
const unread = candidates.filter((p) => !envMemo.has(keyOf3(p)));
|
|
4572
4577
|
const read = await readEnv(unread);
|
|
4573
|
-
for (const p of unread) envMemo.set(
|
|
4578
|
+
for (const p of unread) envMemo.set(keyOf3(p), read.get(p) ?? null);
|
|
4574
4579
|
const envHints = /* @__PURE__ */ new Map();
|
|
4575
4580
|
for (const p of candidates) {
|
|
4576
|
-
const h = envMemo.get(
|
|
4581
|
+
const h = envMemo.get(keyOf3(p));
|
|
4577
4582
|
if (h) envHints.set(p, h);
|
|
4578
4583
|
}
|
|
4579
4584
|
if (envMemo.size > 4096) envMemo.clear();
|
|
@@ -5419,9 +5424,9 @@ async function readFirstTurn(sessionId) {
|
|
|
5419
5424
|
let stream;
|
|
5420
5425
|
try {
|
|
5421
5426
|
stream = createReadStream(sessionEventsPath(sessionId), { encoding: "utf8" });
|
|
5422
|
-
await new Promise((
|
|
5427
|
+
await new Promise((resolve47, reject) => {
|
|
5423
5428
|
stream.once("error", reject);
|
|
5424
|
-
stream.once("open",
|
|
5429
|
+
stream.once("open", resolve47);
|
|
5425
5430
|
});
|
|
5426
5431
|
} catch {
|
|
5427
5432
|
return null;
|
|
@@ -8935,7 +8940,7 @@ function boundaryMeta(b) {
|
|
|
8935
8940
|
return { [APP_BOUNDARY_META_KEY]: encodeAppBoundary(b) };
|
|
8936
8941
|
}
|
|
8937
8942
|
function inAny(canon, zones) {
|
|
8938
|
-
return zones.some((
|
|
8943
|
+
return zones.some((z62) => isPathWithin(canon, canonicalizePath(z62)));
|
|
8939
8944
|
}
|
|
8940
8945
|
function resolveBoundaryPath(b, input, mode) {
|
|
8941
8946
|
if (typeof input !== "string" || input.length === 0) {
|
|
@@ -9271,7 +9276,7 @@ async function* pollAgentOutputFallback(host, remoteSessionId, since) {
|
|
|
9271
9276
|
`sandbox proxy: ${consecutivePollFailures} consecutive poll failures against the box daemon (session "${remoteSessionId}") \u2014 giving up. Last error: ${pollErr instanceof Error ? pollErr.message : String(pollErr)}`
|
|
9272
9277
|
);
|
|
9273
9278
|
}
|
|
9274
|
-
await new Promise((
|
|
9279
|
+
await new Promise((resolve47) => setTimeout(resolve47, POLL_RETRY_DELAY_MS));
|
|
9275
9280
|
continue;
|
|
9276
9281
|
}
|
|
9277
9282
|
if (result.timedOut) continue;
|
|
@@ -9506,19 +9511,19 @@ async function startDeviceSandboxBridge(opts) {
|
|
|
9506
9511
|
}
|
|
9507
9512
|
}
|
|
9508
9513
|
const port = await getFreePort();
|
|
9509
|
-
await new Promise((
|
|
9514
|
+
await new Promise((resolve47, reject) => {
|
|
9510
9515
|
server.once("error", reject);
|
|
9511
|
-
server.listen(port, "127.0.0.1", () =>
|
|
9516
|
+
server.listen(port, "127.0.0.1", () => resolve47());
|
|
9512
9517
|
});
|
|
9513
9518
|
return {
|
|
9514
9519
|
mcpUrl: `http://127.0.0.1:${port}/mcp`,
|
|
9515
9520
|
async close() {
|
|
9516
|
-
await new Promise((
|
|
9521
|
+
await new Promise((resolve47) => server.close(() => resolve47()));
|
|
9517
9522
|
}
|
|
9518
9523
|
};
|
|
9519
9524
|
}
|
|
9520
9525
|
async function getFreePort() {
|
|
9521
|
-
return new Promise((
|
|
9526
|
+
return new Promise((resolve47, reject) => {
|
|
9522
9527
|
const srv = createServer$1();
|
|
9523
9528
|
srv.once("error", reject);
|
|
9524
9529
|
srv.listen(0, "127.0.0.1", () => {
|
|
@@ -9528,7 +9533,7 @@ async function getFreePort() {
|
|
|
9528
9533
|
return;
|
|
9529
9534
|
}
|
|
9530
9535
|
const { port } = address;
|
|
9531
|
-
srv.close(() =>
|
|
9536
|
+
srv.close(() => resolve47(port));
|
|
9532
9537
|
});
|
|
9533
9538
|
});
|
|
9534
9539
|
}
|
|
@@ -9673,7 +9678,7 @@ async function pollServeReady(url, opts) {
|
|
|
9673
9678
|
for (; ; ) {
|
|
9674
9679
|
if (await probe(url)) return true;
|
|
9675
9680
|
if (Date.now() >= deadline) return false;
|
|
9676
|
-
await new Promise((
|
|
9681
|
+
await new Promise((resolve47) => setTimeout(resolve47, intervalMs));
|
|
9677
9682
|
}
|
|
9678
9683
|
}
|
|
9679
9684
|
async function seedAllowlist(client) {
|
|
@@ -9798,7 +9803,7 @@ var init_sandbox_app_serve = __esm({
|
|
|
9798
9803
|
}
|
|
9799
9804
|
});
|
|
9800
9805
|
async function getFreePort2() {
|
|
9801
|
-
return new Promise((
|
|
9806
|
+
return new Promise((resolve47, reject) => {
|
|
9802
9807
|
const srv = createServer$1();
|
|
9803
9808
|
srv.once("error", reject);
|
|
9804
9809
|
srv.listen(0, "127.0.0.1", () => {
|
|
@@ -9808,7 +9813,7 @@ async function getFreePort2() {
|
|
|
9808
9813
|
return;
|
|
9809
9814
|
}
|
|
9810
9815
|
const { port } = address;
|
|
9811
|
-
srv.close(() =>
|
|
9816
|
+
srv.close(() => resolve47(port));
|
|
9812
9817
|
});
|
|
9813
9818
|
});
|
|
9814
9819
|
}
|
|
@@ -9821,7 +9826,7 @@ async function probeHealth(url, timeoutMs) {
|
|
|
9821
9826
|
} catch {
|
|
9822
9827
|
}
|
|
9823
9828
|
if (Date.now() >= deadline) return false;
|
|
9824
|
-
await new Promise((
|
|
9829
|
+
await new Promise((resolve47) => setTimeout(resolve47, POLL_INTERVAL_MS));
|
|
9825
9830
|
}
|
|
9826
9831
|
}
|
|
9827
9832
|
var HEALTH_TIMEOUT_MS, POLL_INTERVAL_MS, localSandboxProvider;
|
|
@@ -11398,8 +11403,8 @@ async function spawnAgentSession(deps2, input) {
|
|
|
11398
11403
|
}
|
|
11399
11404
|
let resolveClaim;
|
|
11400
11405
|
claims.set(key, {
|
|
11401
|
-
result: new Promise((
|
|
11402
|
-
resolveClaim =
|
|
11406
|
+
result: new Promise((resolve47) => {
|
|
11407
|
+
resolveClaim = resolve47;
|
|
11403
11408
|
}),
|
|
11404
11409
|
// An implicit claim expires sooner than an explicit one — see
|
|
11405
11410
|
// `IMPLICIT_SPAWN_CLAIM_WINDOW_MS`'s docblock.
|
|
@@ -11958,13 +11963,13 @@ async function isSharedDirtyCwd(cwd) {
|
|
|
11958
11963
|
const identity = resolveWorktreeIdentity(cwd);
|
|
11959
11964
|
if (identity?.worktreeId !== void 0) return false;
|
|
11960
11965
|
const { spawn: spawn8 } = await import('child_process');
|
|
11961
|
-
return await new Promise((
|
|
11966
|
+
return await new Promise((resolve47) => {
|
|
11962
11967
|
let stdout = "";
|
|
11963
11968
|
let settled = false;
|
|
11964
11969
|
const done = (v) => {
|
|
11965
11970
|
if (!settled) {
|
|
11966
11971
|
settled = true;
|
|
11967
|
-
|
|
11972
|
+
resolve47(v);
|
|
11968
11973
|
}
|
|
11969
11974
|
};
|
|
11970
11975
|
const child = spawn8("git", ["-C", cwd, "status", "--porcelain"], {
|
|
@@ -11979,15 +11984,15 @@ async function isSharedDirtyCwd(cwd) {
|
|
|
11979
11984
|
}
|
|
11980
11985
|
async function resolveMcpCredentialHeaders(mcpServers) {
|
|
11981
11986
|
if (!mcpServers || mcpServers.length === 0) return mcpServers;
|
|
11982
|
-
const { resolveMcpCredentialHeaders:
|
|
11983
|
-
if (!
|
|
11987
|
+
const { resolveMcpCredentialHeaders: resolve47 } = getMcpCredentialDeps();
|
|
11988
|
+
if (!resolve47) return mcpServers;
|
|
11984
11989
|
return Promise.all(
|
|
11985
11990
|
mcpServers.map(async (entry) => {
|
|
11986
11991
|
const ref = entry.credentialRef;
|
|
11987
11992
|
if (!ref) return entry;
|
|
11988
11993
|
let brokered;
|
|
11989
11994
|
try {
|
|
11990
|
-
brokered = await
|
|
11995
|
+
brokered = await resolve47({ credentialRef: ref });
|
|
11991
11996
|
} catch (err) {
|
|
11992
11997
|
console.warn(
|
|
11993
11998
|
`[agent_start] credentialRef resolution failed for "${entry.name}" (${ref}): ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -12083,7 +12088,7 @@ async function startDeviceSessionWithRetry(host, args, target) {
|
|
|
12083
12088
|
lastErr = err;
|
|
12084
12089
|
if (!isDeviceTransportError(err)) throw err;
|
|
12085
12090
|
if (attempt < attempts) {
|
|
12086
|
-
await new Promise((
|
|
12091
|
+
await new Promise((resolve47) => setTimeout(resolve47, DEVICE_SPAWN_RETRY_DELAY_MS));
|
|
12087
12092
|
}
|
|
12088
12093
|
}
|
|
12089
12094
|
}
|
|
@@ -12335,10 +12340,10 @@ function markFailedBootLedger(opts) {
|
|
|
12335
12340
|
}
|
|
12336
12341
|
}
|
|
12337
12342
|
async function resolveSandboxSecret(slug) {
|
|
12338
|
-
const { resolveSandboxSecret:
|
|
12339
|
-
if (!
|
|
12343
|
+
const { resolveSandboxSecret: resolve47 } = getMcpCredentialDeps();
|
|
12344
|
+
if (!resolve47) return null;
|
|
12340
12345
|
try {
|
|
12341
|
-
return await
|
|
12346
|
+
return await resolve47(slug);
|
|
12342
12347
|
} catch (err) {
|
|
12343
12348
|
console.warn(
|
|
12344
12349
|
`[agent_start] sandbox secret resolution failed for "${slug}": ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -13044,8 +13049,8 @@ async function readLogTailSafe(dir, n) {
|
|
|
13044
13049
|
async function resolveAppUiBuildState(input) {
|
|
13045
13050
|
const { dir, uiPath } = input;
|
|
13046
13051
|
const pending = peekInFlightBuild(uiPath) ?? ensureAppUiBuilt(input);
|
|
13047
|
-
const timeout = new Promise((
|
|
13048
|
-
const timer = setTimeout(() =>
|
|
13052
|
+
const timeout = new Promise((resolve47) => {
|
|
13053
|
+
const timer = setTimeout(() => resolve47(RESOLVE_TIMEOUT), RESOLVE_FAST_PATH_MS);
|
|
13049
13054
|
timer.unref();
|
|
13050
13055
|
});
|
|
13051
13056
|
const raced = await Promise.race([pending, timeout]);
|
|
@@ -16402,8 +16407,8 @@ function createTerminalTranscriptWriter(opts) {
|
|
|
16402
16407
|
const stream = streams.get(sessionId);
|
|
16403
16408
|
if (!stream) return Promise.resolve();
|
|
16404
16409
|
streams.delete(sessionId);
|
|
16405
|
-
return new Promise((
|
|
16406
|
-
stream.end(() =>
|
|
16410
|
+
return new Promise((resolve47) => {
|
|
16411
|
+
stream.end(() => resolve47());
|
|
16407
16412
|
});
|
|
16408
16413
|
},
|
|
16409
16414
|
closeAll() {
|
|
@@ -16412,8 +16417,8 @@ function createTerminalTranscriptWriter(opts) {
|
|
|
16412
16417
|
const stream = streams.get(sessionId);
|
|
16413
16418
|
if (!stream) continue;
|
|
16414
16419
|
closings.push(
|
|
16415
|
-
new Promise((
|
|
16416
|
-
stream.end(() =>
|
|
16420
|
+
new Promise((resolve47) => {
|
|
16421
|
+
stream.end(() => resolve47());
|
|
16417
16422
|
})
|
|
16418
16423
|
);
|
|
16419
16424
|
}
|
|
@@ -16860,8 +16865,8 @@ function createRegistryHandoffAsker(registry, desc, baseDir) {
|
|
|
16860
16865
|
const turn = registry.sendPrompt(sessionId, prompt, { source: HANDOFF_PROMPT_SOURCE });
|
|
16861
16866
|
let timer;
|
|
16862
16867
|
const timedOut = /* @__PURE__ */ Symbol("timeout");
|
|
16863
|
-
const deadline = new Promise((
|
|
16864
|
-
timer = setTimeout(() =>
|
|
16868
|
+
const deadline = new Promise((resolve47) => {
|
|
16869
|
+
timer = setTimeout(() => resolve47(timedOut), timeoutMs);
|
|
16865
16870
|
});
|
|
16866
16871
|
try {
|
|
16867
16872
|
const outcome = await Promise.race([turn, deadline]);
|
|
@@ -16876,7 +16881,7 @@ function createRegistryHandoffAsker(registry, desc, baseDir) {
|
|
|
16876
16881
|
for (let attempt = 0; attempt < REPLY_POLL_ATTEMPTS; attempt++) {
|
|
16877
16882
|
const reply = readLastAssistantTextSync(eventsPath2);
|
|
16878
16883
|
if (reply && reply !== before) return reply;
|
|
16879
|
-
await new Promise((
|
|
16884
|
+
await new Promise((resolve47) => setTimeout(resolve47, REPLY_POLL_MS));
|
|
16880
16885
|
}
|
|
16881
16886
|
throw new Error("source session produced no reply");
|
|
16882
16887
|
};
|
|
@@ -16980,14 +16985,14 @@ function buildRecentDigest(messages) {
|
|
|
16980
16985
|
}
|
|
16981
16986
|
async function captureGitStatus(cwd) {
|
|
16982
16987
|
if (!cwd) return void 0;
|
|
16983
|
-
return new Promise((
|
|
16988
|
+
return new Promise((resolve47) => {
|
|
16984
16989
|
execFile("git", ["status", "--porcelain"], { cwd }, (err, stdout) => {
|
|
16985
16990
|
if (err) {
|
|
16986
|
-
|
|
16991
|
+
resolve47(void 0);
|
|
16987
16992
|
return;
|
|
16988
16993
|
}
|
|
16989
16994
|
const trimmed = stdout.trim();
|
|
16990
|
-
|
|
16995
|
+
resolve47(trimmed || "(working tree clean)");
|
|
16991
16996
|
});
|
|
16992
16997
|
});
|
|
16993
16998
|
}
|
|
@@ -19135,11 +19140,11 @@ function createSessionsRegistry(opts) {
|
|
|
19135
19140
|
};
|
|
19136
19141
|
const waitForTurnSettled = (rt, id, caller) => {
|
|
19137
19142
|
if (!rt.busy) return Promise.resolve();
|
|
19138
|
-
return new Promise((
|
|
19143
|
+
return new Promise((resolve47, reject) => {
|
|
19139
19144
|
const onBusy = (busy) => {
|
|
19140
19145
|
if (busy) return;
|
|
19141
19146
|
cleanup();
|
|
19142
|
-
|
|
19147
|
+
resolve47();
|
|
19143
19148
|
};
|
|
19144
19149
|
const cleanup = () => {
|
|
19145
19150
|
clearTimeout(timer);
|
|
@@ -19217,9 +19222,9 @@ function createSessionsRegistry(opts) {
|
|
|
19217
19222
|
);
|
|
19218
19223
|
return;
|
|
19219
19224
|
}
|
|
19220
|
-
const
|
|
19221
|
-
if (
|
|
19222
|
-
await answerStructuredQuestion(liveRt,
|
|
19225
|
+
const answer2 = isChildPromptSource(next.source) ? void 0 : matchStructuredQuestionAnswer(liveRt, next.message);
|
|
19226
|
+
if (answer2) {
|
|
19227
|
+
await answerStructuredQuestion(liveRt, answer2);
|
|
19223
19228
|
return;
|
|
19224
19229
|
}
|
|
19225
19230
|
await runAgentTurn(
|
|
@@ -23034,14 +23039,14 @@ var init_pr_provenance_stamp = __esm({
|
|
|
23034
23039
|
init_pr_provenance();
|
|
23035
23040
|
defaultGhRunner = async (args, cwd) => {
|
|
23036
23041
|
const { spawn: spawn8 } = await import('child_process');
|
|
23037
|
-
return await new Promise((
|
|
23042
|
+
return await new Promise((resolve47) => {
|
|
23038
23043
|
let stdout = "";
|
|
23039
23044
|
const child = spawn8("gh", [...args], { cwd, shell: false });
|
|
23040
23045
|
child.stdout?.on("data", (d) => {
|
|
23041
23046
|
stdout += d.toString("utf8");
|
|
23042
23047
|
});
|
|
23043
|
-
child.on("error", () =>
|
|
23044
|
-
child.on("close", (code) =>
|
|
23048
|
+
child.on("error", () => resolve47({ exitCode: 1, stdout }));
|
|
23049
|
+
child.on("close", (code) => resolve47({ exitCode: code ?? 1, stdout }));
|
|
23045
23050
|
});
|
|
23046
23051
|
};
|
|
23047
23052
|
}
|
|
@@ -23575,6 +23580,19 @@ var init_command_tools = __esm({
|
|
|
23575
23580
|
}
|
|
23576
23581
|
});
|
|
23577
23582
|
|
|
23583
|
+
// src/liveness-tick-guard.ts
|
|
23584
|
+
function createLivenessTickGuard(intervalMs, graceMs, now = Date.now) {
|
|
23585
|
+
let last = now();
|
|
23586
|
+
return {
|
|
23587
|
+
shouldSkip() {
|
|
23588
|
+
const t = now();
|
|
23589
|
+
const late = t - last - intervalMs;
|
|
23590
|
+
last = t;
|
|
23591
|
+
return late > graceMs;
|
|
23592
|
+
}
|
|
23593
|
+
};
|
|
23594
|
+
}
|
|
23595
|
+
|
|
23578
23596
|
// src/index.ts
|
|
23579
23597
|
init_browser_mount();
|
|
23580
23598
|
async function writeRuntimeMeta(workspace, meta) {
|
|
@@ -25319,10 +25337,10 @@ function sandboxAuthForBox(auth) {
|
|
|
25319
25337
|
};
|
|
25320
25338
|
}
|
|
25321
25339
|
async function resolveSandboxSecretFromBroker(slug) {
|
|
25322
|
-
const { resolveSandboxSecret:
|
|
25323
|
-
if (!
|
|
25340
|
+
const { resolveSandboxSecret: resolve47 } = getMcpCredentialDeps();
|
|
25341
|
+
if (!resolve47) return null;
|
|
25324
25342
|
try {
|
|
25325
|
-
return await
|
|
25343
|
+
return await resolve47(slug);
|
|
25326
25344
|
} catch (err) {
|
|
25327
25345
|
console.warn(
|
|
25328
25346
|
`[restartAgentSession] sandbox secret resolution failed for "${slug}": ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -25916,6 +25934,12 @@ function createMcpObservationStore(opts = {}) {
|
|
|
25916
25934
|
const obs = touch(sessionId);
|
|
25917
25935
|
obs.httpError = { at: obs.lastSeenAt, status };
|
|
25918
25936
|
},
|
|
25937
|
+
recordResult(sessionId, method, message, clientProtocolVersion) {
|
|
25938
|
+
try {
|
|
25939
|
+
record2(sessionId, method, message, clientProtocolVersion);
|
|
25940
|
+
} catch {
|
|
25941
|
+
}
|
|
25942
|
+
},
|
|
25919
25943
|
get: (sessionId) => bySession.get(sessionId),
|
|
25920
25944
|
delete: (sessionId) => {
|
|
25921
25945
|
bySession.delete(sessionId);
|
|
@@ -26240,7 +26264,7 @@ async function runContinueOnBootPass(opts) {
|
|
|
26240
26264
|
|
|
26241
26265
|
// src/session-tools.ts
|
|
26242
26266
|
init_session_outcome();
|
|
26243
|
-
var defaultPsExecutor = () => new Promise((
|
|
26267
|
+
var defaultPsExecutor = () => new Promise((resolve47, reject) => {
|
|
26244
26268
|
if (process.platform === "win32") {
|
|
26245
26269
|
reject(new Error("processTreeRss: ps is not available on win32"));
|
|
26246
26270
|
return;
|
|
@@ -26251,7 +26275,7 @@ var defaultPsExecutor = () => new Promise((resolve45, reject) => {
|
|
|
26251
26275
|
{ maxBuffer: 16 * 1024 * 1024 },
|
|
26252
26276
|
(err, stdout) => {
|
|
26253
26277
|
if (err) reject(err);
|
|
26254
|
-
else
|
|
26278
|
+
else resolve47(stdout);
|
|
26255
26279
|
}
|
|
26256
26280
|
);
|
|
26257
26281
|
});
|
|
@@ -26304,6 +26328,163 @@ async function processTreeRss(rootPids, exec = defaultPsExecutor) {
|
|
|
26304
26328
|
}
|
|
26305
26329
|
return result;
|
|
26306
26330
|
}
|
|
26331
|
+
var SessionListFilterError = class extends Error {
|
|
26332
|
+
constructor(message) {
|
|
26333
|
+
super(message);
|
|
26334
|
+
this.name = "SessionListFilterError";
|
|
26335
|
+
}
|
|
26336
|
+
};
|
|
26337
|
+
var SESSION_LIST_FILTER_KEYS = [
|
|
26338
|
+
"q",
|
|
26339
|
+
"excludeLabelPrefix",
|
|
26340
|
+
"excludeLabels",
|
|
26341
|
+
"excludeKinds",
|
|
26342
|
+
"rootOnly",
|
|
26343
|
+
"parentSessionId",
|
|
26344
|
+
"updatedSince",
|
|
26345
|
+
"startedSince",
|
|
26346
|
+
"excludeNoise"
|
|
26347
|
+
];
|
|
26348
|
+
function pickSessionListFilters(input) {
|
|
26349
|
+
const out = {};
|
|
26350
|
+
for (const key of SESSION_LIST_FILTER_KEYS) {
|
|
26351
|
+
if (input[key] !== void 0) out[key] = input[key];
|
|
26352
|
+
}
|
|
26353
|
+
return out;
|
|
26354
|
+
}
|
|
26355
|
+
var TIME_UNIT_MS = {
|
|
26356
|
+
s: 1e3,
|
|
26357
|
+
m: 6e4,
|
|
26358
|
+
h: 36e5,
|
|
26359
|
+
d: 864e5,
|
|
26360
|
+
w: 6048e5
|
|
26361
|
+
};
|
|
26362
|
+
var RELATIVE_RE = /^(\d+(?:\.\d+)?)\s*([smhdw])$/i;
|
|
26363
|
+
function parseTimeBound(raw, field, now = Date.now()) {
|
|
26364
|
+
const text13 = raw.trim();
|
|
26365
|
+
const rel = RELATIVE_RE.exec(text13);
|
|
26366
|
+
if (rel) {
|
|
26367
|
+
const n = Number(rel[1]);
|
|
26368
|
+
const unit = TIME_UNIT_MS[rel[2].toLowerCase()];
|
|
26369
|
+
return now - n * unit;
|
|
26370
|
+
}
|
|
26371
|
+
const abs = text13 === "" ? Number.NaN : Date.parse(text13);
|
|
26372
|
+
if (Number.isNaN(abs)) {
|
|
26373
|
+
throw new SessionListFilterError(
|
|
26374
|
+
`${field} must be an ISO-8601 time or a relative age like "30m", "24h", "7d", "2w"; got ${JSON.stringify(raw)}.`
|
|
26375
|
+
);
|
|
26376
|
+
}
|
|
26377
|
+
return abs;
|
|
26378
|
+
}
|
|
26379
|
+
function toList(v) {
|
|
26380
|
+
if (v === void 0) return [];
|
|
26381
|
+
const parts = typeof v === "string" ? [v] : [...v];
|
|
26382
|
+
return parts.flatMap((p) => p.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
26383
|
+
}
|
|
26384
|
+
var REVIEW_LABEL_PREFIX = "review:";
|
|
26385
|
+
var WORKFLOW_LABEL_PREFIX = "wf:";
|
|
26386
|
+
var NOISE_ORIGINS = /* @__PURE__ */ new Set(["review", "workflow"]);
|
|
26387
|
+
function isNoiseSession(s) {
|
|
26388
|
+
const label = s.label ?? "";
|
|
26389
|
+
if (label.startsWith(REVIEW_LABEL_PREFIX) || label.startsWith(WORKFLOW_LABEL_PREFIX)) return true;
|
|
26390
|
+
if (s.origin !== void 0 && NOISE_ORIGINS.has(s.origin)) return true;
|
|
26391
|
+
const ended = s.status === "exited" || s.status === "killed";
|
|
26392
|
+
if (ended && (s.kind === "command" || s.kind === "terminal" && !s.adapterSlug)) return true;
|
|
26393
|
+
return false;
|
|
26394
|
+
}
|
|
26395
|
+
function sessionActivityMs(s) {
|
|
26396
|
+
const ms = Date.parse(s.lastActivityAt ?? s.startedAt);
|
|
26397
|
+
return Number.isNaN(ms) ? 0 : ms;
|
|
26398
|
+
}
|
|
26399
|
+
function sortNewestActivityFirst(rows) {
|
|
26400
|
+
return [...rows].sort((a, b) => sessionActivityMs(b) - sessionActivityMs(a) || a.id.localeCompare(b.id));
|
|
26401
|
+
}
|
|
26402
|
+
function hasSessionListFilters(input) {
|
|
26403
|
+
return input.q !== void 0 && input.q.trim() !== "" || toList(input.excludeLabelPrefix).length > 0 || toList(input.excludeLabels).length > 0 || toList(input.excludeKinds).length > 0 || input.rootOnly === true || input.parentSessionId !== void 0 && input.parentSessionId !== "" || input.updatedSince !== void 0 && input.updatedSince.trim() !== "" || input.startedSince !== void 0 && input.startedSince.trim() !== "" || input.excludeNoise === true;
|
|
26404
|
+
}
|
|
26405
|
+
function compileSessionListFilters(input, now = Date.now()) {
|
|
26406
|
+
const q = input.q?.trim().toLowerCase() ?? "";
|
|
26407
|
+
const prefixes = toList(input.excludeLabelPrefix);
|
|
26408
|
+
const labels = new Set(toList(input.excludeLabels));
|
|
26409
|
+
const kinds = new Set(toList(input.excludeKinds));
|
|
26410
|
+
const parentId = input.parentSessionId && input.parentSessionId !== "" ? input.parentSessionId : void 0;
|
|
26411
|
+
const updatedSince = input.updatedSince && input.updatedSince.trim() !== "" ? parseTimeBound(input.updatedSince, "updatedSince", now) : void 0;
|
|
26412
|
+
const startedSince = input.startedSince && input.startedSince.trim() !== "" ? parseTimeBound(input.startedSince, "startedSince", now) : void 0;
|
|
26413
|
+
return (s) => {
|
|
26414
|
+
if (q) {
|
|
26415
|
+
const hit = [s.id, s.name, s.label, s.title, s.cwd].some((f) => f !== void 0 && f.toLowerCase().includes(q));
|
|
26416
|
+
if (!hit) return false;
|
|
26417
|
+
}
|
|
26418
|
+
if (kinds.has(s.kind)) return false;
|
|
26419
|
+
const label = s.label;
|
|
26420
|
+
if (label !== void 0) {
|
|
26421
|
+
if (labels.has(label)) return false;
|
|
26422
|
+
if (prefixes.some((p) => label.startsWith(p))) return false;
|
|
26423
|
+
}
|
|
26424
|
+
if (input.rootOnly === true && s.parentSessionId) return false;
|
|
26425
|
+
if (parentId !== void 0 && s.parentSessionId !== parentId) return false;
|
|
26426
|
+
if (updatedSince !== void 0 && sessionActivityMs(s) < updatedSince) return false;
|
|
26427
|
+
if (startedSince !== void 0) {
|
|
26428
|
+
const started = Date.parse(s.startedAt);
|
|
26429
|
+
if (Number.isNaN(started) || started < startedSince) return false;
|
|
26430
|
+
}
|
|
26431
|
+
if (input.excludeNoise === true && isNoiseSession(s)) return false;
|
|
26432
|
+
return true;
|
|
26433
|
+
};
|
|
26434
|
+
}
|
|
26435
|
+
function applySessionListFilters(rows, input, now = Date.now()) {
|
|
26436
|
+
if (!hasSessionListFilters(input)) return [...rows];
|
|
26437
|
+
return rows.filter(compileSessionListFilters(input, now));
|
|
26438
|
+
}
|
|
26439
|
+
function parseSessionListFilterParams(params) {
|
|
26440
|
+
const flag = (k) => {
|
|
26441
|
+
const v = params.get(k);
|
|
26442
|
+
return v === null ? void 0 : v === "true" || v === "1";
|
|
26443
|
+
};
|
|
26444
|
+
const list = (k) => {
|
|
26445
|
+
const all = params.getAll(k);
|
|
26446
|
+
return all.length > 0 ? toList(all) : void 0;
|
|
26447
|
+
};
|
|
26448
|
+
const str3 = (k) => {
|
|
26449
|
+
const v = params.get(k);
|
|
26450
|
+
return v === null || v === "" ? void 0 : v;
|
|
26451
|
+
};
|
|
26452
|
+
const out = {};
|
|
26453
|
+
const q = str3("q");
|
|
26454
|
+
if (q !== void 0) out.q = q;
|
|
26455
|
+
const prefix = list("excludeLabelPrefix");
|
|
26456
|
+
if (prefix) out.excludeLabelPrefix = prefix;
|
|
26457
|
+
const labels = list("excludeLabels");
|
|
26458
|
+
if (labels) out.excludeLabels = labels;
|
|
26459
|
+
const kinds = list("excludeKinds");
|
|
26460
|
+
if (kinds) out.excludeKinds = kinds;
|
|
26461
|
+
const rootOnly = flag("rootOnly");
|
|
26462
|
+
if (rootOnly !== void 0) out.rootOnly = rootOnly;
|
|
26463
|
+
const parent = str3("parentSessionId");
|
|
26464
|
+
if (parent !== void 0) out.parentSessionId = parent;
|
|
26465
|
+
const updatedSince = str3("updatedSince");
|
|
26466
|
+
if (updatedSince !== void 0) out.updatedSince = updatedSince;
|
|
26467
|
+
const startedSince = str3("startedSince");
|
|
26468
|
+
if (startedSince !== void 0) out.startedSince = startedSince;
|
|
26469
|
+
const noise = flag("excludeNoise");
|
|
26470
|
+
if (noise !== void 0) out.excludeNoise = noise;
|
|
26471
|
+
return out;
|
|
26472
|
+
}
|
|
26473
|
+
var mcpBool2 = z.preprocess((v) => v === "true" ? true : v === "false" ? false : v, z.boolean());
|
|
26474
|
+
var stringOrList = z.union([z.string(), z.array(z.string())]);
|
|
26475
|
+
var sessionListFilterShape = {
|
|
26476
|
+
q: z.string().optional().describe("Case-insensitive substring over id, name, label, title, cwd."),
|
|
26477
|
+
excludeLabelPrefix: stringOrList.optional().describe("Drop labels starting with any of these, e.g. ['review:','wf:']."),
|
|
26478
|
+
excludeLabels: stringOrList.optional().describe("Drop labels exactly equal to any of these (as session_follow exclude.labels)."),
|
|
26479
|
+
excludeKinds: stringOrList.optional().describe("Drop kinds: terminal | agent-cli | command."),
|
|
26480
|
+
rootOnly: mcpBool2.optional().describe("Only sessions with no parent."),
|
|
26481
|
+
parentSessionId: z.string().optional().describe("Only direct children of this session (id or name)."),
|
|
26482
|
+
updatedSince: z.string().optional().describe("Activity at/after: ISO time or age like '30m','24h','7d'."),
|
|
26483
|
+
startedSince: z.string().optional().describe("Started at/after: ISO time or age like '30m','24h','7d'."),
|
|
26484
|
+
excludeNoise: mcpBool2.optional().describe(
|
|
26485
|
+
'Drop review:*/wf:* sessions and ended command/plain-terminal runs. Details: tool_help {name:"session_list", topic:"excludeNoise"}'
|
|
26486
|
+
)
|
|
26487
|
+
};
|
|
26307
26488
|
|
|
26308
26489
|
// src/session-tools.ts
|
|
26309
26490
|
init_transcript_writer();
|
|
@@ -26646,15 +26827,15 @@ function computeHostWarnings(input) {
|
|
|
26646
26827
|
function createDefaultHostProbes() {
|
|
26647
26828
|
const platform = process.platform;
|
|
26648
26829
|
const own = /* @__PURE__ */ new Set();
|
|
26649
|
-
const run = (cmd, args, timeoutMs) => new Promise((
|
|
26830
|
+
const run = (cmd, args, timeoutMs) => new Promise((resolve47, reject) => {
|
|
26650
26831
|
const child = execFile(
|
|
26651
26832
|
cmd,
|
|
26652
26833
|
args,
|
|
26653
26834
|
{ maxBuffer: 16 * 1024 * 1024, timeout: timeoutMs, env: { ...process.env, LC_ALL: "C" } },
|
|
26654
26835
|
(err, stdout) => {
|
|
26655
|
-
if (!err) return
|
|
26836
|
+
if (!err) return resolve47(stdout);
|
|
26656
26837
|
if (err.killed) return reject(new Error(`${cmd} timed out after ${timeoutMs}ms`));
|
|
26657
|
-
return stdout ?
|
|
26838
|
+
return stdout ? resolve47(stdout) : reject(err);
|
|
26658
26839
|
}
|
|
26659
26840
|
);
|
|
26660
26841
|
if (child.pid !== void 0) own.add(child.pid);
|
|
@@ -27419,9 +27600,9 @@ async function callJevSystemOne(state, questions, opts) {
|
|
|
27419
27600
|
const answers = {};
|
|
27420
27601
|
const answerErrors = {};
|
|
27421
27602
|
for (const [key, value] of Object.entries(parsed.data.answers)) {
|
|
27422
|
-
const
|
|
27423
|
-
if (
|
|
27424
|
-
else answerErrors[key] = describeZodIssues(
|
|
27603
|
+
const answer2 = jevChoiceAnswerSchema.safeParse(value);
|
|
27604
|
+
if (answer2.success) answers[key] = answer2.data;
|
|
27605
|
+
else answerErrors[key] = describeZodIssues(answer2.error);
|
|
27425
27606
|
}
|
|
27426
27607
|
return {
|
|
27427
27608
|
ok: true,
|
|
@@ -27505,24 +27686,24 @@ async function judgeSessionWithJev(input) {
|
|
|
27505
27686
|
}
|
|
27506
27687
|
);
|
|
27507
27688
|
if (!outcome.ok) return fail4(outcome.error);
|
|
27508
|
-
const
|
|
27509
|
-
if (!
|
|
27689
|
+
const answer2 = outcome.answers[VERDICT_QUESTION_KEY];
|
|
27690
|
+
if (!answer2) {
|
|
27510
27691
|
const why = outcome.answerErrors?.[VERDICT_QUESTION_KEY];
|
|
27511
27692
|
return fail4(`response missing a valid verdict answer${why ? ` \u2014 ${why}` : ""}`);
|
|
27512
27693
|
}
|
|
27513
|
-
if (!WRAPUP_VERDICTS.includes(
|
|
27514
|
-
return fail4(`unknown verdict choice '${
|
|
27694
|
+
if (!WRAPUP_VERDICTS.includes(answer2.choice)) {
|
|
27695
|
+
return fail4(`unknown verdict choice '${answer2.choice}'`);
|
|
27515
27696
|
}
|
|
27516
|
-
const confidence =
|
|
27697
|
+
const confidence = answer2.confidence ?? answer2.probabilities[answer2.choice];
|
|
27517
27698
|
if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
27518
27699
|
return fail4("no usable confidence for the chosen verdict");
|
|
27519
27700
|
}
|
|
27520
27701
|
return {
|
|
27521
27702
|
ok: true,
|
|
27522
27703
|
sessionId: input.sessionId,
|
|
27523
|
-
verdict:
|
|
27704
|
+
verdict: answer2.choice,
|
|
27524
27705
|
confidence,
|
|
27525
|
-
probabilities:
|
|
27706
|
+
probabilities: answer2.probabilities,
|
|
27526
27707
|
model: outcome.model ?? model
|
|
27527
27708
|
};
|
|
27528
27709
|
}
|
|
@@ -27723,7 +27904,6 @@ function createBackgroundJobRegistry(opts) {
|
|
|
27723
27904
|
jobId: job.id,
|
|
27724
27905
|
status: "running",
|
|
27725
27906
|
startedAt: job.startedAt,
|
|
27726
|
-
resultPath: resultPathFor(job.id),
|
|
27727
27907
|
followUp: { tool: followUp.tool, args: { jobId: job.id }, pollAfterMs: JOB_POLL_AFTER_MS, hint: followUp.hint }
|
|
27728
27908
|
}),
|
|
27729
27909
|
progressView: (job) => job.status === "failed" ? { jobId: job.id, status: job.status, endedAt: job.endedAt, error: job.error } : {
|
|
@@ -27731,22 +27911,86 @@ function createBackgroundJobRegistry(opts) {
|
|
|
27731
27911
|
status: job.status,
|
|
27732
27912
|
startedAt: job.startedAt,
|
|
27733
27913
|
elapsedMs: Date.now() - job.startedMs,
|
|
27734
|
-
resultPath: resultPathFor(job.id),
|
|
27735
27914
|
followUp: { pollAfterMs: JOB_POLL_AFTER_MS }
|
|
27736
27915
|
}
|
|
27737
27916
|
};
|
|
27738
27917
|
}
|
|
27739
27918
|
function timedOutWaiting(promise, waitMs) {
|
|
27740
|
-
return new Promise((
|
|
27741
|
-
const handle = setTimeout(() =>
|
|
27919
|
+
return new Promise((resolve47) => {
|
|
27920
|
+
const handle = setTimeout(() => resolve47(true), waitMs);
|
|
27742
27921
|
const settled = () => {
|
|
27743
27922
|
clearTimeout(handle);
|
|
27744
|
-
|
|
27923
|
+
resolve47(false);
|
|
27745
27924
|
};
|
|
27746
27925
|
promise.then(settled, settled);
|
|
27747
27926
|
});
|
|
27748
27927
|
}
|
|
27749
27928
|
|
|
27929
|
+
// src/branch-gc.ts
|
|
27930
|
+
function summarizeBranchGcApply(result) {
|
|
27931
|
+
const zero = () => ({ deleted: 0, skipped: 0, failed: 0 });
|
|
27932
|
+
const byScope = { local: zero(), remote: zero(), orphan: zero() };
|
|
27933
|
+
const totals = zero();
|
|
27934
|
+
const byResult = {};
|
|
27935
|
+
let aborted = 0;
|
|
27936
|
+
for (const o of result.outcomes) {
|
|
27937
|
+
byResult[o.result] = (byResult[o.result] ?? 0) + 1;
|
|
27938
|
+
const bucket = o.result === "deleted" ? "deleted" : o.result === "failed" ? "failed" : "skipped";
|
|
27939
|
+
byScope[o.kind][bucket]++;
|
|
27940
|
+
totals[bucket]++;
|
|
27941
|
+
if (o.result.startsWith("aborted-")) aborted++;
|
|
27942
|
+
}
|
|
27943
|
+
const status = totals.deleted === 0 ? totals.failed > 0 ? "failed" : "noop" : totals.failed > 0 || aborted > 0 ? "partial" : "ok";
|
|
27944
|
+
return { status, totals, byScope, byResult, restoreLog: result.restoreLog ?? null };
|
|
27945
|
+
}
|
|
27946
|
+
function withBranchGcApplySummary(result) {
|
|
27947
|
+
if (result.mode !== "apply") return result;
|
|
27948
|
+
const applySummary = summarizeBranchGcApply(result);
|
|
27949
|
+
return { ...result, status: applySummary.status, applySummary };
|
|
27950
|
+
}
|
|
27951
|
+
var BRANCH_GC_PAGE_DEFAULT = 100;
|
|
27952
|
+
var BRANCH_GC_PAGE_MAX = 500;
|
|
27953
|
+
var encodeOffset = (n) => Buffer.from(`o:${n}`).toString("base64url");
|
|
27954
|
+
function decodeOffset(cursor) {
|
|
27955
|
+
const m = /^o:(\d+)$/.exec(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
27956
|
+
if (!m) throw new Error("branch_gc_status: invalid `cursor`");
|
|
27957
|
+
return Number(m[1]);
|
|
27958
|
+
}
|
|
27959
|
+
function sliceBranchGcResult(result, opts) {
|
|
27960
|
+
const section = opts.section ?? (result.mode === "apply" ? "outcomes" : "entries");
|
|
27961
|
+
const scopeOk = (kind) => !opts.scopes?.length || opts.scopes.includes(kind);
|
|
27962
|
+
let rows;
|
|
27963
|
+
if (section === "outcomes") {
|
|
27964
|
+
if (result.mode !== "apply") throw new Error("branch_gc_status: `section: outcomes` is only available on an apply result");
|
|
27965
|
+
rows = result.outcomes.filter((o) => scopeOk(o.kind) && (!opts.results?.length || opts.results.includes(o.result)));
|
|
27966
|
+
} else {
|
|
27967
|
+
rows = result.plan.entries.filter(
|
|
27968
|
+
(e) => scopeOk(e.kind) && (!opts.classes?.length || opts.classes.includes(e.class))
|
|
27969
|
+
);
|
|
27970
|
+
}
|
|
27971
|
+
const limit = Math.min(Math.max(Math.trunc(opts.limit ?? BRANCH_GC_PAGE_DEFAULT), 1), BRANCH_GC_PAGE_MAX);
|
|
27972
|
+
const start = opts.cursor !== void 0 ? decodeOffset(opts.cursor) : 0;
|
|
27973
|
+
const pageRows = rows.slice(start, start + limit);
|
|
27974
|
+
const next = start + limit;
|
|
27975
|
+
const page = {
|
|
27976
|
+
section,
|
|
27977
|
+
total: rows.length,
|
|
27978
|
+
returned: pageRows.length,
|
|
27979
|
+
...next < rows.length ? { nextCursor: encodeOffset(next) } : {}
|
|
27980
|
+
};
|
|
27981
|
+
const { plan, ...rest } = result;
|
|
27982
|
+
const { entries: _entries, ...planMeta } = plan;
|
|
27983
|
+
const { outcomes: _outcomes, ...restNoOutcomes } = rest;
|
|
27984
|
+
return {
|
|
27985
|
+
page,
|
|
27986
|
+
result: {
|
|
27987
|
+
...restNoOutcomes,
|
|
27988
|
+
plan: section === "entries" ? { ...planMeta, entries: pageRows } : planMeta,
|
|
27989
|
+
...section === "outcomes" ? { outcomes: pageRows } : {}
|
|
27990
|
+
}
|
|
27991
|
+
};
|
|
27992
|
+
}
|
|
27993
|
+
|
|
27750
27994
|
// src/session-tools.ts
|
|
27751
27995
|
init_command_allowlist();
|
|
27752
27996
|
init_agent_tools();
|
|
@@ -28245,7 +28489,7 @@ function buildSessionTree(sessions) {
|
|
|
28245
28489
|
});
|
|
28246
28490
|
return sessions.filter((s) => !s.parentSessionId || !idSet.has(s.parentSessionId)).sort((a, b) => a.startedAt.localeCompare(b.startedAt)).map(toNode);
|
|
28247
28491
|
}
|
|
28248
|
-
var
|
|
28492
|
+
var mcpBool3 = z.preprocess(
|
|
28249
28493
|
(v) => v === "true" ? true : v === "false" ? false : v,
|
|
28250
28494
|
z.boolean()
|
|
28251
28495
|
);
|
|
@@ -28366,25 +28610,27 @@ var sessionWrapupApplyJobs = createBackgroundJobRegistry({
|
|
|
28366
28610
|
var BACKGROUND_DEFAULT_WAIT_MS = 25e3;
|
|
28367
28611
|
var branchGcBackgroundView = (job) => branchGcJobs.backgroundView(job, {
|
|
28368
28612
|
tool: "branch_gc_status",
|
|
28369
|
-
hint: "Running in the background; a plan on a large repo takes a few minutes. Call branch_gc_status with this jobId about every 30 s. When done it returns the summary
|
|
28613
|
+
hint: "Running in the background; a plan on a large repo takes a few minutes. Call branch_gc_status with this jobId about every 30 s. When done it returns the summary and, once the file exists, a resultPath holding the full result (pass full: true to get a filtered/paged slice inline)."
|
|
28370
28614
|
});
|
|
28371
|
-
var branchGcDoneView = (jobId, resultPath, result,
|
|
28615
|
+
var branchGcDoneView = (jobId, resultPath, result, slice, endedAt) => ({
|
|
28372
28616
|
jobId,
|
|
28373
28617
|
status: "done",
|
|
28374
28618
|
...endedAt !== void 0 ? { endedAt } : {},
|
|
28375
|
-
resultPath,
|
|
28619
|
+
...resultPath !== void 0 ? { resultPath } : {},
|
|
28376
28620
|
summary: result.summary,
|
|
28377
|
-
// Apply results carry the restore log path
|
|
28378
|
-
// exactly what a caller needs
|
|
28379
|
-
// full ~MB result with `full: true`.
|
|
28621
|
+
// Apply results carry the restore log path, a per-outcome tally and the
|
|
28622
|
+
// per-scope deleted/skipped/failed summary — exactly what a caller needs
|
|
28623
|
+
// to decide "safe?" without fetching the full ~MB result with `full: true`.
|
|
28624
|
+
// Result files written before `applySummary` existed are summarised here.
|
|
28380
28625
|
...result.mode === "apply" ? {
|
|
28381
28626
|
restoreLog: result.restoreLog ?? null,
|
|
28382
28627
|
outcomeCounts: result.outcomes.reduce((acc, o) => {
|
|
28383
28628
|
acc[o.result] = (acc[o.result] ?? 0) + 1;
|
|
28384
28629
|
return acc;
|
|
28385
|
-
}, {})
|
|
28630
|
+
}, {}),
|
|
28631
|
+
applySummary: result.applySummary ?? summarizeBranchGcApply(result)
|
|
28386
28632
|
} : {},
|
|
28387
|
-
...
|
|
28633
|
+
...slice ? sliceBranchGcResult(result, slice) : {}
|
|
28388
28634
|
});
|
|
28389
28635
|
var worktreeGcBackgroundView = (job) => worktreeGcJobs.backgroundView(job, {
|
|
28390
28636
|
tool: "worktree_gc_status",
|
|
@@ -28424,7 +28670,7 @@ var backgroundStatusResult = async (toolName, jobs, jobId) => {
|
|
|
28424
28670
|
jobId: job.id,
|
|
28425
28671
|
status: "done",
|
|
28426
28672
|
endedAt: job.endedAt,
|
|
28427
|
-
resultPath:
|
|
28673
|
+
...job.resultPath !== void 0 ? { resultPath: job.resultPath } : {},
|
|
28428
28674
|
result: job.result
|
|
28429
28675
|
})
|
|
28430
28676
|
}
|
|
@@ -28471,35 +28717,29 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28471
28717
|
project: args.project,
|
|
28472
28718
|
keyOf: args.keyOf,
|
|
28473
28719
|
maxLimit: 200,
|
|
28474
|
-
itemKey: args.itemKey
|
|
28720
|
+
itemKey: args.itemKey,
|
|
28721
|
+
...args.defaultLimit !== void 0 ? { defaultLimit: args.defaultLimit } : {}
|
|
28475
28722
|
})
|
|
28476
28723
|
]
|
|
28477
28724
|
});
|
|
28478
28725
|
};
|
|
28479
28726
|
registerAgentTools(server, opts);
|
|
28480
28727
|
const sessionListSchema = z.object({
|
|
28481
|
-
kind: z.enum(["terminal", "agent-cli", "command", "all"]).optional().describe(
|
|
28482
|
-
|
|
28483
|
-
),
|
|
28484
|
-
includeCommands: z.boolean().optional().describe(
|
|
28485
|
-
"When true and `kind` is unset/`all`, also include `kind:'command'` rows in the result (they're excluded by default \u2014 see `kind`). No effect when `kind` is set explicitly. Default false."
|
|
28486
|
-
),
|
|
28728
|
+
kind: z.enum(["terminal", "agent-cli", "command", "all"]).optional().describe("Filter by kind. `all` (default) = terminal + agent-cli, without `command` unless `includeCommands`."),
|
|
28729
|
+
includeCommands: z.boolean().optional().describe("With kind unset/`all`, also include `kind:'command'` rows. Default false."),
|
|
28487
28730
|
onlyAlive: z.boolean().optional().describe("When true, only running/starting sessions. Default false."),
|
|
28488
28731
|
status: z.enum(["starting", "running", "exited", "killed", "error"]).optional().describe("Filter by exact status (overrides onlyAlive)."),
|
|
28489
|
-
includeArchived: z.boolean().optional().describe(
|
|
28490
|
-
|
|
28491
|
-
),
|
|
28492
|
-
withMemory: z.boolean().optional().describe(
|
|
28493
|
-
"When true, add `rssBytes` (summed RSS in bytes of the process tree, via `ps`) to every live session that has a pid. Default false \u2014 a distinct opt-in from `full`/`compact` since it costs one `ps` spawn per call; omitted otherwise, so a plain listing never pays for it."
|
|
28494
|
-
),
|
|
28732
|
+
includeArchived: z.boolean().optional().describe("Also include archived sessions. Default false."),
|
|
28733
|
+
withMemory: z.boolean().optional().describe("Add `rssBytes` (process-tree RSS) to live sessions with a pid; one `ps` per call. Default false."),
|
|
28495
28734
|
stats: statsParamSchema.describe(
|
|
28496
|
-
'
|
|
28735
|
+
'Per-live-session RSS, %CPU, process count and top commands under `stats`; `true` = summary, `"full"` = every process. Cached ~3s. Host-level view: `session_stats`.'
|
|
28497
28736
|
),
|
|
28737
|
+
...sessionListFilterShape,
|
|
28498
28738
|
...pageParamsShape
|
|
28499
28739
|
});
|
|
28500
28740
|
registerBuiltinTool(server, {
|
|
28501
28741
|
id: "session_list",
|
|
28502
|
-
description: "List sessions tracked by the daemon
|
|
28742
|
+
description: "List sessions tracked by the daemon (agent-CLI and terminal/PTY). Use it to see what's already running before spawning, or to find a session id by name. NARROW BEFORE YOU READ: hundreds of sessions are mostly noise and without `limit` all come back. Filter (`q`, `excludeNoise`, `rootOnly`, `updatedSince`, \u2026) and/or pass `limit`; rows are newest-activity first, `total` is the filtered count. E.g. `{q:'X', excludeNoise:true, limit:10}`. Rows are COMPACT by default (`fields:[\u2026]` picks keys, `full:true` returns everything). Detail: tool_help {name:\"session_list\"}; ranked text search: `session_search`.",
|
|
28503
28743
|
inputSchema: sessionListSchema,
|
|
28504
28744
|
handler: async (input) => {
|
|
28505
28745
|
let rows = registry.list({ includeArchived: true });
|
|
@@ -28522,6 +28762,12 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28522
28762
|
(s) => s.status === "running" || s.status === "starting"
|
|
28523
28763
|
);
|
|
28524
28764
|
}
|
|
28765
|
+
const { parentSessionId, ...filterRest } = pickSessionListFilters(input);
|
|
28766
|
+
rows = applySessionListFilters(rows, {
|
|
28767
|
+
...filterRest,
|
|
28768
|
+
...parentSessionId ? { parentSessionId: registry.findByIdOrName(parentSessionId)?.id ?? parentSessionId } : {}
|
|
28769
|
+
});
|
|
28770
|
+
rows = sortNewestActivityFirst(rows);
|
|
28525
28771
|
if (input.withMemory) {
|
|
28526
28772
|
const live = rows.filter(
|
|
28527
28773
|
(s) => typeof s.pid === "number" && (s.status === "running" || s.status === "starting")
|
|
@@ -28546,11 +28792,13 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28546
28792
|
return rows.map(publicSessionDescriptor);
|
|
28547
28793
|
},
|
|
28548
28794
|
transformers: [
|
|
28795
|
+
catchErrors(),
|
|
28549
28796
|
paginated({
|
|
28550
28797
|
project: compactSessionItem,
|
|
28551
28798
|
keyOf: (s) => s.id,
|
|
28552
28799
|
maxLimit: 200,
|
|
28553
|
-
itemKey: "sessions"
|
|
28800
|
+
itemKey: "sessions",
|
|
28801
|
+
includeTotal: true
|
|
28554
28802
|
})
|
|
28555
28803
|
]
|
|
28556
28804
|
});
|
|
@@ -28620,7 +28868,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28620
28868
|
description: 'Resource usage per session, sampled from the OS process table: RSS bytes, %CPU, process count and top commands by RSS for every live session, sorted by RSS (each row labelled with the session\'s `label`/`name`), plus `daemon` (the daemon process + its own children), `provisioning` (worktree setup work such as `pnpm install` not yet attached to a session, with `inFlight` provisions), `orphans` (processes that look agentproto-owned but belong to no live session - reported only, never killed), `totals`, and `host` (load average, free/total memory). `detail:"full"` adds every process (pid, command, RSS, CPU, elapsed) per row. Cached ~3s; `fresh:true` forces a new sample. A subtree-scoped caller sees only its own sessions (no daemon/provisioning/orphan buckets).',
|
|
28621
28869
|
inputSchema: z.object({
|
|
28622
28870
|
detail: z.enum(["summary", "full"]).optional().describe('Default "summary".'),
|
|
28623
|
-
fresh:
|
|
28871
|
+
fresh: mcpBool3.optional().describe("Bypass the ~3s cache and sample now.")
|
|
28624
28872
|
}),
|
|
28625
28873
|
handler: async (input) => {
|
|
28626
28874
|
const all = registry.list({ includeArchived: true });
|
|
@@ -28640,7 +28888,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28640
28888
|
description: 'Host-level load report: load average vs core count, CPU user/sys/idle, RAM (used/wired/compressor/free) and swap, per-disk transfers/s + MB/s, the top 10 processes by CPU and by memory footprint (compressed pages included on macOS) each tagged with its owning session (`session`/`daemon`/`provisioning`/`orphan`/`system`/`other`), and `warnings` (swap > 50%, orphaned processes older than 30 min that are busy or serving, deleted-cwd processes, filesystem-wide `find`/`bfs`/`du` scans, several servers on one port, load > 4x cores). `detail:"full"` adds a per-session rollup and every process. Bounded to ~2s: a probe that is slow or unavailable is named in `partial` and the rest still ships (raise `budgetMs` to give the macOS `top` footprint probe longer on a saturated host). Cached ~2s; `fresh:true` resamples. Never needs sudo. A subtree-scoped caller sees host metrics plus only its own sessions\' processes.',
|
|
28641
28889
|
inputSchema: z.object({
|
|
28642
28890
|
detail: z.enum(["summary", "full"]).optional().describe('Default "summary".'),
|
|
28643
|
-
fresh:
|
|
28891
|
+
fresh: mcpBool3.optional().describe("Bypass the ~2s cache and sample now."),
|
|
28644
28892
|
budgetMs: z.coerce.number().int().min(300).max(6e4).optional().describe("Time budget for the sample in ms (default 1900).")
|
|
28645
28893
|
}),
|
|
28646
28894
|
handler: async (input) => {
|
|
@@ -28659,7 +28907,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
28659
28907
|
"session_continue_interrupted",
|
|
28660
28908
|
"List the sessions the LAST daemon restart cut off mid-turn (`interrupted: true`) and \u2014 with `dryRun: false` \u2014 send each a one-shot \"continue\" prompt (the interrupted prompt itself is never re-run). Goes through the normal prompt path, so a dead-but-resumable session resumes in place first. Skips sessions that aren't interrupted, were interrupted by an older restart, aren't resumable, hit the resume attempt cap, are already busy, or whose cwd no longer exists. Returns a per-session outcome (`eligible` on a dry run, else `sent`/`skipped` with a reason/`failed`).",
|
|
28661
28909
|
{
|
|
28662
|
-
dryRun:
|
|
28910
|
+
dryRun: mcpBool3.optional().describe("Default true: only report what would be sent. Pass false to send."),
|
|
28663
28911
|
ids: z.array(z.string().min(1)).optional().describe(
|
|
28664
28912
|
"Restrict to these sessions (id or name). Omitted \u21D2 every session interrupted by the last restart."
|
|
28665
28913
|
),
|
|
@@ -29218,7 +29466,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
29218
29466
|
});
|
|
29219
29467
|
registerPaginatedListTool({
|
|
29220
29468
|
id: "terminal_sessions_list",
|
|
29221
|
-
description: "List terminal/PTY sessions tracked by the daemon. Equivalent to `session_list({kind: 'terminal'})`. Each entry includes `kind`, `pty`, `status`, age, etc. Use this when you only want the terminal subset. COMPACT BY DEFAULT: each entry is session_list's slim projection; pass `full: true` (or `compact: false`) for the complete, unprojected per-session record.",
|
|
29469
|
+
description: "List terminal/PTY sessions tracked by the daemon. Equivalent to `session_list({kind: 'terminal'})`. Each entry includes `kind`, `pty`, `status`, age, etc. Use this when you only want the terminal subset. COMPACT BY DEFAULT: each entry is session_list's slim projection; pass `full: true` (or `compact: false`) for the complete, unprojected per-session record. Without `limit`/`cursor` at most 50 rows come back; when there are more the reply carries `total`, `truncated: true` and a `nextCursor` \u2014 pass it as `cursor` (with a `limit`) for the rest, or filter with `onlyAlive` / `status`.",
|
|
29222
29470
|
schema: terminalSessionsListSchema,
|
|
29223
29471
|
body: async (input) => {
|
|
29224
29472
|
let rows = registry.list({ includeArchived: true });
|
|
@@ -29242,7 +29490,10 @@ function registerSessionTools(rawServer, opts) {
|
|
|
29242
29490
|
},
|
|
29243
29491
|
project: compactSessionItemWithProvenance,
|
|
29244
29492
|
keyOf: (s) => s.id,
|
|
29245
|
-
itemKey: "sessions"
|
|
29493
|
+
itemKey: "sessions",
|
|
29494
|
+
// Dead terminals pile up: an unpaged call used to return every row
|
|
29495
|
+
// (65k+ chars, over the MCP output cap). Cap it and say so.
|
|
29496
|
+
defaultLimit: 50
|
|
29246
29497
|
});
|
|
29247
29498
|
const commandListSchema = z.object({
|
|
29248
29499
|
kind: z.enum(["terminal", "agent-cli", "command", "all"]).optional().describe(
|
|
@@ -29974,7 +30225,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
29974
30225
|
workspaceSlug: z.string().optional().describe(
|
|
29975
30226
|
"Workspace slug from `agentproto workspace list`. Resolves the repo root via the active workspace when omitted."
|
|
29976
30227
|
),
|
|
29977
|
-
openOnly:
|
|
30228
|
+
openOnly: mcpBool3.optional().describe(
|
|
29978
30229
|
"When true, only return worktrees whose `pr.state` is `open`. Default false."
|
|
29979
30230
|
),
|
|
29980
30231
|
sessionId: z.string().optional().describe(
|
|
@@ -30025,19 +30276,19 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30025
30276
|
workspaceSlug: z.string().optional().describe(
|
|
30026
30277
|
"Workspace slug from `agentproto workspace list`. Resolves the repo root via the active workspace when omitted."
|
|
30027
30278
|
),
|
|
30028
|
-
apply:
|
|
30279
|
+
apply: mcpBool3.optional().describe(
|
|
30029
30280
|
"When true, EXECUTE the plan (reclaim/salvage). Default false \u2014 a dry run that returns the plan and mutates nothing."
|
|
30030
30281
|
),
|
|
30031
|
-
salvageDirty:
|
|
30282
|
+
salvageDirty: mcpBool3.optional().describe(
|
|
30032
30283
|
"When true, archive (snapshot-then-remove) every `salvage`-class worktree during `apply`. Default false \u2014 salvage entries are left untouched. Ignored on a dry run."
|
|
30033
30284
|
),
|
|
30034
|
-
includeDetached:
|
|
30285
|
+
includeDetached: mcpBool3.optional().describe(
|
|
30035
30286
|
"When true, a clean, idle detached worktree is reclaimed instead of held. Default false. This is the only flag that can promote an entry toward reclaim; no flag ever weakens a hold otherwise."
|
|
30036
30287
|
),
|
|
30037
30288
|
noisePaths: z.array(z.string()).optional().describe(
|
|
30038
30289
|
"Worktree-relative paths whose dirt is known noise (a worktree dirty ONLY on these counts as clean). Default `.opencode/package-lock.json`; pass [] to disable."
|
|
30039
30290
|
),
|
|
30040
|
-
wait:
|
|
30291
|
+
wait: mcpBool3.optional().describe(
|
|
30041
30292
|
"false \u21D2 return a jobId immediately; poll `worktree_gc_status`. true \u21D2 block until the gc finishes (no background fallback). Default: wait up to `waitMs`, then fall back to background."
|
|
30042
30293
|
),
|
|
30043
30294
|
waitMs: mcpNumber.optional().describe(
|
|
@@ -30124,19 +30375,19 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30124
30375
|
const branchGcKind = z.enum(["local", "remote", "orphan"]);
|
|
30125
30376
|
server.tool(
|
|
30126
30377
|
"branch_gc",
|
|
30127
|
-
"Garbage-collect a repo's branches. DEFAULTS TO A DRY RUN: returns a plan classifying every local branch, base-remote branch and orphan tracking ref (refs/remotes/<ns>/* of a removed remote) as `reclaim` (work provably in base: the head commit of a merged PR, merged, squash-merged, patch-merged or content-merged), `review` (unmerged, old enough, not protected \u2014 carries coverage + the files not provably in base for a reviewer), or `hold` (base/protected, checked out in a worktree or its remote twin, open PR head, PR check unavailable, or younger than `minAgeDays`). `apply: true` (requires explicit `scopes`) deletes only `reclaim` entries, re-classifying each right before deleting it, and returns the path of a restore log (sha + re-create command per deleted ref). A plan can take minutes on a big repo: as an MCP caller, pass `wait: false` (or `waitMs: 40000`) and poll `branch_gc_status` with the returned jobId instead of blocking. A `wait: false` (or `waitMs` timed-out) response returns a jobId plus a `followUp` block naming the poll tool and cadence \u2014 poll `branch_gc_status` with that jobId.",
|
|
30378
|
+
"Garbage-collect a repo's branches. DEFAULTS TO A DRY RUN: returns a plan classifying every local branch, base-remote branch and orphan tracking ref (refs/remotes/<ns>/* of a removed remote) as `reclaim` (work provably in base: the head commit of a merged PR, merged, squash-merged, patch-merged or content-merged), `review` (unmerged, old enough, not protected \u2014 carries coverage + the files not provably in base for a reviewer), or `hold` (base/protected, checked out in a worktree or its remote twin, open PR head, PR check unavailable, or younger than `minAgeDays`). `apply: true` (requires explicit `scopes`) deletes only `reclaim` entries, re-classifying each right before deleting it, and returns the path of a restore log (sha + re-create command per deleted ref) plus a top-level `status` and an `applySummary` (deleted / skipped / failed counts per scope, and the restore log path). A plan can take minutes on a big repo: as an MCP caller, pass `wait: false` (or `waitMs: 40000`) and poll `branch_gc_status` with the returned jobId instead of blocking. A `wait: false` (or `waitMs` timed-out) response returns a jobId plus a `followUp` block naming the poll tool and cadence \u2014 poll `branch_gc_status` with that jobId.",
|
|
30128
30379
|
{
|
|
30129
30380
|
repoRoot: z.string().optional().describe("Absolute path to the git repo. Wins over `workspaceSlug`."),
|
|
30130
30381
|
workspaceSlug: z.string().optional().describe("Workspace slug from `agentproto workspace list`. The active workspace when omitted."),
|
|
30131
30382
|
base: z.string().optional().describe("Base ref the work must be in. Default `origin/main`."),
|
|
30132
30383
|
scopes: z.array(branchGcKind).optional().describe("Ref kinds to consider: local, remote, orphan. Default all three for a plan; REQUIRED for apply."),
|
|
30133
30384
|
minAgeDays: mcpNumber.optional().describe("Unmerged refs younger than this many days are held. Default 3."),
|
|
30134
|
-
includeReviewed:
|
|
30385
|
+
includeReviewed: mcpBool3.optional().describe(
|
|
30135
30386
|
"When true, a `review` ref whose stored verdict (branch_gc_verdict) has gate.agree=true for the SAME tip sha becomes `reclaim` (`reviewed`). Default false."
|
|
30136
30387
|
),
|
|
30137
30388
|
anchor: z.string().optional().describe("Explicit anchor commit for a re-rooted base. Auto-detected when omitted."),
|
|
30138
|
-
apply:
|
|
30139
|
-
wait:
|
|
30389
|
+
apply: mcpBool3.optional().describe("When true, EXECUTE the plan for `scopes`. Default false \u2014 a dry run."),
|
|
30390
|
+
wait: mcpBool3.optional().describe("Block until the gc finishes (default true \u2014 today's behaviour). false \u21D2 return a jobId immediately; poll `branch_gc_status`."),
|
|
30140
30391
|
waitMs: mcpNumber.optional().describe('Block at most this many milliseconds, then fall back to background: returns `{ jobId, status: "running" }` to poll with `branch_gc_status`. Only meaningful with the default `wait: true`.')
|
|
30141
30392
|
},
|
|
30142
30393
|
async (input) => {
|
|
@@ -30174,7 +30425,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30174
30425
|
...input.minAgeDays !== void 0 ? { minAgeDays: input.minAgeDays } : {},
|
|
30175
30426
|
...input.anchor ? { anchor: input.anchor } : {}
|
|
30176
30427
|
};
|
|
30177
|
-
const { job, promise } = branchGcJobs.start(() => runBranchGc(runInput));
|
|
30428
|
+
const { job, promise } = branchGcJobs.start(async () => withBranchGcApplySummary(await runBranchGc(runInput)));
|
|
30178
30429
|
if (input.wait === false) {
|
|
30179
30430
|
return { content: [{ type: "text", text: JSON.stringify(branchGcBackgroundView(job)) }] };
|
|
30180
30431
|
}
|
|
@@ -30193,12 +30444,36 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30193
30444
|
);
|
|
30194
30445
|
server.tool(
|
|
30195
30446
|
"branch_gc_status",
|
|
30196
|
-
|
|
30447
|
+
'Poll a branch_gc run started with `wait: false` (or one that fell back to the background via `waitMs`). While running: status + elapsed time. When done: the plan\'s own summary (plus, for an apply, `applySummary` \u2014 deleted/skipped/failed per scope and the restore log path) and the path of the full result saved on disk (`resultPath`). The full result is too big to return whole, so `full: true` (or any of `section`, `classes`, `scopes`, `results`, `limit`, `cursor`) returns ONE filtered page of it: `result` (summary + plan metadata + the selected list) and `page { section, total, returned, nextCursor }` \u2014 default 100 rows, pass `nextCursor` back as `cursor` for the next page. Use `classes: ["reclaim"]` to see only what an apply would delete. While running the view carries `followUp.pollAfterMs` (poll every 30 s). When failed: the error.',
|
|
30197
30448
|
{
|
|
30198
30449
|
jobId: z.string().describe("Job id returned by `branch_gc` (`bgc_\u2026`)."),
|
|
30199
|
-
full:
|
|
30450
|
+
full: mcpBool3.optional().describe("Include a filtered/paged slice of the full result (see description). Implied by any other slice param. Default false."),
|
|
30451
|
+
section: z.enum(["entries", "outcomes"]).optional().describe("Which list to return: plan `entries` or apply `outcomes`. Default `entries` for a plan, `outcomes` for an apply."),
|
|
30452
|
+
classes: z.array(z.enum(["reclaim", "review", "hold"])).optional().describe('Entries only: keep these classes (e.g. ["reclaim"]).'),
|
|
30453
|
+
scopes: z.array(branchGcKind).optional().describe("Keep these ref kinds (local, remote, orphan)."),
|
|
30454
|
+
results: z.array(
|
|
30455
|
+
z.enum(["deleted", "held", "skipped-review", "aborted-moved", "aborted-vanished", "aborted-reclassified", "failed"])
|
|
30456
|
+
).optional().describe("Outcomes only: keep these outcome results."),
|
|
30457
|
+
limit: mcpNumber.optional().describe(`Rows per page. Default 100, max ${BRANCH_GC_PAGE_MAX}.`),
|
|
30458
|
+
cursor: z.string().optional().describe("`page.nextCursor` from the previous page.")
|
|
30200
30459
|
},
|
|
30201
30460
|
async (input) => {
|
|
30461
|
+
const wantsSlice = input.full === true || input.section !== void 0 || input.classes !== void 0 || input.scopes !== void 0 || input.results !== void 0 || input.limit !== void 0 || input.cursor !== void 0;
|
|
30462
|
+
const slice = wantsSlice ? {
|
|
30463
|
+
...input.section ? { section: input.section } : {},
|
|
30464
|
+
...input.classes ? { classes: input.classes } : {},
|
|
30465
|
+
...input.scopes ? { scopes: input.scopes } : {},
|
|
30466
|
+
...input.results ? { results: input.results } : {},
|
|
30467
|
+
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
30468
|
+
...input.cursor !== void 0 ? { cursor: input.cursor } : {}
|
|
30469
|
+
} : void 0;
|
|
30470
|
+
const doneView = (...args) => {
|
|
30471
|
+
try {
|
|
30472
|
+
return { content: [{ type: "text", text: JSON.stringify(branchGcDoneView(...args)) }] };
|
|
30473
|
+
} catch (err) {
|
|
30474
|
+
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
30475
|
+
}
|
|
30476
|
+
};
|
|
30202
30477
|
const job = branchGcJobs.get(input.jobId);
|
|
30203
30478
|
if (!job) {
|
|
30204
30479
|
const parsed = await branchGcJobs.readResultFile(input.jobId);
|
|
@@ -30213,14 +30488,12 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30213
30488
|
isError: true
|
|
30214
30489
|
};
|
|
30215
30490
|
}
|
|
30216
|
-
|
|
30217
|
-
return { content: [{ type: "text", text: JSON.stringify(view3) }] };
|
|
30491
|
+
return doneView(input.jobId, branchGcJobs.resultPathFor(input.jobId), parsed, slice);
|
|
30218
30492
|
}
|
|
30219
30493
|
if (job.status !== "done") {
|
|
30220
30494
|
return { content: [{ type: "text", text: JSON.stringify(branchGcJobs.progressView(job)) }] };
|
|
30221
30495
|
}
|
|
30222
|
-
|
|
30223
|
-
return { content: [{ type: "text", text: JSON.stringify(view2) }] };
|
|
30496
|
+
return doneView(job.id, job.resultPath, job.result, slice, job.endedAt);
|
|
30224
30497
|
}
|
|
30225
30498
|
);
|
|
30226
30499
|
const branchVerdictEnum = z.enum(["obsolete", "superseded", "salvage", "in-progress", "unclear"]);
|
|
@@ -30239,7 +30512,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30239
30512
|
salvage: z.string().optional().describe("What is worth keeping, if anything.")
|
|
30240
30513
|
}),
|
|
30241
30514
|
gate: z.object({
|
|
30242
|
-
agree:
|
|
30515
|
+
agree: mcpBool3.describe("true only if deleting the branch loses nothing of value"),
|
|
30243
30516
|
verdict: branchVerdictEnum,
|
|
30244
30517
|
reason: z.string(),
|
|
30245
30518
|
evidence: z.array(z.string()).describe("Concrete shas/paths. Required non-empty when agree is true.")
|
|
@@ -30852,10 +31125,10 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30852
31125
|
"DRY RUN, mutates nothing \u2014 classify every idle agent-cli session for the session steward: `close` (safe to auto-close: idle past `idleMinutes` AND the session's worktree merged or its parent already ended, with no tool call pending, and not `keepAlive`), `stuck` (stuck in `status:'starting'` with no pid for 10+ minutes \u2014 closing it is free, it never ran), `judge` (idle-enough but ambiguous \u2014 no merge/parent-ended signal, a pending tool call, or a `keepAlive` session that would otherwise close, since `keepAlive` can only ever reach `judge`, never `close`), or `keep` (not idle long enough yet, or busy/awaitingInput/awaitingPermission/archived/pinned/has busy children/has a live parent/is the caller's own session \u2014 NEVER eligible for `session_wrapup_apply`; omitted here unless `includeKeep` is set). Also reports `rssBytes` (process-tree RSS) per entry and summed per class in `totals`. Feed `close`/`stuck` ids straight to `session_wrapup_apply`; `judge` ids need a judge's verdict first (FIX-9B). Computing worktree merge status can take minutes: this call waits up to 25 s (`waitMs`), then returns `{ jobId, status: \"running\", followUp }` \u2014 poll `session_wrapup_status` with that jobId.",
|
|
30853
31126
|
{
|
|
30854
31127
|
idleMinutes: z.number().int().positive().optional().describe("Idle threshold in minutes a `close` candidate must clear. Default 20."),
|
|
30855
|
-
includeKeep:
|
|
31128
|
+
includeKeep: mcpBool3.optional().describe(
|
|
30856
31129
|
"Include `keep`-class entries too. Default false \u2014 `keep` rows are never acted on, so they're omitted to keep the plan focused on what the steward might actually do."
|
|
30857
31130
|
),
|
|
30858
|
-
wait:
|
|
31131
|
+
wait: mcpBool3.optional().describe(
|
|
30859
31132
|
"false \u21D2 return a jobId immediately; poll `session_wrapup_status`. true \u21D2 block until the plan is computed. Default: wait up to `waitMs`, then fall back to background."
|
|
30860
31133
|
),
|
|
30861
31134
|
waitMs: mcpNumber.optional().describe("Block at most this many milliseconds, then fall back to background. Default 25000.")
|
|
@@ -30921,7 +31194,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
30921
31194
|
judgedBy: z.string().optional().describe(
|
|
30922
31195
|
"A judge session id. When set, the outcome's `source` is `'judged'` and a `judge`-class session also becomes eligible (not just `close`/`stuck`). Omitted \u21D2 `source:'declared'`, `judgedBy:'steward-rules'`, and only `close`/`stuck` are eligible."
|
|
30923
31196
|
),
|
|
30924
|
-
wait:
|
|
31197
|
+
wait: mcpBool3.optional().describe(
|
|
30925
31198
|
"false \u21D2 return a jobId immediately; poll `session_wrapup_status`. true \u21D2 block until done. Default: wait up to `waitMs`, then fall back to background (the apply keeps running)."
|
|
30926
31199
|
),
|
|
30927
31200
|
waitMs: mcpNumber.optional().describe("Block at most this many milliseconds, then fall back to background. Default 25000.")
|
|
@@ -31372,7 +31645,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
31372
31645
|
"Set or clear a session's idle-reaper exemption. When `keepAlive` is true, the idle-reaper (`isReapable`) never auto-retires this session no matter how long it sits idle \u2014 for a supervisor that legitimately parks waiting on a child or a scheduled wake, which otherwise looks identical to a finished session. Set false to clear the exemption. Persists across daemon restarts. Does NOT touch the running agent.",
|
|
31373
31646
|
{
|
|
31374
31647
|
idOrName: z.string().min(1).describe("Session id or name to update \u2014 from `session_list`."),
|
|
31375
|
-
keepAlive:
|
|
31648
|
+
keepAlive: mcpBool3.describe(
|
|
31376
31649
|
"true to exempt this session from the idle-reaper, false to clear the exemption."
|
|
31377
31650
|
)
|
|
31378
31651
|
},
|
|
@@ -31434,7 +31707,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
31434
31707
|
"Set or clear a session's list-visibility pin. When `pinned` is true, the session sorts to the top of `agentproto sessions` and the VS Code sessions webview's dedicated Pinned group. Set false to clear it. Persists across daemon restarts. Purely a sort/display flag \u2014 does NOT touch the idle-reaper, keepAlive, or emit any notification, and does NOT touch the running agent.",
|
|
31435
31708
|
{
|
|
31436
31709
|
idOrName: z.string().min(1).describe("Session id or name to update \u2014 from `session_list`."),
|
|
31437
|
-
pinned:
|
|
31710
|
+
pinned: mcpBool3.describe(
|
|
31438
31711
|
"true to pin this session to the top of the list, false to unpin it."
|
|
31439
31712
|
)
|
|
31440
31713
|
},
|
|
@@ -31719,7 +31992,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
31719
31992
|
{
|
|
31720
31993
|
idOrName: z.string().min(1).describe("Session id or name \u2014 from `session_list`."),
|
|
31721
31994
|
key: z.string().min(1).describe("Artifact key, from `session_artifact_list`."),
|
|
31722
|
-
pinned:
|
|
31995
|
+
pinned: mcpBool3.describe("true to pin, false to unpin.")
|
|
31723
31996
|
},
|
|
31724
31997
|
async (input) => {
|
|
31725
31998
|
const prev = registry.findByIdOrName(input.idOrName);
|
|
@@ -31940,7 +32213,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
31940
32213
|
lastBytes: z.number().int().min(1).max(64 * 1024).optional().describe(
|
|
31941
32214
|
"Max bytes from the tail. Default 4096; the full ~64 KiB ring remains reachable by passing `lastBytes: 65536` explicitly."
|
|
31942
32215
|
),
|
|
31943
|
-
clean:
|
|
32216
|
+
clean: mcpBool3.optional().describe(
|
|
31944
32217
|
"Strip ANSI codes, returning human-readable text (as `text` instead of `b64`). Default false = raw base64."
|
|
31945
32218
|
)
|
|
31946
32219
|
},
|
|
@@ -32032,7 +32305,7 @@ function registerSessionTools(rawServer, opts) {
|
|
|
32032
32305
|
|
|
32033
32306
|
// src/index.ts
|
|
32034
32307
|
init_agent_tools();
|
|
32035
|
-
var
|
|
32308
|
+
var mcpBool4 = z.preprocess(
|
|
32036
32309
|
(v) => v === "true" ? true : v === "false" ? false : v,
|
|
32037
32310
|
z.boolean()
|
|
32038
32311
|
);
|
|
@@ -32117,10 +32390,10 @@ function registerBrainTools(server, opts) {
|
|
|
32117
32390
|
{
|
|
32118
32391
|
workspace: z.string().optional().describe("Workspace slug. Omit to use the calling session's workspace."),
|
|
32119
32392
|
sessionId: z.string().optional().describe("Ingest one specific session (sess_xxx) instead of the backlog."),
|
|
32120
|
-
reindex:
|
|
32393
|
+
reindex: mcpBool4.optional().describe(
|
|
32121
32394
|
'When `sessionId` is set and it was already ingested, force a re-ingest from the current transcript. Default false. Pass `true` or the string "true" to enable.'
|
|
32122
32395
|
),
|
|
32123
|
-
reindexStale:
|
|
32396
|
+
reindexStale: mcpBool4.optional().describe(
|
|
32124
32397
|
'When `sessionId` is NOT set (backlog mode), also force-reingest every already-indexed session whose pipeline version is older than the code running now. Default false. Pass `true` or the string "true" to enable.'
|
|
32125
32398
|
)
|
|
32126
32399
|
},
|
|
@@ -32318,7 +32591,7 @@ function createWorkspaceBrainSubscriber(opts) {
|
|
|
32318
32591
|
}
|
|
32319
32592
|
};
|
|
32320
32593
|
}
|
|
32321
|
-
function makeBrowserHandle(entry,
|
|
32594
|
+
function makeBrowserHandle(entry, resolve47) {
|
|
32322
32595
|
return {
|
|
32323
32596
|
slug: entry.id,
|
|
32324
32597
|
name: entry.name,
|
|
@@ -32329,7 +32602,7 @@ function makeBrowserHandle(entry, resolve45) {
|
|
|
32329
32602
|
// check() is available for on-demand health probes but is never called
|
|
32330
32603
|
// by the lister (kit invariant OQ-5). Returns true when the adapter
|
|
32331
32604
|
// is present in the injected resolver map, false otherwise.
|
|
32332
|
-
check: async () =>
|
|
32605
|
+
check: async () => resolve47 ? resolve47(entry.id) != null : true
|
|
32333
32606
|
};
|
|
32334
32607
|
}
|
|
32335
32608
|
var noopLedger = {
|
|
@@ -34936,7 +35209,7 @@ function startHeartbeat(opts) {
|
|
|
34936
35209
|
let timer = null;
|
|
34937
35210
|
let currentEveryMs = null;
|
|
34938
35211
|
let firing = false;
|
|
34939
|
-
async function
|
|
35212
|
+
async function load2() {
|
|
34940
35213
|
if (!existsSync(path)) return null;
|
|
34941
35214
|
let source;
|
|
34942
35215
|
try {
|
|
@@ -34967,7 +35240,7 @@ function startHeartbeat(opts) {
|
|
|
34967
35240
|
firing = true;
|
|
34968
35241
|
let agentId;
|
|
34969
35242
|
try {
|
|
34970
|
-
const hb = await
|
|
35243
|
+
const hb = await load2();
|
|
34971
35244
|
if (!hb || !hb.enabled) return;
|
|
34972
35245
|
agentId = hb.agent;
|
|
34973
35246
|
const agent = await opts.buildAgent(hb.agent);
|
|
@@ -35055,7 +35328,7 @@ function startHeartbeat(opts) {
|
|
|
35055
35328
|
return {
|
|
35056
35329
|
start() {
|
|
35057
35330
|
if (timer) return;
|
|
35058
|
-
void
|
|
35331
|
+
void load2().then((hb) => {
|
|
35059
35332
|
const everyMs = hb?.everyMs ?? 6e4;
|
|
35060
35333
|
reschedule(everyMs);
|
|
35061
35334
|
});
|
|
@@ -35104,6 +35377,356 @@ function preview(text13, max = 120) {
|
|
|
35104
35377
|
// src/http-server.ts
|
|
35105
35378
|
init_process_stats();
|
|
35106
35379
|
init_browser_mount();
|
|
35380
|
+
|
|
35381
|
+
// src/mcp-modern/constants.ts
|
|
35382
|
+
var MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
35383
|
+
var DEFAULT_SUPPORTED_VERSIONS = [MODERN_PROTOCOL_VERSION];
|
|
35384
|
+
var META_PROTOCOL_VERSION = "io.modelcontextprotocol/protocolVersion";
|
|
35385
|
+
var META_CLIENT_INFO = "io.modelcontextprotocol/clientInfo";
|
|
35386
|
+
var META_CLIENT_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
|
|
35387
|
+
var META_SERVER_INFO = "io.modelcontextprotocol/serverInfo";
|
|
35388
|
+
var ERR_PARSE = -32700;
|
|
35389
|
+
var ERR_INVALID_REQUEST = -32600;
|
|
35390
|
+
var ERR_METHOD_NOT_FOUND = -32601;
|
|
35391
|
+
var ERR_INVALID_PARAMS = -32602;
|
|
35392
|
+
var ERR_INTERNAL = -32603;
|
|
35393
|
+
var ERR_HEADER_MISMATCH = -32020;
|
|
35394
|
+
var ERR_UNSUPPORTED_VERSION = -32022;
|
|
35395
|
+
var FORWARDED_METHODS = /* @__PURE__ */ new Set([
|
|
35396
|
+
"tools/list",
|
|
35397
|
+
"tools/call",
|
|
35398
|
+
"events/list",
|
|
35399
|
+
"events/subscribe",
|
|
35400
|
+
"events/unsubscribe",
|
|
35401
|
+
"resources/list",
|
|
35402
|
+
"resources/templates/list",
|
|
35403
|
+
"resources/read",
|
|
35404
|
+
"prompts/list",
|
|
35405
|
+
"prompts/get"
|
|
35406
|
+
]);
|
|
35407
|
+
var ALLOWED_METHODS = /* @__PURE__ */ new Set([...FORWARDED_METHODS, "server/discover"]);
|
|
35408
|
+
var CACHE_HINT_METHODS = /* @__PURE__ */ new Set([
|
|
35409
|
+
"server/discover",
|
|
35410
|
+
"tools/list",
|
|
35411
|
+
"prompts/list",
|
|
35412
|
+
"resources/list",
|
|
35413
|
+
"resources/templates/list",
|
|
35414
|
+
"resources/read"
|
|
35415
|
+
]);
|
|
35416
|
+
var NAME_HEADER_SOURCE = /* @__PURE__ */ new Map([
|
|
35417
|
+
["tools/call", "name"],
|
|
35418
|
+
["prompts/get", "name"],
|
|
35419
|
+
["resources/read", "uri"]
|
|
35420
|
+
]);
|
|
35421
|
+
|
|
35422
|
+
// src/mcp-modern/bridge.ts
|
|
35423
|
+
var HANDSHAKE_VERSION = "2025-11-25";
|
|
35424
|
+
var BridgeError = class extends Error {
|
|
35425
|
+
code;
|
|
35426
|
+
data;
|
|
35427
|
+
constructor(code, message, data) {
|
|
35428
|
+
super(message);
|
|
35429
|
+
this.code = code;
|
|
35430
|
+
this.data = data;
|
|
35431
|
+
}
|
|
35432
|
+
};
|
|
35433
|
+
function forwardParams(params) {
|
|
35434
|
+
const { _meta, ...rest } = params;
|
|
35435
|
+
if (typeof _meta !== "object" || _meta === null || Array.isArray(_meta)) return rest;
|
|
35436
|
+
const handshake = /* @__PURE__ */ new Set([META_PROTOCOL_VERSION, META_CLIENT_INFO, META_CLIENT_CAPABILITIES]);
|
|
35437
|
+
const kept = Object.fromEntries(Object.entries(_meta).filter(([key]) => !handshake.has(key)));
|
|
35438
|
+
return Object.keys(kept).length > 0 ? { ...rest, _meta: kept } : rest;
|
|
35439
|
+
}
|
|
35440
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35441
|
+
async function openBridge(server) {
|
|
35442
|
+
const [clientSide, serverSide] = InMemoryTransport.createLinkedPair();
|
|
35443
|
+
const pending = /* @__PURE__ */ new Map();
|
|
35444
|
+
let nextId = 1;
|
|
35445
|
+
clientSide.onmessage = (message) => {
|
|
35446
|
+
if (!("id" in message) || typeof message.id !== "number") return;
|
|
35447
|
+
const entry = pending.get(message.id);
|
|
35448
|
+
if (!entry) return;
|
|
35449
|
+
if ("error" in message) {
|
|
35450
|
+
pending.delete(message.id);
|
|
35451
|
+
entry.reject(new BridgeError(message.error.code, message.error.message, message.error.data));
|
|
35452
|
+
} else if ("result" in message) {
|
|
35453
|
+
pending.delete(message.id);
|
|
35454
|
+
entry.resolve(isRecord7(message.result) ? message.result : {});
|
|
35455
|
+
}
|
|
35456
|
+
};
|
|
35457
|
+
const close = async () => {
|
|
35458
|
+
for (const entry of pending.values()) entry.reject(new Error("bridge closed"));
|
|
35459
|
+
pending.clear();
|
|
35460
|
+
await clientSide.close().catch(() => {
|
|
35461
|
+
});
|
|
35462
|
+
await server.close().catch(() => {
|
|
35463
|
+
});
|
|
35464
|
+
};
|
|
35465
|
+
const call = (method, params, signal) => new Promise((resolve47, reject) => {
|
|
35466
|
+
if (signal?.aborted) {
|
|
35467
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
35468
|
+
return;
|
|
35469
|
+
}
|
|
35470
|
+
const id = nextId++;
|
|
35471
|
+
const onAbort = () => {
|
|
35472
|
+
pending.delete(id);
|
|
35473
|
+
void clientSide.send({ jsonrpc: "2.0", method: "notifications/cancelled", params: { requestId: id, reason: "client disconnected" } }).catch(() => {
|
|
35474
|
+
});
|
|
35475
|
+
reject(signal?.reason ?? new Error("aborted"));
|
|
35476
|
+
};
|
|
35477
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
35478
|
+
pending.set(id, {
|
|
35479
|
+
resolve: (value) => {
|
|
35480
|
+
signal?.removeEventListener("abort", onAbort);
|
|
35481
|
+
resolve47(value);
|
|
35482
|
+
},
|
|
35483
|
+
reject: (error) => {
|
|
35484
|
+
signal?.removeEventListener("abort", onAbort);
|
|
35485
|
+
reject(error);
|
|
35486
|
+
}
|
|
35487
|
+
});
|
|
35488
|
+
clientSide.send({ jsonrpc: "2.0", id, method, params }).catch((error) => {
|
|
35489
|
+
pending.delete(id);
|
|
35490
|
+
signal?.removeEventListener("abort", onAbort);
|
|
35491
|
+
reject(error);
|
|
35492
|
+
});
|
|
35493
|
+
});
|
|
35494
|
+
try {
|
|
35495
|
+
await server.connect(serverSide);
|
|
35496
|
+
await clientSide.start();
|
|
35497
|
+
const init = await call("initialize", {
|
|
35498
|
+
protocolVersion: HANDSHAKE_VERSION,
|
|
35499
|
+
capabilities: {},
|
|
35500
|
+
clientInfo: { name: "agentproto-modern-bridge", version: "0.0.1" }
|
|
35501
|
+
});
|
|
35502
|
+
await clientSide.send({ jsonrpc: "2.0", method: "notifications/initialized" });
|
|
35503
|
+
const info = isRecord7(init.serverInfo) ? init.serverInfo : {};
|
|
35504
|
+
return {
|
|
35505
|
+
capabilities: isRecord7(init.capabilities) ? { ...init.capabilities } : {},
|
|
35506
|
+
serverInfo: {
|
|
35507
|
+
name: typeof info.name === "string" ? info.name : "agentproto",
|
|
35508
|
+
version: typeof info.version === "string" ? info.version : "0"
|
|
35509
|
+
},
|
|
35510
|
+
instructions: typeof init.instructions === "string" ? init.instructions : void 0,
|
|
35511
|
+
request: call,
|
|
35512
|
+
close
|
|
35513
|
+
};
|
|
35514
|
+
} catch (error) {
|
|
35515
|
+
await close();
|
|
35516
|
+
throw error;
|
|
35517
|
+
}
|
|
35518
|
+
}
|
|
35519
|
+
|
|
35520
|
+
// src/mcp-modern/validate.ts
|
|
35521
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35522
|
+
function normalizeHeaders(raw) {
|
|
35523
|
+
const out = {};
|
|
35524
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
35525
|
+
if (value === void 0) continue;
|
|
35526
|
+
const joined = (Array.isArray(value) ? value.join(", ") : value).trim();
|
|
35527
|
+
const key = name.toLowerCase();
|
|
35528
|
+
const previous = out[key];
|
|
35529
|
+
out[key] = previous === void 0 ? joined : `${previous}, ${joined}`;
|
|
35530
|
+
}
|
|
35531
|
+
return out;
|
|
35532
|
+
}
|
|
35533
|
+
function acceptsJson(accept) {
|
|
35534
|
+
if (accept === void 0) return false;
|
|
35535
|
+
return accept.split(",").some((part) => {
|
|
35536
|
+
const type = (part.split(";")[0] ?? "").trim().toLowerCase();
|
|
35537
|
+
return type === "application/json" || type === "application/*" || type === "*/*";
|
|
35538
|
+
});
|
|
35539
|
+
}
|
|
35540
|
+
function isJsonContentType(contentType) {
|
|
35541
|
+
if (contentType === void 0) return false;
|
|
35542
|
+
return (contentType.split(";")[0] ?? "").trim().toLowerCase() === "application/json";
|
|
35543
|
+
}
|
|
35544
|
+
function decodeHeaderValue(raw) {
|
|
35545
|
+
const match = /^=\?base64\?(.*)\?=$/.exec(raw);
|
|
35546
|
+
if (!match) return raw;
|
|
35547
|
+
return Buffer.from(match[1] ?? "", "base64").toString("utf8");
|
|
35548
|
+
}
|
|
35549
|
+
function parseBody(body) {
|
|
35550
|
+
const fail4 = (status, code, message) => ({ ok: false, error: { status, code, message, id: null } });
|
|
35551
|
+
let value;
|
|
35552
|
+
try {
|
|
35553
|
+
value = JSON.parse(body);
|
|
35554
|
+
} catch {
|
|
35555
|
+
return fail4(400, ERR_PARSE, "Parse error");
|
|
35556
|
+
}
|
|
35557
|
+
if (Array.isArray(value)) return fail4(400, ERR_INVALID_REQUEST, "Batch requests are not supported");
|
|
35558
|
+
if (!isRecord8(value)) return fail4(400, ERR_INVALID_REQUEST, "The body must be a single JSON-RPC request or notification");
|
|
35559
|
+
const method = value.method;
|
|
35560
|
+
if (typeof method !== "string" && ("result" in value || "error" in value)) {
|
|
35561
|
+
return fail4(400, ERR_INVALID_REQUEST, "JSON-RPC responses must not be sent to the server");
|
|
35562
|
+
}
|
|
35563
|
+
if (value.jsonrpc !== "2.0" || typeof method !== "string") return fail4(400, ERR_INVALID_REQUEST, "Not a JSON-RPC 2.0 request");
|
|
35564
|
+
const hasId = "id" in value;
|
|
35565
|
+
const id = value.id;
|
|
35566
|
+
if (hasId && typeof id !== "string" && typeof id !== "number") return fail4(400, ERR_INVALID_REQUEST, "`id` must be a string or a number");
|
|
35567
|
+
if (value.params !== void 0 && !isRecord8(value.params)) return fail4(400, ERR_INVALID_PARAMS, "`params` must be an object");
|
|
35568
|
+
return {
|
|
35569
|
+
ok: true,
|
|
35570
|
+
request: {
|
|
35571
|
+
id: hasId ? id : void 0,
|
|
35572
|
+
method,
|
|
35573
|
+
params: isRecord8(value.params) ? value.params : {}
|
|
35574
|
+
}
|
|
35575
|
+
};
|
|
35576
|
+
}
|
|
35577
|
+
function validateRequest(request3, headers, supported, allowed) {
|
|
35578
|
+
const id = request3.id ?? null;
|
|
35579
|
+
const err = (status, code, message, data) => ({
|
|
35580
|
+
status,
|
|
35581
|
+
code,
|
|
35582
|
+
message,
|
|
35583
|
+
id,
|
|
35584
|
+
...data !== void 0 ? { data } : {}
|
|
35585
|
+
});
|
|
35586
|
+
const { method, params } = request3;
|
|
35587
|
+
if (method === "initialize") {
|
|
35588
|
+
const requested = typeof params.protocolVersion === "string" ? params.protocolVersion : "unknown";
|
|
35589
|
+
return err(404, ERR_METHOD_NOT_FOUND, "Method not found: initialize (this endpoint is stateless, send server/discover)", {
|
|
35590
|
+
supported: [...supported],
|
|
35591
|
+
requested
|
|
35592
|
+
});
|
|
35593
|
+
}
|
|
35594
|
+
const meta = params._meta;
|
|
35595
|
+
const missing = [];
|
|
35596
|
+
if (!isRecord8(meta) || typeof meta[META_PROTOCOL_VERSION] !== "string") missing.push(META_PROTOCOL_VERSION);
|
|
35597
|
+
if (!isRecord8(meta) || !isRecord8(meta[META_CLIENT_CAPABILITIES])) missing.push(META_CLIENT_CAPABILITIES);
|
|
35598
|
+
if (!isRecord8(meta) || missing.length > 0) return err(400, ERR_INVALID_PARAMS, `Missing required _meta field(s): ${missing.join(", ")}`);
|
|
35599
|
+
const version = String(meta[META_PROTOCOL_VERSION]);
|
|
35600
|
+
if (!supported.includes(version)) {
|
|
35601
|
+
return err(400, ERR_UNSUPPORTED_VERSION, "Unsupported protocol version", { supported: [...supported], requested: version });
|
|
35602
|
+
}
|
|
35603
|
+
const headerVersion = headers["mcp-protocol-version"];
|
|
35604
|
+
if (headerVersion === void 0) return err(400, ERR_HEADER_MISMATCH, "Header mismatch: MCP-Protocol-Version header is missing");
|
|
35605
|
+
if (headerVersion !== version) {
|
|
35606
|
+
return err(400, ERR_HEADER_MISMATCH, `Header mismatch: MCP-Protocol-Version ${headerVersion} does not match _meta ${version}`);
|
|
35607
|
+
}
|
|
35608
|
+
const headerMethod = headers["mcp-method"];
|
|
35609
|
+
if (headerMethod === void 0) return err(400, ERR_HEADER_MISMATCH, "Header mismatch: Mcp-Method header is missing");
|
|
35610
|
+
if (headerMethod !== method) {
|
|
35611
|
+
return err(400, ERR_HEADER_MISMATCH, `Header mismatch: Mcp-Method ${headerMethod} does not match body ${method}`);
|
|
35612
|
+
}
|
|
35613
|
+
const source = NAME_HEADER_SOURCE.get(method);
|
|
35614
|
+
if (source !== void 0) {
|
|
35615
|
+
const expected = params[source];
|
|
35616
|
+
if (typeof expected !== "string") return err(400, ERR_INVALID_PARAMS, `Missing required params.${source}`);
|
|
35617
|
+
const rawName = headers["mcp-name"];
|
|
35618
|
+
if (rawName === void 0) return err(400, ERR_HEADER_MISMATCH, "Header mismatch: Mcp-Name header is missing");
|
|
35619
|
+
if (decodeHeaderValue(rawName) !== expected) {
|
|
35620
|
+
return err(400, ERR_HEADER_MISMATCH, `Header mismatch: Mcp-Name does not match params.${source}`);
|
|
35621
|
+
}
|
|
35622
|
+
}
|
|
35623
|
+
if (!allowed.has(method)) return err(404, ERR_METHOD_NOT_FOUND, `Method not found: ${method}`);
|
|
35624
|
+
return null;
|
|
35625
|
+
}
|
|
35626
|
+
|
|
35627
|
+
// src/mcp-modern/index.ts
|
|
35628
|
+
var DEFAULT_CACHE_POLICY = () => ({ ttlMs: 0, cacheScope: "private" });
|
|
35629
|
+
var JSON_HEADERS = { "content-type": "application/json" };
|
|
35630
|
+
function errorResponse(error, extraHeaders = {}) {
|
|
35631
|
+
return {
|
|
35632
|
+
status: error.status,
|
|
35633
|
+
headers: { ...JSON_HEADERS, ...extraHeaders },
|
|
35634
|
+
body: JSON.stringify({
|
|
35635
|
+
jsonrpc: "2.0",
|
|
35636
|
+
id: error.id,
|
|
35637
|
+
error: { code: error.code, message: error.message, ...error.data !== void 0 ? { data: error.data } : {} }
|
|
35638
|
+
})
|
|
35639
|
+
};
|
|
35640
|
+
}
|
|
35641
|
+
function okResponse(id, result) {
|
|
35642
|
+
return { status: 200, headers: { ...JSON_HEADERS }, body: JSON.stringify({ jsonrpc: "2.0", id, result }) };
|
|
35643
|
+
}
|
|
35644
|
+
function decorate(method, result, serverInfo, hints) {
|
|
35645
|
+
const meta = typeof result._meta === "object" && result._meta !== null && !Array.isArray(result._meta) ? result._meta : {};
|
|
35646
|
+
const resultType = typeof result.resultType === "string" ? result.resultType : "complete";
|
|
35647
|
+
const decorated = {
|
|
35648
|
+
...result,
|
|
35649
|
+
resultType,
|
|
35650
|
+
_meta: { ...meta, [META_SERVER_INFO]: meta[META_SERVER_INFO] ?? serverInfo }
|
|
35651
|
+
};
|
|
35652
|
+
if (resultType === "complete" && CACHE_HINT_METHODS.has(method)) {
|
|
35653
|
+
decorated.ttlMs = typeof result.ttlMs === "number" ? result.ttlMs : hints.ttlMs;
|
|
35654
|
+
decorated.cacheScope = typeof result.cacheScope === "string" ? result.cacheScope : hints.cacheScope;
|
|
35655
|
+
}
|
|
35656
|
+
return decorated;
|
|
35657
|
+
}
|
|
35658
|
+
function fromThrown(error, id) {
|
|
35659
|
+
if (error instanceof BridgeError) {
|
|
35660
|
+
const message = error.message.replace(/^(?:MCP error -?\d+: )+/, "");
|
|
35661
|
+
const status = error.code === ERR_METHOD_NOT_FOUND ? 404 : 200;
|
|
35662
|
+
return errorResponse({ status, code: error.code, message, id, ...error.data !== void 0 ? { data: error.data } : {} });
|
|
35663
|
+
}
|
|
35664
|
+
return errorResponse({ status: 500, code: ERR_INTERNAL, message: "Internal error", id });
|
|
35665
|
+
}
|
|
35666
|
+
async function answer(request3, bridge, supported, hints, signal) {
|
|
35667
|
+
if (request3.method === "server/discover") {
|
|
35668
|
+
const capabilities = { ...bridge.capabilities };
|
|
35669
|
+
delete capabilities.logging;
|
|
35670
|
+
return decorate(
|
|
35671
|
+
"server/discover",
|
|
35672
|
+
{
|
|
35673
|
+
supportedVersions: [...supported],
|
|
35674
|
+
capabilities,
|
|
35675
|
+
...bridge.instructions ? { instructions: bridge.instructions } : {}
|
|
35676
|
+
},
|
|
35677
|
+
bridge.serverInfo,
|
|
35678
|
+
hints
|
|
35679
|
+
);
|
|
35680
|
+
}
|
|
35681
|
+
return decorate(request3.method, await bridge.request(request3.method, forwardParams(request3.params), signal), bridge.serverInfo, hints);
|
|
35682
|
+
}
|
|
35683
|
+
async function handleModernRequest(input, deps2) {
|
|
35684
|
+
const supported = deps2.supportedVersions ?? DEFAULT_SUPPORTED_VERSIONS;
|
|
35685
|
+
const cachePolicy = deps2.cachePolicy ?? DEFAULT_CACHE_POLICY;
|
|
35686
|
+
const headers = normalizeHeaders(input.headers);
|
|
35687
|
+
if (input.method !== "POST") {
|
|
35688
|
+
return errorResponse({ status: 405, code: ERR_INVALID_REQUEST, message: "Only POST is supported", id: null }, { allow: "POST" });
|
|
35689
|
+
}
|
|
35690
|
+
if (!acceptsJson(headers["accept"])) {
|
|
35691
|
+
return errorResponse({ status: 406, code: ERR_INVALID_REQUEST, message: "Accept must allow application/json", id: null });
|
|
35692
|
+
}
|
|
35693
|
+
if (!isJsonContentType(headers["content-type"])) {
|
|
35694
|
+
return errorResponse({ status: 415, code: ERR_INVALID_REQUEST, message: "Content-Type must be application/json", id: null });
|
|
35695
|
+
}
|
|
35696
|
+
const parsed = parseBody(input.body);
|
|
35697
|
+
if (!parsed.ok) return errorResponse(parsed.error);
|
|
35698
|
+
const request3 = parsed.request;
|
|
35699
|
+
if (request3.id === void 0) return { status: 202, headers: {} };
|
|
35700
|
+
const invalid = validateRequest(request3, headers, supported, ALLOWED_METHODS);
|
|
35701
|
+
if (invalid) return errorResponse(invalid);
|
|
35702
|
+
const id = request3.id;
|
|
35703
|
+
let bridge;
|
|
35704
|
+
try {
|
|
35705
|
+
bridge = await openBridge(await deps2.createServer());
|
|
35706
|
+
const result = await answer({ ...request3, id }, bridge, supported, cachePolicy(request3.method), deps2.signal);
|
|
35707
|
+
if (deps2.signal?.aborted) return { status: 499, headers: {} };
|
|
35708
|
+
const response = okResponse(id, result);
|
|
35709
|
+
if ((request3.method === "server/discover" || request3.method === "tools/list") && deps2.onObserved) {
|
|
35710
|
+
try {
|
|
35711
|
+
deps2.onObserved(request3.method, { jsonrpc: "2.0", id, result });
|
|
35712
|
+
} catch {
|
|
35713
|
+
}
|
|
35714
|
+
}
|
|
35715
|
+
return response;
|
|
35716
|
+
} catch (error) {
|
|
35717
|
+
if (deps2.signal?.aborted) return { status: 499, headers: {} };
|
|
35718
|
+
const response = fromThrown(error, id);
|
|
35719
|
+
if ((request3.method === "server/discover" || request3.method === "tools/list") && deps2.onObserved && response.body) {
|
|
35720
|
+
try {
|
|
35721
|
+
deps2.onObserved(request3.method, JSON.parse(response.body));
|
|
35722
|
+
} catch {
|
|
35723
|
+
}
|
|
35724
|
+
}
|
|
35725
|
+
return response;
|
|
35726
|
+
} finally {
|
|
35727
|
+
await bridge?.close();
|
|
35728
|
+
}
|
|
35729
|
+
}
|
|
35107
35730
|
init_sessions();
|
|
35108
35731
|
init_session_retirement();
|
|
35109
35732
|
init_session_message();
|
|
@@ -35795,7 +36418,7 @@ function createSentinelStore(opts) {
|
|
|
35795
36418
|
const persist = opts?.persist ?? opts?.filePath !== void 0;
|
|
35796
36419
|
let persistTimer = null;
|
|
35797
36420
|
let secretsPersistTimer = null;
|
|
35798
|
-
const
|
|
36421
|
+
const load2 = () => {
|
|
35799
36422
|
const out = /* @__PURE__ */ new Map();
|
|
35800
36423
|
if (!persist) return out;
|
|
35801
36424
|
let raw;
|
|
@@ -35819,7 +36442,7 @@ function createSentinelStore(opts) {
|
|
|
35819
36442
|
}
|
|
35820
36443
|
return out;
|
|
35821
36444
|
};
|
|
35822
|
-
const sentinels =
|
|
36445
|
+
const sentinels = load2();
|
|
35823
36446
|
const loadSecrets = () => {
|
|
35824
36447
|
const out = /* @__PURE__ */ new Map();
|
|
35825
36448
|
if (!persist) return out;
|
|
@@ -36039,6 +36662,15 @@ function createSentinelStore(opts) {
|
|
|
36039
36662
|
function singleMatch(subject, types) {
|
|
36040
36663
|
return [{ subject, ...types ? { types } : {} }];
|
|
36041
36664
|
}
|
|
36665
|
+
var SentinelBackingExpiredError = class extends Error {
|
|
36666
|
+
code = "backing_subscription_expired";
|
|
36667
|
+
remoteId;
|
|
36668
|
+
constructor(message, remoteId) {
|
|
36669
|
+
super(message);
|
|
36670
|
+
this.name = "SentinelBackingExpiredError";
|
|
36671
|
+
if (remoteId !== void 0) this.remoteId = remoteId;
|
|
36672
|
+
}
|
|
36673
|
+
};
|
|
36042
36674
|
function deliveryPreferenceFor(provider, intervalMs) {
|
|
36043
36675
|
if (provider.preferredDelivery) return provider.preferredDelivery(intervalMs);
|
|
36044
36676
|
return provider.capabilities.poll ? { mode: "poll", intervalMs } : { mode: "push" };
|
|
@@ -36602,7 +37234,7 @@ function compactPrState(snapshot, extra) {
|
|
|
36602
37234
|
const slim = snapshot.state !== "open" && extra.closedEmitted ? { fetchedAt: snapshot.fetchedAt, state: snapshot.state, reviews: [], checks: [] } : snapshot;
|
|
36603
37235
|
return { snapshot: slim, ...extra };
|
|
36604
37236
|
}
|
|
36605
|
-
function
|
|
37237
|
+
function decorate2(events, pr) {
|
|
36606
37238
|
const title = pr.title.length > 80 ? `${pr.title.slice(0, 77)}...` : pr.title;
|
|
36607
37239
|
return events.map((e) => ({
|
|
36608
37240
|
...e,
|
|
@@ -36666,7 +37298,7 @@ function diffRepoPr(prev, pr, repo) {
|
|
|
36666
37298
|
closedEmitted = true;
|
|
36667
37299
|
}
|
|
36668
37300
|
return {
|
|
36669
|
-
events:
|
|
37301
|
+
events: decorate2(events, pr),
|
|
36670
37302
|
next: compactPrState(now, { draft: pr.draft, closedEmitted, ...ciReported ? { ciReported } : {} })
|
|
36671
37303
|
};
|
|
36672
37304
|
}
|
|
@@ -36698,7 +37330,7 @@ function diffRepo(cursor, prs, repo, fetchedAt) {
|
|
|
36698
37330
|
})
|
|
36699
37331
|
];
|
|
36700
37332
|
if (closed) fresh.push(closedEvent(pr.snapshot, { repo, number: pr.number, subject }, false));
|
|
36701
|
-
events.push(...
|
|
37333
|
+
events.push(...decorate2(fresh, pr));
|
|
36702
37334
|
}
|
|
36703
37335
|
const settled = verdict === "success" || verdict === "failure" || verdict === "neutral";
|
|
36704
37336
|
next[key] = compactPrState(pr.snapshot, {
|
|
@@ -37636,11 +38268,11 @@ function verifySignatureV2(secret, headers, body, nowMs, skewMs = SIGNATURE_V2_S
|
|
|
37636
38268
|
if (a.length !== b.length || !timingSafeEqual(a, b)) return { ok: false, reason: "signature_mismatch" };
|
|
37637
38269
|
return { ok: true, timestampSec };
|
|
37638
38270
|
}
|
|
37639
|
-
var
|
|
38271
|
+
var isRecord9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
37640
38272
|
function toSentinelEvent(raw, seq) {
|
|
37641
|
-
if (!
|
|
38273
|
+
if (!isRecord9(raw)) return null;
|
|
37642
38274
|
const { id, source, type, subject, time, data, summary, subjects, terminal, consumerref } = raw;
|
|
37643
|
-
if (raw.specversion !== "1.0" || typeof id !== "string" || id === "" || typeof source !== "string" || typeof type !== "string" || type === "" || typeof subject !== "string" || subject === "" || typeof time !== "string" || !
|
|
38275
|
+
if (raw.specversion !== "1.0" || typeof id !== "string" || id === "" || typeof source !== "string" || typeof type !== "string" || type === "" || typeof subject !== "string" || subject === "" || typeof time !== "string" || !isRecord9(data) || typeof summary !== "string" || !Array.isArray(subjects) || !subjects.every((s) => typeof s === "string") || typeof terminal !== "boolean") {
|
|
37644
38276
|
return null;
|
|
37645
38277
|
}
|
|
37646
38278
|
const envSeq = typeof raw.seq === "number" ? raw.seq : seq;
|
|
@@ -37686,9 +38318,36 @@ function stateOf(handle) {
|
|
|
37686
38318
|
mode: s.mode === "push" ? "push" : "poll",
|
|
37687
38319
|
...typeof s.consumerRef === "string" ? { consumerRef: s.consumerRef } : {},
|
|
37688
38320
|
...typeof s.hookKey === "string" ? { hookKey: s.hookKey } : {},
|
|
37689
|
-
...typeof s.callbackSecret === "string" ? { callbackSecret: s.callbackSecret } : {}
|
|
38321
|
+
...typeof s.callbackSecret === "string" ? { callbackSecret: s.callbackSecret } : {},
|
|
38322
|
+
...s.signature === "v2" ? { signature: "v2" } : {}
|
|
37690
38323
|
};
|
|
37691
38324
|
}
|
|
38325
|
+
function excerptOf(envelope) {
|
|
38326
|
+
if (!isRecord9(envelope)) return void 0;
|
|
38327
|
+
const cap = (v) => typeof v === "string" ? v.slice(0, 200) : void 0;
|
|
38328
|
+
const out = {};
|
|
38329
|
+
const id = cap(envelope.id);
|
|
38330
|
+
const type = cap(envelope.type);
|
|
38331
|
+
const subject = cap(envelope.subject);
|
|
38332
|
+
const source = cap(envelope.source);
|
|
38333
|
+
if (id !== void 0) out.id = id;
|
|
38334
|
+
if (type !== void 0) out.type = type;
|
|
38335
|
+
if (subject !== void 0) out.subject = subject;
|
|
38336
|
+
if (source !== void 0) out.source = source;
|
|
38337
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
38338
|
+
}
|
|
38339
|
+
function whyNotEnvelope(envelope) {
|
|
38340
|
+
if (envelope === void 0 || envelope === null) return "item has no envelope";
|
|
38341
|
+
if (!isRecord9(envelope)) return "envelope is not a JSON object";
|
|
38342
|
+
const missing = [];
|
|
38343
|
+
if (envelope.specversion !== "1.0") missing.push("specversion");
|
|
38344
|
+
for (const k of ["id", "type", "subject"]) if (typeof envelope[k] !== "string" || envelope[k] === "") missing.push(k);
|
|
38345
|
+
for (const k of ["source", "time", "summary"]) if (typeof envelope[k] !== "string") missing.push(k);
|
|
38346
|
+
if (!isRecord9(envelope.data)) missing.push("data");
|
|
38347
|
+
if (!Array.isArray(envelope.subjects) || !envelope.subjects.every((x) => typeof x === "string")) missing.push("subjects");
|
|
38348
|
+
if (typeof envelope.terminal !== "boolean") missing.push("terminal");
|
|
38349
|
+
return `envelope failed validation (invalid or missing: ${missing.join(", ") || "unknown"})`;
|
|
38350
|
+
}
|
|
37692
38351
|
var SUBJECT_SCHEME_RE = /^([A-Za-z][A-Za-z0-9_-]*):/;
|
|
37693
38352
|
function defaultTypesFor(subject) {
|
|
37694
38353
|
return subject.startsWith("github:") ? [...GITHUB_DEFAULT_PR_TYPES] : ["*"];
|
|
@@ -37746,7 +38405,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37746
38405
|
json3 = void 0;
|
|
37747
38406
|
}
|
|
37748
38407
|
if (!res.ok) {
|
|
37749
|
-
const detail =
|
|
38408
|
+
const detail = isRecord9(json3) && typeof json3.error === "string" ? json3.error : text13.slice(0, 200);
|
|
37750
38409
|
throw new AgentpushHttpError(res.status, `agentpush ${method} ${path} \u2192 HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
|
|
37751
38410
|
}
|
|
37752
38411
|
return json3;
|
|
@@ -37779,6 +38438,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37779
38438
|
// Credentials come from `setup_sentinel_provider` OR an imported alias, so
|
|
37780
38439
|
// "set up" is decided by `readiness()`, not the kit's ledger/creds check.
|
|
37781
38440
|
requiresSetup: false,
|
|
38441
|
+
exclusiveRemote: true,
|
|
37782
38442
|
capabilities: {
|
|
37783
38443
|
subjects: ["*"],
|
|
37784
38444
|
push: true,
|
|
@@ -37832,7 +38492,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37832
38492
|
...push ? { callback_url: callbackUrl2(delivery.callbackUrl, hookKey), callback_secret: callbackSecret } : {}
|
|
37833
38493
|
}
|
|
37834
38494
|
});
|
|
37835
|
-
const id =
|
|
38495
|
+
const id = isRecord9(json3) && isRecord9(json3.subscription) ? json3.subscription.id : void 0;
|
|
37836
38496
|
if (typeof id !== "string" || id === "") throw new Error("agentpush: create returned no subscription id");
|
|
37837
38497
|
return {
|
|
37838
38498
|
provider: AGENTPUSH_SLUG,
|
|
@@ -37841,6 +38501,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37841
38501
|
state: {
|
|
37842
38502
|
mode: push ? "push" : "poll",
|
|
37843
38503
|
consumerRef,
|
|
38504
|
+
signature: "v2",
|
|
37844
38505
|
...push ? { hookKey, callbackSecret } : {}
|
|
37845
38506
|
}
|
|
37846
38507
|
};
|
|
@@ -37859,7 +38520,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37859
38520
|
let sub;
|
|
37860
38521
|
try {
|
|
37861
38522
|
const json3 = await api("GET", subPath(handle));
|
|
37862
|
-
sub =
|
|
38523
|
+
sub = isRecord9(json3) && isRecord9(json3.subscription) ? json3.subscription : void 0;
|
|
37863
38524
|
} catch (err) {
|
|
37864
38525
|
if (isHttp(err, 404)) {
|
|
37865
38526
|
throw new Error(`agentpush subscription ${handle.remoteId} no longer exists \u2014 re-create the sentinel`);
|
|
@@ -37887,10 +38548,34 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37887
38548
|
if (!isHttp(err, 404)) throw err;
|
|
37888
38549
|
}
|
|
37889
38550
|
},
|
|
38551
|
+
/**
|
|
38552
|
+
* Extend the remote subscription's lifetime. `PATCH` accepts `until`
|
|
38553
|
+
* without rejecting an already-expired row, so the returned `status` is
|
|
38554
|
+
* checked: anything but `active` (or a 404) means the backing subscription
|
|
38555
|
+
* is gone and must be recreated — never silently re-provisioned here.
|
|
38556
|
+
*/
|
|
38557
|
+
async renew(handle, until) {
|
|
38558
|
+
if (!handle.remoteId) throw new SentinelBackingExpiredError("agentpush: handle has no subscription id");
|
|
38559
|
+
let json3;
|
|
38560
|
+
try {
|
|
38561
|
+
json3 = await api("PATCH", subPath(handle), { body: { until } });
|
|
38562
|
+
} catch (err) {
|
|
38563
|
+
if (isHttp(err, 404)) {
|
|
38564
|
+
throw new SentinelBackingExpiredError(`the agentpush subscription ${handle.remoteId} was deleted`, handle.remoteId);
|
|
38565
|
+
}
|
|
38566
|
+
throw err;
|
|
38567
|
+
}
|
|
38568
|
+
const sub = isRecord9(json3) && isRecord9(json3.subscription) ? json3.subscription : void 0;
|
|
38569
|
+
const status = sub && typeof sub.status === "string" ? sub.status : void 0;
|
|
38570
|
+
if (status === "expired" || status === "deleted") {
|
|
38571
|
+
throw new SentinelBackingExpiredError(`the agentpush subscription ${handle.remoteId} is ${status}`, handle.remoteId);
|
|
38572
|
+
}
|
|
38573
|
+
return { ...handle };
|
|
38574
|
+
},
|
|
37890
38575
|
async status(handle) {
|
|
37891
38576
|
try {
|
|
37892
38577
|
const json3 = await api("GET", subPath(handle));
|
|
37893
|
-
const sub =
|
|
38578
|
+
const sub = isRecord9(json3) && isRecord9(json3.subscription) ? json3.subscription : void 0;
|
|
37894
38579
|
const status = sub && typeof sub.status === "string" ? sub.status : "unknown";
|
|
37895
38580
|
if (status === "active") return { ok: true };
|
|
37896
38581
|
return { ok: false, detail: `the agentpush subscription is ${status}` };
|
|
@@ -37902,18 +38587,35 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37902
38587
|
const after = handle.cursor ?? "0";
|
|
37903
38588
|
if (stateOf(handle).mode === "push") return { events: [], cursor: after };
|
|
37904
38589
|
const json3 = await api("GET", `${subPath(handle)}/events`, { query: { after, limit: Math.max(1, Math.min(limit, 1e3)) } });
|
|
37905
|
-
const items =
|
|
38590
|
+
const items = isRecord9(json3) && Array.isArray(json3.items) ? json3.items : [];
|
|
37906
38591
|
const events = [];
|
|
38592
|
+
const malformed = [];
|
|
37907
38593
|
let cursor = Number(after);
|
|
37908
38594
|
if (!Number.isFinite(cursor)) cursor = 0;
|
|
37909
38595
|
for (const item of items) {
|
|
37910
|
-
|
|
37911
|
-
const seq = typeof item.seq === "number" ? item.seq : void 0;
|
|
38596
|
+
const seq = isRecord9(item) && typeof item.seq === "number" ? item.seq : void 0;
|
|
37912
38597
|
if (seq !== void 0 && seq > cursor) cursor = seq;
|
|
37913
|
-
const
|
|
37914
|
-
|
|
38598
|
+
const envelope = isRecord9(item) ? item.envelope : void 0;
|
|
38599
|
+
const event = toSentinelEvent(envelope, seq);
|
|
38600
|
+
if (event) {
|
|
38601
|
+
events.push(event);
|
|
38602
|
+
continue;
|
|
38603
|
+
}
|
|
38604
|
+
let serialized = "";
|
|
38605
|
+
try {
|
|
38606
|
+
serialized = JSON.stringify(item) ?? "";
|
|
38607
|
+
} catch {
|
|
38608
|
+
}
|
|
38609
|
+
const excerpt = excerptOf(envelope);
|
|
38610
|
+
malformed.push({
|
|
38611
|
+
...seq !== void 0 ? { seq } : {},
|
|
38612
|
+
...isRecord9(item) && typeof item.delivery_id === "string" ? { remoteDeliveryId: item.delivery_id } : {},
|
|
38613
|
+
error: isRecord9(item) ? whyNotEnvelope(envelope) : "poll item is not a JSON object",
|
|
38614
|
+
...excerpt ? { excerpt } : {},
|
|
38615
|
+
...serialized ? { digest: createHash("sha256").update(serialized).digest("hex"), bytes: Buffer.byteLength(serialized) } : {}
|
|
38616
|
+
});
|
|
37915
38617
|
}
|
|
37916
|
-
return { events, cursor: String(cursor) };
|
|
38618
|
+
return { events, cursor: String(cursor), ...malformed.length > 0 ? { malformed } : {} };
|
|
37917
38619
|
},
|
|
37918
38620
|
/** Called by the runtime after a polled batch is delivered. Nothing new
|
|
37919
38621
|
* (cursor unchanged) is a no-op — no request on an empty tick. */
|
|
@@ -37930,6 +38632,7 @@ function agentpushSentinelProvider(opts = {}) {
|
|
|
37930
38632
|
const signature = headerValue3(req.headers, "x-agentpush-signature");
|
|
37931
38633
|
if (!signature || signature.trim() === "") return { ok: false, reason: "missing_signature" };
|
|
37932
38634
|
if (signature.trim().startsWith("sha256=")) {
|
|
38635
|
+
if (st.signature === "v2") return { ok: false, reason: "legacy_signature_rejected" };
|
|
37933
38636
|
const legacy = verifyInboundSignature("agentpush", {
|
|
37934
38637
|
rawBody: req.rawBody,
|
|
37935
38638
|
headers: req.headers,
|
|
@@ -38360,6 +39063,13 @@ async function createSentinelWatch(deps2, input) {
|
|
|
38360
39063
|
async function cancelSentinelWatch(deps2, id) {
|
|
38361
39064
|
const sentinel = deps2.store.get(id);
|
|
38362
39065
|
if (!sentinel) return false;
|
|
39066
|
+
if (deps2.tombstones) {
|
|
39067
|
+
try {
|
|
39068
|
+
await deps2.tombstones.cancel(sentinel.handle, () => deps2.store.remove(id));
|
|
39069
|
+
return true;
|
|
39070
|
+
} catch {
|
|
39071
|
+
}
|
|
39072
|
+
}
|
|
38363
39073
|
const provider = await deps2.resolveProvider(sentinel.provider);
|
|
38364
39074
|
if (provider) {
|
|
38365
39075
|
try {
|
|
@@ -38392,6 +39102,9 @@ function sentinelView(s) {
|
|
|
38392
39102
|
...s.lastError ? { lastError: s.lastError } : {}
|
|
38393
39103
|
};
|
|
38394
39104
|
}
|
|
39105
|
+
function sentinelViewWithDelivery(s, deliveryStatus) {
|
|
39106
|
+
return deliveryStatus ? { ...sentinelView(s), deliveryStatus } : sentinelView(s);
|
|
39107
|
+
}
|
|
38395
39108
|
var ok2 = (body) => ({
|
|
38396
39109
|
content: [{ type: "text", text: JSON.stringify({ ok: true, ...body }) }]
|
|
38397
39110
|
});
|
|
@@ -38402,7 +39115,7 @@ var fail2 = (code, message) => ({
|
|
|
38402
39115
|
var urgencyField2 = z.enum(MESSAGE_URGENCIES);
|
|
38403
39116
|
var untilField = z.enum(["subject_terminal", "never"]);
|
|
38404
39117
|
function registerSentinelTools(server, opts) {
|
|
38405
|
-
const { store, runtime, resolveProvider, isSessionAlive, callerSessionId, activeIntervalMs } = opts;
|
|
39118
|
+
const { store, runtime, resolveProvider, isSessionAlive, callerSessionId, activeIntervalMs, tombstones } = opts;
|
|
38406
39119
|
server.tool(
|
|
38407
39120
|
"sentinel_watch",
|
|
38408
39121
|
"Watch a subject \u2014 a GitHub PR/repo (default) or ANOTHER SESSION's own lifecycle \u2014 and deliver matching events as a `system`/`notice` message into a session's AIP-46 inbox. Provide either `prUrl` (sugar: `https://github.com/o/r/pull/N` -> subject `github:o/r#N`, the default PR type set, and `until: subject_terminal`) or a raw `subject`: a GitHub one (`github:o/r#N`, `github:o/r`), or `session:<id>` to watch another session \u2014 woken on its turn-end, awaiting-input, or exit, even if it never calls `message_parent`, with the watch self-expiring once it exits (`until` defaults to `subject_terminal` for this scheme too). Prefer this over `session_follow` for a single, known target that should auto-expire on exit; prefer `session_follow` for a broad selector (`all`/`cwdPrefix`) with coalesced digests across many sessions. `sessionId` (the DELIVERY target) defaults to the calling session \u2014 distinct from the `<id>` inside a `session:<id>` subject (the WATCHED session). `provider` picks the backend: `local-gh` (poll every ~15-60s over the host's authenticated `gh` CLI, zero infra), `webhook` (near-real-time push via a GitHub repo hook; needs a public daemon URL \u2014 a named tunnel or AGENTPROTO_PUBLIC_URL \u2014 and a `gh` token with admin:repo_hook), `agentpush` (hosted, durable subscription: events queue server-side across daemon downtime; needs an agentpush API key; one subject per sentinel) or `session` (in-process, zero infra, no credentials \u2014 the only backend that understands a `session:<id>` subject, and picked automatically for one); see `list_sentinel_adapters` for readiness. Omitted for a GitHub subject, it defaults to `agentpush` when set up, else `webhook` when a stable public URL exists and webhook is ready, else `local-gh`.",
|
|
@@ -38425,16 +39138,16 @@ function registerSentinelTools(server, opts) {
|
|
|
38425
39138
|
);
|
|
38426
39139
|
server.tool(
|
|
38427
39140
|
"sentinel_list",
|
|
38428
|
-
"List every sentinel this daemon is tracking, with status, match clauses, target, and event counters. Credentials are never returned.",
|
|
39141
|
+
"List every sentinel this daemon is tracking, with status, match clauses, target, and event counters (webhook targets also report `deliveryStatus`). Credentials are never returned.",
|
|
38429
39142
|
{},
|
|
38430
|
-
async () => ok2({ sentinels: store.list().map(
|
|
39143
|
+
async () => ok2({ sentinels: store.list().map((s) => sentinelViewWithDelivery(s, opts.deliveryStatus?.(s.id))) })
|
|
38431
39144
|
);
|
|
38432
39145
|
server.tool(
|
|
38433
39146
|
"sentinel_unwatch",
|
|
38434
39147
|
"Stop a sentinel: best-effort cancels the provider-side watch, then removes the record. Idempotent-ish: an unknown id is reported, not thrown.",
|
|
38435
39148
|
{ id: z.string().describe("Sentinel id (sen_...).") },
|
|
38436
39149
|
async ({ id }) => {
|
|
38437
|
-
const removed = await cancelSentinelWatch({ store, resolveProvider }, id);
|
|
39150
|
+
const removed = await cancelSentinelWatch({ store, resolveProvider, ...tombstones ? { tombstones } : {} }, id);
|
|
38438
39151
|
if (!removed) return fail2("not_found", `sentinel_unwatch: no sentinel "${id}"`);
|
|
38439
39152
|
return ok2({ removed: true, id });
|
|
38440
39153
|
}
|
|
@@ -38495,7 +39208,7 @@ function createSessionFollowStore(opts) {
|
|
|
38495
39208
|
const debounceMs = opts?.debounceMs ?? PERSIST_DEBOUNCE_MS4;
|
|
38496
39209
|
const persist = opts?.persist ?? true;
|
|
38497
39210
|
let persistTimer = null;
|
|
38498
|
-
const
|
|
39211
|
+
const load2 = () => {
|
|
38499
39212
|
const out = /* @__PURE__ */ new Map();
|
|
38500
39213
|
if (!persist) return out;
|
|
38501
39214
|
let raw;
|
|
@@ -38521,7 +39234,7 @@ function createSessionFollowStore(opts) {
|
|
|
38521
39234
|
}
|
|
38522
39235
|
return out;
|
|
38523
39236
|
};
|
|
38524
|
-
const follows =
|
|
39237
|
+
const follows = load2();
|
|
38525
39238
|
const snapshot = () => {
|
|
38526
39239
|
const out = {};
|
|
38527
39240
|
for (const [id, follow] of follows.entries()) out[id] = follow;
|
|
@@ -38803,7 +39516,7 @@ function createHostRegistry(deps2) {
|
|
|
38803
39516
|
let probesRunning = 0;
|
|
38804
39517
|
const probeWaiters = [];
|
|
38805
39518
|
async function withProbeSlot(fn) {
|
|
38806
|
-
if (probesRunning >= probeConcurrency) await new Promise((
|
|
39519
|
+
if (probesRunning >= probeConcurrency) await new Promise((resolve47) => probeWaiters.push(resolve47));
|
|
38807
39520
|
probesRunning++;
|
|
38808
39521
|
try {
|
|
38809
39522
|
return await fn();
|
|
@@ -39342,7 +40055,7 @@ function createHostRegistry(deps2) {
|
|
|
39342
40055
|
};
|
|
39343
40056
|
}
|
|
39344
40057
|
async function awaitTunnelHello(client, sink, record2, closeReason) {
|
|
39345
|
-
return new Promise((
|
|
40058
|
+
return new Promise((resolve47, reject) => {
|
|
39346
40059
|
if (!sink.isOpen) {
|
|
39347
40060
|
reject(
|
|
39348
40061
|
new Error(
|
|
@@ -39361,7 +40074,7 @@ async function awaitTunnelHello(client, sink, record2, closeReason) {
|
|
|
39361
40074
|
client.ready().then(
|
|
39362
40075
|
() => {
|
|
39363
40076
|
offClose();
|
|
39364
|
-
|
|
40077
|
+
resolve47();
|
|
39365
40078
|
},
|
|
39366
40079
|
(err) => {
|
|
39367
40080
|
offClose();
|
|
@@ -39406,13 +40119,13 @@ function isLegacyJoinedShape(rec) {
|
|
|
39406
40119
|
return rec.addedVia === void 0 && rec.name === rec.fingerprint && rec.provider === void 0 && rec.sandboxId === void 0 && rec.labels === void 0 && rec.lastSeen === rec.createdAt;
|
|
39407
40120
|
}
|
|
39408
40121
|
function withTimeout(p, ms, what) {
|
|
39409
|
-
return new Promise((
|
|
40122
|
+
return new Promise((resolve47, reject) => {
|
|
39410
40123
|
const timer = setTimeout(() => reject(new Error(`${what} timed out after ${ms}ms`)), ms);
|
|
39411
40124
|
if (typeof timer.unref === "function") timer.unref();
|
|
39412
40125
|
p.then(
|
|
39413
40126
|
(v) => {
|
|
39414
40127
|
clearTimeout(timer);
|
|
39415
|
-
|
|
40128
|
+
resolve47(v);
|
|
39416
40129
|
},
|
|
39417
40130
|
(e) => {
|
|
39418
40131
|
clearTimeout(timer);
|
|
@@ -39641,7 +40354,7 @@ function isValidPrompt(prompt) {
|
|
|
39641
40354
|
return prompt !== null && typeof prompt === "object";
|
|
39642
40355
|
}
|
|
39643
40356
|
function sleep(ms) {
|
|
39644
|
-
return new Promise((
|
|
40357
|
+
return new Promise((resolve47) => setTimeout(resolve47, ms));
|
|
39645
40358
|
}
|
|
39646
40359
|
function httpErrorMessage(body) {
|
|
39647
40360
|
const raw = Buffer.from(body).toString("utf8");
|
|
@@ -40374,10 +41087,12 @@ init_transcript_export();
|
|
|
40374
41087
|
init_spawn_defaults();
|
|
40375
41088
|
init_app_boundary();
|
|
40376
41089
|
var WORKFLOW_PROMPT_SOURCE = "workflow:agent-step";
|
|
41090
|
+
var TRANSPORT_END_REASONS = /* @__PURE__ */ new Set([void 0, "crashed", "daemon-restart"]);
|
|
41091
|
+
var TRANSPORT_CLOSED_RE = /\bACP connection closed\b/i;
|
|
40377
41092
|
function withReviewWorktreeZones(boundary, cwd) {
|
|
40378
41093
|
const zones = reviewWorktreeReadZones(cwd);
|
|
40379
41094
|
if (!zones) return boundary;
|
|
40380
|
-
const extra = [zones.worktree, zones.gitCommonDir].filter((
|
|
41095
|
+
const extra = [zones.worktree, zones.gitCommonDir].filter((z62) => !boundary.readOnly.includes(z62));
|
|
40381
41096
|
return extra.length === 0 ? boundary : { ...boundary, readOnly: [...boundary.readOnly, ...extra] };
|
|
40382
41097
|
}
|
|
40383
41098
|
function agentStepMcpServers(input) {
|
|
@@ -40681,7 +41396,35 @@ var SessionsRegistryAgentHost = class {
|
|
|
40681
41396
|
* synchronously, before the event bus can fire the rejection asynchronously.
|
|
40682
41397
|
*/
|
|
40683
41398
|
async sendPromptAndWait(sessionId, prompt) {
|
|
40684
|
-
await Promise.all([
|
|
41399
|
+
await Promise.all([
|
|
41400
|
+
this.waitTurnEnd(sessionId),
|
|
41401
|
+
this.registry.sendPrompt(sessionId, prompt, { source: WORKFLOW_PROMPT_SOURCE }).catch((err) => {
|
|
41402
|
+
throw this.classifyPromptFailure(sessionId, err);
|
|
41403
|
+
})
|
|
41404
|
+
]);
|
|
41405
|
+
}
|
|
41406
|
+
/** True when `sessionId` dying (with `reason`) is the session's transport
|
|
41407
|
+
* failing under the step — not this host's own cancel/release, and not a
|
|
41408
|
+
* deliberate end (see {@link TRANSPORT_END_REASONS}). */
|
|
41409
|
+
diedUnderStep(sessionId, reason) {
|
|
41410
|
+
if (this.opts?.signal?.aborted) return false;
|
|
41411
|
+
if (!this.unreleased.has(sessionId)) return false;
|
|
41412
|
+
return TRANSPORT_END_REASONS.has(reason);
|
|
41413
|
+
}
|
|
41414
|
+
/** The error a wait rejects with when the session ends mid-turn. */
|
|
41415
|
+
sessionEndedError(sessionId, status, reason) {
|
|
41416
|
+
const message = `session ${sessionId} ended with status '${status}'${reason ? ` (${reason})` : ""}`;
|
|
41417
|
+
return this.diedUnderStep(sessionId, reason) ? new AgentSessionLostError(sessionId, message) : new Error(message);
|
|
41418
|
+
}
|
|
41419
|
+
/** `registry.sendPrompt` rejected: a closed ACP connection or an already-
|
|
41420
|
+
* dead session is a transport loss ({@link AgentSessionLostError});
|
|
41421
|
+
* anything else propagates unchanged. */
|
|
41422
|
+
classifyPromptFailure(sessionId, err) {
|
|
41423
|
+
if (!(err instanceof Error)) return err;
|
|
41424
|
+
const desc = this.registry.get(sessionId);
|
|
41425
|
+
const dead = err instanceof SessionNotAliveError || TRANSPORT_CLOSED_RE.test(err.message) || desc?.status === "killed" || desc?.status === "error";
|
|
41426
|
+
if (!dead || !this.diedUnderStep(sessionId, desc?.endedReason)) return err;
|
|
41427
|
+
return new AgentSessionLostError(sessionId, `session ${sessionId} lost before its turn ended \u2014 ${err.message}`, err);
|
|
40685
41428
|
}
|
|
40686
41429
|
resolveByLabel(stepId) {
|
|
40687
41430
|
return this.sessionsByLabel.get(stepId);
|
|
@@ -40769,15 +41512,15 @@ var SessionsRegistryAgentHost = class {
|
|
|
40769
41512
|
}
|
|
40770
41513
|
// ── Internal helpers ──────────────────────────────────────────────────
|
|
40771
41514
|
waitTurnEnd(sessionId) {
|
|
40772
|
-
return new Promise((
|
|
41515
|
+
return new Promise((resolve47, reject) => {
|
|
40773
41516
|
const unsubs = [];
|
|
40774
41517
|
const done = () => {
|
|
40775
41518
|
for (const u of unsubs) u();
|
|
40776
|
-
|
|
41519
|
+
resolve47();
|
|
40777
41520
|
};
|
|
40778
41521
|
const fail4 = (reason) => {
|
|
40779
41522
|
for (const u of unsubs) u();
|
|
40780
|
-
reject(new Error(reason));
|
|
41523
|
+
reject(typeof reason === "string" ? new Error(reason) : reason);
|
|
40781
41524
|
};
|
|
40782
41525
|
unsubs.push(
|
|
40783
41526
|
this.sessionEvents.on("session:turn-end", (ev) => {
|
|
@@ -40804,7 +41547,7 @@ var SessionsRegistryAgentHost = class {
|
|
|
40804
41547
|
this.sessionEvents.on("session:exited", (ev) => {
|
|
40805
41548
|
if (ev.sessionId !== sessionId) return;
|
|
40806
41549
|
if (ev.status === "killed" || ev.status === "error") {
|
|
40807
|
-
fail4(
|
|
41550
|
+
fail4(this.sessionEndedError(sessionId, ev.status, ev.reason));
|
|
40808
41551
|
} else {
|
|
40809
41552
|
done();
|
|
40810
41553
|
}
|
|
@@ -40814,7 +41557,7 @@ var SessionsRegistryAgentHost = class {
|
|
|
40814
41557
|
if (desc?.status === "exited" || desc?.awaitingInput === true) {
|
|
40815
41558
|
done();
|
|
40816
41559
|
} else if (desc?.status === "killed" || desc?.status === "error") {
|
|
40817
|
-
fail4(
|
|
41560
|
+
fail4(this.sessionEndedError(sessionId, desc.status, desc.endedReason));
|
|
40818
41561
|
}
|
|
40819
41562
|
});
|
|
40820
41563
|
}
|
|
@@ -40824,11 +41567,11 @@ function sanitize(cacheKey) {
|
|
|
40824
41567
|
const cleaned = cacheKey.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
40825
41568
|
return cleaned.length > 0 ? cleaned : "default";
|
|
40826
41569
|
}
|
|
40827
|
-
function
|
|
41570
|
+
function isRecord10(v) {
|
|
40828
41571
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
40829
41572
|
}
|
|
40830
41573
|
function isEntry2(v) {
|
|
40831
|
-
return
|
|
41574
|
+
return isRecord10(v) && typeof v.resolvedInputHash === "string" && "output" in v;
|
|
40832
41575
|
}
|
|
40833
41576
|
function createFileStepCache(cacheKey, opts) {
|
|
40834
41577
|
const dir = opts?.dir ?? DEFAULT_CACHE_DIR();
|
|
@@ -40841,7 +41584,7 @@ function createFileStepCache(cacheKey, opts) {
|
|
|
40841
41584
|
} catch {
|
|
40842
41585
|
return {};
|
|
40843
41586
|
}
|
|
40844
|
-
if (!
|
|
41587
|
+
if (!isRecord10(parsed)) return {};
|
|
40845
41588
|
const out = {};
|
|
40846
41589
|
for (const [k, v] of Object.entries(parsed)) if (isEntry2(v)) out[k] = v;
|
|
40847
41590
|
return out;
|
|
@@ -41137,6 +41880,7 @@ function findStepDef(defs, stepId) {
|
|
|
41137
41880
|
var DEFAULT_PERSIST_PATH2 = () => join(homedir(), ".agentproto", "workflow-runs.json");
|
|
41138
41881
|
var DEFAULT_LEASE_TTL_MS = 6e4;
|
|
41139
41882
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
41883
|
+
var DEFAULT_PERSIST_MIN_INTERVAL_MS = 2e3;
|
|
41140
41884
|
function cancelledStageStatus(stage) {
|
|
41141
41885
|
return stage.steps.some((s) => s.status === "failed") ? "failed" : "cancelled";
|
|
41142
41886
|
}
|
|
@@ -41228,9 +41972,7 @@ function saveRuns(runs, persistPath) {
|
|
|
41228
41972
|
try {
|
|
41229
41973
|
mkdirSync(dirname(persistPath), { recursive: true });
|
|
41230
41974
|
const payload = JSON.stringify(
|
|
41231
|
-
Array.from(runs.values()).map((s) => s.run)
|
|
41232
|
-
null,
|
|
41233
|
-
2
|
|
41975
|
+
Array.from(runs.values()).map((s) => s.run)
|
|
41234
41976
|
) + "\n";
|
|
41235
41977
|
const tmp = `${persistPath}.tmp.${process.pid}`;
|
|
41236
41978
|
writeFileSync(tmp, payload, "utf8");
|
|
@@ -41306,7 +42048,7 @@ function createOnEscalate(state, persist) {
|
|
|
41306
42048
|
state.run.status = "awaiting-input";
|
|
41307
42049
|
persist();
|
|
41308
42050
|
try {
|
|
41309
|
-
return await new Promise((
|
|
42051
|
+
return await new Promise((resolve47, reject) => {
|
|
41310
42052
|
const timeoutMs = policy.timeoutMs ?? 3e5;
|
|
41311
42053
|
const timer = setTimeout(() => {
|
|
41312
42054
|
state.pendingResolve = void 0;
|
|
@@ -41318,7 +42060,7 @@ function createOnEscalate(state, persist) {
|
|
|
41318
42060
|
resolver: (response) => {
|
|
41319
42061
|
clearTimeout(timer);
|
|
41320
42062
|
state.pendingResolve = void 0;
|
|
41321
|
-
|
|
42063
|
+
resolve47(response);
|
|
41322
42064
|
}
|
|
41323
42065
|
};
|
|
41324
42066
|
});
|
|
@@ -41454,7 +42196,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41454
42196
|
// `resolveApproval` (wired to `workflow_escalation_resolve`'s approval
|
|
41455
42197
|
// form); the decision lands on the app ledger (`kind: "approval"`,
|
|
41456
42198
|
// `by: "human"`) per the trame rule.
|
|
41457
|
-
approve: (req) => new Promise((
|
|
42199
|
+
approve: (req) => new Promise((resolve47) => {
|
|
41458
42200
|
const approvalId = `wfappr_${randomUUID()}`;
|
|
41459
42201
|
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
41460
42202
|
state.run.status = "awaiting-approval";
|
|
@@ -41511,7 +42253,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41511
42253
|
runId: state.run.runId
|
|
41512
42254
|
}
|
|
41513
42255
|
});
|
|
41514
|
-
|
|
42256
|
+
resolve47(decision);
|
|
41515
42257
|
};
|
|
41516
42258
|
if (req.timeoutMs !== void 0) {
|
|
41517
42259
|
timer = setTimeout(() => {
|
|
@@ -41528,7 +42270,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41528
42270
|
// resumes through `resumeSuspend` (workflow_escalation_resolve's
|
|
41529
42271
|
// suspend form); after a daemon restart the run stays suspended and
|
|
41530
42272
|
// the pending entry is re-registered below.
|
|
41531
|
-
resume: (req) => new Promise((
|
|
42273
|
+
resume: (req) => new Promise((resolve47) => {
|
|
41532
42274
|
const since = (/* @__PURE__ */ new Date()).toISOString();
|
|
41533
42275
|
state.run.status = "awaiting-input";
|
|
41534
42276
|
state.run.awaitingSuspend = {
|
|
@@ -41566,7 +42308,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41566
42308
|
});
|
|
41567
42309
|
eventLog?.append({ stepId: req.stepId, type: "step.resumed", data: {} });
|
|
41568
42310
|
eventLog?.append({ type: "run.resumed", data: { stepId: req.stepId } });
|
|
41569
|
-
|
|
42311
|
+
resolve47(payload);
|
|
41570
42312
|
};
|
|
41571
42313
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
41572
42314
|
state.pendingSuspend = { stepId: req.stepId, resolve: finish };
|
|
@@ -41582,7 +42324,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41582
42324
|
// read of the turn's text). On resume, `execAgentStep` sends the
|
|
41583
42325
|
// validated payload back to the SAME session as its next prompt and
|
|
41584
42326
|
// re-applies the outcome rule — this hook only parks and resolves.
|
|
41585
|
-
onInputRequired: (req) => new Promise((
|
|
42327
|
+
onInputRequired: (req) => new Promise((resolve47) => {
|
|
41586
42328
|
const since = (/* @__PURE__ */ new Date()).toISOString();
|
|
41587
42329
|
const suspend = {
|
|
41588
42330
|
reason: "input-required",
|
|
@@ -41635,7 +42377,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41635
42377
|
});
|
|
41636
42378
|
eventLog?.append({ stepId: req.stepId, type: "step.resumed", data: {} });
|
|
41637
42379
|
eventLog?.append({ type: "run.resumed", data: { stepId: req.stepId } });
|
|
41638
|
-
|
|
42380
|
+
resolve47(payload);
|
|
41639
42381
|
};
|
|
41640
42382
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
41641
42383
|
state.pendingSuspend = { stepId: req.stepId, resolve: finish };
|
|
@@ -41671,6 +42413,19 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
41671
42413
|
}
|
|
41672
42414
|
}
|
|
41673
42415
|
},
|
|
42416
|
+
onAgentRetry: (ev) => {
|
|
42417
|
+
eventLog?.append({
|
|
42418
|
+
stepId: ev.stepId,
|
|
42419
|
+
type: "step.retrying",
|
|
42420
|
+
data: {
|
|
42421
|
+
attempt: ev.attempt,
|
|
42422
|
+
maxAttempts: ev.maxAttempts,
|
|
42423
|
+
error: ev.error,
|
|
42424
|
+
delayMs: ev.delayMs,
|
|
42425
|
+
...ev.sessionId !== void 0 ? { sessionId: ev.sessionId } : {}
|
|
42426
|
+
}
|
|
42427
|
+
});
|
|
42428
|
+
},
|
|
41674
42429
|
onStepStart: (stepId, info) => {
|
|
41675
42430
|
const cached = info?.cached === true;
|
|
41676
42431
|
if (nonLeafStepIds.has(stepId)) return;
|
|
@@ -41974,8 +42729,44 @@ function createWorkflowRunner(opts) {
|
|
|
41974
42729
|
});
|
|
41975
42730
|
};
|
|
41976
42731
|
const runs = shouldPersist ? loadRuns(persistPath, runsRoot) : /* @__PURE__ */ new Map();
|
|
42732
|
+
const persistMinIntervalMs = opts.persistMinIntervalMs ?? DEFAULT_PERSIST_MIN_INTERVAL_MS;
|
|
42733
|
+
let lastFlushAt = 0;
|
|
42734
|
+
let lastStatusSig = "";
|
|
42735
|
+
let trailingFlush;
|
|
42736
|
+
let dirty = false;
|
|
42737
|
+
const flushRuns = () => {
|
|
42738
|
+
if (trailingFlush) {
|
|
42739
|
+
clearTimeout(trailingFlush);
|
|
42740
|
+
trailingFlush = void 0;
|
|
42741
|
+
}
|
|
42742
|
+
dirty = false;
|
|
42743
|
+
lastFlushAt = Date.now();
|
|
42744
|
+
saveRuns(runs, persistPath);
|
|
42745
|
+
};
|
|
42746
|
+
const statusSig = () => {
|
|
42747
|
+
let sig = "";
|
|
42748
|
+
for (const st of runs.values()) sig += `${st.run.runId}:${st.run.status}|`;
|
|
42749
|
+
return sig;
|
|
42750
|
+
};
|
|
42751
|
+
if (shouldPersist && persistMinIntervalMs > 0) {
|
|
42752
|
+
process.once("exit", () => {
|
|
42753
|
+
if (dirty) flushRuns();
|
|
42754
|
+
});
|
|
42755
|
+
}
|
|
41977
42756
|
const persist = () => {
|
|
41978
|
-
if (shouldPersist)
|
|
42757
|
+
if (!shouldPersist) return;
|
|
42758
|
+
if (persistMinIntervalMs <= 0) return flushRuns();
|
|
42759
|
+
const sig = statusSig();
|
|
42760
|
+
const wait = lastFlushAt + persistMinIntervalMs - Date.now();
|
|
42761
|
+
if (sig !== lastStatusSig || wait <= 0) {
|
|
42762
|
+
lastStatusSig = sig;
|
|
42763
|
+
return flushRuns();
|
|
42764
|
+
}
|
|
42765
|
+
dirty = true;
|
|
42766
|
+
if (!trailingFlush) {
|
|
42767
|
+
trailingFlush = setTimeout(flushRuns, wait);
|
|
42768
|
+
trailingFlush.unref?.();
|
|
42769
|
+
}
|
|
41979
42770
|
};
|
|
41980
42771
|
const newEventLog = (runId) => shouldPersist ? createRunEventLog(runId, runsRoot) : void 0;
|
|
41981
42772
|
const workspaceRunsRoot = () => shouldPersist ? runsRoot : void 0;
|
|
@@ -43628,14 +44419,14 @@ function createSupervisorTaskGateRunner(opts) {
|
|
|
43628
44419
|
const { supervisor, registry, workspace } = opts;
|
|
43629
44420
|
const exec = opts.runCommand ?? runCommand;
|
|
43630
44421
|
const settleTimeoutMs = opts.settleTimeoutMs ?? DEFAULT_VERIFY_SETTLE_TIMEOUT_MS;
|
|
43631
|
-
const waitForSettle = (policyId) => new Promise((
|
|
44422
|
+
const waitForSettle = (policyId) => new Promise((resolve47) => {
|
|
43632
44423
|
let settled = false;
|
|
43633
44424
|
const finish = (outcome) => {
|
|
43634
44425
|
if (settled) return;
|
|
43635
44426
|
settled = true;
|
|
43636
44427
|
clearTimeout(timer);
|
|
43637
44428
|
unsub();
|
|
43638
|
-
|
|
44429
|
+
resolve47(outcome);
|
|
43639
44430
|
};
|
|
43640
44431
|
const read = () => {
|
|
43641
44432
|
const state = supervisor.getStatus(policyId);
|
|
@@ -44643,9 +45434,17 @@ function policyToActivities(policy) {
|
|
|
44643
45434
|
return records;
|
|
44644
45435
|
}
|
|
44645
45436
|
var STALE_TURN_AFTER_MS = 10 * 6e4;
|
|
45437
|
+
var TURN_NAME_MAX = 60;
|
|
45438
|
+
function sessionDisplayName(session) {
|
|
45439
|
+
const name = session.title?.trim() || session.label?.trim();
|
|
45440
|
+
if (!name) return session.id;
|
|
45441
|
+
const clipped = name.length > TURN_NAME_MAX ? `${name.slice(0, TURN_NAME_MAX - 1).trimEnd()}\u2026` : name;
|
|
45442
|
+
return `"${clipped}"`;
|
|
45443
|
+
}
|
|
44646
45444
|
function turnToActivities(session, opts = {}) {
|
|
44647
45445
|
if (session.kind !== "agent-cli") return [];
|
|
44648
45446
|
const completed = session.turnsCompleted ?? 0;
|
|
45447
|
+
const name = sessionDisplayName(session);
|
|
44649
45448
|
const base = (turn, title) => ({
|
|
44650
45449
|
id: `turn:${session.id}:${turn}`,
|
|
44651
45450
|
kind: "turn",
|
|
@@ -44659,20 +45458,20 @@ function turnToActivities(session, opts = {}) {
|
|
|
44659
45458
|
if (terminal) {
|
|
44660
45459
|
if (session.killedMidTurn === true) {
|
|
44661
45460
|
const rec = record(
|
|
44662
|
-
base(completed + 1, `Turn ${completed + 1} interrupted on ${
|
|
45461
|
+
base(completed + 1, `Turn ${completed + 1} interrupted on ${name}`),
|
|
44663
45462
|
session.status === "error" ? "failed" : "cancelled"
|
|
44664
45463
|
);
|
|
44665
45464
|
return [{ ...rec, ...session.lastActivityAt ? { endedAt: session.lastActivityAt } : {} }];
|
|
44666
45465
|
}
|
|
44667
45466
|
if (completed > 0) {
|
|
44668
|
-
const rec = record(base(completed, `Turn ${completed} completed on ${
|
|
45467
|
+
const rec = record(base(completed, `Turn ${completed} completed on ${name}`), "done");
|
|
44669
45468
|
return [{ ...rec, ...session.lastActivityAt ? { endedAt: session.lastActivityAt } : {} }];
|
|
44670
45469
|
}
|
|
44671
45470
|
return [];
|
|
44672
45471
|
}
|
|
44673
45472
|
if (session.awaitingPermission === true) {
|
|
44674
45473
|
return [
|
|
44675
|
-
pendingRecord(base(completed + 1, `Turn ${completed + 1} on ${
|
|
45474
|
+
pendingRecord(base(completed + 1, `Turn ${completed + 1} on ${name} held on a permission`), {
|
|
44676
45475
|
kind: "human-ack",
|
|
44677
45476
|
refs: [session.id],
|
|
44678
45477
|
detail: "permission request parked for permissions_respond"
|
|
@@ -44680,7 +45479,7 @@ function turnToActivities(session, opts = {}) {
|
|
|
44680
45479
|
];
|
|
44681
45480
|
}
|
|
44682
45481
|
if (session.busy === true) {
|
|
44683
|
-
const rec = record(base(completed + 1, `Turn ${completed + 1} running on ${
|
|
45482
|
+
const rec = record(base(completed + 1, `Turn ${completed + 1} running on ${name}`), "active");
|
|
44684
45483
|
if (opts.now && session.lastActivityAt) {
|
|
44685
45484
|
const nowMs = Date.parse(opts.now);
|
|
44686
45485
|
const lastMs = Date.parse(session.lastActivityAt);
|
|
@@ -44692,7 +45491,7 @@ function turnToActivities(session, opts = {}) {
|
|
|
44692
45491
|
}
|
|
44693
45492
|
if (session.awaitingInput === true) {
|
|
44694
45493
|
return [
|
|
44695
|
-
pendingRecord(base(completed + 1, `Awaiting human input on ${
|
|
45494
|
+
pendingRecord(base(completed + 1, `Awaiting human input on ${name}`), {
|
|
44696
45495
|
kind: "human-ack",
|
|
44697
45496
|
refs: [session.id],
|
|
44698
45497
|
detail: "session asked a question; next turn needs a human prompt"
|
|
@@ -44700,7 +45499,7 @@ function turnToActivities(session, opts = {}) {
|
|
|
44700
45499
|
];
|
|
44701
45500
|
}
|
|
44702
45501
|
if (completed > 0) {
|
|
44703
|
-
const rec = record(base(completed, `Turn ${completed} completed on ${
|
|
45502
|
+
const rec = record(base(completed, `Turn ${completed} completed on ${name}`), "done");
|
|
44704
45503
|
return [{ ...rec, ...session.lastActivityAt ? { endedAt: session.lastActivityAt } : {} }];
|
|
44705
45504
|
}
|
|
44706
45505
|
return [];
|
|
@@ -44982,7 +45781,7 @@ async function monitorSessionWait(opts) {
|
|
|
44982
45781
|
};
|
|
44983
45782
|
}
|
|
44984
45783
|
}
|
|
44985
|
-
return new Promise((
|
|
45784
|
+
return new Promise((resolve47) => {
|
|
44986
45785
|
const unsubs = [];
|
|
44987
45786
|
let settled = false;
|
|
44988
45787
|
const watcherLabel = callerScope?.ownerSessionId ? registry.get(callerScope.ownerSessionId)?.label ?? registry.get(callerScope.ownerSessionId)?.title : void 0;
|
|
@@ -45022,7 +45821,7 @@ async function monitorSessionWait(opts) {
|
|
|
45022
45821
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
45023
45822
|
});
|
|
45024
45823
|
}
|
|
45025
|
-
|
|
45824
|
+
resolve47(result);
|
|
45026
45825
|
};
|
|
45027
45826
|
const relevantTypes = targetEvent === "any" ? ["session:turn-end", "session:awaiting-input", "session:exited"] : targetEvent === "turn-end" ? ["session:turn-end", "session:awaiting-input"] : targetEvent === "awaiting-input" ? ["session:awaiting-input"] : ["session:exited"];
|
|
45028
45827
|
const idSet = new Set(resolvedIds);
|
|
@@ -45063,13 +45862,13 @@ async function monitorPolicyWait(opts) {
|
|
|
45063
45862
|
if (isSettledStatus(initial.status)) {
|
|
45064
45863
|
return { timedOut: false, state: initial };
|
|
45065
45864
|
}
|
|
45066
|
-
return new Promise((
|
|
45865
|
+
return new Promise((resolve47) => {
|
|
45067
45866
|
let settled = false;
|
|
45068
45867
|
const timer = setTimeout(() => {
|
|
45069
45868
|
if (settled) return;
|
|
45070
45869
|
settled = true;
|
|
45071
45870
|
unsub();
|
|
45072
|
-
|
|
45871
|
+
resolve47({ timedOut: true });
|
|
45073
45872
|
}, timeoutMs);
|
|
45074
45873
|
const unsub = supervisor.onSettle((id) => {
|
|
45075
45874
|
if (id !== policyId) return;
|
|
@@ -45079,7 +45878,7 @@ async function monitorPolicyWait(opts) {
|
|
|
45079
45878
|
settled = true;
|
|
45080
45879
|
clearTimeout(timer);
|
|
45081
45880
|
unsub();
|
|
45082
|
-
|
|
45881
|
+
resolve47({ timedOut: false, state });
|
|
45083
45882
|
});
|
|
45084
45883
|
});
|
|
45085
45884
|
}
|
|
@@ -46944,16 +47743,34 @@ var FIRST_PARTY_CATALOG_ENTRIES = [
|
|
|
46944
47743
|
"description": "View and continue any agentproto daemon session's conversation in the Agentik chat UI (assistant-ui based). Pick a session from the live/recent list or deep-link with ?session=<id>; the page replays the session's full transcript, then follows it live over SSE, and lets you send follow-up prompts into the same session. Built on @agstudio/react-agentproto (SSE transcript projection, permission approval, thread list) and @agstudio/chat-starter's assistant-ui shell, reused almost as-is.",
|
|
46945
47744
|
"source": {
|
|
46946
47745
|
"kind": "agentapp",
|
|
46947
|
-
"url": "https://github.com/agentproto/apps/releases/download/session-chat%400.1.
|
|
46948
|
-
"sha256": "
|
|
46949
|
-
"version": "0.1.
|
|
46950
|
-
"size":
|
|
47746
|
+
"url": "https://github.com/agentproto/apps/releases/download/session-chat%400.1.1/session-chat-0.1.1.agentapp",
|
|
47747
|
+
"sha256": "e8084a125e335cbc408b0e8dd3371711191f6aa0019dad4907f26b19ec9f344b",
|
|
47748
|
+
"version": "0.1.1",
|
|
47749
|
+
"size": 516437
|
|
46951
47750
|
},
|
|
46952
|
-
"
|
|
47751
|
+
"icon": "https://raw.githubusercontent.com/agentproto/apps/main/media/@agentik/session-chat/0.1.1/icon.svg",
|
|
47752
|
+
"version": "0.1.1",
|
|
46953
47753
|
"tier": "bundle",
|
|
47754
|
+
"publisher": "Agentik",
|
|
46954
47755
|
"license": {
|
|
46955
47756
|
"kind": "free"
|
|
46956
|
-
}
|
|
47757
|
+
},
|
|
47758
|
+
"tagline": "Read and continue any agentproto session as a chat thread.",
|
|
47759
|
+
"longDescription": "Session Chat turns any agentproto session into a chat thread you can read and continue.\n\nPick a running or recent session, or open one directly with `?session=<id>`. The page replays the full transcript, then follows the session live as the agent works, and lets you send follow-up prompts into the same session.\n\n## What you get\n\n- **Transcript replay and live follow**: the whole conversation, then new turns as they stream in.\n- **Tool activity at a glance**: file reads, commands and edits grouped under each reply, with the files an agent changed.\n- **Reply into the session**: send a follow-up, interrupt a running turn, queue prompts while the agent works.\n- **Approvals**: answer the agent's permission requests from the thread.\n- **Session list**: running and recent sessions, grouped by workspace, with search and pins.\n- **Start a session**: pick a harness and model and start a new chat from the same page.\n\n## Requirements\n\nSession Chat talks to your local agentproto daemon. Its bundled agent runs on the `mastra-agent` adapter: install it first with `agentproto install mastra-agent`.",
|
|
47760
|
+
"screenshots": [
|
|
47761
|
+
{
|
|
47762
|
+
"url": "https://raw.githubusercontent.com/agentproto/apps/main/media/@agentik/session-chat/0.1.1/conversation.png",
|
|
47763
|
+
"alt": "A finished session replayed as a chat thread, with a markdown table in the agent's reply and the composer below",
|
|
47764
|
+
"width": 1280,
|
|
47765
|
+
"height": 800
|
|
47766
|
+
}
|
|
47767
|
+
],
|
|
47768
|
+
"categories": [
|
|
47769
|
+
"chat",
|
|
47770
|
+
"sessions",
|
|
47771
|
+
"developer-tools"
|
|
47772
|
+
],
|
|
47773
|
+
"homepage": "https://agentproto.sh/apps/session-chat"
|
|
46957
47774
|
}
|
|
46958
47775
|
];
|
|
46959
47776
|
|
|
@@ -46994,7 +47811,7 @@ async function waitForSessionTerminal(registry, sessionId) {
|
|
|
46994
47811
|
for (let attempt = 0; attempt < MAX_SEQUENTIAL_POLLS; attempt++) {
|
|
46995
47812
|
const status = registry.get(sessionId)?.status;
|
|
46996
47813
|
if (isSessionTerminal(status)) return;
|
|
46997
|
-
await new Promise((
|
|
47814
|
+
await new Promise((resolve47) => setTimeout(resolve47, SEQUENTIAL_POLL_INTERVAL_MS));
|
|
46998
47815
|
}
|
|
46999
47816
|
}
|
|
47000
47817
|
var MODEL_ROUTED_ADAPTER = "claude-code";
|
|
@@ -47160,7 +47977,7 @@ function buildAppInstallPreview(args) {
|
|
|
47160
47977
|
runsBuildCommand: !isAgentapp && args.allowBuild === true
|
|
47161
47978
|
};
|
|
47162
47979
|
}
|
|
47163
|
-
async function dispatchAllowlistedAppTool(declaredAllowlist, input, deps2) {
|
|
47980
|
+
async function dispatchAllowlistedAppTool(declaredAllowlist, input, deps2, appDir) {
|
|
47164
47981
|
const effectiveAllowlist = [...declaredAllowlist, ...APP_UI_DISCOVERY_TOOLS];
|
|
47165
47982
|
if (!effectiveAllowlist.includes(input.tool)) {
|
|
47166
47983
|
return errorResult2(
|
|
@@ -47168,8 +47985,14 @@ async function dispatchAllowlistedAppTool(declaredAllowlist, input, deps2) {
|
|
|
47168
47985
|
);
|
|
47169
47986
|
}
|
|
47170
47987
|
const args = input.args ?? {};
|
|
47171
|
-
|
|
47172
|
-
|
|
47988
|
+
let dispatchArgs = args;
|
|
47989
|
+
if (input.tool === "app_install") {
|
|
47990
|
+
const cls = appInstallConfirmationClass(args);
|
|
47991
|
+
if (cls?.confirmed === false) return textResult2(buildAppInstallPreview(args));
|
|
47992
|
+
if (cls?.confirmed === true) {
|
|
47993
|
+
const { confirm: _confirmNonce, ...rest } = args;
|
|
47994
|
+
dispatchArgs = rest;
|
|
47995
|
+
}
|
|
47173
47996
|
}
|
|
47174
47997
|
try {
|
|
47175
47998
|
if (input.tool.startsWith("imported:")) {
|
|
@@ -47181,11 +48004,19 @@ async function dispatchAllowlistedAppTool(declaredAllowlist, input, deps2) {
|
|
|
47181
48004
|
`app_tool_call: malformed imported tool id "${input.tool}" \u2014 expected "imported:<alias>/<toolName>".`
|
|
47182
48005
|
);
|
|
47183
48006
|
}
|
|
47184
|
-
const result2 = await deps2.callImportedTool(rest.slice(0, slash), rest.slice(slash + 1),
|
|
48007
|
+
const result2 = await deps2.callImportedTool(rest.slice(0, slash), rest.slice(slash + 1), dispatchArgs);
|
|
47185
48008
|
return textResult2(result2);
|
|
47186
48009
|
}
|
|
48010
|
+
if (appDir !== void 0) {
|
|
48011
|
+
const bundled = await loadAppBundledTools(appDir);
|
|
48012
|
+
const bundledTool = bundled.tools.find((t) => t.id === input.tool);
|
|
48013
|
+
if (bundledTool) {
|
|
48014
|
+
const output = await runTool({ tool: bundledTool, candidates: bundled.drivers, input: args });
|
|
48015
|
+
return textResult2(output);
|
|
48016
|
+
}
|
|
48017
|
+
}
|
|
47187
48018
|
if (!deps2.dispatchTool) return notEnabled("app_tool_call");
|
|
47188
|
-
const result = await deps2.dispatchTool(input.tool,
|
|
48019
|
+
const result = await deps2.dispatchTool(input.tool, dispatchArgs);
|
|
47189
48020
|
return textResult2(result);
|
|
47190
48021
|
} catch (err) {
|
|
47191
48022
|
return errorResult2(`app_tool_call: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -47196,7 +48027,7 @@ async function performAppToolCall(appRegistry, input, deps2) {
|
|
|
47196
48027
|
if (!installed || !installed.ui) {
|
|
47197
48028
|
return errorResult2(`app_tool_call: app "${input.appId}" is not installed or has no UI.`);
|
|
47198
48029
|
}
|
|
47199
|
-
return dispatchAllowlistedAppTool(installed.ui.tools ?? [], input, deps2);
|
|
48030
|
+
return dispatchAllowlistedAppTool(installed.ui.tools ?? [], input, deps2, installed.dir);
|
|
47200
48031
|
}
|
|
47201
48032
|
async function performBuiltinPanelToolCall(tools, input, deps2) {
|
|
47202
48033
|
if (tools === void 0) {
|
|
@@ -47323,7 +48154,7 @@ async function performAppCall(appRegistry, input, deps2) {
|
|
|
47323
48154
|
error: `app_call: workflow "${input.workflow}" on "${input.appId}" did not finish within ${timeoutMs}ms (run ${run.runId} still ${status?.status ?? "unknown"}).`
|
|
47324
48155
|
};
|
|
47325
48156
|
}
|
|
47326
|
-
await new Promise((
|
|
48157
|
+
await new Promise((resolve47) => setTimeout(resolve47, APP_CALL_POLL_INTERVAL_MS));
|
|
47327
48158
|
}
|
|
47328
48159
|
}
|
|
47329
48160
|
function refIdOf(ref) {
|
|
@@ -48709,12 +49540,12 @@ async function readBody2(req) {
|
|
|
48709
49540
|
}
|
|
48710
49541
|
return { raw: Buffer.concat(chunks).toString("utf8") };
|
|
48711
49542
|
}
|
|
48712
|
-
var
|
|
49543
|
+
var isRecord11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
48713
49544
|
function parseParts(raw) {
|
|
48714
49545
|
if (!Array.isArray(raw)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "message.parts must be an array");
|
|
48715
49546
|
const parts = [];
|
|
48716
49547
|
for (const p of raw) {
|
|
48717
|
-
if (!
|
|
49548
|
+
if (!isRecord11(p)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "each message part must be an object");
|
|
48718
49549
|
if ("text" in p) {
|
|
48719
49550
|
if (typeof p.text !== "string") throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "part.text must be a string");
|
|
48720
49551
|
parts.push({ text: p.text });
|
|
@@ -48747,13 +49578,13 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
|
|
|
48747
49578
|
send(res, 200, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.parse, message: "parse error: body is not valid JSON" } });
|
|
48748
49579
|
return true;
|
|
48749
49580
|
}
|
|
48750
|
-
if (!
|
|
49581
|
+
if (!isRecord11(rpc) || rpc.jsonrpc !== "2.0" || typeof rpc.method !== "string") {
|
|
48751
49582
|
send(res, 200, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.invalidRequest, message: 'invalid request: expected a JSON-RPC 2.0 object with a string "method" (batches are not supported)' } });
|
|
48752
49583
|
return true;
|
|
48753
49584
|
}
|
|
48754
49585
|
const id = typeof rpc.id === "string" || typeof rpc.id === "number" ? rpc.id : null;
|
|
48755
49586
|
const method = rpc.method;
|
|
48756
|
-
const params =
|
|
49587
|
+
const params = isRecord11(rpc.params) ? rpc.params : {};
|
|
48757
49588
|
const requested = negotiateVersion(
|
|
48758
49589
|
typeof req.headers["a2a-version"] === "string" ? req.headers["a2a-version"] : void 0,
|
|
48759
49590
|
new URL(req.url ?? "/", "http://localhost").searchParams.get("version")
|
|
@@ -48811,14 +49642,14 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
|
|
|
48811
49642
|
throw new RpcFailure(JSON_RPC_ERROR.unsupportedOperation, `method "SendMessage" is not allowed for app "${appId}": its manifest does not declare accepts.tasks: true`);
|
|
48812
49643
|
}
|
|
48813
49644
|
const rawMessage = params.message;
|
|
48814
|
-
if (!
|
|
49645
|
+
if (!isRecord11(rawMessage) || rawMessage.role !== "ROLE_USER") {
|
|
48815
49646
|
throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'params.message must be an object with role "ROLE_USER"');
|
|
48816
49647
|
}
|
|
48817
49648
|
const parts = parseParts(rawMessage.parts);
|
|
48818
49649
|
const { skill, prompt } = extractSkillRequest(
|
|
48819
|
-
{ parts, ...
|
|
49650
|
+
{ parts, ...isRecord11(rawMessage.metadata) ? { metadata: rawMessage.metadata } : {} },
|
|
48820
49651
|
appId,
|
|
48821
|
-
|
|
49652
|
+
isRecord11(params.metadata) ? params.metadata : void 0
|
|
48822
49653
|
);
|
|
48823
49654
|
if (!skill) {
|
|
48824
49655
|
throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'no skill requested: set message.metadata.skill or start the first text part with "skill:<id>"');
|
|
@@ -49828,6 +50659,7 @@ async function startHttpServer(opts) {
|
|
|
49828
50659
|
const auth = readAuth();
|
|
49829
50660
|
if (auth.mode !== "bearer") return true;
|
|
49830
50661
|
if (path === "/health") return true;
|
|
50662
|
+
if (opts.eventsMcp && path.startsWith("/mcp/events/")) return true;
|
|
49831
50663
|
if (/^\/inbound\/[^/]+$/.test(path)) return true;
|
|
49832
50664
|
if (isAppUiShellRequest(method, path)) return true;
|
|
49833
50665
|
const header = req.headers.authorization;
|
|
@@ -50094,6 +50926,47 @@ async function startHttpServer(opts) {
|
|
|
50094
50926
|
const server2 = await opts.orchestratorMcpServerFactory(scope);
|
|
50095
50927
|
await serveMcp(req, res, server2);
|
|
50096
50928
|
}
|
|
50929
|
+
const EVENTS_BODY_CAP = 1024 * 1024;
|
|
50930
|
+
async function handleEventsMcp(req, res, presented) {
|
|
50931
|
+
const cfg = opts.eventsMcp;
|
|
50932
|
+
const send2 = (status, headers, body) => {
|
|
50933
|
+
res.writeHead(status, { "cache-control": "no-store", ...headers });
|
|
50934
|
+
res.end(body);
|
|
50935
|
+
};
|
|
50936
|
+
if (!cfg) return send2(404, { "content-type": "application/json" }, JSON.stringify({ error: "not_found" }));
|
|
50937
|
+
const want = Buffer.from(cfg.secret);
|
|
50938
|
+
const got = Buffer.from(presented);
|
|
50939
|
+
if (got.length !== want.length || !timingSafeEqual(got, want)) {
|
|
50940
|
+
return send2(404, { "content-type": "application/json" }, JSON.stringify({ error: "not_found" }));
|
|
50941
|
+
}
|
|
50942
|
+
const origin = req.headers.origin;
|
|
50943
|
+
if (typeof origin === "string" && !(cfg.allowedOrigins ?? []).includes(origin)) {
|
|
50944
|
+
return send2(403, { "content-type": "application/json" }, JSON.stringify({ error: "origin_not_allowed" }));
|
|
50945
|
+
}
|
|
50946
|
+
const chunks = [];
|
|
50947
|
+
let size = 0;
|
|
50948
|
+
for await (const chunk of req) {
|
|
50949
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
50950
|
+
size += buf.length;
|
|
50951
|
+
if (size > EVENTS_BODY_CAP) {
|
|
50952
|
+
return send2(413, { "content-type": "application/json" }, JSON.stringify({ error: "body_too_large" }));
|
|
50953
|
+
}
|
|
50954
|
+
chunks.push(buf);
|
|
50955
|
+
}
|
|
50956
|
+
const controller = new AbortController();
|
|
50957
|
+
res.on("close", () => {
|
|
50958
|
+
if (!res.writableEnded) controller.abort();
|
|
50959
|
+
});
|
|
50960
|
+
const out = await handleModernRequest(
|
|
50961
|
+
{
|
|
50962
|
+
method: req.method ?? "GET",
|
|
50963
|
+
headers: req.headers,
|
|
50964
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
50965
|
+
},
|
|
50966
|
+
{ createServer: cfg.createServer, signal: controller.signal }
|
|
50967
|
+
);
|
|
50968
|
+
send2(out.status, out.headers, out.body);
|
|
50969
|
+
}
|
|
50097
50970
|
async function handleImportedMcp(req, res, importId) {
|
|
50098
50971
|
if (!authorizeMcp(req, res)) return;
|
|
50099
50972
|
if (!opts.mcpProxy) {
|
|
@@ -50360,7 +51233,7 @@ async function startHttpServer(opts) {
|
|
|
50360
51233
|
});
|
|
50361
51234
|
}
|
|
50362
51235
|
applyCors(req, res);
|
|
50363
|
-
if (req.method === "OPTIONS") {
|
|
51236
|
+
if (req.method === "OPTIONS" && !(opts.eventsMcp && path.startsWith("/mcp/events/"))) {
|
|
50364
51237
|
res.writeHead(204);
|
|
50365
51238
|
res.end();
|
|
50366
51239
|
return;
|
|
@@ -50387,6 +51260,21 @@ async function startHttpServer(opts) {
|
|
|
50387
51260
|
handleEvents(req, res);
|
|
50388
51261
|
return;
|
|
50389
51262
|
}
|
|
51263
|
+
if (path.startsWith("/mcp/events/")) {
|
|
51264
|
+
let presented;
|
|
51265
|
+
try {
|
|
51266
|
+
presented = decodeURIComponent(path.slice("/mcp/events/".length));
|
|
51267
|
+
} catch (error) {
|
|
51268
|
+
if (!(error instanceof URIError)) throw error;
|
|
51269
|
+
}
|
|
51270
|
+
if (presented === void 0) {
|
|
51271
|
+
res.writeHead(404, { "content-type": "application/json", "cache-control": "no-store" });
|
|
51272
|
+
res.end(JSON.stringify({ error: "not_found" }));
|
|
51273
|
+
return;
|
|
51274
|
+
}
|
|
51275
|
+
await handleEventsMcp(req, res, presented);
|
|
51276
|
+
return;
|
|
51277
|
+
}
|
|
50390
51278
|
if (path === "/mcp/orchestrator") {
|
|
50391
51279
|
await handleOrchestratorMcp(req, res);
|
|
50392
51280
|
return;
|
|
@@ -50994,15 +51882,17 @@ async function startHttpServer(opts) {
|
|
|
50994
51882
|
try {
|
|
50995
51883
|
const base = str3("base");
|
|
50996
51884
|
const anchor = str3("anchor");
|
|
50997
|
-
const result =
|
|
50998
|
-
|
|
50999
|
-
|
|
51000
|
-
|
|
51001
|
-
|
|
51002
|
-
|
|
51003
|
-
|
|
51004
|
-
|
|
51005
|
-
|
|
51885
|
+
const result = withBranchGcApplySummary(
|
|
51886
|
+
await opts.runBranchGc({
|
|
51887
|
+
repoRoot: resolved.repoRoot,
|
|
51888
|
+
apply,
|
|
51889
|
+
includeReviewed: bool3("includeReviewed"),
|
|
51890
|
+
...base ? { base } : {},
|
|
51891
|
+
...scopes?.length ? { scopes } : {},
|
|
51892
|
+
...minAgeDays !== void 0 && Number.isFinite(minAgeDays) ? { minAgeDays } : {},
|
|
51893
|
+
...anchor ? { anchor } : {}
|
|
51894
|
+
})
|
|
51895
|
+
);
|
|
51006
51896
|
res.writeHead(200, { "content-type": "application/json" });
|
|
51007
51897
|
res.end(JSON.stringify(result));
|
|
51008
51898
|
} catch (err) {
|
|
@@ -52074,16 +52964,16 @@ async function startHttpServer(opts) {
|
|
|
52074
52964
|
});
|
|
52075
52965
|
});
|
|
52076
52966
|
const bind = opts.bind ?? "127.0.0.1";
|
|
52077
|
-
await new Promise((
|
|
52967
|
+
await new Promise((resolve47, reject) => {
|
|
52078
52968
|
server.once("error", reject);
|
|
52079
|
-
server.listen(opts.port, bind, () =>
|
|
52969
|
+
server.listen(opts.port, bind, () => resolve47());
|
|
52080
52970
|
});
|
|
52081
52971
|
return {
|
|
52082
52972
|
url: `http://${bind}:${opts.port}`,
|
|
52083
52973
|
async stop() {
|
|
52084
52974
|
wss.close();
|
|
52085
52975
|
server.closeAllConnections();
|
|
52086
|
-
await new Promise((
|
|
52976
|
+
await new Promise((resolve47) => server.close(() => resolve47()));
|
|
52087
52977
|
}
|
|
52088
52978
|
};
|
|
52089
52979
|
}
|
|
@@ -52292,9 +53182,9 @@ async function currentTranscriptSeq(id) {
|
|
|
52292
53182
|
let stream;
|
|
52293
53183
|
try {
|
|
52294
53184
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
52295
|
-
await new Promise((
|
|
53185
|
+
await new Promise((resolve47, reject) => {
|
|
52296
53186
|
stream.once("error", reject);
|
|
52297
|
-
stream.once("open",
|
|
53187
|
+
stream.once("open", resolve47);
|
|
52298
53188
|
});
|
|
52299
53189
|
} catch (err) {
|
|
52300
53190
|
if (err.code === "ENOENT") return 0;
|
|
@@ -52319,9 +53209,9 @@ async function* transcriptDiskRecords(id) {
|
|
|
52319
53209
|
let stream;
|
|
52320
53210
|
try {
|
|
52321
53211
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
52322
|
-
await new Promise((
|
|
53212
|
+
await new Promise((resolve47, reject) => {
|
|
52323
53213
|
stream.once("error", reject);
|
|
52324
|
-
stream.once("open",
|
|
53214
|
+
stream.once("open", resolve47);
|
|
52325
53215
|
});
|
|
52326
53216
|
} catch (err) {
|
|
52327
53217
|
if (err.code === "ENOENT") return;
|
|
@@ -52823,8 +53713,45 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
52823
53713
|
if (kindParam && kindParam !== "all") return s.kind === kindParam;
|
|
52824
53714
|
return includeCommands || s.kind !== "command";
|
|
52825
53715
|
};
|
|
53716
|
+
const listFilters = parseSessionListFilterParams(params);
|
|
53717
|
+
if (listFilters.parentSessionId) {
|
|
53718
|
+
listFilters.parentSessionId = registry.findByIdOrName(listFilters.parentSessionId)?.id ?? listFilters.parentSessionId;
|
|
53719
|
+
}
|
|
53720
|
+
const statusParam = params.get("status");
|
|
53721
|
+
const onlyAlive = params.get("onlyAlive") === "true";
|
|
53722
|
+
const limitParam = params.get("limit");
|
|
53723
|
+
const limit = limitParam === null ? void 0 : Number.parseInt(limitParam, 10);
|
|
53724
|
+
if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1)) {
|
|
53725
|
+
json3(400, {
|
|
53726
|
+
error: "invalid_limit",
|
|
53727
|
+
message: `?limit must be a positive integer, got ${JSON.stringify(limitParam)}.`
|
|
53728
|
+
});
|
|
53729
|
+
return true;
|
|
53730
|
+
}
|
|
53731
|
+
const narrowed = hasSessionListFilters(listFilters) || statusParam !== null || onlyAlive || limit !== void 0;
|
|
53732
|
+
let narrow;
|
|
53733
|
+
try {
|
|
53734
|
+
const keep = compileSessionListFilters(listFilters);
|
|
53735
|
+
narrow = (list) => list.filter((s) => {
|
|
53736
|
+
if (statusParam) {
|
|
53737
|
+
if (s.status !== statusParam) return false;
|
|
53738
|
+
} else if (onlyAlive && s.status !== "running" && s.status !== "starting") {
|
|
53739
|
+
return false;
|
|
53740
|
+
}
|
|
53741
|
+
return keep(s);
|
|
53742
|
+
});
|
|
53743
|
+
} catch (err) {
|
|
53744
|
+
if (err instanceof SessionListFilterError) {
|
|
53745
|
+
json3(400, { error: "invalid_filter", message: err.message });
|
|
53746
|
+
return true;
|
|
53747
|
+
}
|
|
53748
|
+
throw err;
|
|
53749
|
+
}
|
|
52826
53750
|
let rows = registry.list({ includeArchived });
|
|
52827
|
-
rows = rows.filter(matchesFilter);
|
|
53751
|
+
rows = narrow(rows.filter(matchesFilter));
|
|
53752
|
+
if (narrowed) rows = sortNewestActivityFirst(rows);
|
|
53753
|
+
const total = rows.length;
|
|
53754
|
+
if (limit !== void 0 && sinceParam === null) rows = rows.slice(0, limit);
|
|
52828
53755
|
let body;
|
|
52829
53756
|
if (sinceParam !== null) {
|
|
52830
53757
|
const sinceMs = Date.parse(sinceParam);
|
|
@@ -52840,11 +53767,14 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
52840
53767
|
return Number.isNaN(ts) || ts >= sinceMs;
|
|
52841
53768
|
});
|
|
52842
53769
|
const removed = includeArchived ? [] : registry.list({ includeArchived: true }).filter(
|
|
52843
|
-
(s) => s.archived && matchesFilter(s) && (s.archivedAt === void 0 || Date.parse(s.archivedAt) >= sinceMs)
|
|
53770
|
+
(s) => s.archived && matchesFilter(s) && narrow([s]).length > 0 && (s.archivedAt === void 0 || Date.parse(s.archivedAt) >= sinceMs)
|
|
52844
53771
|
).map((s) => s.id);
|
|
52845
53772
|
body = { sessions: changed.map((s) => projectSessionForHttp(s, fields)), removed };
|
|
52846
53773
|
} else {
|
|
52847
|
-
body = {
|
|
53774
|
+
body = {
|
|
53775
|
+
sessions: rows.map((s) => projectSessionForHttp(s, fields)),
|
|
53776
|
+
...narrowed ? { total } : {}
|
|
53777
|
+
};
|
|
52848
53778
|
}
|
|
52849
53779
|
const serialized = JSON.stringify(body);
|
|
52850
53780
|
const etag = strongEtag(serialized);
|
|
@@ -54077,9 +55007,9 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
54077
55007
|
let fileStream;
|
|
54078
55008
|
try {
|
|
54079
55009
|
fileStream = createReadStream(filePath, { encoding: "utf8" });
|
|
54080
|
-
await new Promise((
|
|
55010
|
+
await new Promise((resolve47, reject) => {
|
|
54081
55011
|
fileStream.once("error", reject);
|
|
54082
|
-
fileStream.once("open",
|
|
55012
|
+
fileStream.once("open", resolve47);
|
|
54083
55013
|
});
|
|
54084
55014
|
} catch (err) {
|
|
54085
55015
|
const code = err.code;
|
|
@@ -54135,9 +55065,9 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
54135
55065
|
let fileStream;
|
|
54136
55066
|
try {
|
|
54137
55067
|
fileStream = createReadStream(filePath, { encoding: "utf8" });
|
|
54138
|
-
await new Promise((
|
|
55068
|
+
await new Promise((resolve47, reject) => {
|
|
54139
55069
|
fileStream.once("error", reject);
|
|
54140
|
-
fileStream.once("open",
|
|
55070
|
+
fileStream.once("open", resolve47);
|
|
54141
55071
|
});
|
|
54142
55072
|
} catch (err) {
|
|
54143
55073
|
const code = err.code;
|
|
@@ -54527,7 +55457,7 @@ async function handleSentinels(req, res, path, deps2) {
|
|
|
54527
55457
|
res.end(JSON.stringify(body));
|
|
54528
55458
|
};
|
|
54529
55459
|
if (path === "/sentinels" && req.method === "GET") {
|
|
54530
|
-
json3(200, { sentinels: deps2.store.list().map(
|
|
55460
|
+
json3(200, { sentinels: deps2.store.list().map((s) => sentinelViewWithDelivery(s, deps2.deliveryStatus?.(s.id))) });
|
|
54531
55461
|
return true;
|
|
54532
55462
|
}
|
|
54533
55463
|
if (path === "/sentinels" && req.method === "POST") {
|
|
@@ -54581,7 +55511,10 @@ async function handleSentinels(req, res, path, deps2) {
|
|
|
54581
55511
|
return true;
|
|
54582
55512
|
}
|
|
54583
55513
|
if (req.method === "DELETE") {
|
|
54584
|
-
const removed = await cancelSentinelWatch(
|
|
55514
|
+
const removed = await cancelSentinelWatch(
|
|
55515
|
+
{ store: deps2.store, resolveProvider: deps2.resolveProvider, ...deps2.tombstones ? { tombstones: deps2.tombstones } : {} },
|
|
55516
|
+
id
|
|
55517
|
+
);
|
|
54585
55518
|
if (!removed) {
|
|
54586
55519
|
json3(404, { error: "sentinel_not_found", id });
|
|
54587
55520
|
return true;
|
|
@@ -56891,7 +57824,7 @@ function createTransmitterBindingStore(opts) {
|
|
|
56891
57824
|
const nowMs = Date.now;
|
|
56892
57825
|
const debounceMs = PERSIST_DEBOUNCE_MS6;
|
|
56893
57826
|
let persistTimer = null;
|
|
56894
|
-
const
|
|
57827
|
+
const load2 = () => {
|
|
56895
57828
|
const out = /* @__PURE__ */ new Map();
|
|
56896
57829
|
let raw;
|
|
56897
57830
|
try {
|
|
@@ -56916,7 +57849,7 @@ function createTransmitterBindingStore(opts) {
|
|
|
56916
57849
|
}
|
|
56917
57850
|
return out;
|
|
56918
57851
|
};
|
|
56919
|
-
const bindings =
|
|
57852
|
+
const bindings = load2();
|
|
56920
57853
|
const snapshot = () => {
|
|
56921
57854
|
const out = {};
|
|
56922
57855
|
for (const [key, binding] of bindings.entries()) out[key] = binding;
|
|
@@ -56976,7 +57909,7 @@ function createInboundEndpointStore(opts) {
|
|
|
56976
57909
|
const debounceMs = opts?.debounceMs ?? PERSIST_DEBOUNCE_MS7;
|
|
56977
57910
|
const persist = opts?.persist ?? opts?.filePath !== void 0;
|
|
56978
57911
|
let persistTimer = null;
|
|
56979
|
-
const
|
|
57912
|
+
const load2 = () => {
|
|
56980
57913
|
const out = /* @__PURE__ */ new Map();
|
|
56981
57914
|
if (!persist) return out;
|
|
56982
57915
|
let raw;
|
|
@@ -57002,7 +57935,7 @@ function createInboundEndpointStore(opts) {
|
|
|
57002
57935
|
}
|
|
57003
57936
|
return out;
|
|
57004
57937
|
};
|
|
57005
|
-
const endpoints =
|
|
57938
|
+
const endpoints = load2();
|
|
57006
57939
|
const seen = /* @__PURE__ */ new Map();
|
|
57007
57940
|
let totalSeen = 0;
|
|
57008
57941
|
const snapshot = () => {
|
|
@@ -57165,6 +58098,299 @@ init_session_message();
|
|
|
57165
58098
|
init_sessions();
|
|
57166
58099
|
init_session_end_reason();
|
|
57167
58100
|
init_session_retirement();
|
|
58101
|
+
var tmpSeq6 = 0;
|
|
58102
|
+
var tmpPathFor2 = (filePath) => `${filePath}.tmp.${process.pid}.${++tmpSeq6}`;
|
|
58103
|
+
function isDirSyncUnsupported(err) {
|
|
58104
|
+
const code = err.code;
|
|
58105
|
+
return code === "EINVAL" || code === "EPERM" || code === "EISDIR" || code === "ENOTSUP";
|
|
58106
|
+
}
|
|
58107
|
+
async function writeFileDurable(filePath, data, opts = {}) {
|
|
58108
|
+
const mode = opts.mode ?? 384;
|
|
58109
|
+
const dir = dirname(filePath);
|
|
58110
|
+
await promises.mkdir(dir, { recursive: true });
|
|
58111
|
+
const tmp = tmpPathFor2(filePath);
|
|
58112
|
+
try {
|
|
58113
|
+
const fh = await promises.open(tmp, "w", mode);
|
|
58114
|
+
try {
|
|
58115
|
+
await fh.writeFile(data);
|
|
58116
|
+
await fh.chmod(mode);
|
|
58117
|
+
await fh.sync();
|
|
58118
|
+
} finally {
|
|
58119
|
+
await fh.close();
|
|
58120
|
+
}
|
|
58121
|
+
if (opts.commitIf && !opts.commitIf()) {
|
|
58122
|
+
await promises.unlink(tmp).catch(() => void 0);
|
|
58123
|
+
return;
|
|
58124
|
+
}
|
|
58125
|
+
await promises.rename(tmp, filePath);
|
|
58126
|
+
} catch (err) {
|
|
58127
|
+
await promises.unlink(tmp).catch(() => void 0);
|
|
58128
|
+
throw err;
|
|
58129
|
+
}
|
|
58130
|
+
try {
|
|
58131
|
+
const dh = await promises.open(dir, "r");
|
|
58132
|
+
try {
|
|
58133
|
+
await dh.sync();
|
|
58134
|
+
} finally {
|
|
58135
|
+
await dh.close();
|
|
58136
|
+
}
|
|
58137
|
+
} catch (err) {
|
|
58138
|
+
if (!isDirSyncUnsupported(err)) throw err;
|
|
58139
|
+
}
|
|
58140
|
+
}
|
|
58141
|
+
function writeFileDurableSync(filePath, data, mode = 384) {
|
|
58142
|
+
const dir = dirname(filePath);
|
|
58143
|
+
mkdirSync(dir, { recursive: true });
|
|
58144
|
+
const tmp = tmpPathFor2(filePath);
|
|
58145
|
+
try {
|
|
58146
|
+
const fd = openSync(tmp, "w", mode);
|
|
58147
|
+
try {
|
|
58148
|
+
writeSync(fd, data);
|
|
58149
|
+
fsyncSync(fd);
|
|
58150
|
+
} finally {
|
|
58151
|
+
closeSync(fd);
|
|
58152
|
+
}
|
|
58153
|
+
renameSync(tmp, filePath);
|
|
58154
|
+
} catch (err) {
|
|
58155
|
+
try {
|
|
58156
|
+
unlinkSync(tmp);
|
|
58157
|
+
} catch {
|
|
58158
|
+
}
|
|
58159
|
+
throw err;
|
|
58160
|
+
}
|
|
58161
|
+
try {
|
|
58162
|
+
const dfd = openSync(dir, "r");
|
|
58163
|
+
try {
|
|
58164
|
+
fsyncSync(dfd);
|
|
58165
|
+
} finally {
|
|
58166
|
+
closeSync(dfd);
|
|
58167
|
+
}
|
|
58168
|
+
} catch (err) {
|
|
58169
|
+
if (!isDirSyncUnsupported(err)) throw err;
|
|
58170
|
+
}
|
|
58171
|
+
}
|
|
58172
|
+
function appendLineDurableSync(filePath, line, mode = 384) {
|
|
58173
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
58174
|
+
const existed = (() => {
|
|
58175
|
+
try {
|
|
58176
|
+
closeSync(openSync(filePath, "r"));
|
|
58177
|
+
return true;
|
|
58178
|
+
} catch {
|
|
58179
|
+
return false;
|
|
58180
|
+
}
|
|
58181
|
+
})();
|
|
58182
|
+
const fd = openSync(filePath, "a", mode);
|
|
58183
|
+
try {
|
|
58184
|
+
appendFileSync(fd, line.endsWith("\n") ? line : `${line}
|
|
58185
|
+
`);
|
|
58186
|
+
fsyncSync(fd);
|
|
58187
|
+
} finally {
|
|
58188
|
+
closeSync(fd);
|
|
58189
|
+
}
|
|
58190
|
+
if (!existed) {
|
|
58191
|
+
try {
|
|
58192
|
+
const dfd = openSync(dirname(filePath), "r");
|
|
58193
|
+
try {
|
|
58194
|
+
fsyncSync(dfd);
|
|
58195
|
+
} finally {
|
|
58196
|
+
closeSync(dfd);
|
|
58197
|
+
}
|
|
58198
|
+
} catch (err) {
|
|
58199
|
+
if (!isDirSyncUnsupported(err)) throw err;
|
|
58200
|
+
}
|
|
58201
|
+
}
|
|
58202
|
+
}
|
|
58203
|
+
|
|
58204
|
+
// src/sentinel-cancel-tombstones.ts
|
|
58205
|
+
function defaultCancelTombstonePath() {
|
|
58206
|
+
return resolve(process.env.AGENTPROTO_HOME ?? join(homedir(), ".agentproto"), "sentinel-cancel-tombstones.json");
|
|
58207
|
+
}
|
|
58208
|
+
var SECRETISH = /secret|token|key|password|credential|authorization/i;
|
|
58209
|
+
function stripSecrets(handle) {
|
|
58210
|
+
const state = {};
|
|
58211
|
+
for (const [k, v] of Object.entries(handle.state ?? {})) if (!SECRETISH.test(k)) state[k] = v;
|
|
58212
|
+
return {
|
|
58213
|
+
provider: handle.provider,
|
|
58214
|
+
...handle.remoteId !== void 0 ? { remoteId: handle.remoteId } : {},
|
|
58215
|
+
...handle.cursor !== void 0 ? { cursor: handle.cursor } : {},
|
|
58216
|
+
...Object.keys(state).length > 0 ? { state } : {}
|
|
58217
|
+
};
|
|
58218
|
+
}
|
|
58219
|
+
var keyOf2 = (provider, remoteId) => `${provider}|${remoteId}`;
|
|
58220
|
+
function load(filePath) {
|
|
58221
|
+
try {
|
|
58222
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
58223
|
+
if (!Array.isArray(parsed.tombstones)) return [];
|
|
58224
|
+
return parsed.tombstones.filter(
|
|
58225
|
+
(t) => typeof t === "object" && t !== null && typeof t.key === "string"
|
|
58226
|
+
);
|
|
58227
|
+
} catch {
|
|
58228
|
+
return [];
|
|
58229
|
+
}
|
|
58230
|
+
}
|
|
58231
|
+
function createCancelTombstoneStore(opts) {
|
|
58232
|
+
const nowMs = opts.nowMs ?? Date.now;
|
|
58233
|
+
const log = opts.log ?? ((line) => console.warn(line));
|
|
58234
|
+
const base = opts.baseBackoffMs ?? 3e4;
|
|
58235
|
+
const max = opts.maxBackoffMs ?? 60 * 60 * 1e3;
|
|
58236
|
+
const rows = /* @__PURE__ */ new Map();
|
|
58237
|
+
if (opts.filePath) for (const t of load(opts.filePath)) rows.set(t.key, t);
|
|
58238
|
+
let writeTail = Promise.resolve();
|
|
58239
|
+
function persist() {
|
|
58240
|
+
if (!opts.filePath) return Promise.resolve();
|
|
58241
|
+
const filePath = opts.filePath;
|
|
58242
|
+
const run = () => writeFileDurable(filePath, JSON.stringify({ version: 1, tombstones: [...rows.values()] }, null, 2));
|
|
58243
|
+
const next = writeTail.then(run, run);
|
|
58244
|
+
writeTail = next.catch(() => void 0);
|
|
58245
|
+
return next;
|
|
58246
|
+
}
|
|
58247
|
+
const inFlight2 = /* @__PURE__ */ new Map();
|
|
58248
|
+
async function attempt(t, explicit = false) {
|
|
58249
|
+
const running = inFlight2.get(t.key);
|
|
58250
|
+
if (running) return running;
|
|
58251
|
+
const p = (async () => {
|
|
58252
|
+
if (!explicit && opts.isRemoteInUse?.(t.provider, t.remoteId)) {
|
|
58253
|
+
rows.delete(t.key);
|
|
58254
|
+
await persist().catch((err) => log(`[sentinel-tombstones] persist failed: ${String(err)}`));
|
|
58255
|
+
return true;
|
|
58256
|
+
}
|
|
58257
|
+
const provider = await opts.resolveProvider(t.provider);
|
|
58258
|
+
let error;
|
|
58259
|
+
if (!provider) error = `unknown sentinel provider "${t.provider}"`;
|
|
58260
|
+
else {
|
|
58261
|
+
try {
|
|
58262
|
+
await provider.cancel(t.handle);
|
|
58263
|
+
} catch (err) {
|
|
58264
|
+
error = err instanceof Error ? err.message : String(err);
|
|
58265
|
+
}
|
|
58266
|
+
}
|
|
58267
|
+
const live = rows.get(t.key);
|
|
58268
|
+
if (error === void 0) {
|
|
58269
|
+
if (live) rows.delete(t.key);
|
|
58270
|
+
await persist().catch((err) => log(`[sentinel-tombstones] persist failed: ${String(err)}`));
|
|
58271
|
+
return true;
|
|
58272
|
+
}
|
|
58273
|
+
if (live) {
|
|
58274
|
+
live.attempts += 1;
|
|
58275
|
+
live.lastAttemptAt = nowMs();
|
|
58276
|
+
live.lastError = error.slice(0, 300);
|
|
58277
|
+
live.nextAttemptAt = nowMs() + Math.min(max, base * 2 ** Math.min(live.attempts, 20));
|
|
58278
|
+
await persist().catch((err) => log(`[sentinel-tombstones] persist failed: ${String(err)}`));
|
|
58279
|
+
}
|
|
58280
|
+
return false;
|
|
58281
|
+
})().finally(() => inFlight2.delete(t.key));
|
|
58282
|
+
inFlight2.set(t.key, p);
|
|
58283
|
+
return p;
|
|
58284
|
+
}
|
|
58285
|
+
return {
|
|
58286
|
+
async cancel(handle, afterRecorded) {
|
|
58287
|
+
const provider = await opts.resolveProvider(handle.provider);
|
|
58288
|
+
if (!handle.remoteId || !provider?.exclusiveRemote) {
|
|
58289
|
+
afterRecorded?.();
|
|
58290
|
+
let converged2 = true;
|
|
58291
|
+
try {
|
|
58292
|
+
await provider?.cancel(handle);
|
|
58293
|
+
} catch {
|
|
58294
|
+
converged2 = false;
|
|
58295
|
+
}
|
|
58296
|
+
return { converged: converged2 };
|
|
58297
|
+
}
|
|
58298
|
+
const key = keyOf2(handle.provider, handle.remoteId);
|
|
58299
|
+
let t = rows.get(key);
|
|
58300
|
+
if (!t) {
|
|
58301
|
+
const now = nowMs();
|
|
58302
|
+
t = {
|
|
58303
|
+
key,
|
|
58304
|
+
provider: handle.provider,
|
|
58305
|
+
remoteId: handle.remoteId,
|
|
58306
|
+
handle: stripSecrets(handle),
|
|
58307
|
+
createdAt: now,
|
|
58308
|
+
attempts: 0,
|
|
58309
|
+
nextAttemptAt: now
|
|
58310
|
+
};
|
|
58311
|
+
rows.set(key, t);
|
|
58312
|
+
try {
|
|
58313
|
+
await persist();
|
|
58314
|
+
} catch (err) {
|
|
58315
|
+
rows.delete(key);
|
|
58316
|
+
throw err;
|
|
58317
|
+
}
|
|
58318
|
+
}
|
|
58319
|
+
afterRecorded?.();
|
|
58320
|
+
const converged = await attempt(t, true);
|
|
58321
|
+
const remaining = rows.get(key);
|
|
58322
|
+
return { converged, ...remaining ? { tombstone: remaining } : {} };
|
|
58323
|
+
},
|
|
58324
|
+
async sweep() {
|
|
58325
|
+
const now = nowMs();
|
|
58326
|
+
const due = [...rows.values()].filter((t) => t.nextAttemptAt <= now);
|
|
58327
|
+
let converged = 0;
|
|
58328
|
+
for (const t of due) if (await attempt(t)) converged++;
|
|
58329
|
+
return { attempted: due.length, converged, remaining: rows.size };
|
|
58330
|
+
},
|
|
58331
|
+
list: () => [...rows.values()],
|
|
58332
|
+
has: (provider, remoteId) => rows.has(keyOf2(provider, remoteId))
|
|
58333
|
+
};
|
|
58334
|
+
}
|
|
58335
|
+
function defaultSentinelQuarantinePath() {
|
|
58336
|
+
return resolve(process.env.AGENTPROTO_HOME ?? join(homedir(), ".agentproto"), "sentinel-quarantine.jsonl");
|
|
58337
|
+
}
|
|
58338
|
+
function keyFor(sentinelId, provider, item) {
|
|
58339
|
+
const identity = item.remoteDeliveryId ?? (item.seq !== void 0 ? `seq:${item.seq}` : "");
|
|
58340
|
+
return [sentinelId, provider, identity, item.digest ?? ""].join("|");
|
|
58341
|
+
}
|
|
58342
|
+
function loadFile(filePath) {
|
|
58343
|
+
if (!existsSync(filePath)) return [];
|
|
58344
|
+
const out = [];
|
|
58345
|
+
for (const line of readFileSync(filePath, "utf8").split("\n")) {
|
|
58346
|
+
if (line.trim() === "") continue;
|
|
58347
|
+
try {
|
|
58348
|
+
const rec = JSON.parse(line);
|
|
58349
|
+
if (rec && typeof rec.key === "string") out.push(rec);
|
|
58350
|
+
} catch {
|
|
58351
|
+
}
|
|
58352
|
+
}
|
|
58353
|
+
return out;
|
|
58354
|
+
}
|
|
58355
|
+
function createSentinelQuarantine(opts = {}) {
|
|
58356
|
+
const nowMs = opts.nowMs ?? Date.now;
|
|
58357
|
+
const records = /* @__PURE__ */ new Map();
|
|
58358
|
+
let loaded = false;
|
|
58359
|
+
function ensureLoaded() {
|
|
58360
|
+
if (loaded) return;
|
|
58361
|
+
if (opts.filePath) for (const rec of loadFile(opts.filePath)) records.set(rec.key, rec);
|
|
58362
|
+
loaded = true;
|
|
58363
|
+
}
|
|
58364
|
+
return {
|
|
58365
|
+
record({ sentinelId, provider, remoteId, item }) {
|
|
58366
|
+
ensureLoaded();
|
|
58367
|
+
const key = keyFor(sentinelId, provider, item);
|
|
58368
|
+
const existing = records.get(key);
|
|
58369
|
+
if (existing) return existing;
|
|
58370
|
+
const rec = {
|
|
58371
|
+
key,
|
|
58372
|
+
quarantinedAt: new Date(nowMs()).toISOString(),
|
|
58373
|
+
sentinelId,
|
|
58374
|
+
provider,
|
|
58375
|
+
...remoteId !== void 0 ? { remoteId } : {},
|
|
58376
|
+
...item.seq !== void 0 ? { seq: item.seq } : {},
|
|
58377
|
+
...item.remoteDeliveryId !== void 0 ? { remoteDeliveryId: item.remoteDeliveryId } : {},
|
|
58378
|
+
error: item.error,
|
|
58379
|
+
...item.excerpt ? { excerpt: item.excerpt } : {},
|
|
58380
|
+
...item.digest !== void 0 ? { digest: item.digest } : {},
|
|
58381
|
+
...item.bytes !== void 0 ? { bytes: item.bytes } : {}
|
|
58382
|
+
};
|
|
58383
|
+
if (opts.filePath) appendLineDurableSync(opts.filePath, JSON.stringify(rec));
|
|
58384
|
+
records.set(key, rec);
|
|
58385
|
+
return rec;
|
|
58386
|
+
},
|
|
58387
|
+
list(filter) {
|
|
58388
|
+
ensureLoaded();
|
|
58389
|
+
const all = [...records.values()];
|
|
58390
|
+
return filter?.sentinelId ? all.filter((r) => r.sentinelId === filter.sentinelId) : all;
|
|
58391
|
+
}
|
|
58392
|
+
};
|
|
58393
|
+
}
|
|
57168
58394
|
|
|
57169
58395
|
// src/webhook-egress/delivery.ts
|
|
57170
58396
|
init_signing();
|
|
@@ -57352,7 +58578,7 @@ function finalHeaders(headers, hostname6, port) {
|
|
|
57352
58578
|
return { ...headers, Host: port === 443 ? hostname6 : `${hostname6}:${port}` };
|
|
57353
58579
|
}
|
|
57354
58580
|
function rawPost(args) {
|
|
57355
|
-
return new Promise((
|
|
58581
|
+
return new Promise((resolve47, reject) => {
|
|
57356
58582
|
const req = request({
|
|
57357
58583
|
// Connect to the PRE-VALIDATED address; keep the original hostname in
|
|
57358
58584
|
// `servername` so SNI and the certificate check never loosen.
|
|
@@ -57376,7 +58602,7 @@ function rawPost(args) {
|
|
|
57376
58602
|
const ok4 = (status, body) => {
|
|
57377
58603
|
if (settled) return;
|
|
57378
58604
|
settled = true;
|
|
57379
|
-
|
|
58605
|
+
resolve47({ status, body });
|
|
57380
58606
|
};
|
|
57381
58607
|
req.setTimeout(args.timeoutMs, () => fail4("timeout", `no response within ${args.timeoutMs}ms`));
|
|
57382
58608
|
req.on("error", (err) => {
|
|
@@ -57411,6 +58637,23 @@ function rawPost(args) {
|
|
|
57411
58637
|
}
|
|
57412
58638
|
|
|
57413
58639
|
// src/webhook-egress/delivery.ts
|
|
58640
|
+
function callbackHost(url) {
|
|
58641
|
+
try {
|
|
58642
|
+
return new URL(url).host;
|
|
58643
|
+
} catch {
|
|
58644
|
+
return "invalid-url";
|
|
58645
|
+
}
|
|
58646
|
+
}
|
|
58647
|
+
function redactUrls(text13) {
|
|
58648
|
+
return text13.replace(/https?:\/\/[^\s'"<>)]+/gi, (m) => {
|
|
58649
|
+
try {
|
|
58650
|
+
const u = new URL(m);
|
|
58651
|
+
return `${u.protocol}//${u.host}`;
|
|
58652
|
+
} catch {
|
|
58653
|
+
return "<url>";
|
|
58654
|
+
}
|
|
58655
|
+
});
|
|
58656
|
+
}
|
|
57414
58657
|
var CLAMP_LIMIT_BYTES = 262144;
|
|
57415
58658
|
var MAX_ATTEMPTS = 5;
|
|
57416
58659
|
var BACKOFF_BASE_MS = 250;
|
|
@@ -57442,6 +58685,12 @@ async function deliverEventEnvelope(replay, event, deps2 = {}) {
|
|
|
57442
58685
|
const sleep3 = deps2.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
57443
58686
|
const fetcher = deps2.fetch ?? ((url, init) => ssrfFetch(url, init));
|
|
57444
58687
|
const { bytes: payloadBytes } = envelopeClampedBytes(event);
|
|
58688
|
+
const note = (info) => {
|
|
58689
|
+
try {
|
|
58690
|
+
deps2.onAttempt?.(info);
|
|
58691
|
+
} catch {
|
|
58692
|
+
}
|
|
58693
|
+
};
|
|
57445
58694
|
const delivery = { attempts: 0 };
|
|
57446
58695
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
57447
58696
|
if (attempt > 0) await sleep3(deliveryBackoffDelay(attempt));
|
|
@@ -57453,30 +58702,40 @@ async function deliverEventEnvelope(replay, event, deps2 = {}) {
|
|
|
57453
58702
|
signed = signWebhook({ msgId: event.eventId, timestamp, payload: payloadBytes, secrets: replay.secrets });
|
|
57454
58703
|
} catch (err) {
|
|
57455
58704
|
delivery.lastError = `signing failed: ${err.message}`;
|
|
58705
|
+
note({ attempt: delivery.attempts, error: "invalid_secret", outcome: "dead" });
|
|
57456
58706
|
return { ok: false, reason: "invalid_secret", delivery };
|
|
57457
58707
|
}
|
|
57458
58708
|
let response;
|
|
57459
58709
|
try {
|
|
57460
58710
|
response = await fetcher(replay.callbackUrl, {
|
|
57461
58711
|
method: "POST",
|
|
57462
|
-
headers: { ...signed, "content-type": "application/json" },
|
|
58712
|
+
headers: { ...signed, "content-type": "application/json", "X-MCP-Subscription-Id": replay.subId },
|
|
57463
58713
|
body: payloadBytes,
|
|
57464
58714
|
timeoutMs: 15e3
|
|
57465
58715
|
// delivery default (challenge uses 10_000)
|
|
57466
58716
|
});
|
|
57467
58717
|
} catch (err) {
|
|
57468
|
-
delivery.lastError = err instanceof Error ? err.message : String(err);
|
|
57469
|
-
|
|
58718
|
+
delivery.lastError = redactUrls(err instanceof Error ? err.message : String(err));
|
|
58719
|
+
const last = attempt >= MAX_ATTEMPTS - 1;
|
|
58720
|
+
note({ attempt: delivery.attempts, error: delivery.lastError, outcome: last ? "dead" : "retry" });
|
|
58721
|
+
if (!last) continue;
|
|
57470
58722
|
return { ok: false, reason: "network", delivery };
|
|
57471
58723
|
}
|
|
57472
58724
|
if (response.status >= 200 && response.status <= 299) {
|
|
58725
|
+
note({ attempt: delivery.attempts, status: response.status, outcome: "delivered" });
|
|
57473
58726
|
return { ok: true, delivery };
|
|
57474
58727
|
}
|
|
57475
58728
|
if (TERMINAL_NO_RETRY_STATUSES.has(response.status)) {
|
|
57476
58729
|
delivery.lastError = `terminal status ${response.status}`;
|
|
58730
|
+
note({ attempt: delivery.attempts, status: response.status, outcome: "dead" });
|
|
57477
58731
|
return { ok: false, reason: `http_${response.status}`, delivery };
|
|
57478
58732
|
}
|
|
57479
58733
|
delivery.lastError = `retryable status ${response.status}`;
|
|
58734
|
+
note({
|
|
58735
|
+
attempt: delivery.attempts,
|
|
58736
|
+
status: response.status,
|
|
58737
|
+
outcome: attempt >= MAX_ATTEMPTS - 1 ? "dead" : "retry"
|
|
58738
|
+
});
|
|
57480
58739
|
}
|
|
57481
58740
|
return { ok: false, reason: "retries_exhausted", delivery };
|
|
57482
58741
|
}
|
|
@@ -57891,13 +59150,16 @@ function invalidParams(reason, detail) {
|
|
|
57891
59150
|
function callbackEndpointError(reason, detail) {
|
|
57892
59151
|
return new McpEventsError(-32015, `CallbackEndpointError: ${detail}`, { reason });
|
|
57893
59152
|
}
|
|
59153
|
+
function backingSubscriptionError(reason, detail, subscriptionId2) {
|
|
59154
|
+
return new McpEventsError(-32016, `BackingSubscriptionError: ${detail}`, { reason, subscriptionId: subscriptionId2 });
|
|
59155
|
+
}
|
|
57894
59156
|
var mutationChain = Promise.resolve();
|
|
57895
59157
|
function withSubscribeMutex(fn) {
|
|
57896
59158
|
const run = mutationChain.then(fn, fn);
|
|
57897
59159
|
mutationChain = run.catch(() => void 0);
|
|
57898
59160
|
return run;
|
|
57899
59161
|
}
|
|
57900
|
-
function
|
|
59162
|
+
function isRecord12(value) {
|
|
57901
59163
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57902
59164
|
}
|
|
57903
59165
|
var LIST_PAGE_DEFAULT = 50;
|
|
@@ -57918,7 +59180,7 @@ function decodeListCursor(cursor) {
|
|
|
57918
59180
|
}
|
|
57919
59181
|
}
|
|
57920
59182
|
function requireWebhookDelivery(delivery) {
|
|
57921
|
-
if (!
|
|
59183
|
+
if (!isRecord12(delivery) || delivery.mode !== "webhook") {
|
|
57922
59184
|
throw invalidParams("unsupported_delivery_mode", 'delivery.mode must be "webhook"');
|
|
57923
59185
|
}
|
|
57924
59186
|
if (typeof delivery.url !== "string" || delivery.url.length === 0) {
|
|
@@ -57927,7 +59189,7 @@ function requireWebhookDelivery(delivery) {
|
|
|
57927
59189
|
return { mode: "webhook", url: delivery.url, ...typeof delivery.secret === "string" ? { secret: delivery.secret } : {} };
|
|
57928
59190
|
}
|
|
57929
59191
|
function validateArgsOrThrow(name, registration, args) {
|
|
57930
|
-
if (!
|
|
59192
|
+
if (!isRecord12(args)) throw invalidParams("invalid_arguments", "`arguments` must be an object");
|
|
57931
59193
|
const schemaCheck = validateAgainstInputSchema(registration.definition.inputSchema, args);
|
|
57932
59194
|
if (!schemaCheck.ok) {
|
|
57933
59195
|
throw invalidParams("invalid_arguments", `arguments for "${name}": ${schemaCheck.reason}`);
|
|
@@ -57983,10 +59245,30 @@ async function eventsSubscribe(input, ctx) {
|
|
|
57983
59245
|
if (target.kind !== "webhook") {
|
|
57984
59246
|
throw invalidParams("identity_conflict", `subscription ${id} exists with a non-webhook target`);
|
|
57985
59247
|
}
|
|
59248
|
+
let handle2 = existing.handle;
|
|
59249
|
+
const provider2 = await ctx.resolveProvider(existing.provider);
|
|
59250
|
+
if (provider2?.renew) {
|
|
59251
|
+
try {
|
|
59252
|
+
handle2 = await provider2.renew(existing.handle, until);
|
|
59253
|
+
} catch (err) {
|
|
59254
|
+
if (err instanceof SentinelBackingExpiredError) {
|
|
59255
|
+
throw backingSubscriptionError(
|
|
59256
|
+
"backing_subscription_expired",
|
|
59257
|
+
`${err.message}; unsubscribe and subscribe again`,
|
|
59258
|
+
id
|
|
59259
|
+
);
|
|
59260
|
+
}
|
|
59261
|
+
throw backingSubscriptionError(
|
|
59262
|
+
"backing_renew_failed",
|
|
59263
|
+
err instanceof Error ? err.message : String(err),
|
|
59264
|
+
id
|
|
59265
|
+
);
|
|
59266
|
+
}
|
|
59267
|
+
}
|
|
57986
59268
|
const ref = ctx.store.materializeWebhookTargetRef(target);
|
|
57987
59269
|
if (ref !== void 0) ctx.store.putSentinelSecret(ref, { secret });
|
|
57988
|
-
ctx.store.update(id, { spec: { ...existing.spec, until }, status: "active" });
|
|
57989
|
-
return { id, refreshBefore: computeRefreshBefore(until), cursor: null };
|
|
59270
|
+
ctx.store.update(id, { spec: { ...existing.spec, until }, handle: handle2, status: "active" });
|
|
59271
|
+
return { id, refreshBefore: computeRefreshBefore(until), cursor: null, truncated: false };
|
|
57990
59272
|
}
|
|
57991
59273
|
const provider = await ctx.resolveProvider(mapped.providerSlug);
|
|
57992
59274
|
if (!provider) {
|
|
@@ -58002,7 +59284,7 @@ async function eventsSubscribe(input, ctx) {
|
|
|
58002
59284
|
sentinelId: id
|
|
58003
59285
|
});
|
|
58004
59286
|
ctx.store.create({ id, spec, provider: mapped.providerSlug, handle });
|
|
58005
|
-
return { id, refreshBefore: computeRefreshBefore(until), cursor: null };
|
|
59287
|
+
return { id, refreshBefore: computeRefreshBefore(until), cursor: null, truncated: false };
|
|
58006
59288
|
});
|
|
58007
59289
|
}
|
|
58008
59290
|
async function eventsUnsubscribe(input, ctx) {
|
|
@@ -58022,6 +59304,13 @@ async function eventsUnsubscribe(input, ctx) {
|
|
|
58022
59304
|
await withSubscribeMutex(async () => {
|
|
58023
59305
|
const existing = ctx.store.get(id);
|
|
58024
59306
|
if (!existing) return;
|
|
59307
|
+
if (ctx.tombstones) {
|
|
59308
|
+
try {
|
|
59309
|
+
await ctx.tombstones.cancel(existing.handle, () => ctx.store.remove(id));
|
|
59310
|
+
return;
|
|
59311
|
+
} catch {
|
|
59312
|
+
}
|
|
59313
|
+
}
|
|
58025
59314
|
const provider = await ctx.resolveProvider(existing.provider);
|
|
58026
59315
|
if (provider) {
|
|
58027
59316
|
try {
|
|
@@ -58051,7 +59340,6 @@ var PERSIST_DEBOUNCE_MS8 = 1500;
|
|
|
58051
59340
|
var DELIVERED_REAP_MS = 24 * 60 * 60 * 1e3;
|
|
58052
59341
|
var PENDING_AGE_OUT_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
58053
59342
|
var ROW_CAP = 2e3;
|
|
58054
|
-
var tmpSeq6 = 0;
|
|
58055
59343
|
function webhookRequestId(sentinelId, eventId) {
|
|
58056
59344
|
const digest = createHash("sha256").update(`${sentinelId}:${eventId}`).digest("hex");
|
|
58057
59345
|
return `sub_${digest.slice(0, 32)}`;
|
|
@@ -58065,9 +59353,22 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58065
59353
|
const debounceMs = opts.debounceMs ?? PERSIST_DEBOUNCE_MS8;
|
|
58066
59354
|
const log = opts.log ?? ((line) => console.warn(line));
|
|
58067
59355
|
const persist = opts.persist ?? opts.filePath !== void 0;
|
|
58068
|
-
const
|
|
59356
|
+
const deliveryLog = (row, host, detail) => {
|
|
59357
|
+
log(`[sentinel-webhook-outbox] sentinel=${row.sentinelId} event=${row.eventId} host=${host} ${detail}`);
|
|
59358
|
+
};
|
|
59359
|
+
const deliverEvent = opts.deliverEvent ?? (async (input) => {
|
|
59360
|
+
const host = callbackHost(input.replay.callbackUrl);
|
|
59361
|
+
return deliverEventEnvelope(input.replay, input.row.envelope, {
|
|
59362
|
+
...opts.deliverDeps,
|
|
59363
|
+
onAttempt: (a) => deliveryLog(
|
|
59364
|
+
input.row,
|
|
59365
|
+
host,
|
|
59366
|
+
`attempt=${a.attempt} ${a.status !== void 0 ? `http=${a.status}` : `error="${a.error ?? "unknown"}"`} ${a.outcome}`
|
|
59367
|
+
)
|
|
59368
|
+
});
|
|
59369
|
+
});
|
|
58069
59370
|
const rowKey = (sentinelId, eventId) => `${sentinelId}::${eventId}`;
|
|
58070
|
-
const
|
|
59371
|
+
const load2 = () => {
|
|
58071
59372
|
const out = /* @__PURE__ */ new Map();
|
|
58072
59373
|
if (!persist) return out;
|
|
58073
59374
|
let raw;
|
|
@@ -58091,28 +59392,34 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58091
59392
|
}
|
|
58092
59393
|
return out;
|
|
58093
59394
|
};
|
|
58094
|
-
const rows =
|
|
59395
|
+
const rows = load2();
|
|
58095
59396
|
const snapshot = () => {
|
|
58096
59397
|
const out = {};
|
|
58097
59398
|
for (const [key, row] of rows.entries()) out[key] = row;
|
|
58098
59399
|
return out;
|
|
58099
59400
|
};
|
|
58100
59401
|
let persistTimer = null;
|
|
59402
|
+
let writeTail = Promise.resolve();
|
|
59403
|
+
let syncGeneration = 0;
|
|
59404
|
+
const persistNow = () => {
|
|
59405
|
+
if (!persist) return Promise.resolve();
|
|
59406
|
+
const run = async () => {
|
|
59407
|
+
const generation = syncGeneration;
|
|
59408
|
+
const body = JSON.stringify(snapshot(), null, 2) + "\n";
|
|
59409
|
+
await writeFileDurable(filePath, body, { commitIf: () => generation === syncGeneration });
|
|
59410
|
+
};
|
|
59411
|
+
const next = writeTail.then(run, run);
|
|
59412
|
+
writeTail = next.catch(() => void 0);
|
|
59413
|
+
return next;
|
|
59414
|
+
};
|
|
58101
59415
|
const schedulePersist = () => {
|
|
58102
59416
|
if (!persist) return;
|
|
58103
59417
|
if (persistTimer) clearTimeout(persistTimer);
|
|
58104
59418
|
persistTimer = setTimeout(() => {
|
|
58105
|
-
|
|
58106
|
-
|
|
58107
|
-
|
|
58108
|
-
|
|
58109
|
-
await promises.writeFile(tmp, JSON.stringify(snapshot(), null, 2) + "\n", { mode: 384 });
|
|
58110
|
-
await promises.chmod(tmp, 384);
|
|
58111
|
-
await promises.rename(tmp, filePath);
|
|
58112
|
-
} catch (err) {
|
|
58113
|
-
log(`[sentinel-webhook-outbox] persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
58114
|
-
}
|
|
58115
|
-
})();
|
|
59419
|
+
persistTimer = null;
|
|
59420
|
+
persistNow().catch((err) => {
|
|
59421
|
+
log(`[sentinel-webhook-outbox] persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
59422
|
+
});
|
|
58116
59423
|
}, debounceMs);
|
|
58117
59424
|
};
|
|
58118
59425
|
const flushSync = () => {
|
|
@@ -58121,12 +59428,9 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58121
59428
|
clearTimeout(persistTimer);
|
|
58122
59429
|
persistTimer = null;
|
|
58123
59430
|
}
|
|
59431
|
+
syncGeneration++;
|
|
58124
59432
|
try {
|
|
58125
|
-
|
|
58126
|
-
const tmp = `${filePath}.tmp.${process.pid}.${++tmpSeq6}`;
|
|
58127
|
-
writeFileSync(tmp, JSON.stringify(snapshot(), null, 2) + "\n", { mode: 384 });
|
|
58128
|
-
chmodSync(tmp, 384);
|
|
58129
|
-
renameSync(tmp, filePath);
|
|
59433
|
+
writeFileDurableSync(filePath, JSON.stringify(snapshot(), null, 2) + "\n");
|
|
58130
59434
|
} catch {
|
|
58131
59435
|
}
|
|
58132
59436
|
};
|
|
@@ -58173,14 +59477,19 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58173
59477
|
row.failReason = outcome.reason;
|
|
58174
59478
|
}
|
|
58175
59479
|
row.terminalAt = nowMs();
|
|
59480
|
+
deliveryLog(
|
|
59481
|
+
row,
|
|
59482
|
+
callbackHost(replay.callbackUrl),
|
|
59483
|
+
`final=${row.status} attempts=${outcome.delivery.attempts}` + (outcome.ok ? "" : ` reason=${outcome.reason}`)
|
|
59484
|
+
);
|
|
58176
59485
|
} catch (err) {
|
|
58177
59486
|
row.deliveryState = {
|
|
58178
59487
|
...row.deliveryState ?? { attempts: 0 },
|
|
58179
|
-
lastError: err instanceof Error ? err.message : String(err),
|
|
59488
|
+
lastError: redactUrls(err instanceof Error ? err.message : String(err)),
|
|
58180
59489
|
lastAt: new Date(nowMs()).toISOString()
|
|
58181
59490
|
};
|
|
58182
59491
|
row.status = "pending";
|
|
58183
|
-
|
|
59492
|
+
deliveryLog(row, callbackHost(replay.callbackUrl), "dispatch failed, row left pending");
|
|
58184
59493
|
}
|
|
58185
59494
|
schedulePersist();
|
|
58186
59495
|
if (row.status === "delivered" || row.status === "dead") {
|
|
@@ -58203,7 +59512,14 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58203
59512
|
const key = rowKey(sentinelId, event.id);
|
|
58204
59513
|
const existing = rows.get(key);
|
|
58205
59514
|
if (existing) {
|
|
58206
|
-
if (existing.status !== "pending")
|
|
59515
|
+
if (existing.status !== "pending") {
|
|
59516
|
+
opts.onTerminal(sentinelId, existing);
|
|
59517
|
+
return void 0;
|
|
59518
|
+
}
|
|
59519
|
+
await writeTail;
|
|
59520
|
+
if (rows.get(key) !== existing) {
|
|
59521
|
+
throw new Error(`webhook outbox row ${key} was not persisted`);
|
|
59522
|
+
}
|
|
58207
59523
|
return void 0;
|
|
58208
59524
|
}
|
|
58209
59525
|
const envelope = toWebhookEnvelope(event);
|
|
@@ -58219,12 +59535,23 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58219
59535
|
envelope,
|
|
58220
59536
|
event
|
|
58221
59537
|
};
|
|
59538
|
+
let evicted;
|
|
58222
59539
|
if (rows.size >= ROW_CAP) {
|
|
58223
59540
|
const oldest = oldestTerminalRowKey();
|
|
58224
|
-
|
|
59541
|
+
const evictedRow = oldest ? rows.get(oldest) : void 0;
|
|
59542
|
+
if (oldest && evictedRow) {
|
|
59543
|
+
evicted = [oldest, evictedRow];
|
|
59544
|
+
rows.delete(oldest);
|
|
59545
|
+
}
|
|
58225
59546
|
}
|
|
58226
59547
|
rows.set(key, row);
|
|
58227
|
-
|
|
59548
|
+
try {
|
|
59549
|
+
await persistNow();
|
|
59550
|
+
} catch (err) {
|
|
59551
|
+
if (rows.get(key) === row) rows.delete(key);
|
|
59552
|
+
if (evicted && !rows.has(evicted[0])) rows.set(evicted[0], evicted[1]);
|
|
59553
|
+
throw err;
|
|
59554
|
+
}
|
|
58228
59555
|
enqueueDispatch();
|
|
58229
59556
|
return row;
|
|
58230
59557
|
},
|
|
@@ -58257,6 +59584,27 @@ function createSentinelWebhookOutbox(opts) {
|
|
|
58257
59584
|
rows() {
|
|
58258
59585
|
return Array.from(rows.values());
|
|
58259
59586
|
},
|
|
59587
|
+
deliveryStatus(sentinelId) {
|
|
59588
|
+
let latest;
|
|
59589
|
+
let active = false;
|
|
59590
|
+
let dead = 0;
|
|
59591
|
+
for (const row of rows.values()) {
|
|
59592
|
+
if (row.sentinelId !== sentinelId) continue;
|
|
59593
|
+
if (row.status === "pending") active = true;
|
|
59594
|
+
if (row.status === "dead") dead++;
|
|
59595
|
+
if (!latest || (row.terminalAt ?? row.createdAt) >= (latest.terminalAt ?? latest.createdAt)) latest = row;
|
|
59596
|
+
}
|
|
59597
|
+
if (!latest) return void 0;
|
|
59598
|
+
const lastError = latest.deliveryState.lastError ?? latest.failReason ?? latest.deadReason;
|
|
59599
|
+
return {
|
|
59600
|
+
active,
|
|
59601
|
+
...latest.deliveryState.lastAt ? { lastDeliveryAt: latest.deliveryState.lastAt } : {},
|
|
59602
|
+
lastStatus: latest.status,
|
|
59603
|
+
...lastError && latest.status !== "delivered" ? { lastError: redactUrls(lastError) } : {},
|
|
59604
|
+
attempts: latest.deliveryState.attempts,
|
|
59605
|
+
dead
|
|
59606
|
+
};
|
|
59607
|
+
},
|
|
58260
59608
|
flushSync
|
|
58261
59609
|
};
|
|
58262
59610
|
}
|
|
@@ -58354,14 +59702,27 @@ function createSentinelRuntime(opts) {
|
|
|
58354
59702
|
if (store.get(sentinel.id)?.status !== "expired") {
|
|
58355
59703
|
store.update(sentinel.id, { status: "expired" });
|
|
58356
59704
|
}
|
|
58357
|
-
const provider = await opts.resolveProvider(sentinel.provider);
|
|
58358
|
-
if (!provider) return;
|
|
58359
59705
|
try {
|
|
58360
|
-
await
|
|
59706
|
+
await cancelTombstones.cancel(sentinel.handle);
|
|
58361
59707
|
} catch (err) {
|
|
58362
59708
|
log(`[sentinel-runtime] expiry cancel failed for ${sentinel.id}: ${describeError(err)}`);
|
|
58363
59709
|
}
|
|
58364
59710
|
}
|
|
59711
|
+
const outboxPersistent = opts.outboxPersist ?? (opts.outboxPath !== void 0 || opts.deliverEvent === void 0);
|
|
59712
|
+
const quarantinePath = opts.quarantinePath ?? (outboxPersistent ? defaultSentinelQuarantinePath() : void 0);
|
|
59713
|
+
const quarantine = createSentinelQuarantine({
|
|
59714
|
+
...quarantinePath !== void 0 ? { filePath: quarantinePath } : {},
|
|
59715
|
+
nowMs
|
|
59716
|
+
});
|
|
59717
|
+
const cancelTombstonePath = opts.cancelTombstonePath ?? (outboxPersistent ? defaultCancelTombstonePath() : void 0);
|
|
59718
|
+
const cancelTombstones = createCancelTombstoneStore({
|
|
59719
|
+
...cancelTombstonePath !== void 0 ? { filePath: cancelTombstonePath } : {},
|
|
59720
|
+
resolveProvider: opts.resolveProvider,
|
|
59721
|
+
// A live local watch that owns the remote makes the tombstone stale.
|
|
59722
|
+
isRemoteInUse: (provider, remoteId) => store.list().some((s) => s.provider === provider && s.handle.remoteId === remoteId && s.status !== "expired"),
|
|
59723
|
+
nowMs,
|
|
59724
|
+
log
|
|
59725
|
+
});
|
|
58365
59726
|
const outbox = createSentinelWebhookOutbox({
|
|
58366
59727
|
...opts.outboxPath !== void 0 ? { filePath: opts.outboxPath } : {},
|
|
58367
59728
|
nowMs,
|
|
@@ -58383,7 +59744,7 @@ function createSentinelRuntime(opts) {
|
|
|
58383
59744
|
if (stored.prevSecret && stored.rotatedAt !== void 0 && nowMs() - stored.rotatedAt < windowMs) {
|
|
58384
59745
|
secretsOut.push(stored.prevSecret);
|
|
58385
59746
|
}
|
|
58386
|
-
const subId =
|
|
59747
|
+
const subId = sentinelId;
|
|
58387
59748
|
return { subId, callbackUrl: target.url, secrets: secretsOut };
|
|
58388
59749
|
},
|
|
58389
59750
|
/** Expiry gate consulted BEFORE every dispatch (plan §4 W-B task 2b). */
|
|
@@ -58578,13 +59939,21 @@ function createSentinelRuntime(opts) {
|
|
|
58578
59939
|
}
|
|
58579
59940
|
async function processEvents(sentinelId, provider, events) {
|
|
58580
59941
|
let haltedOnError = false;
|
|
59942
|
+
let stoppedEarly = false;
|
|
58581
59943
|
let delivered = 0;
|
|
58582
59944
|
for (const event of events) {
|
|
58583
59945
|
const current = store.get(sentinelId);
|
|
58584
|
-
if (!current)
|
|
58585
|
-
|
|
59946
|
+
if (!current) {
|
|
59947
|
+
stoppedEarly = true;
|
|
59948
|
+
break;
|
|
59949
|
+
}
|
|
59950
|
+
if (!POLLABLE_STATUSES.has(current.status)) {
|
|
59951
|
+
stoppedEarly = true;
|
|
59952
|
+
break;
|
|
59953
|
+
}
|
|
58586
59954
|
if (isExpiredSpec(current)) {
|
|
58587
59955
|
await expireSentinel(current);
|
|
59956
|
+
stoppedEarly = true;
|
|
58588
59957
|
break;
|
|
58589
59958
|
}
|
|
58590
59959
|
if (store.isSeen(current.id, event.id)) continue;
|
|
@@ -58623,7 +59992,7 @@ function createSentinelRuntime(opts) {
|
|
|
58623
59992
|
});
|
|
58624
59993
|
if (updated) await applyLifetime(updated, event, provider);
|
|
58625
59994
|
}
|
|
58626
|
-
return { haltedOnError, delivered };
|
|
59995
|
+
return { haltedOnError, delivered, stoppedEarly };
|
|
58627
59996
|
}
|
|
58628
59997
|
async function pollSentinel(sentinel) {
|
|
58629
59998
|
const provider = await opts.resolveProvider(sentinel.provider);
|
|
@@ -58635,19 +60004,35 @@ function createSentinelRuntime(opts) {
|
|
|
58635
60004
|
store.update(sentinel.id, { lastError: describeError(err) });
|
|
58636
60005
|
return;
|
|
58637
60006
|
}
|
|
58638
|
-
|
|
58639
|
-
|
|
58640
|
-
|
|
58641
|
-
|
|
58642
|
-
|
|
58643
|
-
|
|
58644
|
-
|
|
58645
|
-
|
|
58646
|
-
|
|
58647
|
-
|
|
60007
|
+
let quarantineFailed = false;
|
|
60008
|
+
for (const item of result.malformed ?? []) {
|
|
60009
|
+
try {
|
|
60010
|
+
quarantine.record({
|
|
60011
|
+
sentinelId: sentinel.id,
|
|
60012
|
+
provider: sentinel.provider,
|
|
60013
|
+
...sentinel.handle.remoteId !== void 0 ? { remoteId: sentinel.handle.remoteId } : {},
|
|
60014
|
+
item
|
|
60015
|
+
});
|
|
60016
|
+
} catch (err) {
|
|
60017
|
+
quarantineFailed = true;
|
|
60018
|
+
log(`[sentinel-runtime] quarantine write failed for ${sentinel.id}: ${describeError(err)}`);
|
|
60019
|
+
}
|
|
60020
|
+
}
|
|
60021
|
+
const { haltedOnError, stoppedEarly } = await processEvents(sentinel.id, provider, result.events);
|
|
60022
|
+
if (haltedOnError || stoppedEarly || quarantineFailed) {
|
|
60023
|
+
if (quarantineFailed) store.update(sentinel.id, { lastError: "quarantine write failed; batch not acknowledged" });
|
|
60024
|
+
return;
|
|
60025
|
+
}
|
|
60026
|
+
const latest = store.get(sentinel.id);
|
|
60027
|
+
if (latest) {
|
|
60028
|
+
if (provider.ack) {
|
|
60029
|
+
try {
|
|
60030
|
+
await provider.ack(latest.handle, result.cursor);
|
|
60031
|
+
} catch (err) {
|
|
60032
|
+
log(`[sentinel-runtime] ack failed for ${sentinel.id}: ${describeError(err)}`);
|
|
58648
60033
|
}
|
|
58649
|
-
store.update(sentinel.id, { handle: { ...latest.handle, cursor: result.cursor } });
|
|
58650
60034
|
}
|
|
60035
|
+
store.update(sentinel.id, { handle: { ...latest.handle, cursor: result.cursor } });
|
|
58651
60036
|
}
|
|
58652
60037
|
}
|
|
58653
60038
|
async function pollOnce() {
|
|
@@ -58655,6 +60040,7 @@ function createSentinelRuntime(opts) {
|
|
|
58655
60040
|
inFlight2 = true;
|
|
58656
60041
|
try {
|
|
58657
60042
|
await sweepExpiredSentinels();
|
|
60043
|
+
await cancelTombstones.sweep().catch((err) => log(`[sentinel-runtime] tombstone sweep failed: ${describeError(err)}`));
|
|
58658
60044
|
outbox.sweep();
|
|
58659
60045
|
const pollable = store.list().filter((s) => POLLABLE_STATUSES.has(s.status));
|
|
58660
60046
|
for (const sentinel of pollable) {
|
|
@@ -58724,6 +60110,7 @@ function createSentinelRuntime(opts) {
|
|
|
58724
60110
|
async start() {
|
|
58725
60111
|
await reattachAll();
|
|
58726
60112
|
await sweepExpiredSentinels();
|
|
60113
|
+
await cancelTombstones.sweep().catch((err) => log(`[sentinel-runtime] tombstone sweep failed: ${describeError(err)}`));
|
|
58727
60114
|
await outbox.dispatch();
|
|
58728
60115
|
if (!timer) scheduleNext();
|
|
58729
60116
|
},
|
|
@@ -58735,7 +60122,9 @@ function createSentinelRuntime(opts) {
|
|
|
58735
60122
|
},
|
|
58736
60123
|
pollOnce,
|
|
58737
60124
|
deliverPushed,
|
|
58738
|
-
webhookOutbox: outbox
|
|
60125
|
+
webhookOutbox: outbox,
|
|
60126
|
+
quarantine,
|
|
60127
|
+
cancelTombstones
|
|
58739
60128
|
};
|
|
58740
60129
|
}
|
|
58741
60130
|
var BUILTIN_SENTINEL_PROVIDERS = {
|
|
@@ -58812,7 +60201,7 @@ async function spawnCloudflaredUntil(argv, opts) {
|
|
|
58812
60201
|
};
|
|
58813
60202
|
let settled = false;
|
|
58814
60203
|
let forwardedLen = 0;
|
|
58815
|
-
return await new Promise((
|
|
60204
|
+
return await new Promise((resolve47, reject) => {
|
|
58816
60205
|
const poll = setInterval(() => {
|
|
58817
60206
|
const text13 = readAll();
|
|
58818
60207
|
if (opts.onLog) {
|
|
@@ -58830,7 +60219,7 @@ async function spawnCloudflaredUntil(argv, opts) {
|
|
|
58830
60219
|
if (m) {
|
|
58831
60220
|
settled = true;
|
|
58832
60221
|
clearTimeout(timer);
|
|
58833
|
-
|
|
60222
|
+
resolve47({ proc, match: m[0], stopTail: () => clearInterval(poll) });
|
|
58834
60223
|
}
|
|
58835
60224
|
}, POLL_INTERVAL_MS2);
|
|
58836
60225
|
if (poll.unref) poll.unref();
|
|
@@ -58967,15 +60356,15 @@ function quickTunnelProvider() {
|
|
|
58967
60356
|
}
|
|
58968
60357
|
}, 3e3);
|
|
58969
60358
|
timer.unref();
|
|
58970
|
-
await new Promise((
|
|
60359
|
+
await new Promise((resolve47) => {
|
|
58971
60360
|
if (proc.exitCode !== null) {
|
|
58972
60361
|
clearTimeout(timer);
|
|
58973
|
-
|
|
60362
|
+
resolve47();
|
|
58974
60363
|
return;
|
|
58975
60364
|
}
|
|
58976
60365
|
proc.once("exit", () => {
|
|
58977
60366
|
clearTimeout(timer);
|
|
58978
|
-
|
|
60367
|
+
resolve47();
|
|
58979
60368
|
});
|
|
58980
60369
|
});
|
|
58981
60370
|
}
|
|
@@ -59128,15 +60517,15 @@ function namedTunnelProvider(cfg) {
|
|
|
59128
60517
|
}
|
|
59129
60518
|
}, 3e3);
|
|
59130
60519
|
timer.unref();
|
|
59131
|
-
await new Promise((
|
|
60520
|
+
await new Promise((resolve47) => {
|
|
59132
60521
|
if (proc.exitCode !== null) {
|
|
59133
60522
|
clearTimeout(timer);
|
|
59134
|
-
|
|
60523
|
+
resolve47();
|
|
59135
60524
|
return;
|
|
59136
60525
|
}
|
|
59137
60526
|
proc.once("exit", () => {
|
|
59138
60527
|
clearTimeout(timer);
|
|
59139
|
-
|
|
60528
|
+
resolve47();
|
|
59140
60529
|
});
|
|
59141
60530
|
});
|
|
59142
60531
|
}
|
|
@@ -59260,7 +60649,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
59260
60649
|
};
|
|
59261
60650
|
proc.stderr?.on("data", forwardLogs);
|
|
59262
60651
|
proc.stdout?.on("data", forwardLogs);
|
|
59263
|
-
const url = await new Promise((
|
|
60652
|
+
const url = await new Promise((resolve47, reject) => {
|
|
59264
60653
|
let settled = false;
|
|
59265
60654
|
const timer = setTimeout(() => {
|
|
59266
60655
|
if (settled) return;
|
|
@@ -59288,7 +60677,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
59288
60677
|
settled = true;
|
|
59289
60678
|
clearTimeout(timer);
|
|
59290
60679
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
59291
|
-
|
|
60680
|
+
resolve47(pubUrl);
|
|
59292
60681
|
}
|
|
59293
60682
|
} catch {
|
|
59294
60683
|
}
|
|
@@ -59303,7 +60692,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
59303
60692
|
settled = true;
|
|
59304
60693
|
clearTimeout(timer);
|
|
59305
60694
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
59306
|
-
|
|
60695
|
+
resolve47(reMatch[1]);
|
|
59307
60696
|
return;
|
|
59308
60697
|
}
|
|
59309
60698
|
for (const line of text13.split(/\r?\n/)) {
|
|
@@ -59314,7 +60703,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
59314
60703
|
settled = true;
|
|
59315
60704
|
clearTimeout(timer);
|
|
59316
60705
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
59317
|
-
|
|
60706
|
+
resolve47(parsed.url);
|
|
59318
60707
|
return;
|
|
59319
60708
|
}
|
|
59320
60709
|
} catch {
|
|
@@ -59361,15 +60750,15 @@ function ngrokTunnelProvider(opts) {
|
|
|
59361
60750
|
}
|
|
59362
60751
|
}, 3e3);
|
|
59363
60752
|
timer.unref();
|
|
59364
|
-
await new Promise((
|
|
60753
|
+
await new Promise((resolve47) => {
|
|
59365
60754
|
if (proc.exitCode !== null) {
|
|
59366
60755
|
clearTimeout(timer);
|
|
59367
|
-
|
|
60756
|
+
resolve47();
|
|
59368
60757
|
return;
|
|
59369
60758
|
}
|
|
59370
60759
|
proc.once("exit", () => {
|
|
59371
60760
|
clearTimeout(timer);
|
|
59372
|
-
|
|
60761
|
+
resolve47();
|
|
59373
60762
|
});
|
|
59374
60763
|
});
|
|
59375
60764
|
}
|
|
@@ -59581,7 +60970,7 @@ function sameCollapseGroup(a, b) {
|
|
|
59581
60970
|
if ((a.key ?? "") !== (b.key ?? "")) return false;
|
|
59582
60971
|
return a.event === b.event || TURN_FAMILY.has(a.event) && TURN_FAMILY.has(b.event);
|
|
59583
60972
|
}
|
|
59584
|
-
function
|
|
60973
|
+
function excerptOf2(text13) {
|
|
59585
60974
|
if (!text13) return void 0;
|
|
59586
60975
|
const flat = text13.replace(/\s+/g, " ").trim();
|
|
59587
60976
|
if (!flat) return void 0;
|
|
@@ -59596,7 +60985,7 @@ function formatFollowDigest(entries, resolveText, desc) {
|
|
|
59596
60985
|
const cwd = d?.cwd ?? entry.cwd;
|
|
59597
60986
|
const parts = [`[session-follow] ${who} ${entry.event}`];
|
|
59598
60987
|
if (cwd) parts.push(basename(cwd) || cwd);
|
|
59599
|
-
const text13 =
|
|
60988
|
+
const text13 = excerptOf2(resolveText(entry));
|
|
59600
60989
|
if (text13) parts.push(text13);
|
|
59601
60990
|
return parts.join(" \u2014 ");
|
|
59602
60991
|
});
|
|
@@ -59872,6 +61261,31 @@ function createSentinelAutoLinker(opts) {
|
|
|
59872
61261
|
linkNow: link
|
|
59873
61262
|
};
|
|
59874
61263
|
}
|
|
61264
|
+
function isSubscriptionPermitted(params, repoAllowlist) {
|
|
61265
|
+
const args = params.arguments;
|
|
61266
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return false;
|
|
61267
|
+
const repo = args.repo;
|
|
61268
|
+
return typeof repo === "string" && repoAllowlist.includes(repo);
|
|
61269
|
+
}
|
|
61270
|
+
function createEventsSurfaceServer(opts) {
|
|
61271
|
+
const server = new McpServer({ name: "agentproto-events", version: opts.version }, { capabilities: { tools: {} } });
|
|
61272
|
+
server.registerTool(
|
|
61273
|
+
"events_ping",
|
|
61274
|
+
{ description: "Liveness probe for the events surface. Returns ok.", inputSchema: {} },
|
|
61275
|
+
async () => ({ content: [{ type: "text", text: "ok" }] })
|
|
61276
|
+
);
|
|
61277
|
+
registerEventsMethods(server, {
|
|
61278
|
+
list: (params) => opts.handlers.list(params),
|
|
61279
|
+
subscribe: async (params) => {
|
|
61280
|
+
if (!isSubscriptionPermitted(params, opts.repoAllowlist)) {
|
|
61281
|
+
throw new McpError(ErrorCode.InvalidParams, "subscription not permitted");
|
|
61282
|
+
}
|
|
61283
|
+
return opts.handlers.subscribe(params);
|
|
61284
|
+
},
|
|
61285
|
+
unsubscribe: (params) => opts.handlers.unsubscribe(params)
|
|
61286
|
+
});
|
|
61287
|
+
return server;
|
|
61288
|
+
}
|
|
59875
61289
|
var TELEGRAM_BOT_CREDS_FAMILY = "telegram-bot";
|
|
59876
61290
|
function makeTelegramBotCredsStore(home) {
|
|
59877
61291
|
const store = makeCredsStore({
|
|
@@ -60350,12 +61764,12 @@ function canonicalJson(value) {
|
|
|
60350
61764
|
}
|
|
60351
61765
|
function isToolAppVisible(tool) {
|
|
60352
61766
|
const meta = tool._meta;
|
|
60353
|
-
const ui =
|
|
61767
|
+
const ui = isRecord13(meta?.ui) ? meta.ui : void 0;
|
|
60354
61768
|
const visibility = ui?.visibility;
|
|
60355
61769
|
if (!Array.isArray(visibility)) return true;
|
|
60356
61770
|
return visibility.includes("app");
|
|
60357
61771
|
}
|
|
60358
|
-
function
|
|
61772
|
+
function isRecord13(v) {
|
|
60359
61773
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
60360
61774
|
}
|
|
60361
61775
|
function buildUiToolIndex(tools) {
|
|
@@ -60880,19 +62294,19 @@ function classifyMcpError(err) {
|
|
|
60880
62294
|
return { status: "unreachable", error };
|
|
60881
62295
|
}
|
|
60882
62296
|
function uiMetaOf(meta) {
|
|
60883
|
-
if (!
|
|
62297
|
+
if (!isRecord14(meta) || !isRecord14(meta.ui)) return {};
|
|
60884
62298
|
const ui = meta.ui;
|
|
60885
62299
|
const csp = cspOf(ui.csp);
|
|
60886
62300
|
return {
|
|
60887
62301
|
...csp ? { csp } : {},
|
|
60888
|
-
...
|
|
62302
|
+
...isRecord14(ui.permissions) ? { permissions: ui.permissions } : {},
|
|
60889
62303
|
...typeof ui.prefersBorder === "boolean" ? { prefersBorder: ui.prefersBorder } : {},
|
|
60890
62304
|
...typeof ui.domain === "string" ? { domain: ui.domain } : {}
|
|
60891
62305
|
};
|
|
60892
62306
|
}
|
|
60893
62307
|
var CSP_DOMAIN_KEYS = ["connectDomains", "resourceDomains", "frameDomains", "baseUriDomains"];
|
|
60894
62308
|
function cspOf(raw) {
|
|
60895
|
-
if (!
|
|
62309
|
+
if (!isRecord14(raw)) return void 0;
|
|
60896
62310
|
const csp = {};
|
|
60897
62311
|
for (const key of CSP_DOMAIN_KEYS) {
|
|
60898
62312
|
const value = raw[key];
|
|
@@ -60902,7 +62316,7 @@ function cspOf(raw) {
|
|
|
60902
62316
|
}
|
|
60903
62317
|
return Object.keys(csp).length > 0 ? csp : void 0;
|
|
60904
62318
|
}
|
|
60905
|
-
function
|
|
62319
|
+
function isRecord14(v) {
|
|
60906
62320
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
60907
62321
|
}
|
|
60908
62322
|
function errorMessage(err) {
|
|
@@ -63139,14 +64553,14 @@ function createActivityProjector(opts) {
|
|
|
63139
64553
|
if (current && isTerminalActivityState(current.state)) {
|
|
63140
64554
|
return Promise.resolve(current);
|
|
63141
64555
|
}
|
|
63142
|
-
return new Promise((
|
|
64556
|
+
return new Promise((resolve47) => {
|
|
63143
64557
|
let settled = false;
|
|
63144
64558
|
const finish = (value) => {
|
|
63145
64559
|
if (settled) return;
|
|
63146
64560
|
settled = true;
|
|
63147
64561
|
clearTimeout(timer);
|
|
63148
64562
|
unsubscribeWait();
|
|
63149
|
-
|
|
64563
|
+
resolve47(value);
|
|
63150
64564
|
};
|
|
63151
64565
|
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
63152
64566
|
const unsubscribeWait = opts.sessionEvents.on("activity:changed", (ev) => {
|
|
@@ -63308,7 +64722,7 @@ function createApprovalsEngine(opts) {
|
|
|
63308
64722
|
const list2 = waiters.get(record2.id);
|
|
63309
64723
|
if (!list2) return;
|
|
63310
64724
|
waiters.delete(record2.id);
|
|
63311
|
-
for (const
|
|
64725
|
+
for (const resolve47 of list2) resolve47(record2);
|
|
63312
64726
|
};
|
|
63313
64727
|
const emit = (type, record2) => {
|
|
63314
64728
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -63378,17 +64792,17 @@ function createApprovalsEngine(opts) {
|
|
|
63378
64792
|
const current = liveGet(id);
|
|
63379
64793
|
if (!current) return Promise.resolve(void 0);
|
|
63380
64794
|
if (current.status !== "pending") return Promise.resolve(current);
|
|
63381
|
-
return new Promise((
|
|
64795
|
+
return new Promise((resolve47) => {
|
|
63382
64796
|
const list2 = waiters.get(id) ?? [];
|
|
63383
|
-
list2.push(
|
|
64797
|
+
list2.push(resolve47);
|
|
63384
64798
|
waiters.set(id, list2);
|
|
63385
64799
|
const timer = setTimeout(() => {
|
|
63386
64800
|
const list22 = waiters.get(id);
|
|
63387
64801
|
if (!list22) return;
|
|
63388
|
-
const idx = list22.indexOf(
|
|
64802
|
+
const idx = list22.indexOf(resolve47);
|
|
63389
64803
|
if (idx >= 0) list22.splice(idx, 1);
|
|
63390
64804
|
if (list22.length === 0) waiters.delete(id);
|
|
63391
|
-
|
|
64805
|
+
resolve47(liveGet(id));
|
|
63392
64806
|
}, timeoutMs);
|
|
63393
64807
|
timer.unref?.();
|
|
63394
64808
|
});
|
|
@@ -63551,7 +64965,7 @@ function humanizeKey(key) {
|
|
|
63551
64965
|
const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-.]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
63552
64966
|
return words.length === 0 ? "Value" : words.charAt(0).toUpperCase() + words.slice(1);
|
|
63553
64967
|
}
|
|
63554
|
-
function
|
|
64968
|
+
function isRecord15(value) {
|
|
63555
64969
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63556
64970
|
}
|
|
63557
64971
|
function scalarText(value) {
|
|
@@ -63581,7 +64995,7 @@ function flattenRows(value, label, depth, out) {
|
|
|
63581
64995
|
});
|
|
63582
64996
|
return;
|
|
63583
64997
|
}
|
|
63584
|
-
if (
|
|
64998
|
+
if (isRecord15(value)) {
|
|
63585
64999
|
if (depth >= MAX_DEPTH) return;
|
|
63586
65000
|
for (const [key, child] of Object.entries(value)) {
|
|
63587
65001
|
flattenRows(child, label ? `${label} / ${humanizeKey(key)}` : humanizeKey(key), depth + 1, out);
|
|
@@ -63603,7 +65017,7 @@ function renderMail(preview2) {
|
|
|
63603
65017
|
const cc = addressList(preview2.cc);
|
|
63604
65018
|
const subject = scalarText(preview2.subject) ?? "";
|
|
63605
65019
|
const body = scalarText(preview2.body) ?? "";
|
|
63606
|
-
const attachments = Array.isArray(preview2.attachments) ? preview2.attachments.filter(
|
|
65020
|
+
const attachments = Array.isArray(preview2.attachments) ? preview2.attachments.filter(isRecord15).map((a) => {
|
|
63607
65021
|
const name = scalarText(a.fileName) ?? "";
|
|
63608
65022
|
const size = typeof a.sizeBytes === "number" ? humanSize(a.sizeBytes) : "";
|
|
63609
65023
|
return size ? `${name} (${size})` : name;
|
|
@@ -63618,9 +65032,9 @@ function renderMail(preview2) {
|
|
|
63618
65032
|
}
|
|
63619
65033
|
function renderPreview(record2) {
|
|
63620
65034
|
const preview2 = record2.preview;
|
|
63621
|
-
if (
|
|
65035
|
+
if (isRecord15(preview2) && preview2.kind === "mail") return renderMail(preview2);
|
|
63622
65036
|
const rows = [];
|
|
63623
|
-
if (
|
|
65037
|
+
if (isRecord15(preview2)) {
|
|
63624
65038
|
for (const [key, value] of Object.entries(preview2)) flattenRows(value, humanizeKey(key), 1, rows);
|
|
63625
65039
|
} else {
|
|
63626
65040
|
flattenRows(preview2, "Details", 1, rows);
|
|
@@ -65717,9 +67131,9 @@ async function* readEventRecords(path) {
|
|
|
65717
67131
|
let stream;
|
|
65718
67132
|
try {
|
|
65719
67133
|
stream = createReadStream(path, { encoding: "utf8" });
|
|
65720
|
-
await new Promise((
|
|
67134
|
+
await new Promise((resolve47, reject) => {
|
|
65721
67135
|
stream.once("error", reject);
|
|
65722
|
-
stream.once("open",
|
|
67136
|
+
stream.once("open", resolve47);
|
|
65723
67137
|
});
|
|
65724
67138
|
} catch (err) {
|
|
65725
67139
|
if (err.code === "ENOENT") return;
|
|
@@ -66177,13 +67591,13 @@ function createLinkGuard(opts) {
|
|
|
66177
67591
|
return {
|
|
66178
67592
|
async start() {
|
|
66179
67593
|
server = createServer(onRequest);
|
|
66180
|
-
return new Promise((
|
|
67594
|
+
return new Promise((resolve47, reject) => {
|
|
66181
67595
|
server.once("error", reject);
|
|
66182
67596
|
server.listen(0, "127.0.0.1", () => {
|
|
66183
67597
|
log(
|
|
66184
67598
|
`[link-guard] listening on 127.0.0.1:${currentPort()} -> ${opts.target.host}:${opts.target.port}`
|
|
66185
67599
|
);
|
|
66186
|
-
|
|
67600
|
+
resolve47(handle());
|
|
66187
67601
|
});
|
|
66188
67602
|
});
|
|
66189
67603
|
},
|
|
@@ -66191,8 +67605,8 @@ function createLinkGuard(opts) {
|
|
|
66191
67605
|
if (!server) return;
|
|
66192
67606
|
const toClose = server;
|
|
66193
67607
|
server = null;
|
|
66194
|
-
await new Promise((
|
|
66195
|
-
toClose.close((err) => err ? reject(err) :
|
|
67608
|
+
await new Promise((resolve47, reject) => {
|
|
67609
|
+
toClose.close((err) => err ? reject(err) : resolve47());
|
|
66196
67610
|
});
|
|
66197
67611
|
},
|
|
66198
67612
|
revoke() {
|
|
@@ -68203,13 +69617,13 @@ function createJoinTokenRegistry(deps2) {
|
|
|
68203
69617
|
return { create, list, revoke, startAutoconnect, shutdown };
|
|
68204
69618
|
}
|
|
68205
69619
|
function sleep2(ms, signal) {
|
|
68206
|
-
return new Promise((
|
|
68207
|
-
if (signal.aborted) return
|
|
68208
|
-
const timer = setTimeout(
|
|
69620
|
+
return new Promise((resolve47) => {
|
|
69621
|
+
if (signal.aborted) return resolve47();
|
|
69622
|
+
const timer = setTimeout(resolve47, ms);
|
|
68209
69623
|
if (typeof timer.unref === "function") timer.unref();
|
|
68210
69624
|
signal.addEventListener("abort", () => {
|
|
68211
69625
|
clearTimeout(timer);
|
|
68212
|
-
|
|
69626
|
+
resolve47();
|
|
68213
69627
|
});
|
|
68214
69628
|
});
|
|
68215
69629
|
}
|
|
@@ -68899,7 +70313,14 @@ async function createGateway(opts) {
|
|
|
68899
70313
|
registry: sessions,
|
|
68900
70314
|
followStore: sessionFollowStore,
|
|
68901
70315
|
sentinelStore,
|
|
68902
|
-
cancelSentinel: (id) => cancelSentinelWatch(
|
|
70316
|
+
cancelSentinel: (id) => cancelSentinelWatch(
|
|
70317
|
+
{
|
|
70318
|
+
store: sentinelStore,
|
|
70319
|
+
resolveProvider: resolveSentinelProviderResolved,
|
|
70320
|
+
tombstones: sentinelRuntime.cancelTombstones
|
|
70321
|
+
},
|
|
70322
|
+
id
|
|
70323
|
+
)
|
|
68903
70324
|
});
|
|
68904
70325
|
const inboundWatcher = opts.resolveAgentAdapter ? createInboundWatcher({
|
|
68905
70326
|
mcpProxy,
|
|
@@ -69281,6 +70702,8 @@ async function createGateway(opts) {
|
|
|
69281
70702
|
registerSentinelTools(server, {
|
|
69282
70703
|
store: sentinelStore,
|
|
69283
70704
|
runtime: sentinelRuntime,
|
|
70705
|
+
tombstones: sentinelRuntime.cancelTombstones,
|
|
70706
|
+
deliveryStatus: (id) => sentinelRuntime.webhookOutbox.deliveryStatus(id),
|
|
69284
70707
|
resolveProvider: resolveSentinelProviderResolved,
|
|
69285
70708
|
isSessionAlive,
|
|
69286
70709
|
...callerSessionId ? { callerSessionId } : {}
|
|
@@ -69310,11 +70733,39 @@ async function createGateway(opts) {
|
|
|
69310
70733
|
unsubscribe: (params) => eventsUnsubscribe(params, {
|
|
69311
70734
|
principal: eventsPrincipal,
|
|
69312
70735
|
store: sentinelStore,
|
|
69313
|
-
resolveProvider: resolveSentinelProviderResolved
|
|
70736
|
+
resolveProvider: resolveSentinelProviderResolved,
|
|
70737
|
+
tombstones: sentinelRuntime.cancelTombstones
|
|
69314
70738
|
})
|
|
69315
70739
|
});
|
|
69316
70740
|
return server;
|
|
69317
70741
|
};
|
|
70742
|
+
const eventsSecret = process.env.AGENTPROTO_MCP_EVENTS_SECRET ?? "";
|
|
70743
|
+
const eventsRepos = (process.env.AGENTPROTO_MCP_EVENTS_REPOS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
70744
|
+
const eventsMcp = eventsSecret.length >= 32 ? {
|
|
70745
|
+
secret: eventsSecret,
|
|
70746
|
+
allowedOrigins: (process.env.AGENTPROTO_MCP_EVENTS_ORIGINS ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
70747
|
+
createServer: async () => {
|
|
70748
|
+
const principal = sessionPrincipal("mcp-events-origin");
|
|
70749
|
+
return createEventsSurfaceServer({
|
|
70750
|
+
version: opts.version ?? "0.1.0-alpha",
|
|
70751
|
+
repoAllowlist: eventsRepos,
|
|
70752
|
+
handlers: {
|
|
70753
|
+
list: (params) => eventsList(params, { principal }),
|
|
70754
|
+
subscribe: (params) => eventsSubscribe(params, {
|
|
70755
|
+
principal,
|
|
70756
|
+
store: sentinelStore,
|
|
70757
|
+
resolveProvider: resolveSentinelProviderResolved
|
|
70758
|
+
}),
|
|
70759
|
+
unsubscribe: (params) => eventsUnsubscribe(params, {
|
|
70760
|
+
principal,
|
|
70761
|
+
store: sentinelStore,
|
|
70762
|
+
resolveProvider: resolveSentinelProviderResolved,
|
|
70763
|
+
tombstones: sentinelRuntime.cancelTombstones
|
|
70764
|
+
})
|
|
70765
|
+
}
|
|
70766
|
+
});
|
|
70767
|
+
}
|
|
70768
|
+
} : void 0;
|
|
69318
70769
|
let internalToolServerPromise;
|
|
69319
70770
|
dispatchToolBox.fn = async (name, inputs) => {
|
|
69320
70771
|
if (!internalToolServerPromise) internalToolServerPromise = mcpServerFactory();
|
|
@@ -69385,6 +70836,7 @@ async function createGateway(opts) {
|
|
|
69385
70836
|
},
|
|
69386
70837
|
mcpServerFactory,
|
|
69387
70838
|
orchestratorMcpServerFactory,
|
|
70839
|
+
...eventsMcp ? { eventsMcp } : {},
|
|
69388
70840
|
verifyOrchestratorScope: scopeTokens.verify,
|
|
69389
70841
|
conversations,
|
|
69390
70842
|
events,
|
|
@@ -69402,7 +70854,9 @@ async function createGateway(opts) {
|
|
|
69402
70854
|
store: sentinelStore,
|
|
69403
70855
|
resolveProvider: resolveSentinelProviderResolved,
|
|
69404
70856
|
isSessionAlive,
|
|
69405
|
-
runtime: sentinelRuntime
|
|
70857
|
+
runtime: sentinelRuntime,
|
|
70858
|
+
tombstones: sentinelRuntime.cancelTombstones,
|
|
70859
|
+
deliveryStatus: (id) => sentinelRuntime.webhookOutbox.deliveryStatus(id)
|
|
69406
70860
|
},
|
|
69407
70861
|
follows: {
|
|
69408
70862
|
store: sessionFollowStore,
|
|
@@ -69595,7 +71049,12 @@ async function createGateway(opts) {
|
|
|
69595
71049
|
}, pendingPromptSweepMs);
|
|
69596
71050
|
pendingPromptTimer.unref?.();
|
|
69597
71051
|
const livenessSweepIntervalMs = 3e4;
|
|
71052
|
+
const livenessTickGuard = createLivenessTickGuard(livenessSweepIntervalMs, 5e3);
|
|
69598
71053
|
const livenessSweepTimer = setInterval(() => {
|
|
71054
|
+
if (livenessTickGuard.shouldSkip()) {
|
|
71055
|
+
console.warn("[liveness-sweep] tick fired late (event loop starved) \u2014 skipping this sweep");
|
|
71056
|
+
return;
|
|
71057
|
+
}
|
|
69599
71058
|
try {
|
|
69600
71059
|
const orphanedRuns = workflowRunner?.sweep().orphaned ?? [];
|
|
69601
71060
|
if (orphanedRuns.length > 0) {
|
|
@@ -69746,6 +71205,6 @@ var export_readPairingsSnapshot = pairing_registry_exports.readPairingsSnapshot;
|
|
|
69746
71205
|
var export_removeProviderKey = providers_store_exports.removeProviderKey;
|
|
69747
71206
|
var export_setProviderKey = providers_store_exports.setProviderKey;
|
|
69748
71207
|
|
|
69749
|
-
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAMP_LIMIT_BYTES, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_TTL_MS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEVICE_PROMPT_PATH_PREFIX, EVENT_REGISTRY, FOLLOW_EVENTS, GC_REAPABLE_STATES, GITHUB_SCHEME, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, INDEX_DEFAULT_LIMIT, INDEX_MAX_OUTPUT, INDEX_MAX_PROMPT, INDEX_TAIL_BYTES, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_ATTEMPTS, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MAX_TTL_MS, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MIN_TTL_MS, MODEL_ROLE_REF_PREFIX, McpEventsError, NO_OUTPUT_STALL_TURN_ERROR, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SIGN_NAMESPACE, SessionRetiredError, SsrfFetchError, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, attachSandbox, backfillSessionIndexes, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, buildSessionRecap, canonicalForModeId, canonicalizeJcs, challengeCacheSize, challengeReasonForSsrf, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, computeRefreshBefore, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSentinelWebhookOutbox, createSessionFollowStore, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonBearerPrincipal, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decodeWhsecSecret, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defaultTranscriptBaseDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deliverEventEnvelope, deliveryBackoffDelay, deriveIndexFromTranscript, deriveSessionOutcome, deriveSessionUsage, describeOwner, encodeWhsecSecret, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, envelopeClampedBytes, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, eventsList, eventsSubscribe, eventsUnsubscribe, execGh, extractHelpSection, fetchHostTurns, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findEventDefinition, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hostPlaceholders, hostRecordToMirrorRecord, indexEntryFromDescriptor, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isAutomatedPromptSource, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isNoSessionPromptFailure, isProviderLimitError, isPubliclyRoutable, isRetired, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeRestartForRouting, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, matchesSessionQuery, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, mirrorCursorForSession, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCallbackUrl, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, promptDeviceSession, promptHostSession, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readAllSessionIndexes, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readSessionIndex, readTranscriptTail, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resetChallengeCacheForTests, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveDevicePromptMapping, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, resolveSuccessor, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, searchSessionIndexes, seedWalkForTranscript, serializeEnvelope, sessionEventsPath, sessionIndexPath, sessionPrincipal, sessionTranscriptDir, setDefaultPreset, setDefaultSessionsBaseDir, setEgressDispatcherForTests, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, signWebhook, ssrfFetch, subscriptionId, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, syncDeviceMirror, tenantScope, toMcpEvent, toWebhookEnvelope, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, untilForTtl, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, validateAgainstInputSchema, verifyCallback, verifyInboundSignature, verifySignedAttestation, webSearchTool, webhookRequestId, wireSessionFollow, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry, writeSessionIndex };
|
|
71208
|
+
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAMP_LIMIT_BYTES, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_TTL_MS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEVICE_PROMPT_PATH_PREFIX, EVENT_REGISTRY, FOLLOW_EVENTS, GC_REAPABLE_STATES, GITHUB_SCHEME, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, INDEX_DEFAULT_LIMIT, INDEX_MAX_OUTPUT, INDEX_MAX_PROMPT, INDEX_TAIL_BYTES, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_ATTEMPTS, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MAX_TTL_MS, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MIN_TTL_MS, MODEL_ROLE_REF_PREFIX, McpEventsError, NO_OUTPUT_STALL_TURN_ERROR, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SESSION_LIST_FILTER_KEYS, SIGN_NAMESPACE, SentinelBackingExpiredError, SessionListFilterError, SessionRetiredError, SsrfFetchError, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, applySessionListFilters, attachSandbox, backfillSessionIndexes, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, buildSessionRecap, canonicalForModeId, canonicalizeJcs, challengeCacheSize, challengeReasonForSsrf, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, compileSessionListFilters, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, computeRefreshBefore, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSentinelWebhookOutbox, createSessionFollowStore, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonBearerPrincipal, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decodeWhsecSecret, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defaultTranscriptBaseDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deliverEventEnvelope, deliveryBackoffDelay, deriveIndexFromTranscript, deriveSessionOutcome, deriveSessionUsage, describeOwner, encodeWhsecSecret, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, envelopeClampedBytes, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, eventsList, eventsSubscribe, eventsUnsubscribe, execGh, extractHelpSection, fetchHostTurns, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findEventDefinition, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hasSessionListFilters, hostPlaceholders, hostRecordToMirrorRecord, indexEntryFromDescriptor, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isAutomatedPromptSource, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isNoSessionPromptFailure, isNoiseSession, isProviderLimitError, isPubliclyRoutable, isRetired, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeRestartForRouting, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, matchesSessionQuery, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, mirrorCursorForSession, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCallbackUrl, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseSessionListFilterParams, parseTaskStatus, parseTimeBound, parseWindow, parseWorktreeIsolationMode, pickSessionListFilters, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, promptDeviceSession, promptHostSession, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readAllSessionIndexes, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readSessionIndex, readTranscriptTail, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resetChallengeCacheForTests, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveDevicePromptMapping, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, resolveSuccessor, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, searchSessionIndexes, seedWalkForTranscript, serializeEnvelope, sessionActivityMs, sessionEventsPath, sessionIndexPath, sessionListFilterShape, sessionPrincipal, sessionTranscriptDir, setDefaultPreset, setDefaultSessionsBaseDir, setEgressDispatcherForTests, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, signWebhook, sortNewestActivityFirst, ssrfFetch, subscriptionId, summarizeBranchGcApply, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, syncDeviceMirror, tenantScope, toMcpEvent, toWebhookEnvelope, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, untilForTtl, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, validateAgainstInputSchema, verifyCallback, verifyInboundSignature, verifySignedAttestation, webSearchTool, webhookRequestId, wireSessionFollow, withBranchGcApplySummary, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry, writeSessionIndex };
|
|
69750
71209
|
//# sourceMappingURL=index.mjs.map
|
|
69751
71210
|
//# sourceMappingURL=index.mjs.map
|