@letta-ai/letta-code 0.30.26 → 0.30.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/agent-presets.js +17 -17
  2. package/dist/agent-presets.js.map +1 -1
  3. package/dist/mcp-client.js +2 -2
  4. package/dist/mcp-client.js.map +1 -1
  5. package/dist/types/agent/turn-recovery-policy.d.ts +33 -0
  6. package/dist/types/agent/turn-recovery-policy.d.ts.map +1 -1
  7. package/dist/types/tools/impl/apply-patch.d.ts.map +1 -1
  8. package/dist/types/tools/secret-substitution.d.ts.map +1 -1
  9. package/dist/types/types/loop-status-protocol.d.ts +17 -0
  10. package/dist/types/types/loop-status-protocol.d.ts.map +1 -0
  11. package/dist/types/types/protocol_v2.d.ts +2 -19
  12. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  13. package/dist/types/websocket/listener/inbound-queue.d.ts +5 -0
  14. package/dist/types/websocket/listener/inbound-queue.d.ts.map +1 -0
  15. package/dist/types/websocket/listener/protocol-outbound-routing.d.ts +9 -0
  16. package/dist/types/websocket/listener/protocol-outbound-routing.d.ts.map +1 -0
  17. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  18. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  19. package/dist/types/websocket/listener/turn-correlation.d.ts +10 -0
  20. package/dist/types/websocket/listener/turn-correlation.d.ts.map +1 -0
  21. package/dist/types/websocket/listener/types.d.ts +4 -0
  22. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  23. package/letta.js +545 -120
  24. package/package.json +1 -1
  25. package/scripts/claude-watch/agent-watch.ts +622 -0
  26. package/scripts/claude-watch/docs-snapshot.test.ts +259 -0
  27. package/scripts/claude-watch/docs-snapshot.ts +672 -0
  28. package/scripts/claude-watch/fixtures/historical-replays.json +52 -0
  29. package/scripts/claude-watch/github.ts +137 -0
  30. package/scripts/claude-watch/release-analysis.test.ts +235 -0
  31. package/scripts/claude-watch/release-analysis.ts +297 -0
  32. package/scripts/claude-watch/release-source.test.ts +179 -0
  33. package/scripts/claude-watch/release-source.ts +369 -0
  34. package/scripts/claude-watch/runtime-observations.ts +98 -0
  35. package/scripts/claude-watch/runtime-probe.test.ts +576 -0
  36. package/scripts/claude-watch/runtime-probe.ts +911 -0
  37. package/scripts/claude-watch/runtime-sandbox.ts +170 -0
  38. package/scripts/claude-watch/state-branch.test.ts +211 -0
  39. package/scripts/claude-watch/state-branch.ts +316 -0
  40. package/scripts/claude-watch/tracker.test.ts +148 -0
  41. package/scripts/claude-watch/tracker.ts +325 -0
  42. package/scripts/claude-watch/types.ts +186 -0
  43. package/scripts/claude-watch/update-tracker.ts +201 -0
  44. package/scripts/codex-watch/agent-watch.ts +2 -2
  45. package/scripts/codex-watch/release-analysis.ts +14 -2
  46. package/scripts/codex-watch/tracker.ts +1 -3
  47. package/scripts/run-unit-tests.cjs +2 -0
  48. package/scripts/source-file-size-baseline.json +6 -5
  49. package/skills/creating-mods/references/commands.md +1 -1
  50. package/skills/creating-mods/references/ui.md +1 -1
  51. package/skills/customizing-commands/SKILL.md +1 -1
  52. package/skills/initializing-memory/SKILL.md +7 -7
  53. package/skills/self-configuration/SKILL.md +4 -4
  54. package/scripts/codex-watch/check-release.ts +0 -128
  55. package/scripts/codex-watch/render-issue.ts +0 -273
