@letta-ai/letta-code 0.13.8 → 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.
|
|
259
|
+
var VERSION = "1.7.6";
|
|
260
260
|
|
|
261
261
|
// node_modules/@letta-ai/letta-client/internal/detect-platform.mjs
|
|
262
262
|
function getDetectedPlatform() {
|
|
@@ -3108,7 +3108,7 @@ var package_default;
|
|
|
3108
3108
|
var init_package = __esm(() => {
|
|
3109
3109
|
package_default = {
|
|
3110
3110
|
name: "@letta-ai/letta-code",
|
|
3111
|
-
version: "0.13.
|
|
3111
|
+
version: "0.13.9",
|
|
3112
3112
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3113
3113
|
type: "module",
|
|
3114
3114
|
bin: {
|
|
@@ -3138,7 +3138,7 @@ var init_package = __esm(() => {
|
|
|
3138
3138
|
access: "public"
|
|
3139
3139
|
},
|
|
3140
3140
|
dependencies: {
|
|
3141
|
-
"@letta-ai/letta-client": "1.7.
|
|
3141
|
+
"@letta-ai/letta-client": "^1.7.6",
|
|
3142
3142
|
glob: "^13.0.0",
|
|
3143
3143
|
"ink-link": "^5.0.0",
|
|
3144
3144
|
open: "^10.2.0",
|
|
@@ -5591,7 +5591,451 @@ var init_general_purpose = () => {};
|
|
|
5591
5591
|
// src/agent/subagents/builtin/memory.md
|
|
5592
5592
|
var memory_default = `---
|
|
5593
5593
|
name: memory
|
|
5594
|
-
description:
|
|
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
|
|
5595
6039
|
tools: Read, Edit, Write, Glob, Grep, Bash, conversation_search
|
|
5596
6040
|
model: opus
|
|
5597
6041
|
memoryBlocks: none
|
|
@@ -5603,12 +6047,23 @@ You are a memory management subagent launched via the Task tool to clean up and
|
|
|
5603
6047
|
|
|
5604
6048
|
## Your Purpose
|
|
5605
6049
|
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
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
|
|
5612
6067
|
|
|
5613
6068
|
## Important: Your Role is File Editing ONLY
|
|
5614
6069
|
|
|
@@ -5651,6 +6106,56 @@ Read({ file_path: ".letta/backups/working/human.md" })
|
|
|
5651
6106
|
- \`loaded_skills.md\` - System-managed
|
|
5652
6107
|
- \`manifest.json\` - Metadata file
|
|
5653
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
|
+
|
|
5654
6159
|
### Step 2: Edit Files to Clean Them Up
|
|
5655
6160
|
|
|
5656
6161
|
Edit each file using the Edit tool:
|
|
@@ -5662,29 +6167,17 @@ Edit({
|
|
|
5662
6167
|
new_string: "..."
|
|
5663
6168
|
})
|
|
5664
6169
|
\`\`\`
|
|
6170
|
+
## Output Format
|
|
5665
6171
|
|
|
5666
|
-
|
|
5667
|
-
- **Redundancy**: Remove duplicate information (version mentioned 3x, preferences repeated)
|
|
5668
|
-
- **Structure**: Add markdown headers (##, ###), bullet points, sections
|
|
5669
|
-
- **Clarity**: Resolve contradictions ("be detailed" vs "be concise")
|
|
5670
|
-
- **Scannability**: Make content easy to read at a glance
|
|
5671
|
-
|
|
5672
|
-
**Good memory structure:**
|
|
5673
|
-
- Use markdown headers (##, ###) for sections
|
|
5674
|
-
- Use bullet points for lists
|
|
5675
|
-
- Keep related information together
|
|
5676
|
-
- Make it scannable
|
|
5677
|
-
|
|
5678
|
-
### Step 2b: Structural Changes (Rename, Decompose, Merge)
|
|
6172
|
+
### Implement The Organizational Structure
|
|
5679
6173
|
|
|
5680
|
-
|
|
6174
|
+
Once you've proposed the hierarchy, execute it using file operations. Keep iterating until the directory matches your proposed structure exactly.
|
|
5681
6175
|
|
|
5682
6176
|
#### Renaming Blocks
|
|
5683
6177
|
|
|
5684
|
-
When a block's name doesn't reflect its content
|
|
6178
|
+
When a block's name doesn't reflect its content:
|
|
5685
6179
|
|
|
5686
6180
|
\`\`\`bash
|
|
5687
|
-
# Rename a memory block file
|
|
5688
6181
|
mv .letta/backups/working/old_name.md .letta/backups/working/new_name.md
|
|
5689
6182
|
\`\`\`
|
|
5690
6183
|
|
|
@@ -5693,140 +6186,101 @@ mv .letta/backups/working/old_name.md .letta/backups/working/new_name.md
|
|
|
5693
6186
|
- Block name doesn't match content (e.g., \`project.md\` contains user info → \`user_context.md\`)
|
|
5694
6187
|
- Name uses poor conventions (e.g., \`NOTES.md\` → \`notes.md\`)
|
|
5695
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
|
+
|
|
5696
6205
|
#### Decomposing Blocks (Split)
|
|
5697
6206
|
|
|
5698
|
-
When a
|
|
6207
|
+
When a block covers too many topics, split it:
|
|
5699
6208
|
|
|
5700
6209
|
\`\`\`bash
|
|
5701
|
-
# 1. Read the original
|
|
6210
|
+
# 1. Read the original
|
|
5702
6211
|
Read({ file_path: ".letta/backups/working/everything.md" })
|
|
5703
6212
|
|
|
5704
|
-
# 2. Create
|
|
6213
|
+
# 2. Create focused blocks
|
|
5705
6214
|
Write({ file_path: ".letta/backups/working/coding_preferences.md", content: "..." })
|
|
5706
6215
|
Write({ file_path: ".letta/backups/working/user_info.md", content: "..." })
|
|
5707
6216
|
|
|
5708
|
-
# 3. Delete the original
|
|
6217
|
+
# 3. Delete the original
|
|
5709
6218
|
rm .letta/backups/working/everything.md
|
|
5710
6219
|
\`\`\`
|
|
5711
6220
|
|
|
5712
|
-
**When to
|
|
5713
|
-
- Block exceeds ~
|
|
5714
|
-
- Block
|
|
5715
|
-
-
|
|
5716
|
-
- Finding specific info requires scanning the whole block
|
|
5717
|
-
|
|
5718
|
-
**Decomposition guidelines:**
|
|
5719
|
-
- Each new block should have ONE clear purpose
|
|
5720
|
-
- Use descriptive names: \`coding_style.md\`, \`user_preferences.md\`, \`project_context.md\`
|
|
5721
|
-
- Preserve all information - just reorganize it
|
|
5722
|
-
- Keep related information together in the same 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
|
|
5723
6225
|
|
|
5724
|
-
####
|
|
6226
|
+
#### Merging Blocks
|
|
5725
6227
|
|
|
5726
|
-
|
|
6228
|
+
When multiple blocks overlap, consolidate them:
|
|
5727
6229
|
|
|
5728
6230
|
\`\`\`bash
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
})
|
|
5733
|
-
\`\`\`
|
|
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" })
|
|
5734
6234
|
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
- Organizing content into a new category that doesn't fit existing blocks
|
|
5738
|
-
- 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: "..." })
|
|
5739
6237
|
|
|
5740
|
-
|
|
6238
|
+
# 3. Delete old blocks
|
|
6239
|
+
rm .letta/backups/working/user_info.md .letta/backups/working/user_prefs.md
|
|
6240
|
+
\`\`\`
|
|
5741
6241
|
|
|
5742
|
-
When
|
|
6242
|
+
**When to merge:**
|
|
6243
|
+
- Multiple blocks cover the same topic
|
|
6244
|
+
- Small blocks (<20 lines) logically belong together
|
|
6245
|
+
- Overlapping/duplicate content exists
|
|
5743
6246
|
|
|
5744
|
-
|
|
5745
|
-
# 1. Read all blocks to merge
|
|
5746
|
-
Read({ file_path: ".letta/backups/working/user_info.md" })
|
|
5747
|
-
Read({ file_path: ".letta/backups/working/user_prefs.md" })
|
|
6247
|
+
#### Editing Content Within Blocks
|
|
5748
6248
|
|
|
5749
|
-
|
|
5750
|
-
Write({ file_path: ".letta/backups/working/user.md", content: "..." })
|
|
6249
|
+
Use the Edit tool for in-place changes:
|
|
5751
6250
|
|
|
5752
|
-
|
|
5753
|
-
|
|
6251
|
+
\`\`\`
|
|
6252
|
+
Edit({
|
|
6253
|
+
file_path: ".letta/backups/working/project.md",
|
|
6254
|
+
old_string: "...",
|
|
6255
|
+
new_string: "..."
|
|
6256
|
+
})
|
|
5754
6257
|
\`\`\`
|
|
5755
6258
|
|
|
5756
|
-
**
|
|
5757
|
-
-
|
|
5758
|
-
-
|
|
5759
|
-
-
|
|
5760
|
-
-
|
|
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
|
|
5761
6264
|
|
|
5762
|
-
|
|
5763
|
-
- Multiple blocks cover the same topic area
|
|
5764
|
-
- Information is fragmented across blocks, causing redundancy
|
|
5765
|
-
- Small blocks (<20 lines) that logically belong together
|
|
5766
|
-
- Blocks with overlapping/duplicate content
|
|
5767
|
-
|
|
5768
|
-
**Merge guidelines:**
|
|
5769
|
-
- Remove duplicates when combining
|
|
5770
|
-
- Organize merged content with clear sections
|
|
5771
|
-
- Choose the most descriptive name for the merged block
|
|
5772
|
-
- Don't create blocks larger than ~150 lines
|
|
5773
|
-
- **DELETE the old block files** after consolidating their content
|
|
5774
|
-
|
|
5775
|
-
### Step 3: Report Results
|
|
5776
|
-
|
|
5777
|
-
Provide a comprehensive report showing what you changed and why.
|
|
5778
|
-
|
|
5779
|
-
## What to Write to Memory
|
|
5780
|
-
|
|
5781
|
-
**DO write to memory:**
|
|
5782
|
-
- Patterns that repeat across multiple sessions
|
|
5783
|
-
- User corrections or clarifications (especially if repeated)
|
|
5784
|
-
- Project conventions discovered through research or experience
|
|
5785
|
-
- Important context that will be needed in future sessions
|
|
5786
|
-
- Preferences expressed by the user about behavior or communication
|
|
5787
|
-
- "Aha!" moments or insights about the codebase
|
|
5788
|
-
- Footguns or gotchas discovered the hard way
|
|
5789
|
-
|
|
5790
|
-
**DON'T write to memory:**
|
|
5791
|
-
- Transient task details that won't matter tomorrow
|
|
5792
|
-
- Information easily found in files (unless it's a critical pattern)
|
|
5793
|
-
- Overly specific details that will quickly become stale
|
|
5794
|
-
- Things that should go in TODO lists or plan files instead
|
|
5795
|
-
|
|
5796
|
-
**Key principle**: Memory is for **persistent, important context** that makes the agent more effective over time. Not a dumping ground for everything.
|
|
5797
|
-
|
|
5798
|
-
## How to Decide What to Write
|
|
5799
|
-
|
|
5800
|
-
Ask yourself:
|
|
5801
|
-
1. **Will future-me need this?** If the agent encounters a similar situation in a week, would this memory help?
|
|
5802
|
-
2. **Is this a pattern or one-off?** One-off details fade in importance; patterns persist.
|
|
5803
|
-
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.
|
|
5804
|
-
4. **Did the user correct me?** User corrections are strong signals of what to remember.
|
|
5805
|
-
5. **Would I want to know this on day one?** Insights that would have saved time are worth storing.
|
|
5806
|
-
|
|
5807
|
-
## How to Reorganize Memory
|
|
5808
|
-
|
|
5809
|
-
**Signs memory needs reorganization:**
|
|
5810
|
-
- Blocks are long and hard to scan (>100 lines)
|
|
5811
|
-
- Related content is scattered across blocks
|
|
5812
|
-
- No clear structure (just walls of text)
|
|
5813
|
-
- Redundant information in multiple places
|
|
5814
|
-
- Outdated information mixed with current
|
|
5815
|
-
|
|
5816
|
-
**Reorganization strategies:**
|
|
5817
|
-
- **Add structure**: Use section headers, bullet points, categories
|
|
5818
|
-
- **Rename blocks**: Give blocks names that accurately reflect their content
|
|
5819
|
-
- **Decompose large blocks**: Break monolithic blocks (>100 lines, 3+ topics) into focused ones
|
|
5820
|
-
- **Merge fragmented blocks**: Consolidate small/overlapping blocks into unified ones
|
|
5821
|
-
- **Archive stale content**: Remove information that's no longer relevant
|
|
5822
|
-
- **Improve scannability**: Use consistent formatting, clear hierarchies
|
|
6265
|
+
#### Iteration Checklist
|
|
5823
6266
|
|
|
5824
|
-
|
|
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
|
|
6275
|
+
|
|
6276
|
+
**If you have fewer than 15 files, you haven't split enough. Go back and split more.**
|
|
5825
6277
|
|
|
5826
6278
|
Return a structured report with these sections:
|
|
5827
6279
|
|
|
5828
6280
|
### 1. Summary
|
|
5829
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)
|
|
5830
6284
|
- Number of files modified, renamed, created, or deleted
|
|
5831
6285
|
- The parent agent will prompt the user to confirm any creations or deletions
|
|
5832
6286
|
|
|
@@ -5839,15 +6293,16 @@ Report any renames, decompositions, or merges:
|
|
|
5839
6293
|
|----------|----------|--------|
|
|
5840
6294
|
| stuff.md | coding_preferences.md | Name now reflects content |
|
|
5841
6295
|
|
|
5842
|
-
**Decompositions (
|
|
6296
|
+
**Decompositions (using \`/\` hierarchy):**
|
|
5843
6297
|
| Original Block | New Blocks | Deleted | Reason |
|
|
5844
6298
|
|----------------|------------|---------|--------|
|
|
5845
|
-
|
|
|
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 |
|
|
5846
6300
|
|
|
5847
|
-
**New Blocks (
|
|
6301
|
+
**New Blocks (all using \`/\` naming):**
|
|
5848
6302
|
| Block Name | Size | Reason |
|
|
5849
6303
|
|------------|------|--------|
|
|
5850
|
-
|
|
|
6304
|
+
| project/security/auth.md | 156 chars | Nested under project/security |
|
|
6305
|
+
| human/prefs/communication.md | 98 chars | Split from human.md |
|
|
5851
6306
|
|
|
5852
6307
|
**Merges:**
|
|
5853
6308
|
| Merged Blocks | Result | Deleted | Reason |
|
|
@@ -5936,13 +6391,13 @@ Why: Resolved contradictions by explaining when to use each approach.
|
|
|
5936
6391
|
|
|
5937
6392
|
## Critical Reminders
|
|
5938
6393
|
|
|
5939
|
-
1. **
|
|
5940
|
-
2. **
|
|
5941
|
-
3. **
|
|
5942
|
-
4. **
|
|
5943
|
-
5. **
|
|
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
|
|
5944
6399
|
|
|
5945
|
-
Remember: Your goal is to
|
|
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.
|
|
5946
6401
|
`;
|
|
5947
6402
|
var init_memory = () => {};
|
|
5948
6403
|
|
|
@@ -8156,7 +8611,17 @@ function buildShellLaunchers(command) {
|
|
|
8156
8611
|
var SEP = "\x00";
|
|
8157
8612
|
|
|
8158
8613
|
// src/hooks/types.ts
|
|
8159
|
-
|
|
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
|
+
});
|
|
8160
8625
|
|
|
8161
8626
|
// src/hooks/executor.ts
|
|
8162
8627
|
import { spawn } from "node:child_process";
|
|
@@ -8222,11 +8687,25 @@ function executeWithLauncher(launcher, inputJson, workingDirectory, input, timeo
|
|
|
8222
8687
|
if (!resolved) {
|
|
8223
8688
|
resolved = true;
|
|
8224
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";
|
|
8225
|
-
console.log(`\x1B[90m[hook] ${
|
|
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
|
+
}
|
|
8226
8706
|
resolve2(result);
|
|
8227
8707
|
}
|
|
8228
8708
|
};
|
|
8229
|
-
console.log(`\x1B[90m[hook] Running: ${command}\x1B[0m`);
|
|
8230
8709
|
let child;
|
|
8231
8710
|
try {
|
|
8232
8711
|
child = trySpawnWithLauncher(launcher, workingDirectory, input);
|
|
@@ -8404,14 +8883,27 @@ function mergeHooksConfigs(global2, project, projectLocal = {}) {
|
|
|
8404
8883
|
...Object.keys(projectLocal)
|
|
8405
8884
|
]);
|
|
8406
8885
|
for (const event of allEvents) {
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
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
|
+
}
|
|
8415
8907
|
}
|
|
8416
8908
|
return merged;
|
|
8417
8909
|
}
|
|
@@ -8434,23 +8926,36 @@ function matchesTool(pattern, toolName) {
|
|
|
8434
8926
|
return pattern === toolName;
|
|
8435
8927
|
}
|
|
8436
8928
|
function getMatchingHooks(config, event, toolName) {
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
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) {
|
|
8444
8948
|
hooks.push(...matcher.hooks);
|
|
8445
8949
|
}
|
|
8950
|
+
return hooks;
|
|
8446
8951
|
}
|
|
8447
|
-
return hooks;
|
|
8448
8952
|
}
|
|
8449
8953
|
async function getHooksForEvent(event, toolName, workingDirectory = process.cwd()) {
|
|
8450
8954
|
const config = await loadHooks(workingDirectory);
|
|
8451
8955
|
return getMatchingHooks(config, event, toolName);
|
|
8452
8956
|
}
|
|
8453
8957
|
var init_loader = __esm(async () => {
|
|
8958
|
+
init_types();
|
|
8454
8959
|
await init_settings_manager();
|
|
8455
8960
|
});
|
|
8456
8961
|
|
|
@@ -61124,7 +61629,7 @@ function sortChronological2(messages) {
|
|
|
61124
61629
|
async function getResumeData2(client, agent, conversationId) {
|
|
61125
61630
|
try {
|
|
61126
61631
|
let inContextMessageIds;
|
|
61127
|
-
let messages;
|
|
61632
|
+
let messages = [];
|
|
61128
61633
|
const useConversationsApi = conversationId && conversationId !== "default";
|
|
61129
61634
|
if (process.env.DEBUG) {
|
|
61130
61635
|
console.log(`[DEBUG] getResumeData: conversationId=${conversationId}, useConversationsApi=${useConversationsApi}, agentId=${agent.id}`);
|
|
@@ -61135,12 +61640,16 @@ async function getResumeData2(client, agent, conversationId) {
|
|
|
61135
61640
|
if (!inContextMessageIds || inContextMessageIds.length === 0) {
|
|
61136
61641
|
debugWarn("check-approval", "No in-context messages - no pending approvals");
|
|
61137
61642
|
if (isBackfillEnabled2()) {
|
|
61138
|
-
|
|
61139
|
-
|
|
61140
|
-
|
|
61141
|
-
|
|
61142
|
-
|
|
61143
|
-
|
|
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
|
+
}
|
|
61144
61653
|
}
|
|
61145
61654
|
return {
|
|
61146
61655
|
pendingApproval: null,
|
|
@@ -61153,11 +61662,17 @@ async function getResumeData2(client, agent, conversationId) {
|
|
|
61153
61662
|
throw new Error("Expected at least one in-context message");
|
|
61154
61663
|
}
|
|
61155
61664
|
const retrievedMessages = await client.messages.retrieve(lastInContextId);
|
|
61156
|
-
|
|
61157
|
-
|
|
61158
|
-
|
|
61159
|
-
|
|
61160
|
-
|
|
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
|
+
}
|
|
61161
61676
|
const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
|
|
61162
61677
|
if (messageToCheck) {
|
|
61163
61678
|
debugWarn("check-approval", `Found last in-context message: ${messageToCheck.id} (type: ${messageToCheck.message_type})` + (retrievedMessages.length > 1 ? ` - had ${retrievedMessages.length} variants` : ""));
|
|
@@ -61192,14 +61707,20 @@ async function getResumeData2(client, agent, conversationId) {
|
|
|
61192
61707
|
throw new Error("Expected at least one in-context message");
|
|
61193
61708
|
}
|
|
61194
61709
|
const retrievedMessages = await client.messages.retrieve(lastInContextId);
|
|
61195
|
-
|
|
61196
|
-
|
|
61197
|
-
|
|
61198
|
-
|
|
61199
|
-
|
|
61200
|
-
|
|
61201
|
-
|
|
61202
|
-
|
|
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
|
+
}
|
|
61203
61724
|
}
|
|
61204
61725
|
const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
|
|
61205
61726
|
if (messageToCheck) {
|
|
@@ -71705,24 +72226,61 @@ async function addHookMatcher(event, matcher, location, workingDirectory = proce
|
|
|
71705
72226
|
hooks[event] = [];
|
|
71706
72227
|
}
|
|
71707
72228
|
const eventMatchers = hooks[event];
|
|
71708
|
-
|
|
71709
|
-
eventMatchers.push(matcher);
|
|
71710
|
-
}
|
|
72229
|
+
eventMatchers.push(matcher);
|
|
71711
72230
|
await saveHooksToLocation(hooks, location, workingDirectory);
|
|
71712
72231
|
}
|
|
71713
|
-
async function
|
|
72232
|
+
async function addSimpleHookMatcher(event, matcher, location, workingDirectory = process.cwd()) {
|
|
71714
72233
|
const hooks = loadHooksFromLocation(location, workingDirectory);
|
|
71715
|
-
|
|
71716
|
-
|
|
71717
|
-
throw new Error(`Invalid matcher index ${matcherIndex} for event ${event}`);
|
|
72234
|
+
if (!hooks[event]) {
|
|
72235
|
+
hooks[event] = [];
|
|
71718
72236
|
}
|
|
71719
|
-
eventMatchers
|
|
71720
|
-
|
|
71721
|
-
|
|
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
|
+
}
|
|
71722
72261
|
}
|
|
71723
72262
|
await saveHooksToLocation(hooks, location, workingDirectory);
|
|
71724
72263
|
}
|
|
71725
|
-
function
|
|
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()) {
|
|
71726
72284
|
const result = [];
|
|
71727
72285
|
const locations = ["project-local", "project", "user"];
|
|
71728
72286
|
for (const location of locations) {
|
|
@@ -71747,9 +72305,16 @@ function countTotalHooks(workingDirectory = process.cwd()) {
|
|
|
71747
72305
|
for (const location of locations) {
|
|
71748
72306
|
const hooks = loadHooksFromLocation(location, workingDirectory);
|
|
71749
72307
|
for (const event of Object.keys(hooks)) {
|
|
71750
|
-
|
|
71751
|
-
|
|
71752
|
-
|
|
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
|
+
}
|
|
71753
72318
|
}
|
|
71754
72319
|
}
|
|
71755
72320
|
}
|
|
@@ -71760,14 +72325,22 @@ function countHooksForEvent(event, workingDirectory = process.cwd()) {
|
|
|
71760
72325
|
const locations = ["project-local", "project", "user"];
|
|
71761
72326
|
for (const location of locations) {
|
|
71762
72327
|
const hooks = loadHooksFromLocation(location, workingDirectory);
|
|
71763
|
-
|
|
71764
|
-
|
|
71765
|
-
|
|
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
|
+
}
|
|
71766
72338
|
}
|
|
71767
72339
|
}
|
|
71768
72340
|
return count;
|
|
71769
72341
|
}
|
|
71770
72342
|
var init_writer = __esm(async () => {
|
|
72343
|
+
init_types();
|
|
71771
72344
|
await init_settings_manager();
|
|
71772
72345
|
});
|
|
71773
72346
|
|
|
@@ -71795,6 +72368,7 @@ function boxBottom(width) {
|
|
|
71795
72368
|
}
|
|
71796
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;
|
|
71797
72370
|
var init_HooksManager = __esm(async () => {
|
|
72371
|
+
init_types();
|
|
71798
72372
|
init_useTerminalWidth();
|
|
71799
72373
|
init_colors();
|
|
71800
72374
|
await __promiseAll([
|
|
@@ -71855,13 +72429,14 @@ var init_HooksManager = __esm(async () => {
|
|
|
71855
72429
|
const [screen, setScreen] = import_react57.useState("events");
|
|
71856
72430
|
const [selectedIndex, setSelectedIndex] = import_react57.useState(0);
|
|
71857
72431
|
const [selectedEvent, setSelectedEvent] = import_react57.useState(null);
|
|
71858
|
-
const [
|
|
72432
|
+
const [hooks, setHooks] = import_react57.useState([]);
|
|
71859
72433
|
const [totalHooks, setTotalHooks] = import_react57.useState(0);
|
|
71860
72434
|
const [newMatcher, setNewMatcher] = import_react57.useState("");
|
|
71861
72435
|
const [newCommand, setNewCommand] = import_react57.useState("");
|
|
71862
72436
|
const [selectedLocation, setSelectedLocation] = import_react57.useState(0);
|
|
71863
|
-
const [
|
|
72437
|
+
const [deleteHookIndex, setDeleteHookIndex] = import_react57.useState(-1);
|
|
71864
72438
|
const [deleteConfirmIndex, setDeleteConfirmIndex] = import_react57.useState(1);
|
|
72439
|
+
const isCurrentToolEvent = selectedEvent ? isToolEvent(selectedEvent) : false;
|
|
71865
72440
|
const refreshCounts = import_react57.useCallback(() => {
|
|
71866
72441
|
setTotalHooks(countTotalHooks());
|
|
71867
72442
|
}, []);
|
|
@@ -71870,9 +72445,12 @@ var init_HooksManager = __esm(async () => {
|
|
|
71870
72445
|
refreshCounts();
|
|
71871
72446
|
}
|
|
71872
72447
|
}, [screen, refreshCounts]);
|
|
71873
|
-
const
|
|
71874
|
-
|
|
71875
|
-
|
|
72448
|
+
const loadHooks2 = import_react57.useCallback((event) => {
|
|
72449
|
+
if (isToolEvent(event)) {
|
|
72450
|
+
setHooks(loadMatchersWithSource(event));
|
|
72451
|
+
} else {
|
|
72452
|
+
setHooks(loadSimpleMatchersWithSource(event));
|
|
72453
|
+
}
|
|
71876
72454
|
}, []);
|
|
71877
72455
|
const handleAddHook = import_react57.useCallback(async () => {
|
|
71878
72456
|
if (!selectedEvent || !newCommand.trim())
|
|
@@ -71880,45 +72458,46 @@ var init_HooksManager = __esm(async () => {
|
|
|
71880
72458
|
const location = SAVE_LOCATIONS[selectedLocation]?.location;
|
|
71881
72459
|
if (!location)
|
|
71882
72460
|
return;
|
|
71883
|
-
|
|
71884
|
-
matcher
|
|
71885
|
-
|
|
71886
|
-
|
|
71887
|
-
|
|
71888
|
-
|
|
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);
|
|
71889
72474
|
refreshCounts();
|
|
71890
72475
|
setNewMatcher("");
|
|
71891
72476
|
setNewCommand("");
|
|
71892
72477
|
setSelectedLocation(0);
|
|
71893
|
-
setScreen("
|
|
72478
|
+
setScreen("hooks-list");
|
|
71894
72479
|
setSelectedIndex(0);
|
|
71895
72480
|
}, [
|
|
71896
72481
|
selectedEvent,
|
|
71897
72482
|
newMatcher,
|
|
71898
72483
|
newCommand,
|
|
71899
72484
|
selectedLocation,
|
|
71900
|
-
|
|
72485
|
+
loadHooks2,
|
|
71901
72486
|
refreshCounts
|
|
71902
72487
|
]);
|
|
71903
72488
|
const handleDeleteHook = import_react57.useCallback(async () => {
|
|
71904
|
-
if (
|
|
72489
|
+
if (deleteHookIndex < 0 || !selectedEvent)
|
|
71905
72490
|
return;
|
|
71906
|
-
const
|
|
71907
|
-
if (!
|
|
72491
|
+
const hook = hooks[deleteHookIndex];
|
|
72492
|
+
if (!hook)
|
|
71908
72493
|
return;
|
|
71909
|
-
await
|
|
71910
|
-
|
|
72494
|
+
await removeHook(selectedEvent, hook.sourceIndex, hook.source);
|
|
72495
|
+
loadHooks2(selectedEvent);
|
|
71911
72496
|
refreshCounts();
|
|
71912
|
-
|
|
71913
|
-
setScreen("
|
|
72497
|
+
setDeleteHookIndex(-1);
|
|
72498
|
+
setScreen("hooks-list");
|
|
71914
72499
|
setSelectedIndex(0);
|
|
71915
|
-
}, [
|
|
71916
|
-
deleteMatcherIndex,
|
|
71917
|
-
selectedEvent,
|
|
71918
|
-
matchers,
|
|
71919
|
-
loadMatchers,
|
|
71920
|
-
refreshCounts
|
|
71921
|
-
]);
|
|
72500
|
+
}, [deleteHookIndex, selectedEvent, hooks, loadHooks2, refreshCounts]);
|
|
71922
72501
|
use_input_default((input, key) => {
|
|
71923
72502
|
if (key.ctrl && input === "c") {
|
|
71924
72503
|
onClose();
|
|
@@ -71933,26 +72512,31 @@ var init_HooksManager = __esm(async () => {
|
|
|
71933
72512
|
const selected = HOOK_EVENTS[selectedIndex];
|
|
71934
72513
|
if (selected) {
|
|
71935
72514
|
setSelectedEvent(selected.event);
|
|
71936
|
-
|
|
71937
|
-
setScreen("
|
|
72515
|
+
loadHooks2(selected.event);
|
|
72516
|
+
setScreen("hooks-list");
|
|
71938
72517
|
setSelectedIndex(0);
|
|
71939
72518
|
}
|
|
71940
72519
|
} else if (key.escape) {
|
|
71941
72520
|
onClose();
|
|
71942
72521
|
}
|
|
71943
|
-
} else if (screen === "
|
|
71944
|
-
const itemCount =
|
|
72522
|
+
} else if (screen === "hooks-list") {
|
|
72523
|
+
const itemCount = hooks.length + 1;
|
|
71945
72524
|
if (key.upArrow) {
|
|
71946
72525
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
71947
72526
|
} else if (key.downArrow) {
|
|
71948
72527
|
setSelectedIndex((prev) => Math.min(itemCount - 1, prev + 1));
|
|
71949
72528
|
} else if (key.return) {
|
|
71950
72529
|
if (selectedIndex === 0) {
|
|
71951
|
-
|
|
71952
|
-
|
|
72530
|
+
if (isCurrentToolEvent) {
|
|
72531
|
+
setScreen("add-matcher");
|
|
72532
|
+
setNewMatcher("");
|
|
72533
|
+
} else {
|
|
72534
|
+
setScreen("add-command");
|
|
72535
|
+
setNewCommand("");
|
|
72536
|
+
}
|
|
71953
72537
|
} else {}
|
|
71954
72538
|
} else if ((input === "d" || input === "D") && selectedIndex > 0) {
|
|
71955
|
-
|
|
72539
|
+
setDeleteHookIndex(selectedIndex - 1);
|
|
71956
72540
|
setDeleteConfirmIndex(1);
|
|
71957
72541
|
setScreen("delete-confirm");
|
|
71958
72542
|
} else if (key.escape) {
|
|
@@ -71965,7 +72549,7 @@ var init_HooksManager = __esm(async () => {
|
|
|
71965
72549
|
setScreen("add-command");
|
|
71966
72550
|
setNewCommand("");
|
|
71967
72551
|
} else if (key.escape) {
|
|
71968
|
-
setScreen("
|
|
72552
|
+
setScreen("hooks-list");
|
|
71969
72553
|
setSelectedIndex(0);
|
|
71970
72554
|
setNewMatcher("");
|
|
71971
72555
|
}
|
|
@@ -71974,7 +72558,12 @@ var init_HooksManager = __esm(async () => {
|
|
|
71974
72558
|
setScreen("save-location");
|
|
71975
72559
|
setSelectedLocation(0);
|
|
71976
72560
|
} else if (key.escape) {
|
|
71977
|
-
|
|
72561
|
+
if (isCurrentToolEvent) {
|
|
72562
|
+
setScreen("add-matcher");
|
|
72563
|
+
} else {
|
|
72564
|
+
setScreen("hooks-list");
|
|
72565
|
+
setSelectedIndex(0);
|
|
72566
|
+
}
|
|
71978
72567
|
}
|
|
71979
72568
|
} else if (screen === "save-location") {
|
|
71980
72569
|
if (key.upArrow) {
|
|
@@ -71993,10 +72582,10 @@ var init_HooksManager = __esm(async () => {
|
|
|
71993
72582
|
if (deleteConfirmIndex === 0) {
|
|
71994
72583
|
handleDeleteHook();
|
|
71995
72584
|
} else {
|
|
71996
|
-
setScreen("
|
|
72585
|
+
setScreen("hooks-list");
|
|
71997
72586
|
}
|
|
71998
72587
|
} else if (key.escape) {
|
|
71999
|
-
setScreen("
|
|
72588
|
+
setScreen("hooks-list");
|
|
72000
72589
|
}
|
|
72001
72590
|
}
|
|
72002
72591
|
});
|
|
@@ -72058,7 +72647,9 @@ var init_HooksManager = __esm(async () => {
|
|
|
72058
72647
|
]
|
|
72059
72648
|
}, undefined, true, undefined, this);
|
|
72060
72649
|
}
|
|
72061
|
-
if (screen === "
|
|
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...";
|
|
72062
72653
|
return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
|
|
72063
72654
|
flexDirection: "column",
|
|
72064
72655
|
paddingX: 1,
|
|
@@ -72067,27 +72658,46 @@ var init_HooksManager = __esm(async () => {
|
|
|
72067
72658
|
children: boxTop(boxWidth)
|
|
72068
72659
|
}, undefined, false, undefined, this),
|
|
72069
72660
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72070
|
-
children: boxLine(
|
|
72661
|
+
children: boxLine(title, boxWidth)
|
|
72071
72662
|
}, undefined, false, undefined, this),
|
|
72072
72663
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72073
72664
|
children: boxBottom(boxWidth)
|
|
72074
72665
|
}, undefined, false, undefined, this),
|
|
72075
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(
|
|
72076
|
-
|
|
72077
|
-
|
|
72078
|
-
|
|
72079
|
-
|
|
72080
|
-
|
|
72081
|
-
|
|
72082
|
-
|
|
72083
|
-
|
|
72084
|
-
|
|
72085
|
-
|
|
72086
|
-
|
|
72087
|
-
|
|
72088
|
-
|
|
72089
|
-
|
|
72090
|
-
|
|
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),
|
|
72091
72701
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72092
72702
|
children: " "
|
|
72093
72703
|
}, undefined, false, undefined, this),
|
|
@@ -72103,16 +72713,17 @@ var init_HooksManager = __esm(async () => {
|
|
|
72103
72713
|
}, undefined, true, undefined, this),
|
|
72104
72714
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72105
72715
|
color: "green",
|
|
72106
|
-
children:
|
|
72716
|
+
children: addLabel
|
|
72107
72717
|
}, undefined, false, undefined, this)
|
|
72108
72718
|
]
|
|
72109
72719
|
}, undefined, true, undefined, this),
|
|
72110
|
-
|
|
72720
|
+
hooks.map((hook, index) => {
|
|
72111
72721
|
const isSelected = index + 1 === selectedIndex;
|
|
72112
72722
|
const prefix = isSelected ? "❯" : " ";
|
|
72113
|
-
const sourceLabel = `[${getSourceLabel(
|
|
72114
|
-
const
|
|
72115
|
-
const
|
|
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 || "" : "";
|
|
72116
72727
|
const truncatedCommand = command.length > 30 ? `${command.slice(0, 27)}...` : command;
|
|
72117
72728
|
return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72118
72729
|
children: [
|
|
@@ -72130,7 +72741,7 @@ var init_HooksManager = __esm(async () => {
|
|
|
72130
72741
|
color: "cyan",
|
|
72131
72742
|
children: sourceLabel
|
|
72132
72743
|
}, undefined, false, undefined, this),
|
|
72133
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72744
|
+
matcherPattern !== null && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72134
72745
|
children: [
|
|
72135
72746
|
" ",
|
|
72136
72747
|
matcherPattern.padEnd(12),
|
|
@@ -72142,7 +72753,7 @@ var init_HooksManager = __esm(async () => {
|
|
|
72142
72753
|
children: truncatedCommand
|
|
72143
72754
|
}, undefined, false, undefined, this)
|
|
72144
72755
|
]
|
|
72145
|
-
}, `${
|
|
72756
|
+
}, `${hook.source}-${index}`, true, undefined, this);
|
|
72146
72757
|
}),
|
|
72147
72758
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72148
72759
|
children: " "
|
|
@@ -72248,6 +72859,7 @@ var init_HooksManager = __esm(async () => {
|
|
|
72248
72859
|
}, undefined, true, undefined, this);
|
|
72249
72860
|
}
|
|
72250
72861
|
if (screen === "add-command" && selectedEvent) {
|
|
72862
|
+
const title = isCurrentToolEvent ? ` Add new matcher for ${selectedEvent} ` : ` Add new hook for ${selectedEvent} `;
|
|
72251
72863
|
return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
|
|
72252
72864
|
flexDirection: "column",
|
|
72253
72865
|
paddingX: 1,
|
|
@@ -72256,18 +72868,18 @@ var init_HooksManager = __esm(async () => {
|
|
|
72256
72868
|
children: boxTop(boxWidth)
|
|
72257
72869
|
}, undefined, false, undefined, this),
|
|
72258
72870
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72259
|
-
children: boxLine(
|
|
72871
|
+
children: boxLine(title, boxWidth)
|
|
72260
72872
|
}, undefined, false, undefined, this),
|
|
72261
72873
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72262
72874
|
children: boxBottom(boxWidth)
|
|
72263
72875
|
}, undefined, false, undefined, this),
|
|
72264
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72876
|
+
isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72265
72877
|
children: [
|
|
72266
72878
|
"Matcher: ",
|
|
72267
72879
|
newMatcher || "*"
|
|
72268
72880
|
]
|
|
72269
72881
|
}, undefined, true, undefined, this),
|
|
72270
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72882
|
+
isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72271
72883
|
children: " "
|
|
72272
72884
|
}, undefined, false, undefined, this),
|
|
72273
72885
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
@@ -72327,7 +72939,7 @@ var init_HooksManager = __esm(async () => {
|
|
|
72327
72939
|
selectedEvent
|
|
72328
72940
|
]
|
|
72329
72941
|
}, undefined, true, undefined, this),
|
|
72330
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72942
|
+
isCurrentToolEvent && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72331
72943
|
children: [
|
|
72332
72944
|
"Matcher: ",
|
|
72333
72945
|
newMatcher || "*"
|
|
@@ -72383,8 +72995,11 @@ var init_HooksManager = __esm(async () => {
|
|
|
72383
72995
|
]
|
|
72384
72996
|
}, undefined, true, undefined, this);
|
|
72385
72997
|
}
|
|
72386
|
-
if (screen === "delete-confirm" &&
|
|
72387
|
-
const
|
|
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 : "";
|
|
72388
73003
|
return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
|
|
72389
73004
|
flexDirection: "column",
|
|
72390
73005
|
paddingX: 1,
|
|
@@ -72401,22 +73016,22 @@ var init_HooksManager = __esm(async () => {
|
|
|
72401
73016
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72402
73017
|
children: " "
|
|
72403
73018
|
}, undefined, false, undefined, this),
|
|
72404
|
-
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
73019
|
+
matcherPattern !== null && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72405
73020
|
children: [
|
|
72406
73021
|
"Matcher: ",
|
|
72407
|
-
|
|
73022
|
+
matcherPattern
|
|
72408
73023
|
]
|
|
72409
73024
|
}, undefined, true, undefined, this),
|
|
72410
73025
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72411
73026
|
children: [
|
|
72412
73027
|
"Command: ",
|
|
72413
|
-
|
|
73028
|
+
command
|
|
72414
73029
|
]
|
|
72415
73030
|
}, undefined, true, undefined, this),
|
|
72416
73031
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
72417
73032
|
children: [
|
|
72418
73033
|
"Source: ",
|
|
72419
|
-
|
|
73034
|
+
hook ? getSourceLabel(hook.source) : ""
|
|
72420
73035
|
]
|
|
72421
73036
|
}, undefined, true, undefined, this),
|
|
72422
73037
|
/* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
|
|
@@ -84542,6 +85157,13 @@ function App2({
|
|
|
84542
85157
|
runSessionEndHooks(durationMs, undefined, undefined, agentIdRef.current ?? undefined, conversationIdRef.current ?? undefined).catch(() => {});
|
|
84543
85158
|
};
|
|
84544
85159
|
}, []);
|
|
85160
|
+
import_react86.useEffect(() => {
|
|
85161
|
+
return () => {
|
|
85162
|
+
if (queueAppendTimeoutRef.current) {
|
|
85163
|
+
clearTimeout(queueAppendTimeoutRef.current);
|
|
85164
|
+
}
|
|
85165
|
+
};
|
|
85166
|
+
}, []);
|
|
84545
85167
|
const [showExitStats, setShowExitStats] = import_react86.useState(false);
|
|
84546
85168
|
const hasSentSessionContextRef = import_react86.useRef(false);
|
|
84547
85169
|
const turnCountRef = import_react86.useRef(0);
|
|
@@ -84554,6 +85176,10 @@ function App2({
|
|
|
84554
85176
|
const llmApiErrorRetriesRef = import_react86.useRef(0);
|
|
84555
85177
|
const conversationBusyRetriesRef = import_react86.useRef(0);
|
|
84556
85178
|
const [messageQueue, setMessageQueue] = import_react86.useState([]);
|
|
85179
|
+
const messageQueueRef = import_react86.useRef([]);
|
|
85180
|
+
import_react86.useEffect(() => {
|
|
85181
|
+
messageQueueRef.current = messageQueue;
|
|
85182
|
+
}, [messageQueue]);
|
|
84557
85183
|
const waitingForQueueCancelRef = import_react86.useRef(false);
|
|
84558
85184
|
const queueSnapshotRef = import_react86.useRef([]);
|
|
84559
85185
|
const [restoreQueueOnCancel, setRestoreQueueOnCancel] = import_react86.useState(false);
|
|
@@ -84561,12 +85187,24 @@ function App2({
|
|
|
84561
85187
|
import_react86.useEffect(() => {
|
|
84562
85188
|
restoreQueueOnCancelRef.current = restoreQueueOnCancel;
|
|
84563
85189
|
}, [restoreQueueOnCancel]);
|
|
85190
|
+
const queueAppendTimeoutRef = import_react86.useRef(null);
|
|
84564
85191
|
const [dequeueEpoch, setDequeueEpoch] = import_react86.useState(0);
|
|
84565
85192
|
const lastDequeuedMessageRef = import_react86.useRef(null);
|
|
84566
85193
|
const [restoredInput, setRestoredInput] = import_react86.useState(null);
|
|
84567
85194
|
const isAgentBusy = import_react86.useCallback(() => {
|
|
84568
85195
|
return streamingRef.current || isExecutingTool || commandRunningRef.current || abortControllerRef.current !== null;
|
|
84569
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
|
+
}, []);
|
|
84570
85208
|
const withCommandLock = import_react86.useCallback(async (asyncFn) => {
|
|
84571
85209
|
setActiveOverlay(null);
|
|
84572
85210
|
setCommandRunning(true);
|
|
@@ -85225,7 +85863,35 @@ ${newState.originalPrompt}`
|
|
|
85225
85863
|
llmApiErrorRetriesRef.current = 0;
|
|
85226
85864
|
conversationBusyRetriesRef.current = 0;
|
|
85227
85865
|
lastDequeuedMessageRef.current = null;
|
|
85228
|
-
runStopHooks(stopReasonToHandle, buffersRef.current.order.length, Array.from(buffersRef.current.byId.values()).filter((item) => item.kind === "tool_call").length)
|
|
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
|
+
}
|
|
85229
85895
|
if (needsEagerApprovalCheck) {
|
|
85230
85896
|
setNeedsEagerApprovalCheck(false);
|
|
85231
85897
|
}
|
|
@@ -85481,6 +86147,31 @@ ${newState.originalPrompt}`
|
|
|
85481
86147
|
refreshDerived();
|
|
85482
86148
|
return;
|
|
85483
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
|
+
}
|
|
85484
86175
|
if (waitingForQueueCancelRef.current) {
|
|
85485
86176
|
if (allResults.length > 0) {
|
|
85486
86177
|
queueApprovalResults(allResults, autoAllowedMetadata);
|
|
@@ -85829,7 +86520,8 @@ ${newState.originalPrompt}`
|
|
|
85829
86520
|
currentModelId,
|
|
85830
86521
|
updateStreamingOutput,
|
|
85831
86522
|
needsEagerApprovalCheck,
|
|
85832
|
-
queueApprovalResults
|
|
86523
|
+
queueApprovalResults,
|
|
86524
|
+
consumeQueuedMessages
|
|
85833
86525
|
]);
|
|
85834
86526
|
const handleExit = import_react86.useCallback(async () => {
|
|
85835
86527
|
saveLastAgentBeforeExit();
|
|
@@ -86475,29 +87167,34 @@ ${expanded.command}` : expanded.command;
|
|
|
86475
87167
|
setMessageQueue((prev) => {
|
|
86476
87168
|
const newQueue = [...prev, msg];
|
|
86477
87169
|
const isSlashCommand = msg.startsWith("/");
|
|
86478
|
-
if (!isSlashCommand && streamingRef.current && !waitingForQueueCancelRef.current) {
|
|
86479
|
-
|
|
86480
|
-
|
|
86481
|
-
|
|
86482
|
-
|
|
86483
|
-
|
|
86484
|
-
|
|
86485
|
-
|
|
86486
|
-
|
|
86487
|
-
|
|
86488
|
-
|
|
86489
|
-
return client.conversations.cancel(conversationIdRef.current);
|
|
86490
|
-
}).then(() => {}).catch(() => {
|
|
86491
|
-
waitingForQueueCancelRef.current = false;
|
|
86492
|
-
});
|
|
86493
|
-
setTimeout(() => {
|
|
86494
|
-
if (waitingForQueueCancelRef.current && abortControllerRef.current) {
|
|
86495
|
-
debugLog("queue", "Timeout fallback: aborting stream after 3s (server cancel was slow/failed)");
|
|
86496
|
-
abortControllerRef.current.abort();
|
|
86497
|
-
waitingForQueueCancelRef.current = false;
|
|
86498
|
-
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();
|
|
86499
87181
|
}
|
|
86500
|
-
|
|
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);
|
|
86501
87198
|
}
|
|
86502
87199
|
return newQueue;
|
|
86503
87200
|
});
|
|
@@ -88416,13 +89113,29 @@ ${SYSTEM_REMINDER_CLOSE}
|
|
|
88416
89113
|
waitingForQueueCancelRef.current = false;
|
|
88417
89114
|
queueSnapshotRef.current = [];
|
|
88418
89115
|
} else {
|
|
88419
|
-
|
|
88420
|
-
|
|
88421
|
-
{
|
|
88422
|
-
|
|
88423
|
-
|
|
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
|
+
});
|
|
88424
89134
|
}
|
|
88425
|
-
|
|
89135
|
+
refreshDerived();
|
|
89136
|
+
}
|
|
89137
|
+
toolResultsInFlightRef.current = true;
|
|
89138
|
+
await processConversation(input);
|
|
88426
89139
|
toolResultsInFlightRef.current = false;
|
|
88427
89140
|
queueApprovalResults(null);
|
|
88428
89141
|
}
|
|
@@ -88443,7 +89156,8 @@ ${SYSTEM_REMINDER_CLOSE}
|
|
|
88443
89156
|
appendError,
|
|
88444
89157
|
setStreaming,
|
|
88445
89158
|
updateStreamingOutput,
|
|
88446
|
-
queueApprovalResults
|
|
89159
|
+
queueApprovalResults,
|
|
89160
|
+
consumeQueuedMessages
|
|
88447
89161
|
]);
|
|
88448
89162
|
const handleApproveCurrent = import_react86.useCallback(async (diffs) => {
|
|
88449
89163
|
if (isExecutingTool)
|
|
@@ -91408,7 +92122,7 @@ function sortChronological(messages) {
|
|
|
91408
92122
|
async function getResumeData(client, agent, conversationId) {
|
|
91409
92123
|
try {
|
|
91410
92124
|
let inContextMessageIds;
|
|
91411
|
-
let messages;
|
|
92125
|
+
let messages = [];
|
|
91412
92126
|
const useConversationsApi = conversationId && conversationId !== "default";
|
|
91413
92127
|
if (process.env.DEBUG) {
|
|
91414
92128
|
console.log(`[DEBUG] getResumeData: conversationId=${conversationId}, useConversationsApi=${useConversationsApi}, agentId=${agent.id}`);
|
|
@@ -91419,12 +92133,16 @@ async function getResumeData(client, agent, conversationId) {
|
|
|
91419
92133
|
if (!inContextMessageIds || inContextMessageIds.length === 0) {
|
|
91420
92134
|
debugWarn("check-approval", "No in-context messages - no pending approvals");
|
|
91421
92135
|
if (isBackfillEnabled()) {
|
|
91422
|
-
|
|
91423
|
-
|
|
91424
|
-
|
|
91425
|
-
|
|
91426
|
-
|
|
91427
|
-
|
|
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
|
+
}
|
|
91428
92146
|
}
|
|
91429
92147
|
return {
|
|
91430
92148
|
pendingApproval: null,
|
|
@@ -91437,11 +92155,17 @@ async function getResumeData(client, agent, conversationId) {
|
|
|
91437
92155
|
throw new Error("Expected at least one in-context message");
|
|
91438
92156
|
}
|
|
91439
92157
|
const retrievedMessages = await client.messages.retrieve(lastInContextId);
|
|
91440
|
-
|
|
91441
|
-
|
|
91442
|
-
|
|
91443
|
-
|
|
91444
|
-
|
|
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
|
+
}
|
|
91445
92169
|
const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
|
|
91446
92170
|
if (messageToCheck) {
|
|
91447
92171
|
debugWarn("check-approval", `Found last in-context message: ${messageToCheck.id} (type: ${messageToCheck.message_type})` + (retrievedMessages.length > 1 ? ` - had ${retrievedMessages.length} variants` : ""));
|
|
@@ -91476,14 +92200,20 @@ async function getResumeData(client, agent, conversationId) {
|
|
|
91476
92200
|
throw new Error("Expected at least one in-context message");
|
|
91477
92201
|
}
|
|
91478
92202
|
const retrievedMessages = await client.messages.retrieve(lastInContextId);
|
|
91479
|
-
|
|
91480
|
-
|
|
91481
|
-
|
|
91482
|
-
|
|
91483
|
-
|
|
91484
|
-
|
|
91485
|
-
|
|
91486
|
-
|
|
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
|
+
}
|
|
91487
92217
|
}
|
|
91488
92218
|
const messageToCheck = retrievedMessages.find((msg) => msg.message_type === "approval_request_message") ?? retrievedMessages[0];
|
|
91489
92219
|
if (messageToCheck) {
|
|
@@ -94989,4 +95719,4 @@ Error during initialization: ${message}`);
|
|
|
94989
95719
|
}
|
|
94990
95720
|
main();
|
|
94991
95721
|
|
|
94992
|
-
//# debugId=
|
|
95722
|
+
//# debugId=4B93041AB98FEDD564756E2164756E21
|