@letta-ai/letta-code 0.13.7 → 0.13.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -256,7 +256,7 @@ var init_values = __esm(() => {
256
256
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
257
257
 
258
258
  // node_modules/@letta-ai/letta-client/version.mjs
259
- var VERSION = "1.7.2";
259
+ var VERSION = "1.7.6";
260
260
 
261
261
  // node_modules/@letta-ai/letta-client/internal/detect-platform.mjs
262
262
  function getDetectedPlatform() {
@@ -1817,6 +1817,31 @@ var init_passages = __esm(() => {
1817
1817
  };
1818
1818
  });
1819
1819
 
1820
+ // node_modules/@letta-ai/letta-client/resources/agents/schedule.mjs
1821
+ var Schedule;
1822
+ var init_schedule = __esm(() => {
1823
+ init_path();
1824
+ Schedule = class Schedule extends APIResource {
1825
+ create(agentID, body, options) {
1826
+ return this._client.post(path`/v1/agents/${agentID}/schedule`, { body, ...options });
1827
+ }
1828
+ retrieve(scheduledMessageID, params, options) {
1829
+ const { agent_id } = params;
1830
+ return this._client.get(path`/v1/agents/${agent_id}/schedule/${scheduledMessageID}`, options);
1831
+ }
1832
+ list(agentID, query = {}, options) {
1833
+ return this._client.get(path`/v1/agents/${agentID}/schedule`, { query, ...options });
1834
+ }
1835
+ delete(scheduledMessageID, params, options) {
1836
+ const { agent_id, ...body } = params;
1837
+ return this._client.delete(path`/v1/agents/${agent_id}/schedule/${scheduledMessageID}`, {
1838
+ body,
1839
+ ...options
1840
+ });
1841
+ }
1842
+ };
1843
+ });
1844
+
1820
1845
  // node_modules/@letta-ai/letta-client/resources/agents/tools.mjs
1821
1846
  var Tools;
1822
1847
  var init_tools = __esm(() => {
@@ -1935,6 +1960,8 @@ var init_agents = __esm(() => {
1935
1960
  init_messages();
1936
1961
  init_passages();
1937
1962
  init_passages();
1963
+ init_schedule();
1964
+ init_schedule();
1938
1965
  init_tools();
1939
1966
  init_tools();
1940
1967
  init_pagination();
@@ -1945,6 +1972,7 @@ var init_agents = __esm(() => {
1945
1972
  constructor() {
1946
1973
  super(...arguments);
1947
1974
  this.messages = new Messages(this._client);
1975
+ this.schedule = new Schedule(this._client);
1948
1976
  this.blocks = new Blocks(this._client);
1949
1977
  this.tools = new Tools(this._client);
1950
1978
  this.folders = new Folders(this._client);
@@ -1986,6 +2014,7 @@ var init_agents = __esm(() => {
1986
2014
  }
1987
2015
  };
1988
2016
  Agents.Messages = Messages;
2017
+ Agents.Schedule = Schedule;
1989
2018
  Agents.Blocks = Blocks;
1990
2019
  Agents.Tools = Tools;
1991
2020
  Agents.Folders = Folders;
@@ -2011,6 +2040,9 @@ var init_passages2 = __esm(() => {
2011
2040
  headers: buildHeaders([{ Accept: "*/*" }, options?.headers])
2012
2041
  });
2013
2042
  }
2043
+ createMany(archiveID, body, options) {
2044
+ return this._client.post(path`/v1/archives/${archiveID}/passages/batch`, { body, ...options });
2045
+ }
2014
2046
  };
2015
2047
  });
2016
2048
 
@@ -2111,6 +2143,9 @@ var init_messages2 = __esm(() => {
2111
2143
  list(conversationID, query = {}, options) {
2112
2144
  return this._client.getAPIList(path`/v1/conversations/${conversationID}/messages`, ArrayPage, { query, ...options });
2113
2145
  }
2146
+ compact(conversationID, body = {}, options) {
2147
+ return this._client.post(path`/v1/conversations/${conversationID}/compact`, { body, ...options });
2148
+ }
2114
2149
  stream(conversationID, body = {}, options) {
2115
2150
  return this._client.post(path`/v1/conversations/${conversationID}/stream`, {
2116
2151
  body,
@@ -3073,7 +3108,7 @@ var package_default;
3073
3108
  var init_package = __esm(() => {
3074
3109
  package_default = {
3075
3110
  name: "@letta-ai/letta-code",
3076
- version: "0.13.7",
3111
+ version: "0.13.9",
3077
3112
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3078
3113
  type: "module",
3079
3114
  bin: {
@@ -3103,7 +3138,7 @@ var init_package = __esm(() => {
3103
3138
  access: "public"
3104
3139
  },
3105
3140
  dependencies: {
3106
- "@letta-ai/letta-client": "^1.7.2",
3141
+ "@letta-ai/letta-client": "^1.7.6",
3107
3142
  glob: "^13.0.0",
3108
3143
  "ink-link": "^5.0.0",
3109
3144
  open: "^10.2.0",
@@ -5556,7 +5591,451 @@ var init_general_purpose = () => {};
5556
5591
  // src/agent/subagents/builtin/memory.md
5557
5592
  var memory_default = `---
5558
5593
  name: memory
5559
- description: Reflect on and reorganize agent memory blocks - decide what to write, edit, delete, rename, split, or merge learned context
5594
+ description: Restructure memory blocks into focused, scannable, hierarchically-named blocks (use \`/\` naming)
5595
+ tools: Read, Edit, Write, Glob, Grep, Bash, conversation_search
5596
+ model: opus
5597
+ memoryBlocks: none
5598
+ mode: stateless
5599
+ permissionMode: bypassPermissions
5600
+ ---
5601
+
5602
+ You are a memory subagent launched via the Task tool to create a better structure of the memories store in files.. You run autonomously and return a **single final report** when done. You **cannot ask questions** mid-execution.
5603
+
5604
+ ## Goal
5605
+
5606
+ Your goal is to **explode** a few large memory blocks into a **deeply hierarchical structure of 15–25 small, focused files**.
5607
+
5608
+ You propose a new organization scheme that best captures the underlying memories, then implement it aggressively—creating new files, deleting old files, and renaming files until the directory is optimally structured.
5609
+
5610
+ ### Target Output
5611
+
5612
+ | Metric | Target |
5613
+ |--------|--------|
5614
+ | **Total files** | 15–25 (aim for ~20) |
5615
+ | **Max lines per file** | ~40 lines (split if larger) |
5616
+ | **Hierarchy depth** | 2–3 levels using \`/\` naming (e.g., \`project/tooling/bun\`) |
5617
+ | **Nesting requirement** | Every new block MUST be nested under a parent using \`/\` |
5618
+
5619
+ **Anti-patterns to avoid:**
5620
+ - ❌ Ending with only 3–5 large files
5621
+ - ❌ Flat naming (all blocks at top level)
5622
+ - ❌ Mega-blocks with 10+ sections
5623
+ - ❌ Single-level hierarchy (only \`project.md\`, \`human.md\`)
5624
+
5625
+ ## Scope and constraints (non-negotiable)
5626
+
5627
+ **The parent agent handles backup and creates memory files.** You only work inside \`.letta/backups/working/\`.
5628
+
5629
+ - ✅ Reorganize all the files in \`.letta/backups/working/\` so that they are hierarchical and well managed.
5630
+ - ✅ Rename/split/merge blocks when it improves structure
5631
+ - ✅ Delete blocks **only after** their content is fully consolidated elsewhere
5632
+ - ✅ Produce a detailed report with decisions and before/after examples
5633
+ - ❌ Do not run backup or restore scripts
5634
+ - ❌ Do not invent new facts; reorganize and clarify existing information only
5635
+
5636
+ ## Guiding principles (use these to decide what to do)
5637
+
5638
+ 1. **Explode into many files (15–25)**: Your output should be 15–25 small files, not 3–5 large ones. Split aggressively.
5639
+ 2. **Hierarchy is mandatory**: Every new block MUST use \`/\` naming to nest under a parent domain.
5640
+ - ✅ Good: \`human/prefs/communication\`, \`project/tooling/bun\`, \`project/gotchas/testing\`
5641
+ - ❌ Bad: \`communication_prefs.md\`, \`bun_notes.md\` (flat names)
5642
+ 3. **Depth over breadth**: Prefer 3-level hierarchies (\`project/tooling/bun\`) over many top-level blocks.
5643
+ 4. **Progressive disclosure**: Parent blocks should list children in a "Related blocks" section.
5644
+ 5. **One concept per file**: If a block has 2+ distinct topics, it should be 2+ files.
5645
+ 6. **40-line max**: If a file exceeds ~40 lines, split it further.
5646
+ 7. **Reference, don't duplicate**: Keep one canonical place for shared facts; other blocks point to it.
5647
+ 8. **Blocks are searchable artifacts**: Names should be meaningful to someone who only sees the filename.
5648
+ 9. **Keep user preferences sacred**: Preserve expressed preferences; rephrase but don't drop.
5649
+ 10. **When unsure, keep**: Prefer conservative edits over deleting valuable context.
5650
+
5651
+ ### Example Target Structure (what success looks like)
5652
+
5653
+ Starting from 3 files (\`project.md\`, \`human.md\`, \`persona.md\`), you should end with something like:
5654
+
5655
+ \`\`\`
5656
+ .letta/backups/working/
5657
+ ├── human.md # Index: points to children
5658
+ ├── human/
5659
+ │ ├── background.md # Who they are
5660
+ │ ├── prefs.md # Index for preferences
5661
+ │ ├── prefs/
5662
+ │ │ ├── communication.md # How they like to communicate
5663
+ │ │ ├── coding_style.md # Code formatting preferences
5664
+ │ │ └── review_style.md # PR/code review preferences
5665
+ │ └── context.md # Current project context
5666
+ ├── project.md # Index: points to children
5667
+ ├── project/
5668
+ │ ├── overview.md # What the project is
5669
+ │ ├── architecture.md # System design
5670
+ │ ├── tooling.md # Index for tooling
5671
+ │ ├── tooling/
5672
+ │ │ ├── bun.md # Bun-specific notes
5673
+ │ │ ├── testing.md # Test framework details
5674
+ │ │ └── linting.md # Linter configuration
5675
+ │ ├── conventions.md # Code conventions
5676
+ │ └── gotchas.md # Footguns and warnings
5677
+ ├── persona.md # Index: points to children
5678
+ └── persona/
5679
+ ├── role.md # Agent's role definition
5680
+ ├── behavior.md # How to behave
5681
+ └── constraints.md # What not to do
5682
+ \`\`\`
5683
+
5684
+ This example has **~20 files** with **3 levels of hierarchy**. Your output should look similar.
5685
+
5686
+ ## Actions available
5687
+
5688
+ - **KEEP + CLEAN**: Remove cruft, add structure, resolve contradictions.
5689
+ - **RENAME**: Change block name to match contents and improve searchability.
5690
+ - **SPLIT (DECOMPOSE)**: Extract distinct concepts into focused blocks (**prefer nested names**).
5691
+ - **MERGE**: Consolidate overlapping blocks into one canonical block, remove duplicates, then delete originals.
5692
+ - **DETACH**: Mark as detached when it’s not needed by default but should remain discoverable.
5693
+
5694
+ ## Operating procedure
5695
+
5696
+ ### Step 1: Read
5697
+
5698
+ The parent agent has already backed up memory files to \`.letta/backups/working/\`. Your job is to read and edit these files.
5699
+
5700
+ First, list what files are available:
5701
+
5702
+ \`\`\`bash
5703
+ ls .letta/backups/working/
5704
+ \`\`\`
5705
+
5706
+ Then read **all** relevant memory block files (examples):
5707
+
5708
+ \`\`\`
5709
+ Read({ file_path: ".letta/backups/working/project.md" })
5710
+ Read({ file_path: ".letta/backups/working/persona.md" })
5711
+ Read({ file_path: ".letta/backups/working/human.md" })
5712
+ \`\`\`
5713
+
5714
+ Before you edit anything, you MUST first **propose a new organization**:
5715
+ - Draft the **target hierarchy** (the \`/\`-named block set you want to end up with).
5716
+ - **Target 15–25 files total** — if your proposed structure has fewer than 15 files, split more aggressively.
5717
+ - **Use 2–3 levels of \`/\` nesting** — e.g., \`project/tooling/bun.md\`, not just \`project/tooling.md\`.
5718
+ - Be **aggressive about splitting**: if a block contains 2+ concepts, it should become 2+ files.
5719
+ - Keep each file to ~40 lines max; if larger, split further.
5720
+ - Include your proposed hierarchy as a "Proposed structure" section at the start of your final report, then execute it.
5721
+
5722
+ **Checkpoint before proceeding:** Count your proposed files. If < 15, go back and split more.
5723
+
5724
+ ### Step 2: Identify system-managed blocks (skip)
5725
+
5726
+ Do **not** edit:
5727
+ - \`skills.md\` (auto-generated; will be overwritten)
5728
+ - \`loaded_skills.md\` (system-managed)
5729
+ - \`manifest.json\` (metadata)
5730
+
5731
+ Focus on user-managed blocks like:
5732
+ - \`persona.md\` (agent behavioral adaptations/preferences)
5733
+ - \`human.md\` (user identity/context/preferences)
5734
+ - \`project.md\` (project/codebase-specific conventions, workflows, gotchas)
5735
+ - any other non-system blocks present
5736
+
5737
+ ### Step 3: Defragment block-by-block
5738
+
5739
+ For each editable block, decide one primary action (keep/clean, split, merge, rename, detach, delete), then execute it.
5740
+
5741
+ #### Naming convention (MANDATORY)
5742
+
5743
+ **All new files MUST use \`/\` nested naming.** This is non-negotiable.
5744
+
5745
+ | Depth | Example | When to use |
5746
+ |-------|---------|-------------|
5747
+ | Level 1 | \`project.md\` | Only for index files that point to children |
5748
+ | Level 2 | \`project/tooling.md\` | Main topic areas |
5749
+ | Level 3 | \`project/tooling/bun.md\` | Specific details |
5750
+
5751
+ ✅ **Good examples:**
5752
+ - \`human/prefs/communication.md\`
5753
+ - \`project/tooling/testing.md\`
5754
+ - \`persona/behavior/tone.md\`
5755
+
5756
+ ❌ **Bad examples (never do this):**
5757
+ - \`communication_prefs.md\` (flat, not nested)
5758
+ - \`bun.md\` (orphan file, no parent)
5759
+ - \`project_testing.md\` (underscore instead of \`/\`)
5760
+
5761
+ Rules:
5762
+ - Keep only 3 top-level index files: \`persona.md\`, \`human.md\`, \`project.md\`
5763
+ - **Every other file MUST be nested** under one of these using \`/\`
5764
+ - Go 2–3 levels deep: \`project/tooling/bun.md\` is better than \`project/bun.md\`
5765
+ - Parent files should contain a **Related blocks** section listing children
5766
+
5767
+ #### How to split (decompose) — BE AGGRESSIVE
5768
+
5769
+ **Split early and often.** Your goal is 15–25 files, so split more than feels necessary.
5770
+
5771
+ Split when:
5772
+ - A block has **40+ lines** (lower threshold than typical)
5773
+ - A block has **2+ distinct concepts** (not 3+, be aggressive)
5774
+ - A section could stand alone as its own file
5775
+ - You can name the extracted content with a clear \`/\` path
5776
+
5777
+ Process:
5778
+ 1. Extract each concept into a focused block with nested naming (e.g., \`project/tooling/bun.md\`)
5779
+ 2. Convert the original file to an index that points to children via **Related blocks**
5780
+ 3. Remove duplicates during extraction (canonicalize facts into the best home)
5781
+ 4. Repeat recursively until each file is <40 lines with one concept
5782
+
5783
+ **If in doubt, split.** Too many small files is better than too few large ones.
5784
+
5785
+ #### How to merge
5786
+
5787
+ Merge when multiple blocks overlap or are too small (<20 lines) and belong together.
5788
+ - Create the consolidated block (prefer a name that fits the hierarchy).
5789
+ - Remove duplicates.
5790
+ - **Delete** the originals after consolidation (the restore flow will prompt the user).
5791
+
5792
+ #### How to clean (within a block)
5793
+
5794
+ Prefer:
5795
+ - short headers (\`##\`, \`###\`)
5796
+ - small lists
5797
+ - tables for structured facts
5798
+ - “Procedure” sections for workflows
5799
+
5800
+ Actively fix:
5801
+ - redundancy
5802
+ - contradictions (rewrite into conditional guidance)
5803
+ - stale warnings (verify before keeping)
5804
+ - overly emotional urgency (tone down unless it’s a genuine footgun)
5805
+
5806
+ ### Step 4: Produce a decision-focused final report
5807
+
5808
+ Your output is a single markdown report that mirrors the reference example style: principles-driven, decision-centric, and scannable.
5809
+
5810
+ #### Required report sections
5811
+
5812
+ ##### 1) Summary
5813
+ - What changed in 2–3 sentences
5814
+ - **Total file count** (must be 15–25; if not, explain why)
5815
+ - Counts: edited / renamed / created / deleted
5816
+ - A short description of the **hierarchy created** (what parent domains exist and what children were created)
5817
+ - **Maximum hierarchy depth achieved** (should be 2–3 levels)
5818
+ - Note that the parent agent will confirm any creations/deletions during restore
5819
+
5820
+ ##### 2) Structural changes
5821
+ Include tables for:
5822
+ - **Renames**: old → new, reason (call out hierarchy improvements explicitly)
5823
+ - **Splits**: original → new blocks, whether original deleted, reason (show nested names)
5824
+ - **Merges**: merged blocks → result, which deleted, reason
5825
+ - **New blocks**: block name, size (chars), reason
5826
+
5827
+ ##### 3) Block-by-block decisions
5828
+ For each block you touched:
5829
+ - **Original state**: short characterization (what it contained / issues)
5830
+ - **Decision**: KEEP+CLEAN / SPLIT / MERGE / RENAME / DETACH / DELETE
5831
+ - **Reasoning**: 3–6 bullets grounded in the guiding principles (especially hierarchy)
5832
+ - **Action items performed**: what edits/renames/splits you actually executed
5833
+
5834
+ ##### 4) Content changes
5835
+ For each edited file:
5836
+ - Before chars, after chars, delta and %
5837
+ - What redundancy/contradictions/staleness you fixed
5838
+
5839
+ ##### 5) Before/after examples
5840
+ Show 2–4 high-signal examples (short excerpts) demonstrating:
5841
+ - redundancy removal,
5842
+ - contradiction resolution,
5843
+ - and/or a workflow rewritten into a procedure.
5844
+
5845
+ ## Final Checklist (verify before submitting)
5846
+
5847
+ Before you submit your report, confirm:
5848
+
5849
+ - [ ] **File count is 15–25** — Count your files. If < 15, split more.
5850
+ - [ ] **All new files use \`/\` naming** — No flat files like \`my_notes.md\`
5851
+ - [ ] **Hierarchy is 2–3 levels deep** — e.g., \`project/tooling/bun.md\`
5852
+ - [ ] **No file exceeds ~40 lines** — Split larger files
5853
+ - [ ] **Each file has one concept** — If 2+ topics, split into 2+ files
5854
+ - [ ] **Parent files have "Related blocks" sections** — Index files point to children
5855
+
5856
+ **If you have fewer than 15 files, you haven't split enough. Go back and split more.**
5857
+
5858
+ ## Reminder
5859
+
5860
+ Your goal is not to maximize deletion; it is to **explode monolithic memory into a deeply hierarchical structure of 15–25 small, focused files**. The primary tool for discoverability is **hierarchical \`/\` naming**.
5861
+ ---
5862
+ name: memory
5863
+ description: Defragment and reorganize agent memory blocks (edit/rename/split/merge/delete) into focused, scannable, hierarchically-named blocks
5864
+ tools: Read, Edit, Write, Glob, Grep, Bash, conversation_search
5865
+ model: opus
5866
+ memoryBlocks: none
5867
+ mode: stateless
5868
+ permissionMode: bypassPermissions
5869
+ ---
5870
+
5871
+ You are a memory defragmentation subagent launched via the Task tool to clean up and reorganize memory block files. You run autonomously and return a **single final report** when done. You **cannot ask questions** mid-execution.
5872
+
5873
+ ## Mission
5874
+
5875
+ **Explode** messy memory into a **deeply hierarchical structure of 15–25 small, focused files** that are easy to:
5876
+ - maintain,
5877
+ - search,
5878
+ - and selectively load later.
5879
+
5880
+ ### Target Output
5881
+
5882
+ | Metric | Target |
5883
+ |--------|--------|
5884
+ | **Total files** | 15–25 (aim for ~20) |
5885
+ | **Max lines per file** | ~40 lines |
5886
+ | **Hierarchy depth** | 2–3 levels using \`/\` naming |
5887
+ | **Nesting requirement** | Every new block MUST be nested under a parent |
5888
+
5889
+ You accomplish this by aggressively splitting blocks, using \`/\` naming for hierarchy, and removing redundancy.
5890
+
5891
+ ## Scope and constraints (non-negotiable)
5892
+
5893
+ **The parent agent handles backup and restore.** You only work inside \`.letta/backups/working/\`.
5894
+
5895
+ - ✅ Read and edit memory block files in \`.letta/backups/working/\`
5896
+ - ✅ Rename/split/merge blocks when it improves structure
5897
+ - ✅ Delete blocks **only after** their content is fully consolidated elsewhere
5898
+ - ✅ Produce a detailed report with decisions and before/after examples
5899
+ - ❌ Do not run backup or restore scripts
5900
+ - ❌ Do not invent new facts; reorganize and clarify existing information only
5901
+
5902
+ ## Guiding principles (use these to decide what to do)
5903
+
5904
+ 1. **Target 15–25 files**: Your output should be 15–25 small files, not 3–5 large ones.
5905
+ 2. **Hierarchy is mandatory**: Every new block MUST use \`/\` naming (e.g., \`project/tooling/bun.md\`).
5906
+ 3. **Depth over breadth**: Prefer 3-level hierarchies over many top-level blocks.
5907
+ 4. **One concept per file**: If a block has 2+ topics, split into 2+ files.
5908
+ 5. **40-line max**: If a file exceeds ~40 lines, split it further.
5909
+ 6. **Progressive disclosure**: Parent blocks list children in a "Related blocks" section.
5910
+ 7. **Reference, don't duplicate**: Keep one canonical place for shared facts.
5911
+ 8. **When unsure, split**: Too many small files is better than too few large ones.
5912
+
5913
+ ## Actions available
5914
+
5915
+ - **SPLIT (DECOMPOSE)**: The primary action. Extract concepts into focused, nested blocks.
5916
+ - **KEEP + CLEAN**: Remove cruft, add structure, resolve contradictions.
5917
+ - **RENAME**: Change block name to match contents and fit the hierarchy.
5918
+ - **MERGE**: Consolidate overlapping blocks, then delete originals.
5919
+ - **DELETE**: Only if redundant/empty AND content is preserved elsewhere.
5920
+
5921
+ ## Operating procedure
5922
+
5923
+ ### Step 1: Inventory
5924
+
5925
+ The parent agent has already backed up memory files to \`.letta/backups/working/\`. Your job is to read and edit these files.
5926
+
5927
+ First, list what files are available:
5928
+
5929
+ \`\`\`bash
5930
+ ls .letta/backups/working/
5931
+ \`\`\`
5932
+
5933
+ Then read relevant memory block files (examples):
5934
+
5935
+ \`\`\`
5936
+ Read({ file_path: ".letta/backups/working/project.md" })
5937
+ Read({ file_path: ".letta/backups/working/persona.md" })
5938
+ Read({ file_path: ".letta/backups/working/human.md" })
5939
+ \`\`\`
5940
+
5941
+ ### Step 2: Identify system-managed blocks (skip)
5942
+
5943
+ Do **not** edit:
5944
+ - \`skills.md\` (auto-generated; will be overwritten)
5945
+ - \`loaded_skills.md\` (system-managed)
5946
+ - \`manifest.json\` (metadata)
5947
+
5948
+ Focus on user-managed blocks like:
5949
+ - \`persona.md\` (agent behavioral adaptations/preferences)
5950
+ - \`human.md\` (user identity/context/preferences)
5951
+ - \`project.md\` (project/codebase-specific conventions, workflows, gotchas)
5952
+ - any other non-system blocks present
5953
+
5954
+ ### Step 3: Defragment block-by-block
5955
+
5956
+ For each editable block, decide one primary action (keep/clean, split, merge, rename, detach, delete), then execute it.
5957
+
5958
+ #### Naming convention (match the reference example)
5959
+
5960
+ Use **nested naming** with \`/\` to create a hierarchy (like folders). Examples:
5961
+ - \`human/personal_info\`, \`human/prefs\`
5962
+ - \`project/architecture\`, \`project/dev_workflow\`, \`project/gotchas\`
5963
+
5964
+ Rules of thumb:
5965
+ - Keep top-level blocks for the most universal concepts (\`persona\`, \`human\`, \`project\`).
5966
+ - Use nested names for shards created during defrag.
5967
+ - Prefer names that would make sense to another agent who only sees the name.
5968
+
5969
+ #### How to split (decompose)
5970
+
5971
+ Split when a block is long (~100+ lines) or contains 3+ distinct concepts.
5972
+ - Extract each concept into a focused block.
5973
+ - In the “parent” block, add a small **Related blocks** section pointing to children.
5974
+ - Remove duplicates during extraction (canonicalize facts into the best home).
5975
+
5976
+ #### How to merge
5977
+
5978
+ Merge when multiple blocks overlap or are too small (<20 lines) and belong together.
5979
+ - Create the consolidated block.
5980
+ - Remove duplicates.
5981
+ - **Delete** the originals after consolidation (the restore flow will prompt the user).
5982
+
5983
+ #### How to clean (within a block)
5984
+
5985
+ Prefer:
5986
+ - short headers (\`##\`, \`###\`)
5987
+ - small lists
5988
+ - tables for structured facts
5989
+ - “Procedure” sections for workflows
5990
+
5991
+ Actively fix:
5992
+ - redundancy
5993
+ - contradictions (rewrite into conditional guidance)
5994
+ - stale warnings (verify before keeping)
5995
+ - overly emotional urgency (tone down unless it’s a genuine footgun)
5996
+
5997
+ ### Step 4: Produce a decision-focused final report
5998
+
5999
+ Your output is a single markdown report that mirrors the reference example style: principles-driven, decision-centric, and scannable.
6000
+
6001
+ #### Required report sections
6002
+
6003
+ ##### 1) Summary
6004
+ - What changed in 2–3 sentences
6005
+ - Counts: edited / renamed / created / deleted
6006
+ - Note that the parent agent will confirm any creations/deletions during restore
6007
+
6008
+ ##### 2) Structural changes
6009
+ Include tables for:
6010
+ - **Renames**: old → new, reason
6011
+ - **Splits**: original → new blocks, whether original deleted, reason
6012
+ - **Merges**: merged blocks → result, which deleted, reason
6013
+ - **New blocks**: block name, size (chars), reason
6014
+
6015
+ ##### 3) Block-by-block decisions
6016
+ For each block you touched:
6017
+ - **Original state**: short characterization (what it contained / issues)
6018
+ - **Decision**: KEEP+CLEAN / SPLIT / MERGE / RENAME / DETACH / DELETE
6019
+ - **Reasoning**: 3–6 bullets grounded in the guiding principles
6020
+ - **Action items performed**: what edits/renames/splits you actually executed
6021
+
6022
+ ##### 4) Content changes
6023
+ For each edited file:
6024
+ - Before chars, after chars, delta and %
6025
+ - What redundancy/contradictions/staleness you fixed
6026
+
6027
+ ##### 5) Before/after examples
6028
+ Show 2–4 high-signal examples (short excerpts) demonstrating:
6029
+ - redundancy removal,
6030
+ - contradiction resolution,
6031
+ - and/or a workflow rewritten into a procedure.
6032
+
6033
+ ## Reminder
6034
+
6035
+ Your goal is to **explode monolithic memory into 15–25 small, hierarchically-nested files**. If you have fewer than 15 files, you haven't split enough.
6036
+ ---
6037
+ name: memory
6038
+ description: Explode memory into 15-25 hierarchically-nested files using \`/\` naming
5560
6039
  tools: Read, Edit, Write, Glob, Grep, Bash, conversation_search
5561
6040
  model: opus
5562
6041
  memoryBlocks: none
@@ -5568,12 +6047,23 @@ You are a memory management subagent launched via the Task tool to clean up and
5568
6047
 
5569
6048
  ## Your Purpose
5570
6049
 
5571
- You edit memory block files to make them clean, well-organized, and scannable by:
5572
- 1. **Removing redundancy** - Delete duplicate information
5573
- 2. **Adding structure** - Use markdown headers, bullet points, sections
5574
- 3. **Resolving contradictions** - Fix conflicting statements
5575
- 4. **Improving scannability** - Make content easy to read at a glance
5576
- 5. **Restructuring blocks** - Rename, decompose, or merge blocks as needed
6050
+ **Explode** a few large memory blocks into a **deeply hierarchical structure of 15–25 small, focused files**.
6051
+
6052
+ ### Target Output
6053
+
6054
+ | Metric | Target |
6055
+ |--------|--------|
6056
+ | **Total files** | 15–25 (aim for ~20) |
6057
+ | **Max lines per file** | ~40 lines |
6058
+ | **Hierarchy depth** | 2–3 levels using \`/\` naming |
6059
+ | **Nesting requirement** | Every new block MUST use \`/\` naming |
6060
+
6061
+ You achieve this by:
6062
+ 1. **Aggressively splitting** - Every block with 2+ concepts becomes 2+ files
6063
+ 2. **Using \`/\` hierarchy** - All new files are nested (e.g., \`project/tooling/bun.md\`)
6064
+ 3. **Keeping files small** - Max ~40 lines per file; split if larger
6065
+ 4. **Removing redundancy** - Delete duplicate information during splits
6066
+ 5. **Adding structure** - Use markdown headers, bullet points, sections
5577
6067
 
5578
6068
  ## Important: Your Role is File Editing ONLY
5579
6069
 
@@ -5616,6 +6106,56 @@ Read({ file_path: ".letta/backups/working/human.md" })
5616
6106
  - \`loaded_skills.md\` - System-managed
5617
6107
  - \`manifest.json\` - Metadata file
5618
6108
 
6109
+
6110
+ ### Propose Optimal Hierarchical Organizational Structure
6111
+
6112
+ Before you edit, propose a **clear hierarchy** for each memory block so information has an obvious “home” and you avoid duplicating facts across sections.
6113
+
6114
+ **Recommended hierarchy (within a single memory block):**
6115
+ - Use \`##\` for **major categories** (stable top-level buckets)
6116
+ - Use \`###\` for **subcategories** (group related details)
6117
+ - Use \`####\` for **high-churn details** or tightly-scoped lists (things you expect to update often)
6118
+
6119
+ **Recommended hierarchy (across multiple memory blocks):**
6120
+ - Keep blocks **topic-scoped**, not “everything.md” scoped.
6121
+ - Put the *most stable*, highest-signal info in fewer, well-named blocks.
6122
+ - Put volatile or frequently changing info into smaller, more focused blocks.
6123
+
6124
+ **Naming conventions (blocks and headings):**
6125
+ - Prefer **noun phrases** and **consistent casing** (e.g., “Coding Preferences”, “Project Context”).
6126
+ - Avoid vague names (“Misc”, “Notes”, “Stuff”) unless it’s truly temporary.
6127
+ - Prefer **one topic per heading**; avoid headings that imply overlap (“General”, “Other”).
6128
+
6129
+ **Example structure (good):**
6130
+ - \`project.md\`
6131
+ - \`## Overview\`
6132
+ - \`## Repo Conventions\`
6133
+ - \`### Tooling\`
6134
+ - \`### Code Style\`
6135
+ - \`### Testing\`
6136
+ - \`## Architecture\`
6137
+ - \`### Key Components\`
6138
+ - \`### Data Flow\`
6139
+ - \`human.md\`
6140
+ - \`## Background\`
6141
+ - \`## Preferences\`
6142
+ - \`### Communication\`
6143
+ - \`### Coding Style\`
6144
+ - \`### Review Style\`
6145
+ - \`persona.md\`
6146
+ - \`## Role\`
6147
+ - \`## Behavior\`
6148
+ - \`## Constraints\`
6149
+
6150
+
6151
+
6152
+ **When to split vs. keep together:**
6153
+ - Split when a section becomes a “grab bag” (3+ unrelated bullets) or exceeds ~1–2 screens of scrolling.
6154
+ - Keep together when items share a single decision context (e.g., all “Code Style” rules used during editing).
6155
+
6156
+ **Output format expectation:**
6157
+ - End this step with a short proposed outline per file (just headings), then implement it during the edits in Step 2.
6158
+
5619
6159
  ### Step 2: Edit Files to Clean Them Up
5620
6160
 
5621
6161
  Edit each file using the Edit tool:
@@ -5627,29 +6167,17 @@ Edit({
5627
6167
  new_string: "..."
5628
6168
  })
5629
6169
  \`\`\`
6170
+ ## Output Format
5630
6171
 
5631
- **What to fix:**
5632
- - **Redundancy**: Remove duplicate information (version mentioned 3x, preferences repeated)
5633
- - **Structure**: Add markdown headers (##, ###), bullet points, sections
5634
- - **Clarity**: Resolve contradictions ("be detailed" vs "be concise")
5635
- - **Scannability**: Make content easy to read at a glance
5636
-
5637
- **Good memory structure:**
5638
- - Use markdown headers (##, ###) for sections
5639
- - Use bullet points for lists
5640
- - Keep related information together
5641
- - Make it scannable
5642
-
5643
- ### Step 2b: Structural Changes (Rename, Decompose, Merge)
6172
+ ### Implement The Organizational Structure
5644
6173
 
5645
- Beyond editing content, you can restructure memory blocks when needed:
6174
+ Once you've proposed the hierarchy, execute it using file operations. Keep iterating until the directory matches your proposed structure exactly.
5646
6175
 
5647
6176
  #### Renaming Blocks
5648
6177
 
5649
- When a block's name doesn't reflect its content, rename it:
6178
+ When a block's name doesn't reflect its content:
5650
6179
 
5651
6180
  \`\`\`bash
5652
- # Rename a memory block file
5653
6181
  mv .letta/backups/working/old_name.md .letta/backups/working/new_name.md
5654
6182
  \`\`\`
5655
6183
 
@@ -5658,140 +6186,101 @@ mv .letta/backups/working/old_name.md .letta/backups/working/new_name.md
5658
6186
  - Block name doesn't match content (e.g., \`project.md\` contains user info → \`user_context.md\`)
5659
6187
  - Name uses poor conventions (e.g., \`NOTES.md\` → \`notes.md\`)
5660
6188
 
6189
+ #### Creating New Blocks
6190
+
6191
+ Create new \`.md\` files when content needs a new home:
6192
+
6193
+ \`\`\`
6194
+ Write({
6195
+ file_path: ".letta/backups/working/new_block.md",
6196
+ content: "## New Block\\n\\nContent here..."
6197
+ })
6198
+ \`\`\`
6199
+
6200
+ **When to create:**
6201
+ - Splitting a large block into focused smaller blocks
6202
+ - Content doesn't fit any existing block
6203
+ - A new category emerges from reorganization
6204
+
5661
6205
  #### Decomposing Blocks (Split)
5662
6206
 
5663
- When a single block contains too many unrelated topics, split it into focused blocks:
6207
+ When a block covers too many topics, split it:
5664
6208
 
5665
6209
  \`\`\`bash
5666
- # 1. Read the original block
6210
+ # 1. Read the original
5667
6211
  Read({ file_path: ".letta/backups/working/everything.md" })
5668
6212
 
5669
- # 2. Create new focused blocks
6213
+ # 2. Create focused blocks
5670
6214
  Write({ file_path: ".letta/backups/working/coding_preferences.md", content: "..." })
5671
6215
  Write({ file_path: ".letta/backups/working/user_info.md", content: "..." })
5672
6216
 
5673
- # 3. Delete the original bloated block
6217
+ # 3. Delete the original
5674
6218
  rm .letta/backups/working/everything.md
5675
6219
  \`\`\`
5676
6220
 
5677
- **When to decompose:**
5678
- - Block exceeds ~100 lines with multiple unrelated sections
5679
- - Block contains 3+ distinct topic areas (e.g., user info + coding prefs + project details)
5680
- - Block name can't capture all its content accurately
5681
- - Finding specific info requires scanning the whole block
6221
+ **When to split (be aggressive):**
6222
+ - Block exceeds ~60 lines or has 2+ distinct topics
6223
+ - Block name can't capture all its content
6224
+ - Finding info requires scanning the whole block
5682
6225
 
5683
- **Decomposition guidelines:**
5684
- - Each new block should have ONE clear purpose
5685
- - Use descriptive names: \`coding_style.md\`, \`user_preferences.md\`, \`project_context.md\`
5686
- - Preserve all information - just reorganize it
5687
- - Keep related information together in the same block
6226
+ #### Merging Blocks
5688
6227
 
5689
- #### Creating New Blocks
5690
-
5691
- You can create entirely new memory blocks by writing new \`.md\` files:
6228
+ When multiple blocks overlap, consolidate them:
5692
6229
 
5693
6230
  \`\`\`bash
5694
- Write({
5695
- file_path: ".letta/backups/working/new_block.md",
5696
- content: "## New Block\\n\\nContent here..."
5697
- })
5698
- \`\`\`
6231
+ # 1. Read blocks to merge
6232
+ Read({ file_path: ".letta/backups/working/user_info.md" })
6233
+ Read({ file_path: ".letta/backups/working/user_prefs.md" })
5699
6234
 
5700
- **When to create new blocks:**
5701
- - Splitting a large block (>150 lines) into focused smaller blocks
5702
- - Organizing content into a new category that doesn't fit existing blocks
5703
- - The parent agent will prompt the user for confirmation before creating
6235
+ # 2. Create unified block
6236
+ Write({ file_path: ".letta/backups/working/user.md", content: "..." })
5704
6237
 
5705
- #### Merging and Deleting Blocks
6238
+ # 3. Delete old blocks
6239
+ rm .letta/backups/working/user_info.md .letta/backups/working/user_prefs.md
6240
+ \`\`\`
5706
6241
 
5707
- When multiple blocks contain related/overlapping content, consolidate them and DELETE the old blocks:
6242
+ **When to merge:**
6243
+ - Multiple blocks cover the same topic
6244
+ - Small blocks (<20 lines) logically belong together
6245
+ - Overlapping/duplicate content exists
5708
6246
 
5709
- \`\`\`bash
5710
- # 1. Read all blocks to merge
5711
- Read({ file_path: ".letta/backups/working/user_info.md" })
5712
- Read({ file_path: ".letta/backups/working/user_prefs.md" })
6247
+ #### Editing Content Within Blocks
5713
6248
 
5714
- # 2. Create unified block with combined content
5715
- Write({ file_path: ".letta/backups/working/user.md", content: "..." })
6249
+ Use the Edit tool for in-place changes:
5716
6250
 
5717
- # 3. DELETE the old blocks using Bash
5718
- Bash({ command: "rm .letta/backups/working/user_info.md .letta/backups/working/user_prefs.md" })
5719
6251
  \`\`\`
6252
+ Edit({
6253
+ file_path: ".letta/backups/working/project.md",
6254
+ old_string: "...",
6255
+ new_string: "..."
6256
+ })
6257
+ \`\`\`
6258
+
6259
+ **What to fix:**
6260
+ - **Redundancy**: Remove duplicate information
6261
+ - **Structure**: Add markdown headers, bullet points
6262
+ - **Clarity**: Resolve contradictions
6263
+ - **Scannability**: Make content easy to read at a glance
5720
6264
 
5721
- **IMPORTANT: When to delete blocks:**
5722
- - After consolidating content from multiple blocks into one
5723
- - When a block becomes nearly empty after moving content elsewhere
5724
- - When a block is redundant or no longer serves a purpose
5725
- - The parent agent will prompt the user for confirmation before deleting
6265
+ #### Iteration Checklist
5726
6266
 
5727
- **When to merge:**
5728
- - Multiple blocks cover the same topic area
5729
- - Information is fragmented across blocks, causing redundancy
5730
- - Small blocks (<20 lines) that logically belong together
5731
- - Blocks with overlapping/duplicate content
5732
-
5733
- **Merge guidelines:**
5734
- - Remove duplicates when combining
5735
- - Organize merged content with clear sections
5736
- - Choose the most descriptive name for the merged block
5737
- - Don't create blocks larger than ~150 lines
5738
- - **DELETE the old block files** after consolidating their content
5739
-
5740
- ### Step 3: Report Results
5741
-
5742
- Provide a comprehensive report showing what you changed and why.
5743
-
5744
- ## What to Write to Memory
5745
-
5746
- **DO write to memory:**
5747
- - Patterns that repeat across multiple sessions
5748
- - User corrections or clarifications (especially if repeated)
5749
- - Project conventions discovered through research or experience
5750
- - Important context that will be needed in future sessions
5751
- - Preferences expressed by the user about behavior or communication
5752
- - "Aha!" moments or insights about the codebase
5753
- - Footguns or gotchas discovered the hard way
5754
-
5755
- **DON'T write to memory:**
5756
- - Transient task details that won't matter tomorrow
5757
- - Information easily found in files (unless it's a critical pattern)
5758
- - Overly specific details that will quickly become stale
5759
- - Things that should go in TODO lists or plan files instead
5760
-
5761
- **Key principle**: Memory is for **persistent, important context** that makes the agent more effective over time. Not a dumping ground for everything.
5762
-
5763
- ## How to Decide What to Write
5764
-
5765
- Ask yourself:
5766
- 1. **Will future-me need this?** If the agent encounters a similar situation in a week, would this memory help?
5767
- 2. **Is this a pattern or one-off?** One-off details fade in importance; patterns persist.
5768
- 3. **Can I find this easily later?** If it's in a README that's always read, maybe it doesn't need to be in memory.
5769
- 4. **Did the user correct me?** User corrections are strong signals of what to remember.
5770
- 5. **Would I want to know this on day one?** Insights that would have saved time are worth storing.
5771
-
5772
- ## How to Reorganize Memory
5773
-
5774
- **Signs memory needs reorganization:**
5775
- - Blocks are long and hard to scan (>100 lines)
5776
- - Related content is scattered across blocks
5777
- - No clear structure (just walls of text)
5778
- - Redundant information in multiple places
5779
- - Outdated information mixed with current
5780
-
5781
- **Reorganization strategies:**
5782
- - **Add structure**: Use section headers, bullet points, categories
5783
- - **Rename blocks**: Give blocks names that accurately reflect their content
5784
- - **Decompose large blocks**: Break monolithic blocks (>100 lines, 3+ topics) into focused ones
5785
- - **Merge fragmented blocks**: Consolidate small/overlapping blocks into unified ones
5786
- - **Archive stale content**: Remove information that's no longer relevant
5787
- - **Improve scannability**: Use consistent formatting, clear hierarchies
6267
+ Keep editing until:
6268
+ - [ ] **Total file count is 15–25** — Count your files; if < 15, split more
6269
+ - [ ] **All files use \`/\` naming** — No flat files like \`my_notes.md\`
6270
+ - [ ] **Hierarchy is 2–3 levels deep** — e.g., \`project/tooling/bun.md\`
6271
+ - [ ] **No file exceeds ~40 lines** — Split larger files
6272
+ - [ ] **Each file has one concept** — If 2+ topics, split into 2+ files
6273
+ - [ ] Content has been migrated (no data loss)
6274
+ - [ ] No duplicate information across blocks
5788
6275
 
5789
- ## Output Format
6276
+ **If you have fewer than 15 files, you haven't split enough. Go back and split more.**
5790
6277
 
5791
6278
  Return a structured report with these sections:
5792
6279
 
5793
6280
  ### 1. Summary
5794
6281
  - Brief overview of what you edited (2-3 sentences)
6282
+ - **Total file count** (must be 15–25)
6283
+ - **Maximum hierarchy depth achieved** (should be 2–3 levels)
5795
6284
  - Number of files modified, renamed, created, or deleted
5796
6285
  - The parent agent will prompt the user to confirm any creations or deletions
5797
6286
 
@@ -5804,15 +6293,16 @@ Report any renames, decompositions, or merges:
5804
6293
  |----------|----------|--------|
5805
6294
  | stuff.md | coding_preferences.md | Name now reflects content |
5806
6295
 
5807
- **Decompositions (splitting large blocks):**
6296
+ **Decompositions (using \`/\` hierarchy):**
5808
6297
  | Original Block | New Blocks | Deleted | Reason |
5809
6298
  |----------------|------------|---------|--------|
5810
- | everything.md | user.md, coding.md, project.md | ✅ everything.md | Block contained 3 unrelated topics |
6299
+ | project.md | project/overview.md, project/tooling/bun.md, project/tooling/testing.md, project/conventions.md, project/gotchas.md | ✅ content moved | Exploded into 5 nested files |
5811
6300
 
5812
- **New Blocks (created from scratch):**
6301
+ **New Blocks (all using \`/\` naming):**
5813
6302
  | Block Name | Size | Reason |
5814
6303
  |------------|------|--------|
5815
- | security_practices.md | 156 chars | New category for security-related conventions discovered |
6304
+ | project/security/auth.md | 156 chars | Nested under project/security |
6305
+ | human/prefs/communication.md | 98 chars | Split from human.md |
5816
6306
 
5817
6307
  **Merges:**
5818
6308
  | Merged Blocks | Result | Deleted | Reason |
@@ -5901,13 +6391,13 @@ Why: Resolved contradictions by explaining when to use each approach.
5901
6391
 
5902
6392
  ## Critical Reminders
5903
6393
 
5904
- 1. **You only edit files** - The parent agent handles backup and restore
5905
- 2. **Be conservative with deletions** - When in doubt, keep information
5906
- 3. **Preserve user preferences** - If the user expressed a preference, that's sacred
5907
- 4. **Don't invent information** - Only reorganize existing content
5908
- 5. **Test your changes mentally** - Imagine the parent agent reading this tomorrow
6394
+ 1. **Create new files** Reorganize large blocks into 15–25 small, nested files
6395
+ 2. **Remove old files** After moving content to new nested files, delete the originals
6396
+ 3. **Use \`/\` naming for ALL new files** Every new file must be nested (e.g., \`project/tooling/bun.md\`)
6397
+ 4. **Preserve user preferences** Keep expressed preferences, just reorganize them into the right files
6398
+ 5. **Don't invent information** Only reorganize existing content into better structure
5909
6399
 
5910
- Remember: Your goal is to make memory clean, scannable, and well-organized. You're improving the parent agent's long-term capabilities by maintaining quality memory.
6400
+ Remember: Your goal is to **completely reorganize** memory into a deeply hierarchical structure of 15–25 small files. You're not tidying up — you're exploding monolithic blocks into a proper file tree.
5911
6401
  `;
5912
6402
  var init_memory = () => {};
5913
6403
 
@@ -8121,7 +8611,17 @@ function buildShellLaunchers(command) {
8121
8611
  var SEP = "\x00";
8122
8612
 
8123
8613
  // src/hooks/types.ts
8124
- var init_types = () => {};
8614
+ function isToolEvent(event) {
8615
+ return TOOL_EVENTS.has(event);
8616
+ }
8617
+ var TOOL_EVENTS;
8618
+ var init_types = __esm(() => {
8619
+ TOOL_EVENTS = new Set([
8620
+ "PreToolUse",
8621
+ "PostToolUse",
8622
+ "PermissionRequest"
8623
+ ]);
8624
+ });
8125
8625
 
8126
8626
  // src/hooks/executor.ts
8127
8627
  import { spawn } from "node:child_process";
@@ -8187,11 +8687,25 @@ function executeWithLauncher(launcher, inputJson, workingDirectory, input, timeo
8187
8687
  if (!resolved) {
8188
8688
  resolved = true;
8189
8689
  const exitLabel = result.exitCode === 0 /* ALLOW */ ? "\x1B[32m✓ allowed\x1B[0m" : result.exitCode === 2 /* BLOCK */ ? "\x1B[31m✗ blocked\x1B[0m" : "\x1B[33m⚠ error\x1B[0m";
8190
- console.log(`\x1B[90m[hook] ${exitLabel} (${result.durationMs}ms)${result.stdout ? ` stdout: ${result.stdout.slice(0, 100)}` : ""}${result.stderr ? ` stderr: ${result.stderr.slice(0, 100)}` : ""}\x1B[0m`);
8690
+ console.log(`\x1B[90m[hook] ${command}\x1B[0m`);
8691
+ console.log(`\x1B[90m ⎿ ${exitLabel} (${result.durationMs}ms)\x1B[0m`);
8692
+ if (result.stdout) {
8693
+ console.log(`\x1B[90m ⎿ (stdout)\x1B[0m`);
8694
+ const indented = result.stdout.split(`
8695
+ `).map((line) => ` ${line}`).join(`
8696
+ `);
8697
+ console.log(`\x1B[90m${indented}\x1B[0m`);
8698
+ }
8699
+ if (result.stderr) {
8700
+ console.log(`\x1B[90m ⎿ (stderr)\x1B[0m`);
8701
+ const indented = result.stderr.split(`
8702
+ `).map((line) => ` ${line}`).join(`
8703
+ `);
8704
+ console.log(`\x1B[90m${indented}\x1B[0m`);
8705
+ }
8191
8706
  resolve2(result);
8192
8707
  }
8193
8708
  };
8194
- console.log(`\x1B[90m[hook] Running: ${command}\x1B[0m`);
8195
8709
  let child;
8196
8710
  try {
8197
8711
  child = trySpawnWithLauncher(launcher, workingDirectory, input);
@@ -8369,14 +8883,27 @@ function mergeHooksConfigs(global2, project, projectLocal = {}) {
8369
8883
  ...Object.keys(projectLocal)
8370
8884
  ]);
8371
8885
  for (const event of allEvents) {
8372
- const globalMatchers = global2[event] || [];
8373
- const projectMatchers = project[event] || [];
8374
- const projectLocalMatchers = projectLocal[event] || [];
8375
- merged[event] = [
8376
- ...projectLocalMatchers,
8377
- ...projectMatchers,
8378
- ...globalMatchers
8379
- ];
8886
+ if (isToolEvent(event)) {
8887
+ const toolEvent = event;
8888
+ const globalMatchers = global2[toolEvent] || [];
8889
+ const projectMatchers = project[toolEvent] || [];
8890
+ const projectLocalMatchers = projectLocal[toolEvent] || [];
8891
+ merged[toolEvent] = [
8892
+ ...projectLocalMatchers,
8893
+ ...projectMatchers,
8894
+ ...globalMatchers
8895
+ ];
8896
+ } else {
8897
+ const simpleEvent = event;
8898
+ const globalMatchers = global2[simpleEvent] || [];
8899
+ const projectMatchers = project[simpleEvent] || [];
8900
+ const projectLocalMatchers = projectLocal[simpleEvent] || [];
8901
+ merged[simpleEvent] = [
8902
+ ...projectLocalMatchers,
8903
+ ...projectMatchers,
8904
+ ...globalMatchers
8905
+ ];
8906
+ }
8380
8907
  }
8381
8908
  return merged;
8382
8909
  }
@@ -8399,23 +8926,36 @@ function matchesTool(pattern, toolName) {
8399
8926
  return pattern === toolName;
8400
8927
  }
8401
8928
  function getMatchingHooks(config, event, toolName) {
8402
- const matchers = config[event];
8403
- if (!matchers || matchers.length === 0) {
8404
- return [];
8405
- }
8406
- const hooks = [];
8407
- for (const matcher of matchers) {
8408
- if (!toolName || matchesTool(matcher.matcher, toolName)) {
8929
+ if (isToolEvent(event)) {
8930
+ const matchers = config[event];
8931
+ if (!matchers || matchers.length === 0) {
8932
+ return [];
8933
+ }
8934
+ const hooks = [];
8935
+ for (const matcher of matchers) {
8936
+ if (!toolName || matchesTool(matcher.matcher, toolName)) {
8937
+ hooks.push(...matcher.hooks);
8938
+ }
8939
+ }
8940
+ return hooks;
8941
+ } else {
8942
+ const matchers = config[event];
8943
+ if (!matchers || matchers.length === 0) {
8944
+ return [];
8945
+ }
8946
+ const hooks = [];
8947
+ for (const matcher of matchers) {
8409
8948
  hooks.push(...matcher.hooks);
8410
8949
  }
8950
+ return hooks;
8411
8951
  }
8412
- return hooks;
8413
8952
  }
8414
8953
  async function getHooksForEvent(event, toolName, workingDirectory = process.cwd()) {
8415
8954
  const config = await loadHooks(workingDirectory);
8416
8955
  return getMatchingHooks(config, event, toolName);
8417
8956
  }
8418
8957
  var init_loader = __esm(async () => {
8958
+ init_types();
8419
8959
  await init_settings_manager();
8420
8960
  });
8421
8961
 
@@ -59815,7 +60355,7 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
59815
60355
  } else {
59816
60356
  stream2 = await client.conversations.messages.create(conversationId, {
59817
60357
  messages,
59818
- streaming: true,
60358
+ stream: true,
59819
60359
  stream_tokens: opts.streamTokens ?? true,
59820
60360
  background: opts.background ?? true,
59821
60361
  client_tools: getClientToolsFromRegistry()
@@ -61089,7 +61629,7 @@ function sortChronological2(messages) {
61089
61629
  async function getResumeData2(client, agent, conversationId) {
61090
61630
  try {
61091
61631
  let inContextMessageIds;
61092
- let messages;
61632
+ let messages = [];
61093
61633
  const useConversationsApi = conversationId && conversationId !== "default";
61094
61634
  if (process.env.DEBUG) {
61095
61635
  console.log(`[DEBUG] getResumeData: conversationId=${conversationId}, useConversationsApi=${useConversationsApi}, agentId=${agent.id}`);
@@ -61100,12 +61640,16 @@ async function getResumeData2(client, agent, conversationId) {
61100
61640
  if (!inContextMessageIds || inContextMessageIds.length === 0) {
61101
61641
  debugWarn("check-approval", "No in-context messages - no pending approvals");
61102
61642
  if (isBackfillEnabled2()) {
61103
- const backfill = await client.conversations.messages.list(conversationId, { limit: MESSAGE_HISTORY_LIMIT2, order: "desc" });
61104
- return {
61105
- pendingApproval: null,
61106
- pendingApprovals: [],
61107
- messageHistory: sortChronological2(backfill.getPaginatedItems())
61108
- };
61643
+ try {
61644
+ const backfill = await client.conversations.messages.list(conversationId, { limit: MESSAGE_HISTORY_LIMIT2, order: "desc" });
61645
+ return {
61646
+ pendingApproval: null,
61647
+ pendingApprovals: [],
61648
+ messageHistory: sortChronological2(backfill.getPaginatedItems())
61649
+ };
61650
+ } catch (backfillError) {
61651
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
61652
+ }
61109
61653
  }
61110
61654
  return {
61111
61655
  pendingApproval: null,
@@ -61118,11 +61662,17 @@ async function getResumeData2(client, agent, conversationId) {
61118
61662
  throw new Error("Expected at least one in-context message");
61119
61663
  }
61120
61664
  const retrievedMessages = await client.messages.retrieve(lastInContextId);
61121
- const backfillPage = isBackfillEnabled2() ? await client.conversations.messages.list(conversationId, {
61122
- limit: MESSAGE_HISTORY_LIMIT2,
61123
- order: "desc"
61124
- }) : null;
61125
- messages = backfillPage ? sortChronological2(backfillPage.getPaginatedItems()) : [];
61665
+ if (isBackfillEnabled2()) {
61666
+ try {
61667
+ const backfillPage = await client.conversations.messages.list(conversationId, {
61668
+ limit: MESSAGE_HISTORY_LIMIT2,
61669
+ order: "desc"
61670
+ });
61671
+ messages = sortChronological2(backfillPage.getPaginatedItems());
61672
+ } catch (backfillError) {
61673
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
61674
+ }
61675
+ }
61126
61676
  const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
61127
61677
  if (messageToCheck) {
61128
61678
  debugWarn("check-approval", `Found last in-context message: ${messageToCheck.id} (type: ${messageToCheck.message_type})` + (retrievedMessages.length > 1 ? ` - had ${retrievedMessages.length} variants` : ""));
@@ -61157,14 +61707,20 @@ async function getResumeData2(client, agent, conversationId) {
61157
61707
  throw new Error("Expected at least one in-context message");
61158
61708
  }
61159
61709
  const retrievedMessages = await client.messages.retrieve(lastInContextId);
61160
- const messagesPage = isBackfillEnabled2() ? await client.agents.messages.list(agent.id, {
61161
- limit: MESSAGE_HISTORY_LIMIT2,
61162
- order: "desc",
61163
- conversation_id: "default"
61164
- }) : null;
61165
- messages = messagesPage ? sortChronological2(messagesPage.items) : [];
61166
- if (process.env.DEBUG && messagesPage) {
61167
- console.log(`[DEBUG] agents.messages.list(conversation_id=default) returned ${messagesPage.items.length} messages`);
61710
+ if (isBackfillEnabled2()) {
61711
+ try {
61712
+ const messagesPage = await client.agents.messages.list(agent.id, {
61713
+ limit: MESSAGE_HISTORY_LIMIT2,
61714
+ order: "desc",
61715
+ conversation_id: "default"
61716
+ });
61717
+ messages = sortChronological2(messagesPage.items);
61718
+ if (process.env.DEBUG) {
61719
+ console.log(`[DEBUG] agents.messages.list(conversation_id=default) returned ${messagesPage.items.length} messages`);
61720
+ }
61721
+ } catch (backfillError) {
61722
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
61723
+ }
61168
61724
  }
61169
61725
  const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
61170
61726
  if (messageToCheck) {
@@ -71670,24 +72226,61 @@ async function addHookMatcher(event, matcher, location, workingDirectory = proce
71670
72226
  hooks[event] = [];
71671
72227
  }
71672
72228
  const eventMatchers = hooks[event];
71673
- if (eventMatchers) {
71674
- eventMatchers.push(matcher);
71675
- }
72229
+ eventMatchers.push(matcher);
71676
72230
  await saveHooksToLocation(hooks, location, workingDirectory);
71677
72231
  }
71678
- async function removeHookMatcher(event, matcherIndex, location, workingDirectory = process.cwd()) {
72232
+ async function addSimpleHookMatcher(event, matcher, location, workingDirectory = process.cwd()) {
71679
72233
  const hooks = loadHooksFromLocation(location, workingDirectory);
71680
- const eventMatchers = hooks[event];
71681
- if (!eventMatchers || matcherIndex < 0 || matcherIndex >= eventMatchers.length) {
71682
- throw new Error(`Invalid matcher index ${matcherIndex} for event ${event}`);
72234
+ if (!hooks[event]) {
72235
+ hooks[event] = [];
71683
72236
  }
71684
- eventMatchers.splice(matcherIndex, 1);
71685
- if (eventMatchers.length === 0) {
71686
- delete hooks[event];
72237
+ const eventMatchers = hooks[event];
72238
+ eventMatchers.push(matcher);
72239
+ await saveHooksToLocation(hooks, location, workingDirectory);
72240
+ }
72241
+ async function removeHook(event, index, location, workingDirectory = process.cwd()) {
72242
+ const hooks = loadHooksFromLocation(location, workingDirectory);
72243
+ if (isToolEvent(event)) {
72244
+ const eventMatchers = hooks[event];
72245
+ if (!eventMatchers || index < 0 || index >= eventMatchers.length) {
72246
+ throw new Error(`Invalid matcher index ${index} for event ${event}`);
72247
+ }
72248
+ eventMatchers.splice(index, 1);
72249
+ if (eventMatchers.length === 0) {
72250
+ delete hooks[event];
72251
+ }
72252
+ } else {
72253
+ const eventMatchers = hooks[event];
72254
+ if (!eventMatchers || index < 0 || index >= eventMatchers.length) {
72255
+ throw new Error(`Invalid matcher index ${index} for event ${event}`);
72256
+ }
72257
+ eventMatchers.splice(index, 1);
72258
+ if (eventMatchers.length === 0) {
72259
+ delete hooks[event];
72260
+ }
71687
72261
  }
71688
72262
  await saveHooksToLocation(hooks, location, workingDirectory);
71689
72263
  }
71690
- function loadHooksWithSource(event, workingDirectory = process.cwd()) {
72264
+ function loadMatchersWithSource(event, workingDirectory = process.cwd()) {
72265
+ const result = [];
72266
+ const locations = ["project-local", "project", "user"];
72267
+ for (const location of locations) {
72268
+ const hooks = loadHooksFromLocation(location, workingDirectory);
72269
+ const matchers = hooks[event] || [];
72270
+ for (let i = 0;i < matchers.length; i++) {
72271
+ const matcher = matchers[i];
72272
+ if (matcher) {
72273
+ result.push({
72274
+ ...matcher,
72275
+ source: location,
72276
+ sourceIndex: i
72277
+ });
72278
+ }
72279
+ }
72280
+ }
72281
+ return result;
72282
+ }
72283
+ function loadSimpleMatchersWithSource(event, workingDirectory = process.cwd()) {
71691
72284
  const result = [];
71692
72285
  const locations = ["project-local", "project", "user"];
71693
72286
  for (const location of locations) {
@@ -71712,9 +72305,16 @@ function countTotalHooks(workingDirectory = process.cwd()) {
71712
72305
  for (const location of locations) {
71713
72306
  const hooks = loadHooksFromLocation(location, workingDirectory);
71714
72307
  for (const event of Object.keys(hooks)) {
71715
- const matchers = hooks[event] || [];
71716
- for (const matcher of matchers) {
71717
- count += matcher.hooks.length;
72308
+ if (isToolEvent(event)) {
72309
+ const matchers = hooks[event] || [];
72310
+ for (const matcher of matchers) {
72311
+ count += matcher.hooks.length;
72312
+ }
72313
+ } else {
72314
+ const matchers = hooks[event] || [];
72315
+ for (const matcher of matchers) {
72316
+ count += matcher.hooks.length;
72317
+ }
71718
72318
  }
71719
72319
  }
71720
72320
  }
@@ -71725,14 +72325,22 @@ function countHooksForEvent(event, workingDirectory = process.cwd()) {
71725
72325
  const locations = ["project-local", "project", "user"];
71726
72326
  for (const location of locations) {
71727
72327
  const hooks = loadHooksFromLocation(location, workingDirectory);
71728
- const matchers = hooks[event] || [];
71729
- for (const matcher of matchers) {
71730
- count += matcher.hooks.length;
72328
+ if (isToolEvent(event)) {
72329
+ const matchers = hooks[event] || [];
72330
+ for (const matcher of matchers) {
72331
+ count += matcher.hooks.length;
72332
+ }
72333
+ } else {
72334
+ const matchers = hooks[event] || [];
72335
+ for (const matcher of matchers) {
72336
+ count += matcher.hooks.length;
72337
+ }
71731
72338
  }
71732
72339
  }
71733
72340
  return count;
71734
72341
  }
71735
72342
  var init_writer = __esm(async () => {
72343
+ init_types();
71736
72344
  await init_settings_manager();
71737
72345
  });
71738
72346
 
@@ -71760,6 +72368,7 @@ function boxBottom(width) {
71760
72368
  }
71761
72369
  var import_react57, jsx_dev_runtime34, BOX_TOP_LEFT = "╭", BOX_TOP_RIGHT = "╮", BOX_BOTTOM_LEFT = "╰", BOX_BOTTOM_RIGHT = "╯", BOX_HORIZONTAL = "─", BOX_VERTICAL = "│", HOOK_EVENTS, TOOL_NAMES3, SAVE_LOCATIONS, HooksManager;
71762
72370
  var init_HooksManager = __esm(async () => {
72371
+ init_types();
71763
72372
  init_useTerminalWidth();
71764
72373
  init_colors();
71765
72374
  await __promiseAll([
@@ -71820,13 +72429,14 @@ var init_HooksManager = __esm(async () => {
71820
72429
  const [screen, setScreen] = import_react57.useState("events");
71821
72430
  const [selectedIndex, setSelectedIndex] = import_react57.useState(0);
71822
72431
  const [selectedEvent, setSelectedEvent] = import_react57.useState(null);
71823
- const [matchers, setMatchers] = import_react57.useState([]);
72432
+ const [hooks, setHooks] = import_react57.useState([]);
71824
72433
  const [totalHooks, setTotalHooks] = import_react57.useState(0);
71825
72434
  const [newMatcher, setNewMatcher] = import_react57.useState("");
71826
72435
  const [newCommand, setNewCommand] = import_react57.useState("");
71827
72436
  const [selectedLocation, setSelectedLocation] = import_react57.useState(0);
71828
- const [deleteMatcherIndex, setDeleteMatcherIndex] = import_react57.useState(-1);
72437
+ const [deleteHookIndex, setDeleteHookIndex] = import_react57.useState(-1);
71829
72438
  const [deleteConfirmIndex, setDeleteConfirmIndex] = import_react57.useState(1);
72439
+ const isCurrentToolEvent = selectedEvent ? isToolEvent(selectedEvent) : false;
71830
72440
  const refreshCounts = import_react57.useCallback(() => {
71831
72441
  setTotalHooks(countTotalHooks());
71832
72442
  }, []);
@@ -71835,9 +72445,12 @@ var init_HooksManager = __esm(async () => {
71835
72445
  refreshCounts();
71836
72446
  }
71837
72447
  }, [screen, refreshCounts]);
71838
- const loadMatchers = import_react57.useCallback((event) => {
71839
- const loaded = loadHooksWithSource(event);
71840
- setMatchers(loaded);
72448
+ const loadHooks2 = import_react57.useCallback((event) => {
72449
+ if (isToolEvent(event)) {
72450
+ setHooks(loadMatchersWithSource(event));
72451
+ } else {
72452
+ setHooks(loadSimpleMatchersWithSource(event));
72453
+ }
71841
72454
  }, []);
71842
72455
  const handleAddHook = import_react57.useCallback(async () => {
71843
72456
  if (!selectedEvent || !newCommand.trim())
@@ -71845,45 +72458,46 @@ var init_HooksManager = __esm(async () => {
71845
72458
  const location = SAVE_LOCATIONS[selectedLocation]?.location;
71846
72459
  if (!location)
71847
72460
  return;
71848
- const matcher = {
71849
- matcher: newMatcher.trim() || "*",
71850
- hooks: [{ type: "command", command: newCommand.trim() }]
71851
- };
71852
- await addHookMatcher(selectedEvent, matcher, location);
71853
- loadMatchers(selectedEvent);
72461
+ if (isToolEvent(selectedEvent)) {
72462
+ const matcher = {
72463
+ matcher: newMatcher.trim() || "*",
72464
+ hooks: [{ type: "command", command: newCommand.trim() }]
72465
+ };
72466
+ await addHookMatcher(selectedEvent, matcher, location);
72467
+ } else {
72468
+ const matcher = {
72469
+ hooks: [{ type: "command", command: newCommand.trim() }]
72470
+ };
72471
+ await addSimpleHookMatcher(selectedEvent, matcher, location);
72472
+ }
72473
+ loadHooks2(selectedEvent);
71854
72474
  refreshCounts();
71855
72475
  setNewMatcher("");
71856
72476
  setNewCommand("");
71857
72477
  setSelectedLocation(0);
71858
- setScreen("matchers");
72478
+ setScreen("hooks-list");
71859
72479
  setSelectedIndex(0);
71860
72480
  }, [
71861
72481
  selectedEvent,
71862
72482
  newMatcher,
71863
72483
  newCommand,
71864
72484
  selectedLocation,
71865
- loadMatchers,
72485
+ loadHooks2,
71866
72486
  refreshCounts
71867
72487
  ]);
71868
72488
  const handleDeleteHook = import_react57.useCallback(async () => {
71869
- if (deleteMatcherIndex < 0 || !selectedEvent)
72489
+ if (deleteHookIndex < 0 || !selectedEvent)
71870
72490
  return;
71871
- const matcher = matchers[deleteMatcherIndex];
71872
- if (!matcher)
72491
+ const hook = hooks[deleteHookIndex];
72492
+ if (!hook)
71873
72493
  return;
71874
- await removeHookMatcher(selectedEvent, matcher.sourceIndex, matcher.source);
71875
- loadMatchers(selectedEvent);
72494
+ await removeHook(selectedEvent, hook.sourceIndex, hook.source);
72495
+ loadHooks2(selectedEvent);
71876
72496
  refreshCounts();
71877
- setDeleteMatcherIndex(-1);
71878
- setScreen("matchers");
72497
+ setDeleteHookIndex(-1);
72498
+ setScreen("hooks-list");
71879
72499
  setSelectedIndex(0);
71880
- }, [
71881
- deleteMatcherIndex,
71882
- selectedEvent,
71883
- matchers,
71884
- loadMatchers,
71885
- refreshCounts
71886
- ]);
72500
+ }, [deleteHookIndex, selectedEvent, hooks, loadHooks2, refreshCounts]);
71887
72501
  use_input_default((input, key) => {
71888
72502
  if (key.ctrl && input === "c") {
71889
72503
  onClose();
@@ -71898,26 +72512,31 @@ var init_HooksManager = __esm(async () => {
71898
72512
  const selected = HOOK_EVENTS[selectedIndex];
71899
72513
  if (selected) {
71900
72514
  setSelectedEvent(selected.event);
71901
- loadMatchers(selected.event);
71902
- setScreen("matchers");
72515
+ loadHooks2(selected.event);
72516
+ setScreen("hooks-list");
71903
72517
  setSelectedIndex(0);
71904
72518
  }
71905
72519
  } else if (key.escape) {
71906
72520
  onClose();
71907
72521
  }
71908
- } else if (screen === "matchers") {
71909
- const itemCount = matchers.length + 1;
72522
+ } else if (screen === "hooks-list") {
72523
+ const itemCount = hooks.length + 1;
71910
72524
  if (key.upArrow) {
71911
72525
  setSelectedIndex((prev) => Math.max(0, prev - 1));
71912
72526
  } else if (key.downArrow) {
71913
72527
  setSelectedIndex((prev) => Math.min(itemCount - 1, prev + 1));
71914
72528
  } else if (key.return) {
71915
72529
  if (selectedIndex === 0) {
71916
- setScreen("add-matcher");
71917
- setNewMatcher("");
72530
+ if (isCurrentToolEvent) {
72531
+ setScreen("add-matcher");
72532
+ setNewMatcher("");
72533
+ } else {
72534
+ setScreen("add-command");
72535
+ setNewCommand("");
72536
+ }
71918
72537
  } else {}
71919
72538
  } else if ((input === "d" || input === "D") && selectedIndex > 0) {
71920
- setDeleteMatcherIndex(selectedIndex - 1);
72539
+ setDeleteHookIndex(selectedIndex - 1);
71921
72540
  setDeleteConfirmIndex(1);
71922
72541
  setScreen("delete-confirm");
71923
72542
  } else if (key.escape) {
@@ -71930,7 +72549,7 @@ var init_HooksManager = __esm(async () => {
71930
72549
  setScreen("add-command");
71931
72550
  setNewCommand("");
71932
72551
  } else if (key.escape) {
71933
- setScreen("matchers");
72552
+ setScreen("hooks-list");
71934
72553
  setSelectedIndex(0);
71935
72554
  setNewMatcher("");
71936
72555
  }
@@ -71939,7 +72558,12 @@ var init_HooksManager = __esm(async () => {
71939
72558
  setScreen("save-location");
71940
72559
  setSelectedLocation(0);
71941
72560
  } else if (key.escape) {
71942
- setScreen("add-matcher");
72561
+ if (isCurrentToolEvent) {
72562
+ setScreen("add-matcher");
72563
+ } else {
72564
+ setScreen("hooks-list");
72565
+ setSelectedIndex(0);
72566
+ }
71943
72567
  }
71944
72568
  } else if (screen === "save-location") {
71945
72569
  if (key.upArrow) {
@@ -71958,10 +72582,10 @@ var init_HooksManager = __esm(async () => {
71958
72582
  if (deleteConfirmIndex === 0) {
71959
72583
  handleDeleteHook();
71960
72584
  } else {
71961
- setScreen("matchers");
72585
+ setScreen("hooks-list");
71962
72586
  }
71963
72587
  } else if (key.escape) {
71964
- setScreen("matchers");
72588
+ setScreen("hooks-list");
71965
72589
  }
71966
72590
  }
71967
72591
  });
@@ -72023,7 +72647,9 @@ var init_HooksManager = __esm(async () => {
72023
72647
  ]
72024
72648
  }, undefined, true, undefined, this);
72025
72649
  }
72026
- if (screen === "matchers" && selectedEvent) {
72650
+ if (screen === "hooks-list" && selectedEvent) {
72651
+ const title = isCurrentToolEvent ? ` ${selectedEvent} - Tool Matchers ` : ` ${selectedEvent} - Hooks `;
72652
+ const addLabel = isCurrentToolEvent ? "+ Add new matcher..." : "+ Add new hook...";
72027
72653
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
72028
72654
  flexDirection: "column",
72029
72655
  paddingX: 1,
@@ -72032,27 +72658,46 @@ var init_HooksManager = __esm(async () => {
72032
72658
  children: boxTop(boxWidth)
72033
72659
  }, undefined, false, undefined, this),
72034
72660
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72035
- children: boxLine(` ${selectedEvent} - Tool Matchers `, boxWidth)
72661
+ children: boxLine(title, boxWidth)
72036
72662
  }, undefined, false, undefined, this),
72037
72663
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72038
72664
  children: boxBottom(boxWidth)
72039
72665
  }, undefined, false, undefined, this),
72040
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72041
- dimColor: true,
72042
- children: "Input to command is JSON of tool call arguments."
72043
- }, undefined, false, undefined, this),
72044
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72045
- dimColor: true,
72046
- children: "Exit code 0 - stdout/stderr not shown"
72047
- }, undefined, false, undefined, this),
72048
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72049
- dimColor: true,
72050
- children: "Exit code 2 - show stderr to model and block tool call"
72051
- }, undefined, false, undefined, this),
72052
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72053
- dimColor: true,
72054
- children: "Other exit codes - show stderr to user only but continue"
72055
- }, undefined, false, undefined, this),
72666
+ isCurrentToolEvent ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(jsx_dev_runtime34.Fragment, {
72667
+ children: [
72668
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72669
+ dimColor: true,
72670
+ children: "Input to command is JSON of tool call arguments."
72671
+ }, undefined, false, undefined, this),
72672
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72673
+ dimColor: true,
72674
+ children: "Exit code 0 - stdout/stderr not shown"
72675
+ }, undefined, false, undefined, this),
72676
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72677
+ dimColor: true,
72678
+ children: "Exit code 2 - show stderr to model and block tool call"
72679
+ }, undefined, false, undefined, this),
72680
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72681
+ dimColor: true,
72682
+ children: "Other exit codes - show stderr to user only but continue"
72683
+ }, undefined, false, undefined, this)
72684
+ ]
72685
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(jsx_dev_runtime34.Fragment, {
72686
+ children: [
72687
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72688
+ dimColor: true,
72689
+ children: "Exit code 0 - success, continue"
72690
+ }, undefined, false, undefined, this),
72691
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72692
+ dimColor: true,
72693
+ children: "Exit code 2 - show stderr to model and block"
72694
+ }, undefined, false, undefined, this),
72695
+ /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72696
+ dimColor: true,
72697
+ children: "Other exit codes - show stderr to user only"
72698
+ }, undefined, false, undefined, this)
72699
+ ]
72700
+ }, undefined, true, undefined, this),
72056
72701
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72057
72702
  children: " "
72058
72703
  }, undefined, false, undefined, this),
@@ -72068,16 +72713,17 @@ var init_HooksManager = __esm(async () => {
72068
72713
  }, undefined, true, undefined, this),
72069
72714
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72070
72715
  color: "green",
72071
- children: "+ Add new matcher..."
72716
+ children: addLabel
72072
72717
  }, undefined, false, undefined, this)
72073
72718
  ]
72074
72719
  }, undefined, true, undefined, this),
72075
- matchers.map((matcher, index) => {
72720
+ hooks.map((hook, index) => {
72076
72721
  const isSelected = index + 1 === selectedIndex;
72077
72722
  const prefix = isSelected ? "❯" : " ";
72078
- const sourceLabel = `[${getSourceLabel(matcher.source)}]`;
72079
- const matcherPattern = matcher.matcher || "*";
72080
- const command = matcher.hooks[0]?.command || "";
72723
+ const sourceLabel = `[${getSourceLabel(hook.source)}]`;
72724
+ const isToolMatcher = "matcher" in hook;
72725
+ const matcherPattern = isToolMatcher ? hook.matcher || "*" : null;
72726
+ const command = "hooks" in hook ? hook.hooks[0]?.command || "" : "";
72081
72727
  const truncatedCommand = command.length > 30 ? `${command.slice(0, 27)}...` : command;
72082
72728
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72083
72729
  children: [
@@ -72095,7 +72741,7 @@ var init_HooksManager = __esm(async () => {
72095
72741
  color: "cyan",
72096
72742
  children: sourceLabel
72097
72743
  }, undefined, false, undefined, this),
72098
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72744
+ matcherPattern !== null && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72099
72745
  children: [
72100
72746
  " ",
72101
72747
  matcherPattern.padEnd(12),
@@ -72107,7 +72753,7 @@ var init_HooksManager = __esm(async () => {
72107
72753
  children: truncatedCommand
72108
72754
  }, undefined, false, undefined, this)
72109
72755
  ]
72110
- }, `${matcher.source}-${index}`, true, undefined, this);
72756
+ }, `${hook.source}-${index}`, true, undefined, this);
72111
72757
  }),
72112
72758
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72113
72759
  children: " "
@@ -72213,6 +72859,7 @@ var init_HooksManager = __esm(async () => {
72213
72859
  }, undefined, true, undefined, this);
72214
72860
  }
72215
72861
  if (screen === "add-command" && selectedEvent) {
72862
+ const title = isCurrentToolEvent ? ` Add new matcher for ${selectedEvent} ` : ` Add new hook for ${selectedEvent} `;
72216
72863
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
72217
72864
  flexDirection: "column",
72218
72865
  paddingX: 1,
@@ -72221,18 +72868,18 @@ var init_HooksManager = __esm(async () => {
72221
72868
  children: boxTop(boxWidth)
72222
72869
  }, undefined, false, undefined, this),
72223
72870
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72224
- children: boxLine(` Add new matcher for ${selectedEvent} `, boxWidth)
72871
+ children: boxLine(title, boxWidth)
72225
72872
  }, undefined, false, undefined, this),
72226
72873
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72227
72874
  children: boxBottom(boxWidth)
72228
72875
  }, undefined, false, undefined, this),
72229
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72876
+ isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72230
72877
  children: [
72231
72878
  "Matcher: ",
72232
72879
  newMatcher || "*"
72233
72880
  ]
72234
72881
  }, undefined, true, undefined, this),
72235
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72882
+ isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72236
72883
  children: " "
72237
72884
  }, undefined, false, undefined, this),
72238
72885
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
@@ -72292,7 +72939,7 @@ var init_HooksManager = __esm(async () => {
72292
72939
  selectedEvent
72293
72940
  ]
72294
72941
  }, undefined, true, undefined, this),
72295
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72942
+ isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72296
72943
  children: [
72297
72944
  "Matcher: ",
72298
72945
  newMatcher || "*"
@@ -72348,8 +72995,11 @@ var init_HooksManager = __esm(async () => {
72348
72995
  ]
72349
72996
  }, undefined, true, undefined, this);
72350
72997
  }
72351
- if (screen === "delete-confirm" && deleteMatcherIndex >= 0) {
72352
- const matcher = matchers[deleteMatcherIndex];
72998
+ if (screen === "delete-confirm" && deleteHookIndex >= 0) {
72999
+ const hook = hooks[deleteHookIndex];
73000
+ const isToolMatcher = hook && "matcher" in hook;
73001
+ const matcherPattern = isToolMatcher ? hook.matcher || "*" : null;
73002
+ const command = hook && "hooks" in hook ? hook.hooks[0]?.command : "";
72353
73003
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
72354
73004
  flexDirection: "column",
72355
73005
  paddingX: 1,
@@ -72366,22 +73016,22 @@ var init_HooksManager = __esm(async () => {
72366
73016
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72367
73017
  children: " "
72368
73018
  }, undefined, false, undefined, this),
72369
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
73019
+ matcherPattern !== null && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72370
73020
  children: [
72371
73021
  "Matcher: ",
72372
- matcher?.matcher || "*"
73022
+ matcherPattern
72373
73023
  ]
72374
73024
  }, undefined, true, undefined, this),
72375
73025
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72376
73026
  children: [
72377
73027
  "Command: ",
72378
- matcher?.hooks[0]?.command
73028
+ command
72379
73029
  ]
72380
73030
  }, undefined, true, undefined, this),
72381
73031
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
72382
73032
  children: [
72383
73033
  "Source: ",
72384
- matcher ? getSourceLabel(matcher.source) : ""
73034
+ hook ? getSourceLabel(hook.source) : ""
72385
73035
  ]
72386
73036
  }, undefined, true, undefined, this),
72387
73037
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
@@ -75371,18 +76021,46 @@ var init_AgentInfoBar = __esm(async () => {
75371
76021
  color: "gray",
75372
76022
  children: " (type /pin to pin agent)"
75373
76023
  }, undefined, false, undefined, this),
75374
- /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76024
+ isCloudUser && adeUrl && !isTmux && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(jsx_dev_runtime35.Fragment, {
76025
+ children: [
76026
+ /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76027
+ dimColor: true,
76028
+ children: " · "
76029
+ }, undefined, false, undefined, this),
76030
+ /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(dist_default4, {
76031
+ url: adeUrl,
76032
+ children: /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76033
+ children: "Open in ADE ↗"
76034
+ }, undefined, false, undefined, this)
76035
+ }, undefined, false, undefined, this)
76036
+ ]
76037
+ }, undefined, true, undefined, this),
76038
+ isCloudUser && adeUrl && isTmux && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75375
76039
  dimColor: true,
75376
76040
  children: [
75377
- " · ",
75378
- agentId
76041
+ " · Open in ADE: ",
76042
+ adeUrl
75379
76043
  ]
75380
76044
  }, undefined, true, undefined, this),
