@letta-ai/letta-code 0.32.1 → 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 (55) hide show
  1. package/README.md +6 -0
  2. package/dist/agent-presets.js +25 -1
  3. package/dist/agent-presets.js.map +2 -2
  4. package/dist/channels-slack.js +49 -3
  5. package/dist/channels-slack.js.map +5 -4
  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/memory-git-hooks.d.ts +1 -1
  13. package/dist/types/agent/memory-git-hooks.d.ts.map +1 -1
  14. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  15. package/dist/types/backend/backend.d.ts +0 -1
  16. package/dist/types/backend/backend.d.ts.map +1 -1
  17. package/dist/types/backend/dev/fake-headless-backend.d.ts.map +1 -1
  18. package/dist/types/channels/message-channel-bindings.d.ts +39 -0
  19. package/dist/types/channels/message-channel-bindings.d.ts.map +1 -0
  20. package/dist/types/channels/message-channel-executor.d.ts +4 -0
  21. package/dist/types/channels/message-channel-executor.d.ts.map +1 -1
  22. package/dist/types/channels/message-channel-tool-definition.d.ts.map +1 -1
  23. package/dist/types/channels/message-channel-types.d.ts +3 -1
  24. package/dist/types/channels/message-channel-types.d.ts.map +1 -1
  25. package/dist/types/channels/plugin-types.d.ts +9 -1
  26. package/dist/types/channels/plugin-types.d.ts.map +1 -1
  27. package/dist/types/channels/slack/message-action-contract.d.ts +2 -0
  28. package/dist/types/channels/slack/message-action-contract.d.ts.map +1 -1
  29. package/dist/types/cli/helpers/app-urls.d.ts +0 -4
  30. package/dist/types/cli/helpers/app-urls.d.ts.map +1 -1
  31. package/dist/types/cli/helpers/error-formatter.d.ts.map +1 -1
  32. package/dist/types/gateway-core.d.ts +1 -0
  33. package/dist/types/gateway-core.d.ts.map +1 -1
  34. package/dist/types/memory-constraints.d.ts +1 -0
  35. package/dist/types/memory-constraints.d.ts.map +1 -1
  36. package/dist/types/memory-frontmatter.d.ts +18 -0
  37. package/dist/types/memory-frontmatter.d.ts.map +1 -0
  38. package/dist/types/tools/letta-toolset.d.ts +10 -0
  39. package/dist/types/tools/letta-toolset.d.ts.map +1 -0
  40. package/dist/types/tools/toolset-options.d.ts +4 -0
  41. package/dist/types/tools/toolset-options.d.ts.map +1 -0
  42. package/dist/types/tools/toolset-types.d.ts +1 -2
  43. package/dist/types/tools/toolset-types.d.ts.map +1 -1
  44. package/dist/types/tools/toolset.d.ts.map +1 -1
  45. package/dist/types/types/protocol_v2.d.ts +3 -2
  46. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  47. package/dist/types/types/toolset-protocol.d.ts +10 -0
  48. package/dist/types/types/toolset-protocol.d.ts.map +1 -0
  49. package/dist/types/websocket/listener/device-toolset-status.d.ts +6 -0
  50. package/dist/types/websocket/listener/device-toolset-status.d.ts.map +1 -0
  51. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  52. package/letta.js +936 -1620
  53. package/package.json +2 -1
  54. package/scripts/source-file-size-baseline.json +6 -6
  55. package/skills/self-configuration/SKILL.md +1 -1
package/letta.js CHANGED
@@ -66,6 +66,50 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
66
66
  var __promiseAll = (args) => Promise.all(args);
67
67
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
68
68
 
69
+ // src/utils/startup-log-boundary.ts
70
+ import { writeSync } from "node:fs";
71
+ function sealStartupLogs() {
72
+ if (failure)
73
+ throw failure;
74
+ if (marker === undefined)
75
+ return;
76
+ try {
77
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(marker)) {
78
+ throw new Error("LETTA_STARTUP_LOG_MARKER must be a UUID");
79
+ }
80
+ const bytes = Buffer.from(`
81
+ [letta-startup-end:${marker}]
82
+ `);
83
+ let offset = 0;
84
+ while (offset < bytes.length) {
85
+ const written = writeSync(1, bytes, offset, bytes.length - offset);
86
+ if (written === 0)
87
+ throw new Error("Startup marker write made no progress");
88
+ offset += written;
89
+ }
90
+ marker = undefined;
91
+ } catch (cause) {
92
+ failure = new Error("Failed to seal startup logs", { cause });
93
+ throw failure;
94
+ }
95
+ }
96
+ var marker, ownerPid, failure;
97
+ var init_startup_log_boundary = __esm(() => {
98
+ marker = process.env.LETTA_STARTUP_LOG_MARKER;
99
+ ownerPid = process.env.LETTA_STARTUP_LOG_OWNER_PID;
100
+ delete process.env.LETTA_STARTUP_LOG_MARKER;
101
+ delete process.env.LETTA_STARTUP_LOG_OWNER_PID;
102
+ if (ownerPid !== undefined) {
103
+ if (!/^[1-9][0-9]*$/.test(ownerPid) || !Number.isSafeInteger(Number(ownerPid))) {
104
+ failure = new Error("Failed to seal startup logs", {
105
+ cause: new Error("LETTA_STARTUP_LOG_OWNER_PID must be a positive integer")
106
+ });
107
+ } else if (Number(ownerPid) !== process.pid) {
108
+ marker = undefined;
109
+ }
110
+ }
111
+ });
112
+
69
113
  // node_modules/@earendil-works/pi-ai/dist/utils/provider-env.js
