@agentproto/runtime 1.1.0 → 2.0.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/README.md +3 -1
- package/dist/catalog-models.d.ts +255 -0
- package/dist/catalog-models.mjs +386 -0
- package/dist/catalog-models.mjs.map +1 -0
- package/dist/config.d.ts +378 -1
- package/dist/config.mjs.map +1 -1
- package/dist/context-continuity-B9n0t0v-.d.ts +53 -0
- package/dist/index.d.ts +6002 -2816
- package/dist/index.mjs +21719 -11296
- package/dist/index.mjs.map +1 -1
- package/dist/pr-provenance.d.ts +140 -0
- package/dist/pr-provenance.mjs +106 -0
- package/dist/pr-provenance.mjs.map +1 -0
- package/dist/resume-strategies.d.ts +39 -2
- package/dist/resume-strategies.mjs +765 -28
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/session-config-DIf6wYYP.d.ts +192 -0
- package/dist/session-story-panel.d.ts +26 -0
- package/dist/session-story-panel.mjs +970 -0
- package/dist/session-story-panel.mjs.map +1 -0
- package/dist/session-story.d.ts +12 -2
- package/dist/spawn-defaults-7uHRnYH1.d.ts +414 -0
- package/dist/telegram-proxy.d.ts +35 -0
- package/dist/telegram-proxy.mjs +115 -0
- package/dist/telegram-proxy.mjs.map +1 -0
- package/dist/user-presets.d.ts +91 -0
- package/dist/user-presets.mjs +84 -0
- package/dist/user-presets.mjs.map +1 -0
- package/package.json +48 -23
- package/dist/config-BRKy_SAF.d.ts +0 -569
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR provenance footer — daemon/MCP lane.
|
|
3
|
+
*
|
|
4
|
+
* This is the SAME footer the runner-side agentflow scripts stamp on the
|
|
5
|
+
* CI-PR, review, and local-`gh_open_pr` lanes; `scripts/lib/provenance-
|
|
6
|
+
* footer.mjs` re-exports {@link buildFooter}/{@link MARKER}/{@link fmtTokens}
|
|
7
|
+
* from HERE so there is exactly one format, one marker, one renderer. The
|
|
8
|
+
* only reason this canonical copy lives in `@agentproto/runtime` rather than
|
|
9
|
+
* in `scripts/` is direction of dependency: the daemon is a package and
|
|
10
|
+
* cannot import a loose script, but a script can import a package's built
|
|
11
|
+
* `dist/` (it already does for `computeProvenance`).
|
|
12
|
+
*
|
|
13
|
+
* What the DAEMON lane adds over the runner lanes: the footer is built from
|
|
14
|
+
* a live {@link SessionDescriptor} (the executor agent-cli session that ran
|
|
15
|
+
* `gh pr create`) plus its supervisor/parent session, rather than from a
|
|
16
|
+
* `computeProvenance` join over the worktree. So this file also owns the
|
|
17
|
+
* mapping from a session descriptor to the footer's provenance shape
|
|
18
|
+
* ({@link sessionFooterProvenance}) and the small pure helpers the
|
|
19
|
+
* command_execute stamp path needs ({@link parseGhPrCreate},
|
|
20
|
+
* {@link pickExecutorSession}, {@link appendFooterOnce}).
|
|
21
|
+
*
|
|
22
|
+
* Pure by construction — only `node:path`, no daemon imports, no I/O — so it
|
|
23
|
+
* bundles to a standalone `dist/pr-provenance.mjs` the scripts can import
|
|
24
|
+
* without pulling the rest of the runtime in. Keep it that way. The
|
|
25
|
+
* side-effecting orchestration (spawning `gh`, editing the PR body, recording
|
|
26
|
+
* the opened PR) lives in `pr-provenance-stamp.ts`.
|
|
27
|
+
*/
|
|
28
|
+
/** Deterministic marker the runner owns — a reliable native-vs-legacy
|
|
29
|
+
* discriminator across every lane (CI-PR, review, local, daemon). */
|
|
30
|
+
declare const MARKER = "@agentproto-bot";
|
|
31
|
+
declare const fmtTokens: (n: unknown) => string | null;
|
|
32
|
+
/** The footer's provenance shape — field names are the renderer's, not the
|
|
33
|
+
* session registry's (see {@link sessionFooterProvenance} for the mapping). */
|
|
34
|
+
interface FooterProvenance {
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
label?: string;
|
|
37
|
+
/** Adapter / harness slug that ran ("claude-code", "codex", …). */
|
|
38
|
+
adapter?: string;
|
|
39
|
+
model?: string;
|
|
40
|
+
/** Bound auth-profile IDENTITY — a label or ref, NEVER the credential. */
|
|
41
|
+
authProfile?: string;
|
|
42
|
+
sandboxId?: string;
|
|
43
|
+
/** Supervisor / parent session that spawned the executor. */
|
|
44
|
+
parentSessionId?: string;
|
|
45
|
+
tokensIn?: number;
|
|
46
|
+
tokensOut?: number;
|
|
47
|
+
costUsd?: number;
|
|
48
|
+
/** "adapter" | "local" | "daemon" — drives host/cwd vs run-link rendering. */
|
|
49
|
+
source?: string;
|
|
50
|
+
host?: string;
|
|
51
|
+
cwd?: string;
|
|
52
|
+
}
|
|
53
|
+
interface BuildFooterInput {
|
|
54
|
+
prov?: FooterProvenance;
|
|
55
|
+
authMode?: string;
|
|
56
|
+
runId?: string;
|
|
57
|
+
runUrl?: string;
|
|
58
|
+
sha?: string;
|
|
59
|
+
kind?: string;
|
|
60
|
+
}
|
|
61
|
+
declare const buildFooter: ({ prov, authMode, runId, runUrl, sha, kind, }: BuildFooterInput) => string;
|
|
62
|
+
/** Structural subset of `SessionDescriptor` the footer needs. The runtime's
|
|
63
|
+
* full descriptor satisfies this; keeping it structural is what lets this
|
|
64
|
+
* module stay free of a `./sessions.js` import (and thus standalone). */
|
|
65
|
+
interface FooterSession {
|
|
66
|
+
id: string;
|
|
67
|
+
kind?: string;
|
|
68
|
+
status?: string;
|
|
69
|
+
startedAt?: string;
|
|
70
|
+
label?: string;
|
|
71
|
+
cwd?: string;
|
|
72
|
+
adapterSlug?: string;
|
|
73
|
+
harness?: string;
|
|
74
|
+
model?: string;
|
|
75
|
+
parentSessionId?: string;
|
|
76
|
+
costUsd?: number;
|
|
77
|
+
tokensIn?: number;
|
|
78
|
+
tokensOut?: number;
|
|
79
|
+
auth?: {
|
|
80
|
+
mode?: string;
|
|
81
|
+
};
|
|
82
|
+
accessProfile?: {
|
|
83
|
+
profileRef: string;
|
|
84
|
+
label?: string;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
interface SessionFooterOptions {
|
|
88
|
+
/** The supervisor/parent session descriptor, when resolvable. */
|
|
89
|
+
supervisor?: {
|
|
90
|
+
id: string;
|
|
91
|
+
} | null;
|
|
92
|
+
host?: string;
|
|
93
|
+
/** Provenance source tag; defaults to "daemon". */
|
|
94
|
+
source?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Map a live executor session (+ its supervisor) onto the footer's provenance
|
|
98
|
+
* shape and the `authMode` the renderer takes separately. The adapter is the
|
|
99
|
+
* canonical harness slug, falling back to `adapterSlug`. The bound auth
|
|
100
|
+
* profile is the profile's human label (or its ref), never the credential.
|
|
101
|
+
*/
|
|
102
|
+
declare function sessionFooterProvenance(session: FooterSession, options?: SessionFooterOptions): {
|
|
103
|
+
prov: FooterProvenance;
|
|
104
|
+
authMode: string | undefined;
|
|
105
|
+
};
|
|
106
|
+
/** Build the daemon-lane PR footer for one executor session directly. */
|
|
107
|
+
declare function buildSessionPrFooter(session: FooterSession, options?: SessionFooterOptions & {
|
|
108
|
+
sha?: string;
|
|
109
|
+
}): string;
|
|
110
|
+
/**
|
|
111
|
+
* Append the footer to a PR body exactly once. Idempotent by the marker: a
|
|
112
|
+
* body that already carries a `@agentproto-bot` footer is returned unchanged,
|
|
113
|
+
* so a retried stamp (network reply lost after the edit landed) never stacks
|
|
114
|
+
* a second footer.
|
|
115
|
+
*/
|
|
116
|
+
declare function appendFooterOnce(body: string, footer: string): string;
|
|
117
|
+
/**
|
|
118
|
+
* Recognise a successful `gh pr create` from its argv + stdout and pull the
|
|
119
|
+
* created PR's URL + number out. Returns null for anything that isn't a
|
|
120
|
+
* PR-creating `gh` invocation, or a create whose stdout carried no PR URL.
|
|
121
|
+
*
|
|
122
|
+
* `gh pr create` prints the PR URL on its own line (often the last line,
|
|
123
|
+
* after any advisory notices) — we take the LAST `…/pull/<n>` match so a
|
|
124
|
+
* "Warning: …/pull/…" style notice can't shadow the real one.
|
|
125
|
+
*/
|
|
126
|
+
declare function parseGhPrCreate(command: string, args: readonly string[], stdout: string): {
|
|
127
|
+
url: string;
|
|
128
|
+
number: number;
|
|
129
|
+
} | null;
|
|
130
|
+
/**
|
|
131
|
+
* Best-effort attribution of a `command_execute` run to the executor agent
|
|
132
|
+
* session that most likely issued it: the newest agent-cli session whose cwd
|
|
133
|
+
* is related to the command's cwd, preferring a still-alive one. Returns
|
|
134
|
+
* undefined when no agent-cli session matches (e.g. a bare shell opened the
|
|
135
|
+
* PR) — the caller then skips stamping rather than emit a misleading "legacy
|
|
136
|
+
* fallback" footer.
|
|
137
|
+
*/
|
|
138
|
+
declare function pickExecutorSession<T extends FooterSession>(sessions: readonly T[], cwd: string): T | undefined;
|
|
139
|
+
|
|
140
|
+
export { type BuildFooterInput, type FooterProvenance, type FooterSession, MARKER, type SessionFooterOptions, appendFooterOnce, buildFooter, buildSessionPrFooter, fmtTokens, parseGhPrCreate, pickExecutorSession, sessionFooterProvenance };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { basename } from 'path';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/runtime v0.1.0-alpha
|
|
5
|
+
* Long-running gateway: MCP server + HTTP transport + HEARTBEAT autonomy + conversation persistence over a workspace dir.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
var MARKER = "@agentproto-bot";
|
|
9
|
+
var fmtTokens = (n) => {
|
|
10
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
11
|
+
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
12
|
+
};
|
|
13
|
+
var buildFooter = ({
|
|
14
|
+
prov,
|
|
15
|
+
authMode,
|
|
16
|
+
runId,
|
|
17
|
+
runUrl,
|
|
18
|
+
sha,
|
|
19
|
+
kind = "review"
|
|
20
|
+
}) => {
|
|
21
|
+
const parts = [`\u{1F916} **${MARKER}** \u2014 ${kind}`];
|
|
22
|
+
if (prov?.sessionId) {
|
|
23
|
+
parts.push(`session \`${prov.sessionId}\`${prov.label ? ` (\`${prov.label}\`)` : ""}`);
|
|
24
|
+
}
|
|
25
|
+
if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(" / "));
|
|
26
|
+
else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : ""}`);
|
|
27
|
+
else if (authMode) parts.push(authMode);
|
|
28
|
+
if (prov?.authProfile) parts.push(`auth-profile \`${prov.authProfile}\``);
|
|
29
|
+
if (prov?.model) parts.push(`model \`${prov.model}\``);
|
|
30
|
+
if (prov?.sandboxId) parts.push(`e2b \`${prov.sandboxId}\``);
|
|
31
|
+
if (prov?.parentSessionId) parts.push(`supervisor \`${prov.parentSessionId}\``);
|
|
32
|
+
const tin = fmtTokens(prov?.tokensIn);
|
|
33
|
+
const tout = fmtTokens(prov?.tokensOut);
|
|
34
|
+
if (tin || tout) parts.push(`${tin ?? "?"} in / ${tout ?? "?"} out`);
|
|
35
|
+
if (typeof prov?.costUsd === "number") {
|
|
36
|
+
parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== "adapter" ? ` (${prov.source})` : ""}`);
|
|
37
|
+
}
|
|
38
|
+
const showLocalHostCwd = prov?.source === "local" || prov?.source === "daemon" || !runId;
|
|
39
|
+
if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`);
|
|
40
|
+
if (showLocalHostCwd) {
|
|
41
|
+
if (prov?.host) parts.push(`host \`${prov.host}\``);
|
|
42
|
+
if (prov?.cwd) parts.push(`cwd \`${basename(prov.cwd)}\``);
|
|
43
|
+
}
|
|
44
|
+
if (sha) parts.push(`sha \`${sha.slice(0, 7)}\``);
|
|
45
|
+
return `
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
<sub>${parts.join(" \xB7 ")}</sub>`;
|
|
49
|
+
};
|
|
50
|
+
function sessionFooterProvenance(session, options = {}) {
|
|
51
|
+
const prov = {
|
|
52
|
+
sessionId: session.id,
|
|
53
|
+
label: session.label,
|
|
54
|
+
adapter: session.harness ?? session.adapterSlug,
|
|
55
|
+
model: session.model,
|
|
56
|
+
authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,
|
|
57
|
+
parentSessionId: options.supervisor?.id,
|
|
58
|
+
costUsd: session.costUsd,
|
|
59
|
+
tokensIn: session.tokensIn,
|
|
60
|
+
tokensOut: session.tokensOut,
|
|
61
|
+
source: options.source ?? "daemon",
|
|
62
|
+
host: options.host,
|
|
63
|
+
cwd: session.cwd
|
|
64
|
+
};
|
|
65
|
+
return { prov, authMode: session.auth?.mode };
|
|
66
|
+
}
|
|
67
|
+
function buildSessionPrFooter(session, options = {}) {
|
|
68
|
+
const { prov, authMode } = sessionFooterProvenance(session, options);
|
|
69
|
+
return buildFooter({ prov, authMode, sha: options.sha, kind: "PR" });
|
|
70
|
+
}
|
|
71
|
+
function appendFooterOnce(body, footer) {
|
|
72
|
+
if (body.includes(MARKER)) return body;
|
|
73
|
+
return `${body}${footer}`;
|
|
74
|
+
}
|
|
75
|
+
function parseGhPrCreate(command, args, stdout) {
|
|
76
|
+
if (basename(command) !== "gh") return null;
|
|
77
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
78
|
+
if (positionals[0] !== "pr" || positionals[1] !== "create") return null;
|
|
79
|
+
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
80
|
+
let match;
|
|
81
|
+
let last = null;
|
|
82
|
+
while ((match = re.exec(stdout)) !== null) {
|
|
83
|
+
last = { url: match[0], number: Number(match[1]) };
|
|
84
|
+
}
|
|
85
|
+
return last;
|
|
86
|
+
}
|
|
87
|
+
function cwdRelated(sessionCwd, cwd) {
|
|
88
|
+
if (sessionCwd === cwd) return true;
|
|
89
|
+
const sep = "/";
|
|
90
|
+
return cwd.startsWith(sessionCwd + sep) || sessionCwd.startsWith(cwd + sep);
|
|
91
|
+
}
|
|
92
|
+
function pickExecutorSession(sessions, cwd) {
|
|
93
|
+
const candidates = sessions.filter(
|
|
94
|
+
(s) => s.kind === "agent-cli" && typeof s.cwd === "string" && cwdRelated(s.cwd, cwd)
|
|
95
|
+
);
|
|
96
|
+
if (candidates.length === 0) return void 0;
|
|
97
|
+
const alive = (s) => s.status === "running" || s.status === "starting";
|
|
98
|
+
const byRecency = (a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "");
|
|
99
|
+
const live = candidates.filter(alive).sort(byRecency);
|
|
100
|
+
if (live.length > 0) return live[0];
|
|
101
|
+
return [...candidates].sort(byRecency)[0];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { MARKER, appendFooterOnce, buildFooter, buildSessionPrFooter, fmtTokens, parseGhPrCreate, pickExecutorSession, sessionFooterProvenance };
|
|
105
|
+
//# sourceMappingURL=pr-provenance.mjs.map
|
|
106
|
+
//# sourceMappingURL=pr-provenance.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pr-provenance.ts"],"names":[],"mappings":"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","file":"pr-provenance.mjs","sourcesContent":["/**\n * PR provenance footer — daemon/MCP lane.\n *\n * This is the SAME footer the runner-side agentflow scripts stamp on the\n * CI-PR, review, and local-`gh_open_pr` lanes; `scripts/lib/provenance-\n * footer.mjs` re-exports {@link buildFooter}/{@link MARKER}/{@link fmtTokens}\n * from HERE so there is exactly one format, one marker, one renderer. The\n * only reason this canonical copy lives in `@agentproto/runtime` rather than\n * in `scripts/` is direction of dependency: the daemon is a package and\n * cannot import a loose script, but a script can import a package's built\n * `dist/` (it already does for `computeProvenance`).\n *\n * What the DAEMON lane adds over the runner lanes: the footer is built from\n * a live {@link SessionDescriptor} (the executor agent-cli session that ran\n * `gh pr create`) plus its supervisor/parent session, rather than from a\n * `computeProvenance` join over the worktree. So this file also owns the\n * mapping from a session descriptor to the footer's provenance shape\n * ({@link sessionFooterProvenance}) and the small pure helpers the\n * command_execute stamp path needs ({@link parseGhPrCreate},\n * {@link pickExecutorSession}, {@link appendFooterOnce}).\n *\n * Pure by construction — only `node:path`, no daemon imports, no I/O — so it\n * bundles to a standalone `dist/pr-provenance.mjs` the scripts can import\n * without pulling the rest of the runtime in. Keep it that way. The\n * side-effecting orchestration (spawning `gh`, editing the PR body, recording\n * the opened PR) lives in `pr-provenance-stamp.ts`.\n */\n\nimport { basename } from \"node:path\"\n\n/** Deterministic marker the runner owns — a reliable native-vs-legacy\n * discriminator across every lane (CI-PR, review, local, daemon). */\nexport const MARKER = \"@agentproto-bot\"\n\nexport const fmtTokens = (n: unknown): string | null => {\n if (typeof n !== \"number\" || !Number.isFinite(n)) return null\n return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n)\n}\n\n/** The footer's provenance shape — field names are the renderer's, not the\n * session registry's (see {@link sessionFooterProvenance} for the mapping). */\nexport interface FooterProvenance {\n sessionId?: string\n label?: string\n /** Adapter / harness slug that ran (\"claude-code\", \"codex\", …). */\n adapter?: string\n model?: string\n /** Bound auth-profile IDENTITY — a label or ref, NEVER the credential. */\n authProfile?: string\n sandboxId?: string\n /** Supervisor / parent session that spawned the executor. */\n parentSessionId?: string\n tokensIn?: number\n tokensOut?: number\n costUsd?: number\n /** \"adapter\" | \"local\" | \"daemon\" — drives host/cwd vs run-link rendering. */\n source?: string\n host?: string\n cwd?: string\n}\n\nexport interface BuildFooterInput {\n prov?: FooterProvenance\n authMode?: string\n runId?: string\n runUrl?: string\n sha?: string\n kind?: string\n}\n\nexport const buildFooter = ({\n prov,\n authMode,\n runId,\n runUrl,\n sha,\n kind = \"review\",\n}: BuildFooterInput): string => {\n const parts = [`🤖 **${MARKER}** — ${kind}`]\n if (prov?.sessionId) {\n parts.push(`session \\`${prov.sessionId}\\`${prov.label ? ` (\\`${prov.label}\\`)` : \"\"}`)\n }\n // Engine label: \"adapter / authMode\" when an adapter ran; \"legacy fallback\n // (authMode)\" when no agent session exists at all (the API-key fallback path);\n // bare authMode only if a session ran without a resolved adapter slug.\n if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(\" / \"))\n else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : \"\"}`)\n else if (authMode) parts.push(authMode)\n // Bound auth profile identity (the \"wallet\") — daemon lane; NEVER the secret.\n if (prov?.authProfile) parts.push(`auth-profile \\`${prov.authProfile}\\``)\n if (prov?.model) parts.push(`model \\`${prov.model}\\``)\n if (prov?.sandboxId) parts.push(`e2b \\`${prov.sandboxId}\\``)\n if (prov?.parentSessionId) parts.push(`supervisor \\`${prov.parentSessionId}\\``)\n const tin = fmtTokens(prov?.tokensIn)\n const tout = fmtTokens(prov?.tokensOut)\n if (tin || tout) parts.push(`${tin ?? \"?\"} in / ${tout ?? \"?\"} out`)\n if (typeof prov?.costUsd === \"number\") {\n parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== \"adapter\" ? ` (${prov.source})` : \"\"}`)\n }\n const showLocalHostCwd = prov?.source === \"local\" || prov?.source === \"daemon\" || !runId\n if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`)\n if (showLocalHostCwd) {\n if (prov?.host) parts.push(`host \\`${prov.host}\\``)\n if (prov?.cwd) parts.push(`cwd \\`${basename(prov.cwd)}\\``)\n }\n if (sha) parts.push(`sha \\`${sha.slice(0, 7)}\\``)\n return `\\n\\n---\\n<sub>${parts.join(\" · \")}</sub>`\n}\n\n/** Structural subset of `SessionDescriptor` the footer needs. The runtime's\n * full descriptor satisfies this; keeping it structural is what lets this\n * module stay free of a `./sessions.js` import (and thus standalone). */\nexport interface FooterSession {\n id: string\n kind?: string\n status?: string\n startedAt?: string\n label?: string\n cwd?: string\n adapterSlug?: string\n harness?: string\n model?: string\n parentSessionId?: string\n costUsd?: number\n tokensIn?: number\n tokensOut?: number\n auth?: { mode?: string }\n accessProfile?: { profileRef: string; label?: string }\n}\n\nexport interface SessionFooterOptions {\n /** The supervisor/parent session descriptor, when resolvable. */\n supervisor?: { id: string } | null\n host?: string\n /** Provenance source tag; defaults to \"daemon\". */\n source?: string\n}\n\n/**\n * Map a live executor session (+ its supervisor) onto the footer's provenance\n * shape and the `authMode` the renderer takes separately. The adapter is the\n * canonical harness slug, falling back to `adapterSlug`. The bound auth\n * profile is the profile's human label (or its ref), never the credential.\n */\nexport function sessionFooterProvenance(\n session: FooterSession,\n options: SessionFooterOptions = {},\n): { prov: FooterProvenance; authMode: string | undefined } {\n const prov: FooterProvenance = {\n sessionId: session.id,\n label: session.label,\n adapter: session.harness ?? session.adapterSlug,\n model: session.model,\n authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,\n parentSessionId: options.supervisor?.id,\n costUsd: session.costUsd,\n tokensIn: session.tokensIn,\n tokensOut: session.tokensOut,\n source: options.source ?? \"daemon\",\n host: options.host,\n cwd: session.cwd,\n }\n return { prov, authMode: session.auth?.mode }\n}\n\n/** Build the daemon-lane PR footer for one executor session directly. */\nexport function buildSessionPrFooter(\n session: FooterSession,\n options: SessionFooterOptions & { sha?: string } = {},\n): string {\n const { prov, authMode } = sessionFooterProvenance(session, options)\n return buildFooter({ prov, authMode, sha: options.sha, kind: \"PR\" })\n}\n\n/**\n * Append the footer to a PR body exactly once. Idempotent by the marker: a\n * body that already carries a `@agentproto-bot` footer is returned unchanged,\n * so a retried stamp (network reply lost after the edit landed) never stacks\n * a second footer.\n */\nexport function appendFooterOnce(body: string, footer: string): string {\n if (body.includes(MARKER)) return body\n return `${body}${footer}`\n}\n\n/**\n * Recognise a successful `gh pr create` from its argv + stdout and pull the\n * created PR's URL + number out. Returns null for anything that isn't a\n * PR-creating `gh` invocation, or a create whose stdout carried no PR URL.\n *\n * `gh pr create` prints the PR URL on its own line (often the last line,\n * after any advisory notices) — we take the LAST `…/pull/<n>` match so a\n * \"Warning: …/pull/…\" style notice can't shadow the real one.\n */\nexport function parseGhPrCreate(\n command: string,\n args: readonly string[],\n stdout: string,\n): { url: string; number: number } | null {\n if (basename(command) !== \"gh\") return null\n // Skip global flags; the first two non-flag tokens must be `pr` then `create`.\n const positionals = args.filter(a => !a.startsWith(\"-\"))\n if (positionals[0] !== \"pr\" || positionals[1] !== \"create\") return null\n const re = /https?:\\/\\/\\S+?\\/pull\\/(\\d+)/g\n let match: RegExpExecArray | null\n let last: { url: string; number: number } | null = null\n while ((match = re.exec(stdout)) !== null) {\n last = { url: match[0], number: Number(match[1]) }\n }\n return last\n}\n\n/** `session.cwd == cwd || one contains the other` — the executor's cwd is\n * usually the worktree root while the command may run in a subdir (or vice\n * versa), so containment is checked in both directions. */\nfunction cwdRelated(sessionCwd: string, cwd: string): boolean {\n if (sessionCwd === cwd) return true\n const sep = \"/\"\n return cwd.startsWith(sessionCwd + sep) || sessionCwd.startsWith(cwd + sep)\n}\n\n/**\n * Best-effort attribution of a `command_execute` run to the executor agent\n * session that most likely issued it: the newest agent-cli session whose cwd\n * is related to the command's cwd, preferring a still-alive one. Returns\n * undefined when no agent-cli session matches (e.g. a bare shell opened the\n * PR) — the caller then skips stamping rather than emit a misleading \"legacy\n * fallback\" footer.\n */\nexport function pickExecutorSession<T extends FooterSession>(\n sessions: readonly T[],\n cwd: string,\n): T | undefined {\n const candidates = sessions.filter(\n s => s.kind === \"agent-cli\" && typeof s.cwd === \"string\" && cwdRelated(s.cwd, cwd),\n )\n if (candidates.length === 0) return undefined\n const alive = (s: T) => s.status === \"running\" || s.status === \"starting\"\n const byRecency = (a: T, b: T) => (b.startedAt ?? \"\").localeCompare(a.startedAt ?? \"\")\n const live = candidates.filter(alive).sort(byRecency)\n if (live.length > 0) return live[0]\n return [...candidates].sort(byRecency)[0]\n}\n"]}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* graduate into the adapter packages.
|
|
35
35
|
*/
|
|
36
36
|
/** Where on disk the resume id ends up on the session descriptor. */
|
|
37
|
-
type ResumeMetadataKey = "claudeResumeId" | "hermesResumeId" | "codexResumeId" | "openClawResumeId" | "openCodeResumeId";
|
|
37
|
+
type ResumeMetadataKey = "claudeResumeId" | "hermesResumeId" | "codexResumeId" | "openClawResumeId" | "openCodeResumeId" | "mastracodeInprocessResumeId" | "piResumeId";
|
|
38
38
|
interface ResumeStrategy {
|
|
39
39
|
/** When set, run on every output line for this adapter. Group 1
|
|
40
40
|
* captures the resume id. The match is saved on the descriptor
|
|
@@ -80,6 +80,20 @@ interface RestartCandidate {
|
|
|
80
80
|
resumeMetadata?: Record<string, string>;
|
|
81
81
|
pty?: boolean;
|
|
82
82
|
adapterSessionId?: string;
|
|
83
|
+
/** Manifest-declared `capabilities.resumable` for `adapterSlug` (see
|
|
84
|
+
* `SessionDescriptor.resumable`'s doc) — the honesty gate for the whole
|
|
85
|
+
* decision tree below. `false` means an `adapterSessionId` here is a
|
|
86
|
+
* dead end: the adapter cannot rehydrate a prior conversation from it, no
|
|
87
|
+
* matter how it's presented. Omitted/`true` preserves today's behaviour
|
|
88
|
+
* (assumed resumable) for every adapter/row that doesn't set this. */
|
|
89
|
+
resumable?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Manifest-declared `capabilities.nativeTerminalResume` for `adapterSlug`.
|
|
92
|
+
* The `pty-native` restart strategy is only chosen when this flag is true
|
|
93
|
+
* AND a native resume id is available. ACP resumability (`resumable`)
|
|
94
|
+
* alone does NOT imply a native TUI resume is safe or available.
|
|
95
|
+
*/
|
|
96
|
+
nativeTerminalResume?: boolean;
|
|
83
97
|
}
|
|
84
98
|
interface FsProbeCandidate extends RestartCandidate {
|
|
85
99
|
cwd?: string;
|
|
@@ -110,11 +124,28 @@ type RestartStrategy = {
|
|
|
110
124
|
} | {
|
|
111
125
|
kind: "agent";
|
|
112
126
|
resumeSessionId?: string;
|
|
127
|
+
resumeFallback?: boolean;
|
|
113
128
|
} | {
|
|
114
129
|
kind: "unsupported";
|
|
115
130
|
reason: string;
|
|
116
131
|
};
|
|
117
132
|
declare function decideRestartStrategy(prev: RestartCandidate): RestartStrategy;
|
|
133
|
+
/**
|
|
134
|
+
* Matches an adapter's rejection of a `resumeSessionId` it can't honour —
|
|
135
|
+
* the trigger for the "retry as a fresh spawn" fallback in
|
|
136
|
+
* `session-restart-core.ts` and the CLI's `sessions restart`. Beyond a
|
|
137
|
+
* literal "not found" (an unknown/never-persisted id), this also covers the
|
|
138
|
+
* ACP wrapper-level rejection an adapter with `loadSession: false` throws
|
|
139
|
+
* when a generic `resumeSessionId` reaches it despite `capabilities.
|
|
140
|
+
* resumable: true` at the manifest level (e.g. claude-sdk — resumable via
|
|
141
|
+
* its own SDK session store, but no ACP `session/load` surface): the
|
|
142
|
+
* `@agentclientprotocol/sdk` server-side dispatcher throws a JSON-RPC
|
|
143
|
+
* "Method not found" for an unimplemented `session/load`, and an agent may
|
|
144
|
+
* instead reject with its own "does not support" / "not supported" wording.
|
|
145
|
+
* Shared by every fallback call site so they can't drift out of sync (see
|
|
146
|
+
* this module's own header comment on why that matters).
|
|
147
|
+
*/
|
|
148
|
+
declare const RESUME_ID_REJECTED_RE: RegExp;
|
|
118
149
|
/**
|
|
119
150
|
* Generic filesystem-fallback: for each known adapter strategy that
|
|
120
151
|
* defines an `fsProbe`, run it against this session's cwd. If a
|
|
@@ -131,6 +162,12 @@ declare function augmentWithFsResume<T extends FsProbeCandidate>(prev: T): Promi
|
|
|
131
162
|
* Describe which resume path a restart used, for CLI banners / MCP
|
|
132
163
|
* responses. Returns a short phrase like "resumed via claude --resume",
|
|
133
164
|
* or "resumed via ACP", or "" when no resume was attempted.
|
|
165
|
+
*
|
|
166
|
+
* Honesty gate (resume-honesty fix): "resumed via ACP" is a claim of actual
|
|
167
|
+
* conversation continuity, so it's only returned when the adapter's
|
|
168
|
+
* declared capability backs that claim (`resumable !== false`). An adapter
|
|
169
|
+
* that declared `resumable: false` but still carries an `adapterSessionId`
|
|
170
|
+
* gets an honest degraded label instead of the lie — never silently "".
|
|
134
171
|
*/
|
|
135
172
|
declare function describeResumePath(prev: RestartCandidate): string;
|
|
136
173
|
/**
|
|
@@ -141,4 +178,4 @@ declare function describeResumePath(prev: RestartCandidate): string;
|
|
|
141
178
|
*/
|
|
142
179
|
declare function tokenizeCommand(s: string): string[];
|
|
143
180
|
|
|
144
|
-
export { type FsProbeCandidate, RESUME_STRATEGIES, type RestartCandidate, type RestartStrategy, type ResumeMetadataKey, type ResumeStrategy, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, tokenizeCommand };
|
|
181
|
+
export { type FsProbeCandidate, RESUME_ID_REJECTED_RE, RESUME_STRATEGIES, type RestartCandidate, type RestartStrategy, type ResumeMetadataKey, type ResumeStrategy, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, tokenizeCommand };
|