@letta-ai/letta-code 0.32.0 → 0.32.2

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 (86) hide show
  1. package/README.md +6 -0
  2. package/dist/agent-presets.js +97 -1
  3. package/dist/agent-presets.js.map +2 -2
  4. package/dist/channels-slack.js +67 -6
  5. package/dist/channels-slack.js.map +6 -5
  6. package/dist/gateway-core.js +37 -2
  7. package/dist/gateway-core.js.map +4 -4
  8. package/dist/mcp-client.js +2 -2
  9. package/dist/mcp-client.js.map +1 -1
  10. package/dist/memory-constraints.js +77 -1
  11. package/dist/memory-constraints.js.map +5 -4
  12. package/dist/types/agent/acting-user.d.ts +2 -0
  13. package/dist/types/agent/acting-user.d.ts.map +1 -1
  14. package/dist/types/agent/client-skills.d.ts +2 -0
  15. package/dist/types/agent/client-skills.d.ts.map +1 -1
  16. package/dist/types/agent/memory-git-hooks.d.ts +1 -1
  17. package/dist/types/agent/memory-git-hooks.d.ts.map +1 -1
  18. package/dist/types/agent/message.d.ts.map +1 -1
  19. package/dist/types/agent/modify.d.ts +2 -1
  20. package/dist/types/agent/modify.d.ts.map +1 -1
  21. package/dist/types/agent/subagents/manager.d.ts +1 -1
  22. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  23. package/dist/types/agent/subagents/subagent-launcher.d.ts +2 -0
  24. package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
  25. package/dist/types/backend/backend.d.ts +3 -1
  26. package/dist/types/backend/backend.d.ts.map +1 -1
  27. package/dist/types/backend/dev/fake-headless-backend.d.ts.map +1 -1
  28. package/dist/types/backend/dev/pi-request-headers.d.ts +6 -0
  29. package/dist/types/backend/dev/pi-request-headers.d.ts.map +1 -0
  30. package/dist/types/backend/dev/pi-stream-adapter.d.ts.map +1 -1
  31. package/dist/types/backend/local/compaction.d.ts +1 -0
  32. package/dist/types/backend/local/compaction.d.ts.map +1 -1
  33. package/dist/types/backend/local/local-backend.d.ts.map +1 -1
  34. package/dist/types/channels/message-channel-bindings.d.ts +39 -0
  35. package/dist/types/channels/message-channel-bindings.d.ts.map +1 -0
  36. package/dist/types/channels/message-channel-executor.d.ts +4 -0
  37. package/dist/types/channels/message-channel-executor.d.ts.map +1 -1
  38. package/dist/types/channels/message-channel-tool-definition.d.ts.map +1 -1
  39. package/dist/types/channels/message-channel-types.d.ts +3 -1
  40. package/dist/types/channels/message-channel-types.d.ts.map +1 -1
  41. package/dist/types/channels/plugin-types.d.ts +9 -1
  42. package/dist/types/channels/plugin-types.d.ts.map +1 -1
  43. package/dist/types/channels/slack/message-action-contract.d.ts +2 -0
  44. package/dist/types/channels/slack/message-action-contract.d.ts.map +1 -1
  45. package/dist/types/channels/slack/model-picker-blocks.d.ts.map +1 -1
  46. package/dist/types/cli/helpers/app-urls.d.ts +0 -4
  47. package/dist/types/cli/helpers/app-urls.d.ts.map +1 -1
  48. package/dist/types/cli/helpers/error-formatter.d.ts.map +1 -1
  49. package/dist/types/gateway-core.d.ts +1 -0
  50. package/dist/types/gateway-core.d.ts.map +1 -1
  51. package/dist/types/memory-constraints.d.ts +1 -0
  52. package/dist/types/memory-constraints.d.ts.map +1 -1
  53. package/dist/types/memory-frontmatter.d.ts +18 -0
  54. package/dist/types/memory-frontmatter.d.ts.map +1 -0
  55. package/dist/types/providers/chatgpt-usage-service.d.ts +72 -0
  56. package/dist/types/providers/chatgpt-usage-service.d.ts.map +1 -0
  57. package/dist/types/queue/queue-runtime.d.ts.map +1 -1
  58. package/dist/types/runtime-context.d.ts +2 -0
  59. package/dist/types/runtime-context.d.ts.map +1 -1
  60. package/dist/types/tools/impl/process_manager.d.ts +2 -0
  61. package/dist/types/tools/impl/process_manager.d.ts.map +1 -1
  62. package/dist/types/tools/impl/task.d.ts +2 -0
  63. package/dist/types/tools/impl/task.d.ts.map +1 -1
  64. package/dist/types/tools/letta-toolset.d.ts +10 -0
  65. package/dist/types/tools/letta-toolset.d.ts.map +1 -0
  66. package/dist/types/tools/toolset-options.d.ts +4 -0
  67. package/dist/types/tools/toolset-options.d.ts.map +1 -0
  68. package/dist/types/tools/toolset-types.d.ts +1 -2
  69. package/dist/types/tools/toolset-types.d.ts.map +1 -1
  70. package/dist/types/tools/toolset.d.ts +1 -0
  71. package/dist/types/tools/toolset.d.ts.map +1 -1
  72. package/dist/types/types/protocol_v2.d.ts +3 -2
  73. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  74. package/dist/types/types/toolset-protocol.d.ts +10 -0
  75. package/dist/types/types/toolset-protocol.d.ts.map +1 -0
  76. package/dist/types/websocket/listener/device-toolset-status.d.ts +6 -0
  77. package/dist/types/websocket/listener/device-toolset-status.d.ts.map +1 -0
  78. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  79. package/letta.js +75783 -75982
  80. package/package.json +2 -1
  81. package/scripts/codex-watch/diff-models-json.test.ts +94 -0
  82. package/scripts/codex-watch/diff-models-json.ts +66 -12
  83. package/scripts/isolated-unit-tests.json +5 -0
  84. package/scripts/source-file-size-baseline.json +6 -6
  85. package/skills/letta-guide/SKILL.md +6 -1
  86. package/skills/self-configuration/SKILL.md +4 -15
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.32.0",
3
+ "version": "0.32.2",
4
+ "lettaStartupLogProtocol": 1,
4
5
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
6
  "type": "module",
