@agentproto/runtime 2.7.0 → 2.8.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/index.d.ts +57 -6
- package/dist/index.mjs +323 -169
- package/dist/index.mjs.map +1 -1
- package/dist/pr-provenance.d.ts +29 -1
- package/dist/pr-provenance.mjs +17 -2
- package/dist/pr-provenance.mjs.map +1 -1
- package/dist/resume-strategies.d.ts +13 -1
- package/dist/resume-strategies.mjs +25 -14
- package/dist/resume-strategies.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { join, resolve, dirname, basename, isAbsolute, normalize, relative, extname, delimiter, sep } from 'path';
|
|
1
2
|
import { createReadStream, promises, readdirSync, readFileSync, mkdirSync, writeFileSync, existsSync, renameSync, chmodSync, openSync, closeSync, realpathSync, statSync, createWriteStream } from 'fs';
|
|
2
3
|
import { homedir, hostname, tmpdir } from 'os';
|
|
3
|
-
import { join, dirname, resolve, basename, isAbsolute, normalize, relative, extname, delimiter, sep } from 'path';
|
|
4
4
|
import { createInterface } from 'readline';
|
|
5
5
|
import { timingSafeEqual, createHmac, randomBytes, randomUUID, createHash } from 'crypto';
|
|
6
6
|
import { mkdir, writeFile, unlink, readdir, readFile, stat, chmod, rename, rm, appendFile, mkdtemp, realpath, access } from 'fs/promises';
|
|
@@ -198,6 +198,127 @@ var init_tool_call_record = __esm({
|
|
|
198
198
|
"src/tool-call-record.ts"() {
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
|
+
function cwdLabel(cwd, workspaceSlug) {
|
|
202
|
+
const leaf = basename(cwd);
|
|
203
|
+
if (!workspaceSlug || workspaceSlug === leaf) return workspaceSlug ?? leaf;
|
|
204
|
+
return `${workspaceSlug}/${leaf}`;
|
|
205
|
+
}
|
|
206
|
+
function sessionFooterProvenance(session, options = {}) {
|
|
207
|
+
const prov = {
|
|
208
|
+
sessionId: session.id,
|
|
209
|
+
label: session.label,
|
|
210
|
+
adapter: session.harness ?? session.adapterSlug,
|
|
211
|
+
model: session.model,
|
|
212
|
+
authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,
|
|
213
|
+
parentSessionId: options.supervisor?.id,
|
|
214
|
+
costUsd: session.costUsd,
|
|
215
|
+
tokensIn: session.tokensIn,
|
|
216
|
+
tokensOut: session.tokensOut,
|
|
217
|
+
source: options.source ?? "daemon",
|
|
218
|
+
host: options.host,
|
|
219
|
+
cwd: session.cwd,
|
|
220
|
+
workspaceSlug: session.workspaceSlug
|
|
221
|
+
};
|
|
222
|
+
return { prov, authMode: session.auth?.mode };
|
|
223
|
+
}
|
|
224
|
+
function buildSessionPrFooter(session, options = {}) {
|
|
225
|
+
const { prov, authMode } = sessionFooterProvenance(session, options);
|
|
226
|
+
return buildFooter({ prov, authMode, sha: options.sha, kind: "PR" });
|
|
227
|
+
}
|
|
228
|
+
function appendFooterOnce(body, footer) {
|
|
229
|
+
if (hasProvenanceFooter(body)) return body;
|
|
230
|
+
return `${body}${footer}`;
|
|
231
|
+
}
|
|
232
|
+
function hasProvenanceFooter(body) {
|
|
233
|
+
return new RegExp(`<sub>[^\\n]*${MARKER}`).test(body);
|
|
234
|
+
}
|
|
235
|
+
function parseGhPrCreate(command, args, stdout) {
|
|
236
|
+
if (basename(command) !== "gh") return null;
|
|
237
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
238
|
+
if (positionals[0] !== "pr" || positionals[1] !== "create") return null;
|
|
239
|
+
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
240
|
+
let match;
|
|
241
|
+
let last = null;
|
|
242
|
+
while ((match = re.exec(stdout)) !== null) {
|
|
243
|
+
last = { url: match[0], number: Number(match[1]) };
|
|
244
|
+
}
|
|
245
|
+
return last;
|
|
246
|
+
}
|
|
247
|
+
function detectShellPrCreate(command, resultText) {
|
|
248
|
+
if (!command || !resultText) return null;
|
|
249
|
+
const unquoted = command.replace(/'[^']*'/g, " ").replace(/"(?:[^"\\]|\\.)*"/g, " ");
|
|
250
|
+
if (!/(^|[\s;&|({])gh\s+pr\s+create(\s|$)/.test(unquoted)) return null;
|
|
251
|
+
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
252
|
+
let match;
|
|
253
|
+
let last = null;
|
|
254
|
+
while ((match = re.exec(resultText)) !== null) {
|
|
255
|
+
last = { url: match[0], number: Number(match[1]) };
|
|
256
|
+
}
|
|
257
|
+
return last;
|
|
258
|
+
}
|
|
259
|
+
function cwdRelated(sessionCwd, cwd) {
|
|
260
|
+
if (sessionCwd === cwd) return true;
|
|
261
|
+
const sep2 = "/";
|
|
262
|
+
return cwd.startsWith(sessionCwd + sep2) || sessionCwd.startsWith(cwd + sep2);
|
|
263
|
+
}
|
|
264
|
+
function pickExecutorSession(sessions, cwd) {
|
|
265
|
+
const candidates = sessions.filter(
|
|
266
|
+
(s) => s.kind === "agent-cli" && typeof s.cwd === "string" && cwdRelated(s.cwd, cwd)
|
|
267
|
+
);
|
|
268
|
+
if (candidates.length === 0) return void 0;
|
|
269
|
+
const alive = (s) => s.status === "running" || s.status === "starting";
|
|
270
|
+
const byRecency = (a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "");
|
|
271
|
+
const live = candidates.filter(alive).sort(byRecency);
|
|
272
|
+
if (live.length > 0) return live[0];
|
|
273
|
+
return [...candidates].sort(byRecency)[0];
|
|
274
|
+
}
|
|
275
|
+
var MARKER, fmtTokens, buildFooter;
|
|
276
|
+
var init_pr_provenance = __esm({
|
|
277
|
+
"src/pr-provenance.ts"() {
|
|
278
|
+
MARKER = "@agentproto-bot";
|
|
279
|
+
fmtTokens = (n) => {
|
|
280
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
281
|
+
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
282
|
+
};
|
|
283
|
+
buildFooter = ({
|
|
284
|
+
prov,
|
|
285
|
+
authMode,
|
|
286
|
+
runId,
|
|
287
|
+
runUrl,
|
|
288
|
+
sha,
|
|
289
|
+
kind = "review"
|
|
290
|
+
}) => {
|
|
291
|
+
const parts = [`\u{1F916} **${MARKER}** \u2014 ${kind}`];
|
|
292
|
+
if (prov?.sessionId) {
|
|
293
|
+
parts.push(`session \`${prov.sessionId}\`${prov.label ? ` (\`${prov.label}\`)` : ""}`);
|
|
294
|
+
}
|
|
295
|
+
if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(" / "));
|
|
296
|
+
else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : ""}`);
|
|
297
|
+
else if (authMode) parts.push(authMode);
|
|
298
|
+
if (prov?.authProfile) parts.push(`auth-profile \`${prov.authProfile}\``);
|
|
299
|
+
if (prov?.model) parts.push(`model \`${prov.model}\``);
|
|
300
|
+
if (prov?.sandboxId) parts.push(`e2b \`${prov.sandboxId}\``);
|
|
301
|
+
if (prov?.parentSessionId) parts.push(`supervisor \`${prov.parentSessionId}\``);
|
|
302
|
+
const tin = fmtTokens(prov?.tokensIn);
|
|
303
|
+
const tout = fmtTokens(prov?.tokensOut);
|
|
304
|
+
if (tin || tout) parts.push(`${tin ?? "?"} in / ${tout ?? "?"} out`);
|
|
305
|
+
if (typeof prov?.costUsd === "number") {
|
|
306
|
+
parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== "adapter" ? ` (${prov.source})` : ""}`);
|
|
307
|
+
}
|
|
308
|
+
const showLocalHostCwd = prov?.source === "local" || prov?.source === "daemon" || !runId;
|
|
309
|
+
if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`);
|
|
310
|
+
if (showLocalHostCwd) {
|
|
311
|
+
if (prov?.host) parts.push(`host \`${prov.host}\``);
|
|
312
|
+
if (prov?.cwd) parts.push(`cwd \`${cwdLabel(prov.cwd, prov.workspaceSlug)}\``);
|
|
313
|
+
}
|
|
314
|
+
if (sha) parts.push(`sha \`${sha.slice(0, 7)}\``);
|
|
315
|
+
return `
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
<sub>${parts.join(" \xB7 ")}</sub>`;
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
});
|
|
201
322
|
function sessionTranscriptDir(sessionId, baseDir) {
|
|
202
323
|
return join(baseDir ?? join(homedir(), ".agentproto", "sessions"), sessionId);
|
|
203
324
|
}
|
|
@@ -390,6 +511,10 @@ function createTranscriptWriter(opts) {
|
|
|
390
511
|
const meta = evt.toolCallId ? state.toolCallMeta.get(evt.toolCallId) : void 0;
|
|
391
512
|
if (evt.toolCallId) state.toolCallMeta.delete(evt.toolCallId);
|
|
392
513
|
const { command, args } = extractCommandArgs(meta?.arguments);
|
|
514
|
+
const createdPr = evt.isError ?? false ? null : detectShellPrCreate(
|
|
515
|
+
command ?? meta?.toolName,
|
|
516
|
+
typeof evt.result === "string" ? evt.result : void 0
|
|
517
|
+
);
|
|
393
518
|
writeRecord(sessionId, state, {
|
|
394
519
|
kind: "tool-call-record",
|
|
395
520
|
sessionId,
|
|
@@ -397,7 +522,8 @@ function createTranscriptWriter(opts) {
|
|
|
397
522
|
...command !== void 0 ? { command } : {},
|
|
398
523
|
...args !== void 0 ? { args } : {},
|
|
399
524
|
isError: evt.isError ?? false,
|
|
400
|
-
...meta ? { durationMs: Date.now() - meta.startedAt } : {}
|
|
525
|
+
...meta ? { durationMs: Date.now() - meta.startedAt } : {},
|
|
526
|
+
...createdPr ? { createdPrUrl: createdPr.url, createdPrNumber: createdPr.number } : {}
|
|
401
527
|
});
|
|
402
528
|
break;
|
|
403
529
|
}
|
|
@@ -522,6 +648,7 @@ var DEBOUNCE_MS;
|
|
|
522
648
|
var init_transcript_writer = __esm({
|
|
523
649
|
"src/transcript-writer.ts"() {
|
|
524
650
|
init_tool_call_record();
|
|
651
|
+
init_pr_provenance();
|
|
525
652
|
DEBOUNCE_MS = 250;
|
|
526
653
|
}
|
|
527
654
|
});
|
|
@@ -634,14 +761,13 @@ function renderMarkdown(session, opts = {}) {
|
|
|
634
761
|
function renderJson(session) {
|
|
635
762
|
return JSON.stringify(session, null, 2);
|
|
636
763
|
}
|
|
637
|
-
async function exportClaudeCodeSession(adapterSessionId, cwd) {
|
|
764
|
+
async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
|
|
638
765
|
if (!cwd) {
|
|
639
766
|
throw new Error(
|
|
640
767
|
"claude-code exporter: cwd is required to locate the JSONL file.\nPass cwd explicitly or use a session id that is in the registry."
|
|
641
768
|
);
|
|
642
769
|
}
|
|
643
|
-
const
|
|
644
|
-
const filePath = join(homedir(), ".claude", "projects", encoded, `${adapterSessionId}.jsonl`);
|
|
770
|
+
const filePath = join(claudeCodeProjectDir(cwd, configDir), `${adapterSessionId}.jsonl`);
|
|
645
771
|
let stream;
|
|
646
772
|
try {
|
|
647
773
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
@@ -1735,6 +1861,7 @@ async function exportAgentSession(input) {
|
|
|
1735
1861
|
let adapterSlug = input.adapter;
|
|
1736
1862
|
let cwd = input.cwd;
|
|
1737
1863
|
let adapterSessionId = sessionId;
|
|
1864
|
+
let configDir;
|
|
1738
1865
|
const err = (msg) => ({
|
|
1739
1866
|
sessionId,
|
|
1740
1867
|
adapter: adapterSlug ?? "unknown",
|
|
@@ -1747,6 +1874,7 @@ async function exportAgentSession(input) {
|
|
|
1747
1874
|
if (desc) {
|
|
1748
1875
|
adapterSlug = adapterSlug ?? desc.adapterSlug;
|
|
1749
1876
|
cwd = cwd ?? desc.cwd;
|
|
1877
|
+
configDir = desc.adapterConfigDir;
|
|
1750
1878
|
if (desc.adapterSessionId) adapterSessionId = desc.adapterSessionId;
|
|
1751
1879
|
}
|
|
1752
1880
|
const tryNative = async () => {
|
|
@@ -1764,7 +1892,7 @@ Pass adapter explicitly or use a known session id (sess_xxx or name).`
|
|
|
1764
1892
|
Only sessions spawned via claude-code or hermes can be exported.`
|
|
1765
1893
|
);
|
|
1766
1894
|
}
|
|
1767
|
-
return exporter.exportSession(adapterSessionId, cwd);
|
|
1895
|
+
return exporter.exportSession(adapterSessionId, cwd, configDir);
|
|
1768
1896
|
};
|
|
1769
1897
|
const tryDaemon = () => exportDaemonEventsSession(daemonSessionId, desc);
|
|
1770
1898
|
let session;
|
|
@@ -1844,8 +1972,9 @@ var init_transcript_export = __esm({
|
|
|
1844
1972
|
function claudeProjectSlug(cwd) {
|
|
1845
1973
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
1846
1974
|
}
|
|
1847
|
-
function claudeCodeProjectDir(cwd) {
|
|
1848
|
-
|
|
1975
|
+
function claudeCodeProjectDir(cwd, configDir) {
|
|
1976
|
+
const base = configDir ?? resolve(homedir(), ".claude");
|
|
1977
|
+
return resolve(base, "projects", claudeProjectSlug(cwd));
|
|
1849
1978
|
}
|
|
1850
1979
|
function extractFirstText(content) {
|
|
1851
1980
|
if (typeof content === "string") {
|
|
@@ -1906,8 +2035,8 @@ function claudeEntrypointFor(mode) {
|
|
|
1906
2035
|
return mode === "native" ? "cli" : "sdk-ts";
|
|
1907
2036
|
}
|
|
1908
2037
|
async function discoverClaudeCode(input) {
|
|
1909
|
-
const { cwd, since, until, attachmentMode, expectedId } = input;
|
|
1910
|
-
const dir = claudeCodeProjectDir(cwd);
|
|
2038
|
+
const { cwd, since, until, attachmentMode, configDir, expectedId } = input;
|
|
2039
|
+
const dir = claudeCodeProjectDir(cwd, configDir);
|
|
1911
2040
|
if (expectedId) {
|
|
1912
2041
|
const filePath = join(dir, `${expectedId}.jsonl`);
|
|
1913
2042
|
try {
|
|
@@ -1952,9 +2081,9 @@ async function discoverClaudeCode(input) {
|
|
|
1952
2081
|
scored.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1953
2082
|
return scored.map((s) => s.candidate);
|
|
1954
2083
|
}
|
|
1955
|
-
async function readClaudeCode(conversationId, cwd) {
|
|
2084
|
+
async function readClaudeCode(conversationId, cwd, configDir) {
|
|
1956
2085
|
const { exportClaudeCodeSession: exportClaudeCodeSession2 } = await Promise.resolve().then(() => (init_transcript_export(), transcript_export_exports));
|
|
1957
|
-
return exportClaudeCodeSession2(conversationId, cwd);
|
|
2086
|
+
return exportClaudeCodeSession2(conversationId, cwd, configDir);
|
|
1958
2087
|
}
|
|
1959
2088
|
async function discoverHermes(input) {
|
|
1960
2089
|
const { discoverHermesSessions: discoverHermesSessions2 } = await Promise.resolve().then(() => (init_transcript_export(), transcript_export_exports));
|
|
@@ -2274,11 +2403,12 @@ var RESUME_STRATEGIES = Object.fromEntries(
|
|
|
2274
2403
|
{
|
|
2275
2404
|
outputHint: store.outputHint,
|
|
2276
2405
|
storeAs: store.storeAs,
|
|
2277
|
-
fsProbe: async (cwd, prevStartedAt, expectedId) => {
|
|
2406
|
+
fsProbe: async (cwd, prevStartedAt, expectedId, configDir) => {
|
|
2278
2407
|
const candidates = await s.discover({
|
|
2279
2408
|
cwd,
|
|
2280
2409
|
since: prevStartedAt,
|
|
2281
|
-
expectedId
|
|
2410
|
+
expectedId,
|
|
2411
|
+
configDir
|
|
2282
2412
|
});
|
|
2283
2413
|
return candidates[0]?.conversationId ?? null;
|
|
2284
2414
|
},
|
|
@@ -2322,7 +2452,10 @@ async function augmentWithFsResume(prev) {
|
|
|
2322
2452
|
const id = await strategy.fsProbe(
|
|
2323
2453
|
prev.cwd,
|
|
2324
2454
|
prev.startedAt,
|
|
2325
|
-
prev.adapterSessionId
|
|
2455
|
+
prev.adapterSessionId,
|
|
2456
|
+
// A config-dir-isolated session (#824) persisted its transcript under
|
|
2457
|
+
// its own CLAUDE_CONFIG_DIR — probe there, not the global ~/.claude.
|
|
2458
|
+
prev.adapterConfigDir
|
|
2326
2459
|
);
|
|
2327
2460
|
if (!id) return prev;
|
|
2328
2461
|
return {
|
|
@@ -4890,109 +5023,9 @@ async function loadSpawnDedupe(loadCfg = loadConfig) {
|
|
|
4890
5023
|
}
|
|
4891
5024
|
return DEFAULT_SPAWN_DEDUPE;
|
|
4892
5025
|
}
|
|
4893
|
-
var MARKER = "@agentproto-bot";
|
|
4894
|
-
var fmtTokens = (n) => {
|
|
4895
|
-
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
4896
|
-
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
4897
|
-
};
|
|
4898
|
-
function cwdLabel(cwd, workspaceSlug) {
|
|
4899
|
-
const leaf = basename(cwd);
|
|
4900
|
-
if (!workspaceSlug || workspaceSlug === leaf) return workspaceSlug ?? leaf;
|
|
4901
|
-
return `${workspaceSlug}/${leaf}`;
|
|
4902
|
-
}
|
|
4903
|
-
var buildFooter = ({
|
|
4904
|
-
prov,
|
|
4905
|
-
authMode,
|
|
4906
|
-
runId,
|
|
4907
|
-
runUrl,
|
|
4908
|
-
sha,
|
|
4909
|
-
kind = "review"
|
|
4910
|
-
}) => {
|
|
4911
|
-
const parts = [`\u{1F916} **${MARKER}** \u2014 ${kind}`];
|
|
4912
|
-
if (prov?.sessionId) {
|
|
4913
|
-
parts.push(`session \`${prov.sessionId}\`${prov.label ? ` (\`${prov.label}\`)` : ""}`);
|
|
4914
|
-
}
|
|
4915
|
-
if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(" / "));
|
|
4916
|
-
else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : ""}`);
|
|
4917
|
-
else if (authMode) parts.push(authMode);
|
|
4918
|
-
if (prov?.authProfile) parts.push(`auth-profile \`${prov.authProfile}\``);
|
|
4919
|
-
if (prov?.model) parts.push(`model \`${prov.model}\``);
|
|
4920
|
-
if (prov?.sandboxId) parts.push(`e2b \`${prov.sandboxId}\``);
|
|
4921
|
-
if (prov?.parentSessionId) parts.push(`supervisor \`${prov.parentSessionId}\``);
|
|
4922
|
-
const tin = fmtTokens(prov?.tokensIn);
|
|
4923
|
-
const tout = fmtTokens(prov?.tokensOut);
|
|
4924
|
-
if (tin || tout) parts.push(`${tin ?? "?"} in / ${tout ?? "?"} out`);
|
|
4925
|
-
if (typeof prov?.costUsd === "number") {
|
|
4926
|
-
parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== "adapter" ? ` (${prov.source})` : ""}`);
|
|
4927
|
-
}
|
|
4928
|
-
const showLocalHostCwd = prov?.source === "local" || prov?.source === "daemon" || !runId;
|
|
4929
|
-
if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`);
|
|
4930
|
-
if (showLocalHostCwd) {
|
|
4931
|
-
if (prov?.host) parts.push(`host \`${prov.host}\``);
|
|
4932
|
-
if (prov?.cwd) parts.push(`cwd \`${cwdLabel(prov.cwd, prov.workspaceSlug)}\``);
|
|
4933
|
-
}
|
|
4934
|
-
if (sha) parts.push(`sha \`${sha.slice(0, 7)}\``);
|
|
4935
|
-
return `
|
|
4936
|
-
|
|
4937
|
-
---
|
|
4938
|
-
<sub>${parts.join(" \xB7 ")}</sub>`;
|
|
4939
|
-
};
|
|
4940
|
-
function sessionFooterProvenance(session, options = {}) {
|
|
4941
|
-
const prov = {
|
|
4942
|
-
sessionId: session.id,
|
|
4943
|
-
label: session.label,
|
|
4944
|
-
adapter: session.harness ?? session.adapterSlug,
|
|
4945
|
-
model: session.model,
|
|
4946
|
-
authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,
|
|
4947
|
-
parentSessionId: options.supervisor?.id,
|
|
4948
|
-
costUsd: session.costUsd,
|
|
4949
|
-
tokensIn: session.tokensIn,
|
|
4950
|
-
tokensOut: session.tokensOut,
|
|
4951
|
-
source: options.source ?? "daemon",
|
|
4952
|
-
host: options.host,
|
|
4953
|
-
cwd: session.cwd,
|
|
4954
|
-
workspaceSlug: session.workspaceSlug
|
|
4955
|
-
};
|
|
4956
|
-
return { prov, authMode: session.auth?.mode };
|
|
4957
|
-
}
|
|
4958
|
-
function buildSessionPrFooter(session, options = {}) {
|
|
4959
|
-
const { prov, authMode } = sessionFooterProvenance(session, options);
|
|
4960
|
-
return buildFooter({ prov, authMode, sha: options.sha, kind: "PR" });
|
|
4961
|
-
}
|
|
4962
|
-
function appendFooterOnce(body, footer) {
|
|
4963
|
-
if (body.includes(MARKER)) return body;
|
|
4964
|
-
return `${body}${footer}`;
|
|
4965
|
-
}
|
|
4966
|
-
function parseGhPrCreate(command, args, stdout) {
|
|
4967
|
-
if (basename(command) !== "gh") return null;
|
|
4968
|
-
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
4969
|
-
if (positionals[0] !== "pr" || positionals[1] !== "create") return null;
|
|
4970
|
-
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
4971
|
-
let match;
|
|
4972
|
-
let last = null;
|
|
4973
|
-
while ((match = re.exec(stdout)) !== null) {
|
|
4974
|
-
last = { url: match[0], number: Number(match[1]) };
|
|
4975
|
-
}
|
|
4976
|
-
return last;
|
|
4977
|
-
}
|
|
4978
|
-
function cwdRelated(sessionCwd, cwd) {
|
|
4979
|
-
if (sessionCwd === cwd) return true;
|
|
4980
|
-
const sep2 = "/";
|
|
4981
|
-
return cwd.startsWith(sessionCwd + sep2) || sessionCwd.startsWith(cwd + sep2);
|
|
4982
|
-
}
|
|
4983
|
-
function pickExecutorSession(sessions, cwd) {
|
|
4984
|
-
const candidates = sessions.filter(
|
|
4985
|
-
(s) => s.kind === "agent-cli" && typeof s.cwd === "string" && cwdRelated(s.cwd, cwd)
|
|
4986
|
-
);
|
|
4987
|
-
if (candidates.length === 0) return void 0;
|
|
4988
|
-
const alive = (s) => s.status === "running" || s.status === "starting";
|
|
4989
|
-
const byRecency = (a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "");
|
|
4990
|
-
const live = candidates.filter(alive).sort(byRecency);
|
|
4991
|
-
if (live.length > 0) return live[0];
|
|
4992
|
-
return [...candidates].sort(byRecency)[0];
|
|
4993
|
-
}
|
|
4994
5026
|
|
|
4995
5027
|
// src/gh-provenance-shim.ts
|
|
5028
|
+
init_pr_provenance();
|
|
4996
5029
|
var PROVENANCE_WRAP_GH_ENV = "AGENTPROTO_PROVENANCE_WRAP_GH";
|
|
4997
5030
|
var DEFAULT_WRAP_GH = false;
|
|
4998
5031
|
var GH_PROVENANCE_ENABLE_ENV = "AGENTPROTO_GH_PROVENANCE";
|
|
@@ -5364,6 +5397,11 @@ async function resolveAccessProfileAuth(input) {
|
|
|
5364
5397
|
}
|
|
5365
5398
|
};
|
|
5366
5399
|
}
|
|
5400
|
+
function shouldInjectDaemonSelfMount(adapter, sandbox) {
|
|
5401
|
+
if (adapter === "hermes") return true;
|
|
5402
|
+
if (adapter === "claude-code") return sandbox === void 0;
|
|
5403
|
+
return false;
|
|
5404
|
+
}
|
|
5367
5405
|
function cleanAgentLines(lines) {
|
|
5368
5406
|
return lines.map((l) => l.replace(/\x1b\[[0-9;]*m/g, "")).filter((l) => {
|
|
5369
5407
|
const t = l.trim();
|
|
@@ -5634,7 +5672,7 @@ async function spawnAgentSession(deps2, input) {
|
|
|
5634
5672
|
const delegationDenied = role.toolPolicy.delegation === "deny";
|
|
5635
5673
|
let mcpServers = input.mcpServers;
|
|
5636
5674
|
const mintedSessionId = mintSessionId();
|
|
5637
|
-
if (!mcpServers && input.adapter
|
|
5675
|
+
if (!mcpServers && shouldInjectDaemonSelfMount(input.adapter, input.sandbox) && daemonMcpUrl) {
|
|
5638
5676
|
let ref = delegationDenied ? `${daemonMcpUrl}${daemonMcpUrl.includes("?") ? "&" : "?"}denyTools=${DELEGATION_TOOL_NAMES.join(",")}` : daemonMcpUrl;
|
|
5639
5677
|
ref += `${ref.includes("?") ? "&" : "?"}callerSessionId=${encodeURIComponent(mintedSessionId)}`;
|
|
5640
5678
|
mcpServers = [{ name: "agentproto", transport: "http", ref }];
|
|
@@ -7468,11 +7506,13 @@ function listBuckets(root) {
|
|
|
7468
7506
|
}
|
|
7469
7507
|
}
|
|
7470
7508
|
var serialize = (snapshot) => JSON.stringify(snapshot, null, 2) + "\n";
|
|
7509
|
+
var tmpSeq = 0;
|
|
7510
|
+
var tmpPathFor = (target) => `${target}.tmp.${process.pid}.${++tmpSeq}`;
|
|
7471
7511
|
async function writeBucketSnapshot(root, slug, snapshot) {
|
|
7472
7512
|
const dir = bucketDir(root, slug);
|
|
7473
7513
|
await promises.mkdir(dir, { recursive: true });
|
|
7474
7514
|
const target = bucketSessionsFile(root, slug);
|
|
7475
|
-
const tmp =
|
|
7515
|
+
const tmp = tmpPathFor(target);
|
|
7476
7516
|
await promises.writeFile(tmp, serialize(snapshot), "utf8");
|
|
7477
7517
|
await promises.rename(tmp, target);
|
|
7478
7518
|
}
|
|
@@ -7480,7 +7520,7 @@ function writeBucketSnapshotSync(root, slug, snapshot) {
|
|
|
7480
7520
|
const dir = bucketDir(root, slug);
|
|
7481
7521
|
mkdirSync(dir, { recursive: true });
|
|
7482
7522
|
const target = bucketSessionsFile(root, slug);
|
|
7483
|
-
const tmp =
|
|
7523
|
+
const tmp = tmpPathFor(target);
|
|
7484
7524
|
writeFileSync(tmp, serialize(snapshot), "utf8");
|
|
7485
7525
|
renameSync(tmp, target);
|
|
7486
7526
|
}
|
|
@@ -7585,9 +7625,9 @@ async function listClaudeSubagents(projectDir, adapterSessionId) {
|
|
|
7585
7625
|
return entries.filter((e) => e.startsWith("agent-") && e.endsWith(".jsonl")).map((e) => join(dir, e)).sort();
|
|
7586
7626
|
}
|
|
7587
7627
|
async function resolveNativeLink(input) {
|
|
7588
|
-
const { cwd, adapterSlug, adapterSessionId } = input;
|
|
7628
|
+
const { cwd, adapterSlug, adapterSessionId, adapterConfigDir } = input;
|
|
7589
7629
|
if (adapterSlug === "claude-code") {
|
|
7590
|
-
const dir = claudeCodeProjectDir(cwd);
|
|
7630
|
+
const dir = claudeCodeProjectDir(cwd, adapterConfigDir);
|
|
7591
7631
|
const path = join(dir, `${adapterSessionId}.jsonl`);
|
|
7592
7632
|
const subagents = await listClaudeSubagents(dir, adapterSessionId);
|
|
7593
7633
|
return { kind: "claude-jsonl", path, subagents };
|
|
@@ -7937,6 +7977,21 @@ function formatAccessForSpawn(desc) {
|
|
|
7937
7977
|
if (!desc.accessProfile?.profileRef) return void 0;
|
|
7938
7978
|
return { profileRef: desc.accessProfile.profileRef };
|
|
7939
7979
|
}
|
|
7980
|
+
function stripOwnCallerStamp(servers, prevId) {
|
|
7981
|
+
if (!servers) return servers;
|
|
7982
|
+
return servers.map((entry) => {
|
|
7983
|
+
if (entry.transport !== "http" || typeof entry.ref !== "string") return entry;
|
|
7984
|
+
let url;
|
|
7985
|
+
try {
|
|
7986
|
+
url = new URL(entry.ref);
|
|
7987
|
+
} catch {
|
|
7988
|
+
return entry;
|
|
7989
|
+
}
|
|
7990
|
+
if (url.searchParams.get("callerSessionId") !== prevId) return entry;
|
|
7991
|
+
url.searchParams.delete("callerSessionId");
|
|
7992
|
+
return { ...entry, ref: url.toString() };
|
|
7993
|
+
});
|
|
7994
|
+
}
|
|
7940
7995
|
function formatPostureForSpawn(desc) {
|
|
7941
7996
|
return desc.posture;
|
|
7942
7997
|
}
|
|
@@ -7966,7 +8021,7 @@ async function continueAgentSessionFresh(deps2, prev, opts = {}) {
|
|
|
7966
8021
|
access: formatAccessForSpawn(prev),
|
|
7967
8022
|
posture: formatPostureForSpawn(prev),
|
|
7968
8023
|
contextProfile: prev.contextProfile,
|
|
7969
|
-
mcpServers: prev.mcpServers,
|
|
8024
|
+
mcpServers: stripOwnCallerStamp(prev.mcpServers, prev.id),
|
|
7970
8025
|
label: prev.label ? `${prev.label} (continued)` : void 0,
|
|
7971
8026
|
title: prev.title ? `${prev.title} (continued)` : void 0,
|
|
7972
8027
|
contextContinuity: opts.contextContinuity ?? prev.contextContinuity,
|
|
@@ -8175,6 +8230,14 @@ function stampProcessAlive(desc) {
|
|
|
8175
8230
|
desc.processAlive = false;
|
|
8176
8231
|
}
|
|
8177
8232
|
}
|
|
8233
|
+
function killChildIfSpawned(child, signal) {
|
|
8234
|
+
if (!child) return;
|
|
8235
|
+
if (typeof child.pid !== "number" || child.pid <= 0) return;
|
|
8236
|
+
try {
|
|
8237
|
+
child.kill(signal);
|
|
8238
|
+
} catch {
|
|
8239
|
+
}
|
|
8240
|
+
}
|
|
8178
8241
|
function stampInterrupted(desc) {
|
|
8179
8242
|
if (desc.killedMidTurn === true && desc.endedReason === "daemon-restart") {
|
|
8180
8243
|
desc.interrupted = true;
|
|
@@ -8544,7 +8607,14 @@ function createSessionsRegistry(opts) {
|
|
|
8544
8607
|
if (!adapterSlug || !adapterSessionId || !cwd) return;
|
|
8545
8608
|
void (async () => {
|
|
8546
8609
|
try {
|
|
8547
|
-
const native = await resolveNativeLink({
|
|
8610
|
+
const native = await resolveNativeLink({
|
|
8611
|
+
cwd,
|
|
8612
|
+
adapterSlug,
|
|
8613
|
+
adapterSessionId,
|
|
8614
|
+
// Config-dir-isolated session (#824): the native transcript lives
|
|
8615
|
+
// under the session's own CLAUDE_CONFIG_DIR, not ~/.claude.
|
|
8616
|
+
...desc.adapterConfigDir ? { adapterConfigDir: desc.adapterConfigDir } : {}
|
|
8617
|
+
});
|
|
8548
8618
|
const registered = readRegisteredSlugs(workspacesConfigPath);
|
|
8549
8619
|
const slug = resolveBucketSlug(desc.workspaceSlug, registered);
|
|
8550
8620
|
const record2 = {
|
|
@@ -8553,6 +8623,7 @@ function createSessionsRegistry(opts) {
|
|
|
8553
8623
|
cwd,
|
|
8554
8624
|
adapterSlug,
|
|
8555
8625
|
adapterSessionId,
|
|
8626
|
+
...desc.adapterConfigDir ? { adapterConfigDir: desc.adapterConfigDir } : {},
|
|
8556
8627
|
...native ? { native } : {},
|
|
8557
8628
|
agentprotoTranscript: sessionEventsPath(desc.id, transcriptBaseDir),
|
|
8558
8629
|
...desc.title ? { title: desc.title } : {},
|
|
@@ -8568,7 +8639,7 @@ function createSessionsRegistry(opts) {
|
|
|
8568
8639
|
})();
|
|
8569
8640
|
};
|
|
8570
8641
|
const schedulePersist = () => {
|
|
8571
|
-
if (!persist) return;
|
|
8642
|
+
if (!persist || shutdownDone) return;
|
|
8572
8643
|
if (persistTimer) clearTimeout(persistTimer);
|
|
8573
8644
|
persistTimer = setTimeout(() => {
|
|
8574
8645
|
void persistSnapshot();
|
|
@@ -8604,11 +8675,13 @@ function createSessionsRegistry(opts) {
|
|
|
8604
8675
|
rows,
|
|
8605
8676
|
heldIdsByBucket.get(slug) ?? EMPTY_ID_SET
|
|
8606
8677
|
);
|
|
8607
|
-
const
|
|
8678
|
+
const persistOnce = async () => {
|
|
8608
8679
|
try {
|
|
8680
|
+
if (shutdownDone) return;
|
|
8609
8681
|
const savedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8610
8682
|
if (partitioned) {
|
|
8611
8683
|
for (const [slug, rows] of groupRowsByBucket()) {
|
|
8684
|
+
if (shutdownDone) return;
|
|
8612
8685
|
await writeBucketSnapshot(bucketsRoot, slug, {
|
|
8613
8686
|
savedAt,
|
|
8614
8687
|
sessions: rowsToWrite(slug, rows)
|
|
@@ -8625,6 +8698,24 @@ function createSessionsRegistry(opts) {
|
|
|
8625
8698
|
);
|
|
8626
8699
|
}
|
|
8627
8700
|
};
|
|
8701
|
+
let persistRunning = false;
|
|
8702
|
+
let persistRerun = false;
|
|
8703
|
+
const persistSnapshot = async () => {
|
|
8704
|
+
if (shutdownDone) return;
|
|
8705
|
+
if (persistRunning) {
|
|
8706
|
+
persistRerun = true;
|
|
8707
|
+
return;
|
|
8708
|
+
}
|
|
8709
|
+
persistRunning = true;
|
|
8710
|
+
try {
|
|
8711
|
+
do {
|
|
8712
|
+
persistRerun = false;
|
|
8713
|
+
await persistOnce();
|
|
8714
|
+
} while (persistRerun && !shutdownDone);
|
|
8715
|
+
} finally {
|
|
8716
|
+
persistRunning = false;
|
|
8717
|
+
}
|
|
8718
|
+
};
|
|
8628
8719
|
const appendLine = (rt, line, stream) => {
|
|
8629
8720
|
rt.recentLines.push(line);
|
|
8630
8721
|
if (rt.recentLines.length > RECENT_LINES_CAP) {
|
|
@@ -10524,7 +10615,7 @@ ${message}`;
|
|
|
10524
10615
|
}
|
|
10525
10616
|
void terminalTranscriptWriter.close(rt.desc.id);
|
|
10526
10617
|
}
|
|
10527
|
-
rt.child
|
|
10618
|
+
killChildIfSpawned(rt.child, signal);
|
|
10528
10619
|
if (rt.browserStop) {
|
|
10529
10620
|
void rt.browserStop().catch(() => void 0);
|
|
10530
10621
|
}
|
|
@@ -10549,7 +10640,7 @@ ${message}`;
|
|
|
10549
10640
|
tracedSessions.delete(rt.desc.id);
|
|
10550
10641
|
rt.agentSession = void 0;
|
|
10551
10642
|
}
|
|
10552
|
-
rt.child
|
|
10643
|
+
killChildIfSpawned(rt.child, "SIGTERM");
|
|
10553
10644
|
schedulePersist();
|
|
10554
10645
|
const banner = "\u2500\u2500 reaped after being idle past the threshold; the adapter process was freed. Re-prompt to resume this session in place. \u2500\u2500";
|
|
10555
10646
|
appendLine(rt, banner, "stdout");
|
|
@@ -10830,7 +10921,7 @@ ${message}`;
|
|
|
10830
10921
|
} catch {
|
|
10831
10922
|
}
|
|
10832
10923
|
}
|
|
10833
|
-
rt.child
|
|
10924
|
+
killChildIfSpawned(rt.child, "SIGTERM");
|
|
10834
10925
|
emitExited(rt);
|
|
10835
10926
|
}
|
|
10836
10927
|
}
|
|
@@ -10876,9 +10967,17 @@ function loadHistorySnapshot(persistPath, sessions, sessionEvents, bucketSlug, s
|
|
|
10876
10967
|
try {
|
|
10877
10968
|
parsed = JSON.parse(raw);
|
|
10878
10969
|
} catch {
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10970
|
+
const quarantine = `${persistPath}.corrupt-${Date.now()}`;
|
|
10971
|
+
try {
|
|
10972
|
+
renameSync(persistPath, quarantine);
|
|
10973
|
+
console.warn(
|
|
10974
|
+
`[sessions] history file ${persistPath} is malformed \u2014 quarantined to ${quarantine} and starting this bucket empty`
|
|
10975
|
+
);
|
|
10976
|
+
} catch {
|
|
10977
|
+
console.warn(
|
|
10978
|
+
`[sessions] history file ${persistPath} is malformed and could not be quarantined \u2014 ignoring`
|
|
10979
|
+
);
|
|
10980
|
+
}
|
|
10882
10981
|
return;
|
|
10883
10982
|
}
|
|
10884
10983
|
if (!Array.isArray(parsed.sessions)) return;
|
|
@@ -10939,6 +11038,9 @@ function quoteArg(arg) {
|
|
|
10939
11038
|
if (/^[a-zA-Z0-9._/=:@,+-]+$/.test(arg)) return arg;
|
|
10940
11039
|
return `"${arg.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
10941
11040
|
}
|
|
11041
|
+
|
|
11042
|
+
// src/pr-provenance-stamp.ts
|
|
11043
|
+
init_pr_provenance();
|
|
10942
11044
|
async function stampPrProvenance(input) {
|
|
10943
11045
|
try {
|
|
10944
11046
|
if (input.exitCode !== 0) return { stamped: false, reason: "command failed" };
|
|
@@ -10972,19 +11074,19 @@ async function stampFooterOnPr(input) {
|
|
|
10972
11074
|
const view = await run(["pr", "view", input.prUrl, "--json", "body", "--jq", ".body"], input.cwd);
|
|
10973
11075
|
if (view.exitCode !== 0) return { stamped: false, reason: `gh pr view exit ${view.exitCode}` };
|
|
10974
11076
|
const body = view.stdout.replace(/\n+$/, "");
|
|
10975
|
-
const alreadyStamped = body
|
|
11077
|
+
const alreadyStamped = hasProvenanceFooter(body);
|
|
10976
11078
|
if (!alreadyStamped) {
|
|
10977
11079
|
const newBody = appendFooterOnce(body, footer);
|
|
10978
11080
|
const edit = await run(["pr", "edit", input.prUrl, "--body", newBody], input.cwd);
|
|
10979
11081
|
if (edit.exitCode !== 0) {
|
|
10980
11082
|
return { stamped: false, reason: `gh pr edit exit ${edit.exitCode}` };
|
|
10981
11083
|
}
|
|
11084
|
+
input.registry.recordOpenedPr(input.session.id, {
|
|
11085
|
+
adapter: input.session.harness ?? input.session.adapterSlug ?? "gh",
|
|
11086
|
+
number: input.prNumber,
|
|
11087
|
+
url: input.prUrl
|
|
11088
|
+
});
|
|
10982
11089
|
}
|
|
10983
|
-
input.registry.recordOpenedPr(input.session.id, {
|
|
10984
|
-
adapter: input.session.harness ?? input.session.adapterSlug ?? "gh",
|
|
10985
|
-
number: input.prNumber,
|
|
10986
|
-
url: input.prUrl
|
|
10987
|
-
});
|
|
10988
11090
|
return {
|
|
10989
11091
|
stamped: true,
|
|
10990
11092
|
url: input.prUrl,
|
|
@@ -13719,7 +13821,10 @@ async function resolveConversationId(desc) {
|
|
|
13719
13821
|
// A live/ongoing session has no endedAt yet and must keep matching
|
|
13720
13822
|
// with no upper bound — only a dead session's endedAt narrows.
|
|
13721
13823
|
...desc.endedAt ? { until: desc.endedAt } : {},
|
|
13722
|
-
...attachmentMode ? { attachmentMode } : {}
|
|
13824
|
+
...attachmentMode ? { attachmentMode } : {},
|
|
13825
|
+
// A config-dir-isolated session's store lives under its own
|
|
13826
|
+
// CLAUDE_CONFIG_DIR (#824), not the provider's global dir.
|
|
13827
|
+
...desc.adapterConfigDir ? { configDir: desc.adapterConfigDir } : {}
|
|
13723
13828
|
});
|
|
13724
13829
|
if (candidates.length === 0) {
|
|
13725
13830
|
return {
|
|
@@ -13778,7 +13883,7 @@ async function readConversation(registry, input) {
|
|
|
13778
13883
|
});
|
|
13779
13884
|
}
|
|
13780
13885
|
try {
|
|
13781
|
-
const session = await store.read(resolved.conversationId, desc.cwd);
|
|
13886
|
+
const session = await store.read(resolved.conversationId, desc.cwd, desc.adapterConfigDir);
|
|
13782
13887
|
const content = format === "json" ? renderJson(session) : renderMarkdown(session);
|
|
13783
13888
|
return {
|
|
13784
13889
|
conversation: session,
|
|
@@ -16006,7 +16111,7 @@ function registerBrainTools(server, opts) {
|
|
|
16006
16111
|
);
|
|
16007
16112
|
server.tool(
|
|
16008
16113
|
"workspace_brain_status",
|
|
16009
|
-
"Report how current a workspace's brain is: how many sessions are indexed, how many known-but-pending remain, total bytes, and whether the index is query-ready. Resolves the workspace as the explicit `workspace` argument, else the calling session's own workspace.",
|
|
16114
|
+
"Report how current a workspace's brain is: how many sessions are indexed, how many known-but-pending remain, how many were skipped as permanently unavailable (no transcript ever reachable \u2014 bash PTYs, GC'd transcripts; retry one explicitly with `workspace_brain_ingest`'s `sessionId`), total bytes, and whether the index is query-ready. Resolves the workspace as the explicit `workspace` argument, else the calling session's own workspace.",
|
|
16010
16115
|
{
|
|
16011
16116
|
workspace: z.string().optional().describe("Workspace slug. Omit to use the calling session's workspace.")
|
|
16012
16117
|
},
|
|
@@ -16088,7 +16193,7 @@ async function readSessionForBrain(sessionId) {
|
|
|
16088
16193
|
const store = CONVERSATION_STORES[record2.adapterSlug];
|
|
16089
16194
|
if (store) {
|
|
16090
16195
|
try {
|
|
16091
|
-
const exported = await store.read(record2.adapterSessionId, record2.cwd);
|
|
16196
|
+
const exported = await store.read(record2.adapterSessionId, record2.cwd, record2.adapterConfigDir);
|
|
16092
16197
|
if (exported?.messages?.length) return exported;
|
|
16093
16198
|
} catch {
|
|
16094
16199
|
}
|
|
@@ -19411,9 +19516,11 @@ ${panelBridgeScript("agentproto-live-session")}
|
|
|
19411
19516
|
// ============================================================
|
|
19412
19517
|
// INLINED REDUCER COPY \u2014 hand-kept mirror of live-session-app.logic.ts.
|
|
19413
19518
|
// Plain JS, same semantics: coalesce consecutive text-delta of the same
|
|
19414
|
-
// session
|
|
19415
|
-
//
|
|
19416
|
-
//
|
|
19519
|
+
// session (and rejoin an unterminated mid-line fragment split by an
|
|
19520
|
+
// interleaved record \u2014 see the TS module's text-delta arm), pair
|
|
19521
|
+
// tool-call/tool-result by toolCallId, pass through turn-end/usage_update,
|
|
19522
|
+
// ignore unknown kinds. Keep in sync with the TS module; the TS module is
|
|
19523
|
+
// the one the test suite imports.
|
|
19417
19524
|
// ============================================================
|
|
19418
19525
|
|
|
19419
19526
|
function initialTimelineState() {
|
|
@@ -19424,22 +19531,50 @@ function rowId(record, rows) {
|
|
|
19424
19531
|
return record.seq != null ? (record.kind + '-' + record.seq) : (record.kind + '-' + rows.length);
|
|
19425
19532
|
}
|
|
19426
19533
|
|
|
19534
|
+
// Fold a text-delta record into an existing row (fresh object), keeping the
|
|
19535
|
+
// row's "partial" hint in step with the latest record \u2014 see mergeTextDelta in
|
|
19536
|
+
// the TS module.
|
|
19537
|
+
function mergeTextDelta(row, record) {
|
|
19538
|
+
var merged = Object.assign({}, row, {
|
|
19539
|
+
text: row.text + (record.text || ''),
|
|
19540
|
+
seq: record.seq,
|
|
19541
|
+
ts: record.ts,
|
|
19542
|
+
});
|
|
19543
|
+
if (record.partial === true) merged.partial = true;
|
|
19544
|
+
else delete merged.partial;
|
|
19545
|
+
return merged;
|
|
19546
|
+
}
|
|
19547
|
+
|
|
19427
19548
|
function reduceEvent(state, record) {
|
|
19428
19549
|
switch (record.kind) {
|
|
19429
19550
|
case 'text-delta': {
|
|
19430
19551
|
var last = state.rows[state.rows.length - 1];
|
|
19431
19552
|
if (last && last.kind === 'text' && last.sessionId === record.sessionId) {
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
|
|
19436
|
-
|
|
19437
|
-
|
|
19553
|
+
return { rows: state.rows.slice(0, -1).concat([mergeTextDelta(last, record)]) };
|
|
19554
|
+
}
|
|
19555
|
+
// Debounce can flush an unterminated mid-word fragment, let a tool-call
|
|
19556
|
+
// land, then flush the continuation. Terminated lines carry their
|
|
19557
|
+
// trailing newline (transcript-writer contract) \u2014 look back within the
|
|
19558
|
+
// same turn (bounded by this session's last turn-end) for that
|
|
19559
|
+
// session's most recent text row; if unterminated (or flagged
|
|
19560
|
+
// partial), continue it in place instead of splitting the sentence.
|
|
19561
|
+
for (var i = state.rows.length - 1; i >= 0; i--) {
|
|
19562
|
+
var prior = state.rows[i];
|
|
19563
|
+
if (prior.sessionId !== record.sessionId) continue;
|
|
19564
|
+
if (prior.kind === 'turn-end') break;
|
|
19565
|
+
if (prior.kind !== 'text') continue;
|
|
19566
|
+
if (prior.partial === true || !prior.text.endsWith('\\n')) {
|
|
19567
|
+
var patched = state.rows.slice();
|
|
19568
|
+
patched[i] = mergeTextDelta(prior, record);
|
|
19569
|
+
return { rows: patched };
|
|
19570
|
+
}
|
|
19571
|
+
break;
|
|
19438
19572
|
}
|
|
19439
19573
|
var row = {
|
|
19440
19574
|
kind: 'text', id: rowId(record, state.rows), seq: record.seq, ts: record.ts,
|
|
19441
19575
|
sessionId: record.sessionId, text: record.text || '',
|
|
19442
19576
|
};
|
|
19577
|
+
if (record.partial === true) row.partial = true;
|
|
19443
19578
|
return { rows: state.rows.concat([row]) };
|
|
19444
19579
|
}
|
|
19445
19580
|
case 'tool-call': {
|
|
@@ -24384,6 +24519,7 @@ async function startHttpServer(opts) {
|
|
|
24384
24519
|
// node+entry pair launchd (or the shell) exec'd. Lifecycle tooling
|
|
24385
24520
|
// (`agentproto daemon start/stop/status`) reports these.
|
|
24386
24521
|
version: opts.meta.version ?? null,
|
|
24522
|
+
build: opts.meta.build ?? null,
|
|
24387
24523
|
pid: process.pid,
|
|
24388
24524
|
node: process.execPath,
|
|
24389
24525
|
entry: process.argv[1] ?? null,
|
|
@@ -30148,25 +30284,38 @@ function createPrProvenanceReconciler(opts) {
|
|
|
30148
30284
|
if (last !== void 0 && Date.now() - last < POLL_THROTTLE_MS) return;
|
|
30149
30285
|
lastPollAt.set(sessionId, Date.now());
|
|
30150
30286
|
}
|
|
30287
|
+
const shouldStamp = (url) => {
|
|
30288
|
+
if (stampedPrUrls.has(url)) return false;
|
|
30289
|
+
if (desc.openedPrs && desc.openedPrs.some((recorded) => recorded.url === url)) {
|
|
30290
|
+
stampedPrUrls.add(url);
|
|
30291
|
+
return false;
|
|
30292
|
+
}
|
|
30293
|
+
return true;
|
|
30294
|
+
};
|
|
30295
|
+
const supervisor = desc.parentSessionId != null ? opts.registry.get(desc.parentSessionId) ?? { id: desc.parentSessionId } : null;
|
|
30296
|
+
const stamp = async (pr2) => {
|
|
30297
|
+
const outcome = await stampFooterOnPr({
|
|
30298
|
+
registry: opts.registry,
|
|
30299
|
+
session: desc,
|
|
30300
|
+
supervisor,
|
|
30301
|
+
prNumber: pr2.number,
|
|
30302
|
+
prUrl: pr2.url,
|
|
30303
|
+
cwd,
|
|
30304
|
+
...opts.run ? { run: opts.run } : {},
|
|
30305
|
+
...opts.host ? { host: opts.host } : {}
|
|
30306
|
+
});
|
|
30307
|
+
if (outcome.stamped) stampedPrUrls.add(pr2.url);
|
|
30308
|
+
};
|
|
30309
|
+
const records = await (opts.listToolCalls ?? readToolCallRecords)(sessionId);
|
|
30310
|
+
for (const record2 of records) {
|
|
30311
|
+
if (!record2.createdPrUrl || record2.createdPrNumber === void 0) continue;
|
|
30312
|
+
if (!shouldStamp(record2.createdPrUrl)) continue;
|
|
30313
|
+
await stamp({ number: record2.createdPrNumber, url: record2.createdPrUrl });
|
|
30314
|
+
}
|
|
30151
30315
|
const pr = await opts.resolveOpenPr(cwd);
|
|
30152
30316
|
if (!pr) return;
|
|
30153
|
-
if (
|
|
30154
|
-
|
|
30155
|
-
stampedPrUrls.add(pr.url);
|
|
30156
|
-
return;
|
|
30157
|
-
}
|
|
30158
|
-
const supervisor = desc.parentSessionId != null ? opts.registry.get(desc.parentSessionId) ?? { id: desc.parentSessionId } : null;
|
|
30159
|
-
const outcome = await stampFooterOnPr({
|
|
30160
|
-
registry: opts.registry,
|
|
30161
|
-
session: desc,
|
|
30162
|
-
supervisor,
|
|
30163
|
-
prNumber: pr.number,
|
|
30164
|
-
prUrl: pr.url,
|
|
30165
|
-
cwd,
|
|
30166
|
-
...opts.run ? { run: opts.run } : {},
|
|
30167
|
-
...opts.host ? { host: opts.host } : {}
|
|
30168
|
-
});
|
|
30169
|
-
if (outcome.stamped) stampedPrUrls.add(pr.url);
|
|
30317
|
+
if (!shouldStamp(pr.url)) return;
|
|
30318
|
+
await stamp(pr);
|
|
30170
30319
|
};
|
|
30171
30320
|
const safeReconcile = (sessionId, terminal) => {
|
|
30172
30321
|
void reconcile(sessionId, terminal).catch(() => {
|
|
@@ -32424,7 +32573,9 @@ function registerDaemonHealthTools(server, opts) {
|
|
|
32424
32573
|
idleReapAfterMs: opts.idleReapAfterMs,
|
|
32425
32574
|
crashDetectIntervalMs: opts.crashDetectIntervalMs ?? 0,
|
|
32426
32575
|
restartSweepIntervalMs: opts.restartSweepIntervalMs ?? 0,
|
|
32427
|
-
turnStallAfterMs: opts.turnStallAfterMs ?? 0
|
|
32576
|
+
turnStallAfterMs: opts.turnStallAfterMs ?? 0,
|
|
32577
|
+
version: opts.version ?? null,
|
|
32578
|
+
build: opts.build ?? null
|
|
32428
32579
|
});
|
|
32429
32580
|
}
|
|
32430
32581
|
);
|
|
@@ -34930,7 +35081,9 @@ async function createGateway(opts) {
|
|
|
34930
35081
|
idleReapAfterMs,
|
|
34931
35082
|
crashDetectIntervalMs,
|
|
34932
35083
|
restartSweepIntervalMs,
|
|
34933
|
-
turnStallAfterMs
|
|
35084
|
+
turnStallAfterMs,
|
|
35085
|
+
...opts.version ? { version: opts.version } : {},
|
|
35086
|
+
...opts.build ? { build: opts.build } : {}
|
|
34934
35087
|
});
|
|
34935
35088
|
registerCommandTools(server, {
|
|
34936
35089
|
workspace,
|
|
@@ -35234,6 +35387,7 @@ async function createGateway(opts) {
|
|
|
35234
35387
|
registered,
|
|
35235
35388
|
startedAt,
|
|
35236
35389
|
...opts.version ? { version: opts.version } : {},
|
|
35390
|
+
...opts.build ? { build: opts.build } : {},
|
|
35237
35391
|
resumeSessionsOnBoot: opts.resumeSessionsOnBoot === true,
|
|
35238
35392
|
idleReapAfterMs,
|
|
35239
35393
|
crashDetectIntervalMs,
|