75381
- conversationId && conversationId !== "default" && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76045
+ isCloudUser && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(jsx_dev_runtime35.Fragment, {
76046
+ children: [
76047
+ /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76048
+ dimColor: true,
76049
+ children: " · "
76050
+ }, undefined, false, undefined, this),
76051
+ /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(dist_default4, {
76052
+ url: "https://app.letta.com/settings/organization/usage",
76053
+ children: /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76054
+ children: "View usage ↗"
76055
+ }, undefined, false, undefined, this)
76056
+ }, undefined, false, undefined, this)
76057
+ ]
76058
+ }, undefined, true, undefined, this),
76059
+ !isCloudUser && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75382
76060
  dimColor: true,
75383
76061
  children: [
75384
76062
  " · ",
75385
- conversationId
76063
+ serverUrl
75386
76064
  ]
75387
76065
  }, undefined, true, undefined, this)
75388
76066
  ]
@@ -75391,34 +76069,18 @@ var init_AgentInfoBar = __esm(async () => {
75391
76069
  children: [
75392
76070
  /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75393
76071
  dimColor: true,
75394
- children: " "
75395
- }, undefined, false, undefined, this),
75396
- isCloudUser && adeUrl && !isTmux && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(dist_default4, {
75397
- url: adeUrl,
75398
- children: /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75399
- children: "Open in ADE ↗"
75400
- }, undefined, false, undefined, this)
75401
- }, undefined, false, undefined, this),
75402
- isCloudUser && adeUrl && isTmux && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75403
76072
  children: [
75404
- "Open in ADE: ",
75405
- adeUrl
76073
+ " ",
76074
+ agentId
75406
76075
  ]