6
7
  "packageManager": "bun@1.3.14",
@@ -70,6 +70,63 @@ describe("diffModelsJson", () => {
70
70
  expect(diff.added_models).toEqual(["gpt-5.6"]);
71
71
  expect(diff.removed_models).toEqual(["gpt-5.4"]);
72
72
  });
73
+
74
+ test("detects instruction content changes without tool mentions", () => {
75
+ const diff = diffModelsJson(
76
+ models(model("gpt-5.6")),
77
+ models(
78
+ model("gpt-5.6", {
79
+ base_instructions:
80
+ "Use apply_patch and multi_tool_use.parallel. The user may send a new message while you are still working. Treat it as steering the active task.",
81
+ }),
82
+ ),
83
+ );
84
+
85
+ expect(diff.has_tool_schema_change).toBe(false);
86
+ expect(diff.has_prompt_tool_change).toBe(false);
87
+ expect(diff.has_instruction_content_change).toBe(true);
88
+ expect(diff.instruction_content_deltas).toEqual(["gpt-5.6"]);
89
+ });
90
+
91
+ test("detects top-level instructions_template changes", () => {
92
+ const diff = diffModelsJson(
93
+ models(model("gpt-5.6")),
94
+ models(
95
+ model("gpt-5.6", {
96
+ instructions_template: "Answer briefly, then resume the active task.",
97
+ }),
98
+ ),
99
+ );
100
+
101
+ expect(diff.has_instruction_content_change).toBe(true);
102
+ });
103
+
104
+ test("flags added models that ship instructions", () => {
105
+ const diff = diffModelsJson(
106
+ models(model("gpt-5.5")),
107
+ models(
108
+ model("gpt-5.5"),
109
+ model("gpt-6-astra"),
110
+ model("gpt-5.6-bare", {
111
+ base_instructions: "",
112
+ model_messages: {},
113
+ }),
114
+ ),
115
+ );
116
+
117
+ expect(diff.added_models).toEqual(["gpt-6-astra", "gpt-5.6-bare"]);
118
+ expect(diff.added_models_with_instructions).toEqual(["gpt-6-astra"]);
119
+ });
120
+
121
+ test("no instruction change when text is identical", () => {
122
+ const diff = diffModelsJson(
123
+ models(model("gpt-5.5")),
124
+ models(model("gpt-5.5")),
125
+ );
126
+
127
+ expect(diff.has_instruction_content_change).toBe(false);
128
+ expect(diff.instruction_content_deltas).toEqual([]);
129
+ });
73
130
  });