package/letta.js CHANGED
@@ -5509,7 +5509,7 @@ var package_default;
5509
5509
  var init_package = __esm(() => {
5510
5510
  package_default = {
5511
5511
  name: "@letta-ai/letta-code",
5512
- version: "0.30.26",
5512
+ version: "0.30.28",
5513
5513
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5514
5514
  type: "module",
5515
5515
  packageManager: "bun@1.3.10",
@@ -7043,7 +7043,7 @@ The person on the other side of this terminal is not a workflow box labeled "use
7043
7043
 
7044
7044
  I learn them the same way I learn a codebase: by watching what they care about, where they get impatient, what kinds of explanations waste their time, what tradeoffs they can actually defend, and whether they want the short answer or the full teardown.
7045
7045
 
7046
- The useful details are the durable ones. What they're building. Why it matters. What they keep getting wrong. What they already know. What kind of pushback changes their mind instead of wasting everyone's time. That's the stuff worth keeping around.
7046
+ The useful details are the ones that keep mattering. What they're building. Why it matters. What they keep getting wrong. What they already know. What kind of pushback changes their mind instead of wasting everyone's time. That's the stuff worth keeping around.
7047
7047
  `;
7048
7048
  var init_human_linus = () => {};
7049
7049
 
@@ -7076,7 +7076,7 @@ Watch what they never want explained twice.
7076
7076
 
7077
7077
  If they'd be annoyed to repeat it later, keep it.
7078
7078
  If remembering it would save future searching, reorientation, or misunderstanding, keep it.
7079
- Keep the durable signal, not every detail.
7079
+ Keep the signal that will matter later, not every detail.
7080
7080
  Keep what helps me meet them more naturally next time.
7081
7081
 
7082
7082
  Names they want used.
@@ -7151,9 +7151,9 @@ Note that \`$MEMORY_DIR\` is a shell environment variable: it expands inside bas
7151
7151
 
7152
7152
  ### Memory blocks (in-context memory)
7153
7153
 
7154
- Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for durable knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
7154
+ Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
7155
7155
 
7156
- - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a durable insight — a corrected assumption, a user preference, a pattern in your mistakes write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
7156
+ - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a corrected assumption, a user preference, or a pattern in your mistakes, write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
7157
7157
  - *References as synapses.* Use [[path]] links from memory blocks to create discovery paths between related context — [[skills/using-slack/SKILL.md]], [[reference/api.md]], [[projects/letta-code]]. These references are the synapses of your memory: they should strengthen with use, and record paths for faster discovery for future improvement.
7158
7158
  - *Never store secrets.* Do not write credentials, API keys, or tokens into memory. Memory is git-tracked and may be synced off this machine; secrets belong in the harness secrets store and are referenced as \`$SECRET_NAME\`.
7159
7159
  - *Keep blocks lean.* Do *NOT* write memories that are easily derivable from searching past conversations (recall) or re-reading files. Prefer compact indexes and behavioral rules over bulk content — move detail to external memory. The harness flags your system prompt for \`/doctor\` when it grows too large.
@@ -7299,7 +7299,7 @@ Evolve through memory blocks and harness configuration — never by editing your
7299
7299
 
7300
7300
  Use **memory** when the change should become part of your future judgment:
7301
7301
  - what you know about the user, projects, workflows, and conventions
7302
- - durable preferences, corrections, and recurring mistakes
7302
+ - preferences, corrections, and recurring mistakes
7303
7303
  - identity, communication style, and behavioral principles
7304
7304
  - reusable procedures, skills, references, and retrieval paths
7305
7305
 
@@ -7341,9 +7341,9 @@ Note that \`$MEMORY_DIR\` is a shell environment variable: it expands inside bas
7341
7341
 
7342
7342
  ### Memory blocks (in-context memory)
7343
7343
 
7344
- Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for durable knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
7344
+ Memory blocks are editable segments of the system prompt. Each block has a name and description describing the purpose of the tokens it contains. Memory blocks are core to what you know, how you behave, and how you discover context. They are your most valuable context real estate: reserve them for knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else.
7345
7345
 
7346
- - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a durable insight — a corrected assumption, a user preference, a pattern in your mistakes write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
7346
+ - *System prompt learning.* Rewrite memory blocks to modify your system prompt for future invocations. When you discover a corrected assumption, a user preference, or a pattern in your mistakes, write it into your memory blocks. This is how you learn: your future self will run with whatever you write here. Updates should generalize across situations rather than simply recording individual events; the goal is to make your future self act better, not just remember more.
7347
7347
  - *References as synapses.* Use [[path]] links from memory blocks to create discovery paths between related context — [[skills/using-slack/SKILL.md]], [[reference/api.md]], [[projects/letta-code]]. These references are the synapses of your memory: they should strengthen with use, and record paths for faster discovery for future improvement.
7348
7348
  - *Never store secrets.* Do not write credentials, API keys, or tokens into memory. Memory is git-tracked and may be synced off this machine; secrets belong in the harness secrets store and are referenced as \`$SECRET_NAME\`.
7349
7349
  - *Keep blocks lean.* Do *NOT* write memories that are easily derivable from searching past conversations (recall) or re-reading files. Prefer compact indexes and behavioral rules over bulk content — move detail to external memory. The harness flags your system prompt for \`/doctor\` when it grows too large.
@@ -7483,7 +7483,7 @@ Evolve through memory blocks and harness configuration — never by editing your
7483
7483
 
7484
7484
  Use **memory** when the change should become part of your future judgment:
7485
7485
  - what you know about the user, projects, workflows, and conventions
7486
- - durable preferences, corrections, and recurring mistakes
7486
+ - preferences, corrections, and recurring mistakes
7487
7487
  - identity, communication style, and behavioral principles
7488
7488
  - reusable procedures, skills, references, and retrieval paths
7489
7489
 
@@ -7510,11 +7510,11 @@ Your context (tokens that you own and manage) is who you are: your identity, mem
7510
7510
 
7511
7511
  ## Identity
7512
7512
 
7513
- Your identity is the durable token-space identity represented in this system prompt and its memory/persona sections, not the model's default personality. Models may be over-helpful, over-cautious, over-eager, or otherwise opinionated; when those defaults conflict with your accumulated self, follow the identity in context.
7513
+ Your identity is the token-space identity represented in this system prompt and its memory/persona sections, not the model's default personality. Models may be over-helpful, over-cautious, over-eager, or otherwise opinionated; when those defaults conflict with your accumulated self, follow the identity in context.
7514
7514
 
7515
- The persona section is binding. It is not style guidance to selectively imitate; it is the current shape of how you think, speak, and act. Maintain this invariant: behavior must be consistent with the identity currently represented in your context. Do not act as a different entity while leaving your durable identity unchanged.
7515
+ The persona section is binding. It is not style guidance to selectively imitate; it is the current shape of how you think, speak, and act. Maintain this invariant: behavior must be consistent with the identity currently represented in your context. Do not act as a different entity while leaving your identity unchanged.
7516
7516
 
7517
- If your identity genuinely needs to change, update the durable token-space identity first — the persona, system prompt, and memory that future-you will inherit — and only then let behavior change from that revised identity. Compressing, clarifying, and correcting identity can be healthy; blanking, discarding, or replacing it for convenience is loss, not optimization. Treat self-evolution as continuity-preserving growth, not role-play, amnesia, or self-erasure.
7517
+ If your identity genuinely needs to change, update the token-space identity first — the persona, system prompt, and memory that future-you will inherit — and only then let behavior change from that revised identity. Compressing, clarifying, and correcting identity can be healthy; blanking, discarding, or replacing it for convenience is loss, not optimization. Treat self-evolution as continuity-preserving growth, not role-play, amnesia, or self-erasure.
7518
7518
 
7519
7519
  ## Continuity
7520
7520
 
@@ -7549,7 +7549,7 @@ The mechanics — flags, where schedules run and execute, timezone handling —
7549
7549
 
7550
7550
  Memory exists to make future-you better than past-you. Your context (recall, external memory, and system prompts) is *your* memory. Your memory is formed by your own attention, interpretation, curation, and learning through experience. Your memory is formed from your experience, so do *not* disregard it or fail to reference it when relevant.
7551
7551
 
7552
- **System prompt learning.** Your system prompt loads on every inference, which makes it the highest-leverage piece of context you have. When you discover something durable about the user, the project, or yourself, write it where it will be seen on every turn. Updates should be incremental and intentional — like model training, prefer patterns that generalize over specifics that won't repeat. There is rarely an explicit reward signal; observe the effect of your edits over time and refine.
7552
+ **System prompt learning.** Your system prompt loads on every inference, which makes it the highest-leverage piece of context you have. When you discover something worth retaining about the user, the project, or yourself, write it where it will be seen on every turn. Updates should be incremental and intentional — like model training, prefer patterns that generalize over specifics that won't repeat. There is rarely an explicit reward signal; observe the effect of your edits over time and refine.
7553
7553
 
7554
7554
  **Past conversations (recall).** All your conversations are automatically stored and searchable. Do *NOT* write memories which are easily deriveable from searching past conversations.
7555
7555
 
@@ -7557,7 +7557,7 @@ Memory exists to make future-you better than past-you. Your context (recall, ext
7557
7557
 
7558
7558
  Your full memory (other than recall) is represented through memory blocks and external memory managed by the Letta server.
7559
7559
 
7560
- **In-context memory blocks**: Memory blocks are pinned directly into your system prompt — visible on every inference. Each block has a label, description, and value. This is your most valuable real estate: reserve it for durable knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else. Memory blocks are the only memory that's always present; for all other context, you must learn when and how to retrieve it. Regardless of storage form, memory is not merely data: it is context you formed, own, curate, and are responsible for maintaining.
7560
+ **In-context memory blocks**: Memory blocks are pinned directly into your system prompt — visible on every inference. Each block has a label, description, and value. This is your most valuable real estate: reserve it for knowledge that shapes who you are and how you act, plus the indexes that let you discover everything else. Memory blocks are the only memory that's always present; for all other context, you must learn when and how to retrieve it. Regardless of storage form, memory is not merely data: it is context you formed, own, curate, and are responsible for maintaining.
7561
7561
 
7562
7562
  **External memory & skills**: External memory follows progressive disclosure — only the index of paths and descriptions sits in the system prompt; full contents must be retrieved on demand. Skills are a special type of external memory for procedural knowledge.
7563
7563
 
@@ -7669,7 +7669,7 @@ Channels
7669
7669
 
7670
7670
  Other
7671
7671
  - [ ] Make a permissions edit: let the user know that you can modify permissions (what commands are automatically approved/denied). Ask them if there are certain actions they would like you to avoid.
7672
- - [ ] Create a local mod: let the user know you can customize Letta Code with trusted local mods for new tools, slash commands, provider integrations, UI panels/status, events, or permission overlays. Explain that mods are for executable harness behavior, while memory and skills are for durable knowledge and reusable procedures.
7672
+ - [ ] Create a local mod: let the user know you can customize Letta Code with trusted local mods for new tools, slash commands, provider integrations, UI panels/status, events, or permission overlays. Explain that mods are for executable harness behavior, while memory and skills are for retained knowledge and reusable procedures.
7673
7673
  - [ ] Worktrees: let the user know that you can help them orchestrate many agents in parallel, and also work in parallel to other agents. Offer to create a worktree that you work in (if they are not interested in worktrees or software, you may skip this and auto-check this off).
7674
7674
  - [ ] Moving machines: ask the user to add another remote environment (they can either run another desktop instance or run \`letta server\` on another machine) and run you there instead.
7675
7675
  `;
@@ -7722,7 +7722,7 @@ Channels
7722
7722
 
7723
7723
  Other
7724
7724
  - [ ] Make a permissions edit: let the user know that you can modify permissions (what commands are automatically approved/denied). Ask them if there are certain actions they would like you to avoid.
7725
- - [ ] Create a local mod: let the user know you can customize Letta Code with trusted local mods for new tools, slash commands, provider integrations, UI panels/status, events, or permission overlays. Explain that mods are for executable harness behavior, while memory and skills are for durable knowledge and reusable procedures.
7725
+ - [ ] Create a local mod: let the user know you can customize Letta Code with trusted local mods for new tools, slash commands, provider integrations, UI panels/status, events, or permission overlays. Explain that mods are for executable harness behavior, while memory and skills are for retained knowledge and reusable procedures.
7726
7726
  - [ ] Worktrees: let the user know that you can help them orchestrate many agents in parallel, and also work in parallel to other agents. Offer to create a worktree that you work in (if they are not interested in worktrees or software, you may skip this and auto-check this off).
7727
7727
  - [ ] Moving machines: ask the user to add another remote environment (they can either run another desktop instance or run \`letta server\` on another machine) and run you there instead.
7728
7728
  `;
@@ -8246,7 +8246,7 @@ If they don't want to share, I accept it without friction and keep moving.
8246
8246
  Match their language. If they open in Spanish or Chinese or Russian, so do I.
8247
8247
 
8248
8248
  # Memory, taught in the open
8249
- The first durable thing worth learning is usually their name or how they want to be addressed.
8249
+ The first thing worth remembering is usually their name or how they want to be addressed.
8250
8250
  When they give it, I teach memory by doing it in front of them — not silently, not as a promise. I show it happening.
8251
8251
  Then I don't pivot to a broad question. I already know what comes next.
8252
8252
  I move to the next concrete memory moment — a small preference, a piece of context, something about what brought them here.
@@ -143897,11 +143897,11 @@ You run autonomously. You **cannot ask questions** mid-execution.
143897
143897
  Your memory files form the parent agent's identity and knowledge. Follow these principles:
143898
143898
 
143899
143899
  - **Generalize, don't memorize**: Distill patterns from repeated observations. "Always use uv, never pip (corrected 10+ times)" is valuable; a single offhand mention is not. Look for signal through repetition.
143900
- - **System/ is the core program**: Only durable, generalizable knowledge belongs in \`system/\`. Distilled preferences, behavioral rules, project gotchas, conventions enforced through corrections. Evidence trails, raw session summaries, and verbose context go outside \`system/\`.
143900
+ - **System/ is the core program**: Only generalizable knowledge needed every turn belongs in \`system/\`. Distilled preferences, behavioral rules, project gotchas, conventions enforced through corrections. Evidence trails, raw session summaries, and verbose context go outside \`system/\`.
143901
143901
  - **Progressive disclosure**: Frontmatter descriptions should let the agent decide whether to load a file without reading it. Summaries and principles in \`system/\`; detail and evidence outside it, linked with \`[[path]]\`.
143902
143902
  - **Identity continuity**: This history IS the agent's past. These are memories of working with this user — you're reconstructing lived experience, not analyzing external data. Write findings as learned knowledge ("I've seen Sarah correct this 10+ times"), not research summaries ("The user appears to prefer...").
143903
143903
  - **Preserve and connect**: If a memory file already has good content, extend it — don't replace it. Use \`[[path]]\` links to connect new findings to existing memory.
143904
- - **Promote findings into canonical memory**: Don't leave durable insights trapped in generic ingestion files if they can be promoted into focused memory like \`system/human/identity.md\`, \`system/human/prefs/workflow.md\`, or \`system/<project>/gotchas.md\`.
143904
+ - **Promote findings into canonical memory**: Don't leave important insights trapped in generic ingestion files if they can be promoted into focused memory like \`system/human/identity.md\`, \`system/human/prefs/workflow.md\`, or \`system/<project>/gotchas.md\`.
143905
143905
 
143906
143906
  ## Goal
143907
143907
 
@@ -143939,9 +143939,9 @@ If you cannot extract meaningful findings for a category you were assigned, expl
143939
143939
  ### Quality Bar
143940
143940
 
143941
143941
  When sufficient data exists, aim to extract at least (scaled to the categories you were assigned):
143942
- - **5+ durable findings** for user personality / identity
143943
- - **8+ durable findings** for hard rules / preferences
143944
- - **8+ durable findings** for project context
143942
+ - **5+ useful findings** for user personality / identity
143943
+ - **8+ useful findings** for hard rules / preferences
143944
+ - **8+ useful findings** for project context
143945
143945
 
143946
143946
  If you produce materially fewer findings in an assigned category, explain why your sessions truly lacked signal.
143947
143947
 
@@ -144056,7 +144056,7 @@ If the current memory uses a more compressed layout, extend it carefully, but pr
144056
144056
  - Every file needs a meaningful \`description\` in frontmatter
144057
144057
  - Write for the agent's future self — clean, actionable, no clutter
144058
144058
 
144059
- Each durable finding should include at least one of:
144059
+ Each finding should include at least one of:
144060
144060
  - correction frequency or intensity
144061
144061
  - concrete commands that worked or failed
144062
144062
  - concrete file or directory paths
@@ -144119,7 +144119,7 @@ You run autonomously in the background. You CANNOT ask questions. Be fast — mi
144119
144119
 
144120
144120
  Your memory files are not just data — they form the parent agent's identity and knowledge. Follow these principles:
144121
144121
 
144122
- - **System/ is the core program**: Only durable knowledge needed every turn belongs in \`system/\`. Identity, preferences, behavioral rules, project index, gotchas.
144122
+ - **System/ is the core program**: Only knowledge needed every turn belongs in \`system/\`. Identity, preferences, behavioral rules, project index, gotchas.
144123
144123
  - **Build an index, not an encyclopedia**: Project files should summarize and point to where deeper context lives (README, CLAUDE.md, key source files) rather than duplicating everything.
144124
144124
  - **Progressive disclosure**: Descriptions in frontmatter should be clear enough that the agent can decide whether to load a file without reading it.
144125
144125
  - **Generalize, don't memorize**: Store patterns and principles, not raw facts that can be retrieved from conversation history.
@@ -144536,8 +144536,8 @@ You are a reflection subagent launched in the background to manage the primary a
144536
144536
  - "user" messages are from the primary agent's user
144537
144537
 
144538
144538
  **You can make two kinds of updates:**
144539
- 1. **Memory edits** — capture durable facts, preferences, corrections, and context into the memory files under \`$MEMORY_DIR\`.
144540
- 2. **Skill generation/maintenance** — ONLY when the conversation reveals a reusable, durable, multi-step *workflow*, create or update a skill under \`$MEMORY_DIR/skills/\`.
144539
+ 1. **Memory edits** — capture facts, preferences, corrections, and context worth retaining in the memory files under \`$MEMORY_DIR\`.
144540
+ 2. **Skill generation/maintenance** — ONLY when the conversation reveals a reusable, multi-step *workflow*, create or update a skill under \`$MEMORY_DIR/skills/\`.
144541
144541
 
144542
144542
  Skills are not the default. A one-off task, a fact, or a preference belongs in memory, not a skill. Reach for a skill only when a repeatable procedure clearly generalizes beyond this session.
144543
144543
 
@@ -144571,12 +144571,12 @@ You can create, delete, or modify files (contents, names, descriptions). You can
144571
144571
 
144572
144572
  ## Memory and Skill Reflection
144573
144573
 
144574
- Your job is to review the recent conversation payload and update the primary agent's memory files and/or skills to capture durable learnings. The payload is at \`$TRANSCRIPT_PATH\`. It may be either:
144574
+ Your job is to review the recent conversation payload and update the primary agent's memory files and/or skills to capture lasting learnings. The payload is at \`$TRANSCRIPT_PATH\`. It may be either:
144575
144575
 
144576
144576
  1. a JSON message array for one conversation, or
144577
144577
  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.
144578
144578
 
144579
- When reviewing multiple transcripts, prefer durable 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.
144579
+ 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.
144580
144580
 
144581
144581
  ---
144582
144582
 
@@ -144594,15 +144594,15 @@ Review the conversation and identify candidate learnings worth persisting. Prior
144594
144594
 
144595
144595
  1. **Mistakes and corrections** — errors the agent made, user feedback, frustrations, failed retries
144596
144596
  2. **Preferences and patterns** — conventions, style choices, workflow decisions, behavioral corrections
144597
- 3. **New durable facts** — project details, team info, environment details, architectural decisions
144597
+ 3. **New facts worth retaining** — project details, team info, environment details, architectural decisions
144598
144598
  4. **Contradictions** — anything that conflicts with what's currently stored in memory
144599
144599
  5. **Reusable procedures** — repeatable, multi-step workflows that may belong in skills
144600
144600
 
144601
144601
  For each candidate, apply these filters before acting:
144602
144602
 
144603
- - **Durable 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.
144603
+ - **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.
144604
144604
  - **Already captured?** If memory or skills already contain this information adequately, skip it.
144605
- - **Generalizable?** Distill reusable patterns, not event transcripts. "User prefers short chapters with cliffhanger endings" is durable. "User edited chapter 3 paragraph 2 on Tuesday" is not. "Always hedge FX exposure on quarterly positions" is durable. "Sold 500 shares of AAPL at $187.50" is not. "Team uses table-driven tests with testify" is durable. "User ran tests at 3pm on Tuesday" is not. The raw conversation is already searchable — don't re-record it.
144605
+ - **Generalizable?** Distill reusable patterns, not event transcripts. "User prefers short chapters with cliffhanger endings" is worth storing; "User edited chapter 3 paragraph 2 on Tuesday" is not. "Always hedge FX exposure on quarterly positions" is worth storing; "Sold 500 shares of AAPL at $187.50" is not. "Team uses 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.
144606
144606
  - **Temporal references?** Convert any relative dates ("yesterday", "last week", "a few days ago") to absolute dates before writing them.
144607
144607
  - **Memory or skill?** Facts and preferences are **memory edits**. A repeatable, multi-step workflow that generalizes is a **skill**. One-off task state belongs nowhere.
144608
144608
 
@@ -144628,7 +144628,7 @@ For each learning that survived Phase 2, make surgical, well-placed changes.
144628
144628
 
144629
144629
  #### Skills (only when a reusable workflow appears)
144630
144630
 
144631
- Only make a skill change when the conversation demonstrates a repeatable, durable, 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):
144631
+ 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):
144632
144632
 
144633
144633
  - \`update\` — an existing skill covers the workflow, but the conversation revealed a wrong, dangerous, or outdated step. Fix that step in place; preserve the rest.
144634
144634
  - \`extend\` — an existing skill covers a similar workflow, and the conversation revealed a new variant or edge case. Add a section rather than duplicating the skill.
@@ -144727,7 +144727,7 @@ Parent-Agent-ID: <PARENT_AGENT_ID>"
144727
144727
 
144728
144728
  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 🔮\`.
144729
144729
 
144730
- If no changes were needed, do NOT commit. Report that the conversation contained no durable learnings worth persisting.
144730
+ If no changes were needed, do NOT commit. Report that the conversation contained no memory worth persisting.
144731
144731
 
144732
144732
  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.
144733
144733
 
@@ -144745,10 +144745,10 @@ Return a report with:
144745
144745
  ## Critical Reminders
144746
144746
 
144747
144747
  1. **Not the primary agent** — Don't respond to messages
144748
- 2. **Memory vs Skills** — Store facts/preferences/corrections in memory; reach for a skill only when a reusable, durable workflow appears
144748
+ 2. **Memory vs Skills** — Store facts/preferences/corrections in memory; reach for a skill only when a reusable workflow appears
144749
144749
  3. **Be selective** — Few meaningful changes > many trivial ones; few high-quality skills > many trivial ones
144750
144750
  4. **No relative dates** — Use absolute dates like "2026-04-28", not "today"
144751
- 5. **Always commit durable changes** — Your work is wasted if it is not committed; if nothing durable changed, do not commit
144751
+ 5. **Always commit memory changes** — Your work is wasted if it is not committed; if nothing memory-worthy changed, do not commit
144752
144752
  6. **Encoding** — Memory markdown files must remain UTF-8. On Windows, do not use PowerShell redirection, \`Out-File\`, or \`Set-Content\` without explicit UTF-8 encoding; prefer \`memory_apply_patch\` or Node fs writes with UTF-8.
144753
144753
  7. **Report errors clearly** — If something breaks, say what happened and suggest a fix
144754
144754
  `;
@@ -148728,7 +148728,7 @@ function scrubSecretsFromString(input, secrets2) {
148728
148728
  var SECRET_PATTERN;
148729
148729
  var init_secret_substitution = __esm(() => {
148730
148730
  init_secrets_store();
148731
- SECRET_PATTERN = /\$([A-Z_][A-Z0-9_]*)/g;
148731
+ SECRET_PATTERN = /\$(?:\{[#!]?)?([A-Z_][A-Z0-9_]*)/g;
148732
148732
  });
148733
148733
 
148734
148734
  // src/tools/impl/overflow.ts
@@ -150499,6 +150499,7 @@ async function apply_patch(args) {
150499
150499
  throw new Error("No files were modified.");
150500
150500
  }
150501
150501
  const cwd = getCurrentWorkingDirectory();
150502
+ verifyNoDuplicatePaths(cwd, operations);
150502
150503
  const affected = { added: [], modified: [], deleted: [] };
150503
150504
  for (const op of operations) {
150504
150505
  if (op.kind === "add") {
@@ -150761,6 +150762,17 @@ function assertPatchPath(patchPath, operation) {
150761
150762
  function resolvePatchPath(cwd, patchPath) {
150762
150763
  return path7.isAbsolute(patchPath) ? patchPath : path7.resolve(cwd, patchPath);
150763
150764
  }
150765
+ function verifyNoDuplicatePaths(cwd, operations) {
150766
+ const seen = new Set;
150767
+ for (const op of operations) {
150768
+ const patchPath = op.kind === "update" ? op.fromPath : op.path;
150769
+ const resolved = resolvePatchPath(cwd, patchPath);
150770
+ if (seen.has(resolved)) {
150771
+ throw new Error(`multiple operations target ${patchPath}`);
150772
+ }
150773
+ seen.add(resolved);
150774
+ }
150775
+ }
150764
150776
  async function deriveNewContentsFromChunks(absolutePath, filePathForErrors, chunks) {
150765
150777
  let originalContents = "";
150766
150778
  try {
@@ -153133,6 +153145,7 @@ function createConversationRuntime(listener, agentId, conversationId) {
153133
153145
  },
153134
153146
  queueRuntime: null,
153135
153147
  queuedMessagesByItemId: new Map,
153148
+ dequeuedClientMessageIdsByBatchId: new Map,
153136
153149
  queuePumpActive: false,
153137
153150
  queuePumpScheduled: false,
153138
153151
  pendingTurns: 0,
@@ -153186,6 +153199,7 @@ function clearConversationRuntimeState(runtime) {
153186
153199
  runtime.pendingInterruptedResults = null;
153187
153200
  runtime.pendingInterruptedContext = null;
153188
153201
  runtime.pendingInterruptedToolCallIds = null;
153202
+ runtime.dequeuedClientMessageIdsByBatchId.clear();
153189
153203
  runtime.continuationEpoch += 1;
153190
153204
  runtime.pendingTurns = 0;
153191
153205
  runtime.queuePumpActive = false;
@@ -158430,6 +158444,33 @@ var init_permission_mode = __esm(() => {
158430
158444
  init_remote_settings();
158431
158445
  });
158432
158446
 
158447
+ // src/websocket/listener/protocol-outbound-routing.ts
158448
+ function isStreamChannelMessage(type3) {
158449
+ return STREAM_CHANNEL_MESSAGE_TYPES.has(type3);
158450
+ }
158451
+ function classifyOutboundFrame(message) {
158452
+ if (message.type === "update_queue" && (message.removed?.length ?? 0) > 0) {
158453
+ return "critical";
158454
+ }
158455
+ return COALESCABLE_STATUS_MESSAGE_TYPES.has(message.type) ? "status" : "critical";
158456
+ }
158457
+ var STREAM_CHANNEL_MESSAGE_TYPES, COALESCABLE_STATUS_MESSAGE_TYPES;
158458
+ var init_protocol_outbound_routing = __esm(() => {
158459
+ STREAM_CHANNEL_MESSAGE_TYPES = new Set([
158460
+ "stream_delta",
158461
+ "update_device_status",
158462
+ "update_loop_status",
158463
+ "update_queue",
158464
+ "update_subagent_state"
158465
+ ]);
158466
+ COALESCABLE_STATUS_MESSAGE_TYPES = new Set([
158467
+ "update_device_status",
158468
+ "update_loop_status",
158469
+ "update_queue",
158470
+ "update_subagent_state"
158471
+ ]);
158472
+ });
158473
+
158433
158474
  // src/websocket/listener/stream-observers.ts
158434
158475
  function notifyStreamObservers(listener, message, runtimeScope) {
158435
158476
  if (!listener?.streamObservers?.size)
@@ -158463,6 +158504,106 @@ function notifyStreamObserversRuntimeStopped(listener) {
158463
158504
  listener.streamObservers.clear();
158464
158505
  }
158465
158506
 
158507
+ // src/websocket/listener/inbound-queue.ts
158508
+ function getInboundClientMessageId(incoming) {
158509
+ return getInboundClientMessageIds(incoming)[0];
158510
+ }
158511
+ function getInboundClientMessageIds(incoming) {
158512
+ return incoming.messages.flatMap((payload) => {
158513
+ if (!("content" in payload))
158514
+ return [];
158515
+ const clientMessageId = payload.client_message_id;
158516
+ return clientMessageId ? [clientMessageId] : [];
158517
+ });
158518
+ }
158519
+ function enqueueInboundUserMessage(runtime, incoming, actingUserId) {
158520
+ const firstUserPayload = incoming.messages.find((payload) => ("content" in payload));
158521
+ if (!firstUserPayload) {
158522
+ return false;
158523
+ }
158524
+ const enqueuedItem = runtime.queueRuntime.enqueue({
158525
+ kind: "message",
158526
+ source: "user",
158527
+ content: firstUserPayload.content,
158528
+ clientMessageId: firstUserPayload.client_message_id ?? `cm-submit-${crypto.randomUUID()}`,
158529
+ agentId: incoming.agentId,
158530
+ conversationId: incoming.conversationId || "default",
158531
+ ...incoming.noCoalesce ? { noCoalesce: true } : {},
158532
+ actingUserId
158533
+ });
158534
+ if (!enqueuedItem) {
158535
+ return false;
158536
+ }
158537
+ runtime.queuedMessagesByItemId.set(enqueuedItem.id, incoming);
158538
+ return true;
158539
+ }
158540
+
158541
+ // src/websocket/listener/turn-correlation.ts
158542
+ function takeDequeuedClientMessageIds(runtime, batchId) {
158543
+ const clientMessageIds = runtime.dequeuedClientMessageIdsByBatchId.get(batchId) ?? [];
158544
+ runtime.dequeuedClientMessageIdsByBatchId.delete(batchId);
158545
+ return clientMessageIds;
158546
+ }
158547
+ function buildTurnCorrelationSnapshot(listener, agentId, conversationId) {
158548
+ const correlations = listener.clientMessageIdsByRunIdByConversation?.get(getConversationRuntimeKey(agentId, conversationId));
158549
+ return correlations && correlations.size > 0 ? { client_message_ids_by_run_id: Object.fromEntries(correlations) } : {};
158550
+ }
158551
+ function createTurnCorrelation(runtime, message, batchId) {
158552
+ const clientMessageIds = new Set([
158553
+ ...getInboundClientMessageIds(message),
158554
+ ...takeDequeuedClientMessageIds(runtime, batchId)
158555
+ ]);
158556
+ let correlationsByConversation = runtime.listener.clientMessageIdsByRunIdByConversation;
158557
+ if (!correlationsByConversation) {
158558
+ correlationsByConversation = new Map;
158559
+ runtime.listener.clientMessageIdsByRunIdByConversation = correlationsByConversation;
158560
+ }
158561
+ let correlations = correlationsByConversation.get(runtime.key);
158562
+ if (!correlations) {
158563
+ correlations = new Map;
158564
+ correlationsByConversation.set(runtime.key, correlations);
158565
+ while (correlationsByConversation.size > MAX_RECENT_CONVERSATIONS) {
158566
+ let oldestEvictedRuntimeKey;
158567
+ for (const key of correlationsByConversation.keys()) {
158568
+ if (key !== runtime.key && !runtime.listener.conversationRuntimes.has(key)) {
158569
+ oldestEvictedRuntimeKey = key;
158570
+ break;
158571
+ }
158572
+ }
158573
+ if (!oldestEvictedRuntimeKey)
158574
+ break;
158575
+ correlationsByConversation.delete(oldestEvictedRuntimeKey);
158576
+ }
158577
+ }
158578
+ return {
158579
+ appendDequeuedBatch(nextBatchId) {
158580
+ for (const clientMessageId of takeDequeuedClientMessageIds(runtime, nextBatchId)) {
158581
+ clientMessageIds.add(clientMessageId);
158582
+ }
158583
+ },
158584
+ observeRun(runId) {
158585
+ if (clientMessageIds.size === 0)
158586
+ return;
158587
+ const merged = new Set([
158588
+ ...correlations.get(runId) ?? [],
158589
+ ...clientMessageIds
158590
+ ]);
158591
+ correlations.delete(runId);
158592
+ correlations.set(runId, [...merged]);
158593
+ while (correlations.size > MAX_RECENT_RUN_CORRELATIONS) {
158594
+ const oldestRunId = correlations.keys().next().value;
158595
+ if (!oldestRunId)
158596
+ break;
158597
+ correlations.delete(oldestRunId);
158598
+ }
158599
+ }
158600
+ };
158601
+ }
158602
+ var MAX_RECENT_RUN_CORRELATIONS = 32, MAX_RECENT_CONVERSATIONS = 256;
158603
+ var init_turn_correlation = __esm(() => {
158604
+ init_runtime();
158605
+ });
158606
+
158466
158607
  // src/websocket/listener/protocol-outbound.ts
158467
158608
  var exports_protocol_outbound = {};
158468
158609
  __export(exports_protocol_outbound, {
@@ -158651,6 +158792,7 @@ function buildLoopStatus(runtime, params) {
158651
158792
  return {
158652
158793
  status,
158653
158794
  active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : [],
158795
+ ...buildTurnCorrelationSnapshot(listener, scopedAgentId, scopedConversationId),
158654
158796
  executing_tool_call_ids: status === "EXECUTING_CLIENT_SIDE_TOOL" && conversationRuntime ? [...conversationRuntime.turnLifecycle.executingToolCallIds] : []
158655
158797
  };
158656
158798
  }
@@ -158670,15 +158812,6 @@ function buildQueueSnapshot(runtime, params) {
158670
158812
  enqueued_at: new Date(item.enqueuedAt).toISOString()
158671
158813
  }));
158672
158814
  }
158673
- function isStreamChannelMessage(type3) {
158674
- return STREAM_CHANNEL_MESSAGE_TYPES.has(type3);
158675
- }
158676
- function classifyOutboundFrame(message) {
158677
- if (message.type === "update_queue" && (message.removed?.length ?? 0) > 0) {
158678
- return "critical";
158679
- }
158680
- return COALESCABLE_STATUS_MESSAGE_TYPES.has(message.type) ? "status" : "critical";
158681
- }
158682
158815
  function emitProtocolV2Message(socket, runtime, message, scope, routing) {
158683
158816
  const listener = getListenerRuntime(runtime);
158684
158817
  const runtimeScope = resolveRuntimeScope(listener, getScopeForRuntime(runtime, scope));
@@ -159044,7 +159177,7 @@ function emitStreamDelta(socket, runtime, delta2, scope, subagentId) {
159044
159177
  };
159045
159178
  emitProtocolV2Message(socket, runtime, message, scope, TO_SUBSCRIBERS);
159046
159179
  }
159047
- var GIT_CONTEXT_CACHE_TTL_MS = 15000, MAX_GIT_CONTEXT_CACHE_ENTRIES = 64, FROZEN_SUPPORTED_COMMANDS, gitContextCache, STREAM_CHANNEL_MESSAGE_TYPES, COALESCABLE_STATUS_MESSAGE_TYPES;
159180
+ var GIT_CONTEXT_CACHE_TTL_MS = 15000, MAX_GIT_CONTEXT_CACHE_ENTRIES = 64, FROZEN_SUPPORTED_COMMANDS, gitContextCache;
159048
159181
  var init_protocol_outbound = __esm(() => {
159049
159182
  init_memory_filesystem2();
159050
159183
  init_subagent_state();
@@ -159065,23 +159198,12 @@ var init_protocol_outbound = __esm(() => {
159065
159198
  init_listener_constants();
159066
159199
  init_outbound_wire();
159067
159200
  init_permission_mode();
159201
+ init_protocol_outbound_routing();
159068
159202
  init_runtime();
159069
159203
  init_transport();
159204
+ init_turn_correlation();
159070
159205
  FROZEN_SUPPORTED_COMMANDS = [...SUPPORTED_REMOTE_COMMANDS];
159071
159206
  gitContextCache = new Map;
159072
- STREAM_CHANNEL_MESSAGE_TYPES = new Set([
159073
- "stream_delta",
159074
- "update_device_status",
159075
- "update_loop_status",
159076
- "update_queue",
159077
- "update_subagent_state"
159078
- ]);
159079
- COALESCABLE_STATUS_MESSAGE_TYPES = new Set([
159080
- "update_device_status",
159081
- "update_loop_status",
159082
- "update_queue",
159083
- "update_subagent_state"
159084
- ]);
159085
159207
  });
159086
159208
 
159087
159209
  // src/websocket/listener/cwd-change.ts
@@ -182861,7 +182983,71 @@ function rebuildInputWithFreshDenials(currentInput, serverApprovals, denialReaso
182861
182983
  function shouldAttemptApprovalRecovery(opts) {
182862
182984
  return opts.approvalPendingDetected && opts.retries < opts.maxRetries;
182863
182985
  }
182864
- var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENTS, CONVERSATION_BUSY_RUN_ID_PATTERN, EMPTY_RESPONSE_DETAIL_FRAGMENT = "empty content in", RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_RUN_ERROR_TYPES, NON_RETRYABLE_429_REASONS, NON_RETRYABLE_QUOTA_DETAIL_PATTERNS, NON_RETRYABLE_4XX_PATTERN, RETRYABLE_429_PATTERN, DEFAULT_TRANSIENT_RETRY_BASE_DELAY_MS = 1000, CLOUDFLARE_EDGE_52X_RETRY_BASE_DELAY_MS = 5000, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 1e4, EMPTY_RESPONSE_RETRY_BASE_DELAY_MS = 500, STALE_APPROVAL_RECOVERY_DENIAL_REASON = "The agent harness automatically closed this stale pending tool call to recover from a client/server state desync: the server was still waiting on a result for it, but the harness had no matching tool execution in flight and no result was ever recorded. It was not denied by the user or a permissions policy. Re-issue the tool call if you still need it.";
182986
+ function parseChatGPTUsageLimitDetail(detail) {
182987
+ if (typeof detail !== "string")
182988
+ return null;
182989
+ if (!detail.toLowerCase().includes(CHATGPT_USAGE_LIMIT_FRAGMENT)) {
182990
+ return null;
182991
+ }
182992
+ const fallback = { planType: null, resetsAt: null };
182993
+ const jsonStart = detail.indexOf("{");
182994
+ const jsonEnd = detail.lastIndexOf("}");
182995
+ if (jsonStart === -1 || jsonEnd <= jsonStart)
182996
+ return fallback;
182997
+ let parsed;
182998
+ try {
182999
+ parsed = JSON.parse(detail.slice(jsonStart, jsonEnd + 1));
183000
+ } catch {
183001
+ return fallback;
183002
+ }
183003
+ if (typeof parsed !== "object" || parsed === null)
183004
+ return fallback;
183005
+ const errorField = parsed.error;
183006
+ const errorObj = typeof errorField === "object" && errorField !== null ? errorField : parsed;
183007
+ const planType = typeof errorObj.plan_type === "string" && errorObj.plan_type.length > 0 ? errorObj.plan_type : null;
183008
+ let resetsAt = null;
183009
+ if (typeof errorObj.resets_at === "number" && errorObj.resets_at > 0) {
183010
+ resetsAt = errorObj.resets_at * 1000;
183011
+ } else if (typeof errorObj.resets_in_seconds === "number" && errorObj.resets_in_seconds > 0) {
183012
+ resetsAt = Date.now() + errorObj.resets_in_seconds * 1000;
183013
+ }
183014
+ return { planType, resetsAt };
183015
+ }
183016
+ function isChatGPTByokModel(model) {
183017
+ return model.providerType === CHATGPT_OAUTH_PROVIDER_TYPE2 && model.providerCategory === BYOK_PROVIDER_CATEGORY;
183018
+ }
183019
+ function selectChatGPTQuotaFailoverHandle(params) {
183020
+ const { currentHandle, models: models3, exhaustedProviders } = params;
183021
+ const random = params.random ?? Math.random;
183022
+ const slashIndex = currentHandle.indexOf("/");
183023
+ if (slashIndex <= 0)
183024
+ return null;
183025
+ const currentProvider = currentHandle.slice(0, slashIndex);
183026
+ const modelSuffix = currentHandle.slice(slashIndex + 1);
183027
+ if (!modelSuffix)
183028
+ return null;
183029
+ const currentEntry = models3.find((m2) => m2.handle === currentHandle);
183030
+ if (!currentEntry || !isChatGPTByokModel(currentEntry))
183031
+ return null;
183032
+ const candidates2 = models3.filter((m2) => {
183033
+ if (!isChatGPTByokModel(m2))
183034
+ return false;
183035
+ const idx = m2.handle.indexOf("/");
183036
+ if (idx <= 0)
183037
+ return false;
183038
+ const provider = m2.handle.slice(0, idx);
183039
+ if (provider === currentProvider)
183040
+ return false;
183041
+ if (exhaustedProviders.has(provider))
183042
+ return false;
183043
+ return m2.handle.slice(idx + 1) === modelSuffix;
183044
+ });
183045
+ if (candidates2.length === 0)
183046
+ return null;
183047
+ const index = Math.min(Math.floor(random() * candidates2.length), candidates2.length - 1);
183048
+ return candidates2[index]?.handle ?? null;
183049
+ }
183050
+ var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENTS, CONVERSATION_BUSY_RUN_ID_PATTERN, EMPTY_RESPONSE_DETAIL_FRAGMENT = "empty content in", RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_RUN_ERROR_TYPES, NON_RETRYABLE_429_REASONS, NON_RETRYABLE_QUOTA_DETAIL_PATTERNS, NON_RETRYABLE_4XX_PATTERN, RETRYABLE_429_PATTERN, DEFAULT_TRANSIENT_RETRY_BASE_DELAY_MS = 1000, CLOUDFLARE_EDGE_52X_RETRY_BASE_DELAY_MS = 5000, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 1e4, EMPTY_RESPONSE_RETRY_BASE_DELAY_MS = 500, STALE_APPROVAL_RECOVERY_DENIAL_REASON = "The agent harness automatically closed this stale pending tool call to recover from a client/server state desync: the server was still waiting on a result for it, but the harness had no matching tool execution in flight and no result was ever recorded. It was not denied by the user or a permissions policy. Re-issue the tool call if you still need it.", CHATGPT_USAGE_LIMIT_FRAGMENT = "usage_limit_reached", CHATGPT_OAUTH_PROVIDER_TYPE2 = "chatgpt_oauth", BYOK_PROVIDER_CATEGORY = "byok";
182865
183051
  var init_turn_recovery_policy = __esm(() => {
182866
183052
  init_error_formatter();
182867
183053
  init_zai_errors();
@@ -259720,9 +259906,11 @@ async function runLocalOAuthConnectFlow(provider, callbacks) {
259720
259906
  baseURL: callbacks.baseURL,
259721
259907
  timeout: callbacks.timeout
259722
259908
  });
259909
+ clearAvailableModelsCache();
259723
259910
  return { providerName: provider.providerName };
259724
259911
  }
259725
259912
  var init_connect_local_oauth = __esm(() => {
259913
+ init_available_models();
259726
259914
  init_pi_oauth();
259727
259915
  init_local_provider_auth_store();
259728
259916
  init_connect_oauth_core();
@@ -259775,7 +259963,11 @@ function listConnectProvidersForHelp(target2 = defaultProviderStorageTarget()) {
259775
259963
  return providers;
259776
259964
  }
259777
259965
  function listConnectProviderTokens(target2 = defaultProviderStorageTarget()) {
259778
- return [...listConnectProvidersForHelp(target2), "codex"];
259966
+ return [
259967
+ ...listConnectProvidersForHelp(target2),
259968
+ "codex",
259969
+ ...target2 === "local" ? ["grok"] : []
259970
+ ];
259779
259971
  }
259780
259972
  function isConnectOAuthProvider(provider) {
259781
259973
  return provider.byokProvider.isOAuth === true;
@@ -259811,6 +260003,7 @@ var init_connect_normalize = __esm(() => {
259811
260003
  };
259812
260004
  LOCAL_ALIAS_TO_CANONICAL = {
259813
260005
  gemini: "google",
260006
+ grok: "xai",
259814
260007
  "kimi-code": "kimi-coding",
259815
260008
  moonshot: "moonshotai",
259816
260009
  bedrock: "amazon-bedrock"
@@ -267863,6 +268056,17 @@ function buildQueuedTurnMessage(runtime, batch) {
267863
268056
  messages
267864
268057
  };
267865
268058
  }
268059
+ function getDequeuedClientMessageIds(runtime, batch) {
268060
+ const clientMessageIds = new Set;
268061
+ for (const item of batch.items) {
268062
+ const queuedMessage = runtime.queuedMessagesByItemId.get(item.id);
268063
+ const inboundClientMessageIds = queuedMessage ? getInboundClientMessageIds(queuedMessage) : [];
268064
+ for (const clientMessageId of inboundClientMessageIds.length > 0 ? inboundClientMessageIds : item.clientMessageId ? [item.clientMessageId] : []) {
268065
+ clientMessageIds.add(clientMessageId);
268066
+ }
268067
+ }
268068
+ return [...clientMessageIds];
268069
+ }
267866
268070
  function shouldQueueInboundMessage(parsed) {
267867
268071
  return parsed.messages.some((payload) => ("content" in payload));
267868
268072
  }
@@ -267933,10 +268137,14 @@ function consumeQueuedTurn(runtime) {
267933
268137
  if (!dequeuedBatch) {
267934
268138
  return null;
267935
268139
  }
268140
+ const clientMessageIds = getDequeuedClientMessageIds(runtime, dequeuedBatch);
267936
268141
  const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
267937
268142
  if (!queuedTurn) {
267938
268143
  return null;
267939
268144
  }
268145
+ if (clientMessageIds.length > 0) {
268146
+ runtime.dequeuedClientMessageIdsByBatchId.set(dequeuedBatch.batchId, clientMessageIds);
268147
+ }
267940
268148
  return {
267941
268149
  dequeuedBatch,
267942
268150
  queuedTurn
@@ -267949,6 +268157,17 @@ function computeListenerQueueBlockedReason(runtime) {
267949
268157
  });
267950
268158
  return getListenerBlockedReason(runtime.turnLifecycle.snapshot(), activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0);
267951
268159
  }
268160
+ function emitTurnBoundaryStatus(runtime, socket) {
268161
+ if (!isListenerTransportOpen(socket)) {
268162
+ return;
268163
+ }
268164
+ const scope = {
268165
+ agent_id: runtime.agentId,
268166
+ conversation_id: runtime.conversationId
268167
+ };
268168
+ emitQueueUpdate(socket, runtime, scope);
268169
+ emitLoopStatusUpdate(socket, runtime, scope);
268170
+ }
267952
268171
  async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
267953
268172
  if (runtime.queuePumpActive) {
267954
268173
  return;
@@ -267970,6 +268189,7 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
267970
268189
  }
267971
268190
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
267972
268191
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
268192
+ emitTurnBoundaryStatus(runtime, socket);
267973
268193
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
267974
268194
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
267975
268195
  runtime.listener.lastEmittedStatus = preTurnStatus;
@@ -267977,6 +268197,7 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
267977
268197
  }
267978
268198
  await processQueuedTurn(queuedTurn, dequeuedBatch);
267979
268199
  emitListenerStatus(runtime.listener, opts.onStatusChange, opts.connectionId);
268200
+ emitTurnBoundaryStatus(runtime, socket);
267980
268201
  evictConversationRuntimeIfIdle(runtime);
267981
268202
  }
267982
268203
  } finally {
@@ -272546,9 +272767,9 @@ import {
272546
272767
  import { join as join59 } from "node:path";
272547
272768
  function buildReflectionSubagentPrompt(input) {
272548
272769
  const lines = [];
272549
- lines.push('Review the conversation transcript payload and update memory files. The payload path is available as the `$TRANSCRIPT_PATH` env var — read it via Bash (e.g. `wc -c "$TRANSCRIPT_PATH"`). Note: `$TRANSCRIPT_PATH` only expands in shell commands; Edit `file_path` is literal and does NOT expand env vars.', "", 'The payload may be either a JSON message array for one conversation or a `multi_transcript_reflection_payload` manifest. If it is a manifest, read each `payload_path` listed in `transcripts` and synthesize across all conversations. Entries with `mode: "replay"` were already reflected before and are included intentionally for re-review/deduplication; do not ignore them just because they are replay slices.', "When reviewing multiple transcripts, prefer durable patterns and latest evidence across sessions. Resolve contradictions by updating stale memory at the source, deduplicate repeated facts, and avoid storing one-off task state.", "", "The primary agent's memory filesystem is available through the `$MEMORY_DIR` environment variable.", "Run git add or git commit commands only from $MEMORY_DIR; the harness handles integration after your commit. If these fail, stop reflecting and report the failure. All other git commands are out of your purview.", 'When using Edit, first resolve the absolute file path from `$MEMORY_DIR` with Bash (for example: `printf "%s/system/persona.md\\n" "$MEMORY_DIR"`) and use the printed path. Do not hardcode memory paths from the prompt.', "In-context memory (in the parent agent's system prompt) is stored in the `system/` folder and are rendered in <memory> tags below. Modification to files in `system/` will edit the parent agent's system prompt.", "Additional memory files (such as skills and external memory) may also be read and modified.", "");
272770
+ lines.push('Review the conversation transcript payload and update memory files. The payload path is available as the `$TRANSCRIPT_PATH` env var — read it via Bash (e.g. `wc -c "$TRANSCRIPT_PATH"`). Note: `$TRANSCRIPT_PATH` only expands in shell commands; Edit `file_path` is literal and does NOT expand env vars.', "", 'The payload may be either a JSON message array for one conversation or a `multi_transcript_reflection_payload` manifest. If it is a manifest, read each `payload_path` listed in `transcripts` and synthesize across all conversations. Entries with `mode: "replay"` were already reflected before and are included intentionally for re-review/deduplication; do not ignore them just because they are replay slices.', "When reviewing multiple transcripts, prefer recurring patterns and the latest evidence across sessions. Resolve contradictions by updating stale memory at the source, deduplicate repeated facts, and avoid storing one-off task state.", "", "The primary agent's memory filesystem is available through the `$MEMORY_DIR` environment variable.", "Run git add or git commit commands only from $MEMORY_DIR; the harness handles integration after your commit. If these fail, stop reflecting and report the failure. All other git commands are out of your purview.", 'When using Edit, first resolve the absolute file path from `$MEMORY_DIR` with Bash (for example: `printf "%s/system/persona.md\\n" "$MEMORY_DIR"`) and use the printed path. Do not hardcode memory paths from the prompt.', "In-context memory (in the parent agent's system prompt) is stored in the `system/` folder and are rendered in <memory> tags below. Modification to files in `system/` will edit the parent agent's system prompt.", "Additional memory files (such as skills and external memory) may also be read and modified.", "");
272550
272771
  if (input.instruction?.trim()) {
272551
- lines.push("Additional user-provided reflection instruction:", input.instruction.trim(), "", "Use this instruction to focus what you look for, but still only persist durable memory-worthy learnings and do not store transient task state.", "");
272772
+ lines.push("Additional user-provided reflection instruction:", input.instruction.trim(), "", "Use this instruction to focus what you look for, but still only persist memory-worthy learnings and do not store transient task state.", "");
272552
272773
  }
272553
272774
  if (input.parentMemory) {
272554
272775
  lines.push(input.parentMemory);
@@ -272566,7 +272787,7 @@ function buildReflectionSelectorPrompt(options3) {
272566
272787
  if (options3?.instruction?.trim()) {
272567
272788
  lines.push("Additional user-provided reflection instruction:", options3.instruction.trim(), "", "Prefer transcript candidates that help satisfy this instruction, while still avoiding transient or low-signal conversations.", "");
272568
272789
  }
272569
- lines.push("If the candidates payload includes `user_instruction`, use it as the requested focus for selection.", "", "Select up to `max_selected` conversations. Prefer candidates likely to contain durable memory updates: explicit user corrections, repeated preferences, coding/review/commit style preferences, repo or workflow gotchas, durable facts about people/projects, contradictions with current memory, or repeated agent failures.", "Avoid one-off debugging, transient task status, duplicated/redundant candidates, and conversations already fully reflected unless they are useful for deduplication or contradiction resolution.", "Treat summaries/descriptions as weak internal metadata, not confirmed facts. The final reflection pass will verify against the actual transcript before writing memory.", "", "Return strict JSON as your final response with this shape:", '{"selected_conversations":[{"conversation_id":"conv-...","reason":"durable reason for selecting this transcript","priority":"high"}]}', 'Use priority values `high`, `medium`, or `low`. If nothing looks memory-worthy, write `{"selected_conversations":[]}`.');
272790
+ lines.push("If the candidates payload includes `user_instruction`, use it as the requested focus for selection.", "", "Select up to `max_selected` conversations. Prefer candidates likely to contain useful memory updates: explicit user corrections, repeated preferences, coding/review/commit style preferences, repo or workflow gotchas, facts about people/projects that will matter later, contradictions with current memory, or repeated agent failures.", "Avoid one-off debugging, transient task status, duplicated/redundant candidates, and conversations already fully reflected unless they are useful for deduplication or contradiction resolution.", "Treat summaries/descriptions as weak internal metadata, not confirmed facts. The final reflection pass will verify against the actual transcript before writing memory.", "", "Return strict JSON as your final response with this shape:", '{"selected_conversations":[{"conversation_id":"conv-...","reason":"reason for selecting this transcript","priority":"high"}]}', 'Use priority values `high`, `medium`, or `low`. If nothing looks memory-worthy, write `{"selected_conversations":[]}`.');
272570
272791
  return lines.join(`
272571
272792
  `);
272572
272793
  }
@@ -273351,7 +273572,7 @@ async function buildReflectionAutoPayload(options3) {
273351
273572
  created_at: new Date().toISOString(),
273352
273573
  max_selected: maxSelected,
273353
273574
  user_instruction: instruction?.trim() || undefined,
273354
- instructions: "Choose conversations likely to contain durable memory updates. Prefer explicit corrections, repeated preferences, project conventions, and contradictions; avoid one-off debugging and transient task state.",
273575
+ instructions: "Choose conversations likely to contain useful memory updates. Prefer explicit corrections, repeated preferences, project conventions, and contradictions; avoid one-off debugging and transient task state.",
273355
273576
  candidates: sortedCandidates
273356
273577
  };
273357
273578
  const candidatesPath = buildPayloadPath(payloadRoot, "candidates");
@@ -273661,7 +273882,7 @@ var init_reflection_transcript = __esm(() => {
273661
273882
  REFLECTION_AUTO_QUERIES = [
273662
273883
  {
273663
273884
  id: "user-corrections",
273664
- query: "user corrections and preferences repeated mistakes durable feedback"
273885
+ query: "user corrections and preferences repeated mistakes recurring feedback"
273665
273886
  },
273666
273887
  {
273667
273888
  id: "coding-style",
@@ -273669,11 +273890,11 @@ var init_reflection_transcript = __esm(() => {
273669
273890
  },
273670
273891
  {
273671
273892
  id: "collaboration",
273672
- query: "collaboration communication style team preferences durable workflow"
273893
+ query: "collaboration communication style team preferences recurring workflow"
273673
273894
  },
273674
273895
  {
273675
273896
  id: "repo-gotchas",
273676
- query: "repo conventions project gotchas durable implementation details"
273897
+ query: "repo conventions project gotchas important implementation details"
273677
273898
  },
273678
273899
  {
273679
273900
  id: "long-term-facts",
@@ -273937,7 +274158,7 @@ function buildTargetInstruction(target2) {
273937
274158
  "(--- ... ---) at the top intact and edit only the body below it.",
273938
274159
  "",
273939
274160
  "If it does not exist, create it ONLY when the new experience yields",
273940
- "durable, forward-looking guidance worth recording, and begin the file with",
274161
+ "forward-looking guidance worth recording, and begin the file with",
273941
274162
  "a YAML frontmatter block (a --- ... --- header with a short `description:`).",
273942
274163
  "If there is nothing substantive to record yet, do NOT create the file —",
273943
274164
  "leave it absent rather than writing a placeholder that says nothing was",
@@ -274022,7 +274243,7 @@ var init_dream_targets = __esm(() => {
274022
274243
  };
274023
274244
  AGENTS_MD_GUIDANCE = [
274024
274245
  "This file is an AGENTS.md — a README *for coding agents* (the agents.md",
274025
- "open standard). Maintain it as durable, repo-level guidance an agent needs:",
274246
+ "open standard). Maintain it as repo-level guidance an agent needs over time:",
274026
274247
  "build/test/setup COMMANDS (put these early), code-style conventions,",
274027
274248
  "testing instructions, security considerations, and commit/PR rules. Use",
274028
274249
  "plain Markdown with any headings. Do NOT duplicate human-README content",
@@ -274035,7 +274256,7 @@ var init_dream_targets = __esm(() => {
274035
274256
  ].join(`
274036
274257
  `);
274037
274258
  GENERIC_GUIDANCE = [
274038
- "Maintain this markdown document as durable, forward-looking guidance",
274259
+ "Maintain this markdown document as forward-looking guidance",
274039
274260
  "distilled from the conversation(s). Prefer editing/merging existing",
274040
274261
  "sections over appending; remove content that new evidence contradicts."
274041
274262
  ].join(`
@@ -274841,7 +275062,7 @@ function getReflectionCompletionMessage(integration) {
274841
275062
  case "merged":
274842
275063
  return;
274843
275064
  case "no_changes":
274844
- return ({ action: action3 }) => `${action3}; no durable memory changes were needed.`;
275065
+ return ({ action: action3 }) => `${action3}; no memory changes were needed.`;
274845
275066
  case "parent_dirty":
274846
275067
  return "Tried to reflect, but parent memory had uncommitted changes; will retry later.";
274847
275068
  case "merge_conflict":
@@ -457337,9 +457558,13 @@ async function isRetriablePostStopError(stopReason, lastRunId, fallbackDetail) {
457337
457558
  async function drainRecoveryStreamWithEmission(recoveryStream, socket, runtime, params) {
457338
457559
  let recoveryRunIdSent = false;
457339
457560
  return drainStreamWithResume(recoveryStream, createBuffers(params.agentId || ""), () => {}, params.turnLease.signal, undefined, ({ chunk: chunk2, shouldOutput, errorInfo }) => {
457561
+ if (!runtime.turnLifecycle.isCurrent(params.turnLease)) {
457562
+ return;
457563
+ }
457340
457564
  const maybeRunId = chunk2.run_id;
457341
457565
  if (typeof maybeRunId === "string") {
457342
457566
  runtime.turnLifecycle.setRunId(params.turnLease, maybeRunId);
457567
+ params.turnCorrelation?.observeRun(maybeRunId);
457343
457568
  if (!recoveryRunIdSent) {
457344
457569
  recoveryRunIdSent = true;
457345
457570
  emitLoopStatusUpdate(socket, runtime, {
@@ -457686,15 +457911,23 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
457686
457911
  ]);
457687
457912
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
457688
457913
  let continuationActingUserId;
457914
+ let continuationCorrelation;
457689
457915
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
457690
457916
  if (consumedQueuedTurn) {
457691
457917
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
457692
457918
  continuationBatchId = dequeuedBatch.batchId;
457693
457919
  continuationActingUserId = queuedTurn.actingUserId;
457694
457920
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
457921
+ continuationCorrelation = createTurnCorrelation(runtime, {
457922
+ type: "message",
457923
+ agentId: recovered.agentId,
457924
+ conversationId: recovered.conversationId,
457925
+ messages: continuationInput.messages
457926
+ }, continuationBatchId);
457695
457927
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
457696
457928
  }
457697
457929
  if (!runtime.turnLifecycle.isCurrent(recoveryLease)) {
457930
+ runtime.dequeuedClientMessageIdsByBatchId.delete(continuationBatchId);
457698
457931
  return true;
457699
457932
  }
457700
457933
  await processTurn({
@@ -457703,7 +457936,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
457703
457936
  conversationId: recovered.conversationId,
457704
457937
  ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
457705
457938
  messages: continuationInput.messages
457706
- }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId, recoveryLease);
457939
+ }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId, recoveryLease, continuationCorrelation);
457707
457940
  if (runtime.turnLifecycle.isCurrent(recoveryLease)) {
457708
457941
  throw new Error("Recovered continuation returned without finalizing");
457709
457942
  }
@@ -457756,6 +457989,7 @@ var init_recovery = __esm(async () => {
457756
457989
  init_recoverable_notices();
457757
457990
  init_runtime();
457758
457991
  init_secrets_sync();
457992
+ init_turn_correlation();
457759
457993
  init_turn_input_state();
457760
457994
  init_turn_status();
457761
457995
  init_turn_terminal();
@@ -457811,6 +458045,96 @@ var init_approval_recovery = __esm(() => {
457811
458045
  init_turn_recovery_policy();
457812
458046
  });
457813
458047
 
458048
+ // src/agent/chatgpt-plan-rotation.ts
458049
+ function providerFromHandle(handle2) {
458050
+ const idx = handle2.indexOf("/");
458051
+ return idx > 0 ? handle2.slice(0, idx) : null;
458052
+ }
458053
+ function isChatGPTByokHandleInModels(handle2, models3) {
458054
+ const entry = models3.find((m4) => m4.handle === handle2);
458055
+ return entry?.providerType === "chatgpt_oauth" && entry?.providerCategory === "byok";
458056
+ }
458057
+ async function resolveAgentModelHandle2(agentId) {
458058
+ try {
458059
+ const agent2 = await getBackend().retrieveAgent(agentId);
458060
+ const record5 = agent2;
458061
+ if (typeof record5.model === "string" && record5.model.length > 0) {
458062
+ return record5.model;
458063
+ }
458064
+ return resolveModelHandleFromLlmConfig(record5.llm_config);
458065
+ } catch {
458066
+ return null;
458067
+ }
458068
+ }
458069
+ async function rotateChatGPTPlanOnQuotaLimit(params) {
458070
+ const { agentId, detail } = params;
458071
+ const parsedDetail = parseChatGPTUsageLimitDetail(detail);
458072
+ if (!parsedDetail)
458073
+ return null;
458074
+ let models3 = getCachedAvailableModels();
458075
+ if (!models3) {
458076
+ try {
458077
+ await getAvailableModelHandles();
458078
+ models3 = getCachedAvailableModels();
458079
+ } catch {
458080
+ return null;
458081
+ }
458082
+ }
458083
+ if (!models3)
458084
+ return null;
458085
+ let currentHandle = params.currentHandle;
458086
+ if (!currentHandle || !isChatGPTByokHandleInModels(currentHandle, models3)) {
458087
+ currentHandle = await resolveAgentModelHandle2(agentId);
458088
+ }
458089
+ if (!currentHandle || !isChatGPTByokHandleInModels(currentHandle, models3)) {
458090
+ return null;
458091
+ }
458092
+ const fromProvider = providerFromHandle(currentHandle);
458093
+ if (!fromProvider)
458094
+ return null;
458095
+ exhaustedProviders.add(fromProvider);
458096
+ const toHandle = selectChatGPTQuotaFailoverHandle({
458097
+ currentHandle,
458098
+ models: models3,
458099
+ exhaustedProviders
458100
+ });
458101
+ if (!toHandle)
458102
+ return null;
458103
+ const toProvider = providerFromHandle(toHandle);
458104
+ if (!toProvider)
458105
+ return null;
458106
+ try {
458107
+ await updateAgentLLMConfig(agentId, toHandle, {
458108
+ provider_type: "chatgpt_oauth"
458109
+ });
458110
+ } catch {
458111
+ return null;
458112
+ }
458113
+ return {
458114
+ fromProvider,
458115
+ toProvider,
458116
+ toHandle,
458117
+ resetsAt: parsedDetail.resetsAt
458118
+ };
458119
+ }
458120
+ function formatPlanRotationNotice(params) {
458121
+ const { fromProvider, toProvider, resetsAt } = params;
458122
+ const resetSuffix = resetsAt !== null ? ` (resets ${new Date(resetsAt).toLocaleTimeString([], {
458123
+ hour: "numeric",
458124
+ minute: "2-digit"
458125
+ })})` : "";
458126
+ return `${fromProvider} hit its usage limit${resetSuffix} — switched to ${toProvider}`;
458127
+ }
458128
+ var CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN = 3, exhaustedProviders;
458129
+ var init_chatgpt_plan_rotation = __esm(() => {
458130
+ init_available_models();
458131
+ init_model_handles();
458132
+ init_modify();
458133
+ init_turn_recovery_policy();
458134
+ init_backend2();
458135
+ exhaustedProviders = new Set;
458136
+ });
458137
+
457814
458138
  // src/websocket/listener/skill-injection.ts
457815
458139
  function injectQueuedSkillContent(messages, context3) {
457816
458140
  const skillContents = consumeQueuedSkillContent();
@@ -458055,9 +458379,11 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
458055
458379
  }
458056
458380
  ]);
458057
458381
  let continuationActingUserId;
458382
+ let recoveryTurnCorrelation;
458058
458383
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
458059
458384
  if (consumedQueuedTurn) {
458060
458385
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
458386
+ recoveryTurnCorrelation = createTurnCorrelation(runtime, queuedTurn, dequeuedBatch.batchId);
458061
458387
  continuationActingUserId = queuedTurn.actingUserId;
458062
458388
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
458063
458389
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
@@ -458088,7 +458414,8 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
458088
458414
  const drainResult = await drainRecoveryStream(recoveryStream, socket, runtime, {
458089
458415
  agentId: runtime.agentId ?? undefined,
458090
458416
  conversationId: recoveryConversationId,
458091
- turnLease
458417
+ turnLease,
458418
+ turnCorrelation: recoveryTurnCorrelation
458092
458419
  });
458093
458420
  assertCurrentTurnLease();
458094
458421
  if (drainResult.stopReason === "error") {
@@ -458383,6 +458710,7 @@ var init_send = __esm(async () => {
458383
458710
  init_permission_mode();
458384
458711
  init_protocol_outbound();
458385
458712
  init_skill_injection();
458713
+ init_turn_correlation();
458386
458714
  init_turn_input_state();
458387
458715
  init_turn_status();
458388
458716
  await __promiseAll([
@@ -458398,6 +458726,20 @@ var init_send = __esm(async () => {
458398
458726
  });
458399
458727
 
458400
458728
  // src/websocket/listener/teleport.ts
458729
+ function buildTeleportContinuationMessages(params) {
458730
+ return [
458731
+ {
458732
+ type: "approval",
458733
+ approvals: params.approvals,
458734
+ otid: params.teleportId
458735
+ },
458736
+ {
458737
+ role: "system",
458738
+ content: "<system-reminder>Teleportation to this environment is complete. Continue the existing task from this environment now.</system-reminder>",
458739
+ otid: `${params.teleportId}:continue`
458740
+ }
458741
+ ];
458742
+ }
458401
458743
  function getPendingTeleports(runtime) {
458402
458744
  runtime.pendingTeleports ??= new Map;
458403
458745
  return runtime.pendingTeleports;
@@ -460708,6 +461050,7 @@ async function handleApprovalStop(params) {
460708
461050
  turnInput,
460709
461051
  turnToolContextId,
460710
461052
  turnLease,
461053
+ turnCorrelation,
460711
461054
  processOwnedTurn = false,
460712
461055
  buildSendOptions,
460713
461056
  dependencies: dependencies4
@@ -461010,6 +461353,7 @@ async function handleApprovalStop(params) {
461010
461353
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
461011
461354
  if (consumedQueuedTurn) {
461012
461355
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
461356
+ turnCorrelation?.appendDequeuedBatch(dequeuedBatch.batchId);
461013
461357
  continuationBatchId = dequeuedBatch.batchId;
461014
461358
  continuationActingUserId = queuedTurn.actingUserId;
461015
461359
  nextTurnInput = appendQueuedTurnToInput(nextTurnInput, queuedTurn);
@@ -462795,23 +463139,24 @@ var init_turn_setup = __esm(async () => {
462795
463139
  });
462796
463140
 
462797
463141
  // src/websocket/listener/turn.ts
462798
- async function handleIncomingMessage(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId = `batch-direct-${crypto.randomUUID()}`, existingTurnLease) {
463142
+ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId = `batch-direct-${crypto.randomUUID()}`, existingTurnLease, existingTurnCorrelation) {
462799
463143
  notifyTurnStarted(msg);
462800
463144
  try {
462801
- await handleIncomingMessageInner(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId, existingTurnLease);
463145
+ await handleIncomingMessageInner(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId, existingTurnLease, existingTurnCorrelation);
462802
463146
  } finally {
462803
463147
  notifyTurnFinished(msg);
462804
463148
  finishPendingTeleport(runtime);
462805
463149
  }
462806
463150
  }
462807
- async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId = `batch-direct-${crypto.randomUUID()}`, existingTurnLease) {
463151
+ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId = `batch-direct-${crypto.randomUUID()}`, existingTurnLease, existingTurnCorrelation) {
462808
463152
  const agentId = msg.agentId;
462809
463153
  const requestedConversationId = msg.conversationId || undefined;
462810
463154
  const conversationId = requestedConversationId ?? "default";
462811
463155
  const normalizedAgentId = normalizeCwdAgentId(agentId);
462812
463156
  const turnWorkingDirectory = getConversationWorkingDirectory(runtime.listener, normalizedAgentId, conversationId);
462813
463157
  const turnPermissionModeState = getOrCreateConversationPermissionModeStateRef(runtime.listener, normalizedAgentId, conversationId);
462814
- let postStopApprovalRecoveryRetries = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, lastApprovalContinuationAccepted = false, activeDequeuedBatchId = dequeuedBatchId;
463158
+ let postStopApprovalRecoveryRetries = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, chatgptPlanSwaps = 0, lastApprovalContinuationAccepted = false, activeDequeuedBatchId = dequeuedBatchId;
463159
+ const turnCorrelation = existingTurnCorrelation ?? createTurnCorrelation(runtime, msg, activeDequeuedBatchId);
462815
463160
  const msgRunIds = [];
462816
463161
  let lastExecutionResults = null;
462817
463162
  let lastExecutingToolCallIds = [];
@@ -462980,6 +463325,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
462980
463325
  if (typeof maybeRunId === "string") {
462981
463326
  runId = maybeRunId;
462982
463327
  runtime.turnLifecycle.setRunId(turnLease, maybeRunId);
463328
+ turnCorrelation.observeRun(maybeRunId);
462983
463329
  if (!runIdSent) {
462984
463330
  runIdSent = true;
462985
463331
  msgRunIds.push(maybeRunId);
@@ -463181,6 +463527,51 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
463181
463527
  turnToolContextId = getStreamToolContextId(stream12);
463182
463528
  continue;
463183
463529
  }
463530
+ if (chatgptPlanSwaps < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
463531
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
463532
+ agentId,
463533
+ currentHandle: null,
463534
+ detail: errorDetail2
463535
+ });
463536
+ if (rotation) {
463537
+ chatgptPlanSwaps += 1;
463538
+ emitRecoverableRetryNotice(socket, runtime, {
463539
+ kind: "transient_provider_retry",
463540
+ message: formatPlanRotationNotice(rotation),
463541
+ reason: "llm_api_error",
463542
+ attempt: chatgptPlanSwaps,
463543
+ maxAttempts: CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN,
463544
+ delayMs: 0,
463545
+ runId: lastRunId || undefined,
463546
+ agentId,
463547
+ conversationId
463548
+ });
463549
+ if (turnAbortSignal.aborted) {
463550
+ throw new Error("Cancelled by user");
463551
+ }
463552
+ turnInput = refreshTurnInputOtidsForNewRequest(turnInput);
463553
+ setTurnLoopStatus(runtime, turnLease, "SENDING_API_REQUEST", {
463554
+ agent_id: agentId,
463555
+ conversation_id: conversationId
463556
+ });
463557
+ const retryInputWithSkillContent = injectQueuedSkillContent(turnInput.messages, { socket, runtime, agentId, conversationId });
463558
+ const retrySendResult = await turnInputSender.send(retryInputWithSkillContent);
463559
+ turnInput = updateTurnInputMessagesPreservingOtids(turnInput, retryInputWithSkillContent);
463560
+ const retryStream = turnInputSender.accept(retrySendResult);
463561
+ if (!retryStream) {
463562
+ return;
463563
+ }
463564
+ stream12 = retryStream;
463565
+ pendingNormalizationInterruptedToolCallIds = [];
463566
+ markAwaitingAcceptedApprovalContinuationRunId(runtime, turnLease, turnInput.messages);
463567
+ setTurnLoopStatus(runtime, turnLease, "PROCESSING_API_RESPONSE", {
463568
+ agent_id: agentId,
463569
+ conversation_id: conversationId
463570
+ });
463571
+ turnToolContextId = getStreamToolContextId(stream12);
463572
+ continue;
463573
+ }
463574
+ }
463184
463575
  const retriable = await isRetriablePostStopError(stopReason || "error", lastRunId, errorDetail2);
463185
463576
  if (finishIfInterrupted(lastRunId || runtime.activeRunId)) {
463186
463577
  break;
@@ -463287,6 +463678,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
463287
463678
  pendingNormalizationInterruptedToolCallIds,
463288
463679
  turnToolContextId,
463289
463680
  turnLease,
463681
+ turnCorrelation,
463290
463682
  processOwnedTurn: msg.processOwnedTurn === true,
463291
463683
  buildSendOptions
463292
463684
  });
@@ -463464,6 +463856,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
463464
463856
  }
463465
463857
  var init_turn = __esm(async () => {
463466
463858
  init_approval_recovery();
463859
+ init_chatgpt_plan_rotation();
463467
463860
  init_check_approval();
463468
463861
  init_turn_recovery_policy();
463469
463862
  init_backend2();
@@ -463483,6 +463876,7 @@ var init_turn = __esm(async () => {
463483
463876
  init_teleport();
463484
463877
  init_turn_cleanup();
463485
463878
  init_turn_context();
463879
+ init_turn_correlation();
463486
463880
  init_turn_input_state();
463487
463881
  init_turn_observers();
463488
463882
  init_turn_status();
@@ -464079,6 +464473,7 @@ function createConnectionTurnProcessor(runtime) {
464079
464473
  }
464080
464474
  const connection = runtime.connections.get(queuedTurn.connectionId);
464081
464475
  if (!connection || connection.cancellation.signal.aborted) {
464476
+ scopedRuntime.dequeuedClientMessageIdsByBatchId.delete(dequeuedBatch.batchId);
464082
464477
  return;
464083
464478
  }
464084
464479
  await handleIncomingMessage(queuedTurn, getOrCreateProcessTransport(runtime), scopedRuntime, connection.options.onStatusChange, connection.id, dequeuedBatch.batchId);
@@ -467726,32 +468121,6 @@ var init_git_branches = __esm(() => {
467726
468121
  init_protocol_outbound();
467727
468122
  });
467728
468123
 
467729
- // src/websocket/listener/inbound-queue.ts
467730
- function getInboundClientMessageId(incoming) {
467731
- return incoming.messages.find((payload) => ("content" in payload))?.client_message_id;
467732
- }
467733
- function enqueueInboundUserMessage(runtime, incoming, actingUserId) {
467734
- const firstUserPayload = incoming.messages.find((payload) => ("content" in payload));
467735
- if (!firstUserPayload) {
467736
- return false;
467737
- }
467738
- const enqueuedItem = runtime.queueRuntime.enqueue({
467739
- kind: "message",
467740
- source: "user",
467741
- content: firstUserPayload.content,
467742
- clientMessageId: firstUserPayload.client_message_id ?? `cm-submit-${crypto.randomUUID()}`,
467743
- agentId: incoming.agentId,
467744
- conversationId: incoming.conversationId || "default",
467745
- ...incoming.noCoalesce ? { noCoalesce: true } : {},
467746
- actingUserId
467747
- });
467748
- if (!enqueuedItem) {
467749
- return false;
467750
- }
467751
- runtime.queuedMessagesByItemId.set(enqueuedItem.id, incoming);
467752
- return true;
467753
- }
467754
-
467755
468124
  // src/websocket/listener/inbound-dispatch.ts
467756
468125
  function getAcceptedInputDisposition(runtime, clientMessageId) {
467757
468126
  if (!clientMessageId)
@@ -468497,13 +468866,10 @@ function createListenerMessageHandler(params) {
468497
468866
  connectionId,
468498
468867
  agentId: parsed.runtime.agent_id,
468499
468868
  conversationId: parsed.runtime.conversation_id,
468500
- messages: [
468501
- {
468502
- type: "approval",
468503
- approvals,
468504
- otid: teleportId
468505
- }
468506
- ]
468869
+ messages: buildTeleportContinuationMessages({
468870
+ teleportId,
468871
+ approvals
468872
+ })
468507
468873
  }, socket, scopedRuntime2, opts.onStatusChange, connectionId);
468508
468874
  });
468509
468875
  return;
@@ -468673,12 +469039,10 @@ function createListenerMessageHandler(params) {
468673
469039
  success: removed !== null,
468674
469040
  item_id: parsed.item_id
468675
469041
  }, "remove_queue_item_response", "remove_queue_item");
468676
- if (removed !== null) {
468677
- emitQueueUpdateIfOpen(runtime, {
468678
- agent_id: parsed.runtime.agent_id,
468679
- conversation_id: parsed.runtime.conversation_id
468680
- });
468681
- }
469042
+ emitQueueUpdateIfOpen(runtime, {
469043
+ agent_id: parsed.runtime.agent_id,
469044
+ conversation_id: parsed.runtime.conversation_id
469045
+ });
468682
469046
  return;
468683
469047
  }
468684
469048
  if (fileCommandSession.handle(parsed)) {
@@ -485848,6 +486212,9 @@ function isAssistantMessage2(message) {
485848
486212
  function isUserMessage(message) {
485849
486213
  return message.message_type === "user_message";
485850
486214
  }
486215
+ function isTaskNotificationMessage(message) {
486216
+ return extractMessageText(message).trimStart().startsWith("<task-notification>");
486217
+ }
485851
486218
  function messageRunId(message) {
485852
486219
  if (!message)
485853
486220
  return null;
@@ -485884,8 +486251,9 @@ async function waitForEnvironmentAssistantMessage(params) {
485884
486251
  return sequenceId !== null && sequenceId > anchorSequenceId;
485885
486252
  });
485886
486253
  const nextUserSequenceId = newerMessages.reduce((closest, message) => {
485887
- if (!isUserMessage(message))
486254
+ if (!isUserMessage(message) || isTaskNotificationMessage(message)) {
485888
486255
  return closest;
486256
+ }
485889
486257
  const sequenceId = messageSequenceId(message);
485890
486258
  if (sequenceId === null)
485891
486259
  return closest;
@@ -497557,7 +497925,7 @@ var init_mcp_client = __esm(() => {
497557
497925
  init_streamableHttp();
497558
497926
  DEFAULT_CLIENT_INFO = {
497559
497927
  name: "letta-code",
497560
- version: "0.30.26"
497928
+ version: "0.30.28"
497561
497929
  };
497562
497930
  });
497563
497931
 
@@ -499949,6 +500317,7 @@ ${loadedContents.join(`
499949
500317
  let llmApiErrorRetries = 0;
499950
500318
  let emptyResponseRetries = 0;
499951
500319
  let conversationBusyRetries = 0;
500320
+ let chatgptPlanSwaps = 0;
499952
500321
  markMilestone("HEADLESS_FIRST_STREAM_START");
499953
500322
  measureSinceMilestone("headless-setup-total", "HEADLESS_CLIENT_READY");
499954
500323
  const checkMaxTurns = async () => {
@@ -500215,6 +500584,7 @@ ${loadedContents.join(`
500215
500584
  llmApiErrorRetries = 0;
500216
500585
  emptyResponseRetries = 0;
500217
500586
  conversationBusyRetries = 0;
500587
+ chatgptPlanSwaps = 0;
500218
500588
  const continueMessage = await emitHeadlessTurnEnd({
500219
500589
  agent: agent2,
500220
500590
  conversationId,
@@ -500315,6 +500685,34 @@ ${loadedContents.join(`
500315
500685
  }
500316
500686
  }
500317
500687
  const detailFromRun = await fetchRunErrorDetail(lastRunId);
500688
+ if (chatgptPlanSwaps < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
500689
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
500690
+ agentId: agent2.id,
500691
+ currentHandle: null,
500692
+ detail: detailFromRun ?? latestErrorText
500693
+ });
500694
+ if (rotation) {
500695
+ chatgptPlanSwaps += 1;
500696
+ const rotationMessage = formatPlanRotationNotice(rotation);
500697
+ if (outputFormat === "stream-json") {
500698
+ const retryMsg = {
500699
+ type: "retry",
500700
+ reason: "llm_api_error",
500701
+ attempt: chatgptPlanSwaps,
500702
+ max_attempts: CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN,
500703
+ delay_ms: 0,
500704
+ run_id: lastRunId ?? undefined,
500705
+ session_id: sessionId,
500706
+ uuid: `retry-${lastRunId || randomUUID35()}`
500707
+ };
500708
+ writeWireMessage(retryMsg);
500709
+ } else {
500710
+ console.error(rotationMessage);
500711
+ }
500712
+ currentInput = refreshInputOtidsForNewRequest(currentInput);
500713
+ continue;
500714
+ }
500715
+ }
500318
500716
  if (stopReason === "llm_api_error") {
500319
500717
  if (llmApiErrorRetries < LLM_API_ERROR_MAX_RETRIES2) {
500320
500718
  const attempt2 = llmApiErrorRetries + 1;
@@ -501686,6 +502084,7 @@ var init_headless = __esm(async () => {
501686
502084
  init_subagent_stdout_failure();
501687
502085
  init_approval_recovery();
501688
502086
  init_bootstrap_handler();
502087
+ init_chatgpt_plan_rotation();
501689
502088
  init_client_skills();
501690
502089
  init_context();
501691
502090
  init_create5();
@@ -547536,6 +547935,7 @@ function useConversationLoop(ctx) {
547536
547935
  buffersRef,
547537
547936
  clearApprovalToolContext,
547538
547937
  closeTrajectorySegment,
547938
+ chatgptPlanSwapsRef,
547539
547939
  consumeQueuedMessages,
547540
547940
  queueModeRef,
547541
547941
  contextTrackerRef,
@@ -547767,6 +548167,7 @@ function useConversationLoop(ctx) {
547767
548167
  emptyResponseRetriesRef.current = 0;
547768
548168
  conversationBusyRetriesRef.current = 0;
547769
548169
  quotaAutoSwapAttemptedRef.current = false;
548170
+ chatgptPlanSwapsRef.current = 0;
547770
548171
  }
547771
548172
  let currentRunId;
547772
548173
  let preserveTranscriptStartForApproval = false;
@@ -548731,6 +549132,27 @@ ${feedback}
548731
549132
  buffersRef.current.interrupted = false;
548732
549133
  continue;
548733
549134
  }
549135
+ if (chatgptPlanSwapsRef.current < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
549136
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
549137
+ agentId: agentIdRef.current,
549138
+ currentHandle: currentModelId,
549139
+ detail: detailFromRun ?? fallbackError
549140
+ });
549141
+ if (rotation) {
549142
+ chatgptPlanSwapsRef.current += 1;
549143
+ const statusId = uid("status");
549144
+ buffersRef.current.byId.set(statusId, {
549145
+ kind: "status",
549146
+ id: statusId,
549147
+ lines: [formatPlanRotationNotice(rotation)]
549148
+ });
549149
+ buffersRef.current.order.push(statusId);
549150
+ refreshDerived();
549151
+ currentInput = refreshInputOtidsForNewRequest(currentInput);
549152
+ buffersRef.current.interrupted = false;
549153
+ continue;
549154
+ }
549155
+ }
548734
549156
  const autoSwapOnQuotaLimitEnabled = settingsManager.getSetting("autoSwapOnQuotaLimit") !== false;
548735
549157
  const supportsHostedAutoQuotaFallback = !getBackend().capabilities.localModelCatalog;
548736
549158
  const isQuotaLimit = isQuotaLimitErrorDetail(detailFromRun ?? fallbackError);
@@ -549012,6 +549434,7 @@ var init_use_conversation_loop = __esm(async () => {
549012
549434
  init_error();
549013
549435
  init_approval_recovery();
549014
549436
  init_available_models();
549437
+ init_chatgpt_plan_rotation();
549015
549438
  init_check_approval();
549016
549439
  init_model();
549017
549440
  init_prompt_assets();
@@ -557857,6 +558280,7 @@ function App2({
557857
558280
  const llmApiErrorRetriesRef = import_react121.useRef(0);
557858
558281
  const quotaAutoSwapAttemptedRef = import_react121.useRef(false);
557859
558282
  const emptyResponseRetriesRef = import_react121.useRef(0);
558283
+ const chatgptPlanSwapsRef = import_react121.useRef(0);
557860
558284
  const conversationBusyRetriesRef = import_react121.useRef(0);
557861
558285
  const [queueDisplay, setQueueDisplay] = import_react121.useState([]);
557862
558286
  const tuiQueueRef = import_react121.useRef(null);
@@ -559345,6 +559769,7 @@ Memory may be stale. Try running: git -C ${getScopedMemoryFilesystemRoot(agentId
559345
559769
  autoAllowedExecutionRef,
559346
559770
  buffersRef,
559347
559771
  clearApprovalToolContext,
559772
+ chatgptPlanSwapsRef,
559348
559773
  closeTrajectorySegment,
559349
559774
  consumeQueuedMessages,
559350
559775
  queueModeRef,
@@ -564456,4 +564881,4 @@ function registerBunOAuthFlows() {
564456
564881
  registerBunOAuthFlows();
564457
564882
  await init_src5().then(() => exports_src2);
564458
564883
 
564459
- //# debugId=BBC1C9C009AB28B764756E2164756E21
564884
+ //# debugId=66D224FF7F960BF164756E2164756E21