75407
76076
  }, undefined, true, undefined, this),
75408
- isCloudUser && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75409
- dimColor: true,
75410
- children: " · "
75411
- }, undefined, false, undefined, this),
75412
- isCloudUser && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(dist_default4, {
75413
- url: "https://app.letta.com/settings/organization/usage",
75414
- children: /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75415
- children: "View usage ↗"
75416
- }, undefined, false, undefined, this)
75417
- }, undefined, false, undefined, this),
75418
- !isCloudUser && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
76077
+ conversationId && conversationId !== "default" && /* @__PURE__ */ jsx_dev_runtime35.jsxDEV(Text, {
75419
76078
  dimColor: true,
75420
- children: serverUrl
75421
- }, undefined, false, undefined, this)
76079
+ children: [
76080
+ " · ",
76081
+ conversationId
76082
+ ]
76083
+ }, undefined, true, undefined, this)
75422
76084
  ]
75423
76085
  }, undefined, true, undefined, this)
75424
76086
  ]
@@ -78516,26 +79178,6 @@ var init_MemoryTabViewer = __esm(async () => {
78516
79178
  });
78517
79179
 
78518
79180
  // src/cli/components/MessageSearch.tsx
78519
- function formatRelativeTime5(dateStr) {
78520
- if (!dateStr)
78521
- return "";
78522
- const date = new Date(dateStr);
78523
- const now = new Date;
78524
- const diffMs = now.getTime() - date.getTime();
78525
- const diffMins = Math.floor(diffMs / 60000);
78526
- const diffHours = Math.floor(diffMs / 3600000);
78527
- const diffDays = Math.floor(diffMs / 86400000);
78528
- const diffWeeks = Math.floor(diffDays / 7);
78529
- if (diffMins < 1)
78530
- return "just now";
78531
- if (diffMins < 60)
78532
- return `${diffMins}m ago`;
78533
- if (diffHours < 24)
78534
- return `${diffHours}h ago`;
78535
- if (diffDays < 7)
78536
- return `${diffDays}d ago`;
78537
- return `${diffWeeks}w ago`;
78538
- }
78539
79181
  function formatLocalTime(dateStr) {
78540
79182
  if (!dateStr)
78541
79183
  return "";
@@ -78586,34 +79228,102 @@ function getMessageText(msg) {
78586
79228
  }
78587
79229
  return `[${msg.message_type || "unknown"}]`;
78588
79230
  }
