@openacp/cli 0.6.5 → 0.6.6

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.
@@ -12,6 +12,10 @@ import {
12
12
  handleHelp,
13
13
  handleMenu
14
14
  } from "./chunk-7QJS2XBD.js";
15
+ import {
16
+ CheckpointReader,
17
+ DEFAULT_MAX_TOKENS
18
+ } from "./chunk-ZMVVW3BK.js";
15
19
  import {
16
20
  ChannelAdapter
17
21
  } from "./chunk-4GQ3I65A.js";
@@ -1622,13 +1626,290 @@ ${resultText}`,
1622
1626
  });
1623
1627
  }
1624
1628
 
1625
- // src/adapters/telegram/commands/settings.ts
1629
+ // src/adapters/telegram/commands/resume.ts
1630
+ import * as fs from "fs";
1631
+ import * as path from "path";
1632
+ import * as os from "os";
1626
1633
  import { InlineKeyboard as InlineKeyboard6 } from "grammy";
1627
- var log4 = createChildLogger({ module: "telegram-settings" });
1634
+ var log4 = createChildLogger({ module: "telegram-cmd-resume" });
1635
+ var PENDING_TIMEOUT_MS2 = 5 * 60 * 1e3;
1636
+ function botFromCtx2(ctx) {
1637
+ return { api: ctx.api };
1638
+ }
1639
+ var pendingResumes = /* @__PURE__ */ new Map();
1640
+ function cleanupPending2(userId) {
1641
+ const pending = pendingResumes.get(userId);
1642
+ if (pending) {
1643
+ clearTimeout(pending.timer);
1644
+ pendingResumes.delete(userId);
1645
+ }
1646
+ }
1647
+ function parseResumeArgs(matchStr) {
1648
+ const args = matchStr.split(" ").filter(Boolean);
1649
+ if (args.length === 0) return { query: { type: "latest", value: "5" } };
1650
+ const first = args[0];
1651
+ if (first === "pr") return args[1] ? { query: { type: "pr", value: args[1] } } : null;
1652
+ if (first === "branch") return args[1] ? { query: { type: "branch", value: args[1] } } : null;
1653
+ if (first === "commit") return args[1] ? { query: { type: "commit", value: args[1] } } : null;
1654
+ if (CheckpointReader.isCheckpointId(first)) return { query: { type: "checkpoint", value: first } };
1655
+ if (CheckpointReader.isSessionId(first)) return { query: { type: "session", value: first } };
1656
+ if (first.includes("/pull/")) {
1657
+ const prMatch = first.match(/\/pull\/(\d+)/);
1658
+ return prMatch ? { query: { type: "pr", value: prMatch[1] } } : null;
1659
+ }
1660
+ const ghCommitMatch = first.match(/github\.com\/[^/]+\/[^/]+\/commit\/([0-9a-f]+)/);
1661
+ if (ghCommitMatch) return { query: { type: "commit", value: ghCommitMatch[1] } };
1662
+ const ghBranchMatch = first.match(/github\.com\/[^/]+\/[^/]+\/tree\/(.+?)(?:\?|#|$)/);
1663
+ if (ghBranchMatch) return { query: { type: "branch", value: ghBranchMatch[1] } };
1664
+ const ghCompareMatch = first.match(/github\.com\/[^/]+\/[^/]+\/compare\/(?:[^.]+\.{2,3})(.+?)(?:\?|#|$)/);
1665
+ if (ghCompareMatch) return { query: { type: "branch", value: ghCompareMatch[1] } };
1666
+ if (first.match(/github\.com\/[^/]+\/[^/]+\/?$/) && !first.includes("/tree/") && !first.includes("/pull/") && !first.includes("/commit/") && !first.includes("/compare/")) {
1667
+ return { query: { type: "latest", value: "5" } };
1668
+ }
1669
+ const entireCheckpointMatch = first.match(/entire\.io\/gh\/[^/]+\/[^/]+\/checkpoints\/[^/]+\/([0-9a-f]{12})/);
1670
+ if (entireCheckpointMatch) return { query: { type: "checkpoint", value: entireCheckpointMatch[1] } };
1671
+ const entireCommitMatch = first.match(/entire\.io\/gh\/[^/]+\/[^/]+\/commit\/([0-9a-f]+)/);
1672
+ if (entireCommitMatch) return { query: { type: "commit", value: entireCommitMatch[1] } };
1673
+ return { query: { type: "latest", value: "5" } };
1674
+ }
1675
+ function looksLikePath(text) {
1676
+ return text.startsWith("/") || text.startsWith("~") || text.startsWith(".");
1677
+ }
1678
+ function listWorkspaceDirs(baseDir, maxItems = 10) {
1679
+ const resolved = baseDir.replace(/^~/, os.homedir());
1680
+ try {
1681
+ if (!fs.existsSync(resolved)) return [];
1682
+ return fs.readdirSync(resolved, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort().slice(0, maxItems);
1683
+ } catch {
1684
+ return [];
1685
+ }
1686
+ }
1687
+ async function showWorkspacePicker(ctx, core, chatId, userId, query) {
1688
+ const config = core.configManager.get();
1689
+ const baseDir = config.workspace.baseDir;
1690
+ const resolvedBase = baseDir.replace(/^~/, os.homedir());
1691
+ const subdirs = listWorkspaceDirs(baseDir);
1692
+ const keyboard = new InlineKeyboard6();
1693
+ for (const dir of subdirs) {
1694
+ const fullPath = path.join(resolvedBase, dir);
1695
+ keyboard.text(`\u{1F4C1} ${dir}`, `m:resume:ws:${dir}`).row();
1696
+ }
1697
+ keyboard.text(`\u{1F4C1} Use ${baseDir}`, "m:resume:ws:default").row();
1698
+ keyboard.text("\u270F\uFE0F Enter project path", "m:resume:ws:custom");
1699
+ const queryLabel = query.type === "latest" ? "latest sessions" : `${query.type}: ${query.value}`;
1700
+ const text = `\u{1F4C1} <b>Select project directory for resume</b>
1701
+
1702
+ Query: <code>${escapeHtml(queryLabel)}</code>
1703
+
1704
+ Choose the repo that has Entire checkpoints enabled:`;
1705
+ const msg = await ctx.reply(text, { parse_mode: "HTML", reply_markup: keyboard });
1706
+ cleanupPending2(userId);
1707
+ pendingResumes.set(userId, {
1708
+ query,
1709
+ step: "workspace",
1710
+ messageId: msg.message_id,
1711
+ threadId: ctx.message?.message_thread_id,
1712
+ timer: setTimeout(() => pendingResumes.delete(userId), PENDING_TIMEOUT_MS2)
1713
+ });
1714
+ }
1715
+ async function executeResume(ctx, core, chatId, query, repoPath) {
1716
+ const provider = await core.contextManager.getProvider(repoPath);
1717
+ if (!provider) {
1718
+ await ctx.reply(
1719
+ `\u26A0\uFE0F <b>Entire not enabled in <code>${escapeHtml(repoPath)}</code></b>
1720
+
1721
+ To enable conversation history tracking:
1722
+ <code>cd ${escapeHtml(repoPath)} && npx entire enable</code>
1723
+
1724
+ Learn more: https://docs.entire.io/getting-started`,
1725
+ { parse_mode: "HTML" }
1726
+ );
1727
+ return;
1728
+ }
1729
+ const fullQuery = { ...query, repoPath };
1730
+ await ctx.reply(`\u{1F50D} Scanning ${query.type === "latest" ? "latest sessions" : `${query.type}: ${escapeHtml(query.value)}`}...`, { parse_mode: "HTML" });
1731
+ const listResult = await core.contextManager.listSessions(fullQuery);
1732
+ if (!listResult || listResult.sessions.length === 0) {
1733
+ await ctx.reply(
1734
+ `\u{1F50D} <b>No sessions found</b>
1735
+
1736
+ Query: <code>${escapeHtml(query.type)}: ${escapeHtml(query.value)}</code>
1737
+ Repo: <code>${escapeHtml(repoPath)}</code>`,
1738
+ { parse_mode: "HTML" }
1739
+ );
1740
+ return;
1741
+ }
1742
+ const config = core.configManager.get();
1743
+ const agentName = config.defaultAgent;
1744
+ let threadId;
1745
+ try {
1746
+ const queryLabel = query.type === "latest" ? "latest" : `${query.type}: ${query.value.slice(0, 20)}`;
1747
+ const topicName = `\u{1F4DC} Resume \u2014 ${queryLabel}`;
1748
+ threadId = await createSessionTopic(botFromCtx2(ctx), chatId, topicName);
1749
+ await ctx.api.sendMessage(chatId, `\u23F3 Loading context and starting session...`, {
1750
+ message_thread_id: threadId,
1751
+ parse_mode: "HTML"
1752
+ });
1753
+ const { session, contextResult } = await core.createSessionWithContext({
1754
+ channelId: "telegram",
1755
+ agentName,
1756
+ workingDirectory: repoPath,
1757
+ contextQuery: fullQuery,
1758
+ contextOptions: { maxTokens: DEFAULT_MAX_TOKENS }
1759
+ });
1760
+ session.threadId = String(threadId);
1761
+ await core.sessionManager.patchRecord(session.id, { platform: { topicId: threadId } });
1762
+ const sessionCount = contextResult?.sessionCount ?? listResult.sessions.length;
1763
+ const mode = contextResult?.mode ?? "full";
1764
+ const tokens = contextResult?.tokenEstimate ?? listResult.estimatedTokens;
1765
+ const topicLink = buildDeepLink(chatId, threadId);
1766
+ const replyTarget = ctx.message?.message_thread_id;
1767
+ if (replyTarget !== threadId) {
1768
+ await ctx.reply(
1769
+ `\u2705 Session resumed \u2192 <a href="${topicLink}">Open topic</a>`,
1770
+ { parse_mode: "HTML" }
1771
+ );
1772
+ }
1773
+ await ctx.api.sendMessage(
1774
+ chatId,
1775
+ `\u2705 <b>Session resumed with context</b>
1776
+ <b>Agent:</b> ${escapeHtml(session.agentName)}
1777
+ <b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>
1778
+ <b>Sessions loaded:</b> ${sessionCount}
1779
+ <b>Mode:</b> ${escapeHtml(mode)}
1780
+ <b>~Tokens:</b> ${tokens.toLocaleString()}
1781
+
1782
+ Context is ready \u2014 chat here to continue working with the agent.`,
1783
+ {
1784
+ message_thread_id: threadId,
1785
+ parse_mode: "HTML",
1786
+ reply_markup: buildSessionControlKeyboard(session.id, false, false)
1787
+ }
1788
+ );
1789
+ session.warmup().catch((err) => log4.error({ err }, "Warm-up error"));
1790
+ } catch (err) {
1791
+ log4.error({ err }, "Resume session creation failed");
1792
+ if (threadId) {
1793
+ try {
1794
+ await ctx.api.deleteForumTopic(chatId, threadId);
1795
+ } catch {
1796
+ }
1797
+ }
1798
+ const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
1799
+ await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
1800
+ }
1801
+ }
1802
+ async function handleResume(ctx, core, chatId, assistant) {
1803
+ const rawMatch = ctx.match;
1804
+ const matchStr = typeof rawMatch === "string" ? rawMatch : "";
1805
+ const parsed = parseResumeArgs(matchStr);
1806
+ if (!parsed) {
1807
+ await ctx.reply(
1808
+ `\u274C <b>Invalid arguments.</b>
1809
+
1810
+ Usage examples:
1811
+ \u2022 <code>/resume</code> \u2014 latest 5 sessions
1812
+ \u2022 <code>/resume pr 19</code>
1813
+ \u2022 <code>/resume branch main</code>
1814
+ \u2022 <code>/resume commit e0dd2fa4</code>
1815
+ \u2022 <code>/resume f634acf05138</code> \u2014 checkpoint ID
1816
+ \u2022 <code>/resume https://entire.io/gh/.../checkpoints/.../2e884e2c402a</code>
1817
+ \u2022 <code>/resume https://entire.io/gh/.../commit/e0dd2fa4...</code>`,
1818
+ { parse_mode: "HTML" }
1819
+ );
1820
+ return;
1821
+ }
1822
+ const { query } = parsed;
1823
+ const userId = ctx.from?.id;
1824
+ if (!userId) return;
1825
+ await showWorkspacePicker(ctx, core, chatId, userId, query);
1826
+ }
1827
+ async function handlePendingResumeInput(ctx, core, chatId, assistantTopicId) {
1828
+ const userId = ctx.from?.id;
1829
+ if (!userId) return false;
1830
+ const pending = pendingResumes.get(userId);
1831
+ if (!pending || !ctx.message?.text) return false;
1832
+ if (pending.step !== "workspace_input" && pending.step !== "workspace") return false;
1833
+ const threadId = ctx.message.message_thread_id;
1834
+ if (threadId && threadId !== assistantTopicId) return false;
1835
+ if (pending.step === "workspace" && !looksLikePath(ctx.message.text.trim())) return false;
1836
+ let workspace = ctx.message.text.trim();
1837
+ if (!workspace) {
1838
+ await ctx.reply("\u26A0\uFE0F Please enter a valid directory path.", { parse_mode: "HTML" });
1839
+ return true;
1840
+ }
1841
+ if (!workspace.startsWith("/") && !workspace.startsWith("~")) {
1842
+ const baseDir = core.configManager.get().workspace.baseDir;
1843
+ workspace = `${baseDir.replace(/\/$/, "")}/${workspace}`;
1844
+ }
1845
+ const resolved = core.configManager.resolveWorkspace(workspace);
1846
+ cleanupPending2(userId);
1847
+ await executeResume(ctx, core, chatId, pending.query, resolved);
1848
+ return true;
1849
+ }
1850
+ function setupResumeCallbacks(bot, core, chatId) {
1851
+ bot.callbackQuery(/^m:resume:/, async (ctx) => {
1852
+ const data = ctx.callbackQuery.data;
1853
+ const userId = ctx.from?.id;
1854
+ if (!userId) return;
1855
+ try {
1856
+ await ctx.answerCallbackQuery();
1857
+ } catch {
1858
+ }
1859
+ const pending = pendingResumes.get(userId);
1860
+ if (!pending) return;
1861
+ if (data === "m:resume:ws:default") {
1862
+ const baseDir = core.configManager.get().workspace.baseDir;
1863
+ const resolved = core.configManager.resolveWorkspace(baseDir);
1864
+ cleanupPending2(userId);
1865
+ try {
1866
+ await ctx.api.editMessageText(chatId, pending.messageId, `\u23F3 Using <code>${escapeHtml(resolved)}</code>...`, { parse_mode: "HTML" });
1867
+ } catch {
1868
+ }
1869
+ await executeResume(ctx, core, chatId, pending.query, resolved);
1870
+ return;
1871
+ }
1872
+ if (data === "m:resume:ws:custom") {
1873
+ try {
1874
+ await ctx.api.editMessageText(
1875
+ chatId,
1876
+ pending.messageId,
1877
+ `\u270F\uFE0F <b>Enter project path:</b>
1878
+
1879
+ Full path like <code>~/code/my-project</code>
1880
+ Or just the folder name (will use workspace baseDir)`,
1881
+ { parse_mode: "HTML" }
1882
+ );
1883
+ } catch {
1884
+ await ctx.reply(`\u270F\uFE0F <b>Enter project path:</b>`, { parse_mode: "HTML" });
1885
+ }
1886
+ clearTimeout(pending.timer);
1887
+ pending.step = "workspace_input";
1888
+ pending.timer = setTimeout(() => pendingResumes.delete(userId), PENDING_TIMEOUT_MS2);
1889
+ return;
1890
+ }
1891
+ if (data.startsWith("m:resume:ws:")) {
1892
+ const dirName = data.replace("m:resume:ws:", "");
1893
+ const baseDir = core.configManager.get().workspace.baseDir;
1894
+ const resolved = core.configManager.resolveWorkspace(path.join(baseDir.replace(/^~/, os.homedir()), dirName));
1895
+ cleanupPending2(userId);
1896
+ try {
1897
+ await ctx.api.editMessageText(chatId, pending.messageId, `\u23F3 Using <code>${escapeHtml(resolved)}</code>...`, { parse_mode: "HTML" });
1898
+ } catch {
1899
+ }
1900
+ await executeResume(ctx, core, chatId, pending.query, resolved);
1901
+ return;
1902
+ }
1903
+ });
1904
+ }
1905
+
1906
+ // src/adapters/telegram/commands/settings.ts
1907
+ import { InlineKeyboard as InlineKeyboard7 } from "grammy";
1908
+ var log5 = createChildLogger({ module: "telegram-settings" });
1628
1909
  function buildSettingsKeyboard(core) {
1629
1910
  const config = core.configManager.get();
1630
1911
  const fields = getSafeFields();
1631
- const kb = new InlineKeyboard6();
1912
+ const kb = new InlineKeyboard7();
1632
1913
  for (const field of fields) {
1633
1914
  const value = getConfigValue(config, field.path);
1634
1915
  const label = formatFieldLabel(field, value);
@@ -1687,7 +1968,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
1687
1968
  } catch {
1688
1969
  }
1689
1970
  } catch (err) {
1690
- log4.error({ err, fieldPath }, "Failed to toggle config");
1971
+ log5.error({ err, fieldPath }, "Failed to toggle config");
1691
1972
  try {
1692
1973
  await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
1693
1974
  } catch {
@@ -1701,7 +1982,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
1701
1982
  if (!fieldDef) return;
1702
1983
  const options = resolveOptions(fieldDef, config) ?? [];
1703
1984
  const currentValue = getConfigValue(config, fieldPath);
1704
- const kb = new InlineKeyboard6();
1985
+ const kb = new InlineKeyboard7();
1705
1986
  for (const opt of options) {
1706
1987
  const marker = opt === String(currentValue) ? " \u2713" : "";
1707
1988
  kb.text(`${opt}${marker}`, `s:pick:${fieldPath}:${opt}`).row();
@@ -1761,7 +2042,7 @@ Tap to change:`, {
1761
2042
  } catch {
1762
2043
  }
1763
2044
  } catch (err) {
1764
- log4.error({ err, fieldPath }, "Failed to set config");
2045
+ log5.error({ err, fieldPath }, "Failed to set config");
1765
2046
  try {
1766
2047
  await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
1767
2048
  } catch {
@@ -1832,8 +2113,8 @@ function buildNestedUpdate(dotPath, value) {
1832
2113
  }
1833
2114
 
1834
2115
  // src/adapters/telegram/commands/doctor.ts
1835
- import { InlineKeyboard as InlineKeyboard7 } from "grammy";
1836
- var log5 = createChildLogger({ module: "telegram-cmd-doctor" });
2116
+ import { InlineKeyboard as InlineKeyboard8 } from "grammy";
2117
+ var log6 = createChildLogger({ module: "telegram-cmd-doctor" });
1837
2118
  var pendingFixesStore = /* @__PURE__ */ new Map();
1838
2119
  function renderReport(report) {
1839
2120
  const icons = { pass: "\u2705", warn: "\u26A0\uFE0F", fail: "\u274C" };
@@ -1850,7 +2131,7 @@ function renderReport(report) {
1850
2131
  lines.push(`<b>Result:</b> ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`);
1851
2132
  let keyboard;
1852
2133
  if (report.pendingFixes.length > 0) {
1853
- keyboard = new InlineKeyboard7();
2134
+ keyboard = new InlineKeyboard8();
1854
2135
  for (let i = 0; i < report.pendingFixes.length; i++) {
1855
2136
  const label = `\u{1F527} Fix: ${report.pendingFixes[i].message.slice(0, 30)}`;
1856
2137
  keyboard.text(label, `m:doctor:fix:${i}`).row();
@@ -1876,7 +2157,7 @@ async function handleDoctor(ctx) {
1876
2157
  reply_markup: keyboard
1877
2158
  });
1878
2159
  } catch (err) {
1879
- log5.error({ err }, "Doctor command failed");
2160
+ log6.error({ err }, "Doctor command failed");
1880
2161
  await ctx.api.editMessageText(
1881
2162
  ctx.chat.id,
1882
2163
  statusMsg.message_id,
@@ -1925,7 +2206,7 @@ function setupDoctorCallbacks(bot) {
1925
2206
  }
1926
2207
  }
1927
2208
  } catch (err) {
1928
- log5.error({ err, index }, "Doctor fix callback failed");
2209
+ log6.error({ err, index }, "Doctor fix callback failed");
1929
2210
  }
1930
2211
  });
1931
2212
  bot.callbackQuery("m:doctor", async (ctx) => {
@@ -1938,8 +2219,8 @@ function setupDoctorCallbacks(bot) {
1938
2219
  }
1939
2220
 
1940
2221
  // src/adapters/telegram/commands/tunnel.ts
1941
- import { InlineKeyboard as InlineKeyboard8 } from "grammy";
1942
- var log6 = createChildLogger({ module: "telegram-cmd-tunnel" });
2222
+ import { InlineKeyboard as InlineKeyboard9 } from "grammy";
2223
+ var log7 = createChildLogger({ module: "telegram-cmd-tunnel" });
1943
2224
  async function handleTunnel(ctx, core) {
1944
2225
  if (!core.tunnelService) {
1945
2226
  await ctx.reply("\u274C Tunnel service is not enabled.", { parse_mode: "HTML" });
@@ -2026,7 +2307,7 @@ async function handleTunnels(ctx, core) {
2026
2307
  \u2192 <a href="${escapeHtml(e.publicUrl)}">${escapeHtml(e.publicUrl)}</a>` : "";
2027
2308
  return `${status} Port <b>${e.port}</b>${label}${url}`;
2028
2309
  });
2029
- const keyboard = new InlineKeyboard8();
2310
+ const keyboard = new InlineKeyboard9();
2030
2311
  for (const e of entries) {
2031
2312
  keyboard.text(`\u{1F50C} Stop ${e.port}${e.label ? ` (${e.label})` : ""}`, `tn:stop:${e.port}`).row();
2032
2313
  }
@@ -2066,7 +2347,7 @@ function setupTunnelCallbacks(bot, core) {
2066
2347
  if (remaining.length === 0) {
2067
2348
  await ctx.editMessageText("\u{1F50C} All tunnels stopped.", { parse_mode: "HTML" });
2068
2349
  } else {
2069
- const kb = new InlineKeyboard8();
2350
+ const kb = new InlineKeyboard9();
2070
2351
  for (const e of remaining) {
2071
2352
  kb.text(`\u{1F50C} Stop ${e.port}${e.label ? ` (${e.label})` : ""}`, `tn:stop:${e.port}`).row();
2072
2353
  }
@@ -2116,9 +2397,11 @@ function setupCommands(bot, core, chatId, assistant) {
2116
2397
  bot.command("tunnels", (ctx) => handleTunnels(ctx, core));
2117
2398
  bot.command("archive", (ctx) => handleArchive(ctx, core));
2118
2399
  bot.command("text_to_speech", (ctx) => handleTTS(ctx, core));
2400
+ bot.command("resume", (ctx) => handleResume(ctx, core, chatId, assistant));
2119
2401
  }
2120
2402
  function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSession) {
2121
2403
  setupNewSessionCallbacks(bot, core, chatId);
2404
+ setupResumeCallbacks(bot, core, chatId);
2122
2405
  setupSessionCallbacks(bot, core, chatId, systemTopicIds);
2123
2406
  setupSettingsCallbacks(bot, core, getAssistantSession ?? (() => void 0));
2124
2407
  setupDoctorCallbacks(bot);
@@ -2189,13 +2472,14 @@ var STATIC_COMMANDS = [
2189
2472
  { command: "tunnel", description: "Create/stop tunnel for a local port" },
2190
2473
  { command: "tunnels", description: "List active tunnels" },
2191
2474
  { command: "archive", description: "Archive session topic (recreate with clean history)" },
2192
- { command: "text_to_speech", description: "Toggle Text to Speech (/text_to_speech on, /text_to_speech off)" }
2475
+ { command: "text_to_speech", description: "Toggle Text to Speech (/text_to_speech on, /text_to_speech off)" },
2476
+ { command: "resume", description: "Resume with conversation history from Entire checkpoints" }
2193
2477
  ];
2194
2478
 
2195
2479
  // src/adapters/telegram/permissions.ts
2196
- import { InlineKeyboard as InlineKeyboard9 } from "grammy";
2480
+ import { InlineKeyboard as InlineKeyboard10 } from "grammy";
2197
2481
  import { nanoid } from "nanoid";
2198
- var log7 = createChildLogger({ module: "telegram-permissions" });
2482
+ var log8 = createChildLogger({ module: "telegram-permissions" });
2199
2483
  var PermissionHandler = class {
2200
2484
  constructor(bot, chatId, getSession, sendNotification) {
2201
2485
  this.bot = bot;
@@ -2212,7 +2496,7 @@ var PermissionHandler = class {
2212
2496
  requestId: request.id,
2213
2497
  options: request.options.map((o) => ({ id: o.id, isAllow: o.isAllow }))
2214
2498
  });
2215
- const keyboard = new InlineKeyboard9();
2499
+ const keyboard = new InlineKeyboard10();
2216
2500
  for (const option of request.options) {
2217
2501
  const emoji = option.isAllow ? "\u2705" : "\u274C";
2218
2502
  keyboard.text(`${emoji} ${option.label}`, `p:${callbackKey}:${option.id}`);
@@ -2255,7 +2539,7 @@ ${escapeHtml(request.description)}`,
2255
2539
  }
2256
2540
  const session = this.getSession(pending.sessionId);
2257
2541
  const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
2258
- log7.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
2542
+ log8.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
2259
2543
  if (session?.permissionGate.requestId === pending.requestId) {
2260
2544
  session.permissionGate.resolve(optionId);
2261
2545
  }
@@ -2273,10 +2557,10 @@ ${escapeHtml(request.description)}`,
2273
2557
  };
2274
2558
 
2275
2559
  // src/adapters/telegram/assistant.ts
2276
- var log8 = createChildLogger({ module: "telegram-assistant" });
2560
+ var log9 = createChildLogger({ module: "telegram-assistant" });
2277
2561
  async function spawnAssistant(core, adapter, assistantTopicId) {
2278
2562
  const config = core.configManager.get();
2279
- log8.info({ agent: config.defaultAgent }, "Creating assistant session...");
2563
+ log9.info({ agent: config.defaultAgent }, "Creating assistant session...");
2280
2564
  const session = await core.createSession({
2281
2565
  channelId: "telegram",
2282
2566
  agentName: config.defaultAgent,
@@ -2285,7 +2569,7 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
2285
2569
  // Prevent auto-naming from triggering after system prompt
2286
2570
  });
2287
2571
  session.threadId = String(assistantTopicId);
2288
- log8.info({ sessionId: session.id }, "Assistant agent spawned");
2572
+ log9.info({ sessionId: session.id }, "Assistant agent spawned");
2289
2573
  const allRecords = core.sessionManager.listRecords();
2290
2574
  const activeCount = allRecords.filter((r) => r.status === "active" || r.status === "initializing").length;
2291
2575
  const statusCounts = /* @__PURE__ */ new Map();
@@ -2306,9 +2590,9 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
2306
2590
  };
2307
2591
  const systemPrompt = buildAssistantSystemPrompt(ctx);
2308
2592
  const ready = session.enqueuePrompt(systemPrompt).then(() => {
2309
- log8.info({ sessionId: session.id }, "Assistant system prompt completed");
2593
+ log9.info({ sessionId: session.id }, "Assistant system prompt completed");
2310
2594
  }).catch((err) => {
2311
- log8.warn({ err }, "Assistant system prompt failed");
2595
+ log9.warn({ err }, "Assistant system prompt failed");
2312
2596
  });
2313
2597
  return { session, ready };
2314
2598
  }
@@ -2474,7 +2758,7 @@ function redirectToAssistant(chatId, assistantTopicId) {
2474
2758
  }
2475
2759
 
2476
2760
  // src/adapters/telegram/activity.ts
2477
- var log9 = createChildLogger({ module: "telegram:activity" });
2761
+ var log10 = createChildLogger({ module: "telegram:activity" });
2478
2762
  var THINKING_REFRESH_MS = 15e3;
2479
2763
  var THINKING_MAX_MS = 3 * 60 * 1e3;
2480
2764
  var ThinkingIndicator = class {
@@ -2506,7 +2790,7 @@ var ThinkingIndicator = class {
2506
2790
  this.startRefreshTimer();
2507
2791
  }
2508
2792
  } catch (err) {
2509
- log9.warn({ err }, "ThinkingIndicator.show() failed");
2793
+ log10.warn({ err }, "ThinkingIndicator.show() failed");
2510
2794
  } finally {
2511
2795
  this.sending = false;
2512
2796
  }
@@ -2579,7 +2863,7 @@ var UsageMessage = class {
2579
2863
  if (result) this.msgId = result.message_id;
2580
2864
  }
2581
2865
  } catch (err) {
2582
- log9.warn({ err }, "UsageMessage.send() failed");
2866
+ log10.warn({ err }, "UsageMessage.send() failed");
2583
2867
  }
2584
2868
  }
2585
2869
  getMsgId() {
@@ -2592,7 +2876,7 @@ var UsageMessage = class {
2592
2876
  try {
2593
2877
  await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
2594
2878
  } catch (err) {
2595
- log9.warn({ err }, "UsageMessage.delete() failed");
2879
+ log10.warn({ err }, "UsageMessage.delete() failed");
2596
2880
  }
2597
2881
  }
2598
2882
  };
@@ -2678,7 +2962,7 @@ var PlanCard = class {
2678
2962
  if (result) this.msgId = result.message_id;
2679
2963
  }
2680
2964
  } catch (err) {
2681
- log9.warn({ err }, "PlanCard flush failed");
2965
+ log10.warn({ err }, "PlanCard flush failed");
2682
2966
  }
