@letta-ai/letta-code 0.22.0 → 0.22.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.
Files changed (2) hide show
  1. package/letta.js +656 -24
  2. package/package.json +1 -1
package/letta.js CHANGED
@@ -3269,7 +3269,7 @@ var package_default;
3269
3269
  var init_package = __esm(() => {
3270
3270
  package_default = {
3271
3271
  name: "@letta-ai/letta-code",
3272
- version: "0.22.0",
3272
+ version: "0.22.1",
3273
3273
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3274
3274
  type: "module",
3275
3275
  bin: {
@@ -48585,13 +48585,22 @@ function createTelegramAdapter(config) {
48585
48585
  await bot.init();
48586
48586
  const info = bot.botInfo;
48587
48587
  console.log(`[Telegram] Bot started as @${info.username} (dm_policy: ${config.dmPolicy})`);
48588
- bot.start({
48589
- onStart: () => {
48590
- running = true;
48591
- }
48592
- }).catch((error) => {
48593
- running = false;
48594
- console.error("[Telegram] Long-polling stopped unexpectedly:", error);
48588
+ await new Promise((resolve2, reject) => {
48589
+ let started = false;
48590
+ bot.start({
48591
+ onStart: () => {
48592
+ running = true;
48593
+ started = true;
48594
+ resolve2();
48595
+ }
48596
+ }).catch((error) => {
48597
+ running = false;
48598
+ if (!started) {
48599
+ reject(error);
48600
+ return;
48601
+ }
48602
+ console.error("[Telegram] Long-polling stopped unexpectedly:", error);
48603
+ });
48595
48604
  });
48596
48605
  },
48597
48606
  async stop() {
@@ -48635,12 +48644,16 @@ __export(exports_registry, {
48635
48644
  initializeChannels: () => initializeChannels,
48636
48645
  getChannelRegistry: () => getChannelRegistry,
48637
48646
  getActiveChannelIds: () => getActiveChannelIds,
48647
+ ensureChannelRegistry: () => ensureChannelRegistry,
48638
48648
  completePairing: () => completePairing,
48639
48649
  ChannelRegistry: () => ChannelRegistry
48640
48650
  });
48641
48651
  function getChannelRegistry() {
48642
48652
  return instance;
48643
48653
  }
48654
+ function ensureChannelRegistry() {
48655
+ return instance ?? new ChannelRegistry;
48656
+ }
48644
48657
  function getActiveChannelIds() {
48645
48658
  if (!instance)
48646
48659
  return [];
@@ -48651,6 +48664,7 @@ class ChannelRegistry {
48651
48664
  adapters = new Map;
48652
48665
  ready = false;
48653
48666
  messageHandler = null;
48667
+ eventHandler = null;
48654
48668
  buffer = [];
48655
48669
  constructor() {
48656
48670
  if (instance) {
@@ -48673,6 +48687,9 @@ class ChannelRegistry {
48673
48687
  setMessageHandler(handler) {
48674
48688
  this.messageHandler = handler;
48675
48689
  }
48690
+ setEventHandler(handler) {
48691
+ this.eventHandler = handler;
48692
+ }
48676
48693
  setReady() {
48677
48694
  this.ready = true;
48678
48695
  this.flushBuffer();
@@ -48683,6 +48700,39 @@ class ChannelRegistry {
48683
48700
  getRoute(channel, chatId) {
48684
48701
  return getRoute(channel, chatId);
48685
48702
  }
48703
+ async startChannel(channelId) {
48704
+ const config = readChannelConfig(channelId);
48705
+ if (!config) {
48706
+ return false;
48707
+ }
48708
+ loadRoutes(channelId);
48709
+ loadPairingStore(channelId);
48710
+ const existing = this.adapters.get(channelId);
48711
+ if (existing?.isRunning()) {
48712
+ await existing.stop();
48713
+ }
48714
+ this.adapters.delete(channelId);
48715
+ if (channelId === "telegram") {
48716
+ const { createTelegramAdapter: createTelegramAdapter2 } = await Promise.resolve().then(() => (init_adapter(), exports_adapter));
48717
+ const adapter = createTelegramAdapter2(config);
48718
+ this.registerAdapter(adapter);
48719
+ await adapter.start();
48720
+ return true;
48721
+ }
48722
+ console.error(`Unknown channel "${channelId}". Supported: telegram`);
48723
+ return false;
48724
+ }
48725
+ async stopChannel(channelId) {
48726
+ const adapter = this.adapters.get(channelId);
48727
+ if (!adapter) {
48728
+ return false;
48729
+ }
48730
+ if (adapter.isRunning()) {
48731
+ await adapter.stop();
48732
+ }
48733
+ this.adapters.delete(channelId);
48734
+ return true;
48735
+ }
48686
48736
  async startAll() {
48687
48737
  for (const adapter of Array.from(this.adapters.values())) {
48688
48738
  if (!adapter.isRunning()) {
@@ -48693,6 +48743,7 @@ class ChannelRegistry {
48693
48743
  pause() {
48694
48744
  this.ready = false;
48695
48745
  this.messageHandler = null;
48746
+ this.eventHandler = null;
48696
48747
  }
48697
48748
  async stopAll() {
48698
48749
  for (const adapter of Array.from(this.adapters.values())) {
@@ -48702,6 +48753,7 @@ class ChannelRegistry {
48702
48753
  }
48703
48754
  this.ready = false;
48704
48755
  this.messageHandler = null;
48756
+ this.eventHandler = null;
48705
48757
  instance = null;
48706
48758
  }
48707
48759
  async handleInboundMessage(msg) {
@@ -48722,11 +48774,15 @@ class ChannelRegistry {
48722
48774
  }
48723
48775
  if (!isUserApproved(msg.channel, msg.senderId)) {
48724
48776
  const code = createPairingCode(msg.channel, msg.senderId, msg.chatId, msg.senderName);
48777
+ this.eventHandler?.({
48778
+ type: "pairings_updated",
48779
+ channelId: msg.channel
48780
+ });
48725
48781
  await adapter.sendDirectReply(msg.chatId, `To connect this chat to a Letta Code agent, run:
48726
48782
 
48727
- ` + `/channels telegram pair ${code}
48783
+ /channels telegram pair ${code}
48728
48784
 
48729
- ` + `This code expires in 15 minutes.`);
48785
+ This code expires in 15 minutes.`);
48730
48786
  return;
48731
48787
  }
48732
48788
  }
@@ -48736,7 +48792,7 @@ class ChannelRegistry {
48736
48792
  route = getRoute(msg.channel, msg.chatId);
48737
48793
  }
48738
48794
  if (!route) {
48739
- await adapter.sendDirectReply(msg.chatId, `This chat isn't bound to an agent. ` + `Run \`/channels telegram enable --chat-id ${msg.chatId}\` ` + `on your Letta Code agent to connect.`);
48795
+ await adapter.sendDirectReply(msg.chatId, `This chat isn't bound to an agent. Run \`/channels telegram enable --chat-id ${msg.chatId}\` on your Letta Code agent to connect.`);
48740
48796
  return;
48741
48797
  }
48742
48798
  const xmlContent = formatChannelNotification(msg);
@@ -48758,7 +48814,7 @@ class ChannelRegistry {
48758
48814
  }
48759
48815
  }
48760
48816
  async function initializeChannels(channelNames) {
48761
- const registry = new ChannelRegistry;
48817
+ const registry = ensureChannelRegistry();
48762
48818
  for (const channelId of channelNames) {
48763
48819
  const config = readChannelConfig(channelId);
48764
48820
  if (!config) {
@@ -48769,17 +48825,8 @@ async function initializeChannels(channelNames) {
48769
48825
  console.log(`Channel "${channelId}" is disabled in config, skipping.`);
48770
48826
  continue;
48771
48827
  }
48772
- loadRoutes(channelId);
48773
- loadPairingStore(channelId);
48774
- if (channelId === "telegram") {
48775
- const { createTelegramAdapter: createTelegramAdapter2 } = await Promise.resolve().then(() => (init_adapter(), exports_adapter));
48776
- const adapter = createTelegramAdapter2(config);
48777
- registry.registerAdapter(adapter);
48778
- } else {
48779
- console.error(`Unknown channel: "${channelId}". Supported: telegram`);
48780
- }
48828
+ await registry.startChannel(channelId);
48781
48829
  }
48782
- await registry.startAll();
48783
48830
  return registry;
48784
48831
  }
48785
48832
  function completePairing(channelId, code, agentId, conversationId) {
@@ -94145,6 +94192,67 @@ function isSetReflectionSettingsCommand(value) {
94145
94192
  const settings = c.settings;
94146
94193
  return (settings.trigger === "off" || settings.trigger === "step-count" || settings.trigger === "compaction-event") && typeof settings.step_count === "number" && Number.isInteger(settings.step_count) && settings.step_count > 0 && (c.scope === undefined || c.scope === "local_project" || c.scope === "global" || c.scope === "both");
94147
94194
  }
94195
+ function isChannelId(value) {
94196
+ return value === "telegram";
94197
+ }
94198
+ function isChannelsListCommand(value) {
94199
+ if (!value || typeof value !== "object")
94200
+ return false;
94201
+ const c = value;
94202
+ return c.type === "channels_list" && typeof c.request_id === "string";
94203
+ }
94204
+ function isChannelGetConfigCommand(value) {
94205
+ if (!value || typeof value !== "object")
94206
+ return false;
94207
+ const c = value;
94208
+ return c.type === "channel_get_config" && typeof c.request_id === "string" && isChannelId(c.channel_id);
94209
+ }
94210
+ function isChannelSetConfigCommand(value) {
94211
+ if (!value || typeof value !== "object")
94212
+ return false;
94213
+ const c = value;
94214
+ if (c.type !== "channel_set_config" || typeof c.request_id !== "string" || !isChannelId(c.channel_id) || !c.config || typeof c.config !== "object") {
94215
+ return false;
94216
+ }
94217
+ const config = c.config;
94218
+ return (config.token === undefined || typeof config.token === "string") && (config.dm_policy === undefined || config.dm_policy === "pairing" || config.dm_policy === "allowlist" || config.dm_policy === "open") && (config.allowed_users === undefined || Array.isArray(config.allowed_users) && config.allowed_users.every((entry) => typeof entry === "string"));
94219
+ }
94220
+ function isChannelStartCommand(value) {
94221
+ if (!value || typeof value !== "object")
94222
+ return false;
94223
+ const c = value;
94224
+ return c.type === "channel_start" && typeof c.request_id === "string" && isChannelId(c.channel_id);
94225
+ }
94226
+ function isChannelStopCommand(value) {
94227
+ if (!value || typeof value !== "object")
94228
+ return false;
94229
+ const c = value;
94230
+ return c.type === "channel_stop" && typeof c.request_id === "string" && isChannelId(c.channel_id);
94231
+ }
94232
+ function isChannelPairingsListCommand(value) {
94233
+ if (!value || typeof value !== "object")
94234
+ return false;
94235
+ const c = value;
94236
+ return c.type === "channel_pairings_list" && typeof c.request_id === "string" && isChannelId(c.channel_id);
94237
+ }
94238
+ function isChannelPairingBindCommand(value) {
94239
+ if (!value || typeof value !== "object")
94240
+ return false;
94241
+ const c = value;
94242
+ return c.type === "channel_pairing_bind" && typeof c.request_id === "string" && isChannelId(c.channel_id) && isRuntimeScope(c.runtime) && typeof c.code === "string" && c.code.length > 0;
94243
+ }
94244
+ function isChannelRoutesListCommand(value) {
94245
+ if (!value || typeof value !== "object")
94246
+ return false;
94247
+ const c = value;
94248
+ return c.type === "channel_routes_list" && typeof c.request_id === "string" && (c.channel_id === undefined || isChannelId(c.channel_id)) && (c.agent_id === undefined || typeof c.agent_id === "string") && (c.conversation_id === undefined || typeof c.conversation_id === "string");
94249
+ }
94250
+ function isChannelRouteRemoveCommand(value) {
94251
+ if (!value || typeof value !== "object")
94252
+ return false;
94253
+ const c = value;
94254
+ return c.type === "channel_route_remove" && typeof c.request_id === "string" && isChannelId(c.channel_id) && typeof c.chat_id === "string" && c.chat_id.length > 0;
94255
+ }
94148
94256
  function isSearchBranchesCommand(value) {
94149
94257
  if (!value || typeof value !== "object")
94150
94258
  return false;
@@ -94168,7 +94276,7 @@ function parseServerMessage(data) {
94168
94276
  try {
94169
94277
  const raw = typeof data === "string" ? data : data.toString();
94170
94278
  const parsed = JSON.parse(raw);
94171
- if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
94279
+ if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isChannelsListCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelRouteRemoveCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
94172
94280
  return parsed;
94173
94281
  }
94174
94282
  const invalidInput = getInvalidInputReason(parsed);
@@ -94189,6 +94297,201 @@ var init_protocol_inbound = __esm(async () => {
94189
94297
  await init_approval();
94190
94298
  });
94191
94299
 
94300
+ // src/channels/service.ts
94301
+ var exports_service = {};
94302
+ __export(exports_service, {
94303
+ stopChannelLive: () => stopChannelLive,
94304
+ startChannelLive: () => startChannelLive,
94305
+ setChannelConfigLive: () => setChannelConfigLive,
94306
+ removeChannelRouteLive: () => removeChannelRouteLive,
94307
+ listPendingPairingSnapshots: () => listPendingPairingSnapshots,
94308
+ listChannelSummaries: () => listChannelSummaries,
94309
+ listChannelRouteSnapshots: () => listChannelRouteSnapshots,
94310
+ getChannelConfigSnapshot: () => getChannelConfigSnapshot,
94311
+ bindChannelPairing: () => bindChannelPairing,
94312
+ CHANNEL_DISPLAY_NAMES: () => CHANNEL_DISPLAY_NAMES
94313
+ });
94314
+ function assertSupportedChannelId(channelId) {
94315
+ if (channelId !== "telegram") {
94316
+ throw new Error(`Unsupported channel: ${channelId}`);
94317
+ }
94318
+ }
94319
+ function toConfigSnapshot(channelId, config) {
94320
+ return {
94321
+ channelId,
94322
+ enabled: config.enabled,
94323
+ dmPolicy: config.dmPolicy,
94324
+ allowedUsers: [...config.allowedUsers],
94325
+ hasToken: config.token.trim().length > 0
94326
+ };
94327
+ }
94328
+ function toPendingPairingSnapshot(pending) {
94329
+ return {
94330
+ code: pending.code,
94331
+ senderId: pending.telegramUserId,
94332
+ senderName: pending.telegramUsername,
94333
+ chatId: pending.chatId,
94334
+ createdAt: pending.createdAt,
94335
+ expiresAt: pending.expiresAt
94336
+ };
94337
+ }
94338
+ function toRouteSnapshot(channelId, route) {
94339
+ return {
94340
+ channelId,
94341
+ chatId: route.chatId,
94342
+ agentId: route.agentId,
94343
+ conversationId: route.conversationId,
94344
+ enabled: route.enabled,
94345
+ createdAt: route.createdAt
94346
+ };
94347
+ }
94348
+ function listChannelSummaries() {
94349
+ const registry = getChannelRegistry();
94350
+ const channelId = "telegram";
94351
+ const config = readChannelConfig(channelId);
94352
+ if (!config) {
94353
+ return [
94354
+ {
94355
+ channelId,
94356
+ displayName: CHANNEL_DISPLAY_NAMES[channelId],
94357
+ configured: false,
94358
+ enabled: false,
94359
+ running: false,
94360
+ dmPolicy: null,
94361
+ pendingPairingsCount: 0,
94362
+ approvedUsersCount: 0,
94363
+ routesCount: 0
94364
+ }
94365
+ ];
94366
+ }
94367
+ loadRoutes(channelId);
94368
+ loadPairingStore(channelId);
94369
+ return [
94370
+ {
94371
+ channelId,
94372
+ displayName: CHANNEL_DISPLAY_NAMES[channelId],
94373
+ configured: true,
94374
+ enabled: config.enabled,
94375
+ running: registry?.getAdapter(channelId)?.isRunning() ?? false,
94376
+ dmPolicy: config.dmPolicy,
94377
+ pendingPairingsCount: getPendingPairings(channelId).length,
94378
+ approvedUsersCount: getApprovedUsers(channelId).length,
94379
+ routesCount: getRoutesForChannel(channelId).length
94380
+ }
94381
+ ];
94382
+ }
94383
+ function getChannelConfigSnapshot(channelId) {
94384
+ assertSupportedChannelId(channelId);
94385
+ const config = readChannelConfig(channelId);
94386
+ if (!config) {
94387
+ return null;
94388
+ }
94389
+ return toConfigSnapshot(channelId, config);
94390
+ }
94391
+ async function setChannelConfigLive(channelId, patch) {
94392
+ assertSupportedChannelId(channelId);
94393
+ const existing = readChannelConfig(channelId);
94394
+ const merged = {
94395
+ channel: "telegram",
94396
+ enabled: existing?.enabled ?? false,
94397
+ token: patch.token ?? existing?.token ?? "",
94398
+ dmPolicy: patch.dmPolicy ?? existing?.dmPolicy ?? "pairing",
94399
+ allowedUsers: patch.allowedUsers ?? existing?.allowedUsers ?? []
94400
+ };
94401
+ writeChannelConfig(channelId, merged);
94402
+ if (merged.enabled) {
94403
+ const registry = ensureChannelRegistry();
94404
+ await registry.startChannel(channelId);
94405
+ }
94406
+ return toConfigSnapshot(channelId, merged);
94407
+ }
94408
+ async function startChannelLive(channelId) {
94409
+ assertSupportedChannelId(channelId);
94410
+ const existing = readChannelConfig(channelId);
94411
+ if (!existing) {
94412
+ throw new Error(`Channel "${channelId}" is not configured. Configure it first.`);
94413
+ }
94414
+ if (!existing.token.trim()) {
94415
+ throw new Error(`Channel "${channelId}" is missing a token. Configure it first.`);
94416
+ }
94417
+ if (!existing.enabled) {
94418
+ writeChannelConfig(channelId, {
94419
+ ...existing,
94420
+ enabled: true
94421
+ });
94422
+ }
94423
+ if (!getChannelRegistry()) {
94424
+ await initializeChannels([channelId]);
94425
+ } else {
94426
+ await ensureChannelRegistry().startChannel(channelId);
94427
+ }
94428
+ const summary = listChannelSummaries().find((entry) => entry.channelId === channelId);
94429
+ if (!summary) {
94430
+ throw new Error(`Channel "${channelId}" summary not found after start`);
94431
+ }
94432
+ return summary;
94433
+ }
94434
+ async function stopChannelLive(channelId) {
94435
+ assertSupportedChannelId(channelId);
94436
+ const existing = readChannelConfig(channelId);
94437
+ if (!existing) {
94438
+ throw new Error(`Channel "${channelId}" is not configured. Configure it first.`);
94439
+ }
94440
+ writeChannelConfig(channelId, {
94441
+ ...existing,
94442
+ enabled: false
94443
+ });
94444
+ await getChannelRegistry()?.stopChannel(channelId);
94445
+ const summary = listChannelSummaries().find((entry) => entry.channelId === channelId);
94446
+ if (!summary) {
94447
+ throw new Error(`Channel "${channelId}" summary not found after stop`);
94448
+ }
94449
+ return summary;
94450
+ }
94451
+ function listPendingPairingSnapshots(channelId) {
94452
+ assertSupportedChannelId(channelId);
94453
+ loadPairingStore(channelId);
94454
+ return getPendingPairings(channelId).map(toPendingPairingSnapshot);
94455
+ }
94456
+ function bindChannelPairing(channelId, code, agentId, conversationId) {
94457
+ assertSupportedChannelId(channelId);
94458
+ loadRoutes(channelId);
94459
+ loadPairingStore(channelId);
94460
+ const result = completePairing(channelId, code, agentId, conversationId);
94461
+ if (!result.success || !result.chatId) {
94462
+ throw new Error(result.error ?? "Failed to bind pairing");
94463
+ }
94464
+ const route = getRoute(channelId, result.chatId);
94465
+ if (!route) {
94466
+ throw new Error("Pairing succeeded but route was not found");
94467
+ }
94468
+ return {
94469
+ chatId: result.chatId,
94470
+ route: toRouteSnapshot(channelId, route)
94471
+ };
94472
+ }
94473
+ function listChannelRouteSnapshots(params) {
94474
+ const channelId = params?.channelId ?? "telegram";
94475
+ assertSupportedChannelId(channelId);
94476
+ loadRoutes(channelId);
94477
+ return getRoutesForChannel(channelId).filter((route) => params?.agentId ? route.agentId === params.agentId : true).filter((route) => params?.conversationId ? route.conversationId === params.conversationId : true).map((route) => toRouteSnapshot(channelId, route));
94478
+ }
94479
+ function removeChannelRouteLive(channelId, chatId) {
94480
+ assertSupportedChannelId(channelId);
94481
+ loadRoutes(channelId);
94482
+ return removeRoute(channelId, chatId);
94483
+ }
94484
+ var CHANNEL_DISPLAY_NAMES;
94485
+ var init_service = __esm(() => {
94486
+ init_config();
94487
+ init_pairing();
94488
+ init_registry();
94489
+ init_routing();
94490
+ CHANNEL_DISPLAY_NAMES = {
94491
+ telegram: "Telegram"
94492
+ };
94493
+ });
94494
+
94192
94495
  // src/agent/memoryScanner.ts
94193
94496
  var exports_memoryScanner = {};
94194
94497
  __export(exports_memoryScanner, {
@@ -94562,6 +94865,29 @@ function emitCronsUpdated(socket, scope) {
94562
94865
  ...scope?.conversation_id !== undefined ? { conversation_id: scope.conversation_id } : {}
94563
94866
  }, "listener_cron_send_failed", "listener_cron_command");
94564
94867
  }
94868
+ function emitChannelsUpdated(socket, channelId) {
94869
+ safeSocketSend(socket, {
94870
+ type: "channels_updated",
94871
+ timestamp: Date.now(),
94872
+ ...channelId ? { channel_id: channelId } : {}
94873
+ }, "listener_channels_send_failed", "listener_channels_command");
94874
+ }
94875
+ function emitChannelPairingsUpdated(socket, channelId) {
94876
+ safeSocketSend(socket, {
94877
+ type: "channel_pairings_updated",
94878
+ timestamp: Date.now(),
94879
+ channel_id: channelId
94880
+ }, "listener_channels_send_failed", "listener_channels_command");
94881
+ }
94882
+ function emitChannelRoutesUpdated(socket, params) {
94883
+ safeSocketSend(socket, {
94884
+ type: "channel_routes_updated",
94885
+ timestamp: Date.now(),
94886
+ channel_id: params.channelId,
94887
+ ...params.agentId ? { agent_id: params.agentId } : {},
94888
+ ...params.conversationId !== undefined ? { conversation_id: params.conversationId } : {}
94889
+ }, "listener_channels_send_failed", "listener_channels_command");
94890
+ }
94565
94891
  async function handleCronCommand(parsed, socket) {
94566
94892
  if (parsed.type === "cron_list") {
94567
94893
  try {
@@ -94699,6 +95025,299 @@ async function handleCronCommand(parsed, socket) {
94699
95025
  }
94700
95026
  return true;
94701
95027
  }
95028
+ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, processQueuedTurn) {
95029
+ const {
95030
+ bindChannelPairing: bindChannelPairing2,
95031
+ getChannelConfigSnapshot: getChannelConfigSnapshot2,
95032
+ listChannelRouteSnapshots: listChannelRouteSnapshots2,
95033
+ listChannelSummaries: listChannelSummaries2,
95034
+ listPendingPairingSnapshots: listPendingPairingSnapshots2,
95035
+ removeChannelRouteLive: removeChannelRouteLive2,
95036
+ setChannelConfigLive: setChannelConfigLive2,
95037
+ startChannelLive: startChannelLive2,
95038
+ stopChannelLive: stopChannelLive2
95039
+ } = await Promise.resolve().then(() => (init_service(), exports_service));
95040
+ if (parsed.type === "channels_list") {
95041
+ try {
95042
+ safeSocketSend(socket, {
95043
+ type: "channels_list_response",
95044
+ request_id: parsed.request_id,
95045
+ success: true,
95046
+ channels: listChannelSummaries2().map((summary) => ({
95047
+ channel_id: summary.channelId,
95048
+ display_name: summary.displayName,
95049
+ configured: summary.configured,
95050
+ enabled: summary.enabled,
95051
+ running: summary.running,
95052
+ dm_policy: summary.dmPolicy,
95053
+ pending_pairings_count: summary.pendingPairingsCount,
95054
+ approved_users_count: summary.approvedUsersCount,
95055
+ routes_count: summary.routesCount
95056
+ }))
95057
+ }, "listener_channels_send_failed", "listener_channels_command");
95058
+ } catch (err) {
95059
+ safeSocketSend(socket, {
95060
+ type: "channels_list_response",
95061
+ request_id: parsed.request_id,
95062
+ success: false,
95063
+ channels: [],
95064
+ error: err instanceof Error ? err.message : "Failed to list channels"
95065
+ }, "listener_channels_send_failed", "listener_channels_command");
95066
+ }
95067
+ return true;
95068
+ }
95069
+ if (parsed.type === "channel_get_config") {
95070
+ try {
95071
+ safeSocketSend(socket, {
95072
+ type: "channel_get_config_response",
95073
+ request_id: parsed.request_id,
95074
+ success: true,
95075
+ config: (() => {
95076
+ const snapshot = getChannelConfigSnapshot2(parsed.channel_id);
95077
+ return snapshot ? {
95078
+ channel_id: snapshot.channelId,
95079
+ enabled: snapshot.enabled,
95080
+ dm_policy: snapshot.dmPolicy,
95081
+ allowed_users: snapshot.allowedUsers,
95082
+ has_token: snapshot.hasToken
95083
+ } : null;
95084
+ })()
95085
+ }, "listener_channels_send_failed", "listener_channels_command");
95086
+ } catch (err) {
95087
+ safeSocketSend(socket, {
95088
+ type: "channel_get_config_response",
95089
+ request_id: parsed.request_id,
95090
+ success: false,
95091
+ config: null,
95092
+ error: err instanceof Error ? err.message : "Failed to read channel config"
95093
+ }, "listener_channels_send_failed", "listener_channels_command");
95094
+ }
95095
+ return true;
95096
+ }
95097
+ if (parsed.type === "channel_set_config") {
95098
+ try {
95099
+ const snapshot = await setChannelConfigLive2(parsed.channel_id, {
95100
+ token: parsed.config.token,
95101
+ dmPolicy: parsed.config.dm_policy,
95102
+ allowedUsers: parsed.config.allowed_users
95103
+ });
95104
+ if (snapshot.enabled) {
95105
+ wireChannelIngress(runtime, socket, opts, processQueuedTurn);
95106
+ }
95107
+ safeSocketSend(socket, {
95108
+ type: "channel_set_config_response",
95109
+ request_id: parsed.request_id,
95110
+ success: true,
95111
+ config: {
95112
+ channel_id: snapshot.channelId,
95113
+ enabled: snapshot.enabled,
95114
+ dm_policy: snapshot.dmPolicy,
95115
+ allowed_users: snapshot.allowedUsers,
95116
+ has_token: snapshot.hasToken
95117
+ }
95118
+ }, "listener_channels_send_failed", "listener_channels_command");
95119
+ emitChannelsUpdated(socket, parsed.channel_id);
95120
+ } catch (err) {
95121
+ safeSocketSend(socket, {
95122
+ type: "channel_set_config_response",
95123
+ request_id: parsed.request_id,
95124
+ success: false,
95125
+ config: null,
95126
+ error: err instanceof Error ? err.message : "Failed to update channel config"
95127
+ }, "listener_channels_send_failed", "listener_channels_command");
95128
+ }
95129
+ return true;
95130
+ }
95131
+ if (parsed.type === "channel_start") {
95132
+ try {
95133
+ const summary = await startChannelLive2(parsed.channel_id);
95134
+ wireChannelIngress(runtime, socket, opts, processQueuedTurn);
95135
+ safeSocketSend(socket, {
95136
+ type: "channel_start_response",
95137
+ request_id: parsed.request_id,
95138
+ success: true,
95139
+ channel: {
95140
+ channel_id: summary.channelId,
95141
+ display_name: summary.displayName,
95142
+ configured: summary.configured,
95143
+ enabled: summary.enabled,
95144
+ running: summary.running,
95145
+ dm_policy: summary.dmPolicy,
95146
+ pending_pairings_count: summary.pendingPairingsCount,
95147
+ approved_users_count: summary.approvedUsersCount,
95148
+ routes_count: summary.routesCount
95149
+ }
95150
+ }, "listener_channels_send_failed", "listener_channels_command");
95151
+ emitChannelsUpdated(socket, parsed.channel_id);
95152
+ } catch (err) {
95153
+ safeSocketSend(socket, {
95154
+ type: "channel_start_response",
95155
+ request_id: parsed.request_id,
95156
+ success: false,
95157
+ channel: null,
95158
+ error: err instanceof Error ? err.message : "Failed to start channel"
95159
+ }, "listener_channels_send_failed", "listener_channels_command");
95160
+ }
95161
+ return true;
95162
+ }
95163
+ if (parsed.type === "channel_stop") {
95164
+ try {
95165
+ const summary = await stopChannelLive2(parsed.channel_id);
95166
+ safeSocketSend(socket, {
95167
+ type: "channel_stop_response",
95168
+ request_id: parsed.request_id,
95169
+ success: true,
95170
+ channel: {
95171
+ channel_id: summary.channelId,
95172
+ display_name: summary.displayName,
95173
+ configured: summary.configured,
95174
+ enabled: summary.enabled,
95175
+ running: summary.running,
95176
+ dm_policy: summary.dmPolicy,
95177
+ pending_pairings_count: summary.pendingPairingsCount,
95178
+ approved_users_count: summary.approvedUsersCount,
95179
+ routes_count: summary.routesCount
95180
+ }
95181
+ }, "listener_channels_send_failed", "listener_channels_command");
95182
+ emitChannelsUpdated(socket, parsed.channel_id);
95183
+ } catch (err) {
95184
+ safeSocketSend(socket, {
95185
+ type: "channel_stop_response",
95186
+ request_id: parsed.request_id,
95187
+ success: false,
95188
+ channel: null,
95189
+ error: err instanceof Error ? err.message : "Failed to stop channel"
95190
+ }, "listener_channels_send_failed", "listener_channels_command");
95191
+ }
95192
+ return true;
95193
+ }
95194
+ if (parsed.type === "channel_pairings_list") {
95195
+ try {
95196
+ safeSocketSend(socket, {
95197
+ type: "channel_pairings_list_response",
95198
+ request_id: parsed.request_id,
95199
+ success: true,
95200
+ channel_id: parsed.channel_id,
95201
+ pending: listPendingPairingSnapshots2(parsed.channel_id).map((pending) => ({
95202
+ code: pending.code,
95203
+ sender_id: pending.senderId,
95204
+ sender_name: pending.senderName,
95205
+ chat_id: pending.chatId,
95206
+ created_at: pending.createdAt,
95207
+ expires_at: pending.expiresAt
95208
+ }))
95209
+ }, "listener_channels_send_failed", "listener_channels_command");
95210
+ } catch (err) {
95211
+ safeSocketSend(socket, {
95212
+ type: "channel_pairings_list_response",
95213
+ request_id: parsed.request_id,
95214
+ success: false,
95215
+ channel_id: parsed.channel_id,
95216
+ pending: [],
95217
+ error: err instanceof Error ? err.message : "Failed to list pending pairings"
95218
+ }, "listener_channels_send_failed", "listener_channels_command");
95219
+ }
95220
+ return true;
95221
+ }
95222
+ if (parsed.type === "channel_pairing_bind") {
95223
+ try {
95224
+ const result = bindChannelPairing2(parsed.channel_id, parsed.code, parsed.runtime.agent_id, parsed.runtime.conversation_id);
95225
+ safeSocketSend(socket, {
95226
+ type: "channel_pairing_bind_response",
95227
+ request_id: parsed.request_id,
95228
+ success: true,
95229
+ channel_id: parsed.channel_id,
95230
+ chat_id: result.chatId,
95231
+ route: {
95232
+ channel_id: result.route.channelId,
95233
+ chat_id: result.route.chatId,
95234
+ agent_id: result.route.agentId,
95235
+ conversation_id: result.route.conversationId,
95236
+ enabled: result.route.enabled,
95237
+ created_at: result.route.createdAt
95238
+ }
95239
+ }, "listener_channels_send_failed", "listener_channels_command");
95240
+ emitChannelPairingsUpdated(socket, parsed.channel_id);
95241
+ emitChannelRoutesUpdated(socket, {
95242
+ channelId: parsed.channel_id,
95243
+ agentId: parsed.runtime.agent_id,
95244
+ conversationId: parsed.runtime.conversation_id
95245
+ });
95246
+ emitChannelsUpdated(socket, parsed.channel_id);
95247
+ } catch (err) {
95248
+ safeSocketSend(socket, {
95249
+ type: "channel_pairing_bind_response",
95250
+ request_id: parsed.request_id,
95251
+ success: false,
95252
+ channel_id: parsed.channel_id,
95253
+ route: null,
95254
+ error: err instanceof Error ? err.message : "Failed to bind pairing"
95255
+ }, "listener_channels_send_failed", "listener_channels_command");
95256
+ }
95257
+ return true;
95258
+ }
95259
+ if (parsed.type === "channel_routes_list") {
95260
+ try {
95261
+ const channelId = parsed.channel_id ?? "telegram";
95262
+ safeSocketSend(socket, {
95263
+ type: "channel_routes_list_response",
95264
+ request_id: parsed.request_id,
95265
+ success: true,
95266
+ channel_id: channelId,
95267
+ routes: listChannelRouteSnapshots2({
95268
+ channelId,
95269
+ agentId: parsed.agent_id,
95270
+ conversationId: parsed.conversation_id
95271
+ }).map((route) => ({
95272
+ channel_id: route.channelId,
95273
+ chat_id: route.chatId,
95274
+ agent_id: route.agentId,
95275
+ conversation_id: route.conversationId,
95276
+ enabled: route.enabled,
95277
+ created_at: route.createdAt
95278
+ }))
95279
+ }, "listener_channels_send_failed", "listener_channels_command");
95280
+ } catch (err) {
95281
+ safeSocketSend(socket, {
95282
+ type: "channel_routes_list_response",
95283
+ request_id: parsed.request_id,
95284
+ success: false,
95285
+ channel_id: parsed.channel_id,
95286
+ routes: [],
95287
+ error: err instanceof Error ? err.message : "Failed to list routes"
95288
+ }, "listener_channels_send_failed", "listener_channels_command");
95289
+ }
95290
+ return true;
95291
+ }
95292
+ try {
95293
+ const found = removeChannelRouteLive2(parsed.channel_id, parsed.chat_id);
95294
+ safeSocketSend(socket, {
95295
+ type: "channel_route_remove_response",
95296
+ request_id: parsed.request_id,
95297
+ success: true,
95298
+ channel_id: parsed.channel_id,
95299
+ chat_id: parsed.chat_id,
95300
+ found
95301
+ }, "listener_channels_send_failed", "listener_channels_command");
95302
+ if (found) {
95303
+ emitChannelRoutesUpdated(socket, {
95304
+ channelId: parsed.channel_id
95305
+ });
95306
+ emitChannelsUpdated(socket, parsed.channel_id);
95307
+ }
95308
+ } catch (err) {
95309
+ safeSocketSend(socket, {
95310
+ type: "channel_route_remove_response",
95311
+ request_id: parsed.request_id,
95312
+ success: false,
95313
+ channel_id: parsed.channel_id,
95314
+ chat_id: parsed.chat_id,
95315
+ found: false,
95316
+ error: err instanceof Error ? err.message : "Failed to remove route"
95317
+ }, "listener_channels_send_failed", "listener_channels_command");
95318
+ }
95319
+ return true;
95320
+ }
94702
95321
  function emitSkillsUpdated(socket) {
94703
95322
  safeSocketSend(socket, {
94704
95323
  type: "skills_updated",
@@ -94958,6 +95577,12 @@ function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
94958
95577
  enqueueChannelTurn(conversationRuntime, route, xmlContent);
94959
95578
  scheduleQueuePump(conversationRuntime, socket, opts, processQueuedTurn);
94960
95579
  });
95580
+ registry.setEventHandler((event) => {
95581
+ if (event.type === "pairings_updated") {
95582
+ emitChannelPairingsUpdated(socket, event.channelId);
95583
+ emitChannelsUpdated(socket, event.channelId);
95584
+ }
95585
+ });
94961
95586
  registry.setReady();
94962
95587
  }
94963
95588
  function enqueueChannelTurn(runtime, route, xmlContent) {
@@ -96196,6 +96821,12 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
96196
96821
  });
96197
96822
  return;
96198
96823
  }
96824
+ if (isChannelsListCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelRouteRemoveCommand(parsed)) {
96825
+ runDetachedListenerTask("channels_command", async () => {
96826
+ await handleChannelsProtocolCommand(parsed, socket, runtime, opts, processQueuedTurn);
96827
+ });
96828
+ return;
96829
+ }
96199
96830
  if (isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed)) {
96200
96831
  runDetachedListenerTask("skill_command", async () => {
96201
96832
  await handleSkillCommand(parsed, socket);
@@ -96696,6 +97327,7 @@ var init_client4 = __esm(async () => {
96696
97327
  handleAbortMessageInput,
96697
97328
  handleChangeDeviceStateInput,
96698
97329
  handleCronCommand,
97330
+ handleChannelsProtocolCommand,
96699
97331
  handleSkillCommand,
96700
97332
  handleCreateAgentCommand,
96701
97333
  handleReflectionSettingsCommand,
@@ -165182,4 +165814,4 @@ Error during initialization: ${message}`);
165182
165814
  }
165183
165815
  main();
165184
165816
 
165185
- //# debugId=CB456D0ECB3C77E564756E2164756E21
165817
+ //# debugId=25D3963E52DABC3164756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {