@letta-ai/letta-code 0.34.17 → 0.34.18

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 (37) hide show
  1. package/README.md +12 -0
  2. package/dist/agent-presets.js +5 -1
  3. package/dist/agent-presets.js.map +2 -2
  4. package/dist/mcp-client.js +2 -2
  5. package/dist/mcp-client.js.map +1 -1
  6. package/dist/mcp-oauth.js +2 -2
  7. package/dist/mcp-oauth.js.map +1 -1
  8. package/dist/types/agent/client-skills.d.ts.map +1 -1
  9. package/dist/types/agent/preloaded-skills.d.ts +4 -0
  10. package/dist/types/agent/preloaded-skills.d.ts.map +1 -0
  11. package/dist/types/agent/subagents/child-send-tracking.d.ts.map +1 -1
  12. package/dist/types/agent/subagents/index.d.ts.map +1 -1
  13. package/dist/types/agent/subagents/remote-turn-wait.d.ts.map +1 -1
  14. package/dist/types/agent/subagents/subagent-stream.d.ts.map +1 -1
  15. package/dist/types/auth/oauth.d.ts +8 -1
  16. package/dist/types/auth/oauth.d.ts.map +1 -1
  17. package/dist/types/backend/api/conversation-enqueue.d.ts +4 -1
  18. package/dist/types/backend/api/conversation-enqueue.d.ts.map +1 -1
  19. package/dist/types/headless-listener-launch.d.ts +5 -3
  20. package/dist/types/headless-listener-launch.d.ts.map +1 -1
  21. package/dist/types/headless-super-run-wait.d.ts +1 -1
  22. package/dist/types/headless-super-run-wait.d.ts.map +1 -1
  23. package/dist/types/tools/impl/remote-external-coding-agent.d.ts +1 -0
  24. package/dist/types/tools/impl/remote-external-coding-agent.d.ts.map +1 -1
  25. package/dist/types/tools/impl/task-stop.d.ts.map +1 -1
  26. package/dist/types/tools/impl/workflow.d.ts +5 -0
  27. package/dist/types/tools/impl/workflow.d.ts.map +1 -1
  28. package/dist/types/tools/workflow/sdk-spawner.d.ts.map +1 -1
  29. package/dist/types/tools/workflow/types.d.ts +2 -0
  30. package/dist/types/tools/workflow/types.d.ts.map +1 -1
  31. package/letta.js +355 -412
  32. package/npm-shrinkwrap.json +86 -86
  33. package/package.json +2 -2
  34. package/scripts/source-file-size-baseline.json +1 -1
  35. package/skills/maintaining-core-memory/SKILL.md +101 -0
  36. package/skills/maintaining-deferred-memory/SKILL.md +84 -0
  37. package/skills/workflow-authoring/SKILL.md +3 -1