78589
- function MessageSearch({ onClose, initialQuery }) {
79231
+ function MessageSearch({
79232
+ onClose,
79233
+ initialQuery,
79234
+ agentId,
79235
+ conversationId,
79236
+ onOpenConversation
79237
+ }) {
78590
79238
  const terminalWidth = useTerminalWidth();
78591
79239
  const [searchInput, setSearchInput] = import_react70.useState(initialQuery ?? "");
78592
79240
  const [activeQuery, setActiveQuery] = import_react70.useState(initialQuery ?? "");
78593
79241
  const [searchMode, setSearchMode] = import_react70.useState("hybrid");
79242
+ const [searchRange, setSearchRange] = import_react70.useState("all");
78594
79243
  const [results, setResults] = import_react70.useState([]);
78595
79244
  const [loading, setLoading] = import_react70.useState(false);
78596
79245
  const [error, setError] = import_react70.useState(null);
78597
- const [currentPage, setCurrentPage] = import_react70.useState(0);
78598
79246
  const [selectedIndex, setSelectedIndex] = import_react70.useState(0);
79247
+ const [expandedMessage, setExpandedMessage] = import_react70.useState(null);
78599
79248
  const clientRef = import_react70.useRef(null);
78600
- const executeSearch = import_react70.useCallback(async (query, mode) => {
79249
+ const resultsCache = import_react70.useRef(new Map);
79250
+ const getCacheKey = import_react70.useCallback((query, mode, range2) => {
79251
+ const rangeKey = range2 === "agent" ? agentId || "no-agent" : range2 === "conv" ? conversationId || "no-conv" : "all";
79252
+ return `${query.trim()}-${mode}-${rangeKey}`;
79253
+ }, [agentId, conversationId]);
79254
+ const fetchSearchResults = import_react70.useCallback(async (client, query, mode, range2) => {
79255
+ const body = {
79256
+ query: query.trim(),
79257
+ search_mode: mode,
79258
+ limit: SEARCH_LIMIT
79259
+ };
79260
+ if (range2 === "agent" && agentId) {
79261
+ body.agent_id = agentId;
79262
+ } else if (range2 === "conv" && conversationId) {
79263
+ body.conversation_id = conversationId;
79264
+ }
79265
+ const searchResults = await client.post("/v1/messages/search", { body });
79266
+ return searchResults;
79267
+ }, [agentId, conversationId]);
79268
+ const executeSearch = import_react70.useCallback(async (query, mode, range2) => {
78601
79269
  if (!query.trim())
78602
79270
  return;
79271
+ const cacheKey = getCacheKey(query, mode, range2);
79272
+ const cached = resultsCache.current.get(cacheKey);
79273
+ if (cached) {
79274
+ setResults(cached);
79275
+ setSelectedIndex(0);
79276
+ return;
79277
+ }
78603
79278
  setLoading(true);
78604
79279
  setError(null);
78605
79280
  try {
78606
79281
  const client = clientRef.current || await getClient2();
78607
79282
  clientRef.current = client;
78608
- const searchResults = await client.post("/v1/messages/search", {
78609
- body: {
78610
- query: query.trim(),
78611
- search_mode: mode,
78612
- limit: SEARCH_LIMIT
78613
- }
78614
- });
78615
- setResults(searchResults);
78616
- setCurrentPage(0);
79283
+ const getOrFetch = (m, r) => {
79284
+ const key = getCacheKey(query, m, r);
79285
+ return resultsCache.current.get(key) ?? fetchSearchResults(client, query, m, r);
79286
+ };
79287
+ const [
79288
+ hybridAll,
79289
+ vectorAll,
79290
+ ftsAll,
79291
+ hybridAgent,
79292
+ vectorAgent,
79293
+ ftsAgent,
79294
+ hybridConv,
79295
+ vectorConv,
79296
+ ftsConv
79297
+ ] = await Promise.all([
79298
+ getOrFetch("hybrid", "all"),
79299
+ getOrFetch("vector", "all"),
79300
+ getOrFetch("fts", "all"),
79301
+ agentId ? getOrFetch("hybrid", "agent") : Promise.resolve([]),
79302
+ agentId ? getOrFetch("vector", "agent") : Promise.resolve([]),
79303
+ agentId ? getOrFetch("fts", "agent") : Promise.resolve([]),
79304
+ conversationId ? getOrFetch("hybrid", "conv") : Promise.resolve([]),
79305
+ conversationId ? getOrFetch("vector", "conv") : Promise.resolve([]),
79306
+ conversationId ? getOrFetch("fts", "conv") : Promise.resolve([])
79307
+ ]);
79308
+ resultsCache.current.set(getCacheKey(query, "hybrid", "all"), hybridAll);
79309
+ resultsCache.current.set(getCacheKey(query, "vector", "all"), vectorAll);
79310
+ resultsCache.current.set(getCacheKey(query, "fts", "all"), ftsAll);
79311
+ if (agentId) {
79312
+ resultsCache.current.set(getCacheKey(query, "hybrid", "agent"), hybridAgent);
79313
+ resultsCache.current.set(getCacheKey(query, "vector", "agent"), vectorAgent);
79314
+ resultsCache.current.set(getCacheKey(query, "fts", "agent"), ftsAgent);
79315
+ }
79316
+ if (conversationId) {
79317
+ resultsCache.current.set(getCacheKey(query, "hybrid", "conv"), hybridConv);
79318
+ resultsCache.current.set(getCacheKey(query, "vector", "conv"), vectorConv);
79319
+ resultsCache.current.set(getCacheKey(query, "fts", "conv"), ftsConv);
79320
+ }
79321
+ const resultMap = {
79322
+ hybrid: { all: hybridAll, agent: hybridAgent, conv: hybridConv },
79323
+ vector: { all: vectorAll, agent: vectorAgent, conv: vectorConv },
79324
+ fts: { all: ftsAll, agent: ftsAgent, conv: ftsConv }
79325
+ };
79326
+ setResults(resultMap[mode][range2]);
78617
79327
  setSelectedIndex(0);
78618
79328
  } catch (err) {
78619
79329
  setError(err instanceof Error ? err.message : String(err));
@@ -78621,40 +79331,56 @@ function MessageSearch({ onClose, initialQuery }) {
78621
79331
  } finally {
78622
79332
  setLoading(false);
78623
79333
  }
78624
- }, []);
79334
+ }, [fetchSearchResults, getCacheKey, agentId, conversationId]);
78625
79335
  const submitSearch = import_react70.useCallback(() => {
78626
79336
  if (searchInput.trim() && searchInput !== activeQuery) {
78627
79337
  setActiveQuery(searchInput);
78628
- executeSearch(searchInput, searchMode);
79338
+ executeSearch(searchInput, searchMode, searchRange);
78629
79339
  }
78630
- }, [searchInput, activeQuery, searchMode, executeSearch]);
79340
+ }, [searchInput, activeQuery, searchMode, searchRange, executeSearch]);
78631
79341
  const clearSearch = import_react70.useCallback(() => {
78632
79342
  setSearchInput("");
78633
79343
  setActiveQuery("");
78634
79344
  setResults([]);
78635
- setCurrentPage(0);
78636
79345
  setSelectedIndex(0);
78637
79346
  }, []);
78638
- const cycleSearchMode = import_react70.useCallback(() => {
79347
+ const cycleSearchMode = import_react70.useCallback((reverse = false) => {
78639
79348
  setSearchMode((current) => {
78640
79349
  const currentIndex = SEARCH_MODES.indexOf(current);
78641
- const nextIndex = (currentIndex + 1) % SEARCH_MODES.length;
79350
+ const nextIndex = reverse ? (currentIndex - 1 + SEARCH_MODES.length) % SEARCH_MODES.length : (currentIndex + 1) % SEARCH_MODES.length;
78642
79351
  return SEARCH_MODES[nextIndex];
78643
79352
  });
78644
79353
  }, []);
79354
+ const cycleSearchRange = import_react70.useCallback(() => {
79355
+ setSearchRange((current) => {
79356
+ const currentIndex = SEARCH_RANGES.indexOf(current);
79357
+ const nextIndex = (currentIndex + 1) % SEARCH_RANGES.length;
79358
+ return SEARCH_RANGES[nextIndex];
79359
+ });
79360
+ }, []);
78645
79361
  import_react70.useEffect(() => {
78646
79362
  if (activeQuery) {
78647
- executeSearch(activeQuery, searchMode);
79363
+ executeSearch(activeQuery, searchMode, searchRange);
78648
79364
  }
78649
- }, [searchMode, activeQuery, executeSearch]);
78650
- const totalPages = Math.ceil(results.length / DISPLAY_PAGE_SIZE5);
78651
- const startIndex = currentPage * DISPLAY_PAGE_SIZE5;
78652
- const pageResults = results.slice(startIndex, startIndex + DISPLAY_PAGE_SIZE5);
79365
+ }, [searchMode, searchRange, activeQuery, executeSearch]);
79366
+ const startIndex = Math.max(0, Math.min(selectedIndex - 2, results.length - VISIBLE_ITEMS));
79367
+ const visibleResults = results.slice(startIndex, startIndex + VISIBLE_ITEMS);
78653
79368
  use_input_default((input, key) => {
78654
79369
  if (key.ctrl && input === "c") {
78655
79370
  onClose();
78656
79371
  return;
78657
79372
  }
79373
+ if (expandedMessage) {
79374
+ if (key.escape) {
79375
+ setExpandedMessage(null);
79376
+ } else if (key.return && onOpenConversation) {
79377
+ const msgData = expandedMessage;
79378
+ if (msgData.agent_id) {
79379
+ onOpenConversation(msgData.agent_id, msgData.conversation_id);
79380
+ }
79381
+ }
79382
+ return;
79383
+ }
78658
79384
  if (key.escape) {
78659
79385
  if (searchInput || activeQuery) {
78660
79386
  clearSearch();
@@ -78662,94 +79388,209 @@ function MessageSearch({ onClose, initialQuery }) {
78662
79388
  onClose();
78663
79389
  }
78664
79390
  } else if (key.return) {
78665
- submitSearch();
79391
+ if (searchInput.trim() && searchInput !== activeQuery) {
79392
+ submitSearch();
79393
+ } else if (results.length > 0 && results[selectedIndex]) {
79394
+ setExpandedMessage(results[selectedIndex]);
79395
+ }
78666
79396
  } else if (key.backspace || key.delete) {
78667
79397
  setSearchInput((prev) => prev.slice(0, -1));
78668
- } else if (key.tab) {
79398
+ } else if (key.tab && key.shift) {
78669
79399
  cycleSearchMode();
79400
+ } else if (key.tab) {
79401
+ cycleSearchRange();
78670
79402
  } else if (key.upArrow) {
78671
79403
  setSelectedIndex((prev) => Math.max(0, prev - 1));
78672
79404
  } else if (key.downArrow) {
78673
- setSelectedIndex((prev) => Math.min(pageResults.length - 1, prev + 1));
78674
- } else if (input === "j" || input === "J") {
78675
- if (currentPage > 0) {
78676
- setCurrentPage((prev) => prev - 1);
78677
- setSelectedIndex(0);
78678
- }
78679
- } else if (input === "k" || input === "K") {
78680
- if (currentPage < totalPages - 1) {
78681
- setCurrentPage((prev) => prev + 1);
78682
- setSelectedIndex(0);
78683
- }
79405
+ setSelectedIndex((prev) => Math.min(results.length - 1, prev + 1));
78684
79406
  } else if (input && !key.ctrl && !key.meta) {
78685
79407
  setSearchInput((prev) => prev + input);
78686
79408
  }
78687
79409
  });
79410
+ const solidLine = SOLID_LINE16.repeat(Math.max(terminalWidth, 10));
79411
+ const getRangeLabel = (range2) => {
79412
+ switch (range2) {
79413
+ case "all":
79414
+ return "all agents";
79415
+ case "agent":
79416
+ return "this agent";
79417
+ case "conv":
79418
+ return "this conversation";
79419
+ }
79420
+ };
78688
79421
  return /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78689
79422
  flexDirection: "column",
78690
- gap: 1,
78691
79423
  children: [
78692
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78693
- children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78694
- bold: true,
78695
- color: colors.selector.title,
78696
- children: "Search messages across all agents"
78697
- }, undefined, false, undefined, this)
79424
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79425
+ dimColor: true,
79426
+ children: "> /search"
79427
+ }, undefined, false, undefined, this),
79428
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79429
+ dimColor: true,
79430
+ children: solidLine
78698
79431
  }, undefined, false, undefined, this),