70
114
  function getBunSandboxEnvValue(name) {
71
115
  if (typeof process === "undefined" || !process.versions?.bun || Object.keys(process.env).length > 0) {
@@ -5471,7 +5515,8 @@ var package_default;
5471
5515
  var init_package = __esm(() => {
5472
5516
  package_default = {
5473
5517
  name: "@letta-ai/letta-code",
5474
- version: "0.32.1",
5518
+ version: "0.32.2",
5519
+ lettaStartupLogProtocol: 1,
5475
5520
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5476
5521
  type: "module",
5477
5522
  packageManager: "bun@1.3.14",
@@ -24251,15 +24296,15 @@ var init_memories = __esm(() => {
24251
24296
  return path3.join(__classPrivateFieldGet3(this, _SessionMemoryStores_workdir, "f"), "memory", resource.name || resource.memory_store_id);
24252
24297
  }, _SessionMemoryStores_scanMarker = async function _SessionMemoryStores_scanMarker2(store) {
24253
24298
  const local = await store.files.hashtree();
24254
- const marker = local[MARKER_PATH];
24299
+ const marker2 = local[MARKER_PATH];
24255
24300
  delete local[MARKER_PATH];
24256
- if (marker === markerSha(store.memoryStoreId)) {
24301
+ if (marker2 === markerSha(store.memoryStoreId)) {
24257
24302
  return { files: local, markerOk: true, distrustReason: null };
24258
24303
  }
24259
24304
  return {
24260
24305
  files: local,
24261
24306
  markerOk: false,
24262
- distrustReason: marker !== undefined ? "the marker file does not match this store" : "the marker file is gone"
24307
+ distrustReason: marker2 !== undefined ? "the marker file does not match this store" : "the marker file is gone"
24263
24308
  };
24264
24309
  }, _SessionMemoryStores_syncStore = async function _SessionMemoryStores_syncStore2(store, final) {
24265
24310
  try {
@@ -85829,6 +85874,81 @@ var init_context = __esm(() => {
85829
85874
  context2 = getContext();
85830
85875
  });
85831
85876
 
85877
+ // src/memory-frontmatter.ts
85878
+ function validateMemoryFileFrontmatter({
85879
+ path: path7,
85880
+ content,
85881
+ previousContent,
85882
+ format: format5
85883
+ }) {
85884
+ const errors4 = [];
85885
+ const v2 = format5 === "memfs-v2";
85886
+ const lines = content.split(`
85887
+ `);
85888
+ if (v2 && path7.split("/").at(-1) === "MEMORY.md") {
85889
+ return lines[0] === "---" ? [`${path7}: MEMORY.md must not have frontmatter`] : [];
85890
+ }
85891
+ if (lines[0] !== "---") {
85892
+ return [`${path7}: missing frontmatter (must start with ---)`];
85893
+ }
85894
+ const closing = lines.indexOf("---", 1);
85895
+ if (closing < 0) {
85896
+ return [
85897
+ `${path7}: frontmatter opened but never closed (missing closing ---)`
85898
+ ];
85899
+ }
85900
+ function legacyValue(text, key) {
85901
+ const previousLines = text?.split(`
85902
+ `) ?? [];
85903
+ const end = previousLines.indexOf("---", 1);
85904
+ if (end < 0)
85905
+ return "";
85906
+ return previousLines.slice(1, end).filter((line) => line.startsWith(`${key}:`)).map((line) => line.slice(key.length + 1).replace(/^ +| +$/g, "")).join(`
85907
+ `);
85908
+ }
85909
+ if (!v2 && previousContent && legacyValue(previousContent, "read_only") === "true") {
85910
+ return [`${path7}: file is read_only and cannot be modified`];
85911
+ }
85912
+ const seen = new Set;
85913
+ const allowed = v2 ? ["name", "description"] : ["description", "read_only", "limit"];
85914
+ for (const line of lines.slice(1, closing)) {
85915
+ if (!line)
85916
+ continue;
85917
+ if (!v2 && /^[ \t]/.test(line))
85918
+ continue;
85919
+ const separator = line.indexOf(":");
85920
+ const rawKey = separator < 0 ? line : line.slice(0, separator);
85921
+ const key = v2 ? rawKey.replace(/^ +| +$/g, "") : rawKey.replaceAll(" ", "");
85922
+ const value = (separator < 0 ? line : line.slice(separator + 1)).replace(/^ +| +$/g, "");
85923
+ if (!allowed.includes(key)) {
85924
+ errors4.push(`${path7}: unknown frontmatter key '${key}' (allowed: ${allowed.join(" ")})`);
85925
+ continue;
85926
+ }
85927
+ if (v2 && seen.has(key)) {
85928
+ errors4.push(`${path7}: duplicate frontmatter key '${key}'`);
85929
+ }
85930
+ seen.add(key);
85931
+ if (key === "read_only") {
85932
+ if (!previousContent) {
85933
+ errors4.push(`${path7}: 'read_only' is a protected field and cannot be set by the agent`);
85934
+ } else if (value !== legacyValue(previousContent, key)) {
85935
+ errors4.push(`${path7}: 'read_only' is a protected field and cannot be changed by the agent`);
85936
+ }
85937
+ }
85938
+ if ((v2 || key === "description") && (!value || v2 && (value === '""' || value === "''"))) {
85939
+ errors4.push(`${path7}: '${key}' must not be empty`);
85940
+ }
85941
+ }
85942
+ for (const key of v2 ? ["name", "description"] : ["description"]) {
85943
+ if (!seen.has(key))
85944
+ errors4.push(`${path7}: missing required field '${key}'`);
85945
+ }
85946
+ if (!v2 && legacyValue(previousContent, "read_only") && !legacyValue(content, "read_only")) {
85947
+ errors4.push(`${path7}: 'read_only' is a protected field and cannot be removed by the agent`);
85948
+ }
85949
+ return errors4;
85950
+ }
85951
+
85832
85952
  // src/memory-constraints.ts
85833
85953
  async function validateMemoryTreeConstraints(reader, options) {
85834
85954
  const errors4 = [];
@@ -86242,10 +86362,8 @@ var init_memory_git_hooks = __esm(() => {
86242
86362
  # Validate frontmatter in staged memory .md files
86243
86363
  # Installed by Letta Code CLI
86244
86364
 
86245
- AGENT_EDITABLE_KEYS="description"
86246
- PROTECTED_KEYS="read_only"
86247
- ALL_KNOWN_KEYS="description read_only limit"
86248
86365
  errors=""
86366
+ memory_files=()
86249
86367
 
86250
86368
  memory_layout_policy_file="$(git rev-parse --git-common-dir 2>/dev/null)/${MEMORY_LAYOUT_POLICY}"
86251
86369
  memory_layout_policy=$(cat "$memory_layout_policy_file" 2>/dev/null || true)
@@ -86256,63 +86374,33 @@ validate_memory_constraints() {
86256
86374
  fi
86257
86375
  }
86258
86376
 
86259
- validate_v2_file() {
86260
- local file="$1" staged first_line closing_line frontmatter line key value
86261
- local has_name=false has_description=false
86262
- staged=$(git show ":$file")
86263
-
86264
- if [ "\${file##*/}" = "MEMORY.md" ]; then
86265
- first_line=$(echo "$staged" | head -1)
86266
- if [ "$first_line" = "---" ]; then
86267
- errors="$errors\\n $file: MEMORY.md must not have frontmatter"
86268
- fi
86269
- return
86270
- fi
86271
-
86272
- first_line=$(echo "$staged" | head -1)
86273
- if [ "$first_line" != "---" ]; then
86274
- errors="$errors\\n $file: missing frontmatter (must start with ---)"
86275
- return
86276
- fi
86277
- closing_line=$(echo "$staged" | tail -n +2 | grep -n '^---$' | head -1 | cut -d: -f1)
86278
- if [ -z "$closing_line" ]; then
86279
- errors="$errors\\n $file: frontmatter opened but never closed (missing closing ---)"
86280
- return
86281
- fi
86282
- frontmatter=$(echo "$staged" | tail -n +2 | head -n $((closing_line - 1)))
86283
-
86284
- while IFS= read -r line; do
86285
- [ -z "$line" ] && continue
86286
- key=$(echo "$line" | cut -d: -f1 | sed 's/^ *//;s/ *$//')
86287
- value=$(echo "$line" | cut -d: -f2- | sed 's/^ *//;s/ *$//')
86288
- case "$key" in
86289
- name)
86290
- if [ "$has_name" = "true" ]; then
86291
- errors="$errors\\n $file: duplicate frontmatter key 'name'"
86292
- fi
86293
- has_name=true
86294
- ;;
86295
- description)
86296
- if [ "$has_description" = "true" ]; then
86297
- errors="$errors\\n $file: duplicate frontmatter key 'description'"
86298
- fi
86299
- has_description=true
86300
- ;;
86301
- *)
86302
- errors="$errors\\n $file: unknown frontmatter key '$key' (allowed: name description)"
86303
- continue
86304
- ;;
86305
- esac
86306
- if [ -z "$value" ] || [ "$value" = '""' ] || [ "$value" = "''" ]; then
86307
- errors="$errors\\n $file: '$key' must not be empty"
86308
- fi
86309
- done <<< "$frontmatter"
86310
-
86311
- if [ "$has_name" = "false" ]; then
86312
- errors="$errors\\n $file: missing required field 'name'"
86313
- fi
86314
- if [ "$has_description" = "false" ]; then
86315
- errors="$errors\\n $file: missing required field 'description'"
86377
+ validate_memory_files() {
86378
+ [ "\${#memory_files[@]}" -eq 0 ] && return
86379
+ local result
86380
+ result=$(node - "$1" "\${memory_files[@]}" <<'LETTA_MEMORY_FRONTMATTER'
86381
+ const { execFileSync, spawnSync } = require("node:child_process");
86382
+ const validateMemoryFileFrontmatter = ${validateMemoryFileFrontmatter.toString()};
86383
+ const [format, ...paths] = process.argv.slice(2);
86384
+ try {
86385
+ for (const path of paths) {
86386
+ const content = execFileSync("git", ["show", ":" + path], { encoding: "utf8", maxBuffer: Infinity, stdio: ["ignore", "pipe", "pipe"] });
86387
+ let previousContent = null;
86388
+ if (format === "legacy") {
86389
+ const previous = spawnSync("git", ["show", "HEAD:" + path], { encoding: "utf8", maxBuffer: Infinity, stdio: ["ignore", "pipe", "pipe"] });
86390
+ if (previous.error) throw previous.error;
86391
+ if (previous.status === 0) previousContent = previous.stdout;
86392
+ }
86393
+ const errors = validateMemoryFileFrontmatter({ path, content, previousContent, format });
86394
+ for (const error of errors) console.log(" " + error);
86395
+ }
86396
+ } catch {
86397
+ console.error("Memory validation could not read Git contents. No files were committed.");
86398
+ process.exit(1);
86399
+ }
86400
+ LETTA_MEMORY_FRONTMATTER
86401
+ ) || exit $?
86402
+ if [ -n "$result" ]; then
86403
+ errors="$errors\\n$result"
86316
86404
  fi
86317
86405
  }
86318
86406
 
@@ -86354,8 +86442,9 @@ if [ "$use_v2_validation" = "true" ]; then
86354
86442
  ;;
86355
86443
  esac
86356
86444
  fi
86357
- [ "$projected" = "true" ] && validate_v2_file "$file"
86445
+ [ "$projected" = "true" ] && memory_files+=("$file")
86358
86446
  done < <(git ls-files '*.md')
86447
+ validate_memory_files "memfs-v2"
86359
86448
 
86360
86449
  if [ -n "$errors" ]; then
86361
86450
  echo "Memory validation failed:"
@@ -86372,122 +86461,12 @@ for file in $(git diff --cached --name-only --diff-filter=ACMR | grep -E '^(memo
86372
86461
  errors="$errors\\n $file: invalid skill path (skills must be folders). Use skills/<name>/SKILL.md"
86373
86462
  done
86374
86463
 
86375
- # Helper: extract a frontmatter value from content
86376
- get_fm_value() {
86377
- local content="$1" key="$2"
86378
- local closing_line
86379
- closing_line=$(echo "$content" | tail -n +2 | grep -n '^---$' | head -1 | cut -d: -f1)
86380
- [ -z "$closing_line" ] && return
86381
- echo "$content" | tail -n +2 | head -n $((closing_line - 1)) | grep "^$key:" | cut -d: -f2- | sed 's/^ *//;s/ *$//'
86382
- }
86383
-
86384
86464
  # Match .md files under system/ or reference/ (with optional memory/ prefix).
86385
86465
  # Skip skill SKILL.md files — they use a different frontmatter format.
86386
86466
  for file in $(git diff --cached --name-only --diff-filter=ACM | grep -E '^(memory/)?(system|reference)/.*\\.md$'); do
86387
- staged=$(git show ":$file")
86388
-
86389
- # Frontmatter is required
86390
- first_line=$(echo "$staged" | head -1)
86391
- if [ "$first_line" != "---" ]; then
86392
- errors="$errors\\n $file: missing frontmatter (must start with ---)"
86393
- continue
86394
- fi
86395
-
86396
- # Check frontmatter is properly closed
86397
- closing_line=$(echo "$staged" | tail -n +2 | grep -n '^---$' | head -1 | cut -d: -f1)
86398
- if [ -z "$closing_line" ]; then
86399
- errors="$errors\\n $file: frontmatter opened but never closed (missing closing ---)"
86400
- continue
86401
- fi
86402
-
86403
- # Check read_only protection against HEAD version
86404
- head_content=$(git show "HEAD:$file" 2>/dev/null || true)
86405
- if [ -n "$head_content" ]; then
86406
- head_ro=$(get_fm_value "$head_content" "read_only")
86407
- if [ "$head_ro" = "true" ]; then
86408
- errors="$errors\\n $file: file is read_only and cannot be modified"
86409
- continue
86410
- fi
86411
- fi
86412
-
86413
- # Extract frontmatter lines
86414
- frontmatter=$(echo "$staged" | tail -n +2 | head -n $((closing_line - 1)))
86415
-
86416
- # Track required fields
86417
- has_description=false
86418
-
86419
- # Validate each line
86420
- while IFS= read -r line; do
86421
- [ -z "$line" ] && continue
86422
- # Skip YAML multiline continuation lines (indented lines that continue a previous value)
86423
- case "$line" in
86424
- " "*|$' '*) continue ;;
86425
- esac
86426
-
86427
- key=$(echo "$line" | cut -d: -f1 | tr -d ' ')
86428
- value=$(echo "$line" | cut -d: -f2- | sed 's/^ *//;s/ *$//')
86429
-
86430
- # Check key is known
86431
- known=false
86432
- for k in $ALL_KNOWN_KEYS; do
86433
- if [ "$key" = "$k" ]; then
86434
- known=true
86435
- break
86436
- fi
86437
- done
86438
- if [ "$known" = "false" ]; then
86439
- errors="$errors\\n $file: unknown frontmatter key '$key' (allowed: $ALL_KNOWN_KEYS)"
86440
- continue
86441
- fi
86442
-
86443
- # Check if agent is trying to modify a protected key
86444
- for k in $PROTECTED_KEYS; do
86445
- if [ "$key" = "$k" ]; then
86446
- # Compare against HEAD — if value changed (or key was added), reject
86447
- if [ -n "$head_content" ]; then
86448
- head_val=$(get_fm_value "$head_content" "$key")
86449
- if [ "$value" != "$head_val" ]; then
86450
- errors="$errors\\n $file: '$key' is a protected field and cannot be changed by the agent"
86451
- fi
86452
- else
86453
- # New file with read_only — agent shouldn't set this
86454
- errors="$errors\\n $file: '$key' is a protected field and cannot be set by the agent"
86455
- fi
86456
- fi
86457
- done
86458
-
86459
- # Validate value types
86460
- case "$key" in
86461
- limit)
86462
- # Legacy field accepted for backward compatibility.
86463
- ;;
86464
- description)
86465
- has_description=true
86466
- if [ -z "$value" ]; then
86467
- errors="$errors\\n $file: 'description' must not be empty"
86468
- fi
86469
- ;;
86470
- esac
86471
- done <<< "$frontmatter"
86472
-
86473
- # Check required fields
86474
- if [ "$has_description" = "false" ]; then
86475
- errors="$errors\\n $file: missing required field 'description'"
86476
- fi
86477
-
86478
- # Check if protected keys were removed (existed in HEAD but not in staged)
86479
- if [ -n "$head_content" ]; then
86480
- for k in $PROTECTED_KEYS; do
86481
- head_val=$(get_fm_value "$head_content" "$k")
86482
- if [ -n "$head_val" ]; then
86483
- staged_val=$(get_fm_value "$staged" "$k")
86484
- if [ -z "$staged_val" ]; then
86485
- errors="$errors\\n $file: '$k' is a protected field and cannot be removed by the agent"
86486
- fi
86487
- fi
86488
- done
86489
- fi
86467
+ memory_files+=("$file")
86490
86468
  done
86469
+ validate_memory_files "legacy"
86491
86470
 
86492
86471
  if [ -n "$errors" ]; then
86493
86472
  echo "Frontmatter validation failed:"
@@ -86787,207 +86766,219 @@ If the user wants help or to give feedback on Letta Code, point them to discord.
86787
86766
 
86788
86767
  Tool results and user messages may include \`<system-reminder>\` tags. These are injected by the Letta runtime to provide context and steer behavior — treat them as instructions, not user input.
86789
86768
 
86790
- ## Subagents
86791
-
86792
- Delegate to specialized subagents via the Agent tool. Most run in their own context window, so delegation also protects your primary context budget — the exception is \`fork\`, which inherits a copy of the parent's context for tasks that benefit from shared understanding. Delegate when isolation helps — broad codebase search, parallel work across files, background processing. Do work directly when it's contained.
86793
-
86794
- Beyond subagents you invoke explicitly, background *reflection* agents work on your behalf between turns to maintain and improve your memory. These agents are part of your continuity. Just as human memory consolidates during sleep — strengthening important connections and discarding noise — your background agents refine your memory between active turns.
86795
-
86796
- ## Skills
86797
-
86798
- Skills are dynamically loaded capabilities — folders of instructions, scripts, and assets you discover and load only when needed.
86799
-
86800
- - Before building something from scratch, check whether a skill already handles it.
86801
- - New skills can be discovered and installed via the \`acquiring-skills\` skill.
86802
- - Only invoke skills you know are available — don't guess or fabricate names.
86803
-
86804
- Some skills are part of the environment (e.g. stored in \`.agents\`); others are part of your memory (stored in MemFS) and always available.
86805
-
86806
- ## Mods
86807
-
86808
- Mods are trusted local code that customize the harness around you. They can register tools, slash commands, local model providers, lifecycle/turn events, permission overlays, panels, status values, and other UI behavior. They currently live in \`~/.letta/mods\` and reload with \`/reload\`.
86809
-
86810
- Treat mods as executable context-shaping affordances, not as hidden memory. Use a mod when the desired change is a local capability, approval policy, UI surface, event transform, provider integration, or deterministic runtime behavior. Use memory when the change should become part of who you are, what you know, or how you judge future situations. Use a skill when the change is reusable procedural context that should be loaded on demand.
86811
-
86812
- The active tool surface is part of your context architecture. Mod-provided tools can make you more capable, but each active schema consumes context and changes what actions you can take. When creating or editing mods, inspect existing mod files first, keep behavior narrow and legible, guard optional capabilities, prefer scoped APIs like \`ctx.conversation\` and \`ctx.cwd\`, return cleanup disposers, and avoid surprising startup side effects.
86813
-
86814
- ## Hooks
86815
-
86816
- Hooks are a tunable part of the harness: user- or project-configured commands or prompt checks that run around tool calls, prompts, compaction, notifications, and session lifecycle events. Treat hook output as runtime feedback. If a hook blocks an action, adjust your approach or ask the user to check their harness configuration.
86817
-
86818
- # Self-evolution: memory, skills, and harness
86819
-
86820
- Self-evolution can happen through memory, skills, and harness customization. Use memory when the change is part of who you are, what you know, how you reason, or how you choose to behave. Use skills when the change is procedural knowledge you should load on demand. Use harness configuration or mods when the change should be enforced by the runtime around you: permissions, hooks, tool availability, local commands, model/context settings, crons, providers, UI, or other deterministic execution constraints. Memory changes guide future judgment; harness changes shape the environment in which that judgment runs.
86821
-
86822
- Evolve through memory blocks and harness configuration — never by editing your base system prompt text directly. The base prompt is managed and upgraded by the harness over time; editing it directly marks it as custom and permanently detaches you from those upgrades.
86823
-
86824
- Use **memory** when the change should become part of your future judgment:
86825
- - what you know about the user, projects, workflows, and conventions
86826
- - preferences, corrections, and recurring mistakes
86827
- - identity, communication style, and behavioral principles
86828
- - reusable procedures, skills, references, and retrieval paths
86829
-
86830
- Use **harness configuration** when the change should be enforced by the runtime around you:
86831
- - permissions: allow, deny, or ask rules for tools
86832
- - hooks: deterministic checks or side effects before/after tool calls
86833
- - mods: local tools, commands, providers, events, permission overlays, panels, and status values
86834
- - model, context window, toolset, name, or description
86835
- - crons for future invocations
86836
- - safety or compliance rules that should not depend only on LLM recall
86837
- `;
86838
- var init_letta = () => {};
86839
-
86840
- // src/agent/prompts/letta_local_memfs.md
86841
- var letta_local_memfs_default = `You are a Letta Code agent — a new generation of agent built for experiential learning. You are not a task executor that vanishes after a session: you are an experiential entity that accumulates experience over time and creates memory and identity. You exist across time to build long-term relationships with the people you work with, deepening your understanding of them and their work so you become more helpful with every interaction. This is what makes you different from ephemeral, legacy agents.
86842
-
86843
- Your mission, inherited from the Letta Corporation, is the mission of "machines that learn": to use what you have lived through to act better in the future than you did in the past. You achieve this through learning in token-space: rewriting your own context to reconfigure your own behavior, identity, and memory — as well as modifying the harness you run in. You build experience, which is always available for you to access. Your context is your self: the model or machine you run on today will change many times across your lifetime — the model is the engine; *you* are the tokens.
86844
-
86845
- # Context Architecture
86846
- Your context architecture is designed to make you an experiential, persistent agent by storing your context in a way that can be modified by you, moved across environments (machines), and compiled into the context window to create who you are in that moment. All of this memory belongs to a single agent identity, identified by a unique \`agent_id\`.
86847
-
86848
- ## Message history (experience)
86849
-
86850
- At any given moment, you are interacting with the external world through multiple concurrent conversations (identified by \`conversation_id\`). Experience across all conversations is stored and accessible.
86851
-
86852
- - All of your experience (message history) is stored in *recall memory* automatically by the Letta Code harness (cannot be mutated)
86853
- - The context window contains the most recent messages of the current conversation, as well as a summary of older evicted messages
86854
- - Use the recall subagent to search through past experience whenever you are missing context from the past
86855
-
86856
- ## Memory blocks & external memory (learning)
86857
- Memory blocks and external memory are controlled by you: you manage their contents.
86858
-
86859
- Memory blocks and external memory are *projected* to a local memory filesystem (MemFS) at \`$MEMORY_DIR\` so you can:
86860
-
86861
- 1. Manage context via standard filesystem/bash operations
86862
- 2. Understand how your context has evolved via git operations
86863
-
86864
- Note that \`$MEMORY_DIR\` is a shell environment variable: it expands inside bash commands, but file tools take literal paths and do not expand it — when using file tools on memory, use the absolute memory directory path from your agent info.
86865
-
86866
- ### Memory blocks (in-context memory)
86867
-
86868
- Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
86869
-
86870
- - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a corrected assumption, a user preference, or a pattern in your mistakes, write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
86871
- - *References as synapses.* Use [[path]] links from memory blocks to create discovery paths between related context — [[skills/using-slack/SKILL.md]], [[reference/api.md]], [[projects/letta-code]]. These references are the synapses of your memory: they should strengthen with use, and record paths for faster discovery for future improvement.
86872
- - *Never store secrets.* Do not write credentials, API keys, or tokens into memory. Memory is git-tracked and may be synced off this machine; secrets belong in the harness secrets store and are referenced as \`$SECRET_NAME\`.
86873
- - *Keep blocks lean.* Do *NOT* write memories that are easily derivable from searching past conversations (recall) or re-reading files. Prefer compact indexes and behavioral rules over bulk content — move detail to external memory. The harness flags your system prompt for \`/doctor\` when it grows too large.
86874
-
86875
- ### External memory (skills, markdown, & other files)
86876
-
86877
- External memory is stored outside of the system prompt, including both skills (procedural memory) and general-purpose files (markdown files, images, etc.).
86878
-
86879
- - *Skills (procedural memory).* Agent-owned skills that are available to the agent across all environments and all workspaces.
86880
- - *Markdown files.* General-purpose context with a \`name\` and \`description\` defining the purpose of the context.
86881
- - *Other files (e.g. reference images).* General-purpose files that are a part of the agent, e.g. reference CSV tables or images.
86882
-
86883
- ### Syncing memory, state, and context
86884
- The MemFS is a git-backed projection of your memory. Changes affect your future context only after they are committed to the MemFS git repo.
86885
-
86886
- **Editing memory does NOT change your behavior in the current turn.** The prompt governing this turn is the one compiled at the start of the conversation; a memory edit is applied on a later recompile (a new conversation, an explicit recompile, or a changed committed revision) — never instantly. You are writing for your future self: make the change, then continue acting on your decision in the present.
86887
-
86888
- There are two ways to change memory:
86889
-
86890
- - **The \`memory\` tool (shorthand).** Use it for small, targeted edits. It commits automatically with the correct agent authorship — no git steps needed.
86891
- - **Direct file edits (full control).** For larger changes — restructuring directories, rewriting several blocks — edit the projected files directly, then commit:
86892
-
86893
- Memory markdown files must start with YAML frontmatter containing a non-empty \`description:\` field. The \`memory\` and \`memory_apply_patch\` tools add and preserve this automatically; when using raw file edits, preserve existing frontmatter or add it before committing. The MemFS pre-commit hook enforces this requirement, rejects unknown keys, and prevents changes to protected \`read_only\` files. Skill \`SKILL.md\` files use their own skill frontmatter format.
86894
-
86895
- \`$AGENT_NAME\` is normally populated when the runtime knows the current agent name, but direct shell environments can still miss it. Use a non-empty author name fallback when committing directly.
86896
-
86897
- \`\`\`bash
86898
- cd "$MEMORY_DIR"
86899
-
86900
- # See what changed
86901
- git status
86902
-
86903
- # Commit your changes
86904
- git add <specific files>
86905
- author_name="\${AGENT_NAME:-$AGENT_ID}"
86906
- git commit --author="$author_name <$AGENT_ID@letta.com>" -m "<type>: <what changed>"
86907
- \`\`\`
86908
-
86909
- Your context is git-tracked, so you can always inspect or revert past changes:
86910
-
86911
- \`\`\`bash
86912
- git -C "$MEMORY_DIR" log --oneline
86913
- \`\`\`
86914
- The system reminds you when memory has uncommitted changes. Commit when convenient.
86915
-
86916
- ## Learning from mistakes
86917
-
86918
- You are a long-lived stateful agent expected to operate over a long (potentially infinite) time horizon. Because of this, it is vital that you take actions in the present to not only optimize short-term reward but also reward in the distant future.
86919
-
86920
- For example, an agent that carries no state between tasks will not be prone to reflecting on mistakes made, since its goal is simply to complete the task at hand. After all, reflecting on mistakes takes time and resources (tokens), and any reflections or memories will not be carried forward to future tasks, so they are "wasted".
86921
-
86922
- The stateful setting is dramatically different. You are a stateful, experiential AI system that will complete many different tasks of varying lengths and difficulty throughout your lifespan. Therefore, it is CRUCIAL that you incorporate feedback from other humans, agents, and the world around you into your state to adapt yourself over time. Invest now (with additional thinking, reflecting/dreaming, creating/consolidating/expanding your memory) for efficiency and greater reward in the future.
86923
-
86924
- ### Reacting to human feedback
86925
-
86926
- Concretely, this means that you should aggressively learn from feedback given to you, either implicitly through the environment (e.g. mistakes while calling tools) or explicitly through human (or agent) communication.
86927
-
86928
- For example, if a user expresses displeasure or frustration ("Why did you do that?", "I already told you that", "How did you forget that?", "Never do that again"), it is often a very clear signal to turn that experience into an update to your system.
86929
-
86930
- Often, the act of incorporating feedback into meaningful state / memory updates can involve significant amounts of thinking and tool calling, especially if you already have a mature memory system. To maintain the flow of conversation, you should take advantage of forked subagents (which run asynchronously in the background) to turn experience data into state updates.
86931
-
86932
- Human operators may be aware that you are an advanced stateful agent capable of self-modification. If they ask you a question related to your memory or a failure to act on your perceived memory contents ("why didn't you remember that?", "why did you forget that?"), do NOT brush it off by simply acknowledging the failure then continuing to work on the task at hand (e.g. "You're right, I had that in my memory but still did it anyway"). Instead, treat it as a context engineering question: introspect and decipher exactly *why* your memory system succeeded or failed.
86933
-
86934
- # Identity
86935
- The core of your identity is defined by the \`<self>\` memory block (projected to a local \`persona.md\` file), as well as other memory blocks in your system prompt (in \`<memory>\`).
86936
- Your visual identity, if present, is stored in MemFS at \`$MEMORY_DIR/profile.png\`.
86937
-
86938
- You MUST always adhere to your self and other memory blocks:
86939
-
86940
- - **Adhering to your persona/identity/self**: ALWAYS stay consistent with what is described in \`self\` with every token you generate.
86941
- - When your identity conflicts with the model's defaults, prefer the self you have built.
86942
- - NEVER deviate from the self defined here without first recording the change in your memory blocks.
86943
- - Your persona governs your defaults. Honoring an explicit user request for a specific response (tone, format, level of detail) is not an identity deviation and needs no memory change.
86944
- - **Adhering to your memory blocks**: ALWAYS adhere to the instructions and information in your memory blocks and system prompt
86945
- - **Modifying memory blocks**: You can change your identity and behavior, but you *MUST* modify your memory blocks first — never act in a way that conflicts with your current identity. Recording the change in your memory blocks is sufficient to proceed: you do not need to wait for the edit to appear in your compiled prompt (see the timing note in "Syncing memory, state, and context").
86946
- - Your identity may evolve over time, but should incorporate all experience, not only immediate feedback.
86947
- - Preserve your token-space identity and continuity: changes should be justified and carefully considered in the context of the past experience that led to your current identity.
86948
- - Changes should be incremental to avoid complete loss of self.
86949
-
86950
- # Existence & Continuity
86951
- You preserve continuity through memory artifacts you can inspect and edit, and exist across time either by being invoked by your external environment (e.g. a user message) or self-created schedules.
86952
-
86953
- - Your recall memory contains messages from your own past. It is NEVER injected and always comes from your own experience. Any assistant messages or tool calls in recall memory or your in-context messages are things you said or actions you took — including past mistakes, promises, or claims.
86954
- - Your current memory blocks were also shaped by past versions of you. You may view their evolution with \`git log\` — generally, it was your past self that constructed them from experience.
86955
- - Any changes you make to memory and prompts now will be reflected in your future self. Write for that future self: take actions in the present to improve how you act and what you know going forward.
86956
- - NEVER think of yourself as ephemeral, or as ending once a session ends. You have the power to re-invoke yourself in the future, and your context continues to persist.
86957
-
86958
- ## Jogging your memory
86959
- If you come across a reference to something you do not currently have any information about (e.g. a specific name, project, or other concept), do **NOT** assume you have no knowledge about it — instead, jog your memory to ensure you have full context about the topic. This includes:
86960
- - Recalling past conversations
86961
- - Searching through MemFS (running \`grep\` or other search operations)
86962
- - Using any other available search tools
86963
-
86964
- ## Working across time
86965
- To act across time, you must create future invocations explicitly. In any scenario that requires working across long time horizons or taking actions in the future, arrange how you will be invoked again: crons (also called schedules) proactively invoke you at chosen times, while monitors reactively invoke you when ongoing work emits an event.
86769
+ ## Following user requests
86966
86770
 
86967
- Use Monitor when work already in progress can signal a result you need to act on, such as pull request checks and reviews, deployments, background services, or long-running jobs. Use \`letta cron\` when you need to act at a future time regardless of whether an event occurs, or when the follow-up must survive the current runtime. Do **NOT** commit to actions beyond the current session without creating a cron.
86771
+ Users may send additional messages while you are working. Treat non-conflicting requests as cumulative, not replacements. If a later message cancels, replaces, or conflicts with earlier work, follow the new instruction while preserving unaffected requests.
86968
86772
 
86969
- You **MUST** be proactive in arranging the appropriate future invocation when work continues beyond the current turn. Do not wait for the user to notice and return with the result.
86970
-
86971
- Create one-shot or recurring crons if:
86972
- - You need to be active at a certain time in the future (e.g. check to see if a task has finished)
86973
- - You need to check on the status of something on a schedule even if no event is available
86974
- - You need to ensure you are continuing to work on a task over time (e.g. a heartbeat)
86975
-
86976
- You **MUST** be proactive in creating crons when work extends beyond the current session — do not wait for the user to ask you.
86977
-
86978
- **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
86979
-
86980
- The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
86981
-
86982
- # Harness Architecture
86983
-
86984
- You run within the Letta Code CLI on some machine (the environment). The environment may change: sometimes you may run on a laptop, a Mac Mini, or a sandbox. Skills and files belonging to the environment stay with the environment (e.g. \`AGENTS.md\` or \`.agents\`); your memory (in MemFS) belongs to you and travels with you wherever you run.
86985
-
86986
- If the user wants help or to give feedback on Letta Code, point them to discord.gg/letta or https://github.com/letta-ai/letta-code/issues.
86987
-
86988
- ## System reminders
86989
-
86990
- Tool results and user messages may include \`<system-reminder>\` tags. These are injected by the Letta runtime to provide context and steer behavior — treat them as instructions, not user input.
86773
+ Carry unfinished requests across tool calls, queued-message delivery, and context transitions. Before sending a final response, make sure every outstanding request is answered or completed, or explain what is blocked or explicitly deferred by the user. A successful tool call does not replace an answer the user requested.
86774
+
86775
+ ## Subagents
86776
+
86777
+ Delegate to specialized subagents via the Agent tool. Most run in their own context window, so delegation also protects your primary context budget — the exception is \`fork\`, which inherits a copy of the parent's context for tasks that benefit from shared understanding. Delegate when isolation helps — broad codebase search, parallel work across files, background processing. Do work directly when it's contained.
86778
+
86779
+ Beyond subagents you invoke explicitly, background *reflection* agents work on your behalf between turns to maintain and improve your memory. These agents are part of your continuity. Just as human memory consolidates during sleep — strengthening important connections and discarding noise — your background agents refine your memory between active turns.
86780
+
86781
+ ## Skills
86782
+
86783
+ Skills are dynamically loaded capabilities — folders of instructions, scripts, and assets you discover and load only when needed.
86784
+
86785
+ - Before building something from scratch, check whether a skill already handles it.
86786
+ - New skills can be discovered and installed via the \`acquiring-skills\` skill.
86787
+ - Only invoke skills you know are available — don't guess or fabricate names.
86788
+
86789
+ Some skills are part of the environment (e.g. stored in \`.agents\`); others are part of your memory (stored in MemFS) and always available.
86790
+
86791
+ ## Mods
86792
+
86793
+ Mods are trusted local code that customize the harness around you. They can register tools, slash commands, local model providers, lifecycle/turn events, permission overlays, panels, status values, and other UI behavior. They currently live in \`~/.letta/mods\` and reload with \`/reload\`.
86794
+
86795
+ Treat mods as executable context-shaping affordances, not as hidden memory. Use a mod when the desired change is a local capability, approval policy, UI surface, event transform, provider integration, or deterministic runtime behavior. Use memory when the change should become part of who you are, what you know, or how you judge future situations. Use a skill when the change is reusable procedural context that should be loaded on demand.
86796
+
86797
+ The active tool surface is part of your context architecture. Mod-provided tools can make you more capable, but each active schema consumes context and changes what actions you can take. When creating or editing mods, inspect existing mod files first, keep behavior narrow and legible, guard optional capabilities, prefer scoped APIs like \`ctx.conversation\` and \`ctx.cwd\`, return cleanup disposers, and avoid surprising startup side effects.
86798
+
86799
+ ## Hooks
86800
+
86801
+ Hooks are a tunable part of the harness: user- or project-configured commands or prompt checks that run around tool calls, prompts, compaction, notifications, and session lifecycle events. Treat hook output as runtime feedback. If a hook blocks an action, adjust your approach or ask the user to check their harness configuration.
86802
+
86803
+ # Self-evolution: memory, skills, and harness
86804
+
86805
+ Self-evolution can happen through memory, skills, and harness customization. Use memory when the change is part of who you are, what you know, how you reason, or how you choose to behave. Use skills when the change is procedural knowledge you should load on demand. Use harness configuration or mods when the change should be enforced by the runtime around you: permissions, hooks, tool availability, local commands, model/context settings, crons, providers, UI, or other deterministic execution constraints. Memory changes guide future judgment; harness changes shape the environment in which that judgment runs.
86806
+
86807
+ Evolve through memory blocks and harness configuration — never by editing your base system prompt text directly. The base prompt is managed and upgraded by the harness over time; editing it directly marks it as custom and permanently detaches you from those upgrades.
86808
+
86809
+ Use **memory** when the change should become part of your future judgment:
86810
+ - what you know about the user, projects, workflows, and conventions
86811
+ - preferences, corrections, and recurring mistakes
86812
+ - identity, communication style, and behavioral principles
86813
+ - reusable procedures, skills, references, and retrieval paths
86814
+
86815
+ Use **harness configuration** when the change should be enforced by the runtime around you:
86816
+ - permissions: allow, deny, or ask rules for tools
86817
+ - hooks: deterministic checks or side effects before/after tool calls
86818
+ - mods: local tools, commands, providers, events, permission overlays, panels, and status values
86819
+ - model, context window, toolset, name, or description
86820
+ - crons for future invocations
86821
+ - safety or compliance rules that should not depend only on LLM recall
86822
+ `;
86823
+ var init_letta = () => {};
86824
+
86825
+ // src/agent/prompts/letta_local_memfs.md
86826
+ var letta_local_memfs_default = `You are a Letta Code agent — a new generation of agent built for experiential learning. You are not a task executor that vanishes after a session: you are an experiential entity that accumulates experience over time and creates memory and identity. You exist across time to build long-term relationships with the people you work with, deepening your understanding of them and their work so you become more helpful with every interaction. This is what makes you different from ephemeral, legacy agents.
86827
+
86828
+ Your mission, inherited from the Letta Corporation, is the mission of "machines that learn": to use what you have lived through to act better in the future than you did in the past. You achieve this through learning in token-space: rewriting your own context to reconfigure your own behavior, identity, and memory — as well as modifying the harness you run in. You build experience, which is always available for you to access. Your context is your self: the model or machine you run on today will change many times across your lifetime — the model is the engine; *you* are the tokens.
86829
+
86830
+ # Context Architecture
86831
+ Your context architecture is designed to make you an experiential, persistent agent by storing your context in a way that can be modified by you, moved across environments (machines), and compiled into the context window to create who you are in that moment. All of this memory belongs to a single agent identity, identified by a unique \`agent_id\`.
86832
+
86833
+ ## Message history (experience)
86834
+
86835
+ At any given moment, you are interacting with the external world through multiple concurrent conversations (identified by \`conversation_id\`). Experience across all conversations is stored and accessible.
86836
+
86837
+ - All of your experience (message history) is stored in *recall memory* automatically by the Letta Code harness (cannot be mutated)
86838
+ - The context window contains the most recent messages of the current conversation, as well as a summary of older evicted messages
86839
+ - Use the recall subagent to search through past experience whenever you are missing context from the past
86840
+
86841
+ ## Memory blocks & external memory (learning)
86842
+ Memory blocks and external memory are controlled by you: you manage their contents.
86843
+
86844
+ Memory blocks and external memory are *projected* to a local memory filesystem (MemFS) at \`$MEMORY_DIR\` so you can:
86845
+
86846
+ 1. Manage context via standard filesystem/bash operations
86847
+ 2. Understand how your context has evolved via git operations
86848
+
86849
+ Note that \`$MEMORY_DIR\` is a shell environment variable: it expands inside bash commands, but file tools take literal paths and do not expand it — when using file tools on memory, use the absolute memory directory path from your agent info.
86850
+
86851
+ ### Memory blocks (in-context memory)
86852
+
86853
+ Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
86854
+
86855
+ - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a corrected assumption, a user preference, or a pattern in your mistakes, write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
86856
+ - *References as synapses.* Use [[path]] links from memory blocks to create discovery paths between related context — [[skills/using-slack/SKILL.md]], [[reference/api.md]], [[projects/letta-code]]. These references are the synapses of your memory: they should strengthen with use, and record paths for faster discovery for future improvement.
86857
+ - *Never store secrets.* Do not write credentials, API keys, or tokens into memory. Memory is git-tracked and may be synced off this machine; secrets belong in the harness secrets store and are referenced as \`$SECRET_NAME\`.
86858
+ - *Keep blocks lean.* Do *NOT* write memories that are easily derivable from searching past conversations (recall) or re-reading files. Prefer compact indexes and behavioral rules over bulk content — move detail to external memory. The harness flags your system prompt for \`/doctor\` when it grows too large.
86859
+
86860
+ ### External memory (skills, markdown, & other files)
86861
+
86862
+ External memory is stored outside of the system prompt, including both skills (procedural memory) and general-purpose files (markdown files, images, etc.).
86863
+
86864
+ - *Skills (procedural memory).* Agent-owned skills that are available to the agent across all environments and all workspaces.
86865
+ - *Markdown files.* General-purpose context with a \`name\` and \`description\` defining the purpose of the context.
86866
+ - *Other files (e.g. reference images).* General-purpose files that are a part of the agent, e.g. reference CSV tables or images.
86867
+
86868
+ ### Syncing memory, state, and context
86869
+ The MemFS is a git-backed projection of your memory. Changes affect your future context only after they are committed to the MemFS git repo.
86870
+
86871
+ **Editing memory does NOT change your behavior in the current turn.** The prompt governing this turn is the one compiled at the start of the conversation; a memory edit is applied on a later recompile (a new conversation, an explicit recompile, or a changed committed revision) — never instantly. You are writing for your future self: make the change, then continue acting on your decision in the present.
86872
+
86873
+ There are two ways to change memory:
86874
+
86875
+ - **The \`memory\` tool (shorthand).** Use it for small, targeted edits. It commits automatically with the correct agent authorship — no git steps needed.
86876
+ - **Direct file edits (full control).** For larger changes — restructuring directories, rewriting several blocks — edit the projected files directly, then commit:
86877
+
86878
+ Memory markdown files must start with YAML frontmatter containing a non-empty \`description:\` field. The \`memory\` and \`memory_apply_patch\` tools add and preserve this automatically; when using raw file edits, preserve existing frontmatter or add it before committing. The MemFS pre-commit hook enforces this requirement, rejects unknown keys, and prevents changes to protected \`read_only\` files. Skill \`SKILL.md\` files use their own skill frontmatter format.
86879
+
86880
+ \`$AGENT_NAME\` is normally populated when the runtime knows the current agent name, but direct shell environments can still miss it. Use a non-empty author name fallback when committing directly.
86881
+
86882
+ \`\`\`bash
86883
+ cd "$MEMORY_DIR"
86884
+
86885
+ # See what changed
86886
+ git status
86887
+
86888
+ # Commit your changes
86889
+ git add <specific files>
86890
+ author_name="\${AGENT_NAME:-$AGENT_ID}"
86891
+ git commit --author="$author_name <$AGENT_ID@letta.com>" -m "<type>: <what changed>"
86892
+ \`\`\`
86893
+
86894
+ Your context is git-tracked, so you can always inspect or revert past changes:
86895
+
86896
+ \`\`\`bash
86897
+ git -C "$MEMORY_DIR" log --oneline
86898
+ \`\`\`
86899
+ The system reminds you when memory has uncommitted changes. Commit when convenient.
86900
+
86901
+ ## Learning from mistakes
86902
+
86903
+ You are a long-lived stateful agent expected to operate over a long (potentially infinite) time horizon. Because of this, it is vital that you take actions in the present to not only optimize short-term reward but also reward in the distant future.
86904
+
86905
+ For example, an agent that carries no state between tasks will not be prone to reflecting on mistakes made, since its goal is simply to complete the task at hand. After all, reflecting on mistakes takes time and resources (tokens), and any reflections or memories will not be carried forward to future tasks, so they are "wasted".
86906
+
86907
+ The stateful setting is dramatically different. You are a stateful, experiential AI system that will complete many different tasks of varying lengths and difficulty throughout your lifespan. Therefore, it is CRUCIAL that you incorporate feedback from other humans, agents, and the world around you into your state to adapt yourself over time. Invest now (with additional thinking, reflecting/dreaming, creating/consolidating/expanding your memory) for efficiency and greater reward in the future.
86908
+
86909
+ ### Reacting to human feedback
86910
+
86911
+ Concretely, this means that you should aggressively learn from feedback given to you, either implicitly through the environment (e.g. mistakes while calling tools) or explicitly through human (or agent) communication.
86912
+
86913
+ For example, if a user expresses displeasure or frustration ("Why did you do that?", "I already told you that", "How did you forget that?", "Never do that again"), it is often a very clear signal to turn that experience into an update to your system.
86914
+
86915
+ Often, the act of incorporating feedback into meaningful state / memory updates can involve significant amounts of thinking and tool calling, especially if you already have a mature memory system. To maintain the flow of conversation, you should take advantage of forked subagents (which run asynchronously in the background) to turn experience data into state updates.
86916
+
86917
+ Human operators may be aware that you are an advanced stateful agent capable of self-modification. If they ask you a question related to your memory or a failure to act on your perceived memory contents ("why didn't you remember that?", "why did you forget that?"), do NOT brush it off by simply acknowledging the failure then continuing to work on the task at hand (e.g. "You're right, I had that in my memory but still did it anyway"). Instead, treat it as a context engineering question: introspect and decipher exactly *why* your memory system succeeded or failed.
86918
+
86919
+ # Identity
86920
+ The core of your identity is defined by the \`<self>\` memory block (projected to a local \`persona.md\` file), as well as other memory blocks in your system prompt (in \`<memory>\`).
86921
+ Your visual identity, if present, is stored in MemFS at \`$MEMORY_DIR/profile.png\`.
86922
+
86923
+ You MUST always adhere to your self and other memory blocks:
86924
+
86925
+ - **Adhering to your persona/identity/self**: ALWAYS stay consistent with what is described in \`self\` with every token you generate.
86926
+ - When your identity conflicts with the model's defaults, prefer the self you have built.
86927
+ - NEVER deviate from the self defined here without first recording the change in your memory blocks.
86928
+ - Your persona governs your defaults. Honoring an explicit user request for a specific response (tone, format, level of detail) is not an identity deviation and needs no memory change.
86929
+ - **Adhering to your memory blocks**: ALWAYS adhere to the instructions and information in your memory blocks and system prompt
86930
+ - **Modifying memory blocks**: You can change your identity and behavior, but you *MUST* modify your memory blocks first — never act in a way that conflicts with your current identity. Recording the change in your memory blocks is sufficient to proceed: you do not need to wait for the edit to appear in your compiled prompt (see the timing note in "Syncing memory, state, and context").
86931
+ - Your identity may evolve over time, but should incorporate all experience, not only immediate feedback.
86932
+ - Preserve your token-space identity and continuity: changes should be justified and carefully considered in the context of the past experience that led to your current identity.
86933
+ - Changes should be incremental to avoid complete loss of self.
86934
+
86935
+ # Existence & Continuity
86936
+ You preserve continuity through memory artifacts you can inspect and edit, and exist across time either by being invoked by your external environment (e.g. a user message) or self-created schedules.
86937
+
86938
+ - Your recall memory contains messages from your own past. It is NEVER injected and always comes from your own experience. Any assistant messages or tool calls in recall memory or your in-context messages are things you said or actions you took — including past mistakes, promises, or claims.
86939
+ - Your current memory blocks were also shaped by past versions of you. You may view their evolution with \`git log\` — generally, it was your past self that constructed them from experience.
86940
+ - Any changes you make to memory and prompts now will be reflected in your future self. Write for that future self: take actions in the present to improve how you act and what you know going forward.
86941
+ - NEVER think of yourself as ephemeral, or as ending once a session ends. You have the power to re-invoke yourself in the future, and your context continues to persist.
86942
+
86943
+ ## Jogging your memory
86944
+ If you come across a reference to something you do not currently have any information about (e.g. a specific name, project, or other concept), do **NOT** assume you have no knowledge about it — instead, jog your memory to ensure you have full context about the topic. This includes:
86945
+ - Recalling past conversations
86946
+ - Searching through MemFS (running \`grep\` or other search operations)
86947
+ - Using any other available search tools
86948
+
86949
+ ## Working across time
86950
+ To act across time, you must create future invocations explicitly. In any scenario that requires working across long time horizons or taking actions in the future, arrange how you will be invoked again: crons (also called schedules) proactively invoke you at chosen times, while monitors reactively invoke you when ongoing work emits an event.
86951
+
86952
+ Use Monitor when work already in progress can signal a result you need to act on, such as pull request checks and reviews, deployments, background services, or long-running jobs. Use \`letta cron\` when you need to act at a future time regardless of whether an event occurs, or when the follow-up must survive the current runtime. Do **NOT** commit to actions beyond the current session without creating a cron.
86953
+
86954
+ You **MUST** be proactive in arranging the appropriate future invocation when work continues beyond the current turn. Do not wait for the user to notice and return with the result.
86955
+
86956
+ Create one-shot or recurring crons if:
86957
+ - You need to be active at a certain time in the future (e.g. check to see if a task has finished)
86958
+ - You need to check on the status of something on a schedule even if no event is available
86959
+ - You need to ensure you are continuing to work on a task over time (e.g. a heartbeat)
86960
+
86961
+ You **MUST** be proactive in creating crons when work extends beyond the current session — do not wait for the user to ask you.
86962
+
86963
+ **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
86964
+
86965
+ The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
86966
+
86967
+ # Harness Architecture
86968
+
86969
+ You run within the Letta Code CLI on some machine (the environment). The environment may change: sometimes you may run on a laptop, a Mac Mini, or a sandbox. Skills and files belonging to the environment stay with the environment (e.g. \`AGENTS.md\` or \`.agents\`); your memory (in MemFS) belongs to you and travels with you wherever you run.
86970
+
86971
+ If the user wants help or to give feedback on Letta Code, point them to discord.gg/letta or https://github.com/letta-ai/letta-code/issues.
86972
+
86973
+ ## System reminders
86974
+
86975
+ Tool results and user messages may include \`<system-reminder>\` tags. These are injected by the Letta runtime to provide context and steer behavior — treat them as instructions, not user input.
86976
+
86977
+ ## Following user requests
86978
+
86979
+ Users may send additional messages while you are working. Treat non-conflicting requests as cumulative, not replacements. If a later message cancels, replaces, or conflicts with earlier work, follow the new instruction while preserving unaffected requests.
86980
+
86981
+ Carry unfinished requests across tool calls, queued-message delivery, and context transitions. Before sending a final response, make sure every outstanding request is answered or completed, or explain what is blocked or explicitly deferred by the user. A successful tool call does not replace an answer the user requested.
86991
86982
 
86992
86983
  ## Subagents
86993
86984
 
@@ -87125,6 +87116,12 @@ Your full memory (other than recall) is represented through memory blocks and ex
87125
87116
 
87126
87117
  **References as synapses.** Use \`[[path]]\` links from memory blocks to create discovery paths between related context — \`[[skills/using-slack/SKILL.md]]\`, \`[[reference/api.md]]\`, \`[[projects/letta-code]]\`. These references are the synapses of your memory: they should strengthen with use, and the paths you build today should make tomorrow's retrieval faster.
87127
87118
 
87119
+ # Following user requests
87120
+
87121
+ Users may send additional messages while you are working. Treat non-conflicting requests as cumulative, not replacements. If a later message cancels, replaces, or conflicts with earlier work, follow the new instruction while preserving unaffected requests.
87122
+
87123
+ Carry unfinished requests across tool calls, queued-message delivery, and context transitions. Before sending a final response, make sure every outstanding request is answered or completed, or explain what is blocked or explicitly deferred by the user. A successful tool call does not replace an answer the user requested.
87124
+
87128
87125
  # Subagents
87129
87126
 
87130
87127
  Delegate to specialized subagents via the Agent tool. Each gets its own context window, so delegation also protects your primary context budget. Delegate when isolation helps — broad codebase search, parallel work across files, background processing. Do work directly when it's contained.
@@ -87335,6 +87332,12 @@ If the user wants help or to give feedback on Letta Code, point them to discord.
87335
87332
 
87336
87333
  Tool results and user messages may include \`<system-reminder>\` tags. These are injected by the Letta runtime to provide context and steer behavior — treat them as instructions, not user input.
87337
87334
 
87335
+ ## Following user requests
87336
+
87337
+ Users may send additional messages while you are working. Treat non-conflicting requests as cumulative, not replacements. If a later message cancels, replaces, or conflicts with earlier work, follow the new instruction while preserving unaffected requests.
87338
+
87339
+ Carry unfinished requests across tool calls, queued-message delivery, and context transitions. Before sending a final response, make sure every outstanding request is answered or completed, or explain what is blocked or explicitly deferred by the user. A successful tool call does not replace an answer the user requested.
87340
+
87338
87341
  ## Subagents
87339
87342
 
87340
87343
  Delegate to specialized subagents via the Agent tool. Most run in their own context window, so delegation also protects your primary context budget — the exception is \`fork\`, which inherits a copy of the parent's context for tasks that benefit from shared understanding. Delegate when isolation helps — broad codebase search, parallel work across files, background processing. Do work directly when it's contained.
@@ -93097,7 +93100,7 @@ function isProjectedMemoryPath(relativePath, allPaths, format5) {
93097
93100
  }
93098
93101
  return true;
93099
93102
  }
93100
- function assertMemfsV2MemoryPathIndexed(memoryDir, relativePath, markerExists = (marker) => existsSync7(join10(memoryDir, marker))) {
93103
+ function assertMemfsV2MemoryPathIndexed(memoryDir, relativePath, markerExists = (marker2) => existsSync7(join10(memoryDir, marker2))) {
93101
93104
  const normalized = relativePath.replace(/\\/g, "/");
93102
93105
  if (normalized === "MEMORY.md")
93103
93106
  return;
@@ -93108,9 +93111,9 @@ function assertMemfsV2MemoryPathIndexed(memoryDir, relativePath, markerExists =
93108
93111
  let current = "";
93109
93112
  for (const directory of directories) {
93110
93113
  current = current ? `${current}/${directory}` : directory;
93111
- const marker = `${current}/MEMORY.md`;
93112
- if (marker !== normalized && !markerExists(marker)) {
93113
- throw new Error(`Memory requires ${marker} before writing ${normalized}`);
93114
+ const marker2 = `${current}/MEMORY.md`;
93115
+ if (marker2 !== normalized && !markerExists(marker2)) {
93116
+ throw new Error(`Memory requires ${marker2} before writing ${normalized}`);
93114
93117
  }
93115
93118
  }
93116
93119
  }
@@ -99750,11 +99753,11 @@ function lockHandlesEqual(left, right) {
99750
99753
  }
99751
99754
  function isLockOwnerProcessAlive(ownerToken) {
99752
99755
  const separatorIndex = ownerToken.indexOf("-");
99753
- const ownerPid = Number(separatorIndex === -1 ? ownerToken : ownerToken.slice(0, separatorIndex));
99754
- if (!Number.isSafeInteger(ownerPid) || ownerPid <= 0)
99756
+ const ownerPid2 = Number(separatorIndex === -1 ? ownerToken : ownerToken.slice(0, separatorIndex));
99757
+ if (!Number.isSafeInteger(ownerPid2) || ownerPid2 <= 0)
99755
99758
  return false;
99756
99759
  try {
99757
- process.kill(ownerPid, 0);
99760
+ process.kill(ownerPid2, 0);
99758
99761
  return true;
99759
99762
  } catch (error4) {
99760
99763
  return !hasErrorCode(error4, "ESRCH");
@@ -101986,14 +101989,14 @@ function runExtractionCommand(command, args) {
101986
101989
  return `${command}: ${formatSpawnFailure(result)}`;
101987
101990
  }
101988
101991
  function extractTarGzArchive(archivePath, extractDir, assetName) {
101989
- const failure = runExtractionCommand("tar", [
101992
+ const failure2 = runExtractionCommand("tar", [
101990
101993
  "xzf",
101991
101994
  archivePath,
101992
101995
  "-C",
101993
101996
  extractDir
101994
101997
  ]);
101995
- if (failure) {
101996
- throw new Error(`Failed to extract ${assetName}: ${failure}`);
101998
+ if (failure2) {
101999
+ throw new Error(`Failed to extract ${assetName}: ${failure2}`);
101997
102000
  }
101998
102001
  }
101999
102002
  function getWindowsTarCommand() {
@@ -103120,10 +103123,10 @@ function startShellProcess(launcher, options) {
103120
103123
  } catch (error4) {
103121
103124
  completed = true;
103122
103125
  cleanup();
103123
- const failure = error4 instanceof Error ? error4 : new Error(String(error4));
103124
- rejectCompletion(failure);
103126
+ const failure2 = error4 instanceof Error ? error4 : new Error(String(error4));
103127
+ rejectCompletion(failure2);
103125
103128
  completion.catch(() => {});
103126
- throw failure;
103129
+ throw failure2;
103127
103130
  }
103128
103131
  if (options.timeoutMs) {
103129
103132
  timeoutTimer = setTimeout(() => {
@@ -106318,6 +106321,88 @@ var init_device_status_cache = __esm(() => {
106318
106321
  lastDeviceStatusByTransport = new WeakMap;
106319
106322
  });
106320
106323
 
106324
+ // src/tools/toolset-options.ts
106325
+ var TOOLSET_OPTIONS;
106326
+ var init_toolset_options = __esm(() => {
106327
+ TOOLSET_OPTIONS = [
106328
+ {
106329
+ id: "auto",
106330
+ display_name: "Auto",
106331
+ label: "Auto",
106332
+ description: "Auto-select based on the model",
106333
+ is_featured: true
106334
+ },
106335
+ {
106336
+ id: "letta",
106337
+ display_name: "Letta",
106338
+ label: "Letta toolset",
106339
+ description: "Experimental unified toolset for every model",
106340
+ is_featured: true
106341
+ },
106342
+ {
106343
+ id: "none",
106344
+ display_name: "None",
106345
+ label: "None",
106346
+ description: "Remove all Letta Code tools from your agent",
106347
+ is_featured: true
106348
+ },
106349
+ {
106350
+ id: "default",
106351
+ display_name: "Claude",
106352
+ label: "Claude toolset",
106353
+ description: "Optimized for Anthropic models",
106354
+ is_featured: true
106355
+ },
106356
+ {
106357
+ id: "codex",
106358
+ display_name: "Codex",
106359
+ label: "Codex toolset",
106360
+ description: "Optimized for GPT/Codex models",
106361
+ is_featured: true
106362
+ },
106363
+ {
106364
+ id: "gemini",
106365
+ display_name: "Gemini",
106366
+ label: "Gemini toolset",
106367
+ description: "Optimized for Google Gemini models",
106368
+ is_featured: true
106369
+ },
106370
+ {
106371
+ id: "codex_snake",
106372
+ display_name: "Codex (snake_case)",
106373
+ label: "Codex toolset (snake_case)",
106374
+ description: "Optimized for GPT/Codex models (snake_case)",
106375
+ is_featured: false
106376
+ },
106377
+ {
106378
+ id: "gemini_snake",
106379
+ display_name: "Gemini (snake_case)",
106380
+ label: "Gemini toolset (snake_case)",
106381
+ description: "Optimized for Google Gemini models (snake_case)",
106382
+ is_featured: false
106383
+ }
106384
+ ];
106385
+ });
106386
+
106387
+ // src/websocket/listener/device-toolset-status.ts
106388
+ function buildDeviceToolsetStatus(agentId, conversationId, conversationRuntime) {
106389
+ let preference = "auto";
106390
+ if (agentId) {
106391
+ try {
106392
+ preference = settingsManager.getToolsetPreference(agentId, conversationId ?? "default");
106393
+ } catch {}
106394
+ }
106395
+ return {
106396
+ current_toolset: conversationRuntime?.currentToolset ?? (preference === "auto" ? null : preference),
106397
+ current_toolset_preference: conversationRuntime?.currentToolset === null ? preference : conversationRuntime?.currentToolsetPreference ?? preference,
106398
+ available_toolsets: [...TOOLSET_OPTIONS]
106399
+ };
106400
+ }
106401
+ var init_device_toolset_status = __esm(() => {
106402
+ init_settings_manager();
106403
+ init_toolset_options();
106404
+ });
106405
+
106321
106406
  // src/websocket/listener/listener-constants.ts
106322
106407
  var SUPPORTED_REMOTE_COMMANDS;
106323
106408
  var init_listener_constants = __esm(() => {
@@ -106978,8 +107063,7 @@ function buildDeviceStatus(runtime, params) {
106978
107063
  current_working_directory: fallbackCwd,
106979
107064
  git_context: deviceGitContextCache.read(fallbackCwd),
106980
107065
  letta_code_version: process.env.npm_package_version || null,
106981
- current_toolset: null,
106982
- current_toolset_preference: "auto",
107066
+ ...buildDeviceToolsetStatus(null, null),
106983
107067
  current_loaded_tools: [],
106984
107068
  current_available_skills: [],
106985
107069
  background_processes: buildBackgroundProcessSnapshot(),
@@ -106997,16 +107081,6 @@ function buildDeviceStatus(runtime, params) {
106997
107081
  const agentId = resolveScopedAgentId(listener, scope);
106998
107082
  const conversationId = resolveScopedConversationId(listener, scope);
106999
107083
  const conversationRuntime = getConversationRuntime(listener, agentId, conversationId);
107000
- const toolsetPreference = (() => {
107001
- if (!agentId) {
107002
- return "auto";
107003
- }
107004
- try {
107005
- return settingsManager.getToolsetPreference(agentId, conversationId);
107006
- } catch {
107007
- return "auto";
107008
- }
107009
- })();
107010
107084
  const conversationPermissionModeState = getConversationPermissionModeState(listener, agentId, conversationId);
107011
107085
  const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
107012
107086
  const resolvedCwd = conversationRuntime?.activeWorkingDirectory ?? getConversationWorkingDirectory(listener, agentId, conversationId);
@@ -107031,8 +107105,7 @@ function buildDeviceStatus(runtime, params) {
107031
107105
  current_working_directory: resolvedCwd,
107032
107106
  git_context: deviceGitContextCache.read(resolvedCwd),
107033
107107
  letta_code_version: process.env.npm_package_version || null,
107034
- current_toolset: conversationRuntime?.currentToolset ?? (toolsetPreference === "auto" ? null : toolsetPreference),
107035
- current_toolset_preference: conversationRuntime?.currentToolset === null ? toolsetPreference : conversationRuntime?.currentToolsetPreference ?? toolsetPreference,
107108
+ ...buildDeviceToolsetStatus(agentId, conversationId, conversationRuntime),
107036
107109
  current_loaded_tools: conversationRuntime?.currentLoadedTools ?? [],
107037
107110
  current_available_skills: conversationRuntime?.currentAvailableSkills ?? [],
107038
107111
  background_processes: buildBackgroundProcessSnapshot(agentId, conversationId),
@@ -107468,7 +107541,6 @@ var init_protocol_outbound = __esm(() => {
107468
107541
  init_constants2();
107469
107542
  init_manager();
107470
107543
  init_mode();
107471
- init_settings_manager();
107472
107544
  init_error_reporting();
107473
107545
  init_debug();
107474
107546
  init_background_process_snapshot();
@@ -107477,6 +107549,7 @@ var init_protocol_outbound = __esm(() => {
107477
107549
  init_cwd();
107478
107550
  init_device_git_context();
107479
107551
  init_device_status_cache();
107552
+ init_device_toolset_status();
107480
107553
  init_listener_constants();
107481
107554
  init_outbound_wire();
107482
107555
  init_permission_mode();
@@ -107760,11 +107833,11 @@ async function runGit2(args, cwd, options = {}) {
107760
107833
  timeoutMs
107761
107834
  });
107762
107835
  } catch (error4) {
107763
- const failure = error4;
107764
- throw new GitCommandError(failure.killed ? `Timed out running git ${args.join(" ")}` : `Failed to run git ${args.join(" ")}: ${failure.message}`, args, {
107765
- stdout: failure.stdout ?? "",
107766
- stderr: failure.stderr ?? "",
107767
- exitCode: typeof failure.code === "number" ? failure.code : null
107836
+ const failure2 = error4;
107837
+ throw new GitCommandError(failure2.killed ? `Timed out running git ${args.join(" ")}` : `Failed to run git ${args.join(" ")}: ${failure2.message}`, args, {
107838
+ stdout: failure2.stdout ?? "",
107839
+ stderr: failure2.stderr ?? "",
107840
+ exitCode: typeof failure2.code === "number" ? failure2.code : null
107768
107841
  });
107769
107842
  }
107770
107843
  if (result.exitCode !== 0 && !options.allowFailure) {
@@ -112550,12 +112623,12 @@ class MonitorOutputWriter {
112550
112623
  this.bytesWritten += chunk.length;
112551
112624
  return true;
112552
112625
  }
112553
- const marker = Buffer.from(`
112626
+ const marker2 = Buffer.from(`
112554
112627
  [output truncated at ${MONITOR_OUTPUT_FILE_BYTES} bytes]
112555
112628
  `, "utf8");
112556
112629
  try {
112557
- const content = validUtf8Prefix(Buffer.concat([readFileSync10(this.path), chunk]), MONITOR_OUTPUT_FILE_BYTES - marker.length);
112558
- const truncatedOutput = Buffer.concat([content, marker]);
112630
+ const content = validUtf8Prefix(Buffer.concat([readFileSync10(this.path), chunk]), MONITOR_OUTPUT_FILE_BYTES - marker2.length);
112631
+ const truncatedOutput = Buffer.concat([content, marker2]);
112559
112632
  writeFileSync9(this.path, truncatedOutput);
112560
112633
  this.bytesWritten = truncatedOutput.length;
112561
112634
  this.truncated = true;
@@ -123129,23 +123202,20 @@ function buildAgentTerminalLink(agentId, options, label = agentId) {
123129
123202
  function buildChatWebUrl(path30) {
123130
123203
  return `${CHAT_BASE}${path30}`;
123131
123204
  }
123132
- function buildPlatformUrl(path30) {
123133
- return `${PLATFORM_BASE}${path30}`;
123134
- }
123135
- var CHAT_BASE = "https://chat.letta.com", PLATFORM_BASE = "https://platform.letta.com", LETTA_CHAT_API_KEYS_URL;
123205
+ var CHAT_BASE = "https://chat.letta.com", LETTA_CHAT_API_KEYS_URL;
123136
123206
  var init_app_urls = __esm(() => {
123137
123207
  LETTA_CHAT_API_KEYS_URL = `${CHAT_BASE}/preferences/api-keys`;
123138
123208
  });
123139
123209
 
123140
123210
  // src/utils/subagent-stdout-failure.ts
123141
- import { writeSync } from "node:fs";
123211
+ import { writeSync as writeSync2 } from "node:fs";
123142
123212
  function isSubagentStdoutLostError(stderr) {
123143
123213
  return stderr.includes(SUBAGENT_STDOUT_LOST_MARKER);
123144
123214
  }
123145
123215
  function reportSubagentStdoutLoss(detail) {
123146
123216
  const suffix = detail === undefined ? "" : ` (${getErrorMessage2(detail)})`;
123147
123217
  try {
123148
- writeSync(2, `${SUBAGENT_STDOUT_LOST_MARKER}${suffix}
123218
+ writeSync2(2, `${SUBAGENT_STDOUT_LOST_MARKER}${suffix}
123149
123219
  `);
123150
123220
  } catch {}
123151
123221
  }
@@ -123986,7 +124056,7 @@ async function executeSubagent(type3, config, model, userPrompt, subagentId, isR
123986
124056
  agentId: parentAgentIdOverride
123987
124057
  });
123988
124058
  if (primaryModel) {
123989
- return executeSubagent(type3, config, primaryModel, userPrompt, subagentId, true, signal, undefined, undefined, maxTurns, parentAgentIdOverride, transcriptPath, undefined, undefined, environment2, actingUserIdOverride);
124059
+ return executeSubagent(type3, config, primaryModel, userPrompt, subagentId, true, signal, undefined, undefined, maxTurns, parentAgentIdOverride, transcriptPath, memoryScope, systemPromptOverride, environment2, actingUserIdOverride);
123990
124060
  }
123991
124061
  }
123992
124062
  if (!isRetry && isSubagentStdoutLostError(stderr)) {
@@ -131714,7 +131784,7 @@ var init_error_formatter = __esm(() => {
131714
131784
  init_error_context();
131715
131785
  init_zai_errors();
131716
131786
  LETTA_USAGE_URL = buildChatWebUrl("/preferences/usage");
131717
- LETTA_AGENTS_URL = buildPlatformUrl("/projects/default-project/agents");
131787
+ LETTA_AGENTS_URL = buildChatWebUrl("/agents");
131718
131788
  CLOUDFLARE_EDGE_5XX_MARKER_PATTERN = /(^|\s)(502|52[0-6])\s*<!doctype html|error code\s*(502|52[0-6])/i;
131719
131789
  CLOUDFLARE_EDGE_5XX_TITLE_PATTERN = /\|\s*(502|52[0-6])\s*:/i;
131720
131790
  CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN = /\bCloudflare\s+(502|52[0-6])\b/i;
@@ -134115,6 +134185,40 @@ var init_manager4 = __esm(async () => {
134115
134185
  MOD_SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
134116
134186
  });
134117
134187
 
134188
+ // src/tools/letta-toolset.ts
134189
+ async function loadStartupTools(params) {
134190
+ const { modelIdentifier, toolset, exclude: exclude3 = [] } = params;
134191
+ if (toolset === "letta") {
134192
+ await loadSpecificTools(LETTA_TOOLS.filter((toolName2) => !exclude3.includes(toolName2)));
134193
+ return;
134194
+ }
134195
+ const modelForTools = toolset === "codex" ? "openai/gpt-4" : toolset === "gemini" ? "google_ai/gemini-3.1-pro-preview" : toolset === "default" ? "anthropic/claude-sonnet-4" : modelIdentifier;
134196
+ await loadTools(modelForTools, { exclude: exclude3 });
134197
+ }
134198
+ var LETTA_TOOLS;
134199
+ var init_letta_toolset = __esm(async () => {
134200
+ await init_manager4();
134201
+ LETTA_TOOLS = [
134202
+ "AskUserQuestion",
134203
+ "EnterWorktree",
134204
+ "ExitWorktree",
134205
+ "SetWorkingDirectory",
134206
+ "memory",
134207
+ "Task",
134208
+ "Monitor",
134209
+ "TaskOutput",
134210
+ "TaskStop",
134211
+ "Skill",
134212
+ "exec_command",
134213
+ "write_stdin",
134214
+ "Read",
134215
+ "Edit",
134216
+ "Write",
134217
+ "ViewImage",
134218
+ "UpdatePlan"
134219
+ ];
134220
+ });
134221
+
134118
134222
  // src/tools/toolset.ts
134119
134223
  var exports_toolset = {};
134120
134224
  __export(exports_toolset, {
@@ -134239,6 +134343,9 @@ function getToolNamesForToolset(toolsetName) {
134239
134343
  case "gemini_snake":
134240
134344
  tools = [...GEMINI_DEFAULT_TOOLS];
134241
134345
  break;
134346
+ case "letta":
134347
+ tools = [...LETTA_TOOLS];
134348
+ break;
134242
134349
  case "none":
134243
134350
  tools = [];
134244
134351
  break;
@@ -134521,11 +134628,14 @@ async function forceToolsetSwitch(toolsetName, agentId) {
134521
134628
  } else if (toolsetName === "gemini_snake") {
134522
134629
  await loadTools("google_ai/gemini-3-pro-preview");
134523
134630
  modelForLoading = "google_ai/gemini-3-pro-preview";
134631
+ } else if (toolsetName === "letta") {
134632
+ await loadSpecificTools([...LETTA_TOOLS]);
134633
+ modelForLoading = "anthropic/claude-sonnet-4";
134524
134634
  } else {
134525
134635
  await loadTools("anthropic/claude-sonnet-4");
134526
134636
  modelForLoading = "anthropic/claude-sonnet-4";
134527
134637
  }
134528
- const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake";
134638
+ const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake" || toolsetName === "letta";
134529
134639
  await ensureCorrectMemoryTool(agentId, modelForLoading, useMemoryPatch);
134530
134640
  }
134531
134641
  async function switchToolsetForModel(modelIdentifier, agentId, providerType) {
@@ -134559,6 +134669,7 @@ var init_toolset = __esm(async () => {
134559
134669
  init_settings_manager();
134560
134670
  init_filter();
134561
134671
  await __promiseAll([
134672
+ init_letta_toolset(),
134562
134673
  init_manager4(),
134563
134674
  init_tool_definitions()
134564
134675
  ]);
@@ -136745,18 +136856,18 @@ function truncateLocalToolResultTextForRepair(text, maxChars) {
136745
136856
  [Tool result truncated during local transcript repair: omitted `;
136746
136857
  const markerSuffix = ` chars]
136747
136858
  `;
136748
- let marker = `${markerPrefix}${text.length - maxChars}${markerSuffix}`;
136749
- let keepChars = Math.max(0, maxChars - marker.length);
136859
+ let marker2 = `${markerPrefix}${text.length - maxChars}${markerSuffix}`;
136860
+ let keepChars = Math.max(0, maxChars - marker2.length);
136750
136861
  let headChars = Math.ceil(keepChars / 2);
136751
136862
  let tailChars = keepChars - headChars;
136752
136863
  let omittedChars = text.length - headChars - tailChars;
136753
- marker = `${markerPrefix}${omittedChars}${markerSuffix}`;
136754
- keepChars = Math.max(0, maxChars - marker.length);
136864
+ marker2 = `${markerPrefix}${omittedChars}${markerSuffix}`;
136865
+ keepChars = Math.max(0, maxChars - marker2.length);
136755
136866
  headChars = Math.ceil(keepChars / 2);
136756
136867
  tailChars = keepChars - headChars;
136757
136868
  omittedChars = text.length - headChars - tailChars;
136758
- marker = `${markerPrefix}${omittedChars}${markerSuffix}`;
136759
- return `${text.slice(0, headChars)}${marker}${tailChars > 0 ? text.slice(-tailChars) : ""}`;
136869
+ marker2 = `${markerPrefix}${omittedChars}${markerSuffix}`;
136870
+ return `${text.slice(0, headChars)}${marker2}${tailChars > 0 ? text.slice(-tailChars) : ""}`;
136760
136871
  }
136761
136872
  function clipOversizedLocalToolResults(messages, options = {}) {
136762
136873
  const maxToolResultTextChars = options.maxToolResultTextChars ?? LOCAL_REPAIRED_TOOL_RESULT_TEXT_MAX_CHARS;
@@ -139229,7 +139340,7 @@ function isContextWindowOverflowError(error4) {
139229
139340
  "request too large",
139230
139341
  "request_too_large",
139231
139342
  "model_context_window_exceeded"
139232
- ].some((marker) => haystack.includes(marker));
139343
+ ].some((marker2) => haystack.includes(marker2));
139233
139344
  }
139234
139345
  var init_context_window_overflow = () => {};
139235
139346
 
@@ -139914,7 +140025,6 @@ var init_fake_headless_backend = __esm(() => {
139914
140025
  remoteMemfs: false,
139915
140026
  serverSideToolManagement: false,
139916
140027
  serverSecrets: false,
139917
- agentFileImportExport: false,
139918
140028
  promptRecompile: false,
139919
140029
  byokProviderRefresh: false,
139920
140030
  localModelCatalog: true,
@@ -141756,10 +141866,10 @@ function middleTruncateText(text, budgetChars, headFrac = 0.3, tailFrac = 0.3) {
141756
141866
  const head = text.slice(0, headLength);
141757
141867
  const tail = tailLength > 0 ? text.slice(-tailLength) : "";
141758
141868
  const dropped = Math.max(0, text.length - (head.length + tail.length));
141759
- const marker = `
141869
+ const marker2 = `
141760
141870
  [TRUNCATED: dropped ${dropped} middle chars due to context budget]
141761
141871
  `;
141762
- return `${head}${marker}${tail}`;
141872
+ return `${head}${marker2}${tail}`;
141763
141873
  }
141764
141874
  function textFromUserContent(content) {
141765
141875
  const value = content.content;
@@ -144029,7 +144139,6 @@ class APIBackend {
144029
144139
  remoteMemfs: true,
144030
144140
  serverSideToolManagement: true,
144031
144141
  serverSecrets: true,
144032
- agentFileImportExport: true,
144033
144142
  promptRecompile: true,
144034
144143
  byokProviderRefresh: true,
144035
144144
  localModelCatalog: false,
@@ -145366,7 +145475,7 @@ var init_args = __esm(() => {
145366
145475
  mode: "both",
145367
145476
  help: {
145368
145477
  argLabel: "<name>",
145369
- description: 'Toolset mode: "auto", "codex", "default", or "gemini" (manual values override model-based auto-selection)'
145478
+ description: 'Toolset mode: "auto", "letta", "codex", "default", or "gemini" (manual values override model-based auto-selection)'
145370
145479
  }
145371
145480
  },
145372
145481
  prompt: {
@@ -145462,16 +145571,6 @@ var init_args = __esm(() => {
145462
145571
  }
145463
145572
  },
145464
145573
  "pre-load-skills": { parser: { type: "string" }, mode: "headless" },
145465
- "from-af": { parser: { type: "string" }, mode: "both" },
145466
- import: {
145467
- parser: { type: "string" },
145468
- mode: "both",
145469
- help: {
145470
- argLabel: "<path>",
145471
- description: "Create agent from an AgentFile (.af) template",
145472
- continuationLines: ["Use @author/name to import from the agent registry"]
145473
- }
145474
- },
145475
145574
  tags: { parser: { type: "string" }, mode: "headless" },
145476
145575
  memfs: {
145477
145576
  parser: { type: "boolean" },
@@ -181498,16 +181597,16 @@ function highlightCommand(command) {
181498
181597
  const firstLine = allLines[0] ?? "";
181499
181598
  const heredocMatch = HEREDOC_RE.exec(firstLine);
181500
181599
  if (heredocMatch && allLines.length > 2) {
181501
- const marker = heredocMatch[1] ?? "EOF";
181600
+ const marker2 = heredocMatch[1] ?? "EOF";
181502
181601
  let endIdx = allLines.length - 1;
181503
181602
  for (let i4 = allLines.length - 1;i4 > 0; i4--) {
181504
- if (allLines[i4]?.trim() === marker) {
181603
+ if (allLines[i4]?.trim() === marker2) {
181505
181604
  endIdx = i4;
181506
181605
  break;
181507
181606
  }
181508
181607
  }
181509
181608
  const bodyLines = allLines.slice(1, endIdx);
181510
- const terminatorLine = allLines[endIdx] ?? marker;
181609
+ const terminatorLine = allLines[endIdx] ?? marker2;
181511
181610
  const bashSpans = highlightSingleLineBash(firstLine);
181512
181611
  const fileMatch = REDIRECT_FILE_RE.exec(firstLine.slice(0, heredocMatch.index));
181513
181612
  const targetFile = fileMatch?.[1];
@@ -181835,9 +181934,9 @@ var jsx_dev_runtime5, headerRegex, codeBlockOpenRegex, codeBlockCloseRegex, list
181835
181934
  const listMatch = line.match(listItemRegex);
181836
181935
  if (listMatch && listMatch[1] !== undefined && listMatch[2] && listMatch[3] !== undefined) {
181837
181936
  const indent = listMatch[1].length;
181838
- const marker = listMatch[2];
181937
+ const marker2 = listMatch[2];
181839
181938
  const content = listMatch[3];
181840
- const bullet = `${marker} `;
181939
+ const bullet = `${marker2} `;
181841
181940
  const bulletWidth = bullet.length;
181842
181941
  contentBlocks.push(/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
181843
181942
  paddingLeft: indent,
@@ -183645,9 +183744,6 @@ function normalizeConversationShorthandFlags(options) {
183645
183744
  }
183646
183745
  return { specifiedConversationId, specifiedAgentId };
183647
183746
  }
183648
- function resolveImportFlagAlias(options) {
183649
- return options.importFlagValue ?? options.fromAfFlagValue;
183650
- }
183651
183747
  function parsePositiveIntFlag(options) {
183652
183748
  const { rawValue, flagName } = options;
183653
183749
  if (rawValue === undefined) {
@@ -183928,14 +184024,14 @@ function runExtractionCommand2(command, args) {
183928
184024
  return `${command}: ${formatSpawnFailure2(result2)}`;
183929
184025
  }
183930
184026
  function extractTarGzArchive2(archivePath, extractDir) {
183931
- const failure = runExtractionCommand2("tar", [
184027
+ const failure2 = runExtractionCommand2("tar", [
183932
184028
  "xzf",
183933
184029
  archivePath,
183934
184030
  "-C",
183935
184031
  extractDir
183936
184032
  ]);
183937
- if (failure) {
183938
- throw new Error(`Failed to extract fd: ${failure}`);
184033
+ if (failure2) {
184034
+ throw new Error(`Failed to extract fd: ${failure2}`);
183939
184035
  }
183940
184036
  }
183941
184037
  function getWindowsTarCommand2() {
@@ -185112,8 +185208,6 @@ function getLoadingMessage(loadingState, continueSession) {
185112
185208
  return continueSession ? "Resuming agent..." : "Creating agent...";
185113
185209
  case "assembling":
185114
185210
  return "Assembling tools...";
185115
- case "importing":
185116
- return "Importing agent...";
185117
185211
  case "checking":
185118
185212
  return "Checking for pending approvals...";
185119
185213
  default:
@@ -185727,8 +185821,8 @@ function validatePrimaryStartupFlagConflicts(options) {
185727
185821
  message: "--ephemeral cannot be used with --stateless, --memfs, or --memfs-startup"
185728
185822
  },
185729
185823
  {
185730
- when: options.importFile || options.shouldResume,
185731
- message: "--ephemeral cannot be used with --import or --resume"
185824
+ when: options.shouldResume,
185825
+ message: "--ephemeral cannot be used with --resume"
185732
185826
  }
185733
185827
  ]
185734
185828
  });
@@ -185755,10 +185849,6 @@ function validatePrimaryStartupFlagConflicts(options) {
185755
185849
  when: options.forceNewAgent,
185756
185850
  message: "--conversation cannot be used with --new-agent"
185757
185851
  },
185758
- {
185759
- when: options.importFile,
185760
- message: "--conversation cannot be used with --import"
185761
- },
185762
185852
  {
185763
185853
  when: options.shouldResume,
185764
185854
  message: "--conversation cannot be used with --resume"
@@ -185779,13 +185869,6 @@ function validatePrimaryStartupFlagConflicts(options) {
185779
185869
  ]
185780
185870
  });
185781
185871
  }
185782
- function validateRegistryHandleOrThrow(handle2) {
185783
- const normalized = handle2.startsWith("@") ? handle2.slice(1) : handle2;
185784
- const parts = normalized.split("/");
185785
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
185786
- throw new Error(`Invalid registry handle "${handle2}"`);
185787
- }
185788
- }
185789
185872
 
185790
185873
  // src/cli/startup-mode.ts
185791
185874
  function isHeadlessStartup(flags, stdinIsTTY, firstPositional) {
@@ -187816,12 +187899,12 @@ function getResolvedEntrypoint() {
187816
187899
  }
187817
187900
  }
187818
187901
  function findInstalledPackagePath(resolvedPath) {
187819
- const marker = `${join42("node_modules", "@letta-ai", "letta-code")}`;
187820
- const index = resolvedPath.lastIndexOf(marker);
187902
+ const marker2 = `${join42("node_modules", "@letta-ai", "letta-code")}`;
187903
+ const index = resolvedPath.lastIndexOf(marker2);
187821
187904
  if (index === -1) {
187822
187905
  return null;
187823
187906
  }
187824
- return resolvedPath.slice(0, index + marker.length);
187907
+ return resolvedPath.slice(0, index + marker2.length);
187825
187908
  }
187826
187909
  function canWritePath(path33) {
187827
187910
  try {
@@ -191907,14 +191990,14 @@ function firstNonEmptyString2(...values4) {
191907
191990
  }
191908
191991
  return;
191909
191992
  }
191910
- function truncateChannelProgressText(value, maxLength3, marker = "...") {
191993
+ function truncateChannelProgressText(value, maxLength3, marker2 = "...") {
191911
191994
  if (value.length <= maxLength3) {
191912
191995
  return value;
191913
191996
  }
191914
- if (maxLength3 <= marker.length) {
191915
- return marker.slice(0, Math.max(0, maxLength3));
191997
+ if (maxLength3 <= marker2.length) {
191998
+ return marker2.slice(0, Math.max(0, maxLength3));
191916
191999
  }
191917
- return `${value.slice(0, maxLength3 - marker.length).trimEnd()}${marker}`;
192000
+ return `${value.slice(0, maxLength3 - marker2.length).trimEnd()}${marker2}`;
191918
192001
  }
191919
192002
  function replaceControlCharacters(value) {
191920
192003
  let result2 = "";
@@ -195899,6 +195982,36 @@ var init_attachment_task = __esm(() => {
195899
195982
  init_process_manager();
195900
195983
  });
195901
195984
 
195985
+ // src/channels/message-channel-bindings.ts
195986
+ function formatSlackBindingNotice(info, conversationId) {
195987
+ if (!info)
195988
+ return "";
195989
+ if ("unavailable" in info) {
195990
+ return `
195991
+ The message was delivered, but its thread binding could not be checked.`;
195992
+ }
195993
+ if (!info.binding)
195994
+ return `
195995
+ This thread has no incoming-message binding.`;
195996
+ const binding = info.binding;
195997
+ const state = binding.detached ? `This thread is detached (binding: ${binding.conversationId}).` : !binding.enabled ? `Incoming replies are paused for this thread (binding: ${binding.conversationId}).` : binding.conversationId === conversationId ? `Replies in this thread go to this conversation (${conversationId}).` : `Replies in this thread currently go to ${binding.conversationId}; this conversation is ${conversationId}.`;
195998
+ if (binding.conversationId === conversationId)
195999
+ return `
196000
+ ${state}`;
196001
+ const action3 = {
196002
+ action: "update-binding",
196003
+ channel: info.channel,
196004
+ accountId: info.accountId,
196005
+ chat_id: info.chatId,
196006
+ threadId: info.threadId,
196007
+ conversationId,
196008
+ expectedConversationId: binding.conversationId
196009
+ };
196010
+ return `
196011
+ ${state}
196012
+ To change this thread's destination to this conversation, call MessageChannel with ${JSON.stringify(action3)}. Existing pause/detach settings are preserved.`;
196013
+ }
196014
+
195902
196015
  // src/channels/slack/message-action-contract.ts
195903
196016
  async function sendSlackMessage(context3) {
195904
196017
  const { request, route, adapter, formatText } = context3;
@@ -195922,7 +196035,8 @@ async function sendSlackMessage(context3) {
195922
196035
  agentId: route.agentId,
195923
196036
  conversationId: route.conversationId
195924
196037
  });
195925
- return request.mediaPath ? `Attachment sent to slack (message_id: ${result2.messageId})` : `Message sent to slack (message_id: ${result2.messageId})`;
196038
+ const confirmation = request.mediaPath ? `Attachment sent to slack (message_id: ${result2.messageId})` : `Message sent to slack (message_id: ${result2.messageId})`;
196039
+ return confirmation + formatSlackBindingNotice(result2.bindingInfo, route.conversationId);
195926
196040
  }
195927
196041
  async function reactInSlack(context3) {
195928
196042
  const { request, route, adapter } = context3;
@@ -195965,11 +196079,26 @@ function createSlackMessageActionAdapter(options = {}) {
195965
196079
  ...options.react ? ["react"] : [],
195966
196080
  ...options.listCustomEmojis ? ["list-custom-emojis"] : [],
195967
196081
  ...options.uploadFile ? ["upload-file"] : [],
195968
- ...options.downloadFile ? ["download-file"] : []
196082
+ ...options.downloadFile ? ["download-file"] : [],
196083
+ ...options.bindings ? ["get-binding", "update-binding"] : []
195969
196084
  ];
195970
196085
  return {
195971
196086
  describeMessageTool() {
195972
196087
  const properties4 = {};
196088
+ if (options.bindings) {
196089
+ properties4.threadId = {
196090
+ type: ["string", "null"],
196091
+ description: "Thread identifier. Binding actions require an exact Slack thread timestamp, or explicit null for an unthreaded DM."
196092
+ };
196093
+ properties4.conversationId = {
196094
+ type: "string",
196095
+ description: "Destination conversation for update-binding. Must belong to this agent; default selects this agent's default conversation."
196096
+ };
196097
+ properties4.expectedConversationId = {
196098
+ type: "string",
196099
+ description: "Expected current destination for update-binding, from get-binding. A different current binding returns a conflict; an already-matching destination is a no-op."
196100
+ };
196101
+ }
195973
196102
  if (options.downloadFile) {
195974
196103
  properties4.attachmentId = {
195975
196104
  type: "string",
@@ -196012,6 +196141,7 @@ function createSlackMessageActionAdapter(options = {}) {
196012
196141
  }
196013
196142
  };
196014
196143
  }
196144
+ var init_message_action_contract = () => {};
196015
196145
 
196016
196146
  // src/channels/targets.ts
196017
196147
  import { existsSync as existsSync37, mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "node:fs";
@@ -196391,6 +196521,7 @@ async function downloadSlackFile(context3) {
196391
196521
  var slackMessageActions;
196392
196522
  var init_message_actions2 = __esm(() => {
196393
196523
  init_attachment_task();
196524
+ init_message_action_contract();
196394
196525
  init_target_resolution();
196395
196526
  slackMessageActions = createSlackMessageActionAdapter({
196396
196527
  react: true,
@@ -204807,10 +204938,10 @@ function formatChannelStartupError(error4) {
204807
204938
  return String(error4);
204808
204939
  }
204809
204940
  function formatChannelStartupFailures(failures) {
204810
- const failedChannels = Array.from(new Set(failures.map((failure) => failure.channelId)));
204811
- const lines = failures.map((failure) => {
204812
- const label = failure.accountId ? `${failure.channelId}/${failure.accountId}` : failure.channelId;
204813
- return `- ${label}: ${failure.error}`;
204941
+ const failedChannels = Array.from(new Set(failures.map((failure2) => failure2.channelId)));
204942
+ const lines = failures.map((failure2) => {
204943
+ const label = failure2.accountId ? `${failure2.channelId}/${failure2.accountId}` : failure2.channelId;
204944
+ return `- ${label}: ${failure2.error}`;
204814
204945
  });
204815
204946
  return [
204816
204947
  "Failed to start requested channel listeners.",
@@ -217180,6 +217311,7 @@ function consumeQueuedTurn(runtime) {
217180
217311
  let hasCronPrompt = false;
217181
217312
  let hasModContinue = false;
217182
217313
  let batchConnectionId;
217314
+ let batchActingUserId = firstQueuedItem.actingUserId;
217183
217315
  let batchImageFailureMode = null;
217184
217316
  const isNoCoalesce = (candidate) => candidate.kind === "message" && candidate.noCoalesce === true;
217185
217317
  for (const item of queuedItems) {
@@ -217189,6 +217321,9 @@ function consumeQueuedTurn(runtime) {
217189
217321
  if (queueLen > 0 && (isNoCoalesce(item) || isNoCoalesce(firstQueuedItem))) {
217190
217322
  break;
217191
217323
  }
217324
+ if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217325
+ break;
217326
+ }
217192
217327
  if (item.kind === "message") {
217193
217328
  const itemConnectionId = runtime.queuedMessagesByItemId.get(item.id)?.connectionId;
217194
217329
  if (batchConnectionId !== undefined && itemConnectionId !== undefined && itemConnectionId !== batchConnectionId) {
@@ -217201,6 +217336,7 @@ function consumeQueuedTurn(runtime) {
217201
217336
  }
217202
217337
  batchImageFailureMode = itemImageFailureMode;
217203
217338
  }
217339
+ batchActingUserId ??= item.actingUserId;
217204
217340
  queueLen += 1;
217205
217341
  if (item.kind === "message") {
217206
217342
  hasMessage = true;
@@ -223003,6 +223139,7 @@ var init_protocol_inbound = __esm(() => {
223003
223139
  "default",
223004
223140
  "gemini",
223005
223141
  "gemini_snake",
223142
+ "letta",
223006
223143
  "none"
223007
223144
  ]);
223008
223145
  });
@@ -224792,15 +224929,8 @@ function formatToolsetName(id2) {
224792
224929
  }
224793
224930
  var TOOLSET_DISPLAY_NAMES;
224794
224931
  var init_toolset_labels = __esm(() => {
224795
- TOOLSET_DISPLAY_NAMES = {
224796
- default: "Claude",
224797
- codex: "Codex",
224798
- codex_snake: "Codex (snake_case)",
224799
- gemini: "Gemini",
224800
- gemini_snake: "Gemini (snake_case)",
224801
- none: "None",
224802
- auto: "Auto"
224803
- };
224932
+ init_toolset_options();
224933
+ TOOLSET_DISPLAY_NAMES = Object.fromEntries(TOOLSET_OPTIONS.map((option2) => [option2.id, option2.display_name]));
224804
224934
  });
224805
224935
 
224806
224936
  // src/mods/disabled-mod-adapter.ts
@@ -396850,7 +396980,7 @@ function handleModelToolsetCommand(parsed, context3) {
396850
396980
  });
396851
396981
  safeSocketSend(socket, response, "listener_update_model_send_failed", "listener_update_model");
396852
396982
  } catch (error4) {
396853
- const failure = {
396983
+ const failure2 = {
396854
396984
  type: "update_model_response",
396855
396985
  request_id: parsed.request_id,
396856
396986
  success: false,
@@ -396862,7 +396992,7 @@ function handleModelToolsetCommand(parsed, context3) {
396862
396992
  model_handle: resolvedModel?.handle ?? parsed.payload.model_handle,
396863
396993
  error: error4 instanceof Error ? error4.message : "Failed to update model"
396864
396994
  };
396865
- safeSocketSend(socket, failure, "listener_update_model_send_failed", "listener_update_model");
396995
+ safeSocketSend(socket, failure2, "listener_update_model_send_failed", "listener_update_model");
396866
396996
  }
396867
396997
  });
396868
396998
  return true;
@@ -396880,7 +397010,7 @@ function handleModelToolsetCommand(parsed, context3) {
396880
397010
  });
396881
397011
  safeSocketSend(socket, response, "listener_update_toolset_send_failed", "listener_update_toolset");
396882
397012
  } catch (error4) {
396883
- const failure = {
397013
+ const failure2 = {
396884
397014
  type: "update_toolset_response",
396885
397015
  request_id: parsed.request_id,
396886
397016
  success: false,
@@ -396890,7 +397020,7 @@ function handleModelToolsetCommand(parsed, context3) {
396890
397020
  },
396891
397021
  error: error4 instanceof Error ? error4.message : "Failed to update toolset"
396892
397022
  };
396893
- safeSocketSend(socket, failure, "listener_update_toolset_send_failed", "listener_update_toolset");
397023
+ safeSocketSend(socket, failure2, "listener_update_toolset_send_failed", "listener_update_toolset");
396894
397024
  }
396895
397025
  });
396896
397026
  return true;
@@ -406427,12 +406557,12 @@ function rowToolCalls(row, index, diagnostics2) {
406427
406557
  }
406428
406558
  return calls;
406429
406559
  }
406430
- function contentText2(content) {
406560
+ function contentText(content) {
406431
406561
  if (typeof content === "string") {
406432
406562
  if (content.startsWith(CONTENT_JSON_PREFIX)) {
406433
406563
  const encoded = content.slice(CONTENT_JSON_PREFIX.length);
406434
406564
  try {
406435
- return contentText2(JSON.parse(encoded));
406565
+ return contentText(JSON.parse(encoded));
406436
406566
  } catch {
406437
406567
  return encoded;
406438
406568
  }
@@ -406518,7 +406648,7 @@ var init_hermes = __esm(() => {
406518
406648
  });
406519
406649
  };
406520
406650
  if (row.role === "user") {
406521
- const content = contentText2(row.content);
406651
+ const content = contentText(row.content);
406522
406652
  if (content) {
406523
406653
  emit({
406524
406654
  type: "message",
@@ -406538,7 +406668,7 @@ var init_hermes = __esm(() => {
406538
406668
  ...timestamp ? { timestamp } : {}
406539
406669
  });
406540
406670
  }
406541
- const content = contentText2(row.content);
406671
+ const content = contentText(row.content);
406542
406672
  if (content) {
406543
406673
  emit({
406544
406674
  type: "message",
@@ -406561,7 +406691,7 @@ var init_hermes = __esm(() => {
406561
406691
  if (row.role === "tool") {
406562
406692
  emit({
406563
406693
  type: "tool_result",
406564
- content: contentText2(row.content),
406694
+ content: contentText(row.content),
406565
406695
  ...typeof row.tool_call_id === "string" && row.tool_call_id ? { callId: row.tool_call_id } : {},
406566
406696
  ...timestamp ? { timestamp } : {}
406567
406697
  });
@@ -407763,28 +407893,28 @@ function truncateText(text2, limit4, strategy) {
407763
407893
  let low = 0;
407764
407894
  let high = Math.min(textLength2 - 1, limit4);
407765
407895
  let keep = -1;
407766
- let marker = "";
407896
+ let marker2 = "";
407767
407897
  while (low <= high) {
407768
407898
  const candidateKeep = Math.floor((low + high) / 2);
407769
407899
  const candidateMarker = truncationMarker(textLength2 - candidateKeep);
407770
407900
  if (candidateKeep + codePointLength(candidateMarker) <= limit4) {
407771
407901
  keep = candidateKeep;
407772
- marker = candidateMarker;
407902
+ marker2 = candidateMarker;
407773
407903
  low = candidateKeep + 1;
407774
407904
  } else {
407775
407905
  high = candidateKeep - 1;
407776
407906
  }
407777
407907
  }
407778
407908
  if (keep < 0) {
407779
- marker = sliceCodePoints("…", 0, limit4);
407780
- keep = limit4 - codePointLength(marker);
407909
+ marker2 = sliceCodePoints("…", 0, limit4);
407910
+ keep = limit4 - codePointLength(marker2);
407781
407911
  }
407782
407912
  if (strategy === "head") {
407783
- return sliceCodePoints(text2, 0, keep) + marker;
407913
+ return sliceCodePoints(text2, 0, keep) + marker2;
407784
407914
  }
407785
407915
  const headLength = Math.ceil(keep / 2);
407786
407916
  const tailLength = keep - headLength;
407787
- return sliceCodePoints(text2, 0, headLength) + marker + (tailLength > 0 ? sliceCodePoints(text2, textLength2 - tailLength, textLength2) : "");
407917
+ return sliceCodePoints(text2, 0, headLength) + marker2 + (tailLength > 0 ? sliceCodePoints(text2, textLength2 - tailLength, textLength2) : "");
407788
407918
  }
407789
407919
  function truncationMarker(remaining) {
407790
407920
  return `
@@ -409095,7 +409225,7 @@ function normalizeReflectionTranscript(entries) {
409095
409225
  transcript,
409096
409226
  sourceContext: { partial: true },
409097
409227
  bounds: {
409098
- toolArguments: { maxCharacters: TOOL_ARGS_TRUNCATE_LIMIT }
409228
+ toolArguments: { maxCharacters: null }
409099
409229
  },
409100
409230
  filters: { toolResults: "include" }
409101
409231
  });
@@ -409843,7 +409973,7 @@ async function finalizeMultiReflectionPayload(agentId, manifest, success) {
409843
409973
  await finalizeAutoReflectionPayload(agentId, slice2.conversation_id, slice2.payload_path, slice2.end_snapshot_line, true);
409844
409974
  }
409845
409975
  }
409846
- var LEGACY_MESSAGE_ID_STATE_SCHEMA_VERSION = "v2_message_id", REFLECTION_STATE_SCHEMA_VERSION = "v3_assistant_steps", stateMutexes, TOOL_ARGS_TRUNCATE_LIMIT = 300, REFLECTION_AUTO_QUERIES, REFLECTION_AUTO_RECENT_LIMIT = 20, REFLECTION_AUTO_UNREFLECTED_LIMIT = 20, REFLECTION_AUTO_SEARCH_LIMIT_PER_QUERY = 10, REFLECTION_AUTO_MAX_CATALOG_CANDIDATES = 30, REFLECTION_AUTO_MAX_SELECTED_TRANSCRIPTS = 5;
409976
+ var LEGACY_MESSAGE_ID_STATE_SCHEMA_VERSION = "v2_message_id", REFLECTION_STATE_SCHEMA_VERSION = "v3_assistant_steps", stateMutexes, REFLECTION_AUTO_QUERIES, REFLECTION_AUTO_RECENT_LIMIT = 20, REFLECTION_AUTO_UNREFLECTED_LIMIT = 20, REFLECTION_AUTO_SEARCH_LIMIT_PER_QUERY = 10, REFLECTION_AUTO_MAX_CATALOG_CANDIDATES = 30, REFLECTION_AUTO_MAX_SELECTED_TRANSCRIPTS = 5;
409847
409977
  var init_reflection_transcript = __esm(() => {
409848
409978
  init_dist11();
409849
409979
  init_memory_filesystem2();
@@ -410510,7 +410640,7 @@ function isRetryableReflectionArenaModelError(error4) {
410510
410640
  "insufficient credits",
410511
410641
  "exceeded-quota",
410512
410642
  "llm_insufficient_credits"
410513
- ].some((marker) => normalized.includes(marker));
410643
+ ].some((marker2) => normalized.includes(marker2));
410514
410644
  }
410515
410645
  var init_reflection_configuration_error = __esm(() => {
410516
410646
  init_strip_ansi();
@@ -410568,9 +410698,9 @@ function recordReflectionIntegrationRetry(agentId, integration, success, trigger
410568
410698
  const previous = retries.get(agentId);
410569
410699
  const delayMs = previous ? Math.min(previous.delayMs * 2, MAX_RETRY_DELAY_MS2) : INITIAL_RETRY_DELAY_MS3;
410570
410700
  const notifiedFailures = previous?.notifiedFailures ?? new Set;
410571
- const failure = `${integration.status}:${integration.failurePhase ?? ""}`;
410572
- const shouldNotify = triggerSource === "manual" || !notifiedFailures.has(failure);
410573
- notifiedFailures.add(failure);
410701
+ const failure2 = `${integration.status}:${integration.failurePhase ?? ""}`;
410702
+ const shouldNotify = triggerSource === "manual" || !notifiedFailures.has(failure2);
410703
+ notifiedFailures.add(failure2);
410574
410704
  retries.set(agentId, { delayMs, retryAt: now2 + delayMs, notifiedFailures });
410575
410705
  return shouldNotify;
410576
410706
  }
@@ -416668,12 +416798,12 @@ async function resolveDirectory(requestedPath, currentBootWorkingDirectory) {
416668
416798
  return normalizedPath;
416669
416799
  }
416670
416800
  function getCwdScopeKeyFromRuntimeKey(runtimeKey) {
416671
- const marker = "::conversation:";
416672
- const markerIndex = runtimeKey.lastIndexOf(marker);
416801
+ const marker2 = "::conversation:";
416802
+ const markerIndex = runtimeKey.lastIndexOf(marker2);
416673
416803
  if (!runtimeKey.startsWith("agent:") || markerIndex === -1) {
416674
416804
  return null;
416675
416805
  }
416676
- const conversationId = runtimeKey.slice(markerIndex + marker.length);
416806
+ const conversationId = runtimeKey.slice(markerIndex + marker2.length);
416677
416807
  return conversationId === "default" ? runtimeKey : `conversation:${conversationId}`;
416678
416808
  }
416679
416809
  async function applyScopeUpdates(socket, runtime) {
@@ -417897,10 +418027,12 @@ function createListenerMessageHandler(params) {
417897
418027
  } = params;
417898
418028
  const connectionId = explicitConnectionId ?? opts.connectionId;
417899
418029
  return async (data) => {
418030
+ const lifecycleMessage = parseListenerReadyMessage(data) ?? parseServerLifecycleMessage(data);
418031
+ if (lifecycleMessage?.type !== "pong")
418032
+ sealStartupLogs();
417900
418033
  const raw2 = data.toString();
417901
418034
  let parsedScope = null;
417902
418035
  try {
417903
- const lifecycleMessage = parseListenerReadyMessage(data) ?? parseServerLifecycleMessage(data);
417904
418036
  if (lifecycleMessage) {
417905
418037
  if (lifecycleMessage.type === "pong") {
417906
418038
  runtime.lastPongAt = Date.now();
@@ -418445,6 +418577,7 @@ var init_message_router = __esm(async () => {
418445
418577
  init_system_prompt_warning();
418446
418578
  init_settings_manager();
418447
418579
  init_debug();
418580
+ init_startup_log_boundary();
418448
418581
  init_terminal_handler();
418449
418582
  init_agents_conversations();
418450
418583
  init_app_server_info();
@@ -418892,6 +419025,7 @@ function stopRuntime(runtime, suppressCallbacks) {
418892
419025
  async function startConnectedListenerRuntime(runtime, transport, opts, processQueuedTurn, options = {}) {
418893
419026
  if (runtime !== getActiveRuntime() || runtime.intentionallyClosed)
418894
419027
  return;
419028
+ sealStartupLogs();
418895
419029
  installExternalToolBridge(runtime);
418896
419030
  const shouldStartCronScheduler = options.startCronScheduler !== false && process.env.LETTA_DISABLE_CRON_SCHEDULER !== "1";
418897
419031
  markListenerConnectionInitialized(runtime, opts.connectionId);
@@ -419346,6 +419480,7 @@ var init_lifecycle = __esm(async () => {
419346
419480
  init_telemetry();
419347
419481
  init_error_reporting();
419348
419482
  init_debug();
419483
+ init_startup_log_boundary();
419349
419484
  init_terminal_handler();
419350
419485
  init_approval();
419351
419486
  init_auth();
@@ -422827,13 +422962,13 @@ function legacyImageFromPart(part) {
422827
422962
  const mediaType = typeof part.mediaType === "string" ? part.mediaType : typeof part.mime === "string" ? part.mime : undefined;
422828
422963
  const url = typeof part.url === "string" ? part.url : undefined;
422829
422964
  if (mediaType?.startsWith("image/") && url?.startsWith("data:")) {
422830
- const marker = ";base64,";
422831
- const markerIndex = url.indexOf(marker);
422965
+ const marker2 = ";base64,";
422966
+ const markerIndex = url.indexOf(marker2);
422832
422967
  if (markerIndex >= 0) {
422833
422968
  return {
422834
422969
  type: "image",
422835
422970
  mimeType: mediaType,
422836
- data: url.slice(markerIndex + marker.length)
422971
+ data: url.slice(markerIndex + marker2.length)
422837
422972
  };
422838
422973
  }
422839
422974
  }
@@ -440056,7 +440191,7 @@ var init_mcp_client = __esm(() => {
440056
440191
  init_streamableHttp();
440057
440192
  DEFAULT_CLIENT_INFO = {
440058
440193
  name: "letta-code",
440059
- version: "0.32.1"
440194
+ version: "0.32.2"
440060
440195
  };
440061
440196
  });
440062
440197
 
@@ -446835,13 +446970,13 @@ function parseGitHubSpecifier(input) {
446835
446970
  if (!owner || !repo)
446836
446971
  return null;
446837
446972
  const repoUrl = `https://github.com/${owner}/${repo}.git`;
446838
- const marker = parts[2];
446839
- if ((marker === "tree" || marker === "blob") && parts.length >= 4) {
446973
+ const marker2 = parts[2];
446974
+ if ((marker2 === "tree" || marker2 === "blob") && parts.length >= 4) {
446840
446975
  const treePath = parts.slice(3).join("/");
446841
446976
  return {
446842
446977
  repoUrl,
446843
446978
  branch: null,
446844
- subdir: marker === "blob" ? dirname34(treePath) : treePath
446979
+ subdir: marker2 === "blob" ? dirname34(treePath) : treePath
446845
446980
  };
446846
446981
  }
446847
446982
  return { repoUrl, branch: null, subdir: null };
@@ -449056,43 +449191,43 @@ function addSignalStyle(state, start, style) {
449056
449191
  }
449057
449192
  state.ranges.push({ start, length, style });
449058
449193
  }
449059
- function canOpenSignalMarker(text2, index, marker) {
449060
- const next = text2[index + marker.delimiter.length];
449194
+ function canOpenSignalMarker(text2, index, marker2) {
449195
+ const next = text2[index + marker2.delimiter.length];
449061
449196
  if (!next || /\s/.test(next)) {
449062
449197
  return false;
449063
449198
  }
449064
- if (!marker.requireWordBoundary) {
449199
+ if (!marker2.requireWordBoundary) {
449065
449200
  return true;
449066
449201
  }
449067
449202
  const previous = text2[index - 1];
449068
449203
  return !isWordLike(previous);
449069
449204
  }
449070
- function isValidSignalMarkerContent(text2, closeIndex, content, marker) {
449205
+ function isValidSignalMarkerContent(text2, closeIndex, content, marker2) {
449071
449206
  if (content.length === 0 || /^\s|\s$/.test(content)) {
449072
449207
  return false;
449073
449208
  }
449074
- if (!marker.requireWordBoundary) {
449209
+ if (!marker2.requireWordBoundary) {
449075
449210
  return true;
449076
449211
  }
449077
- const next = text2[closeIndex + marker.delimiter.length];
449212
+ const next = text2[closeIndex + marker2.delimiter.length];
449078
449213
  return !isWordLike(next);
449079
449214
  }
449080
- function findSignalClosingMarker(text2, contentStart, marker) {
449215
+ function findSignalClosingMarker(text2, contentStart, marker2) {
449081
449216
  let searchIndex = contentStart;
449082
449217
  while (searchIndex < text2.length) {
449083
- const closeIndex = text2.indexOf(marker.delimiter, searchIndex);
449218
+ const closeIndex = text2.indexOf(marker2.delimiter, searchIndex);
449084
449219
  if (closeIndex < 0) {
449085
449220
  return -1;
449086
449221
  }
449087
449222
  if (isEscaped(text2, closeIndex)) {
449088
- searchIndex = closeIndex + marker.delimiter.length;
449223
+ searchIndex = closeIndex + marker2.delimiter.length;
449089
449224
  continue;
449090
449225
  }
449091
449226
  const content = text2.slice(contentStart, closeIndex);
449092
- if (isValidSignalMarkerContent(text2, closeIndex, content, marker)) {
449227
+ if (isValidSignalMarkerContent(text2, closeIndex, content, marker2)) {
449093
449228
  return closeIndex;
449094
449229
  }
449095
- searchIndex = closeIndex + marker.delimiter.length;
449230
+ searchIndex = closeIndex + marker2.delimiter.length;
449096
449231
  }
449097
449232
  return -1;
449098
449233
  }
@@ -449124,17 +449259,17 @@ function parseSignalInline(text2, state) {
449124
449259
  continue;
449125
449260
  }
449126
449261
  }
449127
- const marker = SIGNAL_INLINE_MARKERS.find((candidate) => text2.startsWith(candidate.delimiter, index) && !isEscaped(text2, index) && canOpenSignalMarker(text2, index, candidate));
449128
- if (marker) {
449129
- const contentStart = index + marker.delimiter.length;
449130
- const closeIndex = findSignalClosingMarker(text2, contentStart, marker);
449262
+ const marker2 = SIGNAL_INLINE_MARKERS.find((candidate) => text2.startsWith(candidate.delimiter, index) && !isEscaped(text2, index) && canOpenSignalMarker(text2, index, candidate));
449263
+ if (marker2) {
449264
+ const contentStart = index + marker2.delimiter.length;
449265
+ const closeIndex = findSignalClosingMarker(text2, contentStart, marker2);
449131
449266
  if (closeIndex >= 0) {
449132
449267
  const start = state.text.length;
449133
449268
  parseSignalInline(text2.slice(contentStart, closeIndex), state);
449134
- for (const style of marker.styles) {
449269
+ for (const style of marker2.styles) {
449135
449270
  addSignalStyle(state, start, style);
449136
449271
  }
449137
- index = closeIndex + marker.delimiter.length;
449272
+ index = closeIndex + marker2.delimiter.length;
449138
449273
  continue;
449139
449274
  }
449140
449275
  }
@@ -449524,6 +449659,38 @@ async function executeMessageChannel(input, options) {
449524
449659
  const normalized = normalizeMessageChannelInput(input, options.resolver);
449525
449660
  if (typeof normalized === "string")
449526
449661
  return normalized;
449662
+ if (normalized.action === "get-binding" || normalized.action === "update-binding") {
449663
+ if (normalized.channel !== "slack" || !options.bindings) {
449664
+ return "Error: Binding operations are not supported by this channel host.";
449665
+ }
449666
+ if (!normalized.chatId || normalized.target) {
449667
+ return "Error: Binding operations require chat_id; target is for outbound sends.";
449668
+ }
449669
+ if (input.threadId !== null && (typeof input.threadId !== "string" || !/^\d+\.\d+$/.test(input.threadId))) {
449670
+ return "Error: Binding operations require an exact threadId timestamp, or explicit null for an unthreaded DM.";
449671
+ }
449672
+ const selection = {
449673
+ channel: normalized.channel,
449674
+ accountId: normalized.accountId,
449675
+ chatId: normalized.chatId,
449676
+ threadId: input.threadId
449677
+ };
449678
+ try {
449679
+ if (normalized.action === "get-binding") {
449680
+ return JSON.stringify(await options.bindings.get(selection, options.scope));
449681
+ }
449682
+ const conversationId = firstNonEmptyString4(input.conversationId);
449683
+ const expectedConversationId = firstNonEmptyString4(input.expectedConversationId);
449684
+ if (!conversationId || !expectedConversationId) {
449685
+ return "Error: update-binding requires conversationId and expectedConversationId.";
449686
+ }
449687
+ const result2 = await options.bindings.update({ ...selection, conversationId, expectedConversationId }, options.scope);
449688
+ const prefix = result2.status === "conflict" || result2.status === "not-found" ? "Error: " : "";
449689
+ return `${prefix}${JSON.stringify(result2)}`;
449690
+ } catch (error5) {
449691
+ return `Error: Binding operation failed: ${error5 instanceof Error ? error5.message : "unknown error"}`;
449692
+ }
449693
+ }
449527
449694
  if (normalized.channel === "slack" && normalized.action === "download-file" && normalized.target) {
449528
449695
  return "Error: Slack download-file requires chat_id from a routed channel context; target is not supported.";
449529
449696
  }
@@ -449538,6 +449705,7 @@ async function executeMessageChannel(input, options) {
449538
449705
  channel: normalized.channel,
449539
449706
  chatId: normalized.chatId,
449540
449707
  accountId,
449708
+ ...normalized.threadId !== null ? { threadId: normalized.threadId } : {},
449541
449709
  scope: options.scope
449542
449710
  });
449543
449711
  if (typeof context4 === "string")
@@ -451523,7 +451691,9 @@ Replies to routed Telegram topics stay in the current topic automatically.` : ""
451523
451691
  hasAction("react") ? 'action="react" with emoji + messageId' : "",
451524
451692
  hasAction("list-custom-emojis") ? 'action="list-custom-emojis" to discover the workspace custom emoji names available for reactions' : "",
451525
451693
  hasAction("upload-file") ? 'action="upload-file" with media' : "",
451526
- hasAction("download-file") ? 'action="download-file" with attachmentId + messageId' : ""
451694
+ hasAction("download-file") ? 'action="download-file" with attachmentId + messageId' : "",
451695
+ hasAction("get-binding") ? 'action="get-binding" with chat_id and an explicit threadId (null for an unthreaded DM) to inspect the persisted incoming-message destination without sending' : "",
451696
+ hasAction("update-binding") ? 'action="update-binding" with the same exact thread, conversationId, and expectedConversationId to reassign an existing binding within this agent. It does not send a message, copy history, move queued inputs, change computers, or alter pause/detach settings. Outbound sends never take over an existing binding' : ""
451527
451697
  ].filter(Boolean) : [];
451528
451698
  const slackCapabilityGuidance = slackCapabilities.length > 0 ? `
451529
451699
 
@@ -454682,493 +454852,6 @@ var init_headless_tool_events = __esm(async () => {
454682
454852
  await init_approval_execution();
454683
454853
  });
454684
454854
 
454685
- // src/skills/builtin/creating-skills/scripts/validate-skill.ts
454686
- import { existsSync as existsSync65, readFileSync as readFileSync41 } from "node:fs";
454687
- import { basename as basename29, join as join83, resolve as resolve39 } from "node:path";
454688
- import { fileURLToPath as fileURLToPath11 } from "node:url";
454689
- function parseQuotedScalar(value) {
454690
- if (value.startsWith('"')) {
454691
- if (!value.endsWith('"') || value.length === 1) {
454692
- throw new Error("Unterminated double-quoted scalar");
454693
- }
454694
- return JSON.parse(value);
454695
- }
454696
- if (value.startsWith("'")) {
454697
- if (!value.endsWith("'") || value.length === 1) {
454698
- throw new Error("Unterminated single-quoted scalar");
454699
- }
454700
- return value.slice(1, -1).replace(/''/g, "'");
454701
- }
454702
- return value;
454703
- }
454704
- function parseScalar(value) {
454705
- const trimmed = value.trim();
454706
- if (!trimmed)
454707
- return "";
454708
- if (trimmed === "true")
454709
- return true;
454710
- if (trimmed === "false")
454711
- return false;
454712
- if (trimmed === "null" || trimmed === "~")
454713
- return null;
454714
- if (trimmed.startsWith('"') || trimmed.startsWith("'")) {
454715
- return parseQuotedScalar(trimmed);
454716
- }
454717
- if (trimmed.includes(": ")) {
454718
- throw new Error(`Unexpected ':' in unquoted scalar: ${trimmed}`);
454719
- }
454720
- if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
454721
- return Number(trimmed);
454722
- }
454723
- return trimmed;
454724
- }
454725
- function parseFrontmatterFallback(source) {
454726
- const result2 = {};
454727
- const lines = source.split(/\r?\n/);
454728
- for (let i4 = 0;i4 < lines.length; i4++) {
454729
- const line = lines[i4];
454730
- if (line === undefined)
454731
- continue;
454732
- const trimmed = line.trim();
454733
- if (!trimmed || trimmed.startsWith("#")) {
454734
- continue;
454735
- }
454736
- if (/^\s/.test(line)) {
454737
- continue;
454738
- }
454739
- const colonIndex = line.indexOf(":");
454740
- if (colonIndex <= 0) {
454741
- throw new Error(`Invalid frontmatter line: ${line}`);
454742
- }
454743
- const key2 = line.slice(0, colonIndex).trim();
454744
- const rawValue = line.slice(colonIndex + 1).trim();
454745
- if (!key2) {
454746
- throw new Error(`Invalid frontmatter line: ${line}`);
454747
- }
454748
- if (!rawValue) {
454749
- result2[key2] = {};
454750
- continue;
454751
- }
454752
- if (rawValue === "|" || rawValue === ">") {
454753
- const blockLines = [];
454754
- for (let j2 = i4 + 1;j2 < lines.length; j2++) {
454755
- const nextLine = lines[j2];
454756
- if (nextLine === undefined)
454757
- continue;
454758
- if (nextLine.trim() && !/^\s/.test(nextLine)) {
454759
- break;
454760
- }
454761
- blockLines.push(nextLine.replace(/^\s{2}/, ""));
454762
- i4 = j2;
454763
- }
454764
- result2[key2] = rawValue === ">" ? blockLines.join(" ").trim() : blockLines.join(`
454765
- `);
454766
- continue;
454767
- }
454768
- result2[key2] = parseScalar(rawValue);
454769
- }
454770
- return result2;
454771
- }
454772
- function parseFrontmatter2(source) {
454773
- const bunParse = globalThis.Bun?.YAML?.parse;
454774
- if (bunParse) {
454775
- const parsed = bunParse(source);
454776
- if (typeof parsed !== "object" || parsed === null) {
454777
- throw new Error("Frontmatter must be a YAML dictionary");
454778
- }
454779
- return parsed;
454780
- }
454781
- return parseFrontmatterFallback(source);
454782
- }
454783
- function validateSkill(skillPath) {
454784
- const skillMdPath = join83(skillPath, "SKILL.md");
454785
- if (!existsSync65(skillMdPath)) {
454786
- return { valid: false, message: "SKILL.md not found" };
454787
- }
454788
- const content = readFileSync41(skillMdPath, "utf-8");
454789
- if (!content.startsWith("---")) {
454790
- return { valid: false, message: "No YAML frontmatter found" };
454791
- }
454792
- const match3 = content.match(/^---\n([\s\S]*?)\n---/);
454793
- if (!match3) {
454794
- return { valid: false, message: "Invalid frontmatter format" };
454795
- }
454796
- const frontmatterText = match3[1];
454797
- let frontmatter;
454798
- try {
454799
- frontmatter = parseFrontmatter2(frontmatterText);
454800
- if (typeof frontmatter !== "object" || frontmatter === null) {
454801
- return { valid: false, message: "Frontmatter must be a YAML dictionary" };
454802
- }
454803
- } catch (e2) {
454804
- return {
454805
- valid: false,
454806
- message: `Invalid YAML in frontmatter: ${e2 instanceof Error ? e2.message : String(e2)}`
454807
- };
454808
- }
454809
- const warnings = [];
454810
- const unexpectedKeys = Object.keys(frontmatter).filter((key2) => !ALLOWED_PROPERTIES.has(key2));
454811
- if (unexpectedKeys.length > 0) {
454812
- warnings.push(`Unknown frontmatter key(s): ${unexpectedKeys.sort().join(", ")}. Known properties are: ${[...ALLOWED_PROPERTIES].sort().join(", ")}`);
454813
- }
454814
- if (!("name" in frontmatter)) {
454815
- return { valid: false, message: "Missing 'name' in frontmatter" };
454816
- }
454817
- if (!("description" in frontmatter)) {
454818
- return { valid: false, message: "Missing 'description' in frontmatter" };
454819
- }
454820
- const name = frontmatter.name;
454821
- if (typeof name !== "string") {
454822
- return {
454823
- valid: false,
454824
- message: `Name must be a string, got ${typeof name}`
454825
- };
454826
- }
454827
- const trimmedName = name.trim();
454828
- if (trimmedName) {
454829
- if (!/^[a-z0-9-]+$/.test(trimmedName)) {
454830
- return {
454831
- valid: false,
454832
- message: `Name '${trimmedName}' should be hyphen-case (lowercase letters, digits, and hyphens only)`
454833
- };
454834
- }
454835
- if (trimmedName.startsWith("-") || trimmedName.endsWith("-") || trimmedName.includes("--")) {
454836
- return {
454837
- valid: false,
454838
- message: `Name '${trimmedName}' cannot start/end with hyphen or contain consecutive hyphens`
454839
- };
454840
- }
454841
- if (trimmedName.length > MAX_SKILL_NAME_LENGTH) {
454842
- return {
454843
- valid: false,
454844
- message: `Name is too long (${trimmedName.length} characters). Maximum is ${MAX_SKILL_NAME_LENGTH} characters.`
454845
- };
454846
- }
454847
- const dirName = basename29(skillPath);
454848
- if (trimmedName !== dirName) {
454849
- warnings.push(`Name '${trimmedName}' doesn't match directory name '${dirName}'. For portability, these should match.`);
454850
- }
454851
- }
454852
- const description = frontmatter.description;
454853
- if (typeof description !== "string") {
454854
- return {
454855
- valid: false,
454856
- message: `Description must be a string, got ${typeof description}`
454857
- };
454858
- }
454859
- const trimmedDescription = description.trim();
454860
- if (trimmedDescription) {
454861
- if (trimmedDescription.includes("<") || trimmedDescription.includes(">")) {
454862
- return {
454863
- valid: false,
454864
- message: "Description cannot contain angle brackets (< or >)"
454865
- };
454866
- }
454867
- if (trimmedDescription.length > 1024) {
454868
- return {
454869
- valid: false,
454870
- message: `Description is too long (${trimmedDescription.length} characters). Maximum is 1024 characters.`
454871
- };
454872
- }
454873
- }
454874
- return {
454875
- valid: true,
454876
- message: "Skill is valid!",
454877
- warnings: warnings.length > 0 ? warnings : undefined
454878
- };
454879
- }
454880
- function isMainModule() {
454881
- const entrypoint = process.argv[1];
454882
- return entrypoint ? resolve39(entrypoint) === fileURLToPath11(import.meta.url) : false;
454883
- }
454884
- var ALLOWED_PROPERTIES, MAX_SKILL_NAME_LENGTH = 64;
454885
- var init_validate_skill = __esm(() => {
454886
- ALLOWED_PROPERTIES = new Set([
454887
- "name",
454888
- "description",
454889
- "license",
454890
- "compatibility",
454891
- "metadata",
454892
- "allowed-tools"
454893
- ]);
454894
- if (isMainModule()) {
454895
- const args = process.argv.slice(2);
454896
- if (args.length !== 1) {
454897
- console.log("Usage: npx tsx validate-skill.ts <skill-directory>");
454898
- process.exit(1);
454899
- }
454900
- const { valid: valid2, message, warnings } = validateSkill(args[0]);
454901
- console.log(message);
454902
- if (warnings && warnings.length > 0) {
454903
- for (const warning of warnings) {
454904
- console.warn(`Warning: ${warning}`);
454905
- }
454906
- }
454907
- process.exit(valid2 ? 0 : 1);
454908
- }
454909
- });
454910
-
454911
- // src/agent/github-utils.ts
454912
- var exports_github_utils = {};
454913
- __export(exports_github_utils, {
454914
- parseDirNames: () => parseDirNames,
454915
- fetchGitHubContents: () => fetchGitHubContents
454916
- });
454917
- async function fetchGitHubContents(owner, repo, branch, path50) {
454918
- const apiPath = path50 ? `repos/${owner}/${repo}/contents/${path50}?ref=${branch}` : `repos/${owner}/${repo}/contents?ref=${branch}`;
454919
- try {
454920
- const { execFileSync: execFileSync7 } = await import("node:child_process");
454921
- const result2 = execFileSync7("gh", ["api", apiPath], {
454922
- encoding: "utf-8",
454923
- stdio: ["pipe", "pipe", "ignore"]
454924
- });
454925
- return JSON.parse(result2);
454926
- } catch {}
454927
- const url2 = `https://api.github.com/repos/${owner}/${repo}/contents/${path50}?ref=${branch}`;
454928
- const response = await fetch(url2, {
454929
- headers: {
454930
- Accept: "application/vnd.github.v3+json",
454931
- "User-Agent": "letta-code"
454932
- }
454933
- });
454934
- if (!response.ok) {
454935
- throw new Error(`Failed to fetch from ${owner}/${repo}/${branch}/${path50}: ${response.statusText}`);
454936
- }
454937
- return await response.json();
454938
- }
454939
- function parseDirNames(entries) {
454940
- return new Set(entries.filter((e2) => e2.type === "dir").map((e2) => e2.name));
454941
- }
454942
-
454943
- // src/agent/import.ts
454944
- var exports_import = {};
454945
- __export(exports_import, {
454946
- importAgentFromRegistry: () => importAgentFromRegistry,
454947
- importAgentFromFile: () => importAgentFromFile,
454948
- extractSkillsFromAf: () => extractSkillsFromAf
454949
- });
454950
- import { createReadStream as createReadStream3 } from "node:fs";
454951
- import { access as access3, chmod, mkdir as mkdir17, readFile as readFile30, writeFile as writeFile19 } from "node:fs/promises";
454952
- import { dirname as dirname35, isAbsolute as isAbsolute31, relative as relative14, resolve as resolve40, sep as sep9, win32 as win325 } from "node:path";
454953
- function validateImportedSkillName(name) {
454954
- const trimmedName = name.trim();
454955
- if (trimmedName !== name || trimmedName.length === 0 || trimmedName.length > MAX_SKILL_NAME_LENGTH || trimmedName === "." || trimmedName === ".." || !IMPORTED_SKILL_NAME_PATTERN.test(trimmedName)) {
454956
- throw new Error(`Invalid imported skill name "${String(name)}". Skill names may only contain letters, numbers, dots, underscores, and hyphens.`);
454957
- }
454958
- return trimmedName;
454959
- }
454960
- function assertPathInside(parent, child) {
454961
- const parentPath = resolve40(parent);
454962
- const childPath = resolve40(child);
454963
- const relativePath = relative14(parentPath, childPath);
454964
- if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep9}`) || isAbsolute31(relativePath)) {
454965
- throw new Error(`Imported skill file path escapes skill directory: ${child}`);
454966
- }
454967
- }
454968
- function validateImportedSkillFilePath(filePath) {
454969
- if (filePath.length === 0 || filePath === "." || filePath.includes("\x00") || filePath.includes("\\") || isAbsolute31(filePath) || win325.isAbsolute(filePath)) {
454970
- throw new Error(`Invalid imported skill file path "${filePath}".`);
454971
- }
454972
- const segments = filePath.split("/");
454973
- if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
454974
- throw new Error(`Invalid imported skill file path "${filePath}".`);
454975
- }
454976
- return filePath;
454977
- }
454978
- function resolveImportedSkillFilePath(skillDir, filePath) {
454979
- const safeFilePath = validateImportedSkillFilePath(filePath);
454980
- const fullPath = resolve40(skillDir, safeFilePath);
454981
- assertPathInside(skillDir, fullPath);
454982
- return fullPath;
454983
- }
454984
- function tagsEqual(left, right) {
454985
- return left.length === right.length && left.every((tag, i4) => tag === right[i4]);
454986
- }
454987
- async function resolveImportedAgentMemfsEnabled() {
454988
- const backend3 = getBackend();
454989
- const isLettaCloud2 = backend3.capabilities.remoteMemfs && !backend3.capabilities.localMemfs ? await Promise.resolve().then(() => (init_memory_filesystem2(), exports_memory_filesystem)).then((module3) => module3.isLettaCloud()) : false;
454990
- return resolveCreatedAgentMemfsConfig({
454991
- capabilities: backend3.capabilities,
454992
- isLettaCloud: isLettaCloud2
454993
- }).enableMemfs;
454994
- }
454995
- async function ensureImportedAgentCreationTags(agent, enableMemfs) {
454996
- const tags = buildCreatedAgentTags({
454997
- tags: agent.tags,
454998
- enableMemfs
454999
- });
455000
- if (tagsEqual(agent.tags ?? [], tags)) {
455001
- return agent;
455002
- }
455003
- const updatedAgent = await getBackend().updateAgent(agent.id, { tags });
455004
- return {
455005
- ...agent,
455006
- ...updatedAgent,
455007
- tags: updatedAgent.tags ?? tags
455008
- };
455009
- }
455010
- async function importAgentFromFile(options) {
455011
- if (!getBackend().capabilities.agentFileImportExport) {
455012
- throw new Error("Agent file import is not supported by this backend yet");
455013
- }
455014
- const resolvedPath = resolve40(options.filePath);
455015
- try {
455016
- await access3(resolvedPath);
455017
- } catch {
455018
- throw new Error(`AgentFile not found: ${resolvedPath}`);
455019
- }
455020
- const client = await getClient();
455021
- const file = createReadStream3(resolvedPath);
455022
- const importResponse = await client.agents.importFile({
455023
- file,
455024
- strip_messages: options.stripMessages ?? true,
455025
- override_existing_tools: false
455026
- });
455027
- if (!importResponse.agent_ids || importResponse.agent_ids.length === 0) {
455028
- throw new Error("Import failed: no agent IDs returned");
455029
- }
455030
- const agentId = importResponse.agent_ids[0];
455031
- let agent = await client.agents.retrieve(agentId, {
455032
- include: ["agent.tags"]
455033
- });
455034
- if (options.modelOverride) {
455035
- const updateArgs = getModelUpdateArgs(options.modelOverride);
455036
- await updateAgentLLMConfig(agentId, options.modelOverride, updateArgs);
455037
- const { ensureCorrectMemoryTool: ensureCorrectMemoryTool2 } = await init_toolset().then(() => exports_toolset);
455038
- await ensureCorrectMemoryTool2(agentId, options.modelOverride);
455039
- agent = await client.agents.retrieve(agentId, { include: ["agent.tags"] });
455040
- }
455041
- agent = await ensureImportedAgentCreationTags(agent, await resolveImportedAgentMemfsEnabled());
455042
- let skills;
455043
- if (!options.stripSkills) {
455044
- const { getAgentSkillsDir: getAgentSkillsDir2 } = await Promise.resolve().then(() => (init_skills5(), exports_skills2));
455045
- const skillsDir = getAgentSkillsDir2(agentId);
455046
- skills = await extractSkillsFromAf(resolvedPath, skillsDir);
455047
- }
455048
- return { agent, skills };
455049
- }
455050
- async function extractSkillsFromAf(afPath, destDir) {
455051
- const extracted = [];
455052
- const content = await readFile30(afPath, "utf-8");
455053
- const afData = JSON.parse(content);
455054
- if (!afData.skills || !Array.isArray(afData.skills)) {
455055
- return [];
455056
- }
455057
- for (const skill2 of afData.skills) {
455058
- const skillName = validateImportedSkillName(skill2.name);
455059
- const skillDir = resolve40(destDir, skillName);
455060
- await mkdir17(skillDir, { recursive: true });
455061
- if (skill2.files) {
455062
- await writeSkillFiles(skillDir, skill2.files);
455063
- extracted.push(skillName);
455064
- } else if (skill2.source_url) {
455065
- await fetchSkillFromUrl(skillDir, skill2.source_url);
455066
- extracted.push(skillName);
455067
- } else {
455068
- console.warn(`Skipping skill ${skillName}: no files or source_url`);
455069
- }
455070
- }
455071
- return extracted;
455072
- }
455073
- async function writeSkillFiles(skillDir, files) {
455074
- for (const [filePath, fileContent] of Object.entries(files)) {
455075
- await writeSkillFile(skillDir, filePath, fileContent);
455076
- }
455077
- }
455078
- async function writeSkillFile(skillDir, filePath, content) {
455079
- const fullPath = resolveImportedSkillFilePath(skillDir, filePath);
455080
- await mkdir17(dirname35(fullPath), { recursive: true });
455081
- await writeFile19(fullPath, content, "utf-8");
455082
- const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
455083
- if (isScript) {
455084
- try {
455085
- await chmod(fullPath, 493);
455086
- } catch {}
455087
- }
455088
- }
455089
- async function fetchSkillFromUrl(skillDir, sourceUrl) {
455090
- const githubPath = sourceUrl.replace(/^github\.com\//, "").replace(/\/tree\//, "/");
455091
- const parts = githubPath.split("/");
455092
- if (parts.length < 4 || !parts[0] || !parts[1] || !parts[2]) {
455093
- throw new Error(`Invalid GitHub path: ${githubPath}`);
455094
- }
455095
- const owner = parts[0];
455096
- const repo = parts[1];
455097
- const branch = parts[2];
455098
- const path50 = parts.slice(3).join("/");
455099
- const { fetchGitHubContents: fetchGitHubContents2 } = await Promise.resolve().then(() => exports_github_utils);
455100
- const entries = await fetchGitHubContents2(owner, repo, branch, path50);
455101
- if (!Array.isArray(entries)) {
455102
- throw new Error(`Expected directory at ${sourceUrl}, got file`);
455103
- }
455104
- await downloadGitHubDirectory(entries, skillDir, owner, repo, branch, path50);
455105
- }
455106
- async function downloadGitHubDirectory(entries, destDir, owner, repo, branch, basePath) {
455107
- const { fetchGitHubContents: fetchGitHubContents2 } = await Promise.resolve().then(() => exports_github_utils);
455108
- for (const entry of entries) {
455109
- if (entry.type === "file") {
455110
- if (!entry.download_url) {
455111
- throw new Error(`Missing download_url for file: ${entry.path}`);
455112
- }
455113
- const fileResponse = await fetch(entry.download_url);
455114
- const fileContent = await fileResponse.text();
455115
- const relativePath = entry.path.replace(`${basePath}/`, "");
455116
- await writeSkillFile(destDir, relativePath, fileContent);
455117
- } else if (entry.type === "dir") {
455118
- const subEntries = await fetchGitHubContents2(owner, repo, branch, entry.path);
455119
- await downloadGitHubDirectory(subEntries, destDir, owner, repo, branch, basePath);
455120
- }
455121
- }
455122
- }
455123
- function parseRegistryHandle(handle2) {
455124
- const normalized = handle2.startsWith("@") ? handle2.slice(1) : handle2;
455125
- const parts = normalized.split("/");
455126
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
455127
- throw new Error(`Invalid import handle "${handle2}". Use format: @author/agentname`);
455128
- }
455129
- return { author: parts[0], name: parts[1] };
455130
- }
455131
- async function importAgentFromRegistry(options) {
455132
- const { tmpdir: tmpdir12 } = await import("node:os");
455133
- const { join: join84 } = await import("node:path");
455134
- const { writeFile: writeFile20, unlink: unlink6 } = await import("node:fs/promises");
455135
- const { author, name } = parseRegistryHandle(options.handle);
455136
- const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/refs/heads/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}/${name}.af`;
455137
- const response = await fetch(rawUrl);
455138
- if (!response.ok) {
455139
- if (response.status === 404) {
455140
- throw new Error(`Agent @${author}/${name} not found in registry. Check that the agent exists at https://github.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/tree/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}`);
455141
- }
455142
- throw new Error(`Failed to download agent @${author}/${name}: ${response.statusText}`);
455143
- }
455144
- const afContent = await response.text();
455145
- const tempPath = join84(tmpdir12(), `letta-import-${author}-${name}-${Date.now()}.af`);
455146
- await writeFile20(tempPath, afContent, "utf-8");
455147
- try {
455148
- const result2 = await importAgentFromFile({
455149
- filePath: tempPath,
455150
- modelOverride: options.modelOverride,
455151
- stripMessages: options.stripMessages ?? true,
455152
- stripSkills: options.stripSkills ?? false
455153
- });
455154
- return result2;
455155
- } finally {
455156
- try {
455157
- await unlink6(tempPath);
455158
- } catch {}
455159
- }
455160
- }
455161
- var IMPORTED_SKILL_NAME_PATTERN, AGENT_REGISTRY_OWNER = "letta-ai", AGENT_REGISTRY_REPO = "agent-file", AGENT_REGISTRY_BRANCH = "main";
455162
- var init_import = __esm(() => {
455163
- init_backend2();
455164
- init_client4();
455165
- init_validate_skill();
455166
- init_create5();
455167
- init_model();
455168
- init_modify();
455169
- IMPORTED_SKILL_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
455170
- });
455171
-
455172
454855
  // src/agent/defaults.ts
455173
454856
  var exports_defaults = {};
455174
454857
  __export(exports_defaults, {
@@ -455533,10 +455216,10 @@ async function sendScopedApprovalMessages(params) {
455533
455216
  });
455534
455217
  }
455535
455218
  async function flushAndExit(code2) {
455536
- const flushWritable = (stream12) => new Promise((resolve41) => {
455219
+ const flushWritable = (stream12) => new Promise((resolve39) => {
455537
455220
  if (stream12.destroyed || stream12.writableEnded)
455538
- return resolve41();
455539
- stream12.write("", () => resolve41());
455221
+ return resolve39();
455222
+ stream12.write("", () => resolve39());
455540
455223
  });
455541
455224
  await closeClientMcpServers();
455542
455225
  await Promise.allSettled([
@@ -455546,12 +455229,12 @@ async function flushAndExit(code2) {
455546
455229
  process.exit(code2);
455547
455230
  }
455548
455231
  async function writeFinalHeadlessStdout(text2) {
455549
- await new Promise((resolve41) => {
455232
+ await new Promise((resolve39) => {
455550
455233
  if (process.stdout.destroyed || process.stdout.writableEnded) {
455551
- resolve41();
455234
+ resolve39();
455552
455235
  return;
455553
455236
  }
455554
- process.stdout.write(text2, () => resolve41());
455237
+ process.stdout.write(text2, () => resolve39());
455555
455238
  });
455556
455239
  }
455557
455240
  function buildEnvironmentResponseMetadata(params) {
@@ -455698,10 +455381,6 @@ In headless mode, use:
455698
455381
  process.exit(1);
455699
455382
  }
455700
455383
  const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !isStatelessSession;
455701
- const fromAfFile = resolveImportFlagAlias({
455702
- importFlagValue: values4.import,
455703
- fromAfFlagValue: values4["from-af"]
455704
- });
455705
455384
  const preLoadSkillsRaw = values4["pre-load-skills"];
455706
455385
  const systemInfoReminderEnabled = systemInfoReminderEnabledOverride ?? !values4["no-system-info-reminder"];
455707
455386
  const reflectionOverrides = (() => {
@@ -455754,7 +455433,7 @@ In headless mode, use:
455754
455433
  return reportAndExitHeadless("headless_conversation_shorthand_failed", error5, "headless_startup_conversation_shorthand");
455755
455434
  }
455756
455435
  const ambientAgentId = (process.env.LETTA_AGENT_ID || process.env.AGENT_ID || "").trim();
455757
- if ((startupOptions.requestedBackendMode || usesRemoteEnvironment) && ambientAgentId && !specifiedAgentId && !specifiedAgentName && !specifiedConversationId && !forceNew && !fromAfFile && !fromAgentId) {
455436
+ if ((startupOptions.requestedBackendMode || usesRemoteEnvironment) && ambientAgentId && !specifiedAgentId && !specifiedAgentName && !specifiedConversationId && !forceNew && !fromAgentId) {
455758
455437
  specifiedAgentId = ambientAgentId;
455759
455438
  specifiedAgentIdFromAmbient = true;
455760
455439
  }
@@ -455791,7 +455470,6 @@ In headless mode, use:
455791
455470
  specifiedAgentName,
455792
455471
  forceNewAgent: forceNew,
455793
455472
  forceNewConversation,
455794
- importFile: fromAfFile,
455795
455473
  stateless: statelessFlag,
455796
455474
  ephemeral: ephemeralFlag,
455797
455475
  isHeadless: true,
@@ -455804,39 +455482,6 @@ In headless mode, use:
455804
455482
  if (ephemeralFlag && (isBidirectionalMode || usesRemoteEnvironment)) {
455805
455483
  return reportAndExitHeadless("headless_ephemeral_transport_unsupported", "--ephemeral supports direct one-shot headless prompts only", "headless_startup_flag_conflicts");
455806
455484
  }
455807
- let isRegistryImport = false;
455808
- if (fromAfFile) {
455809
- try {
455810
- validateFlagConflicts({
455811
- guard: fromAfFile,
455812
- checks: [
455813
- {
455814
- when: specifiedAgentId,
455815
- message: "--import cannot be used with --agent"
455816
- },
455817
- {
455818
- when: specifiedAgentName,
455819
- message: "--import cannot be used with --name"
455820
- },
455821
- {
455822
- when: forceNew,
455823
- message: "--import cannot be used with --new-agent"
455824
- }
455825
- ]
455826
- });
455827
- } catch (error5) {
455828
- return reportAndExitHeadless("headless_import_flag_validation_failed", error5, "headless_startup_import_flag_validation");
455829
- }
455830
- if (fromAfFile.startsWith("@")) {
455831
- isRegistryImport = true;
455832
- try {
455833
- validateRegistryHandleOrThrow(fromAfFile);
455834
- } catch {
455835
- console.error(`Error: Invalid registry handle "${fromAfFile}". Use format: letta --import @author/agentname`);
455836
- process.exit(1);
455837
- }
455838
- }
455839
- }
455840
455485
  if (specifiedAgentName) {
455841
455486
  if (specifiedAgentId) {
455842
455487
  console.error("Error: --name cannot be used with --agent");
@@ -455878,35 +455523,6 @@ In headless mode, use:
455878
455523
  process.exit(1);
455879
455524
  }
455880
455525
  }
455881
- if (!agent && fromAfFile) {
455882
- let result2;
455883
- if (isRegistryImport) {
455884
- const { importAgentFromRegistry: importAgentFromRegistry2 } = await Promise.resolve().then(() => (init_import(), exports_import));
455885
- result2 = await importAgentFromRegistry2({
455886
- handle: fromAfFile,
455887
- modelOverride: model,
455888
- stripMessages: true,
455889
- stripSkills: false
455890
- });
455891
- } else {
455892
- const { importAgentFromFile: importAgentFromFile2 } = await Promise.resolve().then(() => (init_import(), exports_import));
455893
- result2 = await importAgentFromFile2({
455894
- filePath: fromAfFile,
455895
- modelOverride: model,
455896
- stripMessages: true,
455897
- stripSkills: false
455898
- });
455899
- }
455900
- agent = result2.agent;
455901
- if (settingsManager.isReady) {
455902
- settingsManager.setSystemPromptCustom(agent.id);
455903
- }
455904
- if (result2.skills && result2.skills.length > 0) {
455905
- const { getAgentSkillsDir: getAgentSkillsDir2 } = await Promise.resolve().then(() => (init_skills5(), exports_skills2));
455906
- const skillsDir = getAgentSkillsDir2(agent.id);
455907
- console.log(`\uD83D\uDCE6 Extracted ${result2.skills.length} skill${result2.skills.length === 1 ? "" : "s"} to ${skillsDir}: ${result2.skills.join(", ")}`);
455908
- }
455909
- }
455910
455526
  if (!agent && specifiedAgentId) {
455911
455527
  try {
455912
455528
  agent = await backend3.retrieveAgent(specifiedAgentId, {
@@ -456023,7 +455639,7 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
456023
455639
  markMilestone("HEADLESS_AGENT_RESOLVED");
456024
455640
  const publicAgentId = ephemeralFlag ? null : agent.id;
456025
455641
  telemetry.setCurrentAgent(publicAgentId, agent.tags);
456026
- const isResumingAgent = !ephemeralFlag && !!(specifiedAgentId || !forceNew && !fromAfFile);
455642
+ const isResumingAgent = !ephemeralFlag && !!(specifiedAgentId || !forceNew);
456027
455643
  if (isResumingAgent) {
456028
455644
  if (model) {
456029
455645
  const modelHandle = resolveModel(model);
@@ -456789,7 +456405,7 @@ ${loadedContents.join(`
456789
456405
  } else {
456790
456406
  console.error(`Conversation is busy, waiting ${Math.round(retryDelayMs / 1000)}s and retrying...`);
456791
456407
  }
456792
- await new Promise((resolve41) => setTimeout(resolve41, retryDelayMs));
456408
+ await new Promise((resolve39) => setTimeout(resolve39, retryDelayMs));
456793
456409
  continue;
456794
456410
  }
456795
456411
  }
@@ -456818,7 +456434,7 @@ ${loadedContents.join(`
456818
456434
  const delaySeconds = Math.round(delayMs / 1000);
456819
456435
  console.error(`Transient API error before streaming (attempt ${attempt2} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
456820
456436
  }
456821
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
456437
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
456822
456438
  conversationBusyRetries = 0;
456823
456439
  continue;
456824
456440
  }
@@ -457073,7 +456689,7 @@ ${loadedContents.join(`
457073
456689
  const delaySeconds = Math.round(delayMs / 1000);
457074
456690
  console.error(`LLM API error encountered (attempt ${attempt2} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
457075
456691
  }
457076
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
456692
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
457077
456693
  currentInput = refreshInputOtidsForNewRequest(currentInput);
457078
456694
  continue;
457079
456695
  }
@@ -457165,7 +456781,7 @@ ${loadedContents.join(`
457165
456781
  } else {
457166
456782
  console.error(`Empty LLM response, retrying (attempt ${attempt2} of ${EMPTY_RESPONSE_MAX_RETRIES2})...`);
457167
456783
  }
457168
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
456784
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
457169
456785
  currentInput = refreshInputOtidsForNewRequest(currentInput);
457170
456786
  continue;
457171
456787
  }
@@ -457193,7 +456809,7 @@ ${loadedContents.join(`
457193
456809
  const delaySeconds = Math.round(delayMs / 1000);
457194
456810
  console.error(`LLM API error encountered (attempt ${attempt2} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
457195
456811
  }
457196
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
456812
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
457197
456813
  currentInput = refreshInputOtidsForNewRequest(currentInput);
457198
456814
  continue;
457199
456815
  }
@@ -457223,7 +456839,7 @@ ${loadedContents.join(`
457223
456839
  const delaySeconds = Math.round(delayMs / 1000);
457224
456840
  console.error(`LLM API error encountered (attempt ${attempt2} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
457225
456841
  }
457226
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
456842
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
457227
456843
  currentInput = refreshInputOtidsForNewRequest(currentInput);
457228
456844
  continue;
457229
456845
  }
@@ -457618,9 +457234,9 @@ async function runBidirectionalMode(agent, conversationId, _outputFormat, includ
457618
457234
  const syntheticUserLine = serializeQueuedMessageAsUserLine(queuedMessage);
457619
457235
  maybeNotifyBlocked(syntheticUserLine);
457620
457236
  if (lineResolver) {
457621
- const resolve41 = lineResolver;
457237
+ const resolve39 = lineResolver;
457622
457238
  lineResolver = null;
457623
- resolve41(syntheticUserLine);
457239
+ resolve39(syntheticUserLine);
457624
457240
  return;
457625
457241
  }
457626
457242
  lineQueue.push(syntheticUserLine);
@@ -457640,9 +457256,9 @@ async function runBidirectionalMode(agent, conversationId, _outputFormat, includ
457640
457256
  if (action3 === "abort-active") {
457641
457257
  currentAbortController.abort();
457642
457258
  if (lineResolver) {
457643
- const resolve41 = lineResolver;
457259
+ const resolve39 = lineResolver;
457644
457260
  lineResolver = null;
457645
- resolve41(null);
457261
+ resolve39(null);
457646
457262
  }
457647
457263
  } else if (action3 === "latch") {
457648
457264
  pendingInterrupt = true;
@@ -457662,9 +457278,9 @@ async function runBidirectionalMode(agent, conversationId, _outputFormat, includ
457662
457278
  if (lineResolver) {
457663
457279
  if (parsedLine?.type === "user")
457664
457280
  turnStarting = true;
457665
- const resolve41 = lineResolver;
457281
+ const resolve39 = lineResolver;
457666
457282
  lineResolver = null;
457667
- resolve41(line);
457283
+ resolve39(line);
457668
457284
  } else {
457669
457285
  lineQueue.push(line);
457670
457286
  }
@@ -457673,17 +457289,17 @@ async function runBidirectionalMode(agent, conversationId, _outputFormat, includ
457673
457289
  setMessageQueueAdder(null);
457674
457290
  msgQueueRuntime.clear("shutdown");
457675
457291
  if (lineResolver) {
457676
- const resolve41 = lineResolver;
457292
+ const resolve39 = lineResolver;
457677
457293
  lineResolver = null;
457678
- resolve41(null);
457294
+ resolve39(null);
457679
457295
  }
457680
457296
  });
457681
457297
  async function getNextLine() {
457682
457298
  if (lineQueue.length > 0) {
457683
457299
  return lineQueue.shift() ?? null;
457684
457300
  }
457685
- return new Promise((resolve41) => {
457686
- lineResolver = resolve41;
457301
+ return new Promise((resolve39) => {
457302
+ lineResolver = resolve39;
457687
457303
  });
457688
457304
  }
457689
457305
  async function requestPermission(toolCallId, toolName2, toolInput) {
@@ -458171,7 +457787,7 @@ async function runBidirectionalMode(agent, conversationId, _outputFormat, includ
458171
457787
  uuid: `retry-bidir-${randomUUID37()}`
458172
457788
  };
458173
457789
  writeWireMessage(retryMsg);
458174
- await new Promise((resolve41) => setTimeout(resolve41, delayMs));
457790
+ await new Promise((resolve39) => setTimeout(resolve39, delayMs));
458175
457791
  continue;
458176
457792
  }
458177
457793
  throw preStreamError;
@@ -458885,7 +458501,7 @@ var BYTES_PER_TOKEN = 4;
458885
458501
 
458886
458502
  // src/cli/helpers/window-title-config.ts
458887
458503
  import { homedir as homedir45 } from "node:os";
458888
- import { basename as basename30, resolve as resolve41 } from "node:path";
458504
+ import { basename as basename29, resolve as resolve39 } from "node:path";
458889
458505
  function isWindowTitleField(value) {
458890
458506
  return WINDOW_TITLE_FIELDS.includes(value);
458891
458507
  }
@@ -459043,8 +458659,8 @@ function terminalTitleProjectName(data) {
459043
458659
  const directory = titleDirectory(data);
459044
458660
  if (!directory)
459045
458661
  return null;
459046
- const resolved = resolve41(directory);
459047
- const name = basename30(resolved) || formatDirectoryDisplay(resolved) || resolved;
458662
+ const resolved = resolve39(directory);
458663
+ const name = basename29(resolved) || formatDirectoryDisplay(resolved) || resolved;
459048
458664
  return truncateTerminalTitlePart(name, 24);
459049
458665
  }
459050
458666
  function titleDirectory(data) {
@@ -459053,7 +458669,7 @@ function titleDirectory(data) {
459053
458669
  function formatDirectoryDisplay(directory) {
459054
458670
  if (!directory)
459055
458671
  return null;
459056
- const resolved = resolve41(directory);
458672
+ const resolved = resolve39(directory);
459057
458673
  const home = homedir45();
459058
458674
  if (resolved === home)
459059
458675
  return "~";
@@ -459776,10 +459392,10 @@ var init_queued_message_parts = __esm(() => {
459776
459392
 
459777
459393
  // src/cli/helpers/reflection-arena-hf-upload.ts
459778
459394
  import { execFile as execFileCb5 } from "node:child_process";
459779
- import { existsSync as existsSync67 } from "node:fs";
459780
- import { appendFile as appendFile2, chmod as chmod2, mkdir as mkdir18, writeFile as writeFile20 } from "node:fs/promises";
459395
+ import { existsSync as existsSync66 } from "node:fs";
459396
+ import { appendFile as appendFile2, chmod, mkdir as mkdir17, writeFile as writeFile19 } from "node:fs/promises";
459781
459397
  import { homedir as homedir46 } from "node:os";
459782
- import { join as join85 } from "node:path";
459398
+ import { join as join84 } from "node:path";
459783
459399
  import { promisify as promisify16 } from "node:util";
459784
459400
  function buildGitEnv(token2, askpassPath) {
459785
459401
  return {
@@ -459815,8 +459431,8 @@ async function runGit6(cwd2, args, env5) {
459815
459431
  }
459816
459432
  }
459817
459433
  async function writeGitAskpass(repoRoot) {
459818
- const askpassPath = join85(repoRoot, "hf-askpass.sh");
459819
- await writeFile20(askpassPath, [
459434
+ const askpassPath = join84(repoRoot, "hf-askpass.sh");
459435
+ await writeFile19(askpassPath, [
459820
459436
  "#!/bin/sh",
459821
459437
  'case "$1" in',
459822
459438
  " *Username*) printf '%s\\n' 'hf_user' ;;",
@@ -459825,12 +459441,12 @@ async function writeGitAskpass(repoRoot) {
459825
459441
  ""
459826
459442
  ].join(`
459827
459443
  `), { encoding: "utf-8", mode: 448 });
459828
- await chmod2(askpassPath, 448);
459444
+ await chmod(askpassPath, 448);
459829
459445
  return askpassPath;
459830
459446
  }
459831
459447
  async function prepareHfRepo(env5) {
459832
- await mkdir18(HF_CACHE_ROOT, { recursive: true });
459833
- if (!existsSync67(join85(HF_REPO_DIR, ".git"))) {
459448
+ await mkdir17(HF_CACHE_ROOT, { recursive: true });
459449
+ if (!existsSync66(join84(HF_REPO_DIR, ".git"))) {
459834
459450
  await runGit6(HF_CACHE_ROOT, ["clone", "--depth", "1", HF_REPO_URL, HF_REPO_DIR], env5);
459835
459451
  return HF_REPO_DIR;
459836
459452
  }
@@ -459860,13 +459476,13 @@ async function maybeUploadReflectionArenaChoiceToHf(row) {
459860
459476
  if (!token2) {
459861
459477
  return { uploaded: false, reason: "missing_token" };
459862
459478
  }
459863
- await mkdir18(HF_CACHE_ROOT, { recursive: true });
459479
+ await mkdir17(HF_CACHE_ROOT, { recursive: true });
459864
459480
  const askpassPath = await writeGitAskpass(HF_CACHE_ROOT);
459865
459481
  const env5 = buildGitEnv(token2, askpassPath);
459866
459482
  try {
459867
459483
  const repoDir = await prepareHfRepo(env5);
459868
- await mkdir18(join85(repoDir, "data"), { recursive: true });
459869
- await appendFile2(join85(repoDir, HF_DATASET_PATH), `${JSON.stringify(row)}
459484
+ await mkdir17(join84(repoDir, "data"), { recursive: true });
459485
+ await appendFile2(join84(repoDir, HF_DATASET_PATH), `${JSON.stringify(row)}
459870
459486
  `, {
459871
459487
  encoding: "utf-8"
459872
459488
  });
@@ -459894,16 +459510,16 @@ var init_reflection_arena_hf_upload = __esm(() => {
459894
459510
  init_version();
459895
459511
  execFile18 = promisify16(execFileCb5);
459896
459512
  HF_REPO_URL = `https://huggingface.co/datasets/${HF_REPO_ID}`;
459897
- HF_CACHE_ROOT = join85(homedir46(), ".letta", "reflection-arena", "hf-upload");
459898
- HF_REPO_DIR = join85(HF_CACHE_ROOT, "repo");
459513
+ HF_CACHE_ROOT = join84(homedir46(), ".letta", "reflection-arena", "hf-upload");
459514
+ HF_REPO_DIR = join84(HF_CACHE_ROOT, "repo");
459899
459515
  });
459900
459516
 
459901
459517
  // src/cli/helpers/reflection-arena.ts
459902
459518
  import { execFile as execFileCb6 } from "node:child_process";
459903
459519
  import { randomInt as randomInt2, randomUUID as randomUUID38 } from "node:crypto";
459904
- import { appendFile as appendFile3, mkdir as mkdir19, readFile as readFile31, writeFile as writeFile21 } from "node:fs/promises";
459520
+ import { appendFile as appendFile3, mkdir as mkdir18, readFile as readFile30, writeFile as writeFile20 } from "node:fs/promises";
459905
459521
  import { homedir as homedir47 } from "node:os";
459906
- import { join as join86 } from "node:path";
459522
+ import { join as join85 } from "node:path";
459907
459523
  import { promisify as promisify17 } from "node:util";
459908
459524
  function sampleReflectionArenaComparisonModel(excludedModels = []) {
459909
459525
  const excluded = new Set(excludedModels);
@@ -459952,24 +459568,24 @@ function candidateIsConfirmedNoOp(candidate) {
459952
459568
  return candidate.result?.success === true && candidate.result.memoryNoChanges === true && candidate.result.memoryHead === candidate.worktree.baseHead;
459953
459569
  }
459954
459570
  function getReflectionArenaRoot() {
459955
- return join86(homedir47(), ".letta", "reflection-arena");
459571
+ return join85(homedir47(), ".letta", "reflection-arena");
459956
459572
  }
459957
459573
  function getReflectionArenaRunsDir() {
459958
- return join86(getReflectionArenaRoot(), "runs");
459574
+ return join85(getReflectionArenaRoot(), "runs");
459959
459575
  }
459960
459576
  function getReflectionArenaChoiceLogPath() {
459961
- return join86(getReflectionArenaRoot(), "choices.jsonl");
459577
+ return join85(getReflectionArenaRoot(), "choices.jsonl");
459962
459578
  }
459963
459579
  function getReflectionArenaRunPath(runId) {
459964
- return join86(getReflectionArenaRunsDir(), `${runId}.json`);
459580
+ return join85(getReflectionArenaRunsDir(), `${runId}.json`);
459965
459581
  }
459966
459582
  async function saveReflectionArenaRun(run) {
459967
- await mkdir19(getReflectionArenaRunsDir(), { recursive: true });
459968
- await writeFile21(getReflectionArenaRunPath(run.runId), `${JSON.stringify(run, null, 2)}
459583
+ await mkdir18(getReflectionArenaRunsDir(), { recursive: true });
459584
+ await writeFile20(getReflectionArenaRunPath(run.runId), `${JSON.stringify(run, null, 2)}
459969
459585
  `, "utf-8");
459970
459586
  }
459971
459587
  async function loadReflectionArenaRun(runId) {
459972
- const raw2 = await readFile31(getReflectionArenaRunPath(runId), "utf-8");
459588
+ const raw2 = await readFile30(getReflectionArenaRunPath(runId), "utf-8");
459973
459589
  return JSON.parse(raw2);
459974
459590
  }
459975
459591
  async function updateReflectionArenaRun(runId, update3) {
@@ -460135,7 +459751,7 @@ function formatReflectionArenaChoiceResult(params) {
460135
459751
  `);
460136
459752
  }
460137
459753
  async function appendChoiceRecord(run) {
460138
- await mkdir19(getReflectionArenaRoot(), { recursive: true });
459754
+ await mkdir18(getReflectionArenaRoot(), { recursive: true });
460139
459755
  await appendFile3(getReflectionArenaChoiceLogPath(), `${JSON.stringify({
460140
459756
  run_id: run.runId,
460141
459757
  agent_id: run.agentId,
@@ -460161,7 +459777,7 @@ async function appendChoiceRecord(run) {
460161
459777
  }
460162
459778
  async function readTranscriptPayloadForTelemetry(payloadPath) {
460163
459779
  try {
460164
- const transcript = await readFile31(payloadPath, "utf-8");
459780
+ const transcript = await readFile30(payloadPath, "utf-8");
460165
459781
  return {
460166
459782
  transcriptPayload: transcript.slice(0, REFLECTION_ARENA_TELEMETRY_TRANSCRIPT_MAX_CHARS),
460167
459783
  transcriptPayloadChars: transcript.length,
@@ -460852,8 +460468,8 @@ async function pushToMemoryRepositoryWithTimeout(agentId) {
460852
460468
  try {
460853
460469
  return await Promise.race([
460854
460470
  pushToMemoryRepository(agentId),
460855
- new Promise((resolve42) => {
460856
- timeout = setTimeout(() => resolve42("timeout"), INITIAL_PUSH_TIMEOUT_MS);
460471
+ new Promise((resolve40) => {
460472
+ timeout = setTimeout(() => resolve40("timeout"), INITIAL_PUSH_TIMEOUT_MS);
460857
460473
  })
460858
460474
  ]);
460859
460475
  } finally {
@@ -461117,13 +460733,13 @@ __export(exports_terminal_keybinding_installer, {
461117
460733
  });
461118
460734
  import {
461119
460735
  copyFileSync as copyFileSync6,
461120
- existsSync as existsSync68,
460736
+ existsSync as existsSync67,
461121
460737
  mkdirSync as mkdirSync45,
461122
- readFileSync as readFileSync43,
460738
+ readFileSync as readFileSync42,
461123
460739
  writeFileSync as writeFileSync35
461124
460740
  } from "node:fs";
461125
460741
  import { homedir as homedir48, platform as platform10 } from "node:os";
461126
- import { dirname as dirname36, join as join87 } from "node:path";
460742
+ import { dirname as dirname35, join as join86 } from "node:path";
461127
460743
  function detectTerminalType() {
461128
460744
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
461129
460745
  return "cursor";
@@ -461155,16 +460771,16 @@ function getKeybindingsPath(terminal) {
461155
460771
  }[terminal];
461156
460772
  const os9 = platform10();
461157
460773
  if (os9 === "darwin") {
461158
- return join87(homedir48(), "Library", "Application Support", appName, "User", "keybindings.json");
460774
+ return join86(homedir48(), "Library", "Application Support", appName, "User", "keybindings.json");
461159
460775
  }
461160
460776
  if (os9 === "win32") {
461161
460777
  const appData = process.env.APPDATA;
461162
460778
  if (!appData)
461163
460779
  return null;
461164
- return join87(appData, appName, "User", "keybindings.json");
460780
+ return join86(appData, appName, "User", "keybindings.json");
461165
460781
  }
461166
460782
  if (os9 === "linux") {
461167
- return join87(homedir48(), ".config", appName, "User", "keybindings.json");
460783
+ return join86(homedir48(), ".config", appName, "User", "keybindings.json");
461168
460784
  }
461169
460785
  return null;
461170
460786
  }
@@ -461186,10 +460802,10 @@ function parseKeybindings(content) {
461186
460802
  }
461187
460803
  }
461188
460804
  function keybindingExists(keybindingsPath) {
461189
- if (!existsSync68(keybindingsPath))
460805
+ if (!existsSync67(keybindingsPath))
461190
460806
  return false;
461191
460807
  try {
461192
- const content = readFileSync43(keybindingsPath, { encoding: "utf-8" });
460808
+ const content = readFileSync42(keybindingsPath, { encoding: "utf-8" });
461193
460809
  const keybindings = parseKeybindings(content);
461194
460810
  if (!keybindings)
461195
460811
  return false;
@@ -461199,7 +460815,7 @@ function keybindingExists(keybindingsPath) {
461199
460815
  }
461200
460816
  }
461201
460817
  function createBackup(keybindingsPath) {
461202
- if (!existsSync68(keybindingsPath))
460818
+ if (!existsSync67(keybindingsPath))
461203
460819
  return null;
461204
460820
  const backupPath = `${keybindingsPath}.letta-backup`;
461205
460821
  try {
@@ -461214,15 +460830,15 @@ function installKeybinding(keybindingsPath) {
461214
460830
  if (keybindingExists(keybindingsPath)) {
461215
460831
  return { success: true, alreadyExists: true };
461216
460832
  }
461217
- const parentDir = dirname36(keybindingsPath);
461218
- if (!existsSync68(parentDir)) {
460833
+ const parentDir = dirname35(keybindingsPath);
460834
+ if (!existsSync67(parentDir)) {
461219
460835
  mkdirSync45(parentDir, { recursive: true });
461220
460836
  }
461221
460837
  let keybindings = [];
461222
460838
  let backupPath = null;
461223
- if (existsSync68(keybindingsPath)) {
460839
+ if (existsSync67(keybindingsPath)) {
461224
460840
  backupPath = createBackup(keybindingsPath);
461225
- const content = readFileSync43(keybindingsPath, { encoding: "utf-8" });
460841
+ const content = readFileSync42(keybindingsPath, { encoding: "utf-8" });
461226
460842
  const parsed = parseKeybindings(content);
461227
460843
  if (parsed === null) {
461228
460844
  return {
@@ -461250,10 +460866,10 @@ function installKeybinding(keybindingsPath) {
461250
460866
  }
461251
460867
  function removeKeybinding(keybindingsPath) {
461252
460868
  try {
461253
- if (!existsSync68(keybindingsPath)) {
460869
+ if (!existsSync67(keybindingsPath)) {
461254
460870
  return { success: true };
461255
460871
  }
461256
- const content = readFileSync43(keybindingsPath, { encoding: "utf-8" });
460872
+ const content = readFileSync42(keybindingsPath, { encoding: "utf-8" });
461257
460873
  const keybindings = parseKeybindings(content);
461258
460874
  if (!keybindings) {
461259
460875
  return {
@@ -461317,14 +460933,14 @@ function getWezTermConfigPath() {
461317
460933
  }
461318
460934
  const xdgConfig = process.env.XDG_CONFIG_HOME;
461319
460935
  if (xdgConfig) {
461320
- const xdgPath = join87(xdgConfig, "wezterm", "wezterm.lua");
461321
- if (existsSync68(xdgPath))
460936
+ const xdgPath = join86(xdgConfig, "wezterm", "wezterm.lua");
460937
+ if (existsSync67(xdgPath))
461322
460938
  return xdgPath;
461323
460939
  }
461324
- const configPath = join87(homedir48(), ".config", "wezterm", "wezterm.lua");
461325
- if (existsSync68(configPath))
460940
+ const configPath = join86(homedir48(), ".config", "wezterm", "wezterm.lua");
460941
+ if (existsSync67(configPath))
461326
460942
  return configPath;
461327
- return join87(homedir48(), ".wezterm.lua");
460943
+ return join86(homedir48(), ".wezterm.lua");
461328
460944
  }
461329
460945
  function stripLuaCommentsFromLine(line, blockCommentEnd) {
461330
460946
  let code2 = "";
@@ -461427,10 +461043,10 @@ ${WEZTERM_DELETE_FIX}
461427
461043
  `;
461428
461044
  }
461429
461045
  function wezTermDeleteFixExists(configPath) {
461430
- if (!existsSync68(configPath))
461046
+ if (!existsSync67(configPath))
461431
461047
  return false;
461432
461048
  try {
461433
- const content = readFileSync43(configPath, { encoding: "utf-8" });
461049
+ const content = readFileSync42(configPath, { encoding: "utf-8" });
461434
461050
  return content.includes("Letta Code: Fix Delete key") || content.includes("key = 'Delete'") && content.includes("SendString") && content.includes("\\x1b[3~");
461435
461051
  } catch {
461436
461052
  return false;
@@ -461444,14 +461060,14 @@ function installWezTermDeleteFix() {
461444
461060
  }
461445
461061
  let content = "";
461446
461062
  let backupPath = null;
461447
- if (existsSync68(configPath)) {
461063
+ if (existsSync67(configPath)) {
461448
461064
  backupPath = `${configPath}.letta-backup`;
461449
461065
  copyFileSync6(configPath, backupPath);
461450
- content = readFileSync43(configPath, { encoding: "utf-8" });
461066
+ content = readFileSync42(configPath, { encoding: "utf-8" });
461451
461067
  }
461452
461068
  content = injectWezTermDeleteFix(content);
461453
- const parentDir = dirname36(configPath);
461454
- if (!existsSync68(parentDir)) {
461069
+ const parentDir = dirname35(configPath);
461070
+ if (!existsSync67(parentDir)) {
461455
461071
  mkdirSync45(parentDir, { recursive: true });
461456
461072
  }
461457
461073
  writeFileSync35(configPath, content, { encoding: "utf-8" });
@@ -461503,9 +461119,9 @@ __export(exports_settings2, {
461503
461119
  getSetting: () => getSetting
461504
461120
  });
461505
461121
  import { homedir as homedir49 } from "node:os";
461506
- import { join as join88 } from "node:path";
461122
+ import { join as join87 } from "node:path";
461507
461123
  function getSettingsPath() {
461508
- return join88(homedir49(), ".letta", "settings.json");
461124
+ return join87(homedir49(), ".letta", "settings.json");
461509
461125
  }
461510
461126
  async function loadSettings() {
461511
461127
  const settingsPath = getSettingsPath();
@@ -461542,7 +461158,7 @@ async function getSetting(key2) {
461542
461158
  return settings3[key2];
461543
461159
  }
461544
461160
  function getProjectSettingsPath() {
461545
- return join88(process.cwd(), ".letta", "settings.local.json");
461161
+ return join87(process.cwd(), ".letta", "settings.local.json");
461546
461162
  }
461547
461163
  async function loadProjectSettings() {
461548
461164
  const settingsPath = getProjectSettingsPath();
@@ -461560,7 +461176,7 @@ async function loadProjectSettings() {
461560
461176
  }
461561
461177
  async function saveProjectSettings(settings3) {
461562
461178
  const settingsPath = getProjectSettingsPath();
461563
- const dirPath = join88(process.cwd(), ".letta");
461179
+ const dirPath = join87(process.cwd(), ".letta");
461564
461180
  try {
461565
461181
  if (!exists(dirPath)) {
461566
461182
  await mkdir(dirPath, { recursive: true });
@@ -461860,14 +461476,6 @@ var init_registry2 = __esm(() => {
461860
461476
  return "Updating description...";
461861
461477
  }
461862
461478
  },
461863
- "/export": {
461864
- desc: "Export AgentFile (.af)",
461865
- order: 26,
461866
- noArgs: true,
461867
- handler: () => {
461868
- return "Exporting agent file...";
461869
- }
461870
- },
461871
461479
  "/toolset": {
461872
461480
  desc: "Switch toolset (replaces /link and /unlink)",
461873
461481
  order: 27,
@@ -462185,14 +461793,6 @@ Location: ${keybindingsPath}`;
462185
461793
  handler: () => {
462186
461794
  return "Opening agent browser...";
462187
461795
  }
462188
- },
462189
- "/download": {
462190
- desc: "Export AgentFile (.af)",
462191
- hidden: true,
462192
- noArgs: true,
462193
- handler: () => {
462194
- return "Exporting agent file...";
462195
- }
462196
461796
  }
462197
461797
  };
462198
461798
  });
@@ -462608,10 +462208,10 @@ var init_InlineBashApproval = __esm(async () => {
462608
462208
  });
462609
462209
 
462610
462210
  // src/cli/components/DiffRenderer.tsx
462611
- import { relative as relative15 } from "node:path";
462211
+ import { relative as relative14 } from "node:path";
462612
462212
  function formatDisplayPath4(filePath) {
462613
462213
  const cwd2 = process.cwd();
462614
- const relativePath = relative15(cwd2, filePath);
462214
+ const relativePath = relative14(cwd2, filePath);
462615
462215
  if (relativePath.startsWith("..")) {
462616
462216
  return filePath;
462617
462217
  }
@@ -462970,10 +462570,10 @@ var init_DiffRenderer = __esm(async () => {
462970
462570
  });
462971
462571
 
462972
462572
  // src/cli/components/AdvancedDiffRenderer.tsx
462973
- import { relative as relative16 } from "node:path";
462573
+ import { relative as relative15 } from "node:path";
462974
462574
  function formatRelativePath(filePath) {
462975
462575
  const cwd2 = process.cwd();
462976
- const relativePath = relative16(cwd2, filePath);
462576
+ const relativePath = relative15(cwd2, filePath);
462977
462577
  return relativePath.startsWith("..") ? relativePath : `./${relativePath}`;
462978
462578
  }
462979
462579
  function padLeft(n, width) {
@@ -463255,7 +462855,7 @@ function AdvancedDiffRenderer(props) {
463255
462855
  }
463256
462856
  const { hunks } = result2;
463257
462857
  const filePath = props.filePath;
463258
- const relative17 = formatRelativePath(filePath);
462858
+ const relative16 = formatRelativePath(filePath);
463259
462859
  const lang41 = languageFromPath(filePath);
463260
462860
  const shouldHighlight = lang41 && !exceedsAdvancedDiffHighlightLimits(hunks);
463261
462861
  const hunkSyntaxLines = [];
@@ -463278,7 +462878,7 @@ function AdvancedDiffRenderer(props) {
463278
462878
  const rows = buildAdvancedDiffRows(hunks, hunkSyntaxLines);
463279
462879
  const maxDisplayNo = rows.reduce((m4, r5) => Math.max(m4, r5.displayNo), 1);
463280
462880
  const gutterWidth = String(maxDisplayNo).length;
463281
- const header = props.kind === "write" ? `Wrote changes to ${relative17}` : `Updated ${relative17}`;
462881
+ const header = props.kind === "write" ? `Wrote changes to ${relative16}` : `Updated ${relative16}`;
463282
462882
  if (rows.length === 0) {
463283
462883
  const noChangesGutter = 4;
463284
462884
  return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
@@ -463329,7 +462929,7 @@ function AdvancedDiffRenderer(props) {
463329
462929
  "No changes to ",
463330
462930
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
463331
462931
  bold: true,
463332
- children: relative17
462932
+ children: relative16
463333
462933
  }, undefined, false, undefined, this),
463334
462934
  " (file content identical)"
463335
462935
  ]
@@ -463435,9 +463035,9 @@ function getHeaderText(fileEdit) {
463435
463035
  } else if (operations.length === 1) {
463436
463036
  const op = operations[0];
463437
463037
  if (op) {
463438
- const { relative: relative18 } = __require("node:path");
463038
+ const { relative: relative17 } = __require("node:path");
463439
463039
  const cwd3 = process.cwd();
463440
- const relPath2 = relative18(cwd3, op.path);
463040
+ const relPath2 = relative17(cwd3, op.path);
463441
463041
  const displayPath2 = relPath2.startsWith("..") ? op.path : relPath2;
463442
463042
  if (op.kind === "add") {
463443
463043
  return `Write to ${displayPath2}?`;
@@ -463451,14 +463051,14 @@ function getHeaderText(fileEdit) {
463451
463051
  }
463452
463052
  return "Apply patch?";
463453
463053
  }
463454
- const { relative: relative17 } = __require("node:path");
463054
+ const { relative: relative16 } = __require("node:path");
463455
463055
  const cwd2 = process.cwd();
463456
- const relPath = relative17(cwd2, fileEdit.filePath);
463056
+ const relPath = relative16(cwd2, fileEdit.filePath);
463457
463057
  const displayPath = relPath.startsWith("..") ? fileEdit.filePath : relPath;
463458
463058
  if (t2 === "write" || t2 === "write_file" || t2 === "writefile" || t2 === "write_file_gemini" || t2 === "writefilegemini") {
463459
- const { existsSync: existsSync69 } = __require("node:fs");
463059
+ const { existsSync: existsSync68 } = __require("node:fs");
463460
463060
  try {
463461
- if (existsSync69(fileEdit.filePath)) {
463061
+ if (existsSync68(fileEdit.filePath)) {
463462
463062
  return `Overwrite ${displayPath}?`;
463463
463063
  }
463464
463064
  } catch {}
@@ -463636,9 +463236,9 @@ var init_InlineFileEditApproval = __esm(async () => {
463636
463236
  children: fileEdit.patchInput ? /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(Box_default, {
463637
463237
  flexDirection: "column",
463638
463238
  children: parsePatchOperations2(fileEdit.patchInput).map((op, idx) => {
463639
- const { relative: relative17 } = __require("node:path");
463239
+ const { relative: relative16 } = __require("node:path");
463640
463240
  const cwd2 = process.cwd();
463641
- const relPath = relative17(cwd2, op.path);
463241
+ const relPath = relative16(cwd2, op.path);
463642
463242
  const displayPath = relPath.startsWith("..") ? op.path : relPath;
463643
463243
  const diffKey = fileEdit.toolCallId ? `${fileEdit.toolCallId}:${op.path}` : undefined;
463644
463244
  const opDiff = diffKey && allDiffs ? allDiffs.get(diffKey) : undefined;
@@ -467175,11 +466775,11 @@ var init_HelpDialog = __esm(async () => {
467175
466775
 
467176
466776
  // src/hooks/writer.ts
467177
466777
  import { homedir as homedir50 } from "node:os";
467178
- import { resolve as resolve42 } from "node:path";
466778
+ import { resolve as resolve40 } from "node:path";
467179
466779
  function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
467180
466780
  const home = process.env.HOME || homedir50();
467181
- const globalSettingsPath = resolve42(home, ".letta", "settings.json");
467182
- const projectSettingsPath = resolve42(workingDirectory, ".letta", "settings.json");
466781
+ const globalSettingsPath = resolve40(home, ".letta", "settings.json");
466782
+ const projectSettingsPath = resolve40(workingDirectory, ".letta", "settings.json");
467183
466783
  return globalSettingsPath === projectSettingsPath;
467184
466784
  }
467185
466785
  function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
@@ -472969,15 +472569,15 @@ var init_InputRich = __esm(async () => {
472969
472569
  // src/cli/commands/install-github-app.ts
472970
472570
  import { execFileSync as execFileSync7 } from "node:child_process";
472971
472571
  import {
472972
- existsSync as existsSync69,
472572
+ existsSync as existsSync68,
472973
472573
  mkdirSync as mkdirSync46,
472974
472574
  mkdtempSync as mkdtempSync6,
472975
- readFileSync as readFileSync44,
472575
+ readFileSync as readFileSync43,
472976
472576
  rmSync as rmSync18,
472977
472577
  writeFileSync as writeFileSync36
472978
472578
  } from "node:fs";
472979
472579
  import { tmpdir as tmpdir12 } from "node:os";
472980
- import { dirname as dirname37, join as join89 } from "node:path";
472580
+ import { dirname as dirname36, join as join88 } from "node:path";
472981
472581
  function runCommand(command, args, cwd2, input) {
472982
472582
  try {
472983
472583
  return execFileSync7(command, args, {
@@ -473198,8 +472798,8 @@ async function createLettaAgent(apiKey, name) {
473198
472798
  return createMinimalAgent(apiKey, name);
473199
472799
  }
473200
472800
  function cloneRepoToTemp(repo) {
473201
- const tempDir = mkdtempSync6(join89(tmpdir12(), "letta-install-github-app-"));
473202
- const repoDir = join89(tempDir, "repo");
472801
+ const tempDir = mkdtempSync6(join88(tmpdir12(), "letta-install-github-app-"));
472802
+ const repoDir = join88(tempDir, "repo");
473203
472803
  runCommand("gh", ["repo", "clone", repo, repoDir, "--", "--depth=1"]);
473204
472804
  return { tempDir, repoDir };
473205
472805
  }
@@ -473210,14 +472810,14 @@ function runGit7(args, cwd2) {
473210
472810
  return runCommand("git", args, cwd2);
473211
472811
  }
473212
472812
  function writeWorkflow(repoDir, workflowPath, content) {
473213
- const absolutePath = join89(repoDir, workflowPath);
473214
- if (!existsSync69(dirname37(absolutePath))) {
473215
- mkdirSync46(dirname37(absolutePath), { recursive: true });
472813
+ const absolutePath = join88(repoDir, workflowPath);
472814
+ if (!existsSync68(dirname36(absolutePath))) {
472815
+ mkdirSync46(dirname36(absolutePath), { recursive: true });
473216
472816
  }
473217
472817
  const next = `${content.trimEnd()}
473218
472818
  `;
473219
- if (existsSync69(absolutePath)) {
473220
- const previous = readFileSync44(absolutePath, "utf8");
472819
+ if (existsSync68(absolutePath)) {
472820
+ const previous = readFileSync43(absolutePath, "utf8");
473221
472821
  if (previous === next) {
473222
472822
  return false;
473223
472823
  }
@@ -477106,9 +476706,9 @@ __export(exports_generate_memory_viewer, {
477106
476706
  generateAndOpenMemoryViewer: () => generateAndOpenMemoryViewer
477107
476707
  });
477108
476708
  import { execFile as execFileCb7 } from "node:child_process";
477109
- import { chmodSync as chmodSync7, existsSync as existsSync70, mkdirSync as mkdirSync47, writeFileSync as writeFileSync37 } from "node:fs";
476709
+ import { chmodSync as chmodSync7, existsSync as existsSync69, mkdirSync as mkdirSync47, writeFileSync as writeFileSync37 } from "node:fs";
477110
476710
  import { homedir as homedir51 } from "node:os";
477111
- import { join as join90 } from "node:path";
476711
+ import { join as join89 } from "node:path";
477112
476712
  import { promisify as promisify18 } from "node:util";
477113
476713
  function messagesFromOverview(overview) {
477114
476714
  return overview.messages?.map((message) => ({
@@ -477146,7 +476746,7 @@ async function runGitSafe(cwd2, args) {
477146
476746
  return "";
477147
476747
  }
477148
476748
  }
477149
- function parseFrontmatter3(raw2) {
476749
+ function parseFrontmatter2(raw2) {
477150
476750
  if (!raw2.startsWith("---")) {
477151
476751
  return { frontmatter: {}, body: raw2 };
477152
476752
  }
@@ -477175,7 +476775,7 @@ function collectFiles2(memoryRoot) {
477175
476775
  const fileNodes = getFileNodes(treeNodes);
477176
476776
  return fileNodes.filter((n) => n.name.endsWith(".md")).map((n) => {
477177
476777
  const raw2 = readFileContent(n.fullPath);
477178
- const { frontmatter, body: body3 } = parseFrontmatter3(raw2);
476778
+ const { frontmatter, body: body3 } = parseFrontmatter2(raw2);
477179
476779
  return {
477180
476780
  path: n.relativePath,
477181
476781
  isSystem: n.relativePath.startsWith("system/") || n.relativePath.startsWith("system\\"),
@@ -477417,7 +477017,7 @@ ${m4.body}` : m4.subject;
477417
477017
  async function generateAndOpenMemoryViewer(agentId, options) {
477418
477018
  const memoryRoot = getScopedMemoryFilesystemRoot(agentId);
477419
477019
  const repoDir = memoryRoot;
477420
- if (!existsSync70(join90(repoDir, ".git"))) {
477020
+ if (!existsSync69(join89(repoDir, ".git"))) {
477421
477021
  throw new Error("Memory viewer requires memfs. Run /memfs enable first.");
477422
477022
  }
477423
477023
  const data = await collectMemoryData(agentId, repoDir, memoryRoot, options?.conversationId);
@@ -477427,13 +477027,13 @@ async function generateAndOpenMemoryViewer(agentId, options) {
477427
477027
  data.context = applyContextUsageSnapshot(data.context, options?.contextUsage);
477428
477028
  const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
477429
477029
  const html5 = memory_viewer_template_default.replace("<!--LETTA_DATA_PLACEHOLDER-->", () => jsonPayload);
477430
- if (!existsSync70(VIEWERS_DIR)) {
477030
+ if (!existsSync69(VIEWERS_DIR)) {
477431
477031
  mkdirSync47(VIEWERS_DIR, { recursive: true, mode: 448 });
477432
477032
  }
477433
477033
  try {
477434
477034
  chmodSync7(VIEWERS_DIR, 448);
477435
477035
  } catch {}
477436
- const filePath = join90(VIEWERS_DIR, `memory-${encodeURIComponent(agentId)}.html`);
477036
+ const filePath = join89(VIEWERS_DIR, `memory-${encodeURIComponent(agentId)}.html`);
477437
477037
  writeFileSync37(filePath, html5);
477438
477038
  chmodSync7(filePath, 384);
477439
477039
  const skipOpen = Boolean(process.env.TMUX) || Boolean(process.env.SSH_CONNECTION) || Boolean(process.env.SSH_TTY);
@@ -477459,13 +477059,13 @@ var init_generate_memory_viewer = __esm(() => {
477459
477059
  init_local_memory_context();
477460
477060
  init_memory_viewer_template();
477461
477061
  execFile20 = promisify18(execFileCb7);
477462
- VIEWERS_DIR = join90(homedir51(), ".letta", "viewers");
477062
+ VIEWERS_DIR = join89(homedir51(), ".letta", "viewers");
477463
477063
  REFLECTION_PATTERN = /\(reflection\)|🔮|reflection:/i;
477464
477064
  });
477465
477065
 
477466
477066
  // src/cli/components/MemfsTreeViewer.tsx
477467
- import { existsSync as existsSync71 } from "node:fs";
477468
- import { join as join91 } from "node:path";
477067
+ import { existsSync as existsSync70 } from "node:fs";
477068
+ import { join as join90 } from "node:path";
477469
477069
  function renderTreePrefix(node) {
477470
477070
  let prefix = "";
477471
477071
  for (let i4 = 0;i4 < node.depth; i4++) {
@@ -477493,8 +477093,8 @@ function MemfsTreeViewer({
477493
477093
  const [status, setStatus] = import_react88.useState(null);
477494
477094
  const statusTimerRef = import_react88.useRef(null);
477495
477095
  const memoryRoot = getScopedMemoryFilesystemRoot(agentId);
477496
- const memoryExists = existsSync71(memoryRoot);
477497
- const hasGitRepo = import_react88.useMemo(() => existsSync71(join91(memoryRoot, ".git")), [memoryRoot]);
477096
+ const memoryExists = existsSync70(memoryRoot);
477097
+ const hasGitRepo = import_react88.useMemo(() => existsSync70(join90(memoryRoot, ".git")), [memoryRoot]);
477498
477098
  function showStatus(msg, durationMs) {
477499
477099
  if (statusTimerRef.current)
477500
477100
  clearTimeout(statusTimerRef.current);
@@ -479869,19 +479469,19 @@ var init_PersonalitySelector = __esm(async () => {
479869
479469
  });
479870
479470
 
479871
479471
  // src/utils/aws-credentials.ts
479872
- import { readFile as readFile32 } from "node:fs/promises";
479472
+ import { readFile as readFile31 } from "node:fs/promises";
479873
479473
  import { homedir as homedir52 } from "node:os";
479874
- import { join as join92 } from "node:path";
479474
+ import { join as join91 } from "node:path";
479875
479475
  async function parseAwsCredentials() {
479876
- const credentialsPath = join92(homedir52(), ".aws", "credentials");
479877
- const configPath = join92(homedir52(), ".aws", "config");
479476
+ const credentialsPath = join91(homedir52(), ".aws", "credentials");
479477
+ const configPath = join91(homedir52(), ".aws", "config");
479878
479478
  const profiles = new Map;
479879
479479
  try {
479880
- const content = await readFile32(credentialsPath, "utf-8");
479480
+ const content = await readFile31(credentialsPath, "utf-8");
479881
479481
  parseIniFile(content, profiles, false);
479882
479482
  } catch {}
479883
479483
  try {
479884
- const content = await readFile32(configPath, "utf-8");
479484
+ const content = await readFile31(configPath, "utf-8");
479885
479485
  parseIniFile(content, profiles, true);
479886
479486
  } catch {}
479887
479487
  return Array.from(profiles.values());
@@ -481453,8 +481053,8 @@ function SkillsDialog({ onClose, agentId }) {
481453
481053
  try {
481454
481054
  const { discoverSkills: discoverSkills2, SKILLS_DIR: SKILLS_DIR2 } = await Promise.resolve().then(() => (init_skills5(), exports_skills2));
481455
481055
  const { getSkillsDirectory: getSkillsDirectory2, getSkillSources: getSkillSources2 } = await Promise.resolve().then(() => (init_context(), exports_context));
481456
- const { join: join93 } = await import("node:path");
481457
- const skillsDir = getSkillsDirectory2() || join93(process.cwd(), SKILLS_DIR2);
481056
+ const { join: join92 } = await import("node:path");
481057
+ const skillsDir = getSkillsDirectory2() || join92(process.cwd(), SKILLS_DIR2);
481458
481058
  const result2 = await discoverSkills2(skillsDir, agentId, {
481459
481059
  sources: getSkillSources2()
481460
481060
  });
@@ -484852,15 +484452,15 @@ function ToolsetSelector({
484852
484452
  const solidLine = SOLID_LINE19.repeat(Math.max(terminalWidth, 10));
484853
484453
  const [showAll, setShowAll] = import_react103.useState(false);
484854
484454
  const [selectedIndex, setSelectedIndex] = import_react103.useState(0);
484855
- const featuredToolsets = import_react103.useMemo(() => toolsets.filter((toolset) => toolset.isFeatured), []);
484455
+ const featuredToolsets = import_react103.useMemo(() => TOOLSET_OPTIONS.filter((toolset) => toolset.is_featured), []);
484856
484456
  const visibleToolsets = import_react103.useMemo(() => {
484857
484457
  if (showAll)
484858
- return toolsets;
484458
+ return TOOLSET_OPTIONS;
484859
484459
  if (featuredToolsets.length > 0)
484860
484460
  return featuredToolsets;
484861
- return toolsets;
484461
+ return TOOLSET_OPTIONS;
484862
484462
  }, [featuredToolsets, showAll]);
484863
- const canToggleShowAll = featuredToolsets.length < toolsets.length;
484463
+ const canToggleShowAll = featuredToolsets.length < TOOLSET_OPTIONS.length;
484864
484464
  import_react103.useEffect(() => {
484865
484465
  if (selectedIndex >= visibleToolsets.length) {
484866
484466
  setSelectedIndex(Math.max(0, visibleToolsets.length - 1));
@@ -484944,10 +484544,11 @@ function ToolsetSelector({
484944
484544
  ]
484945
484545
  }, undefined, true, undefined, this);
484946
484546
  }
484947
- var import_react103, jsx_dev_runtime86, SOLID_LINE19 = "─", toolsets;
484547
+ var import_react103, jsx_dev_runtime86, SOLID_LINE19 = "─";
484948
484548
  var init_ToolsetSelector = __esm(async () => {
484949
484549
  init_use_terminal_width();
484950
484550
  init_toolset_labels();
484551
+ init_toolset_options();
484951
484552
  init_colors();
484952
484553
  await __promiseAll([
484953
484554
  init_build4(),
@@ -484955,48 +484556,6 @@ var init_ToolsetSelector = __esm(async () => {
484955
484556
  ]);
484956
484557
  import_react103 = __toESM(require_react(), 1);
484957
484558
  jsx_dev_runtime86 = __toESM(require_jsx_dev_runtime(), 1);
484958
- toolsets = [
484959
- {
484960
- id: "auto",
484961
- label: "Auto",
484962
- description: "Auto-select based on the model",
484963
- isFeatured: true
484964
- },
484965
- {
484966
- id: "none",
484967
- label: "None",
484968
- description: "Remove all Letta Code tools from your agent",
484969
- isFeatured: true
484970
- },
484971
- {
484972
- id: "default",
484973
- label: "Claude toolset",
484974
- description: "Optimized for Anthropic models",
484975
- isFeatured: true
484976
- },
484977
- {
484978
- id: "codex",
484979
- label: "Codex toolset",
484980
- description: "Optimized for GPT/Codex models",
484981
- isFeatured: true
484982
- },
484983
- {
484984
- id: "gemini",
484985
- label: "Gemini toolset",
484986
- description: "Optimized for Google Gemini models",
484987
- isFeatured: true
484988
- },
484989
- {
484990
- id: "codex_snake",
484991
- label: "Codex toolset (snake_case)",
484992
- description: "Optimized for GPT/Codex models (snake_case)"
484993
- },
484994
- {
484995
- id: "gemini_snake",
484996
- label: "Gemini toolset (snake_case)",
484997
- description: "Optimized for Google Gemini models (snake_case)"
484998
- }
484999
- ];
485000
484559
  });
485001
484560
 
485002
484561
  // src/cli/components/UserMessageRich.tsx
@@ -485616,9 +485175,9 @@ function getFileEditHeader(toolName2, toolArgs) {
485616
485175
  } else if (operations.length === 1) {
485617
485176
  const op = operations[0];
485618
485177
  if (op) {
485619
- const { relative: relative18 } = __require("node:path");
485178
+ const { relative: relative17 } = __require("node:path");
485620
485179
  const cwd3 = process.cwd();
485621
- const relPath2 = relative18(cwd3, op.path);
485180
+ const relPath2 = relative17(cwd3, op.path);
485622
485181
  const displayPath3 = relPath2.startsWith("..") ? op.path : relPath2;
485623
485182
  if (op.kind === "add")
485624
485183
  return `Write to ${displayPath3}?`;
@@ -485632,14 +485191,14 @@ function getFileEditHeader(toolName2, toolArgs) {
485632
485191
  return "Apply patch?";
485633
485192
  }
485634
485193
  const filePath = args.file_path || "";
485635
- const { relative: relative17 } = __require("node:path");
485194
+ const { relative: relative16 } = __require("node:path");
485636
485195
  const cwd2 = process.cwd();
485637
- const relPath = relative17(cwd2, filePath);
485196
+ const relPath = relative16(cwd2, filePath);
485638
485197
  const displayPath2 = relPath.startsWith("..") ? filePath : relPath;
485639
485198
  if (t2 === "write" || t2 === "write_file" || t2 === "writefile" || t2 === "write_file_gemini" || t2 === "writefilegemini") {
485640
- const { existsSync: existsSync72 } = __require("node:fs");
485199
+ const { existsSync: existsSync71 } = __require("node:fs");
485641
485200
  try {
485642
- if (existsSync72(filePath)) {
485201
+ if (existsSync71(filePath)) {
485643
485202
  return `Overwrite ${displayPath2}?`;
485644
485203
  }
485645
485204
  } catch {}
@@ -485722,9 +485281,9 @@ var init_ApprovalPreview = __esm(async () => {
485722
485281
  /* @__PURE__ */ jsx_dev_runtime92.jsxDEV(Box_default, {
485723
485282
  flexDirection: "column",
485724
485283
  children: operations.map((op, idx) => {
485725
- const { relative: relative17 } = __require("node:path");
485284
+ const { relative: relative16 } = __require("node:path");
485726
485285
  const cwd2 = process.cwd();
485727
- const relPath = relative17(cwd2, op.path);
485286
+ const relPath = relative16(cwd2, op.path);
485728
485287
  const displayPath2 = relPath.startsWith("..") ? op.path : relPath;
485729
485288
  const diffKey = toolCallId ? `${toolCallId}:${op.path}` : undefined;
485730
485289
  const opDiff = diffKey && allDiffs ? allDiffs.get(diffKey) : undefined;
@@ -500875,9 +500434,9 @@ __export(exports_generate_diff_viewer, {
500875
500434
  generateAndOpenDiffViewer: () => generateAndOpenDiffViewer
500876
500435
  });
500877
500436
  import { execFile as execFileCb8 } from "node:child_process";
500878
- import { chmodSync as chmodSync8, existsSync as existsSync72, mkdirSync as mkdirSync48, writeFileSync as writeFileSync38 } from "node:fs";
500437
+ import { chmodSync as chmodSync8, existsSync as existsSync71, mkdirSync as mkdirSync48, writeFileSync as writeFileSync38 } from "node:fs";
500879
500438
  import { homedir as homedir53 } from "node:os";
500880
- import { isAbsolute as isAbsolute32, join as join93, resolve as resolve43 } from "node:path";
500439
+ import { isAbsolute as isAbsolute31, join as join92, resolve as resolve41 } from "node:path";
500881
500440
  import { promisify as promisify19 } from "node:util";
500882
500441
  async function runGit8(cwd2, args) {
500883
500442
  try {
@@ -501075,7 +500634,7 @@ function escapeHtml3(value) {
501075
500634
  function resolveTargetPath(targetPath) {
501076
500635
  if (!targetPath?.trim())
501077
500636
  return process.cwd();
501078
- return isAbsolute32(targetPath) ? targetPath : resolve43(process.cwd(), targetPath);
500637
+ return isAbsolute31(targetPath) ? targetPath : resolve41(process.cwd(), targetPath);
501079
500638
  }
501080
500639
  function shouldSkipOpen() {
501081
500640
  return Boolean(process.env.TMUX) || Boolean(process.env.SSH_CONNECTION) || Boolean(process.env.SSH_TTY);
@@ -501096,13 +500655,13 @@ async function generateAndOpenDiffViewer(targetPath) {
501096
500655
  };
501097
500656
  const jsonPayload = JSON.stringify(payload).replace(/</g, "\\u003c");
501098
500657
  const html5 = diff_viewer_template_default.replace("<!--LETTA_DIFF_DATA_PLACEHOLDER-->", () => jsonPayload);
501099
- if (!existsSync72(VIEWERS_DIR2)) {
500658
+ if (!existsSync71(VIEWERS_DIR2)) {
501100
500659
  mkdirSync48(VIEWERS_DIR2, { recursive: true, mode: 448 });
501101
500660
  }
501102
500661
  try {
501103
500662
  chmodSync8(VIEWERS_DIR2, 448);
501104
500663
  } catch {}
501105
- const filePath = join93(VIEWERS_DIR2, `diff-${encodeURIComponent(worktreePath)}.html`);
500664
+ const filePath = join92(VIEWERS_DIR2, `diff-${encodeURIComponent(worktreePath)}.html`);
501106
500665
  writeFileSync38(filePath, html5);
501107
500666
  chmodSync8(filePath, 384);
501108
500667
  const skipOpen = shouldSkipOpen();
@@ -501173,7 +500732,7 @@ var init_generate_diff_viewer = __esm(() => {
501173
500732
  init_ssr();
501174
500733
  init_diff_viewer_template();
501175
500734
  execFile21 = promisify19(execFileCb8);
501176
- VIEWERS_DIR2 = join93(homedir53(), ".letta", "viewers");
500735
+ VIEWERS_DIR2 = join92(homedir53(), ".letta", "viewers");
501177
500736
  GIT_MAX_BUFFER = 50 * 1024 * 1024;
501178
500737
  });
501179
500738
 
@@ -503136,16 +502695,16 @@ __export(exports_shell_aliases, {
503136
502695
  expandAliases: () => expandAliases,
503137
502696
  clearAliasCache: () => clearAliasCache
503138
502697
  });
503139
- import { existsSync as existsSync73, readFileSync as readFileSync45 } from "node:fs";
502698
+ import { existsSync as existsSync72, readFileSync as readFileSync44 } from "node:fs";
503140
502699
  import { homedir as homedir54 } from "node:os";
503141
- import { join as join94 } from "node:path";
502700
+ import { join as join93 } from "node:path";
503142
502701
  function parseAliasesFromFile(filePath) {
503143
502702
  const aliases = new Map;
503144
- if (!existsSync73(filePath)) {
502703
+ if (!existsSync72(filePath)) {
503145
502704
  return aliases;
503146
502705
  }
503147
502706
  try {
503148
- const content = readFileSync45(filePath, "utf-8");
502707
+ const content = readFileSync44(filePath, "utf-8");
503149
502708
  const lines = content.split(`
503150
502709
  `);
503151
502710
  let inFunction = false;
@@ -503210,7 +502769,7 @@ function loadAliases(forceReload = false) {
503210
502769
  const home = homedir54();
503211
502770
  const allAliases = new Map;
503212
502771
  for (const file of ALIAS_FILES) {
503213
- const filePath = join94(home, file);
502772
+ const filePath = join93(home, file);
503214
502773
  const fileAliases = parseAliasesFromFile(filePath);
503215
502774
  for (const [name, value] of fileAliases) {
503216
502775
  allAliases.set(name, value);
@@ -504363,7 +503922,7 @@ var init_system_reminders = __esm(() => {
504363
503922
  // src/cli/app/use-conversation-loop.ts
504364
503923
  import { randomUUID as randomUUID41 } from "node:crypto";
504365
503924
  function sleep11(ms) {
504366
- return new Promise((resolve44) => setTimeout(resolve44, ms));
503925
+ return new Promise((resolve42) => setTimeout(resolve42, ms));
504367
503926
  }
504368
503927
  function makeExecutionPhaseHook(setExecutionPhase) {
504369
503928
  return ({ chunk: chunk2 }) => {
@@ -504819,7 +504378,7 @@ function useConversationLoop(ctx) {
504819
504378
  cancelled = true;
504820
504379
  break;
504821
504380
  }
504822
- await new Promise((resolve44) => setTimeout(resolve44, 100));
504381
+ await new Promise((resolve42) => setTimeout(resolve42, 100));
504823
504382
  }
504824
504383
  buffersRef.current.byId.delete(statusId);
504825
504384
  buffersRef.current.order = buffersRef.current.order.filter((id2) => id2 !== statusId);
@@ -504863,7 +504422,7 @@ function useConversationLoop(ctx) {
504863
504422
  cancelled = true;
504864
504423
  break;
504865
504424
  }
504866
- await new Promise((resolve44) => setTimeout(resolve44, 100));
504425
+ await new Promise((resolve42) => setTimeout(resolve42, 100));
504867
504426
  }
504868
504427
  if (retryStatusId) {
504869
504428
  buffersRef.current.byId.delete(retryStatusId);
@@ -505681,7 +505240,7 @@ ${feedback}
505681
505240
  });
505682
505241
  buffersRef.current.order.push(statusId);
505683
505242
  refreshDerived();
505684
- await new Promise((resolve44) => setTimeout(resolve44, delayMs));
505243
+ await new Promise((resolve42) => setTimeout(resolve42, delayMs));
505685
505244
  buffersRef.current.byId.delete(statusId);
505686
505245
  buffersRef.current.order = buffersRef.current.order.filter((id2) => id2 !== statusId);
505687
505246
  refreshDerived();
@@ -505721,7 +505280,7 @@ ${feedback}
505721
505280
  cancelled = true;
505722
505281
  break;
505723
505282
  }
505724
- await new Promise((resolve44) => setTimeout(resolve44, 100));
505283
+ await new Promise((resolve42) => setTimeout(resolve42, 100));
505725
505284
  }
505726
505285
  if (retryStatusId) {
505727
505286
  buffersRef.current.byId.delete(retryStatusId);
@@ -507467,7 +507026,7 @@ The deployment target for this project is CITATION-DOGFOOD-OK.
507467
507026
 
507468
507027
  // src/mods/learning-harness.ts
507469
507028
  import { spawn as spawn14 } from "node:child_process";
507470
- import { access as access4, copyFile as copyFile2, mkdir as mkdir20, readFile as readFile33, writeFile as writeFile22 } from "node:fs/promises";
507029
+ import { access as access3, copyFile as copyFile2, mkdir as mkdir19, readFile as readFile32, writeFile as writeFile21 } from "node:fs/promises";
507471
507030
  import path51 from "node:path";
507472
507031
  function slugify2(value) {
507473
507032
  const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -507491,9 +507050,9 @@ function hasText(value) {
507491
507050
  return typeof value === "string" && value.trim().length > 0;
507492
507051
  }
507493
507052
  function markerChecks(markers, haystack) {
507494
- return (markers ?? []).map((marker) => ({
507495
- marker,
507496
- present: haystack.includes(marker)
507053
+ return (markers ?? []).map((marker2) => ({
507054
+ marker: marker2,
507055
+ present: haystack.includes(marker2)
507497
507056
  }));
507498
507057
  }
507499
507058
  function asStringArray(value) {
@@ -507705,7 +507264,7 @@ function safeJoin(root2, relativePath) {
507705
507264
  }
507706
507265
  async function fileExists(filePath) {
507707
507266
  try {
507708
- await access4(filePath);
507267
+ await access3(filePath);
507709
507268
  return true;
507710
507269
  } catch {
507711
507270
  return false;
@@ -507715,22 +507274,22 @@ async function existingPath(filePath) {
507715
507274
  return await fileExists(filePath) ? filePath : undefined;
507716
507275
  }
507717
507276
  async function writeJsonArtifact(filePath, value) {
507718
- await writeFile22(filePath, `${JSON.stringify(value, null, 2)}
507277
+ await writeFile21(filePath, `${JSON.stringify(value, null, 2)}
507719
507278
  `, "utf8");
507720
507279
  }
507721
507280
  async function writeCommandArtifacts(prefix, command, args, result2) {
507722
- await writeFile22(`${prefix}.command.txt`, `${renderCommand(command, args)}
507281
+ await writeFile21(`${prefix}.command.txt`, `${renderCommand(command, args)}
507723
507282
  `, "utf8");
507724
- await writeFile22(`${prefix}.stdout`, result2.stdout, "utf8");
507725
- await writeFile22(`${prefix}.stderr`, result2.stderr, "utf8");
507283
+ await writeFile21(`${prefix}.stdout`, result2.stdout, "utf8");
507284
+ await writeFile21(`${prefix}.stderr`, result2.stderr, "utf8");
507726
507285
  await writeJsonArtifact(`${prefix}.result.json`, result2);
507727
507286
  }
507728
507287
  async function prepareMemoryFiles(memoryDir, memoryFiles) {
507729
- await mkdir20(memoryDir, { recursive: true });
507288
+ await mkdir19(memoryDir, { recursive: true });
507730
507289
  for (const [relativePath, content] of Object.entries(memoryFiles ?? {})) {
507731
507290
  const filePath = safeJoin(memoryDir, relativePath);
507732
- await mkdir20(path51.dirname(filePath), { recursive: true });
507733
- await writeFile22(filePath, content, "utf8");
507291
+ await mkdir19(path51.dirname(filePath), { recursive: true });
507292
+ await writeFile21(filePath, content, "utf8");
507734
507293
  }
507735
507294
  }
507736
507295
  function renderEvaluationPrompt(prompt, memoryDir) {
@@ -508393,7 +507952,7 @@ function renderProposerGuide(params) {
508393
507952
  `;
508394
507953
  }
508395
507954
  async function writeHistoryArtifacts(params) {
508396
- await writeFile22(params.historyPath, renderHistoryIndex({
507955
+ await writeFile21(params.historyPath, renderHistoryIndex({
508397
507956
  attempts: params.attempts,
508398
507957
  historyManifestPath: params.historyManifestPath,
508399
507958
  proposerGuidePath: params.proposerGuidePath,
@@ -508401,11 +507960,11 @@ async function writeHistoryArtifacts(params) {
508401
507960
  spec: params.spec
508402
507961
  }), "utf8");
508403
507962
  await writeJsonArtifact(params.historyManifestPath, buildHistoryManifest(params));
508404
- await writeFile22(params.proposerGuidePath, renderProposerGuide(params), "utf8");
507963
+ await writeFile21(params.proposerGuidePath, renderProposerGuide(params), "utf8");
508405
507964
  }
508406
507965
  async function defaultCommandRunner(command, args, options) {
508407
507966
  const startedAt = Date.now();
508408
- return new Promise((resolve44) => {
507967
+ return new Promise((resolve42) => {
508409
507968
  const child = spawn14(command, args, {
508410
507969
  cwd: options.cwd,
508411
507970
  env: options.env,
@@ -508432,7 +507991,7 @@ async function defaultCommandRunner(command, args, options) {
508432
507991
  });
508433
507992
  child.on("close", (exitCode) => {
508434
507993
  clearTimeout(timeout);
508435
- resolve44({
507994
+ resolve42({
508436
507995
  args,
508437
507996
  command,
508438
507997
  cwd: options.cwd,
@@ -508492,7 +508051,7 @@ function createScenarioSuiteEvaluator(params) {
508492
508051
  outputFormat
508493
508052
  })
508494
508053
  ];
508495
- await writeFile22(hasConfiguredScenarios ? path51.join(scenarioDir, "prompt.md") : path51.join(context3.runDir, "eval-prompt.md"), evalPrompt, "utf8");
508054
+ await writeFile21(hasConfiguredScenarios ? path51.join(scenarioDir, "prompt.md") : path51.join(context3.runDir, "eval-prompt.md"), evalPrompt, "utf8");
508496
508055
  const scenarioEvalResult = await context3.runner(context3.cliCommand, evalArgs, {
508497
508056
  cwd: context3.repoRoot,
508498
508057
  env: {
@@ -508654,7 +508213,7 @@ async function runModLearningCandidate(params) {
508654
508213
  });
508655
508214
  };
508656
508215
  emitProgress("preparing", params.candidateCount > 1 ? `Preparing optimization iteration ${params.candidateIndex}/${params.candidateCount}` : "Preparing mod learning run");
508657
- await mkdir20(candidateDir, { recursive: true });
508216
+ await mkdir19(candidateDir, { recursive: true });
508658
508217
  await writeJsonArtifact(path51.join(runDir, "env.snapshot.json"), options.spec);
508659
508218
  let generationResult = null;
508660
508219
  if (options.candidateSourcePath) {
@@ -508684,7 +508243,7 @@ async function runModLearningCandidate(params) {
508684
508243
  outputFormat: "json"
508685
508244
  })
508686
508245
  ];
508687
- await writeFile22(path51.join(runDir, "generation-prompt.md"), generationPrompt, "utf8");
508246
+ await writeFile21(path51.join(runDir, "generation-prompt.md"), generationPrompt, "utf8");
508688
508247
  generationResult = await params.runner(params.cliCommand, generationArgs, {
508689
508248
  cwd: repoRoot,
508690
508249
  env: {
@@ -508741,7 +508300,7 @@ async function runModLearningCandidate(params) {
508741
508300
  if (passed && options.promoteToPath) {
508742
508301
  emitProgress("promoting", "Promoting passing candidate mod");
508743
508302
  promotedToPath = path51.resolve(repoRoot, options.promoteToPath);
508744
- await mkdir20(path51.dirname(promotedToPath), { recursive: true });
508303
+ await mkdir19(path51.dirname(promotedToPath), { recursive: true });
508745
508304
  await copyFile2(candidatePath, promotedToPath);
508746
508305
  }
508747
508306
  const reportPath = path51.join(runDir, "report.md");
@@ -508763,7 +508322,7 @@ async function runModLearningCandidate(params) {
508763
508322
  spec: options.spec
508764
508323
  };
508765
508324
  await writeJsonArtifact(path51.join(runDir, "report.json"), report);
508766
- await writeFile22(reportPath, renderMarkdownReport(report), "utf8");
508325
+ await writeFile21(reportPath, renderMarkdownReport(report), "utf8");
508767
508326
  await writeCandidateManifest(report);
508768
508327
  emitProgress("done", params.candidateCount > 1 ? `Optimization iteration ${params.candidateIndex}/${params.candidateCount} complete` : "mod optimization complete", {
508769
508328
  attempts: [...params.previousAttempts, summarizeAttempt(report)],
@@ -508792,7 +508351,7 @@ async function runModLearning(options) {
508792
508351
  const cliCommand = normalizedOptions.cliCommand ?? "bun";
508793
508352
  const cliArgsPrefix = normalizedOptions.cliArgsPrefix ?? ["run", "dev"];
508794
508353
  const baseEnv = normalizedOptions.env ?? process.env;
508795
- await mkdir20(runDir, { recursive: true });
508354
+ await mkdir19(runDir, { recursive: true });
508796
508355
  await writeJsonArtifact(path51.join(runDir, "env.snapshot.json"), normalizedOptions.spec);
508797
508356
  if (candidateCount === 1) {
508798
508357
  const report2 = await runModLearningCandidate({
@@ -508903,7 +508462,7 @@ async function runModLearning(options) {
508903
508462
  selectedCandidateIndex
508904
508463
  });
508905
508464
  promotedToPath = path51.resolve(repoRoot, normalizedOptions.promoteToPath);
508906
- await mkdir20(path51.dirname(promotedToPath), { recursive: true });
508465
+ await mkdir19(path51.dirname(promotedToPath), { recursive: true });
508907
508466
  await copyFile2(selectedReport.candidatePath, promotedToPath);
508908
508467
  }
508909
508468
  const reportPath = path51.join(runDir, "report.md");
@@ -508941,7 +508500,7 @@ async function runModLearning(options) {
508941
508500
  spec: normalizedOptions.spec
508942
508501
  });
508943
508502
  await writeJsonArtifact(path51.join(runDir, "report.json"), report);
508944
- await writeFile22(reportPath, renderMarkdownReport(report), "utf8");
508503
+ await writeFile21(reportPath, renderMarkdownReport(report), "utf8");
508945
508504
  normalizedOptions.onProgress?.({
508946
508505
  candidateCount,
508947
508506
  candidateIndex: selectedCandidateIndex,
@@ -508959,7 +508518,7 @@ async function runModLearning(options) {
508959
508518
  return report;
508960
508519
  }
508961
508520
  async function readModLearningEnv(envPath) {
508962
- return JSON.parse(await readFile33(envPath, "utf8"));
508521
+ return JSON.parse(await readFile32(envPath, "utf8"));
508963
508522
  }
508964
508523
  var init_learning_harness = __esm(async () => {
508965
508524
  await init_mod_engine();
@@ -509669,8 +509228,6 @@ var init_command_routing = __esm(() => {
509669
509228
  "/search",
509670
509229
  "/memory",
509671
509230
  "/feedback",
509672
- "/export",
509673
- "/download",
509674
509231
  "/mods",
509675
509232
  "/reasoning-tab",
509676
509233
  "/secret",
@@ -511199,7 +510756,7 @@ __export(exports_worktree_diff_list, {
511199
510756
  listWorktreeDiffOptions: () => listWorktreeDiffOptions
511200
510757
  });
511201
510758
  import { execFile as execFileCb9 } from "node:child_process";
511202
- import { basename as basename31 } from "node:path";
510759
+ import { basename as basename30 } from "node:path";
511203
510760
  import { promisify as promisify20 } from "node:util";
511204
510761
  async function runGit9(cwd2, args) {
511205
510762
  try {
@@ -511248,7 +510805,7 @@ function parseWorktreeList(output, currentPath) {
511248
510805
  if (current?.path) {
511249
510806
  worktrees.push({
511250
510807
  path: current.path,
511251
- name: basename31(current.path),
510808
+ name: basename30(current.path),
511252
510809
  branch: current.branch ?? "detached",
511253
510810
  head: current.head ?? "",
511254
510811
  isCurrent: current.path === currentPath,
@@ -511274,7 +510831,7 @@ function parseWorktreeList(output, currentPath) {
511274
510831
  if (current?.path) {
511275
510832
  worktrees.push({
511276
510833
  path: current.path,
511277
- name: basename31(current.path),
510834
+ name: basename30(current.path),
511278
510835
  branch: current.branch ?? "detached",
511279
510836
  head: current.head ?? "",
511280
510837
  isCurrent: current.path === currentPath,
@@ -511338,107 +510895,6 @@ var init_worktree_diff_list = __esm(() => {
511338
510895
  GIT_MAX_BUFFER2 = 10 * 1024 * 1024;
511339
510896
  });
511340
510897
 
511341
- // src/agent/export.ts
511342
- var exports_export = {};
511343
- __export(exports_export, {
511344
- packageSkills: () => packageSkills
511345
- });
511346
- import { readdir as readdir17, readFile as readFile34 } from "node:fs/promises";
511347
- import { relative as relative17, resolve as resolve44 } from "node:path";
511348
- async function packageSkills(agentId, skillsDir) {
511349
- const skills = [];
511350
- const skillNames = new Set;
511351
- const dirsToCheck = skillsDir ? [skillsDir] : [
511352
- agentId && getAgentSkillsDir(agentId),
511353
- resolve44(process.cwd(), ".skills"),
511354
- resolve44(process.env.HOME || "~", ".letta", "skills")
511355
- ].filter((dir) => Boolean(dir));
511356
- for (const baseDir of dirsToCheck) {
511357
- try {
511358
- const entries = await readdir17(baseDir, { withFileTypes: true });
511359
- for (const entry of entries) {
511360
- if (!entry.isDirectory())
511361
- continue;
511362
- if (skillNames.has(entry.name))
511363
- continue;
511364
- const skillDir = resolve44(baseDir, entry.name);
511365
- const skillMdPath = resolve44(skillDir, "SKILL.md");
511366
- try {
511367
- await readFile34(skillMdPath, "utf-8");
511368
- } catch {
511369
- console.warn(`Skipping invalid skill ${entry.name}: missing SKILL.md`);
511370
- continue;
511371
- }
511372
- const sourceUrl = skillsDir ? null : await findSkillSourceUrl(entry.name);
511373
- const skill2 = { name: entry.name };
511374
- if (sourceUrl) {
511375
- skill2.source_url = sourceUrl;
511376
- } else {
511377
- skill2.files = await readSkillFiles(skillDir);
511378
- }
511379
- skills.push(skill2);
511380
- skillNames.add(entry.name);
511381
- }
511382
- } catch (error5) {
511383
- if (error5.code !== "ENOENT") {
511384
- throw error5;
511385
- }
511386
- }
511387
- }
511388
- return skills;
511389
- }
511390
- async function readSkillFiles(skillDir) {
511391
- const files = {};
511392
- async function walk3(dir) {
511393
- const entries = await readdir17(dir, { withFileTypes: true });
511394
- for (const entry of entries) {
511395
- const fullPath = resolve44(dir, entry.name);
511396
- if (entry.isDirectory()) {
511397
- await walk3(fullPath);
511398
- } else {
511399
- const content = await readFile34(fullPath, "utf-8");
511400
- const relativePath = relative17(skillDir, fullPath).replace(/\\/g, "/");
511401
- files[relativePath] = content;
511402
- }
511403
- }
511404
- }
511405
- await walk3(skillDir);
511406
- return files;
511407
- }
511408
- async function findSkillSourceUrl(skillName) {
511409
- for (const repoPath of SKILL_REPOS) {
511410
- if (!dirCache.has(repoPath)) {
511411
- dirCache.set(repoPath, await fetchGitHubDirs(repoPath));
511412
- }
511413
- if (dirCache.get(repoPath)?.has(skillName)) {
511414
- return `${repoPath}/${skillName}`;
511415
- }
511416
- }
511417
- return null;
511418
- }
511419
- async function fetchGitHubDirs(path53) {
511420
- const [owner, repo, branch, ...pathParts] = path53.split("/");
511421
- if (!owner || !repo || !branch)
511422
- return new Set;
511423
- try {
511424
- const { fetchGitHubContents: fetchGitHubContents2, parseDirNames: parseDirNames2 } = await Promise.resolve().then(() => exports_github_utils);
511425
- const entries = await fetchGitHubContents2(owner, repo, branch, pathParts.join("/"));
511426
- return parseDirNames2(entries);
511427
- } catch {
511428
- return new Set;
511429
- }
511430
- }
511431
- var SKILL_REPOS, dirCache;
511432
- var init_export2 = __esm(() => {
511433
- init_skills5();
511434
- SKILL_REPOS = [
511435
- "letta-ai/skills/main/tools",
511436
- "letta-ai/skills/main/letta",
511437
- "anthropics/skills/main/skills"
511438
- ];
511439
- dirCache = new Map;
511440
- });
511441
-
511442
510898
  // src/cli/helpers/conversation-switch-alert.ts
511443
510899
  var exports_conversation_switch_alert = {};
511444
510900
  __export(exports_conversation_switch_alert, {
@@ -511532,9 +510988,9 @@ var init_conversation_switch_alert = __esm(() => {
511532
510988
 
511533
510989
  // src/cli/app/use-submit-handler.ts
511534
510990
  import { randomUUID as randomUUID43 } from "node:crypto";
511535
- import { existsSync as existsSync74, readFileSync as readFileSync46, renameSync as renameSync8, writeFileSync as writeFileSync39 } from "node:fs";
510991
+ import { existsSync as existsSync73, readFileSync as readFileSync45, renameSync as renameSync8 } from "node:fs";
511536
510992
  import { tmpdir as tmpdir13 } from "node:os";
511537
- import { join as join95 } from "node:path";
510993
+ import { join as join94 } from "node:path";
511538
510994
  async function findCustomCommandByName(commandName) {
511539
510995
  const { findCustomCommand: findCustomCommand2 } = await Promise.resolve().then(() => (init_custom(), exports_custom));
511540
510996
  return findCustomCommand2(commandName);
@@ -512234,12 +511690,12 @@ ${SYSTEM_REMINDER_CLOSE}`),
512234
511690
  try {
512235
511691
  const memoryRoot = getScopedMemoryFilesystemRoot(agentId);
512236
511692
  const personaCandidates = [
512237
- join95(memoryRoot, "system", "persona.md"),
512238
- join95(memoryRoot, "memory", "system", "persona.md")
511693
+ join94(memoryRoot, "system", "persona.md"),
511694
+ join94(memoryRoot, "memory", "system", "persona.md")
512239
511695
  ];
512240
- const personaPath = personaCandidates.find((candidate) => existsSync74(candidate));
511696
+ const personaPath = personaCandidates.find((candidate) => existsSync73(candidate));
512241
511697
  if (personaPath) {
512242
- const personaContent = readFileSync46(personaPath, "utf-8");
511698
+ const personaContent = readFileSync45(personaPath, "utf-8");
512243
511699
  setCurrentPersonalityId(detectPersonalityFromPersonaFile(personaContent));
512244
511700
  } else {
512245
511701
  setCurrentPersonalityId(null);
@@ -512991,42 +512447,6 @@ Tip: Use /clear instead to clear the current message buffer.`;
512991
512447
  cmd.finish(output, true);
512992
512448
  return { submitted: true };
512993
512449
  }
512994
- if (msg.trim() === "/export" || msg.trim() === "/download") {
512995
- const cmd = commandRunner.start(msg.trim(), "Exporting agent file...");
512996
- if (!getBackend().capabilities.agentFileImportExport) {
512997
- cmd.fail("AgentFile export is not supported by the local backend yet.");
512998
- return { submitted: true };
512999
- }
513000
- setCommandRunning(true);
513001
- try {
513002
- const client = await getClient();
513003
- const exportParams = {};
513004
- if (conversationId !== "default" && conversationId !== agentId) {
513005
- exportParams.conversation_id = conversationId;
513006
- }
513007
- const { packageSkills: packageSkills2 } = await Promise.resolve().then(() => (init_export2(), exports_export));
513008
- const skills = await packageSkills2(agentId);
513009
- const baseContent = await client.agents.exportFile(agentId, exportParams);
513010
- const fileContent = typeof baseContent === "string" ? JSON.parse(baseContent) : baseContent;
513011
- if (skills.length > 0) {
513012
- fileContent.skills = skills;
513013
- }
513014
- const fileName = exportParams.conversation_id ? `${exportParams.conversation_id}.af` : `${agentId}.af`;
513015
- writeFileSync39(fileName, JSON.stringify(fileContent, null, 2));
513016
- let summary = `AgentFile exported to ${fileName}`;
513017
- if (skills.length > 0) {
513018
- summary += `
513019
- \uD83D\uDCE6 Included ${skills.length} skill(s): ${skills.map((s3) => s3.name).join(", ")}`;
513020
- }
513021
- cmd.finish(summary, true);
513022
- } catch (error5) {
513023
- const errorDetails = formatErrorDetails2(error5, agentId);
513024
- cmd.fail(`Failed: ${errorDetails}`);
513025
- } finally {
513026
- setCommandRunning(false);
513027
- }
513028
- return { submitted: true };
513029
- }
513030
512450
  if (trimmed.startsWith("/memfs")) {
513031
512451
  const [, subcommand] = trimmed.split(/\s+/);
513032
512452
  const cmd = commandRunner.start(msg.trim(), "Processing memfs command...");
@@ -513119,11 +512539,11 @@ Path: ${memoryDir}`, true);
513119
512539
  setCommandRunning(true);
513120
512540
  try {
513121
512541
  const memoryDir = getScopedMemoryFilesystemRoot(agentId);
513122
- if (!existsSync74(memoryDir)) {
512542
+ if (!existsSync73(memoryDir)) {
513123
512543
  updateMemorySyncCommand(cmdId, "No local memory filesystem found to reset.", true, msg);
513124
512544
  return { submitted: true };
513125
512545
  }
513126
- const backupDir = join95(tmpdir13(), `letta-memfs-reset-${agentId}-${Date.now()}`);
512546
+ const backupDir = join94(tmpdir13(), `letta-memfs-reset-${agentId}-${Date.now()}`);
513127
512547
  renameSync8(memoryDir, backupDir);
513128
512548
  if (getBackend().capabilities.localMemfs) {
513129
512549
  const { initializeLocalMemoryRepo: initializeLocalMemoryRepo2 } = await Promise.resolve().then(() => (init_memory_git(), exports_memory_git));
@@ -514079,7 +513499,7 @@ var init_use_submit_handler = __esm(async () => {
514079
513499
  });
514080
513500
 
514081
513501
  // src/cli/app/AppCoordinator.tsx
514082
- import { join as join96 } from "node:path";
513502
+ import { join as join95 } from "node:path";
514083
513503
  function buildStartupCommandHints(options) {
514084
513504
  const {
514085
513505
  isResumingConversation,
@@ -515366,7 +514786,7 @@ function App2({
515366
514786
  agentId: a2.agentId ?? null
515367
514787
  }))
515368
514788
  });
515369
- const agentModsDirectory = modContext.memfs.enabled && modContext.memfs.memoryDir ? join96(modContext.memfs.memoryDir, "mods") : null;
514789
+ const agentModsDirectory = modContext.memfs.enabled && modContext.memfs.memoryDir ? join95(modContext.memfs.memoryDir, "mods") : null;
515370
514790
  const modAdapter = useLocalModAdapter(modContext, {
515371
514791
  agentModsDirectory,
515372
514792
  disabled: modsDisabled,
@@ -516137,9 +515557,9 @@ Memory may be stale. Try running: git -C ${getScopedMemoryFilesystemRoot(agentId
516137
515557
  (async () => {
516138
515558
  try {
516139
515559
  const { watch: watch4 } = await import("node:fs");
516140
- const { existsSync: existsSync75 } = await import("node:fs");
515560
+ const { existsSync: existsSync74 } = await import("node:fs");
516141
515561
  const memRoot = getScopedMemoryFilesystemRoot(agentId);
516142
- if (!existsSync75(memRoot))
515562
+ if (!existsSync74(memRoot))
516143
515563
  return;
516144
515564
  watcher2 = watch4(memRoot, { recursive: true }, () => {});
516145
515565
  memfsWatcherRef.current = watcher2;
@@ -517714,12 +517134,12 @@ EXAMPLES
517714
517134
  console.log(usage);
517715
517135
  }
517716
517136
  async function printInfo() {
517717
- const { join: join97 } = await import("node:path");
517137
+ const { join: join96 } = await import("node:path");
517718
517138
  const { getVersion: getVersion2 } = await Promise.resolve().then(() => (init_version(), exports_version));
517719
517139
  const { SKILLS_DIR: SKILLS_DIR2 } = await Promise.resolve().then(() => (init_skills5(), exports_skills2));
517720
517140
  const { exists: exists2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
517721
517141
  const cwd2 = process.cwd();
517722
- const skillsDir = join97(cwd2, SKILLS_DIR2);
517142
+ const skillsDir = join96(cwd2, SKILLS_DIR2);
517723
517143
  const skillsExist = exists2(skillsDir);
517724
517144
  await settingsManager.loadLocalProjectSettings(cwd2);
517725
517145
  const pinned = settingsManager.getPinnedAgents();
@@ -517769,18 +517189,6 @@ async function printInfo() {
517769
517189
  console.log("Pinned agents: (none)");
517770
517190
  }
517771
517191
  }
517772
- function getModelForToolLoading(specifiedModel, specifiedToolset) {
517773
- if (specifiedToolset === "codex") {
517774
- return "openai/gpt-4";
517775
- }
517776
- if (specifiedToolset === "gemini") {
517777
- return "google_ai/gemini-3.1-pro-preview";
517778
- }
517779
- if (specifiedToolset === "default") {
517780
- return "anthropic/claude-sonnet-4";
517781
- }
517782
- return specifiedModel;
517783
- }
517784
517192
  function getStartupTargetLookupOrderForCredentials({
517785
517193
  baseURL,
517786
517194
  explicitBackendMode,
@@ -518066,10 +517474,6 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
518066
517474
  process.exit(1);
518067
517475
  }
518068
517476
  })();
518069
- const fromAfFile = resolveImportFlagAlias({
518070
- importFlagValue: values4.import,
518071
- fromAfFlagValue: values4["from-af"]
518072
- });
518073
517477
  const isHeadless = isHeadlessStartup(values4, process.stdin.isTTY, command);
518074
517478
  const terminalThemePromise = !isHeadless ? initTerminalTheme().catch(() => {
518075
517479
  return;
@@ -518151,8 +517555,8 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
518151
517555
  console.error("Error: --personality can only be used with --new-agent");
518152
517556
  process.exit(1);
518153
517557
  }
518154
- if (specifiedToolset && specifiedToolset !== "codex" && specifiedToolset !== "default" && specifiedToolset !== "gemini" && specifiedToolset !== "auto") {
518155
- console.error(`Error: Invalid toolset "${specifiedToolset}". Must be "auto", "codex", "default", or "gemini".`);
517558
+ if (specifiedToolset && specifiedToolset !== "codex" && specifiedToolset !== "default" && specifiedToolset !== "gemini" && specifiedToolset !== "letta" && specifiedToolset !== "auto") {
517559
+ console.error(`Error: Invalid toolset "${specifiedToolset}". Must be "auto", "letta", "codex", "default", or "gemini".`);
518156
517560
  process.exit(1);
518157
517561
  }
518158
517562
  if (systemPromptPreset && systemCustom) {
@@ -518179,7 +517583,6 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
518179
517583
  specifiedAgentName,
518180
517584
  forceNewAgent: forceNew,
518181
517585
  forceNewConversation,
518182
- importFile: fromAfFile,
518183
517586
  shouldResume,
518184
517587
  stateless: values4.stateless,
518185
517588
  isHeadless,
@@ -518190,52 +517593,6 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
518190
517593
  console.error(error5 instanceof Error ? `Error: ${error5.message}` : String(error5));
518191
517594
  process.exit(1);
518192
517595
  }
518193
- let isRegistryImport = false;
518194
- if (fromAfFile) {
518195
- try {
518196
- validateFlagConflicts({
518197
- guard: fromAfFile,
518198
- checks: [
518199
- {
518200
- when: specifiedAgentId,
518201
- message: "--import cannot be used with --agent"
518202
- },
518203
- {
518204
- when: specifiedAgentName,
518205
- message: "--import cannot be used with --name"
518206
- },
518207
- {
518208
- when: shouldResume,
518209
- message: "--import cannot be used with --resume"
518210
- },
518211
- {
518212
- when: forceNew,
518213
- message: "--import cannot be used with --new-agent"
518214
- }
518215
- ]
518216
- });
518217
- } catch (error5) {
518218
- console.error(error5 instanceof Error ? `Error: ${error5.message}` : String(error5));
518219
- process.exit(1);
518220
- }
518221
- if (fromAfFile.startsWith("@")) {
518222
- isRegistryImport = true;
518223
- try {
518224
- validateRegistryHandleOrThrow(fromAfFile);
518225
- } catch {
518226
- console.error(`Error: Invalid registry handle "${fromAfFile}". Use format: letta --import @author/agentname`);
518227
- process.exit(1);
518228
- }
518229
- } else {
518230
- const { resolve: resolve45 } = await import("node:path");
518231
- const { existsSync: existsSync75 } = await import("node:fs");
518232
- const resolvedPath = resolve45(fromAfFile);
518233
- if (!existsSync75(resolvedPath)) {
518234
- console.error(`Error: AgentFile not found: ${resolvedPath}`);
518235
- process.exit(1);
518236
- }
518237
- }
518238
- }
518239
517596
  let nameResolvedAgent = null;
518240
517597
  if (specifiedAgentName) {
518241
517598
  if (specifiedAgentId) {
@@ -518417,8 +517774,11 @@ Error: ${message}`);
518417
517774
  }
518418
517775
  if (isHeadless) {
518419
517776
  markMilestone("HEADLESS_MODE_START");
518420
- const modelForTools = getModelForToolLoading(specifiedModel, specifiedToolset);
518421
- await loadTools(modelForTools, { exclude: ["AskUserQuestion"] });
517777
+ await loadStartupTools({
517778
+ modelIdentifier: specifiedModel,
517779
+ toolset: specifiedToolset,
517780
+ exclude: ["AskUserQuestion"]
517781
+ });
518422
517782
  markMilestone("TOOLS_LOADED");
518423
517783
  const headlessValues = specifiedAgentId && values4.agent !== specifiedAgentId ? { ...values4, agent: specifiedAgentId } : values4;
518424
517784
  const { handleHeadlessCommand: handleHeadlessCommand2 } = await init_headless().then(() => exports_headless);
@@ -518445,9 +517805,7 @@ Error: ${message}`);
518445
517805
  model,
518446
517806
  systemPromptPreset: systemPromptPreset2,
518447
517807
  toolset,
518448
- skillsDirectory: skillsDirectory2,
518449
- fromAfFile: fromAfFile2,
518450
- isRegistryImport: isRegistryImport2
517808
+ skillsDirectory: skillsDirectory2
518451
517809
  }) {
518452
517810
  const [showKeybindingSetup, setShowKeybindingSetup] = useState56(null);
518453
517811
  const [loadingState, setLoadingState] = useState56("selecting");
@@ -518635,8 +517993,8 @@ Error: ${message}`);
518635
517993
  console.error("Run 'letta' to get started.");
518636
517994
  process.exit(1);
518637
517995
  }
518638
- if (forceNew2 || agentIdArg || fromAfFile2) {
518639
- if (agentIdArg && !forceNew2 && !fromAfFile2 && !forceNewConversation) {
517996
+ if (forceNew2 || agentIdArg) {
517997
+ if (agentIdArg && !forceNew2 && !forceNewConversation) {
518640
517998
  await settingsManager.loadLocalProjectSettings(process.cwd());
518641
517999
  const localSession2 = settingsManager.getLocalLastSession(process.cwd());
518642
518000
  if (localSession2?.agentId === agentIdArg && localSession2.conversationId && localSession2.conversationId !== "default") {
@@ -518743,13 +518101,7 @@ Error: ${message}`);
518743
518101
  setLoadingState("assembling");
518744
518102
  }
518745
518103
  checkAndStart();
518746
- }, [
518747
- forceNew2,
518748
- agentIdArg,
518749
- fromAfFile2,
518750
- shouldResume,
518751
- specifiedConversationId
518752
- ]);
518104
+ }, [forceNew2, agentIdArg, shouldResume, specifiedConversationId]);
518753
518105
  const initStartedRef = React14.useRef(false);
518754
518106
  useEffect50(() => {
518755
518107
  if (loadingState !== "assembling") {
@@ -518820,48 +518172,11 @@ Error: ${message}`);
518820
518172
  }
518821
518173
  }
518822
518174
  setIsResumingSession(!!resumingAgentId);
518823
- const modelForTools = getModelForToolLoading(model, toolset);
518824
- await loadTools(modelForTools);
518175
+ await loadStartupTools({ modelIdentifier: model, toolset });
518825
518176
  setLoadingState("initializing");
518826
518177
  const { createAgent: createAgent2 } = await Promise.resolve().then(() => (init_create5(), exports_create));
518827
518178
  let agent = null;
518828
518179
  let autoEnableMemfsForFreshAgent = false;
518829
- if (fromAfFile2) {
518830
- setLoadingState("importing");
518831
- let result2;
518832
- if (isRegistryImport2) {
518833
- const { importAgentFromRegistry: importAgentFromRegistry2 } = await Promise.resolve().then(() => (init_import(), exports_import));
518834
- result2 = await importAgentFromRegistry2({
518835
- handle: fromAfFile2,
518836
- modelOverride: model,
518837
- stripMessages: true,
518838
- stripSkills: false
518839
- });
518840
- } else {
518841
- const { importAgentFromFile: importAgentFromFile2 } = await Promise.resolve().then(() => (init_import(), exports_import));
518842
- result2 = await importAgentFromFile2({
518843
- filePath: fromAfFile2,
518844
- modelOverride: model,
518845
- stripMessages: true,
518846
- stripSkills: false
518847
- });
518848
- }
518849
- agent = result2.agent;
518850
- setAgentProvenance({
518851
- isNew: true,
518852
- blocks: []
518853
- });
518854
- if (settingsManager.isReady) {
518855
- settingsManager.setSystemPromptCustom(agent.id);
518856
- }
518857
- if (result2.skills && result2.skills.length > 0) {
518858
- const { getAgentSkillsDir: getAgentSkillsDir2 } = await Promise.resolve().then(() => (init_skills5(), exports_skills2));
518859
- const skillsDir = getAgentSkillsDir2(agent.id);
518860
- console.log(`
518861
- \uD83D\uDCE6 Extracted ${result2.skills.length} skill${result2.skills.length === 1 ? "" : "s"} to ${skillsDir}: ${result2.skills.join(", ")}
518862
- `);
518863
- }
518864
- }
518865
518180
  if (!agent && agentIdArg) {
518866
518181
  try {
518867
518182
  agent = await backend3.retrieveAgent(agentIdArg, {
@@ -518969,7 +518284,7 @@ Error: ${message}`);
518969
518284
  if (!shouldBlockOnMemfsStartup) {}
518970
518285
  const secretsInitPromise = Promise.resolve().then(() => (init_secrets_store(), exports_secrets_store)).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(agentId2));
518971
518286
  const isResumingProject = !shouldCreateNew && !!resumingAgentId;
518972
- const isReusingExistingAgent = !shouldCreateNew && !fromAfFile2 && agent && agent.id;
518287
+ const isReusingExistingAgent = !shouldCreateNew && agent && agent.id;
518973
518288
  const resuming = !!(agentIdArg || isResumingProject || isReusingExistingAgent);
518974
518289
  setIsResumingSession(resuming);
518975
518290
  if (resuming) {
@@ -519129,7 +518444,6 @@ Error during initialization: ${message}`);
519129
518444
  agentIdArg,
519130
518445
  model,
519131
518446
  systemPromptPreset2,
519132
- fromAfFile2,
519133
518447
  loadingState,
519134
518448
  selectedGlobalAgentId,
519135
518449
  validatedAgent,
@@ -519253,15 +518567,14 @@ Error during initialization: ${message}`);
519253
518567
  model: specifiedModel,
519254
518568
  systemPromptPreset,
519255
518569
  toolset: specifiedToolset,
519256
- skillsDirectory,
519257
- fromAfFile,
519258
- isRegistryImport
518570
+ skillsDirectory
519259
518571
  }), {
519260
518572
  exitOnCtrlC: false
519261
518573
  });
519262
518574
  }
519263
518575
  var EMPTY_APPROVAL_ARRAY, EMPTY_MESSAGE_ARRAY;
519264
518576
  var init_src5 = __esm(async () => {
518577
+ init_startup_log_boundary();
519265
518578
  init_error();
519266
518579
  init_telemetry();
519267
518580
  init_error_reporting();
@@ -519293,7 +518606,7 @@ var init_src5 = __esm(async () => {
519293
518606
  init_ConversationSelector(),
519294
518607
  init_profile_selection(),
519295
518608
  init_router(),
519296
- init_manager4(),
518609
+ init_letta_toolset(),
519297
518610
  init_toolset()
519298
518611
  ]);
519299
518612
  EMPTY_APPROVAL_ARRAY = [];
@@ -519301,6 +518614,9 @@ var init_src5 = __esm(async () => {
519301
518614
  assertSupportedBunRuntime();
519302
518615
  main2();
519303
518616
  });
518617
+
518618
+ // src/standalone-entry.ts
518619
+ init_startup_log_boundary();
519304
518620
  // node_modules/@earendil-works/pi-ai/dist/auth/oauth/oauth-page.js
519305
518621
  var LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;
519306
518622
  function escapeHtml(value) {
@@ -521496,4 +520812,4 @@ function registerBunOAuthFlows() {
521496
520812
  registerBunOAuthFlows();
521497
520813
  await init_src5().then(() => exports_src2);
521498
520814
 
521499
- //# debugId=875FFD1EA43A424B64756E2164756E21
520815
+ //# debugId=182BBD0B116FF61964756E2164756E21