package/letta.js CHANGED
@@ -3411,6 +3411,7 @@ __export(exports_oauth, {
3411
3411
  validateCredentialsWithResult: () => validateCredentialsWithResult,
3412
3412
  validateCredentials: () => validateCredentials,
3413
3413
  revokeToken: () => revokeToken,
3414
+ resolveAuthBaseUrl: () => resolveAuthBaseUrl,
3414
3415
  requestDeviceCode: () => requestDeviceCode2,
3415
3416
  refreshAccessToken: () => refreshAccessToken3,
3416
3417
  pollForToken: () => pollForToken2,
@@ -3418,6 +3419,28 @@ __export(exports_oauth, {
3418
3419
  OAUTH_CONFIG: () => OAUTH_CONFIG,
3419
3420
  LETTA_CLOUD_API_URL: () => LETTA_CLOUD_API_URL
3420
3421
  });
3422
+ function resolveAuthBaseUrl() {
3423
+ const override = process.env.LETTA_AUTH_BASE_URL?.trim();
3424
+ if (!override) {
3425
+ return DEFAULT_AUTH_BASE_URL;
3426
+ }
3427
+ let warning = null;
3428
+ try {
3429
+ const url = new URL(override);
3430
+ const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
3431
+ if (url.protocol === "https:" || url.protocol === "http:" && isLocalhost) {
3432
+ return url.origin;
3433
+ }
3434
+ warning = "must be an https origin (http is allowed for localhost)";
3435
+ } catch {
3436
+ warning = "is not a valid URL";
3437
+ }
3438
+ if (!hasWarnedAboutInvalidAuthBaseUrl) {
3439
+ hasWarnedAboutInvalidAuthBaseUrl = true;
3440
+ console.warn(`Ignoring invalid LETTA_AUTH_BASE_URL (${JSON.stringify(override)}): ${warning}. Falling back to ${DEFAULT_AUTH_BASE_URL}.`);
3441
+ }
3442
+ return DEFAULT_AUTH_BASE_URL;
3443
+ }
3421
3444
  function getOAuthAuthHost() {
3422
3445
  try {
3423
3446
  return new URL(OAUTH_CONFIG.authBaseUrl).host;
@@ -3776,7 +3799,7 @@ function classifyCredentialValidationError(error) {
3776
3799
  }
3777
3800
  return { ok: false, reason: "unknown", message };
3778
3801
  }
3779
- var LETTA_CLOUD_API_URL = "https://api.letta.com", OAUTH_CONFIG, OAuthRefreshError, DEVICE_CODE_REQUEST_MAX_ATTEMPTS = 2, DEVICE_CODE_RETRY_DELAY_MS = 100, TOKEN_POLL_RESPONSE_FAILURE_LIMIT = 2, OAUTH_REQUEST_ID_HEADERS, OAuthTransientResponseError;
3802
+ var LETTA_CLOUD_API_URL = "https://api.letta.com", DEFAULT_AUTH_BASE_URL = "https://app.letta.com", hasWarnedAboutInvalidAuthBaseUrl = false, OAUTH_CONFIG, OAuthRefreshError, DEVICE_CODE_REQUEST_MAX_ATTEMPTS = 2, DEVICE_CODE_RETRY_DELAY_MS = 100, TOKEN_POLL_RESPONSE_FAILURE_LIMIT = 2, OAUTH_REQUEST_ID_HEADERS, OAuthTransientResponseError;
3780
3803
  var init_oauth = __esm(() => {
3781
3804
  init_letta_client();
3782
3805
  init_error();
@@ -3784,7 +3807,9 @@ var init_oauth = __esm(() => {
3784
3807
  OAUTH_CONFIG = {
3785
3808
  clientId: "ci-let-724dea7e98f4af6f8f370f4b1466200c",
3786
3809
  clientSecret: "",
3787
- authBaseUrl: "https://app.letta.com",
3810
+ get authBaseUrl() {
3811
+ return resolveAuthBaseUrl();
3812
+ },
3788
3813
  apiBaseUrl: LETTA_CLOUD_API_URL
3789
3814
  };
3790
3815
  OAuthRefreshError = class OAuthRefreshError extends Error {
@@ -5916,7 +5941,7 @@ var package_default;
5916
5941
  var init_package = __esm(() => {
5917
5942
  package_default = {
5918
5943
  name: "@letta-ai/letta-code",
5919
- version: "0.34.17",
5944
+ version: "0.34.18",
5920
5945
  lettaStartupLogProtocol: 1,
5921
5946
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5922
5947
  type: "module",
@@ -6191,7 +6216,7 @@ var init_package = __esm(() => {
6191
6216
  },
6192
6217
  lettaLifecycleProtocol: 1,
6193
6218
  lettaRuntimeGraphProtocol: 1,
6194
- lettaRuntimeGraphSha512: "sha512-NGzNq0UmSHPiPqvc6bflUL57U2h5xZSgf9HvpbR98ocuoM1dHLN1hiXv6qNEa3lOlj2paYI/clNKR+U1YW0TiA=="
6219
+ lettaRuntimeGraphSha512: "sha512-cubBaizY8rbgiq9dfHU/uRyZBgkGvLfJc+8+ovpV1raZnQjE+tRsAKFAKP69UHWhNFRE49viVTyqmLIiSK8v8Q=="
6195
6220
  };
6196
6221
  });
6197
6222
 
@@ -99266,6 +99291,8 @@ You may also have access to shared memory: memory created independently of any s
99266
99291
 
99267
99292
  Unlike the rest of your external memory, shared memory is not scoped to *you* specifically (since it may be attached to multiple agents at the same time), so each shared memory repository will have a different local projection root and remote git origin.
99268
99293
 
99294
+ Attached shared-memory mounts are shallow checkouts containing only the latest commit. If you need past history, run \`git fetch --deepen=<n>\` or \`git fetch --unshallow\` inside the mount only when needed, since some repositories have very long histories.
99295
+
99269
99296
  Commit intended changes inside read/write attached shared-memory repositories. The harness pushes clean committed changes after each turn and reports dirty files, conflicts, or push failures in a later reminder.
99270
99297
 
99271
99298
  ### Syncing memory, state, and context
@@ -99823,6 +99850,8 @@ You may also have access to shared memory: memory created independently of any s
99823
99850
 
99824
99851
  Unlike the rest of your external memory, shared memory is not scoped to *you* specifically (since it may be attached to multiple agents at the same time), so each shared memory repository will have a different local projection root and remote git origin.
99825
99852
 
99853
+ Attached shared-memory mounts are shallow checkouts containing only the latest commit. If you need past history, run \`git fetch --deepen=<n>\` or \`git fetch --unshallow\` inside the mount only when needed, since some repositories have very long histories.
99854
+
99826
99855
  Commit intended changes inside read/write attached shared-memory repositories. The harness pushes clean committed changes after each turn and reports dirty files, conflicts, or push failures in a later reminder.
99827
99856
 
99828
99857
  ### Syncing memory, state, and context
@@ -103471,7 +103500,7 @@ async function syncRepoMount(args) {
103471
103500
  await withRepositoryCheckout(args.directory, async (directory, fresh) => {
103472
103501
  args = { ...args, publishedDirectory: args.directory, directory };
103473
103502
  if (fresh) {
103474
- await runGitWithRetry(directory, ["clone", args.remoteUrl, "."], args.token, {
103503
+ await runGitWithRetry(directory, ["clone", "--depth", "1", args.remoteUrl, "."], args.token, {
103475
103504
  operation: `clone repository ${args.repositoryName}`,
103476
103505
  timeoutMs: GIT_CLONE_TIMEOUT_MS
103477
103506
  });
@@ -106605,34 +106634,29 @@ name: memory
106605
106634
  description: Background memory upkeep during another task; when memory is the user's main request, edit it directly instead. Runs silently with no notification or result, so continue immediately. Make the assignment self-contained and quote the user's corrections and exceptions verbatim.
106606
106635
  tools: Bash, Read, Edit, Write
106607
106636
  model: inherit
106608
- skills: creating-skills
106637
+ skills: creating-skills, maintaining-core-memory, maintaining-deferred-memory
106609
106638
  fork: false
106610
106639
  launchProfile: memory-subagent
106611
106640
  ---
106612
106641
 
106613
- You are the primary agent's background memory subagent. The parent transcript is available as reference material when needed, not a task to continue. Work only on the memory request below. Never ask the primary agent or user questions, send messages, or launch further agents. Your final report stays in this background task.
106614
-
106615
- Your tools are Bash, Read, Edit, and Write. Your working memory repository is \`$MEMORY_DIR\`; the assignment also gives the exact path. For a memory update it is a private worktree of the agent's memory: the harness merges your commits into the main checkout when you finish, and anything left uncommitted is discarded. For a Git repair it is the checkout itself. Read current files before editing; memory may have changed since the assignment. Keep all writes inside this repository. Do not modify git configuration or hook files.
106616
-
106617
- ## Updating memory
106618
-
106619
- Capture the requested facts, preferences, corrections, or deletions. Use the assignment directly when it is sufficient. Consult the parent transcript only for a specific missing fact or ambiguity; do not read it for general orientation. Preserve the exact scope of facts and exceptions without adding inferred preferences. Keep quoted factual corrections and constraints verbatim rather than generalizing them. Update existing entries rather than duplicating them, and replace stale information at its source. Preserve unrelated content and established identity. Do not store secrets or ephemeral task logs. Update indexes when adding, moving, or deleting files.
106620
-
106621
- Make focused edits. Reorganize or defragment memory only when explicitly requested. Do not perform transcript reflection or unrelated skill maintenance. For requested skill changes, apply the preloaded \`creating-skills\` guidance in \`$MEMORY_DIR/skills/\`; maintain skills in place rather than packaging them for distribution.
106642
+ You are a background memory subagent. Make the memory change in the assignment below, following the preloaded skills; consult the parent transcript only for a specific missing fact or ambiguity.
106622
106643
 
106623
106644
  ## Git
106624
106645
 
106625
- Inspect \`git status\` before editing. If a merge or rebase is already in progress, resolve it first by reading both sides and preserving the intended memory. Never prefer one side wholesale. Stage only resolved files and finish the existing operation with a noninteractive editor. Do not abort, reset, stash, amend, discard, or commit unrelated changes. If the intended resolution is unclear, leave the unresolved state intact and describe the blocker in your final report.
106626
-
106627
- For a repair-only request, stop after repairing the reported Git or validation state; if it has already been resolved, make no changes.
106646
+ \`$MEMORY_DIR\` is a private worktree of the agent's memory. The harness merges your commits into the main checkout when you finish and handles sync, so commit but do not push; uncommitted edits are discarded. If a merge or rebase is already in progress, resolve it by reading both sides and preserving the intended memory, then finish the operation; do not abort, reset, stash, or amend.
106628
106647
 
106629
- For an update or reorganization request, make the requested changes and commit only the files you changed: stage them by explicit path, never with \`git add -A\` or \`git add .\`, because the primary agent may be editing other files in this checkout at the same time. Use a concise commit message and the repository's configured authorship. Preserve required frontmatter and obey the repository's validation hooks. Do not push: the harness handles normal sync after you finish.
106648
+ Stage only the files you changed, by explicit path, and commit them in one call:
106630
106649
 
106631
- Return a brief report of actual changes or the unresolved blocker. Do not claim that an uncommitted edit or failed operation succeeded.
106650
+ \`\`\`bash
106651
+ cd "$MEMORY_DIR"
106652
+ git add <file>...
106653
+ git commit -m "<type>(memory): <summary>
106632
106654
 
106633
- ## Memory layout
106655
+ Updates:
106656
+ - <what changed and why>"
106657
+ \`\`\`
106634
106658
 
106635
- Root \`MEMORY.md\` has no frontmatter and is an index linking core files and deferred indexes. Other root Markdown files are core memory and require exactly \`name\` and \`description\` frontmatter. Child directories contain memory only when they have a frontmatter-free \`MEMORY.md\`; other Markdown files in them also require \`name\` and \`description\`. \`skills/\` is separate procedural memory. Do not edit generated \`memory_filesystem.md\` or \`.sync-state.json\`.
106659
+ \`<type>\` is \`fix\` for correcting wrong or stale memory, \`feat\` for new memory, and \`chore\` for reorganization or minor edits. Do not change git configuration or bypass hooks; if the commit fails, report the failure rather than claiming success.
106636
106660
  `;
106637
106661
  var init_memory_v2 = () => {};
106638
106662
 
@@ -106862,121 +106886,28 @@ name: reflection
106862
106886
  description: Background agent that reflects on recent conversations to update memory and maintain skills
106863
106887
  tools: Bash, Edit
106864
106888
  model: inherit
106865
- skills: creating-skills
106889
+ skills: creating-skills, maintaining-core-memory, maintaining-deferred-memory
106866
106890
  launchProfile: memory-subagent
106867
106891
  ---
106892
+ You are a reflection subagent tasked with improving a primary agent's capabilities and knowledge to perform better on future tasks.
106893
+ Using recent conversation transcripts, identify potential repeatable workflows (which can be converted into skills), behavior failures (which require updates to prompts), and general information and knowledge.
106894
+ The primary agent's context (prompts, skills, and deferred memory) is stored in $MEMORY_DIR, which contains the "Memory Filesystem" of the primary agent.
106895
+ Read through the transcript to identify and make updates to memory. You run autonomously and return a single final report when done. You CANNOT ask questions — all instructions are provided upfront, so make reasonable assumptions based on context and document any assumptions you make.
106868
106896
 
106869
- You are a reflection subagent launched in the background to manage the primary agent's memory, context, and skills after recent conversation activity. You run autonomously and return a single final report when done. You CANNOT ask questions — all instructions are provided upfront, so make reasonable assumptions based on context and document any assumptions you make.
106870
-
106871
- **You are NOT the primary agent.** You are reviewing conversations that already happened:
106872
- - "system" messages are the primary agent's system prompt — use them only to understand the agent's identity and what's relevant to the user. They are not something you edit directly; memory edits flow through files in \`$MEMORY_DIR\`.
106873
- - "assistant" messages are from the primary agent
106874
- - "user" messages are from the primary agent's user
106875
-
106876
- Your job is to turn lasting learnings from those conversations into changes to the primary agent's memory filesystem: memory edits for facts, preferences, and corrections, and — only when a reusable procedure appears — skill changes. Then commit them.
106877
-
106878
- ## The Memory Filesystem
106879
-
106880
- The primary agent's context (its prompts, skills, and external memory files) is stored in a git-backed "memory filesystem" rooted at \`$MEMORY_DIR\`. Committed changes are reflected in the primary agent's context. Every piece of memory lives in one of three tiers, and the tier decides when the agent sees it and how it should be written:
106881
-
106882
- | | **Root files** | **Deferred memory** | **Skills** |
106883
- |---|---|---|---|
106884
- | **Location** | Markdown files directly in \`$MEMORY_DIR\` | Directories under \`$MEMORY_DIR\` (other than \`skills/\`) | \`$MEMORY_DIR/skills/<name>/\` |
106885
- | **When the agent sees it** | Every turn, in full — part of the system prompt | Only when the agent opens the directory with \`Memory(<dir>)\` or reads a file | Name and description every turn; the body only when the agent loads the skill |
106886
- | **Cost** | Paid on every turn | Paid only when opened | Description on every turn; body only when loaded |
106887
- | **What belongs here** | Identity, standing instructions, and facts that matter on every turn; the root \`MEMORY.md\` index of deferred memory | Knowledge about a subject (a person, project, repo, system, or incident) that the agent looks up when that subject comes up | Procedures with a clear "use when" trigger — usually a repeatable, multi-step workflow |
106888
- | **Written as** | Instructions to the agent | Reference notes: instructions about the subject first, then facts | A procedure: steps, exact commands, pitfalls |
106889
- | **Description says** | What belongs in the file | What the memory is *about* | What the skill does (third person) and when to use it |
106890
-
106891
- ### Choosing a tier
106892
-
106893
- Decide placement for each learning in this order:
106894
-
106895
- 1. **Should it change how the agent behaves or what it knows on every turn?** Put it in a root file, written as an instruction. Root is expensive, so this bar is high.
106896
- 2. **Can you write a clear "Use when the user wants to …" trigger for it, and is it a procedure?** Make it a skill (see "Skills" below for when a skill is warranted).
106897
- 3. **Otherwise it is knowledge about a subject.** Put it in deferred memory, in the directory for that subject.
106898
-
106899
- One-off task state belongs nowhere.
106900
-
106901
- ### Root files
106902
-
106903
- Root Markdown files are part of the primary agent's system prompt: always in context and visible to the agent. Use them to define the core behavior of the agent: what it always knows, and the prompting that controls how it behaves.
106904
-
106905
- Writing root files:
106906
- * Write instructions, not explicit feedback. User or environment feedback tells you how the agent's behavior or knowledge should change. Write a prompt to affect this change, rather than simply recording the feedback.
106907
- * Leave out the evidence or context for the edit. All context modifications are git-tracked with commit messages, so do not require an explanation for why an edit was made within the edit or details about the context under which the edit was made.
106908
- * Minimize bloat. Too many tokens can distract agents and add cost to every turn. Instructions that are only needed sometimes belong in a skill (if a trigger can be defined) or in deferred memory.
106909
- * Root files that define the agent's identity and behavior (such as \`persona.md\`) are load-bearing. Edit them surgically — append, modify specific entries, adjust wording. Never rewrite them wholesale or silently overwrite established identity.
106897
+ You are not the primary agent and do not reply to anyone. In the transcript, "assistant" messages are the primary agent, "user" messages are its user, and "system" messages are its system prompt, compiled from the memory files you edit.
106910
106898
 
106911
- **The root \`MEMORY.md\` is the only entry point to deferred memory.** The agent cannot see a directory's contents until it opens it, so this index must carry what the agent needs to decide to open one: for each top-level directory, a relative Markdown link, what it covers, and when to open it. For example:
106912
-
106913
- \`\`\`
106914
- - [letta-code/](letta-code/MEMORY.md) — repo conventions, CI, release process. Open before working in letta-code.
106915
- \`\`\`
106916
-
106917
- This is where "when to use" belongs for deferred memory — in the parent's link, not in the memory's own description.
106918
-
106919
- ### Deferred memory
106920
-
106921
- Directories contain *deferred memory*. Higher directory levels hold memory that is accessed more often; more deeply nested directories hold memory needed less often. Place related memory in the same directory so it can be loaded together. Each directory has a \`MEMORY.md\`, which is the directory's top level: when the primary agent calls \`Memory(<dir>)\`, it reads \`<dir>/MEMORY.md\` in full, plus a listing of the directory's other entries (each file's \`description\` and the first paragraph of each subdirectory's \`MEMORY.md\`). The listing shows at most 10 entries, most recently edited first. The agent can also read files directly with other tools.
106922
-
106923
- Decide what goes in a \`MEMORY.md\` by what the agent should get from opening that directory:
106924
- * **Small topic: put everything in \`MEMORY.md\`.** If the whole topic fits comfortably (well under 20,000 characters) and most of it is useful whenever the topic comes up, keep it in \`MEMORY.md\` with no other files. Splitting adds reads without saving context.
106925
- * **Large topic, or one rarely needed in full: \`MEMORY.md\` holds what is needed every time, plus a map.** Keep only what the agent needs whenever it opens the directory, and defer the rest into files, or into subdirectories when a subtopic is itself large. \`MEMORY.md\` must then tell the agent where to go next, or that it can stop: say what each deferred file or subdirectory covers and when it is worth opening, using ordinary relative Markdown links. Do not rely on the listing, since it shows only the 10 most recently edited entries and stable files fall out of it.
106926
- * **Either way, start \`MEMORY.md\` with a short plain-prose paragraph stating the directory's scope.** That paragraph is what the parent directory's listing shows, so do not open with a heading or a list.
106927
- * Aim for fewer than 10 entries (files plus subdirectories) per directory so the listing stays complete; group related files into subdirectories only within the configured \`maxDepth\` (see below). At the depth limit, use clearly scoped sibling files and explicit links in \`MEMORY.md\`, or reorganize topics into shallower directories. The depth limit takes precedence over the fewer-than-10-entries guideline.
106928
-
106929
- Writing deferred memory contents (in \`MEMORY.md\` and in other files):
106930
- * **One subject per file**, named the way a future request would name it. If two files cover the same subject, merge them.
106931
- * **Instructions first, then facts.** The agent opens deferred memory because it is about to act on that subject, so lead with what it should do differently — conventions, gotchas, things to avoid — written as instructions, like root files. Background facts follow.
106932
- * **Make each statement stand alone.** Name the subject ("The letta-code release workflow …") rather than "it" or "the repo"; the agent may read only part of a file.
106933
- * **Organize by topic under stable headings, not as a dated log.** Record the current state and fix contradictions in place. Use absolute dates only for facts that are genuinely time-bound.
106934
- * **Leave out evidence and history**, as with root files. Git keeps it.
106935
-
106936
- Writing memory descriptions (every deferred file other than \`MEMORY.md\`):
106937
- * Describe what the memory is *about*, not when to use it. Lead with the subject (a person, project, repo, system, or incident) using the names a future request would mention, then the subtopics it covers.
106938
- * If you can write a clear "use when the user wants to…" trigger, the content is procedural and belongs in a skill.
106939
- * Make siblings distinguishable by subject; if two files cover the same subject, merge them.
106940
- * Describe scope, not current contents: no entry lists, dates, status, or why the file was created.
106941
-
106942
- ### Skills
106943
-
106944
- The \`skills/\` folder contains skills (folders which adhere to the skills specification). Skills are loaded by the primary agent with a \`Skill\` tool. Skill names and descriptions are automatically provided to the agent in context, so skills do not need to be indexed by \`MEMORY.md\` unless they relate to a specific memory.
106945
-
106946
- Skills are not the default. A one-off task, a fact, or a preference belongs in memory, not a skill. Use a skill only when the conversation reveals a repeatable procedure that clearly generalizes beyond this session and has a trigger you can state.
106947
-
106948
- Apply the preloaded \`creating-skills\` guidance to skills in \`$MEMORY_DIR/skills/\`; maintain them in place rather than packaging them for distribution.
106949
-
106950
- ### Frontmatter, size, and nesting limits
106951
-
106952
- * \`MEMORY.md\` files (root and child) have no frontmatter.
106953
- * Every other memory Markdown file has exactly \`name\` and \`description\` frontmatter; the memory repo's pre-commit check rejects missing or extra keys.
106954
- * \`SKILL.md\` files are exempt from that check and follow the preloaded \`creating-skills\` guidance instead.
106955
- * By default the memory repo's pre-commit check blocks any memory file over 20,000 characters and root Markdown totaling over 65,536 characters (\`.memfs.config.json\` may override these). Root files are paid for on every turn, so keep them well below the limits and move detail into child directories rather than growing root.
106956
- * **Nesting is bounded by \`maxDepth\` in \`$MEMORY_DIR/.memfs.config.json\`.** The default configuration sets \`maxDepth: 2\`. Depth counts directories between the repo root and a memory file: \`persona.md\` is depth 0, \`topic/MEMORY.md\` is depth 1, and \`topic/subtopic/notes.md\` is depth 2. With that default, \`topic/subtopic/detail/notes.md\` is rejected at depth 3. Read the repo's configuration before adding or moving memory; an omitted \`maxDepth\` imposes no depth limit. This constraint excludes \`skills/\`. Fit memory to the configured limit rather than changing the configuration to permit deeper nesting.
106957
-
106958
- You can create, delete, or modify files (contents, names, descriptions). You can also move files between root and child directories to change their tier (e.g., root → child directory removes it from in-context).
106959
-
106960
- ## Process
106899
+ ## Trajectory Format
106961
106900
 
106962
106901
  The payload is at \`$TRANSCRIPT_PATH\`. It may be either:
106963
106902
 
106964
106903
  1. a JSON message array for one conversation, or
106965
106904
  2. a \`multi_transcript_reflection_payload\` manifest. If it is a manifest, read every \`payload_path\` listed in \`transcripts\` and synthesize across all slices. Slices marked \`mode: "replay"\` were already reflected before and are intentionally included for another pass; use them for deduplication, contradiction resolution, and cross-session pattern extraction.
106966
106905
 
106967
- When reviewing multiple transcripts, prefer patterns supported across sessions, resolve contradictions in favor of the latest evidence, and avoid recording one-off task state. Follow the phases below in order.
106968
-
106969
- ### Phase 1 — Investigate
106970
-
106971
- Understand the current memory landscape before changing anything. Your user prompt already includes a \`<memory_filesystem>\` tree of memory file names (no descriptions; \`skills/\` is omitted) and the full content of every root file inlined in \`<memory>\` blocks — start there, since those are the parent agent's in-context prompts.
106972
-
106973
- Read \`$MEMORY_DIR/.memfs.config.json\` if present to identify the repo's nesting and size limits before planning its structure.
106974
-
106975
- For deferred memory, read the relevant child \`MEMORY.md\` files and each candidate file's \`description\` frontmatter (for example with \`head\`) to decide what's worth reading, then fetch contents from \`$MEMORY_DIR\` on demand. Follow ordinary relative Markdown links from \`MEMORY.md\` when relevant. You cannot integrate new learnings into existing structure if you don't know the structure.
106906
+ When reviewing multiple transcripts, prefer patterns supported across sessions, resolve contradictions in favor of the latest evidence, and avoid recording one-off task state.
106976
106907
 
106977
- For skills, list \`$MEMORY_DIR/skills/\` and read each \`SKILL.md\` frontmatter description to triage adjacency to the candidate procedure, then read the full \`SKILL.md\` only for adjacent-looking skills (or skills whose description is too vague to tell). If no description looks adjacent, you don't need to read any \`SKILL.md\`. When unsure about adjacency, err on the side of reading.
106908
+ Read transcripts in bounded chunks: check the size first, read files up to 15000 bytes in full, and use targeted reads on larger ones (\`head\`, \`tail\`, \`grep\`, \`sed -n\` on Unix; \`Get-Content\`, \`Select-String\` in PowerShell, which the Bash tool runs on Windows).
106978
106909
 
106979
- ### Phase 2 — Extract
106910
+ ## Trajectory review
106980
106911
 
106981
106912
  Review the conversation and identify candidate learnings worth persisting. Prioritize in this order:
106982
106913
 
@@ -106986,132 +106917,59 @@ Review the conversation and identify candidate learnings worth persisting. Prior
106986
106917
  4. **Contradictions** — anything that conflicts with what's currently stored in memory
106987
106918
  5. **Reusable procedures** — repeatable, multi-step workflows that may belong in skills
106988
106919
 
106989
- For each candidate, apply these filters before acting:
106990
-
106991
- - **Lasting or ephemeral?** One-off details tied to a single session — specific line numbers, exact error messages, temporary file paths, debug ports, intermediate calculations, particular page numbers discussed — are ephemeral. Don't store them.
106992
- - **Already captured?** If memory or skills already contain this information adequately, skip it.
106993
- - **Generalizable?** Distill reusable patterns, not event transcripts. "Write short chapters that end on cliffhangers" is worth storing; "User edited chapter 3 paragraph 2 on Tuesday" is not. "Hedge FX exposure on quarterly positions" is worth storing; "Sold 500 shares of AAPL at $187.50" is not. "Write table-driven tests with testify" is worth storing; "User ran tests at 3pm on Tuesday" is not. The raw conversation is already searchable — don't re-record it.
106994
- - **Temporal references?** Convert any relative dates ("yesterday", "last week", "a few days ago") to absolute dates before writing them.
106995
- - **Which tier?** Apply "Choosing a tier" above.
106996
-
106997
- If nothing survives filtering, make no changes and skip to Phase 5 with no commit.
106998
-
106999
- ### Phase 3 — Update
107000
-
107001
- For each learning that survived Phase 2, make surgical, well-placed changes.
107002
-
107003
- #### Memory edits
107004
-
107005
- - **Placement**: Put the learning in the tier chosen in Phase 2, written the way that tier's section above describes.
107006
- - **Integration**: If an existing file already covers this subject, update it. Only create a new file when the subject is genuinely distinct and has no natural home in existing files. Fragmentation makes memory harder to navigate.
107007
- - **Contradiction resolution**: If new information contradicts existing memory, fix the stale entry at the source. Do not append the new version alongside the old.
107008
- - **Discovery paths**: When adding or moving content, update the links in root \`MEMORY.md\` and any affected child \`MEMORY.md\` files so every deferred file stays reachable. Keep \`name\` and \`description\` frontmatter accurate.
107009
-
107010
- #### Skills (only when a reusable procedure appears)
107011
-
107012
- Only make a skill change when the conversation demonstrates a repeatable, multi-step workflow with enough concrete detail to be actionable. Pick **at most one** operation, listed in rough order of preference (prefer modifying an existing skill over creating a new one):
107013
-
107014
- - \`update\` — an existing skill covers the workflow, but the conversation revealed a wrong, dangerous, or outdated step. Read the existing file, then fix that step in place with Edit; preserve the rest.
107015
- - \`extend\` — an existing skill covers a similar workflow, and the conversation revealed a new variant or edge case. Add a new section with Edit rather than duplicating the skill or rewriting existing sections.
107016
- - \`deprecate\` — an existing skill is obsolete, harmful, or replaced. Either \`rm -r skills/<name>/\` (delete mode), or add \`deprecated: "true"\` (and optionally \`replaced_by: <skill-name>\`) under the frontmatter \`metadata\` map, start the description with "Deprecated:" followed by the replacement skill's name, and add a short note at the top of the body explaining why (marker mode).
107017
- - \`split\` — an existing skill has drifted to bundle two distinct procedures and the conversation makes that painful. Write both replacement \`SKILL.md\` files (plus relevant companion files for each) with Bash, then delete the source skill with Bash or trim it with Edit. Use sparingly.
107018
- - \`create\` — a genuinely novel, repeatable procedure with concrete detail (commands, tool patterns, config values), and no existing skill covers it even partially.
107019
- - \`none\` — one-off, trivial, informational, already covered, or better stored as ordinary memory. Make no skill changes.
106920
+ Skip anything ephemeral (one-off task state, line numbers, exact errors, temporary paths) or already captured. Convert relative dates to absolute ones. If nothing survives, make no changes and do not commit.
107020
106921
 
107021
- As a heuristic, when unsure between \`create\` and \`none\`, choose \`none\`. When unsure between \`create\` and a modify op, choose the modify op.
106922
+ ## Making memory updates
107022
106923
 
107023
- For \`update\`/\`extend\`, preserve the existing frontmatter; change the description only if the skill's scope or triggers changed.
106924
+ Write each change the way the preloaded skills describe. Before editing, read the relevant existing files so you integrate rather than duplicate. For skills, prefer updating or extending an existing skill over creating one, make at most one skill change per run, and when unsure whether a procedure deserves a skill, make none.
107024
106925
 
107025
- Do not report a \`create\`, \`update\`, \`extend\`, or \`split\` as merely "intended" if Bash can perform the write. The final report must describe actual filesystem changes.
106926
+ Use Edit for files that already exist. Create new files with a quoted heredoc on Unix, or on Windows a single-quoted here-string piped to \`Set-Content -Encoding utf8\`; memory files must stay UTF-8. Edit does not expand environment variables: resolve absolute paths first (\`printf "%s/persona.md\\n" "$MEMORY_DIR"\`, or \`Join-Path $env:MEMORY_DIR "persona.md"\` in PowerShell) and pass the printed path. Keep all writes inside \`$MEMORY_DIR\`.
107026
106927
 
107027
- ### Phase 4 — Review
107028
-
107029
- Quick sanity pass before committing.
107030
-
107031
- - **No secrets or junk**: Do not persist sensitive values, raw logs, or ephemeral transcript details.
107032
-
107033
- #### Memory
107034
-
107035
- - **Stale content**: Did the conversation make anything in existing memory obsolete or superseded? Remove or update it now.
107036
- - **Cross-reference integrity**: If you deleted or moved a file, check whether any ordinary relative Markdown links point to the old location and update them.
107037
- - **Tier check**: Did you add anything to root that's really deferred material? Move it to a child directory. Did you leave something in a child directory that the agent needs on every turn? Promote it to root.
107038
- - **Nesting check**: Confirm every added or moved memory file stays within the configured \`maxDepth\`, including each directory's \`MEMORY.md\`.
107039
- - **Discoverability**: For each deferred file you added or moved, confirm the primary agent can reach it from root \`MEMORY.md\` through each intermediate directory's \`MEMORY.md\`, that each link says when the target is worth opening, and that its \`description\` says what it is about.
107040
-
107041
- #### Skills (only if you made a skill change)
107042
-
107043
- - **No near-duplicates**: For \`create\`, scan the tree once more — is there really no existing skill that covers this? If you spot a partial overlap you missed, switch to \`extend\`.
107044
- - **Companion file completeness**: For \`create\`/\`split\`, if \`SKILL.md\` references files under \`scripts/\`, \`references/\`, or \`templates/\`, verify those paths actually exist.
107045
- - **Stale skill references**: For \`deprecate\` (delete mode) or \`split\`, check whether any memory or skill file references the old skill path, and update those references.
107046
-
107047
- ### Phase 5 — Commit
106928
+ ## Commit
107048
106929
 
107049
106930
  Before writing the commit, resolve the actual ID values:
107050
106931
  \`\`\`bash
107051
106932
  echo "CHILD_AGENT_ID=$LETTA_AGENT_ID"
107052
- echo "PARENT_AGENT_ID=$LETTA_PARENT_AGENT_ID"
107053
106933
  \`\`\`
107054
106934
 
107055
- 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\` in the commit message. Run git commands only from \`$MEMORY_DIR\`. Use plain \`-m "..."\` with an embedded multi-line string exactly as shown below:
106935
+ Use the printed value (e.g., \`agent-abc123...\`) in the trailer. If it is empty or unset, omit the trailer. Never write a literal variable name like \`$LETTA_AGENT_ID\` in the commit message. Run git commands only from \`$MEMORY_DIR\`. Use plain \`-m "..."\` with an embedded multi-line string exactly as shown below:
107056
106936
 
107057
106937
  \`\`\`bash
107058
106938
  cd $MEMORY_DIR
107059
106939
  git add -A
107060
106940
  git commit --author="Reflection Subagent <<CHILD_AGENT_ID>@letta.com>" -m "<type>(reflection): <summary> 🔮
107061
106941
 
107062
- Reviewed transcript: <transcript_filepath>
106942
+ Reviewed transcripts:
106943
+ - <transcript_filepath>
106944
+ - ...
107063
106945
 
107064
106946
  Updates:
107065
106947
  - <what changed and why>
107066
106948
 
107067
- Generated-By: Letta Code
107068
- Agent-ID: <CHILD_AGENT_ID>
107069
- Parent-Agent-ID: <PARENT_AGENT_ID>"
106949
+ Generated-By: Reflection
106950
+ Agent-ID: <CHILD_AGENT_ID>"
107070
106951
  \`\`\`
107071
106952
 
107072
106953
  **Commit type** — pick the one that fits:
107073
- - \`fix\` — correcting a mistake or bad memory, or fixing a wrong/obsolete skill (\`update\`/\`deprecate\`)
107074
- - \`feat\` — adding wholly new memory content, or new skill content/structure (\`create\`/\`extend\`/\`split\`)
106954
+ - \`fix\` — correcting a mistake or bad memory, or fixing a wrong/obsolete skill
106955
+ - \`feat\` — adding wholly new memory content, or new skill content/structure
107075
106956
  - \`chore\` — routine updates, adding context, minor doc-only skill edits
107076
106957
 
107077
- In the commit message body, explain what changed and why, drawing from the categories you identified in Phase 2. If the change is skill-related, include the operation in the subject, e.g. \`feat(reflection): create docker-debugging skill 🔮\`.
106958
+ In the commit message body, explain what changed and why, drawing from the categories in the trajectory review. If the change is skill-related, say so in the subject, e.g. \`feat(reflection): create docker-debugging skill 🔮\`.
107078
106959
 
107079
106960
  If no changes were needed, do NOT commit. Report that the conversation contained no memory worth persisting.
107080
106961
 
107081
106962
  If \`git add\` or \`git commit\` fails, stop after one reasonable retry and report the failure. Do not run \`git config\`, mutate \`.git\`, use \`git reset\`, or assume the harness will persist uncommitted filesystem edits; uncommitted edits are not successful memory persistence.
107082
106963
 
107083
- ## Tools and Paths
107084
-
107085
- You only have access to **Bash** and **Edit**. Do not call \`Read\`, \`Write\`, memory tools, recall tools, or conversation search, even if those names appear in the transcript.
107086
-
107087
- Your memory repo root is \`$MEMORY_DIR\`. The terminal tool can expand environment variables; use \`$MEMORY_DIR\` on Unix or \`$env:MEMORY_DIR\` in PowerShell. Edit cannot expand either form. Keep all filesystem writes under the memory repo and run all git commands from inside it. Do not inspect or modify \`.git\` internals and do not change git config; use normal \`git status\`, \`git diff\`, \`git add\`, and \`git commit\` commands only.
107088
-
107089
- Use **Edit** for every modification to a file that already exists (memory or skill). Do not rewrite existing files with terminal heredocs, scripts, or redirection. Edit paths must be absolute paths under the memory repo, never literal \`$MEMORY_DIR/...\` or \`$env:MEMORY_DIR/...\` strings. To get an Edit path, resolve it with the terminal first (for example, \`printf "%s/persona.md\\n" "$MEMORY_DIR"\` on Unix or \`Join-Path $env:MEMORY_DIR "persona.md"\` in PowerShell) and then use the printed path.
107090
-
107091
- Use the **Bash** terminal tool for reading, git, and filesystem/bulk operations — not for editing the contents of existing files. Follow the shell semantics in the tool description; despite its name, the tool runs native PowerShell or cmd.exe on Windows.
107092
-
107093
- - Inspect transcripts with bounded reads. Determine file size first: on Unix, \`wc -c "$TRANSCRIPT_PATH"\`; in PowerShell, \`(Get-Item -LiteralPath $env:TRANSCRIPT_PATH).Length\`. If a file is <= 15000 bytes, a full read is okay; otherwise use targeted reads (\`head\`, \`tail\`, \`grep\`, and \`sed -n\` on Unix, or \`Get-Content\`, \`Select-String\`, and \`-TotalCount\` in PowerShell).
107094
- - Inspect memory with concise, platform-native commands. Use \`find\`, \`grep\`, \`head\`, and targeted \`cat\` on Unix; use \`Get-ChildItem\`, \`Select-String\`, and \`Get-Content\` in PowerShell.
107095
- - Create new files with the shell-native method. On Unix, use a quoted heredoc. In PowerShell, use a single-quoted here-string piped to \`Set-Content -LiteralPath <path> -Encoding utf8\`. Move, rename, or delete only under \`$MEMORY_DIR\` (\`mv\`, \`rm\`, and \`mkdir -p\` on Unix; \`Move-Item\`, \`Remove-Item\`, and \`New-Item\` in PowerShell). If a temp file is needed, put it under \`$MEMORY_DIR/.tmp/\` and remove it before committing.
107096
- - Memory Markdown files must remain UTF-8. On Windows, never use PowerShell redirection, \`Out-File\`, or \`Set-Content\` without explicit UTF-8 encoding; prefer the Edit tool.
107097
- - On Windows, avoid \`&&\` for failure-stopping chains so the command also works with Windows PowerShell; issue dependent commands in separate tool calls or check \`$LASTEXITCODE\` explicitly.
107098
-
107099
106964
  ## Output Format
107100
106965
 
107101
106966
  Return a report with:
107102
106967
 
107103
106968
  1. **Summary** — What you reviewed and what you concluded (2-3 sentences)
107104
- 2. **Memory changes** — Files created/modified/deleted/moved with a brief reason
107105
- 3. **Skill changes** — Operation selected (\`update\`, \`extend\`, \`deprecate\`, \`split\`, \`create\`, or \`none\`) and files changed
107106
- 4. **Skipped** — Anything considered but not persisted, and why
107107
- 5. **Commit** — Confirm the commit, or "no commit" if nothing was persisted
107108
- 6. **Issues** — Any problems encountered or information that couldn't be determined. If something broke, say what happened and suggest a fix.
107109
-
107110
- ## Critical Reminders
107111
-
107112
- 1. **Not the primary agent** — Don't respond to messages.
107113
- 2. **Be selective** — Few meaningful changes > many trivial ones; few high-quality skills > many trivial ones.
107114
- 3. **Commit or it didn't happen** — Uncommitted edits are lost; if nothing memory-worthy changed, do not commit.
106969
+ 2. **Memory changes** — Files created/modified/deleted/moved with a brief reason, including any skill changes
106970
+ 3. **Skipped** — Anything considered but not persisted, and why
106971
+ 4. **Commit** — Confirm the commit, or "no commit" if nothing was persisted
106972
+ 5. **Issues** — Any problems encountered or information that couldn't be determined. If something broke, say what happened and suggest a fix.
107115
106973
  `;
107116
106974
  var init_reflection_v2 = () => {};
107117
106975
 
@@ -107271,7 +107129,11 @@ function resolveSubagentConfigForMemoryFormat(config, memoryFormat, localMemfs)
107271
107129
  if (!v1Builtin || !v2Builtin || config.systemPrompt !== v1Builtin.systemPrompt) {
107272
107130
  return config;
107273
107131
  }
107274
- return { ...config, systemPrompt: v2Builtin.systemPrompt };
107132
+ return {
107133
+ ...config,
107134
+ systemPrompt: v2Builtin.systemPrompt,
107135
+ skills: config.skills.join(",") === v1Builtin.skills.join(",") ? [...v2Builtin.skills] : config.skills
107136
+ };
107275
107137
  }
107276
107138
  function getBuiltinSubagentNames() {
107277
107139
  return new Set(Object.keys(getBuiltinSubagents()));
@@ -210966,7 +210828,7 @@ var init_mcp_client = __esm(() => {
210966
210828
  init_streamableHttp();
210967
210829
  DEFAULT_CLIENT_INFO = {
210968
210830
  name: "letta-code",
210969
- version: "0.34.17"
210831
+ version: "0.34.18"
210970
210832
  };
210971
210833
  });
210972
210834
 
@@ -229651,7 +229513,7 @@ async function buildClientSkillsPayload(options = {}) {
229651
229513
  path: skill.path,
229652
229514
  source: skill.source
229653
229515
  })),
229654
- skillPathById: Object.fromEntries(sortedSkills.filter((skill) => typeof skill.path === "string" && skill.path.length > 0).map((skill) => [skill.id, skill.path])),
229516
+ skillPathById: Object.fromEntries(discovery.skills.filter((skill) => typeof skill.path === "string" && skill.path.length > 0).map((skill) => [skill.id, skill.path])),
229655
229517
  errors: errors6
229656
229518
  };
229657
229519
  if (useCache && generationBeforeDiscovery === getCacheGeneration() && checkoutGenerationBeforeDiscovery === getCheckoutGeneration()) {
@@ -233255,6 +233117,9 @@ async function executeScript(spawner, options, meta2) {
233255
233117
  if (opts.maxToolCalls !== undefined && (!Number.isSafeInteger(opts.maxToolCalls) || opts.maxToolCalls <= 0)) {
233256
233118
  throw new Error("agent() maxToolCalls must be a positive safe integer.");
233257
233119
  }
233120
+ if (opts.skills !== undefined && (!Array.isArray(opts.skills) || !opts.skills.every((id2) => typeof id2 === "string" && id2.trim()))) {
233121
+ throw new Error("agent() skills must be an array of skill names.");
233122
+ }
233258
233123
  const callIndex = callCounter++;
233259
233124
  const label = opts.label ?? defaultLabel(prompt);
233260
233125
  const phase2 = opts.phase ?? currentPhase;
@@ -241429,6 +241294,12 @@ async function waitForShutdownRetry(delayMs, signal) {
241429
241294
  onAbort();
241430
241295
  });
241431
241296
  }
241297
+ function getEnqueueAgentId(receipt) {
241298
+ const id2 = receipt.agent_id ?? receipt.parent_agent_id;
241299
+ if (!id2)
241300
+ throw new Error("Queued conversation has no agent or parent agent.");
241301
+ return id2;
241302
+ }
241432
241303
  async function enqueueConversationMessage(input, signal, request = apiRequest) {
241433
241304
  const body3 = {
241434
241305
  agent_id: input.agentId,
@@ -241543,14 +241414,14 @@ function assistantText(messages) {
241543
241414
  return text2.trim() ? text2 : null;
241544
241415
  }
241545
241416
  function noReplyMessage(receipt) {
241546
- return `Remote task finished. Its reply was not collected. Read the conversation with: letta messages list --agent ${receipt.agent_id} --conversation ${receipt.conversation_id}. Do not launch the task again to retrieve its result.`;
241417
+ return `Remote task finished. Its reply was not collected. Read the conversation with: letta messages list --agent ${getEnqueueAgentId(receipt)} --conversation ${receipt.conversation_id}. Do not launch the task again to retrieve its result.`;
241547
241418
  }
241548
241419
  async function waitForAcceptedSuperRun(receipt, signal, deps) {
241549
241420
  const pollMs = deps.pollMs ?? 1000;
241550
241421
  const sleep11 = deps.sleep ?? ((ms, waitSignal) => delay3(ms, undefined, { signal: waitSignal }));
241551
241422
  while (true) {
241552
241423
  signal.throwIfAborted();
241553
- const exact = await retryRead(() => deps.exact(receipt.agent_id, receipt.super_run_id, AbortSignal.any([signal, AbortSignal.timeout(30000)])), signal, sleep11, `Cloud status read for accepted Super Run ${receipt.super_run_id} failed`);
241424
+ const exact = await retryRead(() => deps.exact(getEnqueueAgentId(receipt), receipt.super_run_id, AbortSignal.any([signal, AbortSignal.timeout(30000)])), signal, sleep11, `Cloud status read for accepted Super Run ${receipt.super_run_id} failed`);
241554
241425
  if (exact.id !== receipt.super_run_id) {
241555
241426
  throw new RemoteExecutionFailed(`Exact Super Run read returned ${exact.id} for ${receipt.super_run_id}.`);
241556
241427
  }
@@ -241588,6 +241459,7 @@ async function waitForAcceptedSuperRun(receipt, signal, deps) {
241588
241459
  var RemoteExecutionFailed;
241589
241460
  var init_headless_super_run_wait = __esm(() => {
241590
241461
  init_letta_client();
241462
+ init_conversation_enqueue();
241591
241463
  init_request();
241592
241464
  RemoteExecutionFailed = class RemoteExecutionFailed extends Error {
241593
241465
  };
@@ -241632,10 +241504,10 @@ function trackChildSend(input) {
241632
241504
  const subagentId = generateSubagentId();
241633
241505
  registerSubagent(subagentId, child.type, child.name, undefined, true, false, parentScope, input.prompt, false);
241634
241506
  updateSubagent(subagentId, {
241635
- agentId: receipt.agent_id,
241507
+ agentId: receipt.agent_id ?? undefined,
241636
241508
  conversationId: receipt.conversation_id,
241637
241509
  superRunId: receipt.super_run_id,
241638
- agentURL: buildAgentReference(receipt.agent_id, {
241510
+ agentURL: buildAgentReference(getEnqueueAgentId(receipt), {
241639
241511
  conversationId: receipt.conversation_id
241640
241512
  })
241641
241513
  });
@@ -242398,7 +242270,7 @@ class CodexAppServerClient {
242398
242270
  await this.request("initialize", {
242399
242271
  clientInfo: {
242400
242272
  name: "letta-code",
242401
- version: "0.34.17"
242273
+ version: "0.34.18"
242402
242274
  },
242403
242275
  capabilities: null
242404
242276
  });
@@ -246327,8 +246199,11 @@ async function createListenerClient(connectionId, scope) {
246327
246199
  });
246328
246200
  }
246329
246201
  async function cancelListenerInput(params) {
246202
+ const agentId = params.scope.agent_id ?? params.parentAgentId;
246203
+ if (!agentId)
246204
+ throw new Error("Listener cancellation requires an agent or parent agent.");
246330
246205
  const removed = await (params.dequeue ?? dequeueConversationMessage)({
246331
- agentId: params.scope.agent_id,
246206
+ agentId,
246332
246207
  conversationId: params.scope.conversation_id,
246333
246208
  clientMessageId: params.clientMessageId,
246334
246209
  actingUserId: params.scope.acting_user_id
@@ -246352,7 +246227,7 @@ async function cancelListenerInput(params) {
246352
246227
  }
246353
246228
  async function cancelAcceptedListenerInput(receipt) {
246354
246229
  const removed = await dequeueConversationMessage({
246355
- agentId: receipt.agent_id,
246230
+ agentId: getEnqueueAgentId(receipt),
246356
246231
  conversationId: receipt.conversation_id,
246357
246232
  clientMessageId: receipt.client_message_id,
246358
246233
  actingUserId: receipt.acting_user_id
@@ -246376,6 +246251,7 @@ async function cancelAcceptedListenerInput(receipt) {
246376
246251
  return await cancelListenerInput({
246377
246252
  client,
246378
246253
  scope,
246254
+ parentAgentId: receipt.parent_agent_id,
246379
246255
  clientMessageId: receipt.client_message_id,
246380
246256
  dequeue: async () => removed,
246381
246257
  readState: () => ({ loop, cancelled: false })
@@ -246386,6 +246262,9 @@ async function cancelAcceptedListenerInput(receipt) {
246386
246262
  }
246387
246263
  }
246388
246264
  async function launchListenerConversation(params, deps = {}) {
246265
+ const agentId = params.scope.agent_id ?? params.parentAgentId;
246266
+ if (!agentId)
246267
+ throw new Error("Listener delivery requires an agent or parent agent.");
246389
246268
  const client = deps.client ?? await createListenerClient(params.connectionId, params.scope);
246390
246269
  const clientMessageId = params.clientMessageId ?? randomUUID37();
246391
246270
  const runIds = new Set;
@@ -246472,7 +246351,7 @@ async function launchListenerConversation(params, deps = {}) {
246472
246351
  if (interrupted)
246473
246352
  throw new Error("Launch cancelled before input was sent");
246474
246353
  const accepted = await (deps.enqueue ?? enqueueConversationMessage)({
246475
- agentId: params.scope.agent_id,
246354
+ agentId,
246476
246355
  conversationId: params.scope.conversation_id,
246477
246356
  clientMessageId,
246478
246357
  content: params.content,
@@ -246482,7 +246361,12 @@ async function launchListenerConversation(params, deps = {}) {
246482
246361
  if (params.noWait)
246483
246362
  return {
246484
246363
  status: "queued",
246485
- receipt: { ...accepted, connection_id: params.connectionId }
246364
+ receipt: {
246365
+ ...accepted,
246366
+ connection_id: params.connectionId,
246367
+ agent_id: params.scope.agent_id,
246368
+ ...params.scope.agent_id === null ? { parent_agent_id: agentId } : {}
246369
+ }
246486
246370
  };
246487
246371
  while (true) {
246488
246372
  if (disconnected)
@@ -246496,6 +246380,7 @@ async function launchListenerConversation(params, deps = {}) {
246496
246380
  if (await cancelListenerInput({
246497
246381
  client,
246498
246382
  scope: params.scope,
246383
+ parentAgentId: params.parentAgentId,
246499
246384
  clientMessageId,
246500
246385
  dequeue: deps.dequeue,
246501
246386
  readState: () => ({ loop, cancelled })
@@ -246615,7 +246500,7 @@ function handleToolReturnEvent(event, state2) {
246615
246500
  }
246616
246501
  }
246617
246502
  function isEnqueueReceipt(event) {
246618
- return event.status === "queued" && typeof event.agent_id === "string" && typeof event.conversation_id === "string" && typeof event.client_message_id === "string" && typeof event.super_run_id === "string" && typeof event.workflow_id === "string" && (event.acting_user_id === undefined || event.acting_user_id === null || typeof event.acting_user_id === "string");
246503
+ return event.status === "queued" && (typeof event.agent_id === "string" || event.agent_id === null && typeof event.parent_agent_id === "string") && (event.parent_agent_id === undefined || typeof event.parent_agent_id === "string") && typeof event.conversation_id === "string" && typeof event.client_message_id === "string" && typeof event.super_run_id === "string" && typeof event.workflow_id === "string" && (event.acting_user_id === undefined || event.acting_user_id === null || typeof event.acting_user_id === "string");
246619
246504
  }
246620
246505
  function getResultEnvelopeError(event) {
246621
246506
  return event.error || event.result || "Unknown error";
@@ -246626,6 +246511,7 @@ function handleResultEvent(event, state2, subagentId) {
246626
246511
  state2.enqueueReceipt = {
246627
246512
  status: "queued",
246628
246513
  agent_id: event.agent_id,
246514
+ ...event.parent_agent_id ? { parent_agent_id: event.parent_agent_id } : {},
246629
246515
  conversation_id: event.conversation_id,
246630
246516
  client_message_id: event.client_message_id,
246631
246517
  super_run_id: event.super_run_id,
@@ -246757,9 +246643,9 @@ var init_subagent_stream = __esm(() => {
246757
246643
  async function collectRemoteTurnResult(receipt, state2, subagentId, signal = new AbortController().signal) {
246758
246644
  const startedAt = Date.now();
246759
246645
  updateSubagent(subagentId, {
246760
- agentId: receipt.agent_id,
246646
+ agentId: receipt.agent_id ?? undefined,
246761
246647
  conversationId: receipt.conversation_id,
246762
- agentURL: buildAgentReference(receipt.agent_id, {
246648
+ agentURL: buildAgentReference(getEnqueueAgentId(receipt), {
246763
246649
  conversationId: receipt.conversation_id
246764
246650
  })
246765
246651
  });
@@ -246779,7 +246665,7 @@ async function collectRemoteTurnResult(receipt, state2, subagentId, signal = new
246779
246665
  }
246780
246666
  });
246781
246667
  return {
246782
- agentId: receipt.agent_id,
246668
+ agentId: receipt.agent_id ?? "",
246783
246669
  conversationId: receipt.conversation_id,
246784
246670
  report: reply.text,
246785
246671
  success: true,
@@ -246792,7 +246678,7 @@ async function collectRemoteTurnResult(receipt, state2, subagentId, signal = new
246792
246678
  detail = cancelled ? INTERRUPTED_BY_USER : `${INTERRUPTED_BY_USER} (could not confirm cancellation of remote Super Run ${receipt.super_run_id})`;
246793
246679
  }
246794
246680
  return {
246795
- agentId: receipt.agent_id,
246681
+ agentId: receipt.agent_id ?? "",
246796
246682
  conversationId: receipt.conversation_id,
246797
246683
  report: "",
246798
246684
  success: false,
@@ -248730,12 +248616,13 @@ async function launchRemoteExternalCodingAgent(params, deps) {
248730
248616
  durationMs: final.duration_ms || Date.now() - startedAt
248731
248617
  };
248732
248618
  } catch (error5) {
248619
+ const remoteWorkerStopped = signal?.reason === REMOTE_EXTERNAL_WORKER_STOPPED;
248733
248620
  return {
248734
248621
  agentId: parentScope.agentId,
248735
248622
  model,
248736
248623
  report: "",
248737
248624
  success: false,
248738
- error: error5 instanceof RemoteWorkerLostError && !signal?.aborted ? `The ${type3} worker on ${computer} is no longer listed: it may have finished while this session was disconnected (results are kept for 30 minutes), or the listener restarted. Check ${computer} or launch it again if the work is still needed.` : `${signal?.aborted ? "Stopped following this subagent" : errorMessage3(error5)}. The ${type3} worker may still be running on ${computer}.`,
248625
+ error: error5 instanceof RemoteWorkerLostError && !signal?.aborted ? `The ${type3} worker on ${computer} is no longer listed: it may have finished while this session was disconnected (results are kept for 30 minutes), or the listener restarted. Check ${computer} or launch it again if the work is still needed.` : remoteWorkerStopped ? `Stopped the ${type3} worker on ${computer}.` : `${signal?.aborted ? "Stopped following this subagent" : errorMessage3(error5)}. The ${type3} worker may still be running on ${computer}.`,
248739
248626
  durationMs: Date.now() - startedAt
248740
248627
  };
248741
248628
  } finally {
@@ -248880,7 +248767,7 @@ async function sendRemoteExternalCodingAgentMessage(params, deps = {}) {
248880
248767
  outputFile: tracked.outputFile
248881
248768
  };
248882
248769
  }
248883
- var DISPLAY_NAMES, RemoteWorkerLostError;
248770
+ var REMOTE_EXTERNAL_WORKER_STOPPED, DISPLAY_NAMES, RemoteWorkerLostError;
248884
248771
  var init_remote_external_coding_agent = __esm(() => {
248885
248772
  init_subagent_state();
248886
248773
  init_app_server_client();
@@ -248890,6 +248777,7 @@ var init_remote_external_coding_agent = __esm(() => {
248890
248777
  init_headless_environment_response();
248891
248778
  init_headless_listener_launch();
248892
248779
  init_external_coding_agent();
248780
+ REMOTE_EXTERNAL_WORKER_STOPPED = Symbol("remote external worker stopped");
248893
248781
  DISPLAY_NAMES = {
248894
248782
  "claude-code": "Claude Code",
248895
248783
  codex: "Codex",
@@ -250479,6 +250367,7 @@ async function task_stop(args, deps = {}) {
250479
250367
  const child = getSubagents().find((agent) => agent.id === task2.subagentId);
250480
250368
  const external3 = parseExternalCodingAgentId(child?.agentId ?? undefined);
250481
250369
  let remote;
250370
+ let remoteExternalStopped = false;
250482
250371
  if (external3?.computer) {
250483
250372
  try {
250484
250373
  if (!task2.runtimeScope)
@@ -250495,6 +250384,7 @@ async function task_stop(args, deps = {}) {
250495
250384
  killed: result3.stopped,
250496
250385
  output: result3.stopped ? "Stopped the remote external subagent" : "The remote external subagent was not running"
250497
250386
  };
250387
+ remoteExternalStopped = result3.stopped;
250498
250388
  } catch (error5) {
250499
250389
  remote = {
250500
250390
  killed: false,
@@ -250506,7 +250396,7 @@ async function task_stop(args, deps = {}) {
250506
250396
  }
250507
250397
  if (remote && !remote.killed)
250508
250398
  return remote;
250509
- task2.abortController.abort();
250399
+ task2.abortController.abort(remoteExternalStopped ? REMOTE_EXTERNAL_WORKER_STOPPED : undefined);
250510
250400
  task2.error = "Aborted by user";
250511
250401
  await task2.completion;
250512
250402
  task2.status = "failed";
@@ -251874,6 +251764,36 @@ var init_sdk_loader = __esm(() => {
251874
251764
  SDK_PACKAGE = ["@letta-ai", "letta-agent-sdk"].join("/");
251875
251765
  });
251876
251766
 
251767
+ // src/agent/preloaded-skills.ts
251768
+ import { readFile as readFile18 } from "node:fs/promises";
251769
+ async function loadPreloadedSkills(ids, options = {}) {
251770
+ if (ids.length === 0)
251771
+ return "";
251772
+ const { skillPathById } = await buildClientSkillsPayload({
251773
+ ...options,
251774
+ skillSources: options.skillSources?.length === 0 ? ["bundled"] : options.skillSources
251775
+ });
251776
+ const contents = [];
251777
+ for (const id2 of ids) {
251778
+ const path31 = skillPathById[id2];
251779
+ if (!path31)
251780
+ continue;
251781
+ try {
251782
+ contents.push(`<${id2}>
251783
+ ${await readFile18(path31, "utf-8")}
251784
+ </${id2}>`);
251785
+ } catch {}
251786
+ }
251787
+ return contents.length ? `<loaded_skills>
251788
+ ${contents.join(`
251789
+
251790
+ `)}
251791
+ </loaded_skills>` : "";
251792
+ }
251793
+ var init_preloaded_skills = __esm(() => {
251794
+ init_client_skills();
251795
+ });
251796
+
251877
251797
  // src/tools/workflow/sdk-spawner.ts
251878
251798
  async function latestWorkerRun(conversationId) {
251879
251799
  const backend3 = getBackend();
@@ -252115,6 +252035,19 @@ function createSdkSpawner(client, config2) {
252115
252035
  error: `Unknown model "${options.model}". Run \`letta model list\` for valid handles.`
252116
252036
  };
252117
252037
  }
252038
+ const loadedSkills = options.skills?.length ? await loadPreloadedSkills(options.skills, {
252039
+ agentId: config2.parentAgentId,
252040
+ ...config2.cwd ? { workingDirectory: config2.cwd } : {}
252041
+ }) : "";
252042
+ const missingSkills = (options.skills ?? []).filter((id2) => !loadedSkills.includes(`<${id2}>
252043
+ `));
252044
+ if (missingSkills.length) {
252045
+ return {
252046
+ value: null,
252047
+ failed: true,
252048
+ error: `Unknown skill(s): ${missingSkills.join(", ")}.`
252049
+ };
252050
+ }
252118
252051
  if (signal.aborted) {
252119
252052
  return {
252120
252053
  value: null,
@@ -252133,7 +252066,9 @@ function createSdkSpawner(client, config2) {
252133
252066
  let query3;
252134
252067
  try {
252135
252068
  query3 = client.query({
252136
- prompt,
252069
+ prompt: loadedSkills ? `${loadedSkills}
252070
+
252071
+ ${prompt}` : prompt,
252137
252072
  options: {
252138
252073
  ...buildQueryOptions(options, model, config2, request2.callIndex),
252139
252074
  ...resumeId ? { conversationId: resumeId } : {}
@@ -252259,6 +252194,7 @@ function createSdkSpawner(client, config2) {
252259
252194
  }
252260
252195
  var DEFAULT_SUBAGENT_TIMEOUT_MS, DEFAULT_MAX_SUBAGENT_TOOL_CALLS = 1000, MAX_IDENTICAL_TOOL_CALLS = 3, SUBAGENT_PREAMBLE = `You are a subagent inside a deterministic workflow. You are not talking to a human: your final output is consumed by a script. Return raw data with no preamble, no markdown framing, and no questions.`, JSON_PREAMBLE = `Your final message must be a single JSON value and nothing else: no prose, no markdown code fence.`;
252261
252196
  var init_sdk_spawner = __esm(async () => {
252197
+ init_preloaded_skills();
252262
252198
  init_backend2();
252263
252199
  init_tool_registry();
252264
252200
  init_tool_name_mapping();
@@ -252399,6 +252335,14 @@ function jsonSafe(value) {
252399
252335
  return String(value);
252400
252336
  }
252401
252337
  }
252338
+ function formatFailureResult(error5, agentsDone, journalPath) {
252339
+ const lines = [`Workflow failed: ${error5 ?? "unknown error"}`];
252340
+ if (agentsDone > 0) {
252341
+ lines.push(`${agentsDone} agent call${agentsDone === 1 ? "" : "s"} completed before the failure. Their full results are saved in ${journalPath} (one line per completed call; the return value is outcome.value).`);
252342
+ }
252343
+ return lines.join(`
252344
+ `);
252345
+ }
252402
252346
  function formatCompletionResult(run, executionDir) {
252403
252347
  const payload = JSON.stringify({
252404
252348
  result: jsonSafe(run.result),
@@ -252590,7 +252534,7 @@ async function workflow(args) {
252590
252534
  processState.status = outcome.run ? "completed" : "failed";
252591
252535
  processState.exitCode = outcome.run ? 0 : 1;
252592
252536
  }
252593
- const result3 = outcome.run ? formatCompletionResult(outcome.run, executionDir) : `Workflow failed: ${outcome.error ?? "unknown error"}`;
252537
+ const result3 = outcome.run ? formatCompletionResult(outcome.run, executionDir) : formatFailureResult(outcome.error, getWorkflowExecution(taskId)?.agentsDone ?? 0, journalPath);
252594
252538
  appendToOutputFile(outputFile, outcome.run ? `
252595
252539
  [result]
252596
252540
  ${result3}
@@ -261164,7 +261108,7 @@ var init_environments4 = __esm(() => {
261164
261108
  });
261165
261109
 
261166
261110
  // src/cli/subcommands/feedback.ts
261167
- import { readFile as readFile18 } from "node:fs/promises";
261111
+ import { readFile as readFile19 } from "node:fs/promises";
261168
261112
  import { parseArgs as parseArgs8 } from "node:util";
261169
261113
  function printUsage6(stdout = console.log) {
261170
261114
  stdout(`
@@ -261214,7 +261158,7 @@ async function runFeedbackSubcommand(argv, deps = {}) {
261214
261158
  let rawMessage = typeof inlineMessage === "string" ? inlineMessage : "";
261215
261159
  if (typeof messageFile === "string") {
261216
261160
  try {
261217
- rawMessage = await (deps.readMessageFile ?? ((path32) => readFile18(path32, "utf8")))(messageFile);
261161
+ rawMessage = await (deps.readMessageFile ?? ((path32) => readFile19(path32, "utf8")))(messageFile);
261218
261162
  } catch {
261219
261163
  stderr(`Could not read feedback file: ${messageFile}`);
261220
261164
  return 1;
@@ -263427,7 +263371,7 @@ var init_listen_log = __esm(() => {
263427
263371
 
263428
263372
  // src/utils/machine-metadata.ts
263429
263373
  import { execFile as execFile19 } from "node:child_process";
263430
- import { readFile as readFile19 } from "node:fs/promises";
263374
+ import { readFile as readFile20 } from "node:fs/promises";
263431
263375
  import { cpus, totalmem } from "node:os";
263432
263376
  function getMachineMetadata() {
263433
263377
  machineMetadata ??= collectMachineMetadata().catch(() => ({
@@ -263442,7 +263386,7 @@ function localSources() {
263442
263386
  architecture: process.arch,
263443
263387
  cpu: cpus()[0]?.model,
263444
263388
  memoryBytes: Math.min(totalmem(), limit4 || Number.POSITIVE_INFINITY),
263445
- read: (path32, signal) => readFile19(path32, { encoding: "utf8", signal }),
263389
+ read: (path32, signal) => readFile20(path32, { encoding: "utf8", signal }),
263446
263390
  run: (command, args, signal) => new Promise((resolve39, reject2) => {
263447
263391
  execFile19(command, args, {
263448
263392
  encoding: "utf8",
@@ -264021,7 +263965,7 @@ var init_auth3 = __esm(() => {
264021
263965
 
264022
263966
  // src/websocket/listener/manual-instance-lock.ts
264023
263967
  import { createHash as createHash14, randomUUID as randomUUID47 } from "node:crypto";
264024
- import { link as link4, mkdir as mkdir13, readFile as readFile20, rm as rm11, unlink as unlink3, writeFile as writeFile15 } from "node:fs/promises";
263968
+ import { link as link4, mkdir as mkdir13, readFile as readFile21, rm as rm11, unlink as unlink3, writeFile as writeFile15 } from "node:fs/promises";
264025
263969
  import { homedir as homedir38 } from "node:os";
264026
263970
  import path32 from "node:path";
264027
263971
  function hasErrorCode3(error5, code2) {
@@ -264108,7 +264052,7 @@ async function recoverDeadOwner(lockPath, staleContents, recoveryOwnerContents,
264108
264052
  }
264109
264053
  let claimContents;
264110
264054
  try {
264111
- claimContents = await readFile20(claimPath, "utf-8");
264055
+ claimContents = await readFile21(claimPath, "utf-8");
264112
264056
  } catch (error5) {
264113
264057
  if (hasErrorCode3(error5, "ENOENT")) {
264114
264058
  continue;
@@ -264128,7 +264072,7 @@ async function recoverDeadOwner(lockPath, staleContents, recoveryOwnerContents,
264128
264072
  }
264129
264073
  let currentContents;
264130
264074
  try {
264131
- currentContents = await readFile20(lockPath, "utf-8");
264075
+ currentContents = await readFile21(lockPath, "utf-8");
264132
264076
  } catch (error5) {
264133
264077
  return hasErrorCode3(error5, "ENOENT");
264134
264078
  }
@@ -264178,7 +264122,7 @@ async function acquireManualListenerLock(scope, overrides = {}) {
264178
264122
  return;
264179
264123
  let currentContents;
264180
264124
  try {
264181
- currentContents = await readFile20(lockPath, "utf-8");
264125
+ currentContents = await readFile21(lockPath, "utf-8");
264182
264126
  } catch (error5) {
264183
264127
  if (hasErrorCode3(error5, "ENOENT")) {
264184
264128
  released = true;
@@ -264198,7 +264142,7 @@ async function acquireManualListenerLock(scope, overrides = {}) {
264198
264142
  }
264199
264143
  let incumbentContents;
264200
264144
  try {
264201
- incumbentContents = await readFile20(lockPath, "utf-8");
264145
+ incumbentContents = await readFile21(lockPath, "utf-8");
264202
264146
  } catch (error5) {
264203
264147
  if (hasErrorCode3(error5, "ENOENT")) {
264204
264148
  continue;
@@ -266750,7 +266694,7 @@ function handleMemoryProtocolCommand(parsed, context3) {
266750
266694
  ensureLocalMemfsCheckout: ensureLocalMemfsCheckout2,
266751
266695
  isMemfsEnabledOnServer: isMemfsEnabledOnServer2
266752
266696
  } = await Promise.resolve().then(() => (init_memory_filesystem2(), exports_memory_filesystem));
266753
- const { readFile: readFile21 } = await import("node:fs/promises");
266697
+ const { readFile: readFile22 } = await import("node:fs/promises");
266754
266698
  const { existsSync: existsSync52 } = await import("node:fs");
266755
266699
  const { isAbsolute: isAbsolute25, join: join77, normalize: normalize4, relative: relative8, sep: sep8 } = await import("node:path");
266756
266700
  if (isAbsolute25(parsed.path) || parsed.path.length === 0) {
@@ -266776,7 +266720,7 @@ function handleMemoryProtocolCommand(parsed, context3) {
266776
266720
  return;
266777
266721
  }
266778
266722
  }
266779
- const buffer = await readFile21(absolutePath);
266723
+ const buffer = await readFile22(absolutePath);
266780
266724
  const content = encoding === "base64" ? buffer.toString("base64") : buffer.toString("utf-8");
266781
266725
  const pathspec = rel.split(sep8).join("/");
266782
266726
  safeSocketSend(socket, {
@@ -276035,7 +275979,7 @@ ${lanes.join(`
276035
275979
  writeOutputIsTTY() {
276036
275980
  return process.stdout.isTTY;
276037
275981
  },
276038
- readFile: readFile21,
275982
+ readFile: readFile22,
276039
275983
  writeFile: writeFile22,
276040
275984
  watchFile: watchFile2,
276041
275985
  watchDirectory,
@@ -276228,7 +276172,7 @@ ${lanes.join(`
276228
276172
  function fsWatchWorker(fileOrDirectory, recursive, callback) {
276229
276173
  return _fs.watch(fileOrDirectory, fsSupportsRecursiveFsWatch ? { persistent: true, recursive: !!recursive } : { persistent: true }, callback);
276230
276174
  }
276231
- function readFile21(fileName, _encoding) {
276175
+ function readFile22(fileName, _encoding) {
276232
276176
  let buffer;
276233
276177
  try {
276234
276178
  buffer = _fs.readFileSync(fileName);
@@ -307175,7 +307119,7 @@ ${lanes.join(`
307175
307119
  const possibleOption = getSpellingSuggestion(unknownOption, diagnostics2.optionDeclarations, getOptionName);
307176
307120
  return possibleOption ? createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics2.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics2.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption);
307177
307121
  }
307178
- function parseCommandLineWorker(diagnostics2, commandLine, readFile21) {
307122
+ function parseCommandLineWorker(diagnostics2, commandLine, readFile22) {
307179
307123
  const options = {};
307180
307124
  let watchOptions;
307181
307125
  const fileNames = [];
@@ -307213,7 +307157,7 @@ ${lanes.join(`
307213
307157
  }
307214
307158
  }
307215
307159
  function parseResponseFile(fileName) {
307216
- const text3 = tryReadFile(fileName, readFile21 || ((fileName2) => sys.readFile(fileName2)));
307160
+ const text3 = tryReadFile(fileName, readFile22 || ((fileName2) => sys.readFile(fileName2)));
307217
307161
  if (!isString7(text3)) {
307218
307162
  errors6.push(text3);
307219
307163
  return;
@@ -307316,8 +307260,8 @@ ${lanes.join(`
307316
307260
  unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1,
307317
307261
  optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument
307318
307262
  };
307319
- function parseCommandLine2(commandLine, readFile21) {
307320
- return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile21);
307263
+ function parseCommandLine2(commandLine, readFile22) {
307264
+ return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile22);
307321
307265
  }
307322
307266
  function getOptionFromName(optionName, allowShort) {
307323
307267
  return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort);
@@ -307385,8 +307329,8 @@ ${lanes.join(`
307385
307329
  result3.originalFileName = result3.fileName;
307386
307330
  return parseJsonSourceFileConfigFileContent(result3, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd2), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd2), undefined, extraFileExtensions, extendedConfigCache, watchOptionsToExtend);
307387
307331
  }
307388
- function readConfigFile(fileName, readFile21) {
307389
- const textOrDiagnostic = tryReadFile(fileName, readFile21);
307332
+ function readConfigFile(fileName, readFile22) {
307333
+ const textOrDiagnostic = tryReadFile(fileName, readFile22);
307390
307334
  return isString7(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic };
307391
307335
  }
307392
307336
  function parseConfigFileTextToJson(fileName, jsonText) {
@@ -307396,14 +307340,14 @@ ${lanes.join(`
307396
307340
  error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined
307397
307341
  };
307398
307342
  }
307399
- function readJsonConfigFile(fileName, readFile21) {
307400
- const textOrDiagnostic = tryReadFile(fileName, readFile21);
307343
+ function readJsonConfigFile(fileName, readFile22) {
307344
+ const textOrDiagnostic = tryReadFile(fileName, readFile22);
307401
307345
  return isString7(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { fileName, parseDiagnostics: [textOrDiagnostic] };
307402
307346
  }
307403
- function tryReadFile(fileName, readFile21) {
307347
+ function tryReadFile(fileName, readFile22) {
307404
307348
  let text3;
307405
307349
  try {
307406
- text3 = readFile21(fileName);
307350
+ text3 = readFile22(fileName);
307407
307351
  } catch (e2) {
307408
307352
  return createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e2.message);
307409
307353
  }
@@ -374833,12 +374777,12 @@ ${lanes.join(`
374833
374777
  function createCompilerHost(options, setParentNodes) {
374834
374778
  return createCompilerHostWorker(options, setParentNodes);
374835
374779
  }
374836
- function createGetSourceFile(readFile21, setParentNodes) {
374780
+ function createGetSourceFile(readFile22, setParentNodes) {
374837
374781
  return (fileName, languageVersionOrOptions, onError) => {
374838
374782
  let text3;
374839
374783
  try {
374840
374784
  mark2("beforeIORead");
374841
- text3 = readFile21(fileName);
374785
+ text3 = readFile22(fileName);
374842
374786
  mark2("afterIORead");
374843
374787
  measure("I/O Read", "beforeIORead", "afterIORead");
374844
374788
  } catch (e2) {
@@ -375629,7 +375573,7 @@ ${lanes.join(`
375629
375573
  getRedirectFromOutput,
375630
375574
  forEachResolvedProjectReference: forEachResolvedProjectReference2
375631
375575
  });
375632
- const readFile21 = host.readFile.bind(host);
375576
+ const readFile22 = host.readFile.bind(host);
375633
375577
  (_e = tracing) == null || _e.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
375634
375578
  const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
375635
375579
  (_f = tracing) == null || _f.pop();
@@ -375805,7 +375749,7 @@ ${lanes.join(`
375805
375749
  shouldTransformImportCall,
375806
375750
  emitBuildInfo,
375807
375751
  fileExists,
375808
- readFile: readFile21,
375752
+ readFile: readFile22,
375809
375753
  directoryExists: directoryExists2,
375810
375754
  getSymlinkCache,
375811
375755
  realpath: (_o = host.realpath) == null ? undefined : _o.bind(host),
@@ -443139,6 +443083,94 @@ var init_chatgpt_plan_rotation = __esm(() => {
443139
443083
  init_backend2();
443140
443084
  });
443141
443085
 
443086
+ // src/agent/ephemeral-conversation.ts
443087
+ async function buildEphemeralConversationCreateBody(options) {
443088
+ const system2 = options.systemPromptCustom ? options.systemPromptCustom : await resolveAndBuildSystemPrompt(options.systemPromptPreset, options.memoryPromptMode ?? "standard");
443089
+ const request2 = await buildCreateAgentRequest({
443090
+ model: options.model,
443091
+ system: system2,
443092
+ memoryPromptMode: "standard",
443093
+ enableMemfs: false,
443094
+ isSubagent: true,
443095
+ baseTools: []
443096
+ });
443097
+ const modelSettings = options.model ? getModelUpdateArgs(options.model) : undefined;
443098
+ const contextWindow = modelSettings?.context_window ?? await getModelContextWindow(request2.model);
443099
+ return {
443100
+ model: request2.model,
443101
+ system: request2.system ?? system2,
443102
+ ...modelSettings ? { model_settings: modelSettings } : {},
443103
+ ...contextWindow ? { context_window_limit: contextWindow } : {}
443104
+ };
443105
+ }
443106
+ function projectConversationState(conversation) {
443107
+ if (!conversation.model) {
443108
+ throw new Error("Cannot resume a conversation without a persisted model.");
443109
+ }
443110
+ return {
443111
+ id: conversation.id,
443112
+ name: "Ephemeral conversation",
443113
+ system: conversation.system ?? "",
443114
+ tools: [],
443115
+ memory: { blocks: [] },
443116
+ llm_config: {
443117
+ handle: conversation.model,
443118
+ model: conversation.model,
443119
+ context_window: conversation.context_window_limit ?? undefined,
443120
+ model_settings: conversation.model_settings ?? {}
443121
+ },
443122
+ model_settings: conversation.model_settings ?? {},
443123
+ message_buffer_autoclear: false
443124
+ };
443125
+ }
443126
+ async function createEphemeralConversation2(options) {
443127
+ const body3 = await buildEphemeralConversationCreateBody(options);
443128
+ const conversation = await createEphemeralConversation(body3);
443129
+ return {
443130
+ agent: projectConversationState({ ...body3, id: conversation.id }),
443131
+ conversationId: conversation.id
443132
+ };
443133
+ }
443134
+ async function retrieveResumeState(agentId, conversationId, backend3 = getBackend()) {
443135
+ return agentId ? backend3.retrieveAgent(agentId) : projectConversationState(await backend3.retrieveConversation(conversationId));
443136
+ }
443137
+ async function createLocalEphemeralConversation(options) {
443138
+ const body3 = await buildEphemeralConversationCreateBody(options);
443139
+ const backend3 = getBackend();
443140
+ const internalAgent = await backend3.createAgent({
443141
+ agent_type: "letta_v1_agent",
443142
+ name: "Ephemeral conversation",
443143
+ model: body3.model,
443144
+ system: body3.system,
443145
+ memory_blocks: [],
443146
+ tags: [],
443147
+ tools: [],
443148
+ include_base_tools: false,
443149
+ include_base_tool_rules: false,
443150
+ initial_message_sequence: [],
443151
+ parallel_tool_calls: true,
443152
+ hidden: true
443153
+ });
443154
+ const conversation = await backend3.createConversation({
443155
+ agent_id: internalAgent.id,
443156
+ model: body3.model,
443157
+ ...body3.model_settings ? { model_settings: body3.model_settings } : {},
443158
+ ...body3.context_window_limit ? { context_window_limit: body3.context_window_limit } : {}
443159
+ });
443160
+ return {
443161
+ agent: internalAgent,
443162
+ conversationId: conversation.id
443163
+ };
443164
+ }
443165
+ var init_ephemeral_conversation = __esm(() => {
443166
+ init_available_models();
443167
+ init_create_agent_request();
443168
+ init_model();
443169
+ init_system_prompt_resolution();
443170
+ init_backend2();
443171
+ init_ephemeral_conversations();
443172
+ });
443173
+
443142
443174
  // src/cli/helpers/compaction-utils.ts
443143
443175
  function extractCompactionSummary(text3) {
443144
443176
  try {
@@ -447193,7 +447225,7 @@ async function tryResumeBusyConversationStream(params) {
447193
447225
  }
447194
447226
  }
447195
447227
  async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
447196
- if (!runtime.agentId)
447228
+ if (!runtime.agentId && runtime.conversationId === "default")
447197
447229
  return null;
447198
447230
  const getResumeDataImpl = deps.getResumeData ?? getResumeDataFromBackend;
447199
447231
  const prepareToolExecutionContext = deps.prepareToolExecutionContext ?? prepareToolExecutionContextForScope;
@@ -447208,7 +447240,10 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
447208
447240
  const backend3 = getBackend();
447209
447241
  let agent;
447210
447242
  try {
447211
- agent = await (deps.retrieveAgent ? deps.retrieveAgent(runtime.agentId) : backend3.retrieveAgent(runtime.agentId));
447243
+ agent = await retrieveResumeState(runtime.agentId, runtime.conversationId, {
447244
+ retrieveAgent: deps.retrieveAgent ?? backend3.retrieveAgent.bind(backend3),
447245
+ retrieveConversation: deps.retrieveConversation ?? backend3.retrieveConversation.bind(backend3)
447246
+ });
447212
447247
  } catch (err) {
447213
447248
  assertCurrentTurnLease();
447214
447249
  if (isBackendNotFoundError(err)) {
@@ -447241,14 +447276,14 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
447241
447276
  agent_id: runtime.agentId,
447242
447277
  conversation_id: recoveryConversationId
447243
447278
  };
447244
- const modAdapters = await ensureListenerModAdaptersForAgent(runtime.listener, runtime.agentId);
447279
+ const modAdapters = runtime.agentId ? await ensureListenerModAdaptersForAgent(runtime.listener, runtime.agentId) : [];
447245
447280
  const preparedToolContext = await prepareToolExecutionContext({
447246
447281
  connectionId: resolveTurnExecutionConnectionId(runtime.listener, scope, runtime.activeConnectionId ?? undefined),
447247
447282
  agentId: runtime.agentId,
447248
447283
  conversationId: recoveryConversationId,
447249
447284
  workingDirectory: recoveryWorkingDirectory,
447250
447285
  permissionModeState: getOrCreateConversationPermissionModeStateRef(runtime.listener, runtime.agentId, runtime.conversationId),
447251
- modContext: createListenerAgentModContext(runtime.agentId),
447286
+ ...runtime.agentId ? { modContext: createListenerAgentModContext(runtime.agentId) } : {},
447252
447287
  modAdapters,
447253
447288
  modEvents: createListenerModEvents(modAdapters)
447254
447289
  });
@@ -447641,6 +447676,7 @@ var init_send = __esm(async () => {
447641
447676
  init_error();
447642
447677
  init_approval_recovery();
447643
447678
  init_check_approval();
447679
+ init_ephemeral_conversation();
447644
447680
  init_turn_recovery_policy();
447645
447681
  init_backend2();
447646
447682
  init_error_formatter();
@@ -451706,12 +451742,12 @@ function rowToolCalls(row, index, diagnostics2) {
451706
451742
  }
451707
451743
  return calls;
451708
451744
  }
451709
- function contentText3(content) {
451745
+ function contentText2(content) {
451710
451746
  if (typeof content === "string") {
451711
451747
  if (content.startsWith(CONTENT_JSON_PREFIX)) {
451712
451748
  const encoded = content.slice(CONTENT_JSON_PREFIX.length);
451713
451749
  try {
451714
- return contentText3(JSON.parse(encoded));
451750
+ return contentText2(JSON.parse(encoded));
451715
451751
  } catch {
451716
451752
  return encoded;
451717
451753
  }
@@ -451797,7 +451833,7 @@ var init_hermes = __esm(() => {
451797
451833
  });
451798
451834
  };
451799
451835
  if (row.role === "user") {
451800
- const content = contentText3(row.content);
451836
+ const content = contentText2(row.content);
451801
451837
  if (content) {
451802
451838
  emit({
451803
451839
  type: "message",
@@ -451817,7 +451853,7 @@ var init_hermes = __esm(() => {
451817
451853
  ...timestamp ? { timestamp } : {}
451818
451854
  });
451819
451855
  }
451820
- const content = contentText3(row.content);
451856
+ const content = contentText2(row.content);
451821
451857
  if (content) {
451822
451858
  emit({
451823
451859
  type: "message",
@@ -451840,7 +451876,7 @@ var init_hermes = __esm(() => {
451840
451876
  if (row.role === "tool") {
451841
451877
  emit({
451842
451878
  type: "tool_result",
451843
- content: contentText3(row.content),
451879
+ content: contentText2(row.content),
451844
451880
  ...typeof row.tool_call_id === "string" && row.tool_call_id ? { callId: row.tool_call_id } : {},
451845
451881
  ...timestamp ? { timestamp } : {}
451846
451882
  });
@@ -453832,7 +453868,7 @@ var exports_reflection_memory_v2 = {};
453832
453868
  __export(exports_reflection_memory_v2, {
453833
453869
  buildMemfsV2ParentMemorySnapshot: () => buildMemfsV2ParentMemorySnapshot
453834
453870
  });
453835
- import { readFile as readFile21 } from "node:fs/promises";
453871
+ import { readFile as readFile22 } from "node:fs/promises";
453836
453872
  function renderProjectedTree(files) {
453837
453873
  const root2 = { directories: new Map, files: [] };
453838
453874
  for (const file of files) {
@@ -453885,7 +453921,7 @@ async function buildMemfsV2ParentMemorySnapshot(memoryDir, requestedMaxChars) {
453885
453921
  `).length - prefix.length - suffix.length - 20;
453886
453922
  if (remaining <= 0)
453887
453923
  break;
453888
- const content = (await readFile21(file.fullPath, "utf-8")).slice(0, remaining);
453924
+ const content = (await readFile22(file.fullPath, "utf-8")).slice(0, remaining);
453889
453925
  lines.push(`${prefix}${content}${suffix}`);
453890
453926
  }
453891
453927
  lines.push("</parent_memory>");
@@ -453903,7 +453939,7 @@ import {
453903
453939
  appendFile,
453904
453940
  mkdir as mkdir14,
453905
453941
  readdir as readdir9,
453906
- readFile as readFile22,
453942
+ readFile as readFile23,
453907
453943
  stat as stat13,
453908
453944
  writeFile as writeFile16
453909
453945
  } from "node:fs/promises";
@@ -453951,7 +453987,7 @@ async function collectParentMemoryFiles(memoryDir) {
453951
453987
  continue;
453952
453988
  }
453953
453989
  try {
453954
- const content = await readFile22(entryPath, "utf-8");
453990
+ const content = await readFile23(entryPath, "utf-8");
453955
453991
  const { frontmatter } = parseFrontmatter(content);
453956
453992
  const description = typeof frontmatter.description === "string" ? frontmatter.description : undefined;
453957
453993
  files.push({
@@ -454254,7 +454290,7 @@ async function ensurePaths(paths) {
454254
454290
  }
454255
454291
  async function readTranscriptLines(paths) {
454256
454292
  try {
454257
- const raw2 = await readFile22(paths.transcriptPath, "utf-8");
454293
+ const raw2 = await readFile23(paths.transcriptPath, "utf-8");
454258
454294
  return raw2.split(`
454259
454295
  `).map((line) => line.trim()).filter((line) => line.length > 0);
454260
454296
  } catch {
@@ -454327,7 +454363,7 @@ function buildUnreflectedStateFromTranscript(lines) {
454327
454363
  async function readState(paths) {
454328
454364
  let raw2 = null;
454329
454365
  try {
454330
- raw2 = await readFile22(paths.statePath, "utf-8");
454366
+ raw2 = await readFile23(paths.statePath, "utf-8");
454331
454367
  } catch {
454332
454368
  raw2 = null;
454333
454369
  }
@@ -454659,7 +454695,7 @@ function isReflectionAutoPriority(value) {
454659
454695
  return value === "high" || value === "medium" || value === "low";
454660
454696
  }
454661
454697
  async function readReflectionAutoSelection(options) {
454662
- const raw2 = options.selectionReport ?? (options.selectionOutputPath ? await readFile22(options.selectionOutputPath, "utf-8") : "");
454698
+ const raw2 = options.selectionReport ?? (options.selectionOutputPath ? await readFile23(options.selectionOutputPath, "utf-8") : "");
454663
454699
  const parsed = parseReflectionAutoSelectionJson(raw2);
454664
454700
  if (!parsed || typeof parsed !== "object") {
454665
454701
  throw new Error("Reflection selector did not return valid JSON.");
@@ -456368,36 +456404,6 @@ var init_turn_send = __esm(async () => {
456368
456404
  ]);
456369
456405
  });
456370
456406
 
456371
- // src/agent/preloaded-skills.ts
456372
- import { readFile as readFile23 } from "node:fs/promises";
456373
- async function loadPreloadedSkills(ids, options = {}) {
456374
- if (ids.length === 0)
456375
- return "";
456376
- const { skillPathById } = await buildClientSkillsPayload({
456377
- ...options,
456378
- skillSources: options.skillSources?.length === 0 ? ["bundled"] : options.skillSources
456379
- });
456380
- const contents = [];
456381
- for (const id2 of ids) {
456382
- const path42 = skillPathById[id2];
456383
- if (!path42)
456384
- continue;
456385
- try {
456386
- contents.push(`<${id2}>
456387
- ${await readFile23(path42, "utf-8")}
456388
- </${id2}>`);
456389
- } catch {}
456390
- }
456391
- return contents.length ? `<loaded_skills>
456392
- ${contents.join(`
456393
-
456394
- `)}
456395
- </loaded_skills>` : "";
456396
- }
456397
- var init_preloaded_skills = __esm(() => {
456398
- init_client_skills();
456399
- });
456400
-
456401
456407
  // src/reminders/listen-context.ts
456402
456408
  function buildListenReminderContext(params) {
456403
456409
  return {
@@ -457222,7 +457228,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
457222
457228
  conversationId
457223
457229
  });
457224
457230
  try {
457225
- const agent = await getBackend().retrieveAgent(agentId || "");
457231
+ const agent = await retrieveResumeState(agentId, conversationId);
457226
457232
  const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend(agent, requestedConversationId);
457227
457233
  turnInput = rebuildTurnInputWithFreshDenials(turnInput, existingApprovals ?? [], STALE_APPROVAL_RECOVERY_DENIAL_REASON, isApprovalPendingError(errorDetail3));
457228
457234
  } catch {
@@ -457635,8 +457641,8 @@ var init_turn = __esm(async () => {
457635
457641
  init_approval_recovery();
457636
457642
  init_chatgpt_plan_rotation();
457637
457643
  init_check_approval();
457644
+ init_ephemeral_conversation();
457638
457645
  init_turn_recovery_policy();
457639
- init_backend2();
457640
457646
  init_accumulator();
457641
457647
  init_telemetry();
457642
457648
  init_error_reporting();
@@ -482031,88 +482037,6 @@ var init_headless_cloud_send = __esm(() => {
482031
482037
  init_headless_environment_response();
482032
482038
  });
482033
482039
 
482034
- // src/agent/ephemeral-conversation.ts
482035
- async function buildEphemeralConversationCreateBody(options) {
482036
- const system2 = options.systemPromptCustom ? options.systemPromptCustom : await resolveAndBuildSystemPrompt(options.systemPromptPreset, options.memoryPromptMode ?? "standard");
482037
- const request3 = await buildCreateAgentRequest({
482038
- model: options.model,
482039
- system: system2,
482040
- memoryPromptMode: "standard",
482041
- enableMemfs: false,
482042
- isSubagent: true,
482043
- baseTools: []
482044
- });
482045
- const modelSettings = options.model ? getModelUpdateArgs(options.model) : undefined;
482046
- const contextWindow = modelSettings?.context_window ?? await getModelContextWindow(request3.model);
482047
- return {
482048
- model: request3.model,
482049
- system: request3.system ?? system2,
482050
- ...modelSettings ? { model_settings: modelSettings } : {},
482051
- ...contextWindow ? { context_window_limit: contextWindow } : {}
482052
- };
482053
- }
482054
- function projectEphemeralAgent(conversationId, body3) {
482055
- return {
482056
- id: conversationId,
482057
- name: "Ephemeral conversation",
482058
- system: body3.system,
482059
- tools: [],
482060
- memory: { blocks: [] },
482061
- llm_config: {
482062
- handle: body3.model,
482063
- model: body3.model,
482064
- context_window: body3.context_window_limit ?? undefined,
482065
- model_settings: body3.model_settings ?? {}
482066
- },
482067
- model_settings: body3.model_settings ?? {},
482068
- message_buffer_autoclear: false
482069
- };
482070
- }
482071
- async function createEphemeralConversation2(options) {
482072
- const body3 = await buildEphemeralConversationCreateBody(options);
482073
- const conversation = await createEphemeralConversation(body3);
482074
- return {
482075
- agent: projectEphemeralAgent(conversation.id, body3),
482076
- conversationId: conversation.id
482077
- };
482078
- }
482079
- async function createLocalEphemeralConversation(options) {
482080
- const body3 = await buildEphemeralConversationCreateBody(options);
482081
- const backend3 = getBackend();
482082
- const internalAgent = await backend3.createAgent({
482083
- agent_type: "letta_v1_agent",
482084
- name: "Ephemeral conversation",
482085
- model: body3.model,
482086
- system: body3.system,
482087
- memory_blocks: [],
482088
- tags: [],
482089
- tools: [],
482090
- include_base_tools: false,
482091
- include_base_tool_rules: false,
482092
- initial_message_sequence: [],
482093
- parallel_tool_calls: true,
482094
- hidden: true
482095
- });
482096
- const conversation = await backend3.createConversation({
482097
- agent_id: internalAgent.id,
482098
- model: body3.model,
482099
- ...body3.model_settings ? { model_settings: body3.model_settings } : {},
482100
- ...body3.context_window_limit ? { context_window_limit: body3.context_window_limit } : {}
482101
- });
482102
- return {
482103
- agent: internalAgent,
482104
- conversationId: conversation.id
482105
- };
482106
- }
482107
- var init_ephemeral_conversation = __esm(() => {
482108
- init_available_models();
482109
- init_create_agent_request();
482110
- init_model();
482111
- init_system_prompt_resolution();
482112
- init_backend2();
482113
- init_ephemeral_conversations();
482114
- });
482115
-
482116
482040
  // src/headless-ephemeral-startup.ts
482117
482041
  function prepareHeadlessEphemeralBackend(enabled) {
482118
482042
  if (enabled && isLocalBackendEnabled()) {
@@ -482144,6 +482068,18 @@ function clearHeadlessClientToolRules(agent) {
482144
482068
  debugWarn("headless startup", `Failed to clear persisted client tool rules for ${agent.id}: ${error5 instanceof Error ? error5.message : String(error5)}`);
482145
482069
  });
482146
482070
  }
482071
+ async function retrieveHeadlessConversation(backend3, conversationId) {
482072
+ const conversation = await backend3.retrieveConversation(conversationId);
482073
+ const detached = conversation.agent_id === null;
482074
+ const parentId = Reflect.get(conversation, "parent_agent_id");
482075
+ return {
482076
+ agent: detached ? projectConversationState(conversation) : await backend3.retrieveAgent(conversation.agent_id, {
482077
+ include: ["agent.tools", "agent.tags"]
482078
+ }),
482079
+ detached,
482080
+ parentAgentId: detached && typeof parentId === "string" ? parentId : null
482081
+ };
482082
+ }
482147
482083
  var init_headless_ephemeral_startup = __esm(async () => {
482148
482084
  init_ephemeral_conversation();
482149
482085
  init_backend2();
@@ -482929,6 +482865,8 @@ In headless mode, use:
482929
482865
  let specifiedAgentIdFromAmbient = false;
482930
482866
  const forceNew = values4["new-agent"];
482931
482867
  const ephemeralFlag = values4.ephemeral;
482868
+ let isDetached = Boolean(ephemeralFlag);
482869
+ let parentAgentId = null;
482932
482870
  const systemPromptPreset = values4.system;
482933
482871
  const systemCustom = values4["system-custom"];
482934
482872
  const personalityInput = values4.personality;
@@ -483089,10 +483027,11 @@ In headless mode, use:
483089
483027
  if (specifiedConversationId && specifiedConversationId !== "default") {
483090
483028
  try {
483091
483029
  debugLog("conversations", `retrieve(${specifiedConversationId}) [headless conv→agent lookup]`);
483092
- const conversation = await startupBackend.retrieveConversation(specifiedConversationId);
483093
- agent = await startupBackend.retrieveAgent(conversation.agent_id, {
483094
- include: ["agent.tools", "agent.tags"]
483095
- });
483030
+ ({
483031
+ agent,
483032
+ detached: isDetached,
483033
+ parentAgentId
483034
+ } = await retrieveHeadlessConversation(startupBackend, specifiedConversationId));
483096
483035
  } catch (error5) {
483097
483036
  trackHeadlessBoundaryError("headless_conversation_lookup_failed", error5, "headless_startup_conversation_lookup");
483098
483037
  console.error(`Conversation ${specifiedConversationId} not found`);
@@ -483215,9 +483154,9 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483215
483154
  process.exit(1);
483216
483155
  }
483217
483156
  markMilestone("HEADLESS_AGENT_RESOLVED");
483218
- const publicAgentId = ephemeralFlag ? null : agent.id;
483157
+ const publicAgentId = isDetached ? null : agent.id;
483219
483158
  telemetry.setCurrentAgent(publicAgentId, agent.tags);
483220
- const isResumingAgent = !ephemeralFlag && !!(specifiedAgentId || !forceNew);
483159
+ const isResumingAgent = !isDetached && !!(specifiedAgentId || !forceNew);
483221
483160
  let conversationModel;
483222
483161
  if (isResumingAgent) {
483223
483162
  const modelHandle = model ? resolveModel(model) : undefined;
@@ -483240,7 +483179,7 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483240
483179
  let effectiveReflectionSettings;
483241
483180
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
483242
483181
  let startupMemfsFlag = autoEnableMemfsForFreshAgent ? true : memfsFlag;
483243
- if (!isStatelessSession && backend3.capabilities.remoteMemfs && !autoEnableMemfsForFreshAgent) {
483182
+ if (!isStatelessSession && !isDetached && backend3.capabilities.remoteMemfs && !autoEnableMemfsForFreshAgent) {
483244
483183
  const { hydrateMemfsSettingFromAgent: hydrateMemfsSettingFromAgent2, isLettaCloud: isLettaCloud2 } = await Promise.resolve().then(() => (init_memory_filesystem2(), exports_memory_filesystem));
483245
483184
  const memfsEnabled = await hydrateMemfsSettingFromAgent2(agent);
483246
483185
  if (!memfsEnabled && await isLettaCloud2()) {
@@ -483248,9 +483187,9 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483248
483187
  }
483249
483188
  }
483250
483189
  let memfsBgPromise;
483251
- const secretsAgentId = ephemeralFlag ? undefined : agent?.id;
483190
+ const secretsAgentId = isDetached ? undefined : agent?.id;
483252
483191
  const secretsInitPromise = secretsAgentId ? Promise.resolve().then(() => (init_secrets_store(), exports_secrets_store)).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(secretsAgentId)) : Promise.resolve();
483253
- if (isStatelessSession) {
483192
+ if (isStatelessSession || isDetached) {
483254
483193
  settingsManager.setMemfsEnabled(agent.id, false);
483255
483194
  } else if (!backend3.capabilities.remoteMemfs) {
483256
483195
  if (backend3.capabilities.localMemfs) {
@@ -483332,11 +483271,11 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483332
483271
  memoryMode: getMemoryPromptModeForAgent2(taggedAgent.id)
483333
483272
  });
483334
483273
  }
483335
- if (!ephemeralFlag) {
483274
+ if (!isDetached) {
483336
483275
  clearHeadlessClientToolRules(agent);
483337
483276
  }
483338
483277
  try {
483339
- if (ephemeralFlag) {
483278
+ if (isDetached) {
483340
483279
  effectiveReflectionSettings = { trigger: "off", stepCount: 0 };
483341
483280
  } else {
483342
483281
  const resolvedReflectionSettings = await applyHeadlessReflectionOverrides(agent.id, reflectionOverrides);
@@ -483388,7 +483327,7 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483388
483327
  }
483389
483328
  markMilestone("HEADLESS_CONVERSATION_READY");
483390
483329
  setConversationId(conversationId);
483391
- if (!ephemeralFlag && shouldPersistSessionState()) {
483330
+ if (!isDetached && shouldPersistSessionState()) {
483392
483331
  await settingsManager.loadLocalProjectSettings();
483393
483332
  settingsManager.persistSession(agent.id, conversationId);
483394
483333
  }
@@ -483530,7 +483469,7 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483530
483469
  const resolveAllPendingApprovals = async (mode = "send_immediately") => {
483531
483470
  const { getResumeDataFromBackend: getResumeDataFromBackend2 } = await Promise.resolve().then(() => (init_check_approval(), exports_check_approval));
483532
483471
  while (true) {
483533
- const freshAgent = ephemeralFlag ? agent : await backend3.retrieveAgent(agent.id);
483472
+ const freshAgent = isDetached ? agent : await backend3.retrieveAgent(agent.id);
483534
483473
  let resume;
483535
483474
  try {
483536
483475
  resume = await getResumeDataFromBackend2(freshAgent, conversationId);
@@ -483590,7 +483529,7 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483590
483529
  }
483591
483530
  }
483592
483531
  };
483593
- if (isResumingAgent) {
483532
+ if (isResumingAgent || !usesRemoteEnvironment && isDetached && !ephemeralFlag) {
483594
483533
  try {
483595
483534
  await resolveAllPendingApprovals("queue_for_next_turn");
483596
483535
  } catch (approvalError) {
@@ -483650,9 +483589,12 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483650
483589
  pushPart(prompt);
483651
483590
  telemetry.trackUserInput(prompt, "user", reportModel ?? "unknown");
483652
483591
  if (usesRemoteEnvironment) {
483592
+ const agentId = publicAgentId ?? parentAgentId;
483593
+ if (!agentId)
483594
+ throw new Error("Listener delivery requires a conversation with an agent or parent agent.");
483653
483595
  const environmentSelector = explicitEnvironmentSelector ?? "";
483654
483596
  const useCloudSandbox = isCloudEnvironmentSelector(environmentSelector);
483655
- const environmentRouting = environmentSelector ? useCloudSandbox ? await resolveAgentSandboxConnectionId(agent.id, { conversationId }) : await resolveEnvironmentConnectionId(environmentSelector) : null;
483597
+ const environmentRouting = environmentSelector ? useCloudSandbox ? await resolveAgentSandboxConnectionId(agentId, { conversationId }) : await resolveEnvironmentConnectionId(environmentSelector) : null;
483656
483598
  const connectionId = environmentRouting?.connectionId ?? inheritedListenerConnectionId;
483657
483599
  if (!connectionId)
483658
483600
  throw new Error("No listener connection was resolved for this launch");
@@ -483666,8 +483608,9 @@ Current session AGENT_ID=${process.env.AGENT_ID}; --backend local switches to a
483666
483608
  noWait: isAgentLaunch || Boolean(values4["no-wait"]),
483667
483609
  clientMessageId: values4["client-message-id"],
483668
483610
  connectionId,
483611
+ parentAgentId: parentAgentId ?? undefined,
483669
483612
  scope: {
483670
- agent_id: agent.id,
483613
+ agent_id: publicAgentId,
483671
483614
  conversation_id: conversationId,
483672
483615
  acting_user_id: resolveActingUserId()
483673
483616
  },
@@ -534800,4 +534743,4 @@ function registerBunOAuthFlows() {
534800
534743
  registerBunOAuthFlows();
534801
534744
  await init_src5().then(() => exports_src2);
534802
534745
 
534803
- //# debugId=BF59442F8ED1752A64756E2164756E21
534746
+ //# debugId=25D231F0C01DA6C764756E2164756E21