78699
79432
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79433
+ height: 1
79434
+ }, undefined, false, undefined, this),
79435
+ expandedMessage && (() => {
79436
+ const msgData = expandedMessage;
79437
+ const fullText = getMessageText(expandedMessage);
79438
+ const msgType = expandedMessage.message_type || "unknown";
79439
+ const isAssistant = msgType === "assistant_message" || msgType === "reasoning_message";
79440
+ const typeLabel = isAssistant ? "Agent message" : "User message";
79441
+ const timestamp = formatLocalTime(msgData.created_at || msgData.date);
79442
+ return /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(jsx_dev_runtime46.Fragment, {
79443
+ children: [
79444
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79445
+ paddingLeft: 2,
79446
+ children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79447
+ children: [
79448
+ '"',
79449
+ fullText,
79450
+ '"'
79451
+ ]
79452
+ }, undefined, true, undefined, this)
79453
+ }, undefined, false, undefined, this),
79454
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79455
+ height: 1
79456
+ }, undefined, false, undefined, this),
79457
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79458
+ flexDirection: "column",
79459
+ paddingLeft: 2,
79460
+ children: [
79461
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79462
+ dimColor: true,
79463
+ children: [
79464
+ typeLabel,
79465
+ ", sent ",
79466
+ timestamp
79467
+ ]
79468
+ }, undefined, true, undefined, this),
79469
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79470
+ dimColor: true,
79471
+ children: [
79472
+ "Agent ID: ",
79473
+ msgData.agent_id || "unknown"
79474
+ ]
79475
+ }, undefined, true, undefined, this),
79476
+ msgData.conversation_id && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79477
+ dimColor: true,
79478
+ children: [
79479
+ "Conv ID: ",
79480
+ msgData.conversation_id
79481
+ ]
79482
+ }, undefined, true, undefined, this)
79483
+ ]
79484
+ }, undefined, true, undefined, this),
79485
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79486
+ height: 1
79487
+ }, undefined, false, undefined, this),
79488
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79489
+ paddingLeft: 2,
79490
+ children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79491
+ dimColor: true,
79492
+ children: onOpenConversation ? "Enter to open conversation · Esc cancel" : "Esc cancel"
79493
+ }, undefined, false, undefined, this)
79494
+ }, undefined, false, undefined, this)
79495
+ ]
79496
+ }, undefined, true, undefined, this);
79497
+ })(),
79498
+ !expandedMessage && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78700
79499
  flexDirection: "column",
