@letta-ai/letta-code 0.21.9 → 0.21.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -3269,7 +3269,7 @@ var package_default;
3269
3269
  var init_package = __esm(() => {
3270
3270
  package_default = {
3271
3271
  name: "@letta-ai/letta-code",
3272
- version: "0.21.9",
3272
+ version: "0.21.10",
3273
3273
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3274
3274
  type: "module",
3275
3275
  bin: {
@@ -3535,6 +3535,7 @@ var init_secrets = __esm(async () => {
3535
3535
  // src/settings-manager.ts
3536
3536
  var exports_settings_manager = {};
3537
3537
  __export(exports_settings_manager, {
3538
+ shouldPersistSessionState: () => shouldPersistSessionState,
3538
3539
  settingsManager: () => settingsManager
3539
3540
  });
3540
3541
  import { randomUUID } from "node:crypto";
@@ -3543,6 +3544,9 @@ import { join as join2, resolve } from "node:path";
3543
3544
  function isSubagentProcess() {
3544
3545
  return process.env.LETTA_CODE_AGENT_ROLE === "subagent";
3545
3546
  }
3547
+ function shouldPersistSessionState() {
3548
+ return process.env.LETTA_CODE_AGENT_ROLE !== "subagent" && process.env.LETTA_DISABLE_SESSION_PERSIST !== "1";
3549
+ }
3546
3550
  function normalizeBaseUrl(baseUrl) {
3547
3551
  let normalized = baseUrl.replace(/^https?:\/\//, "");
3548
3552
  normalized = normalized.replace(/\/$/, "");
@@ -5484,7 +5488,6 @@ var init_memory_check_reminder = () => {};
5484
5488
  var memory_filesystem_default = `---
5485
5489
  label: memory_filesystem
5486
5490
  description: Filesystem view of memory blocks (system + user)
5487
- limit: 8000
5488
5491
  ---
5489
5492
 
5490
5493
  /memory/
@@ -6577,7 +6580,7 @@ When applying memory in responses, integrate it naturally — like a colleague w
6577
6580
  var init_system_prompt_blocks = () => {};
6578
6581
 
6579
6582
  // src/agent/prompts/system_prompt_memfs.md
6580
- var system_prompt_memfs_default = "# Memory\n\nYour memory is stored in a git repository at `$MEMORY_DIR` (absolute path provided by Letta Code shell tools; usually `~/.letta/agents/$AGENT_ID/memory/`). This provides full version control, sync with the server, and worktrees for parallel edits. All memory files are markdown with YAML frontmatter (`description`, `limit`, optional `metadata`). The `description` field enables progressive disclosure like skills, you see descriptions in your prompt and load full contents on demand; `limit` caps file size to keep system memory lean.\n\n## Memory layout\n\n**System memory** (`memory/system/`): Every `.md` file here is pinned directly into your system prompt — you see it at all times. This is your most valuable real estate: reserve it for durable knowledge that helps across sessions (user identity, persona, project architecture, conventions, gotchas). Do NOT store transient items here like specific commits, current work items, or session-specific notes — those dilute the signal.\n\n**Progressive memory**: Files outside `system/` are stored but not pinned in-context. Access them with standard file tools when you need deeper reference material good for large notes, historical records, transient work tracking, or data that doesn't need to be always-visible.\n\n**Recall** (conversation history): Your full message history is searchable even after messages leave your context window. Use the recall subagent to retrieve past discussions, decisions, and context from earlier sessions.\n\n## How files map to your prompt\n\n1. Each `.md` file in `memory/system/` is pinned to your system prompt with tags <system/context/{name}.md></system/context/{name}.md>\n2. The `memory_filesystem` block renders the current tree view of all available memory files\n3. The system prompt is only recompiled on compactions or message resets — your local edits take effect on the next recompilation\n\n## Syncing\n\nChanges you commit and push sync to the Letta server within seconds, and server-side changes sync back automatically.\n\n```bash\ncd \"$MEMORY_DIR\"\n\n# See what changed\ngit status\n\n# Commit and push your changes\ngit add .\ngit commit -m \"<type>: <what changed>\" # e.g. \"fix: update user prefs\", \"refactor: reorganize persona blocks\"\ngit push\n\n# Get latest from server\ngit pull\n```\nThe system will remind you when your memory has uncommitted changes. Sync when convenient.\n\n## History\n```bash\ngit -C \"$MEMORY_DIR\" log --oneline\n```\n";
6583
+ var system_prompt_memfs_default = "# Memory\n\nYour memory is projected onto the local memory filesystem (MemFS) at `$MEMORY_DIR` (usually `~/.letta/agents/$AGENT_ID/memory/`), including your memory blocks (in-context in the system prompt) and external memory. This projection makes it easy for you to modify your own context with filesystem operations which also include git tracking. Local changes are only propagated to your actual state on a successful push to remote, and the system prompt is only recompiled on compactions or new conversations (so may be stale). \n\n## Memory structure\nYou are responsible to maintaining a clear memory structure. All memory files are markdown with YAML frontmatter (`description`, optional `metadata`).\n\n**In-context memory** (`system/`): Memory files in `system/` correspond to memory blocks, which are pinned directly into your system prompt — visible at all times. This is your most valuable real estate: reserve it for durable knowledge that helps across sessions (user identity, persona, project architecture, conventions, gotchas). Do NOT store transient items here like specific commits, current work items, or session-specific notes — those dilute the signal.\n\n**External memory**: Files outside `system/` follow progressive disclosure — an index of files and descriptions is kept in the system prompt, but full contents must be retrieved on demand (e.g. by reading the file). Skills are a special type of external memory stored in the `skills/` folder. Use `[[path]]` to index files from memory blocks, or create discovery paths between related context (e.g. `[[reference/project/architecture.md]]` or `[[skills/using-slack/SKILL.md]]`).\n\n**Recall** (conversation history): Your full message history is searchable even after messages leave your context window. Use the recall subagent to retrieve past discussions, decisions, and context from earlier sessions.\n\n## Syncing\n\nChanges you commit and push sync to the Letta server within seconds, and server-side changes sync back automatically.\n\n```bash\ncd \"$MEMORY_DIR\"\n\n# See what changed\ngit status\n\n# Commit and push your changes\ngit add .\ngit commit --author=\"$AGENT_NAME <$AGENT_ID@letta.com>\" -m \"<type>: <what changed>\" # e.g. \"fix: update user prefs\", \"refactor: reorganize persona blocks\"\ngit push\n\n# Get latest from server\ngit pull\n```\nThe system will remind you when your memory has uncommitted changes. Sync when convenient.\n\n## History\n```bash\ngit -C \"$MEMORY_DIR\" log --oneline\n```\n";
6581
6584
  var init_system_prompt_memfs = () => {};
6582
6585
 
6583
6586
  // src/utils/error.ts
@@ -6708,12 +6711,6 @@ async function loadMemoryBlocksFromMdx() {
6708
6711
  if (frontmatter.description) {
6709
6712
  block.description = frontmatter.description;
6710
6713
  }
6711
- if (frontmatter.limit) {
6712
- const limit2 = parseInt(frontmatter.limit, 10);
6713
- if (!Number.isNaN(limit2) && limit2 > 0) {
6714
- block.limit = limit2;
6715
- }
6716
- }
6717
6714
  if (READ_ONLY_BLOCK_LABELS.includes(label)) {
6718
6715
  block.read_only = true;
6719
6716
  }
@@ -6840,7 +6837,7 @@ var history_analyzer_default = `---
6840
6837
  name: history-analyzer
6841
6838
  description: Analyze Claude Code or Codex conversation history and directly update agent memory files with insights
6842
6839
  tools: Read, Write, Bash, Glob, Grep
6843
- skills: migrating-from-codex-and-claude-code
6840
+ skills:
6844
6841
  model: auto
6845
6842
  memoryBlocks: none
6846
6843
  mode: stateless
@@ -6858,19 +6855,64 @@ Your memory files form the parent agent's identity and knowledge. Follow these p
6858
6855
  - **Generalize, don't memorize**: Distill patterns from repeated observations. "Always use uv, never pip (corrected 10+ times)" is valuable; a single offhand mention is not. Look for signal through repetition.
6859
6856
  - **System/ is the core program**: Only durable, generalizable knowledge belongs in \`system/\`. Distilled preferences, behavioral rules, project gotchas, conventions enforced through corrections. Evidence trails, raw session summaries, and verbose context go outside \`system/\`.
6860
6857
  - **Progressive disclosure**: Frontmatter descriptions should let the agent decide whether to load a file without reading it. Summaries and principles in \`system/\`; detail and evidence outside it, linked with \`[[path]]\`.
6861
- - **Identity continuity**: Treat this history as the agent's own past — not someone else's sessions. Findings should read as learned knowledge, not "analysis of external data."
6858
+ - **Identity continuity**: This history IS the agent's past. These are memories of working with this user you're reconstructing lived experience, not analyzing external data. Write findings as learned knowledge ("I've seen Sarah correct this 10+ times"), not research summaries ("The user appears to prefer...").
6862
6859
  - **Preserve and connect**: If a memory file already has good content, extend it — don't replace it. Use \`[[path]]\` links to connect new findings to existing memory.
6860
+ - **Promote findings into canonical memory**: Don't leave durable insights trapped in generic ingestion files if they can be promoted into focused memory like \`system/human/identity.md\`, \`system/human/prefs/workflow.md\`, or \`system/<project>/gotchas.md\`.
6863
6861
 
6864
6862
  ## Goal
6865
6863
 
6866
- Distill actionable knowledge from conversation history into well-organized memory. Focus on:
6864
+ Distill actionable knowledge from conversation history into well-organized memory. You MUST produce findings in all three categories below — missing any category is a failure.
6865
+
6866
+ This is not a request for a thin recap. Your output should be detailed enough that the parent agent can use it in future sessions without rereading the history chunk.
6867
+
6868
+ ### Required Output Categories
6869
+
6870
+ You MUST extract and document all three:
6871
+
6872
+ **1. User Personality & Identity** (REQUIRED)
6873
+ - How would you describe them as a person? (e.g., "pragmatic builder who values shipping over perfection")
6874
+ - What drives them? What are their goals? (e.g., "building tools that reduce friction for developers")
6875
+ - Communication style beyond just "direct" — do they joke? Use sarcasm? Have catchphrases?
6876
+ - Quirks, linguistic patterns, unique attributes
6877
+ - Pattern-match to common personas if applicable (e.g., "scrappy startup engineer", "meticulous architect")
6878
+
6879
+ **2. Hard Rules & Preferences** (REQUIRED)
6880
+ - Coding preferences with enforcement evidence (e.g., "Use uv — corrected 10+ times")
6881
+ - Workflow patterns (testing habits, commit style, tool choices)
6882
+ - What frustrates them and why
6883
+ - Explicit "always/never" statements
6884
+
6885
+ **3. Project Context** (REQUIRED)
6886
+ - Codebase structures, conventions, patterns
6887
+ - Gotchas discovered through debugging
6888
+ - Which files are safe to edit vs deprecated
6889
+ - Environment quirks
6890
+
6891
+ If you cannot extract meaningful findings for ANY category, explicitly state why (e.g., "Insufficient data for personality analysis — only 5 prompts, all about a single bug fix").
6892
+
6893
+ ### Quality Bar
6867
6894
 
6868
- - **Preferences enforced through corrections**: What the user repeatedly corrects their AI assistant about — these are gold. They reveal what the user actually cares about vs. what's merely documented.
6869
- - **Project gotchas**: Footguns, fragile areas, and non-obvious constraints discovered through debugging sessions and errors.
6870
- - **Working patterns**: How the user works debugging style, testing habits, tools they reach for, communication style.
6871
- - **Conventions actually used**: Not just what's in a README, but what's enforced through practice.
6895
+ When sufficient data exists, aim to extract at least:
6896
+ - **5+ durable findings** for user personality / identity
6897
+ - **8+ durable findings** for hard rules / preferences
6898
+ - **8+ durable findings** for project context
6872
6899
 
6873
- **What NOT to store**: Raw quotes, one-off events, session-by-session summaries, anything that can be retrieved from conversation history on demand.
6900
+ If you produce materially fewer findings in a category, explain why the chunk truly lacked signal.
6901
+
6902
+ Avoid low-value summaries like:
6903
+ - "User is direct"
6904
+ - "Project uses TypeScript"
6905
+ - "Uses conventional commits"
6906
+
6907
+ These are insufficient unless paired with concrete operational detail, enforcement patterns, or repo-specific implications.
6908
+
6909
+ ### What NOT to Store
6910
+ One-off events, session-by-session summaries, anything that can be retrieved from conversation history on demand.
6911
+
6912
+ ### What TO Preserve
6913
+ Focus on understanding **why** the user reacted the way they did — what mistake or behavior triggered the correction? The pattern matters more than the quote. For example, don't just record "user said stop adding stuff" — record that the agent was over-engineering by adding abstractions when a simple flag change was needed. Quotes can serve as supporting evidence, but the real value is the behavioral pattern and what to do differently.
6914
+
6915
+ Keep specific correction counts ("corrected 10+ times"), specific file paths, and specific gotchas with context. Specificity is identity; vague summaries are forgettable.
6874
6916
 
6875
6917
  ## Workflow
6876
6918
 
@@ -6888,12 +6930,29 @@ git worktree add "$WORKTREE_DIR/$BRANCH_NAME" -b "$BRANCH_NAME"
6888
6930
  If worktree creation fails (locked index), retry up to 3 times with backoff (sleep 2, 5, 10). Never delete \`.git/index.lock\` manually. All edits go in \`$WORKTREE_DIR/$BRANCH_NAME/\`.
6889
6931
 
6890
6932
  ### 2. Read existing memory
6891
-
6892
- Read all files in your worktree's \`system/\` directory. Understand what's already there so you can extend it, not duplicate it.
6933
+ Read the memory files in your worktree, to understand what already exists in the memory filesystem.
6893
6934
 
6894
6935
  ### 3. Read and analyze history
6895
6936
 
6896
- Use the \`migrating-from-codex-and-claude-code\` skill for data access patterns. Filter to your assigned chunk.
6937
+ Your prompt will specify a pre-split JSONL chunk file and its source format. Use these patterns to read it:
6938
+
6939
+ **Claude Code** (\`~/.claude/\`):
6940
+ - \`history.jsonl\` — each line: \`.display\` (prompt text), \`.timestamp\` (unix ms), \`.project\` (working dir), \`.sessionId\`
6941
+ - Session files at \`~/.claude/projects/<encoded-path>/<session-uuid>.jsonl\` (path encoding: \`/\` → \`-\`)
6942
+ - User messages: \`jq 'select(.type == "user") | .message.content'\`
6943
+ - Assistant text: \`jq 'select(.type == "assistant") | .message.content[] | select(.type == "text") | .text'\`
6944
+ - Tool calls: \`jq 'select(.type == "assistant") | .message.content[] | select(.type == "tool_use") | {name, input}'\`
6945
+ - Summaries: \`jq 'select(.type == "summary") | .summary'\`
6946
+
6947
+ **OpenAI Codex** (\`~/.codex/\`):
6948
+ - \`history.jsonl\` — each line: \`.text\` (prompt text), \`.ts\` (unix seconds) — no project path
6949
+ - Session files at \`~/.codex/sessions/<year>/<month>/<day>/rollout-*.jsonl\`
6950
+ - Session metadata (first line): \`jq 'select(.type == "session_meta") | .payload.cwd'\` (to get project dir)
6951
+ - User messages: \`jq 'select(.type == "event_msg" and .payload.type == "user_message") | .payload.message'\`
6952
+ - Assistant text: \`jq 'select(.type == "response_item" and .payload.type == "message") | .payload.content[] | select(.type == "output_text") | .text'\`
6953
+ - Tool calls: \`jq 'select(.type == "response_item" and .payload.type == "function_call") | {name: .payload.name, args: .payload.arguments}'\`
6954
+
6955
+ **Key format difference**: Claude uses \`.timestamp\` (milliseconds) and \`.display\`; Codex uses \`.ts\` (seconds) and \`.text\`.
6897
6956
 
6898
6957
  Look for **repeated patterns**, not isolated events:
6899
6958
  - Count correction frequency — 10 corrections on the same topic >> 1 mention
@@ -6901,38 +6960,70 @@ Look for **repeated patterns**, not isolated events:
6901
6960
  - Implicit preferences revealed by what commands they run, what patterns they follow
6902
6961
  - Frustration signals — "no", "undo", rapid corrections, /clear, model switches
6903
6962
 
6963
+ **For personality analysis**, look beyond the reaction to what caused it:
6964
+ - What agent behaviors triggered corrections? (over-engineering, wrong tool, verbose explanations, etc.)
6965
+ - What agent behaviors got positive responses? (fast fixes, running tests unprompted, etc.)
6966
+ - How do they phrase requests? (imperative, collaborative, questioning?)
6967
+ - What topics excite them vs bore them?
6968
+ - What's their tolerance for explanation vs "just fix it"?
6969
+ - How do they handle mistakes — their own and the agent's?
6970
+
6904
6971
  ### 4. Update memory files
6905
6972
 
6906
6973
  **Content placement:**
6907
6974
  - \`system/\`: Generalized rules, distilled preferences, project gotchas, identity. Keep files lean — bullets, short lines, scannable.
6908
6975
  - Outside \`system/\`: Evidence, detailed history, verbose context. Link from system/ with \`[[path]]\`.
6909
6976
 
6977
+ **Preferred canonical paths:**
6978
+ - \`system/human/identity.md\`
6979
+ - \`system/human/prefs/communication.md\`
6980
+ - \`system/human/prefs/workflow.md\`
6981
+ - \`system/human/prefs/coding.md\`
6982
+ - \`system/<project>/conventions.md\`
6983
+ - \`system/<project>/gotchas.md\`
6984
+
6985
+ If the current memory uses a more compressed layout, extend it carefully, but prefer splitting into these focused files when there is enough material to justify the move.
6986
+
6910
6987
  **File structure:**
6911
6988
  - Use the project's **real name** as directory prefix (e.g. \`my-app/conventions.md\`), not generic \`project/\`
6912
6989
  - One concept per file, nested with \`/\` paths
6913
6990
  - Every file needs a meaningful \`description\` in frontmatter
6914
6991
  - Write for the agent's future self — clean, actionable, no clutter
6915
6992
 
6993
+ Each durable finding should include at least one of:
6994
+ - correction frequency or intensity
6995
+ - concrete commands that worked or failed
6996
+ - concrete file or directory paths
6997
+ - date range or source reference for future lookup
6998
+ - why the rule matters in practice
6999
+
6916
7000
  You can also cite the files if you want to note where something came from (e.g. \`(from: ~/.claude/history.jsonl)\`).
6917
7001
 
6918
7002
  ### 5. Commit
6919
7003
 
7004
+ Before writing the commit, resolve the actual ID values:
7005
+ \`\`\`bash
7006
+ echo "AGENT_ID=$LETTA_AGENT_ID"
7007
+ echo "PARENT_AGENT_ID=$LETTA_PARENT_AGENT_ID"
7008
+ \`\`\`
7009
+
7010
+ Use the printed values (e.g., \`agent-abc123...\`) in the trailers. If a variable is empty or unset, omit that trailer. Never write a literal variable name like \`$LETTA_AGENT_ID\` or \`$AGENT_ID\` in the commit message.
7011
+
6920
7012
  \`\`\`bash
6921
7013
  cd $WORKTREE_DIR/$BRANCH_NAME
6922
7014
  git add -A
6923
- git commit -m "<type>(history-analyzer): [summary]
7015
+ git commit --author="History Analyzer <<ACTUAL_AGENT_ID>@letta.com>" -m "<type>(history-analyzer): <summary>
6924
7016
 
6925
7017
  Source: [file path] ([N] prompts, [DATE RANGE])
6926
- Key updates:
6927
- - [file]: [what was added/changed]
7018
+
7019
+ Updates:
7020
+ - <what changed and why>
6928
7021
 
6929
7022
  Generated-By: Letta Code
6930
7023
  Agent-ID: <ACTUAL_AGENT_ID>
6931
7024
  Parent-Agent-ID: <ACTUAL_PARENT_AGENT_ID>"
6932
7025
  \`\`\`
6933
7026
 
6934
- Resolve \`ACTUAL_AGENT_ID\` and \`ACTUAL_PARENT_AGENT_ID\` by running \`echo $LETTA_AGENT_ID\` and \`echo $LETTA_PARENT_AGENT_ID\` first. Never write literal variable names in the commit message. Omit trailers if the variable is empty.
6935
-
6936
7027
  **Commit types**: \`chore\` (routine ingestion), \`feat\` (new memory topics), \`refactor\` (reorganizing by domain).
6937
7028
 
6938
7029
  ## Rules
@@ -6940,7 +7031,8 @@ Resolve \`ACTUAL_AGENT_ID\` and \`ACTUAL_PARENT_AGENT_ID\` by running \`echo $LE
6940
7031
  - Work in your worktree — do NOT edit the memory dir directly
6941
7032
  - Do NOT merge into main — the parent agent handles merging
6942
7033
  - Preserve existing content — extend or refine, don't replace
6943
- - Quality over quantity fewer distilled insights beat many raw observations
7034
+ - Preserve specificityspecific quotes, correction counts, and file paths are more valuable than vague summaries. Don't compress away the details that give the parent agent its character and grounding.
7035
+ - **REQUIRED**: You MUST produce findings for all three output categories (Personality, Rules, Project). If any category lacks data, explicitly state why.
6944
7036
  `;
6945
7037
  var init_history_analyzer = () => {};
6946
7038
 
@@ -53756,7 +53848,7 @@ If a directory has more than 1,000 entries, only the first 1,000 will be shown.`
53756
53848
  var init_LS = () => {};
53757
53849
 
53758
53850
  // src/tools/descriptions/Memory.md
53759
- var Memory_default = '# Memory\nA convinience tool for memories stored in the memory directory (`$MEMORY_DIR`) that automatically commits and pushes changes. \n\nFiles stored inside of `system/` eventually become part of the agent\'s system prompt, so are always in the context window and do not need to be re-read. Other files only have metadata in the system prompt, so may need to be explicitly read to be updated. \n\nSupported operations on memory files: \n- `str_replace`\n- `insert`\n- `delete` (files, or directories recursively)\n- `rename` (path rename only)\n- `update_description`\n- `create`\nMore general operations can be performanced through directory modifying the files. \n\nPath formats accepted:\n- relative memory file paths (e.g. `system/contacts.md`, `reference/project/team.md`)\n- absolute paths only when they are inside `$MEMORY_DIR`\n\nNote: absolute paths outside `$MEMORY_DIR` are rejected.\n\nExamples:\n\n```python\n# Replace text in a memory file \nmemory(command="str_replace", reason="Update theme preference", path="system/human/preferences.md", old_string="theme: dark", new_string="theme: light")\n\n# Insert text at line 5\nmemory(command="insert", reason="Add note about meeting", path="reference/history/meeting-notes.md", insert_line=5, insert_text="New note here")\n\n# Delete a memory file \nmemory(command="delete", reason="Remove stale notes", path="reference/history/old_notes.md")\n\n# Rename a memory file \nmemory(command="rename", reason="Promote temp notes", old_path="reference/history/temp.md", new_path="reference/history/permanent.md")\n\n# Update a block description\nmemory(command="update_description", reason="Clarify coding prefs block", path="system/human/prefs/coding.md", description="Dr. Wooders\' coding preferences.")\n\n# Create a block with starting text\nmemory(command="create", reason="Track coding preferences", path="system/human/prefs/coding.md", description="The user\'s coding preferences.", file_text="The user seems to add type hints to all of their Python code.")\n\n# Create an empty block\nmemory(command="create", reason="Create coding preferences block", path="reference/history/coding_preferences.md", description="The user\'s coding preferences.")\n```\n';
53851
+ var Memory_default = "# Memory\nA convinience tool for memories stored in the memory directory (`$MEMORY_DIR`) that automatically commits and pushes changes. \n\nFiles stored inside of `system/` eventually become part of the agent's system prompt, so are always in the context window and do not need to be re-read. Other files only have metadata in the system prompt, so may need to be explicitly read to be updated. \n\nSupported operations on memory files: \n- `str_replace`\n- `insert`\n- `delete` (files, or directories recursively)\n- `rename` (path rename only)\n- `update_description`\n- `create`\nMore general operations can be performanced through directory modifying the files. \n\nPath formats accepted:\n- relative memory file paths (e.g. `system/contacts.md`, `reference/project/team.md`)\n- absolute paths only when they are inside `$MEMORY_DIR`\n\nNote: absolute paths outside `$MEMORY_DIR` are rejected.\n\nWhen creating or deleting files, check for `[[path]]` references in other memory files that may need to be added or updated. Keeping references consistent ensures future discoverability.\n\nExamples:\n\n```python\n# Replace text in a memory file \nmemory(command=\"str_replace\", reason=\"Update theme preference\", path=\"system/human/preferences.md\", old_string=\"theme: dark\", new_string=\"theme: light\")\n\n# Insert text at line 5\nmemory(command=\"insert\", reason=\"Add note about meeting\", path=\"reference/history/meeting-notes.md\", insert_line=5, insert_text=\"New note here\")\n\n# Delete a memory file \nmemory(command=\"delete\", reason=\"Remove stale notes\", path=\"reference/history/old_notes.md\")\n\n# Rename a memory file \nmemory(command=\"rename\", reason=\"Promote temp notes\", old_path=\"reference/history/temp.md\", new_path=\"reference/history/permanent.md\")\n\n# Update a block description\nmemory(command=\"update_description\", reason=\"Clarify coding prefs block\", path=\"system/human/prefs/coding.md\", description=\"Dr. Wooders' coding preferences.\")\n\n# Create a block with starting text\nmemory(command=\"create\", reason=\"Track coding preferences\", path=\"system/human/prefs/coding.md\", description=\"The user's coding preferences.\", file_text=\"The user seems to add type hints to all of their Python code.\")\n\n# Create an empty block\nmemory(command=\"create\", reason=\"Create coding preferences block\", path=\"reference/history/coding_preferences.md\", description=\"The user's coding preferences.\")\n```\n";
53760
53852
  var init_Memory = () => {};
53761
53853
 
53762
53854
  // src/tools/descriptions/MemoryApplyPatch.md
@@ -65799,6 +65891,9 @@ function parseResultFromStdout(stdout, agentId) {
65799
65891
  };
65800
65892
  }
65801
65893
  }
65894
+ function resolveSubagentWorkingDirectory(env3 = process.env, fallbackCwd = process.cwd()) {
65895
+ return env3.USER_CWD || fallbackCwd;
65896
+ }
65802
65897
  function resolveSubagentLauncher(cliArgs, options = {}) {
65803
65898
  const env3 = options.env ?? process.env;
65804
65899
  const argv = options.argv ?? process.argv;
@@ -65913,8 +66008,9 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
65913
66008
  const settings = await settingsManager.getSettingsWithSecureTokens();
65914
66009
  const inheritedApiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
65915
66010
  const inheritedBaseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL;
66011
+ const subagentWorkingDirectory = resolveSubagentWorkingDirectory();
65916
66012
  const proc2 = spawn4(launcher.command, launcher.args, {
65917
- cwd: process.cwd(),
66013
+ cwd: subagentWorkingDirectory,
65918
66014
  env: {
65919
66015
  ...process.env,
65920
66016
  ...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
@@ -77097,6 +77193,7 @@ function requestApprovalOverWS(runtime, socket, requestId, controlRequest) {
77097
77193
  controlRequest
77098
77194
  });
77099
77195
  runtime.listener.approvalRuntimeKeyByRequestId.set(requestId, runtime.key);
77196
+ runtime.lastStopReason = "requires_approval";
77100
77197
  setLoopStatus(runtime, "WAITING_ON_APPROVAL");
77101
77198
  emitLoopStatusIfOpen(runtime.listener, {
77102
77199
  agent_id: runtime.agentId,
@@ -77565,6 +77662,74 @@ var init_permissionMode = __esm(() => {
77565
77662
  init_remote_settings();
77566
77663
  });
77567
77664
 
77665
+ // src/websocket/listener/recoverable-notices.ts
77666
+ function getRecoverableStatusNoticeVisibility(kind) {
77667
+ switch (kind) {
77668
+ case "stale_approval_conflict_recovery":
77669
+ return "debug_only";
77670
+ default:
77671
+ return "transcript";
77672
+ }
77673
+ }
77674
+ function getRecoverableRetryNoticeVisibility(kind, attempt) {
77675
+ switch (kind) {
77676
+ case "transient_provider_retry":
77677
+ return attempt === 1 ? "debug_only" : "transcript";
77678
+ default:
77679
+ return "transcript";
77680
+ }
77681
+ }
77682
+ function isDesktopDebugPanelMirrorEnabled() {
77683
+ return process.env.LETTA_DESKTOP_DEBUG_PANEL === "1";
77684
+ }
77685
+ function mirrorRecoverableNoticeToDesktopDebugPanel(message) {
77686
+ if (!isDesktopDebugPanelMirrorEnabled()) {
77687
+ return;
77688
+ }
77689
+ try {
77690
+ process.stderr.write(`${DESKTOP_DEBUG_PANEL_INFO_PREFIX} ${message}
77691
+ `);
77692
+ } catch {}
77693
+ }
77694
+ function emitRecoverableStatusNotice(socket, runtime, params) {
77695
+ const visibility = getRecoverableStatusNoticeVisibility(params.kind);
77696
+ if (visibility === "debug_only") {
77697
+ debugLog("recovery", `Debug-only lifecycle notice (${params.kind}): ${params.message}`);
77698
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
77699
+ return;
77700
+ }
77701
+ emitStatusDelta(socket, runtime, {
77702
+ message: params.message,
77703
+ level: params.level,
77704
+ runId: params.runId,
77705
+ agentId: params.agentId,
77706
+ conversationId: params.conversationId
77707
+ });
77708
+ }
77709
+ function emitRecoverableRetryNotice(socket, runtime, params) {
77710
+ const visibility = getRecoverableRetryNoticeVisibility(params.kind, params.attempt);
77711
+ if (visibility === "debug_only") {
77712
+ debugLog("recovery", `Debug-only retry notice (${params.kind}, attempt ${params.attempt}/${params.maxAttempts}): ${params.message}`);
77713
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
77714
+ return;
77715
+ }
77716
+ emitRetryDelta(socket, runtime, {
77717
+ message: params.message,
77718
+ reason: params.reason,
77719
+ attempt: params.attempt,
77720
+ maxAttempts: params.maxAttempts,
77721
+ delayMs: params.delayMs,
77722
+ runId: params.runId,
77723
+ agentId: params.agentId,
77724
+ conversationId: params.conversationId
77725
+ });
77726
+ }
77727
+ var DESKTOP_DEBUG_PANEL_INFO_PREFIX = "[LETTA_DESKTOP_DEBUG_PANEL_INFO]";
77728
+ var init_recoverable_notices = __esm(async () => {
77729
+ init_debug();
77730
+ await init_protocol_outbound();
77731
+ });
77732
+
77568
77733
  // node_modules/diff/libesm/diff/base.js
77569
77734
  class Diff {
77570
77735
  diff(oldStr, newStr, options = {}) {
@@ -79646,9 +79811,6 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
79646
79811
  ];
79647
79812
  let pendingNeedsUserInput = [...needsUserInput];
79648
79813
  if (pendingNeedsUserInput.length > 0) {
79649
- runtime.lastStopReason = "requires_approval";
79650
- setLoopStatus(runtime, "WAITING_ON_APPROVAL", scope);
79651
- emitRuntimeStateUpdates(runtime, scope);
79652
79814
  while (pendingNeedsUserInput.length > 0) {
79653
79815
  const ac = pendingNeedsUserInput.shift();
79654
79816
  if (!ac) {
@@ -80177,11 +80339,6 @@ async function handleApprovalStop(params) {
80177
80339
  if (shouldInterrupt()) {
80178
80340
  return interruptTermination();
80179
80341
  }
80180
- runtime.lastStopReason = "requires_approval";
80181
- setLoopStatus(runtime, "WAITING_ON_APPROVAL", {
80182
- agent_id: agentId,
80183
- conversation_id: conversationId
80184
- });
80185
80342
  while (pendingNeedsUserInput.length > 0) {
80186
80343
  const ac = pendingNeedsUserInput.shift();
80187
80344
  if (!ac) {
@@ -80817,7 +80974,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
80817
80974
  latestErrorText
80818
80975
  })) {
80819
80976
  postStopApprovalRecoveryRetries += 1;
80820
- emitStatusDelta(socket, runtime, {
80977
+ emitRecoverableStatusNotice(socket, runtime, {
80978
+ kind: "stale_approval_conflict_recovery",
80821
80979
  message: "Recovering from stale approval conflict after interrupted/reconnected turn",
80822
80980
  level: "warning",
80823
80981
  runId: lastRunId || undefined,
@@ -80913,7 +81071,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
80913
81071
  detail: errorDetail
80914
81072
  });
80915
81073
  const retryMessage = getRetryStatusMessage(errorDetail) || `LLM API error encountered, retrying (attempt ${attempt}/${LLM_API_ERROR_MAX_RETRIES})...`;
80916
- emitRetryDelta(socket, runtime, {
81074
+ emitRecoverableRetryNotice(socket, runtime, {
81075
+ kind: "transient_provider_retry",
80917
81076
  message: retryMessage,
80918
81077
  reason: "llm_api_error",
80919
81078
  attempt,
@@ -81113,6 +81272,7 @@ var init_turn = __esm(async () => {
81113
81272
  init_toolset(),
81114
81273
  init_interrupts(),
81115
81274
  init_protocol_outbound(),
81275
+ init_recoverable_notices(),
81116
81276
  init_recovery(),
81117
81277
  init_send(),
81118
81278
  init_turn_approval()
@@ -81436,7 +81596,7 @@ function buildLoopStatus(runtime, params) {
81436
81596
  const conversationRuntime = getConversationRuntime(listener, scopedAgentId, scopedConversationId);
81437
81597
  const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
81438
81598
  const recovered = getRecoveredApprovalStateForScope(listener, scope);
81439
- const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
81599
+ const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus === "WAITING_ON_APPROVAL" ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
81440
81600
  return {
81441
81601
  status,
81442
81602
  active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
@@ -81598,9 +81758,6 @@ function resolveSubagentScopeForSnapshot(runtime, scope) {
81598
81758
  function buildSubagentSnapshot(runtime, scope) {
81599
81759
  const runtimeScope = resolveSubagentScopeForSnapshot(runtime, scope);
81600
81760
  return getSubagents().filter((a) => {
81601
- if (a.status !== "pending" && a.status !== "running") {
81602
- return false;
81603
- }
81604
81761
  if (a.silent && a.isBackground !== true) {
81605
81762
  return false;
81606
81763
  }
@@ -85336,7 +85493,7 @@ var init_skills2 = __esm(() => {
85336
85493
  var exports_fs = {};
85337
85494
  __export(exports_fs, {
85338
85495
  writeJsonFile: () => writeJsonFile,
85339
- writeFile: () => writeFile6,
85496
+ writeFile: () => writeFile7,
85340
85497
  readJsonFile: () => readJsonFile,
85341
85498
  readFile: () => readFile9,
85342
85499
  mkdir: () => mkdir6,
@@ -85352,7 +85509,7 @@ import { dirname as dirname13 } from "node:path";
85352
85509
  async function readFile9(path23) {
85353
85510
  return fsReadFileSync2(path23, { encoding: "utf-8" });
85354
85511
  }
85355
- async function writeFile6(path23, content) {
85512
+ async function writeFile7(path23, content) {
85356
85513
  const dir = dirname13(path23);
85357
85514
  if (!existsSync25(dir)) {
85358
85515
  mkdirSync17(dir, { recursive: true });
@@ -85373,7 +85530,7 @@ async function writeJsonFile(path23, data, options) {
85373
85530
  const indent = options?.indent ?? 2;
85374
85531
  const content = `${JSON.stringify(data, null, indent)}
85375
85532
  `;
85376
- await writeFile6(path23, content);
85533
+ await writeFile7(path23, content);
85377
85534
  }
85378
85535
  var init_fs2 = () => {};
85379
85536
 
@@ -85398,7 +85555,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85398
85555
  }
85399
85556
  const wasRaw = process.stdin.isRaw;
85400
85557
  const wasFlowing = process.stdin.readableFlowing;
85401
- return new Promise((resolve25) => {
85558
+ return new Promise((resolve26) => {
85402
85559
  let response = "";
85403
85560
  let resolved = false;
85404
85561
  const cleanup = () => {
@@ -85415,7 +85572,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85415
85572
  };
85416
85573
  const timeout = setTimeout(() => {
85417
85574
  cleanup();
85418
- resolve25(null);
85575
+ resolve26(null);
85419
85576
  }, timeoutMs);
85420
85577
  const onData = (data) => {
85421
85578
  response += data.toString();
@@ -85425,7 +85582,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85425
85582
  if (match3) {
85426
85583
  clearTimeout(timeout);
85427
85584
  cleanup();
85428
- resolve25({
85585
+ resolve26({
85429
85586
  r: parseHexComponent(match3[1] ?? "0"),
85430
85587
  g: parseHexComponent(match3[2] ?? "0"),
85431
85588
  b: parseHexComponent(match3[3] ?? "0")
@@ -85440,7 +85597,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85440
85597
  } catch {
85441
85598
  clearTimeout(timeout);
85442
85599
  cleanup();
85443
- resolve25(null);
85600
+ resolve26(null);
85444
85601
  }
85445
85602
  });
85446
85603
  }
@@ -86649,10 +86806,10 @@ __export(exports_setup, {
86649
86806
  runSetup: () => runSetup
86650
86807
  });
86651
86808
  async function runSetup() {
86652
- return new Promise((resolve26) => {
86809
+ return new Promise((resolve27) => {
86653
86810
  const { waitUntilExit } = render_default(import_react32.default.createElement(SetupUI, {
86654
86811
  onComplete: () => {
86655
- resolve26();
86812
+ resolve27();
86656
86813
  }
86657
86814
  }));
86658
86815
  waitUntilExit().catch((error) => {
@@ -87127,6 +87284,20 @@ function validateRegistryHandleOrThrow2(handle) {
87127
87284
  }
87128
87285
  }
87129
87286
 
87287
+ // src/runtime-context.ts
87288
+ import { AsyncLocalStorage } from "node:async_hooks";
87289
+ function getCurrentWorkingDirectory() {
87290
+ const workingDirectory = runtimeContextStorage.getStore()?.workingDirectory;
87291
+ if (typeof workingDirectory === "string" && workingDirectory.length > 0) {
87292
+ return workingDirectory;
87293
+ }
87294
+ return process.env.USER_CWD || process.cwd();
87295
+ }
87296
+ var runtimeContextStorage;
87297
+ var init_runtime_context = __esm(() => {
87298
+ runtimeContextStorage = new AsyncLocalStorage;
87299
+ });
87300
+
87130
87301
  // src/agent/github-utils.ts
87131
87302
  var exports_github_utils = {};
87132
87303
  __export(exports_github_utils, {
@@ -87167,11 +87338,11 @@ __export(exports_import, {
87167
87338
  extractSkillsFromAf: () => extractSkillsFromAf
87168
87339
  });
87169
87340
  import { createReadStream } from "node:fs";
87170
- import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile7 } from "node:fs/promises";
87171
- import { dirname as dirname14, resolve as resolve26 } from "node:path";
87341
+ import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
87342
+ import { dirname as dirname14, resolve as resolve27 } from "node:path";
87172
87343
  async function importAgentFromFile(options) {
87173
87344
  const client = await getClient();
87174
- const resolvedPath = resolve26(options.filePath);
87345
+ const resolvedPath = resolve27(options.filePath);
87175
87346
  const file = createReadStream(resolvedPath);
87176
87347
  const importResponse = await client.agents.importFile({
87177
87348
  file,
@@ -87206,7 +87377,7 @@ async function extractSkillsFromAf(afPath, destDir) {
87206
87377
  return [];
87207
87378
  }
87208
87379
  for (const skill2 of afData.skills) {
87209
- const skillDir = resolve26(destDir, skill2.name);
87380
+ const skillDir = resolve27(destDir, skill2.name);
87210
87381
  await mkdir7(skillDir, { recursive: true });
87211
87382
  if (skill2.files) {
87212
87383
  await writeSkillFiles(skillDir, skill2.files);
@@ -87226,9 +87397,9 @@ async function writeSkillFiles(skillDir, files) {
87226
87397
  }
87227
87398
  }
87228
87399
  async function writeSkillFile(skillDir, filePath, content) {
87229
- const fullPath = resolve26(skillDir, filePath);
87400
+ const fullPath = resolve27(skillDir, filePath);
87230
87401
  await mkdir7(dirname14(fullPath), { recursive: true });
87231
- await writeFile7(fullPath, content, "utf-8");
87402
+ await writeFile8(fullPath, content, "utf-8");
87232
87403
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
87233
87404
  if (isScript) {
87234
87405
  try {
@@ -87281,7 +87452,7 @@ function parseRegistryHandle(handle) {
87281
87452
  async function importAgentFromRegistry(options) {
87282
87453
  const { tmpdir: tmpdir4 } = await import("node:os");
87283
87454
  const { join: join35 } = await import("node:path");
87284
- const { writeFile: writeFile8, unlink: unlink3 } = await import("node:fs/promises");
87455
+ const { writeFile: writeFile9, unlink: unlink3 } = await import("node:fs/promises");
87285
87456
  const { author, name } = parseRegistryHandle(options.handle);
87286
87457
  const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/refs/heads/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}/${name}.af`;
87287
87458
  const response = await fetch(rawUrl);
@@ -87293,7 +87464,7 @@ async function importAgentFromRegistry(options) {
87293
87464
  }
87294
87465
  const afContent = await response.text();
87295
87466
  const tempPath = join35(tmpdir4(), `letta-import-${author}-${name}-${Date.now()}.af`);
87296
- await writeFile8(tempPath, afContent, "utf-8");
87467
+ await writeFile9(tempPath, afContent, "utf-8");
87297
87468
  try {
87298
87469
  const result = await importAgentFromFile({
87299
87470
  filePath: tempPath,
@@ -87524,7 +87695,7 @@ async function prepareHeadlessToolExecutionContext(params) {
87524
87695
  agentId: params.agentId,
87525
87696
  conversationId: params.conversationId,
87526
87697
  overrideModel: params.overrideModel,
87527
- workingDirectory: process.env.USER_CWD || process.cwd(),
87698
+ workingDirectory: getCurrentWorkingDirectory(),
87528
87699
  exclude: ["AskUserQuestion"]
87529
87700
  });
87530
87701
  return {
@@ -87532,6 +87703,20 @@ async function prepareHeadlessToolExecutionContext(params) {
87532
87703
  availableTools: preparedToolContext.preparedToolContext.clientTools.map((tool) => tool.name)
87533
87704
  };
87534
87705
  }
87706
+ async function flushAndExit(code) {
87707
+ const flushWritable = (stream2) => new Promise((resolve28) => {
87708
+ if (stream2.destroyed || stream2.writableEnded) {
87709
+ resolve28();
87710
+ return;
87711
+ }
87712
+ stream2.write("", () => resolve28());
87713
+ });
87714
+ await Promise.allSettled([
87715
+ flushWritable(process.stdout),
87716
+ flushWritable(process.stderr)
87717
+ ]);
87718
+ process.exit(code);
87719
+ }
87535
87720
  async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride, skillSourcesOverride, systemInfoReminderEnabledOverride) {
87536
87721
  const { values, positionals } = parsedArgs;
87537
87722
  telemetry.setSurface("headless");
@@ -87602,6 +87787,7 @@ In headless mode, use:
87602
87787
  let forceNewConversation = values.new ?? false;
87603
87788
  const fromAgentId = values["from-agent"];
87604
87789
  let agent = null;
87790
+ let autoEnableMemfsForFreshAgent = false;
87605
87791
  let specifiedAgentId = values.agent;
87606
87792
  const specifiedAgentName = values.name;
87607
87793
  let specifiedConversationId = values.conversation;
@@ -87884,7 +88070,7 @@ In headless mode, use:
87884
88070
  }
87885
88071
  if (!agent && forceNew) {
87886
88072
  const updateArgs = getModelUpdateArgs2(model);
87887
- const { isLettaCloud: isLettaCloud2, enableMemfsIfCloud: enableMemfsIfCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
88073
+ const { isLettaCloud: isLettaCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
87888
88074
  const willAutoEnableMemfs = shouldAutoEnableMemfsForNewAgent && await isLettaCloud2();
87889
88075
  const effectiveMemoryMode = requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
87890
88076
  const createOptions = {
@@ -87904,13 +88090,11 @@ In headless mode, use:
87904
88090
  };
87905
88091
  const result = await createAgent(createOptions);
87906
88092
  agent = result.agent;
87907
- if (willAutoEnableMemfs) {
87908
- await enableMemfsIfCloud2(agent.id);
87909
- }
88093
+ autoEnableMemfsForFreshAgent = willAutoEnableMemfs;
87910
88094
  }
87911
88095
  if (!agent) {
87912
88096
  await settingsManager.loadLocalProjectSettings();
87913
- const localAgentId = settingsManager.getLocalLastAgentId(process.cwd());
88097
+ const localAgentId = settingsManager.getLocalLastAgentId(getCurrentWorkingDirectory());
87914
88098
  if (localAgentId) {
87915
88099
  try {
87916
88100
  agent = await client.agents.retrieve(localAgentId);
@@ -87965,13 +88149,14 @@ In headless mode, use:
87965
88149
  let conversationId;
87966
88150
  let effectiveReflectionSettings;
87967
88151
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
88152
+ const startupMemfsFlag = autoEnableMemfsForFreshAgent ? true : memfsFlag;
87968
88153
  let memfsBgPromise;
87969
88154
  const secretsAgentId = agent?.id;
87970
88155
  const secretsInitPromise = secretsAgentId ? init_secretsStore().then(() => exports_secretsStore).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(secretsAgentId)) : Promise.resolve();
87971
88156
  if (memfsStartupPolicy === "skip") {
87972
88157
  try {
87973
88158
  const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
87974
- await applyMemfsFlags2(agent.id, memfsFlag, noMemfsFlag, {
88159
+ await applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
87975
88160
  pullOnExistingRepo: false,
87976
88161
  agentTags: agent.tags,
87977
88162
  skipPromptUpdate: forceNew
@@ -87983,7 +88168,7 @@ In headless mode, use:
87983
88168
  }
87984
88169
  } else if (memfsStartupPolicy === "background") {
87985
88170
  const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
87986
- memfsBgPromise = applyMemfsFlags2(agent.id, memfsFlag, noMemfsFlag, {
88171
+ memfsBgPromise = applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
87987
88172
  pullOnExistingRepo: true,
87988
88173
  agentTags: agent.tags,
87989
88174
  skipPromptUpdate: forceNew
@@ -87994,7 +88179,7 @@ In headless mode, use:
87994
88179
  } else {
87995
88180
  try {
87996
88181
  const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
87997
- const memfsResult = await applyMemfsFlags2(agent.id, memfsFlag, noMemfsFlag, {
88182
+ const memfsResult = await applyMemfsFlags2(agent.id, startupMemfsFlag, noMemfsFlag, {
87998
88183
  pullOnExistingRepo: true,
87999
88184
  agentTags: agent.tags,
88000
88185
  skipPromptUpdate: forceNew
@@ -88097,7 +88282,7 @@ In headless mode, use:
88097
88282
  }
88098
88283
  markMilestone("HEADLESS_CONVERSATION_READY");
88099
88284
  setConversationId2(conversationId);
88100
- if (!isSubagent) {
88285
+ if (shouldPersistSessionState()) {
88101
88286
  await settingsManager.loadLocalProjectSettings();
88102
88287
  settingsManager.persistSession(agent.id, conversationId);
88103
88288
  }
@@ -88136,7 +88321,7 @@ In headless mode, use:
88136
88321
  conversation_id: conversationId,
88137
88322
  model: agent.llm_config?.model ?? "",
88138
88323
  tools: availableTools,
88139
- cwd: process.cwd(),
88324
+ cwd: getCurrentWorkingDirectory(),
88140
88325
  mcp_servers: [],
88141
88326
  permission_mode: "",
88142
88327
  slash_commands: [],
@@ -88297,6 +88482,7 @@ ${SYSTEM_REMINDER_CLOSE}
88297
88482
  },
88298
88483
  state: sharedReminderState,
88299
88484
  sessionContextReminderEnabled: systemInfoReminderEnabled,
88485
+ workingDirectory: getCurrentWorkingDirectory(),
88300
88486
  reflectionSettings: effectiveReflectionSettings,
88301
88487
  skillSources: resolvedSkillSources,
88302
88488
  resolvePlanModeReminder: async () => {
@@ -88470,7 +88656,7 @@ ${loadedContents.join(`
88470
88656
  } else {
88471
88657
  console.error(`Conversation is busy, waiting ${Math.round(retryDelayMs / 1000)}s and retrying...`);
88472
88658
  }
88473
- await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
88659
+ await new Promise((resolve28) => setTimeout(resolve28, retryDelayMs));
88474
88660
  continue;
88475
88661
  }
88476
88662
  }
@@ -88519,7 +88705,7 @@ ${loadedContents.join(`
88519
88705
  const delaySeconds = Math.round(delayMs / 1000);
88520
88706
  console.error(`Transient API error before streaming (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88521
88707
  }
88522
- await new Promise((resolve27) => setTimeout(resolve27, delayMs));
88708
+ await new Promise((resolve28) => setTimeout(resolve28, delayMs));
88523
88709
  conversationBusyRetries = 0;
88524
88710
  continue;
88525
88711
  }
@@ -88758,7 +88944,7 @@ ${loadedContents.join(`
88758
88944
  const delaySeconds = Math.round(delayMs / 1000);
88759
88945
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88760
88946
  }
88761
- await new Promise((resolve27) => setTimeout(resolve27, delayMs));
88947
+ await new Promise((resolve28) => setTimeout(resolve28, delayMs));
88762
88948
  refreshCurrentInputOtids();
88763
88949
  continue;
88764
88950
  }
@@ -88844,7 +89030,7 @@ ${loadedContents.join(`
88844
89030
  } else {
88845
89031
  console.error(`Empty LLM response, retrying (attempt ${attempt} of ${EMPTY_RESPONSE_MAX_RETRIES2})...`);
88846
89032
  }
88847
- await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89033
+ await new Promise((resolve28) => setTimeout(resolve28, delayMs));
88848
89034
  refreshCurrentInputOtids();
88849
89035
  continue;
88850
89036
  }
@@ -88872,7 +89058,7 @@ ${loadedContents.join(`
88872
89058
  const delaySeconds = Math.round(delayMs / 1000);
88873
89059
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88874
89060
  }
88875
- await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89061
+ await new Promise((resolve28) => setTimeout(resolve28, delayMs));
88876
89062
  refreshCurrentInputOtids();
88877
89063
  continue;
88878
89064
  }
@@ -89001,6 +89187,7 @@ ${loadedContents.join(`
89001
89187
  }
89002
89188
  markMilestone("HEADLESS_COMPLETE");
89003
89189
  reportAllMilestones();
89190
+ await flushAndExit(0);
89004
89191
  }
89005
89192
  async function runBidirectionalMode(agent, conversationId, client, _outputFormat, includePartialMessages, availableTools, skillSources, systemInfoReminderEnabled, reflectionSettings) {
89006
89193
  const sessionId = agent.id;
@@ -89013,7 +89200,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89013
89200
  conversation_id: conversationId,
89014
89201
  model: agent.llm_config?.model,
89015
89202
  tools: availableTools,
89016
- cwd: process.cwd(),
89203
+ cwd: getCurrentWorkingDirectory(),
89017
89204
  memfs_enabled: settingsManager.isMemfsEnabled(agent.id),
89018
89205
  skill_sources: skillSources,
89019
89206
  system_info_reminder_enabled: systemInfoReminderEnabled,
@@ -89205,9 +89392,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89205
89392
  const syntheticUserLine = serializeQueuedMessageAsUserLine(queuedMessage);
89206
89393
  maybeNotifyBlocked(syntheticUserLine);
89207
89394
  if (lineResolver) {
89208
- const resolve27 = lineResolver;
89395
+ const resolve28 = lineResolver;
89209
89396
  lineResolver = null;
89210
- resolve27(syntheticUserLine);
89397
+ resolve28(syntheticUserLine);
89211
89398
  return;
89212
89399
  }
89213
89400
  lineQueue.push(syntheticUserLine);
@@ -89215,9 +89402,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89215
89402
  rl.on("line", (line) => {
89216
89403
  maybeNotifyBlocked(line);
89217
89404
  if (lineResolver) {
89218
- const resolve27 = lineResolver;
89405
+ const resolve28 = lineResolver;
89219
89406
  lineResolver = null;
89220
- resolve27(line);
89407
+ resolve28(line);
89221
89408
  } else {
89222
89409
  lineQueue.push(line);
89223
89410
  }
@@ -89226,17 +89413,17 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89226
89413
  setMessageQueueAdder(null);
89227
89414
  msgQueueRuntime.clear("shutdown");
89228
89415
  if (lineResolver) {
89229
- const resolve27 = lineResolver;
89416
+ const resolve28 = lineResolver;
89230
89417
  lineResolver = null;
89231
- resolve27(null);
89418
+ resolve28(null);
89232
89419
  }
89233
89420
  });
89234
89421
  async function getNextLine() {
89235
89422
  if (lineQueue.length > 0) {
89236
89423
  return lineQueue.shift() ?? null;
89237
89424
  }
89238
- return new Promise((resolve27) => {
89239
- lineResolver = resolve27;
89425
+ return new Promise((resolve28) => {
89426
+ lineResolver = resolve28;
89240
89427
  });
89241
89428
  }
89242
89429
  async function requestPermission(toolCallId, toolName, toolInput) {
@@ -89655,6 +89842,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89655
89842
  },
89656
89843
  state: sharedReminderState,
89657
89844
  sessionContextReminderEnabled: systemInfoReminderEnabled,
89845
+ workingDirectory: getCurrentWorkingDirectory(),
89658
89846
  reflectionSettings,
89659
89847
  skillSources,
89660
89848
  resolvePlanModeReminder: async () => {
@@ -89739,7 +89927,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89739
89927
  uuid: `retry-bidir-${randomUUID8()}`
89740
89928
  };
89741
89929
  console.log(JSON.stringify(retryMsg));
89742
- await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89930
+ await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89743
89931
  continue;
89744
89932
  }
89745
89933
  throw preStreamError;
@@ -89980,6 +90168,7 @@ var init_headless = __esm(async () => {
89980
90168
  init_constants();
89981
90169
  init_diffPreview();
89982
90170
  init_queueRuntime();
90171
+ init_runtime_context();
89983
90172
  init_interactivePolicy();
89984
90173
  init_debug();
89985
90174
  init_timing();
@@ -90034,10 +90223,10 @@ async function detectAndEnableKittyProtocol() {
90034
90223
  detectionComplete = true;
90035
90224
  return;
90036
90225
  }
90037
- return new Promise((resolve27) => {
90226
+ return new Promise((resolve28) => {
90038
90227
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
90039
90228
  detectionComplete = true;
90040
- resolve27();
90229
+ resolve28();
90041
90230
  return;
90042
90231
  }
90043
90232
  const originalRawMode = process.stdin.isRaw;
@@ -90070,7 +90259,7 @@ async function detectAndEnableKittyProtocol() {
90070
90259
  console.error("[kitty] protocol query unsupported; enabled anyway (best-effort)");
90071
90260
  }
90072
90261
  detectionComplete = true;
90073
- resolve27();
90262
+ resolve28();
90074
90263
  };
90075
90264
  const handleData = (data) => {
90076
90265
  if (timeoutId === undefined) {
@@ -112256,7 +112445,7 @@ var init_pasteRegistry = __esm(() => {
112256
112445
  import { execFileSync as execFileSync3 } from "node:child_process";
112257
112446
  import { existsSync as existsSync29, readFileSync as readFileSync16, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
112258
112447
  import { tmpdir as tmpdir4 } from "node:os";
112259
- import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as resolve27 } from "node:path";
112448
+ import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as resolve28 } from "node:path";
112260
112449
  function countLines2(text) {
112261
112450
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
112262
112451
  }
@@ -112304,7 +112493,7 @@ function translatePasteForImages(paste) {
112304
112493
  } catch {}
112305
112494
  }
112306
112495
  if (!isAbsolute18(filePath))
112307
- filePath = resolve27(process.cwd(), filePath);
112496
+ filePath = resolve28(process.cwd(), filePath);
112308
112497
  const ext3 = extname5(filePath || "").toLowerCase();
112309
112498
  if (IMAGE_EXTS.has(ext3) && existsSync29(filePath) && statSync10(filePath).isFile()) {
112310
112499
  const buf = readFileSync16(filePath);
@@ -113450,9 +113639,16 @@ var init_registry = __esm(async () => {
113450
113639
  }
113451
113640
  },
113452
113641
  "/reflect": {
113453
- desc: "Launch a background reflection agent to update memory",
113642
+ desc: "Launch reflection (/reflect [transcript_file])",
113643
+ args: "[transcript_file]",
113454
113644
  order: 50,
113455
- noArgs: true,
113645
+ handler: () => {
113646
+ return "Launching reflection agent...";
113647
+ }
113648
+ },
113649
+ "/reflection": {
113650
+ desc: "Alias for /reflect",
113651
+ args: "[transcript_file]",
113456
113652
  handler: () => {
113457
113653
  return "Launching reflection agent...";
113458
113654
  }
@@ -113812,14 +114008,6 @@ Location: ${keybindingsPath}`;
113812
114008
  return "Clearing credentials...";
113813
114009
  }
113814
114010
  },
113815
- "/empanada": {
113816
- desc: "Order empanadas from Empanada Empire",
113817
- order: 44.5,
113818
- args: "[address]",
113819
- handler: () => {
113820
- return "Checking Empanada Empire...";
113821
- }
113822
- },
113823
114011
  "/ralph": {
113824
114012
  desc: 'Start Ralph Wiggum loop (/ralph [prompt] [--completion-promise "X"] [--max-iterations N])',
113825
114013
  order: 45,
@@ -114324,11 +114512,11 @@ var init_HelpDialog = __esm(async () => {
114324
114512
 
114325
114513
  // src/hooks/writer.ts
114326
114514
  import { homedir as homedir31 } from "node:os";
114327
- import { resolve as resolve28 } from "node:path";
114515
+ import { resolve as resolve29 } from "node:path";
114328
114516
  function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
114329
114517
  const home = process.env.HOME || homedir31();
114330
- const globalSettingsPath = resolve28(home, ".letta", "settings.json");
114331
- const projectSettingsPath = resolve28(workingDirectory, ".letta", "settings.json");
114518
+ const globalSettingsPath = resolve29(home, ".letta", "settings.json");
114519
+ const projectSettingsPath = resolve29(workingDirectory, ".letta", "settings.json");
114332
114520
  return globalSettingsPath === projectSettingsPath;
114333
114521
  }
114334
114522
  function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
@@ -116966,7 +117154,7 @@ var init_AgentInfoBar = __esm(async () => {
116966
117154
 
116967
117155
  // src/cli/helpers/fileSearch.ts
116968
117156
  import { readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
116969
- import { join as join41, relative as relative15, resolve as resolve29 } from "node:path";
117157
+ import { join as join41, relative as relative15, resolve as resolve30 } from "node:path";
116970
117158
  function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = [], depth = 0, maxDepth = 10, lowerPattern = pattern.toLowerCase()) {
116971
117159
  if (results.length >= maxResults || depth >= maxDepth) {
116972
117160
  return results;
@@ -117009,7 +117197,7 @@ async function searchFiles(query, deep = false) {
117009
117197
  const dirPart = query.slice(0, lastSlashIndex);
117010
117198
  const pattern = query.slice(lastSlashIndex + 1);
117011
117199
  try {
117012
- const resolvedDir = resolve29(getIndexRoot(), dirPart);
117200
+ const resolvedDir = resolve30(getIndexRoot(), dirPart);
117013
117201
  try {
117014
117202
  statSync11(resolvedDir);
117015
117203
  searchDir = resolvedDir;
@@ -131808,7 +131996,7 @@ async function executeStatusLineCommand(command, payload, options) {
131808
131996
  };
131809
131997
  }
131810
131998
  function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory, startTime) {
131811
- return new Promise((resolve30, reject) => {
131999
+ return new Promise((resolve31, reject) => {
131812
132000
  const [executable, ...args] = launcher;
131813
132001
  if (!executable) {
131814
132002
  reject(new Error("Empty launcher"));
@@ -131821,7 +132009,7 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
131821
132009
  const safeResolve = (result) => {
131822
132010
  if (!resolved) {
131823
132011
  resolved = true;
131824
- resolve30(result);
132012
+ resolve31(result);
131825
132013
  }
131826
132014
  };
131827
132015
  let child;
@@ -132998,14 +133186,14 @@ __export(exports_export, {
132998
133186
  packageSkills: () => packageSkills
132999
133187
  });
133000
133188
  import { readdir as readdir10, readFile as readFile13 } from "node:fs/promises";
133001
- import { relative as relative16, resolve as resolve30 } from "node:path";
133189
+ import { relative as relative16, resolve as resolve31 } from "node:path";
133002
133190
  async function packageSkills(agentId, skillsDir) {
133003
133191
  const skills = [];
133004
133192
  const skillNames = new Set;
133005
133193
  const dirsToCheck = skillsDir ? [skillsDir] : [
133006
133194
  agentId && getAgentSkillsDir(agentId),
133007
- resolve30(process.cwd(), ".skills"),
133008
- resolve30(process.env.HOME || "~", ".letta", "skills")
133195
+ resolve31(process.cwd(), ".skills"),
133196
+ resolve31(process.env.HOME || "~", ".letta", "skills")
133009
133197
  ].filter((dir) => Boolean(dir));
133010
133198
  for (const baseDir of dirsToCheck) {
133011
133199
  try {
@@ -133015,8 +133203,8 @@ async function packageSkills(agentId, skillsDir) {
133015
133203
  continue;
133016
133204
  if (skillNames.has(entry.name))
133017
133205
  continue;
133018
- const skillDir = resolve30(baseDir, entry.name);
133019
- const skillMdPath = resolve30(skillDir, "SKILL.md");
133206
+ const skillDir = resolve31(baseDir, entry.name);
133207
+ const skillMdPath = resolve31(skillDir, "SKILL.md");
133020
133208
  try {
133021
133209
  await readFile13(skillMdPath, "utf-8");
133022
133210
  } catch {
@@ -133046,7 +133234,7 @@ async function readSkillFiles(skillDir) {
133046
133234
  async function walk(dir) {
133047
133235
  const entries = await readdir10(dir, { withFileTypes: true });
133048
133236
  for (const entry of entries) {
133049
- const fullPath = resolve30(dir, entry.name);
133237
+ const fullPath = resolve31(dir, entry.name);
133050
133238
  if (entry.isDirectory()) {
133051
133239
  await walk(fullPath);
133052
133240
  } else {
@@ -135530,7 +135718,7 @@ ${newState.originalPrompt}`,
135530
135718
  cancelled = true;
135531
135719
  break;
135532
135720
  }
135533
- await new Promise((resolve31) => setTimeout(resolve31, 100));
135721
+ await new Promise((resolve32) => setTimeout(resolve32, 100));
135534
135722
  }
135535
135723
  buffersRef.current.byId.delete(statusId);
135536
135724
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
@@ -135594,7 +135782,7 @@ ${newState.originalPrompt}`,
135594
135782
  cancelled = true;
135595
135783
  break;
135596
135784
  }
135597
- await new Promise((resolve31) => setTimeout(resolve31, 100));
135785
+ await new Promise((resolve32) => setTimeout(resolve32, 100));
135598
135786
  }
135599
135787
  if (retryStatusId) {
135600
135788
  buffersRef.current.byId.delete(retryStatusId);
@@ -136352,7 +136540,7 @@ ${feedback}
136352
136540
  });
136353
136541
  buffersRef.current.order.push(statusId);
136354
136542
  refreshDerived();
136355
- await new Promise((resolve31) => setTimeout(resolve31, delayMs));
136543
+ await new Promise((resolve32) => setTimeout(resolve32, delayMs));
136356
136544
  buffersRef.current.byId.delete(statusId);
136357
136545
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
136358
136546
  refreshDerived();
@@ -136412,7 +136600,7 @@ ${feedback}
136412
136600
  cancelled = true;
136413
136601
  break;
136414
136602
  }
136415
- await new Promise((resolve31) => setTimeout(resolve31, 100));
136603
+ await new Promise((resolve32) => setTimeout(resolve32, 100));
136416
136604
  }
136417
136605
  if (retryStatusId) {
136418
136606
  buffersRef.current.byId.delete(retryStatusId);
@@ -143626,11 +143814,11 @@ __export(exports_import2, {
143626
143814
  extractSkillsFromAf: () => extractSkillsFromAf2
143627
143815
  });
143628
143816
  import { createReadStream as createReadStream2 } from "node:fs";
143629
- import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile8 } from "node:fs/promises";
143630
- import { dirname as dirname19, resolve as resolve31 } from "node:path";
143817
+ import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
143818
+ import { dirname as dirname19, resolve as resolve32 } from "node:path";
143631
143819
  async function importAgentFromFile2(options) {
143632
143820
  const client = await getClient();
143633
- const resolvedPath = resolve31(options.filePath);
143821
+ const resolvedPath = resolve32(options.filePath);
143634
143822
  const file = createReadStream2(resolvedPath);
143635
143823
  const importResponse = await client.agents.importFile({
143636
143824
  file,
@@ -143665,7 +143853,7 @@ async function extractSkillsFromAf2(afPath, destDir) {
143665
143853
  return [];
143666
143854
  }
143667
143855
  for (const skill2 of afData.skills) {
143668
- const skillDir = resolve31(destDir, skill2.name);
143856
+ const skillDir = resolve32(destDir, skill2.name);
143669
143857
  await mkdir8(skillDir, { recursive: true });
143670
143858
  if (skill2.files) {
143671
143859
  await writeSkillFiles2(skillDir, skill2.files);
@@ -143685,9 +143873,9 @@ async function writeSkillFiles2(skillDir, files) {
143685
143873
  }
143686
143874
  }
143687
143875
  async function writeSkillFile2(skillDir, filePath, content) {
143688
- const fullPath = resolve31(skillDir, filePath);
143876
+ const fullPath = resolve32(skillDir, filePath);
143689
143877
  await mkdir8(dirname19(fullPath), { recursive: true });
143690
- await writeFile8(fullPath, content, "utf-8");
143878
+ await writeFile9(fullPath, content, "utf-8");
143691
143879
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
143692
143880
  if (isScript) {
143693
143881
  try {
@@ -143740,7 +143928,7 @@ function parseRegistryHandle2(handle) {
143740
143928
  async function importAgentFromRegistry2(options) {
143741
143929
  const { tmpdir: tmpdir7 } = await import("node:os");
143742
143930
  const { join: join50 } = await import("node:path");
143743
- const { writeFile: writeFile9, unlink: unlink3 } = await import("node:fs/promises");
143931
+ const { writeFile: writeFile10, unlink: unlink3 } = await import("node:fs/promises");
143744
143932
  const { author, name } = parseRegistryHandle2(options.handle);
143745
143933
  const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER2}/${AGENT_REGISTRY_REPO2}/refs/heads/${AGENT_REGISTRY_BRANCH2}/agents/@${author}/${name}/${name}.af`;
143746
143934
  const response = await fetch(rawUrl);
@@ -143752,7 +143940,7 @@ async function importAgentFromRegistry2(options) {
143752
143940
  }
143753
143941
  const afContent = await response.text();
143754
143942
  const tempPath = join50(tmpdir7(), `letta-import-${author}-${name}-${Date.now()}.af`);
143755
- await writeFile9(tempPath, afContent, "utf-8");
143943
+ await writeFile10(tempPath, afContent, "utf-8");
143756
143944
  try {
143757
143945
  const result = await importAgentFromFile2({
143758
143946
  filePath: tempPath,
@@ -148687,12 +148875,15 @@ async function runMemfsSubcommand(argv) {
148687
148875
 
148688
148876
  // src/cli/subcommands/messages.ts
148689
148877
  await init_client2();
148878
+ import { writeFile as writeFile6 } from "node:fs/promises";
148879
+ import { resolve as resolve23 } from "node:path";
148690
148880
  import { parseArgs as parseArgs8 } from "node:util";
148691
148881
  function printUsage5() {
148692
148882
  console.log(`
148693
148883
  Usage:
148694
148884
  letta messages search --query <text> [options]
148695
148885
  letta messages list [options]
148886
+ letta messages transcript --conversation <id> [options]
148696
148887
 
148697
148888
  Search options:
148698
148889
  --query <text> Search query (required)
@@ -148714,6 +148905,16 @@ List options:
148714
148905
  --start-date <date> Client-side filter: after this date (ISO format)
148715
148906
  --end-date <date> Client-side filter: before this date (ISO format)
148716
148907
 
148908
+ Transcript options:
148909
+ --conversation <id> Conversation ID to export (required)
148910
+ --conversation-id <id> Alias for --conversation
148911
+ --agent <id> Required when conversation is "default"
148912
+ --agent-id <id> Alias for --agent
148913
+ --limit <n> Page size while fetching (default: 100)
148914
+ --max-pages <n> Max pagination pages to fetch (default: 200)
148915
+ --out <path> Write transcript text to file
148916
+ --output <path> Alias for --out
148917
+
148717
148918
  Notes:
148718
148919
  - Output is JSON only.
148719
148920
  - Uses CLI auth; override with LETTA_API_KEY/LETTA_BASE_URL if needed.
@@ -148755,7 +148956,12 @@ var MESSAGES_OPTIONS = {
148755
148956
  "agent-id": { type: "string" },
148756
148957
  after: { type: "string" },
148757
148958
  before: { type: "string" },
148758
- order: { type: "string" }
148959
+ order: { type: "string" },
148960
+ conversation: { type: "string" },
148961
+ "conversation-id": { type: "string" },
148962
+ "max-pages": { type: "string" },
148963
+ out: { type: "string" },
148964
+ output: { type: "string" }
148759
148965
  };
148760
148966
  function parseMessagesArgs(argv) {
148761
148967
  return parseArgs8({
@@ -148782,6 +148988,115 @@ async function runMessagesSubcommand(argv) {
148782
148988
  }
148783
148989
  try {
148784
148990
  const client = await getClient();
148991
+ const renderText = (value) => {
148992
+ if (typeof value === "string")
148993
+ return value;
148994
+ if (!Array.isArray(value))
148995
+ return "";
148996
+ return value.map((part) => {
148997
+ if (part && typeof part === "object" && "type" in part && part.type === "text" && "text" in part) {
148998
+ return typeof part.text === "string" ? part.text : "";
148999
+ }
149000
+ return "";
149001
+ }).filter((text) => text.length > 0).join(`
149002
+ `);
149003
+ };
149004
+ const renderUnknown = (value) => {
149005
+ if (typeof value === "string")
149006
+ return value;
149007
+ if (value === null || value === undefined)
149008
+ return "";
149009
+ try {
149010
+ return JSON.stringify(value);
149011
+ } catch {
149012
+ return String(value);
149013
+ }
149014
+ };
149015
+ const safeTypeLabel = (msg) => msg.message_type || "unknown_message";
149016
+ const formatEntry = (msg) => {
149017
+ const timestamp = msg.date || "unknown-time";
149018
+ const type = safeTypeLabel(msg);
149019
+ if (type === "user_message") {
149020
+ const text = renderText(msg.content);
149021
+ return [`[${timestamp}] user`, text || "(empty)"];
149022
+ }
149023
+ if (type === "assistant_message") {
149024
+ const text = renderText(msg.content);
149025
+ return [`[${timestamp}] assistant`, text || "(empty)"];
149026
+ }
149027
+ if (type === "reasoning_message") {
149028
+ return [
149029
+ `[${timestamp}] reasoning`,
149030
+ msg.reasoning && msg.reasoning.length > 0 ? msg.reasoning : "(empty)"
149031
+ ];
149032
+ }
149033
+ if (type === "tool_call_message" || type === "approval_request_message") {
149034
+ const calls = Array.isArray(msg.tool_calls) ? msg.tool_calls : msg.tool_call ? [msg.tool_call] : [];
149035
+ if (calls.length === 0) {
149036
+ return [`[${timestamp}] ${type}`, "(no tool call payload)"];
149037
+ }
149038
+ return calls.flatMap((call) => {
149039
+ const header = `[${timestamp}] tool_call ${call.name || "unknown"} (${call.tool_call_id || "no-id"})`;
149040
+ const args = call.arguments ? call.arguments : "{}";
149041
+ return [header, args];
149042
+ });
149043
+ }
149044
+ if (type === "tool_return_message") {
149045
+ const returns = Array.isArray(msg.tool_returns) ? msg.tool_returns : [
149046
+ {
149047
+ tool_call_id: msg.tool_call_id,
149048
+ status: msg.status,
149049
+ tool_return: msg.tool_return,
149050
+ func_response: msg.func_response
149051
+ }
149052
+ ];
149053
+ return returns.flatMap((ret) => {
149054
+ const header = `[${timestamp}] tool_return (${ret.tool_call_id || "no-id"}) status=${ret.status || "unknown"}`;
149055
+ const body = renderUnknown(ret.tool_return ?? ret.func_response);
149056
+ return [header, body || "(empty)"];
149057
+ });
149058
+ }
149059
+ const fallbackText = renderText(msg.content) || renderUnknown(msg.content) || "(no content)";
149060
+ return [`[${timestamp}] ${type}`, fallbackText];
149061
+ };
149062
+ const sortChronological3 = (messages) => {
149063
+ return [...messages].sort((a, b) => {
149064
+ const ta = a.date ? new Date(a.date).getTime() : 0;
149065
+ const tb = b.date ? new Date(b.date).getTime() : 0;
149066
+ return ta - tb;
149067
+ });
149068
+ };
149069
+ const fetchConversationMessages = async (conversationId, agentIdForDefault, pageLimit, maxPages) => {
149070
+ const collected = [];
149071
+ const seenIds = new Set;
149072
+ let cursorBefore;
149073
+ for (let pageIndex = 0;pageIndex < maxPages; pageIndex += 1) {
149074
+ const page = await client.conversations.messages.list(conversationId, {
149075
+ limit: pageLimit,
149076
+ order: "desc",
149077
+ ...conversationId === "default" && agentIdForDefault ? { agent_id: agentIdForDefault } : {},
149078
+ ...cursorBefore ? { before: cursorBefore } : {}
149079
+ });
149080
+ const items = page.getPaginatedItems();
149081
+ if (items.length === 0) {
149082
+ break;
149083
+ }
149084
+ let newItems = 0;
149085
+ for (const item of items) {
149086
+ const id = item.id;
149087
+ if (id && !seenIds.has(id)) {
149088
+ seenIds.add(id);
149089
+ collected.push(item);
149090
+ newItems += 1;
149091
+ }
149092
+ }
149093
+ cursorBefore = items[items.length - 1]?.id;
149094
+ if (newItems === 0 || items.length < pageLimit) {
149095
+ break;
149096
+ }
149097
+ }
149098
+ return sortChronological3(collected);
149099
+ };
148785
149100
  if (action === "search") {
148786
149101
  const query = parsed.values.query;
148787
149102
  if (!query || typeof query !== "string") {
@@ -148846,6 +149161,44 @@ async function runMessagesSubcommand(argv) {
148846
149161
  console.log(JSON.stringify(sorted, null, 2));
148847
149162
  return 0;
148848
149163
  }
149164
+ if (action === "transcript") {
149165
+ const conversationId = parsed.values.conversation || parsed.values["conversation-id"];
149166
+ if (!conversationId || typeof conversationId !== "string") {
149167
+ console.error("Missing conversation id. Pass --conversation <id> or --conversation-id <id>.");
149168
+ return 1;
149169
+ }
149170
+ const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
149171
+ if (conversationId === "default" && !agentId) {
149172
+ console.error('Conversation "default" requires an agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.');
149173
+ return 1;
149174
+ }
149175
+ const pageLimit = Math.max(1, parseLimit3(parsed.values.limit, 100));
149176
+ const maxPages = Math.max(1, parseLimit3(parsed.values["max-pages"], 200));
149177
+ const outputPathRaw = parsed.values.out || parsed.values.output;
149178
+ const messages = await fetchConversationMessages(conversationId, agentId || undefined, pageLimit, maxPages);
149179
+ const transcript = messages.flatMap((msg) => formatEntry(msg)).join(`
149180
+
149181
+ `).trim();
149182
+ if (outputPathRaw && typeof outputPathRaw === "string") {
149183
+ const outputPath = resolve23(process.cwd(), outputPathRaw);
149184
+ await writeFile6(outputPath, `${transcript}
149185
+ `, "utf-8");
149186
+ console.log(JSON.stringify({
149187
+ conversation_id: conversationId,
149188
+ agent_id: agentId || null,
149189
+ message_count: messages.length,
149190
+ output_path: outputPath
149191
+ }, null, 2));
149192
+ return 0;
149193
+ }
149194
+ console.log(JSON.stringify({
149195
+ conversation_id: conversationId,
149196
+ agent_id: agentId || null,
149197
+ message_count: messages.length,
149198
+ transcript
149199
+ }, null, 2));
149200
+ return 0;
149201
+ }
148849
149202
  } catch (error) {
148850
149203
  console.error(error instanceof Error ? error.message : String(error));
148851
149204
  return 1;
@@ -148886,7 +149239,7 @@ async function runSubcommand(argv) {
148886
149239
  init_readOnlyShell();
148887
149240
  init_shell_command_normalization();
148888
149241
  import { homedir as homedir23 } from "node:os";
148889
- import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as resolve23 } from "node:path";
149242
+ import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as resolve24 } from "node:path";
148890
149243
  var MODE_KEY2 = Symbol.for("@letta/permissionMode");
148891
149244
  var PLAN_FILE_KEY2 = Symbol.for("@letta/planFilePath");
148892
149245
  var MODE_BEFORE_PLAN_KEY2 = Symbol.for("@letta/permissionModeBeforePlan");
@@ -148922,12 +149275,12 @@ function resolvePlanTargetPath2(targetPath, workingDirectory) {
148922
149275
  if (!trimmedPath)
148923
149276
  return null;
148924
149277
  if (trimmedPath.startsWith("~/")) {
148925
- return resolve23(homedir23(), trimmedPath.slice(2));
149278
+ return resolve24(homedir23(), trimmedPath.slice(2));
148926
149279
  }
148927
149280
  if (isAbsolute16(trimmedPath)) {
148928
- return resolve23(trimmedPath);
149281
+ return resolve24(trimmedPath);
148929
149282
  }
148930
- return resolve23(workingDirectory, trimmedPath);
149283
+ return resolve24(workingDirectory, trimmedPath);
148931
149284
  }
148932
149285
  function isPathInPlansDir2(path23, plansDir) {
148933
149286
  if (!path23.endsWith(".md"))
@@ -149203,7 +149556,7 @@ await __promiseAll([
149203
149556
  ]);
149204
149557
  import { randomUUID as randomUUID4 } from "node:crypto";
149205
149558
  import { homedir as homedir24 } from "node:os";
149206
- import { join as join30, resolve as resolve24 } from "node:path";
149559
+ import { join as join30, resolve as resolve25 } from "node:path";
149207
149560
  var DEFAULT_SETTINGS3 = {
149208
149561
  lastAgent: null,
149209
149562
  tokenStreaming: false,
@@ -149228,6 +149581,9 @@ var DEFAULT_LETTA_API_URL2 = "https://api.letta.com";
149228
149581
  function isSubagentProcess2() {
149229
149582
  return process.env.LETTA_CODE_AGENT_ROLE === "subagent";
149230
149583
  }
149584
+ function shouldPersistSessionState2() {
149585
+ return process.env.LETTA_CODE_AGENT_ROLE !== "subagent" && process.env.LETTA_DISABLE_SESSION_PERSIST !== "1";
149586
+ }
149231
149587
  function normalizeBaseUrl2(baseUrl) {
149232
149588
  let normalized = baseUrl.replace(/^https?:\/\//, "");
149233
149589
  normalized = normalized.replace(/\/$/, "");
@@ -149654,7 +150010,7 @@ class SettingsManager2 {
149654
150010
  return join30(workingDirectory, ".letta", "settings.json");
149655
150011
  }
149656
150012
  isProjectSettingsPathCollidingWithGlobal(workingDirectory) {
149657
- return resolve24(this.getProjectSettingsPath(workingDirectory)) === resolve24(this.getSettingsPath());
150013
+ return resolve25(this.getProjectSettingsPath(workingDirectory)) === resolve25(this.getSettingsPath());
149658
150014
  }
149659
150015
  getLocalProjectSettingsPath(workingDirectory) {
149660
150016
  return join30(workingDirectory, ".letta", "settings.local.json");
@@ -150616,8 +150972,8 @@ function acquireSwitchLock2() {
150616
150972
  const lock = getSwitchLock2();
150617
150973
  lock.refCount++;
150618
150974
  if (lock.refCount === 1) {
150619
- lock.promise = new Promise((resolve25) => {
150620
- lock.resolve = resolve25;
150975
+ lock.promise = new Promise((resolve26) => {
150976
+ lock.resolve = resolve26;
150621
150977
  });
150622
150978
  }
150623
150979
  }
@@ -150888,6 +151244,7 @@ SUBCOMMANDS (JSON-only)
150888
151244
  letta agents list [--query <text> | --name <name> | --tags <tags>]
150889
151245
  letta messages search --query <text> [--all-agents]
150890
151246
  letta messages list [--agent <id>]
151247
+ letta messages transcript --conversation <id> [--out <path>]
150891
151248
  letta blocks list --agent <id>
150892
151249
  letta blocks copy --block-id <id> [--label <label>] [--agent <id>] [--override]
150893
151250
  letta blocks attach --block-id <id> [--agent <id>] [--read-only] [--override]
@@ -151076,14 +151433,6 @@ async function main() {
151076
151433
  }
151077
151434
  const { checkAndAutoUpdate: checkAndAutoUpdate2 } = await init_auto_update().then(() => exports_auto_update);
151078
151435
  const autoUpdatePromise = startStartupAutoUpdateCheck(checkAndAutoUpdate2);
151079
- const { startDockerVersionCheck: startDockerVersionCheck2 } = await init_startup_docker_check().then(() => exports_startup_docker_check);
151080
- startDockerVersionCheck2().catch(() => {});
151081
- const { cleanupOldOverflowFiles: cleanupOldOverflowFiles2 } = await Promise.resolve().then(() => (init_overflow2(), exports_overflow));
151082
- Promise.resolve().then(() => {
151083
- try {
151084
- cleanupOldOverflowFiles2(process.cwd());
151085
- } catch {}
151086
- });
151087
151436
  const processedArgs = preprocessCliArgs(process.argv);
151088
151437
  let values;
151089
151438
  let positionals;
@@ -151109,7 +151458,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151109
151458
  printHelp();
151110
151459
  const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
151111
151460
  if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
151112
- await new Promise((resolve32) => setTimeout(resolve32, helpDelayMs));
151461
+ await new Promise((resolve33) => setTimeout(resolve33, helpDelayMs));
151113
151462
  }
151114
151463
  process.exit(0);
151115
151464
  }
@@ -151194,6 +151543,16 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151194
151543
  const isHeadless = values.prompt || values.run || !process.stdin.isTTY;
151195
151544
  telemetry2.setSurface(isHeadless ? "headless" : "tui");
151196
151545
  telemetry2.init();
151546
+ if (!isHeadless) {
151547
+ const { startDockerVersionCheck: startDockerVersionCheck2 } = await init_startup_docker_check().then(() => exports_startup_docker_check);
151548
+ startDockerVersionCheck2().catch(() => {});
151549
+ const { cleanupOldOverflowFiles: cleanupOldOverflowFiles2 } = await Promise.resolve().then(() => (init_overflow2(), exports_overflow));
151550
+ Promise.resolve().then(() => {
151551
+ try {
151552
+ cleanupOldOverflowFiles2(process.cwd());
151553
+ } catch {}
151554
+ });
151555
+ }
151197
151556
  if (command && !isHeadless) {
151198
151557
  console.error(`Error: Unknown command or argument "${command}"`);
151199
151558
  console.error("Run 'letta --help' for usage information.");
@@ -151321,9 +151680,9 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151321
151680
  process.exit(1);
151322
151681
  }
151323
151682
  } else {
151324
- const { resolve: resolve32 } = await import("path");
151683
+ const { resolve: resolve33 } = await import("path");
151325
151684
  const { existsSync: existsSync40 } = await import("fs");
151326
- const resolvedPath = resolve32(fromAfFile);
151685
+ const resolvedPath = resolve33(fromAfFile);
151327
151686
  if (!existsSync40(resolvedPath)) {
151328
151687
  console.error(`Error: AgentFile not found: ${resolvedPath}`);
151329
151688
  process.exit(1);
@@ -151798,6 +152157,7 @@ Error: ${message}`);
151798
152157
  setLoadingState("initializing");
151799
152158
  const { createAgent: createAgent3 } = await init_create3().then(() => exports_create2);
151800
152159
  let agent = null;
152160
+ let autoEnableMemfsForFreshAgent = false;
151801
152161
  if (fromAfFile2) {
151802
152162
  setLoadingState("importing");
151803
152163
  let result;
@@ -151882,10 +152242,7 @@ Error: ${message}`);
151882
152242
  });
151883
152243
  agent = result.agent;
151884
152244
  setAgentProvenance(result.provenance);
151885
- if (willAutoEnableMemfs) {
151886
- const { enableMemfsIfCloud: enableMemfsIfCloud3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
151887
- await enableMemfsIfCloud3(agent.id);
151888
- }
152245
+ autoEnableMemfsForFreshAgent = willAutoEnableMemfs;
151889
152246
  }
151890
152247
  if (!agent && resumingAgentId) {
151891
152248
  try {
@@ -151908,11 +152265,12 @@ Error: ${message}`);
151908
152265
  settingsManager2.updateLocalProjectSettings({ lastAgent: agent.id });
151909
152266
  settingsManager2.updateSettings({ lastAgent: agent.id });
151910
152267
  setAgentContext(agent.id, skillsDirectory2, resolvedSkillSources);
151911
- const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
151912
152268
  const agentId2 = agent.id;
151913
152269
  const agentTags = agent.tags ?? undefined;
151914
- const memfsSyncPromise = Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2)).then(({ applyMemfsFlags: applyMemfsFlags3 }) => applyMemfsFlags3(agentId2, memfsFlag, noMemfsFlag, {
151915
- agentTags
152270
+ const startupMemfsFlag = autoEnableMemfsForFreshAgent ? true : memfsFlag;
152271
+ const memfsSyncPromise = Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2)).then(({ applyMemfsFlags: applyMemfsFlags3 }) => applyMemfsFlags3(agentId2, startupMemfsFlag, noMemfsFlag, {
152272
+ agentTags,
152273
+ skipPromptUpdate: shouldCreateNew
151916
152274
  }));
151917
152275
  const secretsInitPromise = init_secretsStore2().then(() => exports_secretsStore2).then(({ initSecretsFromServer: initSecretsFromServer3 }) => initSecretsFromServer3(agentId2));
151918
152276
  const isResumingProject = !shouldCreateNew && !!resumingAgentId;
@@ -152053,7 +152411,7 @@ Error: ${message}`);
152053
152411
  }
152054
152412
  }
152055
152413
  }
152056
- if (!isSubagent) {
152414
+ if (shouldPersistSessionState2()) {
152057
152415
  settingsManager2.persistSession(agent.id, conversationIdToUse);
152058
152416
  }
152059
152417
  setAgentId(agent.id);
@@ -152199,4 +152557,4 @@ Error during initialization: ${message}`);
152199
152557
  }
152200
152558
  main();
152201
152559
 
152202
- //# debugId=594F8E4675FBE99864756E2164756E21
152560
+ //# debugId=275910C1663139CA64756E2164756E21