@letta-ai/letta-code 0.30.9 → 0.30.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/channels-public.js +411 -2
  2. package/dist/channels-public.js.map +6 -3
  3. package/dist/channels-slack.js +209 -11
  4. package/dist/channels-slack.js.map +6 -5
  5. package/dist/gateway-core.js +110 -13
  6. package/dist/gateway-core.js.map +6 -5
  7. package/dist/mcp-client.js +2 -2
  8. package/dist/mcp-client.js.map +1 -1
  9. package/dist/schedules.js +4 -2
  10. package/dist/schedules.js.map +3 -3
  11. package/dist/types/agent/client-skills.d.ts +2 -0
  12. package/dist/types/agent/client-skills.d.ts.map +1 -1
  13. package/dist/types/agent/message.d.ts.map +1 -1
  14. package/dist/types/backend/local/local-store.d.ts.map +1 -1
  15. package/dist/types/channels/command-surface.d.ts +54 -0
  16. package/dist/types/channels/command-surface.d.ts.map +1 -0
  17. package/dist/types/channels/gateway-core.d.ts +2 -1
  18. package/dist/types/channels/gateway-core.d.ts.map +1 -1
  19. package/dist/types/channels/message-channel-executor.d.ts +2 -0
  20. package/dist/types/channels/message-channel-executor.d.ts.map +1 -1
  21. package/dist/types/channels/message-channel-idempotency.d.ts +13 -0
  22. package/dist/types/channels/message-channel-idempotency.d.ts.map +1 -0
  23. package/dist/types/channels-public.d.ts +4 -0
  24. package/dist/types/channels-public.d.ts.map +1 -1
  25. package/dist/types/channels-slack.d.ts +1 -0
  26. package/dist/types/channels-slack.d.ts.map +1 -1
  27. package/dist/types/cron/scheduled-task-prompt.d.ts +7 -0
  28. package/dist/types/cron/scheduled-task-prompt.d.ts.map +1 -1
  29. package/dist/types/schedules.d.ts +1 -1
  30. package/dist/types/schedules.d.ts.map +1 -1
  31. package/dist/types/tools/manager.d.ts.map +1 -1
  32. package/dist/types/tools/toolset.d.ts +1 -0
  33. package/dist/types/tools/toolset.d.ts.map +1 -1
  34. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  35. package/dist/types/websocket/listener/types.d.ts +3 -1
  36. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  37. package/letta.js +413 -236
  38. package/package.json +1 -1
  39. package/scripts/source-file-size-baseline.json +2 -2