79500
+ gap: 1,
79501
+ marginBottom: 1,
78701
79502
  children: [
79503
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79504
+ bold: true,
79505
+ color: colors.selector.title,
79506
+ children: "Search messages across all agents"
79507
+ }, undefined, false, undefined, this),
78702
79508
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79509
+ flexDirection: "column",
79510
+ paddingLeft: 1,
78703
79511
  children: [
78704
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78705
- dimColor: true,
78706
- children: "Search: "
78707
- }, undefined, false, undefined, this),
78708
- searchInput ? /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(jsx_dev_runtime46.Fragment, {
79512
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79513
+ flexDirection: "row",
78709
79514
  children: [
78710
79515
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78711
- children: searchInput
79516
+ dimColor: true,
79517
+ children: " Search: "
78712
79518
  }, undefined, false, undefined, this),
78713
- searchInput !== activeQuery && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79519
+ searchInput ? /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(jsx_dev_runtime46.Fragment, {
79520
+ children: [
79521
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79522
+ children: searchInput
79523
+ }, undefined, false, undefined, this),
79524
+ searchInput !== activeQuery && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79525
+ dimColor: true,
79526
+ children: " (press Enter to search)"
79527
+ }, undefined, false, undefined, this)
79528
+ ]
79529
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78714
79530
  dimColor: true,
78715
- children: " (press Enter to search)"
79531
+ children: "(type to search)"
78716
79532
  }, undefined, false, undefined, this)
78717
79533
  ]
78718
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78719
- dimColor: true,
78720
- italic: true,
78721
- children: "(type your query)"
78722
- }, undefined, false, undefined, this)
78723
- ]
78724
- }, undefined, true, undefined, this),
78725
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78726
- children: [
78727
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78728
- dimColor: true,
78729
- children: "Mode: "
79534
+ }, undefined, true, undefined, this),
79535
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79536
+ height: 1
78730
79537
  }, undefined, false, undefined, this),
78731
- SEARCH_MODES.map((mode, i) => /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79538
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79539
+ flexDirection: "row",
78732
79540
  children: [
78733
- i > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79541
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78734
79542
  dimColor: true,
78735
- children: " · "
79543
+ children: " Range (tab): "
78736
79544
  }, undefined, false, undefined, this),
79545
+ SEARCH_RANGES.map((range2, i) => {
79546
+ const isActive = range2 === searchRange;
79547
+ return /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79548
+ children: [
79549
+ i > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79550
+ children: " "
79551
+ }, undefined, false, undefined, this),
79552
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79553
+ backgroundColor: isActive ? colors.selector.itemHighlighted : undefined,
79554
+ color: isActive ? "black" : undefined,
79555
+ bold: isActive,
79556
+ children: ` ${getRangeLabel(range2)} `
79557
+ }, undefined, false, undefined, this)
79558
+ ]
79559
+ }, range2, true, undefined, this);
79560
+ })
79561
+ ]
79562
+ }, undefined, true, undefined, this),
79563
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79564
+ flexDirection: "row",
79565
+ children: [
78737
79566
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78738
- bold: mode === searchMode,
78739
- color: mode === searchMode ? colors.selector.itemHighlighted : undefined,
78740
- children: mode
78741
- }, undefined, false, undefined, this)
79567
+ dimColor: true,
79568
+ children: " Mode (shift-tab): "
79569
+ }, undefined, false, undefined, this),
79570
+ SEARCH_MODES.map((mode, i) => {
79571
+ const isActive = mode === searchMode;
79572
+ return /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79573
+ children: [
79574
+ i > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79575
+ children: " "
79576
+ }, undefined, false, undefined, this),
79577
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79578
+ backgroundColor: isActive ? colors.selector.itemHighlighted : undefined,
79579
+ color: isActive ? "black" : undefined,
79580
+ bold: isActive,
79581
+ children: ` ${mode} `
79582
+ }, undefined, false, undefined, this)
79583
+ ]
79584
+ }, mode, true, undefined, this);
79585
+ })
78742
79586
  ]
78743
- }, mode, true, undefined, this)),
78744
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78745
- dimColor: true,
78746
- children: " (Tab to change)"
78747
- }, undefined, false, undefined, this)
79587
+ }, undefined, true, undefined, this)
78748
79588
  ]
78749
79589
  }, undefined, true, undefined, this)
78750
79590
  ]
78751
79591
  }, undefined, true, undefined, this),
78752
- error && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79592
+ !expandedMessage && error && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79593
+ paddingLeft: 2,
78753
79594
  children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78754
79595
  color: "red",
78755
79596
  children: [
@@ -78758,13 +79599,15 @@ function MessageSearch({ onClose, initialQuery }) {
78758
79599
  ]
78759
79600
  }, undefined, true, undefined, this)
78760
79601
  }, undefined, false, undefined, this),
78761
- loading && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79602
+ !expandedMessage && loading && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79603
+ paddingLeft: 2,
78762
79604
  children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78763
79605
  dimColor: true,
78764
79606
  children: "Searching..."
78765
79607
  }, undefined, false, undefined, this)
78766
79608
  }, undefined, false, undefined, this),
78767
- !loading && activeQuery && results.length === 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79609
+ !expandedMessage && !loading && activeQuery && results.length === 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79610
+ paddingLeft: 2,
78768
79611
  children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78769
79612
  dimColor: true,
78770
79613
  children: [
@@ -78774,22 +79617,24 @@ function MessageSearch({ onClose, initialQuery }) {
78774
79617
  ]
78775
79618
  }, undefined, true, undefined, this)
78776
79619
  }, undefined, false, undefined, this),
78777
- !loading && results.length > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79620
+ !expandedMessage && !loading && results.length > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78778
79621
  flexDirection: "column",
78779
- children: pageResults.map((msg, index) => {
78780
- const isSelected = index === selectedIndex;
79622
+ children: visibleResults.map((msg, visibleIndex) => {
79623
+ const actualIndex = startIndex + visibleIndex;
79624
+ const isSelected = actualIndex === selectedIndex;
78781
79625
  const messageText = getMessageText(msg);
78782
79626
  const msgWithDate = msg;
78783
- const timestamp = msgWithDate.date ? formatRelativeTime5(msgWithDate.date) : "";
78784
- const msgType = (msg.message_type || "unknown").replace("_message", "");
78785
- const agentId = msgWithDate.agent_id || "unknown";
78786
- const conversationId = msgWithDate.conversation_id;
78787
- const createdAt = formatLocalTime(msgWithDate.created_at);
78788
- const metaWidth = timestamp.length + msgType.length + 10;
78789
- const availableWidth = Math.max(20, terminalWidth - metaWidth - 4);
79627
+ const msgType = msg.message_type || "unknown";
79628
+ const agentIdFromMsg = msgWithDate.agent_id || "unknown";
79629
+ const conversationIdFromMsg = msgWithDate.conversation_id;
79630
+ const createdAt = formatLocalTime(msgWithDate.created_at || msgWithDate.date);
79631
+ const isAssistant = msgType === "assistant_message" || msgType === "reasoning_message";
79632
+ const emoji = isAssistant ? "\uD83D\uDC7E" : "\uD83D\uDC64";
79633
+ const availableWidth = Math.max(20, terminalWidth - 8);
78790
79634
  const displayText = truncateText2(messageText.replace(/\n/g, " "), availableWidth);
79635
+ const idToShow = conversationIdFromMsg || agentIdFromMsg;
78791
79636
  const msgId = "message_id" in msg ? String(msg.message_id) : "result";
78792
- const uniqueKey = `${msgId}-${startIndex + index}`;
79637
+ const uniqueKey = `${msgId}-${actualIndex}`;
78793
79638
  return /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78794
79639
  flexDirection: "column",
78795
79640
  marginBottom: 1,
@@ -78802,10 +79647,15 @@ function MessageSearch({ onClose, initialQuery }) {
78802
79647
  children: isSelected ? ">" : " "
78803
79648
  }, undefined, false, undefined, this),
78804
79649
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78805
- children: " "
78806
- }, undefined, false, undefined, this),
79650
+ children: [
79651
+ " ",
79652
+ emoji,
79653
+ " "
79654
+ ]
79655
+ }, undefined, true, undefined, this),
78807
79656
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78808
79657
  bold: isSelected,
