@letta-ai/letta-code 0.23.4 → 0.23.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.4",
3272
+ version: "0.23.6",
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: {
@@ -8391,10 +8391,10 @@ var init_models2 = __esm(() => {
8391
8391
  }
8392
8392
  },
8393
8393
  {
8394
- id: "opus",
8394
+ id: "opus-4.6-high",
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,74 @@ var init_models2 = __esm(() => {
8458
8458
  parallel_tool_calls: true
8459
8459
  }
8460
8460
  },
8461
+ {
8462
+ id: "opus",
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-high",
8492
+ handle: "anthropic/claude-opus-4-7",
8493
+ label: "Opus 4.7",
8494
+ description: "Opus 4.7 (high reasoning)",
8495
+ updateArgs: {
8496
+ context_window: 200000,
8497
+ max_output_tokens: 128000,
8498
+ reasoning_effort: "high",
8499
+ enable_reasoner: true,
8500
+ parallel_tool_calls: true
8501
+ }
8502
+ },
8503
+ {
8504
+ id: "opus-4.7-xhigh",
8505
+ handle: "anthropic/claude-opus-4-7",
8506
+ label: "Opus 4.7",
8507
+ description: "Opus 4.7 (extra-high reasoning)",
8508
+ updateArgs: {
8509
+ context_window: 200000,
8510
+ max_output_tokens: 128000,
8511
+ reasoning_effort: "xhigh",
8512
+ enable_reasoner: true,
8513
+ parallel_tool_calls: true
8514
+ }
8515
+ },
8516
+ {
8517
+ id: "opus-4.7-max",
8518
+ handle: "anthropic/claude-opus-4-7",
8519
+ label: "Opus 4.7",
8520
+ description: "Opus 4.7 (max reasoning)",
8521
+ updateArgs: {
8522
+ context_window: 200000,
8523
+ max_output_tokens: 128000,
8524
+ reasoning_effort: "max",
8525
+ enable_reasoner: true,
8526
+ parallel_tool_calls: true
8527
+ }
8528
+ },
8461
8529
  {
8462
8530
  id: "opus-1m",
8463
8531
  handle: "anthropic/claude-opus-4-6",
@@ -10179,6 +10247,11 @@ function getModelInfoForLlmConfig(modelHandle, llmConfig) {
10179
10247
  const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
10180
10248
  if (match)
10181
10249
  return match;
10250
+ if (effort === "max") {
10251
+ const legacyXHighMatch = candidates.find((m) => m.updateArgs?.reasoning_effort === "xhigh");
10252
+ if (legacyXHighMatch)
10253
+ return legacyXHighMatch;
10254
+ }
10182
10255
  }
10183
10256
  if (llmConfig?.enable_reasoner === false) {
10184
10257
  const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
@@ -10295,7 +10368,8 @@ var init_model = __esm(() => {
10295
10368
  "low",
10296
10369
  "medium",
10297
10370
  "high",
10298
- "xhigh"
10371
+ "xhigh",
10372
+ "max"
10299
10373
  ];
10300
10374
  RESUME_REFRESH_FIELDS = [
10301
10375
  "max_output_tokens",
@@ -37776,6 +37850,11 @@ function getModelInfoForLlmConfig2(modelHandle, llmConfig) {
37776
37850
  const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
37777
37851
  if (match)
37778
37852
  return match;
37853
+ if (effort === "max") {
37854
+ const legacyXHighMatch = candidates.find((m) => m.updateArgs?.reasoning_effort === "xhigh");
37855
+ if (legacyXHighMatch)
37856
+ return legacyXHighMatch;
37857
+ }
37779
37858
  }
37780
37859
  if (llmConfig?.enable_reasoner === false) {
37781
37860
  const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
@@ -37892,7 +37971,8 @@ var init_model2 = __esm(() => {
37892
37971
  "low",
37893
37972
  "medium",
37894
37973
  "high",
37895
- "xhigh"
37974
+ "xhigh",
37975
+ "max"
37896
37976
  ];
37897
37977
  RESUME_REFRESH_FIELDS2 = [
37898
37978
  "max_output_tokens",
@@ -38170,6 +38250,9 @@ __export(exports_modify, {
38170
38250
  updateAgentLLMConfig: () => updateAgentLLMConfig2,
38171
38251
  recompileAgentSystemPrompt: () => recompileAgentSystemPrompt
38172
38252
  });
38253
+ function supportsDistinctAnthropicXHighEffort2(modelHandle) {
38254
+ return modelHandle.includes("claude-opus-4-7");
38255
+ }
38173
38256
  function buildModelSettings2(modelHandle, updateArgs) {
38174
38257
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
38175
38258
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -38203,10 +38286,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
38203
38286
  parallel_tool_calls: true
38204
38287
  };
38205
38288
  const effort = updateArgs?.reasoning_effort;
38289
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
38206
38290
  if (effort === "low" || effort === "medium" || effort === "high") {
38207
38291
  anthropicSettings.effort = effort;
38208
38292
  } else if (effort === "xhigh") {
38209
- anthropicSettings.effort = "max";
38293
+ anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
38294
+ } else if (effort === "max") {
38295
+ anthropicSettings.effort = effort;
38210
38296
  }
38211
38297
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38212
38298
  anthropicSettings.thinking = {
@@ -38259,10 +38345,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
38259
38345
  parallel_tool_calls: true
38260
38346
  };
38261
38347
  const effort = updateArgs?.reasoning_effort;
38348
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
38262
38349
  if (effort === "low" || effort === "medium" || effort === "high") {
38263
38350
  bedrockSettings.effort = effort;
38264
38351
  } else if (effort === "xhigh") {
38265
- bedrockSettings.effort = "max";
38352
+ bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
38353
+ } else if (effort === "max") {
38354
+ bedrockSettings.effort = effort;
38266
38355
  }
38267
38356
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38268
38357
  bedrockSettings.thinking = {
@@ -39720,6 +39809,9 @@ function normalizeLoadedAccount(account) {
39720
39809
  if (next.channel === "telegram" && (next.displayName === "Telegram bot" || next.displayName === "Migrated Telegram bot") || next.channel === "slack" && (next.displayName === "Slack app" || next.displayName === "Migrated Slack app")) {
39721
39810
  next.displayName = undefined;
39722
39811
  }
39812
+ if (next.channel === "slack") {
39813
+ next.defaultPermissionMode = next.defaultPermissionMode ?? "default";
39814
+ }
39723
39815
  return next;
39724
39816
  }
39725
39817
  function makeDefaultLegacyAccount(channelId) {
@@ -39754,6 +39846,7 @@ function makeDefaultLegacyAccount(channelId) {
39754
39846
  dmPolicy: config.dmPolicy,
39755
39847
  allowedUsers: [...config.allowedUsers],
39756
39848
  agentId: null,
39849
+ defaultPermissionMode: "default",
39757
39850
  createdAt: now,
39758
39851
  updatedAt: now
39759
39852
  };
@@ -40574,14 +40667,27 @@ function getPackageManagerExecutable(packageManager) {
40574
40667
  }
40575
40668
  return packageManager;
40576
40669
  }
40670
+ function resolveInstallPlatform() {
40671
+ return platformOverride ?? process.platform;
40672
+ }
40577
40673
  function getInstallArgs(packageManager, installPackages) {
40674
+ const noBinLinks = resolveInstallPlatform() === "win32" && packageManager !== "bun";
40578
40675
  switch (packageManager) {
40579
40676
  case "bun":
40580
40677
  return ["add", "--no-save", ...installPackages];
40581
40678
  case "pnpm":
40582
- return ["add", ...installPackages];
40679
+ return [
40680
+ "add",
40681
+ ...noBinLinks ? ["--no-bin-links"] : [],
40682
+ ...installPackages
40683
+ ];
40583
40684
  case "npm":
40584
- return ["install", "--no-save", ...installPackages];
40685
+ return [
40686
+ "install",
40687
+ "--no-save",
40688
+ ...noBinLinks ? ["--no-bin-links"] : [],
40689
+ ...installPackages
40690
+ ];
40585
40691
  }
40586
40692
  }
40587
40693
  async function installChannelRuntime(channelId) {
@@ -41214,6 +41320,14 @@ var init_plugin = __esm(() => {
41214
41320
  import { randomUUID as randomUUID4 } from "node:crypto";
41215
41321
  import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
41216
41322
  import { basename as basename3, extname as extname2, join as join12 } from "node:path";
41323
+ function mapSlackThreadMessage(message) {
41324
+ return {
41325
+ text: resolveSlackThreadMessageText(message),
41326
+ userId: isNonEmptyString(message.user) ? message.user : undefined,
41327
+ botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
41328
+ ts: isNonEmptyString(message.ts) ? message.ts : undefined
41329
+ };
41330
+ }
41217
41331
  function asRecord(value) {
41218
41332
  return value && typeof value === "object" ? value : null;
41219
41333
  }
@@ -41245,6 +41359,18 @@ function normalizeSlackAttachmentLike(value) {
41245
41359
  files
41246
41360
  };
41247
41361
  }
41362
+ function resolveSlackThreadMessageText(message) {
41363
+ const text = typeof message.text === "string" ? message.text.trim() : "";
41364
+ if (text) {
41365
+ return text;
41366
+ }
41367
+ const files = Array.isArray(message.files) ? message.files.map((entry) => normalizeSlackFileLike(entry)).filter((entry) => Boolean(entry)) : [];
41368
+ if (files.length === 0) {
41369
+ return "";
41370
+ }
41371
+ const fileNames = files.map((file) => file.name ?? "file").join(", ");
41372
+ return `[attached: ${fileNames}]`;
41373
+ }
41248
41374
  function isAllowedSlackHostname(hostname3) {
41249
41375
  const normalized = hostname3.trim().toLowerCase();
41250
41376
  return ALLOWED_SLACK_HOST_SUFFIXES.some((suffix) => normalized === suffix || normalized.endsWith(`.${suffix}`));
@@ -41413,6 +41539,97 @@ async function resolveSlackInboundAttachments(params) {
41413
41539
  }).catch(() => null)));
41414
41540
  return resolved.filter((attachment) => Boolean(attachment));
41415
41541
  }
41542
+ async function resolveSlackThreadStarter(params) {
41543
+ try {
41544
+ const response = await params.client.conversations.replies({
41545
+ channel: params.channelId,
41546
+ ts: params.threadTs,
41547
+ limit: 1,
41548
+ inclusive: true
41549
+ });
41550
+ const message = response.messages?.[0];
41551
+ if (!message) {
41552
+ return null;
41553
+ }
41554
+ const text = resolveSlackThreadMessageText(message);
41555
+ if (!text) {
41556
+ return null;
41557
+ }
41558
+ return {
41559
+ text,
41560
+ userId: isNonEmptyString(message.user) ? message.user : undefined,
41561
+ botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
41562
+ ts: isNonEmptyString(message.ts) ? message.ts : undefined
41563
+ };
41564
+ } catch {
41565
+ return null;
41566
+ }
41567
+ }
41568
+ async function resolveSlackThreadHistory(params) {
41569
+ const maxMessages = params.limit ?? 20;
41570
+ if (!Number.isFinite(maxMessages) || maxMessages <= 0) {
41571
+ return [];
41572
+ }
41573
+ const fetchLimit = 200;
41574
+ const retained = [];
41575
+ let cursor;
41576
+ try {
41577
+ do {
41578
+ const response = await params.client.conversations.replies({
41579
+ channel: params.channelId,
41580
+ ts: params.threadTs,
41581
+ limit: fetchLimit,
41582
+ inclusive: true,
41583
+ ...cursor ? { cursor } : {}
41584
+ });
41585
+ for (const message of response.messages ?? []) {
41586
+ const text = resolveSlackThreadMessageText(message);
41587
+ if (!text) {
41588
+ continue;
41589
+ }
41590
+ if (params.currentMessageTs && message.ts === params.currentMessageTs) {
41591
+ continue;
41592
+ }
41593
+ if (message.ts === params.threadTs) {
41594
+ continue;
41595
+ }
41596
+ retained.push(message);
41597
+ if (retained.length > maxMessages) {
41598
+ retained.shift();
41599
+ }
41600
+ }
41601
+ const nextCursor = response.response_metadata?.next_cursor;
41602
+ cursor = typeof nextCursor === "string" && nextCursor.trim().length > 0 ? nextCursor.trim() : undefined;
41603
+ } while (cursor);
41604
+ return retained.map(mapSlackThreadMessage);
41605
+ } catch {
41606
+ return [];
41607
+ }
41608
+ }
41609
+ async function resolveSlackChannelHistory(params) {
41610
+ const maxMessages = params.limit ?? 20;
41611
+ if (!Number.isFinite(maxMessages) || maxMessages <= 0) {
41612
+ return [];
41613
+ }
41614
+ const fetchLimit = Math.min(Math.max(maxMessages * 3, maxMessages), 100);
41615
+ try {
41616
+ const response = await params.client.conversations.history({
41617
+ channel: params.channelId,
41618
+ latest: params.beforeTs,
41619
+ inclusive: false,
41620
+ limit: fetchLimit
41621
+ });
41622
+ const retained = (response.messages ?? []).filter((message) => {
41623
+ if (message.ts === params.beforeTs) {
41624
+ return false;
41625
+ }
41626
+ return Boolean(resolveSlackThreadMessageText(message));
41627
+ }).slice(0, fetchLimit).reverse();
41628
+ return retained.slice(-maxMessages).map(mapSlackThreadMessage);
41629
+ } catch {
41630
+ return [];
41631
+ }
41632
+ }
41416
41633
  var MAX_SLACK_ATTACHMENTS = 8, MAX_SLACK_ATTACHMENT_BYTES, ALLOWED_SLACK_HOST_SUFFIXES;
41417
41634
  var init_media2 = __esm(() => {
41418
41635
  init_config();
@@ -41428,6 +41645,9 @@ var init_media2 = __esm(() => {
41428
41645
  async function loadSlackBoltModule() {
41429
41646
  return loadChannelRuntimeModule("slack", "@slack/bolt");
41430
41647
  }
41648
+ async function loadSlackWebApiModule() {
41649
+ return loadChannelRuntimeModule("slack", "@slack/web-api");
41650
+ }
41431
41651
  async function ensureSlackRuntimeInstalled() {
41432
41652
  return ensureChannelRuntimeInstalled("slack");
41433
41653
  }
@@ -41457,6 +41677,22 @@ function resolveSlackAppConstructor(mod) {
41457
41677
  }
41458
41678
  return App2;
41459
41679
  }
41680
+ function resolveSlackWebClientModule(value) {
41681
+ if (!value || typeof value !== "object") {
41682
+ return null;
41683
+ }
41684
+ const webClient = Reflect.get(value, "WebClient");
41685
+ return isConstructorFunction(webClient) ? webClient : null;
41686
+ }
41687
+ function resolveSlackWebClientConstructor(mod) {
41688
+ const defaultExport = mod && typeof mod === "object" ? Reflect.get(mod, "default") : undefined;
41689
+ const nestedDefault = defaultExport && typeof defaultExport === "object" ? Reflect.get(defaultExport, "default") : undefined;
41690
+ const WebClient = resolveSlackWebClientModule(mod) ?? resolveSlackWebClientModule(defaultExport) ?? resolveSlackWebClientModule(nestedDefault) ?? (isConstructorFunction(defaultExport) ? defaultExport : null);
41691
+ if (!WebClient) {
41692
+ throw new Error('Installed Slack runtime did not export constructor "WebClient".');
41693
+ }
41694
+ return WebClient;
41695
+ }
41460
41696
  function isNonEmptyString2(value) {
41461
41697
  return typeof value === "string" && value.length > 0;
41462
41698
  }
@@ -41499,7 +41735,7 @@ function resolveUploadMimeType(filePath) {
41499
41735
  return;
41500
41736
  }
41501
41737
  }
41502
- async function uploadSlackFile(slackApp, msg) {
41738
+ async function uploadSlackFile(slackClient, msg) {
41503
41739
  if (!msg.mediaPath) {
41504
41740
  throw new Error("mediaPath is required for Slack file uploads.");
41505
41741
  }
@@ -41507,7 +41743,7 @@ async function uploadSlackFile(slackApp, msg) {
41507
41743
  const uploadFileName = msg.fileName ?? basename4(msg.mediaPath);
41508
41744
  const uploadTitle = msg.title ?? uploadFileName;
41509
41745
  const uploadMimeType = resolveUploadMimeType(uploadFileName);
41510
- const uploadUrlResp = await slackApp.client.files.getUploadURLExternal({
41746
+ const uploadUrlResp = await slackClient.files.getUploadURLExternal({
41511
41747
  filename: uploadFileName,
41512
41748
  length: buffer.length
41513
41749
  });
@@ -41522,7 +41758,7 @@ async function uploadSlackFile(slackApp, msg) {
41522
41758
  if (!uploadResp.ok) {
41523
41759
  throw new Error(`Failed to upload Slack file: HTTP ${uploadResp.status}`);
41524
41760
  }
41525
- const completeResp = await slackApp.client.files.completeUploadExternal({
41761
+ const completeResp = await slackClient.files.completeUploadExternal({
41526
41762
  files: [{ id: uploadUrlResp.file_id, title: uploadTitle }],
41527
41763
  channel_id: msg.chatId,
41528
41764
  ...msg.text.trim() ? { initial_comment: msg.text } : {},
@@ -41538,6 +41774,34 @@ function resolveSlackUserDisplayName(userInfo) {
41538
41774
  const profile = asRecord2(user?.profile);
41539
41775
  return firstNonEmptyString(profile?.display_name, profile?.real_name, user?.name);
41540
41776
  }
41777
+ function truncateSlackThreadLabel(text, maxLength = 80) {
41778
+ const normalized = text.replace(/\s+/g, " ").trim();
41779
+ if (!normalized) {
41780
+ return null;
41781
+ }
41782
+ if (normalized.length <= maxLength) {
41783
+ return normalized;
41784
+ }
41785
+ return `${normalized.slice(0, maxLength - 1).trimEnd()}…`;
41786
+ }
41787
+ function buildSlackThreadLabel(msg, starterText) {
41788
+ if (msg.chatType !== "channel") {
41789
+ return;
41790
+ }
41791
+ const roomLabel = isNonEmptyString2(msg.chatLabel) && msg.chatLabel !== msg.chatId ? ` in ${msg.chatLabel}` : "";
41792
+ const preview = truncateSlackThreadLabel(starterText ?? msg.text);
41793
+ if (preview) {
41794
+ return `Slack thread${roomLabel}: ${preview}`;
41795
+ }
41796
+ return roomLabel ? `Slack thread${roomLabel}` : `Slack thread ${msg.chatId}`;
41797
+ }
41798
+ function buildSlackChannelContextLabel(msg) {
41799
+ if (msg.chatType !== "channel") {
41800
+ return;
41801
+ }
41802
+ const roomLabel = isNonEmptyString2(msg.chatLabel) && msg.chatLabel !== msg.chatId ? ` in ${msg.chatLabel}` : "";
41803
+ return roomLabel ? `Slack channel context${roomLabel} before thread start` : `Slack channel context before thread start`;
41804
+ }
41541
41805
  async function resolveSlackAccountDisplayName(botToken, appToken) {
41542
41806
  const bolt = await loadSlackBoltModule();
41543
41807
  const App2 = resolveSlackAppConstructor(bolt);
@@ -41563,11 +41827,14 @@ async function resolveSlackAccountDisplayName(botToken, appToken) {
41563
41827
  }
41564
41828
  function createSlackAdapter(config) {
41565
41829
  let app = null;
41830
+ let writeClient = null;
41566
41831
  let running = false;
41567
41832
  let botUserId = null;
41568
41833
  const knownThreadIdsByMessageId = new Map;
41569
41834
  const knownUserDisplayNames = new Map;
41570
41835
  const seenIngressMessageKeys = new Map;
41836
+ const lifecycleStateByMessageKey = new Map;
41837
+ const lifecycleOperationByMessageKey = new Map;
41571
41838
  function buildIngressMessageKey(channelId, messageId) {
41572
41839
  if (!isNonEmptyString2(channelId) || !isNonEmptyString2(messageId)) {
41573
41840
  return null;
@@ -41592,6 +41859,96 @@ function createSlackAdapter(config) {
41592
41859
  }
41593
41860
  }
41594
41861
  }
41862
+ function getLifecycleMessageKey(source) {
41863
+ if (source.channel !== "slack" || !isNonEmptyString2(source.chatId) || !isNonEmptyString2(source.messageId)) {
41864
+ return null;
41865
+ }
41866
+ return `${source.chatId}:${source.messageId}`;
41867
+ }
41868
+ function pruneLifecycleState(now = Date.now()) {
41869
+ for (const [key, entry] of lifecycleStateByMessageKey) {
41870
+ if (entry.updatedAt + SLACK_LIFECYCLE_STATE_TTL_MS <= now) {
41871
+ lifecycleStateByMessageKey.delete(key);
41872
+ }
41873
+ }
41874
+ if (lifecycleStateByMessageKey.size <= SLACK_LIFECYCLE_STATE_MAX) {
41875
+ return;
41876
+ }
41877
+ const oldestEntries = Array.from(lifecycleStateByMessageKey.entries()).sort((a, b) => a[1].updatedAt - b[1].updatedAt);
41878
+ const overflowCount = lifecycleStateByMessageKey.size - SLACK_LIFECYCLE_STATE_MAX;
41879
+ for (let index = 0;index < overflowCount; index += 1) {
41880
+ const entry = oldestEntries[index];
41881
+ if (entry) {
41882
+ lifecycleStateByMessageKey.delete(entry[0]);
41883
+ }
41884
+ }
41885
+ }
41886
+ async function sendLifecycleReaction(source, emoji, removeReaction = false) {
41887
+ if (!isNonEmptyString2(source.messageId)) {
41888
+ return;
41889
+ }
41890
+ await ensureApp();
41891
+ const slackClient = await ensureWriteClient();
41892
+ if (removeReaction) {
41893
+ await slackClient.reactions.remove({
41894
+ channel: source.chatId,
41895
+ timestamp: source.messageId,
41896
+ name: emoji
41897
+ });
41898
+ return;
41899
+ }
41900
+ await slackClient.reactions.add({
41901
+ channel: source.chatId,
41902
+ timestamp: source.messageId,
41903
+ name: emoji
41904
+ });
41905
+ }
41906
+ function scheduleLifecycleTransition(source, nextState) {
41907
+ const key = getLifecycleMessageKey(source);
41908
+ if (!key) {
41909
+ return null;
41910
+ }
41911
+ const previous = lifecycleOperationByMessageKey.get(key) ?? Promise.resolve();
41912
+ const operation = previous.catch(() => {}).then(async () => {
41913
+ pruneLifecycleState();
41914
+ const currentState = lifecycleStateByMessageKey.get(key)?.state;
41915
+ if (currentState === nextState) {
41916
+ lifecycleStateByMessageKey.set(key, {
41917
+ state: nextState,
41918
+ updatedAt: Date.now()
41919
+ });
41920
+ return;
41921
+ }
41922
+ if (nextState === "queued") {
41923
+ if (!currentState) {
41924
+ await sendLifecycleReaction(source, "eyes");
41925
+ lifecycleStateByMessageKey.set(key, {
41926
+ state: nextState,
41927
+ updatedAt: Date.now()
41928
+ });
41929
+ }
41930
+ return;
41931
+ }
41932
+ if (currentState === "queued") {
41933
+ try {
41934
+ await sendLifecycleReaction(source, "eyes", true);
41935
+ } catch {}
41936
+ }
41937
+ await sendLifecycleReaction(source, nextState === "completed" ? "white_check_mark" : "x");
41938
+ lifecycleStateByMessageKey.set(key, {
41939
+ state: nextState,
41940
+ updatedAt: Date.now()
41941
+ });
41942
+ }).catch((error) => {
41943
+ console.warn(`[Slack] Failed to update lifecycle reaction for ${key}:`, error instanceof Error ? error.message : error);
41944
+ }).finally(() => {
41945
+ if (lifecycleOperationByMessageKey.get(key) === operation) {
41946
+ lifecycleOperationByMessageKey.delete(key);
41947
+ }
41948
+ });
41949
+ lifecycleOperationByMessageKey.set(key, operation);
41950
+ return operation;
41951
+ }
41595
41952
  function markIngressMessageSeen(channelId, messageId) {
41596
41953
  const key = buildIngressMessageKey(channelId, messageId);
41597
41954
  if (!key) {
@@ -41815,6 +42172,19 @@ function createSlackAdapter(config) {
41815
42172
  app = instance;
41816
42173
  return instance;
41817
42174
  }
42175
+ async function ensureWriteClient() {
42176
+ if (writeClient) {
42177
+ return writeClient;
42178
+ }
42179
+ const webApi = await loadSlackWebApiModule();
42180
+ const WebClient = resolveSlackWebClientConstructor(webApi);
42181
+ writeClient = new WebClient(config.botToken, {
42182
+ retryConfig: {
42183
+ retries: 0
42184
+ }
42185
+ });
42186
+ return writeClient;
42187
+ }
41818
42188
  const adapter = {
41819
42189
  id: `slack:${config.accountId}`,
41820
42190
  channelId: "slack",
@@ -41838,15 +42208,33 @@ function createSlackAdapter(config) {
41838
42208
  await app.stop();
41839
42209
  running = false;
41840
42210
  app = null;
42211
+ writeClient = null;
41841
42212
  botUserId = null;
41842
42213
  seenIngressMessageKeys.clear();
42214
+ lifecycleStateByMessageKey.clear();
42215
+ lifecycleOperationByMessageKey.clear();
41843
42216
  console.log("[Slack] App stopped");
41844
42217
  },
41845
42218
  isRunning() {
41846
42219
  return running;
41847
42220
  },
42221
+ async handleTurnLifecycleEvent(event) {
42222
+ if (!running) {
42223
+ return;
42224
+ }
42225
+ if (event.type === "queued") {
42226
+ await scheduleLifecycleTransition(event.source, "queued");
42227
+ return;
42228
+ }
42229
+ if (event.type === "processing") {
42230
+ return;
42231
+ }
42232
+ const nextState = event.outcome === "completed" ? "completed" : event.outcome === "cancelled" ? "cancelled" : "error";
42233
+ await Promise.all(event.sources.map((source) => scheduleLifecycleTransition(source, nextState)));
42234
+ },
41848
42235
  async sendMessage(msg) {
41849
- const slackApp = await ensureApp();
42236
+ await ensureApp();
42237
+ const slackClient = await ensureWriteClient();
41850
42238
  if (msg.reaction) {
41851
42239
  const targetMessageId = msg.targetMessageId ?? msg.replyToMessageId;
41852
42240
  if (!targetMessageId) {
@@ -41857,13 +42245,13 @@ function createSlackAdapter(config) {
41857
42245
  throw new Error("Slack reaction emoji cannot be empty.");
41858
42246
  }
41859
42247
  if (msg.removeReaction) {
41860
- await slackApp.client.reactions.remove({
42248
+ await slackClient.reactions.remove({
41861
42249
  channel: msg.chatId,
41862
42250
  timestamp: targetMessageId,
41863
42251
  name: emoji
41864
42252
  });
41865
42253
  } else {
41866
- await slackApp.client.reactions.add({
42254
+ await slackClient.reactions.add({
41867
42255
  channel: msg.chatId,
41868
42256
  timestamp: targetMessageId,
41869
42257
  name: emoji
@@ -41872,9 +42260,9 @@ function createSlackAdapter(config) {
41872
42260
  return { messageId: targetMessageId };
41873
42261
  }
41874
42262
  if (msg.mediaPath) {
41875
- return uploadSlackFile(slackApp, msg);
42263
+ return uploadSlackFile(slackClient, msg);
41876
42264
  }
41877
- const response = await slackApp.client.chat.postMessage({
42265
+ const response = await slackClient.chat.postMessage({
41878
42266
  channel: msg.chatId,
41879
42267
  text: msg.text,
41880
42268
  ...msg.threadId ?? msg.replyToMessageId ? { thread_ts: msg.threadId ?? msg.replyToMessageId } : {}
@@ -41883,22 +42271,98 @@ function createSlackAdapter(config) {
41883
42271
  return { messageId: response.ts ?? "" };
41884
42272
  },
41885
42273
  async sendDirectReply(chatId, text, options) {
41886
- const slackApp = await ensureApp();
41887
- const response = await slackApp.client.chat.postMessage({
42274
+ await ensureApp();
42275
+ const slackClient = await ensureWriteClient();
42276
+ const response = await slackClient.chat.postMessage({
41888
42277
  channel: chatId,
41889
42278
  text,
41890
42279
  ...options?.replyToMessageId ? { thread_ts: options.replyToMessageId } : {}
41891
42280
  });
41892
42281
  rememberMessageThread(response.ts, options?.replyToMessageId ?? response.ts ?? null);
41893
42282
  },
42283
+ async prepareInboundMessage(msg, options) {
42284
+ if (!options?.isFirstRouteTurn || msg.channel !== "slack" || msg.chatType !== "channel" || !isNonEmptyString2(msg.threadId) || !isNonEmptyString2(msg.messageId)) {
42285
+ return msg;
42286
+ }
42287
+ const shouldHydrateExistingThreadContext = msg.threadId !== msg.messageId;
42288
+ const shouldHydrateChannelBootstrapContext = msg.isMention === true && msg.threadId === msg.messageId;
42289
+ if (!shouldHydrateExistingThreadContext && !shouldHydrateChannelBootstrapContext) {
42290
+ return msg;
42291
+ }
42292
+ const slackApp = await ensureApp();
42293
+ const starter = shouldHydrateExistingThreadContext ? await resolveSlackThreadStarter({
42294
+ channelId: msg.chatId,
42295
+ threadTs: msg.threadId,
42296
+ client: slackApp.client
42297
+ }) : null;
42298
+ const history = shouldHydrateExistingThreadContext ? await resolveSlackThreadHistory({
42299
+ channelId: msg.chatId,
42300
+ threadTs: msg.threadId,
42301
+ client: slackApp.client,
42302
+ currentMessageTs: msg.messageId,
42303
+ limit: INITIAL_SLACK_THREAD_HISTORY_LIMIT
42304
+ }) : await resolveSlackChannelHistory({
42305
+ channelId: msg.chatId,
42306
+ beforeTs: msg.messageId,
42307
+ client: slackApp.client,
42308
+ limit: INITIAL_SLACK_THREAD_HISTORY_LIMIT
42309
+ });
42310
+ if (!starter && history.length === 0) {
42311
+ return msg;
42312
+ }
42313
+ const uniqueUserIds = new Set;
42314
+ if (isNonEmptyString2(starter?.userId)) {
42315
+ uniqueUserIds.add(starter.userId);
42316
+ }
42317
+ for (const entry of history) {
42318
+ if (isNonEmptyString2(entry.userId)) {
42319
+ uniqueUserIds.add(entry.userId);
42320
+ }
42321
+ }
42322
+ await Promise.all(Array.from(uniqueUserIds).map(async (userId) => {
42323
+ await resolveUserName(slackApp, userId);
42324
+ }));
42325
+ const resolveThreadSenderName = (userId, botId) => {
42326
+ if (isNonEmptyString2(userId)) {
42327
+ return knownUserDisplayNames.get(userId) ?? userId;
42328
+ }
42329
+ if (isNonEmptyString2(botId)) {
42330
+ return `Bot (${botId})`;
42331
+ }
42332
+ return;
42333
+ };
42334
+ return {
42335
+ ...msg,
42336
+ threadContext: {
42337
+ label: shouldHydrateExistingThreadContext ? buildSlackThreadLabel(msg, starter?.text) : buildSlackChannelContextLabel(msg),
42338
+ ...starter ? {
42339
+ starter: {
42340
+ messageId: starter.ts,
42341
+ senderId: starter.userId ?? starter.botId,
42342
+ senderName: resolveThreadSenderName(starter.userId, starter.botId),
42343
+ text: starter.text
42344
+ }
42345
+ } : {},
42346
+ ...history.length > 0 ? {
42347
+ history: history.map((entry) => ({
42348
+ messageId: entry.ts,
42349
+ senderId: entry.userId ?? entry.botId,
42350
+ senderName: resolveThreadSenderName(entry.userId, entry.botId),
42351
+ text: entry.text
42352
+ }))
42353
+ } : {}
42354
+ }
42355
+ };
42356
+ },
41894
42357
  onMessage: undefined
41895
42358
  };
41896
42359
  return adapter;
41897
42360
  }
41898
- var SLACK_INGRESS_DEDUPE_TTL_MS = 60000, SLACK_INGRESS_DEDUPE_MAX = 2000;
42361
+ 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
42362
  var init_adapter2 = __esm(() => {
41900
42363
  init_media2();
41901
42364
  init_runtime2();
42365
+ SLACK_LIFECYCLE_STATE_TTL_MS = 6 * 60 * 60 * 1000;
41902
42366
  });
41903
42367
 
41904
42368
  // src/channels/slack/messageActions.ts
@@ -42040,6 +42504,7 @@ DM Policy — who can message this app directly?
42040
42504
  botToken,
42041
42505
  appToken,
42042
42506
  agentId: null,
42507
+ defaultPermissionMode: "default",
42043
42508
  dmPolicy: policy,
42044
42509
  allowedUsers,
42045
42510
  createdAt: now,
@@ -42081,8 +42546,8 @@ var init_plugin2 = __esm(() => {
42081
42546
  metadata: {
42082
42547
  id: "slack",
42083
42548
  displayName: "Slack",
42084
- runtimePackages: ["@slack/bolt@4.7.0"],
42085
- runtimeModules: ["@slack/bolt"]
42549
+ runtimePackages: ["@slack/bolt@4.7.0", "@slack/web-api@7.15.0"],
42550
+ runtimeModules: ["@slack/bolt", "@slack/web-api"]
42086
42551
  },
42087
42552
  createAdapter(account) {
42088
42553
  return createSlackAdapter(account);
@@ -42138,8 +42603,8 @@ var init_pluginRegistry = __esm(() => {
42138
42603
  metadata: {
42139
42604
  id: "slack",
42140
42605
  displayName: "Slack",
42141
- runtimePackages: ["@slack/bolt@4.7.0"],
42142
- runtimeModules: ["@slack/bolt"]
42606
+ runtimePackages: ["@slack/bolt@4.7.0", "@slack/web-api@7.15.0"],
42607
+ runtimeModules: ["@slack/bolt", "@slack/web-api"]
42143
42608
  },
42144
42609
  load: async () => {
42145
42610
  const { slackChannelPlugin: slackChannelPlugin2 } = await Promise.resolve().then(() => (init_plugin2(), exports_plugin2));
@@ -42724,6 +43189,47 @@ function buildReactionXml(msg) {
42724
43189
  }
42725
43190
  return `<reaction ${attrs.join(" ")} />`;
42726
43191
  }
43192
+ function buildThreadContextEntryXml(tagName, entry) {
43193
+ const attrs = [];
43194
+ if (entry.senderId) {
43195
+ attrs.push(`sender_id="${escapeXmlAttribute(entry.senderId)}"`);
43196
+ }
43197
+ if (entry.senderName) {
43198
+ attrs.push(`sender_name="${escapeXmlAttribute(entry.senderName)}"`);
43199
+ }
43200
+ if (entry.messageId) {
43201
+ attrs.push(`message_id="${escapeXmlAttribute(entry.messageId)}"`);
43202
+ }
43203
+ const attrString = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
43204
+ return `<${tagName}${attrString}>
43205
+ ${escapeXmlText(entry.text)}
43206
+ </${tagName}>`;
43207
+ }
43208
+ function buildThreadContextXml(msg) {
43209
+ const threadContext = msg.threadContext;
43210
+ if (!threadContext) {
43211
+ return null;
43212
+ }
43213
+ const parts = [];
43214
+ if (threadContext.starter) {
43215
+ parts.push(buildThreadContextEntryXml("thread-starter", threadContext.starter));
43216
+ }
43217
+ const historyEntries = threadContext.history ?? [];
43218
+ if (historyEntries.length > 0) {
43219
+ parts.push([
43220
+ "<thread-history>",
43221
+ ...historyEntries.map((entry) => buildThreadContextEntryXml("thread-message", entry)),
43222
+ "</thread-history>"
43223
+ ].join(`
43224
+ `));
43225
+ }
43226
+ if (parts.length === 0) {
43227
+ return null;
43228
+ }
43229
+ const attrs = threadContext.label ? ` label="${escapeXmlAttribute(threadContext.label)}"` : "";
43230
+ return [`<thread-context${attrs}>`, ...parts, "</thread-context>"].join(`
43231
+ `);
43232
+ }
42727
43233
  function buildChannelNotificationXml(msg) {
42728
43234
  const attrs = [
42729
43235
  `source="${escapeXmlAttribute(msg.channel)}"`,
@@ -42742,8 +43248,9 @@ function buildChannelNotificationXml(msg) {
42742
43248
  const attrString = attrs.join(" ");
42743
43249
  const escapedText = msg.text ? escapeXmlText(msg.text) : "";
42744
43250
  const reactionXml = buildReactionXml(msg);
43251
+ const threadContextXml = buildThreadContextXml(msg);
42745
43252
  const attachmentXml = (msg.attachments ?? []).map(buildAttachmentXml);
42746
- const body = [reactionXml, ...attachmentXml, escapedText].filter(Boolean).join(`
43253
+ const body = [threadContextXml, reactionXml, ...attachmentXml, escapedText].filter(Boolean).join(`
42747
43254
  `);
42748
43255
  return `<channel-notification ${attrString}>
42749
43256
  ${body}
@@ -42828,6 +43335,18 @@ function buildSlackConversationSummary(msg) {
42828
43335
  }
42829
43336
  return `[Slack] Thread${channelLabel || ` ${msg.chatId}`}`;
42830
43337
  }
43338
+ function buildChannelTurnSource(route, msg) {
43339
+ return {
43340
+ channel: msg.channel,
43341
+ accountId: msg.accountId,
43342
+ chatId: msg.chatId,
43343
+ chatType: msg.chatType,
43344
+ messageId: msg.messageId,
43345
+ threadId: msg.threadId,
43346
+ agentId: route.agentId,
43347
+ conversationId: route.conversationId
43348
+ };
43349
+ }
42831
43350
  function getChannelRegistry() {
42832
43351
  return instance;
42833
43352
  }
@@ -42867,6 +43386,51 @@ class ChannelRegistry {
42867
43386
  getActiveChannelIds() {
42868
43387
  return Array.from(this.adapters.values()).filter((adapter) => adapter.isRunning()).map((adapter) => adapter.channelId ?? adapter.id);
42869
43388
  }
43389
+ async dispatchTurnLifecycleEvent(event) {
43390
+ const groups = new Map;
43391
+ const sources = event.type === "queued" ? [event.source] : event.sources;
43392
+ for (const source of sources) {
43393
+ const adapter = this.getAdapter(source.channel, source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID);
43394
+ if (!adapter?.handleTurnLifecycleEvent) {
43395
+ continue;
43396
+ }
43397
+ const groupKey = this.getAdapterKey(source.channel, source.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID);
43398
+ const existing = groups.get(groupKey);
43399
+ if (existing) {
43400
+ existing.sources.push(source);
43401
+ continue;
43402
+ }
43403
+ groups.set(groupKey, {
43404
+ adapter,
43405
+ sources: [source]
43406
+ });
43407
+ }
43408
+ for (const { adapter, sources: groupedSources } of groups.values()) {
43409
+ const handleTurnLifecycleEvent = adapter.handleTurnLifecycleEvent;
43410
+ if (!handleTurnLifecycleEvent) {
43411
+ continue;
43412
+ }
43413
+ try {
43414
+ if (event.type === "queued") {
43415
+ const [firstSource] = groupedSources;
43416
+ if (!firstSource) {
43417
+ continue;
43418
+ }
43419
+ await handleTurnLifecycleEvent({
43420
+ type: "queued",
43421
+ source: firstSource
43422
+ });
43423
+ continue;
43424
+ }
43425
+ await handleTurnLifecycleEvent({
43426
+ ...event,
43427
+ sources: groupedSources
43428
+ });
43429
+ } catch (error) {
43430
+ 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);
43431
+ }
43432
+ }
43433
+ }
42870
43434
  setMessageHandler(handler) {
42871
43435
  this.messageHandler = handler;
42872
43436
  }
@@ -42985,11 +43549,20 @@ class ChannelRegistry {
42985
43549
  if (!config)
42986
43550
  return;
42987
43551
  if (msg.channel === "slack" && config.channel === "slack") {
42988
- const slackRoute = await this.ensureSlackRoute(adapter, msg, config);
42989
- if (!slackRoute) {
43552
+ const slackResult = await this.ensureSlackRoute(adapter, msg, config);
43553
+ if (!slackResult) {
42990
43554
  return;
42991
43555
  }
42992
- this.deliverOrBuffer(slackRoute, formatChannelNotification(msg));
43556
+ const preparedMessage = adapter.prepareInboundMessage ? await adapter.prepareInboundMessage(msg, {
43557
+ isFirstRouteTurn: slackResult.isFirstRouteTurn
43558
+ }) : msg;
43559
+ this.deliverOrBuffer({
43560
+ route: slackResult.route,
43561
+ content: formatChannelNotification(preparedMessage),
43562
+ turnSources: [
43563
+ buildChannelTurnSource(slackResult.route, preparedMessage)
43564
+ ]
43565
+ });
42993
43566
  return;
42994
43567
  }
42995
43568
  if (config.dmPolicy === "allowlist") {
@@ -43021,7 +43594,11 @@ class ChannelRegistry {
43021
43594
  return;
43022
43595
  }
43023
43596
  const content = formatChannelNotification(msg);
43024
- this.deliverOrBuffer(route, content);
43597
+ this.deliverOrBuffer({
43598
+ route,
43599
+ content,
43600
+ turnSources: [buildChannelTurnSource(route, msg)]
43601
+ });
43025
43602
  }
43026
43603
  async createConversationForAgent(agentId, summary) {
43027
43604
  const client = await getClient();
@@ -43050,6 +43627,16 @@ class ChannelRegistry {
43050
43627
  updatedAt: now
43051
43628
  };
43052
43629
  addRoute(msg.channel, route);
43630
+ if (config.defaultPermissionMode !== "default") {
43631
+ this.eventHandler?.({
43632
+ type: "slack_conversation_created",
43633
+ channelId: "slack",
43634
+ accountId: config.accountId,
43635
+ agentId: config.agentId,
43636
+ conversationId,
43637
+ defaultPermissionMode: config.defaultPermissionMode
43638
+ });
43639
+ }
43053
43640
  return route;
43054
43641
  }
43055
43642
  async ensureSlackRoute(adapter, msg, config) {
@@ -43071,7 +43658,10 @@ class ChannelRegistry {
43071
43658
  route = getRoute(msg.channel, msg.chatId, accountId, routeThreadId);
43072
43659
  }
43073
43660
  if (route) {
43074
- return route;
43661
+ return {
43662
+ route,
43663
+ isFirstRouteTurn: false
43664
+ };
43075
43665
  }
43076
43666
  if (msg.chatType === "channel" && !msg.isMention) {
43077
43667
  return null;
@@ -43092,14 +43682,17 @@ class ChannelRegistry {
43092
43682
  type: "targets_updated",
43093
43683
  channelId: msg.channel
43094
43684
  });
43095
- return this.createSlackRoute(config, msg);
43685
+ return {
43686
+ route: await this.createSlackRoute(config, msg),
43687
+ isFirstRouteTurn: true
43688
+ };
43096
43689
  }
43097
- deliverOrBuffer(route, content) {
43690
+ deliverOrBuffer(delivery) {
43098
43691
  if (this.isReady()) {
43099
- this.messageHandler?.(route, content);
43692
+ this.messageHandler?.(delivery);
43100
43693
  return;
43101
43694
  }
43102
- this.buffer.push({ route, content });
43695
+ this.buffer.push(delivery);
43103
43696
  }
43104
43697
  flushBuffer() {
43105
43698
  if (!this.messageHandler)
@@ -43107,7 +43700,7 @@ class ChannelRegistry {
43107
43700
  while (this.buffer.length > 0) {
43108
43701
  const item = this.buffer.shift();
43109
43702
  if (item) {
43110
- this.messageHandler(item.route, item.content);
43703
+ this.messageHandler(item);
43111
43704
  }
43112
43705
  }
43113
43706
  }
@@ -45617,10 +46210,12 @@ var exports_memoryFilesystem = {};
45617
46210
  __export(exports_memoryFilesystem, {
45618
46211
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
45619
46212
  labelFromRelativePath: () => labelFromRelativePath,
46213
+ isMemfsEnabledOnServer: () => isMemfsEnabledOnServer,
45620
46214
  isLettaCloud: () => isLettaCloud,
45621
46215
  getMemorySystemDir: () => getMemorySystemDir,
45622
46216
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
45623
46217
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
46218
+ ensureLocalMemfsCheckout: () => ensureLocalMemfsCheckout,
45624
46219
  enableMemfsIfCloud: () => enableMemfsIfCloud,
45625
46220
  applyMemfsFlags: () => applyMemfsFlags,
45626
46221
  MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES,
@@ -45650,6 +46245,23 @@ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir10()) {
45650
46245
  mkdirSync13(systemDir, { recursive: true });
45651
46246
  }
45652
46247
  }
46248
+ async function isMemfsEnabledOnServer(agentId) {
46249
+ const { getClient: getClient3 } = await Promise.resolve().then(() => (init_client2(), exports_client));
46250
+ const client = await getClient3();
46251
+ const agent = await client.agents.retrieve(agentId);
46252
+ const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
46253
+ const enabled = agent.tags?.includes(GIT_MEMORY_ENABLED_TAG2) ?? false;
46254
+ const { settingsManager: settingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
46255
+ settingsManager2.setMemfsEnabled(agentId, enabled);
46256
+ return enabled;
46257
+ }
46258
+ async function ensureLocalMemfsCheckout(agentId) {
46259
+ const { isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
46260
+ if (isGitRepo2(agentId)) {
46261
+ return;
46262
+ }
46263
+ await cloneMemoryRepo2(agentId);
46264
+ }
45653
46265
  function labelFromRelativePath(relativePath) {
45654
46266
  const normalized = relativePath.replace(/\\/g, "/");
45655
46267
  return normalized.replace(/\.md$/, "");
@@ -49895,6 +50507,19 @@ function resolveLettaInvocation(env3 = process.env, argv = process.argv, execPat
49895
50507
  }
49896
50508
  const scriptPath = argv[1] || "";
49897
50509
  if (scriptPath && isDevLettaEntryScript(scriptPath)) {
50510
+ const runtimeName = path4.basename(execPath).toLowerCase();
50511
+ if (runtimeName.includes("bun")) {
50512
+ return {
50513
+ command: execPath,
50514
+ args: [
50515
+ "--loader:.md=text",
50516
+ "--loader:.mdx=text",
50517
+ "--loader:.txt=text",
50518
+ "run",
50519
+ scriptPath
50520
+ ]
50521
+ };
50522
+ }
49898
50523
  return { command: execPath, args: [scriptPath] };
49899
50524
  }
49900
50525
  return null;
@@ -74195,7 +74820,12 @@ async function executeTool(name, args, options) {
74195
74820
  enhancedArgs = { ...enhancedArgs, signal: options.signal };
74196
74821
  }
74197
74822
  if (options?.onOutput) {
74198
- enhancedArgs = { ...enhancedArgs, onOutput: options.onOutput };
74823
+ enhancedArgs = {
74824
+ ...enhancedArgs,
74825
+ onOutput: (chunk, stream2) => {
74826
+ options.onOutput?.(scrubSecretsFromString(chunk), stream2);
74827
+ }
74828
+ };
74199
74829
  }
74200
74830
  enhancedArgs = substituteSecretsInArgs(enhancedArgs);
74201
74831
  }
@@ -74695,6 +75325,7 @@ function toAccountSnapshot(account) {
74695
75325
  hasBotToken: account.botToken.trim().length > 0,
74696
75326
  hasAppToken: account.appToken.trim().length > 0,
74697
75327
  agentId: account.agentId,
75328
+ defaultPermissionMode: account.defaultPermissionMode ?? "default",
74698
75329
  createdAt: account.createdAt,
74699
75330
  updatedAt: account.updatedAt
74700
75331
  };
@@ -74727,6 +75358,7 @@ function createAccountFromPatch(channelId, accountId, patch) {
74727
75358
  botToken: patch.botToken ?? "",
74728
75359
  appToken: patch.appToken ?? "",
74729
75360
  agentId: patch.agentId ?? null,
75361
+ defaultPermissionMode: patch.defaultPermissionMode ?? "default",
74730
75362
  dmPolicy: patch.dmPolicy ?? "open",
74731
75363
  allowedUsers: patch.allowedUsers ?? [],
74732
75364
  createdAt: now,
@@ -74754,6 +75386,7 @@ function mergeAccountPatch(existing, patch) {
74754
75386
  botToken: patch.botToken ?? existing.botToken,
74755
75387
  appToken: patch.appToken ?? existing.appToken,
74756
75388
  agentId: patch.agentId ?? existing.agentId,
75389
+ defaultPermissionMode: patch.defaultPermissionMode ?? existing.defaultPermissionMode ?? "default",
74757
75390
  dmPolicy: patch.dmPolicy ?? existing.dmPolicy,
74758
75391
  allowedUsers: patch.allowedUsers ?? existing.allowedUsers,
74759
75392
  updatedAt: nextUpdatedAt
@@ -76790,7 +77423,7 @@ function addTask(input) {
76790
77423
  prompt: input.prompt,
76791
77424
  status: "active",
76792
77425
  created_at: now.toISOString(),
76793
- expires_at: input.recurring ? new Date(now.getTime() + DEFAULT_TTL_MS).toISOString() : null,
77426
+ expires_at: null,
76794
77427
  last_fired_at: null,
76795
77428
  fire_count: 0,
76796
77429
  cancel_reason: null,
@@ -76918,10 +77551,9 @@ function getCronFileMtime() {
76918
77551
  return 0;
76919
77552
  }
76920
77553
  }
76921
- var CRON_FILE_NAME = "crons.json", LOCK_DIR_NAME = "crons.lock", LOCK_TOKEN_FILE = "owner.json", LOCK_TIMEOUT_MS = 5000, LOCK_RETRY_MS = 50, LOCK_STALE_AGE_MS = 30000, MAX_ACTIVE_TASKS_PER_AGENT = 50, TASK_ID_BYTES = 4, DEFAULT_TTL_MS, GC_AGE_MS;
77554
+ var CRON_FILE_NAME = "crons.json", LOCK_DIR_NAME = "crons.lock", LOCK_TOKEN_FILE = "owner.json", LOCK_TIMEOUT_MS = 5000, LOCK_RETRY_MS = 50, LOCK_STALE_AGE_MS = 30000, MAX_ACTIVE_TASKS_PER_AGENT = 50, TASK_ID_BYTES = 4, GC_AGE_MS;
76922
77555
  var init_cronFile = __esm(() => {
76923
77556
  init_parseInterval();
76924
- DEFAULT_TTL_MS = 3 * 24 * 60 * 60 * 1000;
76925
77557
  GC_AGE_MS = 24 * 60 * 60 * 1000;
76926
77558
  });
76927
77559
 
@@ -83968,6 +84600,25 @@ function normalizeInterruptOutputLines(value) {
83968
84600
  const combinedLength = filtered.reduce((sum, entry) => sum + entry.length, 0);
83969
84601
  return combinedLength <= INTERRUPT_TOOL_RETURN_MAX_CHARS ? filtered : undefined;
83970
84602
  }
84603
+ function appendStreamingOutputWithCap(current, chunk) {
84604
+ if (chunk.length === 0) {
84605
+ return current;
84606
+ }
84607
+ const next = `${current}${chunk}`;
84608
+ if (next.length <= STREAMING_TOOL_OUTPUT_MAX_CHARS) {
84609
+ return next;
84610
+ }
84611
+ return next.slice(next.length - STREAMING_TOOL_OUTPUT_MAX_CHARS);
84612
+ }
84613
+ function normalizeStreamingOutputLines(text) {
84614
+ if (text.length === 0) {
84615
+ return;
84616
+ }
84617
+ const lines = text.replace(/\r\n/g, `
84618
+ `).split(`
84619
+ `).filter((line) => line.length > 0);
84620
+ return lines.length > 0 ? lines : undefined;
84621
+ }
83971
84622
  function asToolReturnStatus(value) {
83972
84623
  if (value === "success" || value === "error") {
83973
84624
  return value;
@@ -84181,6 +84832,54 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
84181
84832
  });
84182
84833
  }
84183
84834
  }
84835
+ function createToolExecutionOutputEmitter(socket, runtime, params) {
84836
+ const outputByToolCallId = new Map;
84837
+ return (toolCallId, chunk, isStderr = false) => {
84838
+ if (!toolCallId || chunk.length === 0) {
84839
+ return;
84840
+ }
84841
+ const existing = outputByToolCallId.get(toolCallId);
84842
+ const outputState = existing ?? {
84843
+ messageId: `message-tool-return-stream-${toolCallId}`,
84844
+ stdout: "",
84845
+ stderr: ""
84846
+ };
84847
+ if (isStderr) {
84848
+ outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
84849
+ } else {
84850
+ outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
84851
+ }
84852
+ outputByToolCallId.set(toolCallId, outputState);
84853
+ const stdout = normalizeStreamingOutputLines(outputState.stdout);
84854
+ const stderr = normalizeStreamingOutputLines(outputState.stderr);
84855
+ const toolReturn = [stdout?.join(`
84856
+ `), stderr?.join(`
84857
+ `)].filter((part) => typeof part === "string" && part.length > 0).join(`
84858
+ `);
84859
+ emitCanonicalMessageDelta(socket, runtime, {
84860
+ type: "message",
84861
+ message_type: "tool_return_message",
84862
+ id: outputState.messageId,
84863
+ date: new Date().toISOString(),
84864
+ run_id: params.runId ?? runtime.activeRunId ?? undefined,
84865
+ status: "success",
84866
+ tool_call_id: toolCallId,
84867
+ tool_return: toolReturn,
84868
+ tool_returns: [
84869
+ {
84870
+ tool_call_id: toolCallId,
84871
+ status: "success",
84872
+ tool_return: toolReturn,
84873
+ ...stdout ? { stdout } : {},
84874
+ ...stderr ? { stderr } : {}
84875
+ }
84876
+ ]
84877
+ }, {
84878
+ agent_id: params.agentId,
84879
+ conversation_id: params.conversationId
84880
+ });
84881
+ };
84882
+ }
84184
84883
  function getInterruptApprovalsForEmission(runtime, params) {
84185
84884
  if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
84186
84885
  return params.lastExecutionResults;
@@ -84297,7 +84996,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
84297
84996
  clearRecoveredApprovalState(runtime);
84298
84997
  return true;
84299
84998
  }
84300
- var INTERRUPT_TOOL_RETURN_MAX_CHARS;
84999
+ var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS;
84301
85000
  var init_interrupts = __esm(async () => {
84302
85001
  init_approval_result_normalization();
84303
85002
  init_constants();
@@ -84309,6 +85008,7 @@ var init_interrupts = __esm(async () => {
84309
85008
  init_protocol_outbound()
84310
85009
  ]);
84311
85010
  INTERRUPT_TOOL_RETURN_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
85011
+ STREAMING_TOOL_OUTPUT_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
84312
85012
  });
84313
85013
 
84314
85014
  // src/websocket/listener/permissionMode.ts
@@ -85382,7 +86082,7 @@ __export(exports_diff, {
85382
86082
  ADV_DIFF_IGNORE_WHITESPACE: () => ADV_DIFF_IGNORE_WHITESPACE,
85383
86083
  ADV_DIFF_CONTEXT_LINES: () => ADV_DIFF_CONTEXT_LINES
85384
86084
  });
85385
- import { basename as basename7 } from "node:path";
86085
+ import { basename as basename8 } from "node:path";
85386
86086
  function readFileOrNull(p) {
85387
86087
  try {
85388
86088
  return __require("node:fs").readFileSync(p, "utf-8");
@@ -85406,7 +86106,7 @@ function applyAllOccurrences(content, oldStr, newStr) {
85406
86106
  return { ok: true, out: content.split(oldStr).join(newStr) };
85407
86107
  }
85408
86108
  function computeAdvancedDiff(input, opts) {
85409
- const fileName = basename7(input.filePath || "");
86109
+ const fileName = basename8(input.filePath || "");
85410
86110
  const fileContent = opts?.oldStrOverride !== undefined ? opts.oldStrOverride : readFileOrNull(input.filePath);
85411
86111
  if (fileContent === null && input.kind !== "write") {
85412
86112
  return { mode: "fallback", reason: "File not readable" };
@@ -85472,7 +86172,7 @@ function computeAdvancedDiff(input, opts) {
85472
86172
  return { mode: "advanced", fileName, oldStr, newStr, hunks };
85473
86173
  }
85474
86174
  function parsePatchToAdvancedDiff(patchLines, filePath) {
85475
- const fileName = basename7(filePath);
86175
+ const fileName = basename8(filePath);
85476
86176
  const hunks = [];
85477
86177
  let currentHunk = null;
85478
86178
  let oldLine = 1;
@@ -85753,8 +86453,8 @@ function isFormatterSegment(tokens) {
85753
86453
  }
85754
86454
  }
85755
86455
  function isShellExecutor2(token) {
85756
- const basename8 = token.split("/").pop() ?? token;
85757
- return ["bash", "sh", "zsh", "dash", "ksh"].includes(basename8.toLowerCase());
86456
+ const basename9 = token.split("/").pop() ?? token;
86457
+ return ["bash", "sh", "zsh", "dash", "ksh"].includes(basename9.toLowerCase());
85758
86458
  }
85759
86459
  function normalizeRawCommand(command) {
85760
86460
  if (Array.isArray(command)) {
@@ -86463,7 +87163,7 @@ var init_formatArgsDisplay = __esm(async () => {
86463
87163
  });
86464
87164
 
86465
87165
  // src/helpers/diffPreview.ts
86466
- import path21, { basename as basename8 } from "node:path";
87166
+ import path21, { basename as basename9 } from "node:path";
86467
87167
  function parseHunkLinePrefix(raw) {
86468
87168
  if (raw.length === 0) {
86469
87169
  return { type: "context", content: "" };
@@ -86569,7 +87269,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86569
87269
  filePath: resolvedFilePath,
86570
87270
  content: toolArgs.content || ""
86571
87271
  });
86572
- previews.push(toDiffPreview(result, basename8(filePath)));
87272
+ previews.push(toDiffPreview(result, basename9(filePath)));
86573
87273
  }
86574
87274
  } else if (isFileEditTool2(toolName)) {
86575
87275
  const filePath = toolArgs.file_path;
@@ -86581,7 +87281,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86581
87281
  filePath: resolvedFilePath,
86582
87282
  edits: toolArgs.edits
86583
87283
  });
86584
- previews.push(toDiffPreview(result, basename8(filePath)));
87284
+ previews.push(toDiffPreview(result, basename9(filePath)));
86585
87285
  } else {
86586
87286
  const result = computeAdvancedDiff2({
86587
87287
  kind: "edit",
@@ -86590,7 +87290,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86590
87290
  newString: toolArgs.new_string || "",
86591
87291
  replaceAll: toolArgs.replace_all
86592
87292
  });
86593
- previews.push(toDiffPreview(result, basename8(filePath)));
87293
+ previews.push(toDiffPreview(result, basename9(filePath)));
86594
87294
  }
86595
87295
  }
86596
87296
  } else if (isPatchTool2(toolName) && toolArgs.input) {
@@ -86599,7 +87299,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86599
87299
  if (op.kind === "add" || op.kind === "update") {
86600
87300
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
86601
87301
  if (result) {
86602
- previews.push(toDiffPreview(result, basename8(op.path)));
87302
+ previews.push(toDiffPreview(result, basename9(op.path)));
86603
87303
  }
86604
87304
  }
86605
87305
  }
@@ -86609,7 +87309,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
86609
87309
  if (op.kind === "add" || op.kind === "update") {
86610
87310
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
86611
87311
  if (result) {
86612
- previews.push(toDiffPreview(result, basename8(op.path)));
87312
+ previews.push(toDiffPreview(result, basename9(op.path)));
86613
87313
  }
86614
87314
  }
86615
87315
  }
@@ -87149,6 +87849,11 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
87149
87849
  agentId: recovered.agentId,
87150
87850
  conversationId: recovered.conversationId
87151
87851
  });
87852
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
87853
+ runId: runtime.activeRunId ?? undefined,
87854
+ agentId: recovered.agentId,
87855
+ conversationId: recovered.conversationId
87856
+ });
87152
87857
  const recoveryAbortController = new AbortController;
87153
87858
  runtime.activeAbortController = recoveryAbortController;
87154
87859
  const preparedToolContext = await prepareToolExecutionContextForScope({
@@ -87163,6 +87868,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
87163
87868
  try {
87164
87869
  const approvalResults = await executeApprovalBatch(decisions, undefined, {
87165
87870
  abortSignal: recoveryAbortController.signal,
87871
+ onStreamingOutput: emitToolExecutionOutput,
87166
87872
  toolContextId: preparedToolContext.preparedToolContext.contextId,
87167
87873
  workingDirectory,
87168
87874
  parentScope: recovered.agentId && recovered.conversationId ? {
@@ -87433,9 +88139,15 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
87433
88139
  agentId: runtime.agentId ?? undefined,
87434
88140
  conversationId: recoveryConversationId
87435
88141
  });
88142
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
88143
+ runId: runtime.activeRunId ?? undefined,
88144
+ agentId: runtime.agentId ?? undefined,
88145
+ conversationId: recoveryConversationId
88146
+ });
87436
88147
  try {
87437
88148
  const approvalResults = await executeApprovalBatch(decisions, undefined, {
87438
88149
  abortSignal,
88150
+ onStreamingOutput: emitToolExecutionOutput,
87439
88151
  toolContextId: preparedToolContext.preparedToolContext.contextId,
87440
88152
  workingDirectory: recoveryWorkingDirectory,
87441
88153
  parentScope: runtime.agentId && runtime.conversationId ? {
@@ -88004,6 +88716,11 @@ async function handleApprovalStop(params) {
88004
88716
  agentId,
88005
88717
  conversationId
88006
88718
  });
88719
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
88720
+ runId: executionRunId,
88721
+ agentId,
88722
+ conversationId
88723
+ });
88007
88724
  if (shouldInterrupt()) {
88008
88725
  return interruptTermination();
88009
88726
  }
@@ -88022,6 +88739,7 @@ async function handleApprovalStop(params) {
88022
88739
  const executionResults = await executeApprovalBatch(decisions, undefined, {
88023
88740
  toolContextId: turnToolContextId ?? undefined,
88024
88741
  abortSignal: abortController.signal,
88742
+ onStreamingOutput: emitToolExecutionOutput,
88025
88743
  workingDirectory: turnWorkingDirectory,
88026
88744
  parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
88027
88745
  onFileWrite
@@ -89703,6 +90421,65 @@ function mergeDequeuedBatchContent(items) {
89703
90421
  normalizeUserContent: (content) => content
89704
90422
  });
89705
90423
  }
90424
+ function getChannelTurnSourceKey(source) {
90425
+ return [
90426
+ source.channel,
90427
+ source.accountId ?? "",
90428
+ source.chatId,
90429
+ source.messageId ?? "",
90430
+ source.threadId ?? "",
90431
+ source.agentId,
90432
+ source.conversationId
90433
+ ].join(":");
90434
+ }
90435
+ function collectBatchChannelTurnSources(runtime, batch) {
90436
+ const seen = new Set;
90437
+ const sources = [];
90438
+ for (const item of batch.items) {
90439
+ const template = runtime.queuedMessagesByItemId.get(item.id);
90440
+ for (const source of template?.channelTurnSources ?? []) {
90441
+ const key = getChannelTurnSourceKey(source);
90442
+ if (seen.has(key)) {
90443
+ continue;
90444
+ }
90445
+ seen.add(key);
90446
+ sources.push(source);
90447
+ }
90448
+ }
90449
+ return sources.length > 0 ? sources : undefined;
90450
+ }
90451
+ async function dispatchChannelTurnLifecycleEvent(event) {
90452
+ if (event.sources.length === 0) {
90453
+ return;
90454
+ }
90455
+ const registry = getChannelRegistry();
90456
+ if (!registry) {
90457
+ return;
90458
+ }
90459
+ if (event.type === "processing") {
90460
+ await registry.dispatchTurnLifecycleEvent(event);
90461
+ return;
90462
+ }
90463
+ await registry.dispatchTurnLifecycleEvent({
90464
+ type: "finished",
90465
+ batchId: event.batchId,
90466
+ sources: event.sources,
90467
+ outcome: event.outcome,
90468
+ ...event.error ? { error: event.error } : {}
90469
+ });
90470
+ }
90471
+ function mapTurnLifecycleOutcome(lastStopReason, didThrow) {
90472
+ if (didThrow) {
90473
+ return "error";
90474
+ }
90475
+ if (lastStopReason === "cancelled") {
90476
+ return "cancelled";
90477
+ }
90478
+ if (lastStopReason && lastStopReason !== "end_turn") {
90479
+ return "error";
90480
+ }
90481
+ return "completed";
90482
+ }
89706
90483
  function isBase64ImageContentPart(part) {
89707
90484
  if (!part || typeof part !== "object") {
89708
90485
  return false;
@@ -89762,6 +90539,7 @@ function getPrimaryQueueMessageItem(items) {
89762
90539
  return null;
89763
90540
  }
89764
90541
  function buildQueuedTurnMessage(runtime, batch) {
90542
+ const channelTurnSources = collectBatchChannelTurnSources(runtime, batch);
89765
90543
  const primaryItem = getPrimaryQueueMessageItem(batch.items);
89766
90544
  if (!primaryItem) {
89767
90545
  for (const item of batch.items) {
@@ -89776,6 +90554,7 @@ function buildQueuedTurnMessage(runtime, batch) {
89776
90554
  type: "message",
89777
90555
  agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
89778
90556
  conversationId: scopeItem?.conversationId ?? runtime.conversationId,
90557
+ ...channelTurnSources ? { channelTurnSources } : {},
89779
90558
  messages: [
89780
90559
  {
89781
90560
  role: "user",
@@ -89808,6 +90587,7 @@ function buildQueuedTurnMessage(runtime, batch) {
89808
90587
  messages[firstMessageIndex] = mergedFirstMessage;
89809
90588
  return {
89810
90589
  ...template,
90590
+ ...channelTurnSources ? { channelTurnSources } : {},
89811
90591
  messages
89812
90592
  };
89813
90593
  }
@@ -89888,13 +90668,39 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
89888
90668
  return;
89889
90669
  }
89890
90670
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
90671
+ const channelTurnSources = queuedTurn.channelTurnSources ?? [];
89891
90672
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
89892
90673
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
89893
90674
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
89894
90675
  runtime.listener.lastEmittedStatus = preTurnStatus;
89895
90676
  opts.onStatusChange?.(preTurnStatus, opts.connectionId);
89896
90677
  }
89897
- await processQueuedTurn(queuedTurn, dequeuedBatch);
90678
+ if (channelTurnSources.length > 0) {
90679
+ await dispatchChannelTurnLifecycleEvent({
90680
+ type: "processing",
90681
+ batchId: dequeuedBatch.batchId,
90682
+ sources: channelTurnSources
90683
+ });
90684
+ }
90685
+ let turnError;
90686
+ let didThrow = false;
90687
+ try {
90688
+ await processQueuedTurn(queuedTurn, dequeuedBatch);
90689
+ } catch (error) {
90690
+ didThrow = true;
90691
+ turnError = error instanceof Error ? error.message : String(error);
90692
+ throw error;
90693
+ } finally {
90694
+ if (channelTurnSources.length > 0) {
90695
+ await dispatchChannelTurnLifecycleEvent({
90696
+ type: "finished",
90697
+ batchId: dequeuedBatch.batchId,
90698
+ sources: channelTurnSources,
90699
+ outcome: mapTurnLifecycleOutcome(runtime.lastStopReason, didThrow),
90700
+ ...turnError ? { error: turnError } : {}
90701
+ });
90702
+ }
90703
+ }
89898
90704
  emitListenerStatus(runtime.listener, opts.onStatusChange, opts.connectionId);
89899
90705
  evictConversationRuntimeIfIdle(runtime);
89900
90706
  }
@@ -89927,6 +90733,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
89927
90733
  });
89928
90734
  }
89929
90735
  var init_queue = __esm(async () => {
90736
+ init_registry();
89930
90737
  init_queueRuntime();
89931
90738
  init_errorReporting();
89932
90739
  init_runtime3();
@@ -89961,11 +90768,6 @@ function refreshTaskCache(state) {
89961
90768
  }
89962
90769
  }
89963
90770
  function shouldFireTask(task2, now) {
89964
- if (task2.recurring && task2.expires_at) {
89965
- if (new Date(task2.expires_at).getTime() <= now.getTime()) {
89966
- return false;
89967
- }
89968
- }
89969
90771
  if (!task2.recurring && task2.scheduled_for) {
89970
90772
  const scheduledMs = new Date(task2.scheduled_for).getTime() + task2.jitter_offset_ms;
89971
90773
  return scheduledMs <= now.getTime();
@@ -90005,16 +90807,6 @@ function fireCronTask(task2, now, socket, opts, processQueuedTurn) {
90005
90807
  });
90006
90808
  }
90007
90809
  }
90008
- function handleExpiredRecurring(task2, now) {
90009
- if (!task2.recurring || !task2.expires_at)
90010
- return;
90011
- if (new Date(task2.expires_at).getTime() <= now.getTime()) {
90012
- updateTask(task2.id, (t) => {
90013
- t.status = "cancelled";
90014
- t.cancel_reason = "expired";
90015
- });
90016
- }
90017
- }
90018
90810
  function handleMissedOneShot(task2, now) {
90019
90811
  if (task2.recurring || !task2.scheduled_for)
90020
90812
  return false;
@@ -90043,9 +90835,6 @@ function tick2(state, socket, opts, processQueuedTurn) {
90043
90835
  }
90044
90836
  refreshTaskCache(state);
90045
90837
  for (const task2 of state.cachedTasks) {
90046
- if (task2.status !== "active")
90047
- continue;
90048
- handleExpiredRecurring(task2, now);
90049
90838
  if (task2.status !== "active")
90050
90839
  continue;
90051
90840
  if (handleMissedOneShot(task2, now))
@@ -90509,6 +91298,12 @@ function isListInDirectoryCommand(value) {
90509
91298
  const c = value;
90510
91299
  return c.type === "list_in_directory" && typeof c.path === "string";
90511
91300
  }
91301
+ function isGetTreeCommand(value) {
91302
+ if (!value || typeof value !== "object")
91303
+ return false;
91304
+ const c = value;
91305
+ return c.type === "get_tree" && typeof c.path === "string" && typeof c.depth === "number" && typeof c.request_id === "string";
91306
+ }
90512
91307
  function isReadFileCommand(value) {
90513
91308
  if (!value || typeof value !== "object")
90514
91309
  return false;
@@ -90835,7 +91630,7 @@ function parseServerMessage(data) {
90835
91630
  try {
90836
91631
  const raw = typeof data === "string" ? data : data.toString();
90837
91632
  const parsed = JSON.parse(raw);
90838
- if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isFileOpsCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isChannelsListCommand(parsed) || isChannelAccountsListCommand(parsed) || isChannelAccountCreateCommand(parsed) || isChannelAccountUpdateCommand(parsed) || isChannelAccountBindCommand(parsed) || isChannelAccountUnbindCommand(parsed) || isChannelAccountDeleteCommand(parsed) || isChannelAccountStartCommand(parsed) || isChannelAccountStopCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelTargetsListCommand(parsed) || isChannelTargetBindCommand(parsed) || isChannelRouteUpdateCommand(parsed) || isChannelRouteRemoveCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
91633
+ if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed) || isListInDirectoryCommand(parsed) || isGetTreeCommand(parsed) || isReadFileCommand(parsed) || isWriteFileCommand(parsed) || isWatchFileCommand(parsed) || isUnwatchFileCommand(parsed) || isEditFileCommand(parsed) || isFileOpsCommand(parsed) || isListMemoryCommand(parsed) || isMemoryHistoryCommand(parsed) || isMemoryFileAtRefCommand(parsed) || isEnableMemfsCommand(parsed) || isListModelsCommand(parsed) || isUpdateModelCommand(parsed) || isCronListCommand(parsed) || isCronAddCommand(parsed) || isCronGetCommand(parsed) || isCronDeleteCommand(parsed) || isCronDeleteAllCommand(parsed) || isSkillEnableCommand(parsed) || isSkillDisableCommand(parsed) || isCreateAgentCommand(parsed) || isGetReflectionSettingsCommand(parsed) || isSetReflectionSettingsCommand(parsed) || isChannelsListCommand(parsed) || isChannelAccountsListCommand(parsed) || isChannelAccountCreateCommand(parsed) || isChannelAccountUpdateCommand(parsed) || isChannelAccountBindCommand(parsed) || isChannelAccountUnbindCommand(parsed) || isChannelAccountDeleteCommand(parsed) || isChannelAccountStartCommand(parsed) || isChannelAccountStopCommand(parsed) || isChannelGetConfigCommand(parsed) || isChannelSetConfigCommand(parsed) || isChannelStartCommand(parsed) || isChannelStopCommand(parsed) || isChannelPairingsListCommand(parsed) || isChannelPairingBindCommand(parsed) || isChannelRoutesListCommand(parsed) || isChannelTargetsListCommand(parsed) || isChannelTargetBindCommand(parsed) || isChannelRouteUpdateCommand(parsed) || isChannelRouteRemoveCommand(parsed) || isExecuteCommandCommand(parsed) || isSearchBranchesCommand(parsed) || isCheckoutBranchCommand(parsed)) {
90839
91634
  return parsed;
90840
91635
  }
90841
91636
  const invalidInput = getInvalidInputReason(parsed);
@@ -91033,7 +91828,7 @@ function scanMemoryFilesystem(memoryRoot) {
91033
91828
  } catch {
91034
91829
  return;
91035
91830
  }
91036
- const relativePath = relative12(memoryRoot, fullPath);
91831
+ const relativePath = relative12(memoryRoot, fullPath).replace(/\\/g, "/");
91037
91832
  const isLast = index === sorted.length - 1;
91038
91833
  nodes.push({
91039
91834
  name: isDir ? `${name}/` : name,
@@ -91208,18 +92003,20 @@ function formatToolsetStatusMessageForModelUpdate(params) {
91208
92003
  }
91209
92004
  return "Manual toolset override remains active: " + formatToolsetName(toolsetPreference) + ".";
91210
92005
  }
91211
- function formatEffortSuffix(updateArgs) {
92006
+ function formatEffortSuffix(modelLabel, updateArgs) {
91212
92007
  if (!updateArgs)
91213
92008
  return "";
91214
92009
  const effort = updateArgs.reasoning_effort;
91215
92010
  if (typeof effort !== "string" || effort.length === 0)
91216
92011
  return "";
92012
+ const xhighLabel = modelLabel.includes("Opus 4.7") ? "Extra-High" : "Max";
91217
92013
  const labels = {
91218
92014
  none: "No Reasoning",
91219
92015
  low: "Low",
91220
92016
  medium: "Medium",
91221
92017
  high: "High",
91222
- xhigh: "Max"
92018
+ xhigh: xhighLabel,
92019
+ max: "Max"
91223
92020
  };
91224
92021
  return ` (${labels[effort] ?? effort})`;
91225
92022
  }
@@ -91232,7 +92029,7 @@ function buildModelUpdateStatusMessage(params) {
91232
92029
  toolsetPreference,
91233
92030
  updateArgs
91234
92031
  } = params;
91235
- let message = `Model updated to ${modelLabel}${formatEffortSuffix(updateArgs)}.`;
92032
+ let message = `Model updated to ${modelLabel}${formatEffortSuffix(modelLabel, updateArgs)}.`;
91236
92033
  if (toolsetError) {
91237
92034
  message += ` Warning: toolset switch failed (${toolsetError}).`;
91238
92035
  return { message, level: "warning" };
@@ -91406,6 +92203,165 @@ function emitChannelTargetsUpdated(socket, channelId) {
91406
92203
  channel_id: channelId
91407
92204
  }, "listener_channels_send_failed", "listener_channels_command");
91408
92205
  }
92206
+ async function handleListMemoryCommand(parsed, socket, overrides = {}) {
92207
+ try {
92208
+ const {
92209
+ ensureLocalMemfsCheckout: actualEnsureLocalMemfsCheckout,
92210
+ getMemoryFilesystemRoot: actualGetMemoryFilesystemRoot,
92211
+ isMemfsEnabledOnServer: actualIsMemfsEnabledOnServer
92212
+ } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
92213
+ const ensureLocalMemfsCheckout2 = overrides.ensureLocalMemfsCheckout ?? actualEnsureLocalMemfsCheckout;
92214
+ const getMemoryFilesystemRoot2 = overrides.getMemoryFilesystemRoot ?? actualGetMemoryFilesystemRoot;
92215
+ const isMemfsEnabledOnServer2 = overrides.isMemfsEnabledOnServer ?? actualIsMemfsEnabledOnServer;
92216
+ const { scanMemoryFilesystem: scanMemoryFilesystem2, getFileNodes: getFileNodes2, readFileContent: readFileContent2 } = await Promise.resolve().then(() => (init_memoryScanner(), exports_memoryScanner));
92217
+ const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => exports_frontmatter);
92218
+ const { existsSync: existsSync26 } = await import("node:fs");
92219
+ const { join: join31, posix: posix2 } = await import("node:path");
92220
+ const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
92221
+ let memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
92222
+ const memfsEnabled = memfsInitialized ? true : await isMemfsEnabledOnServer2(parsed.agent_id);
92223
+ if (!memfsEnabled) {
92224
+ safeSocketSend(socket, {
92225
+ type: "list_memory_response",
92226
+ request_id: parsed.request_id,
92227
+ entries: [],
92228
+ done: true,
92229
+ total: 0,
92230
+ success: true,
92231
+ memfs_enabled: false,
92232
+ memfs_initialized: false
92233
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92234
+ return true;
92235
+ }
92236
+ if (!memfsInitialized) {
92237
+ await ensureLocalMemfsCheckout2(parsed.agent_id);
92238
+ memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
92239
+ }
92240
+ if (!memfsInitialized) {
92241
+ throw new Error("MemFS is enabled, but the local memory checkout could not be initialized.");
92242
+ }
92243
+ const treeNodes = scanMemoryFilesystem2(memoryRoot);
92244
+ const fileNodes = getFileNodes2(treeNodes).filter((n) => n.name.endsWith(".md"));
92245
+ const includeReferences = parsed.include_references === true;
92246
+ const allPaths = new Set(fileNodes.map((node) => node.relativePath));
92247
+ const normalizeMemoryReference = (rawReference, sourcePath) => {
92248
+ let target = rawReference.trim();
92249
+ if (!target) {
92250
+ return null;
92251
+ }
92252
+ if (target.startsWith("http://") || target.startsWith("https://") || target.startsWith("mailto:")) {
92253
+ return null;
92254
+ }
92255
+ target = target.replace(/^<|>$/g, "");
92256
+ target = target.split("#")[0] ?? "";
92257
+ target = target.split("?")[0] ?? "";
92258
+ target = target.trim().replace(/\\/g, "/");
92259
+ if (!target || target.startsWith("#")) {
92260
+ return null;
92261
+ }
92262
+ if (target.includes("|")) {
92263
+ target = target.split("|")[0] ?? "";
92264
+ }
92265
+ if (!target) {
92266
+ return null;
92267
+ }
92268
+ const sourceDir = posix2.dirname(sourcePath.replace(/\\/g, "/"));
92269
+ const candidate = target.startsWith("./") || target.startsWith("../") ? posix2.normalize(posix2.join(sourceDir, target)) : posix2.normalize(target.startsWith("/") ? target.slice(1) : target);
92270
+ if (!candidate || candidate.startsWith("../") || candidate === "." || candidate === "..") {
92271
+ return null;
92272
+ }
92273
+ const withExtension = candidate.endsWith(".md") ? candidate : `${candidate}.md`;
92274
+ const candidates = new Set([withExtension]);
92275
+ const isExplicitRelative = target.startsWith("./") || target.startsWith("../");
92276
+ if (!isExplicitRelative && !target.startsWith("/") && sourceDir && sourceDir !== ".") {
92277
+ candidates.add(posix2.normalize(posix2.join(sourceDir, withExtension)));
92278
+ }
92279
+ if (!withExtension.startsWith("system/")) {
92280
+ candidates.add(posix2.normalize(`system/${withExtension}`));
92281
+ }
92282
+ for (const resolved of candidates) {
92283
+ if (allPaths.has(resolved)) {
92284
+ return resolved;
92285
+ }
92286
+ }
92287
+ return null;
92288
+ };
92289
+ const extractMemoryReferences = (body, sourcePath) => {
92290
+ if (!body.includes("[[")) {
92291
+ return [];
92292
+ }
92293
+ const refs = new Set;
92294
+ for (const wikiMatch of body.matchAll(WIKI_LINK_REGEX)) {
92295
+ const rawTarget = wikiMatch[1];
92296
+ if (!rawTarget)
92297
+ continue;
92298
+ const normalized = normalizeMemoryReference(rawTarget, sourcePath);
92299
+ if (normalized && normalized !== sourcePath) {
92300
+ refs.add(normalized);
92301
+ }
92302
+ }
92303
+ return [...refs];
92304
+ };
92305
+ const CHUNK_SIZE = 5;
92306
+ const total = fileNodes.length;
92307
+ for (let i = 0;i < total; i += CHUNK_SIZE) {
92308
+ const chunk = fileNodes.slice(i, i + CHUNK_SIZE);
92309
+ const entries = chunk.map((node) => {
92310
+ const raw = readFileContent2(node.fullPath);
92311
+ const { frontmatter, body } = parseFrontmatter2(raw);
92312
+ const desc = frontmatter.description;
92313
+ return {
92314
+ relative_path: node.relativePath,
92315
+ is_system: node.relativePath.startsWith("system/") || node.relativePath.startsWith("system\\"),
92316
+ description: typeof desc === "string" ? desc : null,
92317
+ content: body,
92318
+ size: body.length,
92319
+ ...includeReferences ? {
92320
+ references: extractMemoryReferences(body, node.relativePath)
92321
+ } : {}
92322
+ };
92323
+ });
92324
+ const done = i + CHUNK_SIZE >= total;
92325
+ const sent = safeSocketSend(socket, {
92326
+ type: "list_memory_response",
92327
+ request_id: parsed.request_id,
92328
+ entries,
92329
+ done,
92330
+ total,
92331
+ success: true,
92332
+ memfs_enabled: true,
92333
+ memfs_initialized: true
92334
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92335
+ if (!sent) {
92336
+ return true;
92337
+ }
92338
+ }
92339
+ if (total === 0) {
92340
+ safeSocketSend(socket, {
92341
+ type: "list_memory_response",
92342
+ request_id: parsed.request_id,
92343
+ entries: [],
92344
+ done: true,
92345
+ total: 0,
92346
+ success: true,
92347
+ memfs_enabled: true,
92348
+ memfs_initialized: true
92349
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92350
+ }
92351
+ } catch (err) {
92352
+ trackListenerError("listener_list_memory_failed", err, "listener_memory_browser");
92353
+ safeSocketSend(socket, {
92354
+ type: "list_memory_response",
92355
+ request_id: parsed.request_id,
92356
+ entries: [],
92357
+ done: true,
92358
+ total: 0,
92359
+ success: false,
92360
+ error: err instanceof Error ? err.message : "Failed to list memory"
92361
+ }, "listener_list_memory_send_failed", "listener_list_memory");
92362
+ }
92363
+ return true;
92364
+ }
91409
92365
  async function handleCronCommand(parsed, socket) {
91410
92366
  if (parsed.type === "cron_list") {
91411
92367
  try {
@@ -91638,6 +92594,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
91638
92594
  has_bot_token: snapshot.hasBotToken,
91639
92595
  has_app_token: snapshot.hasAppToken,
91640
92596
  agent_id: snapshot.agentId,
92597
+ default_permission_mode: snapshot.defaultPermissionMode,
91641
92598
  created_at: snapshot.createdAt,
91642
92599
  updated_at: snapshot.updatedAt
91643
92600
  };
@@ -91731,6 +92688,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
91731
92688
  appToken: "app_token" in parsed.account ? parsed.account.app_token : undefined,
91732
92689
  mode: "mode" in parsed.account ? parsed.account.mode : undefined,
91733
92690
  agentId: "agent_id" in parsed.account ? parsed.account.agent_id : undefined,
92691
+ defaultPermissionMode: "default_permission_mode" in parsed.account ? parsed.account.default_permission_mode : undefined,
91734
92692
  dmPolicy: parsed.account.dm_policy,
91735
92693
  allowedUsers: parsed.account.allowed_users
91736
92694
  }, {
@@ -91771,6 +92729,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
91771
92729
  appToken: "app_token" in parsed.patch ? parsed.patch.app_token : undefined,
91772
92730
  mode: "mode" in parsed.patch ? parsed.patch.mode : undefined,
91773
92731
  agentId: "agent_id" in parsed.patch ? parsed.patch.agent_id : undefined,
92732
+ defaultPermissionMode: "default_permission_mode" in parsed.patch ? parsed.patch.default_permission_mode : undefined,
91774
92733
  dmPolicy: parsed.patch.dm_policy,
91775
92734
  allowedUsers: parsed.patch.allowed_users
91776
92735
  });
@@ -92259,7 +93218,7 @@ async function handleSkillCommand(parsed, socket) {
92259
93218
  symlinkSync,
92260
93219
  unlinkSync: unlinkSync8
92261
93220
  } = await import("node:fs");
92262
- const { basename: basename9, join: join31 } = await import("node:path");
93221
+ const { basename: basename10, join: join31 } = await import("node:path");
92263
93222
  const lettaHome = process.env.LETTA_HOME || join31(process.env.HOME || process.env.USERPROFILE || "~", ".letta");
92264
93223
  const globalSkillsDir = join31(lettaHome, "skills");
92265
93224
  if (parsed.type === "skill_enable") {
@@ -92283,7 +93242,7 @@ async function handleSkillCommand(parsed, socket) {
92283
93242
  }, "listener_skill_send_failed", "listener_skill_command");
92284
93243
  return true;
92285
93244
  }
92286
- const linkName = basename9(parsed.skill_path);
93245
+ const linkName = basename10(parsed.skill_path);
92287
93246
  const linkPath = join31(globalSkillsDir, linkName);
92288
93247
  mkdirSync19(globalSkillsDir, { recursive: true });
92289
93248
  if (existsSync26(linkPath)) {
@@ -92495,25 +93454,45 @@ function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
92495
93454
  const registry = getChannelRegistry();
92496
93455
  if (!registry)
92497
93456
  return;
92498
- registry.setMessageHandler((route, messageContent) => {
92499
- const rawRuntime = getOrCreateConversationRuntime(listener, route.agentId, route.conversationId);
93457
+ registry.setMessageHandler((delivery) => {
93458
+ const rawRuntime = getOrCreateConversationRuntime(listener, delivery.route.agentId, delivery.route.conversationId);
92500
93459
  if (!rawRuntime)
92501
93460
  return;
92502
93461
  const conversationRuntime = ensureConversationQueueRuntime(listener, rawRuntime);
92503
- enqueueChannelTurn(conversationRuntime, route, messageContent);
93462
+ const enqueuedItem = enqueueChannelTurn(conversationRuntime, delivery.route, delivery.content, delivery.turnSources);
93463
+ if (!enqueuedItem) {
93464
+ return;
93465
+ }
93466
+ for (const turnSource of delivery.turnSources ?? []) {
93467
+ registry.dispatchTurnLifecycleEvent({
93468
+ type: "queued",
93469
+ source: turnSource
93470
+ });
93471
+ }
92504
93472
  scheduleQueuePump(conversationRuntime, socket, opts, processQueuedTurn);
92505
93473
  });
92506
93474
  registry.setEventHandler((event) => {
92507
- if (event.type === "pairings_updated") {
92508
- emitChannelPairingsUpdated(socket, event.channelId);
92509
- emitChannelsUpdated(socket, event.channelId);
92510
- return;
92511
- }
92512
- emitChannelTargetsUpdated(socket, event.channelId);
92513
- emitChannelsUpdated(socket, event.channelId);
93475
+ handleChannelRegistryEvent(event, socket, listener);
92514
93476
  });
92515
93477
  registry.setReady();
92516
93478
  }
93479
+ function handleChannelRegistryEvent(event, socket, runtime) {
93480
+ if (event.type === "pairings_updated") {
93481
+ emitChannelPairingsUpdated(socket, event.channelId);
93482
+ emitChannelsUpdated(socket, event.channelId);
93483
+ return;
93484
+ }
93485
+ if (event.type === "targets_updated") {
93486
+ emitChannelTargetsUpdated(socket, event.channelId);
93487
+ emitChannelsUpdated(socket, event.channelId);
93488
+ return;
93489
+ }
93490
+ const permissionModeState = getOrCreateConversationPermissionModeStateRef(runtime, event.agentId, event.conversationId);
93491
+ permissionModeState.mode = event.defaultPermissionMode;
93492
+ permissionModeState.planFilePath = null;
93493
+ permissionModeState.modeBeforePlan = null;
93494
+ persistPermissionModeMapForRuntime(runtime);
93495
+ }
92517
93496
  function stampInboundUserMessageOtids(incoming) {
92518
93497
  let didChange = false;
92519
93498
  const messages = incoming.messages.map((payload) => {
@@ -92534,7 +93513,7 @@ function stampInboundUserMessageOtids(incoming) {
92534
93513
  messages
92535
93514
  };
92536
93515
  }
92537
- function enqueueChannelTurn(runtime, route, messageContent) {
93516
+ function enqueueChannelTurn(runtime, route, messageContent, turnSources) {
92538
93517
  const clientMessageId = `cm-channel-${crypto.randomUUID()}`;
92539
93518
  const enqueuedItem = runtime.queueRuntime.enqueue({
92540
93519
  kind: "message",
@@ -92551,6 +93530,7 @@ function enqueueChannelTurn(runtime, route, messageContent) {
92551
93530
  type: "message",
92552
93531
  agentId: route.agentId,
92553
93532
  conversationId: route.conversationId,
93533
+ ...turnSources?.length ? { channelTurnSources: turnSources } : {},
92554
93534
  messages: [
92555
93535
  {
92556
93536
  role: "user",
@@ -92921,6 +93901,51 @@ async function startListenerClient(opts) {
92921
93901
  telemetry.init();
92922
93902
  await connectWithRetry(runtime, opts);
92923
93903
  }
93904
+ async function listDirectoryHybrid(absDir, indexRoot3, includeFiles) {
93905
+ let indexedNames;
93906
+ const indexedFolders = [];
93907
+ const indexedFiles = [];
93908
+ if (indexRoot3 !== undefined) {
93909
+ const relPath = path23.relative(indexRoot3, absDir);
93910
+ if (!relPath.startsWith("..")) {
93911
+ const indexed = searchFileIndex({
93912
+ searchDir: relPath || ".",
93913
+ pattern: "",
93914
+ deep: false,
93915
+ maxResults: 1e4
93916
+ });
93917
+ indexedNames = new Set;
93918
+ for (const entry of indexed) {
93919
+ const name = entry.path.split(path23.sep).pop() ?? entry.path;
93920
+ indexedNames.add(name);
93921
+ if (entry.type === "dir") {
93922
+ indexedFolders.push(name);
93923
+ } else {
93924
+ indexedFiles.push(name);
93925
+ }
93926
+ }
93927
+ }
93928
+ }
93929
+ const { readdir: readdir7 } = await import("node:fs/promises");
93930
+ const entries = await readdir7(absDir, { withFileTypes: true });
93931
+ const extraFolders = [];
93932
+ const extraFiles = [];
93933
+ for (const e of entries) {
93934
+ if (DIR_LISTING_IGNORED_NAMES.has(e.name))
93935
+ continue;
93936
+ if (indexedNames?.has(e.name))
93937
+ continue;
93938
+ if (e.isDirectory()) {
93939
+ extraFolders.push(e.name);
93940
+ } else if (includeFiles) {
93941
+ extraFiles.push(e.name);
93942
+ }
93943
+ }
93944
+ return {
93945
+ folders: [...indexedFolders, ...extraFolders].sort((a, b) => a.localeCompare(b)),
93946
+ files: includeFiles ? [...indexedFiles, ...extraFiles].sort((a, b) => a.localeCompare(b)) : []
93947
+ };
93948
+ }
92924
93949
  async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now()) {
92925
93950
  if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
92926
93951
  return;
@@ -93241,33 +94266,21 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93241
94266
  console.log(`[Listen] Received list_in_directory command: path=${parsed.path}`);
93242
94267
  runDetachedListenerTask("list_in_directory", async () => {
93243
94268
  try {
93244
- const { readdir: readdir7 } = await import("node:fs/promises");
94269
+ let indexRoot3;
94270
+ try {
94271
+ await ensureFileIndex2();
94272
+ indexRoot3 = getIndexRoot();
94273
+ } catch {}
93245
94274
  console.log(`[Listen] Reading directory: ${parsed.path}`);
93246
- const entries = await readdir7(parsed.path, { withFileTypes: true });
93247
- console.log(`[Listen] Directory read success, ${entries.length} entries`);
93248
- const IGNORED_NAMES = new Set([
93249
- ".DS_Store",
93250
- ".git",
93251
- ".gitignore",
93252
- "Thumbs.db"
93253
- ]);
93254
- const sortedEntries = entries.filter((e) => !IGNORED_NAMES.has(e.name)).sort((a, b) => a.name.localeCompare(b.name));
93255
- const allFolders = [];
93256
- const allFiles = [];
93257
- for (const e of sortedEntries) {
93258
- if (e.isDirectory()) {
93259
- allFolders.push(e.name);
93260
- } else if (parsed.include_files) {
93261
- allFiles.push(e.name);
93262
- }
93263
- }
94275
+ const { folders: allFolders, files: allFiles } = await listDirectoryHybrid(parsed.path, indexRoot3, !!parsed.include_files);
93264
94276
  const total = allFolders.length + allFiles.length;
93265
94277
  const offset = parsed.offset ?? 0;
93266
94278
  const limit2 = parsed.limit ?? total;
93267
94279
  const combined = [...allFolders, ...allFiles];
93268
94280
  const page = combined.slice(offset, offset + limit2);
93269
- const folders = page.filter((name) => allFolders.includes(name));
93270
- const files = page.filter((name) => allFiles.includes(name));
94281
+ const folderSet = new Set(allFolders);
94282
+ const folders = page.filter((name) => folderSet.has(name));
94283
+ const files = page.filter((name) => !folderSet.has(name));
93271
94284
  const response = {
93272
94285
  type: "list_in_directory_response",
93273
94286
  path: parsed.path,
@@ -93298,6 +94311,71 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93298
94311
  });
93299
94312
  return;
93300
94313
  }
94314
+ if (isGetTreeCommand(parsed)) {
94315
+ console.log(`[Listen] Received get_tree command: path=${parsed.path}, depth=${parsed.depth}`);
94316
+ runDetachedListenerTask("get_tree", async () => {
94317
+ try {
94318
+ const results = [];
94319
+ let hasMoreDepth = false;
94320
+ let indexRoot3;
94321
+ try {
94322
+ await ensureFileIndex2();
94323
+ indexRoot3 = getIndexRoot();
94324
+ } catch {}
94325
+ const queue = [[parsed.path, "", 0]];
94326
+ let qi = 0;
94327
+ while (qi < queue.length) {
94328
+ const item = queue[qi++];
94329
+ if (!item)
94330
+ break;
94331
+ const [absDir, relDir, depth] = item;
94332
+ if (depth >= parsed.depth) {
94333
+ if (depth === parsed.depth && relDir !== "") {
94334
+ hasMoreDepth = true;
94335
+ }
94336
+ continue;
94337
+ }
94338
+ let listing;
94339
+ try {
94340
+ listing = await listDirectoryHybrid(absDir, indexRoot3, true);
94341
+ } catch {
94342
+ continue;
94343
+ }
94344
+ for (const name of listing.folders) {
94345
+ const entryRel = relDir === "" ? name : `${relDir}/${name}`;
94346
+ results.push({ path: entryRel, type: "dir" });
94347
+ queue.push([path23.join(absDir, name), entryRel, depth + 1]);
94348
+ }
94349
+ for (const name of listing.files) {
94350
+ const entryRel = relDir === "" ? name : `${relDir}/${name}`;
94351
+ results.push({ path: entryRel, type: "file" });
94352
+ }
94353
+ }
94354
+ console.log(`[Listen] Sending get_tree_response: ${results.length} entries, has_more_depth=${hasMoreDepth}`);
94355
+ safeSocketSend(socket, {
94356
+ type: "get_tree_response",
94357
+ path: parsed.path,
94358
+ request_id: parsed.request_id,
94359
+ entries: results,
94360
+ has_more_depth: hasMoreDepth,
94361
+ success: true
94362
+ }, "listener_get_tree_send_failed", "listener_get_tree");
94363
+ } catch (err) {
94364
+ trackListenerError("listener_get_tree_failed", err, "listener_file_browser");
94365
+ console.error(`[Listen] get_tree error: ${err instanceof Error ? err.message : "Unknown error"}`);
94366
+ safeSocketSend(socket, {
94367
+ type: "get_tree_response",
94368
+ path: parsed.path,
94369
+ request_id: parsed.request_id,
94370
+ entries: [],
94371
+ has_more_depth: false,
94372
+ success: false,
94373
+ error: err instanceof Error ? err.message : "Failed to get tree"
94374
+ }, "listener_get_tree_send_failed", "listener_get_tree");
94375
+ }
94376
+ });
94377
+ return;
94378
+ }
93301
94379
  if (isReadFileCommand(parsed)) {
93302
94380
  console.log(`[Listen] Received read_file command: path=${parsed.path}, request_id=${parsed.request_id}`);
93303
94381
  runDetachedListenerTask("read_file", async () => {
@@ -93358,6 +94436,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93358
94436
  }
93359
94437
  }
93360
94438
  console.log(`[Listen] write_file success: ${parsed.path} (${parsed.content.length} bytes)`);
94439
+ refreshFileIndex();
93361
94440
  safeSocketSend(socket, {
93362
94441
  type: "write_file_response",
93363
94442
  request_id: parsed.request_id,
@@ -93454,6 +94533,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93454
94533
  expected_replacements: parsed.expected_replacements
93455
94534
  });
93456
94535
  console.log(`[Listen] edit_file success: ${result.replacements} replacement(s) at line ${result.startLine}`);
94536
+ if (result.replacements > 0) {
94537
+ refreshFileIndex();
94538
+ }
93457
94539
  if (result.replacements > 0) {
93458
94540
  try {
93459
94541
  const contentAfter = await readFile11(parsed.file_path, "utf-8");
@@ -93509,144 +94591,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
93509
94591
  }
93510
94592
  if (isListMemoryCommand(parsed)) {
93511
94593
  runDetachedListenerTask("list_memory", async () => {
93512
- try {
93513
- const { getMemoryFilesystemRoot: getMemoryFilesystemRoot2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
93514
- const { scanMemoryFilesystem: scanMemoryFilesystem2, getFileNodes: getFileNodes2, readFileContent: readFileContent2 } = await Promise.resolve().then(() => (init_memoryScanner(), exports_memoryScanner));
93515
- const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => exports_frontmatter);
93516
- const { existsSync: existsSync26 } = await import("node:fs");
93517
- const { join: join31, posix: posix2 } = await import("node:path");
93518
- const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
93519
- const memfsInitialized = existsSync26(join31(memoryRoot, ".git"));
93520
- if (!memfsInitialized) {
93521
- safeSocketSend(socket, {
93522
- type: "list_memory_response",
93523
- request_id: parsed.request_id,
93524
- entries: [],
93525
- done: true,
93526
- total: 0,
93527
- success: true,
93528
- memfs_initialized: false
93529
- }, "listener_list_memory_send_failed", "listener_list_memory");
93530
- return;
93531
- }
93532
- const treeNodes = scanMemoryFilesystem2(memoryRoot);
93533
- const fileNodes = getFileNodes2(treeNodes).filter((n) => n.name.endsWith(".md"));
93534
- const includeReferences = parsed.include_references === true;
93535
- const allPaths = new Set(fileNodes.map((node) => node.relativePath));
93536
- const normalizeMemoryReference = (rawReference, sourcePath) => {
93537
- let target = rawReference.trim();
93538
- if (!target) {
93539
- return null;
93540
- }
93541
- if (target.startsWith("http://") || target.startsWith("https://") || target.startsWith("mailto:")) {
93542
- return null;
93543
- }
93544
- target = target.replace(/^<|>$/g, "");
93545
- target = target.split("#")[0] ?? "";
93546
- target = target.split("?")[0] ?? "";
93547
- target = target.trim().replace(/\\/g, "/");
93548
- if (!target || target.startsWith("#")) {
93549
- return null;
93550
- }
93551
- if (target.includes("|")) {
93552
- target = target.split("|")[0] ?? "";
93553
- }
93554
- if (!target) {
93555
- return null;
93556
- }
93557
- const sourceDir = posix2.dirname(sourcePath.replace(/\\/g, "/"));
93558
- const candidate = target.startsWith("./") || target.startsWith("../") ? posix2.normalize(posix2.join(sourceDir, target)) : posix2.normalize(target.startsWith("/") ? target.slice(1) : target);
93559
- if (!candidate || candidate.startsWith("../") || candidate === "." || candidate === "..") {
93560
- return null;
93561
- }
93562
- const withExtension = candidate.endsWith(".md") ? candidate : `${candidate}.md`;
93563
- const candidates = new Set([withExtension]);
93564
- const isExplicitRelative = target.startsWith("./") || target.startsWith("../");
93565
- if (!isExplicitRelative && !target.startsWith("/") && sourceDir && sourceDir !== ".") {
93566
- candidates.add(posix2.normalize(posix2.join(sourceDir, withExtension)));
93567
- }
93568
- if (!withExtension.startsWith("system/")) {
93569
- candidates.add(posix2.normalize(`system/${withExtension}`));
93570
- }
93571
- for (const resolved of candidates) {
93572
- if (allPaths.has(resolved)) {
93573
- return resolved;
93574
- }
93575
- }
93576
- return null;
93577
- };
93578
- const extractMemoryReferences = (body, sourcePath) => {
93579
- if (!body.includes("[[")) {
93580
- return [];
93581
- }
93582
- const refs = new Set;
93583
- for (const wikiMatch of body.matchAll(WIKI_LINK_REGEX)) {
93584
- const rawTarget = wikiMatch[1];
93585
- if (!rawTarget)
93586
- continue;
93587
- const normalized = normalizeMemoryReference(rawTarget, sourcePath);
93588
- if (normalized && normalized !== sourcePath) {
93589
- refs.add(normalized);
93590
- }
93591
- }
93592
- return [...refs];
93593
- };
93594
- const CHUNK_SIZE = 5;
93595
- const total = fileNodes.length;
93596
- for (let i = 0;i < total; i += CHUNK_SIZE) {
93597
- const chunk = fileNodes.slice(i, i + CHUNK_SIZE);
93598
- const entries = chunk.map((node) => {
93599
- const raw2 = readFileContent2(node.fullPath);
93600
- const { frontmatter, body } = parseFrontmatter2(raw2);
93601
- const desc = frontmatter.description;
93602
- return {
93603
- relative_path: node.relativePath,
93604
- is_system: node.relativePath.startsWith("system/") || node.relativePath.startsWith("system\\"),
93605
- description: typeof desc === "string" ? desc : null,
93606
- content: body,
93607
- size: body.length,
93608
- ...includeReferences ? {
93609
- references: extractMemoryReferences(body, node.relativePath)
93610
- } : {}
93611
- };
93612
- });
93613
- const done = i + CHUNK_SIZE >= total;
93614
- const sent = safeSocketSend(socket, {
93615
- type: "list_memory_response",
93616
- request_id: parsed.request_id,
93617
- entries,
93618
- done,
93619
- total,
93620
- success: true,
93621
- memfs_initialized: true
93622
- }, "listener_list_memory_send_failed", "listener_list_memory");
93623
- if (!sent) {
93624
- return;
93625
- }
93626
- }
93627
- if (total === 0) {
93628
- safeSocketSend(socket, {
93629
- type: "list_memory_response",
93630
- request_id: parsed.request_id,
93631
- entries: [],
93632
- done: true,
93633
- total: 0,
93634
- success: true,
93635
- memfs_initialized: true
93636
- }, "listener_list_memory_send_failed", "listener_list_memory");
93637
- }
93638
- } catch (err) {
93639
- trackListenerError("listener_list_memory_failed", err, "listener_memory_browser");
93640
- safeSocketSend(socket, {
93641
- type: "list_memory_response",
93642
- request_id: parsed.request_id,
93643
- entries: [],
93644
- done: true,
93645
- total: 0,
93646
- success: false,
93647
- error: err instanceof Error ? err.message : "Failed to list memory"
93648
- }, "listener_list_memory_send_failed", "listener_list_memory");
93649
- }
94594
+ await handleListMemoryCommand(parsed, socket);
93650
94595
  });
93651
94596
  return;
93652
94597
  }
@@ -94225,7 +95170,7 @@ function createLegacyTestRuntime() {
94225
95170
  }
94226
95171
  return bridge;
94227
95172
  }
94228
- var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, __listenClientTestUtils;
95173
+ var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, DIR_LISTING_IGNORED_NAMES, __listenClientTestUtils;
94229
95174
  var init_client4 = __esm(async () => {
94230
95175
  init_available_models();
94231
95176
  init_client2();
@@ -94272,6 +95217,7 @@ var init_client4 = __esm(async () => {
94272
95217
  init_protocol_outbound()
94273
95218
  ]);
94274
95219
  WIKI_LINK_REGEX = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g;
95220
+ DIR_LISTING_IGNORED_NAMES = new Set([".DS_Store", ".git", "Thumbs.db"]);
94275
95221
  __listenClientTestUtils = {
94276
95222
  setChannelsServiceLoaderForTests: (loader) => {
94277
95223
  channelsServiceLoaderOverride = loader;
@@ -94324,8 +95270,10 @@ var init_client4 = __esm(async () => {
94324
95270
  handleAbortMessageInput,
94325
95271
  handleChangeDeviceStateInput,
94326
95272
  handleCronCommand,
95273
+ handleListMemoryCommand,
94327
95274
  isDetachedChannelsCommand,
94328
95275
  handleChannelsProtocolCommand,
95276
+ handleChannelRegistryEvent,
94329
95277
  handleSkillCommand,
94330
95278
  handleCreateAgentCommand,
94331
95279
  handleReflectionSettingsCommand,
@@ -99479,10 +100427,10 @@ var init_headless = __esm(async () => {
99479
100427
  init_toolset()
99480
100428
  ]);
99481
100429
  PROVIDER_FALLBACK_MAP = {
99482
- opus: "bedrock-opus-4.6",
99483
100430
  "opus-4.6-no-reasoning": "bedrock-opus-4.6",
99484
100431
  "opus-4.6-low": "bedrock-opus-4.6",
99485
100432
  "opus-4.6-medium": "bedrock-opus-4.6",
100433
+ "opus-4.6-high": "bedrock-opus-4.6",
99486
100434
  "opus-4.6-xhigh": "bedrock-opus-4.6",
99487
100435
  sonnet: "bedrock-sonnet-4.6",
99488
100436
  "sonnet-1m": "bedrock-sonnet-4.6",
@@ -115408,16 +116356,16 @@ function clipStyledSpans(spans, maxColumns) {
115408
116356
  return { spans: clipped, clipped: false };
115409
116357
  }
115410
116358
  function languageFromPath(filePath) {
115411
- const basename9 = filePath.split("/").pop() ?? filePath;
115412
- const lower = basename9.toLowerCase();
116359
+ const basename10 = filePath.split("/").pop() ?? filePath;
116360
+ const lower = basename10.toLowerCase();
115413
116361
  if (lower === "makefile")
115414
116362
  return "makefile";
115415
116363
  if (lower === "dockerfile")
115416
116364
  return "dockerfile";
115417
- const dotIdx = basename9.lastIndexOf(".");
116365
+ const dotIdx = basename10.lastIndexOf(".");
115418
116366
  if (dotIdx < 0)
115419
116367
  return;
115420
- const ext3 = basename9.slice(dotIdx + 1).toLowerCase();
116368
+ const ext3 = basename10.slice(dotIdx + 1).toLowerCase();
115421
116369
  return EXT_TO_LANG[ext3];
115422
116370
  }
115423
116371
  function colorForClassName(className, palette) {
@@ -121829,7 +122777,7 @@ var init_pasteRegistry = __esm(() => {
121829
122777
  import { execFileSync as execFileSync3 } from "node:child_process";
121830
122778
  import { existsSync as existsSync34, readFileSync as readFileSync20, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
121831
122779
  import { tmpdir as tmpdir4 } from "node:os";
121832
- import { basename as basename9, extname as extname8, isAbsolute as isAbsolute20, join as join42, resolve as resolve29 } from "node:path";
122780
+ import { basename as basename10, extname as extname8, isAbsolute as isAbsolute20, join as join42, resolve as resolve29 } from "node:path";
121833
122781
  function countLines2(text) {
121834
122782
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
121835
122783
  }
@@ -121886,7 +122834,7 @@ function translatePasteForImages(paste) {
121886
122834
  const id = allocateImage({
121887
122835
  data: b64,
121888
122836
  mediaType: mt,
121889
- filename: basename9(filePath)
122837
+ filename: basename10(filePath)
121890
122838
  });
121891
122839
  s = `[Image #${id}]`;
121892
122840
  }
@@ -123493,7 +124441,7 @@ __export(exports_custom, {
123493
124441
  });
123494
124442
  import { existsSync as existsSync36 } from "node:fs";
123495
124443
  import { readdir as readdir10, readFile as readFile14 } from "node:fs/promises";
123496
- import { basename as basename10, dirname as dirname17, join as join44 } from "node:path";
124444
+ import { basename as basename11, dirname as dirname17, join as join44 } from "node:path";
123497
124445
  async function getCustomCommands() {
123498
124446
  if (cachedCommands !== null) {
123499
124447
  return cachedCommands;
@@ -123553,7 +124501,7 @@ async function findCommandFiles(currentPath, rootPath, commands2, source2) {
123553
124501
  async function parseCommandFile(filePath, rootPath, source2) {
123554
124502
  const content = await readFile14(filePath, "utf-8");
123555
124503
  const { frontmatter, body } = parseFrontmatter(content);
123556
- const id = basename10(filePath, ".md");
124504
+ const id = basename11(filePath, ".md");
123557
124505
  const relativePath = dirname17(filePath).slice(rootPath.length);
123558
124506
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
123559
124507
  let description = getStringField(frontmatter, "description");
@@ -126340,6 +127288,8 @@ function formatReasoningLabel(effort) {
126340
127288
  if (effort === "none")
126341
127289
  return null;
126342
127290
  if (effort === "xhigh")
127291
+ return "xhigh";
127292
+ if (effort === "max")
126343
127293
  return "max";
126344
127294
  if (effort === "minimal")
126345
127295
  return "minimal";
@@ -127378,6 +128328,8 @@ function getReasoningEffortTag(effort) {
127378
128328
  if (effort === "none")
127379
128329
  return null;
127380
128330
  if (effort === "xhigh")
128331
+ return "xhigh";
128332
+ if (effort === "max")
127381
128333
  return "max";
127382
128334
  if (effort === "minimal")
127383
128335
  return "minimal";
@@ -135005,10 +135957,12 @@ var init_MessageSearch = __esm(async () => {
135005
135957
  });
135006
135958
 
135007
135959
  // src/cli/components/ModelReasoningSelector.tsx
135008
- function formatEffortLabel(effort) {
135960
+ function formatEffortLabel(effort, hasDistinctMaxTier) {
135009
135961
  if (effort === "none")
135010
135962
  return "Off";
135011
135963
  if (effort === "xhigh")
135964
+ return hasDistinctMaxTier ? "Extra-High" : "Max";
135965
+ if (effort === "max")
135012
135966
  return "Max";
135013
135967
  if (effort === "minimal")
135014
135968
  return "Minimal";
@@ -135035,6 +135989,7 @@ function ModelReasoningSelector({
135035
135989
  }, [options, initialModelId]);
135036
135990
  const selectedOption = options[selectedIndex] ?? options[0];
135037
135991
  const effortOptions = import_react82.useMemo(() => options.filter((option) => option.effort !== "none"), [options]);
135992
+ const hasDistinctMaxTier = import_react82.useMemo(() => options.some((option) => option.effort === "max"), [options]);
135038
135993
  const totalBars = Math.max(effortOptions.length, 1);
135039
135994
  const selectedBars = import_react82.useMemo(() => {
135040
135995
  if (!selectedOption)
@@ -135073,7 +136028,7 @@ function ModelReasoningSelector({
135073
136028
  setSelectedIndex((prev) => (prev + 1) % options.length);
135074
136029
  }
135075
136030
  });
135076
- const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort) : "Medium";
136031
+ const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort, hasDistinctMaxTier) : "Medium";
135077
136032
  const selectedText = selectedBars > 0 ? EFFORT_BLOCK.repeat(selectedBars) : "";
135078
136033
  const remainingBars = totalBars > selectedBars ? EFFORT_BLOCK.repeat(totalBars - selectedBars) : "";
135079
136034
  return /* @__PURE__ */ jsx_dev_runtime59.jsxDEV(Box_default, {
@@ -143160,12 +144115,12 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
143160
144115
  const effort = modelSettings.effort;
143161
144116
  if (effort === "low" || effort === "medium" || effort === "high")
143162
144117
  return effort;
143163
- if (effort === "max")
143164
- return "xhigh";
144118
+ if (effort === "xhigh" || effort === "max")
144119
+ return effort;
143165
144120
  }
143166
144121
  }
143167
144122
  const re = llmConfig?.reasoning_effort;
143168
- if (re === "none" || re === "minimal" || re === "low" || re === "medium" || re === "high" || re === "xhigh")
144123
+ if (re === "none" || re === "minimal" || re === "low" || re === "medium" || re === "high" || re === "xhigh" || re === "max")
143169
144124
  return re;
143170
144125
  if (llmConfig?.enable_reasoner === false)
143171
144126
  return "none";
@@ -143174,7 +144129,7 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
143174
144129
  function inferReasoningEffortFromModelPreset(modelId, modelHandle) {
143175
144130
  const modelInfo = (modelId ? getModelInfo2(modelId) : null) ?? (modelHandle ? getModelInfo2(modelHandle) : null);
143176
144131
  const presetEffort = modelInfo?.updateArgs?.reasoning_effort;
143177
- if (presetEffort === "none" || presetEffort === "minimal" || presetEffort === "low" || presetEffort === "medium" || presetEffort === "high" || presetEffort === "xhigh") {
144132
+ if (presetEffort === "none" || presetEffort === "minimal" || presetEffort === "low" || presetEffort === "medium" || presetEffort === "high" || presetEffort === "xhigh" || presetEffort === "max") {
143178
144133
  return presetEffort;
143179
144134
  }
143180
144135
  return null;
@@ -143215,7 +144170,8 @@ function getErrorHintForStopReason(stopReason, currentModelId, modelEndpointType
143215
144170
  }
143216
144171
  const isAutoModel = currentModelId?.startsWith("auto") ?? false;
143217
144172
  const statusInfo = modelEndpointType && !isAutoModel ? PROVIDER_STATUS_PAGES[modelEndpointType] : undefined;
143218
- const hasBedrockOpus = currentModelId === "opus" && modelEndpointType === "anthropic" && getModelInfo2("bedrock-opus-4.6");
144173
+ const isOpus46 = currentModelId?.startsWith("opus-4.6") ?? false;
144174
+ const hasBedrockOpus = isOpus46 && modelEndpointType === "anthropic" && getModelInfo2("bedrock-opus-4.6");
143219
144175
  const modelSwapSuffix = hasBedrockOpus ? " (e.g. Opus 4.6 via Amazon Bedrock)" : "";
143220
144176
  if (statusInfo) {
143221
144177
  return [
@@ -143554,6 +144510,10 @@ function App2({
143554
144510
  import_react104.useEffect(() => {
143555
144511
  conversationIdRef.current = conversationId;
143556
144512
  }, [conversationId]);
144513
+ const setConversationIdAndRef = import_react104.useCallback((nextConversationId) => {
144514
+ conversationIdRef.current = nextConversationId;
144515
+ setConversationId3(nextConversationId);
144516
+ }, []);
143557
144517
  const pendingTranscriptStartLineIndexRef = import_react104.useRef(null);
143558
144518
  const lastRunIdRef = import_react104.useRef(null);
143559
144519
  const resumeKey = useSuspend();
@@ -143577,10 +144537,9 @@ function App2({
143577
144537
  import_react104.useEffect(() => {
143578
144538
  if (initialConversationId !== prevInitialConversationIdRef.current) {
143579
144539
  prevInitialConversationIdRef.current = initialConversationId;
143580
- conversationIdRef.current = initialConversationId;
143581
- setConversationId3(initialConversationId);
144540
+ setConversationIdAndRef(initialConversationId);
143582
144541
  }
143583
- }, [initialConversationId]);
144542
+ }, [initialConversationId, setConversationIdAndRef]);
143584
144543
  import_react104.useEffect(() => {
143585
144544
  if (agentId) {
143586
144545
  setCurrentAgentId(agentId);
@@ -147071,7 +148030,7 @@ ${feedback}
147071
148030
  const client = await getClient();
147072
148031
  const resumeData = await getResumeData2(client, agentState, conversationId2);
147073
148032
  await maybeCarryOverActiveConversationModel(conversationId2);
147074
- setConversationId3(conversationId2);
148033
+ setConversationIdAndRef(conversationId2);
147075
148034
  pendingConversationSwitchRef.current = {
147076
148035
  origin: "fork",
147077
148036
  conversationId: conversationId2,
@@ -147136,6 +148095,7 @@ ${feedback}
147136
148095
  runEndHooks,
147137
148096
  maybeCarryOverActiveConversationModel,
147138
148097
  resetBootstrapReminderState,
148098
+ setConversationIdAndRef,
147139
148099
  setCommandRunning,
147140
148100
  setStreaming,
147141
148101
  recoverRestoredPendingApprovals,
@@ -147188,7 +148148,7 @@ ${feedback}
147188
148148
  setLlmConfig(agent.llm_config);
147189
148149
  const agentModelHandle = getPreferredAgentModelHandle2(agent);
147190
148150
  setCurrentModelHandle(agentModelHandle);
147191
- setConversationId3(targetConversationId);
148151
+ setConversationIdAndRef(targetConversationId);
147192
148152
  resetBootstrapReminderState();
147193
148153
  {
147194
148154
  const { getModelDisplayName: getModelDisplayName3 } = await Promise.resolve().then(() => (init_model2(), exports_model2));
@@ -147241,7 +148201,8 @@ ${feedback}
147241
148201
  resetDeferredToolCallCommits,
147242
148202
  resetTrajectoryBases,
147243
148203
  resetBootstrapReminderState,
147244
- resetPendingReasoningCycle
148204
+ resetPendingReasoningCycle,
148205
+ setConversationIdAndRef
147245
148206
  ]);
147246
148207
  const handleCreateNewAgent = import_react104.useCallback(async (name) => {
147247
148208
  setActiveOverlay(null);
@@ -147309,7 +148270,7 @@ ${feedback}
147309
148270
  setLlmConfig(agent.llm_config);
147310
148271
  const agentModelHandle = getPreferredAgentModelHandle2(agent);
147311
148272
  setCurrentModelHandle(agentModelHandle);
147312
- setConversationId3(targetConversationId);
148273
+ setConversationIdAndRef(targetConversationId);
147313
148274
  pendingConversationSwitchRef.current = {
147314
148275
  origin: "agent-switch",
147315
148276
  conversationId: targetConversationId,
@@ -147343,7 +148304,8 @@ ${feedback}
147343
148304
  setCommandRunning,
147344
148305
  resetDeferredToolCallCommits,
147345
148306
  resetTrajectoryBases,
147346
- resetBootstrapReminderState
148307
+ resetBootstrapReminderState,
148308
+ setConversationIdAndRef
147347
148309
  ]);
147348
148310
  const handleBashSubmit = import_react104.useCallback(async (command) => {
147349
148311
  if (bashRunning)
@@ -148284,7 +149246,7 @@ Type your task to begin the loop.`, true);
148284
149246
  hasSetConversationSummaryRef.current = true;
148285
149247
  }
148286
149248
  await maybeCarryOverActiveConversationModel(conversation.id);
148287
- setConversationId3(conversation.id);
149249
+ setConversationIdAndRef(conversation.id);
148288
149250
  pendingConversationSwitchRef.current = {
148289
149251
  origin: "new",
148290
149252
  conversationId: conversation.id,
@@ -148329,7 +149291,7 @@ Type your task to begin the loop.`, true);
148329
149291
  hasSetConversationSummaryRef.current = true;
148330
149292
  }
148331
149293
  await maybeCarryOverActiveConversationModel(forked.id);
148332
- setConversationId3(forked.id);
149294
+ setConversationIdAndRef(forked.id);
148333
149295
  pendingConversationSwitchRef.current = {
148334
149296
  origin: "fork",
148335
149297
  conversationId: forked.id,
@@ -148383,7 +149345,7 @@ Type your task to begin the loop.`, true);
148383
149345
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
148384
149346
  });
148385
149347
  await maybeCarryOverActiveConversationModel(conversation.id);
148386
- setConversationId3(conversation.id);
149348
+ setConversationIdAndRef(conversation.id);
148387
149349
  pendingConversationSwitchRef.current = {
148388
149350
  origin: "clear",
148389
149351
  conversationId: conversation.id,
@@ -148634,7 +149596,7 @@ Type your task to begin the loop.`, true);
148634
149596
  if (agentState) {
148635
149597
  const client = await getClient();
148636
149598
  const resumeData = await getResumeData2(client, agentState, targetConvId);
148637
- setConversationId3(targetConvId);
149599
+ setConversationIdAndRef(targetConvId);
148638
149600
  pendingConversationSwitchRef.current = {
148639
149601
  origin: "resume-direct",
148640
149602
  conversationId: targetConvId,
@@ -149569,7 +150531,7 @@ ${SYSTEM_REMINDER_CLOSE}
149569
150531
  name: agentName,
149570
150532
  description: agentDescription,
149571
150533
  lastRunAt: agentLastRunAt,
149572
- conversationId
150534
+ conversationId: conversationIdRef.current
149573
150535
  },
149574
150536
  state: sharedReminderStateRef.current,
149575
150537
  sessionContextReminderEnabled,
@@ -150022,6 +150984,7 @@ ${SYSTEM_REMINDER_CLOSE}
150022
150984
  agentName,
150023
150985
  agentDescription,
150024
150986
  agentLastRunAt,
150987
+ conversationId,
150025
150988
  commandRunner,
150026
150989
  handleExit,
150027
150990
  isExecutingTool,
@@ -150040,7 +151003,8 @@ ${SYSTEM_REMINDER_CLOSE}
150040
151003
  resetTrajectoryBases,
150041
151004
  sessionContextReminderEnabled,
150042
151005
  appendTaskNotificationEvents,
150043
- maybeCarryOverActiveConversationModel
151006
+ maybeCarryOverActiveConversationModel,
151007
+ setConversationIdAndRef
150044
151008
  ]);
150045
151009
  const onSubmitRef = import_react104.useRef(onSubmit);
150046
151010
  import_react104.useEffect(() => {
@@ -150555,7 +151519,7 @@ ${SYSTEM_REMINDER_CLOSE}
150555
151519
  const modelHandle = model.handle ?? model.id;
150556
151520
  const modelUpdateArgs = model.updateArgs;
150557
151521
  const rawReasoningEffort = modelUpdateArgs?.reasoning_effort;
150558
- const reasoningLevel = typeof rawReasoningEffort === "string" ? rawReasoningEffort === "none" ? "no" : rawReasoningEffort === "xhigh" ? "max" : rawReasoningEffort : modelUpdateArgs?.enable_reasoner === false ? "no" : null;
151522
+ const reasoningLevel = typeof rawReasoningEffort === "string" ? rawReasoningEffort === "none" ? "no" : rawReasoningEffort === "xhigh" ? model.label.includes("Opus 4.7") ? "extra-high" : "max" : rawReasoningEffort : modelUpdateArgs?.enable_reasoner === false ? "no" : null;
150559
151523
  const selectedContextWindow = model.updateArgs?.context_window;
150560
151524
  const reasoningTierOptions = getReasoningTierOptionsForHandle3(modelHandle, selectedContextWindow);
150561
151525
  if (!opts?.skipReasoningPrompt && activeOverlay === "model" && reasoningTierOptions.length > 1) {
@@ -151040,7 +152004,7 @@ ${guidance}`);
151040
152004
  const client = await getClient();
151041
152005
  if (agentState) {
151042
152006
  const resumeData = await getResumeData2(client, agentState, action.conversationId);
151043
- setConversationId3(action.conversationId);
152007
+ setConversationIdAndRef(action.conversationId);
151044
152008
  pendingConversationSwitchRef.current = {
151045
152009
  origin: "resume-selector",
151046
152010
  conversationId: action.conversationId,
@@ -151093,7 +152057,8 @@ ${guidance}`);
151093
152057
  commandRunner.getHandle,
151094
152058
  commandRunner.start,
151095
152059
  recoverRestoredPendingApprovals,
151096
- resetBootstrapReminderState
152060
+ resetBootstrapReminderState,
152061
+ setConversationIdAndRef
151097
152062
  ]);
151098
152063
  const handleFeedbackSubmit = import_react104.useCallback(async (message) => {
151099
152064
  const overlayCommand = consumeOverlayCommand("feedback");
@@ -151330,7 +152295,16 @@ ${guidance}`);
151330
152295
  }).filter((m) => Boolean(m.effort));
151331
152296
  if (tiers.length < 2)
151332
152297
  return;
151333
- const order = ["none", "minimal", "low", "medium", "high", "xhigh"];
152298
+ const anthropicXHighEffort = modelHandle.includes("claude-opus-4-7") ? "xhigh" : "max";
152299
+ const order = [
152300
+ "none",
152301
+ "minimal",
152302
+ "low",
152303
+ "medium",
152304
+ "high",
152305
+ "xhigh",
152306
+ "max"
152307
+ ];
151334
152308
  const rank = (effort) => {
151335
152309
  const idx = order.indexOf(effort);
151336
152310
  return idx >= 0 ? idx : 999;
@@ -151367,12 +152341,11 @@ ${guidance}`);
151367
152341
  };
151368
152342
  }
151369
152343
  if (ms.provider_type === "anthropic" || ms.provider_type === "bedrock") {
151370
- const anthropicEffort = next.effort === "xhigh" ? "max" : next.effort;
151371
152344
  return {
151372
152345
  ...prev,
151373
152346
  model_settings: {
151374
152347
  ...ms,
151375
- effort: anthropicEffort
152348
+ effort: next.effort === "xhigh" ? anthropicXHighEffort : next.effort
151376
152349
  }
151377
152350
  };
151378
152351
  }
@@ -152351,7 +153324,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152351
153324
  if (agentState) {
152352
153325
  const client = await getClient();
152353
153326
  const resumeData = await getResumeData2(client, agentState, convId);
152354
- setConversationId3(convId);
153327
+ setConversationIdAndRef(convId);
152355
153328
  pendingConversationSwitchRef.current = {
152356
153329
  origin: "resume-selector",
152357
153330
  conversationId: convId,
@@ -152463,7 +153436,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152463
153436
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
152464
153437
  });
152465
153438
  await maybeCarryOverActiveConversationModel(conversation.id);
152466
- setConversationId3(conversation.id);
153439
+ setConversationIdAndRef(conversation.id);
152467
153440
  settingsManager.persistSession(agentId, conversation.id);
152468
153441
  const currentAgentName = agentState?.name || "Unnamed Agent";
152469
153442
  const shortConvId = conversation.id.slice(0, 20);
@@ -152547,7 +153520,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
152547
153520
  if (agentState) {
152548
153521
  const client = await getClient();
152549
153522
  const resumeData = await getResumeData2(client, agentState, actualTargetConv);
152550
- setConversationId3(actualTargetConv);
153523
+ setConversationIdAndRef(actualTargetConv);
152551
153524
  pendingConversationSwitchRef.current = {
152552
153525
  origin: "search",
152553
153526
  conversationId: actualTargetConv,
@@ -153919,10 +154892,12 @@ var exports_memoryFilesystem2 = {};
153919
154892
  __export(exports_memoryFilesystem2, {
153920
154893
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree2,
153921
154894
  labelFromRelativePath: () => labelFromRelativePath2,
154895
+ isMemfsEnabledOnServer: () => isMemfsEnabledOnServer2,
153922
154896
  isLettaCloud: () => isLettaCloud2,
153923
154897
  getMemorySystemDir: () => getMemorySystemDir2,
153924
154898
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot2,
153925
154899
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs2,
154900
+ ensureLocalMemfsCheckout: () => ensureLocalMemfsCheckout2,
153926
154901
  enableMemfsIfCloud: () => enableMemfsIfCloud2,
153927
154902
  applyMemfsFlags: () => applyMemfsFlags2,
153928
154903
  MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES2,
@@ -153952,6 +154927,23 @@ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir41()) {
153952
154927
  mkdirSync30(systemDir, { recursive: true });
153953
154928
  }
153954
154929
  }
154930
+ async function isMemfsEnabledOnServer2(agentId) {
154931
+ const { getClient: getClient3 } = await Promise.resolve().then(() => (init_client2(), exports_client));
154932
+ const client = await getClient3();
154933
+ const agent = await client.agents.retrieve(agentId);
154934
+ const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
154935
+ const enabled = agent.tags?.includes(GIT_MEMORY_ENABLED_TAG2) ?? false;
154936
+ const { settingsManager: settingsManager3 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
154937
+ settingsManager3.setMemfsEnabled(agentId, enabled);
154938
+ return enabled;
154939
+ }
154940
+ async function ensureLocalMemfsCheckout2(agentId) {
154941
+ const { isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
154942
+ if (isGitRepo2(agentId)) {
154943
+ return;
154944
+ }
154945
+ await cloneMemoryRepo2(agentId);
154946
+ }
153955
154947
  function labelFromRelativePath2(relativePath) {
153956
154948
  const normalized = relativePath.replace(/\\/g, "/");
153957
154949
  return normalized.replace(/\.md$/, "");
@@ -154794,6 +155786,9 @@ init_openai_codex_provider();
154794
155786
  init_debug();
154795
155787
  init_available_models();
154796
155788
  init_client2();
155789
+ function supportsDistinctAnthropicXHighEffort(modelHandle) {
155790
+ return modelHandle.includes("claude-opus-4-7");
155791
+ }
154797
155792
  function buildModelSettings(modelHandle, updateArgs) {
154798
155793
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
154799
155794
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -154827,10 +155822,13 @@ function buildModelSettings(modelHandle, updateArgs) {
154827
155822
  parallel_tool_calls: true
154828
155823
  };
154829
155824
  const effort = updateArgs?.reasoning_effort;
155825
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
154830
155826
  if (effort === "low" || effort === "medium" || effort === "high") {
154831
155827
  anthropicSettings.effort = effort;
154832
155828
  } else if (effort === "xhigh") {
154833
- anthropicSettings.effort = "max";
155829
+ anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
155830
+ } else if (effort === "max") {
155831
+ anthropicSettings.effort = effort;
154834
155832
  }
154835
155833
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
154836
155834
  anthropicSettings.thinking = {
@@ -154883,10 +155881,13 @@ function buildModelSettings(modelHandle, updateArgs) {
154883
155881
  parallel_tool_calls: true
154884
155882
  };
154885
155883
  const effort = updateArgs?.reasoning_effort;
155884
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
154886
155885
  if (effort === "low" || effort === "medium" || effort === "high") {
154887
155886
  bedrockSettings.effort = effort;
154888
155887
  } else if (effort === "xhigh") {
154889
- bedrockSettings.effort = "max";
155888
+ bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
155889
+ } else if (effort === "max") {
155890
+ bedrockSettings.effort = effort;
154890
155891
  }
154891
155892
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
154892
155893
  bedrockSettings.thinking = {
@@ -157714,8 +158715,14 @@ Usage:
157714
158715
  letta channels route list [--channel <ch>] Show routing table
157715
158716
  letta channels route add [options] Add a route
157716
158717
  letta channels route remove [options] Remove a route
158718
+ letta channels bind [options] Bind a Slack app to an agent
157717
158719
  letta channels pair [options] Approve pairing + bind to agent
157718
158720
 
158721
+ Bind options (Slack only):
158722
+ --channel slack Required
158723
+ --account-id <id> Channel account ID (optional; inferred when only one account exists)
158724
+ --agent <id> Agent ID (defaults to LETTA_AGENT_ID)
158725
+
157719
158726
  Route add options:
157720
158727
  --channel <name> Channel name (e.g. "telegram")
157721
158728
  --account-id <id> Channel account ID (required when multiple accounts exist)
@@ -158009,6 +159016,46 @@ async function handlePair(values) {
158009
159016
  }
158010
159017
  return result.success ? 0 : 1;
158011
159018
  }
159019
+ function handleBind(values) {
159020
+ const channelId = values.channel;
159021
+ const accountId = values["account-id"];
159022
+ const agentId = getAgentId3(values.agent);
159023
+ if (!channelId) {
159024
+ console.error("Error: --channel is required.");
159025
+ return 1;
159026
+ }
159027
+ if (channelId !== "slack") {
159028
+ console.error(`"bind" is only supported for Slack. Telegram binding is route-scoped — use "pair" or "route add" instead.`);
159029
+ return 1;
159030
+ }
159031
+ if (!agentId) {
159032
+ console.error("Error: --agent is required (or set LETTA_AGENT_ID env var).");
159033
+ return 1;
159034
+ }
159035
+ let resolvedAccountId;
159036
+ try {
159037
+ resolvedAccountId = resolveSelectedAccountId(channelId, accountId);
159038
+ } catch (error) {
159039
+ console.error(error instanceof Error ? error.message : String(error));
159040
+ return 1;
159041
+ }
159042
+ const account = getChannelAccount(channelId, resolvedAccountId);
159043
+ if (!account) {
159044
+ console.error(`Account "${resolvedAccountId}" not found for channel "${channelId}".`);
159045
+ return 1;
159046
+ }
159047
+ account.agentId = agentId;
159048
+ account.updatedAt = new Date().toISOString();
159049
+ upsertChannelAccount(channelId, account);
159050
+ console.log(JSON.stringify({
159051
+ success: true,
159052
+ channel: channelId,
159053
+ accountId: resolvedAccountId,
159054
+ agentId
159055
+ }, null, 2));
159056
+ console.warn("Note: If a listener is running, restart it for the binding to take effect.");
159057
+ return 0;
159058
+ }
158012
159059
  async function runChannelsSubcommand(argv) {
158013
159060
  const { values, positionals } = parseChannelsArgs(argv);
158014
159061
  if (values.help) {
@@ -158049,6 +159096,8 @@ async function runChannelsSubcommand(argv) {
158049
159096
  return 1;
158050
159097
  }
158051
159098
  }
159099
+ case "bind":
159100
+ return handleBind(values);
158052
159101
  case "pair":
158053
159102
  return await handlePair(values);
158054
159103
  default:
@@ -158056,7 +159105,7 @@ async function runChannelsSubcommand(argv) {
158056
159105
  printUsage3();
158057
159106
  return 0;
158058
159107
  }
158059
- console.error(`Unknown channels action: "${action}". Use: install, configure, status, route, pair`);
159108
+ console.error(`Unknown channels action: "${action}". Use: install, configure, status, route, bind, pair`);
158060
159109
  return 1;
158061
159110
  }
158062
159111
  }
@@ -163181,4 +164230,4 @@ Error during initialization: ${message}`);
163181
164230
  }
163182
164231
  main();
163183
164232
 
163184
- //# debugId=29DBF7A2819CB6C064756E2164756E21
164233
+ //# debugId=B587994115421B1064756E2164756E21