@letta-ai/letta-code 0.23.3 → 0.23.5

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 +997 -249
  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.23.3",
3272
+ version: "0.23.5",
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: {
@@ -8394,7 +8394,7 @@ var init_models2 = __esm(() => {
8394
8394
  id: "opus",
8395
8395
  handle: "anthropic/claude-opus-4-6",
8396
8396
  label: "Opus 4.6",
8397
- description: "Anthropic's best model (high reasoning)",
8397
+ description: "Anthropic's (legacy) best model (high reasoning)",
8398
8398
  isFeatured: true,
8399
8399
  updateArgs: {
8400
8400
  context_window: 200000,
@@ -8458,6 +8458,88 @@ var init_models2 = __esm(() => {
8458
8458
  parallel_tool_calls: true
8459
8459
  }
8460
8460
  },
8461
+ {
8462
+ id: "opus-4.7",
8463
+ handle: "anthropic/claude-opus-4-7",
8464
+ label: "Opus 4.7",
8465
+ description: "Anthropic's best model (med reasoning)",
8466
+ isFeatured: true,
8467
+ updateArgs: {
8468
+ context_window: 200000,
8469
+ max_output_tokens: 128000,
8470
+ reasoning_effort: "medium",
8471
+ enable_reasoner: true,
8472
+ max_reasoning_tokens: 12000,
8473
+ parallel_tool_calls: true
8474
+ }
8475
+ },
8476
+ {
8477
+ id: "opus-4.7-low",
8478
+ handle: "anthropic/claude-opus-4-7",
8479
+ label: "Opus 4.7",
8480
+ description: "Opus 4.7 (low reasoning)",
8481
+ updateArgs: {
8482
+ context_window: 200000,
8483
+ max_output_tokens: 128000,
8484
+ reasoning_effort: "low",
8485
+ enable_reasoner: true,
8486
+ max_reasoning_tokens: 4000,
8487
+ parallel_tool_calls: true
8488
+ }
8489
+ },
8490
+ {
8491
+ id: "opus-4.7-medium",
8492
+ handle: "anthropic/claude-opus-4-7",
8493
+ label: "Opus 4.7",
8494
+ description: "Opus 4.7 (med reasoning)",
8495
+ updateArgs: {
8496
+ context_window: 200000,
8497
+ max_output_tokens: 128000,
8498
+ reasoning_effort: "medium",
8499
+ enable_reasoner: true,
8500
+ max_reasoning_tokens: 12000,
8501
+ parallel_tool_calls: true
8502
+ }
8503
+ },
8504
+ {
8505
+ id: "opus-4.7-high",
8506
+ handle: "anthropic/claude-opus-4-7",
8507
+ label: "Opus 4.7",
8508
+ description: "Opus 4.7 (high reasoning)",
8509
+ updateArgs: {
8510
+ context_window: 200000,
8511
+ max_output_tokens: 128000,
8512
+ reasoning_effort: "high",
8513
+ enable_reasoner: true,
8514
+ parallel_tool_calls: true
8515
+ }
8516
+ },
8517
+ {
8518
+ id: "opus-4.7-xhigh",
8519
+ handle: "anthropic/claude-opus-4-7",
8520
+ label: "Opus 4.7",
8521
+ description: "Opus 4.7 (extra-high reasoning)",
8522
+ updateArgs: {
8523
+ context_window: 200000,
8524
+ max_output_tokens: 128000,
8525
+ reasoning_effort: "xhigh",
8526
+ enable_reasoner: true,
8527
+ parallel_tool_calls: true
8528
+ }
8529
+ },
8530
+ {
8531
+ id: "opus-4.7-max",
8532
+ handle: "anthropic/claude-opus-4-7",
8533
+ label: "Opus 4.7",
8534
+ description: "Opus 4.7 (max reasoning)",
8535
+ updateArgs: {
8536
+ context_window: 200000,
8537
+ max_output_tokens: 128000,
8538
+ reasoning_effort: "max",
8539
+ enable_reasoner: true,
8540
+ parallel_tool_calls: true
8541
+ }
8542
+ },
8461
8543
  {
8462
8544
  id: "opus-1m",
8463
8545
  handle: "anthropic/claude-opus-4-6",
@@ -41245,6 +41327,18 @@ function normalizeSlackAttachmentLike(value) {
41245
41327
  files
41246
41328
  };
41247
41329
  }
41330
+ function resolveSlackThreadMessageText(message) {
41331
+ const text = typeof message.text === "string" ? message.text.trim() : "";
41332
+ if (text) {
41333
+ return text;
41334
+ }
41335
+ const files = Array.isArray(message.files) ? message.files.map((entry) => normalizeSlackFileLike(entry)).filter((entry) => Boolean(entry)) : [];
41336
+ if (files.length === 0) {
41337
+ return "";
41338
+ }
41339
+ const fileNames = files.map((file) => file.name ?? "file").join(", ");
41340
+ return `[attached: ${fileNames}]`;
41341
+ }
41248
41342
  function isAllowedSlackHostname(hostname3) {
41249
41343
  const normalized = hostname3.trim().toLowerCase();
41250
41344
  return ALLOWED_SLACK_HOST_SUFFIXES.some((suffix) => normalized === suffix || normalized.endsWith(`.${suffix}`));
@@ -41413,6 +41507,78 @@ async function resolveSlackInboundAttachments(params) {
41413
41507
  }).catch(() => null)));
41414
41508
  return resolved.filter((attachment) => Boolean(attachment));
41415
41509
  }
41510
+ async function resolveSlackThreadStarter(params) {
41511
+ try {
41512
+ const response = await params.client.conversations.replies({
41513
+ channel: params.channelId,
41514
+ ts: params.threadTs,
41515
+ limit: 1,
41516
+ inclusive: true
41517
+ });
41518
+ const message = response.messages?.[0];
41519
+ if (!message) {
41520
+ return null;
41521
+ }
41522
+ const text = resolveSlackThreadMessageText(message);
41523
+ if (!text) {
41524
+ return null;
41525
+ }
41526
+ return {
41527
+ text,
41528
+ userId: isNonEmptyString(message.user) ? message.user : undefined,
41529
+ botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
41530
+ ts: isNonEmptyString(message.ts) ? message.ts : undefined
41531
+ };
41532
+ } catch {
41533
+ return null;
41534
+ }
41535
+ }
41536
+ async function resolveSlackThreadHistory(params) {
41537
+ const maxMessages = params.limit ?? 20;
41538
+ if (!Number.isFinite(maxMessages) || maxMessages <= 0) {
41539
+ return [];
41540
+ }
41541
+ const fetchLimit = 200;
41542
+ const retained = [];
41543
+ let cursor;
41544
+ try {
41545
+ do {
41546
+ const response = await params.client.conversations.replies({
41547
+ channel: params.channelId,
41548
+ ts: params.threadTs,
41549
+ limit: fetchLimit,
41550
+ inclusive: true,
41551
+ ...cursor ? { cursor } : {}
41552
+ });
41553
+ for (const message of response.messages ?? []) {
41554
+ const text = resolveSlackThreadMessageText(message);
41555
+ if (!text) {
41556
+ continue;
41557
+ }
41558
+ if (params.currentMessageTs && message.ts === params.currentMessageTs) {
41559
+ continue;
41560
+ }
41561
+ if (message.ts === params.threadTs) {
41562
+ continue;
41563
+ }
41564
+ retained.push(message);
41565
+ if (retained.length > maxMessages) {
41566
+ retained.shift();
41567
+ }
41568
+ }
41569
+ const nextCursor = response.response_metadata?.next_cursor;
41570
+ cursor = typeof nextCursor === "string" && nextCursor.trim().length > 0 ? nextCursor.trim() : undefined;
41571
+ } while (cursor);
41572
+ return retained.map((message) => ({
41573
+ text: resolveSlackThreadMessageText(message),
41574
+ userId: isNonEmptyString(message.user) ? message.user : undefined,
41575
+ botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
41576
+ ts: isNonEmptyString(message.ts) ? message.ts : undefined
41577
+ }));
41578
+ } catch {
41579
+ return [];
41580
+ }
41581
+ }
41416
41582
  var MAX_SLACK_ATTACHMENTS = 8, MAX_SLACK_ATTACHMENT_BYTES, ALLOWED_SLACK_HOST_SUFFIXES;
41417
41583
  var init_media2 = __esm(() => {
41418
41584
  init_config();
@@ -41428,6 +41594,9 @@ var init_media2 = __esm(() => {
41428
41594
  async function loadSlackBoltModule() {
41429
41595
  return loadChannelRuntimeModule("slack", "@slack/bolt");
41430
41596
  }
41597
+ async function loadSlackWebApiModule() {
41598
+ return loadChannelRuntimeModule("slack", "@slack/web-api");
41599
+ }
41431
41600
  async function ensureSlackRuntimeInstalled() {
41432
41601
  return ensureChannelRuntimeInstalled("slack");
41433
41602
  }
@@ -41457,6 +41626,22 @@ function resolveSlackAppConstructor(mod) {
41457
41626
  }
41458
41627
  return App2;
41459
41628
  }
41629
+ function resolveSlackWebClientModule(value) {
41630
+ if (!value || typeof value !== "object") {
41631
+ return null;
41632
+ }
41633
+ const webClient = Reflect.get(value, "WebClient");
41634
+ return isConstructorFunction(webClient) ? webClient : null;
41635
+ }
41636
+ function resolveSlackWebClientConstructor(mod) {
41637
+ const defaultExport = mod && typeof mod === "object" ? Reflect.get(mod, "default") : undefined;
41638
+ const nestedDefault = defaultExport && typeof defaultExport === "object" ? Reflect.get(defaultExport, "default") : undefined;
41639
+ const WebClient = resolveSlackWebClientModule(mod) ?? resolveSlackWebClientModule(defaultExport) ?? resolveSlackWebClientModule(nestedDefault) ?? (isConstructorFunction(defaultExport) ? defaultExport : null);
41640
+ if (!WebClient) {
41641
+ throw new Error('Installed Slack runtime did not export constructor "WebClient".');
41642
+ }
41643
+ return WebClient;
41644
+ }
41460
41645
  function isNonEmptyString2(value) {
41461
41646
  return typeof value === "string" && value.length > 0;
41462
41647
  }
@@ -41499,7 +41684,7 @@ function resolveUploadMimeType(filePath) {
41499
41684
  return;
41500
41685
  }
41501
41686
  }
41502
- async function uploadSlackFile(slackApp, msg) {
41687
+ async function uploadSlackFile(slackClient, msg) {
41503
41688
  if (!msg.mediaPath) {
41504
41689
  throw new Error("mediaPath is required for Slack file uploads.");
41505
41690
  }
@@ -41507,7 +41692,7 @@ async function uploadSlackFile(slackApp, msg) {
41507
41692
  const uploadFileName = msg.fileName ?? basename4(msg.mediaPath);
41508
41693
  const uploadTitle = msg.title ?? uploadFileName;
41509
41694
  const uploadMimeType = resolveUploadMimeType(uploadFileName);
41510
- const uploadUrlResp = await slackApp.client.files.getUploadURLExternal({
41695
+ const uploadUrlResp = await slackClient.files.getUploadURLExternal({
41511
41696
  filename: uploadFileName,
41512
41697
  length: buffer.length
41513
41698
  });
@@ -41522,7 +41707,7 @@ async function uploadSlackFile(slackApp, msg) {
41522
41707
  if (!uploadResp.ok) {
41523
41708
  throw new Error(`Failed to upload Slack file: HTTP ${uploadResp.status}`);
41524
41709
  }
41525
- const completeResp = await slackApp.client.files.completeUploadExternal({
41710
+ const completeResp = await slackClient.files.completeUploadExternal({
41526
41711
  files: [{ id: uploadUrlResp.file_id, title: uploadTitle }],
41527
41712
  channel_id: msg.chatId,
41528
41713
  ...msg.text.trim() ? { initial_comment: msg.text } : {},
@@ -41538,6 +41723,27 @@ function resolveSlackUserDisplayName(userInfo) {
41538
41723
  const profile = asRecord2(user?.profile);
41539
41724
  return firstNonEmptyString(profile?.display_name, profile?.real_name, user?.name);
41540
41725
  }
41726
+ function truncateSlackThreadLabel(text, maxLength = 80) {
41727
+ const normalized = text.replace(/\s+/g, " ").trim();
41728
+ if (!normalized) {
41729
+ return null;
41730
+ }
41731
+ if (normalized.length <= maxLength) {
41732
+ return normalized;
41733
+ }
41734
+ return `${normalized.slice(0, maxLength - 1).trimEnd()}…`;
41735
+ }
41736
+ function buildSlackThreadLabel(msg, starterText) {
41737
+ if (msg.chatType !== "channel") {
41738
+ return;
41739
+ }
41740
+ const roomLabel = isNonEmptyString2(msg.chatLabel) && msg.chatLabel !== msg.chatId ? ` in ${msg.chatLabel}` : "";
41741
+ const preview = truncateSlackThreadLabel(starterText ?? msg.text);
41742
+ if (preview) {
41743
+ return `Slack thread${roomLabel}: ${preview}`;
41744
+ }
41745
+ return roomLabel ? `Slack thread${roomLabel}` : `Slack thread ${msg.chatId}`;
41746
+ }
41541
41747
  async function resolveSlackAccountDisplayName(botToken, appToken) {
41542
41748
  const bolt = await loadSlackBoltModule();
41543
41749
  const App2 = resolveSlackAppConstructor(bolt);
@@ -41563,11 +41769,14 @@ async function resolveSlackAccountDisplayName(botToken, appToken) {
41563
41769
  }
41564
41770
  function createSlackAdapter(config) {
41565
41771
  let app = null;
41772
+ let writeClient = null;
41566
41773
  let running = false;
41567
41774
  let botUserId = null;
41568
41775
  const knownThreadIdsByMessageId = new Map;
41569
41776
  const knownUserDisplayNames = new Map;
41570
41777
  const seenIngressMessageKeys = new Map;
41778
+ const lifecycleStateByMessageKey = new Map;
41779
+ const lifecycleOperationByMessageKey = new Map;
41571
41780
  function buildIngressMessageKey(channelId, messageId) {
41572
41781
  if (!isNonEmptyString2(channelId) || !isNonEmptyString2(messageId)) {
41573
41782
  return null;
@@ -41592,6 +41801,96 @@ function createSlackAdapter(config) {
41592
41801
  }
41593
41802
  }
41594
41803
  }
41804
+ function getLifecycleMessageKey(source) {
41805
+ if (source.channel !== "slack" || !isNonEmptyString2(source.chatId) || !isNonEmptyString2(source.messageId)) {
41806
+ return null;
41807
+ }
41808
+ return `${source.chatId}:${source.messageId}`;
41809
+ }
41810
+ function pruneLifecycleState(now = Date.now()) {
41811
+ for (const [key, entry] of lifecycleStateByMessageKey) {
41812
+ if (entry.updatedAt + SLACK_LIFECYCLE_STATE_TTL_MS <= now) {
41813
+ lifecycleStateByMessageKey.delete(key);
41814
+ }
41815
+ }
41816
+ if (lifecycleStateByMessageKey.size <= SLACK_LIFECYCLE_STATE_MAX) {
41817
+ return;
41818
+ }
41819
+ const oldestEntries = Array.from(lifecycleStateByMessageKey.entries()).sort((a, b) => a[1].updatedAt - b[1].updatedAt);
41820
+ const overflowCount = lifecycleStateByMessageKey.size - SLACK_LIFECYCLE_STATE_MAX;
41821
+ for (let index = 0;index < overflowCount; index += 1) {
41822
+ const entry = oldestEntries[index];
41823
+ if (entry) {
41824
+ lifecycleStateByMessageKey.delete(entry[0]);
41825
+ }
41826
+ }
41827
+ }
41828
+ async function sendLifecycleReaction(source, emoji, removeReaction = false) {
41829
+ if (!isNonEmptyString2(source.messageId)) {
41830
+ return;
41831
+ }
41832
+ await ensureApp();
41833
+ const slackClient = await ensureWriteClient();
41834
+ if (removeReaction) {
41835
+ await slackClient.reactions.remove({
41836
+ channel: source.chatId,
41837
+ timestamp: source.messageId,
41838
+ name: emoji
41839
+ });
41840
+ return;
41841
+ }
41842
+ await slackClient.reactions.add({
41843
+ channel: source.chatId,
41844
+ timestamp: source.messageId,
41845
+ name: emoji
41846
+ });
41847
+ }
41848
+ function scheduleLifecycleTransition(source, nextState) {
41849
+ const key = getLifecycleMessageKey(source);
41850
+ if (!key) {
41851
+ return null;
41852
+ }
41853
+ const previous = lifecycleOperationByMessageKey.get(key) ?? Promise.resolve();
41854
+ const operation = previous.catch(() => {}).then(async () => {
41855
+ pruneLifecycleState();
41856
+ const currentState = lifecycleStateByMessageKey.get(key)?.state;
41857
+ if (currentState === nextState) {
41858
+ lifecycleStateByMessageKey.set(key, {
41859
+ state: nextState,
41860
+ updatedAt: Date.now()
41861
+ });
41862
+ return;
41863
+ }
41864
+ if (nextState === "queued") {
41865
+ if (!currentState) {
41866
+ await sendLifecycleReaction(source, "eyes");
41867
+ lifecycleStateByMessageKey.set(key, {
41868
+ state: nextState,
41869
+ updatedAt: Date.now()
41870
+ });
41871
+ }
41872
+ return;
41873
+ }
41874
+ if (currentState === "queued") {
41875
+ try {
41876
+ await sendLifecycleReaction(source, "eyes", true);
41877
+ } catch {}
41878
+ }
41879
+ await sendLifecycleReaction(source, nextState === "completed" ? "white_check_mark" : "x");
41880
+ lifecycleStateByMessageKey.set(key, {
41881
+ state: nextState,
41882
+ updatedAt: Date.now()
41883
+ });
41884
+ }).catch((error) => {
41885
+ console.warn(`[Slack] Failed to update lifecycle reaction for ${key}:`, error instanceof Error ? error.message : error);
41886
+ }).finally(() => {
41887
+ if (lifecycleOperationByMessageKey.get(key) === operation) {
41888
+ lifecycleOperationByMessageKey.delete(key);
41889
+ }
41890
+ });
41891
+ lifecycleOperationByMessageKey.set(key, operation);
41892
+ return operation;
41893
+ }
41595
41894
  function markIngressMessageSeen(channelId, messageId) {
41596
41895
  const key = buildIngressMessageKey(channelId, messageId);
41597
41896
  if (!key) {
@@ -41815,6 +42114,19 @@ function createSlackAdapter(config) {
41815
42114
  app = instance;
41816
42115
  return instance;
41817
42116
  }
42117
+ async function ensureWriteClient() {
42118
+ if (writeClient) {
42119
+ return writeClient;
42120
+ }
42121
+ const webApi = await loadSlackWebApiModule();
42122
+ const WebClient = resolveSlackWebClientConstructor(webApi);
42123
+ writeClient = new WebClient(config.botToken, {
42124
+ retryConfig: {
42125
+ retries: 0
42126
+ }
42127
+ });
42128
+ return writeClient;
42129
+ }
41818
42130
  const adapter = {
41819
42131
  id: `slack:${config.accountId}`,
41820
42132
  channelId: "slack",
@@ -41838,15 +42150,33 @@ function createSlackAdapter(config) {
41838
42150
  await app.stop();
41839
42151
  running = false;
41840
42152
  app = null;
42153
+ writeClient = null;
41841
42154
  botUserId = null;
41842
42155
  seenIngressMessageKeys.clear();
42156
+ lifecycleStateByMessageKey.clear();
42157
+ lifecycleOperationByMessageKey.clear();
41843
42158
  console.log("[Slack] App stopped");
41844
42159
  },
41845
42160
  isRunning() {
41846
42161
  return running;
41847
42162
  },
42163
+ async handleTurnLifecycleEvent(event) {
42164
+ if (!running) {
42165
+ return;
42166
+ }
42167
+ if (event.type === "queued") {
42168
+ await scheduleLifecycleTransition(event.source, "queued");
42169
+ return;
42170
+ }
42171
+ if (event.type === "processing") {
42172
+ return;
42173
+ }
42174
+ const nextState = event.outcome === "completed" ? "completed" : event.outcome === "cancelled" ? "cancelled" : "error";
42175
+ await Promise.all(event.sources.map((source) => scheduleLifecycleTransition(source, nextState)));
42176
+ },
41848
42177
  async sendMessage(msg) {
41849
- const slackApp = await ensureApp();
42178
+ await ensureApp();
42179
+ const slackClient = await ensureWriteClient();
41850
42180
  if (msg.reaction) {
41851
42181
  const targetMessageId = msg.targetMessageId ?? msg.replyToMessageId;
41852
42182
  if (!targetMessageId) {
@@ -41857,13 +42187,13 @@ function createSlackAdapter(config) {
41857
42187
  throw new Error("Slack reaction emoji cannot be empty.");
41858
42188
  }
41859
42189
  if (msg.removeReaction) {
41860
- await slackApp.client.reactions.remove({
42190
+ await slackClient.reactions.remove({
41861
42191
  channel: msg.chatId,
41862
42192
  timestamp: targetMessageId,
41863
42193
  name: emoji
41864
42194
  });
41865
42195
  } else {
41866
- await slackApp.client.reactions.add({
42196
+ await slackClient.reactions.add({
41867
42197
  channel: msg.chatId,
41868
42198
  timestamp: targetMessageId,
41869
42199
  name: emoji
@@ -41872,9 +42202,9 @@ function createSlackAdapter(config) {
41872
42202
  return { messageId: targetMessageId };
41873
42203
  }
41874
42204
  if (msg.mediaPath) {
41875
- return uploadSlackFile(slackApp, msg);
42205
+ return uploadSlackFile(slackClient, msg);
41876
42206
  }
41877
- const response = await slackApp.client.chat.postMessage({
42207
+ const response = await slackClient.chat.postMessage({
41878
42208
  channel: msg.chatId,
41879
42209
  text: msg.text,
41880
42210
  ...msg.threadId ?? msg.replyToMessageId ? { thread_ts: msg.threadId ?? msg.replyToMessageId } : {}
@@ -41883,22 +42213,88 @@ function createSlackAdapter(config) {
41883
42213
  return { messageId: response.ts ?? "" };
41884
42214
  },
41885
42215
  async sendDirectReply(chatId, text, options) {
41886
- const slackApp = await ensureApp();
41887
- const response = await slackApp.client.chat.postMessage({
42216
+ await ensureApp();
42217
+ const slackClient = await ensureWriteClient();
42218
+ const response = await slackClient.chat.postMessage({
41888
42219
  channel: chatId,
41889
42220
  text,
41890
42221
  ...options?.replyToMessageId ? { thread_ts: options.replyToMessageId } : {}
41891
42222
  });
41892
42223
  rememberMessageThread(response.ts, options?.replyToMessageId ?? response.ts ?? null);
41893
42224
  },
42225
+ async prepareInboundMessage(msg, options) {
42226
+ if (!options?.isFirstRouteTurn || msg.channel !== "slack" || msg.chatType !== "channel" || !isNonEmptyString2(msg.threadId) || !isNonEmptyString2(msg.messageId) || msg.threadId === msg.messageId) {
42227
+ return msg;
42228
+ }
42229
+ const slackApp = await ensureApp();
42230
+ const starter = await resolveSlackThreadStarter({
42231
+ channelId: msg.chatId,
42232
+ threadTs: msg.threadId,
42233
+ client: slackApp.client
42234
+ });
42235
+ const history = await resolveSlackThreadHistory({
42236
+ channelId: msg.chatId,
42237
+ threadTs: msg.threadId,
42238
+ client: slackApp.client,
42239
+ currentMessageTs: msg.messageId,
42240
+ limit: INITIAL_SLACK_THREAD_HISTORY_LIMIT
42241
+ });
42242
+ if (!starter && history.length === 0) {
42243
+ return msg;
42244
+ }
42245
+ const uniqueUserIds = new Set;
42246
+ if (isNonEmptyString2(starter?.userId)) {
42247
+ uniqueUserIds.add(starter.userId);
42248
+ }
42249
+ for (const entry of history) {
42250
+ if (isNonEmptyString2(entry.userId)) {
42251
+ uniqueUserIds.add(entry.userId);
42252
+ }
42253
+ }
42254
+ await Promise.all(Array.from(uniqueUserIds).map(async (userId) => {
42255
+ await resolveUserName(slackApp, userId);
42256
+ }));
42257
+ const resolveThreadSenderName = (userId, botId) => {
42258
+ if (isNonEmptyString2(userId)) {
42259
+ return knownUserDisplayNames.get(userId) ?? userId;
42260
+ }
42261
+ if (isNonEmptyString2(botId)) {
42262
+ return `Bot (${botId})`;
42263
+ }
42264
+ return;
42265
+ };
42266
+ return {
42267
+ ...msg,
42268
+ threadContext: {
42269
+ label: buildSlackThreadLabel(msg, starter?.text),
42270
+ ...starter ? {
42271
+ starter: {
42272
+ messageId: starter.ts,
42273
+ senderId: starter.userId ?? starter.botId,
42274
+ senderName: resolveThreadSenderName(starter.userId, starter.botId),
42275
+ text: starter.text
42276
+ }
42277
+ } : {},
42278
+ ...history.length > 0 ? {
42279
+ history: history.map((entry) => ({
42280
+ messageId: entry.ts,
42281
+ senderId: entry.userId ?? entry.botId,
42282
+ senderName: resolveThreadSenderName(entry.userId, entry.botId),
42283
+ text: entry.text
42284
+ }))
42285
+ } : {}
42286
+ }
42287
+ };
42288
+ },
41894
42289
  onMessage: undefined
41895
42290
  };
41896
42291
  return adapter;
41897
42292
  }
41898
- var SLACK_INGRESS_DEDUPE_TTL_MS = 60000, SLACK_INGRESS_DEDUPE_MAX = 2000;
42293
+ var INITIAL_SLACK_THREAD_HISTORY_LIMIT = 20, SLACK_INGRESS_DEDUPE_TTL_MS = 60000, SLACK_INGRESS_DEDUPE_MAX = 2000, SLACK_LIFECYCLE_STATE_TTL_MS, SLACK_LIFECYCLE_STATE_MAX = 2000;
41899
42294
  var init_adapter2 = __esm(() => {
41900
42295
  init_media2();
41901
42296
  init_runtime2();
42297
+ SLACK_LIFECYCLE_STATE_TTL_MS = 6 * 60 * 60 * 1000;
41902
42298
  });
41903
42299
 
41904
42300
  // src/channels/slack/messageActions.ts
@@ -42081,8 +42477,8 @@ var init_plugin2 = __esm(() => {
42081
42477
  metadata: {
42082
42478
  id: "slack",
42083
42479
  displayName: "Slack",
42084
- runtimePackages: ["@slack/bolt@4.7.0"],
42085
- runtimeModules: ["@slack/bolt"]
42480
+ runtimePackages: ["@slack/bolt@4.7.0", "@slack/web-api@7.15.0"],
42481
+ runtimeModules: ["@slack/bolt", "@slack/web-api"]
42086
42482
  },
42087
42483
  createAdapter(account) {
42088
42484
  return createSlackAdapter(account);
@@ -42138,8 +42534,8 @@ var init_pluginRegistry = __esm(() => {
42138
42534
  metadata: {
42139
42535
  id: "slack",
42140
42536
  displayName: "Slack",
42141
- runtimePackages: ["@slack/bolt@4.7.0"],
42142
- runtimeModules: ["@slack/bolt"]
42537
+ runtimePackages: ["@slack/bolt@4.7.0", "@slack/web-api@7.15.0"],
42538
+ runtimeModules: ["@slack/bolt", "@slack/web-api"]
42143
42539
  },
42144
42540
  load: async () => {
42145
42541
  const { slackChannelPlugin: slackChannelPlugin2 } = await Promise.resolve().then(() => (init_plugin2(), exports_plugin2));
@@ -42724,6 +43120,47 @@ function buildReactionXml(msg) {
42724
43120
  }
42725
43121
  return `<reaction ${attrs.join(" ")} />`;
42726
43122
  }
43123
+ function buildThreadContextEntryXml(tagName, entry) {
43124
+ const attrs = [];
43125
+ if (entry.senderId) {
43126
+ attrs.push(`sender_id="${escapeXmlAttribute(entry.senderId)}"`);
43127
+ }
43128
+ if (entry.senderName) {
43129
+ attrs.push(`sender_name="${escapeXmlAttribute(entry.senderName)}"`);
43130
+ }
43131
+ if (entry.messageId) {
43132
+ attrs.push(`message_id="${escapeXmlAttribute(entry.messageId)}"`);
43133
+ }
43134
+ const attrString = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
43135
+ return `<${tagName}${attrString}>
43136
+ ${escapeXmlText(entry.text)}
43137
+ </${tagName}>`;
43138
+ }
43139
+ function buildThreadContextXml(msg) {
43140
+ const threadContext = msg.threadContext;
43141
+ if (!threadContext) {
43142
+ return null;
43143
+ }
43144
+ const parts = [];
43145
+ if (threadContext.starter) {
43146
+ parts.push(buildThreadContextEntryXml("thread-starter", threadContext.starter));
43147
+ }
43148
+ const historyEntries = threadContext.history ?? [];
43149
+ if (historyEntries.length > 0) {
43150
+ parts.push([
43151
+ "<thread-history>",
43152
+ ...historyEntries.map((entry) => buildThreadContextEntryXml("thread-message", entry)),
43153
+ "</thread-history>"
43154
+ ].join(`
43155
+ `));
43156
+ }
43157
+ if (parts.length === 0) {
43158
+ return null;
43159
+ }
43160
+ const attrs = threadContext.label ? ` label="${escapeXmlAttribute(threadContext.label)}"` : "";
43161
+ return [`<thread-context${attrs}>`, ...parts, "</thread-context>"].join(`
43162
+ `);
43163
+ }
42727
43164
  function buildChannelNotificationXml(msg) {
42728
43165
  const attrs = [
42729
43166
  `source="${escapeXmlAttribute(msg.channel)}"`,
@@ -42742,8 +43179,9 @@ function buildChannelNotificationXml(msg) {
42742
43179
  const attrString = attrs.join(" ");
42743
43180
  const escapedText = msg.text ? escapeXmlText(msg.text) : "";
42744
43181
  const reactionXml = buildReactionXml(msg);
43182
+ const threadContextXml = buildThreadContextXml(msg);
42745
43183
  const attachmentXml = (msg.attachments ?? []).map(buildAttachmentXml);
42746
- const body = [reactionXml, ...attachmentXml, escapedText].filter(Boolean).join(`
43184
+ const body = [threadContextXml, reactionXml, ...attachmentXml, escapedText].filter(Boolean).join(`
42747
43185
  `);
42748
43186
  return `<channel-notification ${attrString}>
42749
43187
  ${body}
@@ -42828,6 +43266,18 @@ function buildSlackConversationSummary(msg) {
42828
43266
  }
42829
43267
  return `[Slack] Thread${channelLabel || ` ${msg.chatId}`}`;
42830
43268
  }
43269
+ function buildChannelTurnSource(route, msg) {
43270
+ return {
43271
+ channel: msg.channel,
43272
+ accountId: msg.accountId,
43273
+ chatId: msg.chatId,
43274
+ chatType: msg.chatType,
43275
+ messageId: msg.messageId,
43276
+ threadId: msg.threadId,
43277
+ agentId: route.agentId,
43278
+ conversationId: route.conversationId
43279
+ };
43280
+ }
42831
43281
  function getChannelRegistry() {
42832
43282
  return instance;
42833
43283
  }
@@ -42867,6 +43317,51 @@ class ChannelRegistry {
42867
43317
  getActiveChannelIds() {
42868
43318
  return Array.from(this.adapters.values()).filter((adapter) => adapter.isRunning()).map((adapter) => adapter.channelId ?? adapter.id);
42869
43319
  }
43320
+ async dispatchTurnLifecycleEvent(event) {
43321
+ const groups = new Map;
43322
+ const sources = event.type === "queued" ? [event.source] : event.sources;
43323
+ for (const source of sources) {
43324
+ const adapter = this.getAdapter(source.channel, source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID);
43325
+ if (!adapter?.handleTurnLifecycleEvent) {
43326
+ continue;
43327
+ }
43328
+ const groupKey = this.getAdapterKey(source.channel, source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID);
43329
+ const existing = groups.get(groupKey);
43330
+ if (existing) {
43331
+ existing.sources.push(source);
43332
+ continue;
43333
+ }
43334
+ groups.set(groupKey, {
43335
+ adapter,
43336
+ sources: [source]
43337
+ });
43338
+ }
43339
+ for (const { adapter, sources: groupedSources } of groups.values()) {
43340
+ const handleTurnLifecycleEvent = adapter.handleTurnLifecycleEvent;
43341
+ if (!handleTurnLifecycleEvent) {
43342
+ continue;
43343
+ }
43344
+ try {
43345
+ if (event.type === "queued") {
43346
+ const [firstSource] = groupedSources;
43347
+ if (!firstSource) {
43348
+ continue;
43349
+ }
43350
+ await handleTurnLifecycleEvent({
43351
+ type: "queued",
43352
+ source: firstSource
43353
+ });
43354
+ continue;
43355
+ }
43356
+ await handleTurnLifecycleEvent({
43357
+ ...event,
43358
+ sources: groupedSources
43359
+ });
43360
+ } catch (error) {
43361
+ console.error(`[Channels] Failed to handle ${event.type} lifecycle event for ${adapter.channelId ?? adapter.id}/${adapter.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID}:`, error instanceof Error ? error.message : error);
43362
+ }
43363
+ }
43364
+ }
42870
43365
  setMessageHandler(handler) {
42871
43366
  this.messageHandler = handler;
42872
43367
  }
@@ -42985,11 +43480,20 @@ class ChannelRegistry {
42985
43480
  if (!config)
42986
43481
  return;
42987
43482
  if (msg.channel === "slack" && config.channel === "slack") {
42988
- const slackRoute = await this.ensureSlackRoute(adapter, msg, config);
42989
- if (!slackRoute) {
43483
+ const slackResult = await this.ensureSlackRoute(adapter, msg, config);
43484
+ if (!slackResult) {
42990
43485
  return;
42991
43486
  }
42992
- this.deliverOrBuffer(slackRoute, formatChannelNotification(msg));
43487
+ const preparedMessage = adapter.prepareInboundMessage ? await adapter.prepareInboundMessage(msg, {
43488
+ isFirstRouteTurn: slackResult.isFirstRouteTurn
43489
+ }) : msg;
43490
+ this.deliverOrBuffer({
43491
+ route: slackResult.route,
43492
+ content: formatChannelNotification(preparedMessage),
43493
+ turnSources: [
43494
+ buildChannelTurnSource(slackResult.route, preparedMessage)
43495
+ ]
43496
+ });
42993
43497
  return;
42994
43498
  }
42995
43499
  if (config.dmPolicy === "allowlist") {
@@ -43021,7 +43525,11 @@ class ChannelRegistry {
43021
43525
  return;
43022
43526
  }
43023
43527
  const content = formatChannelNotification(msg);
43024
- this.deliverOrBuffer(route, content);
43528
+ this.deliverOrBuffer({
43529
+ route,
43530
+ content,
43531
+ turnSources: [buildChannelTurnSource(route, msg)]
43532
+ });
43025
43533
  }
43026
43534
  async createConversationForAgent(agentId, summary) {
43027
43535
  const client = await getClient();
@@ -43071,7 +43579,10 @@ class ChannelRegistry {
43071
43579
  route = getRoute(msg.channel, msg.chatId, accountId, routeThreadId);
43072
43580
  }
43073
43581
  if (route) {
43074
- return route;
43582
+ return {
43583
+ route,
43584
+ isFirstRouteTurn: false
43585
+ };
43075
43586
  }
43076
43587
  if (msg.chatType === "channel" && !msg.isMention) {
43077
43588
  return null;
@@ -43092,14 +43603,17 @@ class ChannelRegistry {
43092
43603
  type: "targets_updated",
43093
43604
  channelId: msg.channel
43094
43605
  });
43095
- return this.createSlackRoute(config, msg);
43606
+ return {
43607
+ route: await this.createSlackRoute(config, msg),
43608
+ isFirstRouteTurn: true
43609
+ };
43096
43610
  }
43097
- deliverOrBuffer(route, content) {
43611
+ deliverOrBuffer(delivery) {
43098
43612
  if (this.isReady()) {
43099
- this.messageHandler?.(route, content);
43613
+ this.messageHandler?.(delivery);
43100
43614
  return;
43101
43615
  }
43102
- this.buffer.push({ route, content });
43616
+ this.buffer.push(delivery);
43103
43617
  }
43104
43618
  flushBuffer() {
43105
43619
  if (!this.messageHandler)
@@ -43107,7 +43621,7 @@ class ChannelRegistry {
43107
43621
  while (this.buffer.length > 0) {
43108
43622
  const item = this.buffer.shift();
43109
43623
  if (item) {
43110
- this.messageHandler(item.route, item.content);
43624
+ this.messageHandler(item);
43111
43625
  }
43112
43626
  }
43113
43627
  }
@@ -43512,15 +44026,20 @@ No external channel adapters are currently running.`;
43512
44026
 
43513
44027
  Currently active channels: ${channelList}. Available actions across the active channels: ${actionList}. The JSON schema reflects the currently active channel plugins.`;
43514
44028
  }
43515
- async function resolveMessageChannelToolDiscovery() {
43516
- const activeChannels = getActiveChannelIds();
44029
+ async function resolveMessageChannelToolDiscovery(scope) {
44030
+ const scopedChannels = scope?.channels ?? [];
44031
+ const discoveryTargets = scopedChannels.length > 0 ? scopedChannels : getActiveChannelIds().map((channelId) => ({
44032
+ channelId,
44033
+ accountId: null
44034
+ }));
44035
+ const activeChannels = Array.from(new Set(discoveryTargets.map(({ channelId }) => channelId)));
43517
44036
  const actions = new Set(["send"]);
43518
44037
  const schemaContributions = [];
43519
- for (const channelId of activeChannels) {
44038
+ for (const { channelId, accountId } of discoveryTargets) {
43520
44039
  try {
43521
44040
  const plugin = await loadChannelPlugin(channelId);
43522
44041
  const discovery = plugin.messageActions?.describeMessageTool({
43523
- accountId: null
44042
+ accountId: accountId ?? null
43524
44043
  });
43525
44044
  for (const action of collectDiscoveryActions(discovery)) {
43526
44045
  actions.add(action);
@@ -43536,16 +44055,18 @@ async function resolveMessageChannelToolDiscovery() {
43536
44055
  schemaContributions
43537
44056
  };
43538
44057
  }
43539
- async function buildDynamicMessageChannelToolDefinition(baseDescription, baseSchema) {
43540
- const discovery = await resolveMessageChannelToolDiscovery();
44058
+ async function buildDynamicMessageChannelToolDefinition(baseDescription, baseSchema, scope) {
44059
+ const discovery = await resolveMessageChannelToolDiscovery(scope);
43541
44060
  const resolved = {
43542
44061
  description: buildDynamicMessageChannelDescriptionFromDiscovery(baseDescription, discovery),
43543
44062
  schema: buildDynamicMessageChannelSchemaFromDiscovery(baseSchema, discovery)
43544
44063
  };
43545
- cachedDynamicMessageChannelTool = {
43546
- description: resolved.description,
43547
- schema: structuredClone(resolved.schema)
43548
- };
44064
+ if (!scope || scope.channels.length === 0) {
44065
+ cachedDynamicMessageChannelTool = {
44066
+ description: resolved.description,
44067
+ schema: structuredClone(resolved.schema)
44068
+ };
44069
+ }
43549
44070
  return resolved;
43550
44071
  }
43551
44072
  function getCachedDynamicMessageChannelToolDefinition() {
@@ -45239,7 +45760,7 @@ function getPreferredAgentModelHandle(agent) {
45239
45760
  }
45240
45761
  return buildModelHandleFromLlmConfig(agent.llm_config);
45241
45762
  }
45242
- function getToolNamesForToolset(toolsetName) {
45763
+ function getToolNamesForToolset(toolsetName, channelToolScope) {
45243
45764
  let tools;
45244
45765
  switch (toolsetName) {
45245
45766
  case "codex":
@@ -45260,7 +45781,8 @@ function getToolNamesForToolset(toolsetName) {
45260
45781
  tools = [...ANTHROPIC_DEFAULT_TOOLS];
45261
45782
  break;
45262
45783
  }
45263
- if (getActiveChannelIds().length > 0 && !tools.includes("MessageChannel")) {
45784
+ const hasScopedChannelTool = channelToolScope !== undefined ? (channelToolScope?.channels.length ?? 0) > 0 : (getChannelRegistry()?.getActiveChannelIds().length ?? 0) > 0;
45785
+ if (hasScopedChannelTool && !tools.includes("MessageChannel")) {
45264
45786
  tools.push("MessageChannel");
45265
45787
  }
45266
45788
  return tools;
@@ -45271,14 +45793,16 @@ async function prepareToolExecutionContextForResolvedTarget(params) {
45271
45793
  toolsetPreference,
45272
45794
  exclude,
45273
45795
  workingDirectory,
45274
- permissionModeState
45796
+ permissionModeState,
45797
+ channelToolScope
45275
45798
  } = params;
45276
45799
  const effectiveModel = modelIdentifier && modelIdentifier.length > 0 ? resolveModel2(modelIdentifier) ?? modelIdentifier : null;
45277
45800
  if (toolsetPreference === "auto") {
45278
45801
  const preparedToolContext2 = await prepareToolExecutionContextForModel(effectiveModel ?? undefined, {
45279
45802
  exclude,
45280
45803
  workingDirectory,
45281
- permissionModeState
45804
+ permissionModeState,
45805
+ channelToolScope
45282
45806
  });
45283
45807
  return {
45284
45808
  preparedToolContext: preparedToolContext2,
@@ -45287,9 +45811,10 @@ async function prepareToolExecutionContextForResolvedTarget(params) {
45287
45811
  effectiveModel
45288
45812
  };
45289
45813
  }
45290
- const preparedToolContext = await prepareToolExecutionContextForSpecificTools(getToolNamesForToolset(toolsetPreference).filter((toolName) => exclude ? !exclude.includes(toolName) : true), {
45814
+ const preparedToolContext = await prepareToolExecutionContextForSpecificTools(getToolNamesForToolset(toolsetPreference, channelToolScope).filter((toolName) => exclude ? !exclude.includes(toolName) : true), {
45291
45815
  workingDirectory,
45292
- permissionModeState
45816
+ permissionModeState,
45817
+ channelToolScope
45293
45818
  });
45294
45819
  return {
45295
45820
  preparedToolContext,
@@ -45298,6 +45823,36 @@ async function prepareToolExecutionContextForResolvedTarget(params) {
45298
45823
  effectiveModel
45299
45824
  };
45300
45825
  }
45826
+ function resolveConversationChannelToolScope(agentId, conversationId) {
45827
+ const registry = getChannelRegistry();
45828
+ if (!registry) {
45829
+ return { channels: [] };
45830
+ }
45831
+ const channels = [];
45832
+ const seen = new Set;
45833
+ for (const channelId of SUPPORTED_CHANNEL_IDS) {
45834
+ loadRoutes(channelId);
45835
+ for (const route of getRoutesForChannel(channelId)) {
45836
+ if (route.agentId !== agentId || route.conversationId !== conversationId || !route.enabled) {
45837
+ continue;
45838
+ }
45839
+ const adapter = registry.getAdapter(channelId, route.accountId);
45840
+ if (!adapter?.isRunning()) {
45841
+ continue;
45842
+ }
45843
+ const key = `${channelId}:${route.accountId ?? ""}`;
45844
+ if (seen.has(key)) {
45845
+ continue;
45846
+ }
45847
+ seen.add(key);
45848
+ channels.push({
45849
+ channelId,
45850
+ accountId: route.accountId ?? null
45851
+ });
45852
+ }
45853
+ }
45854
+ return { channels };
45855
+ }
45301
45856
  async function prepareToolExecutionContextForScope(params) {
45302
45857
  const {
45303
45858
  agentId,
@@ -45332,7 +45887,8 @@ async function prepareToolExecutionContextForScope(params) {
45332
45887
  toolsetPreference,
45333
45888
  exclude,
45334
45889
  workingDirectory,
45335
- permissionModeState
45890
+ permissionModeState,
45891
+ channelToolScope: resolveConversationChannelToolScope(agentId, conversationId ?? "default")
45336
45892
  });
45337
45893
  }
45338
45894
  async function ensureCorrectMemoryTool(agentId, modelIdentifier, useMemoryPatch) {
@@ -45489,6 +46045,8 @@ var init_toolset = __esm(async () => {
45489
46045
  init_client2();
45490
46046
  init_model2();
45491
46047
  init_registry();
46048
+ init_routing();
46049
+ init_types();
45492
46050
  init_settings_manager();
45493
46051
  init_filter();
45494
46052
  await init_manager3();
@@ -45573,10 +46131,12 @@ var exports_memoryFilesystem = {};
45573
46131
  __export(exports_memoryFilesystem, {
45574
46132
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
45575
46133
  labelFromRelativePath: () => labelFromRelativePath,
46134
+ isMemfsEnabledOnServer: () => isMemfsEnabledOnServer,
45576
46135
  isLettaCloud: () => isLettaCloud,
45577
46136
  getMemorySystemDir: () => getMemorySystemDir,
45578
46137
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
45579
46138
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
46139
+ ensureLocalMemfsCheckout: () => ensureLocalMemfsCheckout,
45580
46140
  enableMemfsIfCloud: () => enableMemfsIfCloud,
45581
46141
  applyMemfsFlags: () => applyMemfsFlags,
45582
46142
  MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES,
@@ -45606,6 +46166,23 @@ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir10()) {
45606
46166
  mkdirSync13(systemDir, { recursive: true });
45607
46167
  }
45608
46168
  }
46169
+ async function isMemfsEnabledOnServer(agentId) {
46170
+ const { getClient: getClient3 } = await Promise.resolve().then(() => (init_client2(), exports_client));
46171
+ const client = await getClient3();
46172
+ const agent = await client.agents.retrieve(agentId);
46173
+ const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
46174
+ const enabled = agent.tags?.includes(GIT_MEMORY_ENABLED_TAG2) ?? false;
46175
+ const { settingsManager: settingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
46176
+ settingsManager2.setMemfsEnabled(agentId, enabled);
46177
+ return enabled;
46178
+ }
46179
+ async function ensureLocalMemfsCheckout(agentId) {
46180
+ const { isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
46181
+ if (isGitRepo2(agentId)) {
46182
+ return;
46183
+ }
46184
+ await cloneMemoryRepo2(agentId);
46185
+ }
45609
46186
  function labelFromRelativePath(relativePath) {
45610
46187
  const normalized = relativePath.replace(/\\/g, "/");
45611
46188
  return normalized.replace(/\.md$/, "");
@@ -49851,6 +50428,19 @@ function resolveLettaInvocation(env3 = process.env, argv = process.argv, execPat
49851
50428
  }
49852
50429
  const scriptPath = argv[1] || "";
49853
50430
  if (scriptPath && isDevLettaEntryScript(scriptPath)) {
50431
+ const runtimeName = path4.basename(execPath).toLowerCase();
50432
+ if (runtimeName.includes("bun")) {
50433
+ return {
50434
+ command: execPath,
50435
+ args: [
50436
+ "--loader:.md=text",
50437
+ "--loader:.mdx=text",
50438
+ "--loader:.txt=text",
50439
+ "run",
50440
+ scriptPath
50441
+ ]
50442
+ };
50443
+ }
49854
50444
  return { command: execPath, args: [scriptPath] };
49855
50445
  }
49856
50446
  return null;
@@ -73556,20 +74146,21 @@ var init_analyzer = __esm(() => {
73556
74146
  // src/tools/manager.ts
73557
74147
  import * as nodeFs from "node:fs/promises";
73558
74148
  import * as nodePath from "node:path";
73559
- function maybeAppendChannelTools(toolNames) {
73560
- if (getActiveChannelIds().length > 0 && !toolNames.includes("MessageChannel")) {
74149
+ function maybeAppendChannelTools(toolNames, channelToolScope) {
74150
+ const hasActiveChannelTools = channelToolScope !== undefined ? (channelToolScope?.channels.length ?? 0) > 0 : getActiveChannelIds().length > 0;
74151
+ if (hasActiveChannelTools && !toolNames.includes("MessageChannel")) {
73561
74152
  return [...toolNames, "MessageChannel"];
73562
74153
  }
73563
74154
  return toolNames;
73564
74155
  }
73565
- async function maybeResolveDynamicChannelTool(name, description, schema) {
74156
+ async function maybeResolveDynamicChannelTool(name, description, schema, channelToolScope) {
73566
74157
  if (name !== "MessageChannel") {
73567
74158
  return {
73568
74159
  description,
73569
74160
  input_schema: schema
73570
74161
  };
73571
74162
  }
73572
- const resolved = await buildDynamicMessageChannelToolDefinition(description, schema);
74163
+ const resolved = await buildDynamicMessageChannelToolDefinition(description, schema, channelToolScope);
73573
74164
  return {
73574
74165
  description: resolved.description,
73575
74166
  input_schema: resolved.schema
@@ -73577,6 +74168,10 @@ async function maybeResolveDynamicChannelTool(name, description, schema) {
73577
74168
  }
73578
74169
  function withDynamicMessageChannelCache(registry) {
73579
74170
  const nextRegistry = new Map(registry);
74171
+ const existing = nextRegistry.get("MessageChannel");
74172
+ if (existing && existing.schema.description !== TOOL_DEFINITIONS.MessageChannel.description) {
74173
+ return nextRegistry;
74174
+ }
73580
74175
  if (getActiveChannelIds().length === 0) {
73581
74176
  nextRegistry.delete("MessageChannel");
73582
74177
  return nextRegistry;
@@ -73585,7 +74180,6 @@ function withDynamicMessageChannelCache(registry) {
73585
74180
  if (!cachedMessageChannel) {
73586
74181
  return nextRegistry;
73587
74182
  }
73588
- const existing = nextRegistry.get("MessageChannel");
73589
74183
  nextRegistry.set("MessageChannel", {
73590
74184
  schema: {
73591
74185
  name: "MessageChannel",
@@ -73788,7 +74382,7 @@ async function prepareCurrentToolExecutionContext(options) {
73788
74382
  }, options);
73789
74383
  }
73790
74384
  async function prepareToolExecutionContextForSpecificTools(toolNames, options) {
73791
- const toolRegistrySnapshot = await buildSpecificToolRegistry(toolNames);
74385
+ const toolRegistrySnapshot = await buildSpecificToolRegistry(toolNames, options?.channelToolScope);
73792
74386
  return capturePreparedToolExecutionContext({
73793
74387
  toolRegistry: toolRegistrySnapshot,
73794
74388
  externalTools: new Map(getExternalToolsRegistry()),
@@ -73861,7 +74455,7 @@ function maybeApplyLspReadOverride(registry) {
73861
74455
  fn: lspDefinition.impl
73862
74456
  });
73863
74457
  }
73864
- async function buildSpecificToolRegistry(toolNames) {
74458
+ async function buildSpecificToolRegistry(toolNames, channelToolScope) {
73865
74459
  const { toolFilter: toolFilter2 } = await Promise.resolve().then(() => (init_filter(), exports_filter));
73866
74460
  const newRegistry = new Map;
73867
74461
  for (const name of toolNames) {
@@ -73877,7 +74471,7 @@ async function buildSpecificToolRegistry(toolNames) {
73877
74471
  if (!definition.impl) {
73878
74472
  throw new Error(`Tool implementation not found for ${internalName}`);
73879
74473
  }
73880
- const resolvedTool = await maybeResolveDynamicChannelTool(internalName, definition.description, definition.schema);
74474
+ const resolvedTool = await maybeResolveDynamicChannelTool(internalName, definition.description, definition.schema, channelToolScope);
73881
74475
  const toolSchema = {
73882
74476
  name: internalName,
73883
74477
  description: resolvedTool.description,
@@ -73907,7 +74501,7 @@ async function resolveBaseToolNamesForModel(modelIdentifier, options) {
73907
74501
  const excludeSet = new Set(options.exclude);
73908
74502
  baseToolNames = baseToolNames.filter((name) => !excludeSet.has(name));
73909
74503
  }
73910
- baseToolNames = maybeAppendChannelTools(baseToolNames);
74504
+ baseToolNames = maybeAppendChannelTools(baseToolNames, options?.channelToolScope);
73911
74505
  return baseToolNames;
73912
74506
  }
73913
74507
  async function buildRegistryForModel(modelIdentifier, options) {
@@ -73936,7 +74530,7 @@ async function buildRegistryForModel(modelIdentifier, options) {
73936
74530
  if (name === "Task" && discoveredSubagents.length > 0) {
73937
74531
  description = injectSubagentsIntoTaskDescription(description, discoveredSubagents);
73938
74532
  }
73939
- const resolvedTool = await maybeResolveDynamicChannelTool(name, description, definition.schema);
74533
+ const resolvedTool = await maybeResolveDynamicChannelTool(name, description, definition.schema, options?.channelToolScope);
73940
74534
  const toolSchema = {
73941
74535
  name,
73942
74536
  description: resolvedTool.description,
@@ -85334,7 +85928,7 @@ __export(exports_diff, {
85334
85928
  ADV_DIFF_IGNORE_WHITESPACE: () => ADV_DIFF_IGNORE_WHITESPACE,
85335
85929
  ADV_DIFF_CONTEXT_LINES: () => ADV_DIFF_CONTEXT_LINES
85336
85930
  });
85337
- import { basename as basename7 } from "node:path";
85931
+ import { basename as basename8 } from "node:path";
85338
85932
  function readFileOrNull(p) {
85339
85933
  try {
85340
85934
  return __require("node:fs").readFileSync(p, "utf-8");
@@ -85358,7 +85952,7 @@ function applyAllOccurrences(content, oldStr, newStr) {
85358
85952
  return { ok: true, out: content.split(oldStr).join(newStr) };
85359
85953
  }
85360
85954
  function computeAdvancedDiff(input, opts) {
85361
- const fileName = basename7(input.filePath || "");
85955
+ const fileName = basename8(input.filePath || "");
85362
85956
  const fileContent = opts?.oldStrOverride !== undefined ? opts.oldStrOverride : readFileOrNull(input.filePath);
85363
85957
  if (fileContent === null && input.kind !== "write") {
85364
85958
  return { mode: "fallback", reason: "File not readable" };
@@ -85424,7 +86018,7 @@ function computeAdvancedDiff(input, opts) {
85424
86018
  return { mode: "advanced", fileName, oldStr, newStr, hunks };
85425
86019
  }
85426
86020
  function parsePatchToAdvancedDiff(patchLines, filePath) {
85427
- const fileName = basename7(filePath);
86021
+ const fileName = basename8(filePath);
85428
86022
  const hunks = [];
85429
86023
  let currentHunk = null;
85430
86024
  let oldLine = 1;
@@ -85705,8 +86299,8 @@ function isFormatterSegment(tokens) {
85705
86299
  }
85706
86300
  }
85707
86301
  function isShellExecutor2(token) {
85708
- const basename8 = token.split("/").pop() ?? token;
85709
- return ["bash", "sh", "zsh", "dash", "ksh"].includes(basename8.toLowerCase());
86302
+ const basename9 = token.split("/").pop() ?? token;
86303
+ return ["bash", "sh", "zsh", "dash", "ksh"].includes(basename9.toLowerCase());
85710
86304
  }
85711
86305
  function normalizeRawCommand(command) {
85712
86306
  if (Array.isArray(command)) {
@@ -86415,7 +87009,7 @@ var init_formatArgsDisplay = __esm(async () => {
86415
87009
  });
86416
87010
 
86417
87011
  // src/helpers/diffPreview.ts
86418
- import path21, { basename as basename8 } from "node:path";
87012
+ import path21, { basename as basename9 } from "node:path";
86419
87013
  function parseHunkLinePrefix(raw) {
86420
87014
  if (raw.length === 0) {
86421
87015
  return { type: "context", content: "" };
@@ -86521,7 +87115,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86521
87115
  filePath: resolvedFilePath,
86522
87116
  content: toolArgs.content || ""
86523
87117
  });
86524
- previews.push(toDiffPreview(result, basename8(filePath)));
87118
+ previews.push(toDiffPreview(result, basename9(filePath)));
86525
87119
  }
86526
87120
  } else if (isFileEditTool2(toolName)) {
86527
87121
  const filePath = toolArgs.file_path;
@@ -86533,7 +87127,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86533
87127
  filePath: resolvedFilePath,
86534
87128
  edits: toolArgs.edits
86535
87129
  });
86536
- previews.push(toDiffPreview(result, basename8(filePath)));
87130
+ previews.push(toDiffPreview(result, basename9(filePath)));
86537
87131
  } else {
86538
87132
  const result = computeAdvancedDiff2({
86539
87133
  kind: "edit",
@@ -86542,7 +87136,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86542
87136
  newString: toolArgs.new_string || "",
86543
87137
  replaceAll: toolArgs.replace_all
86544
87138
  });
86545
- previews.push(toDiffPreview(result, basename8(filePath)));
87139
+ previews.push(toDiffPreview(result, basename9(filePath)));
86546
87140
  }
86547
87141
  }
86548
87142
  } else if (isPatchTool2(toolName) && toolArgs.input) {
@@ -86551,7 +87145,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86551
87145
  if (op.kind === "add" || op.kind === "update") {
86552
87146
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
86553
87147
  if (result) {
86554
- previews.push(toDiffPreview(result, basename8(op.path)));
87148
+ previews.push(toDiffPreview(result, basename9(op.path)));
86555
87149
  }
86556
87150
  }
86557
87151
  }
@@ -86561,7 +87155,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86561
87155
  if (op.kind === "add" || op.kind === "update") {
86562
87156
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
86563
87157
  if (result) {
86564
- previews.push(toDiffPreview(result, basename8(op.path)));
87158
+ previews.push(toDiffPreview(result, basename9(op.path)));
86565
87159
  }
86566
87160
  }
86567
87161
  }
@@ -89655,6 +90249,65 @@ function mergeDequeuedBatchContent(items) {
89655
90249
  normalizeUserContent: (content) => content
89656
90250
  });
89657
90251
  }
90252
+ function getChannelTurnSourceKey(source) {
90253
+ return [
90254
+ source.channel,
90255
+ source.accountId ?? "",
90256
+ source.chatId,
90257
+ source.messageId ?? "",
90258
+ source.threadId ?? "",
90259
+ source.agentId,
90260
+ source.conversationId
90261
+ ].join(":");
90262
+ }
90263
+ function collectBatchChannelTurnSources(runtime, batch) {
90264
+ const seen = new Set;
90265
+ const sources = [];
90266
+ for (const item of batch.items) {
90267
+ const template = runtime.queuedMessagesByItemId.get(item.id);
90268
+ for (const source of template?.channelTurnSources ?? []) {
90269
+ const key = getChannelTurnSourceKey(source);
90270
+ if (seen.has(key)) {
90271
+ continue;
90272
+ }
90273
+ seen.add(key);
90274
+ sources.push(source);
90275
+ }
90276
+ }
90277
+ return sources.length > 0 ? sources : undefined;
90278
+ }
90279
+ async function dispatchChannelTurnLifecycleEvent(event) {
90280
+ if (event.sources.length === 0) {
90281
+ return;
90282
+ }
90283
+ const registry = getChannelRegistry();
90284
+ if (!registry) {
90285
+ return;
90286
+ }
90287
+ if (event.type === "processing") {
90288
+ await registry.dispatchTurnLifecycleEvent(event);
90289
+ return;
90290
+ }
90291
+ await registry.dispatchTurnLifecycleEvent({
90292
+ type: "finished",
90293
+ batchId: event.batchId,
90294
+ sources: event.sources,
90295
+ outcome: event.outcome,
90296
+ ...event.error ? { error: event.error } : {}
90297
+ });
90298
+ }
90299
+ function mapTurnLifecycleOutcome(lastStopReason, didThrow) {
90300
+ if (didThrow) {
90301
+ return "error";
90302
+ }
90303
+ if (lastStopReason === "cancelled") {
90304
+ return "cancelled";
90305
+ }
90306
+ if (lastStopReason && lastStopReason !== "end_turn") {
90307
+ return "error";
90308
+ }
90309
+ return "completed";
90310
+ }
89658
90311
  function isBase64ImageContentPart(part) {
89659
90312
  if (!part || typeof part !== "object") {
89660
90313
  return false;
@@ -89714,6 +90367,7 @@ function getPrimaryQueueMessageItem(items) {
89714
90367
  return null;
89715
90368
  }
89716
90369
  function buildQueuedTurnMessage(runtime, batch) {
90370
+ const channelTurnSources = collectBatchChannelTurnSources(runtime, batch);
89717
90371
  const primaryItem = getPrimaryQueueMessageItem(batch.items);
89718
90372
  if (!primaryItem) {
89719
90373
  for (const item of batch.items) {
@@ -89728,6 +90382,7 @@ function buildQueuedTurnMessage(runtime, batch) {
89728
90382
  type: "message",
89729
90383
  agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
89730
90384
  conversationId: scopeItem?.conversationId ?? runtime.conversationId,
90385
+ ...channelTurnSources ? { channelTurnSources } : {},
89731
90386
  messages: [
89732
90387
  {
89733
90388
  role: "user",
@@ -89760,6 +90415,7 @@ function buildQueuedTurnMessage(runtime, batch) {
89760
90415
  messages[firstMessageIndex] = mergedFirstMessage;
89761
90416
  return {
89762
90417
  ...template,
90418
+ ...channelTurnSources ? { channelTurnSources } : {},
89763
90419
  messages
89764
90420
  };
89765
90421
  }
@@ -89840,13 +90496,39 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
89840
90496
  return;
89841
90497
  }
89842
90498
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
90499
+ const channelTurnSources = queuedTurn.channelTurnSources ?? [];
89843
90500
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
89844
90501
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
89845
90502
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
89846
90503
  runtime.listener.lastEmittedStatus = preTurnStatus;
89847
90504
  opts.onStatusChange?.(preTurnStatus, opts.connectionId);
89848
90505
  }
89849
- await processQueuedTurn(queuedTurn, dequeuedBatch);
90506
+ if (channelTurnSources.length > 0) {
90507
+ await dispatchChannelTurnLifecycleEvent({
90508
+ type: "processing",
90509
+ batchId: dequeuedBatch.batchId,
90510
+ sources: channelTurnSources
90511
+ });
90512
+ }
90513
+ let turnError;
90514
+ let didThrow = false;
90515
+ try {
90516
+ await processQueuedTurn(queuedTurn, dequeuedBatch);
90517
+ } catch (error) {
90518
+ didThrow = true;
90519
+ turnError = error instanceof Error ? error.message : String(error);
90520
+ throw error;
90521
+ } finally {
90522
+ if (channelTurnSources.length > 0) {
90523
+ await dispatchChannelTurnLifecycleEvent({
90524
+ type: "finished",
90525
+ batchId: dequeuedBatch.batchId,
90526
+ sources: channelTurnSources,
90527
+ outcome: mapTurnLifecycleOutcome(runtime.lastStopReason, didThrow),
90528
+ ...turnError ? { error: turnError } : {}
90529
+ });
90530
+ }
90531
+ }
89850
90532
  emitListenerStatus(runtime.listener, opts.onStatusChange, opts.connectionId);
89851
90533
  evictConversationRuntimeIfIdle(runtime);
89852
90534
  }
@@ -89879,6 +90561,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
89879
90561
  });
89880
90562
  }
89881
90563
  var init_queue = __esm(async () => {
90564
+ init_registry();
89882
90565
  init_queueRuntime();
89883
90566
  init_errorReporting();
89884
90567
  init_runtime3();
@@ -90985,7 +91668,7 @@ function scanMemoryFilesystem(memoryRoot) {
90985
91668
  } catch {
90986
91669
  return;
90987
91670
  }
90988
- const relativePath = relative12(memoryRoot, fullPath);
91671
+ const relativePath = relative12(memoryRoot, fullPath).replace(/\\/g, "/");
90989
91672
  const isLast = index === sorted.length - 1;
90990
91673
  nodes.push({
90991
91674
  name: isDir ? `${name}/` : name,
@@ -91232,9 +91915,10 @@ async function applyModelUpdateForRuntime(params) {
91232
91915
  let toolsetError = null;
91233
91916
  try {
91234
91917
  await ensureCorrectMemoryTool(agentId, model.handle);
91235
- const preparedToolContext = await prepareToolExecutionContextForResolvedTarget({
91236
- modelIdentifier: model.handle,
91237
- toolsetPreference
91918
+ const preparedToolContext = await prepareToolExecutionContextForScope({
91919
+ agentId,
91920
+ conversationId,
91921
+ overrideModel: model.handle
91238
91922
  });
91239
91923
  nextToolset = preparedToolContext.toolset;
91240
91924
  nextLoadedTools = preparedToolContext.preparedToolContext.loadedToolNames;
@@ -91357,6 +92041,165 @@ function emitChannelTargetsUpdated(socket, channelId) {
91357
92041
  channel_id: channelId
91358
92042
  }, "listener_channels_send_failed", "listener_channels_command");
91359
92043
  }
92044
+ async function handleListMemoryCommand(parsed, socket, overrides = {}) {
92045
+ try {
92046
+ const {
92047
+ ensureLocalMemfsCheckout: actualEnsureLocalMemfsCheckout,
92048
+ getMemoryFilesystemRoot: actualGetMemoryFilesystemRoot,
92049
+ isMemfsEnabledOnServer: actualIsMemfsEnabledOnServer
92050
+ } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
92051
+ const ensureLocalMemfsCheckout2 = overrides.ensureLocalMemfsCheckout ?? actualEnsureLocalMemfsCheckout;
92052
+ const getMemoryFilesystemRoot2 = overrides.getMemoryFilesystemRoot ?? actualGetMemoryFilesystemRoot;
92053
+ const isMemfsEnabledOnServer2 = overrides.isMemfsEnabledOnServer ?? actualIsMemfsEnabledOnServer;
92054
+ const { scanMemoryFilesystem: scanMemoryFilesystem2, getFileNodes: getFileNodes2, readFileContent: readFileContent2 } = await Promise.resolve().then(() => (init_memoryScanner(), exports_memoryScanner));
92055
+ const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => exports_frontmatter);
92056
+ const { existsSync: existsSync26 } = await import("node:fs");
92057
+ const { join: join31, posix: posix2 } = await import("node:path");
92058
+ const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
92059
+ let memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
92060
+ const memfsEnabled = memfsInitialized ? true : await isMemfsEnabledOnServer2(parsed.agent_id);
92061
+ if (!memfsEnabled) {
92062
+ safeSocketSend(socket, {
92063
+ type: "list_memory_response",
92064
+ request_id: parsed.request_id,
92065
+ entries: [],
92066
+ done: true,
92067
+ total: 0,
92068
+ success: true,
92069
+ memfs_enabled: false,
92070
+ memfs_initialized: false
92071
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92072
+ return true;
92073
+ }
92074
+ if (!memfsInitialized) {
92075
+ await ensureLocalMemfsCheckout2(parsed.agent_id);
92076
+ memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
92077
+ }
92078
+ if (!memfsInitialized) {
92079
+ throw new Error("MemFS is enabled, but the local memory checkout could not be initialized.");
92080
+ }
92081
+ const treeNodes = scanMemoryFilesystem2(memoryRoot);
92082
+ const fileNodes = getFileNodes2(treeNodes).filter((n) => n.name.endsWith(".md"));
92083
+ const includeReferences = parsed.include_references === true;
92084
+ const allPaths = new Set(fileNodes.map((node) => node.relativePath));
92085
+ const normalizeMemoryReference = (rawReference, sourcePath) => {
92086
+ let target = rawReference.trim();
92087
+ if (!target) {
92088
+ return null;
92089
+ }
92090
+ if (target.startsWith("http://") || target.startsWith("https://") || target.startsWith("mailto:")) {
92091
+ return null;
92092
+ }
92093
+ target = target.replace(/^<|>$/g, "");
92094
+ target = target.split("#")[0] ?? "";
92095
+ target = target.split("?")[0] ?? "";
92096
+ target = target.trim().replace(/\\/g, "/");
92097
+ if (!target || target.startsWith("#")) {
92098
+ return null;
92099
+ }
92100
+ if (target.includes("|")) {
92101
+ target = target.split("|")[0] ?? "";
92102
+ }
92103
+ if (!target) {
92104
+ return null;
92105
+ }
92106
+ const sourceDir = posix2.dirname(sourcePath.replace(/\\/g, "/"));
92107
+ const candidate = target.startsWith("./") || target.startsWith("../") ? posix2.normalize(posix2.join(sourceDir, target)) : posix2.normalize(target.startsWith("/") ? target.slice(1) : target);
92108
+ if (!candidate || candidate.startsWith("../") || candidate === "." || candidate === "..") {
92109
+ return null;
92110
+ }
92111
+ const withExtension = candidate.endsWith(".md") ? candidate : `${candidate}.md`;
92112
+ const candidates = new Set([withExtension]);
92113
+ const isExplicitRelative = target.startsWith("./") || target.startsWith("../");
92114
+ if (!isExplicitRelative && !target.startsWith("/") && sourceDir && sourceDir !== ".") {
92115
+ candidates.add(posix2.normalize(posix2.join(sourceDir, withExtension)));
92116
+ }
92117
+ if (!withExtension.startsWith("system/")) {
92118
+ candidates.add(posix2.normalize(`system/${withExtension}`));
92119
+ }
92120
+ for (const resolved of candidates) {
92121
+ if (allPaths.has(resolved)) {
92122
+ return resolved;
92123
+ }
92124
+ }
92125
+ return null;
92126
+ };
92127
+ const extractMemoryReferences = (body, sourcePath) => {
92128
+ if (!body.includes("[[")) {
92129
+ return [];
92130
+ }
92131
+ const refs = new Set;
92132
+ for (const wikiMatch of body.matchAll(WIKI_LINK_REGEX)) {
92133
+ const rawTarget = wikiMatch[1];
92134
+ if (!rawTarget)
92135
+ continue;
92136
+ const normalized = normalizeMemoryReference(rawTarget, sourcePath);
92137
+ if (normalized && normalized !== sourcePath) {
92138
+ refs.add(normalized);
92139
+ }
92140
+ }
92141
+ return [...refs];
92142
+ };
92143
+ const CHUNK_SIZE = 5;
92144
+ const total = fileNodes.length;
92145
+ for (let i = 0;i < total; i += CHUNK_SIZE) {
92146
+ const chunk = fileNodes.slice(i, i + CHUNK_SIZE);
92147
+ const entries = chunk.map((node) => {
92148
+ const raw = readFileContent2(node.fullPath);
92149
+ const { frontmatter, body } = parseFrontmatter2(raw);
92150
+ const desc = frontmatter.description;
92151
+ return {
92152
+ relative_path: node.relativePath,
92153
+ is_system: node.relativePath.startsWith("system/") || node.relativePath.startsWith("system\\"),
92154
+ description: typeof desc === "string" ? desc : null,
92155
+ content: body,
92156
+ size: body.length,
92157
+ ...includeReferences ? {
92158
+ references: extractMemoryReferences(body, node.relativePath)
92159
+ } : {}
92160
+ };
92161
+ });
92162
+ const done = i + CHUNK_SIZE >= total;
92163
+ const sent = safeSocketSend(socket, {
92164
+ type: "list_memory_response",
92165
+ request_id: parsed.request_id,
92166
+ entries,
92167
+ done,
92168
+ total,
92169
+ success: true,
92170
+ memfs_enabled: true,
92171
+ memfs_initialized: true
92172
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92173
+ if (!sent) {
92174
+ return true;
92175
+ }
92176
+ }
92177
+ if (total === 0) {
92178
+ safeSocketSend(socket, {
92179
+ type: "list_memory_response",
92180
+ request_id: parsed.request_id,
92181
+ entries: [],
92182
+ done: true,
92183
+ total: 0,
92184
+ success: true,
92185
+ memfs_enabled: true,
92186
+ memfs_initialized: true
92187
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92188
+ }
92189
+ } catch (err) {
92190
+ trackListenerError("listener_list_memory_failed", err, "listener_memory_browser");
92191
+ safeSocketSend(socket, {
92192
+ type: "list_memory_response",
92193
+ request_id: parsed.request_id,
92194
+ entries: [],
92195
+ done: true,
92196
+ total: 0,
92197
+ success: false,
92198
+ error: err instanceof Error ? err.message : "Failed to list memory"
92199
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92200
+ }
92201
+ return true;
92202
+ }
91360
92203
  async function handleCronCommand(parsed, socket) {
91361
92204
  if (parsed.type === "cron_list") {
91362
92205
  try {
@@ -92210,7 +93053,7 @@ async function handleSkillCommand(parsed, socket) {
92210
93053
  symlinkSync,
92211
93054
  unlinkSync: unlinkSync8
92212
93055
  } = await import("node:fs");
92213
- const { basename: basename9, join: join31 } = await import("node:path");
93056
+ const { basename: basename10, join: join31 } = await import("node:path");
92214
93057
  const lettaHome = process.env.LETTA_HOME || join31(process.env.HOME || process.env.USERPROFILE || "~", ".letta");
92215
93058
  const globalSkillsDir = join31(lettaHome, "skills");
92216
93059
  if (parsed.type === "skill_enable") {
@@ -92234,7 +93077,7 @@ async function handleSkillCommand(parsed, socket) {
92234
93077
  }, "listener_skill_send_failed", "listener_skill_command");
92235
93078
  return true;
92236
93079
  }
92237
- const linkName = basename9(parsed.skill_path);
93080
+ const linkName = basename10(parsed.skill_path);
92238
93081
  const linkPath = join31(globalSkillsDir, linkName);
92239
93082
  mkdirSync19(globalSkillsDir, { recursive: true });
92240
93083
  if (existsSync26(linkPath)) {
@@ -92446,12 +93289,21 @@ function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
92446
93289
  const registry = getChannelRegistry();
92447
93290
  if (!registry)
92448
93291
  return;
92449
- registry.setMessageHandler((route, messageContent) => {
92450
- const rawRuntime = getOrCreateConversationRuntime(listener, route.agentId, route.conversationId);
93292
+ registry.setMessageHandler((delivery) => {
93293
+ const rawRuntime = getOrCreateConversationRuntime(listener, delivery.route.agentId, delivery.route.conversationId);
92451
93294
  if (!rawRuntime)
92452
93295
  return;
92453
93296
  const conversationRuntime = ensureConversationQueueRuntime(listener, rawRuntime);
92454
- enqueueChannelTurn(conversationRuntime, route, messageContent);
93297
+ const enqueuedItem = enqueueChannelTurn(conversationRuntime, delivery.route, delivery.content, delivery.turnSources);
93298
+ if (!enqueuedItem) {
93299
+ return;
93300
+ }
93301
+ for (const turnSource of delivery.turnSources ?? []) {
93302
+ registry.dispatchTurnLifecycleEvent({
93303
+ type: "queued",
93304
+ source: turnSource
93305
+ });
93306
+ }
92455
93307
  scheduleQueuePump(conversationRuntime, socket, opts, processQueuedTurn);
92456
93308
  });
92457
93309
  registry.setEventHandler((event) => {
@@ -92485,7 +93337,7 @@ function stampInboundUserMessageOtids(incoming) {
92485
93337
  messages
92486
93338
  };
92487
93339
  }
92488
- function enqueueChannelTurn(runtime, route, messageContent) {
93340
+ function enqueueChannelTurn(runtime, route, messageContent, turnSources) {
92489
93341
  const clientMessageId = `cm-channel-${crypto.randomUUID()}`;
92490
93342
  const enqueuedItem = runtime.queueRuntime.enqueue({
92491
93343
  kind: "message",
@@ -92502,6 +93354,7 @@ function enqueueChannelTurn(runtime, route, messageContent) {
92502
93354
  type: "message",
92503
93355
  agentId: route.agentId,
92504
93356
  conversationId: route.conversationId,
93357
+ ...turnSources?.length ? { channelTurnSources: turnSources } : {},
92505
93358
  messages: [
92506
93359
  {
92507
93360
  role: "user",
@@ -93460,144 +94313,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93460
94313
  }
93461
94314
  if (isListMemoryCommand(parsed)) {
93462
94315
  runDetachedListenerTask("list_memory", async () => {
93463
- try {
93464
- const { getMemoryFilesystemRoot: getMemoryFilesystemRoot2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
93465
- const { scanMemoryFilesystem: scanMemoryFilesystem2, getFileNodes: getFileNodes2, readFileContent: readFileContent2 } = await Promise.resolve().then(() => (init_memoryScanner(), exports_memoryScanner));
93466
- const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => exports_frontmatter);
93467
- const { existsSync: existsSync26 } = await import("node:fs");
93468
- const { join: join31, posix: posix2 } = await import("node:path");
93469
- const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
93470
- const memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
93471
- if (!memfsInitialized) {
93472
- safeSocketSend(socket, {
93473
- type: "list_memory_response",
93474
- request_id: parsed.request_id,
93475
- entries: [],
93476
- done: true,
93477
- total: 0,
93478
- success: true,
93479
- memfs_initialized: false
93480
- }, "listener_list_memory_send_failed", "listener_list_memory");
93481
- return;
93482
- }
93483
- const treeNodes = scanMemoryFilesystem2(memoryRoot);
93484
- const fileNodes = getFileNodes2(treeNodes).filter((n) => n.name.endsWith(".md"));
93485
- const includeReferences = parsed.include_references === true;
93486
- const allPaths = new Set(fileNodes.map((node) => node.relativePath));
93487
- const normalizeMemoryReference = (rawReference, sourcePath) => {
93488
- let target = rawReference.trim();
93489
- if (!target) {
93490
- return null;
93491
- }
93492
- if (target.startsWith("http://") || target.startsWith("https://") || target.startsWith("mailto:")) {
93493
- return null;
93494
- }
93495
- target = target.replace(/^<|>$/g, "");
93496
- target = target.split("#")[0] ?? "";
93497
- target = target.split("?")[0] ?? "";
93498
- target = target.trim().replace(/\\/g, "/");
93499
- if (!target || target.startsWith("#")) {
93500
- return null;
93501
- }
93502
- if (target.includes("|")) {
93503
- target = target.split("|")[0] ?? "";
93504
- }
93505
- if (!target) {
93506
- return null;
93507
- }
93508
- const sourceDir = posix2.dirname(sourcePath.replace(/\\/g, "/"));
93509
- const candidate = target.startsWith("./") || target.startsWith("../") ? posix2.normalize(posix2.join(sourceDir, target)) : posix2.normalize(target.startsWith("/") ? target.slice(1) : target);
93510
- if (!candidate || candidate.startsWith("../") || candidate === "." || candidate === "..") {
93511
- return null;
93512
- }
93513
- const withExtension = candidate.endsWith(".md") ? candidate : `${candidate}.md`;
93514
- const candidates = new Set([withExtension]);
93515
- const isExplicitRelative = target.startsWith("./") || target.startsWith("../");
93516
- if (!isExplicitRelative && !target.startsWith("/") && sourceDir && sourceDir !== ".") {
93517
- candidates.add(posix2.normalize(posix2.join(sourceDir, withExtension)));
93518
- }
93519
- if (!withExtension.startsWith("system/")) {
93520
- candidates.add(posix2.normalize(`system/${withExtension}`));
93521
- }
93522
- for (const resolved of candidates) {
93523
- if (allPaths.has(resolved)) {
93524
- return resolved;
93525
- }
93526
- }
93527
- return null;
93528
- };
93529
- const extractMemoryReferences = (body, sourcePath) => {
93530
- if (!body.includes("[[")) {
93531
- return [];
93532
- }
93533
- const refs = new Set;
93534
- for (const wikiMatch of body.matchAll(WIKI_LINK_REGEX)) {
93535
- const rawTarget = wikiMatch[1];
93536
- if (!rawTarget)
93537
- continue;
93538
- const normalized = normalizeMemoryReference(rawTarget, sourcePath);
93539
- if (normalized && normalized !== sourcePath) {
93540
- refs.add(normalized);
93541
- }
93542
- }
93543
- return [...refs];
93544
- };
93545
- const CHUNK_SIZE = 5;
93546
- const total = fileNodes.length;
93547
- for (let i = 0;i < total; i += CHUNK_SIZE) {
93548
- const chunk = fileNodes.slice(i, i + CHUNK_SIZE);
93549
- const entries = chunk.map((node) => {
93550
- const raw2 = readFileContent2(node.fullPath);
93551
- const { frontmatter, body } = parseFrontmatter2(raw2);
93552
- const desc = frontmatter.description;
93553
- return {
93554
- relative_path: node.relativePath,
93555
- is_system: node.relativePath.startsWith("system/") || node.relativePath.startsWith("system\\"),
93556
- description: typeof desc === "string" ? desc : null,
93557
- content: body,
93558
- size: body.length,
93559
- ...includeReferences ? {
93560
- references: extractMemoryReferences(body, node.relativePath)
93561
- } : {}
93562
- };
93563
- });
93564
- const done = i + CHUNK_SIZE >= total;
93565
- const sent = safeSocketSend(socket, {
93566
- type: "list_memory_response",
93567
- request_id: parsed.request_id,
93568
- entries,
93569
- done,
93570
- total,
93571
- success: true,
93572
- memfs_initialized: true
93573
- }, "listener_list_memory_send_failed", "listener_list_memory");
93574
- if (!sent) {
93575
- return;
93576
- }
93577
- }
93578
- if (total === 0) {
93579
- safeSocketSend(socket, {
93580
- type: "list_memory_response",
93581
- request_id: parsed.request_id,
93582
- entries: [],
93583
- done: true,
93584
- total: 0,
93585
- success: true,
93586
- memfs_initialized: true
93587
- }, "listener_list_memory_send_failed", "listener_list_memory");
93588
- }
93589
- } catch (err) {
93590
- trackListenerError("listener_list_memory_failed", err, "listener_memory_browser");
93591
- safeSocketSend(socket, {
93592
- type: "list_memory_response",
93593
- request_id: parsed.request_id,
93594
- entries: [],
93595
- done: true,
93596
- total: 0,
93597
- success: false,
93598
- error: err instanceof Error ? err.message : "Failed to list memory"
93599
- }, "listener_list_memory_send_failed", "listener_list_memory");
93600
- }
94316
+ await handleListMemoryCommand(parsed, socket);
93601
94317
  });
93602
94318
  return;
93603
94319
  }
@@ -94275,6 +94991,7 @@ var init_client4 = __esm(async () => {
94275
94991
  handleAbortMessageInput,
94276
94992
  handleChangeDeviceStateInput,
94277
94993
  handleCronCommand,
94994
+ handleListMemoryCommand,
94278
94995
  isDetachedChannelsCommand,
94279
94996
  handleChannelsProtocolCommand,
94280
94997
  handleSkillCommand,
@@ -115359,16 +116076,16 @@ function clipStyledSpans(spans, maxColumns) {
115359
116076
  return { spans: clipped, clipped: false };
115360
116077
  }
115361
116078
  function languageFromPath(filePath) {
115362
- const basename9 = filePath.split("/").pop() ?? filePath;
115363
- const lower = basename9.toLowerCase();
116079
+ const basename10 = filePath.split("/").pop() ?? filePath;
116080
+ const lower = basename10.toLowerCase();
115364
116081
  if (lower === "makefile")
115365
116082
  return "makefile";
115366
116083
  if (lower === "dockerfile")
115367
116084
  return "dockerfile";
115368
- const dotIdx = basename9.lastIndexOf(".");
116085
+ const dotIdx = basename10.lastIndexOf(".");
115369
116086
  if (dotIdx < 0)
115370
116087
  return;
115371
- const ext3 = basename9.slice(dotIdx + 1).toLowerCase();
116088
+ const ext3 = basename10.slice(dotIdx + 1).toLowerCase();
115372
116089
  return EXT_TO_LANG[ext3];
115373
116090
  }
115374
116091
  function colorForClassName(className, palette) {
@@ -121780,7 +122497,7 @@ var init_pasteRegistry = __esm(() => {
121780
122497
  import { execFileSync as execFileSync3 } from "node:child_process";
121781
122498
  import { existsSync as existsSync34, readFileSync as readFileSync20, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
121782
122499
  import { tmpdir as tmpdir4 } from "node:os";
121783
- import { basename as basename9, extname as extname8, isAbsolute as isAbsolute20, join as join42, resolve as resolve29 } from "node:path";
122500
+ import { basename as basename10, extname as extname8, isAbsolute as isAbsolute20, join as join42, resolve as resolve29 } from "node:path";
121784
122501
  function countLines2(text) {
121785
122502
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
121786
122503
  }
@@ -121837,7 +122554,7 @@ function translatePasteForImages(paste) {
121837
122554
  const id = allocateImage({
121838
122555
  data: b64,
121839
122556
  mediaType: mt,
121840
- filename: basename9(filePath)
122557
+ filename: basename10(filePath)
121841
122558
  });
121842
122559
  s = `[Image #${id}]`;
121843
122560
  }
@@ -123444,7 +124161,7 @@ __export(exports_custom, {
123444
124161
  });
123445
124162
  import { existsSync as existsSync36 } from "node:fs";
123446
124163
  import { readdir as readdir10, readFile as readFile14 } from "node:fs/promises";
123447
- import { basename as basename10, dirname as dirname17, join as join44 } from "node:path";
124164
+ import { basename as basename11, dirname as dirname17, join as join44 } from "node:path";
123448
124165
  async function getCustomCommands() {
123449
124166
  if (cachedCommands !== null) {
123450
124167
  return cachedCommands;
@@ -123504,7 +124221,7 @@ async function findCommandFiles(currentPath, rootPath, commands2, source2) {
123504
124221
  async function parseCommandFile(filePath, rootPath, source2) {
123505
124222
  const content = await readFile14(filePath, "utf-8");
123506
124223
  const { frontmatter, body } = parseFrontmatter(content);
123507
- const id = basename10(filePath, ".md");
124224
+ const id = basename11(filePath, ".md");
123508
124225
  const relativePath = dirname17(filePath).slice(rootPath.length);
123509
124226
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
123510
124227
  let description = getStringField(frontmatter, "description");
@@ -143505,6 +144222,10 @@ function App2({
143505
144222
  import_react104.useEffect(() => {
143506
144223
  conversationIdRef.current = conversationId;
143507
144224
  }, [conversationId]);
144225
+ const setConversationIdAndRef = import_react104.useCallback((nextConversationId) => {
144226
+ conversationIdRef.current = nextConversationId;
144227
+ setConversationId3(nextConversationId);
144228
+ }, []);
143508
144229
  const pendingTranscriptStartLineIndexRef = import_react104.useRef(null);
143509
144230
  const lastRunIdRef = import_react104.useRef(null);
143510
144231
  const resumeKey = useSuspend();
@@ -143528,10 +144249,9 @@ function App2({
143528
144249
  import_react104.useEffect(() => {
143529
144250
  if (initialConversationId !== prevInitialConversationIdRef.current) {
143530
144251
  prevInitialConversationIdRef.current = initialConversationId;
143531
- conversationIdRef.current = initialConversationId;
143532
- setConversationId3(initialConversationId);
144252
+ setConversationIdAndRef(initialConversationId);
143533
144253
  }
143534
- }, [initialConversationId]);
144254
+ }, [initialConversationId, setConversationIdAndRef]);
143535
144255
  import_react104.useEffect(() => {
143536
144256
  if (agentId) {
143537
144257
  setCurrentAgentId(agentId);
@@ -147022,7 +147742,7 @@ ${feedback}
147022
147742
  const client = await getClient();
147023
147743
  const resumeData = await getResumeData2(client, agentState, conversationId2);
147024
147744
  await maybeCarryOverActiveConversationModel(conversationId2);
147025
- setConversationId3(conversationId2);
147745
+ setConversationIdAndRef(conversationId2);
147026
147746
  pendingConversationSwitchRef.current = {
147027
147747
  origin: "fork",
147028
147748
  conversationId: conversationId2,
@@ -147087,6 +147807,7 @@ ${feedback}
147087
147807
  runEndHooks,
147088
147808
  maybeCarryOverActiveConversationModel,
147089
147809
  resetBootstrapReminderState,
147810
+ setConversationIdAndRef,
147090
147811
  setCommandRunning,
147091
147812
  setStreaming,
147092
147813
  recoverRestoredPendingApprovals,
@@ -147139,7 +147860,7 @@ ${feedback}
147139
147860
  setLlmConfig(agent.llm_config);
147140
147861
  const agentModelHandle = getPreferredAgentModelHandle2(agent);
147141
147862
  setCurrentModelHandle(agentModelHandle);
147142
- setConversationId3(targetConversationId);
147863
+ setConversationIdAndRef(targetConversationId);
147143
147864
  resetBootstrapReminderState();
147144
147865
  {
147145
147866
  const { getModelDisplayName: getModelDisplayName3 } = await Promise.resolve().then(() => (init_model2(), exports_model2));
@@ -147192,7 +147913,8 @@ ${feedback}
147192
147913
  resetDeferredToolCallCommits,
147193
147914
  resetTrajectoryBases,
147194
147915
  resetBootstrapReminderState,
147195
- resetPendingReasoningCycle
147916
+ resetPendingReasoningCycle,
147917
+ setConversationIdAndRef
147196
147918
  ]);
147197
147919
  const handleCreateNewAgent = import_react104.useCallback(async (name) => {
147198
147920
  setActiveOverlay(null);
@@ -147260,7 +147982,7 @@ ${feedback}
147260
147982
  setLlmConfig(agent.llm_config);
147261
147983
  const agentModelHandle = getPreferredAgentModelHandle2(agent);
147262
147984
  setCurrentModelHandle(agentModelHandle);
147263
- setConversationId3(targetConversationId);
147985
+ setConversationIdAndRef(targetConversationId);
147264
147986
  pendingConversationSwitchRef.current = {
147265
147987
  origin: "agent-switch",
147266
147988
  conversationId: targetConversationId,
@@ -147294,7 +148016,8 @@ ${feedback}
147294
148016
  setCommandRunning,
147295
148017
  resetDeferredToolCallCommits,
147296
148018
  resetTrajectoryBases,
147297
- resetBootstrapReminderState
148019
+ resetBootstrapReminderState,
148020
+ setConversationIdAndRef
147298
148021
  ]);
147299
148022
  const handleBashSubmit = import_react104.useCallback(async (command) => {
147300
148023
  if (bashRunning)
@@ -148235,7 +148958,7 @@ Type your task to begin the loop.`, true);
148235
148958
  hasSetConversationSummaryRef.current = true;
148236
148959
  }
148237
148960
  await maybeCarryOverActiveConversationModel(conversation.id);
148238
- setConversationId3(conversation.id);
148961
+ setConversationIdAndRef(conversation.id);
148239
148962
  pendingConversationSwitchRef.current = {
148240
148963
  origin: "new",
148241
148964
  conversationId: conversation.id,
@@ -148280,7 +149003,7 @@ Type your task to begin the loop.`, true);
148280
149003
  hasSetConversationSummaryRef.current = true;
148281
149004
  }
148282
149005
  await maybeCarryOverActiveConversationModel(forked.id);
148283
- setConversationId3(forked.id);
149006
+ setConversationIdAndRef(forked.id);
148284
149007
  pendingConversationSwitchRef.current = {
148285
149008
  origin: "fork",
148286
149009
  conversationId: forked.id,
@@ -148334,7 +149057,7 @@ Type your task to begin the loop.`, true);
148334
149057
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
148335
149058
  });
148336
149059
  await maybeCarryOverActiveConversationModel(conversation.id);
148337
- setConversationId3(conversation.id);
149060
+ setConversationIdAndRef(conversation.id);
148338
149061
  pendingConversationSwitchRef.current = {
148339
149062
  origin: "clear",
148340
149063
  conversationId: conversation.id,
@@ -148585,7 +149308,7 @@ Type your task to begin the loop.`, true);
148585
149308
  if (agentState) {
148586
149309
  const client = await getClient();
148587
149310
  const resumeData = await getResumeData2(client, agentState, targetConvId);
148588
- setConversationId3(targetConvId);
149311
+ setConversationIdAndRef(targetConvId);
148589
149312
  pendingConversationSwitchRef.current = {
148590
149313
  origin: "resume-direct",
148591
149314
  conversationId: targetConvId,
@@ -149520,7 +150243,7 @@ ${SYSTEM_REMINDER_CLOSE}
149520
150243
  name: agentName,
149521
150244
  description: agentDescription,
149522
150245
  lastRunAt: agentLastRunAt,
149523
- conversationId
150246
+ conversationId: conversationIdRef.current
149524
150247
  },
149525
150248
  state: sharedReminderStateRef.current,
149526
150249
  sessionContextReminderEnabled,
@@ -149973,6 +150696,7 @@ ${SYSTEM_REMINDER_CLOSE}
149973
150696
  agentName,
149974
150697
  agentDescription,
149975
150698
  agentLastRunAt,
150699
+ conversationId,
149976
150700
  commandRunner,
149977
150701
  handleExit,
149978
150702
  isExecutingTool,
@@ -149991,7 +150715,8 @@ ${SYSTEM_REMINDER_CLOSE}
149991
150715
  resetTrajectoryBases,
149992
150716
  sessionContextReminderEnabled,
149993
150717
  appendTaskNotificationEvents,
149994
- maybeCarryOverActiveConversationModel
150718
+ maybeCarryOverActiveConversationModel,
150719
+ setConversationIdAndRef
149995
150720
  ]);
149996
150721
  const onSubmitRef = import_react104.useRef(onSubmit);
149997
150722
  import_react104.useEffect(() => {
@@ -150991,7 +151716,7 @@ ${guidance}`);
150991
151716
  const client = await getClient();
150992
151717
  if (agentState) {
150993
151718
  const resumeData = await getResumeData2(client, agentState, action.conversationId);
150994
- setConversationId3(action.conversationId);
151719
+ setConversationIdAndRef(action.conversationId);
150995
151720
  pendingConversationSwitchRef.current = {
150996
151721
  origin: "resume-selector",
150997
151722
  conversationId: action.conversationId,
@@ -151044,7 +151769,8 @@ ${guidance}`);
151044
151769
  commandRunner.getHandle,
151045
151770
  commandRunner.start,
151046
151771
  recoverRestoredPendingApprovals,
151047
- resetBootstrapReminderState
151772
+ resetBootstrapReminderState,
151773
+ setConversationIdAndRef
151048
151774
  ]);
151049
151775
  const handleFeedbackSubmit = import_react104.useCallback(async (message) => {
151050
151776
  const overlayCommand = consumeOverlayCommand("feedback");
@@ -152302,7 +153028,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152302
153028
  if (agentState) {
152303
153029
  const client = await getClient();
152304
153030
  const resumeData = await getResumeData2(client, agentState, convId);
152305
- setConversationId3(convId);
153031
+ setConversationIdAndRef(convId);
152306
153032
  pendingConversationSwitchRef.current = {
152307
153033
  origin: "resume-selector",
152308
153034
  conversationId: convId,
@@ -152414,7 +153140,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152414
153140
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
152415
153141
  });
152416
153142
  await maybeCarryOverActiveConversationModel(conversation.id);
152417
- setConversationId3(conversation.id);
153143
+ setConversationIdAndRef(conversation.id);
152418
153144
  settingsManager.persistSession(agentId, conversation.id);
152419
153145
  const currentAgentName = agentState?.name || "Unnamed Agent";
152420
153146
  const shortConvId = conversation.id.slice(0, 20);
@@ -152498,7 +153224,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152498
153224
  if (agentState) {
152499
153225
  const client = await getClient();
152500
153226
  const resumeData = await getResumeData2(client, agentState, actualTargetConv);
152501
- setConversationId3(actualTargetConv);
153227
+ setConversationIdAndRef(actualTargetConv);
152502
153228
  pendingConversationSwitchRef.current = {
152503
153229
  origin: "search",
152504
153230
  conversationId: actualTargetConv,
@@ -153870,10 +154596,12 @@ var exports_memoryFilesystem2 = {};
153870
154596
  __export(exports_memoryFilesystem2, {
153871
154597
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree2,
153872
154598
  labelFromRelativePath: () => labelFromRelativePath2,
154599
+ isMemfsEnabledOnServer: () => isMemfsEnabledOnServer2,
153873
154600
  isLettaCloud: () => isLettaCloud2,
153874
154601
  getMemorySystemDir: () => getMemorySystemDir2,
153875
154602
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot2,
153876
154603
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs2,
154604
+ ensureLocalMemfsCheckout: () => ensureLocalMemfsCheckout2,
153877
154605
  enableMemfsIfCloud: () => enableMemfsIfCloud2,
153878
154606
  applyMemfsFlags: () => applyMemfsFlags2,
153879
154607
  MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES2,
@@ -153903,6 +154631,23 @@ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir41()) {
153903
154631
  mkdirSync30(systemDir, { recursive: true });
153904
154632
  }
153905
154633
  }
154634
+ async function isMemfsEnabledOnServer2(agentId) {
154635
+ const { getClient: getClient3 } = await Promise.resolve().then(() => (init_client2(), exports_client));
154636
+ const client = await getClient3();
154637
+ const agent = await client.agents.retrieve(agentId);
154638
+ const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
154639
+ const enabled = agent.tags?.includes(GIT_MEMORY_ENABLED_TAG2) ?? false;
154640
+ const { settingsManager: settingsManager3 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
154641
+ settingsManager3.setMemfsEnabled(agentId, enabled);
154642
+ return enabled;
154643
+ }
154644
+ async function ensureLocalMemfsCheckout2(agentId) {
154645
+ const { isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
154646
+ if (isGitRepo2(agentId)) {
154647
+ return;
154648
+ }
154649
+ await cloneMemoryRepo2(agentId);
154650
+ }
153906
154651
  function labelFromRelativePath2(relativePath) {
153907
154652
  const normalized = relativePath.replace(/\\/g, "/");
153908
154653
  return normalized.replace(/\.md$/, "");
@@ -161433,20 +162178,21 @@ await __promiseAll([
161433
162178
  init_toolDefinitions()
161434
162179
  ]);
161435
162180
  var TOOL_NAMES2 = Object.keys(TOOL_DEFINITIONS);
161436
- function maybeAppendChannelTools2(toolNames) {
161437
- if (getActiveChannelIds().length > 0 && !toolNames.includes("MessageChannel")) {
162181
+ function maybeAppendChannelTools2(toolNames, channelToolScope) {
162182
+ const hasActiveChannelTools = channelToolScope !== undefined ? (channelToolScope?.channels.length ?? 0) > 0 : getActiveChannelIds().length > 0;
162183
+ if (hasActiveChannelTools && !toolNames.includes("MessageChannel")) {
161438
162184
  return [...toolNames, "MessageChannel"];
161439
162185
  }
161440
162186
  return toolNames;
161441
162187
  }
161442
- async function maybeResolveDynamicChannelTool2(name, description, schema) {
162188
+ async function maybeResolveDynamicChannelTool2(name, description, schema, channelToolScope) {
161443
162189
  if (name !== "MessageChannel") {
161444
162190
  return {
161445
162191
  description,
161446
162192
  input_schema: schema
161447
162193
  };
161448
162194
  }
161449
- const resolved = await buildDynamicMessageChannelToolDefinition(description, schema);
162195
+ const resolved = await buildDynamicMessageChannelToolDefinition(description, schema, channelToolScope);
161450
162196
  return {
161451
162197
  description: resolved.description,
161452
162198
  input_schema: resolved.schema
@@ -161591,7 +162337,7 @@ async function resolveBaseToolNamesForModel2(modelIdentifier, options) {
161591
162337
  const excludeSet = new Set(options.exclude);
161592
162338
  baseToolNames = baseToolNames.filter((name) => !excludeSet.has(name));
161593
162339
  }
161594
- baseToolNames = maybeAppendChannelTools2(baseToolNames);
162340
+ baseToolNames = maybeAppendChannelTools2(baseToolNames, options?.channelToolScope);
161595
162341
  return baseToolNames;
161596
162342
  }
161597
162343
  async function buildRegistryForModel2(modelIdentifier, options) {
@@ -161620,7 +162366,7 @@ async function buildRegistryForModel2(modelIdentifier, options) {
161620
162366
  if (name === "Task" && discoveredSubagents.length > 0) {
161621
162367
  description = injectSubagentsIntoTaskDescription2(description, discoveredSubagents);
161622
162368
  }
161623
- const resolvedTool = await maybeResolveDynamicChannelTool2(name, description, definition.schema);
162369
+ const resolvedTool = await maybeResolveDynamicChannelTool2(name, description, definition.schema, options?.channelToolScope);
161624
162370
  const toolSchema = {
161625
162371
  name,
161626
162372
  description: resolvedTool.description,
@@ -161694,6 +162440,8 @@ ${after}`;
161694
162440
  init_client2();
161695
162441
  init_model2();
161696
162442
  init_registry();
162443
+ init_routing();
162444
+ init_types();
161697
162445
  init_settings_manager();
161698
162446
  init_filter();
161699
162447
  await init_manager3();
@@ -163129,4 +163877,4 @@ Error during initialization: ${message}`);
163129
163877
  }
163130
163878
  main();
163131
163879
 
163132
- //# debugId=41CC74E0316710DC64756E2164756E21
163880
+ //# debugId=E826850D234138B664756E2164756E21