@dreb/telegram 2.4.4 → 2.5.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.
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-lifecycle.d.ts","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAmDxG","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
1
+ {"version":3,"file":"bridge-lifecycle.d.ts","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAkDxG","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// cmdNew always sets newSessionCwd to a concrete string (never null).\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd!;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
@@ -38,10 +38,9 @@ export async function ensureBridge(config, userState) {
38
38
  */
39
39
  export async function ensureBridgeWithSession(config, userState) {
40
40
  // Handle new session — always kill and recreate the bridge for clean state.
41
- // For /new <path>: uses the user-specified directory.
42
- // For /new (bare): preserves the current effectiveCwd.
41
+ // cmdNew always sets newSessionCwd to a concrete string (never null).
43
42
  if (userState.newSessionFlag) {
44
- const cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;
43
+ const cwd = userState.newSessionCwd;
45
44
  userState.newSessionFlag = false;
46
45
  userState.newSessionCwd = null;
47
46
  // Kill existing bridge and start a new one with the resolved cwd
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-lifecycle.js","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
1
+ {"version":3,"file":"bridge-lifecycle.js","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,SAAoB,EAAwB;IAC9F,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpD,yEAAuE;QACvE,MAAM,eAAe,GACpB,SAAS,CAAC,YAAY,IAAI,SAAS,CAAC,YAAY,KAAK,MAAM,CAAC,UAAU;YACrE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,YAAY,EAAE;YACnD,CAAC,CAAC,MAAM,CAAC;QACX,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,eAAe,CAAC,CAAC;QAChD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC;QAE1B,oCAAoC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC;YAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;gBAC7C,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE;oBAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB,CAAC,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;gBAClD,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC;QAAA,CACD,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC;AAAA,CACxB;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAc,EAAE,SAAoB,EAAwB;IACzG,8EAA4E;IAC5E,sEAAsE;IACtE,IAAI,SAAS,CAAC,cAAc,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,aAAc,CAAC;QACrC,SAAS,CAAC,cAAc,GAAG,KAAK,CAAC;QACjC,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC;QAE/B,iEAAiE;QACjE,IAAI,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC/B,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QACD,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC;QAExB,MAAM,YAAY,GAAG,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;QACpD,iEAAiE;QACjE,yDAAyD;QACzD,SAAS,CAAC,YAAY,GAAG,GAAG,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC3D,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,CAAC,SAAS,CAAC;IAE/B,qEAAqE;IACrE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;QAC7B,SAAS,CAAC,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC;IAC5C,CAAC;IAED,0CAAwC;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAC7B,CAAC;IAED,sEAAsE;IACtE,IAAI,WAAW,EAAE,CAAC;QACjB,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,KAAK,EAAE,oBAAoB,EAAE,CAAC;gBACjC,SAAS,CAAC,2BAA2B,GAAG,KAAK,CAAC,oBAAoB,CAAC;YACpE,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,8CAA4C;QAC7C,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AAAA,CACd","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// cmdNew always sets newSessionCwd to a concrete string (never null).\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd!;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst hadBridge = !!userState.bridge?.isAlive;\n\tconst bridge = await ensureBridge(config, userState);\n\tconst freshBridge = !hadBridge;\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\t// Check for model fallback warning on fresh bridge (e.g. after crash)\n\tif (freshBridge) {\n\t\ttry {\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.modelFallbackMessage) {\n\t\t\t\tuserState.pendingModelFallbackWarning = state.modelFallbackMessage;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Non-critical — the warning is best-effort\n\t\t}\n\t}\n\n\treturn bridge;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/commands/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BjG;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB5F;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1F;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E","sourcesContent":["/**\n * Core slash commands: /start, /status, /cwd, /new, /stop, /restart\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport type { Api, Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdStart(ctx: Context): Promise<void> {\n\tawait ctx.reply(\n\t\t\"🤖 *dreb Telegram*\\n\\n\" +\n\t\t\t\"Send me a message and I'll forward it to dreb.\\n\\n\" +\n\t\t\t\"*Session:*\\n\" +\n\t\t\t\"/new — Start a fresh session (keeps current directory)\\n\" +\n\t\t\t\"/new <path> — Start a fresh session in a different directory\\n\" +\n\t\t\t\"/sessions — List recent sessions\\n\" +\n\t\t\t\"/resume <id> — Resume a session\\n\" +\n\t\t\t\"/recent \\\\[N\\\\] — Resend last N messages\\n\\n\" +\n\t\t\t\"*Agent:*\\n\" +\n\t\t\t\"/status — Connection info\\n\" +\n\t\t\t\"/cwd — Working directory\\n\" +\n\t\t\t\"/stats — Token usage & cost\\n\" +\n\t\t\t\"/compact — Compact context\\n\" +\n\t\t\t\"/agents — Background subagents\\n\" +\n\t\t\t\"/model \\\\[pattern\\\\] — View/switch model\\n\" +\n\t\t\t\"/thinking \\\\[level\\\\] — View/set thinking\\n\" +\n\t\t\t\"/skills — List available skills\\n\\n\" +\n\t\t\t\"*Control:*\\n\" +\n\t\t\t\"/stop — Interrupt current task\\n\" +\n\t\t\t\"/restart — Restart the bot\",\n\t\t{ parse_mode: \"Markdown\" },\n\t);\n}\n\nexport async function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tlet version = \"unknown\";\n\tlet model = \"none\";\n\ttry {\n\t\tif (bridge?.isAlive) {\n\t\t\tversion = await bridge.getVersion();\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) model = `${state.model.provider}/${state.model.id}`;\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /status error: ${e}`);\n\t}\n\n\tconst lines = [\n\t\tbridge?.isAlive ? \"✅ Connected\" : \"⚠️ Not connected (will start on next message)\",\n\t\t`📁 Working dir: \\`${config.workingDir}\\``,\n\t\t`🔧 dreb ${version}`,\n\t\t`🧠 Model: ${model}`,\n\t];\n\n\tif (bridge?.sessionId) {\n\t\tlines.push(`📂 Session: \\`${bridge.sessionId.slice(0, 8)}...\\``);\n\t}\n\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst cwd = userState.effectiveCwd ?? config.workingDir;\n\tawait safeSend(ctx.api, ctx.chat!.id, `📁 Working directory: \\`${cwd}\\``);\n}\n\nexport async function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst pathArg = args.trim();\n\n\tif (pathArg) {\n\t\t// Resolve path (expand ~ and make absolute)\n\t\tconst expanded = pathArg.startsWith(\"~\") ? pathArg.replace(\"~\", homedir()) : pathArg;\n\t\tconst resolved = resolve(expanded);\n\n\t\tif (!existsSync(resolved)) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Directory not found: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\t\tif (!statSync(resolved).isDirectory()) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Not a directory: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = resolved;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${resolved}\\``);\n\t} else {\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = null;\n\t\tawait ctx.reply(\"🆕 Next message will start a fresh session.\");\n\t}\n}\n\nexport async function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void> {\n\tuserState.stopRequested = true;\n\n\t// Abort current agent activity — like pressing Esc in the TUI.\n\t// This stops the agent, not the bridge. Session stays connected.\n\tif (userState.bridge?.isAlive) {\n\t\tawait userState.bridge.abort();\n\t}\n\n\tconst parts: string[] = [];\n\tif (userState.bridge?.isStreaming || userState.promptInFlight) parts.push(\"interrupted current task\");\n\tawait ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(\", \")}.` : \"🛑 Stopped.\");\n}\n\nexport async function cmdRestart(ctx: Context, config: Config): Promise<void> {\n\tawait ctx.reply(\"🔄 Restarting...\");\n\tlog(\"[CMD] /restart — triggering systemctl restart\");\n\ttry {\n\t\texecSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });\n\t} catch {\n\t\t// Process will be killed by systemd restart\n\t}\n}\n"]}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/commands/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BjG;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B5F;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1F;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E","sourcesContent":["/**\n * Core slash commands: /start, /status, /cwd, /new, /stop, /restart\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport type { Api, Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdStart(ctx: Context): Promise<void> {\n\tawait ctx.reply(\n\t\t\"🤖 *dreb Telegram*\\n\\n\" +\n\t\t\t\"Send me a message and I'll forward it to dreb.\\n\\n\" +\n\t\t\t\"*Session:*\\n\" +\n\t\t\t\"/new — Start a fresh session (keeps current directory)\\n\" +\n\t\t\t\"/new <path> — Start a fresh session in a different directory\\n\" +\n\t\t\t\"/sessions — List recent sessions\\n\" +\n\t\t\t\"/resume <id> — Resume a session\\n\" +\n\t\t\t\"/recent \\\\[N\\\\] — Resend last N messages\\n\\n\" +\n\t\t\t\"*Agent:*\\n\" +\n\t\t\t\"/status — Connection info\\n\" +\n\t\t\t\"/cwd — Working directory\\n\" +\n\t\t\t\"/stats — Token usage & cost\\n\" +\n\t\t\t\"/compact — Compact context\\n\" +\n\t\t\t\"/agents — Background subagents\\n\" +\n\t\t\t\"/model \\\\[pattern\\\\] — View/switch model\\n\" +\n\t\t\t\"/thinking \\\\[level\\\\] — View/set thinking\\n\" +\n\t\t\t\"/skills — List available skills\\n\\n\" +\n\t\t\t\"*Control:*\\n\" +\n\t\t\t\"/stop — Interrupt current task\\n\" +\n\t\t\t\"/restart — Restart the bot\",\n\t\t{ parse_mode: \"Markdown\" },\n\t);\n}\n\nexport async function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tlet version = \"unknown\";\n\tlet model = \"none\";\n\ttry {\n\t\tif (bridge?.isAlive) {\n\t\t\tversion = await bridge.getVersion();\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) model = `${state.model.provider}/${state.model.id}`;\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /status error: ${e}`);\n\t}\n\n\tconst lines = [\n\t\tbridge?.isAlive ? \"✅ Connected\" : \"⚠️ Not connected (will start on next message)\",\n\t\t`📁 Working dir: \\`${config.workingDir}\\``,\n\t\t`🔧 dreb ${version}`,\n\t\t`🧠 Model: ${model}`,\n\t];\n\n\tif (bridge?.sessionId) {\n\t\tlines.push(`📂 Session: \\`${bridge.sessionId.slice(0, 8)}...\\``);\n\t}\n\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst cwd = userState.effectiveCwd ?? config.workingDir;\n\tawait safeSend(ctx.api, ctx.chat!.id, `📁 Working directory: \\`${cwd}\\``);\n}\n\nexport async function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst pathArg = args.trim();\n\n\tif (pathArg) {\n\t\t// Resolve path (expand ~ and make absolute)\n\t\tconst expanded = pathArg.startsWith(\"~\") ? pathArg.replace(\"~\", homedir()) : pathArg;\n\t\tconst resolved = resolve(expanded);\n\n\t\tif (!existsSync(resolved)) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Directory not found: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\t\tif (!statSync(resolved).isDirectory()) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Not a directory: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = resolved;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${resolved}\\``);\n\t} else {\n\t\t// Eagerly resolve CWD — never store null as a sentinel.\n\t\t// After /restart, effectiveCwd is null (in-memory state is lost),\n\t\t// so this falls back to config.workingDir deterministically.\n\t\tconst cwd = userState.effectiveCwd ?? userState.config.workingDir;\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = cwd;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${cwd}\\``);\n\t}\n}\n\nexport async function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void> {\n\tuserState.stopRequested = true;\n\n\t// Abort current agent activity — like pressing Esc in the TUI.\n\t// This stops the agent, not the bridge. Session stays connected.\n\tif (userState.bridge?.isAlive) {\n\t\tawait userState.bridge.abort();\n\t}\n\n\tconst parts: string[] = [];\n\tif (userState.bridge?.isStreaming || userState.promptInFlight) parts.push(\"interrupted current task\");\n\tawait ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(\", \")}.` : \"🛑 Stopped.\");\n}\n\nexport async function cmdRestart(ctx: Context, config: Config): Promise<void> {\n\tawait ctx.reply(\"🔄 Restarting...\");\n\tlog(\"[CMD] /restart — triggering systemctl restart\");\n\ttry {\n\t\texecSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });\n\t} catch {\n\t\t// Process will be killed by systemd restart\n\t}\n}\n"]}
@@ -78,9 +78,13 @@ export async function cmdNew(ctx, userState, args) {
78
78
  await ctx.reply(`🆕 Next message will start a fresh session in \`${resolved}\``);
79
79
  }
80
80
  else {
81
+ // Eagerly resolve CWD — never store null as a sentinel.
82
+ // After /restart, effectiveCwd is null (in-memory state is lost),
83
+ // so this falls back to config.workingDir deterministically.
84
+ const cwd = userState.effectiveCwd ?? userState.config.workingDir;
81
85
  userState.newSessionFlag = true;
82
- userState.newSessionCwd = null;
83
- await ctx.reply("🆕 Next message will start a fresh session.");
86
+ userState.newSessionCwd = cwd;
87
+ await ctx.reply(`🆕 Next message will start a fresh session in \`${cwd}\``);
84
88
  }
85
89
  }
86
90
  export async function cmdStop(ctx, _api, userState) {
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/commands/core.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core slash commands: /start, /status, /cwd, /new, /stop, /restart\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport type { Api, Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdStart(ctx: Context): Promise<void> {\n\tawait ctx.reply(\n\t\t\"🤖 *dreb Telegram*\\n\\n\" +\n\t\t\t\"Send me a message and I'll forward it to dreb.\\n\\n\" +\n\t\t\t\"*Session:*\\n\" +\n\t\t\t\"/new — Start a fresh session (keeps current directory)\\n\" +\n\t\t\t\"/new <path> — Start a fresh session in a different directory\\n\" +\n\t\t\t\"/sessions — List recent sessions\\n\" +\n\t\t\t\"/resume <id> — Resume a session\\n\" +\n\t\t\t\"/recent \\\\[N\\\\] — Resend last N messages\\n\\n\" +\n\t\t\t\"*Agent:*\\n\" +\n\t\t\t\"/status — Connection info\\n\" +\n\t\t\t\"/cwd — Working directory\\n\" +\n\t\t\t\"/stats — Token usage & cost\\n\" +\n\t\t\t\"/compact — Compact context\\n\" +\n\t\t\t\"/agents — Background subagents\\n\" +\n\t\t\t\"/model \\\\[pattern\\\\] — View/switch model\\n\" +\n\t\t\t\"/thinking \\\\[level\\\\] — View/set thinking\\n\" +\n\t\t\t\"/skills — List available skills\\n\\n\" +\n\t\t\t\"*Control:*\\n\" +\n\t\t\t\"/stop — Interrupt current task\\n\" +\n\t\t\t\"/restart — Restart the bot\",\n\t\t{ parse_mode: \"Markdown\" },\n\t);\n}\n\nexport async function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tlet version = \"unknown\";\n\tlet model = \"none\";\n\ttry {\n\t\tif (bridge?.isAlive) {\n\t\t\tversion = await bridge.getVersion();\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) model = `${state.model.provider}/${state.model.id}`;\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /status error: ${e}`);\n\t}\n\n\tconst lines = [\n\t\tbridge?.isAlive ? \"✅ Connected\" : \"⚠️ Not connected (will start on next message)\",\n\t\t`📁 Working dir: \\`${config.workingDir}\\``,\n\t\t`🔧 dreb ${version}`,\n\t\t`🧠 Model: ${model}`,\n\t];\n\n\tif (bridge?.sessionId) {\n\t\tlines.push(`📂 Session: \\`${bridge.sessionId.slice(0, 8)}...\\``);\n\t}\n\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst cwd = userState.effectiveCwd ?? config.workingDir;\n\tawait safeSend(ctx.api, ctx.chat!.id, `📁 Working directory: \\`${cwd}\\``);\n}\n\nexport async function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst pathArg = args.trim();\n\n\tif (pathArg) {\n\t\t// Resolve path (expand ~ and make absolute)\n\t\tconst expanded = pathArg.startsWith(\"~\") ? pathArg.replace(\"~\", homedir()) : pathArg;\n\t\tconst resolved = resolve(expanded);\n\n\t\tif (!existsSync(resolved)) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Directory not found: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\t\tif (!statSync(resolved).isDirectory()) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Not a directory: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = resolved;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${resolved}\\``);\n\t} else {\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = null;\n\t\tawait ctx.reply(\"🆕 Next message will start a fresh session.\");\n\t}\n}\n\nexport async function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void> {\n\tuserState.stopRequested = true;\n\n\t// Abort current agent activity — like pressing Esc in the TUI.\n\t// This stops the agent, not the bridge. Session stays connected.\n\tif (userState.bridge?.isAlive) {\n\t\tawait userState.bridge.abort();\n\t}\n\n\tconst parts: string[] = [];\n\tif (userState.bridge?.isStreaming || userState.promptInFlight) parts.push(\"interrupted current task\");\n\tawait ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(\", \")}.` : \"🛑 Stopped.\");\n}\n\nexport async function cmdRestart(ctx: Context, config: Config): Promise<void> {\n\tawait ctx.reply(\"🔄 Restarting...\");\n\tlog(\"[CMD] /restart — triggering systemctl restart\");\n\ttry {\n\t\texecSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });\n\t} catch {\n\t\t// Process will be killed by systemd restart\n\t}\n}\n"]}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/commands/core.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core slash commands: /start, /status, /cwd, /new, /stop, /restart\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport type { Api, Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdStart(ctx: Context): Promise<void> {\n\tawait ctx.reply(\n\t\t\"🤖 *dreb Telegram*\\n\\n\" +\n\t\t\t\"Send me a message and I'll forward it to dreb.\\n\\n\" +\n\t\t\t\"*Session:*\\n\" +\n\t\t\t\"/new — Start a fresh session (keeps current directory)\\n\" +\n\t\t\t\"/new <path> — Start a fresh session in a different directory\\n\" +\n\t\t\t\"/sessions — List recent sessions\\n\" +\n\t\t\t\"/resume <id> — Resume a session\\n\" +\n\t\t\t\"/recent \\\\[N\\\\] — Resend last N messages\\n\\n\" +\n\t\t\t\"*Agent:*\\n\" +\n\t\t\t\"/status — Connection info\\n\" +\n\t\t\t\"/cwd — Working directory\\n\" +\n\t\t\t\"/stats — Token usage & cost\\n\" +\n\t\t\t\"/compact — Compact context\\n\" +\n\t\t\t\"/agents — Background subagents\\n\" +\n\t\t\t\"/model \\\\[pattern\\\\] — View/switch model\\n\" +\n\t\t\t\"/thinking \\\\[level\\\\] — View/set thinking\\n\" +\n\t\t\t\"/skills — List available skills\\n\\n\" +\n\t\t\t\"*Control:*\\n\" +\n\t\t\t\"/stop — Interrupt current task\\n\" +\n\t\t\t\"/restart — Restart the bot\",\n\t\t{ parse_mode: \"Markdown\" },\n\t);\n}\n\nexport async function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tlet version = \"unknown\";\n\tlet model = \"none\";\n\ttry {\n\t\tif (bridge?.isAlive) {\n\t\t\tversion = await bridge.getVersion();\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) model = `${state.model.provider}/${state.model.id}`;\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /status error: ${e}`);\n\t}\n\n\tconst lines = [\n\t\tbridge?.isAlive ? \"✅ Connected\" : \"⚠️ Not connected (will start on next message)\",\n\t\t`📁 Working dir: \\`${config.workingDir}\\``,\n\t\t`🔧 dreb ${version}`,\n\t\t`🧠 Model: ${model}`,\n\t];\n\n\tif (bridge?.sessionId) {\n\t\tlines.push(`📂 Session: \\`${bridge.sessionId.slice(0, 8)}...\\``);\n\t}\n\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst cwd = userState.effectiveCwd ?? config.workingDir;\n\tawait safeSend(ctx.api, ctx.chat!.id, `📁 Working directory: \\`${cwd}\\``);\n}\n\nexport async function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst pathArg = args.trim();\n\n\tif (pathArg) {\n\t\t// Resolve path (expand ~ and make absolute)\n\t\tconst expanded = pathArg.startsWith(\"~\") ? pathArg.replace(\"~\", homedir()) : pathArg;\n\t\tconst resolved = resolve(expanded);\n\n\t\tif (!existsSync(resolved)) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Directory not found: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\t\tif (!statSync(resolved).isDirectory()) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Not a directory: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = resolved;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${resolved}\\``);\n\t} else {\n\t\t// Eagerly resolve CWD — never store null as a sentinel.\n\t\t// After /restart, effectiveCwd is null (in-memory state is lost),\n\t\t// so this falls back to config.workingDir deterministically.\n\t\tconst cwd = userState.effectiveCwd ?? userState.config.workingDir;\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = cwd;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${cwd}\\``);\n\t}\n}\n\nexport async function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void> {\n\tuserState.stopRequested = true;\n\n\t// Abort current agent activity — like pressing Esc in the TUI.\n\t// This stops the agent, not the bridge. Session stays connected.\n\tif (userState.bridge?.isAlive) {\n\t\tawait userState.bridge.abort();\n\t}\n\n\tconst parts: string[] = [];\n\tif (userState.bridge?.isStreaming || userState.promptInFlight) parts.push(\"interrupted current task\");\n\tawait ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(\", \")}.` : \"🛑 Stopped.\");\n}\n\nexport async function cmdRestart(ctx: Context, config: Config): Promise<void> {\n\tawait ctx.reply(\"🔄 Restarting...\");\n\tlog(\"[CMD] /restart — triggering systemctl restart\");\n\ttry {\n\t\texecSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });\n\t} catch {\n\t\t// Process will be killed by systemd restart\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreb/telegram",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "description": "Telegram bot frontend for dreb coding agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",