@@ -1,3 +1,207 @@
1
+ // src/channels/command-surface.ts
2
+ var DEFAULT_CHANNEL_DISPLAY_NAMES = {
3
+ custom: "Custom",
4
+ discord: "Discord",
5
+ signal: "Signal",
6
+ slack: "Slack",
7
+ telegram: "Telegram",
8
+ whatsapp: "WhatsApp"
9
+ };
10
+ function defaultChannelDisplayName(channelId) {
11
+ return DEFAULT_CHANNEL_DISPLAY_NAMES[channelId] ?? channelId;
12
+ }
13
+ var CHANNEL_SLASH_COMMANDS = [
14
+ {
15
+ name: "help",
16
+ kind: "direct",
17
+ summary: "Show channel usage guidance."
18
+ },
19
+ {
20
+ name: "status",
21
+ kind: "direct",
22
+ summary: "Show this chat's channel connection status."
23
+ },
24
+ {
25
+ name: "whoami",
26
+ kind: "direct",
27
+ summary: "Show your access tier and runnable commands here."
28
+ },
29
+ {
30
+ name: "pause",
31
+ kind: "direct",
32
+ summary: "Pause agent routing for this chat."
33
+ },
34
+ {
35
+ name: "resume",
36
+ kind: "direct",
37
+ summary: "Resume agent routing for this chat."
38
+ },
39
+ {
40
+ name: "cancel",
41
+ kind: "agent-scoped",
42
+ summary: "Cancel the in-progress agent turn for this chat."
43
+ },
44
+ {
45
+ name: "chat",
46
+ kind: "direct",
47
+ summary: "Show the Letta web chat link for this channel route."
48
+ },
49
+ {
50
+ name: "feedback",
51
+ kind: "direct",
52
+ summary: "Send feedback about Letta Code from this routed chat."
53
+ },
54
+ {
55
+ name: "model",
56
+ kind: "agent-scoped",
57
+ summary: "Show, list, or switch the model for this chat's routed conversation."
58
+ },
59
+ {
60
+ name: "reflection",
61
+ aliases: ["reflect"],
62
+ kind: "agent-scoped",
63
+ summary: "Start a memory reflection pass for this conversation."
64
+ },
65
+ {
66
+ name: "reload",
67
+ kind: "agent-scoped",
68
+ summary: "Reload settings, local mods, and agent secrets."
69
+ }
70
+ ];
71
+ var SLACK_MENTION_COMMAND_NAMES = [
72
+ "help",
73
+ "detach",
74
+ "model",
75
+ "new",
76
+ "reload"
77
+ ];
78
+ function listChannelSlashCommands() {
79
+ return CHANNEL_SLASH_COMMANDS.map((definition) => ({
80
+ ...definition,
81
+ aliases: definition.aliases ? [...definition.aliases] : undefined
82
+ }));
83
+ }
84
+ function parseSingleLineChannelCommand(text, prefix) {
85
+ const trimmed = text.trim();
86
+ const escapedPrefix = prefix === "/" ? "\\/" : "!";
87
+ const match = trimmed.match(new RegExp(`^${escapedPrefix}([A-Za-z][\\w-]*)(?:@[A-Za-z0-9_]+)?(?:[^\\S\\r\\n]+(.*))?$`));
88
+ if (!match) {
89
+ return null;
90
+ }
91
+ const [, name, args] = match;
92
+ if (!name) {
93
+ return null;
94
+ }
95
+ return {
96
+ name: name.toLowerCase(),
97
+ args: args?.trim() ?? "",
98
+ raw: trimmed
99
+ };
100
+ }
101
+ function parseAnySingleLineChannelCommand(text) {
102
+ return parseSingleLineChannelCommand(text, "/") ?? parseSingleLineChannelCommand(text, "!");
103
+ }
104
+ function parseChannelCommand(text, prefix) {
105
+ const lines = text.trim().split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
106
+ const [firstLine, ...remainingLines] = lines;
107
+ if (!firstLine) {
108
+ return null;
109
+ }
110
+ const firstCommand = parseSingleLineChannelCommand(firstLine, prefix);
111
+ if (!firstCommand) {
112
+ return null;
113
+ }
114
+ const laterCommand = remainingLines.find((line) => Boolean(parseAnySingleLineChannelCommand(line)));
115
+ if (laterCommand) {
116
+ return firstCommand;
117
+ }
118
+ const continuationArgs = remainingLines.join(`
119
+ `).trim();
120
+ if (!continuationArgs) {
121
+ return firstCommand;
122
+ }
123
+ return {
124
+ ...firstCommand,
125
+ args: [firstCommand.args, continuationArgs].filter((part) => part.length > 0).join(`
126
+ `)
127
+ };
128
+ }
129
+ function parseChannelSlashCommand(text) {
130
+ return parseChannelCommand(text, "/");
131
+ }
132
+ function parseChannelBangCommand(text) {
133
+ return parseChannelCommand(text, "!");
134
+ }
135
+ function supportedCommandsText(prefix = "/") {
136
+ return listChannelSlashCommands().map((definition) => `${prefix}${definition.name}`).join(", ");
137
+ }
138
+ var SLACK_MENTION_SLASH_COMMAND_EXAMPLES = [
139
+ "@agent /help",
140
+ "@agent /status",
141
+ "@agent /whoami",
142
+ "@agent /model",
143
+ "@agent /model list",
144
+ "@agent /model <handle-or-id>",
145
+ "@agent /cancel",
146
+ "@agent /chat",
147
+ "@agent /feedback <message>",
148
+ "@agent /reflection",
149
+ "@agent /detach",
150
+ "@agent /new",
151
+ "@agent /reload"
152
+ ];
153
+ function supportedSlackMentionSlashCommandsText() {
154
+ return SLACK_MENTION_SLASH_COMMAND_EXAMPLES.join(", ");
155
+ }
156
+ function supportedBangCommandsText() {
157
+ return SLACK_MENTION_COMMAND_NAMES.map((name) => `!${name}`).join(", ");
158
+ }
159
+ function buildChannelHelpMessage(channelId, resolveDisplayName = defaultChannelDisplayName) {
160
+ const displayName = resolveDisplayName(channelId);
161
+ if (channelId === "slack") {
162
+ return [
163
+ `${displayName} is connected to Letta Code.`,
164
+ "Talk by mentioning the app in a channel thread. Once a thread is routed, normal replies continue the same agent conversation until detached.",
165
+ "Control commands start immediately after the mention:",
166
+ "@agent /model - show this thread's current model",
167
+ "@agent /model list - show available models",
168
+ "@agent /model <handle-or-id> - switch this thread's model",
169
+ "@agent /status - show route and listener status",
170
+ "@agent /cancel - cancel the current turn",
171
+ "@agent /chat - show the web chat link",
172
+ "@agent /feedback <message> - send feedback to the Letta team from this routed thread",
173
+ "@agent /reflection - start a memory reflection pass",
174
+ "@agent /detach - stop replying in this thread until mentioned again",
175
+ "@agent /new - start a fresh conversation for this thread",
176
+ "@agent /reload - reload settings, local mods, and agent secrets",
177
+ `Legacy bang aliases still work after a mention: ${supportedBangCommandsText()}.`,
178
+ "If this chat is not connected yet, send a normal message and follow the pairing instructions."
179
+ ].join(`
180
+ `);
181
+ }
182
+ return [
183
+ `${displayName} is connected to Letta Code.`,
184
+ "Send a normal message here and the connected agent will reply in this chat.",
185
+ `Supported slash commands here: ${supportedCommandsText()}.`,
186
+ "If this chat is not connected yet, send any non-command message and follow the pairing instructions."
187
+ ].join(`
188
+
189
+ `);
190
+ }
191
+ function buildUnsupportedChannelCommandMessage(channelId, command, resolveDisplayName = defaultChannelDisplayName) {
192
+ const displayName = resolveDisplayName(channelId);
193
+ const isBang = command.raw.startsWith("!");
194
+ const commandKind = isBang ? "bang" : "slash";
195
+ const supportedCommands = isBang ? supportedBangCommandsText() : channelId === "slack" ? supportedSlackMentionSlashCommandsText() : supportedCommandsText();
196
+ const supportedLabel = channelId === "slack" && !isBang ? "Slack mention commands" : `${commandKind} commands`;
197
+ return [
198
+ `${displayName} received ${command.raw}, but that ${commandKind} command is not supported in channels yet.`,
199
+ `Supported ${supportedLabel}: ${supportedCommands}.`,
200
+ `Send normal messages without a leading ${isBang ? "bang" : "slash"} command to talk to the connected agent.`
201
+ ].join(`
202
+
203
+ `);
204
+ }
1
205
  // src/channels/core-stream.ts