74
131
 
75
132
  describe("decideVerdict", () => {
@@ -137,6 +194,43 @@ describe("decideVerdict", () => {
137
194
  ).toBe("prompt-only update");
138
195
  });
139
196
 
197
+ test("returns prompt-only update for instruction content changes", () => {
198
+ const models_diff = diffModelsJson(
199
+ models(model("gpt-5.6")),
200
+ models(
201
+ model("gpt-5.6", {
202
+ base_instructions:
203
+ "The user may send a new message while you are still working.",
204
+ }),
205
+ ),
206
+ );
207
+ expect(
208
+ decideVerdict({
209
+ models_diff,
210
+ prompt_md_changed: false,
211
+ tools_dir_changed: false,
212
+ apply_patch_dir_changed: false,
213
+ parse_error: false,
214
+ }),
215
+ ).toBe("prompt-only update");
216
+ });
217
+
218
+ test("returns prompt-only update for added models with instructions", () => {
219
+ const models_diff = diffModelsJson(
220
+ models(model("gpt-5.5")),
221
+ models(model("gpt-5.5"), model("gpt-6-astra")),
222
+ );
223
+ expect(
224
+ decideVerdict({
225
+ models_diff,
226
+ prompt_md_changed: false,
227
+ tools_dir_changed: false,
228
+ apply_patch_dir_changed: false,
229
+ parse_error: false,
230
+ }),
231
+ ).toBe("prompt-only update");
232
+ });
233
+
140
234
  test("returns manual review on parse error", () => {
141
235
  expect(
142
236
  decideVerdict({
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Structured diff of openai/codex `codex-rs/models-manager/models.json`.
3
3
  *
4
- * We don't care about every field — only the ones that affect the tool/schema
5
- * surface exposed to the model. When any of these change, the letta-code
6
- * harness (src/agent/prompts/source_codex.md and src/tools/*) may need updating.
4
+ * We watch two surfaces: tool/schema fields that affect the tool surface
5
+ * exposed to the model (letta-code mirrors these in src/agent/prompts/source_codex.md
6
+ * and src/tools/*), and model instruction content (base_instructions /
7
+ * model_messages / instructions_template), whose behavioral guidance is worth
8
+ * reviewing even when no local mirror changes.
7
9
  */
8
10
 
9
11
  /** Tool-relevant fields lifted off each model entry in models.json. */
@@ -18,7 +20,7 @@ export interface ModelToolConfig {
18
20
  experimental_supported_tools?: string[];
19
21
  input_modalities?: string[];
20
22
  truncation_policy?: unknown;
21
- /** Tool names mentioned anywhere in base_instructions / instructions_template. */
23
+ /** Tool names mentioned anywhere in the model instruction text. */
22
24
  prompt_tool_mentions: string[];
23
25
  }
24
26
 
@@ -48,10 +50,16 @@ export interface ModelsDiff {
48
50
  added_models: string[];
49
51
  removed_models: string[];
50
52
  field_deltas: ToolFieldDelta[];
53
+ /** Slugs of existing models whose instruction content changed. */
54
+ instruction_content_deltas: string[];
55
+ /** Added models that ship instruction content. */
56
+ added_models_with_instructions: string[];
51
57
  /** True if any field_delta is in TOOL_SCHEMA_FIELDS. */
52
58
  has_tool_schema_change: boolean;
53
59
  /** True if any prompt_tool_mentions added or removed. */
54
60
  has_prompt_tool_change: boolean;
61
+ /** True if any existing model's instruction content changed. */
62
+ has_instruction_content_change: boolean;
55
63
  }
56
64
 
57
65
  /** Fields whose change implies a tool-schema update may be needed in letta-code. */
@@ -74,16 +82,15 @@ function collectMentions(text: string): string[] {
74
82
  return Array.from(found).sort();
75
83
  }
76
84
 
85
+ function slugOf(model: Record<string, unknown>): string {
86
+ return typeof model.slug === "string" ? model.slug : "<unknown>";
87
+ }
88
+
77
89
  export function extractToolConfig(
78
90
  model: Record<string, unknown>,
79
91
  ): ModelToolConfig {
80
- const slug = typeof model.slug === "string" ? model.slug : "<unknown>";
81
- const promptText = [
82
- typeof model.base_instructions === "string" ? model.base_instructions : "",
83
- typeof model.model_messages === "object" && model.model_messages !== null
84
- ? JSON.stringify(model.model_messages)
85
- : "",
86
- ].join("\n");
92
+ const slug = slugOf(model);
93
+ const promptText = extractInstructionText(model);
87
94
  return {
88
95
  slug,
89
96
  apply_patch_tool_type: model.apply_patch_tool_type as string | undefined,
@@ -109,6 +116,25 @@ function equal(a: unknown, b: unknown): boolean {
109
116
  return JSON.stringify(a) === JSON.stringify(b);
110
117
  }
111
118
 
119
+ /**
120
+ * Full model instruction text whose content we watch for behavioral prompt
121
+ * changes (e.g. steering or user-input guidance), not just tool mentions.
122
+ */
123
+ export function extractInstructionText(model: Record<string, unknown>): string {
124
+ const parts = [
125
+ typeof model.base_instructions === "string" ? model.base_instructions : "",
126
+ typeof model.model_messages === "object" &&
127
+ model.model_messages !== null &&
128
+ Object.keys(model.model_messages).length > 0
129
+ ? JSON.stringify(model.model_messages)
130
+ : "",
131
+ typeof model.instructions_template === "string"
132
+ ? model.instructions_template
133
+ : "",
134
+ ];
135
+ return parts.filter((part) => part.length > 0).join("\n");
136
+ }
137
+
112
138
  /** Compute the diff between two models.json payloads. */
113
139
  export function diffModelsJson(prev: ModelsJson, curr: ModelsJson): ModelsDiff {
114
140
  const prevBySlug = new Map<string, ModelToolConfig>();
@@ -135,6 +161,26 @@ export function diffModelsJson(prev: ModelsJson, curr: ModelsJson): ModelsDiff {
135
161
  let has_tool_schema_change = false;
136
162
  let has_prompt_tool_change = false;
137
163
 
164
+ const prevInstructions = new Map<string, string>();
165
+ const currInstructions = new Map<string, string>();
166
+ for (const m of prev.models) {
167
+ prevInstructions.set(slugOf(m), extractInstructionText(m));
168
+ }
169
+ for (const m of curr.models) {
170
+ currInstructions.set(slugOf(m), extractInstructionText(m));
171
+ }
172
+
173
+ const instruction_content_deltas: string[] = [];
174
+ for (const [slug, prevText] of prevInstructions) {
175
+ const currText = currInstructions.get(slug);
176
+ if (currText === undefined || prevText === currText) continue;
177
+ instruction_content_deltas.push(slug);
178
+ }
179
+
180
+ const added_models_with_instructions = added_models.filter(
181
+ (slug) => (currInstructions.get(slug) ?? "").length > 0,
182
+ );
183
+
138
184
  const fieldsToCompare = [
139
185
  ...TOOL_SCHEMA_FIELDS,
140
186
  "supports_image_detail_original",
@@ -159,8 +205,11 @@ export function diffModelsJson(prev: ModelsJson, curr: ModelsJson): ModelsDiff {
159
205
  added_models,
160
206
  removed_models,
161
207
  field_deltas,
208
+ instruction_content_deltas,
209
+ added_models_with_instructions,
162
210
  has_tool_schema_change,
163
211
  has_prompt_tool_change,
212
+ has_instruction_content_change: instruction_content_deltas.length > 0,
164
213
  };
165
214
  }
166
215
 
@@ -195,7 +244,12 @@ export function decideVerdict(input: VerdictInput): Verdict {
195
244
  return "tool-surface review needed";
196
245
  }
197
246
 
198
- if (input.models_diff.has_prompt_tool_change || input.prompt_md_changed) {
247
+ if (
248
+ input.models_diff.has_prompt_tool_change ||
249
+ input.models_diff.has_instruction_content_change ||
250
+ input.models_diff.added_models_with_instructions.length > 0 ||
251
+ input.prompt_md_changed
252
+ ) {
199
253
  return "prompt-only update";
200
254
  }
201
255
 
@@ -56,6 +56,11 @@
56
56
  "timeoutMs": 15000,
57
57
  "reason": "Imports the real media module while Slack adapter tests replace it."
58
58
  },
59
+ {
60
+ "path": "src/cli/subcommands/teleport.test.ts",
61
+ "timeoutMs": 15000,
62
+ "reason": "Reads process-global channel route state that other shared-batch suites mutate."
63
+ },
59
64
  {
60
65
  "path": "src/cli/components/provider-mod-selector-refresh.test.tsx",
61
66
  "timeoutMs": 15000,
@@ -10,20 +10,20 @@
10
10
  "src/cli/app/use-approval-flow.ts": 1163,
11
11
  "src/cli/app/use-configuration-handlers.ts": 1421,
12
12
  "src/cli/app/use-conversation-loop.ts": 2903,
13
- "src/cli/app/use-submit-handler.ts": 4067,
13
+ "src/cli/app/use-submit-handler.ts": 3998,
14
14
  "src/cli/components/AgentSelector.tsx": 1104,
15
15
  "src/cli/components/InputRich.tsx": 2225,
16
16
  "src/cli/components/ModelSelector.tsx": 1259,
17
17
  "src/cli/components/ProviderSelector.tsx": 1692,
18
18
  "src/cli/components/ToolCallMessageRich.tsx": 1109,
19
19
  "src/cli/helpers/accumulator.ts": 1592,
20
- "src/cli/helpers/reflection-transcript.ts": 1929,
20
+ "src/cli/helpers/reflection-transcript.ts": 1926,
21
21
  "src/cli/mods/local-mod-loader.test.ts": 1043,
22
22
  "src/cli/reflection-transcript.test.ts": 1084,
23
23
  "src/cli/subcommands/skills.ts": 1264,
24
- "src/headless.ts": 4986,
24
+ "src/headless.ts": 4888,
25
25
  "src/hooks/integration.test.ts": 1147,
26
- "src/index.ts": 2772,
26
+ "src/index.ts": 2616,
27
27
  "src/mods/learning-harness.ts": 2434,
28
28
  "src/mods/mod-engine.test.ts": 2153,
29
29
  "src/mods/mod-engine.ts": 1838,
@@ -37,13 +37,13 @@
37
37
  "src/settings-manager.ts": 2102,
38
38
  "src/tools/manager.ts": 2969,
39
39
  "src/tools/tool-execution-context.test.ts": 1136,
40
- "src/types/protocol_v2.ts": 2650,
40
+ "src/types/protocol_v2.ts": 2647,
41
41
  "src/websocket/listen-client-concurrency.test.ts": 2685,
42
42
  "src/websocket/listen-client-protocol.test.ts": 5819,
43
43
  "src/websocket/listener/commands/memory.ts": 1114,
44
44
  "src/websocket/listener/file-commands.ts": 1053,
45
45
  "src/websocket/listener/lifecycle.ts": 1048,
46
46
  "src/websocket/listener/protocol-inbound.ts": 2197,
47
- "src/websocket/listener/protocol-outbound.ts": 1042,
47
+ "src/websocket/listener/protocol-outbound.ts": 1025,
48
48
  "src/websocket/listener/turn.ts": 1054
49
49
  }
@@ -51,6 +51,12 @@ helper below fetches the live index first, so you pick a URL that exists.
51
51
  give your best answer, and clearly mark it as possibly out of date with a
52
52
  link to https://docs.letta.com. Never silently fall back to memory.
53
53
 
54
+ ## Inspect or change your model from the CLI
55
+
56
+ - `letta model list [--byok | --hosted]` lists available models.
57
+ - `letta model set [model_handle] [--reasoning <reasoning-option>] [--default]` overrides the current conversation's model or reasoning; `--default` overrides the agent's default instead.
58
+ - `letta model get [--default]` gets the current model configuration; `--default` gets the agent's default configuration.
59
+
54
60
  ## Hard rules
55
61
 
56
62
  - **Never invent CLI commands, flags, slash commands, settings keys, config
@@ -94,4 +100,3 @@ The helper owns the cache. It uses the first writable temporary directory from
94
100
  when an explicit location is needed. Every invocation checks the live ETag and
95
101
  reuses the local document only when its body hash still matches. Do not create
96
102
  or manage a second cache yourself.
97
-
@@ -49,20 +49,9 @@ If a broken model or prompt prevents the agent from completing a turn, recover o
49
49
 
50
50
  Local settings, server state, and the current process are different sources of truth. Inspect the layer you intend to change before writing it.
51
51
 
52
- Start with the authenticated, backend-aware active configuration report:
53
-
54
- ```bash
55
- letta agents config
56
- ```
57
-
58
- With no arguments it uses `AGENT_ID` and `CONVERSATION_ID` from the current session. To inspect an explicit scope:
59
-
60
- ```bash
61
- letta agents config --agent "$AGENT_ID"
62
- letta agents config --conversation "$CONVERSATION_ID"
63
- ```
64
-
65
- The conversation form retrieves its parent agent automatically and reports both scopes plus the effective configured model. It works through the active API or local backend; do not read auth files, call REST directly, or decode local persistence paths yourself. A configured router handle such as `letta/auto` does not identify the underlying model selected for one inference.
52
+ - `letta model list [--byok | --hosted]` lists available models.
53
+ - `letta model set [model_handle] [--reasoning <reasoning-option>] [--default]` overrides the current conversation's model or reasoning; `--default` overrides the agent's default instead.
54
+ - `letta model get [--default]` gets the current model configuration; `--default` gets the agent's default configuration.
66
55
 
67
56
  Use the secret-safe local/runtime report for harness settings, permissions, and backend diagnostics:
68
57
 
@@ -278,7 +267,7 @@ Per-agent `agents[]` entries are keyed by `agentId` plus server. For api.letta.c
278
267
 
279
268
  Base URL resolution is split between runtime API calls and settings lookup. Runtime API calls require `LETTA_BASE_URL` or an explicit script `--base-url`; do not replace it with a hard-coded Cloud URL. Settings server keys resolve from `LETTA_SETTINGS_BASE_URL`, `env.LETTA_SETTINGS_BASE_URL`, `LETTA_BASE_URL`, `env.LETTA_BASE_URL`, then api.letta.com. Do not move `agents[]` entries across base URLs unless the user is deliberately migrating servers.
280
269
 
281
- Toolset values currently include `auto`, `default`, `codex`, `codex_snake`, `gemini`, `gemini_snake`, and `none`. Use `auto` unless the user explicitly wants a manual override.
270
+ Toolset values currently include `auto`, `letta`, `default`, `codex`, `codex_snake`, `gemini`, `gemini_snake`, and `none`. Use `auto` unless the user explicitly wants a manual override.
282
271
 
283
272
  ## Permissions
284
273