@agentproto/runtime 3.4.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,6 +78,10 @@ interface ResumeStrategy {
78
78
  * why this exists and what silently omitting it breaks. Undefined for
79
79
  * a provider with no config-dir-isolated store. */
80
80
  configDirEnvVar?: string;
81
+ /** Absolute transcript path for a conversation id — see
82
+ * `ConversationStore.transcriptPath`. Present only when the provider's
83
+ * resume command also accepts a path in place of a bare id. */
84
+ transcriptPath?(cwd: string, conversationId: string, configDir?: string): string;
81
85
  }
82
86
  declare const RESUME_STRATEGIES: Record<string, ResumeStrategy>;
83
87
  /**
@@ -101,9 +105,10 @@ interface RestartCandidate {
101
105
  resumable?: boolean;
102
106
  /**
103
107
  * Manifest-declared `capabilities.nativeTerminalResume` for `adapterSlug`.
104
- * The `pty-native` restart strategy is only chosen when this flag is true
105
- * AND a native resume id is available. ACP resumability (`resumable`)
106
- * alone does NOT imply a native TUI resume is safe or available.
108
+ * The `pty-native` strategy requires this flag for ACP-origin sessions.
109
+ * Legacy PTY rows without the flag can use a newly-added store strategy
110
+ * when they already carry an exact native resume id; explicit false blocks
111
+ * that fallback. ACP resumability (`resumable`) does not imply a TUI resume.
107
112
  */
108
113
  nativeTerminalResume?: boolean;
109
114
  }
@@ -197,6 +202,30 @@ declare const RESUME_ID_REJECTED_RE: RegExp;
197
202
  * "not found", triggering the fresh-spawn fallback + digest injection.
198
203
  */
199
204
  declare function augmentWithFsResume<T extends FsProbeCandidate>(prev: T): Promise<T>;
205
+ /** What `probeNativeTranscript` found for a candidate's own transcript. */
206
+ interface NativeTranscriptProbe {
207
+ /** Directory that was (or would be) probed for the transcript. */
208
+ dir: string;
209
+ /** Absolute path of the transcript file the id maps to. */
210
+ path: string;
211
+ /** Whether that exact file exists on disk right now. */
212
+ exists: boolean;
213
+ }
214
+ /**
215
+ * Locate the candidate's own on-disk transcript for a provider whose native
216
+ * resume command accepts an absolute path (`ResumeStrategy.transcriptPath`).
217
+ * Returns `undefined` when the adapter declares no `transcriptPath`, or the
218
+ * candidate has no cwd / no conversation id to map — callers treat that as
219
+ * "nothing to upgrade, and no directory worth naming in diagnostics".
220
+ *
221
+ * The id lookup mirrors `decideRestartStrategy`'s (`resumeMetadata[storeAs]`),
222
+ * falling back to `adapterSessionId` (for claude-code the ACP session id IS
223
+ * the on-disk `.jsonl` uuid — acp-host.ts pins them equal) so a probe can
224
+ * still name the transcript it looked for when the sniffer never fired.
225
+ * `exists: false` is the honest "transcript not found" answer — never fall
226
+ * through to a sibling file (cross-session contamination, see fsProbe's doc).
227
+ */
228
+ declare function probeNativeTranscript(prev: FsProbeCandidate): Promise<NativeTranscriptProbe | undefined>;
200
229
  /**
201
230
  * Describe which resume path a restart used, for CLI banners / MCP
202
231
  * responses. Returns a short phrase like "resumed via claude --resume",
@@ -226,4 +255,4 @@ declare function describeResumePath(prev: RestartCandidate, opts?: DecideRestart
226
255
  */
227
256
  declare function tokenizeCommand(s: string): string[];
228
257
 
229
- export { type DecideRestartStrategyOptions, type FsProbeCandidate, RESUME_ID_REJECTED_RE, RESUME_STRATEGIES, type RestartCandidate, type RestartStrategy, type ResumeMetadataKey, type ResumeStrategy, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, tokenizeCommand };
258
+ export { type DecideRestartStrategyOptions, type FsProbeCandidate, type NativeTranscriptProbe, RESUME_ID_REJECTED_RE, RESUME_STRATEGIES, type RestartCandidate, type RestartStrategy, type ResumeMetadataKey, type ResumeStrategy, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, probeNativeTranscript, tokenizeCommand };
@@ -1,4 +1,4 @@
1
- import { join, resolve } from 'path';
1
+ import { join, resolve, dirname } from 'path';
2
2
  import { createReadStream, promises } from 'fs';