2
206
  var LETTA_STREAM_NO_ASSISTANT_MESSAGE_ERROR = "No assistant message received in stream";
3
207
 
@@ -148,6 +352,199 @@ async function collectLettaSseAssistantText(body, options = {}) {
148
352
  stopReason
149
353
  };
150
354
  }
355
+ // src/utils/conversation-busy-error.ts
356
+ var CONVERSATION_BUSY_ERROR_PATTERNS = [
357
+ /another request is (?:currently )?(?:being )?processed/i,
358
+ /currently being processed for this conversation/i,
359
+ /busy with another active run/i,
360
+ /already processing for this conversation/i,
361
+ /turn still running/i
362
+ ];
363
+ var CONVERSATION_BUSY_TITLE = "Turn still running";
364
+ function isConversationBusyErrorText(errorText) {
365
+ if (!errorText)
366
+ return false;
367
+ return CONVERSATION_BUSY_ERROR_PATTERNS.some((pattern) => pattern.test(errorText));
368
+ }
369
+ function buildConversationBusyErrorBody(automaticRetry) {
370
+ return automaticRetry ? "Another request is already processing for this conversation. I’ll wait for it to finish and retry automatically." : "Another request is already processing for this conversation. Please wait for it to finish, then try again.";
371
+ }
372
+
373
+ // src/channels/lifecycle-error.ts
374
+ var RAW_LOOP_ERROR_PATTERN = /^Unexpected stop reason:\s*error$/i;
375
+ var APP_CHAT_URL_PATTERN = /https:\/\/app\.letta\.com\/chat\/\S+/i;
376
+ var ESCAPE_CHARACTER = String.fromCharCode(27);
377
+ var OSC8_PREFIX = `${ESCAPE_CHARACTER}]8;;`;
378
+ var OSC8_TERMINATOR = `${ESCAPE_CHARACTER}\\`;
379
+ var APPROVAL_PENDING_ERROR_PATTERNS = [
380
+ /waiting for approval/i,
381
+ /pending request before continuing/i,
382
+ /approve or deny the pending request/i
383
+ ];
384
+ var DATABASE_LOCK_TIMEOUT_PATTERN = /\bcancel(?:l)?ing statement due to lock timeout\b/i;
385
+ var POSTGRES_LOCK_NOT_AVAILABLE_CODE_PATTERN = /\b55P03\b/i;
386
+ var POSTGRES_LOCK_NOT_AVAILABLE_SQLSTATE_PATTERN = /\b(?:sqlstate|pgcode)\s*[:=]?\s*["']?55P03\b/i;
387
+ var POSTGRES_LOCK_NOT_AVAILABLE_CONTEXT_PATTERN = /\b(?:postgres(?:ql)?|psycopg|sqlalchemy|lock[_ -]?not[_ -]?available|lock timeout)\b/i;
388
+ var RUN_ID_PATTERNS = [
389
+ /"run_id"\s*:\s*"([^"\\]+)"/i,
390
+ /\brun[_\s-]?id\b["']?\s*[:=]\s*["']?([A-Za-z0-9_-]+)/i,
391
+ /\(run:\s*([A-Za-z0-9_-]+)\)/i
392
+ ];
393
+ var CHANNEL_LIFECYCLE_FALLBACK_ERROR_MESSAGE = "Something went wrong while processing that message. Please try again.";
394
+ var CHANNEL_LIFECYCLE_APPROVAL_PENDING_MESSAGE = "The agent is still waiting on a tool approval from an earlier turn. Please approve or deny that pending request, then send your message again.";
395
+ var CHANNEL_LIFECYCLE_TRANSIENT_ERROR_MESSAGE = "A temporary error interrupted this turn. Please try again.";
396
+ var CHANNEL_LIFECYCLE_CONVERSATION_BUSY_TITLE = CONVERSATION_BUSY_TITLE;
397
+ function stripTrailingRunIdPunctuation(runId) {
398
+ return runId.replace(/[)"'.,;]+$/g, "");
399
+ }
400
+ function extractChannelLifecycleRunId(errorText) {
401
+ if (!errorText)
402
+ return;
403
+ for (const pattern of RUN_ID_PATTERNS) {
404
+ const match = errorText.match(pattern);
405
+ const runId = match?.[1]?.trim();
406
+ if (runId) {
407
+ return stripTrailingRunIdPunctuation(runId);
408
+ }
409
+ }
410
+ return;
411
+ }
412
+ function stripOsc8TerminalLinks(errorText) {
413
+ let output = "";
414
+ let cursor = 0;
415
+ while (cursor < errorText.length) {
416
+ const linkStart = errorText.indexOf(OSC8_PREFIX, cursor);
417
+ if (linkStart === -1) {
418
+ output += errorText.slice(cursor);
419
+ break;
420
+ }
421
+ const labelStart = errorText.indexOf(OSC8_TERMINATOR, linkStart + OSC8_PREFIX.length);
422
+ if (labelStart === -1) {
423
+ output += errorText.slice(cursor);
424
+ break;
425
+ }
426
+ const closeStart = errorText.indexOf(OSC8_PREFIX, labelStart + OSC8_TERMINATOR.length);
427
+ if (closeStart === -1) {
428
+ output += errorText.slice(cursor);
429
+ break;
430
+ }
431
+ const closeEnd = errorText.indexOf(OSC8_TERMINATOR, closeStart + OSC8_PREFIX.length);
432
+ if (closeEnd === -1) {
433
+ output += errorText.slice(cursor);
434
+ break;
435
+ }
436
+ output += errorText.slice(cursor, linkStart);
437
+ output += errorText.slice(labelStart + OSC8_TERMINATOR.length, closeStart);
438
+ cursor = closeEnd + OSC8_TERMINATOR.length;
439
+ }
440
+ return output;
441
+ }
442
+ function sanitizeChannelLifecycleErrorText(errorText) {
443
+ if (!errorText)
444
+ return "";
445
+ const withoutTerminalLinks = stripOsc8TerminalLinks(errorText);
446
+ return withoutTerminalLinks.split(`
447
+ `).filter((line) => {
448
+ const trimmed = line.trim();
449
+ if (/^View agent:/i.test(trimmed))
450
+ return false;
451
+ if (APP_CHAT_URL_PATTERN.test(trimmed))
452
+ return false;
453
+ return true;
454
+ }).join(`
455
+ `).replace(/[ \t]+\n/g, `
456
+ `).trim();
457
+ }
458
+ function truncateLifecycleMessage(text, maxLength) {
459
+ if (text.length <= maxLength)
460
+ return text;
461
+ return `${text.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
462
+ }
463
+ function isDatabaseLockErrorText(errorText) {
464
+ return DATABASE_LOCK_TIMEOUT_PATTERN.test(errorText) || POSTGRES_LOCK_NOT_AVAILABLE_SQLSTATE_PATTERN.test(errorText) || POSTGRES_LOCK_NOT_AVAILABLE_CODE_PATTERN.test(errorText) && POSTGRES_LOCK_NOT_AVAILABLE_CONTEXT_PATTERN.test(errorText);
465
+ }
466
+ function getChannelLifecycleErrorDisplay(errorText, options = {}) {
467
+ const normalized = sanitizeChannelLifecycleErrorText(errorText);
468
+ const optionsRunId = options.runId?.trim();
469
+ const runId = optionsRunId || extractChannelLifecycleRunId(errorText);
470
+ if (!normalized || RAW_LOOP_ERROR_PATTERN.test(normalized)) {
471
+ return {
472
+ kind: "generic",
473
+ title: "Turn failed",
474
+ body: CHANNEL_LIFECYCLE_FALLBACK_ERROR_MESSAGE,
475
+ runId
476
+ };
477
+ }
478
+ if (isDatabaseLockErrorText(normalized)) {
479
+ return {
480
+ kind: "database_lock_timeout",
481
+ title: "Turn failed",
482
+ body: CHANNEL_LIFECYCLE_TRANSIENT_ERROR_MESSAGE,
483
+ runId
484
+ };
485
+ }
486
+ if (isConversationBusyErrorText(normalized)) {
487
+ return {
488
+ kind: "conversation_busy",
489
+ title: CHANNEL_LIFECYCLE_CONVERSATION_BUSY_TITLE,
490
+ body: buildConversationBusyErrorBody(options.automaticRetry ?? false),
491
+ runId
492
+ };
493
+ }
494
+ if (APPROVAL_PENDING_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) {
495
+ return {
496
+ kind: "approval_pending",
497
+ title: "Turn failed",
498
+ body: CHANNEL_LIFECYCLE_APPROVAL_PENDING_MESSAGE,
499
+ runId
500
+ };
501
+ }
502
+ return {
503
+ kind: "generic",
504
+ title: "Turn failed",
505
+ body: normalized,
506
+ runId
507
+ };
508
+ }
509
+ function normalizeChannelLifecycleErrorMessage(errorText, options = {}) {
510
+ return getChannelLifecycleErrorDisplay(errorText, options).body;
511
+ }
512
+ function formatChannelLifecycleErrorMessage(errorText, options = {}) {
513
+ const display = getChannelLifecycleErrorDisplay(errorText, options);
514
+ const maxLength = options.maxLength ?? Number.POSITIVE_INFINITY;
515
+ const body = truncateLifecycleMessage(display.body, maxLength);
516
+ if (display.kind === "conversation_busy") {
517
+ const lines2 = [display.title, body];
518
+ if (display.runId) {
519
+ lines2.push("", `Run ID: ${display.runId}`);
520
+ }
521
+ return lines2.join(`
522
+ `);
523
+ }
524
+ if (display.kind === "database_lock_timeout") {
525
+ const lines2 = [`${display.title}:`, body];
526
+ if (display.runId) {
527
+ lines2.push("", `Run ID: ${display.runId}`);
528
+ }
529
+ return lines2.join(`
530
+ `);
531
+ }
532
+ if (options.codeBlock) {
533
+ const escaped = body.replace(/```/g, "``​`");
534
+ const lines2 = [`${display.title}:`, "```", escaped, "```"];
535
+ if (display.runId) {
536
+ lines2.push("", `Run ID: ${display.runId}`);
537
+ }
538
+ return lines2.join(`
539
+ `);
540
+ }
541
+ const lines = [`${display.title}:`, body];
542
+ if (display.runId) {
543
+ lines.push("", `Run ID: ${display.runId}`);
544
+ }
545
+ return lines.join(`
546
+ `);
547
+ }
151
548
  // src/constants.ts