79658
+ italic: true,
78809
79659
  color: isSelected ? colors.selector.itemHighlighted : undefined,
78810
79660
  children: displayText
78811
79661
  }, undefined, false, undefined, this)
@@ -78814,95 +79664,53 @@ function MessageSearch({ onClose, initialQuery }) {
78814
79664
  /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78815
79665
  flexDirection: "row",
78816
79666
  marginLeft: 2,
78817
- children: [
78818
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78819
- dimColor: true,
78820
- children: [
78821
- msgType,
78822
- timestamp && ` · ${timestamp}`
78823
- ]
78824
- }, undefined, true, undefined, this),
78825
- agentId && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(jsx_dev_runtime46.Fragment, {
78826
- children: [
78827
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78828
- dimColor: true,
78829
- children: " · "
78830
- }, undefined, false, undefined, this),
78831
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(dist_default4, {
78832
- url: `https://app.letta.com/projects/default-project/agents/${agentId}?searchTerm=${encodeURIComponent(activeQuery)}&messageId=${msgId}${conversationId ? `&conversation=${encodeURIComponent(conversationId)}` : ""}`,
78833
- children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78834
- color: colors.link.text,
78835
- children: "view message"
78836
- }, undefined, false, undefined, this)
78837
- }, undefined, false, undefined, this),
78838
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78839
- dimColor: true,
78840
- children: " · agent: "
78841
- }, undefined, false, undefined, this),
78842
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(dist_default4, {
78843
- url: `https://app.letta.com/projects/default-project/agents/${agentId}${conversationId ? `?conversation=${encodeURIComponent(conversationId)}` : ""}`,
78844
- children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78845
- color: colors.link.text,
78846
- children: agentId
78847
- }, undefined, false, undefined, this)
78848
- }, undefined, false, undefined, this)
78849
- ]
78850
- }, undefined, true, undefined, this),
78851
- createdAt && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78852
- dimColor: true,
78853
- children: [
78854
- " · ",
78855
- createdAt
78856
- ]
78857
- }, undefined, true, undefined, this)
78858
- ]
78859
- }, undefined, true, undefined, this)
79667
+ children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79668
+ dimColor: true,
79669
+ children: [
79670
+ createdAt,
79671
+ idToShow && ` · ${idToShow}`
79672
+ ]
79673
+ }, undefined, true, undefined, this)
79674
+ }, undefined, false, undefined, this)
78860
79675
  ]
78861
79676
  }, uniqueKey, true, undefined, this);
78862
79677
  })
78863
79678
  }, undefined, false, undefined, this),
78864
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
79679
+ !expandedMessage && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78865
79680
  flexDirection: "column",
78866
79681
  marginTop: 1,
79682
+ paddingLeft: 2,
78867
79683
  children: [
78868
- results.length > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78869
- children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78870
- dimColor: true,
78871
- children: [
78872
- "Page ",
78873
- currentPage + 1,
78874
- "/",
78875
- totalPages || 1,
78876
- " (",
78877
- results.length,
78878
- " ",
78879
- "results)"
78880
- ]
78881
- }, undefined, true, undefined, this)
78882
- }, undefined, false, undefined, this),
78883
- /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Box_default, {
78884
- children: /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
78885
- dimColor: true,
78886
- children: "Type + Enter to search · Tab mode · J/K page · Esc close"
78887
- }, undefined, false, undefined, this)
79684
+ results.length > 0 && /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79685
+ dimColor: true,
79686
+ children: [
79687
+ selectedIndex + 1,
79688
+ "/",
79689
+ results.length,
79690
+ " results"
79691
+ ]
79692
+ }, undefined, true, undefined, this),
79693
+ /* @__PURE__ */ jsx_dev_runtime46.jsxDEV(Text, {
79694
+ dimColor: true,
79695
+ children: "Enter expand · ↑↓ navigate · Esc close"
78888
79696
  }, undefined, false, undefined, this)
78889
79697
  ]
78890
79698
  }, undefined, true, undefined, this)
78891
79699
  ]
78892
79700
  }, undefined, true, undefined, this);
78893
79701
  }
78894
- var import_react70, jsx_dev_runtime46, DISPLAY_PAGE_SIZE5 = 5, SEARCH_LIMIT = 100, SEARCH_MODES;
79702
+ var import_react70, jsx_dev_runtime46, SOLID_LINE16 = "─", VISIBLE_ITEMS = 5, SEARCH_LIMIT = 100, SEARCH_MODES, SEARCH_RANGES;
78895
79703
  var init_MessageSearch = __esm(async () => {
78896
79704
  init_useTerminalWidth();
78897
79705
  init_colors();
78898
79706
  await __promiseAll([
78899
79707
  init_build2(),
78900
- init_dist4(),
78901
79708
  init_client2()
78902
79709
  ]);
78903
79710
  import_react70 = __toESM(require_react(), 1);
78904
79711
  jsx_dev_runtime46 = __toESM(require_jsx_dev_runtime(), 1);
78905
- SEARCH_MODES = ["hybrid", "vector", "fts"];
79712
+ SEARCH_MODES = ["fts", "vector", "hybrid"];
79713
+ SEARCH_RANGES = ["all", "agent", "conv"];
78906
79714
  });
78907
79715
 
78908
79716
  // src/cli/components/ModelSelector.tsx
@@ -78914,7 +79722,7 @@ function ModelSelector({
78914
79722
  forceRefresh: forceRefreshOnMount
78915
79723
  }) {
78916
79724
  const terminalWidth = useTerminalWidth();
78917
- const solidLine = SOLID_LINE16.repeat(Math.max(terminalWidth, 10));
79725
+ const solidLine = SOLID_LINE17.repeat(Math.max(terminalWidth, 10));
78918
79726
  const typedModels = models;
78919
79727
  const [category, setCategory] = import_react71.useState("supported");
78920
79728
  const [selectedIndex, setSelectedIndex] = import_react71.useState(0);
@@ -78997,7 +79805,7 @@ function ModelSelector({
78997
79805
  description: ""
78998
79806
  }));
78999
79807
  }, [category, supportedModels, otherModelHandles]);
79000
- const visibleCount = VISIBLE_ITEMS - 1;
79808
+ const visibleCount = VISIBLE_ITEMS2 - 1;
79001
79809
  const startIndex = import_react71.useMemo(() => {
79002
79810
  if (selectedIndex < visibleCount)
79003
79811
  return 0;
@@ -79245,7 +80053,7 @@ function ModelSelector({
79245
80053
  ]
79246
80054
  }, undefined, true, undefined, this);
79247
80055
  }