3
3
  import { homedir } from 'os';
4
4
  import { createInterface } from 'readline';
@@ -1063,8 +1063,8 @@ async function discoverOpenCodeSessions(cwd, since, expectedId) {
1063
1063
  const sinceMs = since ? Date.parse(since) : NaN;
1064
1064
  return rows.filter((r) => {
1065
1065
  if (!Number.isFinite(sinceMs)) return true;
1066
- const last = r.time_updated ?? r.time_created;
1067
- return last === void 0 || last >= sinceMs - 1e3;
1066
+ if (r.time_created === void 0) return false;
1067
+ return r.time_created >= sinceMs - 1e3;
1068
1068
  }).map(openCodeRowToCandidate);
1069
1069
  } finally {
1070
1070
  db.close();
@@ -1495,6 +1495,9 @@ function extractFirstText(content) {
1495
1495
  }
1496
1496
  return void 0;
1497
1497
  }
1498
+ function isClaudeControlText(text) {
1499
+ return /^<(?:local-command-caveat|local-command-stdout|command-name|task-notification)>/i.test(text);
1500
+ }
1498
1501
  async function scanClaudeJsonl(filePath) {
1499
1502
  const stream = createReadStream(filePath, { encoding: "utf8" });
1500
1503
  const rl = createInterface({ input: stream, crlfDelay: Infinity });
@@ -1523,7 +1526,7 @@ async function scanClaudeJsonl(filePath) {
1523
1526
  messageCount += 1;
1524
1527
  if (preview === void 0 && entry.type === "user") {
1525
1528
  const text = extractFirstText(entry.message?.content);
1526
- if (text !== void 0) {
1529
+ if (text !== void 0 && !isClaudeControlText(text)) {
1527
1530
  preview = text.length > 120 ? text.slice(0, 120) : text;
1528
1531
  }
1529
1532
  }
@@ -1639,6 +1642,7 @@ var init_conversation_store = __esm({
1639
1642
  // (default). Example: `claude --resume 0e483f81-1a44-4bec-9667-b37158450296`
1640
1643
  outputHint: /claude\s+--resume\s+([0-9a-f-]{8,})/i,
1641
1644
  attachArgv: (conversationId) => ["claude", "--resume", conversationId],
1645
+ transcriptPath: (cwd, conversationId, configDir) => join(claudeCodeProjectDir(cwd, configDir), `${conversationId}.jsonl`),
1642
1646
  configDirEnvVar: "CLAUDE_CONFIG_DIR",
1643
1647
  discover: discoverClaudeCode,
1644
1648
  read: readClaudeCode
@@ -1663,6 +1667,9 @@ var init_conversation_store = __esm({
1663
1667
  },
1664
1668
  opencode: {
1665
1669
  storeAs: "openCodeResumeId",
1670
+ // Match the adapter's portable npx installation: a direct `opencode`
1671
+ // binary is not guaranteed to be on the daemon's PATH on every host.
1672
+ attachArgv: (conversationId) => ["npx", "-y", "opencode-ai", "-s", conversationId],
1666
1673
  discover: discoverOpenCode,
1667
1674
  read: readOpenCode
1668
1675
  },
@@ -1677,6 +1684,9 @@ var init_conversation_store = __esm({
1677
1684
  read: readPi
1678
1685
  }
1679
1686
  };
1687
+ new Set(
1688
+ Object.values(CONVERSATION_STORES).map((s) => s.storeAs)
1689
+ );
1680
1690
  }
1681
1691
  });
1682
1692
 
@@ -1691,6 +1701,7 @@ var RESUME_STRATEGIES = Object.fromEntries(
1691
1701
  outputHint: store.outputHint,
1692
1702
  storeAs: store.storeAs,
1693
1703
  ...store.configDirEnvVar ? { configDirEnvVar: store.configDirEnvVar } : {},
1704
+ ...store.transcriptPath ? { transcriptPath: store.transcriptPath } : {},
1694
1705
  fsProbe: async (cwd, prevStartedAt, expectedId, configDir) => {
1695
1706
  const candidates = await s.discover({
1696
1707
  cwd,
@@ -1712,7 +1723,8 @@ function hasResumeStrategy(adapterSlug) {
1712
1723
  }
1713
1724
  function decideRestartStrategy(prev, opts = {}) {
1714
1725
  const mayPreferNative = prev.pty === true || opts.preferNativeTerminal === true;
1715
- if (prev.adapterSlug && prev.nativeTerminalResume === true && mayPreferNative) {
1726
+ const hasNativeCapability = prev.nativeTerminalResume === true || prev.pty === true && prev.nativeTerminalResume === void 0;
1727
+ if (prev.adapterSlug && hasNativeCapability && mayPreferNative) {
1716
1728
  const strategy = RESUME_STRATEGIES[prev.adapterSlug];
1717
1729
  const id = strategy?.storeAs ? prev.resumeMetadata?.[strategy.storeAs] : void 0;
1718
1730
  if (strategy?.spawnArgs && id) {
@@ -1767,13 +1779,30 @@ async function augmentWithFsResume(prev) {
1767
1779
  }
1768
1780
  };
1769
1781
  }
1782
+ async function probeNativeTranscript(prev) {
1783
+ if (!prev.adapterSlug || !prev.cwd) return void 0;
1784
+ const strategy = RESUME_STRATEGIES[prev.adapterSlug];
1785
+ if (!strategy?.transcriptPath) return void 0;
1786
+ const id = prev.resumeMetadata?.[strategy.storeAs] ?? prev.adapterSessionId;
1787
+ if (!id) return void 0;
1788
+ const path = strategy.transcriptPath(prev.cwd, id, prev.adapterConfigDir);
1789
+ let exists = false;
1790
+ try {
1791
+ exists = (await promises.stat(path)).isFile();
1792
+ } catch {
1793
+ exists = false;
1794
+ }
1795
+ return { dir: dirname(path), path, exists };
1796
+ }
1770
1797
  function describeResumePath(prev, opts = {}) {
1771
1798
  const mayPreferNative = prev.pty === true || opts.preferNativeTerminal === true;
1772
- if (prev.adapterSlug && mayPreferNative) {
1799
+ const hasNativeCapability = prev.nativeTerminalResume === true || prev.pty === true && prev.nativeTerminalResume === void 0;
1800
+ if (prev.adapterSlug && hasNativeCapability && mayPreferNative) {
1773
1801
  const s = RESUME_STRATEGIES[prev.adapterSlug];
1774
1802
  if (s?.spawnArgs && s.storeAs && prev.resumeMetadata?.[s.storeAs]) {
1775
- const sample = s.spawnArgs("\u2026")[0] ?? prev.adapterSlug;
1776
- return `resumed via ${sample} --resume`;
1803
+ const sample = s.spawnArgs("\u2026");
1804
+ const idIndex = sample.indexOf("\u2026");
1805
+ return `resumed via ${idIndex > 0 ? sample.slice(0, idIndex).join(" ") : sample[0] ?? prev.adapterSlug}`;
1777
1806
  }
1778
1807
  }
1779
1808
  if (prev.adapterSlug && prev.adapterSessionId) {
@@ -1812,6 +1841,6 @@ function tokenizeCommand(s) {
1812
1841
  return out;
1813
1842
  }
1814
1843
 
1815
- export { RESUME_ID_REJECTED_RE, RESUME_STRATEGIES, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, tokenizeCommand };
1844
+ export { RESUME_ID_REJECTED_RE, RESUME_STRATEGIES, augmentWithFsResume, decideRestartStrategy, describeResumePath, hasResumeStrategy, probeNativeTranscript, tokenizeCommand };
1816
1845
  //# sourceMappingURL=resume-strategies.mjs.map
1817
1846
  //# sourceMappingURL=resume-strategies.mjs.map