@letta-ai/letta-code 0.30.11 → 0.30.12

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 (42) hide show
  1. package/dist/channels-public.js +302 -11
  2. package/dist/channels-public.js.map +5 -4
  3. package/dist/mcp-client.js +2 -2
  4. package/dist/mcp-client.js.map +1 -1
  5. package/dist/types/channels/command-runtime-executor.d.ts +181 -0
  6. package/dist/types/channels/command-runtime-executor.d.ts.map +1 -0
  7. package/dist/types/channels/command-surface.d.ts +40 -3
  8. package/dist/types/channels/command-surface.d.ts.map +1 -1
  9. package/dist/types/channels-public.d.ts +3 -1
  10. package/dist/types/channels-public.d.ts.map +1 -1
  11. package/dist/types/mods/invocation-context.d.ts +8 -0
  12. package/dist/types/mods/invocation-context.d.ts.map +1 -0
  13. package/dist/types/mods/mod-engine.d.ts +2 -1
  14. package/dist/types/mods/mod-engine.d.ts.map +1 -1
  15. package/dist/types/mods/tool-registry.d.ts.map +1 -1
  16. package/dist/types/mods/ui-helpers.d.ts +3 -0
  17. package/dist/types/mods/ui-helpers.d.ts.map +1 -0
  18. package/dist/types/settings-manager.d.ts +7 -10
  19. package/dist/types/settings-manager.d.ts.map +1 -1
  20. package/dist/types/tools/impl/enter-worktree-messages.d.ts +22 -0
  21. package/dist/types/tools/impl/enter-worktree-messages.d.ts.map +1 -0
  22. package/dist/types/tools/impl/enter-worktree.d.ts.map +1 -1
  23. package/dist/types/tools/toolset-types.d.ts +3 -0
  24. package/dist/types/tools/toolset-types.d.ts.map +1 -0
  25. package/dist/types/tools/toolset.d.ts +2 -3
  26. package/dist/types/tools/toolset.d.ts.map +1 -1
  27. package/dist/types/websocket/listener/mod-command-registry.d.ts +6 -0
  28. package/dist/types/websocket/listener/mod-command-registry.d.ts.map +1 -0
  29. package/letta.js +278320 -278056
  30. package/package.json +1 -1
  31. package/scripts/isolated-unit-tests.json +5 -0
  32. package/scripts/source-file-size-baseline.json +5 -5
  33. package/dist/types/cli/mods/command-runtime.d.ts +0 -13
  34. package/dist/types/cli/mods/command-runtime.d.ts.map +0 -1
  35. package/dist/types/cli/mods/types.d.ts +0 -2
  36. package/dist/types/cli/mods/types.d.ts.map +0 -1
  37. package/dist/types/websocket/listener/memfs-sync.d.ts +0 -24
  38. package/dist/types/websocket/listener/memfs-sync.d.ts.map +0 -1
  39. package/dist/types/websocket/listener/mod-adapter.d.ts +0 -49
  40. package/dist/types/websocket/listener/mod-adapter.d.ts.map +0 -1
  41. package/dist/types/websocket/listener/mod-commands.d.ts +0 -23
  42. package/dist/types/websocket/listener/mod-commands.d.ts.map +0 -1
@@ -1,4 +1,5 @@
1
1
  // src/channels/command-surface.ts