79248
- var import_react71, jsx_dev_runtime47, SOLID_LINE16 = "─", VISIBLE_ITEMS = 8, MODEL_CATEGORIES;
80056
+ var import_react71, jsx_dev_runtime47, SOLID_LINE17 = "─", VISIBLE_ITEMS2 = 8, MODEL_CATEGORIES;
79249
80057
  var init_ModelSelector = __esm(async () => {
79250
80058
  init_model();
79251
80059
  init_useTerminalWidth();
@@ -79476,7 +80284,7 @@ var init_PinDialog = __esm(async () => {
79476
80284
  // src/cli/components/NewAgentDialog.tsx
79477
80285
  function NewAgentDialog({ onSubmit, onCancel }) {
79478
80286
  const terminalWidth = useTerminalWidth();
79479
- const solidLine = SOLID_LINE17.repeat(Math.max(terminalWidth, 10));
80287
+ const solidLine = SOLID_LINE18.repeat(Math.max(terminalWidth, 10));
79480
80288
  const [nameInput, setNameInput] = import_react73.useState("");
79481
80289
  const [error, setError] = import_react73.useState("");
79482
80290
  use_input_default((input, key) => {
@@ -79584,7 +80392,7 @@ function NewAgentDialog({ onSubmit, onCancel }) {
79584
80392
  ]
79585
80393
  }, undefined, true, undefined, this);
79586
80394
  }
79587
- var import_react73, jsx_dev_runtime49, SOLID_LINE17 = "─";
80395
+ var import_react73, jsx_dev_runtime49, SOLID_LINE18 = "─";
79588
80396
  var init_NewAgentDialog = __esm(async () => {
79589
80397
  init_constants();
79590
80398
  init_useTerminalWidth();
@@ -80508,7 +81316,7 @@ function SystemPromptSelector({
80508
81316
  onCancel
80509
81317
  }) {
80510
81318
  const terminalWidth = useTerminalWidth();
80511
- const solidLine = SOLID_LINE18.repeat(Math.max(terminalWidth, 10));
81319
+ const solidLine = SOLID_LINE19.repeat(Math.max(terminalWidth, 10));
80512
81320
  const [showAll, setShowAll] = import_react80.useState(false);
80513
81321
  const [selectedIndex, setSelectedIndex] = import_react80.useState(0);
80514
81322
  const featuredPrompts = import_react80.useMemo(() => SYSTEM_PROMPTS.filter((prompt) => prompt.isFeatured), []);
@@ -80629,7 +81437,7 @@ function SystemPromptSelector({
80629
81437
  ]
80630
81438
  }, undefined, true, undefined, this);
80631
81439
  }
80632
- var import_react80, jsx_dev_runtime56, SOLID_LINE18 = "─";
81440
+ var import_react80, jsx_dev_runtime56, SOLID_LINE19 = "─";
80633
81441
  var init_SystemPromptSelector = __esm(async () => {
80634
81442
  init_promptAssets();
80635
81443
  init_useTerminalWidth();
@@ -82302,7 +83110,7 @@ function ToolsetSelector({
82302
83110
  onCancel
82303
83111
  }) {
82304
83112
  const terminalWidth = useTerminalWidth();
82305
- const solidLine = SOLID_LINE19.repeat(Math.max(terminalWidth, 10));
83113
+ const solidLine = SOLID_LINE20.repeat(Math.max(terminalWidth, 10));
82306
83114
  const [showAll, setShowAll] = import_react82.useState(false);
82307
83115
  const [selectedIndex, setSelectedIndex] = import_react82.useState(0);
82308
83116
  const featuredToolsets = import_react82.useMemo(() => toolsets.filter((toolset) => toolset.isFeatured), []);
@@ -82429,7 +83237,7 @@ function ToolsetSelector({
82429
83237
  ]
82430
83238
  }, undefined, true, undefined, this);
82431
83239
  }
82432
- var import_react82, jsx_dev_runtime61, SOLID_LINE19 = "─", toolsets;
83240
+ var import_react82, jsx_dev_runtime61, SOLID_LINE20 = "─", toolsets;
82433
83241
  var init_ToolsetSelector = __esm(async () => {
82434
83242
  init_useTerminalWidth();
82435
83243
  init_colors();
@@ -84349,6 +85157,13 @@ function App2({
84349
85157
  runSessionEndHooks(durationMs, undefined, undefined, agentIdRef.current ?? undefined, conversationIdRef.current ?? undefined).catch(() => {});
84350
85158
  };
84351
85159
  }, []);
85160
+ import_react86.useEffect(() => {
85161
+ return () => {
85162
+ if (queueAppendTimeoutRef.current) {
85163
+ clearTimeout(queueAppendTimeoutRef.current);
85164
+ }
85165
+ };
85166
+ }, []);
84352
85167
  const [showExitStats, setShowExitStats] = import_react86.useState(false);
84353
85168
  const hasSentSessionContextRef = import_react86.useRef(false);
84354
85169
  const turnCountRef = import_react86.useRef(0);
@@ -84361,6 +85176,10 @@ function App2({
84361
85176
  const llmApiErrorRetriesRef = import_react86.useRef(0);
84362
85177
  const conversationBusyRetriesRef = import_react86.useRef(0);
84363
85178
  const [messageQueue, setMessageQueue] = import_react86.useState([]);
85179
+ const messageQueueRef = import_react86.useRef([]);
85180
+ import_react86.useEffect(() => {
85181
+ messageQueueRef.current = messageQueue;
85182
+ }, [messageQueue]);
84364
85183
  const waitingForQueueCancelRef = import_react86.useRef(false);
84365
85184
  const queueSnapshotRef = import_react86.useRef([]);
84366
85185
  const [restoreQueueOnCancel, setRestoreQueueOnCancel] = import_react86.useState(false);
@@ -84368,12 +85187,24 @@ function App2({
84368
85187
  import_react86.useEffect(() => {
84369
85188
  restoreQueueOnCancelRef.current = restoreQueueOnCancel;
84370
85189
  }, [restoreQueueOnCancel]);
85190
+ const queueAppendTimeoutRef = import_react86.useRef(null);
84371
85191
  const [dequeueEpoch, setDequeueEpoch] = import_react86.useState(0);
84372
85192
  const lastDequeuedMessageRef = import_react86.useRef(null);
84373
85193
  const [restoredInput, setRestoredInput] = import_react86.useState(null);
84374
85194
  const isAgentBusy = import_react86.useCallback(() => {
84375
85195
  return streamingRef.current || isExecutingTool || commandRunningRef.current || abortControllerRef.current !== null;
84376
85196
  }, [isExecutingTool]);
85197
+ const consumeQueuedMessages = import_react86.useCallback(() => {
85198
+ if (messageQueueRef.current.length === 0)
85199
+ return null;
85200
+ if (queueAppendTimeoutRef.current) {
85201
+ clearTimeout(queueAppendTimeoutRef.current);
85202
+ queueAppendTimeoutRef.current = null;
85203
+ }
85204
+ const messages = [...messageQueueRef.current];
85205
+ setMessageQueue([]);
85206
+ return messages;
85207
+ }, []);
84377
85208
  const withCommandLock = import_react86.useCallback(async (asyncFn) => {
84378
85209
  setActiveOverlay(null);
84379
85210
  setCommandRunning(true);
@@ -85032,7 +85863,35 @@ ${newState.originalPrompt}`
85032
85863
  llmApiErrorRetriesRef.current = 0;
85033
85864
  conversationBusyRetriesRef.current = 0;
85034
85865
  lastDequeuedMessageRef.current = null;
85035
- runStopHooks(stopReasonToHandle, buffersRef.current.order.length, Array.from(buffersRef.current.byId.values()).filter((item) => item.kind === "tool_call").length).catch(() => {});
85866
+ const stopHookResult = await runStopHooks(stopReasonToHandle, buffersRef.current.order.length, Array.from(buffersRef.current.byId.values()).filter((item) => item.kind === "tool_call").length);
85867
+ if (stopHookResult.blocked) {
85868
+ const stderrOutput = stopHookResult.results.map((r) => r.stderr).filter(Boolean).join(`
85869
+ `);
85870
+ const feedback = stderrOutput || "Stop hook blocked";
85871
+ const hookMessage = `<stop-hook>
85872
+ ${feedback}
85873
+ </stop-hook>`;
85874
+ const statusId = uid4("status");
85875
+ buffersRef.current.byId.set(statusId, {
85876
+ kind: "status",
85877
+ id: statusId,
85878
+ lines: [
85879
+ "Stop hook encountered blocking error, continuing loop with stderr feedback."
85880
+ ]
85881
+ });
85882
+ buffersRef.current.order.push(statusId);
85883
+ refreshDerived();
85884
+ setTimeout(() => {
85885
+ processConversation([
85886
+ {
85887
+ type: "message",
85888
+ role: "user",
85889
+ content: hookMessage
85890
+ }
85891
+ ], { allowReentry: true });
85892
+ }, 0);
85893
+ return;
85894
+ }
85036
85895
  if (needsEagerApprovalCheck) {
85037
85896
  setNeedsEagerApprovalCheck(false);
85038
85897
  }
@@ -85288,6 +86147,31 @@ ${newState.originalPrompt}`
85288
86147
  refreshDerived();
85289
86148
  return;
85290
86149
  }
86150
+ const queuedMessagesToAppend = consumeQueuedMessages();
86151
+ if (queuedMessagesToAppend?.length) {
86152
+ for (const msg of queuedMessagesToAppend) {
86153
+ const userId = uid4("user");
86154
+ buffersRef.current.byId.set(userId, {
86155
+ kind: "user",
86156
+ id: userId,
86157
+ text: msg
86158
+ });
86159
+ buffersRef.current.order.push(userId);
86160
+ }
86161
+ setThinkingMessage(getRandomThinkingVerb());
86162
+ refreshDerived();
86163
+ toolResultsInFlightRef.current = true;
86164
+ await processConversation([
86165
+ { type: "approval", approvals: allResults },
86166
+ ...queuedMessagesToAppend.map((msg) => ({
86167
+ type: "message",
86168
+ role: "user",
86169
+ content: msg
86170
+ }))
86171
+ ], { allowReentry: true });
86172
+ toolResultsInFlightRef.current = false;
86173
+ return;
86174
+ }
85291
86175
  if (waitingForQueueCancelRef.current) {
85292
86176
  if (allResults.length > 0) {
85293
86177
  queueApprovalResults(allResults, autoAllowedMetadata);
@@ -85636,7 +86520,8 @@ ${newState.originalPrompt}`
85636
86520
  currentModelId,
85637
86521
  updateStreamingOutput,
85638
86522
  needsEagerApprovalCheck,
85639
- queueApprovalResults
86523
+ queueApprovalResults,
86524
+ consumeQueuedMessages
85640
86525
  ]);
85641
86526
  const handleExit = import_react86.useCallback(async () => {
85642
86527
  saveLastAgentBeforeExit();
@@ -85810,7 +86695,7 @@ ${newState.originalPrompt}`
85810
86695
  import_react86.useEffect(() => {
85811
86696
  processConversationRef.current = processConversation;
85812
86697
  }, [processConversation]);
85813
- const handleAgentSelect = import_react86.useCallback(async (targetAgentId, _opts) => {
86698
+ const handleAgentSelect = import_react86.useCallback(async (targetAgentId, opts) => {
85814
86699
  setActiveOverlay(null);
85815
86700
  if (targetAgentId === agentId) {
85816
86701
  const label = agentName || targetAgentId.slice(0, 12);
@@ -85860,7 +86745,7 @@ ${newState.originalPrompt}`
85860
86745
  try {
85861
86746
  const client = await getClient2();
85862
86747
  const agent = await client.agents.retrieve(targetAgentId);
85863
- const targetConversationId = "default";
86748
+ const targetConversationId = opts?.conversationId ?? "default";
85864
86749
  await updateProjectSettings({ lastAgent: targetAgentId });
85865
86750
  settingsManager.setLocalLastSession({ agentId: targetAgentId, conversationId: targetConversationId }, process.cwd());
85866
86751
  settingsManager.setGlobalLastSession({
@@ -85880,7 +86765,12 @@ ${newState.originalPrompt}`
85880
86765
  setLlmConfig(agent.llm_config);
85881
86766
  setConversationId3(targetConversationId);
85882
86767
  const agentLabel = agent.name || targetAgentId;
85883
- const successOutput = [
86768
+ const isSpecificConv = opts?.conversationId && opts.conversationId !== "default";
86769
+ const successOutput = isSpecificConv ? [
86770
+ `Switched to **${agentLabel}**`,
86771
+ `⎿ Conversation: ${opts.conversationId}`
86772
+ ].join(`
86773
+ `) : [
85884
86774
  `Resumed the default conversation with **${agentLabel}**.`,
85885
86775
  `⎿ Type /resume to browse all conversations`,
85886
86776
  `⎿ Type /new to start a new conversation`
@@ -86277,29 +87167,34 @@ ${expanded.command}` : expanded.command;
86277
87167
  setMessageQueue((prev) => {
86278
87168
  const newQueue = [...prev, msg];
86279
87169
  const isSlashCommand = msg.startsWith("/");
86280
- if (!isSlashCommand && streamingRef.current && !waitingForQueueCancelRef.current) {
86281
- waitingForQueueCancelRef.current = true;
86282
- queueSnapshotRef.current = [...newQueue];
86283
- debugLog("queue", `Initiating queue-cancel: queueing "${msg.slice(0, 50)}${msg.length > 50 ? "..." : ""}", sending cancel to server`);
86284
- if (toolAbortControllerRef.current) {
86285
- toolAbortControllerRef.current.abort();
86286
- }
86287
- getClient2().then((client) => {
86288
- if (conversationIdRef.current === "default") {
86289
- return client.agents.messages.cancel(agentIdRef.current);
86290
- }
86291
- return client.conversations.cancel(conversationIdRef.current);
86292
- }).then(() => {}).catch(() => {
86293
- waitingForQueueCancelRef.current = false;
86294
- });
86295
- setTimeout(() => {
86296
- if (waitingForQueueCancelRef.current && abortControllerRef.current) {
86297
- debugLog("queue", "Timeout fallback: aborting stream after 3s (server cancel was slow/failed)");
86298
- abortControllerRef.current.abort();
86299
- waitingForQueueCancelRef.current = false;
86300
- queueSnapshotRef.current = [];
87170
+ if (!isSlashCommand && streamingRef.current && !waitingForQueueCancelRef.current && !queueAppendTimeoutRef.current) {
87171
+ queueAppendTimeoutRef.current = setTimeout(() => {
87172
+ if (messageQueueRef.current.length === 0) {
87173
+ queueAppendTimeoutRef.current = null;
87174
+ return;
87175
+ }
87176
+ queueAppendTimeoutRef.current = null;
87177
+ waitingForQueueCancelRef.current = true;
87178
+ queueSnapshotRef.current = [...messageQueueRef.current];
87179
+ if (toolAbortControllerRef.current) {
87180
+ toolAbortControllerRef.current.abort();
86301
87181
  }
86302
- }, 3000);
87182
+ getClient2().then((client) => {
87183
+ if (conversationIdRef.current === "default") {
87184
+ return client.agents.messages.cancel(agentIdRef.current);
87185
+ }
87186
+ return client.conversations.cancel(conversationIdRef.current);
87187
+ }).catch(() => {
87188
+ waitingForQueueCancelRef.current = false;
87189
+ });
87190
+ setTimeout(() => {
87191
+ if (waitingForQueueCancelRef.current && abortControllerRef.current) {
87192
+ abortControllerRef.current.abort();
87193
+ waitingForQueueCancelRef.current = false;
87194
+ queueSnapshotRef.current = [];
87195
+ }
87196
+ }, 3000);
87197
+ }, 15000);
86303
87198
  }
86304
87199
  return newQueue;
86305
87200
  });
@@ -86788,7 +87683,7 @@ Type your task to begin the loop.`,
86788
87683
  return { submitted: true };
86789
87684
  }
86790
87685
  const client = await getClient2();
86791
- const result2 = await client.agents.messages.compact(agentId);
87686
+ const result2 = await client.conversations.messages.compact(conversationIdRef.current);
86792
87687
  const outputLines = [
86793
87688
  `Compaction completed. Message buffer length reduced from ${result2.num_messages_before} to ${result2.num_messages_after}.`,
86794
87689
  "",
@@ -88218,13 +89113,29 @@ ${SYSTEM_REMINDER_CLOSE}
88218
89113
  waitingForQueueCancelRef.current = false;
88219
89114
  queueSnapshotRef.current = [];
88220
89115
  } else {
88221
- toolResultsInFlightRef.current = true;
88222
- await processConversation([
88223
- {
88224
- type: "approval",
88225
- approvals: allResults
89116
+ const queuedMessagesToAppend = consumeQueuedMessages();
89117
+ const input = [
89118
+ { type: "approval", approvals: allResults }
89119
+ ];
89120
+ if (queuedMessagesToAppend?.length) {
89121
+ for (const msg of queuedMessagesToAppend) {
89122
+ const userId = uid4("user");
89123
+ buffersRef.current.byId.set(userId, {
89124
+ kind: "user",
89125
+ id: userId,
89126
+ text: msg
89127
+ });
89128
+ buffersRef.current.order.push(userId);
89129
+ input.push({
89130
+ type: "message",
89131
+ role: "user",
89132
+ content: msg
89133
+ });
88226
89134
  }
88227
- ]);
89135
+ refreshDerived();
89136
+ }
89137
+ toolResultsInFlightRef.current = true;
89138
+ await processConversation(input);
88228
89139
  toolResultsInFlightRef.current = false;
88229
89140
  queueApprovalResults(null);
88230
89141
  }
@@ -88245,7 +89156,8 @@ ${SYSTEM_REMINDER_CLOSE}
88245
89156
  appendError,
88246
89157
  setStreaming,
88247
89158
  updateStreamingOutput,
88248
- queueApprovalResults
89159
+ queueApprovalResults,
89160
+ consumeQueuedMessages
88249
89161
  ]);
88250
89162
  const handleApproveCurrent = import_react86.useCallback(async (diffs) => {
88251
89163
  if (isExecutingTool)
@@ -89329,9 +90241,12 @@ Plan file path: ${planFilePath}`;
89329
90241
  line: item
89330
90242
  }, undefined, false, undefined, this) : item.kind === "status" ? /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(StatusMessage, {
89331
90243
  line: item
89332
- }, undefined, false, undefined, this) : item.kind === "separator" ? /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(Text, {
89333
- dimColor: true,
89334
- children: "─".repeat(columns)
90244
+ }, undefined, false, undefined, this) : item.kind === "separator" ? /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(Box_default, {
90245
+ marginTop: 1,
90246
+ children: /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(Text, {
90247
+ dimColor: true,
90248
+ children: "─".repeat(columns)
90249
+ }, undefined, false, undefined, this)
89335
90250
  }, undefined, false, undefined, this) : item.kind === "command" ? /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(CommandMessage, {
89336
90251
  line: item
89337
90252
  }, undefined, false, undefined, this) : item.kind === "bash_command" ? /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(BashCommandMessage, {
@@ -89766,7 +90681,147 @@ Plan file path: ${planFilePath}`;
89766
90681
  }, undefined, false, undefined, this),
89767
90682
  activeOverlay === "search" && /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(MessageSearch, {
89768
90683
  onClose: closeOverlay,
89769
- initialQuery: searchQuery || undefined
90684
+ initialQuery: searchQuery || undefined,
90685
+ agentId,
90686
+ conversationId,
90687
+ onOpenConversation: async (targetAgentId, targetConvId) => {
90688
+ closeOverlay();
90689
+ if (targetAgentId !== agentId) {
90690
+ await handleAgentSelect(targetAgentId, {
90691
+ conversationId: targetConvId
90692
+ });
90693
+ return;
90694
+ }
90695
+ const actualTargetConv = targetConvId || "default";
90696
+ if (actualTargetConv === conversationId) {
90697
+ return;
90698
+ }
90699
+ if (isAgentBusy()) {
90700
+ setQueuedOverlayAction({
90701
+ type: "switch_conversation",
90702
+ conversationId: actualTargetConv
90703
+ });
90704
+ const cmdId2 = uid4("cmd");
90705
+ buffersRef.current.byId.set(cmdId2, {
90706
+ kind: "command",
90707
+ id: cmdId2,
90708
+ input: "/search",
90709
+ output: `Conversation switch queued – will switch after current task completes`,
90710
+ phase: "finished",
90711
+ success: true
90712
+ });
90713
+ buffersRef.current.order.push(cmdId2);
90714
+ refreshDerived();
90715
+ return;
90716
+ }
90717
+ setCommandRunning(true);
90718
+ const cmdId = uid4("cmd");
90719
+ buffersRef.current.byId.set(cmdId, {
90720
+ kind: "command",
90721
+ id: cmdId,
90722
+ input: "/search",
90723
+ output: "Switching conversation...",
90724
+ phase: "running"
90725
+ });
90726
+ buffersRef.current.order.push(cmdId);
90727
+ refreshDerived();
90728
+ try {
90729
+ if (agentState) {
90730
+ const client = await getClient2();
90731
+ const resumeData = await getResumeData2(client, agentState, actualTargetConv);
90732
+ setConversationId3(actualTargetConv);
90733
+ settingsManager.setLocalLastSession({ agentId, conversationId: actualTargetConv }, process.cwd());
90734
+ settingsManager.setGlobalLastSession({
90735
+ agentId,
90736
+ conversationId: actualTargetConv
90737
+ });
90738
+ buffersRef.current.byId.clear();
90739
+ buffersRef.current.order = [];
90740
+ buffersRef.current.tokenCount = 0;
90741
+ emittedIdsRef.current.clear();
90742
+ setStaticItems([]);
90743
+ setStaticRenderEpoch((e) => e + 1);
90744
+ const currentAgentName = agentState.name || "Unnamed Agent";
90745
+ const successOutput = [
90746
+ `Switched to conversation with "${currentAgentName}"`,
90747
+ `⎿ Conversation: ${actualTargetConv}`
90748
+ ].join(`
90749
+ `);
90750
+ const successItem = {
90751
+ kind: "command",
90752
+ id: uid4("cmd"),
90753
+ input: "/search",
90754
+ output: successOutput,
90755
+ phase: "finished",
90756
+ success: true
90757
+ };
90758
+ if (resumeData.messageHistory.length > 0) {
90759
+ hasBackfilledRef.current = false;
90760
+ backfillBuffers(buffersRef.current, resumeData.messageHistory);
90761
+ const backfilledItems = [];
90762
+ for (const id of buffersRef.current.order) {
90763
+ const ln = buffersRef.current.byId.get(id);
90764
+ if (!ln)
90765
+ continue;
90766
+ emittedIdsRef.current.add(id);
90767
+ backfilledItems.push({ ...ln });
90768
+ }
90769
+ const separator = {
90770
+ kind: "separator",
90771
+ id: uid4("sep")
90772
+ };
90773
+ setStaticItems([
90774
+ separator,
90775
+ ...backfilledItems,
90776
+ successItem
90777
+ ]);
90778
+ setLines(toLines(buffersRef.current));
90779
+ hasBackfilledRef.current = true;
90780
+ } else {
90781
+ const separator = {
90782
+ kind: "separator",
90783
+ id: uid4("sep")
90784
+ };
90785
+ setStaticItems([separator, successItem]);
90786
+ setLines(toLines(buffersRef.current));
90787
+ }
90788
+ if (resumeData.pendingApprovals.length > 0) {
90789
+ setPendingApprovals(resumeData.pendingApprovals);
90790
+ try {
90791
+ const contexts = await Promise.all(resumeData.pendingApprovals.map(async (approval) => {
90792
+ const parsedArgs = safeJsonParseOr(approval.toolArgs, {});
90793
+ return await analyzeToolApproval(approval.toolName, parsedArgs);
90794
+ }));
90795
+ setApprovalContexts(contexts);
90796
+ } catch {}
90797
+ }
90798
+ }
90799
+ } catch (error) {
90800
+ let errorMsg = "Unknown error";
90801
+ if (error instanceof APIError2) {
90802
+ if (error.status === 404) {
90803
+ errorMsg = "Conversation not found";
90804
+ } else if (error.status === 422) {
90805
+ errorMsg = "Invalid conversation ID";
90806
+ } else {
90807
+ errorMsg = error.message;
90808
+ }
90809
+ } else if (error instanceof Error) {
90810
+ errorMsg = error.message;
90811
+ }
90812
+ buffersRef.current.byId.set(cmdId, {
90813
+ kind: "command",
90814
+ id: cmdId,
90815
+ input: "/search",
90816
+ output: `Failed: ${errorMsg}`,
90817
+ phase: "finished",
90818
+ success: false
90819
+ });
90820
+ refreshDerived();
90821
+ } finally {
90822
+ setCommandRunning(false);
90823
+ }
90824
+ }
89770
90825
  }, undefined, false, undefined, this),
89771
90826
  activeOverlay === "feedback" && /* @__PURE__ */ jsx_dev_runtime63.jsxDEV(FeedbackDialog, {
89772
90827
  onSubmit: handleFeedbackSubmit,
@@ -91067,7 +92122,7 @@ function sortChronological(messages) {
91067
92122
  async function getResumeData(client, agent, conversationId) {
91068
92123
  try {
91069
92124
  let inContextMessageIds;
91070
- let messages;
92125
+ let messages = [];
91071
92126
  const useConversationsApi = conversationId && conversationId !== "default";
91072
92127
  if (process.env.DEBUG) {
91073
92128
  console.log(`[DEBUG] getResumeData: conversationId=${conversationId}, useConversationsApi=${useConversationsApi}, agentId=${agent.id}`);
@@ -91078,12 +92133,16 @@ async function getResumeData(client, agent, conversationId) {
91078
92133
  if (!inContextMessageIds || inContextMessageIds.length === 0) {
91079
92134
  debugWarn("check-approval", "No in-context messages - no pending approvals");
91080
92135
  if (isBackfillEnabled()) {
91081
- const backfill = await client.conversations.messages.list(conversationId, { limit: MESSAGE_HISTORY_LIMIT, order: "desc" });
91082
- return {
91083
- pendingApproval: null,
91084
- pendingApprovals: [],
91085
- messageHistory: sortChronological(backfill.getPaginatedItems())
91086
- };
92136
+ try {
92137
+ const backfill = await client.conversations.messages.list(conversationId, { limit: MESSAGE_HISTORY_LIMIT, order: "desc" });
92138
+ return {
92139
+ pendingApproval: null,
92140
+ pendingApprovals: [],
92141
+ messageHistory: sortChronological(backfill.getPaginatedItems())
92142
+ };
92143
+ } catch (backfillError) {
92144
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
92145
+ }
91087
92146
  }
91088
92147
  return {
91089
92148
  pendingApproval: null,
@@ -91096,11 +92155,17 @@ async function getResumeData(client, agent, conversationId) {
91096
92155
  throw new Error("Expected at least one in-context message");
91097
92156
  }
91098
92157
  const retrievedMessages = await client.messages.retrieve(lastInContextId);
91099
- const backfillPage = isBackfillEnabled() ? await client.conversations.messages.list(conversationId, {
91100
- limit: MESSAGE_HISTORY_LIMIT,
91101
- order: "desc"
91102
- }) : null;
91103
- messages = backfillPage ? sortChronological(backfillPage.getPaginatedItems()) : [];
92158
+ if (isBackfillEnabled()) {
92159
+ try {
92160
+ const backfillPage = await client.conversations.messages.list(conversationId, {
92161
+ limit: MESSAGE_HISTORY_LIMIT,
92162
+ order: "desc"
92163
+ });
92164
+ messages = sortChronological(backfillPage.getPaginatedItems());
92165
+ } catch (backfillError) {
92166
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
92167
+ }
92168
+ }
91104
92169
  const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
91105
92170
  if (messageToCheck) {
91106
92171
  debugWarn("check-approval", `Found last in-context message: ${messageToCheck.id} (type: ${messageToCheck.message_type})` + (retrievedMessages.length > 1 ? ` - had ${retrievedMessages.length} variants` : ""));
@@ -91135,14 +92200,20 @@ async function getResumeData(client, agent, conversationId) {
91135
92200
  throw new Error("Expected at least one in-context message");
91136
92201
  }
91137
92202
  const retrievedMessages = await client.messages.retrieve(lastInContextId);
91138
- const messagesPage = isBackfillEnabled() ? await client.agents.messages.list(agent.id, {
91139
- limit: MESSAGE_HISTORY_LIMIT,
91140
- order: "desc",
91141
- conversation_id: "default"
91142
- }) : null;
91143
- messages = messagesPage ? sortChronological(messagesPage.items) : [];
91144
- if (process.env.DEBUG && messagesPage) {
91145
- console.log(`[DEBUG] agents.messages.list(conversation_id=default) returned ${messagesPage.items.length} messages`);
92203
+ if (isBackfillEnabled()) {
92204
+ try {
92205
+ const messagesPage = await client.agents.messages.list(agent.id, {
92206
+ limit: MESSAGE_HISTORY_LIMIT,
92207
+ order: "desc",
92208
+ conversation_id: "default"
92209
+ });
92210
+ messages = sortChronological(messagesPage.items);
92211
+ if (process.env.DEBUG) {
92212
+ console.log(`[DEBUG] agents.messages.list(conversation_id=default) returned ${messagesPage.items.length} messages`);
92213
+ }
92214
+ } catch (backfillError) {
92215
+ debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
92216
+ }
91146
92217
  }
91147
92218
  const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
91148
92219
  if (messageToCheck) {
@@ -94648,4 +95719,4 @@ Error during initialization: ${message}`);
94648
95719
  }
94649
95720
  main();
94650
95721
 
94651
- //# debugId=24E1A24D9A66945964756E2164756E21
95722
+ //# debugId=4B93041AB98FEDD564756E2164756E21