2683
2967
  }
2684
2968
  };
@@ -2741,7 +3025,7 @@ var ActivityTracker = class {
2741
3025
  })
2742
3026
  );
2743
3027
  } catch (err) {
2744
- log9.warn({ err }, "ActivityTracker.onComplete() Done send failed");
3028
+ log10.warn({ err }, "ActivityTracker.onComplete() Done send failed");
2745
3029
  }
2746
3030
  }
2747
3031
  }
@@ -2824,7 +3108,7 @@ var TelegramSendQueue = class {
2824
3108
 
2825
3109
  // src/adapters/telegram/action-detect.ts
2826
3110
  import { nanoid as nanoid2 } from "nanoid";
2827
- import { InlineKeyboard as InlineKeyboard10 } from "grammy";
3111
+ import { InlineKeyboard as InlineKeyboard11 } from "grammy";
2828
3112
  var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
2829
3113
  var CMD_CANCEL_RE = /\/cancel\b/;
2830
3114
  var KW_NEW_RE = /(?:create|new)\s+session/i;
@@ -2871,7 +3155,7 @@ function removeAction(id) {
2871
3155
  actionMap.delete(id);
2872
3156
  }
2873
3157
  function buildActionKeyboard(actionId, action) {
2874
- const keyboard = new InlineKeyboard10();
3158
+ const keyboard = new InlineKeyboard11();
2875
3159
  if (action.action === "new_session") {
2876
3160
  keyboard.text("\u2705 Create session", `a:${actionId}`);
2877
3161
  keyboard.text("\u274C Cancel", `a:dismiss:${actionId}`);
@@ -2980,7 +3264,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
2980
3264
  }
2981
3265
 
2982
3266
  // src/adapters/telegram/tool-call-tracker.ts
2983
- var log10 = createChildLogger({ module: "tool-call-tracker" });
3267
+ var log11 = createChildLogger({ module: "tool-call-tracker" });
2984
3268
  var ToolCallTracker = class {
2985
3269
  constructor(bot, chatId, sendQueue) {
2986
3270
  this.bot = bot;
@@ -3024,7 +3308,7 @@ var ToolCallTracker = class {
3024
3308
  if (!toolState) return;
3025
3309
  if (meta.viewerLinks) {
3026
3310
  toolState.viewerLinks = meta.viewerLinks;
3027
- log10.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
3311
+ log11.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
3028
3312
  }
3029
3313
  if (meta.viewerFilePath) toolState.viewerFilePath = meta.viewerFilePath;
3030
3314
  if (meta.name) toolState.name = meta.name;
@@ -3032,7 +3316,7 @@ var ToolCallTracker = class {
3032
3316
  const isTerminal = meta.status === "completed" || meta.status === "failed";
3033
3317
  if (!isTerminal) return;
3034
3318
  await toolState.ready;
3035
- log10.debug(
3319
+ log11.debug(
3036
3320
  {
3037
3321
  toolId: meta.id,
3038
3322
  status: meta.status,
@@ -3061,7 +3345,7 @@ var ToolCallTracker = class {
3061
3345
  )
3062
3346
  );
3063
3347
  } catch (err) {
3064
- log10.warn(
3348
+ log11.warn(
3065
3349
  {
3066
3350
  err,
3067
3351
  msgId: toolState.msgId,
@@ -3332,7 +3616,7 @@ var DraftManager = class {
3332
3616
  };
3333
3617
 
3334
3618
  // src/adapters/telegram/skill-command-manager.ts
3335
- var log11 = createChildLogger({ module: "skill-commands" });
3619
+ var log12 = createChildLogger({ module: "skill-commands" });
3336
3620
  var SkillCommandManager = class {
3337
3621
  // sessionId → pinned msgId
3338
3622
  constructor(bot, chatId, sendQueue, sessionManager) {
@@ -3398,7 +3682,7 @@ var SkillCommandManager = class {
3398
3682
  disable_notification: true
3399
3683
  });
3400
3684
  } catch (err) {
3401
- log11.error({ err, sessionId }, "Failed to send skill commands");
3685
+ log12.error({ err, sessionId }, "Failed to send skill commands");
3402
3686
  }
3403
3687
  }
3404
3688
  async cleanup(sessionId) {
@@ -3427,7 +3711,7 @@ var SkillCommandManager = class {
3427
3711
  };
3428
3712
 
3429
3713
  // src/adapters/telegram/adapter.ts
3430
- var log12 = createChildLogger({ module: "telegram" });
3714
+ var log13 = createChildLogger({ module: "telegram" });
3431
3715
  function patchedFetch(input, init) {
3432
3716
  if (init?.signal && !(init.signal instanceof AbortSignal)) {
3433
3717
  const nativeController = new AbortController();
@@ -3491,7 +3775,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3491
3775
  );
3492
3776
  this.bot.catch((err) => {
3493
3777
  const rootCause = err.error instanceof Error ? err.error : err;
3494
- log12.error({ err: rootCause }, "Telegram bot error");
3778
+ log13.error({ err: rootCause }, "Telegram bot error");
3495
3779
  });
3496
3780
  this.bot.api.config.use(async (prev, method, payload, signal) => {
3497
3781
  const maxRetries = 3;
@@ -3505,7 +3789,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3505
3789
  if (rateLimitedMethods.includes(method)) {
3506
3790
  this.sendQueue.onRateLimited();
3507
3791
  }
3508
- log12.warn(
3792
+ log13.warn(
3509
3793
  { method, retryAfter, attempt: attempt + 1 },
3510
3794
  "Rate limited by Telegram, retrying"
3511
3795
  );
@@ -3638,7 +3922,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3638
3922
  this.setupRoutes();
3639
3923
  this.bot.start({
3640
3924
  allowed_updates: ["message", "callback_query"],
3641
- onStart: () => log12.info(
3925
+ onStart: () => log13.info(
3642
3926
  { chatId: this.telegramConfig.chatId },
3643
3927
  "Telegram bot started"
3644
3928
  )
@@ -3660,10 +3944,10 @@ var TelegramAdapter = class extends ChannelAdapter {
3660
3944
  reply_markup: buildMenuKeyboard()
3661
3945
  });
3662
3946
  } catch (err) {
3663
- log12.warn({ err }, "Failed to send welcome message");
3947
+ log13.warn({ err }, "Failed to send welcome message");
3664
3948
  }
3665
3949
  try {
3666
- log12.info("Spawning assistant session...");
3950
+ log13.info("Spawning assistant session...");
3667
3951
  const { session, ready } = await spawnAssistant(
3668
3952
  this.core,
3669
3953
  this,
@@ -3671,13 +3955,13 @@ var TelegramAdapter = class extends ChannelAdapter {
3671
3955
  );
3672
3956
  this.assistantSession = session;
3673
3957
  this.assistantInitializing = true;
3674
- log12.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
3958
+ log13.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
3675
3959
  ready.then(() => {
3676
3960
  this.assistantInitializing = false;
3677
- log12.info({ sessionId: session.id }, "Assistant ready for user messages");
3961
+ log13.info({ sessionId: session.id }, "Assistant ready for user messages");
3678
3962
  });
3679
3963
  } catch (err) {
3680
- log12.error({ err }, "Failed to spawn assistant");
3964
+ log13.error({ err }, "Failed to spawn assistant");
3681
3965
  this.bot.api.sendMessage(
3682
3966
  this.telegramConfig.chatId,
3683
3967
  `\u26A0\uFE0F <b>Failed to start assistant session.</b>
@@ -3693,7 +3977,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3693
3977
  await this.assistantSession.destroy();
3694
3978
  }
3695
3979
  await this.bot.stop();
3696
- log12.info("Telegram bot stopped");
3980
+ log13.info("Telegram bot stopped");
3697
3981
  }
3698
3982
  setupRoutes() {
3699
3983
  this.bot.on("message:text", async (ctx) => {
@@ -3702,6 +3986,9 @@ var TelegramAdapter = class extends ChannelAdapter {
3702
3986
  if (await handlePendingWorkspaceInput(ctx, this.core, this.telegramConfig.chatId, this.assistantTopicId)) {
3703
3987
  return;
3704
3988
  }
3989
+ if (await handlePendingResumeInput(ctx, this.core, this.telegramConfig.chatId, this.assistantTopicId)) {
3990
+ return;
3991
+ }
3705
3992
  if (!threadId) {
3706
3993
  const html = redirectToAssistant(
3707
3994
  this.telegramConfig.chatId,
@@ -3721,7 +4008,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3721
4008
  ctx.replyWithChatAction("typing").catch(() => {
3722
4009
  });
3723
4010
  handleAssistantMessage(this.assistantSession, forwardText).catch(
3724
- (err) => log12.error({ err }, "Assistant error")
4011
+ (err) => log13.error({ err }, "Assistant error")
3725
4012
  );
3726
4013
  return;
3727
4014
  }
@@ -3738,7 +4025,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3738
4025
  threadId: String(threadId),
3739
4026
  userId: String(ctx.from.id),
3740
4027
  text: forwardText
3741
- }).catch((err) => log12.error({ err }, "handleMessage error"));
4028
+ }).catch((err) => log13.error({ err }, "handleMessage error"));
3742
4029
  });
3743
4030
  this.bot.on("message:photo", async (ctx) => {
3744
4031
  const threadId = ctx.message.message_thread_id;
@@ -3880,7 +4167,7 @@ Task completed.
3880
4167
  if (!content.attachment) return;
3881
4168
  const { attachment } = content;
3882
4169
  if (attachment.size > 50 * 1024 * 1024) {
3883
- log12.warn({ sessionId: ctx.sessionId, fileName: attachment.fileName, size: attachment.size }, "File too large for Telegram (>50MB)");
4170
+ log13.warn({ sessionId: ctx.sessionId, fileName: attachment.fileName, size: attachment.size }, "File too large for Telegram (>50MB)");
3884
4171
  await this.sendQueue.enqueue(
3885
4172
  () => this.bot.api.sendMessage(
3886
4173
  this.telegramConfig.chatId,
@@ -3917,7 +4204,7 @@ Task completed.
3917
4204
  );
3918
4205
  }
3919
4206
  } catch (err) {
3920
- log12.error({ err, sessionId: ctx.sessionId, fileName: attachment.fileName }, "Failed to send attachment");
4207
+ log13.error({ err, sessionId: ctx.sessionId, fileName: attachment.fileName }, "Failed to send attachment");
3921
4208
  }
3922
4209
  },
3923
4210
  onSessionEnd: async (ctx, _content) => {
@@ -3985,14 +4272,14 @@ Task completed.
3985
4272
  if (session.archiving) return;
3986
4273
  const threadId = Number(session.threadId);
3987
4274
  if (!threadId || isNaN(threadId)) {
3988
- log12.warn({ sessionId, threadId: session.threadId }, "Session has no valid threadId, skipping message");
4275
+ log13.warn({ sessionId, threadId: session.threadId }, "Session has no valid threadId, skipping message");
3989
4276
  return;
3990
4277
  }
3991
4278
  const ctx = { sessionId, threadId };
3992
4279
  await dispatchMessage(this.messageHandlers, ctx, content);
3993
4280
  }
3994
4281
  async sendPermissionRequest(sessionId, request) {
3995
- log12.info({ sessionId, requestId: request.id }, "Permission request sent");
4282
+ log13.info({ sessionId, requestId: request.id }, "Permission request sent");
3996
4283
  const session = this.core.sessionManager.getSession(sessionId);
3997
4284
  if (!session) return;
3998
4285
  await this.sendQueue.enqueue(
@@ -4001,7 +4288,7 @@ Task completed.
4001
4288
  }
4002
4289
  async sendNotification(notification) {
4003
4290
  if (notification.sessionId === this.assistantSession?.id) return;
4004
- log12.info(
4291
+ log13.info(
4005
4292
  { sessionId: notification.sessionId, type: notification.type },
4006
4293
  "Notification sent"
4007
4294
  );
@@ -4037,7 +4324,7 @@ Task completed.
4037
4324
  );
4038
4325
  }
4039
4326
  async createSessionThread(sessionId, name) {
4040
- log12.info({ sessionId, name }, "Session topic created");
4327
+ log13.info({ sessionId, name }, "Session topic created");
4041
4328
  return String(
4042
4329
  await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
4043
4330
  );
@@ -4061,7 +4348,7 @@ Task completed.
4061
4348
  try {
4062
4349
  await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
4063
4350
  } catch (err) {
4064
- log12.warn({ err, sessionId, topicId }, "Failed to delete forum topic (may already be deleted)");
4351
+ log13.warn({ err, sessionId, topicId }, "Failed to delete forum topic (may already be deleted)");
4065
4352
  }
4066
4353
  }
4067
4354
  async sendSkillCommands(sessionId, commands) {
@@ -4085,7 +4372,7 @@ Task completed.
4085
4372
  const buffer = Buffer.from(await response.arrayBuffer());
4086
4373
  return { buffer, filePath: file.file_path };
4087
4374
  } catch (err) {
4088
- log12.error({ err }, "Failed to download file from Telegram");
4375
+ log13.error({ err }, "Failed to download file from Telegram");
4089
4376
  return null;
4090
4377
  }
4091
4378
  }
@@ -4101,7 +4388,7 @@ Task completed.
4101
4388
  try {
4102
4389
  buffer = await this.fileService.convertOggToWav(buffer);
4103
4390
  } catch (err) {
4104
- log12.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
4391
+ log13.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
4105
4392
  fileName = "voice.ogg";
4106
4393
  mimeType = "audio/ogg";
4107
4394
  originalFilePath = void 0;
@@ -4127,7 +4414,7 @@ Task completed.
4127
4414
  userId: String(userId),
4128
4415
  text,
4129
4416
  attachments: [att]
4130
- }).catch((err) => log12.error({ err }, "handleMessage error"));
4417
+ }).catch((err) => log13.error({ err }, "handleMessage error"));
4131
4418
  }
4132
4419
  async cleanupSkillCommands(sessionId) {
4133
4420
  await this.skillManager.cleanup(sessionId);
@@ -4178,4 +4465,4 @@ Task completed.
4178
4465
  export {
4179
4466
  TelegramAdapter
4180
4467
  };
4181
- //# sourceMappingURL=chunk-FW6HM4VU.js.map
4468
+ //# sourceMappingURL=chunk-AHPRT3RY.js.map