2
+ var DEFAULT_EXTRA_COMMANDS_LABEL = "Host commands";
2
3
  var DEFAULT_CHANNEL_DISPLAY_NAMES = {
3
4
  custom: "Custom",
4
5
  discord: "Discord",
@@ -75,11 +76,51 @@ var SLACK_MENTION_COMMAND_NAMES = [
75
76
  "new",
76
77
  "reload"
77
78
  ];
78
- function listChannelSlashCommands() {
79
- return CHANNEL_SLASH_COMMANDS.map((definition) => ({
79
+ function copyDefinition(definition) {
80
+ return {
80
81
  ...definition,
81
82
  aliases: definition.aliases ? [...definition.aliases] : undefined
82
- }));
83
+ };
84
+ }
85
+ function dedupedExtraCommands(options) {
86
+ const extras = options?.extraCommands;
87
+ if (!extras || extras.length === 0) {
88
+ return [];
89
+ }
90
+ const takenNames = new Set(SLACK_MENTION_COMMAND_NAMES);
91
+ for (const definition of CHANNEL_SLASH_COMMANDS) {
92
+ takenNames.add(definition.name.toLowerCase());
93
+ for (const alias of definition.aliases ?? []) {
94
+ takenNames.add(alias.toLowerCase());
95
+ }
96
+ }
97
+ const surviving = [];
98
+ for (const extra of extras) {
99
+ const nameKey = extra.name.toLowerCase();
100
+ if (takenNames.has(nameKey)) {
101
+ continue;
102
+ }
103
+ takenNames.add(nameKey);
104
+ const aliases = (extra.aliases ?? []).filter((alias) => {
105
+ const aliasKey = alias.toLowerCase();
106
+ if (takenNames.has(aliasKey)) {
107
+ return false;
108
+ }
109
+ takenNames.add(aliasKey);
110
+ return true;
111
+ });
112
+ surviving.push({
113
+ ...extra,
114
+ aliases: aliases.length > 0 ? aliases : undefined
115
+ });
116
+ }
117
+ return surviving;
118
+ }
119
+ function listChannelSlashCommands(options) {
120
+ return [
121
+ ...CHANNEL_SLASH_COMMANDS.map(copyDefinition),
122
+ ...dedupedExtraCommands(options)
123
+ ];
83
124
  }
84
125
  function parseSingleLineChannelCommand(text, prefix) {
85
126
  const trimmed = text.trim();
@@ -132,8 +173,8 @@ function parseChannelSlashCommand(text) {
132
173
  function parseChannelBangCommand(text) {
133
174
  return parseChannelCommand(text, "!");
134
175
  }
135
- function supportedCommandsText(prefix = "/") {
136
- return listChannelSlashCommands().map((definition) => `${prefix}${definition.name}`).join(", ");
176
+ function supportedCommandsText(prefix = "/", options) {
177
+ return listChannelSlashCommands(options).map((definition) => `${prefix}${definition.name}`).join(", ");
137
178
  }
138
179
  var SLACK_MENTION_SLASH_COMMAND_EXAMPLES = [
139
180
  "@agent /help",
@@ -150,15 +191,22 @@ var SLACK_MENTION_SLASH_COMMAND_EXAMPLES = [
150
191
  "@agent /new",
151
192
  "@agent /reload"
152
193
  ];
153
- function supportedSlackMentionSlashCommandsText() {
154
- return SLACK_MENTION_SLASH_COMMAND_EXAMPLES.join(", ");
194
+ function supportedSlackMentionSlashCommandsText(options) {
195
+ const extras = dedupedExtraCommands(options).map((definition) => `@agent /${definition.name}`);
196
+ return [...SLACK_MENTION_SLASH_COMMAND_EXAMPLES, ...extras].join(", ");
155
197
  }
156
198
  function supportedBangCommandsText() {
157
199
  return SLACK_MENTION_COMMAND_NAMES.map((name) => `!${name}`).join(", ");
158
200
  }
159
- function buildChannelHelpMessage(channelId, resolveDisplayName = defaultChannelDisplayName) {
201
+ function buildChannelHelpMessage(channelId, resolveDisplayName = defaultChannelDisplayName, options) {
160
202
  const displayName = resolveDisplayName(channelId);
203
+ const extras = dedupedExtraCommands(options);
204
+ const extrasLabel = options?.extraCommandsLabel ?? DEFAULT_EXTRA_COMMANDS_LABEL;
161
205
  if (channelId === "slack") {
206
+ const extraLines = extras.length > 0 ? [
207
+ `${extrasLabel}:`,
208
+ ...extras.map((definition) => `@agent /${definition.name} - ${definition.summary}`)
209
+ ] : [];
162
210
  return [
163
211
  `${displayName} is connected to Letta Code.`,
164
212
  "Talk by mentioning the app in a channel thread. Once a thread is routed, normal replies continue the same agent conversation until detached.",
@@ -174,25 +222,30 @@ function buildChannelHelpMessage(channelId, resolveDisplayName = defaultChannelD
174
222
  "@agent /detach - stop replying in this thread until mentioned again",
175
223
  "@agent /new - start a fresh conversation for this thread",
176
224
  "@agent /reload - reload settings, local mods, and agent secrets",
225
+ ...extraLines,
177
226
  `Legacy bang aliases still work after a mention: ${supportedBangCommandsText()}.`,
178
227
  "If this chat is not connected yet, send a normal message and follow the pairing instructions."
179
228
  ].join(`
180
229
  `);
181
230
  }
231
+ const extraParagraphs = extras.length > 0 ? [
232
+ `${extrasLabel} here: ${extras.map((definition) => `/${definition.name}`).join(", ")}.`
233
+ ] : [];
182
234
  return [
183
235
  `${displayName} is connected to Letta Code.`,
184
236
  "Send a normal message here and the connected agent will reply in this chat.",
185
237
  `Supported slash commands here: ${supportedCommandsText()}.`,
238
+ ...extraParagraphs,
186
239
  "If this chat is not connected yet, send any non-command message and follow the pairing instructions."
187
240
  ].join(`
188
241
 
189
242
  `);
190
243
  }
191
- function buildUnsupportedChannelCommandMessage(channelId, command, resolveDisplayName = defaultChannelDisplayName) {
244
+ function buildUnsupportedChannelCommandMessage(channelId, command, resolveDisplayName = defaultChannelDisplayName, options) {
192
245
  const displayName = resolveDisplayName(channelId);
193
246
  const isBang = command.raw.startsWith("!");
194
247
  const commandKind = isBang ? "bang" : "slash";
195
- const supportedCommands = isBang ? supportedBangCommandsText() : channelId === "slack" ? supportedSlackMentionSlashCommandsText() : supportedCommandsText();
248
+ const supportedCommands = isBang ? supportedBangCommandsText() : channelId === "slack" ? supportedSlackMentionSlashCommandsText(options) : supportedCommandsText("/", options);
196
249
  const supportedLabel = channelId === "slack" && !isBang ? "Slack mention commands" : `${commandKind} commands`;
197
250
  return [
198
251
  `${displayName} received ${command.raw}, but that ${commandKind} command is not supported in channels yet.`,
@@ -202,6 +255,229 @@ function buildUnsupportedChannelCommandMessage(channelId, command, resolveDispla
202
255
 
203
256
  `);
204
257
  }
258
+
259
+ // src/channels/command-runtime-executor.ts
260
+ var DEFAULT_CHANNEL_MODEL_LIST_LIMIT = 8;
261
+ function getModelEntryRank(entry) {
262
+ if (entry.isDefault)
263
+ return 0;
264
+ if (entry.isFeatured)
265
+ return 1;
266
+ const effort = entry.updateArgs?.reasoning_effort;
267
+ if (effort === "medium")
268
+ return 2;
269
+ if (effort === "high")
270
+ return 3;
271
+ return 4;
272
+ }
273
+ function preferModelEntry(current, candidate) {
274
+ return getModelEntryRank(candidate) < getModelEntryRank(current) ? candidate : current;
275
+ }
276
+ function buildModelEntriesByHandle(entries) {
277
+ const byHandle = new Map;
278
+ for (const entry of entries) {
279
+ const current = byHandle.get(entry.handle);
280
+ byHandle.set(entry.handle, current ? preferModelEntry(current, entry) : entry);
281
+ }
282
+ return byHandle;
283
+ }
284
+ function makeUnknownModelEntry(handle) {
285
+ return {
286
+ id: handle,
287
+ handle,
288
+ label: handle,
289
+ description: ""
290
+ };
291
+ }
292
+ function resolveModelHandles(params) {
293
+ const { handles, byHandle, availableHandles } = params;
294
+ const seen = new Set;
295
+ const resolved = [];
296
+ for (const handle of handles) {
297
+ if (!handle || seen.has(handle))
298
+ continue;
299
+ seen.add(handle);
300
+ if (availableHandles && !availableHandles.has(handle))
301
+ continue;
302
+ resolved.push(byHandle.get(handle) ?? makeUnknownModelEntry(handle));
303
+ }
304
+ return resolved;
305
+ }
306
+ function getFallbackModelEntries(byHandle) {
307
+ const preferred = Array.from(byHandle.values()).filter((entry) => entry.isDefault || entry.isFeatured);
308
+ return preferred.length > 0 ? preferred : Array.from(byHandle.values());
309
+ }
310
+ function modelCommandPrefix(channelId) {
311
+ return channelId === "slack" ? "@agent /model" : "/model";
312
+ }
313
+ function buildChannelModelNotFoundText(channelId) {
314
+ return `Model not found. Use ${modelCommandPrefix(channelId)} list to see available models.`;
315
+ }
316
+ function buildChannelCurrentModelMessage(channelId, params, displayNameResolver = defaultChannelDisplayName) {
317
+ const displayName = displayNameResolver(channelId);
318
+ const scope = params.scope === "agent" ? "agent" : "conversation";
319
+ const handleText = params.modelHandle && params.modelHandle !== params.modelLabel ? ` (${params.modelHandle})` : "";
320
+ const switchCommand = modelCommandPrefix(channelId);
321
+ return [
322
+ `${displayName} current ${scope} model: ${params.modelLabel}${handleText}.`,
323
+ `Use ${switchCommand} list to see available models, or ${switchCommand} <handle-or-id> to switch.`
324
+ ].join(`
325
+ `);
326
+ }
327
+ function formatChannelModelEntry(channelId, entry) {
328
+ const selector = entry.id || entry.handle;
329
+ const handleText = entry.handle === entry.label ? "" : ` — ${entry.handle}`;
330
+ return `• ${entry.label}${handleText} (${modelCommandPrefix(channelId)} ${selector})`;
331
+ }
332
+ function appendModelEntrySection(lines, channelId, title, entries, limit) {
333
+ if (entries.length === 0)
334
+ return;
335
+ lines.push("", `${title}:`);
336
+ for (const entry of entries.slice(0, limit)) {
337
+ lines.push(formatChannelModelEntry(channelId, entry));
338
+ }
339
+ const remaining = entries.length - limit;
340
+ if (remaining > 0) {
341
+ lines.push(`…and ${remaining} more.`);
342
+ }
343
+ }
344
+ function buildChannelModelListMessage(channelId, params, displayNameResolver = defaultChannelDisplayName) {
345
+ const displayName = displayNameResolver(channelId);
346
+ const limit = params.limit ?? DEFAULT_CHANNEL_MODEL_LIST_LIMIT;
347
+ const entries = params.entries;
348
+ const byHandle = buildModelEntriesByHandle(entries);
349
+ const availableHandleList = Array.isArray(params.availableHandles) ? params.availableHandles : null;
350
+ const availableSet = availableHandleList ? new Set(availableHandleList) : null;
351
+ const recentEntries = resolveModelHandles({
352
+ handles: params.recentHandles ?? [],
353
+ byHandle,
354
+ availableHandles: availableSet
355
+ });
356
+ const availableEntries = availableHandleList ? resolveModelHandles({ handles: availableHandleList, byHandle }) : getFallbackModelEntries(byHandle);
357
+ const lines = [`${displayName} model selector`];
358
+ if (params.availableHandles === null) {
359
+ lines.push("Availability lookup failed; showing built-in recommended models.");
360
+ } else if (params.availableHandles === undefined) {
361
+ lines.push("Available model data was not returned; showing built-in recommended models.");
362
+ }
363
+ appendModelEntrySection(lines, channelId, "Recent models", recentEntries, limit);
364
+ appendModelEntrySection(lines, channelId, "Available models", availableEntries, limit);
365
+ if (availableEntries.length === 0) {
366
+ lines.push("", "No available models were reported. Use /connect in Letta Code to configure a provider, then try again.");
367
+ }
368
+ lines.push("");
369
+ if (channelId === "slack") {
370
+ lines.push("Mention the app with @agent /model <handle-or-id> to switch this thread's routed model. Use @agent /model to show the current model. Legacy !model still works after a mention.");
371
+ } else {
372
+ lines.push("Use /model <handle-or-id> to switch this chat's routed model, or /model to show the current model.");
373
+ }
374
+ return lines.join(`
375
+ `);
376
+ }
377
+ function buildChannelModelListUnavailableMessage(channelId, error, displayNameResolver = defaultChannelDisplayName) {
378
+ const displayName = displayNameResolver(channelId);
379
+ return `${displayName} could not load the model list: ${error}`;
380
+ }
381
+ function buildChannelCurrentModelUnavailableMessage(channelId, error, displayNameResolver = defaultChannelDisplayName) {
382
+ const displayName = displayNameResolver(channelId);
383
+ return `${displayName} could not load the current model: ${error}`;
384
+ }
385
+ function buildChannelModelUpdatedMessage(channelId, params, displayNameResolver = defaultChannelDisplayName) {
386
+ const displayName = displayNameResolver(channelId);
387
+ const scope = params.appliedTo === "agent" ? "agent" : "conversation";
388
+ const handleText = params.modelHandle === params.modelLabel ? "" : ` (${params.modelHandle})`;
389
+ return `${displayName} updated this ${scope}'s model to ${params.modelLabel}${handleText}.`;
390
+ }
391
+ function buildChannelModelUpdateFailedMessage(channelId, identifier, error, displayNameResolver = defaultChannelDisplayName) {
392
+ const displayName = displayNameResolver(channelId);
393
+ return `${displayName} could not switch this chat's routed model to ${identifier}: ${error}`;
394
+ }
395
+ function buildChannelCancelAcceptedMessage(channelId, displayNameResolver = defaultChannelDisplayName) {
396
+ const displayName = displayNameResolver(channelId);
397
+ return `${displayName} cancelled the in-progress agent turn for this chat.`;
398
+ }
399
+ function buildChannelCancelNoActiveTurnMessage(channelId, displayNameResolver = defaultChannelDisplayName) {
400
+ const displayName = displayNameResolver(channelId);
401
+ return `${displayName} received /cancel, but there is no in-progress agent turn to cancel for this chat.`;
402
+ }
403
+ function parseChannelModelCommand(args) {
404
+ const modelIdentifier = args.trim();
405
+ if (!modelIdentifier) {
406
+ return { kind: "current" };
407
+ }
408
+ if (modelIdentifier.toLowerCase() === "list") {
409
+ return { kind: "list" };
410
+ }
411
+ return { kind: "update", modelIdentifier };
412
+ }
413
+ async function runChannelModelListCommand(params) {
414
+ const displayNameResolver = params.channelDisplayName ?? defaultChannelDisplayName;
415
+ const result = await params.client.listModels();
416
+ return {
417
+ handled: true,
418
+ text: result.success ? buildChannelModelListMessage(params.channelId, {
419
+ entries: result.entries,
420
+ availableHandles: result.availableHandles,
421
+ recentHandles: params.recentHandles
422
+ }, displayNameResolver) : buildChannelModelListUnavailableMessage(params.channelId, result.error ?? "Failed to list models", displayNameResolver)
423
+ };
424
+ }
425
+ async function runChannelModelUpdateCommand(params) {
426
+ const displayNameResolver = params.channelDisplayName ?? defaultChannelDisplayName;
427
+ const result = await params.client.updateModel({
428
+ runtime: params.runtime,
429
+ modelIdentifier: params.modelIdentifier
430
+ });
431
+ if (!result.success) {
432
+ return {
433
+ handled: true,
434
+ success: false,
435
+ text: buildChannelModelUpdateFailedMessage(params.channelId, params.modelIdentifier, result.error ?? "Failed to update model", displayNameResolver)
436
+ };
437
+ }
438
+ const appliedHandle = result.modelHandle ?? params.modelIdentifier;
439
+ return {
440
+ handled: true,
441
+ success: true,
442
+ modelHandle: appliedHandle,
443
+ text: buildChannelModelUpdatedMessage(params.channelId, {
444
+ modelLabel: params.resolveModelLabel?.(appliedHandle) ?? params.modelIdentifier,
445
+ modelHandle: appliedHandle,
446
+ appliedTo: result.appliedTo
447
+ }, displayNameResolver)
448
+ };
449
+ }
450
+ async function runChannelCancelCommand(params) {
451
+ const response = await params.client.abortMessage({
452
+ runtime: params.runtime,
453
+ runId: null
454
+ });
455
+ const cancelled = response.success && response.aborted;
456
+ if (params.channelId === undefined) {
457
+ return { handled: true, cancelled };
458
+ }
459
+ const displayNameResolver = params.channelDisplayName ?? defaultChannelDisplayName;
460
+ return {
461
+ handled: true,
462
+ cancelled,
463
+ text: cancelled ? buildChannelCancelAcceptedMessage(params.channelId, displayNameResolver) : buildChannelCancelNoActiveTurnMessage(params.channelId, displayNameResolver)
464
+ };
465
+ }
466
+ async function runChannelExecuteCommand(params) {
467
+ const trimmedArgs = params.args?.trim();
468
+ const response = await params.client.executeCommand({
469
+ runtime: params.runtime,
470
+ commandId: params.commandId,
471
+ ...trimmedArgs ? { args: trimmedArgs } : {}
472
+ });
473
+ return { handled: true, text: response.output };
474
+ }
475
+ function runChannelReflectionCommand(params) {
476
+ return runChannelExecuteCommand({ ...params, commandId: "reflect" });
477
+ }
478
+ function runChannelReloadCommand(params) {
479
+ return runChannelExecuteCommand({ ...params, commandId: "reload" });
480
+ }
205
481
  // src/channels/core-stream.ts
206
482
  var LETTA_STREAM_NO_ASSISTANT_MESSAGE_ERROR = "No assistant message received in stream";
207
483
 
@@ -1607,7 +1883,13 @@ function createChannelTurnProgressBuilder(options = {}) {
1607
1883
  }
1608
1884
  export {
1609
1885
  sanitizeChannelLifecycleErrorText,
1886
+ runChannelReloadCommand,
1887
+ runChannelReflectionCommand,
1888
+ runChannelModelUpdateCommand,
1889
+ runChannelModelListCommand,
1890
+ runChannelCancelCommand,
1610
1891
  parseChannelSlashCommand,
1892
+ parseChannelModelCommand,
1611
1893
  parseChannelBangCommand,
1612
1894
  normalizeChannelLifecycleErrorMessage,
1613
1895
  listChannelSlashCommands,
@@ -1623,11 +1905,20 @@ export {
1623
1905
  buildUnsupportedChannelCommandMessage,
1624
1906
  buildOutboundChannelMessageFromTurnSource,
1625
1907
  buildChannelTurnSource,
1908
+ buildChannelModelUpdatedMessage,
1909
+ buildChannelModelUpdateFailedMessage,
1910
+ buildChannelModelNotFoundText,
1911
+ buildChannelModelListUnavailableMessage,
1912
+ buildChannelModelListMessage,
1626
1913
  buildChannelHelpMessage,
1914
+ buildChannelCurrentModelUnavailableMessage,
1915
+ buildChannelCurrentModelMessage,
1916
+ buildChannelCancelNoActiveTurnMessage,
1917
+ buildChannelCancelAcceptedMessage,
1627
1918
  LettaStreamNoAssistantMessageError,
1628
1919
  LettaStreamCoreError,
1629
1920
  LETTA_STREAM_NO_ASSISTANT_MESSAGE_ERROR,
1630
1921
  CHANNEL_LIFECYCLE_FALLBACK_ERROR_MESSAGE
1631
1922
  };
1632
1923
 
1633
- //# debugId=A70285B59041328B64756E2164756E21
1924
+ //# debugId=7BECF32CA37A743264756E2164756E21