@letta-ai/letta-code 0.23.5 → 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.5",
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,7 +8391,7 @@ 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
8397
  description: "Anthropic's (legacy) best model (high reasoning)",
@@ -8459,7 +8459,7 @@ var init_models2 = __esm(() => {
8459
8459
  }
8460
8460
  },
8461
8461
  {
8462
- id: "opus-4.7",
8462
+ id: "opus",
8463
8463
  handle: "anthropic/claude-opus-4-7",
8464
8464
  label: "Opus 4.7",
8465
8465
  description: "Anthropic's best model (med reasoning)",
@@ -8487,20 +8487,6 @@ var init_models2 = __esm(() => {
8487
8487
  parallel_tool_calls: true
8488
8488
  }
8489
8489
  },
8490
- {
8491
- id: "opus-4.7-medium",
8492
- handle: "anthropic/claude-opus-4-7",
8493
- label: "Opus 4.7",
8494
- description: "Opus 4.7 (med reasoning)",
8495
- updateArgs: {
8496
- context_window: 200000,
8497
- max_output_tokens: 128000,
8498
- reasoning_effort: "medium",
8499
- enable_reasoner: true,
8500
- max_reasoning_tokens: 12000,
8501
- parallel_tool_calls: true
8502
- }
8503
- },
8504
8490
  {
8505
8491
  id: "opus-4.7-high",
8506
8492
  handle: "anthropic/claude-opus-4-7",
@@ -10261,6 +10247,11 @@ function getModelInfoForLlmConfig(modelHandle, llmConfig) {
10261
10247
  const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
10262
10248
  if (match)
10263
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
+ }
10264
10255
  }
10265
10256
  if (llmConfig?.enable_reasoner === false) {
10266
10257
  const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
@@ -10377,7 +10368,8 @@ var init_model = __esm(() => {
10377
10368
  "low",
10378
10369
  "medium",
10379
10370
  "high",
10380
- "xhigh"
10371
+ "xhigh",
10372
+ "max"
10381
10373
  ];
10382
10374
  RESUME_REFRESH_FIELDS = [
10383
10375
  "max_output_tokens",
@@ -37858,6 +37850,11 @@ function getModelInfoForLlmConfig2(modelHandle, llmConfig) {
37858
37850
  const match = candidates.find((m) => m.updateArgs?.reasoning_effort === effort);
37859
37851
  if (match)
37860
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
+ }
37861
37858
  }
37862
37859
  if (llmConfig?.enable_reasoner === false) {
37863
37860
  const match = candidates.find((m) => m.updateArgs?.enable_reasoner === false);
@@ -37974,7 +37971,8 @@ var init_model2 = __esm(() => {
37974
37971
  "low",
37975
37972
  "medium",
37976
37973
  "high",
37977
- "xhigh"
37974
+ "xhigh",
37975
+ "max"
37978
37976
  ];
37979
37977
  RESUME_REFRESH_FIELDS2 = [
37980
37978
  "max_output_tokens",
@@ -38252,6 +38250,9 @@ __export(exports_modify, {
38252
38250
  updateAgentLLMConfig: () => updateAgentLLMConfig2,
38253
38251
  recompileAgentSystemPrompt: () => recompileAgentSystemPrompt
38254
38252
  });
38253
+ function supportsDistinctAnthropicXHighEffort2(modelHandle) {
38254
+ return modelHandle.includes("claude-opus-4-7");
38255
+ }
38255
38256
  function buildModelSettings2(modelHandle, updateArgs) {
38256
38257
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
38257
38258
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -38285,10 +38286,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
38285
38286
  parallel_tool_calls: true
38286
38287
  };
38287
38288
  const effort = updateArgs?.reasoning_effort;
38289
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
38288
38290
  if (effort === "low" || effort === "medium" || effort === "high") {
38289
38291
  anthropicSettings.effort = effort;
38290
38292
  } else if (effort === "xhigh") {
38291
- anthropicSettings.effort = "max";
38293
+ anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
38294
+ } else if (effort === "max") {
38295
+ anthropicSettings.effort = effort;
38292
38296
  }
38293
38297
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38294
38298
  anthropicSettings.thinking = {
@@ -38341,10 +38345,13 @@ function buildModelSettings2(modelHandle, updateArgs) {
38341
38345
  parallel_tool_calls: true
38342
38346
  };
38343
38347
  const effort = updateArgs?.reasoning_effort;
38348
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort2(modelHandle);
38344
38349
  if (effort === "low" || effort === "medium" || effort === "high") {
38345
38350
  bedrockSettings.effort = effort;
38346
38351
  } else if (effort === "xhigh") {
38347
- bedrockSettings.effort = "max";
38352
+ bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
38353
+ } else if (effort === "max") {
38354
+ bedrockSettings.effort = effort;
38348
38355
  }
38349
38356
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38350
38357
  bedrockSettings.thinking = {
@@ -39802,6 +39809,9 @@ function normalizeLoadedAccount(account) {
39802
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")) {
39803
39810
  next.displayName = undefined;
39804
39811
  }
39812
+ if (next.channel === "slack") {
39813
+ next.defaultPermissionMode = next.defaultPermissionMode ?? "default";
39814
+ }
39805
39815
  return next;
39806
39816
  }
39807
39817
  function makeDefaultLegacyAccount(channelId) {
@@ -39836,6 +39846,7 @@ function makeDefaultLegacyAccount(channelId) {
39836
39846
  dmPolicy: config.dmPolicy,
39837
39847
  allowedUsers: [...config.allowedUsers],
39838
39848
  agentId: null,
39849
+ defaultPermissionMode: "default",
39839
39850
  createdAt: now,
39840
39851
  updatedAt: now
39841
39852
  };
@@ -40656,14 +40667,27 @@ function getPackageManagerExecutable(packageManager) {
40656
40667
  }
40657
40668
  return packageManager;
40658
40669
  }
40670
+ function resolveInstallPlatform() {
40671
+ return platformOverride ?? process.platform;
40672
+ }
40659
40673
  function getInstallArgs(packageManager, installPackages) {
40674
+ const noBinLinks = resolveInstallPlatform() === "win32" && packageManager !== "bun";
40660
40675
  switch (packageManager) {
40661
40676
  case "bun":
40662
40677
  return ["add", "--no-save", ...installPackages];
40663
40678
  case "pnpm":
40664
- return ["add", ...installPackages];
40679
+ return [
40680
+ "add",
40681
+ ...noBinLinks ? ["--no-bin-links"] : [],
40682
+ ...installPackages
40683
+ ];
40665
40684
  case "npm":
40666
- return ["install", "--no-save", ...installPackages];
40685
+ return [
40686
+ "install",
40687
+ "--no-save",
40688
+ ...noBinLinks ? ["--no-bin-links"] : [],
40689
+ ...installPackages
40690
+ ];
40667
40691
  }
40668
40692
  }
40669
40693
  async function installChannelRuntime(channelId) {
@@ -41296,6 +41320,14 @@ var init_plugin = __esm(() => {
41296
41320
  import { randomUUID as randomUUID4 } from "node:crypto";
41297
41321
  import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
41298
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
+ }
41299
41331
  function asRecord(value) {
41300
41332
  return value && typeof value === "object" ? value : null;
41301
41333
  }
@@ -41569,12 +41601,31 @@ async function resolveSlackThreadHistory(params) {
41569
41601
  const nextCursor = response.response_metadata?.next_cursor;
41570
41602
  cursor = typeof nextCursor === "string" && nextCursor.trim().length > 0 ? nextCursor.trim() : undefined;
41571
41603
  } while (cursor);
41572
- return retained.map((message) => ({
41573
- text: resolveSlackThreadMessageText(message),
41574
- userId: isNonEmptyString(message.user) ? message.user : undefined,
41575
- botId: isNonEmptyString(message.bot_id) ? message.bot_id : undefined,
41576
- ts: isNonEmptyString(message.ts) ? message.ts : undefined
41577
- }));
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);
41578
41629
  } catch {
41579
41630
  return [];
41580
41631
  }
@@ -41744,6 +41795,13 @@ function buildSlackThreadLabel(msg, starterText) {
41744
41795
  }
41745
41796
  return roomLabel ? `Slack thread${roomLabel}` : `Slack thread ${msg.chatId}`;
41746
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
+ }
41747
41805
  async function resolveSlackAccountDisplayName(botToken, appToken) {
41748
41806
  const bolt = await loadSlackBoltModule();
41749
41807
  const App2 = resolveSlackAppConstructor(bolt);
@@ -42223,21 +42281,31 @@ function createSlackAdapter(config) {
42223
42281
  rememberMessageThread(response.ts, options?.replyToMessageId ?? response.ts ?? null);
42224
42282
  },
42225
42283
  async prepareInboundMessage(msg, options) {
42226
- if (!options?.isFirstRouteTurn || msg.channel !== "slack" || msg.chatType !== "channel" || !isNonEmptyString2(msg.threadId) || !isNonEmptyString2(msg.messageId) || msg.threadId === msg.messageId) {
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) {
42227
42290
  return msg;
42228
42291
  }
42229
42292
  const slackApp = await ensureApp();
42230
- const starter = await resolveSlackThreadStarter({
42293
+ const starter = shouldHydrateExistingThreadContext ? await resolveSlackThreadStarter({
42231
42294
  channelId: msg.chatId,
42232
42295
  threadTs: msg.threadId,
42233
42296
  client: slackApp.client
42234
- });
42235
- const history = await resolveSlackThreadHistory({
42297
+ }) : null;
42298
+ const history = shouldHydrateExistingThreadContext ? await resolveSlackThreadHistory({
42236
42299
  channelId: msg.chatId,
42237
42300
  threadTs: msg.threadId,
42238
42301
  client: slackApp.client,
42239
42302
  currentMessageTs: msg.messageId,
42240
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
42241
42309
  });
42242
42310
  if (!starter && history.length === 0) {
42243
42311
  return msg;
@@ -42266,7 +42334,7 @@ function createSlackAdapter(config) {
42266
42334
  return {
42267
42335
  ...msg,
42268
42336
  threadContext: {
42269
- label: buildSlackThreadLabel(msg, starter?.text),
42337
+ label: shouldHydrateExistingThreadContext ? buildSlackThreadLabel(msg, starter?.text) : buildSlackChannelContextLabel(msg),
42270
42338
  ...starter ? {
42271
42339
  starter: {
42272
42340
  messageId: starter.ts,
@@ -42436,6 +42504,7 @@ DM Policy — who can message this app directly?
42436
42504
  botToken,
42437
42505
  appToken,
42438
42506
  agentId: null,
42507
+ defaultPermissionMode: "default",
42439
42508
  dmPolicy: policy,
42440
42509
  allowedUsers,
42441
42510
  createdAt: now,
@@ -43558,6 +43627,16 @@ class ChannelRegistry {
43558
43627
  updatedAt: now
43559
43628
  };
43560
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
+ }
43561
43640
  return route;
43562
43641
  }
43563
43642
  async ensureSlackRoute(adapter, msg, config) {
@@ -74741,7 +74820,12 @@ async function executeTool(name, args, options) {
74741
74820
  enhancedArgs = { ...enhancedArgs, signal: options.signal };
74742
74821
  }
74743
74822
  if (options?.onOutput) {
74744
- enhancedArgs = { ...enhancedArgs, onOutput: options.onOutput };
74823
+ enhancedArgs = {
74824
+ ...enhancedArgs,
74825
+ onOutput: (chunk, stream2) => {
74826
+ options.onOutput?.(scrubSecretsFromString(chunk), stream2);
74827
+ }
74828
+ };
74745
74829
  }
74746
74830
  enhancedArgs = substituteSecretsInArgs(enhancedArgs);
74747
74831
  }
@@ -75241,6 +75325,7 @@ function toAccountSnapshot(account) {
75241
75325
  hasBotToken: account.botToken.trim().length > 0,
75242
75326
  hasAppToken: account.appToken.trim().length > 0,
75243
75327
  agentId: account.agentId,
75328
+ defaultPermissionMode: account.defaultPermissionMode ?? "default",
75244
75329
  createdAt: account.createdAt,
75245
75330
  updatedAt: account.updatedAt
75246
75331
  };
@@ -75273,6 +75358,7 @@ function createAccountFromPatch(channelId, accountId, patch) {
75273
75358
  botToken: patch.botToken ?? "",
75274
75359
  appToken: patch.appToken ?? "",
75275
75360
  agentId: patch.agentId ?? null,
75361
+ defaultPermissionMode: patch.defaultPermissionMode ?? "default",
75276
75362
  dmPolicy: patch.dmPolicy ?? "open",
75277
75363
  allowedUsers: patch.allowedUsers ?? [],
75278
75364
  createdAt: now,
@@ -75300,6 +75386,7 @@ function mergeAccountPatch(existing, patch) {
75300
75386
  botToken: patch.botToken ?? existing.botToken,
75301
75387
  appToken: patch.appToken ?? existing.appToken,
75302
75388
  agentId: patch.agentId ?? existing.agentId,
75389
+ defaultPermissionMode: patch.defaultPermissionMode ?? existing.defaultPermissionMode ?? "default",
75303
75390
  dmPolicy: patch.dmPolicy ?? existing.dmPolicy,
75304
75391
  allowedUsers: patch.allowedUsers ?? existing.allowedUsers,
75305
75392
  updatedAt: nextUpdatedAt
@@ -77336,7 +77423,7 @@ function addTask(input) {
77336
77423
  prompt: input.prompt,
77337
77424
  status: "active",
77338
77425
  created_at: now.toISOString(),
77339
- expires_at: input.recurring ? new Date(now.getTime() + DEFAULT_TTL_MS).toISOString() : null,
77426
+ expires_at: null,
77340
77427
  last_fired_at: null,
77341
77428
  fire_count: 0,
77342
77429
  cancel_reason: null,
@@ -77464,10 +77551,9 @@ function getCronFileMtime() {
77464
77551
  return 0;
77465
77552
  }
77466
77553
  }
77467
- 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;
77468
77555
  var init_cronFile = __esm(() => {
77469
77556
  init_parseInterval();
77470
- DEFAULT_TTL_MS = 3 * 24 * 60 * 60 * 1000;
77471
77557
  GC_AGE_MS = 24 * 60 * 60 * 1000;
77472
77558
  });
77473
77559
 
@@ -84514,6 +84600,25 @@ function normalizeInterruptOutputLines(value) {
84514
84600
  const combinedLength = filtered.reduce((sum, entry) => sum + entry.length, 0);
84515
84601
  return combinedLength <= INTERRUPT_TOOL_RETURN_MAX_CHARS ? filtered : undefined;
84516
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
+ }
84517
84622
  function asToolReturnStatus(value) {
84518
84623
  if (value === "success" || value === "error") {
84519
84624
  return value;
@@ -84727,6 +84832,54 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
84727
84832
  });
84728
84833
  }
84729
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
+ }
84730
84883
  function getInterruptApprovalsForEmission(runtime, params) {
84731
84884
  if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
84732
84885
  return params.lastExecutionResults;
@@ -84843,7 +84996,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
84843
84996
  clearRecoveredApprovalState(runtime);
84844
84997
  return true;
84845
84998
  }
84846
- var INTERRUPT_TOOL_RETURN_MAX_CHARS;
84999
+ var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS;
84847
85000
  var init_interrupts = __esm(async () => {
84848
85001
  init_approval_result_normalization();
84849
85002
  init_constants();
@@ -84855,6 +85008,7 @@ var init_interrupts = __esm(async () => {
84855
85008
  init_protocol_outbound()
84856
85009
  ]);
84857
85010
  INTERRUPT_TOOL_RETURN_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
85011
+ STREAMING_TOOL_OUTPUT_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
84858
85012
  });
84859
85013
 
84860
85014
  // src/websocket/listener/permissionMode.ts
@@ -87695,6 +87849,11 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
87695
87849
  agentId: recovered.agentId,
87696
87850
  conversationId: recovered.conversationId
87697
87851
  });
87852
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
87853
+ runId: runtime.activeRunId ?? undefined,
87854
+ agentId: recovered.agentId,
87855
+ conversationId: recovered.conversationId
87856
+ });
87698
87857
  const recoveryAbortController = new AbortController;
87699
87858
  runtime.activeAbortController = recoveryAbortController;
87700
87859
  const preparedToolContext = await prepareToolExecutionContextForScope({
@@ -87709,6 +87868,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
87709
87868
  try {
87710
87869
  const approvalResults = await executeApprovalBatch(decisions, undefined, {
87711
87870
  abortSignal: recoveryAbortController.signal,
87871
+ onStreamingOutput: emitToolExecutionOutput,
87712
87872
  toolContextId: preparedToolContext.preparedToolContext.contextId,
87713
87873
  workingDirectory,
87714
87874
  parentScope: recovered.agentId && recovered.conversationId ? {
@@ -87979,9 +88139,15 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
87979
88139
  agentId: runtime.agentId ?? undefined,
87980
88140
  conversationId: recoveryConversationId
87981
88141
  });
88142
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
88143
+ runId: runtime.activeRunId ?? undefined,
88144
+ agentId: runtime.agentId ?? undefined,
88145
+ conversationId: recoveryConversationId
88146
+ });
87982
88147
  try {
87983
88148
  const approvalResults = await executeApprovalBatch(decisions, undefined, {
87984
88149
  abortSignal,
88150
+ onStreamingOutput: emitToolExecutionOutput,
87985
88151
  toolContextId: preparedToolContext.preparedToolContext.contextId,
87986
88152
  workingDirectory: recoveryWorkingDirectory,
87987
88153
  parentScope: runtime.agentId && runtime.conversationId ? {
@@ -88550,6 +88716,11 @@ async function handleApprovalStop(params) {
88550
88716
  agentId,
88551
88717
  conversationId
88552
88718
  });
88719
+ const emitToolExecutionOutput = createToolExecutionOutputEmitter(socket, runtime, {
88720
+ runId: executionRunId,
88721
+ agentId,
88722
+ conversationId
88723
+ });
88553
88724
  if (shouldInterrupt()) {
88554
88725
  return interruptTermination();
88555
88726
  }
@@ -88568,6 +88739,7 @@ async function handleApprovalStop(params) {
88568
88739
  const executionResults = await executeApprovalBatch(decisions, undefined, {
88569
88740
  toolContextId: turnToolContextId ?? undefined,
88570
88741
  abortSignal: abortController.signal,
88742
+ onStreamingOutput: emitToolExecutionOutput,
88571
88743
  workingDirectory: turnWorkingDirectory,
88572
88744
  parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
88573
88745
  onFileWrite
@@ -90596,11 +90768,6 @@ function refreshTaskCache(state) {
90596
90768
  }
90597
90769
  }
90598
90770
  function shouldFireTask(task2, now) {
90599
- if (task2.recurring && task2.expires_at) {
90600
- if (new Date(task2.expires_at).getTime() <= now.getTime()) {
90601
- return false;
90602
- }
90603
- }
90604
90771
  if (!task2.recurring && task2.scheduled_for) {
90605
90772
  const scheduledMs = new Date(task2.scheduled_for).getTime() + task2.jitter_offset_ms;
90606
90773
  return scheduledMs <= now.getTime();
@@ -90640,16 +90807,6 @@ function fireCronTask(task2, now, socket, opts, processQueuedTurn) {
90640
90807
  });
90641
90808
  }
90642
90809
  }
90643
- function handleExpiredRecurring(task2, now) {
90644
- if (!task2.recurring || !task2.expires_at)
90645
- return;
90646
- if (new Date(task2.expires_at).getTime() <= now.getTime()) {
90647
- updateTask(task2.id, (t) => {
90648
- t.status = "cancelled";
90649
- t.cancel_reason = "expired";
90650
- });
90651
- }
90652
- }
90653
90810
  function handleMissedOneShot(task2, now) {
90654
90811
  if (task2.recurring || !task2.scheduled_for)
90655
90812
  return false;
@@ -90678,9 +90835,6 @@ function tick2(state, socket, opts, processQueuedTurn) {
90678
90835
  }
90679
90836
  refreshTaskCache(state);
90680
90837
  for (const task2 of state.cachedTasks) {
90681
- if (task2.status !== "active")
90682
- continue;
90683
- handleExpiredRecurring(task2, now);
90684
90838
  if (task2.status !== "active")
90685
90839
  continue;
90686
90840
  if (handleMissedOneShot(task2, now))
@@ -91144,6 +91298,12 @@ function isListInDirectoryCommand(value) {
91144
91298
  const c = value;
91145
91299
  return c.type === "list_in_directory" && typeof c.path === "string";
91146
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
+ }
91147
91307
  function isReadFileCommand(value) {
91148
91308
  if (!value || typeof value !== "object")
91149
91309
  return false;
@@ -91470,7 +91630,7 @@ function parseServerMessage(data) {
91470
91630
  try {
91471
91631
  const raw = typeof data === "string" ? data : data.toString();
91472
91632
  const parsed = JSON.parse(raw);
91473
- 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)) {
91474
91634
  return parsed;
91475
91635
  }
91476
91636
  const invalidInput = getInvalidInputReason(parsed);
@@ -91843,18 +92003,20 @@ function formatToolsetStatusMessageForModelUpdate(params) {
91843
92003
  }
91844
92004
  return "Manual toolset override remains active: " + formatToolsetName(toolsetPreference) + ".";
91845
92005
  }
91846
- function formatEffortSuffix(updateArgs) {
92006
+ function formatEffortSuffix(modelLabel, updateArgs) {
91847
92007
  if (!updateArgs)
91848
92008
  return "";
91849
92009
  const effort = updateArgs.reasoning_effort;
91850
92010
  if (typeof effort !== "string" || effort.length === 0)
91851
92011
  return "";
92012
+ const xhighLabel = modelLabel.includes("Opus 4.7") ? "Extra-High" : "Max";
91852
92013
  const labels = {
91853
92014
  none: "No Reasoning",
91854
92015
  low: "Low",
91855
92016
  medium: "Medium",
91856
92017
  high: "High",
91857
- xhigh: "Max"
92018
+ xhigh: xhighLabel,
92019
+ max: "Max"
91858
92020
  };
91859
92021
  return ` (${labels[effort] ?? effort})`;
91860
92022
  }
@@ -91867,7 +92029,7 @@ function buildModelUpdateStatusMessage(params) {
91867
92029
  toolsetPreference,
91868
92030
  updateArgs
91869
92031
  } = params;
91870
- let message = `Model updated to ${modelLabel}${formatEffortSuffix(updateArgs)}.`;
92032
+ let message = `Model updated to ${modelLabel}${formatEffortSuffix(modelLabel, updateArgs)}.`;
91871
92033
  if (toolsetError) {
91872
92034
  message += ` Warning: toolset switch failed (${toolsetError}).`;
91873
92035
  return { message, level: "warning" };
@@ -92432,6 +92594,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
92432
92594
  has_bot_token: snapshot.hasBotToken,
92433
92595
  has_app_token: snapshot.hasAppToken,
92434
92596
  agent_id: snapshot.agentId,
92597
+ default_permission_mode: snapshot.defaultPermissionMode,
92435
92598
  created_at: snapshot.createdAt,
92436
92599
  updated_at: snapshot.updatedAt
92437
92600
  };
@@ -92525,6 +92688,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
92525
92688
  appToken: "app_token" in parsed.account ? parsed.account.app_token : undefined,
92526
92689
  mode: "mode" in parsed.account ? parsed.account.mode : undefined,
92527
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,
92528
92692
  dmPolicy: parsed.account.dm_policy,
92529
92693
  allowedUsers: parsed.account.allowed_users
92530
92694
  }, {
@@ -92565,6 +92729,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
92565
92729
  appToken: "app_token" in parsed.patch ? parsed.patch.app_token : undefined,
92566
92730
  mode: "mode" in parsed.patch ? parsed.patch.mode : undefined,
92567
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,
92568
92733
  dmPolicy: parsed.patch.dm_policy,
92569
92734
  allowedUsers: parsed.patch.allowed_users
92570
92735
  });
@@ -93307,16 +93472,27 @@ function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
93307
93472
  scheduleQueuePump(conversationRuntime, socket, opts, processQueuedTurn);
93308
93473
  });
93309
93474
  registry.setEventHandler((event) => {
93310
- if (event.type === "pairings_updated") {
93311
- emitChannelPairingsUpdated(socket, event.channelId);
93312
- emitChannelsUpdated(socket, event.channelId);
93313
- return;
93314
- }
93315
- emitChannelTargetsUpdated(socket, event.channelId);
93316
- emitChannelsUpdated(socket, event.channelId);
93475
+ handleChannelRegistryEvent(event, socket, listener);
93317
93476
  });
93318
93477
  registry.setReady();
93319
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
+ }
93320
93496
  function stampInboundUserMessageOtids(incoming) {
93321
93497
  let didChange = false;
93322
93498
  const messages = incoming.messages.map((payload) => {
@@ -93725,6 +93901,51 @@ async function startListenerClient(opts) {
93725
93901
  telemetry.init();
93726
93902
  await connectWithRetry(runtime, opts);
93727
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
+ }
93728
93949
  async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now()) {
93729
93950
  if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
93730
93951
  return;
@@ -94045,33 +94266,21 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
94045
94266
  console.log(`[Listen] Received list_in_directory command: path=${parsed.path}`);
94046
94267
  runDetachedListenerTask("list_in_directory", async () => {
94047
94268
  try {
94048
- const { readdir: readdir7 } = await import("node:fs/promises");
94269
+ let indexRoot3;
94270
+ try {
94271
+ await ensureFileIndex2();
94272
+ indexRoot3 = getIndexRoot();
94273
+ } catch {}
94049
94274
  console.log(`[Listen] Reading directory: ${parsed.path}`);
94050
- const entries = await readdir7(parsed.path, { withFileTypes: true });
94051
- console.log(`[Listen] Directory read success, ${entries.length} entries`);
94052
- const IGNORED_NAMES = new Set([
94053
- ".DS_Store",
94054
- ".git",
94055
- ".gitignore",
94056
- "Thumbs.db"
94057
- ]);
94058
- const sortedEntries = entries.filter((e) => !IGNORED_NAMES.has(e.name)).sort((a, b) => a.name.localeCompare(b.name));
94059
- const allFolders = [];
94060
- const allFiles = [];
94061
- for (const e of sortedEntries) {
94062
- if (e.isDirectory()) {
94063
- allFolders.push(e.name);
94064
- } else if (parsed.include_files) {
94065
- allFiles.push(e.name);
94066
- }
94067
- }
94275
+ const { folders: allFolders, files: allFiles } = await listDirectoryHybrid(parsed.path, indexRoot3, !!parsed.include_files);
94068
94276
  const total = allFolders.length + allFiles.length;
94069
94277
  const offset = parsed.offset ?? 0;
94070
94278
  const limit2 = parsed.limit ?? total;
94071
94279
  const combined = [...allFolders, ...allFiles];
94072
94280
  const page = combined.slice(offset, offset + limit2);
94073
- const folders = page.filter((name) => allFolders.includes(name));
94074
- 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));
94075
94284
  const response = {
94076
94285
  type: "list_in_directory_response",
94077
94286
  path: parsed.path,
@@ -94102,6 +94311,71 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
94102
94311
  });
94103
94312
  return;
94104
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
+ }
94105
94379
  if (isReadFileCommand(parsed)) {
94106
94380
  console.log(`[Listen] Received read_file command: path=${parsed.path}, request_id=${parsed.request_id}`);
94107
94381
  runDetachedListenerTask("read_file", async () => {
@@ -94162,6 +94436,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
94162
94436
  }
94163
94437
  }
94164
94438
  console.log(`[Listen] write_file success: ${parsed.path} (${parsed.content.length} bytes)`);
94439
+ refreshFileIndex();
94165
94440
  safeSocketSend(socket, {
94166
94441
  type: "write_file_response",
94167
94442
  request_id: parsed.request_id,
@@ -94258,6 +94533,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
94258
94533
  expected_replacements: parsed.expected_replacements
94259
94534
  });
94260
94535
  console.log(`[Listen] edit_file success: ${result.replacements} replacement(s) at line ${result.startLine}`);
94536
+ if (result.replacements > 0) {
94537
+ refreshFileIndex();
94538
+ }
94261
94539
  if (result.replacements > 0) {
94262
94540
  try {
94263
94541
  const contentAfter = await readFile11(parsed.file_path, "utf-8");
@@ -94892,7 +95170,7 @@ function createLegacyTestRuntime() {
94892
95170
  }
94893
95171
  return bridge;
94894
95172
  }
94895
- var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, __listenClientTestUtils;
95173
+ var channelsServiceLoaderOverride = null, WIKI_LINK_REGEX, DIR_LISTING_IGNORED_NAMES, __listenClientTestUtils;
94896
95174
  var init_client4 = __esm(async () => {
94897
95175
  init_available_models();
94898
95176
  init_client2();
@@ -94939,6 +95217,7 @@ var init_client4 = __esm(async () => {
94939
95217
  init_protocol_outbound()
94940
95218
  ]);
94941
95219
  WIKI_LINK_REGEX = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g;
95220
+ DIR_LISTING_IGNORED_NAMES = new Set([".DS_Store", ".git", "Thumbs.db"]);
94942
95221
  __listenClientTestUtils = {
94943
95222
  setChannelsServiceLoaderForTests: (loader) => {
94944
95223
  channelsServiceLoaderOverride = loader;
@@ -94994,6 +95273,7 @@ var init_client4 = __esm(async () => {
94994
95273
  handleListMemoryCommand,
94995
95274
  isDetachedChannelsCommand,
94996
95275
  handleChannelsProtocolCommand,
95276
+ handleChannelRegistryEvent,
94997
95277
  handleSkillCommand,
94998
95278
  handleCreateAgentCommand,
94999
95279
  handleReflectionSettingsCommand,
@@ -100147,10 +100427,10 @@ var init_headless = __esm(async () => {
100147
100427
  init_toolset()
100148
100428
  ]);
100149
100429
  PROVIDER_FALLBACK_MAP = {
100150
- opus: "bedrock-opus-4.6",
100151
100430
  "opus-4.6-no-reasoning": "bedrock-opus-4.6",
100152
100431
  "opus-4.6-low": "bedrock-opus-4.6",
100153
100432
  "opus-4.6-medium": "bedrock-opus-4.6",
100433
+ "opus-4.6-high": "bedrock-opus-4.6",
100154
100434
  "opus-4.6-xhigh": "bedrock-opus-4.6",
100155
100435
  sonnet: "bedrock-sonnet-4.6",
100156
100436
  "sonnet-1m": "bedrock-sonnet-4.6",
@@ -127008,6 +127288,8 @@ function formatReasoningLabel(effort) {
127008
127288
  if (effort === "none")
127009
127289
  return null;
127010
127290
  if (effort === "xhigh")
127291
+ return "xhigh";
127292
+ if (effort === "max")
127011
127293
  return "max";
127012
127294
  if (effort === "minimal")
127013
127295
  return "minimal";
@@ -128046,6 +128328,8 @@ function getReasoningEffortTag(effort) {
128046
128328
  if (effort === "none")
128047
128329
  return null;
128048
128330
  if (effort === "xhigh")
128331
+ return "xhigh";
128332
+ if (effort === "max")
128049
128333
  return "max";
128050
128334
  if (effort === "minimal")
128051
128335
  return "minimal";
@@ -135673,10 +135957,12 @@ var init_MessageSearch = __esm(async () => {
135673
135957
  });
135674
135958
 
135675
135959
  // src/cli/components/ModelReasoningSelector.tsx
135676
- function formatEffortLabel(effort) {
135960
+ function formatEffortLabel(effort, hasDistinctMaxTier) {
135677
135961
  if (effort === "none")
135678
135962
  return "Off";
135679
135963
  if (effort === "xhigh")
135964
+ return hasDistinctMaxTier ? "Extra-High" : "Max";
135965
+ if (effort === "max")
135680
135966
  return "Max";
135681
135967
  if (effort === "minimal")
135682
135968
  return "Minimal";
@@ -135703,6 +135989,7 @@ function ModelReasoningSelector({
135703
135989
  }, [options, initialModelId]);
135704
135990
  const selectedOption = options[selectedIndex] ?? options[0];
135705
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]);
135706
135993
  const totalBars = Math.max(effortOptions.length, 1);
135707
135994
  const selectedBars = import_react82.useMemo(() => {
135708
135995
  if (!selectedOption)
@@ -135741,7 +136028,7 @@ function ModelReasoningSelector({
135741
136028
  setSelectedIndex((prev) => (prev + 1) % options.length);
135742
136029
  }
135743
136030
  });
135744
- const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort) : "Medium";
136031
+ const effortLabel = selectedOption ? formatEffortLabel(selectedOption.effort, hasDistinctMaxTier) : "Medium";
135745
136032
  const selectedText = selectedBars > 0 ? EFFORT_BLOCK.repeat(selectedBars) : "";
135746
136033
  const remainingBars = totalBars > selectedBars ? EFFORT_BLOCK.repeat(totalBars - selectedBars) : "";
135747
136034
  return /* @__PURE__ */ jsx_dev_runtime59.jsxDEV(Box_default, {
@@ -143828,12 +144115,12 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
143828
144115
  const effort = modelSettings.effort;
143829
144116
  if (effort === "low" || effort === "medium" || effort === "high")
143830
144117
  return effort;
143831
- if (effort === "max")
143832
- return "xhigh";
144118
+ if (effort === "xhigh" || effort === "max")
144119
+ return effort;
143833
144120
  }
143834
144121
  }
143835
144122
  const re = llmConfig?.reasoning_effort;
143836
- 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")
143837
144124
  return re;
143838
144125
  if (llmConfig?.enable_reasoner === false)
143839
144126
  return "none";
@@ -143842,7 +144129,7 @@ function deriveReasoningEffort(modelSettings, llmConfig) {
143842
144129
  function inferReasoningEffortFromModelPreset(modelId, modelHandle) {
143843
144130
  const modelInfo = (modelId ? getModelInfo2(modelId) : null) ?? (modelHandle ? getModelInfo2(modelHandle) : null);
143844
144131
  const presetEffort = modelInfo?.updateArgs?.reasoning_effort;
143845
- 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") {
143846
144133
  return presetEffort;
143847
144134
  }
143848
144135
  return null;
@@ -143883,7 +144170,8 @@ function getErrorHintForStopReason(stopReason, currentModelId, modelEndpointType
143883
144170
  }
143884
144171
  const isAutoModel = currentModelId?.startsWith("auto") ?? false;
143885
144172
  const statusInfo = modelEndpointType && !isAutoModel ? PROVIDER_STATUS_PAGES[modelEndpointType] : undefined;
143886
- 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");
143887
144175
  const modelSwapSuffix = hasBedrockOpus ? " (e.g. Opus 4.6 via Amazon Bedrock)" : "";
143888
144176
  if (statusInfo) {
143889
144177
  return [
@@ -151231,7 +151519,7 @@ ${SYSTEM_REMINDER_CLOSE}
151231
151519
  const modelHandle = model.handle ?? model.id;
151232
151520
  const modelUpdateArgs = model.updateArgs;
151233
151521
  const rawReasoningEffort = modelUpdateArgs?.reasoning_effort;
151234
- 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;
151235
151523
  const selectedContextWindow = model.updateArgs?.context_window;
151236
151524
  const reasoningTierOptions = getReasoningTierOptionsForHandle3(modelHandle, selectedContextWindow);
151237
151525
  if (!opts?.skipReasoningPrompt && activeOverlay === "model" && reasoningTierOptions.length > 1) {
@@ -152007,7 +152295,16 @@ ${guidance}`);
152007
152295
  }).filter((m) => Boolean(m.effort));
152008
152296
  if (tiers.length < 2)
152009
152297
  return;
152010
- 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
+ ];
152011
152308
  const rank = (effort) => {
152012
152309
  const idx = order.indexOf(effort);
152013
152310
  return idx >= 0 ? idx : 999;
@@ -152044,12 +152341,11 @@ ${guidance}`);
152044
152341
  };
152045
152342
  }
152046
152343
  if (ms.provider_type === "anthropic" || ms.provider_type === "bedrock") {
152047
- const anthropicEffort = next.effort === "xhigh" ? "max" : next.effort;
152048
152344
  return {
152049
152345
  ...prev,
152050
152346
  model_settings: {
152051
152347
  ...ms,
152052
- effort: anthropicEffort
152348
+ effort: next.effort === "xhigh" ? anthropicXHighEffort : next.effort
152053
152349
  }
152054
152350
  };
152055
152351
  }
@@ -155490,6 +155786,9 @@ init_openai_codex_provider();
155490
155786
  init_debug();
155491
155787
  init_available_models();
155492
155788
  init_client2();
155789
+ function supportsDistinctAnthropicXHighEffort(modelHandle) {
155790
+ return modelHandle.includes("claude-opus-4-7");
155791
+ }
155493
155792
  function buildModelSettings(modelHandle, updateArgs) {
155494
155793
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
155495
155794
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -155523,10 +155822,13 @@ function buildModelSettings(modelHandle, updateArgs) {
155523
155822
  parallel_tool_calls: true
155524
155823
  };
155525
155824
  const effort = updateArgs?.reasoning_effort;
155825
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
155526
155826
  if (effort === "low" || effort === "medium" || effort === "high") {
155527
155827
  anthropicSettings.effort = effort;
155528
155828
  } else if (effort === "xhigh") {
155529
- anthropicSettings.effort = "max";
155829
+ anthropicSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
155830
+ } else if (effort === "max") {
155831
+ anthropicSettings.effort = effort;
155530
155832
  }
155531
155833
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
155532
155834
  anthropicSettings.thinking = {
@@ -155579,10 +155881,13 @@ function buildModelSettings(modelHandle, updateArgs) {
155579
155881
  parallel_tool_calls: true
155580
155882
  };
155581
155883
  const effort = updateArgs?.reasoning_effort;
155884
+ const hasDistinctXHigh = supportsDistinctAnthropicXHighEffort(modelHandle);
155582
155885
  if (effort === "low" || effort === "medium" || effort === "high") {
155583
155886
  bedrockSettings.effort = effort;
155584
155887
  } else if (effort === "xhigh") {
155585
- bedrockSettings.effort = "max";
155888
+ bedrockSettings.effort = hasDistinctXHigh ? "xhigh" : "max";
155889
+ } else if (effort === "max") {
155890
+ bedrockSettings.effort = effort;
155586
155891
  }
155587
155892
  if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
155588
155893
  bedrockSettings.thinking = {
@@ -158410,8 +158715,14 @@ Usage:
158410
158715
  letta channels route list [--channel <ch>] Show routing table
158411
158716
  letta channels route add [options] Add a route
158412
158717
  letta channels route remove [options] Remove a route
158718
+ letta channels bind [options] Bind a Slack app to an agent
158413
158719
  letta channels pair [options] Approve pairing + bind to agent
158414
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
+
158415
158726
  Route add options:
158416
158727
  --channel <name> Channel name (e.g. "telegram")
158417
158728
  --account-id <id> Channel account ID (required when multiple accounts exist)
@@ -158705,6 +159016,46 @@ async function handlePair(values) {
158705
159016
  }
158706
159017
  return result.success ? 0 : 1;
158707
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
+ }
158708
159059
  async function runChannelsSubcommand(argv) {
158709
159060
  const { values, positionals } = parseChannelsArgs(argv);
158710
159061
  if (values.help) {
@@ -158745,6 +159096,8 @@ async function runChannelsSubcommand(argv) {
158745
159096
  return 1;
158746
159097
  }
158747
159098
  }
159099
+ case "bind":
159100
+ return handleBind(values);
158748
159101
  case "pair":
158749
159102
  return await handlePair(values);
158750
159103
  default:
@@ -158752,7 +159105,7 @@ async function runChannelsSubcommand(argv) {
158752
159105
  printUsage3();
158753
159106
  return 0;
158754
159107
  }
158755
- 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`);
158756
159109
  return 1;
158757
159110
  }
158758
159111
  }
@@ -163877,4 +164230,4 @@ Error during initialization: ${message}`);
163877
164230
  }
163878
164231
  main();
163879
164232
 
163880
- //# debugId=E826850D234138B664756E2164756E21
164233
+ //# debugId=B587994115421B1064756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.23.5",
3
+ "version": "0.23.6",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -141,7 +141,7 @@ Include context about what the user originally asked for, so you can give a help
141
141
  ## Important Notes
142
142
 
143
143
  - **Minimum granularity**: 1 minute. Intervals under 60 seconds are rounded up.
144
- - **Recurring TTL**: Recurring tasks auto-expire after 3 days. The user would need to re-create them or you can note this limitation.
144
+ - **Recurring tasks**: No longer auto-expire. They remain active until explicitly cancelled.
145
145
  - **One-shot cleanup**: One-shot tasks are garbage-collected 24 hours after firing.
146
146
  - **Timezone**: Tasks use the user's local timezone by default.
147
147
  - **Scheduler requirement**: Tasks only fire while a Letta session is running (a WS listener must be active). If no session is running, tasks will be marked as missed.