152
549
  var SYSTEM_REMINDER_TAG = "system-reminder";
153
550
  var SYSTEM_REMINDER_OPEN = `<${SYSTEM_REMINDER_TAG}>`;
@@ -1209,16 +1606,28 @@ function createChannelTurnProgressBuilder(options = {}) {
1209
1606
  return { buildUpdates };
1210
1607
  }
1211
1608
  export {
1609
+ sanitizeChannelLifecycleErrorText,
1610
+ parseChannelSlashCommand,
1611
+ parseChannelBangCommand,
1612
+ normalizeChannelLifecycleErrorMessage,
1613
+ listChannelSlashCommands,
1614
+ getChannelLifecycleErrorDisplay,
1212
1615
  formatLettaStreamCoreErrorForChannel,
1213
1616
  formatInboundChannelMessageForAgent,
1617
+ formatChannelLifecycleErrorMessage,
1214
1618
  formatBatchedChannelMessagesForAgent,
1619
+ extractChannelLifecycleRunId,
1620
+ defaultChannelDisplayName,
1215
1621
  createChannelTurnProgressBuilder,
1216
1622
  collectLettaSseAssistantText,
1623
+ buildUnsupportedChannelCommandMessage,
1217
1624
  buildOutboundChannelMessageFromTurnSource,
1218
1625
  buildChannelTurnSource,
1626
+ buildChannelHelpMessage,
1219
1627
  LettaStreamNoAssistantMessageError,
1220
1628
  LettaStreamCoreError,
1221
- LETTA_STREAM_NO_ASSISTANT_MESSAGE_ERROR
1629
+ LETTA_STREAM_NO_ASSISTANT_MESSAGE_ERROR,
1630
+ CHANNEL_LIFECYCLE_FALLBACK_ERROR_MESSAGE
1222
1631
  };
1223
1632
 
1224
- //# debugId=39670CE4D9784BEC64756E2164756E21
1633
+ //# debugId=A70285B59041328B64756E2164756E21