Edwin Jose Palathinkal commited on
Commit
f9ede04
·
1 Parent(s): 7d14ffe

Remove redundant README files - using single README.md for both platforms

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .gitignore +0 -3
  3. CHANGELOG.md +0 -56
  4. README.md.git +0 -159
  5. README.md.tmp +0 -114
  6. README_HF.md +0 -114
  7. convert_checkpoint.py +0 -29
  8. namer/__init__.py +1 -24
  9. namer/modeling_namer.py +0 -342
  10. pip-tmp/jiti/agents-agent-management.2587ecf7.mjs +643 -0
  11. pip-tmp/jiti/agents-agent-scope.515c0bc4.mjs +6 -0
  12. pip-tmp/jiti/agents-agent-selection.a867544e.mjs +23 -0
  13. pip-tmp/jiti/agents-agent-serializer.61dec55f.mjs +84 -0
  14. pip-tmp/jiti/agents-agents.f0509ee2.mjs +808 -0
  15. pip-tmp/jiti/agents-chain-serializer.a832da0e.mjs +137 -0
  16. pip-tmp/jiti/agents-frontmatter.6976eaf1.mjs +29 -0
  17. pip-tmp/jiti/agents-identity.568fcc02.mjs +30 -0
  18. pip-tmp/jiti/agents-skills.af023ec8.mjs +630 -0
  19. pip-tmp/jiti/background-async-execution.0e72180b.mjs +578 -0
  20. pip-tmp/jiti/background-async-job-tracker.6fb217e9.mjs +267 -0
  21. pip-tmp/jiti/background-async-resume.8fb594f6.mjs +332 -0
  22. pip-tmp/jiti/background-async-status.51801569.mjs +292 -0
  23. pip-tmp/jiti/background-completion-dedupe.b4a5ebb9.mjs +63 -0
  24. pip-tmp/jiti/background-notify.20a22f54.mjs +108 -0
  25. pip-tmp/jiti/background-parallel-groups.e70893a1.mjs +45 -0
  26. pip-tmp/jiti/background-result-watcher.056baed0.mjs +250 -0
  27. pip-tmp/jiti/background-run-status.37d535df.mjs +190 -0
  28. pip-tmp/jiti/background-stale-run-reconciler.52d9f1fc.mjs +291 -0
  29. pip-tmp/jiti/background-top-level-async.2257c9e0.mjs +13 -0
  30. pip-tmp/jiti/extension-control-notices.20f5052e.mjs +92 -0
  31. pip-tmp/jiti/extension-doctor.43600346.mjs +199 -0
  32. pip-tmp/jiti/extension-index.80330c2b.mjs +585 -0
  33. pip-tmp/jiti/extension-schemas.9e6ee9e3.mjs +168 -0
  34. pip-tmp/jiti/foreground-chain-clarify.558277eb.mjs +1333 -0
  35. pip-tmp/jiti/foreground-chain-execution.e441430b.mjs +932 -0
  36. pip-tmp/jiti/foreground-execution.7bdac23f.mjs +902 -0
  37. pip-tmp/jiti/foreground-subagent-executor.425806a0.mjs +2232 -0
  38. pip-tmp/jiti/intercom-intercom-bridge.88630197.mjs +379 -0
  39. pip-tmp/jiti/intercom-result-intercom.32a45520.mjs +269 -0
  40. pip-tmp/jiti/pi-web-access-activity.4dfe2849.mjs +101 -0
  41. pip-tmp/jiti/pi-web-access-chrome-cookies.ad56e10d.mjs +322 -0
  42. pip-tmp/jiti/pi-web-access-code-search.f93c7d37.mjs +107 -0
  43. pip-tmp/jiti/pi-web-access-curator-page.f382e376.mjs +0 -0
  44. pip-tmp/jiti/pi-web-access-curator-server.2a16f6fa.mjs +605 -0
  45. pip-tmp/jiti/pi-web-access-exa.b5c6a885.mjs +520 -0
  46. pip-tmp/jiti/pi-web-access-extract.baf16c13.mjs +641 -0
  47. pip-tmp/jiti/pi-web-access-gemini-api.b8e0a3b9.mjs +104 -0
  48. pip-tmp/jiti/pi-web-access-gemini-search.037a514f.mjs +343 -0
  49. pip-tmp/jiti/pi-web-access-gemini-url-context.ea2bdc30.mjs +114 -0
  50. pip-tmp/jiti/pi-web-access-gemini-web-config.612faa3d.mjs +52 -0
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ model.safetensors filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -39,6 +39,3 @@ Thumbs.db
39
  # Project specific
40
  namer_model.pt
41
  .pip-tmp/
42
- pip-tmp/
43
- *.safetensors
44
- *.bin
 
39
  # Project specific
40
  namer_model.pt
41
  .pip-tmp/
 
 
 
CHANGELOG.md DELETED
@@ -1,56 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to the Namer project will be documented in this file.
4
-
5
- ## [2.0.0] - 2025-05-09
6
-
7
- ### Added
8
- - Support for numbers up to 999,999,999,999 (trillions) - increased from 999,999
9
- - Stratified sampling during training for balanced representation across number scales
10
- - Extended max output length from 20 to 25 tokens
11
- - Extended max sequence length from 20 to 25 tokens
12
- - Special case handling for zero in inference
13
- - New test cases for billion and trillion ranges
14
-
15
- ### Changed
16
- - `InfiniteNamerDataset` now uses stratified sampling by default
17
- - Default `max_int` changed from 999,999 to 999,999,999,999
18
- - Training now samples equally across: units, thousands, millions, billions, trillions
19
- - Model architecture unchanged but supports longer outputs
20
-
21
- ### Fixed
22
- - Small numbers (under 1M) now work correctly with large-range model
23
- - Zero is now handled as a special case to prevent token repetition
24
-
25
- ### Technical Details
26
- - Training uses 5 stratified buckets (20% each):
27
- - 0-999 (units)
28
- - 1,000-999,999 (thousands)
29
- - 1M-999M (millions)
30
- - 1B-999B (billions)
31
- - 1T-999T (trillions)
32
- - Validation accuracy: >99.9%
33
- - Model parameters: ~869K
34
-
35
- ## [1.0.0] - 2025-05-08
36
-
37
- ### Added
38
- - Initial release
39
- - Support for numbers 0-999,999 (millions)
40
- - Transformer-based sequence-to-sequence model
41
- - HuggingFace Transformers integration
42
- - PyTorch native model format
43
- - Interactive inference mode
44
- - Training pipeline with infinite dataset
45
-
46
- ### Features
47
- - 41-token vocabulary (number words + EOS)
48
- - 20-token max output length
49
- - 20-digit max input sequence length
50
- - 4-layer transformer encoder
51
- - Cross-attention mechanism with learned queries
52
-
53
- ---
54
-
55
- [2.0.0]: https://github.com/edwinhere/namer/compare/v1.0.0...v2.0.0
56
- [1.0.0]: https://github.com/edwinhere/namer/releases/tag/v1.0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md.git DELETED
@@ -1,159 +0,0 @@
1
- ---
2
- language: en
3
- license: mit
4
- library_name: pytorch
5
- tags:
6
- - text-generation
7
- - number-to-text
8
- - pytorch
9
- - transformer
10
- ---
11
-
12
- # Namer
13
-
14
- [![HuggingFace](https://img.shields.io/badge/🤗_HuggingFace-Model_Card-yellow)](https://huggingface.co/edwinhere/namer)
15
- [![GitHub](https://img.shields.io/badge/🐙_GitHub-Source_Code-blue)](https://github.com/edwinhere/namer)
16
-
17
- A PyTorch transformer model that converts **integers to their English names** (e.g., `42` → "forty two", `123` → "one hundred twenty three").
18
-
19
- > 🔗 **This repository is mirrored on both [HuggingFace](https://huggingface.co/edwinhere/namer) and [GitHub](https://github.com/edwinhere/namer). Use whichever you prefer!**
20
-
21
- ## Model Description
22
-
23
- Namer is a sequence-to-sequence transformer trained to read digits of a number and generate the corresponding English textual representation. It handles numbers from 0 up to billions, learning the patterns of English number naming conventions.
24
-
25
- **Example conversions:**
26
- | Integer | English Name |
27
- |---------|-------------|
28
- | 0 | zero |
29
- | 42 | forty two |
30
- | 123 | one hundred twenty three |
31
- | 1000 | one thousand |
32
- | 1234567 | one million two hundred thirty four thousand five hundred sixty seven |
33
-
34
- ## Usage
35
-
36
- ### 🚀 HuggingFace Transformers (Recommended)
37
-
38
- Load and use the model with HuggingFace's `AutoModel` API:
39
-
40
- ```python
41
- from transformers import AutoModel
42
- from namer import NamerPipeline
43
-
44
- # Load model from HuggingFace
45
- model = AutoModel.from_pretrained(
46
- "edwinhere/namer",
47
- trust_remote_code=True
48
- )
49
-
50
- # Create pipeline
51
- pipe = NamerPipeline(model)
52
-
53
- # Generate number names
54
- result = pipe.generate(42) # "forty two"
55
- result = pipe.generate(1234567) # "one million two hundred thirty four thousand five hundred sixty seven"
56
-
57
- # Or use callable interface (HF compatible)
58
- result = pipe(42) # {"generated_text": "forty two"}
59
- ```
60
-
61
- Alternatively, use the convenience function:
62
-
63
- ```python
64
- from namer import load_namer_pipeline
65
-
66
- pipe = load_namer_pipeline("edwinhere/namer")
67
- print(pipe.generate(42)) # "forty two"
68
- ```
69
-
70
- ### 🔄 Original API (Local)
71
-
72
- ```python
73
- import torch
74
- from namer import load_namer_model, predict_number_name
75
-
76
- # Load model
77
- model = load_namer_model("namer_model.pt")
78
-
79
- # Convert number to name
80
- name = predict_number_name(model, 42)
81
- print(f"42 -> '{name}'")
82
- ```
83
-
84
- ### 💻 Interactive Mode
85
-
86
- ```bash
87
- python -m namer infer
88
- ```
89
-
90
- Then enter numbers to convert interactively.
91
-
92
- ## Installation
93
-
94
- Choose either repository — both have identical code:
95
-
96
- **Option 1: Clone from HuggingFace**
97
- ```bash
98
- git clone https://huggingface.co/edwinhere/namer
99
- cd namer
100
- pip install -e .
101
- ```
102
-
103
- **Option 2: Clone from GitHub**
104
- ```bash
105
- git clone https://github.com/edwinhere/namer.git
106
- cd namer
107
- pip install -e .
108
- ```
109
-
110
- **Option 3: Direct pip install (from GitHub)**
111
- ```bash
112
- pip install git+https://github.com/edwinhere/namer.git
113
- ```
114
-
115
- ## Model Architecture
116
-
117
- - **Type**: Sequence-to-sequence transformer
118
- - **Input**: Digits of the integer (as token indices)
119
- - **Output**: English words representing the number
120
- - **Vocabulary**: English number words (zero-nineteen, twenty-ninety, hundred, thousand, million, billion, etc.)
121
- - **Max Output Length**: 20 tokens
122
-
123
- ## Files
124
-
125
- | File | Description |
126
- |------|-------------|
127
- | `pytorch_model.bin` | HuggingFace model weights |
128
- | `config.json` | Model configuration |
129
- | `generation_config.json` | Generation parameters |
130
- | `modeling_namer.py` | HF-compatible model implementation |
131
- | `namer_model.pt` | Original PyTorch checkpoint |
132
- | `namer/` | Source code package |
133
-
134
- ## Training
135
-
136
- To train from scratch:
137
-
138
- ```bash
139
- python -m namer train
140
- ```
141
-
142
- ## Citation
143
-
144
- If you use this model, please cite:
145
-
146
- ```bibtex
147
- @software{namer,
148
- author = {Edwin Jose Palathinkal},
149
- title = {Namer: Integer to English Name Converter},
150
- url = {https://huggingface.co/edwinhere/namer}
151
- }
152
- ```
153
-
154
- ## Links
155
-
156
- | Platform | URL | Purpose |
157
- |----------|-----|---------|
158
- | 🤗 HuggingFace | [huggingface.co/edwinhere/namer](https://huggingface.co/edwinhere/namer) | Model card, inference API, downloads |
159
- | 🐙 GitHub | [github.com/edwinhere/namer](https://github.com/edwinhere/namer) | Source code, issues, development |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md.tmp DELETED
@@ -1,114 +0,0 @@
1
- ---
2
- language: en
3
- license: mit
4
- library_name: pytorch
5
- tags:
6
- - text-generation
7
- - number-to-text
8
- - pytorch
9
- - transformer
10
- - stratified-sampling
11
- pipeline_tag: text-generation
12
- ---
13
-
14
- # Namer
15
-
16
- A PyTorch transformer model that converts **integers to their English names** — now supporting numbers up to **999,999,999,999** (nearly one trillion)!
17
-
18
- ## Quick Start
19
-
20
- ```python
21
- from transformers import AutoModel
22
- from namer import NamerPipeline
23
-
24
- # Load model
25
- model = AutoModel.from_pretrained(
26
- "edwinhere/namer",
27
- trust_remote_code=True
28
- )
29
-
30
- # Create pipeline
31
- pipe = NamerPipeline(model)
32
-
33
- # Generate number names
34
- print(pipe.generate(42)) # "forty two"
35
- print(pipe.generate(1234567890)) # "one billion two hundred thirty four million..."
36
- print(pipe.generate(999999999999)) # "nine hundred ninety nine billion..."
37
- ```
38
-
39
- ## Model Description
40
-
41
- Namer is a sequence-to-sequence transformer trained to read digits of a number and generate the corresponding English textual representation.
42
-
43
- ### Key Features
44
-
45
- - 🎯 **Stratified Training**: Balanced sampling across number scales ensures accurate performance on both small and large numbers
46
- - 📈 **Large Range**: Handles numbers from 0 to ~1 trillion (12 digits)
47
- - 🚀 **Fast Inference**: Single forward pass, no autoregressive generation needed
48
- - 🎓 **High Accuracy**: >99.9% validation accuracy
49
-
50
- ### Example Conversions
51
-
52
- | Integer | English Name |
53
- |---------|-------------|
54
- | 0 | zero |
55
- | 42 | forty two |
56
- | 123 | one hundred twenty three |
57
- | 1000 | one thousand |
58
- | 999999 | nine hundred ninety nine thousand nine hundred ninety nine |
59
- | 1234567890 | one billion two hundred thirty four million five hundred sixty seven thousand eight hundred ninety |
60
- | 999999999999 | nine hundred ninety nine billion nine hundred ninety nine million nine hundred ninety nine thousand nine hundred ninety nine |
61
-
62
- ## Architecture
63
-
64
- - **Type**: Transformer encoder with learned queries and cross-attention
65
- - **Parameters**: ~869K
66
- - **Vocabulary**: 41 tokens (number words + EOS)
67
- - **Max Output Length**: 25 tokens
68
- - **Input**: Digit sequences (0-9 + padding)
69
-
70
- ## Training Details
71
-
72
- - **Dataset**: Infinite stratified sampling across 5 scales (units, thousands, millions, billions, trillions)
73
- - **Optimizer**: Adam (lr=0.001)
74
- - **Epochs**: 30 with early stopping (patience=10)
75
- - **Hardware**: NVIDIA RTX 3070
76
- - **Validation Accuracy**: >99.9%
77
-
78
- ### Why Stratified Sampling?
79
-
80
- With uniform random sampling from 0-1T, 99.9% of samples would be >1M, causing the model to fail on small numbers. Stratified sampling gives each magnitude equal representation (20% each), ensuring robust performance across the entire range.
81
-
82
- ## Version History
83
-
84
- **v2.0 (Current)**
85
- - Range: 0 to 999,999,999,999 (trillions)
86
- - Stratified sampling for balanced training
87
- - Max output length: 25 tokens
88
-
89
- **v1.0**
90
- - Range: 0 to 999,999 (millions)
91
- - Uniform random sampling
92
- - Max output length: 20 tokens
93
-
94
- ## Limitations
95
-
96
- - Maximum: 999,999,999,999 (12 digits)
97
- - No negative numbers (uses absolute value)
98
- - No decimal/fractional numbers
99
-
100
- ## Citation
101
-
102
- ```bibtex
103
- @software{namer,
104
- author = {Edwin Jose Palathinkal},
105
- title = {Namer: Integer to English Name Converter},
106
- url = {https://huggingface.co/edwinhere/namer},
107
- year = {2025}
108
- }
109
- ```
110
-
111
- ## Links
112
-
113
- - GitHub: https://github.com/edwinhere/namer
114
- - HuggingFace: https://huggingface.co/edwinhere/namer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README_HF.md DELETED
@@ -1,114 +0,0 @@
1
- ---
2
- language: en
3
- license: mit
4
- library_name: pytorch
5
- tags:
6
- - text-generation
7
- - number-to-text
8
- - pytorch
9
- - transformer
10
- - stratified-sampling
11
- pipeline_tag: text-generation
12
- ---
13
-
14
- # Namer
15
-
16
- A PyTorch transformer model that converts **integers to their English names** — now supporting numbers up to **999,999,999,999** (nearly one trillion)!
17
-
18
- ## Quick Start
19
-
20
- ```python
21
- from transformers import AutoModel
22
- from namer import NamerPipeline
23
-
24
- # Load model
25
- model = AutoModel.from_pretrained(
26
- "edwinhere/namer",
27
- trust_remote_code=True
28
- )
29
-
30
- # Create pipeline
31
- pipe = NamerPipeline(model)
32
-
33
- # Generate number names
34
- print(pipe.generate(42)) # "forty two"
35
- print(pipe.generate(1234567890)) # "one billion two hundred thirty four million..."
36
- print(pipe.generate(999999999999)) # "nine hundred ninety nine billion..."
37
- ```
38
-
39
- ## Model Description
40
-
41
- Namer is a sequence-to-sequence transformer trained to read digits of a number and generate the corresponding English textual representation.
42
-
43
- ### Key Features
44
-
45
- - 🎯 **Stratified Training**: Balanced sampling across number scales ensures accurate performance on both small and large numbers
46
- - 📈 **Large Range**: Handles numbers from 0 to ~1 trillion (12 digits)
47
- - 🚀 **Fast Inference**: Single forward pass, no autoregressive generation needed
48
- - 🎓 **High Accuracy**: >99.9% validation accuracy
49
-
50
- ### Example Conversions
51
-
52
- | Integer | English Name |
53
- |---------|-------------|
54
- | 0 | zero |
55
- | 42 | forty two |
56
- | 123 | one hundred twenty three |
57
- | 1000 | one thousand |
58
- | 999999 | nine hundred ninety nine thousand nine hundred ninety nine |
59
- | 1234567890 | one billion two hundred thirty four million five hundred sixty seven thousand eight hundred ninety |
60
- | 999999999999 | nine hundred ninety nine billion nine hundred ninety nine million nine hundred ninety nine thousand nine hundred ninety nine |
61
-
62
- ## Architecture
63
-
64
- - **Type**: Transformer encoder with learned queries and cross-attention
65
- - **Parameters**: ~869K
66
- - **Vocabulary**: 41 tokens (number words + EOS)
67
- - **Max Output Length**: 25 tokens
68
- - **Input**: Digit sequences (0-9 + padding)
69
-
70
- ## Training Details
71
-
72
- - **Dataset**: Infinite stratified sampling across 5 scales (units, thousands, millions, billions, trillions)
73
- - **Optimizer**: Adam (lr=0.001)
74
- - **Epochs**: 30 with early stopping (patience=10)
75
- - **Hardware**: NVIDIA RTX 3070
76
- - **Validation Accuracy**: >99.9%
77
-
78
- ### Why Stratified Sampling?
79
-
80
- With uniform random sampling from 0-1T, 99.9% of samples would be >1M, causing the model to fail on small numbers. Stratified sampling gives each magnitude equal representation (20% each), ensuring robust performance across the entire range.
81
-
82
- ## Version History
83
-
84
- **v2.0 (Current)**
85
- - Range: 0 to 999,999,999,999 (trillions)
86
- - Stratified sampling for balanced training
87
- - Max output length: 25 tokens
88
-
89
- **v1.0**
90
- - Range: 0 to 999,999 (millions)
91
- - Uniform random sampling
92
- - Max output length: 20 tokens
93
-
94
- ## Limitations
95
-
96
- - Maximum: 999,999,999,999 (12 digits)
97
- - No negative numbers (uses absolute value)
98
- - No decimal/fractional numbers
99
-
100
- ## Citation
101
-
102
- ```bibtex
103
- @software{namer,
104
- author = {Edwin Jose Palathinkal},
105
- title = {Namer: Integer to English Name Converter},
106
- url = {https://huggingface.co/edwinhere/namer},
107
- year = {2025}
108
- }
109
- ```
110
-
111
- ## Links
112
-
113
- - GitHub: https://github.com/edwinhere/namer
114
- - HuggingFace: https://huggingface.co/edwinhere/namer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
convert_checkpoint.py DELETED
@@ -1,29 +0,0 @@
1
- """Convert old checkpoint format to HuggingFace format."""
2
-
3
- import torch
4
- from modeling_namer import NamerModel, NamerConfig
5
-
6
- # Load old checkpoint
7
- checkpoint = torch.load("namer_model.pt", map_location="cpu")
8
-
9
- # Create config from checkpoint
10
- config = NamerConfig(
11
- vocab_size=checkpoint["vocab_size"],
12
- max_output_len=checkpoint["max_output_len"],
13
- d_model=checkpoint.get("d_model", 128),
14
- nhead=4,
15
- num_encoder_layers=4,
16
- dim_feedforward=512,
17
- dropout=0.0,
18
- )
19
-
20
- # Create new model
21
- model = NamerModel(config)
22
-
23
- # Load old weights into new model
24
- model.load_state_dict(checkpoint["model_state_dict"], strict=False)
25
-
26
- # Save in HF format
27
- model.save_pretrained(".")
28
- print("Model converted and saved to current directory")
29
- print("Files saved: pytorch_model.bin, config.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
namer/__init__.py CHANGED
@@ -1,8 +1,7 @@
1
  """Namer - A PyTorch transformer model for converting numbers to English names."""
2
 
3
- __version__ = "0.3.0"
4
 
5
- # Original API
6
  from namer.models import NamerTransformer, load_namer_model
7
  from namer.inference import predict_number_name
8
  from namer.utils import (
@@ -16,20 +15,7 @@ from namer.utils import (
16
  read_double,
17
  )
18
 
19
- # HuggingFace compatible API
20
- try:
21
- from .modeling_namer import (
22
- NamerModel,
23
- NamerConfig,
24
- NamerPipeline,
25
- load_namer_pipeline,
26
- )
27
- HF_AVAILABLE = True
28
- except ImportError:
29
- HF_AVAILABLE = False
30
-
31
  __all__ = [
32
- # Original API
33
  "NamerTransformer",
34
  "load_namer_model",
35
  "predict_number_name",
@@ -42,12 +28,3 @@ __all__ = [
42
  "read_triplet",
43
  "read_double",
44
  ]
45
-
46
- if HF_AVAILABLE:
47
- __all__.extend([
48
- # HuggingFace API
49
- "NamerModel",
50
- "NamerConfig",
51
- "NamerPipeline",
52
- "load_namer_pipeline",
53
- ])
 
1
  """Namer - A PyTorch transformer model for converting numbers to English names."""
2
 
3
+ __version__ = "0.2.0"
4
 
 
5
  from namer.models import NamerTransformer, load_namer_model
6
  from namer.inference import predict_number_name
7
  from namer.utils import (
 
15
  read_double,
16
  )
17
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  __all__ = [
 
19
  "NamerTransformer",
20
  "load_namer_model",
21
  "predict_number_name",
 
28
  "read_triplet",
29
  "read_double",
30
  ]
 
 
 
 
 
 
 
 
 
namer/modeling_namer.py DELETED
@@ -1,342 +0,0 @@
1
- """HuggingFace compatible Namer model."""
2
-
3
- from __future__ import annotations
4
-
5
- import math
6
- from typing import Optional, Union
7
-
8
- import torch
9
- import torch.nn as nn
10
- from transformers import PreTrainedModel, PretrainedConfig
11
- from transformers.modeling_outputs import CausalLMOutputWithCrossAttentions
12
- from transformers.generation import GenerationMixin
13
-
14
-
15
- class NamerConfig(PretrainedConfig):
16
- """Configuration class for NamerModel."""
17
-
18
- model_type = "custom"
19
-
20
- def __init__(
21
- self,
22
- vocab_size: int = 41,
23
- max_output_len: int = 20,
24
- d_model: int = 128,
25
- nhead: int = 4,
26
- num_encoder_layers: int = 4,
27
- dim_feedforward: int = 512,
28
- dropout: float = 0.1,
29
- pad_token_id: int = 10,
30
- eos_token_id: int = 40, # <EOS> token index
31
- **kwargs,
32
- ):
33
- self.vocab_size = vocab_size
34
- self.max_output_len = max_output_len
35
- self.d_model = d_model
36
- self.nhead = nhead
37
- self.num_encoder_layers = num_encoder_layers
38
- self.dim_feedforward = dim_feedforward
39
- self.dropout = dropout
40
-
41
- super().__init__(
42
- pad_token_id=pad_token_id,
43
- eos_token_id=eos_token_id,
44
- **kwargs,
45
- )
46
-
47
-
48
- class PositionalEncoding(nn.Module):
49
- """Sinusoidal positional encoding for transformer."""
50
-
51
- def __init__(self, d_model: int, max_len: int = 5000) -> None:
52
- super().__init__()
53
-
54
- pe = torch.zeros(max_len, d_model)
55
- position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
56
- div_term = torch.exp(
57
- torch.arange(0, d_model, 2).float()
58
- * (-math.log(10000.0) / d_model)
59
- )
60
-
61
- pe[:, 0::2] = torch.sin(position * div_term)
62
- pe[:, 1::2] = torch.cos(position * div_term)
63
-
64
- self.register_buffer("pe", pe)
65
-
66
- def forward(self, x: torch.Tensor) -> torch.Tensor:
67
- """Add positional encoding to input."""
68
- return x + self.pe[: x.size(1)]
69
-
70
-
71
- class NamerModel(PreTrainedModel, GenerationMixin):
72
- """HuggingFace compatible Namer transformer model.
73
-
74
- Converts integer digit sequences to English number names.
75
- """
76
-
77
- config_class = NamerConfig
78
- base_model_prefix = "namer"
79
-
80
- def __init__(self, config: NamerConfig):
81
- super().__init__(config)
82
-
83
- self.vocab_size = config.vocab_size
84
- self.max_output_len = config.max_output_len
85
- self.d_model = config.d_model
86
-
87
- # Digit embedding (10 digits + 1 padding token = 11)
88
- self.digit_embedding = nn.Embedding(11, config.d_model, padding_idx=config.pad_token_id)
89
-
90
- # Positional encoding
91
- self.pos_encoder = PositionalEncoding(config.d_model, max_len=100)
92
-
93
- # Transformer encoder
94
- encoder_layer = nn.TransformerEncoderLayer(
95
- d_model=config.d_model,
96
- nhead=config.nhead,
97
- dim_feedforward=config.dim_feedforward,
98
- dropout=config.dropout,
99
- batch_first=True,
100
- )
101
- self.transformer_encoder = nn.TransformerEncoder(
102
- encoder_layer, num_layers=config.num_encoder_layers
103
- )
104
-
105
- # Output projection
106
- self.output_projection = nn.Linear(config.d_model, config.vocab_size)
107
-
108
- # Learned queries for each output position
109
- self.output_queries = nn.Parameter(torch.randn(config.max_output_len, config.d_model))
110
-
111
- # Cross-attention from output positions to encoded input
112
- self.cross_attention = nn.MultiheadAttention(
113
- config.d_model, config.nhead, dropout=config.dropout, batch_first=True
114
- )
115
-
116
- # Final output layers
117
- self.output_norm = nn.LayerNorm(config.d_model)
118
-
119
- self.post_init()
120
-
121
- def forward(
122
- self,
123
- input_ids: Optional[torch.Tensor] = None,
124
- attention_mask: Optional[torch.Tensor] = None,
125
- labels: Optional[torch.Tensor] = None,
126
- **kwargs,
127
- ) -> CausalLMOutputWithCrossAttentions:
128
- """Forward pass for HF compatibility.
129
-
130
- Args:
131
- input_ids: (batch_size, seq_len) tensor of digit indices (0-9), padding=10
132
- attention_mask: Optional mask for padding
133
- labels: Optional target labels for training
134
-
135
- Returns:
136
- CausalLMOutputWithCrossAttentions with logits
137
- """
138
- if input_ids is None:
139
- raise ValueError("input_ids must be provided")
140
-
141
- batch_size, seq_len = input_ids.shape
142
-
143
- # Handle padding: convert -1 padding to 10 (our padding index)
144
- digits = input_ids.clone()
145
- digits[digits == -1] = self.config.pad_token_id
146
-
147
- # Create padding mask for transformer (True = padding)
148
- if attention_mask is None:
149
- src_key_padding_mask = digits == self.config.pad_token_id
150
- else:
151
- src_key_padding_mask = ~attention_mask.bool()
152
-
153
- # Embed digits: (batch, seq_len, d_model)
154
- embedded = self.digit_embedding(digits)
155
-
156
- # Add positional encoding
157
- embedded = self.pos_encoder(embedded)
158
-
159
- # Transformer encoder: (batch, seq_len, d_model)
160
- memory = self.transformer_encoder(
161
- embedded, src_key_padding_mask=src_key_padding_mask
162
- )
163
-
164
- # Expand queries for batch: (batch, max_output_len, d_model)
165
- queries = self.output_queries.unsqueeze(0).expand(batch_size, -1, -1)
166
-
167
- # Cross-attention from queries to encoded input
168
- attn_output, _ = self.cross_attention(
169
- queries, memory, memory, key_padding_mask=src_key_padding_mask
170
- )
171
-
172
- # Normalize and project to vocab
173
- output = self.output_norm(attn_output)
174
- logits = self.output_projection(output)
175
-
176
- loss = None
177
- if labels is not None:
178
- loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
179
- loss = loss_fct(logits.view(-1, self.vocab_size), labels.view(-1))
180
-
181
- return CausalLMOutputWithCrossAttentions(
182
- loss=loss,
183
- logits=logits,
184
- hidden_states=None,
185
- attentions=None,
186
- cross_attentions=None,
187
- )
188
-
189
- def prepare_inputs_for_generation(self, input_ids, **kwargs):
190
- """Prepare inputs for text generation."""
191
- return {"input_ids": input_ids}
192
-
193
- def _reorder_cache(self, past_key_values, beam_idx):
194
- """Reorder cache for beam search."""
195
- return past_key_values
196
-
197
-
198
- class NamerPipeline:
199
- """Simple pipeline for Namer model inference.
200
-
201
- Usage:
202
- from transformers import AutoModel
203
-
204
- # Load model
205
- model = AutoModel.from_pretrained(
206
- "edwinhere/namer",
207
- trust_remote_code=True
208
- )
209
-
210
- # Create pipeline
211
- pipe = NamerPipeline(model)
212
-
213
- # Generate
214
- result = pipe.generate(42) # "forty two"
215
- result = pipe(42) # {"generated_text": "forty two"}
216
- """
217
-
218
- def __init__(self, model: NamerModel, tokenizer=None, device: str = None):
219
- if device is None:
220
- device = "cuda" if torch.cuda.is_available() else "cpu"
221
- self.model = model.to(device)
222
- self.model.eval()
223
- self.device = device
224
- self.tokenizer = tokenizer # Placeholder if we add a tokenizer later
225
-
226
- # Vocabulary mapping (index -> word)
227
- # Must match utils.py vocabulary exactly
228
- self.id2word = {
229
- 0: "zero", 1: "one", 2: "two", 3: "three", 4: "four",
230
- 5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine",
231
- 10: "ten", 11: "eleven", 12: "twelve", 13: "thirteen", 14: "fourteen",
232
- 15: "fifteen", 16: "sixteen", 17: "seventeen", 18: "eighteen", 19: "nineteen",
233
- 20: "twenty", 21: "thirty", 22: "forty", 23: "fifty",
234
- 24: "sixty", 25: "seventy", 26: "eighty", 27: "ninety",
235
- 28: "hundred",
236
- 29: "thousand", 30: "million", 31: "billion", 32: "trillion",
237
- 33: "quadrillion", 34: "quintillion", 35: "sextillion",
238
- 36: "septillion", 37: "octillion", 38: "nonillion", 39: "decillion",
239
- 40: "<EOS>"
240
- }
241
-
242
- # Reverse mapping
243
- self.word2id = {v: k for k, v in self.id2word.items()}
244
-
245
- def _int_to_digits(self, n: int) -> list[int]:
246
- """Convert integer to list of digit indices."""
247
- if n == 0:
248
- return [0]
249
- digits = []
250
- while n > 0:
251
- digits.append(n % 10)
252
- n //= 10
253
- return digits[::-1] # Reverse to get most significant digit first
254
-
255
- def _decode(self, token_ids: list[int]) -> str:
256
- """Decode token IDs to text, stopping at first EOS."""
257
- words = []
258
- eos_idx = self.model.config.eos_token_id # Should be 40
259
-
260
- for idx in token_ids:
261
- if idx == eos_idx: # Stop at EOS
262
- break
263
- if idx in self.id2word:
264
- word = self.id2word[idx]
265
- if word != "<EOS>": # Skip EOS token itself
266
- words.append(word)
267
-
268
- return " ".join(words) if words else "zero"
269
-
270
- def generate(self, text: Union[str, int], **kwargs) -> str:
271
- """Generate English name for a number.
272
-
273
- Args:
274
- text: Integer or string representation of integer
275
-
276
- Returns:
277
- English name of the number
278
- """
279
- # Parse input
280
- if isinstance(text, str):
281
- n = int(text.strip())
282
- else:
283
- n = int(text)
284
-
285
- # Convert to digits
286
- digits = self._int_to_digits(n)
287
-
288
- # Pad to max length (20)
289
- while len(digits) < 20:
290
- digits.append(10) # padding token
291
-
292
- # Create tensor
293
- input_ids = torch.tensor([digits], dtype=torch.long).to(self.device)
294
-
295
- # Forward pass
296
- with torch.no_grad():
297
- outputs = self.model(input_ids)
298
- logits = outputs.logits
299
- predictions = logits.argmax(dim=-1)[0].cpu().tolist()
300
-
301
- # Decode
302
- return self._decode(predictions)
303
-
304
- def __call__(self, text: Union[str, int], **kwargs) -> dict:
305
- """Callable interface for pipeline.
306
-
307
- Returns dict with 'generated_text' key for HF pipeline compatibility.
308
- """
309
- result = self.generate(text, **kwargs)
310
- return {"generated_text": result}
311
-
312
-
313
- def load_namer_pipeline(model_name_or_path: str = "edwinhere/namer", device: str = None, **kwargs):
314
- """Load a Namer pipeline with model.
315
-
316
- This is a convenience function that loads both the model and creates
317
- a pipeline for easy inference.
318
-
319
- Args:
320
- model_name_or_path: HuggingFace model ID or local path
321
- device: Device to run on ('cuda', 'cpu', or None for auto)
322
- **kwargs: Additional args passed to from_pretrained
323
-
324
- Returns:
325
- NamerPipeline instance ready for inference
326
-
327
- Example:
328
- >>> pipe = load_namer_pipeline("edwinhere/namer")
329
- >>> pipe.generate(42)
330
- 'forty two'
331
- >>> pipe(123)
332
- {'generated_text': 'one hundred twenty three'}
333
- """
334
- from transformers import AutoModel
335
-
336
- model = AutoModel.from_pretrained(
337
- model_name_or_path,
338
- trust_remote_code=True,
339
- **kwargs
340
- )
341
-
342
- return NamerPipeline(model, device=device)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pip-tmp/jiti/agents-agent-management.2587ecf7.mjs ADDED
@@ -0,0 +1,643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.handleCreate = handleCreate;exports.handleList = handleList;exports.handleManagementAction = handleManagementAction;exports.handleUpdate = handleUpdate;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+
4
+
5
+ var _agents = await jitiImport("./agents.ts");
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+ var _agentSerializer = await jitiImport("./agent-serializer.ts");
20
+ var _chainSerializer = await jitiImport("./chain-serializer.ts");
21
+ var _skills = await jitiImport("./skills.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+ function result(text, isError = false) {
37
+ return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
38
+ }
39
+
40
+ function parseCsv(value) {
41
+ return [...new Set(value.split(",").map((v) => v.trim()).filter(Boolean))];
42
+ }
43
+
44
+ function configObject(config) {
45
+ let val = config;
46
+ if (typeof val === "string") {
47
+ try {
48
+ val = JSON.parse(val);
49
+ } catch (error) {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ return { error: `config must be valid JSON: ${message}` };
52
+ }
53
+ }
54
+ if (!val || typeof val !== "object" || Array.isArray(val)) return {};
55
+ return { value: val };
56
+ }
57
+
58
+ function hasKey(obj, key) {
59
+ return Object.prototype.hasOwnProperty.call(obj, key);
60
+ }
61
+
62
+ function asDisambiguationScope(scope) {
63
+ if (scope === "user" || scope === "project") return scope;
64
+ return undefined;
65
+ }
66
+
67
+ function normalizeListScope(scope) {
68
+ if (scope === undefined) return "both";
69
+ if (scope === "user" || scope === "project" || scope === "both") return scope;
70
+ return undefined;
71
+ }
72
+
73
+ function sanitizeName(name) {
74
+ return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
75
+ }
76
+
77
+ function parsePackageConfig(value) {
78
+ return (0, _agents.parsePackageName)(value, "config.package");
79
+ }
80
+
81
+ function allAgents(d) {
82
+ return [...d.builtin, ...d.user, ...d.project];
83
+ }
84
+
85
+ function availableNames(cwd, kind) {
86
+ const d = (0, _agents.discoverAgentsAll)(cwd);
87
+ const items = kind === "agent" ? allAgents(d) : d.chains;
88
+ return [...new Set(items.map((x) => x.name))].sort((a, b) => a.localeCompare(b));
89
+ }
90
+
91
+ function findAgents(name, cwd, scope = "both") {
92
+ const d = (0, _agents.discoverAgentsAll)(cwd);
93
+ const raw = name.trim();
94
+ const sanitized = sanitizeName(raw);
95
+ return allAgents(d).
96
+ filter((a) => (scope === "both" || a.source === scope) && (a.name === raw || a.name === sanitized)).
97
+ sort((a, b) => a.source.localeCompare(b.source));
98
+ }
99
+
100
+ function findChains(name, cwd, scope = "both") {
101
+ const raw = name.trim();
102
+ const sanitized = sanitizeName(raw);
103
+ return (0, _agents.discoverAgentsAll)(cwd).chains.
104
+ filter((c) => (scope === "both" || c.source === scope) && (c.name === raw || c.name === sanitized)).
105
+ sort((a, b) => a.source.localeCompare(b.source));
106
+ }
107
+
108
+ function nameExistsInScope(cwd, scope, name, excludePath) {
109
+ const d = (0, _agents.discoverAgentsAll)(cwd);
110
+ for (const a of scope === "user" ? d.user : d.project) {
111
+ if (a.name === name && a.filePath !== excludePath) return true;
112
+ }
113
+ for (const c of d.chains) {
114
+ if (c.source === scope && c.name === name && c.filePath !== excludePath) return true;
115
+ }
116
+ return false;
117
+ }
118
+
119
+ function unknownChainAgents(cwd, steps) {
120
+ const d = (0, _agents.discoverAgentsAll)(cwd);
121
+ const known = new Set(allAgents(d).map((a) => a.name));
122
+ return [...new Set(steps.map((s) => s.agent).filter((a) => !known.has(a)))].sort((a, b) => a.localeCompare(b));
123
+ }
124
+
125
+ function chainStepWarnings(ctx, steps) {
126
+ const warnings = [];
127
+ const available = new Set((0, _skills.discoverAvailableSkills)(ctx.cwd).map((s) => s.name));
128
+ for (let i = 0; i < steps.length; i++) {
129
+ const s = steps[i];
130
+ if (s.model) {
131
+ const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === s.model || m.id === s.model);
132
+ if (!found) warnings.push(`Warning: step ${i + 1} (${s.agent}): model '${s.model}' is not in the current model registry.`);
133
+ }
134
+ if (Array.isArray(s.skills) && s.skills.length > 0) {
135
+ const missing = s.skills.filter((sk) => !available.has(sk));
136
+ if (missing.length) warnings.push(`Warning: step ${i + 1} (${s.agent}): skills not found: ${missing.join(", ")}.`);
137
+ }
138
+ }
139
+ return warnings;
140
+ }
141
+
142
+ function modelWarning(ctx, model) {
143
+ if (!model) return undefined;
144
+ const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === model || m.id === model);
145
+ return found ? undefined : `Warning: model '${model}' is not in the current model registry.`;
146
+ }
147
+
148
+ function fallbackModelsWarning(ctx, fallbackModels) {
149
+ if (!fallbackModels || fallbackModels.length === 0) return undefined;
150
+ const available = new Set(ctx.modelRegistry.getAvailable().flatMap((m) => [`${m.provider}/${m.id}`, m.id]));
151
+ const missing = fallbackModels.filter((model) => !available.has(model));
152
+ return missing.length ? `Warning: fallback models not in the current model registry: ${missing.join(", ")}.` : undefined;
153
+ }
154
+
155
+ function skillsWarning(cwd, skills) {
156
+ if (!skills || skills.length === 0) return undefined;
157
+ const available = new Set((0, _skills.discoverAvailableSkills)(cwd).map((s) => s.name));
158
+ const missing = skills.filter((s) => !available.has(s));
159
+ return missing.length ? `Warning: skills not found: ${missing.join(", ")}.` : undefined;
160
+ }
161
+
162
+ function parseStepList(raw) {
163
+ if (!Array.isArray(raw)) return { error: "config.steps must be an array." };
164
+ if (raw.length === 0) return { error: "config.steps must include at least one step." };
165
+ const steps = [];
166
+ for (let i = 0; i < raw.length; i++) {
167
+ const item = raw[i];
168
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { error: `config.steps[${i}] must be an object.` };
169
+ const s = item;
170
+ if (typeof s.agent !== "string" || !s.agent.trim()) return { error: `config.steps[${i}].agent must be a non-empty string.` };
171
+ const step = { agent: s.agent.trim(), task: typeof s.task === "string" ? s.task : "" };
172
+ if (hasKey(s, "output")) {
173
+ if (s.output === false) step.output = false;else
174
+ if (typeof s.output === "string") step.output = s.output;else
175
+ return { error: `config.steps[${i}].output must be a string or false.` };
176
+ }
177
+ if (hasKey(s, "outputMode")) {
178
+ if (s.outputMode === "inline" || s.outputMode === "file-only") step.outputMode = s.outputMode;else
179
+ return { error: `config.steps[${i}].outputMode must be 'inline' or 'file-only'.` };
180
+ }
181
+ if (hasKey(s, "reads")) {
182
+ if (s.reads === false) step.reads = false;else
183
+ if (Array.isArray(s.reads)) step.reads = s.reads.filter((v) => typeof v === "string").map((v) => v.trim()).filter(Boolean);else
184
+ return { error: `config.steps[${i}].reads must be an array or false.` };
185
+ }
186
+ if (hasKey(s, "model")) {
187
+ if (typeof s.model === "string") step.model = s.model;else
188
+ return { error: `config.steps[${i}].model must be a string.` };
189
+ }
190
+ if (hasKey(s, "skills")) {
191
+ if (s.skills === false) step.skills = false;else
192
+ if (Array.isArray(s.skills)) step.skills = s.skills.filter((v) => typeof v === "string").map((v) => v.trim()).filter(Boolean);else
193
+ return { error: `config.steps[${i}].skills must be an array or false.` };
194
+ }
195
+ if (hasKey(s, "progress")) {
196
+ if (typeof s.progress === "boolean") step.progress = s.progress;else
197
+ return { error: `config.steps[${i}].progress must be a boolean.` };
198
+ }
199
+ steps.push(step);
200
+ }
201
+ return { steps };
202
+ }
203
+
204
+ function parseTools(raw) {
205
+ const tools = [];
206
+ const mcpDirectTools = [];
207
+ for (const item of parseCsv(raw)) {
208
+ if (item.startsWith("mcp:")) {
209
+ const direct = item.slice(4).trim();
210
+ if (direct) mcpDirectTools.push(direct);
211
+ } else tools.push(item);
212
+ }
213
+ return { tools: tools.length ? tools : undefined, mcpDirectTools: mcpDirectTools.length ? mcpDirectTools : undefined };
214
+ }
215
+
216
+ function applyAgentConfig(target, cfg) {
217
+ if (hasKey(cfg, "systemPrompt")) {
218
+ if (cfg.systemPrompt === false || cfg.systemPrompt === "") target.systemPrompt = "";else
219
+ if (typeof cfg.systemPrompt === "string") target.systemPrompt = cfg.systemPrompt;else
220
+ return "config.systemPrompt must be a string or false when provided.";
221
+ }
222
+ if (hasKey(cfg, "model")) {
223
+ if (cfg.model === false || cfg.model === "") target.model = undefined;else
224
+ if (typeof cfg.model === "string") target.model = cfg.model.trim() || undefined;else
225
+ return "config.model must be a string or false when provided.";
226
+ }
227
+ if (hasKey(cfg, "fallbackModels")) {
228
+ if (cfg.fallbackModels === false || cfg.fallbackModels === "") target.fallbackModels = undefined;else
229
+ if (typeof cfg.fallbackModels === "string") {
230
+ const models = parseCsv(cfg.fallbackModels);
231
+ target.fallbackModels = models.length ? models : undefined;
232
+ } else if (Array.isArray(cfg.fallbackModels)) {
233
+ const models = cfg.fallbackModels.
234
+ filter((value) => typeof value === "string").
235
+ map((value) => value.trim()).
236
+ filter(Boolean);
237
+ target.fallbackModels = models.length ? [...new Set(models)] : undefined;
238
+ } else return "config.fallbackModels must be a comma-separated string, string array, or false when provided.";
239
+ }
240
+ if (hasKey(cfg, "tools")) {
241
+ if (cfg.tools === false || cfg.tools === "") {target.tools = undefined;target.mcpDirectTools = undefined;} else
242
+ if (typeof cfg.tools === "string") {const parsed = parseTools(cfg.tools);target.tools = parsed.tools;target.mcpDirectTools = parsed.mcpDirectTools;} else
243
+ return "config.tools must be a comma-separated string or false when provided.";
244
+ }
245
+ if (hasKey(cfg, "skills")) {
246
+ if (cfg.skills === false || cfg.skills === "") target.skills = undefined;else
247
+ if (typeof cfg.skills === "string") {const skills = parseCsv(cfg.skills);target.skills = skills.length ? skills : undefined;} else
248
+ return "config.skills must be a comma-separated string or false when provided.";
249
+ }
250
+ if (hasKey(cfg, "extensions")) {
251
+ if (cfg.extensions === false) target.extensions = undefined;else
252
+ if (cfg.extensions === "") target.extensions = [];else
253
+ if (typeof cfg.extensions === "string") target.extensions = parseCsv(cfg.extensions);else
254
+ return "config.extensions must be a comma-separated string, empty string, or false when provided.";
255
+ }
256
+ if (hasKey(cfg, "thinking")) {
257
+ if (cfg.thinking === false || cfg.thinking === "") target.thinking = undefined;else
258
+ if (typeof cfg.thinking === "string") target.thinking = cfg.thinking.trim() || undefined;else
259
+ return "config.thinking must be a string or false when provided.";
260
+ }
261
+ if (hasKey(cfg, "systemPromptMode")) {
262
+ if (cfg.systemPromptMode === "append" || cfg.systemPromptMode === "replace") target.systemPromptMode = cfg.systemPromptMode;else
263
+ return "config.systemPromptMode must be 'append' or 'replace' when provided.";
264
+ }
265
+ if (hasKey(cfg, "inheritProjectContext")) {
266
+ if (typeof cfg.inheritProjectContext !== "boolean") return "config.inheritProjectContext must be a boolean when provided.";
267
+ target.inheritProjectContext = cfg.inheritProjectContext;
268
+ }
269
+ if (hasKey(cfg, "inheritSkills")) {
270
+ if (typeof cfg.inheritSkills !== "boolean") return "config.inheritSkills must be a boolean when provided.";
271
+ target.inheritSkills = cfg.inheritSkills;
272
+ }
273
+ if (hasKey(cfg, "defaultContext")) {
274
+ if (cfg.defaultContext === false || cfg.defaultContext === "") target.defaultContext = undefined;else
275
+ if (cfg.defaultContext === "fresh" || cfg.defaultContext === "fork") target.defaultContext = cfg.defaultContext;else
276
+ return "config.defaultContext must be 'fresh', 'fork', or false when provided.";
277
+ }
278
+ if (hasKey(cfg, "output")) {
279
+ if (cfg.output === false || cfg.output === "") target.output = undefined;else
280
+ if (typeof cfg.output === "string") target.output = cfg.output;else
281
+ return "config.output must be a string or false when provided.";
282
+ }
283
+ if (hasKey(cfg, "reads")) {
284
+ if (cfg.reads === false || cfg.reads === "") target.defaultReads = undefined;else
285
+ if (typeof cfg.reads === "string") {
286
+ const reads = parseCsv(cfg.reads);
287
+ target.defaultReads = reads.length ? reads : undefined;
288
+ } else return "config.reads must be a comma-separated string or false when provided.";
289
+ }
290
+ if (hasKey(cfg, "progress")) {
291
+ if (typeof cfg.progress !== "boolean") return "config.progress must be a boolean when provided.";
292
+ target.defaultProgress = cfg.progress;
293
+ }
294
+ if (hasKey(cfg, "maxSubagentDepth")) {
295
+ if (cfg.maxSubagentDepth === false || cfg.maxSubagentDepth === "") target.maxSubagentDepth = undefined;else
296
+ if (typeof cfg.maxSubagentDepth === "number" && Number.isInteger(cfg.maxSubagentDepth) && cfg.maxSubagentDepth >= 0) {
297
+ target.maxSubagentDepth = cfg.maxSubagentDepth;
298
+ } else return "config.maxSubagentDepth must be an integer >= 0 or false when provided.";
299
+ }
300
+ return undefined;
301
+ }
302
+
303
+ function resolveTarget(
304
+ kind,
305
+ name,
306
+ matches,
307
+ cwd,
308
+ scopeHint)
309
+ {
310
+ const mutable = matches.filter((m) => m.source !== "builtin");
311
+ if (mutable.length === 0) {
312
+ if (matches.length > 0) {
313
+ return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is builtin and cannot be modified. Create a same-named ${kind} in user or project scope to override it.`, true);
314
+ }
315
+ const available = availableNames(cwd, kind);
316
+ return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found. Available: ${available.join(", ") || "none"}.`, true);
317
+ }
318
+ if (mutable.length === 1) return mutable[0];
319
+ const scope = asDisambiguationScope(scopeHint);
320
+ if (!scope) {
321
+ const paths = mutable.map((m) => `${m.source}: ${m.filePath}`).join("\n");
322
+ return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' exists in both scopes. Specify agentScope: 'user' or 'project'.\n${paths}`, true);
323
+ }
324
+ const scoped = mutable.filter((m) => m.source === scope);
325
+ if (scoped.length === 0) return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found in scope '${scope}'.`, true);
326
+ if (scoped.length > 1) return result(`Multiple ${kind}s named '${name}' found in scope '${scope}': ${scoped.map((m) => m.filePath).join(", ")}`, true);
327
+ return scoped[0];
328
+ }
329
+
330
+ function renamePath(
331
+ kind,
332
+ currentPath,
333
+ newName,
334
+ scope,
335
+ cwd)
336
+ {
337
+ if (nameExistsInScope(cwd, scope, newName, currentPath)) return { error: `Name '${newName}' already exists in ${scope} scope.` };
338
+ const ext = kind === "agent" ? ".md" : ".chain.md";
339
+ const filePath = path.join(path.dirname(currentPath), `${newName}${ext}`);
340
+ if (fs.existsSync(filePath) && filePath !== currentPath) {
341
+ return { error: `File already exists at ${filePath} but is not a valid ${kind} definition. Remove or rename it first.` };
342
+ }
343
+ fs.renameSync(currentPath, filePath);
344
+ return { filePath };
345
+ }
346
+
347
+ function formatAgentDetail(agent) {
348
+ const tools = [...(agent.tools ?? []), ...(agent.mcpDirectTools ?? []).map((t) => `mcp:${t}`)];
349
+ const lines = [`Agent: ${agent.name} (${agent.source})`, `Path: ${agent.filePath}`, `Description: ${agent.description}`];
350
+ if (agent.packageName) {
351
+ lines.push(`Local name: ${(0, _agents.frontmatterNameForConfig)(agent)}`);
352
+ lines.push(`Package: ${agent.packageName}`);
353
+ }
354
+ if (agent.model) lines.push(`Model: ${agent.model}`);
355
+ if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
356
+ if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
357
+ if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
358
+ lines.push(`System prompt mode: ${agent.systemPromptMode}`);
359
+ lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
360
+ lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
361
+ if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
362
+ if (agent.source === "builtin") lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
363
+ if (agent.extensions !== undefined) lines.push(`Extensions: ${agent.extensions.length ? agent.extensions.join(", ") : "(none)"}`);
364
+ if (agent.thinking) lines.push(`Thinking: ${agent.thinking}`);
365
+ if (agent.output) lines.push(`Output: ${agent.output}`);
366
+ if (agent.defaultReads?.length) lines.push(`Reads: ${agent.defaultReads.join(", ")}`);
367
+ if (agent.defaultProgress) lines.push("Progress: true");
368
+ if (agent.maxSubagentDepth !== undefined) lines.push(`Max subagent depth: ${agent.maxSubagentDepth}`);
369
+ if (agent.systemPrompt.trim()) lines.push("", "System Prompt:", agent.systemPrompt);
370
+ return lines.join("\n");
371
+ }
372
+
373
+ function formatChainDetail(chain) {
374
+ const lines = [`Chain: ${chain.name} (${chain.source})`, `Path: ${chain.filePath}`, `Description: ${chain.description}`];
375
+ if (chain.packageName) {
376
+ lines.push(`Local name: ${(0, _agents.frontmatterNameForConfig)(chain)}`);
377
+ lines.push(`Package: ${chain.packageName}`);
378
+ }
379
+ lines.push("", "Steps:");
380
+ for (let i = 0; i < chain.steps.length; i++) {
381
+ const s = chain.steps[i];
382
+ lines.push(`${i + 1}. ${s.agent}`);
383
+ if (s.task.trim()) lines.push(` Task: ${s.task}`);
384
+ if (s.output === false) lines.push(" Output: false");else
385
+ if (s.output) lines.push(` Output: ${s.output}`);
386
+ if (s.outputMode) lines.push(` Output mode: ${s.outputMode}`);
387
+ if (s.reads === false) lines.push(" Reads: false");else
388
+ if (Array.isArray(s.reads) && s.reads.length > 0) lines.push(` Reads: ${s.reads.join(", ")}`);
389
+ if (s.model) lines.push(` Model: ${s.model}`);
390
+ if (s.skills === false) lines.push(" Skills: false");else
391
+ if (Array.isArray(s.skills) && s.skills.length > 0) lines.push(` Skills: ${s.skills.join(", ")}`);
392
+ if (s.progress !== undefined) lines.push(` Progress: ${s.progress ? "true" : "false"}`);
393
+ }
394
+ return lines.join("\n");
395
+ }
396
+
397
+ function handleList(params, ctx) {
398
+ const scope = normalizeListScope(params.agentScope) ?? "both";
399
+ const d = (0, _agents.discoverAgentsAll)(ctx.cwd);
400
+ const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
401
+ const agents = scopedAgents.filter((a) => !a.disabled);
402
+ const chains = d.chains.filter((c) => scope === "both" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
403
+ const lines = [
404
+ "Executable agents:",
405
+ ...(agents.length ?
406
+ agents.map((a) => `- ${a.name} (${a.source}${a.defaultContext ? `, context: ${a.defaultContext}` : ""}): ${a.description}`) :
407
+ ["- (none)"]),
408
+ "",
409
+ "Chains:",
410
+ ...(chains.length ? chains.map((c) => `- ${c.name} (${c.source}): ${c.description}`) : ["- (none)"])];
411
+
412
+ return result(lines.join("\n"));
413
+ }
414
+
415
+ function handleGet(params, ctx) {
416
+ if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for get.", true);
417
+ const hasBoth = Boolean(params.agent && params.chainName);
418
+ const blocks = [];
419
+ let anyFound = false;
420
+ if (params.agent) {
421
+ const matches = findAgents(params.agent, ctx.cwd, "both");
422
+ if (!matches.length) {
423
+ const msg = `Agent '${params.agent}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`;
424
+ if (!hasBoth) return result(msg, true);
425
+ blocks.push(msg);
426
+ } else {
427
+ anyFound = true;
428
+ blocks.push(...matches.map(formatAgentDetail));
429
+ }
430
+ }
431
+ if (params.chainName) {
432
+ const matches = findChains(params.chainName, ctx.cwd, "both");
433
+ if (!matches.length) {
434
+ const msg = `Chain '${params.chainName}' not found. Available: ${availableNames(ctx.cwd, "chain").join(", ") || "none"}.`;
435
+ if (!hasBoth) return result(msg, true);
436
+ blocks.push(msg);
437
+ } else {
438
+ anyFound = true;
439
+ blocks.push(...matches.map(formatChainDetail));
440
+ }
441
+ }
442
+ return result(blocks.join("\n\n"), !anyFound);
443
+ }
444
+
445
+ function handleCreate(params, ctx) {
446
+ const parsedConfig = configObject(params.config);
447
+ if (parsedConfig.error) return result(parsedConfig.error, true);
448
+ const cfg = parsedConfig.value;
449
+ if (!cfg) return result("config required for create.", true);
450
+ if (typeof cfg.name !== "string" || !cfg.name.trim()) return result("config.name is required and must be a non-empty string.", true);
451
+ if (typeof cfg.description !== "string" || !cfg.description.trim()) return result("config.description is required and must be a non-empty string.", true);
452
+ const name = sanitizeName(cfg.name);
453
+ if (!name) return result("config.name is invalid after sanitization. Use letters, numbers, spaces, or hyphens.", true);
454
+ const parsedPackage = parsePackageConfig(cfg.package);
455
+ if (parsedPackage.error) return result(parsedPackage.error, true);
456
+ const runtimeName = (0, _agents.buildRuntimeName)(name, parsedPackage.packageName);
457
+ const scopeRaw = cfg.scope ?? "user";
458
+ if (scopeRaw !== "user" && scopeRaw !== "project") return result("config.scope must be 'user' or 'project'.", true);
459
+ const scope = scopeRaw;
460
+ const isChain = hasKey(cfg, "steps");
461
+ const d = (0, _agents.discoverAgentsAll)(ctx.cwd);
462
+ const targetDir = isChain ?
463
+ scope === "user" ? d.userChainDir : d.projectChainDir ?? path.join(ctx.cwd, ".pi", "chains") :
464
+ scope === "user" ? d.userDir : d.projectDir ?? path.join(ctx.cwd, ".pi", "agents");
465
+ fs.mkdirSync(targetDir, { recursive: true });
466
+ if (nameExistsInScope(ctx.cwd, scope, runtimeName)) return result(`Name '${runtimeName}' already exists in ${scope} scope. Use update instead.`, true);
467
+ const targetPath = path.join(targetDir, isChain ? `${runtimeName}.chain.md` : `${runtimeName}.md`);
468
+ if (fs.existsSync(targetPath)) return result(`File already exists at ${targetPath} but is not a valid ${isChain ? "chain" : "agent"} definition. Remove or rename it first.`, true);
469
+ const warnings = [];
470
+ if (!isChain && d.builtin.some((a) => a.name === runtimeName)) warnings.push(`Note: this shadows the builtin agent '${runtimeName}'.`);
471
+ if (isChain) {
472
+ const parsed = parseStepList(cfg.steps);
473
+ if (parsed.error) return result(parsed.error, true);
474
+ const chain = { name: runtimeName, localName: name, packageName: parsedPackage.packageName, description: cfg.description.trim(), source: scope, filePath: targetPath, steps: parsed.steps };
475
+ fs.writeFileSync(targetPath, (0, _chainSerializer.serializeChain)(chain), "utf-8");
476
+ const missing = unknownChainAgents(ctx.cwd, chain.steps);
477
+ if (missing.length) warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
478
+ warnings.push(...chainStepWarnings(ctx, chain.steps));
479
+ return result([`Created chain '${runtimeName}' at ${targetPath}.`, ...warnings].join("\n"));
480
+ }
481
+ const agent = {
482
+ name: runtimeName,
483
+ localName: name,
484
+ packageName: parsedPackage.packageName,
485
+ description: cfg.description.trim(),
486
+ source: scope,
487
+ filePath: targetPath,
488
+ systemPrompt: "",
489
+ systemPromptMode: (0, _agents.defaultSystemPromptMode)(name),
490
+ inheritProjectContext: (0, _agents.defaultInheritProjectContext)(name),
491
+ inheritSkills: (0, _agents.defaultInheritSkills)()
492
+ };
493
+ const applyError = applyAgentConfig(agent, cfg);
494
+ if (applyError) return result(applyError, true);
495
+ const mw = modelWarning(ctx, agent.model);
496
+ if (mw) warnings.push(mw);
497
+ const fmw = fallbackModelsWarning(ctx, agent.fallbackModels);
498
+ if (fmw) warnings.push(fmw);
499
+ const sw = skillsWarning(ctx.cwd, agent.skills);
500
+ if (sw) warnings.push(sw);
501
+ fs.writeFileSync(targetPath, (0, _agentSerializer.serializeAgent)(agent), "utf-8");
502
+ return result([`Created agent '${runtimeName}' at ${targetPath}.`, ...warnings].join("\n"));
503
+ }
504
+
505
+ function handleUpdate(params, ctx) {
506
+ if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for update.", true);
507
+ if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
508
+ const parsedConfig = configObject(params.config);
509
+ if (parsedConfig.error) return result(parsedConfig.error, true);
510
+ const cfg = parsedConfig.value;
511
+ if (!cfg) return result("config required for update.", true);
512
+ const warnings = [];
513
+ if (params.agent) {
514
+ const scopeHint = asDisambiguationScope(params.agentScope);
515
+ const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
516
+ if ("content" in targetOrError) return targetOrError;
517
+ const target = targetOrError;
518
+ const updated = { ...target };
519
+ const oldName = target.name;
520
+ if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
521
+ if (hasKey(cfg, "description") && (typeof cfg.description !== "string" || !cfg.description.trim())) return result("config.description must be a non-empty string when provided.", true);
522
+ let newLocalName = target.localName ?? (0, _agents.frontmatterNameForConfig)(target);
523
+ if (hasKey(cfg, "name")) {
524
+ newLocalName = sanitizeName(cfg.name);
525
+ if (!newLocalName) return result("config.name is invalid after sanitization.", true);
526
+ }
527
+ let newPackageName = target.packageName;
528
+ if (hasKey(cfg, "package")) {
529
+ const parsedPackage = parsePackageConfig(cfg.package);
530
+ if (parsedPackage.error) return result(parsedPackage.error, true);
531
+ newPackageName = parsedPackage.packageName;
532
+ }
533
+ const applyError = applyAgentConfig(updated, cfg);
534
+ if (applyError) return result(applyError, true);
535
+ updated.localName = newLocalName;
536
+ updated.packageName = newPackageName;
537
+ updated.name = (0, _agents.buildRuntimeName)(newLocalName, newPackageName);
538
+ if (hasKey(cfg, "description")) updated.description = cfg.description.trim();
539
+ if (hasKey(cfg, "model")) {
540
+ const mw = modelWarning(ctx, updated.model);
541
+ if (mw) warnings.push(mw);
542
+ }
543
+ if (hasKey(cfg, "fallbackModels")) {
544
+ const fmw = fallbackModelsWarning(ctx, updated.fallbackModels);
545
+ if (fmw) warnings.push(fmw);
546
+ }
547
+ if (hasKey(cfg, "skills")) {
548
+ const sw = skillsWarning(ctx.cwd, updated.skills);
549
+ if (sw) warnings.push(sw);
550
+ }
551
+ if (updated.name !== oldName) {
552
+ const renamed = renamePath("agent", target.filePath, updated.name, target.source, ctx.cwd);
553
+ if (renamed.error) return result(renamed.error, true);
554
+ updated.filePath = renamed.filePath;
555
+ }
556
+ fs.writeFileSync(updated.filePath, (0, _agentSerializer.serializeAgent)(updated), "utf-8");
557
+ if (updated.name !== oldName) {
558
+ const refs = (0, _agents.discoverAgentsAll)(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === oldName)).map((c) => `${c.name} (${c.source})`);
559
+ if (refs.length) warnings.push(`Warning: chains still reference '${oldName}': ${refs.join(", ")}.`);
560
+ }
561
+ const headline = updated.name === oldName ?
562
+ `Updated agent '${updated.name}' at ${updated.filePath}.` :
563
+ `Updated agent '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
564
+ return result([headline, ...warnings].join("\n"));
565
+ }
566
+ const scopeHint = asDisambiguationScope(params.agentScope);
567
+ const targetOrError = resolveTarget("chain", params.chainName, findChains(params.chainName, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
568
+ if ("content" in targetOrError) return targetOrError;
569
+ const target = targetOrError;
570
+ const updated = { ...target, steps: [...target.steps] };
571
+ const oldName = target.name;
572
+ if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
573
+ if (hasKey(cfg, "description") && (typeof cfg.description !== "string" || !cfg.description.trim())) return result("config.description must be a non-empty string when provided.", true);
574
+ let newLocalName = target.localName ?? (0, _agents.frontmatterNameForConfig)(target);
575
+ if (hasKey(cfg, "name")) {
576
+ newLocalName = sanitizeName(cfg.name);
577
+ if (!newLocalName) return result("config.name is invalid after sanitization.", true);
578
+ }
579
+ let newPackageName = target.packageName;
580
+ if (hasKey(cfg, "package")) {
581
+ const parsedPackage = parsePackageConfig(cfg.package);
582
+ if (parsedPackage.error) return result(parsedPackage.error, true);
583
+ newPackageName = parsedPackage.packageName;
584
+ }
585
+ let parsedSteps;
586
+ if (hasKey(cfg, "steps")) {
587
+ const parsed = parseStepList(cfg.steps);
588
+ if (parsed.error) return result(parsed.error, true);
589
+ parsedSteps = parsed.steps;
590
+ }
591
+ updated.localName = newLocalName;
592
+ updated.packageName = newPackageName;
593
+ updated.name = (0, _agents.buildRuntimeName)(newLocalName, newPackageName);
594
+ if (hasKey(cfg, "description")) updated.description = cfg.description.trim();
595
+ if (parsedSteps) {
596
+ updated.steps = parsedSteps;
597
+ const missing = unknownChainAgents(ctx.cwd, updated.steps);
598
+ if (missing.length) warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
599
+ warnings.push(...chainStepWarnings(ctx, updated.steps));
600
+ }
601
+ if (updated.name !== oldName) {
602
+ const renamed = renamePath("chain", target.filePath, updated.name, target.source, ctx.cwd);
603
+ if (renamed.error) return result(renamed.error, true);
604
+ updated.filePath = renamed.filePath;
605
+ }
606
+ fs.writeFileSync(updated.filePath, (0, _chainSerializer.serializeChain)(updated), "utf-8");
607
+ const headline = updated.name === oldName ?
608
+ `Updated chain '${updated.name}' at ${updated.filePath}.` :
609
+ `Updated chain '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
610
+ return result([headline, ...warnings].join("\n"));
611
+ }
612
+
613
+ function handleDelete(params, ctx) {
614
+ if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for delete.", true);
615
+ if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
616
+ const scopeHint = asDisambiguationScope(params.agentScope);
617
+ if (params.agent) {
618
+ const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
619
+ if ("content" in targetOrError) return targetOrError;
620
+ const target = targetOrError;
621
+ fs.unlinkSync(target.filePath);
622
+ const refs = (0, _agents.discoverAgentsAll)(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === target.name)).map((c) => `${c.name} (${c.source})`);
623
+ const lines = [`Deleted agent '${target.name}' at ${target.filePath}.`];
624
+ if (refs.length) lines.push(`Warning: chains reference deleted agent '${target.name}': ${refs.join(", ")}.`);
625
+ return result(lines.join("\n"));
626
+ }
627
+ const targetOrError = resolveTarget("chain", params.chainName, findChains(params.chainName, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
628
+ if ("content" in targetOrError) return targetOrError;
629
+ const target = targetOrError;
630
+ fs.unlinkSync(target.filePath);
631
+ return result(`Deleted chain '${target.name}' at ${target.filePath}.`);
632
+ }
633
+
634
+ function handleManagementAction(action, params, ctx) {
635
+ switch (action) {
636
+ case "list":return handleList(params, ctx);
637
+ case "get":return handleGet(params, ctx);
638
+ case "create":return handleCreate(params, ctx);
639
+ case "update":return handleUpdate(params, ctx);
640
+ case "delete":return handleDelete(params, ctx);
641
+ default:return result(`Unknown action: ${action}`, true);
642
+ }
643
+ } /* v9-f1aaa04295c5b5e7 */
pip-tmp/jiti/agents-agent-scope.515c0bc4.mjs ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.resolveExecutionAgentScope = resolveExecutionAgentScope;
2
+
3
+ function resolveExecutionAgentScope(scope) {
4
+ if (scope === "user" || scope === "project" || scope === "both") return scope;
5
+ return "both";
6
+ } /* v9-28865fb4666b353e */
pip-tmp/jiti/agents-agent-selection.a867544e.mjs ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.mergeAgentsForScope = mergeAgentsForScope;
2
+
3
+ function mergeAgentsForScope(
4
+ scope,
5
+ userAgents,
6
+ projectAgents,
7
+ builtinAgents = [])
8
+ {
9
+ const agentMap = new Map();
10
+
11
+ for (const agent of builtinAgents) agentMap.set(agent.name, agent);
12
+
13
+ if (scope === "both") {
14
+ for (const agent of userAgents) agentMap.set(agent.name, agent);
15
+ for (const agent of projectAgents) agentMap.set(agent.name, agent);
16
+ } else if (scope === "user") {
17
+ for (const agent of userAgents) agentMap.set(agent.name, agent);
18
+ } else {
19
+ for (const agent of projectAgents) agentMap.set(agent.name, agent);
20
+ }
21
+
22
+ return Array.from(agentMap.values());
23
+ } /* v9-2c3ea403e7844ffa */
pip-tmp/jiti/agents-agent-serializer.61dec55f.mjs ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.KNOWN_FIELDS = void 0;exports.serializeAgent = serializeAgent;
2
+ var _identity = await jitiImport("./identity.ts");
3
+
4
+ const KNOWN_FIELDS = exports.KNOWN_FIELDS = new Set([
5
+ "name",
6
+ "package",
7
+ "description",
8
+ "tools",
9
+ "model",
10
+ "fallbackModels",
11
+ "thinking",
12
+ "systemPromptMode",
13
+ "inheritProjectContext",
14
+ "inheritSkills",
15
+ "defaultContext",
16
+ "skill",
17
+ "skills",
18
+ "extensions",
19
+ "output",
20
+ "defaultReads",
21
+ "defaultProgress",
22
+ "interactive",
23
+ "maxSubagentDepth"]
24
+ );
25
+
26
+ function joinComma(values) {
27
+ if (!values || values.length === 0) return undefined;
28
+ return values.join(", ");
29
+ }
30
+
31
+ function serializeAgent(config) {
32
+ const lines = [];
33
+ lines.push("---");
34
+ lines.push(`name: ${(0, _identity.frontmatterNameForConfig)(config)}`);
35
+ if (config.packageName) lines.push(`package: ${config.packageName}`);
36
+ lines.push(`description: ${config.description}`);
37
+
38
+ const tools = [
39
+ ...(config.tools ?? []),
40
+ ...(config.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`)];
41
+
42
+ const toolsValue = joinComma(tools);
43
+ if (toolsValue) lines.push(`tools: ${toolsValue}`);
44
+
45
+ if (config.model) lines.push(`model: ${config.model}`);
46
+ const fallbackModelsValue = joinComma(config.fallbackModels);
47
+ if (fallbackModelsValue) lines.push(`fallbackModels: ${fallbackModelsValue}`);
48
+ if (config.thinking && config.thinking !== "off") lines.push(`thinking: ${config.thinking}`);
49
+ lines.push(`systemPromptMode: ${config.systemPromptMode}`);
50
+ lines.push(`inheritProjectContext: ${config.inheritProjectContext ? "true" : "false"}`);
51
+ lines.push(`inheritSkills: ${config.inheritSkills ? "true" : "false"}`);
52
+ if (config.defaultContext) lines.push(`defaultContext: ${config.defaultContext}`);
53
+
54
+ const skillsValue = joinComma(config.skills);
55
+ if (skillsValue) lines.push(`skills: ${skillsValue}`);
56
+
57
+ if (config.extensions !== undefined) {
58
+ const extensionsValue = joinComma(config.extensions);
59
+ lines.push(`extensions: ${extensionsValue ?? ""}`);
60
+ }
61
+
62
+ if (config.output) lines.push(`output: ${config.output}`);
63
+
64
+ const readsValue = joinComma(config.defaultReads);
65
+ if (readsValue) lines.push(`defaultReads: ${readsValue}`);
66
+
67
+ if (config.defaultProgress) lines.push("defaultProgress: true");
68
+ if (config.interactive) lines.push("interactive: true");
69
+ if (Number.isInteger(config.maxSubagentDepth) && config.maxSubagentDepth >= 0) {
70
+ lines.push(`maxSubagentDepth: ${config.maxSubagentDepth}`);
71
+ }
72
+
73
+ if (config.extraFields) {
74
+ for (const [key, value] of Object.entries(config.extraFields)) {
75
+ if (KNOWN_FIELDS.has(key)) continue;
76
+ lines.push(`${key}: ${value}`);
77
+ }
78
+ }
79
+
80
+ lines.push("---");
81
+
82
+ const body = config.systemPrompt ?? "";
83
+ return `${lines.join("\n")}\n\n${body}\n`;
84
+ } /* v9-0dca73d6d583a7d0 */
pip-tmp/jiti/agents-agents.f0509ee2.mjs ADDED
@@ -0,0 +1,808 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildBuiltinOverrideConfig = buildBuiltinOverrideConfig;Object.defineProperty(exports, "buildRuntimeName", { enumerable: true, get: function () {return _identity.buildRuntimeName;} });exports.defaultInheritProjectContext = defaultInheritProjectContext;exports.defaultInheritSkills = defaultInheritSkills;exports.defaultSystemPromptMode = defaultSystemPromptMode;exports.discoverAgents = discoverAgents;exports.discoverAgentsAll = discoverAgentsAll;Object.defineProperty(exports, "frontmatterNameForConfig", { enumerable: true, get: function () {return _identity.frontmatterNameForConfig;} });Object.defineProperty(exports, "parsePackageName", { enumerable: true, get: function () {return _identity.parsePackageName;} });exports.removeBuiltinAgentOverride = removeBuiltinAgentOverride;exports.saveBuiltinAgentOverride = saveBuiltinAgentOverride;
2
+
3
+
4
+
5
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
6
+ var os = _interopRequireWildcard(await jitiImport("node:os"));
7
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
8
+ var _nodeUrl = await jitiImport("node:url");
9
+
10
+ var _agentSerializer = await jitiImport("./agent-serializer.ts");
11
+ var _chainSerializer = await jitiImport("./chain-serializer.ts");
12
+ var _agentSelection = await jitiImport("./agent-selection.ts");
13
+ var _frontmatter = await jitiImport("./frontmatter.ts");
14
+ var _identity = await jitiImport("./identity.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /**
15
+ * Agent discovery and configuration
16
+ */
17
+
18
+
19
+
20
+
21
+
22
+
23
+ function defaultSystemPromptMode(name) {
24
+ return name === "delegate" ? "append" : "replace";
25
+ }
26
+
27
+ function defaultInheritProjectContext(name) {
28
+ return name === "delegate";
29
+ }
30
+
31
+ function defaultInheritSkills() {
32
+ return false;
33
+ }
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+ const EMPTY_SUBAGENT_SETTINGS = { overrides: {} };
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+ function getUserChainDir() {
134
+ return path.join(os.homedir(), ".pi", "agent", "chains");
135
+ }
136
+
137
+ function splitToolList(rawTools) {
138
+ const mcpDirectTools = [];
139
+ const tools = [];
140
+ for (const tool of rawTools ?? []) {
141
+ if (tool.startsWith("mcp:")) {
142
+ mcpDirectTools.push(tool.slice(4));
143
+ } else {
144
+ tools.push(tool);
145
+ }
146
+ }
147
+ return {
148
+ ...(tools.length > 0 ? { tools } : {}),
149
+ ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {})
150
+ };
151
+ }
152
+
153
+ function joinToolList(config) {
154
+ const joined = [
155
+ ...(config.tools ?? []),
156
+ ...(config.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`)];
157
+
158
+ return joined.length > 0 ? joined : undefined;
159
+ }
160
+
161
+ function arraysEqual(a, b) {
162
+ if (!a && !b) return true;
163
+ if (!a || !b) return false;
164
+ if (a.length !== b.length) return false;
165
+ for (let i = 0; i < a.length; i++) {
166
+ if (a[i] !== b[i]) return false;
167
+ }
168
+ return true;
169
+ }
170
+
171
+ function cloneOverrideBase(agent) {
172
+ return {
173
+ model: agent.model,
174
+ fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
175
+ thinking: agent.thinking,
176
+ systemPromptMode: agent.systemPromptMode,
177
+ inheritProjectContext: agent.inheritProjectContext,
178
+ inheritSkills: agent.inheritSkills,
179
+ defaultContext: agent.defaultContext,
180
+ disabled: agent.disabled,
181
+ systemPrompt: agent.systemPrompt,
182
+ skills: agent.skills ? [...agent.skills] : undefined,
183
+ tools: agent.tools ? [...agent.tools] : undefined,
184
+ mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined
185
+ };
186
+ }
187
+
188
+ function cloneOverrideValue(override) {
189
+ return {
190
+ ...(override.model !== undefined ? { model: override.model } : {}),
191
+ ...(override.fallbackModels !== undefined ?
192
+ { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] } :
193
+ {}),
194
+ ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
195
+ ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
196
+ ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
197
+ ...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),
198
+ ...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),
199
+ ...(override.disabled !== undefined ? { disabled: override.disabled } : {}),
200
+ ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
201
+ ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
202
+ ...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {})
203
+ };
204
+ }
205
+
206
+ function findNearestProjectRoot(cwd) {
207
+ let currentDir = cwd;
208
+ while (true) {
209
+ if (isDirectory(path.join(currentDir, ".pi")) || isDirectory(path.join(currentDir, ".agents"))) {
210
+ return currentDir;
211
+ }
212
+
213
+ const parentDir = path.dirname(currentDir);
214
+ if (parentDir === currentDir) return null;
215
+ currentDir = parentDir;
216
+ }
217
+ }
218
+
219
+ function getUserAgentSettingsPath() {
220
+ return path.join(os.homedir(), ".pi", "agent", "settings.json");
221
+ }
222
+
223
+ function getProjectAgentSettingsPath(cwd) {
224
+ const projectRoot = findNearestProjectRoot(cwd);
225
+ return projectRoot ? path.join(projectRoot, ".pi", "settings.json") : null;
226
+ }
227
+
228
+ function readSettingsFileStrict(filePath) {
229
+ if (!fs.existsSync(filePath)) return {};
230
+ let raw;
231
+ try {
232
+ raw = fs.readFileSync(filePath, "utf-8");
233
+ } catch (error) {
234
+ const message = error instanceof Error ? error.message : String(error);
235
+ throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
236
+ }
237
+
238
+ let parsed;
239
+ try {
240
+ parsed = JSON.parse(raw);
241
+ } catch (error) {
242
+ const message = error instanceof Error ? error.message : String(error);
243
+ throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
244
+ }
245
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
246
+ throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
247
+ }
248
+ return parsed;
249
+ }
250
+
251
+ function writeSettingsFile(filePath, settings) {
252
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
253
+ fs.writeFileSync(filePath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
254
+ }
255
+
256
+ function parseOverrideStringArrayOrFalse(
257
+ value,
258
+ meta)
259
+ {
260
+ if (value === undefined) return undefined;
261
+ if (value === false) return false;
262
+ if (!Array.isArray(value)) {
263
+ throw new Error(`Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`);
264
+ }
265
+
266
+ const items = [];
267
+ for (const item of value) {
268
+ if (typeof item !== "string") {
269
+ throw new Error(`Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`);
270
+ }
271
+ const trimmed = item.trim();
272
+ if (trimmed) items.push(trimmed);
273
+ }
274
+ return items;
275
+ }
276
+
277
+ function parseBuiltinOverrideEntry(
278
+ name,
279
+ value,
280
+ filePath)
281
+ {
282
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
283
+ throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);
284
+ }
285
+
286
+ const input = value;
287
+ const override = {};
288
+
289
+ if ("model" in input) {
290
+ if (typeof input.model === "string" || input.model === false) override.model = input.model;else
291
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
292
+ }
293
+
294
+ if ("thinking" in input) {
295
+ if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;else
296
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`);
297
+ }
298
+
299
+ if ("systemPromptMode" in input) {
300
+ if (input.systemPromptMode === "append" || input.systemPromptMode === "replace") {
301
+ override.systemPromptMode = input.systemPromptMode;
302
+ } else {
303
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);
304
+ }
305
+ }
306
+
307
+ if ("inheritProjectContext" in input) {
308
+ if (typeof input.inheritProjectContext === "boolean") {
309
+ override.inheritProjectContext = input.inheritProjectContext;
310
+ } else {
311
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`);
312
+ }
313
+ }
314
+
315
+ if ("inheritSkills" in input) {
316
+ if (typeof input.inheritSkills === "boolean") {
317
+ override.inheritSkills = input.inheritSkills;
318
+ } else {
319
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);
320
+ }
321
+ }
322
+
323
+ if ("defaultContext" in input) {
324
+ if (input.defaultContext === "fresh" || input.defaultContext === "fork" || input.defaultContext === false) {
325
+ override.defaultContext = input.defaultContext;
326
+ } else {
327
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);
328
+ }
329
+ }
330
+
331
+ if ("disabled" in input) {
332
+ if (typeof input.disabled === "boolean") {
333
+ override.disabled = input.disabled;
334
+ } else {
335
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);
336
+ }
337
+ }
338
+
339
+ if ("systemPrompt" in input) {
340
+ if (typeof input.systemPrompt === "string") override.systemPrompt = input.systemPrompt;else
341
+ throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);
342
+ }
343
+
344
+ const fallbackModels = parseOverrideStringArrayOrFalse(input.fallbackModels, { filePath, name, field: "fallbackModels" });
345
+ if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
346
+
347
+ const skills = parseOverrideStringArrayOrFalse(input.skills, { filePath, name, field: "skills" });
348
+ if (skills !== undefined) override.skills = skills;
349
+
350
+ const tools = parseOverrideStringArrayOrFalse(input.tools, { filePath, name, field: "tools" });
351
+ if (tools !== undefined) override.tools = tools;
352
+
353
+ return Object.keys(override).length > 0 ? override : undefined;
354
+ }
355
+
356
+ function readSubagentSettings(filePath) {
357
+ if (!filePath) return EMPTY_SUBAGENT_SETTINGS;
358
+ const settings = readSettingsFileStrict(filePath);
359
+ const subagents = settings.subagents;
360
+ if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return EMPTY_SUBAGENT_SETTINGS;
361
+
362
+ const subagentsObject = subagents;
363
+ let disableBuiltins;
364
+ if ("disableBuiltins" in subagentsObject) {
365
+ if (typeof subagentsObject.disableBuiltins === "boolean") {
366
+ disableBuiltins = subagentsObject.disableBuiltins;
367
+ } else {
368
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);
369
+ }
370
+ }
371
+
372
+ const parsed = {};
373
+ const agentOverrides = subagentsObject.agentOverrides;
374
+ if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) {
375
+ return { overrides: parsed, disableBuiltins };
376
+ }
377
+ for (const [name, value] of Object.entries(agentOverrides)) {
378
+ const override = parseBuiltinOverrideEntry(name, value, filePath);
379
+ if (override) parsed[name] = override;
380
+ }
381
+ return { overrides: parsed, disableBuiltins };
382
+ }
383
+
384
+ function applyBuiltinOverride(
385
+ agent,
386
+ override,
387
+ meta)
388
+ {
389
+ const next = {
390
+ ...agent,
391
+ override: { ...meta, base: cloneOverrideBase(agent) }
392
+ };
393
+
394
+ if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
395
+ if (override.fallbackModels !== undefined) {
396
+ next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
397
+ }
398
+ if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;
399
+ if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
400
+ if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
401
+ if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;
402
+ if (override.defaultContext !== undefined) next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;
403
+ if (override.disabled !== undefined) next.disabled = override.disabled;
404
+ if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
405
+ if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];
406
+ if (override.tools !== undefined) {
407
+ const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
408
+ next.tools = tools;
409
+ next.mcpDirectTools = mcpDirectTools;
410
+ }
411
+
412
+ return next;
413
+ }
414
+
415
+ function applyBuiltinOverrides(
416
+ builtinAgents,
417
+ userSettings,
418
+ projectSettings,
419
+ userSettingsPath,
420
+ projectSettingsPath)
421
+ {
422
+ const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;
423
+ const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;
424
+
425
+ return builtinAgents.map((agent) => {
426
+ const projectOverride = projectSettings.overrides[agent.name];
427
+ if (projectOverride && projectSettingsPath) {
428
+ return applyBuiltinOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath });
429
+ }
430
+
431
+ if (projectBulkDisabled && projectSettingsPath) {
432
+ return applyBuiltinOverride(agent, { disabled: true }, { scope: "project", path: projectSettingsPath });
433
+ }
434
+
435
+ const userOverride = userSettings.overrides[agent.name];
436
+ if (userOverride) {
437
+ return applyBuiltinOverride(agent, userOverride, { scope: "user", path: userSettingsPath });
438
+ }
439
+
440
+ if (userBulkDisabled) {
441
+ return applyBuiltinOverride(agent, { disabled: true }, { scope: "user", path: userSettingsPath });
442
+ }
443
+
444
+ return agent;
445
+ });
446
+ }
447
+
448
+ function buildBuiltinOverrideConfig(
449
+ base,
450
+ draft)
451
+ {
452
+ const override = {};
453
+
454
+ if (draft.model !== base.model) override.model = draft.model ?? false;
455
+ if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
456
+ if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
457
+ if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
458
+ if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
459
+ if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;
460
+ if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;
461
+ if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;
462
+ if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;
463
+ if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;
464
+
465
+ const baseTools = joinToolList(base);
466
+ const draftTools = joinToolList(draft);
467
+ if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
468
+
469
+ return Object.keys(override).length > 0 ? override : undefined;
470
+ }
471
+
472
+ function saveBuiltinAgentOverride(
473
+ cwd,
474
+ name,
475
+ scope,
476
+ override)
477
+ {
478
+ const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
479
+ if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
480
+
481
+ const settings = readSettingsFileStrict(filePath);
482
+ const subagents = settings.subagents && typeof settings.subagents === "object" && !Array.isArray(settings.subagents) ?
483
+ { ...settings.subagents } :
484
+ {};
485
+ const agentOverrides = subagents.agentOverrides && typeof subagents.agentOverrides === "object" && !Array.isArray(subagents.agentOverrides) ?
486
+ { ...subagents.agentOverrides } :
487
+ {};
488
+
489
+ agentOverrides[name] = cloneOverrideValue(override);
490
+ subagents.agentOverrides = agentOverrides;
491
+ settings.subagents = subagents;
492
+ writeSettingsFile(filePath, settings);
493
+ return filePath;
494
+ }
495
+
496
+ function removeBuiltinAgentOverride(cwd, name, scope) {
497
+ const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
498
+ if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
499
+ if (!fs.existsSync(filePath)) return filePath;
500
+
501
+ const settings = readSettingsFileStrict(filePath);
502
+ const subagents = settings.subagents;
503
+ if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return filePath;
504
+ const nextSubagents = { ...subagents };
505
+ const agentOverrides = nextSubagents.agentOverrides;
506
+ if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return filePath;
507
+
508
+ const nextOverrides = { ...agentOverrides };
509
+ delete nextOverrides[name];
510
+ if (Object.keys(nextOverrides).length > 0) nextSubagents.agentOverrides = nextOverrides;else
511
+ delete nextSubagents.agentOverrides;
512
+
513
+ if (Object.keys(nextSubagents).length > 0) settings.subagents = nextSubagents;else
514
+ delete settings.subagents;
515
+
516
+ writeSettingsFile(filePath, settings);
517
+ return filePath;
518
+ }
519
+
520
+ function listMarkdownFilesRecursive(dir, predicate) {
521
+ const files = [];
522
+ if (!fs.existsSync(dir)) return files;
523
+
524
+ let entries;
525
+ try {
526
+ entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
527
+ } catch {
528
+ return files;
529
+ }
530
+
531
+ for (const entry of entries) {
532
+ const filePath = path.join(dir, entry.name);
533
+ if (entry.isDirectory()) {
534
+ files.push(...listMarkdownFilesRecursive(filePath, predicate));
535
+ continue;
536
+ }
537
+ if (!entry.isFile() && !entry.isSymbolicLink()) continue;
538
+ if (!predicate(entry.name)) continue;
539
+ files.push(filePath);
540
+ }
541
+ return files;
542
+ }
543
+
544
+ function loadAgentsFromDir(dir, source) {
545
+ const agents = [];
546
+
547
+ for (const filePath of listMarkdownFilesRecursive(dir, (fileName) => fileName.endsWith(".md") && !fileName.endsWith(".chain.md"))) {
548
+ let content;
549
+ try {
550
+ content = fs.readFileSync(filePath, "utf-8");
551
+ } catch {
552
+ continue;
553
+ }
554
+
555
+ const { frontmatter, body } = (0, _frontmatter.parseFrontmatter)(content);
556
+
557
+ if (!frontmatter.name || !frontmatter.description) {
558
+ continue;
559
+ }
560
+
561
+ const localName = frontmatter.name;
562
+ const parsedPackage = (0, _identity.parsePackageName)(frontmatter.package, `Agent '${localName}' package`);
563
+ if (parsedPackage.error) continue;
564
+ const packageName = parsedPackage.packageName;
565
+ const runtimeName = (0, _identity.buildRuntimeName)(localName, packageName);
566
+
567
+ const rawTools = frontmatter.tools?.
568
+ split(",").
569
+ map((t) => t.trim()).
570
+ filter(Boolean);
571
+
572
+ const mcpDirectTools = [];
573
+ const tools = [];
574
+ if (rawTools) {
575
+ for (const tool of rawTools) {
576
+ if (tool.startsWith("mcp:")) {
577
+ mcpDirectTools.push(tool.slice(4));
578
+ } else {
579
+ tools.push(tool);
580
+ }
581
+ }
582
+ }
583
+
584
+ const defaultReads = frontmatter.defaultReads?.
585
+ split(",").
586
+ map((f) => f.trim()).
587
+ filter(Boolean);
588
+
589
+ const skillStr = frontmatter.skill || frontmatter.skills;
590
+ const skills = skillStr?.
591
+ split(",").
592
+ map((s) => s.trim()).
593
+ filter(Boolean);
594
+ const fallbackModels = frontmatter.fallbackModels?.
595
+ split(",").
596
+ map((model) => model.trim()).
597
+ filter(Boolean);
598
+ const systemPromptMode = frontmatter.systemPromptMode === "replace" ?
599
+ "replace" :
600
+ frontmatter.systemPromptMode === "append" ?
601
+ "append" :
602
+ defaultSystemPromptMode(localName);
603
+ const inheritProjectContext = frontmatter.inheritProjectContext === "true" ?
604
+ true :
605
+ frontmatter.inheritProjectContext === "false" ?
606
+ false :
607
+ defaultInheritProjectContext(localName);
608
+ const inheritSkills = frontmatter.inheritSkills === "true" ?
609
+ true :
610
+ frontmatter.inheritSkills === "false" ?
611
+ false :
612
+ defaultInheritSkills();
613
+ const defaultContext = frontmatter.defaultContext === "fork" ?
614
+ "fork" :
615
+ frontmatter.defaultContext === "fresh" ?
616
+ "fresh" :
617
+ undefined;
618
+
619
+ let extensions;
620
+ if (frontmatter.extensions !== undefined) {
621
+ extensions = frontmatter.extensions.
622
+ split(",").
623
+ map((e) => e.trim()).
624
+ filter(Boolean);
625
+ }
626
+
627
+ const extraFields = {};
628
+ for (const [key, value] of Object.entries(frontmatter)) {
629
+ if (!_agentSerializer.KNOWN_FIELDS.has(key)) extraFields[key] = value;
630
+ }
631
+
632
+ const parsedMaxSubagentDepth = Number(frontmatter.maxSubagentDepth);
633
+
634
+ agents.push({
635
+ name: runtimeName,
636
+ localName,
637
+ packageName,
638
+ description: frontmatter.description,
639
+ tools: tools.length > 0 ? tools : undefined,
640
+ mcpDirectTools: mcpDirectTools.length > 0 ? mcpDirectTools : undefined,
641
+ model: frontmatter.model,
642
+ fallbackModels: fallbackModels && fallbackModels.length > 0 ? fallbackModels : undefined,
643
+ thinking: frontmatter.thinking,
644
+ systemPromptMode,
645
+ inheritProjectContext,
646
+ inheritSkills,
647
+ defaultContext,
648
+ systemPrompt: body,
649
+ source,
650
+ filePath,
651
+ skills: skills && skills.length > 0 ? skills : undefined,
652
+ extensions,
653
+ output: frontmatter.output,
654
+ defaultReads: defaultReads && defaultReads.length > 0 ? defaultReads : undefined,
655
+ defaultProgress: frontmatter.defaultProgress === "true",
656
+ interactive: frontmatter.interactive === "true",
657
+ maxSubagentDepth:
658
+ Number.isInteger(parsedMaxSubagentDepth) && parsedMaxSubagentDepth >= 0 ?
659
+ parsedMaxSubagentDepth :
660
+ undefined,
661
+ extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined
662
+ });
663
+ }
664
+
665
+ return agents;
666
+ }
667
+
668
+ function loadChainsFromDir(dir, source) {
669
+ const chains = [];
670
+
671
+ for (const filePath of listMarkdownFilesRecursive(dir, (fileName) => fileName.endsWith(".chain.md"))) {
672
+ let content;
673
+ try {
674
+ content = fs.readFileSync(filePath, "utf-8");
675
+ } catch {
676
+ continue;
677
+ }
678
+
679
+ try {
680
+ chains.push((0, _chainSerializer.parseChain)(content, source, filePath));
681
+ } catch {
682
+ continue;
683
+ }
684
+ }
685
+
686
+ return chains;
687
+ }
688
+
689
+ function isDirectory(p) {
690
+ try {
691
+ return fs.statSync(p).isDirectory();
692
+ } catch {
693
+ return false;
694
+ }
695
+ }
696
+
697
+ function resolveNearestProjectAgentDirs(cwd) {
698
+ const projectRoot = findNearestProjectRoot(cwd);
699
+ if (!projectRoot) return { readDirs: [], preferredDir: null };
700
+
701
+ const legacyDir = path.join(projectRoot, ".agents");
702
+ const preferredDir = path.join(projectRoot, ".pi", "agents");
703
+ const readDirs = [];
704
+ if (isDirectory(legacyDir)) readDirs.push(legacyDir);
705
+ if (isDirectory(preferredDir)) readDirs.push(preferredDir);
706
+
707
+ return {
708
+ readDirs,
709
+ preferredDir
710
+ };
711
+ }
712
+
713
+ function resolveNearestProjectChainDirs(cwd) {
714
+ const projectRoot = findNearestProjectRoot(cwd);
715
+ if (!projectRoot) return { readDirs: [], preferredDir: null };
716
+
717
+ const preferredDir = path.join(projectRoot, ".pi", "chains");
718
+ return {
719
+ readDirs: isDirectory(preferredDir) ? [preferredDir] : [],
720
+ preferredDir
721
+ };
722
+ }
723
+ const BUILTIN_AGENTS_DIR = path.resolve(path.dirname((0, _nodeUrl.fileURLToPath)("file:///home/edwin/.config/nvm/versions/node/v22.20.0/lib/node_modules/pi-subagents/src/agents/agents.ts")), "..", "..", "agents");
724
+
725
+ function discoverAgents(cwd, scope) {
726
+ const userDirOld = path.join(os.homedir(), ".pi", "agent", "agents");
727
+ const userDirNew = path.join(os.homedir(), ".agents");
728
+ const { readDirs: projectAgentDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(cwd);
729
+ const userSettingsPath = getUserAgentSettingsPath();
730
+ const projectSettingsPath = getProjectAgentSettingsPath(cwd);
731
+ const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
732
+ const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
733
+
734
+ const builtinAgents = applyBuiltinOverrides(
735
+ loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
736
+ userSettings,
737
+ projectSettings,
738
+ userSettingsPath,
739
+ projectSettingsPath
740
+ );
741
+
742
+ const userAgentsOld = scope === "project" ? [] : loadAgentsFromDir(userDirOld, "user");
743
+ const userAgentsNew = scope === "project" ? [] : loadAgentsFromDir(userDirNew, "user");
744
+ const userAgents = [...userAgentsOld, ...userAgentsNew];
745
+
746
+ const projectAgents = scope === "user" ? [] : projectAgentDirs.flatMap((dir) => loadAgentsFromDir(dir, "project"));
747
+ const agents = (0, _agentSelection.mergeAgentsForScope)(scope, userAgents, projectAgents, builtinAgents).
748
+ filter((agent) => agent.disabled !== true);
749
+
750
+ return { agents, projectAgentsDir };
751
+ }
752
+
753
+ function discoverAgentsAll(cwd)
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+ {
765
+ const userDirOld = path.join(os.homedir(), ".pi", "agent", "agents");
766
+ const userDirNew = path.join(os.homedir(), ".agents");
767
+ const userChainDir = getUserChainDir();
768
+ const { readDirs: projectDirs, preferredDir: projectDir } = resolveNearestProjectAgentDirs(cwd);
769
+ const { readDirs: projectChainDirs, preferredDir: projectChainDir } = resolveNearestProjectChainDirs(cwd);
770
+ const userSettingsPath = getUserAgentSettingsPath();
771
+ const projectSettingsPath = getProjectAgentSettingsPath(cwd);
772
+ const userSettings = readSubagentSettings(userSettingsPath);
773
+ const projectSettings = readSubagentSettings(projectSettingsPath);
774
+
775
+ const builtin = applyBuiltinOverrides(
776
+ loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
777
+ userSettings,
778
+ projectSettings,
779
+ userSettingsPath,
780
+ projectSettingsPath
781
+ );
782
+ const user = [
783
+ ...loadAgentsFromDir(userDirOld, "user"),
784
+ ...loadAgentsFromDir(userDirNew, "user")];
785
+
786
+ const projectMap = new Map();
787
+ for (const dir of projectDirs) {
788
+ for (const agent of loadAgentsFromDir(dir, "project")) {
789
+ projectMap.set(agent.name, agent);
790
+ }
791
+ }
792
+ const project = Array.from(projectMap.values());
793
+
794
+ const chainMap = new Map();
795
+ for (const dir of projectChainDirs) {
796
+ for (const chain of loadChainsFromDir(dir, "project")) {
797
+ chainMap.set(chain.name, chain);
798
+ }
799
+ }
800
+ const chains = [
801
+ ...loadChainsFromDir(userChainDir, "user"),
802
+ ...Array.from(chainMap.values())];
803
+
804
+
805
+ const userDir = fs.existsSync(userDirNew) ? userDirNew : userDirOld;
806
+
807
+ return { builtin, user, project, chains, userDir, projectDir, userChainDir, projectChainDir, userSettingsPath, projectSettingsPath };
808
+ } /* v9-467e5e633d01fc28 */
pip-tmp/jiti/agents-chain-serializer.a832da0e.mjs ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.parseChain = parseChain;exports.serializeChain = serializeChain;
2
+ var _identity = await jitiImport("./identity.ts");
3
+ var _frontmatter = await jitiImport("./frontmatter.ts");
4
+
5
+ function parseStepBody(agent, sectionBody) {
6
+ const lines = sectionBody.split("\n");
7
+ const blankIndex = lines.findIndex((line) => line.trim() === "");
8
+ const configLines = blankIndex === -1 ? lines : lines.slice(0, blankIndex);
9
+ const task = (blankIndex === -1 ? "" : lines.slice(blankIndex + 1).join("\n")).trim();
10
+
11
+ const step = { agent, task };
12
+ for (const line of configLines) {
13
+ const match = line.match(/^([\w-]+):\s*(.*)$/);
14
+ if (!match) continue;
15
+ const key = match[1].trim().toLowerCase();
16
+ const rawValue = match[2].trim();
17
+
18
+ if (key === "output") {
19
+ if (rawValue === "false") step.output = false;else
20
+ if (rawValue) step.output = rawValue;
21
+ continue;
22
+ }
23
+ if (key === "outputmode") {
24
+ if (rawValue === "inline" || rawValue === "file-only") step.outputMode = rawValue;
25
+ continue;
26
+ }
27
+ if (key === "reads") {
28
+ if (rawValue === "false") {
29
+ step.reads = false;
30
+ } else {
31
+ const reads = rawValue.
32
+ split(",").
33
+ map((v) => v.trim()).
34
+ filter(Boolean);
35
+ step.reads = reads.length > 0 ? reads : false;
36
+ }
37
+ continue;
38
+ }
39
+ if (key === "model") {
40
+ if (rawValue) step.model = rawValue;
41
+ continue;
42
+ }
43
+ if (key === "skills") {
44
+ if (rawValue === "false") {
45
+ step.skills = false;
46
+ } else {
47
+ const skills = rawValue.
48
+ split(",").
49
+ map((v) => v.trim()).
50
+ filter(Boolean);
51
+ step.skills = skills.length > 0 ? skills : false;
52
+ }
53
+ continue;
54
+ }
55
+ if (key === "progress") {
56
+ if (rawValue === "true") step.progress = true;else
57
+ if (rawValue === "false") step.progress = false;
58
+ }
59
+ }
60
+
61
+ return step;
62
+ }
63
+
64
+ function parseChain(content, source, filePath) {
65
+ const { frontmatter, body } = (0, _frontmatter.parseFrontmatter)(content);
66
+ if (!frontmatter.name || !frontmatter.description) {
67
+ throw new Error("Chain frontmatter must include name and description");
68
+ }
69
+
70
+ const matches = [...body.matchAll(/^##\s+(.+)[^\S\n]*$/gm)];
71
+ const steps = [];
72
+
73
+ for (let i = 0; i < matches.length; i++) {
74
+ const match = matches[i];
75
+ const agent = match[1].trim();
76
+ const lineEndOffset = body[match.index + match[0].length] === "\n" ? 1 : 0;
77
+ const sectionStart = match.index + match[0].length + lineEndOffset;
78
+ const sectionEnd = i + 1 < matches.length ? matches[i + 1].index : body.length;
79
+ const sectionBody = body.slice(sectionStart, sectionEnd).trimEnd();
80
+ steps.push(parseStepBody(agent, sectionBody));
81
+ }
82
+
83
+ const localName = frontmatter.name;
84
+ const parsedPackage = (0, _identity.parsePackageName)(frontmatter.package, `Chain '${localName}' package`);
85
+ if (parsedPackage.error) throw new Error(parsedPackage.error);
86
+ const packageName = parsedPackage.packageName;
87
+ const extraFields = {};
88
+ for (const [key, value] of Object.entries(frontmatter)) {
89
+ if (key === "name" || key === "package" || key === "description") continue;
90
+ extraFields[key] = value;
91
+ }
92
+
93
+ return {
94
+ name: (0, _identity.buildRuntimeName)(localName, packageName),
95
+ localName,
96
+ packageName,
97
+ description: frontmatter.description,
98
+ source,
99
+ filePath,
100
+ steps,
101
+ extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined
102
+ };
103
+ }
104
+
105
+ function serializeChain(config) {
106
+ const lines = [];
107
+ lines.push("---");
108
+ lines.push(`name: ${(0, _identity.frontmatterNameForConfig)(config)}`);
109
+ if (config.packageName) lines.push(`package: ${config.packageName}`);
110
+ lines.push(`description: ${config.description}`);
111
+ if (config.extraFields) {
112
+ for (const [key, value] of Object.entries(config.extraFields)) {
113
+ lines.push(`${key}: ${value}`);
114
+ }
115
+ }
116
+ lines.push("---");
117
+ lines.push("");
118
+
119
+ for (let i = 0; i < config.steps.length; i++) {
120
+ const step = config.steps[i];
121
+ lines.push(`## ${step.agent}`);
122
+ if (step.output === false) lines.push("output: false");else
123
+ if (step.output) lines.push(`output: ${step.output}`);
124
+ if (step.outputMode) lines.push(`outputMode: ${step.outputMode}`);
125
+ if (step.reads === false) lines.push("reads: false");else
126
+ if (Array.isArray(step.reads) && step.reads.length > 0) lines.push(`reads: ${step.reads.join(", ")}`);
127
+ if (step.model) lines.push(`model: ${step.model}`);
128
+ if (step.skills === false) lines.push("skills: false");else
129
+ if (Array.isArray(step.skills) && step.skills.length > 0) lines.push(`skills: ${step.skills.join(", ")}`);
130
+ if (step.progress !== undefined) lines.push(`progress: ${step.progress ? "true" : "false"}`);
131
+ lines.push("");
132
+ lines.push(step.task ?? "");
133
+ if (i < config.steps.length - 1) lines.push("");
134
+ }
135
+
136
+ return `${lines.join("\n")}\n`;
137
+ } /* v9-06495e239253795d */
pip-tmp/jiti/agents-frontmatter.6976eaf1.mjs ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.parseFrontmatter = parseFrontmatter;function parseFrontmatter(content) {
2
+ const frontmatter = {};
3
+ const normalized = content.replace(/\r\n/g, "\n");
4
+
5
+ if (!normalized.startsWith("---")) {
6
+ return { frontmatter, body: normalized };
7
+ }
8
+
9
+ const endIndex = normalized.indexOf("\n---", 3);
10
+ if (endIndex === -1) {
11
+ return { frontmatter, body: normalized };
12
+ }
13
+
14
+ const frontmatterBlock = normalized.slice(4, endIndex);
15
+ const body = normalized.slice(endIndex + 4).trim();
16
+
17
+ for (const line of frontmatterBlock.split("\n")) {
18
+ const match = line.match(/^([\w-]+):\s*(.*)$/);
19
+ if (match) {
20
+ let value = match[2].trim();
21
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
22
+ value = value.slice(1, -1);
23
+ }
24
+ frontmatter[match[1]] = value;
25
+ }
26
+ }
27
+
28
+ return { frontmatter, body };
29
+ } /* v9-41830bb6fbb8227a */
pip-tmp/jiti/agents-identity.568fcc02.mjs ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildRuntimeName = buildRuntimeName;exports.frontmatterNameForConfig = frontmatterNameForConfig;exports.parsePackageName = parsePackageName;
2
+
3
+ const IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/;
4
+
5
+ function normalizePackageName(value) {
6
+ const trimmed = value?.trim();
7
+ if (!trimmed) return undefined;
8
+ return trimmed.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9.-]/g, "").replace(/-+/g, "-").replace(/\.+/g, ".").replace(/(?:^[-.]+|[-.]+$)/g, "");
9
+ }
10
+
11
+ function parsePackageName(value, label = "package") {
12
+ if (value === undefined || value === false || value === "") return { packageName: undefined };
13
+ if (typeof value !== "string") return { error: `${label} must be a string or false when provided.` };
14
+ const packageName = normalizePackageName(value);
15
+ if (!packageName || !IDENTIFIER_PATTERN.test(packageName)) return { error: `${label} is invalid after sanitization.` };
16
+ return { packageName };
17
+ }
18
+
19
+ function buildRuntimeName(localName, packageName) {
20
+ const trimmedPackage = packageName?.trim();
21
+ return trimmedPackage ? `${trimmedPackage}.${localName}` : localName;
22
+ }
23
+
24
+ function frontmatterNameForConfig(config) {
25
+ if (config.localName) return config.localName;
26
+ if (config.packageName && config.name.startsWith(`${config.packageName}.`)) {
27
+ return config.name.slice(config.packageName.length + 1);
28
+ }
29
+ return config.name;
30
+ } /* v9-1a128e76bae61f4f */
pip-tmp/jiti/agents-skills.af023ec8.mjs ADDED
@@ -0,0 +1,630 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildSkillInjection = buildSkillInjection;exports.clearSkillCache = clearSkillCache;exports.discoverAvailableSkills = discoverAvailableSkills;exports.normalizeSkillInput = normalizeSkillInput;exports.resolveSkillPath = resolveSkillPath;exports.resolveSkills = resolveSkills;exports.resolveSkillsWithFallback = resolveSkillsWithFallback;
2
+
3
+
4
+
5
+ var _nodeChild_process = await jitiImport("node:child_process");
6
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
7
+ var os = _interopRequireWildcard(await jitiImport("node:os"));
8
+ var path = _interopRequireWildcard(await jitiImport("node:path"));function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /**
9
+ * Skill resolution and caching for subagent extension
10
+ */
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ const skillCache = new Map();
47
+ const MAX_CACHE_SIZE = 50;
48
+
49
+ let loadSkillsCache = null;
50
+ const LOAD_SKILLS_CACHE_TTL_MS = 5000;
51
+
52
+ const CONFIG_DIR = ".pi";
53
+ const AGENT_DIR = path.join(os.homedir(), ".pi", "agent");
54
+ const SUBAGENT_ORCHESTRATION_SKILL = "pi-subagents";
55
+
56
+ const SOURCE_PRIORITY = {
57
+ project: 700,
58
+ "project-settings": 650,
59
+ "project-package": 600,
60
+ user: 300,
61
+ "user-settings": 250,
62
+ "user-package": 200,
63
+ extension: 150,
64
+ builtin: 100,
65
+ unknown: 0
66
+ };
67
+
68
+ function stripSkillFrontmatter(content) {
69
+ const normalized = content.replace(/\r\n/g, "\n");
70
+ if (!normalized.startsWith("---")) return normalized;
71
+
72
+ const endIndex = normalized.indexOf("\n---", 3);
73
+ if (endIndex === -1) return normalized;
74
+
75
+ return normalized.slice(endIndex + 4).trim();
76
+ }
77
+
78
+ function isWithinPath(filePath, dir) {
79
+ const relative = path.relative(dir, filePath);
80
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
81
+ }
82
+
83
+ function readOptionalJsonFile(filePath, label) {
84
+ try {
85
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
86
+ } catch (error) {
87
+ const code = typeof error === "object" && error !== null && "code" in error ?
88
+ error.code :
89
+ undefined;
90
+ if (code === "ENOENT") return null;
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ throw new Error(`Failed to read ${label} '${filePath}': ${message}`, {
93
+ cause: error instanceof Error ? error : undefined
94
+ });
95
+ }
96
+ }
97
+
98
+ function readJsonFileBestEffort(filePath) {
99
+ try {
100
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
101
+ } catch {
102
+ // Package scans over installed dependencies are opportunistic.
103
+ return null;
104
+ }
105
+ }
106
+
107
+ function extractSkillPathsFromPackageRoot(packageRoot, source, bestEffort = false) {
108
+ const packageJsonPath = path.join(packageRoot, "package.json");
109
+ const pkg = bestEffort ?
110
+ readJsonFileBestEffort(packageJsonPath) :
111
+ readOptionalJsonFile(packageJsonPath, "package manifest");
112
+ if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return [];
113
+ const pi = pkg.pi;
114
+ if (!pi || typeof pi !== "object" || Array.isArray(pi)) return [];
115
+ const skills = pi.skills;
116
+ if (!Array.isArray(skills)) return [];
117
+ return skills.
118
+ filter((entry) => typeof entry === "string").
119
+ map((entry) => ({ path: path.resolve(packageRoot, entry), source }));
120
+ }
121
+
122
+ let cachedGlobalNpmRoot = null;
123
+
124
+ function getGlobalNpmRoot() {
125
+ if (cachedGlobalNpmRoot !== null) return cachedGlobalNpmRoot;
126
+ try {
127
+ cachedGlobalNpmRoot = (0, _nodeChild_process.execSync)("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
128
+ return cachedGlobalNpmRoot;
129
+ } catch {
130
+ // Global npm root is optional in constrained environments.
131
+ cachedGlobalNpmRoot = ""; // Empty string means "tried but failed"
132
+ return null;
133
+ }
134
+ }
135
+
136
+ function collectInstalledPackageSkillPaths(cwd) {
137
+ const dirs = [
138
+ { path: path.join(cwd, CONFIG_DIR, "npm", "node_modules"), source: "project-package" },
139
+ { path: path.join(AGENT_DIR, "npm", "node_modules"), source: "user-package" }];
140
+
141
+
142
+ const globalRoot = getGlobalNpmRoot();
143
+ if (globalRoot) {
144
+ dirs.push({ path: globalRoot, source: "user-package" });
145
+ }
146
+
147
+ const results = [];
148
+
149
+ for (const dir of dirs) {
150
+ if (!fs.existsSync(dir.path)) continue;
151
+ let entries;
152
+ try {
153
+ entries = fs.readdirSync(dir.path, { withFileTypes: true });
154
+ } catch {
155
+ continue;
156
+ }
157
+
158
+ for (const entry of entries) {
159
+ if (entry.name.startsWith(".")) continue;
160
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
161
+
162
+ if (entry.name.startsWith("@")) {
163
+ const scopeDir = path.join(dir.path, entry.name);
164
+ let scopeEntries;
165
+ try {
166
+ scopeEntries = fs.readdirSync(scopeDir, { withFileTypes: true });
167
+ } catch {
168
+ continue;
169
+ }
170
+ for (const scopeEntry of scopeEntries) {
171
+ if (scopeEntry.name.startsWith(".")) continue;
172
+ if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink()) continue;
173
+ const pkgRoot = path.join(scopeDir, scopeEntry.name);
174
+ results.push(...extractSkillPathsFromPackageRoot(pkgRoot, dir.source, true));
175
+ }
176
+ continue;
177
+ }
178
+
179
+ const pkgRoot = path.join(dir.path, entry.name);
180
+ results.push(...extractSkillPathsFromPackageRoot(pkgRoot, dir.source, true));
181
+ }
182
+ }
183
+
184
+ return results;
185
+ }
186
+
187
+ function collectSettingsSkillPaths(cwd) {
188
+ const results = [];
189
+ const settingsFiles = [
190
+ { file: path.join(cwd, CONFIG_DIR, "settings.json"), base: path.join(cwd, CONFIG_DIR), source: "project-settings" },
191
+ { file: path.join(AGENT_DIR, "settings.json"), base: AGENT_DIR, source: "user-settings" }];
192
+
193
+
194
+ for (const { file, base, source } of settingsFiles) {
195
+ const settings = readOptionalJsonFile(file, "skills settings file");
196
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) continue;
197
+ const skills = settings.skills;
198
+ if (!Array.isArray(skills)) continue;
199
+ for (const entry of skills) {
200
+ if (typeof entry !== "string") continue;
201
+ let resolved = entry;
202
+ if (resolved.startsWith("~/")) {
203
+ resolved = path.join(os.homedir(), resolved.slice(2));
204
+ } else if (!path.isAbsolute(resolved)) {
205
+ resolved = path.resolve(base, resolved);
206
+ }
207
+ results.push({ path: resolved, source });
208
+ }
209
+ }
210
+
211
+ return results;
212
+ }
213
+
214
+ function isSafePackagePath(value) {
215
+ return value.length > 0 &&
216
+ !path.isAbsolute(value) &&
217
+ value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
218
+ }
219
+
220
+ function parseNpmPackageName(source) {
221
+ const spec = source.slice(4).trim();
222
+ if (!spec) return undefined;
223
+ const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
224
+ const packageName = match?.[1] ?? spec;
225
+ return isSafePackagePath(packageName) ? packageName : undefined;
226
+ }
227
+
228
+ function stripGitRef(repoPath) {
229
+ const atIndex = repoPath.indexOf("@");
230
+ const hashIndex = repoPath.indexOf("#");
231
+ const refIndex = [atIndex, hashIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
232
+ return refIndex === undefined ? repoPath : repoPath.slice(0, refIndex);
233
+ }
234
+
235
+ function parseGitPackagePath(source) {
236
+ const spec = source.slice(4).trim();
237
+ if (!spec) return undefined;
238
+
239
+ let host = "";
240
+ let repoPath = "";
241
+ const scpLike = spec.match(/^git@([^:]+):(.+)$/);
242
+ if (scpLike) {
243
+ host = scpLike[1] ?? "";
244
+ repoPath = scpLike[2] ?? "";
245
+ } else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(spec)) {
246
+ try {
247
+ const url = new URL(spec);
248
+ host = url.hostname;
249
+ repoPath = url.pathname.replace(/^\/+/, "");
250
+ } catch {
251
+ return undefined;
252
+ }
253
+ } else {
254
+ const slashIndex = spec.indexOf("/");
255
+ if (slashIndex < 0) return undefined;
256
+ host = spec.slice(0, slashIndex);
257
+ repoPath = spec.slice(slashIndex + 1);
258
+ }
259
+
260
+ const normalizedPath = stripGitRef(repoPath).replace(/\.git$/, "").replace(/^\/+/, "");
261
+ if (!host || !isSafePackagePath(host) || !isSafePackagePath(normalizedPath) || normalizedPath.split(/[\\/]/).length < 2) {
262
+ return undefined;
263
+ }
264
+ return { host, repoPath: normalizedPath };
265
+ }
266
+
267
+ function resolveSettingsPackageRoot(source, baseDir) {
268
+ const trimmed = source.trim();
269
+ if (!trimmed) return undefined;
270
+ if (trimmed.startsWith("git:")) {
271
+ const parsed = parseGitPackagePath(trimmed);
272
+ return parsed ? path.join(baseDir, "git", parsed.host, parsed.repoPath) : undefined;
273
+ }
274
+ if (trimmed.startsWith("npm:")) {
275
+ const packageName = parseNpmPackageName(trimmed);
276
+ return packageName ? path.join(baseDir, "npm", "node_modules", packageName) : undefined;
277
+ }
278
+ const normalized = trimmed.startsWith("file:") ? trimmed.slice(5) : trimmed;
279
+ if (normalized === "~") return os.homedir();
280
+ if (normalized.startsWith("~/")) return path.join(os.homedir(), normalized.slice(2));
281
+ if (path.isAbsolute(normalized)) return normalized;
282
+ if (normalized === "." || normalized === ".." || normalized.startsWith("./") || normalized.startsWith("../")) {
283
+ return path.resolve(baseDir, normalized);
284
+ }
285
+ return undefined;
286
+ }
287
+
288
+ function collectSettingsPackageSkillPaths(cwd) {
289
+ const settingsFiles = [
290
+ { file: path.join(cwd, CONFIG_DIR, "settings.json"), base: path.join(cwd, CONFIG_DIR), source: "project-package" },
291
+ { file: path.join(AGENT_DIR, "settings.json"), base: AGENT_DIR, source: "user-package" }];
292
+
293
+ const results = [];
294
+
295
+ for (const { file, base, source } of settingsFiles) {
296
+ const settings = readOptionalJsonFile(file, "skills settings file");
297
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) continue;
298
+ const packages = settings.packages;
299
+ if (!Array.isArray(packages)) continue;
300
+
301
+ for (const entry of packages) {
302
+ const packageSource = typeof entry === "string" ?
303
+ entry :
304
+ typeof entry === "object" && entry !== null && typeof entry.source === "string" ?
305
+ entry.source :
306
+ undefined;
307
+ if (!packageSource) continue;
308
+
309
+ const packageRoot = resolveSettingsPackageRoot(packageSource, base);
310
+ if (!packageRoot) continue;
311
+ results.push(...extractSkillPathsFromPackageRoot(packageRoot, source));
312
+ }
313
+ }
314
+
315
+ return results;
316
+ }
317
+
318
+ function buildSkillPaths(cwd) {
319
+ const skillPaths = [
320
+ { path: path.join(cwd, CONFIG_DIR, "skills"), source: "project" },
321
+ { path: path.join(cwd, ".agents", "skills"), source: "project" },
322
+ { path: path.join(AGENT_DIR, "skills"), source: "user" },
323
+ { path: path.join(os.homedir(), ".agents", "skills"), source: "user" },
324
+ ...collectInstalledPackageSkillPaths(cwd),
325
+ ...collectSettingsPackageSkillPaths(cwd),
326
+ ...extractSkillPathsFromPackageRoot(cwd, "project-package"),
327
+ ...collectSettingsSkillPaths(cwd)];
328
+
329
+
330
+ const deduped = new Map();
331
+ for (const entry of skillPaths) {
332
+ const resolvedPath = path.resolve(entry.path);
333
+ if (!deduped.has(resolvedPath)) {
334
+ deduped.set(resolvedPath, { path: resolvedPath, source: entry.source });
335
+ }
336
+ }
337
+ return [...deduped.values()];
338
+ }
339
+
340
+ function inferSkillSource(filePath, cwd, sourceHint) {
341
+ if (sourceHint) return sourceHint;
342
+
343
+ const projectConfigRoot = path.resolve(cwd, CONFIG_DIR);
344
+ const projectSkillsRoot = path.resolve(cwd, CONFIG_DIR, "skills");
345
+ const projectPackagesRoot = path.resolve(cwd, CONFIG_DIR, "npm", "node_modules");
346
+ const projectAgentsRoot = path.resolve(cwd, ".agents");
347
+ const userSkillsRoot = path.resolve(AGENT_DIR, "skills");
348
+ const userPackagesRoot = path.resolve(AGENT_DIR, "npm", "node_modules");
349
+ const userAgentsRoot = path.resolve(os.homedir(), ".agents");
350
+
351
+ if (isWithinPath(filePath, projectPackagesRoot)) return "project-package";
352
+ if (isWithinPath(filePath, projectSkillsRoot) || isWithinPath(filePath, projectAgentsRoot)) return "project";
353
+ if (isWithinPath(filePath, projectConfigRoot)) return "project-settings";
354
+
355
+ if (isWithinPath(filePath, userPackagesRoot)) return "user-package";
356
+ if (isWithinPath(filePath, userSkillsRoot) || isWithinPath(filePath, userAgentsRoot)) return "user";
357
+ if (isWithinPath(filePath, AGENT_DIR)) return "user-settings";
358
+
359
+ const globalRoot = getGlobalNpmRoot();
360
+ if (globalRoot && isWithinPath(filePath, globalRoot)) return "user-package";
361
+
362
+ return "unknown";
363
+ }
364
+
365
+ function chooseHigherPrioritySkill(existing, candidate) {
366
+ if (!existing) return candidate;
367
+ const existingPriority = SOURCE_PRIORITY[existing.source] ?? 0;
368
+ const candidatePriority = SOURCE_PRIORITY[candidate.source] ?? 0;
369
+ if (candidatePriority > existingPriority) return candidate;
370
+ if (candidatePriority < existingPriority) return existing;
371
+ return candidate.order < existing.order ? candidate : existing;
372
+ }
373
+
374
+ function maybeReadSkillDescription(filePath) {
375
+ try {
376
+ const content = fs.readFileSync(filePath, "utf-8");
377
+ const normalized = content.replace(/\r\n/g, "\n");
378
+ if (!normalized.startsWith("---")) return undefined;
379
+
380
+ const endIndex = normalized.indexOf("\n---", 3);
381
+ if (endIndex === -1) return undefined;
382
+
383
+ const frontmatter = normalized.slice(3, endIndex).trim();
384
+ const match = frontmatter.match(/^description:\s*(.+)$/m);
385
+ if (!match) return undefined;
386
+ return match[1]?.trim().replace(/^['\"]|['\"]$/g, "");
387
+ } catch {
388
+ // Description parsing is best-effort metadata extraction.
389
+ return undefined;
390
+ }
391
+ }
392
+
393
+ function collectFilesystemSkills(cwd, skillPaths) {
394
+ const entries = [];
395
+ const seen = new Set();
396
+ let order = 0;
397
+
398
+ const pushEntry = (name, filePath, sourceHint) => {
399
+ const resolvedFile = path.resolve(filePath);
400
+ if (seen.has(resolvedFile)) return;
401
+ if (!fs.existsSync(resolvedFile)) return;
402
+ seen.add(resolvedFile);
403
+ entries.push({
404
+ name,
405
+ filePath: resolvedFile,
406
+ source: inferSkillSource(resolvedFile, cwd, sourceHint),
407
+ description: maybeReadSkillDescription(resolvedFile),
408
+ order: order++
409
+ });
410
+ };
411
+
412
+ for (const skillPath of skillPaths) {
413
+ if (!fs.existsSync(skillPath.path)) continue;
414
+
415
+ let stat;
416
+ try {
417
+ stat = fs.statSync(skillPath.path);
418
+ } catch {
419
+ continue;
420
+ }
421
+
422
+ if (stat.isFile()) {
423
+ const fileName = path.basename(skillPath.path);
424
+ if (!fileName.toLowerCase().endsWith(".md")) continue;
425
+ const skillName = fileName.toLowerCase() === "skill.md" ?
426
+ path.basename(path.dirname(skillPath.path)) :
427
+ path.basename(fileName, path.extname(fileName));
428
+ pushEntry(skillName, skillPath.path, skillPath.source);
429
+ continue;
430
+ }
431
+
432
+ if (!stat.isDirectory()) continue;
433
+
434
+ const rootSkillFile = path.join(skillPath.path, "SKILL.md");
435
+ if (fs.existsSync(rootSkillFile)) {
436
+ pushEntry(path.basename(skillPath.path), rootSkillFile, skillPath.source);
437
+ }
438
+
439
+ let childEntries;
440
+ try {
441
+ childEntries = fs.readdirSync(skillPath.path, { withFileTypes: true });
442
+ } catch {
443
+ continue;
444
+ }
445
+
446
+ for (const child of childEntries) {
447
+ if (child.name.startsWith(".")) continue;
448
+ const childPath = path.join(skillPath.path, child.name);
449
+ if (child.isDirectory() || child.isSymbolicLink()) {
450
+ const nestedSkillPath = path.join(childPath, "SKILL.md");
451
+ if (fs.existsSync(nestedSkillPath)) {
452
+ pushEntry(child.name, nestedSkillPath, skillPath.source);
453
+ }
454
+ continue;
455
+ }
456
+ if (child.isFile() && child.name.toLowerCase().endsWith(".md")) {
457
+ pushEntry(path.basename(child.name, path.extname(child.name)), childPath, skillPath.source);
458
+ }
459
+ }
460
+ }
461
+
462
+ return entries;
463
+ }
464
+
465
+ function getCachedSkills(cwd) {
466
+ const now = Date.now();
467
+ if (loadSkillsCache && loadSkillsCache.cwd === cwd && now - loadSkillsCache.timestamp < LOAD_SKILLS_CACHE_TTL_MS) {
468
+ return loadSkillsCache.skills;
469
+ }
470
+
471
+ const skillPaths = buildSkillPaths(cwd);
472
+ const loaded = collectFilesystemSkills(cwd, skillPaths);
473
+ const dedupedByName = new Map();
474
+
475
+ for (const entry of loaded) {
476
+ const current = dedupedByName.get(entry.name);
477
+ dedupedByName.set(entry.name, chooseHigherPrioritySkill(current, entry));
478
+ }
479
+
480
+ const skills = [...dedupedByName.values()].sort((a, b) => a.order - b.order);
481
+ loadSkillsCache = { cwd, skills, timestamp: now };
482
+ return skills;
483
+ }
484
+
485
+ function resolveSkillPath(
486
+ skillName,
487
+ cwd)
488
+ {
489
+ const skills = getCachedSkills(cwd);
490
+ const skill = skills.find((s) => s.name === skillName);
491
+ if (!skill) return undefined;
492
+ return { path: skill.filePath, source: skill.source };
493
+ }
494
+
495
+ function readSkill(
496
+ skillName,
497
+ skillPath,
498
+ source)
499
+ {
500
+ try {
501
+ const stat = fs.statSync(skillPath);
502
+ const cached = skillCache.get(skillPath);
503
+ if (cached && cached.mtime === stat.mtimeMs) {
504
+ return cached.skill;
505
+ }
506
+
507
+ const raw = fs.readFileSync(skillPath, "utf-8");
508
+ const content = stripSkillFrontmatter(raw);
509
+ const skill = {
510
+ name: skillName,
511
+ path: skillPath,
512
+ content,
513
+ source
514
+ };
515
+
516
+ skillCache.set(skillPath, { mtime: stat.mtimeMs, skill });
517
+ if (skillCache.size > MAX_CACHE_SIZE) {
518
+ const firstKey = skillCache.keys().next().value;
519
+ if (firstKey) skillCache.delete(firstKey);
520
+ }
521
+
522
+ return skill;
523
+ } catch {
524
+ // Treat unreadable skill files as unresolved so callers can surface as missing.
525
+ return undefined;
526
+ }
527
+ }
528
+
529
+ function resolveSkills(
530
+ skillNames,
531
+ cwd)
532
+ {
533
+ const resolved = [];
534
+ const missing = [];
535
+
536
+ for (const name of skillNames) {
537
+ const trimmed = name.trim();
538
+ if (!trimmed) continue;
539
+ if (trimmed === SUBAGENT_ORCHESTRATION_SKILL) {
540
+ missing.push(trimmed);
541
+ continue;
542
+ }
543
+
544
+ const location = resolveSkillPath(trimmed, cwd);
545
+ if (!location) {
546
+ missing.push(trimmed);
547
+ continue;
548
+ }
549
+
550
+ const skill = readSkill(trimmed, location.path, location.source);
551
+ if (skill) {
552
+ resolved.push(skill);
553
+ } else {
554
+ missing.push(trimmed);
555
+ }
556
+ }
557
+
558
+ return { resolved, missing };
559
+ }
560
+
561
+ function resolveSkillsWithFallback(
562
+ skillNames,
563
+ primaryCwd,
564
+ fallbackCwd)
565
+ {
566
+ const primary = resolveSkills(skillNames, primaryCwd);
567
+ if (!fallbackCwd || primary.missing.length === 0) return primary;
568
+ if (path.resolve(primaryCwd) === path.resolve(fallbackCwd)) return primary;
569
+
570
+ const fallback = resolveSkills(primary.missing, fallbackCwd);
571
+ return {
572
+ resolved: [...primary.resolved, ...fallback.resolved],
573
+ missing: fallback.missing
574
+ };
575
+ }
576
+
577
+ function buildSkillInjection(skills) {
578
+ if (skills.length === 0) return "";
579
+
580
+ return skills.
581
+ map((s) => `<skill name="${s.name}">\n${s.content}\n</skill>`).
582
+ join("\n\n");
583
+ }
584
+
585
+ function normalizeSkillInput(
586
+ input)
587
+ {
588
+ if (input === false) return false;
589
+ if (input === true || input === undefined) return undefined;
590
+ if (Array.isArray(input)) {
591
+ return [...new Set(input.map((s) => s.trim()).filter((s) => s.length > 0))];
592
+ }
593
+ // Guard against JSON-encoded arrays arriving as strings (e.g. '["a","b"]').
594
+ // Models sometimes serialise the skill parameter as a JSON string instead of
595
+ // a native array, and naively splitting on "," would embed brackets/quotes
596
+ // into the skill names, causing resolution to silently fail.
597
+ const trimmed = input.trim();
598
+ if (trimmed.startsWith("[")) {
599
+ try {
600
+ const parsed = JSON.parse(trimmed);
601
+ if (Array.isArray(parsed)) {
602
+ return normalizeSkillInput(parsed);
603
+ }
604
+ } catch {
605
+
606
+ // Not valid JSON – fall through to comma-split
607
+ }}
608
+ return [...new Set(input.split(",").map((s) => s.trim()).filter((s) => s.length > 0))];
609
+ }
610
+
611
+ function discoverAvailableSkills(cwd)
612
+
613
+
614
+
615
+ {
616
+ const skills = getCachedSkills(cwd);
617
+ return skills.
618
+ filter((s) => s.name !== SUBAGENT_ORCHESTRATION_SKILL).
619
+ map((s) => ({
620
+ name: s.name,
621
+ source: s.source,
622
+ description: s.description
623
+ })).
624
+ sort((a, b) => a.name.localeCompare(b.name));
625
+ }
626
+
627
+ function clearSkillCache() {
628
+ skillCache.clear();
629
+ loadSkillsCache = null;
630
+ } /* v9-27d30b81130fc85f */
pip-tmp/jiti/background-async-execution.0e72180b.mjs ADDED
@@ -0,0 +1,578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.executeAsyncChain = executeAsyncChain;exports.executeAsyncSingle = executeAsyncSingle;exports.formatAsyncStartedMessage = formatAsyncStartedMessage;exports.isAsyncAvailable = isAsyncAvailable;
2
+
3
+
4
+
5
+ var _nodeChild_process = await jitiImport("node:child_process");
6
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
7
+
8
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
9
+ var _nodeUrl = await jitiImport("node:url");
10
+ var _nodeModule = await jitiImport("node:module");
11
+
12
+
13
+ var _piArgs = await jitiImport("../shared/pi-args.ts");
14
+ var _singleOutput = await jitiImport("../shared/single-output.ts");
15
+ var _settings = await jitiImport("../../shared/settings.ts");
16
+
17
+ var _piSpawn = await jitiImport("../shared/pi-spawn.ts");
18
+ var _skills = await jitiImport("../../agents/skills.ts");
19
+ var _utils = await jitiImport("../../shared/utils.ts");
20
+ var _modelFallback = await jitiImport("../shared/model-fallback.ts");
21
+ var _worktree = await jitiImport("../shared/worktree.ts");
22
+ var _types = await jitiImport("../../shared/types.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /**
23
+ * Async execution logic for subagent tool
24
+ */
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+ const _require = (0, _nodeModule.createRequire)("file:///home/edwin/.config/nvm/versions/node/v22.20.0/lib/node_modules/pi-subagents/src/runs/background/async-execution.ts");
37
+ const piPackageRoot = (0, _piSpawn.resolvePiPackageRoot)();
38
+ const jitiCliPath = (() => {
39
+ const candidates = [
40
+ () => path.join(path.dirname(_require.resolve("jiti/package.json")), "lib/jiti-cli.mjs"),
41
+ () => path.join(path.dirname(_require.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs"),
42
+ () => {
43
+ const piEntry = fs.realpathSync(process.argv[1]);
44
+ const piRequire = (0, _nodeModule.createRequire)(piEntry);
45
+ return path.join(path.dirname(piRequire.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs");
46
+ }];
47
+
48
+ for (const candidate of candidates) {
49
+ try {
50
+ const p = candidate();
51
+ if (fs.existsSync(p)) return p;
52
+ } catch {
53
+
54
+ // Candidate not available in this install, continue probing.
55
+ }}
56
+ return undefined;
57
+ })();
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+ function formatAsyncStartedMessage(headline) {
121
+ return [
122
+ headline,
123
+ "",
124
+ "The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
125
+ "If you have independent work, continue that work. If you have nothing else to do until the async result arrives, end your turn now; Pi will deliver the completion when the run finishes.",
126
+ "Use subagent({ action: \"status\", id: \"...\" }) when you need the current status/result, or to inspect a blocked/stale run. Do not poll just to wait."].
127
+ join("\n");
128
+ }
129
+
130
+ /**
131
+ * Check if jiti is available for async execution
132
+ */
133
+ function isAsyncAvailable() {
134
+ return jitiCliPath !== undefined;
135
+ }
136
+
137
+ /**
138
+ * Spawn the async runner process
139
+ */
140
+ function spawnRunner(cfg, suffix, cwd) {
141
+ if (!jitiCliPath) {
142
+ return { error: "jiti for TypeScript execution could not be found" };
143
+ }
144
+
145
+ try {
146
+ const cwdStats = fs.statSync(cwd);
147
+ if (!cwdStats.isDirectory()) {
148
+ return { error: `cwd is not a directory: ${cwd}` };
149
+ }
150
+ } catch {
151
+ return { error: `cwd does not exist: ${cwd}` };
152
+ }
153
+
154
+ fs.mkdirSync(_types.TEMP_ROOT_DIR, { recursive: true });
155
+ const cfgPath = (0, _types.getAsyncConfigPath)(suffix);
156
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg));
157
+ const runner = path.join(path.dirname((0, _nodeUrl.fileURLToPath)("file:///home/edwin/.config/nvm/versions/node/v22.20.0/lib/node_modules/pi-subagents/src/runs/background/async-execution.ts")), "subagent-runner.ts");
158
+
159
+ const proc = (0, _nodeChild_process.spawn)(process.execPath, [jitiCliPath, runner, cfgPath], {
160
+ cwd,
161
+ detached: true,
162
+ stdio: "ignore",
163
+ windowsHide: true
164
+ });
165
+ proc.on("error", (error) => {
166
+ console.error(`[pi-subagents] async spawn failed: ${error.message}`);
167
+ });
168
+ if (typeof proc.pid !== "number") {
169
+ return { error: `async runner did not produce a pid for cwd: ${cwd}` };
170
+ }
171
+ proc.unref();
172
+ return { pid: proc.pid };
173
+ }
174
+
175
+ function formatAsyncStartError(mode, message) {
176
+ return {
177
+ content: [{ type: "text", text: message }],
178
+ isError: true,
179
+ details: { mode, results: [] }
180
+ };
181
+ }
182
+
183
+ const UNAVAILABLE_SUBAGENT_SKILL_ERROR = "Skills not found: pi-subagents";
184
+
185
+ class UnavailableSubagentSkillError extends Error {}
186
+ class AsyncStartValidationError extends Error {}
187
+
188
+ /**
189
+ * Execute a chain asynchronously
190
+ */
191
+ function executeAsyncChain(
192
+ id,
193
+ params)
194
+ {
195
+ const {
196
+ chain,
197
+ agents,
198
+ ctx,
199
+ cwd,
200
+ maxOutput,
201
+ artifactsDir,
202
+ artifactConfig,
203
+ shareEnabled,
204
+ sessionRoot,
205
+ sessionFilesByFlatIndex,
206
+ maxSubagentDepth,
207
+ worktreeSetupHook,
208
+ worktreeSetupHookTimeoutMs,
209
+ controlConfig,
210
+ controlIntercomTarget,
211
+ childIntercomTarget
212
+ } = params;
213
+ const resultMode = params.resultMode ?? "chain";
214
+ const chainSkills = params.chainSkills ?? [];
215
+ const availableModels = params.availableModels;
216
+ const runnerCwd = (0, _utils.resolveChildCwd)(ctx.cwd, cwd);
217
+ const firstStep = chain[0];
218
+ const originalTask = params.task ?? (firstStep ?
219
+ (0, _settings.isParallelStep)(firstStep) ? firstStep.parallel[0]?.task : firstStep.task :
220
+ undefined);
221
+
222
+ for (const s of chain) {
223
+ const stepAgents = (0, _settings.isParallelStep)(s) ?
224
+ s.parallel.map((t) => t.agent) :
225
+ [s.agent];
226
+ for (const agentName of stepAgents) {
227
+ if (!agents.find((x) => x.name === agentName)) {
228
+ return {
229
+ content: [{ type: "text", text: `Unknown agent: ${agentName}` }],
230
+ isError: true,
231
+ details: { mode: resultMode, results: [] }
232
+ };
233
+ }
234
+ }
235
+ }
236
+
237
+ const asyncDir = path.join(_types.ASYNC_DIR, id);
238
+ try {
239
+ fs.mkdirSync(asyncDir, { recursive: true });
240
+ } catch (error) {
241
+ const message = error instanceof Error ? error.message : String(error);
242
+ return {
243
+ content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
244
+ isError: true,
245
+ details: { mode: resultMode, results: [] }
246
+ };
247
+ }
248
+
249
+ let progressInstructionCreated = false;
250
+ const buildStepOverrides = (s) => {
251
+ const stepSkillInput = (0, _skills.normalizeSkillInput)(s.skill);
252
+ return {
253
+ ...(s.output !== undefined ? { output: s.output } : {}),
254
+ ...(s.outputMode !== undefined ? { outputMode: s.outputMode } : {}),
255
+ ...(s.reads !== undefined ? { reads: s.reads } : {}),
256
+ ...(s.progress !== undefined ? { progress: s.progress } : {}),
257
+ ...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
258
+ ...(s.model ? { model: s.model } : {})
259
+ };
260
+ };
261
+ const buildSeqStep = (s, sessionFile, behaviorCwd, progressPrecreated = false, resolvedBehavior) => {
262
+ const a = agents.find((x) => x.name === s.agent);
263
+ const stepCwd = (0, _utils.resolveChildCwd)(runnerCwd, s.cwd);
264
+ const instructionCwd = behaviorCwd ?? stepCwd;
265
+ const behavior = (0, _settings.suppressProgressForReadOnlyTask)(resolvedBehavior ?? (0, _settings.resolveStepBehavior)(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
266
+ const skillNames = behavior.skills === false ? [] : behavior.skills;
267
+ const { resolved: resolvedSkills, missing: missingSkills } = (0, _skills.resolveSkillsWithFallback)(skillNames, stepCwd, ctx.cwd);
268
+ if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
269
+
270
+ let systemPrompt = a.systemPrompt?.trim() ?? "";
271
+ if (resolvedSkills.length > 0) {
272
+ const injection = (0, _skills.buildSkillInjection)(resolvedSkills);
273
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
274
+ }
275
+
276
+ const readInstructions = (0, _settings.buildChainInstructions)({ ...behavior, output: false, progress: false }, instructionCwd, false);
277
+ const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
278
+ if (behavior.progress) progressInstructionCreated = true;
279
+ const progressInstructions = (0, _settings.buildChainInstructions)({ ...behavior, output: false, reads: false }, runnerCwd, isFirstProgressAgent);
280
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(behavior.output, ctx.cwd, instructionCwd);
281
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(behavior.outputMode, outputPath, `Async step (${s.agent})`);
282
+ if (validationError) throw new AsyncStartValidationError(validationError);
283
+ const task = (0, _singleOutput.injectSingleOutputInstruction)(`${readInstructions.prefix}${s.task ?? "{previous}"}${progressInstructions.suffix}`, outputPath);
284
+
285
+ const primaryModel = (0, _modelFallback.resolveModelCandidate)(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
286
+ return {
287
+ agent: s.agent,
288
+ task,
289
+ cwd: stepCwd,
290
+ model: (0, _piArgs.applyThinkingSuffix)(primaryModel, a.thinking),
291
+ modelCandidates: (0, _modelFallback.buildModelCandidates)(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
292
+ (0, _piArgs.applyThinkingSuffix)(candidate, a.thinking)
293
+ ),
294
+ tools: a.tools,
295
+ extensions: a.extensions,
296
+ mcpDirectTools: a.mcpDirectTools,
297
+ systemPrompt,
298
+ systemPromptMode: a.systemPromptMode,
299
+ inheritProjectContext: a.inheritProjectContext,
300
+ inheritSkills: a.inheritSkills,
301
+ skills: resolvedSkills.map((r) => r.name),
302
+ outputPath,
303
+ outputMode: behavior.outputMode,
304
+ sessionFile,
305
+ maxSubagentDepth: (0, _types.resolveChildMaxSubagentDepth)(maxSubagentDepth, a.maxSubagentDepth)
306
+ };
307
+ };
308
+
309
+ let flatStepIndex = 0;
310
+ const nextSessionFile = () => {
311
+ const sessionFile = sessionFilesByFlatIndex?.[flatStepIndex];
312
+ flatStepIndex++;
313
+ return sessionFile;
314
+ };
315
+
316
+ let steps;
317
+ try {
318
+ steps = chain.map((s, stepIndex) => {
319
+ if ((0, _settings.isParallelStep)(s)) {
320
+ const parallelBehaviors = s.parallel.map((task) => {
321
+ const agent = agents.find((candidate) => candidate.name === task.agent);
322
+ return (0, _settings.suppressProgressForReadOnlyTask)((0, _settings.resolveStepBehavior)(agent, buildStepOverrides(task), chainSkills), task.task, originalTask);
323
+ });
324
+ const progressPrecreated = parallelBehaviors.some((behavior) => behavior.progress);
325
+ if (progressPrecreated) {
326
+ if (!s.worktree) (0, _settings.writeInitialProgressFile)(runnerCwd);
327
+ progressInstructionCreated = true;
328
+ }
329
+ return {
330
+ parallel: s.parallel.map((t, taskIndex) => {
331
+ let behaviorCwd;
332
+ if (s.worktree) {
333
+ try {
334
+ behaviorCwd = (0, _worktree.resolveExpectedWorktreeAgentCwd)(runnerCwd, `${id}-s${stepIndex}`, taskIndex);
335
+ } catch {
336
+ behaviorCwd = undefined;
337
+ }
338
+ }
339
+ return buildSeqStep(t, nextSessionFile(), behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex]);
340
+ }),
341
+ concurrency: s.concurrency,
342
+ failFast: s.failFast,
343
+ worktree: s.worktree
344
+ };
345
+ }
346
+ return buildSeqStep(s, nextSessionFile());
347
+ });
348
+ } catch (error) {
349
+ if (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError) return formatAsyncStartError(resultMode, error.message);
350
+ throw error;
351
+ }
352
+ let childTargetIndex = 0;
353
+ const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
354
+ if ("parallel" in step) {
355
+ return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
356
+ }
357
+ return [childIntercomTarget(step.agent, childTargetIndex++)];
358
+ }) : undefined;
359
+
360
+ let spawnResult = {};
361
+ try {
362
+ spawnResult = spawnRunner(
363
+ {
364
+ id,
365
+ steps,
366
+ resultPath: path.join(_types.RESULTS_DIR, `${id}.json`),
367
+ cwd: runnerCwd,
368
+ placeholder: "{previous}",
369
+ maxOutput,
370
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
371
+ artifactConfig,
372
+ share: shareEnabled,
373
+ sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
374
+ asyncDir,
375
+ sessionId: ctx.currentSessionId,
376
+ piPackageRoot,
377
+ piArgv1: process.argv[1],
378
+ worktreeSetupHook,
379
+ worktreeSetupHookTimeoutMs,
380
+ controlConfig,
381
+ controlIntercomTarget,
382
+ childIntercomTargets,
383
+ resultMode
384
+ },
385
+ id,
386
+ runnerCwd
387
+ );
388
+ } catch (error) {
389
+ const message = error instanceof Error ? error.message : String(error);
390
+ return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${message}`);
391
+ }
392
+
393
+ if (spawnResult.error) {
394
+ return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${spawnResult.error}`);
395
+ }
396
+
397
+ if (spawnResult.pid) {
398
+ const firstStep = chain[0];
399
+ const firstAgents = (0, _settings.isParallelStep)(firstStep) ?
400
+ firstStep.parallel.map((t) => t.agent) :
401
+ [firstStep.agent];
402
+ const parallelGroups = [];
403
+ const flatAgents = [];
404
+ let flatStepStart = 0;
405
+ for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
406
+ const step = chain[stepIndex];
407
+ if ((0, _settings.isParallelStep)(step)) {
408
+ parallelGroups.push({ start: flatStepStart, count: step.parallel.length, stepIndex });
409
+ flatAgents.push(...step.parallel.map((task) => task.agent));
410
+ flatStepStart += step.parallel.length;
411
+ } else {
412
+ flatAgents.push(step.agent);
413
+ flatStepStart++;
414
+ }
415
+ }
416
+ ctx.pi.events.emit(_types.SUBAGENT_ASYNC_STARTED_EVENT, {
417
+ id,
418
+ pid: spawnResult.pid,
419
+ sessionId: ctx.currentSessionId,
420
+ mode: resultMode,
421
+ agent: firstAgents[0],
422
+ agents: flatAgents,
423
+ task: (0, _settings.isParallelStep)(firstStep) ?
424
+ firstStep.parallel[0]?.task?.slice(0, 50) :
425
+ firstStep.task?.slice(0, 50),
426
+ chain: chain.map((s) =>
427
+ (0, _settings.isParallelStep)(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : s.agent
428
+ ),
429
+ chainStepCount: chain.length,
430
+ parallelGroups,
431
+ cwd: runnerCwd,
432
+ asyncDir
433
+ });
434
+ }
435
+
436
+ const chainDesc = chain.
437
+ map((s) =>
438
+ (0, _settings.isParallelStep)(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : s.agent
439
+ ).
440
+ join(" -> ");
441
+
442
+ return {
443
+ content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
444
+ details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir }
445
+ };
446
+ }
447
+
448
+ /**
449
+ * Execute a single agent asynchronously
450
+ */
451
+ function executeAsyncSingle(
452
+ id,
453
+ params)
454
+ {
455
+ const {
456
+ agent,
457
+ agentConfig,
458
+ ctx,
459
+ cwd,
460
+ maxOutput,
461
+ artifactsDir,
462
+ artifactConfig,
463
+ shareEnabled,
464
+ sessionRoot,
465
+ sessionFile,
466
+ maxSubagentDepth,
467
+ worktreeSetupHook,
468
+ worktreeSetupHookTimeoutMs,
469
+ controlConfig,
470
+ controlIntercomTarget,
471
+ childIntercomTarget
472
+ } = params;
473
+ const task = params.task ?? "";
474
+ const runnerCwd = (0, _utils.resolveChildCwd)(ctx.cwd, cwd);
475
+ const skillNames = params.skills ?? agentConfig.skills ?? [];
476
+ const availableModels = params.availableModels;
477
+ const { resolved: resolvedSkills, missing: missingSkills } = (0, _skills.resolveSkillsWithFallback)(skillNames, runnerCwd, ctx.cwd);
478
+ if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
479
+ let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
480
+ if (resolvedSkills.length > 0) {
481
+ const injection = (0, _skills.buildSkillInjection)(resolvedSkills);
482
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
483
+ }
484
+
485
+ const asyncDir = path.join(_types.ASYNC_DIR, id);
486
+ try {
487
+ fs.mkdirSync(asyncDir, { recursive: true });
488
+ } catch (error) {
489
+ const message = error instanceof Error ? error.message : String(error);
490
+ return {
491
+ content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
492
+ isError: true,
493
+ details: { mode: "single", results: [] }
494
+ };
495
+ }
496
+
497
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(params.output, ctx.cwd, runnerCwd);
498
+ const outputMode = params.outputMode ?? "inline";
499
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(outputMode, outputPath, `Async single run (${agent})`);
500
+ if (validationError) return formatAsyncStartError("single", validationError);
501
+ const taskWithOutputInstruction = (0, _singleOutput.injectSingleOutputInstruction)(task, outputPath);
502
+ let spawnResult = {};
503
+ try {
504
+ spawnResult = spawnRunner(
505
+ {
506
+ id,
507
+ steps: [
508
+ {
509
+ agent,
510
+ task: taskWithOutputInstruction,
511
+ cwd: runnerCwd,
512
+ model: (0, _piArgs.applyThinkingSuffix)((0, _modelFallback.resolveModelCandidate)(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider), agentConfig.thinking),
513
+ modelCandidates: (0, _modelFallback.buildModelCandidates)(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
514
+ (0, _piArgs.applyThinkingSuffix)(candidate, agentConfig.thinking)
515
+ ),
516
+ tools: agentConfig.tools,
517
+ extensions: agentConfig.extensions,
518
+ mcpDirectTools: agentConfig.mcpDirectTools,
519
+ systemPrompt,
520
+ systemPromptMode: agentConfig.systemPromptMode,
521
+ inheritProjectContext: agentConfig.inheritProjectContext,
522
+ inheritSkills: agentConfig.inheritSkills,
523
+ skills: resolvedSkills.map((r) => r.name),
524
+ outputPath,
525
+ outputMode,
526
+ sessionFile,
527
+ maxSubagentDepth: (0, _types.resolveChildMaxSubagentDepth)(maxSubagentDepth, agentConfig.maxSubagentDepth)
528
+ }],
529
+
530
+ resultPath: path.join(_types.RESULTS_DIR, `${id}.json`),
531
+ cwd: runnerCwd,
532
+ placeholder: "{previous}",
533
+ maxOutput,
534
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
535
+ artifactConfig,
536
+ share: shareEnabled,
537
+ sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
538
+ asyncDir,
539
+ sessionId: ctx.currentSessionId,
540
+ piPackageRoot,
541
+ piArgv1: process.argv[1],
542
+ worktreeSetupHook,
543
+ worktreeSetupHookTimeoutMs,
544
+ controlConfig,
545
+ controlIntercomTarget,
546
+ childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
547
+ resultMode: "single"
548
+ },
549
+ id,
550
+ runnerCwd
551
+ );
552
+ } catch (error) {
553
+ const message = error instanceof Error ? error.message : String(error);
554
+ return formatAsyncStartError("single", `Failed to start async run '${id}': ${message}`);
555
+ }
556
+
557
+ if (spawnResult.error) {
558
+ return formatAsyncStartError("single", `Failed to start async run '${id}': ${spawnResult.error}`);
559
+ }
560
+
561
+ if (spawnResult.pid) {
562
+ ctx.pi.events.emit(_types.SUBAGENT_ASYNC_STARTED_EVENT, {
563
+ id,
564
+ pid: spawnResult.pid,
565
+ sessionId: ctx.currentSessionId,
566
+ mode: "single",
567
+ agent,
568
+ task: task?.slice(0, 50),
569
+ cwd: runnerCwd,
570
+ asyncDir
571
+ });
572
+ }
573
+
574
+ return {
575
+ content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`) }],
576
+ details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir }
577
+ };
578
+ } /* v9-4e5f8b1d7a19d9c5 */
pip-tmp/jiti/background-async-job-tracker.6fb217e9.mjs ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.createAsyncJobTracker = createAsyncJobTracker;
2
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
3
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
4
+ var _render = await jitiImport("../../tui/render.ts");
5
+ var _subagentControl = await jitiImport("../shared/subagent-control.ts");
6
+ var _types = await jitiImport("../../shared/types.ts");
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ var _utils = await jitiImport("../../shared/utils.ts");
17
+ var _parallelGroups = await jitiImport("./parallel-groups.ts");
18
+ var _staleRunReconciler = await jitiImport("./stale-run-reconciler.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {})
29
+
30
+
31
+
32
+
33
+ {
34
+ const completionRetentionMs = options.completionRetentionMs ?? 10000;
35
+ const pollIntervalMs = options.pollIntervalMs ?? _types.POLL_INTERVAL_MS;
36
+ const resultsDir = options.resultsDir ?? _types.RESULTS_DIR;
37
+ const rerenderWidget = (ctx, jobs = Array.from(state.asyncJobs.values())) => {
38
+ (0, _render.renderWidget)(ctx, jobs);
39
+ ctx.ui.requestRender?.();
40
+ };
41
+ const scheduleCleanup = (asyncId) => {
42
+ const existingTimer = state.cleanupTimers.get(asyncId);
43
+ if (existingTimer) clearTimeout(existingTimer);
44
+ const timer = setTimeout(() => {
45
+ state.cleanupTimers.delete(asyncId);
46
+ state.asyncJobs.delete(asyncId);
47
+ if (state.lastUiContext) {
48
+ rerenderWidget(state.lastUiContext);
49
+ }
50
+ }, completionRetentionMs);
51
+ state.cleanupTimers.set(asyncId, timer);
52
+ };
53
+ const emitNewControlEvents = (job) => {
54
+ const eventsPath = path.join(job.asyncDir, "events.jsonl");
55
+ let fd;
56
+ try {
57
+ fd = fs.openSync(eventsPath, "r");
58
+ } catch (error) {
59
+ if (error.code === "ENOENT") return;
60
+ console.error(`Failed to open async control events for '${job.asyncDir}':`, error);
61
+ return;
62
+ }
63
+ try {
64
+ const stat = fs.fstatSync(fd);
65
+ const cursor = stat.size < (job.controlEventCursor ?? 0) ? 0 : job.controlEventCursor ?? 0;
66
+ if (stat.size <= cursor) return;
67
+ const buffer = Buffer.alloc(stat.size - cursor);
68
+ fs.readSync(fd, buffer, 0, buffer.length, cursor);
69
+ const lastNewline = buffer.lastIndexOf(0x0a);
70
+ if (lastNewline === -1) return;
71
+ job.controlEventCursor = cursor + lastNewline + 1;
72
+ for (const line of buffer.subarray(0, lastNewline).toString("utf-8").split("\n")) {
73
+ if (!line.trim()) continue;
74
+ let parsed;
75
+ try {
76
+ parsed = JSON.parse(line);
77
+ } catch (error) {
78
+ console.error(`Ignoring malformed async control event in '${eventsPath}':`, error);
79
+ continue;
80
+ }
81
+ if (!parsed || typeof parsed !== "object" || parsed.type !== "subagent.control") continue;
82
+ const record = parsed;
83
+ if (!record.event || !Array.isArray(record.channels)) continue;
84
+ const payload = {
85
+ event: record.event,
86
+ source: "async",
87
+ asyncDir: job.asyncDir,
88
+ childIntercomTarget: record.childIntercomTarget,
89
+ noticeText: record.noticeText ?? (0, _subagentControl.formatControlNoticeMessage)(record.event, record.childIntercomTarget)
90
+ };
91
+ if (record.channels.includes("event")) {
92
+ pi.events.emit(_types.SUBAGENT_CONTROL_EVENT, payload);
93
+ }
94
+ if (record.event.type !== "active_long_running" && record.channels.includes("intercom") && record.intercom?.to && record.intercom.message) {
95
+ pi.events.emit(_types.SUBAGENT_CONTROL_INTERCOM_EVENT, {
96
+ ...payload,
97
+ to: record.intercom.to,
98
+ message: record.intercom.message
99
+ });
100
+ }
101
+ }
102
+ } catch (error) {
103
+ console.error(`Failed to read async control events for '${job.asyncDir}':`, error);
104
+ } finally {
105
+ fs.closeSync(fd);
106
+ }
107
+ };
108
+
109
+ const ensurePoller = () => {
110
+ if (state.poller) return;
111
+ state.poller = setInterval(() => {
112
+ if (state.asyncJobs.size === 0) {
113
+ if (state.lastUiContext?.hasUI) rerenderWidget(state.lastUiContext, []);
114
+ if (state.poller) {
115
+ clearInterval(state.poller);
116
+ state.poller = null;
117
+ }
118
+ return;
119
+ }
120
+
121
+ for (const job of state.asyncJobs.values()) {
122
+ try {
123
+ emitNewControlEvents(job);
124
+ const reconciliation = (0, _staleRunReconciler.reconcileAsyncRun)(job.asyncDir, {
125
+ resultsDir,
126
+ kill: options.kill,
127
+ now: options.now,
128
+ startedRun: {
129
+ runId: job.asyncId,
130
+ pid: job.pid,
131
+ sessionId: job.sessionId,
132
+ mode: job.mode,
133
+ agents: job.agents,
134
+ chainStepCount: job.chainStepCount,
135
+ parallelGroups: job.parallelGroups,
136
+ startedAt: job.startedAt,
137
+ sessionFile: job.sessionFile
138
+ }
139
+ });
140
+ const status = reconciliation.status ?? (0, _utils.readStatus)(job.asyncDir);
141
+ if (status) {
142
+ const previousStatus = job.status;
143
+ job.status = status.state;
144
+ job.sessionId = status.sessionId ?? job.sessionId;
145
+ job.activityState = status.activityState;
146
+ job.lastActivityAt = status.lastActivityAt ?? job.lastActivityAt;
147
+ job.currentTool = status.currentTool;
148
+ job.currentToolStartedAt = status.currentToolStartedAt;
149
+ job.currentPath = status.currentPath;
150
+ job.turnCount = status.turnCount ?? job.turnCount;
151
+ job.toolCount = status.toolCount ?? job.toolCount;
152
+ job.mode = status.mode;
153
+ job.currentStep = status.currentStep ?? job.currentStep;
154
+ job.chainStepCount = status.chainStepCount ?? job.chainStepCount;
155
+ job.startedAt = status.startedAt ?? job.startedAt;
156
+ job.updatedAt = status.lastUpdate ?? Date.now();
157
+ if (status.steps?.length) {
158
+ const groups = (0, _parallelGroups.normalizeParallelGroups)(status.parallelGroups, status.steps.length, status.chainStepCount ?? status.steps.length);
159
+ job.parallelGroups = groups.length ? groups : job.parallelGroups;
160
+ job.hasParallelGroups = groups.length > 0 || job.hasParallelGroups;
161
+ const activeGroup = status.currentStep !== undefined ?
162
+ groups.find((group) => status.currentStep >= group.start && status.currentStep < group.start + group.count) :
163
+ undefined;
164
+ const visibleSteps = activeGroup ?
165
+ status.steps.slice(activeGroup.start, activeGroup.start + activeGroup.count).map((step, index) => ({ ...step, index: activeGroup.start + index })) :
166
+ status.steps.map((step, index) => ({ ...step, index }));
167
+ job.activeParallelGroup = Boolean(activeGroup);
168
+ job.agents = visibleSteps.map((step) => step.agent);
169
+ job.steps = visibleSteps;
170
+ job.stepsTotal = visibleSteps.length;
171
+ job.runningSteps = visibleSteps.filter((step) => step.status === "running").length;
172
+ job.completedSteps = visibleSteps.filter((step) => step.status === "complete" || step.status === "completed").length;
173
+ if (status.state === "complete") job.completedSteps = visibleSteps.length;
174
+ }
175
+ job.sessionDir = status.sessionDir ?? job.sessionDir;
176
+ job.outputFile = status.outputFile ?? job.outputFile;
177
+ job.totalTokens = status.totalTokens ?? job.totalTokens;
178
+ job.sessionFile = status.sessionFile ?? job.sessionFile;
179
+ if ((job.status === "complete" || job.status === "failed" || job.status === "paused") && (previousStatus !== job.status || !state.cleanupTimers.has(job.asyncId))) {
180
+ scheduleCleanup(job.asyncId);
181
+ }
182
+ continue;
183
+ }
184
+ job.status = job.status === "queued" ? "running" : job.status;
185
+ job.updatedAt = Date.now();
186
+ } catch (error) {
187
+ console.error(`Failed to read async status for '${job.asyncDir}':`, error);
188
+ job.status = "failed";
189
+ job.updatedAt = Date.now();
190
+ if (!state.cleanupTimers.has(job.asyncId)) {
191
+ scheduleCleanup(job.asyncId);
192
+ }
193
+ }
194
+ }
195
+
196
+ if (state.lastUiContext?.hasUI) rerenderWidget(state.lastUiContext);
197
+ }, pollIntervalMs);
198
+ state.poller.unref?.();
199
+ };
200
+
201
+ const handleStarted = (data) => {
202
+ const info = data;
203
+ if (!info.id) return;
204
+ const now = Date.now();
205
+ const asyncDir = info.asyncDir ?? path.join(asyncDirRoot, info.id);
206
+ const rawAgents = info.agents?.length ? info.agents : info.chain && info.chain.length > 0 ? info.chain : info.agent ? [info.agent] : undefined;
207
+ const validParallelGroups = (0, _parallelGroups.normalizeParallelGroups)(info.parallelGroups, Number.MAX_SAFE_INTEGER, info.chainStepCount ?? Number.MAX_SAFE_INTEGER);
208
+ const firstGroup = validParallelGroups.find((group) => group.start === 0);
209
+ const firstGroupCount = firstGroup?.count;
210
+ const agents = firstGroupCount && firstGroupCount > 0 ?
211
+ rawAgents?.slice(0, firstGroupCount) :
212
+ rawAgents;
213
+ state.asyncJobs.set(info.id, {
214
+ asyncId: info.id,
215
+ asyncDir,
216
+ status: "queued",
217
+ pid: typeof info.pid === "number" ? info.pid : undefined,
218
+ ...(typeof info.sessionId === "string" ? { sessionId: info.sessionId } : {}),
219
+ mode: info.mode ?? (info.chain ? "chain" : "single"),
220
+ agents,
221
+ chainStepCount: info.chainStepCount,
222
+ parallelGroups: validParallelGroups,
223
+ stepsTotal: firstGroupCount ?? agents?.length,
224
+ hasParallelGroups: validParallelGroups.length > 0,
225
+ activeParallelGroup: Boolean(firstGroupCount && firstGroupCount > 0),
226
+ startedAt: now,
227
+ updatedAt: now
228
+ });
229
+ ensurePoller();
230
+ if (state.lastUiContext) {
231
+ rerenderWidget(state.lastUiContext);
232
+ }
233
+ };
234
+
235
+ const handleComplete = (data) => {
236
+ const result = data;
237
+ const asyncId = result.id;
238
+ if (!asyncId) return;
239
+ const job = state.asyncJobs.get(asyncId);
240
+ if (job) {
241
+ job.status = result.success ? "complete" : "failed";
242
+ job.updatedAt = Date.now();
243
+ if (result.asyncDir) job.asyncDir = result.asyncDir;
244
+ }
245
+ if (state.lastUiContext) {
246
+ rerenderWidget(state.lastUiContext);
247
+ }
248
+ scheduleCleanup(asyncId);
249
+ };
250
+
251
+ const resetJobs = (ctx) => {
252
+ for (const timer of state.cleanupTimers.values()) {
253
+ clearTimeout(timer);
254
+ }
255
+ state.cleanupTimers.clear();
256
+ state.asyncJobs.clear();
257
+ state.foregroundControls?.clear();
258
+ state.lastForegroundControlId = null;
259
+ state.resultFileCoalescer.clear();
260
+ if (ctx?.hasUI) {
261
+ state.lastUiContext = ctx;
262
+ rerenderWidget(ctx, []);
263
+ }
264
+ };
265
+
266
+ return { ensurePoller, handleStarted, handleComplete, resetJobs };
267
+ } /* v9-8d628c367d3af3d8 */
pip-tmp/jiti/background-async-resume.8fb594f6.mjs ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildRevivedAsyncTask = buildRevivedAsyncTask;exports.resolveAsyncResumeTarget = resolveAsyncResumeTarget;exports.resolveAsyncRunLocation = resolveAsyncRunLocation;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+ var _types = await jitiImport("../../shared/types.ts");
4
+ var _intercomBridge = await jitiImport("../../intercom/intercom-bridge.ts");
5
+ var _staleRunReconciler = await jitiImport("./stale-run-reconciler.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+ function getErrorMessage(error) {
52
+ return error instanceof Error ? error.message : String(error);
53
+ }
54
+
55
+ function ensureObject(value, source) {
56
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
57
+ throw new Error(`Async result file '${source}' must contain a JSON object.`);
58
+ }
59
+ return value;
60
+ }
61
+
62
+ function validateOptionalString(value, field, source, displayField = field) {
63
+ const fieldValue = value[field];
64
+ if (fieldValue === undefined) return undefined;
65
+ if (typeof fieldValue !== "string") throw new Error(`Invalid async result file '${source}': ${displayField} must be a string.`);
66
+ return fieldValue;
67
+ }
68
+
69
+ function validateResultFile(value, resultPath) {
70
+ const data = ensureObject(value, resultPath);
71
+ const resultsValue = data.results;
72
+ let results;
73
+ if (resultsValue !== undefined) {
74
+ if (!Array.isArray(resultsValue)) throw new Error(`Invalid async result file '${resultPath}': results must be an array.`);
75
+ results = resultsValue.map((entry, index) => {
76
+ const child = ensureObject(entry, `${resultPath} results[${index}]`);
77
+ const agent = validateOptionalString(child, "agent", resultPath, `results[${index}].agent`);
78
+ const sessionFile = validateOptionalString(child, "sessionFile", resultPath, `results[${index}].sessionFile`);
79
+ const intercomTarget = validateOptionalString(child, "intercomTarget", resultPath, `results[${index}].intercomTarget`);
80
+ const success = child.success;
81
+ if (success !== undefined && typeof success !== "boolean") throw new Error(`Invalid async result file '${resultPath}': results[${index}].success must be a boolean.`);
82
+ return { agent, sessionFile, intercomTarget, ...(typeof success === "boolean" ? { success } : {}) };
83
+ });
84
+ }
85
+ const success = data.success;
86
+ if (success !== undefined && typeof success !== "boolean") throw new Error(`Invalid async result file '${resultPath}': success must be a boolean.`);
87
+ return {
88
+ id: validateOptionalString(data, "id", resultPath),
89
+ runId: validateOptionalString(data, "runId", resultPath),
90
+ agent: validateOptionalString(data, "agent", resultPath),
91
+ mode: validateOptionalString(data, "mode", resultPath),
92
+ state: validateOptionalString(data, "state", resultPath),
93
+ cwd: validateOptionalString(data, "cwd", resultPath),
94
+ sessionFile: validateOptionalString(data, "sessionFile", resultPath),
95
+ ...(typeof success === "boolean" ? { success } : {}),
96
+ ...(results ? { results } : {})
97
+ };
98
+ }
99
+
100
+ function readResultFile(resultPath) {
101
+ let raw;
102
+ try {
103
+ raw = fs.readFileSync(resultPath, "utf-8");
104
+ } catch (error) {
105
+ throw new Error(`Failed to read async result file '${resultPath}': ${getErrorMessage(error)}`, {
106
+ cause: error instanceof Error ? error : undefined
107
+ });
108
+ }
109
+ try {
110
+ return validateResultFile(JSON.parse(raw), resultPath);
111
+ } catch (error) {
112
+ if (error instanceof SyntaxError) {
113
+ throw new Error(`Failed to parse async result file '${resultPath}': ${getErrorMessage(error)}`, {
114
+ cause: error
115
+ });
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+
121
+ function assertRunId(value, field) {
122
+ if (value === undefined) return undefined;
123
+ if (value.trim() === "") throw new Error(`${field} must not be empty.`);
124
+ if (path.isAbsolute(value) || /[\\/]/.test(value) || value.includes("..")) {
125
+ throw new Error(`${field} must be an async run id or prefix, not a path.`);
126
+ }
127
+ return value;
128
+ }
129
+
130
+ function assertInsideRoot(root, target, label) {
131
+ const rootPath = path.resolve(root);
132
+ const targetPath = path.resolve(target);
133
+ const relative = path.relative(rootPath, targetPath);
134
+ if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) return;
135
+ throw new Error(`${label} must be inside ${rootPath}.`);
136
+ }
137
+
138
+ function prefixedRunIds(dir, prefix, suffix = "") {
139
+ if (!fs.existsSync(dir)) return [];
140
+ return fs.readdirSync(dir).
141
+ filter((entry) => entry.startsWith(prefix) && (!suffix || entry.endsWith(suffix))).
142
+ map((entry) => suffix ? entry.slice(0, -suffix.length) : entry).
143
+ sort();
144
+ }
145
+
146
+ function exactResultPath(resultsDir, runId) {
147
+ const resultPath = path.join(resultsDir, `${runId}.json`);
148
+ assertInsideRoot(resultsDir, resultPath, "Async result file");
149
+ return fs.existsSync(resultPath) ? resultPath : null;
150
+ }
151
+
152
+ function resolveAsyncRunLocation(params, asyncDirRoot, resultsDir) {
153
+ const asyncRoot = path.resolve(asyncDirRoot);
154
+ const resultRoot = path.resolve(resultsDir);
155
+ const requestedId = assertRunId(params.id, "id") ?? assertRunId(params.runId, "runId");
156
+ if (params.dir) {
157
+ const asyncDir = path.resolve(params.dir);
158
+ assertInsideRoot(asyncRoot, asyncDir, "Async run directory");
159
+ const resolvedId = requestedId ?? path.basename(asyncDir);
160
+ if (requestedId && requestedId !== path.basename(asyncDir)) {
161
+ throw new Error(`Async run id '${requestedId}' does not match directory '${path.basename(asyncDir)}'.`);
162
+ }
163
+ return { asyncDir, resultPath: exactResultPath(resultRoot, resolvedId), resolvedId };
164
+ }
165
+ if (!requestedId) return { asyncDir: null, resultPath: null };
166
+
167
+ const directAsyncDir = path.join(asyncRoot, requestedId);
168
+ assertInsideRoot(asyncRoot, directAsyncDir, "Async run directory");
169
+ const directResultPath = exactResultPath(resultRoot, requestedId);
170
+ if (fs.existsSync(directAsyncDir) || directResultPath) {
171
+ return {
172
+ asyncDir: fs.existsSync(directAsyncDir) ? directAsyncDir : null,
173
+ resultPath: directResultPath,
174
+ resolvedId: requestedId
175
+ };
176
+ }
177
+
178
+ const matchingIds = [...new Set([
179
+ ...prefixedRunIds(asyncRoot, requestedId),
180
+ ...prefixedRunIds(resultRoot, requestedId, ".json")]
181
+ )].sort();
182
+ if (matchingIds.length === 0) return { asyncDir: null, resultPath: null, resolvedId: requestedId };
183
+ if (matchingIds.length > 1) {
184
+ throw new Error(`Ambiguous async run id prefix '${requestedId}' matched: ${matchingIds.join(", ")}. Provide a longer id.`);
185
+ }
186
+ const resolvedId = matchingIds[0];
187
+ const asyncDir = path.join(asyncRoot, resolvedId);
188
+ assertInsideRoot(asyncRoot, asyncDir, "Async run directory");
189
+ return {
190
+ asyncDir: fs.existsSync(asyncDir) ? asyncDir : null,
191
+ resultPath: exactResultPath(resultRoot, resolvedId),
192
+ resolvedId
193
+ };
194
+ }
195
+
196
+ function resultState(result) {
197
+ if (result.state === "complete" || result.state === "failed" || result.state === "paused" || result.state === "running" || result.state === "queued") {
198
+ return result.state;
199
+ }
200
+ return result.success ? "complete" : "failed";
201
+ }
202
+
203
+ function validateStatusForResume(status, source) {
204
+ if (!status) return;
205
+ if (typeof status.runId !== "string") throw new Error(`Invalid async status '${source}': runId must be a string.`);
206
+ if (status.sessionId !== undefined && typeof status.sessionId !== "string") throw new Error(`Invalid async status '${source}': sessionId must be a string.`);
207
+ if (status.cwd !== undefined && typeof status.cwd !== "string") throw new Error(`Invalid async status '${source}': cwd must be a string.`);
208
+ if (status.sessionFile !== undefined && typeof status.sessionFile !== "string") throw new Error(`Invalid async status '${source}': sessionFile must be a string.`);
209
+ if (status.steps !== undefined) {
210
+ if (!Array.isArray(status.steps)) throw new Error(`Invalid async status '${source}': steps must be an array.`);
211
+ status.steps.forEach((step, index) => {
212
+ if (!step || typeof step !== "object" || Array.isArray(step)) throw new Error(`Invalid async status '${source}': steps[${index}] must be an object.`);
213
+ if (typeof step.agent !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].agent must be a string.`);
214
+ if (step.sessionFile !== undefined && typeof step.sessionFile !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].sessionFile must be a string.`);
215
+ });
216
+ }
217
+ }
218
+
219
+ function validateResumeSessionFile(runId, sessionFile) {
220
+ if (path.extname(sessionFile) !== ".jsonl") throw new Error(`Async run '${runId}' session file must be a .jsonl file: ${sessionFile}`);
221
+ const resolved = path.resolve(sessionFile);
222
+ if (!fs.existsSync(resolved)) throw new Error(`Async run '${runId}' session file does not exist: ${sessionFile}`);
223
+ return resolved;
224
+ }
225
+
226
+ function resolveAsyncResumeTarget(params, deps = {}) {
227
+ const asyncDirRoot = deps.asyncDirRoot ?? _types.ASYNC_DIR;
228
+ const resultsDir = deps.resultsDir ?? _types.RESULTS_DIR;
229
+ const location = resolveAsyncRunLocation(params, asyncDirRoot, resultsDir);
230
+ if (!location.asyncDir && !location.resultPath) {
231
+ throw new Error("Async run not found. Provide id or dir.");
232
+ }
233
+
234
+ const reconciliation = location.asyncDir ?
235
+ (0, _staleRunReconciler.reconcileAsyncRun)(location.asyncDir, { resultsDir, kill: deps.kill, now: deps.now }) :
236
+ undefined;
237
+ const status = reconciliation?.status ?? null;
238
+ validateStatusForResume(status, location.asyncDir ? path.join(location.asyncDir, "status.json") : "status.json");
239
+ const result = location.resultPath ? readResultFile(location.resultPath) : undefined;
240
+ const runId = status?.runId ?? result?.runId ?? result?.id ?? location.resolvedId ?? (location.asyncDir ? path.basename(location.asyncDir) : "unknown");
241
+ const state = status?.state ?? (result ? resultState(result) : undefined);
242
+ if (!state) throw new Error(`Status file not found for async run '${runId}'.`);
243
+
244
+ const statusSteps = status?.steps ?? [];
245
+ const resultSteps = result?.results ?? [];
246
+ const stepCount = statusSteps.length || resultSteps.length || (result?.agent ? 1 : 0);
247
+ const requestedIndex = params.index;
248
+ if (requestedIndex !== undefined && !Number.isInteger(requestedIndex)) throw new Error(`Async run '${runId}' index must be an integer.`);
249
+ const terminalStepStatuses = new Set(["complete", "completed", "failed", "paused"]);
250
+
251
+ if (state === "running") {
252
+ if (requestedIndex !== undefined) {
253
+ if (requestedIndex < 0 || requestedIndex >= stepCount) throw new Error(`Async run '${runId}' has ${stepCount} children. Index ${requestedIndex} is out of range.`);
254
+ const selectedStep = statusSteps[requestedIndex];
255
+ if (selectedStep?.status === "running") {
256
+ return {
257
+ kind: "live",
258
+ runId,
259
+ asyncDir: location.asyncDir ?? undefined,
260
+ state,
261
+ agent: selectedStep.agent,
262
+ index: requestedIndex,
263
+ intercomTarget: (0, _intercomBridge.resolveSubagentIntercomTarget)(runId, selectedStep.agent, requestedIndex),
264
+ cwd: status?.cwd ?? result?.cwd,
265
+ sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile
266
+ };
267
+ }
268
+ if (selectedStep?.status === "pending") throw new Error(`Async run '${runId}' child ${requestedIndex} is pending and has not started yet. Wait for it to run or complete before resuming.`);
269
+ if (selectedStep && !terminalStepStatuses.has(selectedStep.status)) throw new Error(`Async run '${runId}' child ${requestedIndex} is ${selectedStep.status} and cannot be revived yet.`);
270
+ } else {
271
+ const running = statusSteps.
272
+ map((step, index) => ({ step, index })).
273
+ filter(({ step }) => step.status === "running");
274
+ const selected = running.length === 1 ? running[0] : undefined;
275
+ if (!selected) {
276
+ throw new Error(`Async run '${runId}' has ${running.length} running children. Provide index to choose one.`);
277
+ }
278
+ return {
279
+ kind: "live",
280
+ runId,
281
+ asyncDir: location.asyncDir ?? undefined,
282
+ state,
283
+ agent: selected.step.agent,
284
+ index: selected.index,
285
+ intercomTarget: (0, _intercomBridge.resolveSubagentIntercomTarget)(runId, selected.step.agent, selected.index),
286
+ cwd: status?.cwd ?? result?.cwd,
287
+ sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile
288
+ };
289
+ }
290
+ }
291
+
292
+ if (stepCount > 1 && requestedIndex === undefined) {
293
+ throw new Error(`Async run '${runId}' has ${stepCount} children. Provide index to choose one.`);
294
+ }
295
+ const index = requestedIndex ?? 0;
296
+ if (!Number.isInteger(index)) throw new Error(`Async run '${runId}' index must be an integer.`);
297
+ if (index < 0 || index >= stepCount) throw new Error(`Async run '${runId}' has ${stepCount} children. Index ${index} is out of range.`);
298
+ const agent = statusSteps[index]?.agent ?? resultSteps[index]?.agent ?? result?.agent;
299
+ if (!agent) throw new Error(`Could not determine child agent for async run '${runId}'.`);
300
+ const sessionFile = statusSteps[index]?.sessionFile ??
301
+ resultSteps[index]?.sessionFile ?? (
302
+ stepCount === 1 ? status?.sessionFile ?? result?.sessionFile : undefined);
303
+ if (!sessionFile) throw new Error(`Async run '${runId}' child ${index} does not have a persisted session file to resume from.`);
304
+ const resolvedSessionFile = validateResumeSessionFile(runId, sessionFile);
305
+
306
+ return {
307
+ kind: "revive",
308
+ runId,
309
+ asyncDir: location.asyncDir ?? undefined,
310
+ state,
311
+ agent,
312
+ index,
313
+ intercomTarget: (0, _intercomBridge.resolveSubagentIntercomTarget)(runId, agent, index),
314
+ cwd: status?.cwd ?? result?.cwd,
315
+ sessionFile: resolvedSessionFile
316
+ };
317
+ }
318
+
319
+ function buildRevivedAsyncTask(target, message) {
320
+ return [
321
+ "You are reviving a previous subagent conversation.",
322
+ "",
323
+ `Original run: ${target.runId}`,
324
+ `Original agent: ${target.agent}`,
325
+ target.sessionFile ? `Original session file: ${target.sessionFile}` : undefined,
326
+ "",
327
+ "Use the stored session context as background. Answer the orchestrator's follow-up below. Do not assume the original child process is still alive.",
328
+ "",
329
+ "Follow-up:",
330
+ message].
331
+ filter((line) => line !== undefined).join("\n");
332
+ } /* v9-4bfa08afb714d9c3 */
pip-tmp/jiti/background-async-status.51801569.mjs ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.formatAsyncRunList = formatAsyncRunList;exports.formatAsyncRunOutputPath = formatAsyncRunOutputPath;exports.formatAsyncRunProgressLabel = formatAsyncRunProgressLabel;exports.listAsyncRuns = listAsyncRuns;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+ var _formatters = await jitiImport("../../shared/formatters.ts");
4
+ var _statusFormat = await jitiImport("../../shared/status-format.ts");
5
+
6
+ var _utils = await jitiImport("../../shared/utils.ts");
7
+ var _parallelGroups = await jitiImport("./parallel-groups.ts");
8
+ var _staleRunReconciler = await jitiImport("./stale-run-reconciler.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+ function getErrorMessage(error) {
70
+ return error instanceof Error ? error.message : String(error);
71
+ }
72
+
73
+ function isNotFoundError(error) {
74
+ return typeof error === "object" &&
75
+ error !== null &&
76
+ "code" in error &&
77
+ error.code === "ENOENT";
78
+ }
79
+
80
+ function isAsyncRunDir(root, entry) {
81
+ const entryPath = path.join(root, entry);
82
+ try {
83
+ return fs.statSync(entryPath).isDirectory();
84
+ } catch (error) {
85
+ if (isNotFoundError(error)) return false;
86
+ throw new Error(`Failed to inspect async run path '${entryPath}': ${getErrorMessage(error)}`, {
87
+ cause: error instanceof Error ? error : undefined
88
+ });
89
+ }
90
+ }
91
+
92
+ function outputFileMtime(outputFile) {
93
+ if (!outputFile) return undefined;
94
+ try {
95
+ return fs.statSync(outputFile).mtimeMs;
96
+ } catch (error) {
97
+ if (isNotFoundError(error)) return undefined;
98
+ throw new Error(`Failed to inspect async output file '${outputFile}': ${getErrorMessage(error)}`, {
99
+ cause: error instanceof Error ? error : undefined
100
+ });
101
+ }
102
+ }
103
+
104
+ function deriveAsyncActivityState(asyncDir, status) {
105
+ if (status.state !== "running") return { activityState: status.activityState, lastActivityAt: status.lastActivityAt };
106
+ const outputPath = status.outputFile ? path.isAbsolute(status.outputFile) ? status.outputFile : path.join(asyncDir, status.outputFile) : undefined;
107
+ const currentStep = typeof status.currentStep === "number" ? status.steps?.[status.currentStep] : undefined;
108
+ return {
109
+ activityState: status.activityState,
110
+ lastActivityAt: status.lastActivityAt ?? outputFileMtime(outputPath) ?? currentStep?.lastActivityAt ?? currentStep?.startedAt ?? status.startedAt
111
+ };
112
+ }
113
+
114
+ function statusToSummary(asyncDir, status) {
115
+ if (status.sessionId !== undefined && typeof status.sessionId !== "string") {
116
+ throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': sessionId must be a string.`);
117
+ }
118
+ const { activityState, lastActivityAt } = deriveAsyncActivityState(asyncDir, status);
119
+ const steps = status.steps ?? [];
120
+ const chainStepCount = status.chainStepCount ?? steps.length;
121
+ const parallelGroups = (0, _parallelGroups.normalizeParallelGroups)(status.parallelGroups, steps.length, chainStepCount);
122
+ return {
123
+ id: status.runId || path.basename(asyncDir),
124
+ asyncDir,
125
+ ...(status.sessionId ? { sessionId: status.sessionId } : {}),
126
+ state: status.state,
127
+ activityState,
128
+ lastActivityAt,
129
+ currentTool: status.currentTool,
130
+ currentToolStartedAt: status.currentToolStartedAt,
131
+ currentPath: status.currentPath,
132
+ turnCount: status.turnCount,
133
+ toolCount: status.toolCount,
134
+ mode: status.mode,
135
+ cwd: status.cwd,
136
+ startedAt: status.startedAt,
137
+ lastUpdate: status.lastUpdate,
138
+ endedAt: status.endedAt,
139
+ currentStep: status.currentStep,
140
+ ...(status.chainStepCount !== undefined ? { chainStepCount: status.chainStepCount } : {}),
141
+ ...(parallelGroups.length ? { parallelGroups } : {}),
142
+ steps: steps.map((step, index) => {
143
+ const stepActivityState = step.activityState;
144
+ const stepLastActivityAt = step.lastActivityAt;
145
+ return {
146
+ index,
147
+ agent: step.agent,
148
+ status: step.status,
149
+ ...(stepActivityState ? { activityState: stepActivityState } : {}),
150
+ ...(stepLastActivityAt ? { lastActivityAt: stepLastActivityAt } : {}),
151
+ ...(step.currentTool ? { currentTool: step.currentTool } : {}),
152
+ ...(step.currentToolArgs ? { currentToolArgs: step.currentToolArgs } : {}),
153
+ ...(step.currentToolStartedAt ? { currentToolStartedAt: step.currentToolStartedAt } : {}),
154
+ ...(step.currentPath ? { currentPath: step.currentPath } : {}),
155
+ ...(step.recentTools ? { recentTools: step.recentTools.map((tool) => ({ ...tool })) } : {}),
156
+ ...(step.recentOutput ? { recentOutput: [...step.recentOutput] } : {}),
157
+ ...(step.turnCount !== undefined ? { turnCount: step.turnCount } : {}),
158
+ ...(step.toolCount !== undefined ? { toolCount: step.toolCount } : {}),
159
+ ...(step.durationMs !== undefined ? { durationMs: step.durationMs } : {}),
160
+ ...(step.tokens ? { tokens: step.tokens } : {}),
161
+ ...(step.skills ? { skills: step.skills } : {}),
162
+ ...(step.model ? { model: step.model } : {}),
163
+ ...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
164
+ ...(step.error ? { error: step.error } : {})
165
+ };
166
+ }),
167
+ ...(status.sessionDir ? { sessionDir: status.sessionDir } : {}),
168
+ ...(status.outputFile ? { outputFile: status.outputFile } : {}),
169
+ ...(status.totalTokens ? { totalTokens: status.totalTokens } : {}),
170
+ ...(status.sessionFile ? { sessionFile: status.sessionFile } : {})
171
+ };
172
+ }
173
+
174
+ function sortRuns(runs) {
175
+ const rank = (state) => {
176
+ switch (state) {
177
+ case "running":return 0;
178
+ case "queued":return 1;
179
+ case "failed":return 2;
180
+ case "paused":return 2;
181
+ case "complete":return 3;
182
+ }
183
+ };
184
+ return [...runs].sort((a, b) => {
185
+ const byState = rank(a.state) - rank(b.state);
186
+ if (byState !== 0) return byState;
187
+ const aTime = a.lastUpdate ?? a.endedAt ?? a.startedAt;
188
+ const bTime = b.lastUpdate ?? b.endedAt ?? b.startedAt;
189
+ return bTime - aTime;
190
+ });
191
+ }
192
+
193
+ function listAsyncRuns(asyncDirRoot, options = {}) {
194
+ let entries;
195
+ try {
196
+ entries = fs.readdirSync(asyncDirRoot).filter((entry) => isAsyncRunDir(asyncDirRoot, entry));
197
+ } catch (error) {
198
+ if (isNotFoundError(error)) return [];
199
+ throw new Error(`Failed to list async runs in '${asyncDirRoot}': ${getErrorMessage(error)}`, {
200
+ cause: error instanceof Error ? error : undefined
201
+ });
202
+ }
203
+
204
+ const allowedStates = options.states ? new Set(options.states) : undefined;
205
+ const runs = [];
206
+ for (const entry of entries) {
207
+ const asyncDir = path.join(asyncDirRoot, entry);
208
+ const reconciliation = options.reconcile === false ?
209
+ undefined :
210
+ (0, _staleRunReconciler.reconcileAsyncRun)(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now });
211
+ const status = reconciliation?.status ?? (0, _utils.readStatus)(asyncDir);
212
+ if (!status) continue;
213
+ const summary = statusToSummary(asyncDir, status);
214
+ if (allowedStates && !allowedStates.has(summary.state)) continue;
215
+ if (options.sessionId && summary.sessionId !== options.sessionId) continue;
216
+ runs.push(summary);
217
+ }
218
+
219
+ const sorted = sortRuns(runs);
220
+ return options.limit !== undefined ? sorted.slice(0, options.limit) : sorted;
221
+ }
222
+
223
+ function formatActivityFacts(input) {
224
+ const facts = [];
225
+ if (input.currentTool && input.currentToolStartedAt !== undefined) facts.push(`tool ${input.currentTool} ${(0, _formatters.formatDuration)(Math.max(0, Date.now() - input.currentToolStartedAt))}`);else
226
+ if (input.currentTool) facts.push(`tool ${input.currentTool}`);
227
+ if (input.currentPath) facts.push((0, _formatters.shortenPath)(input.currentPath));
228
+ if (input.turnCount !== undefined) facts.push(`${input.turnCount} turns`);
229
+ if (input.toolCount !== undefined) facts.push(`${input.toolCount} tools`);
230
+ const activity = (0, _statusFormat.formatActivityLabel)(input.lastActivityAt, input.activityState);
231
+ return activity || facts.length ? [activity, ...facts].filter(Boolean).join(" | ") : undefined;
232
+ }
233
+
234
+ function formatStepLine(step) {
235
+ const parts = [`${step.index + 1}. ${step.agent}`, step.status];
236
+ const activity = formatActivityFacts(step);
237
+ if (activity) parts.push(activity);
238
+ if (step.model) parts.push(step.model);
239
+ if (step.durationMs !== undefined) parts.push((0, _formatters.formatDuration)(step.durationMs));
240
+ if (step.tokens) parts.push(`${(0, _formatters.formatTokens)(step.tokens.total)} tok`);
241
+ return parts.join(" | ");
242
+ }
243
+
244
+ function formatAsyncRunOutputPath(run) {
245
+ if (!run.outputFile) return undefined;
246
+ return path.isAbsolute(run.outputFile) ? run.outputFile : path.join(run.asyncDir, run.outputFile);
247
+ }
248
+
249
+ function formatAsyncRunProgressLabel(run) {
250
+ const stepCount = run.steps.length || 1;
251
+ const chainStepCount = run.chainStepCount ?? stepCount;
252
+ const groups = (0, _parallelGroups.normalizeParallelGroups)(run.parallelGroups, run.steps.length, chainStepCount);
253
+ const activeGroup = run.currentStep !== undefined ?
254
+ groups.find((group) => run.currentStep >= group.start && run.currentStep < group.start + group.count) :
255
+ undefined;
256
+ if (activeGroup) {
257
+ const groupSteps = run.steps.slice(activeGroup.start, activeGroup.start + activeGroup.count);
258
+ const groupLabel = (0, _statusFormat.formatParallelOutcome)(groupSteps, activeGroup.count, { showRunning: run.state === "running" });
259
+ if (run.mode === "parallel") return groupLabel;
260
+ return `step ${activeGroup.stepIndex + 1}/${chainStepCount} · parallel group: ${groupLabel}`;
261
+ }
262
+ if (run.mode === "parallel") return (0, _statusFormat.formatParallelOutcome)(run.steps, stepCount, { showRunning: run.state === "running" });
263
+ if (run.mode === "chain" && run.currentStep !== undefined && groups.length > 0) {
264
+ const logicalStep = (0, _parallelGroups.flatToLogicalStepIndex)(run.currentStep, chainStepCount, groups);
265
+ return `step ${logicalStep + 1}/${chainStepCount}`;
266
+ }
267
+ return run.currentStep !== undefined ? `step ${run.currentStep + 1}/${stepCount}` : `steps ${stepCount}`;
268
+ }
269
+
270
+ function formatRunHeader(run) {
271
+ const stepLabel = formatAsyncRunProgressLabel(run);
272
+ const cwd = run.cwd ? (0, _formatters.shortenPath)(run.cwd) : (0, _formatters.shortenPath)(run.asyncDir);
273
+ const activity = formatActivityFacts(run);
274
+ return `${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode} | ${stepLabel} | ${cwd}`;
275
+ }
276
+
277
+ function formatAsyncRunList(runs, heading = "Active async runs") {
278
+ if (runs.length === 0) return `No ${heading.toLowerCase()}.`;
279
+
280
+ const lines = [`${heading}: ${runs.length}`, ""];
281
+ for (const run of runs) {
282
+ lines.push(`- ${formatRunHeader(run)}`);
283
+ for (const step of run.steps) {
284
+ lines.push(` ${formatStepLine(step)}`);
285
+ }
286
+ const outputPath = formatAsyncRunOutputPath(run);
287
+ if (outputPath) lines.push(` output: ${(0, _formatters.shortenPath)(outputPath)}`);
288
+ if (run.sessionFile) lines.push(` session: ${(0, _formatters.shortenPath)(run.sessionFile)}`);
289
+ lines.push("");
290
+ }
291
+ return lines.join("\n").trimEnd();
292
+ } /* v9-c2a3317ae1027d19 */
pip-tmp/jiti/background-completion-dedupe.b4a5ebb9.mjs ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildCompletionKey = buildCompletionKey;exports.getGlobalSeenMap = getGlobalSeenMap;exports.markSeenWithTtl = markSeenWithTtl;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+ function asNonEmptyString(value) {
12
+ if (typeof value !== "string") return undefined;
13
+ const trimmed = value.trim();
14
+ return trimmed.length > 0 ? trimmed : undefined;
15
+ }
16
+
17
+ function asFiniteNumber(value) {
18
+ if (typeof value !== "number") return undefined;
19
+ return Number.isFinite(value) ? value : undefined;
20
+ }
21
+
22
+ function buildCompletionKey(data, fallback) {
23
+ const id = asNonEmptyString(data.id);
24
+ if (id) return `id:${id}`;
25
+ const sessionId = asNonEmptyString(data.sessionId) ?? "no-session";
26
+ const agent = asNonEmptyString(data.agent) ?? "unknown";
27
+ const timestamp = asFiniteNumber(data.timestamp);
28
+ const taskIndex = asFiniteNumber(data.taskIndex);
29
+ const totalTasks = asFiniteNumber(data.totalTasks);
30
+ const success = typeof data.success === "boolean" ? data.success ? "1" : "0" : "?";
31
+ return [
32
+ "meta",
33
+ sessionId,
34
+ agent,
35
+ timestamp !== undefined ? String(timestamp) : "no-ts",
36
+ taskIndex !== undefined ? String(taskIndex) : "-",
37
+ totalTasks !== undefined ? String(totalTasks) : "-",
38
+ success,
39
+ fallback].
40
+ join(":");
41
+ }
42
+
43
+ function pruneSeenMap(seen, now, ttlMs) {
44
+ for (const [key, ts] of seen.entries()) {
45
+ if (now - ts > ttlMs) seen.delete(key);
46
+ }
47
+ }
48
+
49
+ function markSeenWithTtl(seen, key, now, ttlMs) {
50
+ pruneSeenMap(seen, now, ttlMs);
51
+ if (seen.has(key)) return true;
52
+ seen.set(key, now);
53
+ return false;
54
+ }
55
+
56
+ function getGlobalSeenMap(storeKey) {
57
+ const globalStore = globalThis;
58
+ const existing = globalStore[storeKey];
59
+ if (existing instanceof Map) return existing;
60
+ const map = new Map();
61
+ globalStore[storeKey] = map;
62
+ return map;
63
+ } /* v9-eb576fab2c31b37a */
pip-tmp/jiti/background-notify.20a22f54.mjs ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = registerSubagentNotify;
2
+
3
+
4
+
5
+
6
+ var _completionDedupe = await jitiImport("./completion-dedupe.ts");
7
+ var _types = await jitiImport("../../shared/types.ts"); /**
8
+ * Subagent completion notifications.
9
+ */
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+ function registerSubagentNotify(pi) {
44
+ const unsubscribeStoreKey = "__pi_subagents_notify_unsubscribe__";
45
+ const globalStore = globalThis;
46
+ const previousUnsubscribe = globalStore[unsubscribeStoreKey];
47
+ if (typeof previousUnsubscribe === "function") {
48
+ try {
49
+ previousUnsubscribe();
50
+ } catch {
51
+
52
+ // Best effort cleanup for stale handlers from an older reload.
53
+ }}
54
+
55
+ const seen = (0, _completionDedupe.getGlobalSeenMap)("__pi_subagents_notify_seen__");
56
+ const ttlMs = 10 * 60 * 1000;
57
+
58
+ const handleComplete = (data) => {
59
+ const result = data;
60
+ const now = Date.now();
61
+ const key = (0, _completionDedupe.buildCompletionKey)(result, "notify");
62
+ if ((0, _completionDedupe.markSeenWithTtl)(seen, key, now, ttlMs)) return;
63
+
64
+ const agent = result.agent ?? "unknown";
65
+ const summary = typeof result.summary === "string" ? result.summary : "";
66
+ const paused = !result.success && (
67
+ result.exitCode === 0 ||
68
+ result.state === "paused" ||
69
+ summary.startsWith("Paused after interrupt."));
70
+
71
+ const status = paused ? "paused" : result.success ? "completed" : "failed";
72
+
73
+ const taskInfo =
74
+ result.taskIndex !== undefined && result.totalTasks !== undefined ?
75
+ ` (${result.taskIndex + 1}/${result.totalTasks})` :
76
+ "";
77
+
78
+ const sessionLine = result.shareUrl ?
79
+ `Session: ${result.shareUrl}` :
80
+ result.shareError ?
81
+ `Session share error: ${result.shareError}` :
82
+ result.sessionFile ?
83
+ `Session file: ${result.sessionFile}` :
84
+ undefined;
85
+
86
+ const displaySummary = summary.trim() ? summary : "(no output)";
87
+ const content = [
88
+ `Background task ${status}: **${agent}**${taskInfo}`,
89
+ "",
90
+ displaySummary,
91
+ sessionLine ? "" : undefined,
92
+ sessionLine].
93
+
94
+ filter((line) => line !== undefined).
95
+ join("\n");
96
+
97
+ pi.sendMessage(
98
+ {
99
+ customType: "subagent-notify",
100
+ content,
101
+ display: true
102
+ },
103
+ { triggerTurn: true }
104
+ );
105
+ };
106
+
107
+ globalStore[unsubscribeStoreKey] = pi.events.on(_types.SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete);
108
+ } /* v9-d92e3a54b93efe80 */
pip-tmp/jiti/background-parallel-groups.e70893a1.mjs ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.flatToLogicalStepIndex = flatToLogicalStepIndex;exports.normalizeParallelGroups = normalizeParallelGroups;
2
+
3
+ function isValidParallelGroup(group, stepCount, chainStepCount) {
4
+ if (typeof group !== "object" || group === null) return false;
5
+ const { start, count, stepIndex } = group;
6
+ return typeof start === "number" &&
7
+ typeof count === "number" &&
8
+ typeof stepIndex === "number" &&
9
+ Number.isInteger(start) &&
10
+ Number.isInteger(count) &&
11
+ Number.isInteger(stepIndex) &&
12
+ start >= 0 &&
13
+ count > 0 &&
14
+ stepIndex >= 0 &&
15
+ stepIndex < chainStepCount &&
16
+ start + count <= stepCount;
17
+ }
18
+
19
+ function normalizeParallelGroups(groups, stepCount, chainStepCount) {
20
+ if (!Array.isArray(groups)) return [];
21
+ return groups.
22
+ filter((group) => isValidParallelGroup(group, stepCount, chainStepCount)).
23
+ sort((left, right) => left.stepIndex - right.stepIndex || left.start - right.start);
24
+ }
25
+
26
+ function flatToLogicalStepIndex(flatIndex, chainStepCount, groups) {
27
+ let logicalIndex = 0;
28
+ let cursor = 0;
29
+ for (const group of groups) {
30
+ while (cursor < group.start && logicalIndex < chainStepCount) {
31
+ if (cursor === flatIndex) return logicalIndex;
32
+ cursor++;
33
+ logicalIndex++;
34
+ }
35
+ if (flatIndex >= group.start && flatIndex < group.start + group.count) return group.stepIndex;
36
+ cursor = group.start + group.count;
37
+ logicalIndex = group.stepIndex + 1;
38
+ }
39
+ while (cursor <= flatIndex && logicalIndex < chainStepCount) {
40
+ if (cursor === flatIndex) return logicalIndex;
41
+ cursor++;
42
+ logicalIndex++;
43
+ }
44
+ return Math.max(0, chainStepCount - 1);
45
+ } /* v9-bcbcba92133f8cc7 */
pip-tmp/jiti/background-result-watcher.056baed0.mjs ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.createResultWatcher = createResultWatcher;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+ var _completionDedupe = await jitiImport("./completion-dedupe.ts");
4
+ var _fileCoalescer = await jitiImport("../../shared/file-coalescer.ts");
5
+ var _types = await jitiImport("../../shared/types.ts");
6
+
7
+
8
+
9
+
10
+ var _resultIntercom = await jitiImport("../../intercom/result-intercom.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
11
+
12
+
13
+
14
+
15
+
16
+ const WATCHER_RESTART_DELAY_MS = 3000;
17
+ const POLL_INTERVAL_MS = 3000;
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+ function getErrorCode(error) {
34
+ return typeof error === "object" && error !== null && "code" in error ?
35
+ error.code :
36
+ undefined;
37
+ }
38
+
39
+ function isNotFoundError(error) {
40
+ return getErrorCode(error) === "ENOENT";
41
+ }
42
+
43
+ function shouldFallBackToPolling(error) {
44
+ const code = getErrorCode(error);
45
+ return code === "EMFILE" || code === "ENOSPC";
46
+ }
47
+
48
+ function createResultWatcher(
49
+ pi,
50
+ state,
51
+ resultsDir,
52
+ completionTtlMs,
53
+ deps = {})
54
+
55
+
56
+
57
+
58
+ {
59
+ const fsApi = deps.fs ?? fs;
60
+ const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
61
+
62
+ const handleResult = async (file) => {
63
+ const resultPath = path.join(resultsDir, file);
64
+ if (!fsApi.existsSync(resultPath)) return;
65
+ try {
66
+ const data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8"));
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+ if (data.sessionId && data.sessionId !== state.currentSessionId) return;
90
+ if (!data.sessionId && data.cwd && data.cwd !== state.baseCwd) return;
91
+
92
+ const now = Date.now();
93
+ const completionKey = (0, _completionDedupe.buildCompletionKey)(data, `result:${file}`);
94
+ if ((0, _completionDedupe.markSeenWithTtl)(state.completionSeen, completionKey, now, completionTtlMs)) {
95
+ fsApi.unlinkSync(resultPath);
96
+ return;
97
+ }
98
+
99
+ const intercomTarget = data.intercomTarget?.trim();
100
+ if (intercomTarget) {
101
+ const childResults = Array.isArray(data.results) && data.results.length > 0 ?
102
+ data.results :
103
+ [{
104
+ agent: data.agent,
105
+ output: data.summary,
106
+ success: data.success
107
+ }];
108
+ const runId = data.runId ?? data.id ?? file.replace(/\.json$/i, "");
109
+ const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain" ?
110
+ data.mode :
111
+ childResults.length > 1 ? "chain" : "single";
112
+ const payload = (0, _resultIntercom.buildSubagentResultIntercomPayload)({
113
+ to: intercomTarget,
114
+ runId,
115
+ mode,
116
+ source: "async",
117
+ children: childResults.map((result = {}, index) => {
118
+ const baseOutput = result.output ?? data.summary;
119
+ const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
120
+ const output = hasRealOutput ? baseOutput : "(no output)";
121
+ const summary = result.success === false && result.error ?
122
+ `${result.error}${hasRealOutput ? `\n\nOutput:\n${baseOutput}` : ""}` :
123
+ output;
124
+ const sessionPath = result.sessionFile ?? (childResults.length === 1 ? data.sessionFile : undefined);
125
+ return {
126
+ agent: result.agent ?? data.agent ?? `step-${index + 1}`,
127
+ status: (0, _resultIntercom.resolveSubagentResultStatus)({
128
+ success: result.success,
129
+ state: data.state === "paused" || typeof result.success !== "boolean" ? data.state : undefined
130
+ }),
131
+ summary,
132
+ index,
133
+ artifactPath: result.artifactPaths?.outputPath,
134
+ ...(typeof sessionPath === "string" && fsApi.existsSync(sessionPath) ? { sessionPath } : {}),
135
+ intercomTarget: result.intercomTarget
136
+ };
137
+ }),
138
+ asyncId: data.id,
139
+ asyncDir: data.asyncDir
140
+ });
141
+ const delivered = await (0, _resultIntercom.deliverSubagentResultIntercomEvent)(pi.events, payload);
142
+ if (!delivered) {
143
+ console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
144
+ }
145
+ }
146
+
147
+ pi.events.emit(_types.SUBAGENT_ASYNC_COMPLETE_EVENT, data);
148
+ fsApi.unlinkSync(resultPath);
149
+ } catch (error) {
150
+ if (isNotFoundError(error)) return;
151
+ console.error(`Failed to process subagent result file '${resultPath}':`, error);
152
+ }
153
+ };
154
+
155
+ state.resultFileCoalescer = (0, _fileCoalescer.createFileCoalescer)((file) => {
156
+ void handleResult(file);
157
+ }, 50);
158
+
159
+ const primeExistingResults = () => {
160
+ try {
161
+ fsApi.readdirSync(resultsDir).
162
+ filter((f) => f.endsWith(".json")).
163
+ forEach((file) => state.resultFileCoalescer.schedule(file, 0));
164
+ } catch (error) {
165
+ if (isNotFoundError(error)) return;
166
+ console.error(`Failed to scan subagent result directory '${resultsDir}':`, error);
167
+ }
168
+ };
169
+
170
+ const startPollingFallback = (reason) => {
171
+ state.watcher?.close();
172
+ state.watcher = null;
173
+ if (state.watcherRestartTimer) return;
174
+
175
+ console.error(
176
+ `Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`
177
+ );
178
+ primeExistingResults();
179
+ state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS);
180
+ state.watcherRestartTimer.unref?.();
181
+ };
182
+
183
+ const scheduleRestart = () => {
184
+ if (state.watcherRestartTimer) return;
185
+ state.watcherRestartTimer = timers.setTimeout(() => {
186
+ state.watcherRestartTimer = null;
187
+ try {
188
+ fsApi.mkdirSync(resultsDir, { recursive: true });
189
+ startResultWatcher();
190
+ } catch (error) {
191
+ if (shouldFallBackToPolling(error)) {
192
+ startPollingFallback(error);
193
+ return;
194
+ }
195
+ console.error(`Failed to restart subagent result watcher for '${resultsDir}':`, error);
196
+ scheduleRestart();
197
+ }
198
+ }, WATCHER_RESTART_DELAY_MS);
199
+ state.watcherRestartTimer.unref?.();
200
+ };
201
+
202
+ const startResultWatcher = () => {
203
+ if (state.watcher) return;
204
+ if (state.watcherRestartTimer) {
205
+ timers.clearTimeout(state.watcherRestartTimer);
206
+ timers.clearInterval(state.watcherRestartTimer);
207
+ state.watcherRestartTimer = null;
208
+ }
209
+ try {
210
+ state.watcher = fsApi.watch(resultsDir, (ev, file) => {
211
+ if (ev !== "rename" || !file) return;
212
+ const fileName = file.toString();
213
+ if (!fileName.endsWith(".json")) return;
214
+ state.resultFileCoalescer.schedule(fileName);
215
+ });
216
+ state.watcher.on("error", (error) => {
217
+ if (shouldFallBackToPolling(error)) {
218
+ startPollingFallback(error);
219
+ return;
220
+ }
221
+ console.error(`Subagent result watcher failed for '${resultsDir}':`, error);
222
+ state.watcher?.close();
223
+ state.watcher = null;
224
+ scheduleRestart();
225
+ });
226
+ state.watcher.unref?.();
227
+ } catch (error) {
228
+ if (shouldFallBackToPolling(error)) {
229
+ startPollingFallback(error);
230
+ return;
231
+ }
232
+ console.error(`Failed to start subagent result watcher for '${resultsDir}':`, error);
233
+ state.watcher = null;
234
+ scheduleRestart();
235
+ }
236
+ };
237
+
238
+ const stopResultWatcher = () => {
239
+ state.watcher?.close();
240
+ state.watcher = null;
241
+ if (state.watcherRestartTimer) {
242
+ timers.clearTimeout(state.watcherRestartTimer);
243
+ timers.clearInterval(state.watcherRestartTimer);
244
+ }
245
+ state.watcherRestartTimer = null;
246
+ state.resultFileCoalescer.clear();
247
+ };
248
+
249
+ return { startResultWatcher, primeExistingResults, stopResultWatcher };
250
+ } /* v9-43f45ffab2803705 */
pip-tmp/jiti/background-run-status.37d535df.mjs ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.inspectSubagentStatus = inspectSubagentStatus;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+
4
+ var _asyncStatus = await jitiImport("./async-status.ts");
5
+ var _statusFormat = await jitiImport("../../shared/status-format.ts");
6
+ var _types = await jitiImport("../../shared/types.ts");
7
+ var _intercomBridge = await jitiImport("../../intercom/intercom-bridge.ts");
8
+ var _asyncResume = await jitiImport("./async-resume.ts");
9
+ var _parallelGroups = await jitiImport("./parallel-groups.ts");
10
+ var _staleRunReconciler = await jitiImport("./stale-run-reconciler.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+ function hasExistingSessionFile(value) {
27
+ return typeof value === "string" && fs.existsSync(value);
28
+ }
29
+
30
+ function formatResumeGuidance(runId, children, fallbackSessionFile) {
31
+ const knownChildren = children.
32
+ map((child, index) => ({ child, index })).
33
+ filter(({ child }) => typeof child.agent === "string");
34
+ if (!runId || knownChildren.length === 0) return "Resume: unavailable; no child session file was persisted.";
35
+ const singleSessionFile = knownChildren[0]?.child.sessionFile ?? fallbackSessionFile;
36
+ if (children.length === 1 && knownChildren.length === 1 && hasExistingSessionFile(singleSessionFile)) {
37
+ return `Revive: subagent({ action: "resume", id: "${runId}", message: "..." })`;
38
+ }
39
+ const childWithSession = knownChildren.find(({ child }) => hasExistingSessionFile(child.sessionFile));
40
+ if (childWithSession) {
41
+ return `Revive child: subagent({ action: "resume", id: "${runId}", index: ${childWithSession.index}, message: "..." })`;
42
+ }
43
+ return "Resume: unavailable; no child session file was persisted.";
44
+ }
45
+
46
+ function stepLineLabel(status, index) {
47
+ const steps = status.steps ?? [];
48
+ if (status.mode === "parallel") return `Agent ${index + 1}/${steps.length || 1}`;
49
+ if (status.mode === "chain") {
50
+ const chainStepCount = status.chainStepCount ?? (steps.length || 1);
51
+ const groups = (0, _parallelGroups.normalizeParallelGroups)(status.parallelGroups, steps.length, chainStepCount);
52
+ const group = groups.find((candidate) => index >= candidate.start && index < candidate.start + candidate.count);
53
+ if (group) return `Step ${group.stepIndex + 1}/${chainStepCount} Agent ${index - group.start + 1}/${group.count}`;
54
+ return `Step ${(0, _parallelGroups.flatToLogicalStepIndex)(index, chainStepCount, groups) + 1}/${chainStepCount}`;
55
+ }
56
+ return `Step ${index + 1}`;
57
+ }
58
+
59
+ function inspectSubagentStatus(params, deps = {}) {
60
+ const asyncDirRoot = deps.asyncDirRoot ?? _types.ASYNC_DIR;
61
+ const resultsDir = deps.resultsDir ?? _types.RESULTS_DIR;
62
+ if (!params.id && !params.runId && !params.dir) {
63
+ try {
64
+ const runs = (0, _asyncStatus.listAsyncRuns)(asyncDirRoot, { states: ["queued", "running"], resultsDir, kill: deps.kill, now: deps.now });
65
+ return {
66
+ content: [{ type: "text", text: (0, _asyncStatus.formatAsyncRunList)(runs) }],
67
+ details: { mode: "single", results: [] }
68
+ };
69
+ } catch (error) {
70
+ const message = error instanceof Error ? error.message : String(error);
71
+ return {
72
+ content: [{ type: "text", text: message }],
73
+ isError: true,
74
+ details: { mode: "single", results: [] }
75
+ };
76
+ }
77
+ }
78
+
79
+ let location;
80
+ try {
81
+ location = (0, _asyncResume.resolveAsyncRunLocation)(params, asyncDirRoot, resultsDir);
82
+ } catch (error) {
83
+ const message = error instanceof Error ? error.message : String(error);
84
+ return {
85
+ content: [{ type: "text", text: message }],
86
+ isError: true,
87
+ details: { mode: "single", results: [] }
88
+ };
89
+ }
90
+ const { asyncDir, resultPath, resolvedId } = location;
91
+
92
+ if (!asyncDir && !resultPath) {
93
+ return {
94
+ content: [{ type: "text", text: "Async run not found. Provide id or dir." }],
95
+ isError: true,
96
+ details: { mode: "single", results: [] }
97
+ };
98
+ }
99
+
100
+ if (asyncDir) {
101
+ let reconciliation;
102
+ try {
103
+ reconciliation = (0, _staleRunReconciler.reconcileAsyncRun)(asyncDir, { resultsDir, kill: deps.kill, now: deps.now });
104
+ } catch (error) {
105
+ const message = error instanceof Error ? error.message : String(error);
106
+ return {
107
+ content: [{ type: "text", text: message }],
108
+ isError: true,
109
+ details: { mode: "single", results: [] }
110
+ };
111
+ }
112
+ const status = reconciliation.status;
113
+ const effectiveRunId = status?.runId ?? resolvedId ?? "unknown";
114
+ const logPath = path.join(asyncDir, `subagent-log-${effectiveRunId}.md`);
115
+ const eventsPath = path.join(asyncDir, "events.jsonl");
116
+ if (status) {
117
+ const outputPath = (0, _asyncStatus.formatAsyncRunOutputPath)({ asyncDir, outputFile: status.outputFile });
118
+ const progressLabel = (0, _asyncStatus.formatAsyncRunProgressLabel)({
119
+ mode: status.mode,
120
+ state: status.state,
121
+ currentStep: status.currentStep,
122
+ chainStepCount: status.chainStepCount,
123
+ parallelGroups: status.parallelGroups,
124
+ steps: (status.steps ?? []).map((step, index) => ({ index, agent: step.agent, status: step.status }))
125
+ });
126
+ const started = new Date(status.startedAt).toISOString();
127
+ const updated = status.lastUpdate ? new Date(status.lastUpdate).toISOString() : "n/a";
128
+ const statusActivityText = status.state === "running" ? (0, _statusFormat.formatActivityLabel)(status.lastActivityAt, status.activityState) : undefined;
129
+
130
+ const lines = [
131
+ `Run: ${status.runId}`,
132
+ `State: ${status.state}`,
133
+ statusActivityText ? `Activity: ${statusActivityText}` : undefined,
134
+ `Mode: ${status.mode}`,
135
+ `Progress: ${progressLabel}`,
136
+ `Started: ${started}`,
137
+ `Updated: ${updated}`,
138
+ `Dir: ${asyncDir}`,
139
+ outputPath ? `Output: ${outputPath}` : undefined,
140
+ reconciliation.message ? `Diagnosis: ${reconciliation.message}` : undefined,
141
+ reconciliation.resultPath && fs.existsSync(reconciliation.resultPath) ? `Result: ${reconciliation.resultPath}` : undefined].
142
+ filter((line) => Boolean(line));
143
+ for (const [index, step] of (status.steps ?? []).entries()) {
144
+ const stepActivityText = step.status === "running" ? (0, _statusFormat.formatActivityLabel)(step.lastActivityAt, step.activityState) : undefined;
145
+ const errorText = step.error ? `, error: ${step.error}` : "";
146
+ lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);
147
+ const stepOutputPath = path.join(asyncDir, `output-${index}.log`);
148
+ if (stepOutputPath !== outputPath && fs.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
149
+ if (step.status === "running") {
150
+ lines.push(` Intercom target: ${(0, _intercomBridge.resolveSubagentIntercomTarget)(status.runId, step.agent, index)} (if registered)`);
151
+ }
152
+ }
153
+ if (status.sessionFile) lines.push(`Session: ${status.sessionFile}`);
154
+ if (status.state !== "running") {
155
+ lines.push(formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile));
156
+ }
157
+ if (fs.existsSync(logPath)) lines.push(`Log: ${logPath}`);
158
+ if (fs.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
159
+
160
+ return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "single", results: [] } };
161
+ }
162
+ }
163
+
164
+ if (resultPath) {
165
+ try {
166
+ const raw = fs.readFileSync(resultPath, "utf-8");
167
+ const data = JSON.parse(raw);
168
+ const status = data.success ? "complete" : data.state === "paused" || data.exitCode === 0 ? "paused" : "failed";
169
+ const runId = data.runId ?? data.id ?? resolvedId;
170
+ const lines = [`Run: ${runId}`, `State: ${status}`, `Result: ${resultPath}`];
171
+ const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
172
+ lines.push(formatResumeGuidance(runId, children, data.sessionFile));
173
+ if (data.summary) lines.push("", data.summary);
174
+ return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "single", results: [] } };
175
+ } catch (error) {
176
+ const message = error instanceof Error ? error.message : String(error);
177
+ return {
178
+ content: [{ type: "text", text: `Failed to read async result file: ${message}` }],
179
+ isError: true,
180
+ details: { mode: "single", results: [] }
181
+ };
182
+ }
183
+ }
184
+
185
+ return {
186
+ content: [{ type: "text", text: "Status file not found." }],
187
+ isError: true,
188
+ details: { mode: "single", results: [] }
189
+ };
190
+ } /* v9-502343157cae842f */
pip-tmp/jiti/background-stale-run-reconciler.52d9f1fc.mjs ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.checkPidLiveness = checkPidLiveness;exports.reconcileAsyncRun = reconcileAsyncRun;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+ var _atomicJson = await jitiImport("../../shared/atomic-json.ts");
4
+ var _types = await jitiImport("../../shared/types.ts");
5
+ var _parallelGroups = await jitiImport("./parallel-groups.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+ function getErrorMessage(error) {
40
+ return error instanceof Error ? error.message : String(error);
41
+ }
42
+
43
+ function isNotFoundError(error) {
44
+ return typeof error === "object" &&
45
+ error !== null &&
46
+ "code" in error &&
47
+ error.code === "ENOENT";
48
+ }
49
+
50
+ function appendJsonl(filePath, payload) {
51
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
52
+ fs.appendFileSync(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
53
+ }
54
+
55
+ function readStatusFile(asyncDir) {
56
+ const statusPath = path.join(asyncDir, "status.json");
57
+ let content;
58
+ try {
59
+ content = fs.readFileSync(statusPath, "utf-8");
60
+ } catch (error) {
61
+ if (isNotFoundError(error)) return null;
62
+ throw new Error(`Failed to read async status file '${statusPath}': ${getErrorMessage(error)}`, {
63
+ cause: error instanceof Error ? error : undefined
64
+ });
65
+ }
66
+ try {
67
+ return JSON.parse(content);
68
+ } catch (error) {
69
+ throw new Error(`Failed to parse async status file '${statusPath}': ${getErrorMessage(error)}`, {
70
+ cause: error instanceof Error ? error : undefined
71
+ });
72
+ }
73
+ }
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+ function readResultRepairData(resultPath) {
91
+ try {
92
+ const data = JSON.parse(fs.readFileSync(resultPath, "utf-8"));
93
+ const state = data.success ? "complete" : data.state === "paused" || data.exitCode === 0 ? "paused" : "failed";
94
+ return { state, ...(Array.isArray(data.results) ? { results: data.results } : {}) };
95
+ } catch (error) {
96
+ if (isNotFoundError(error)) return undefined;
97
+ throw new Error(`Failed to read async result file '${resultPath}': ${getErrorMessage(error)}`, {
98
+ cause: error instanceof Error ? error : undefined
99
+ });
100
+ }
101
+ }
102
+
103
+ function childState(overallState, child) {
104
+ if (child?.success === true) return "complete";
105
+ if (child?.success === false) return "failed";
106
+ return overallState;
107
+ }
108
+
109
+ function terminalStatusFromResult(status, resultPath, now) {
110
+ const repair = readResultRepairData(resultPath);
111
+ if (!repair) return undefined;
112
+ const steps = (status.steps ?? []).map((step, index) => {
113
+ if (step.status !== "running" && step.status !== "pending") return step;
114
+ const child = repair.results?.[index];
115
+ const state = childState(repair.state, child);
116
+ return {
117
+ ...step,
118
+ status: state === "complete" ? "complete" : state,
119
+ endedAt: step.endedAt ?? now,
120
+ durationMs: step.startedAt !== undefined && step.durationMs === undefined ? Math.max(0, now - step.startedAt) : step.durationMs,
121
+ exitCode: step.exitCode ?? (state === "complete" || state === "paused" ? 0 : 1),
122
+ error: state === "failed" ? step.error ?? child?.error : step.error,
123
+ sessionFile: step.sessionFile ?? child?.sessionFile,
124
+ model: step.model ?? child?.model,
125
+ attemptedModels: step.attemptedModels ?? child?.attemptedModels,
126
+ modelAttempts: step.modelAttempts ?? child?.modelAttempts
127
+ };
128
+ });
129
+ return {
130
+ ...status,
131
+ state: repair.state,
132
+ activityState: undefined,
133
+ lastUpdate: now,
134
+ endedAt: status.endedAt ?? now,
135
+ steps
136
+ };
137
+ }
138
+
139
+ function buildStartedStatus(asyncDir, startedRun, now) {
140
+ const startedAt = startedRun.startedAt ?? now;
141
+ const agents = startedRun.agents?.length ? startedRun.agents : ["subagent"];
142
+ const chainStepCount = startedRun.chainStepCount;
143
+ const parallelGroups = chainStepCount !== undefined ?
144
+ (0, _parallelGroups.normalizeParallelGroups)(startedRun.parallelGroups, agents.length, chainStepCount) :
145
+ [];
146
+ return {
147
+ runId: startedRun.runId || path.basename(asyncDir),
148
+ ...(startedRun.sessionId ? { sessionId: startedRun.sessionId } : {}),
149
+ mode: startedRun.mode ?? "single",
150
+ state: "running",
151
+ pid: startedRun.pid,
152
+ startedAt,
153
+ lastUpdate: now,
154
+ currentStep: 0,
155
+ ...(chainStepCount !== undefined ? { chainStepCount } : {}),
156
+ ...(parallelGroups.length ? { parallelGroups } : {}),
157
+ steps: agents.map((agent) => ({
158
+ agent,
159
+ status: "running",
160
+ startedAt
161
+ })),
162
+ ...(startedRun.sessionFile ? { sessionFile: startedRun.sessionFile } : {})
163
+ };
164
+ }
165
+
166
+ function buildFailedRepair(status, asyncDir, now, reason) {
167
+ const runId = status.runId || path.basename(asyncDir);
168
+ const pid = typeof status.pid === "number" ? status.pid : "unknown";
169
+ const message = reason ?? `Async runner process ${pid} exited or disappeared before writing a result. Marked run failed by stale-run reconciliation.`;
170
+ const steps = status.steps?.length ? status.steps : [{ agent: "subagent", status: "running" }];
171
+ const repairedSteps = steps.map((step) => step.status === "running" || step.status === "pending" ?
172
+ {
173
+ ...step,
174
+ status: "failed",
175
+ activityState: undefined,
176
+ endedAt: step.endedAt ?? now,
177
+ durationMs: step.startedAt !== undefined && step.durationMs === undefined ? Math.max(0, now - step.startedAt) : step.durationMs,
178
+ exitCode: step.exitCode ?? 1,
179
+ error: step.error ?? message
180
+ } :
181
+ step);
182
+ const repairedStatus = {
183
+ ...status,
184
+ state: "failed",
185
+ activityState: undefined,
186
+ lastUpdate: now,
187
+ endedAt: now,
188
+ steps: repairedSteps
189
+ };
190
+ const resultAgent = repairedSteps[status.currentStep ?? 0]?.agent ?? repairedSteps[0]?.agent ?? "subagent";
191
+ return {
192
+ status: repairedStatus,
193
+ message,
194
+ result: {
195
+ id: runId,
196
+ agent: resultAgent,
197
+ mode: status.mode,
198
+ success: false,
199
+ state: "failed",
200
+ summary: message,
201
+ results: repairedSteps.map((step) => ({
202
+ agent: step.agent,
203
+ output: step.status === "complete" || step.status === "completed" ? "" : message,
204
+ error: step.status === "complete" || step.status === "completed" ? undefined : step.error ?? message,
205
+ success: step.status === "complete" || step.status === "completed",
206
+ model: step.model,
207
+ attemptedModels: step.attemptedModels,
208
+ modelAttempts: step.modelAttempts,
209
+ sessionFile: step.sessionFile
210
+ })),
211
+ exitCode: 1,
212
+ timestamp: now,
213
+ durationMs: Math.max(0, now - status.startedAt),
214
+ asyncDir,
215
+ sessionId: status.sessionId,
216
+ sessionFile: status.sessionFile
217
+ }
218
+ };
219
+ }
220
+
221
+ function writeFailedRepair(asyncDir, status, resultPath, now, reason) {
222
+ const repair = buildFailedRepair(status, asyncDir, now, reason);
223
+ (0, _atomicJson.writeAtomicJson)(resultPath, repair.result);
224
+ (0, _atomicJson.writeAtomicJson)(path.join(asyncDir, "status.json"), repair.status);
225
+ appendJsonl(path.join(asyncDir, "events.jsonl"), {
226
+ type: "subagent.run.repaired_stale",
227
+ ts: now,
228
+ runId: repair.status.runId,
229
+ pid: status.pid,
230
+ resultPath,
231
+ message: repair.message
232
+ });
233
+ return { status: repair.status, repaired: true, resultPath, message: repair.message };
234
+ }
235
+
236
+ function checkPidLiveness(pid, kill = process.kill) {
237
+ try {
238
+ kill(pid, 0);
239
+ return "alive";
240
+ } catch (error) {
241
+ const code = typeof error === "object" && error !== null && "code" in error ?
242
+ error.code :
243
+ undefined;
244
+ if (code === "ESRCH") return "dead";
245
+ if (code === "EPERM") return "unknown";
246
+ return "unknown";
247
+ }
248
+ }
249
+
250
+ function reconcileAsyncRun(asyncDir, options = {}) {
251
+ const now = options.now?.() ?? Date.now();
252
+ const status = readStatusFile(asyncDir);
253
+ const startedStatus = !status && options.startedRun ? buildStartedStatus(asyncDir, options.startedRun, now) : undefined;
254
+ const effectiveStatus = status ?? startedStatus;
255
+ if (!effectiveStatus) return { status: null, repaired: false };
256
+
257
+ const runId = effectiveStatus.runId || path.basename(asyncDir);
258
+ const resultPath = path.join(options.resultsDir ?? _types.RESULTS_DIR, `${runId}.json`);
259
+ if (fs.existsSync(resultPath)) {
260
+ const terminalStatus = effectiveStatus.state === "running" || effectiveStatus.state === "queued" ?
261
+ terminalStatusFromResult(effectiveStatus, resultPath, now) :
262
+ undefined;
263
+ if (terminalStatus) {
264
+ (0, _atomicJson.writeAtomicJson)(path.join(asyncDir, "status.json"), terminalStatus);
265
+ return { status: terminalStatus, repaired: true, resultPath, message: "Existing async result file was used to repair stale running status." };
266
+ }
267
+ return { status: effectiveStatus, repaired: false, resultPath };
268
+ }
269
+
270
+ if (effectiveStatus.state !== "running" || typeof effectiveStatus.pid !== "number") {
271
+ return { status: status ?? null, repaired: false, resultPath };
272
+ }
273
+
274
+ if (!status) {
275
+ const startedAt = options.startedRun?.startedAt ?? effectiveStatus.startedAt;
276
+ if (now - startedAt < (options.missingStatusGraceMs ?? 1000)) {
277
+ return { status: null, repaired: false, resultPath };
278
+ }
279
+ }
280
+
281
+ const liveness = checkPidLiveness(effectiveStatus.pid, options.kill);
282
+ if (liveness !== "dead") {
283
+ const staleAfterMs = options.staleAlivePidMs ?? 24 * 60 * 60 * 1000;
284
+ const lastUpdate = effectiveStatus.lastUpdate ?? effectiveStatus.startedAt;
285
+ if (now - lastUpdate <= staleAfterMs) return { status: status ?? null, repaired: false, resultPath };
286
+ const message = `Async runner process ${effectiveStatus.pid} still has a live PID, but status has not updated for ${now - lastUpdate}ms. Marked run failed by stale-run reconciliation because PID ownership cannot be verified.`;
287
+ return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, message);
288
+ }
289
+
290
+ return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now);
291
+ } /* v9-0a417b7586570d91 */
pip-tmp/jiti/background-top-level-async.2257c9e0.mjs ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.applyForceTopLevelAsyncOverride = applyForceTopLevelAsyncOverride;
2
+
3
+
4
+
5
+
6
+ function applyForceTopLevelAsyncOverride(
7
+ params,
8
+ depth,
9
+ forceTopLevelAsync)
10
+ {
11
+ if (!(depth === 0 && forceTopLevelAsync)) return params;
12
+ return { ...params, async: true, clarify: false };
13
+ } /* v9-477ca4de2563a7d0 */
pip-tmp/jiti/extension-control-notices.20f5052e.mjs ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.SUBAGENT_CONTROL_MESSAGE_TYPE = void 0;exports.clearPendingForegroundControlNotices = clearPendingForegroundControlNotices;exports.controlNoticeTarget = controlNoticeTarget;exports.formatSubagentControlNotice = formatSubagentControlNotice;exports.handleSubagentControlNotice = handleSubagentControlNotice;
2
+ var _subagentControl = await jitiImport("../runs/shared/subagent-control.ts");
3
+
4
+
5
+ const SUBAGENT_CONTROL_MESSAGE_TYPE = exports.SUBAGENT_CONTROL_MESSAGE_TYPE = "subagent_control_notice";
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+ function controlNoticeTarget(details) {
16
+ return details.childIntercomTarget;
17
+ }
18
+
19
+ function formatSubagentControlNotice(details, content) {
20
+ return details.noticeText ?? content ?? (0, _subagentControl.formatControlNoticeMessage)(details.event, controlNoticeTarget(details));
21
+ }
22
+
23
+ function noticeTimerKey(details) {
24
+ const childIntercomTarget = controlNoticeTarget(details);
25
+ return `${details.event.runId}:${(0, _subagentControl.controlNotificationKey)(details.event, childIntercomTarget)}`;
26
+ }
27
+
28
+ function clearPendingForegroundControlNotices(state, runId) {
29
+ const pending = state.pendingForegroundControlNotices;
30
+ if (!pending) return;
31
+ for (const [key, timer] of pending) {
32
+ if (runId !== undefined && !key.startsWith(`${runId}:`)) continue;
33
+ clearTimeout(timer);
34
+ pending.delete(key);
35
+ }
36
+ }
37
+
38
+ function deliverControlNotice(input)
39
+
40
+
41
+
42
+ {
43
+ const childIntercomTarget = controlNoticeTarget(input.details);
44
+ const key = (0, _subagentControl.controlNotificationKey)(input.details.event, childIntercomTarget);
45
+ if (input.visibleControlNotices.has(key)) return;
46
+ input.visibleControlNotices.add(key);
47
+ const noticeText = input.details.noticeText ?? (0, _subagentControl.formatControlNoticeMessage)(input.details.event, childIntercomTarget);
48
+ input.pi.sendMessage(
49
+ {
50
+ customType: SUBAGENT_CONTROL_MESSAGE_TYPE,
51
+ content: noticeText,
52
+ display: true,
53
+ details: { ...input.details, childIntercomTarget, noticeText }
54
+ },
55
+ { triggerTurn: input.details.source !== "foreground" }
56
+ );
57
+ }
58
+
59
+ function isForegroundNoticeStillActionable(state, details) {
60
+ const control = state.foregroundControls.get(details.event.runId);
61
+ if (!control) return false;
62
+ if (control.currentAgent && control.currentAgent !== details.event.agent) return false;
63
+ if (details.event.index !== undefined && control.currentIndex !== details.event.index) return false;
64
+ return control.currentActivityState === "needs_attention";
65
+ }
66
+
67
+ function handleSubagentControlNotice(input)
68
+
69
+
70
+
71
+
72
+
73
+ {
74
+ if (!input.details?.event || input.details.event.type === "active_long_running") return;
75
+ if (input.details.source !== "foreground") {
76
+ deliverControlNotice(input);
77
+ return;
78
+ }
79
+
80
+ const pending = input.state.pendingForegroundControlNotices ?? new Map();
81
+ input.state.pendingForegroundControlNotices = pending;
82
+ const timerKey = noticeTimerKey(input.details);
83
+ const existing = pending.get(timerKey);
84
+ if (existing) clearTimeout(existing);
85
+ const timer = setTimeout(() => {
86
+ pending.delete(timerKey);
87
+ if (!isForegroundNoticeStillActionable(input.state, input.details)) return;
88
+ deliverControlNotice(input);
89
+ }, input.foregroundDelayMs ?? 1000);
90
+ timer.unref?.();
91
+ pending.set(timerKey, timer);
92
+ } /* v9-d318b884735a249e */
pip-tmp/jiti/extension-doctor.43600346.mjs ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildDoctorReport = buildDoctorReport;var fs = _interopRequireWildcard(await jitiImport("node:fs"));
2
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
3
+ var _agents = await jitiImport("../agents/agents.ts");
4
+ var _asyncExecution = await jitiImport("../runs/background/async-execution.ts");
5
+ var _intercomBridge = await jitiImport("../intercom/intercom-bridge.ts");
6
+ var _skills = await jitiImport("../agents/skills.ts");
7
+ var _types = await jitiImport("../shared/types.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+ const DEFAULT_PATHS = {
46
+ tempRootDir: _types.TEMP_ROOT_DIR,
47
+ asyncDir: _types.ASYNC_DIR,
48
+ resultsDir: _types.RESULTS_DIR,
49
+ chainRunsDir: _types.CHAIN_RUNS_DIR
50
+ };
51
+
52
+ const DEFAULT_DEPS = {
53
+ isAsyncAvailable: _asyncExecution.isAsyncAvailable,
54
+ discoverAgentsAll: _agents.discoverAgentsAll,
55
+ discoverAvailableSkills: _skills.discoverAvailableSkills,
56
+ diagnoseIntercomBridge: _intercomBridge.diagnoseIntercomBridge
57
+ };
58
+
59
+ function errorText(error) {
60
+ return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
61
+ }
62
+
63
+ function lineFromCheck(label, check) {
64
+ try {
65
+ return check();
66
+ } catch (error) {
67
+ return `- ${label}: failed — ${errorText(error)}`;
68
+ }
69
+ }
70
+
71
+ function formatExistingDirectory(label, dirPath) {
72
+ try {
73
+ if (!fs.existsSync(dirPath)) return `- ${label}: missing (${dirPath})`;
74
+ const stats = fs.statSync(dirPath);
75
+ if (!stats.isDirectory()) throw new Error(`not a directory: ${dirPath}`);
76
+ fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
77
+ return `- ${label}: ok (${dirPath})`;
78
+ } catch (error) {
79
+ return `- ${label}: failed (${dirPath}) — ${errorText(error)}`;
80
+ }
81
+ }
82
+
83
+ function formatSourceCounts(counts) {
84
+ return `builtin ${counts.builtin}, user ${counts.user}, project ${counts.project}`;
85
+ }
86
+
87
+ function formatSkillSourceCounts(skills) {
88
+ const counts = new Map();
89
+ for (const skill of skills) counts.set(skill.source, (counts.get(skill.source) ?? 0) + 1);
90
+ const ordered = [
91
+ "project",
92
+ "project-settings",
93
+ "project-package",
94
+ "user",
95
+ "user-settings",
96
+ "user-package",
97
+ "extension",
98
+ "builtin",
99
+ "unknown"];
100
+
101
+ const parts = ordered.
102
+ map((source) => `${source} ${counts.get(source) ?? 0}`).
103
+ filter((part) => !part.endsWith(" 0"));
104
+ return parts.length > 0 ? parts.join(", ") : "none";
105
+ }
106
+
107
+ function formatConfiguredSessionDir(input) {
108
+ if (input.requestedSessionDir) {
109
+ return path.resolve(input.expandTilde?.(input.requestedSessionDir) ?? input.requestedSessionDir);
110
+ }
111
+ if (input.config.defaultSessionDir) {
112
+ return path.resolve(input.expandTilde?.(input.config.defaultSessionDir) ?? input.config.defaultSessionDir);
113
+ }
114
+ return "not configured";
115
+ }
116
+
117
+ function formatSessionLines(input) {
118
+ const sessionFile = input.currentSessionFile ?? null;
119
+ const lines = [
120
+ lineFromCheck("configured session dir", () => `- configured session dir: ${formatConfiguredSessionDir(input)}`),
121
+ `- current session file: ${sessionFile ?? "not available"}`,
122
+ `- current session dir: ${sessionFile ? path.dirname(sessionFile) : "not available"}`,
123
+ `- current session id: ${input.currentSessionId ?? input.state.currentSessionId ?? "not available"}`];
124
+
125
+ if (input.sessionError) lines.push(`- session manager: failed — ${input.sessionError}`);
126
+ return lines;
127
+ }
128
+
129
+ function formatDiscovery(input, deps) {
130
+ return [
131
+ lineFromCheck("agents/chains", () => {
132
+ const discovered = deps.discoverAgentsAll(input.cwd);
133
+ const agentCounts = {
134
+ builtin: discovered.builtin.length,
135
+ user: discovered.user.length,
136
+ project: discovered.project.length
137
+ };
138
+ const chainCounts = discovered.chains.reduce((counts, chain) => {
139
+ counts[chain.source] += 1;
140
+ return counts;
141
+ }, { builtin: 0, user: 0, project: 0 });
142
+ return [
143
+ `- agents: total ${agentCounts.builtin + agentCounts.user + agentCounts.project} (${formatSourceCounts(agentCounts)})`,
144
+ `- chains: total ${discovered.chains.length} (${formatSourceCounts(chainCounts)})`].
145
+ join("\n");
146
+ }),
147
+ lineFromCheck("skills", () => {
148
+ const skills = deps.discoverAvailableSkills(input.cwd);
149
+ return `- skills: total ${skills.length} (${formatSkillSourceCounts(skills)})`;
150
+ })];
151
+
152
+ }
153
+
154
+ function formatIntercomDiagnostic(diagnostic, context) {
155
+ const lines = [
156
+ `- bridge: ${diagnostic.active ? "active" : "inactive"}${diagnostic.reason ? ` (${diagnostic.reason})` : ""}`,
157
+ `- mode: ${diagnostic.mode}; context: ${context ?? "unspecified"}`,
158
+ `- orchestrator target: ${diagnostic.orchestratorTarget ?? "not available"}`,
159
+ `- pi-intercom: ${diagnostic.piIntercomAvailable ? "available" : "unavailable"} at ${diagnostic.extensionDir}`];
160
+
161
+ if (diagnostic.configPath && diagnostic.intercomConfigEnabled !== undefined) {
162
+ lines.push(`- intercom config: ${diagnostic.intercomConfigEnabled === false ? "disabled" : "enabled or absent"} (${diagnostic.configPath})`);
163
+ }
164
+ if (diagnostic.intercomConfigError) {
165
+ lines.push(`- intercom config warning: ${diagnostic.intercomConfigError}; runtime assumes enabled`);
166
+ }
167
+ return lines;
168
+ }
169
+
170
+ function buildDoctorReport(input) {
171
+ const paths = input.paths ?? DEFAULT_PATHS;
172
+ const deps = { ...DEFAULT_DEPS, ...input.deps };
173
+ const lines = [
174
+ "Subagents doctor report",
175
+ "",
176
+ "Runtime",
177
+ `- cwd: ${input.cwd}`,
178
+ lineFromCheck("async support", () => `- async support: ${deps.isAsyncAvailable() ? "available" : "unavailable"}`),
179
+ ...formatSessionLines(input),
180
+ "",
181
+ "Filesystem",
182
+ formatExistingDirectory("temp root", paths.tempRootDir),
183
+ formatExistingDirectory("async runs", paths.asyncDir),
184
+ formatExistingDirectory("results", paths.resultsDir),
185
+ formatExistingDirectory("chain runs", paths.chainRunsDir),
186
+ "",
187
+ "Discovery",
188
+ ...formatDiscovery(input, deps),
189
+ "",
190
+ "Intercom bridge",
191
+ ...lineFromCheck("intercom bridge", () => formatIntercomDiagnostic(deps.diagnoseIntercomBridge({
192
+ config: input.config.intercomBridge,
193
+ context: input.context,
194
+ orchestratorTarget: input.orchestratorTarget,
195
+ cwd: input.cwd
196
+ }), input.context).join("\n")).split("\n")];
197
+
198
+ return lines.join("\n");
199
+ } /* v9-0e2e3df69035f087 */
pip-tmp/jiti/extension-index.80330c2b.mjs ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = registerSubagentExtension;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
16
+ var os = _interopRequireWildcard(await jitiImport("node:os"));
17
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
18
+
19
+
20
+ var _piTui = await jitiImport("@mariozechner/pi-tui");
21
+ var _agents = await jitiImport("../agents/agents.ts");
22
+ var _artifacts = await jitiImport("../shared/artifacts.ts");
23
+ var _sessionIdentity = await jitiImport("../shared/session-identity.ts");
24
+ var _settings = await jitiImport("../shared/settings.ts");
25
+ var _render = await jitiImport("../tui/render.ts");
26
+ var _schemas = await jitiImport("./schemas.ts");
27
+ var _subagentExecutor = await jitiImport("../runs/foreground/subagent-executor.ts");
28
+ var _asyncJobTracker = await jitiImport("../runs/background/async-job-tracker.ts");
29
+ var _resultWatcher = await jitiImport("../runs/background/result-watcher.ts");
30
+ var _slashCommands = await jitiImport("../slash/slash-commands.ts");
31
+ var _promptTemplateBridge = await jitiImport("../slash/prompt-template-bridge.ts");
32
+ var _slashBridge = await jitiImport("../slash/slash-bridge.ts");
33
+ var _slashLiveState = await jitiImport("../slash/slash-live-state.ts");
34
+
35
+ var _notify = _interopRequireDefault(await jitiImport("../runs/background/notify.ts"));
36
+ var _piArgs = await jitiImport("../runs/shared/pi-args.ts");
37
+ var _formatters = await jitiImport("../shared/formatters.ts");
38
+ var _types = await jitiImport("../shared/types.ts");
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+ var _controlNotices = await jitiImport("./control-notices.ts");function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /**
52
+ * Subagent Tool
53
+ *
54
+ * Full-featured subagent with sync and async modes.
55
+ * - Sync (default): Streams output, renders markdown, tracks usage
56
+ * - Async: Background execution, emits events when done
57
+ *
58
+ * Modes: single (agent + task), parallel (tasks[]), chain (chain[] with {previous})
59
+ * Toggle: async parameter (default: false, configurable via config.json)
60
+ *
61
+ * Config file: ~/.pi/agent/extensions/subagent/config.json
62
+ * { "asyncByDefault": true, "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
63
+ */ /**
64
+ * Derive subagent session base directory from parent session file.
65
+ * If parent session is ~/.pi/agent/sessions/abc123.jsonl,
66
+ * returns ~/.pi/agent/sessions/abc123/ as the base.
67
+ * Callers add runId to create the actual session root: abc123/{runId}/
68
+ * Falls back to a unique temp directory if no parent session.
69
+ */function getSubagentSessionRoot(parentSessionFile) {if (parentSessionFile) {const baseName = path.basename(parentSessionFile, ".jsonl");const sessionsDir = path.dirname(parentSessionFile);
70
+ return path.join(sessionsDir, baseName);
71
+ }
72
+ return fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-session-"));
73
+ }
74
+
75
+ function loadConfig() {
76
+ const configPath = path.join(os.homedir(), ".pi", "agent", "extensions", "subagent", "config.json");
77
+ try {
78
+ if (fs.existsSync(configPath)) {
79
+ return JSON.parse(fs.readFileSync(configPath, "utf-8"));
80
+ }
81
+ } catch (error) {
82
+ console.error(`Failed to load subagent config from '${configPath}':`, error);
83
+ }
84
+ return {};
85
+ }
86
+
87
+ function expandTilde(p) {
88
+ return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
89
+ }
90
+
91
+ /**
92
+ * Create a directory and verify it is actually accessible.
93
+ * On Windows with Azure AD/Entra ID, directories created shortly after
94
+ * wake-from-sleep can end up with broken NTFS ACLs (null DACL) when the
95
+ * cloud SID cannot be resolved without network connectivity. This leaves
96
+ * the directory completely inaccessible to the creating user.
97
+ */
98
+ function ensureAccessibleDir(dirPath) {
99
+ fs.mkdirSync(dirPath, { recursive: true });
100
+ try {
101
+ fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
102
+ } catch {
103
+ try {
104
+ fs.rmSync(dirPath, { recursive: true, force: true });
105
+ } catch {
106
+
107
+ // Best effort: retry mkdir/access even if cleanup fails.
108
+ }fs.mkdirSync(dirPath, { recursive: true });
109
+ fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
110
+ }
111
+ }
112
+
113
+ function isSlashResultRunning(result) {
114
+ return result.details?.progress?.some((entry) => entry.status === "running") ||
115
+ result.details?.results.some((entry) => entry.progress?.status === "running") ||
116
+ false;
117
+ }
118
+
119
+ function isSlashResultError(result) {
120
+ return result.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || false;
121
+ }
122
+
123
+ function isStaleExtensionContextError(error) {
124
+ return error instanceof Error && error.message.includes("Extension context no longer active");
125
+ }
126
+
127
+ function rebuildSlashResultContainer(
128
+ container,
129
+ result,
130
+ options,
131
+ theme)
132
+ {
133
+ container.clear();
134
+ container.addChild(new _piTui.Spacer(1));
135
+ const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
136
+ const box = new _piTui.Box(1, 1, (text) => theme.bg(boxTheme, text));
137
+ box.addChild((0, _render.renderSubagentResult)(result, options, theme));
138
+ container.addChild(box);
139
+ }
140
+
141
+ function createSlashResultComponent(
142
+ details,
143
+ options,
144
+ theme,
145
+ requestRender)
146
+ {
147
+ const container = new _piTui.Container();
148
+ const animationState = {};
149
+ let lastVersion = -1;
150
+ container.render = (width) => {
151
+ const snapshot = (0, _slashLiveState.getSlashRenderableSnapshot)(details);
152
+ (0, _render.syncResultAnimation)(snapshot.result, { state: animationState, invalidate: requestRender });
153
+ if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
154
+ lastVersion = snapshot.version;
155
+ rebuildSlashResultContainer(container, snapshot.result, options, theme);
156
+ }
157
+ return _piTui.Container.prototype.render.call(container, width);
158
+ };
159
+ return container;
160
+ }
161
+
162
+ function parseSubagentNotifyContent(content) {
163
+ const lines = content.split("\n");
164
+ const header = lines[0] ?? "";
165
+ const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
166
+ if (!match) return undefined;
167
+ const body = lines.slice(2);
168
+ let sessionIndex = -1;
169
+ for (let i = body.length - 1; i >= 1; i--) {
170
+ if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i])) {
171
+ sessionIndex = i;
172
+ break;
173
+ }
174
+ }
175
+ const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
176
+ const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
177
+ const resultPreview = resultLines.join("\n").trim() || "(no output)";
178
+ let sessionLabel;
179
+ let sessionValue;
180
+ if (sessionLine) {
181
+ const separator = sessionLine.indexOf(":");
182
+ sessionLabel = sessionLine.slice(0, separator).toLowerCase();
183
+ sessionValue = sessionLine.slice(separator + 1).trim();
184
+ }
185
+ return {
186
+ agent: match[2],
187
+ status: match[1],
188
+ ...(match[3] ? { taskInfo: match[3] } : {}),
189
+ resultPreview,
190
+ ...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {})
191
+ };
192
+ }
193
+
194
+ class SubagentControlNoticeComponent {
195
+ constructor(
196
+ details,
197
+ theme)
198
+ {this.details = details;this.theme = theme;}
199
+
200
+ invalidate() {}
201
+
202
+ render(width) {
203
+ const eventLabel = this.details.event.type.replaceAll("_", " ");
204
+ if (width < 3) return [(0, _piTui.truncateToWidth)(`Subagent ${eventLabel}`, width)];
205
+ const bodyWidth = Math.max(1, width - 2);
206
+ const borderChar = "─";
207
+ const header = ` ⚠ Subagent ${eventLabel}: ${this.details.event.agent} `;
208
+ const headerText = (0, _piTui.truncateToWidth)(header, bodyWidth, "");
209
+ const headerPadding = Math.max(0, bodyWidth - (0, _piTui.visibleWidth)(headerText));
210
+ const lines = [this.theme.fg("accent", `╭${headerText}${borderChar.repeat(headerPadding)}╮`)];
211
+
212
+ for (const line of (0, _piTui.wrapTextWithAnsi)((0, _controlNotices.formatSubagentControlNotice)(this.details), bodyWidth)) {
213
+ const text = (0, _piTui.truncateToWidth)(line, bodyWidth, "");
214
+ const padding = Math.max(0, bodyWidth - (0, _piTui.visibleWidth)(text));
215
+ lines.push(this.theme.fg("accent", `│${text}${" ".repeat(padding)}│`));
216
+ }
217
+ lines.push(this.theme.fg("accent", `╰${borderChar.repeat(bodyWidth)}╯`));
218
+ return lines;
219
+ }
220
+ }
221
+
222
+ function registerSubagentExtension(pi) {
223
+ if (process.env[_piArgs.SUBAGENT_CHILD_ENV] === "1") return;
224
+ const globalStore = globalThis;
225
+ const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
226
+ const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
227
+ if (typeof previousRuntimeCleanup === "function") {
228
+ try {
229
+ previousRuntimeCleanup();
230
+ } catch {
231
+
232
+ // Best effort cleanup for stale timers from an older reload.
233
+ }}
234
+
235
+ ensureAccessibleDir(_types.RESULTS_DIR);
236
+ ensureAccessibleDir(_types.ASYNC_DIR);
237
+ (0, _settings.cleanupOldChainDirs)();
238
+
239
+ const config = loadConfig();
240
+ const asyncByDefault = config.asyncByDefault === true;
241
+ const tempArtifactsDir = (0, _artifacts.getArtifactsDir)(null);
242
+ (0, _artifacts.cleanupAllArtifactDirs)(_types.DEFAULT_ARTIFACT_CONFIG.cleanupDays);
243
+
244
+ const state = {
245
+ baseCwd: process.cwd(),
246
+ currentSessionId: null,
247
+ asyncJobs: new Map(),
248
+ foregroundRuns: new Map(),
249
+ foregroundControls: new Map(),
250
+ lastForegroundControlId: null,
251
+ pendingForegroundControlNotices: new Map(),
252
+ cleanupTimers: new Map(),
253
+ lastUiContext: null,
254
+ poller: null,
255
+ completionSeen: new Map(),
256
+ watcher: null,
257
+ watcherRestartTimer: null,
258
+ resultFileCoalescer: {
259
+ schedule: () => false,
260
+ clear: () => {}
261
+ }
262
+ };
263
+
264
+ const { startResultWatcher, primeExistingResults, stopResultWatcher } = (0, _resultWatcher.createResultWatcher)(
265
+ pi,
266
+ state,
267
+ _types.RESULTS_DIR,
268
+ 10 * 60 * 1000
269
+ );
270
+ startResultWatcher();
271
+ primeExistingResults();
272
+
273
+ const runtimeCleanup = () => {
274
+ (0, _render.stopWidgetAnimation)();
275
+ (0, _render.stopResultAnimations)();
276
+ stopResultWatcher();
277
+ (0, _controlNotices.clearPendingForegroundControlNotices)(state);
278
+ if (state.poller) {
279
+ clearInterval(state.poller);
280
+ state.poller = null;
281
+ }
282
+ };
283
+ globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
284
+
285
+ const { ensurePoller, handleStarted, handleComplete, resetJobs } = (0, _asyncJobTracker.createAsyncJobTracker)(pi, state, _types.ASYNC_DIR);
286
+ const executor = (0, _subagentExecutor.createSubagentExecutor)({
287
+ pi,
288
+ state,
289
+ config,
290
+ asyncByDefault,
291
+ tempArtifactsDir,
292
+ getSubagentSessionRoot,
293
+ expandTilde,
294
+ discoverAgents: _agents.discoverAgents
295
+ });
296
+
297
+ pi.registerMessageRenderer(_types.SLASH_RESULT_TYPE, (message, options, theme) => {
298
+ const details = (0, _slashLiveState.resolveSlashMessageDetails)(message.details);
299
+ if (!details) return undefined;
300
+ return createSlashResultComponent(details, options, theme, () => state.lastUiContext?.ui.requestRender?.());
301
+ });
302
+
303
+ pi.registerMessageRenderer("subagent-notify", (message, options, theme) => {
304
+ const content = typeof message.content === "string" ? message.content : "";
305
+ const details = message.details ?? parseSubagentNotifyContent(content);
306
+ if (!details) return new _piTui.Text(content, 0, 0);
307
+ const icon = details.status === "completed" ?
308
+ theme.fg("success", "✓") :
309
+ details.status === "paused" ?
310
+ theme.fg("warning", "■") :
311
+ theme.fg("error", "✗");
312
+ const parts = [];
313
+ if (details.taskInfo) parts.push(details.taskInfo);
314
+ if (details.durationMs !== undefined) parts.push((0, _formatters.formatDuration)(details.durationMs));
315
+ let text = `${icon} ${theme.bold(details.agent)} ${theme.fg("dim", details.status)}`;
316
+ if (parts.length > 0) text += ` ${theme.fg("dim", "·")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "·")} `)}`;
317
+ const trimmedPreview = details.resultPreview.trim();
318
+ const previewLines = options.expanded ?
319
+ trimmedPreview.split("\n").filter((line) => line.trim()) :
320
+ [trimmedPreview.split("\n", 1)[0] ?? ""].filter((line) => line.trim());
321
+ for (const line of previewLines.length > 0 ? previewLines : ["(no output)"]) {
322
+ text += `\n ${theme.fg("dim", `⎿ ${line}`)}`;
323
+ }
324
+ if (!options.expanded && trimmedPreview.includes("\n")) {
325
+ text += `\n ${theme.fg("dim", "Ctrl+O full notification")}`;
326
+ }
327
+ if (details.sessionLabel && details.sessionValue) {
328
+ text += `\n ${theme.fg("muted", `${details.sessionLabel}: ${(0, _formatters.shortenPath)(details.sessionValue)}`)}`;
329
+ }
330
+ return new _piTui.Text(text, 0, 0);
331
+ });
332
+
333
+ pi.registerMessageRenderer(_controlNotices.SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
334
+ const details = message.details;
335
+ if (!details?.event) return undefined;
336
+ const content = typeof message.content === "string" ? message.content : undefined;
337
+ return new SubagentControlNoticeComponent({ ...details, noticeText: (0, _controlNotices.formatSubagentControlNotice)(details, content) }, theme);
338
+ });
339
+
340
+ const executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) => {
341
+ if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
342
+ return executor.execute(id, params, signal, onUpdate, ctx);
343
+ };
344
+
345
+ const slashBridge = (0, _slashBridge.registerSlashSubagentBridge)({
346
+ events: pi.events,
347
+ getContext: () => state.lastUiContext,
348
+ execute: (id, params, signal, onUpdate, ctx) =>
349
+ executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
350
+ });
351
+
352
+ const promptTemplateBridge = (0, _promptTemplateBridge.registerPromptTemplateDelegationBridge)({
353
+ events: pi.events,
354
+ getContext: () => state.lastUiContext,
355
+ execute: async (requestId, request, signal, ctx, onUpdate) => {
356
+ if (request.tasks && request.tasks.length > 0) {
357
+ return executeSubagentCollapsed(
358
+ requestId,
359
+ {
360
+ tasks: request.tasks,
361
+ context: request.context,
362
+ cwd: request.cwd,
363
+ worktree: request.worktree,
364
+ async: false,
365
+ clarify: false
366
+ },
367
+ signal,
368
+ onUpdate,
369
+ ctx
370
+ );
371
+ }
372
+ return executeSubagentCollapsed(
373
+ requestId,
374
+ {
375
+ agent: request.agent,
376
+ task: request.task,
377
+ context: request.context,
378
+ cwd: request.cwd,
379
+ model: request.model,
380
+ async: false,
381
+ clarify: false
382
+ },
383
+ signal,
384
+ onUpdate,
385
+ ctx
386
+ );
387
+ }
388
+ });
389
+
390
+ function effectiveParallelTaskCount(tasks) {
391
+ if (!tasks || tasks.length === 0) return 0;
392
+ return tasks.reduce((total, task) => {
393
+ const count = typeof task.count === "number" && Number.isInteger(task.count) && task.count >= 1 ? task.count : 1;
394
+ return total + count;
395
+ }, 0);
396
+ }
397
+
398
+ const tool = {
399
+ name: "subagent",
400
+ label: "Subagent",
401
+ description: `Delegate to subagents or manage agent definitions.
402
+
403
+ EXECUTION (use exactly ONE mode):
404
+ • Before executing, use { action: "list" } to inspect configured agents/chains. Only execute agents listed as executable/non-disabled.
405
+ • SINGLE: { agent, task? } - one task; omit task for self-contained agents
406
+ • CHAIN: { chain: [{agent:"agent-a"}, {parallel:[{agent:"agent-b",count:3}]}] } - sequential pipeline with optional parallel fan-out
407
+ • PARALLEL: { tasks: [{agent,task,count?,output?,reads?,progress?}, ...], concurrency?: number, worktree?: true } - concurrent execution (worktree: isolate each task in a git worktree)
408
+ • Optional context: { context: "fresh" | "fork" } (default: if any requested agent has defaultContext: "fork", the whole invocation uses fork; otherwise "fresh"; inspect agent defaults via { action: "list" })
409
+
410
+ CHAIN TEMPLATE VARIABLES (use in task strings):
411
+ • {task} - The original task/request from the user
412
+ • {previous} - Text response from the previous step (empty for first step)
413
+ • {chain_dir} - Shared directory for chain files (e.g., <tmpdir>/pi-subagents-<scope>/chain-runs/abc123/)
414
+
415
+ Example: { chain: [{agent:"agent-a", task:"Analyze {task}"}, {agent:"agent-b", task:"Plan based on {previous}"}] }
416
+
417
+ MANAGEMENT (use action field, omit agent/task/chain/tasks):
418
+ • { action: "list" } - discover executable agents/chains
419
+ • { action: "get", agent: "name" } - full detail; packaged agents use dotted runtime names like "package.agent"
420
+ • { action: "create", config: { name: "custom-agent", package: "code-analysis", systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, defaultContext, ... } }
421
+ • { action: "update", agent: "code-analysis.custom-agent", config: { package: "analysis", ... } } - merge
422
+ • { action: "delete", agent: "code-analysis.custom-agent" }
423
+ • Use chainName for chain operations; packaged chains also use dotted runtime names
424
+
425
+ CONTROL:
426
+ • { action: "status", id: "..." } - inspect an async/background run by id or prefix
427
+ • { action: "interrupt", id?: "..." } - soft-interrupt the current child turn and leave the run paused
428
+ • { action: "resume", id: "...", message: "...", index?: 0 } - follow up with a live async child or revive a completed async/foreground child from its session
429
+
430
+ DIAGNOSTICS:
431
+ • { action: "doctor" } - read-only report for runtime paths, discovery, sessions, and intercom`,
432
+ parameters: _schemas.SubagentParams,
433
+
434
+ execute(id, params, signal, onUpdate, ctx) {
435
+ return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
436
+ },
437
+
438
+ renderCall(args, theme) {
439
+ if (args.action) {
440
+ const target = args.agent || args.chainName || "";
441
+ return new _piTui.Text(
442
+ `${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`,
443
+ 0, 0
444
+ );
445
+ }
446
+ const isParallel = (args.tasks?.length ?? 0) > 0;
447
+ const parallelCount = effectiveParallelTaskCount(args.tasks);
448
+ const asyncLabel = args.async === true && !isParallel ? theme.fg("warning", " [async]") : "";
449
+ if (args.chain?.length)
450
+ return new _piTui.Text(
451
+ `${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})${asyncLabel}`,
452
+ 0,
453
+ 0
454
+ );
455
+ if (isParallel)
456
+ return new _piTui.Text(
457
+ `${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})`,
458
+ 0,
459
+ 0
460
+ );
461
+ return new _piTui.Text(
462
+ `${theme.fg("toolTitle", theme.bold("subagent "))}${theme.fg("accent", args.agent || "?")}${asyncLabel}`,
463
+ 0,
464
+ 0
465
+ );
466
+ },
467
+
468
+ renderResult(result, options, theme, context) {
469
+ (0, _render.syncResultAnimation)(result, context);
470
+ return (0, _render.renderSubagentResult)(result, options, theme);
471
+ }
472
+
473
+ };
474
+
475
+ pi.registerTool(tool);
476
+ (0, _slashCommands.registerSlashCommands)(pi, state);
477
+
478
+ const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
479
+ const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
480
+ const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
481
+ if (Array.isArray(previousEventUnsubscribes)) {
482
+ for (const unsubscribe of previousEventUnsubscribes) {
483
+ if (typeof unsubscribe !== "function") continue;
484
+ try {
485
+ unsubscribe();
486
+ } catch {
487
+
488
+ // Best effort cleanup for stale handlers from an older reload.
489
+ }}
490
+ }
491
+ (0, _notify.default)(pi);
492
+
493
+ const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
494
+ const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices : new Set();
495
+ globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
496
+ const controlEventHandler = (payload) => {
497
+ (0, _controlNotices.handleSubagentControlNotice)({
498
+ pi,
499
+ state,
500
+ visibleControlNotices,
501
+ details: payload
502
+ });
503
+ };
504
+ const eventUnsubscribes = [
505
+ pi.events.on(_types.SUBAGENT_ASYNC_STARTED_EVENT, handleStarted),
506
+ pi.events.on(_types.SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete),
507
+ pi.events.on(_types.SUBAGENT_CONTROL_EVENT, controlEventHandler)];
508
+
509
+ globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
510
+
511
+ pi.on("tool_result", (event, ctx) => {
512
+ if (event.toolName !== "subagent") return;
513
+ if (!ctx.hasUI) return;
514
+ state.lastUiContext = ctx;
515
+ if (state.asyncJobs.size > 0) {
516
+ (0, _render.renderWidget)(ctx, Array.from(state.asyncJobs.values()));
517
+ ensurePoller();
518
+ }
519
+ });
520
+
521
+ const cleanupSessionArtifacts = (ctx) => {
522
+ try {
523
+ const sessionFile = ctx.sessionManager.getSessionFile();
524
+ if (sessionFile) {
525
+ (0, _artifacts.cleanupOldArtifacts)((0, _artifacts.getArtifactsDir)(sessionFile), _types.DEFAULT_ARTIFACT_CONFIG.cleanupDays);
526
+ }
527
+ } catch {
528
+
529
+ // Cleanup failures should not block session lifecycle events.
530
+ }};
531
+
532
+ const resetSessionState = (ctx) => {
533
+ state.baseCwd = ctx.cwd;
534
+ state.currentSessionId = (0, _sessionIdentity.resolveCurrentSessionId)(ctx.sessionManager);
535
+ state.lastUiContext = ctx;
536
+ cleanupSessionArtifacts(ctx);
537
+ (0, _controlNotices.clearPendingForegroundControlNotices)(state);
538
+ resetJobs(ctx);
539
+ (0, _slashLiveState.restoreSlashFinalSnapshots)(ctx.sessionManager.getEntries());
540
+ primeExistingResults();
541
+ };
542
+
543
+ pi.on("session_start", (_event, ctx) => {
544
+ resetSessionState(ctx);
545
+ });
546
+
547
+ pi.on("session_shutdown", () => {
548
+ for (const unsubscribe of eventUnsubscribes) {
549
+ try {
550
+ unsubscribe();
551
+ } catch {
552
+
553
+ // Best effort cleanup during shutdown.
554
+ }}
555
+ if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
556
+ delete globalStore[eventUnsubscribeStoreKey];
557
+ }
558
+ stopResultWatcher();
559
+ if (state.poller) clearInterval(state.poller);
560
+ state.poller = null;
561
+ (0, _controlNotices.clearPendingForegroundControlNotices)(state);
562
+ for (const timer of state.cleanupTimers.values()) {
563
+ clearTimeout(timer);
564
+ }
565
+ state.cleanupTimers.clear();
566
+ state.asyncJobs.clear();
567
+ (0, _slashLiveState.clearSlashSnapshots)();
568
+ slashBridge.cancelAll();
569
+ slashBridge.dispose();
570
+ promptTemplateBridge.cancelAll();
571
+ promptTemplateBridge.dispose();
572
+ (0, _render.stopWidgetAnimation)();
573
+ (0, _render.stopResultAnimations)();
574
+ if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
575
+ delete globalStore[runtimeCleanupStoreKey];
576
+ }
577
+ try {
578
+ if (state.lastUiContext?.hasUI) {
579
+ state.lastUiContext.ui.setWidget(_types.WIDGET_KEY, undefined);
580
+ }
581
+ } catch (error) {
582
+ if (!isStaleExtensionContextError(error)) throw error;
583
+ }
584
+ });
585
+ } /* v9-ae8021cc1ece3974 */
pip-tmp/jiti/extension-schemas.9e6ee9e3.mjs ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.SubagentParams = void 0;
2
+
3
+
4
+
5
+ var _typebox = await jitiImport("typebox");
6
+ var _types = await jitiImport("../shared/types.ts"); /**
7
+ * TypeBox schemas for subagent tool parameters
8
+ */const SkillOverride = _typebox.Type.Unsafe({
9
+ anyOf: [
10
+ { type: "array", items: { type: "string" } },
11
+ { type: "boolean" },
12
+ { type: "string" }],
13
+
14
+ description: "Skill name(s) to inject (comma-separated), array of strings, or boolean (false disables, true uses default)"
15
+ });
16
+
17
+ const OutputOverride = _typebox.Type.Unsafe({
18
+ anyOf: [
19
+ { type: "string" },
20
+ { type: "boolean" }],
21
+
22
+ description: "Output filename/path (string), or false to disable file output"
23
+ });
24
+
25
+ const OutputModeOverride = _typebox.Type.String({
26
+ enum: ["inline", "file-only"],
27
+ description: "Return saved output inline (default) or only a concise file reference. file-only requires output to be a path."
28
+ });
29
+
30
+ const ReadsOverride = _typebox.Type.Unsafe({
31
+ anyOf: [
32
+ { type: "array", items: { type: "string" } },
33
+ { type: "boolean" }],
34
+
35
+ description: "Files to read before running (array of filenames), or false to disable"
36
+ });
37
+
38
+ const TaskItem = _typebox.Type.Object({
39
+ agent: _typebox.Type.String(),
40
+ task: _typebox.Type.String(),
41
+ cwd: _typebox.Type.Optional(_typebox.Type.String()),
42
+ count: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Repeat this parallel task N times with the same settings." })),
43
+ output: _typebox.Type.Optional(OutputOverride),
44
+ outputMode: _typebox.Type.Optional(OutputModeOverride),
45
+ reads: _typebox.Type.Optional(ReadsOverride),
46
+ progress: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Enable progress.md tracking for this task" })),
47
+ model: _typebox.Type.Optional(_typebox.Type.String({ description: "Override model for this task (e.g. 'google/gemini-3-pro')" })),
48
+ skill: _typebox.Type.Optional(SkillOverride)
49
+ });
50
+
51
+ // Parallel task item (within a parallel step)
52
+ const ParallelTaskSchema = _typebox.Type.Object({
53
+ agent: _typebox.Type.String(),
54
+ task: _typebox.Type.Optional(_typebox.Type.String({ description: "Task template with {task}, {previous}, {chain_dir} variables. Defaults to {previous}." })),
55
+ cwd: _typebox.Type.Optional(_typebox.Type.String()),
56
+ count: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Repeat this parallel task N times with the same settings." })),
57
+ output: _typebox.Type.Optional(OutputOverride),
58
+ outputMode: _typebox.Type.Optional(OutputModeOverride),
59
+ reads: _typebox.Type.Optional(ReadsOverride),
60
+ progress: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
61
+ skill: _typebox.Type.Optional(SkillOverride),
62
+ model: _typebox.Type.Optional(_typebox.Type.String({ description: "Override model for this task" }))
63
+ });
64
+
65
+ // Flattened so chain steps do not need an object-shape anyOf/oneOf union.
66
+ const ChainItem = _typebox.Type.Object({
67
+ agent: _typebox.Type.Optional(_typebox.Type.String({ description: "Sequential step agent name" })),
68
+ task: _typebox.Type.Optional(_typebox.Type.String({
69
+ description: "Task template with variables: {task}=original request, {previous}=prior step's text response, {chain_dir}=shared folder. Required for first step, defaults to '{previous}' for subsequent steps."
70
+ })),
71
+ cwd: _typebox.Type.Optional(_typebox.Type.String()),
72
+ output: _typebox.Type.Optional(OutputOverride),
73
+ outputMode: _typebox.Type.Optional(OutputModeOverride),
74
+ reads: _typebox.Type.Optional(ReadsOverride),
75
+ progress: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
76
+ skill: _typebox.Type.Optional(SkillOverride),
77
+ model: _typebox.Type.Optional(_typebox.Type.String({ description: "Override model for this step" })),
78
+ parallel: _typebox.Type.Optional(_typebox.Type.Array(ParallelTaskSchema, { minItems: 1, description: "Tasks to run in parallel" })),
79
+ concurrency: _typebox.Type.Optional(_typebox.Type.Number({ description: "Max concurrent tasks (default: 4)" })),
80
+ failFast: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Stop on first failure (default: false)" })),
81
+ worktree: _typebox.Type.Optional(_typebox.Type.Boolean({
82
+ description: "Create isolated git worktrees for each parallel task."
83
+ }))
84
+ }, { description: "Chain step: use {agent, task?, ...} for sequential or {parallel: [...]} for concurrent execution" });
85
+
86
+ const ControlOverrides = _typebox.Type.Object({
87
+ enabled: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
88
+ needsAttentionAfterMs: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "No-observed-activity window before a run needs attention" })),
89
+ activeNoticeAfterMs: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Active-long-running notice threshold by elapsed ms (default: 240000)" })),
90
+ activeNoticeAfterTurns: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Optional active-long-running notice threshold by assistant turns (disabled by default)" })),
91
+ activeNoticeAfterTokens: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Optional active-long-running notice threshold by total tokens (disabled by default)" })),
92
+ failedToolAttemptsBeforeAttention: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Consecutive mutating-tool failures before escalating to needs_attention (default: 3)" })),
93
+ notifyOn: _typebox.Type.Optional(_typebox.Type.Array(_typebox.Type.String({ enum: ["active_long_running", "needs_attention"] }), {
94
+ description: "Control event types that should notify the parent/orchestrator. Defaults to active_long_running and needs_attention."
95
+ })),
96
+ notifyChannels: _typebox.Type.Optional(_typebox.Type.Array(_typebox.Type.String({ enum: ["event", "async", "intercom"] }), {
97
+ description: "Notification channels to use when available. Defaults to event, async, and intercom."
98
+ }))
99
+ });
100
+
101
+ const SubagentParams = exports.SubagentParams = _typebox.Type.Object({
102
+ agent: _typebox.Type.Optional(_typebox.Type.String({ description: "Agent name (SINGLE mode) or target for management get/update/delete" })),
103
+ task: _typebox.Type.Optional(_typebox.Type.String({ description: "Task (SINGLE mode, optional for self-contained agents)" })),
104
+ // Management action (when present, tool operates in management mode)
105
+ action: _typebox.Type.Optional(_typebox.Type.String({
106
+ enum: [..._types.SUBAGENT_ACTIONS],
107
+ description: "Management/control action. Omit for execution mode."
108
+ })),
109
+ id: _typebox.Type.Optional(_typebox.Type.String({
110
+ description: "Run id or prefix for action='status', action='interrupt', or action='resume'."
111
+ })),
112
+ runId: _typebox.Type.Optional(_typebox.Type.String({
113
+ description: "Target run ID for action='interrupt' or action='resume'. Defaults to the most recently active controllable run for interrupt. Prefer id for new calls."
114
+ })),
115
+ dir: _typebox.Type.Optional(_typebox.Type.String({
116
+ description: "Async run directory for action='status' or action='resume'."
117
+ })),
118
+ index: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 0, description: "Zero-based child index for actions that target a specific child." })),
119
+ message: _typebox.Type.Optional(_typebox.Type.String({ description: "Follow-up message for action='resume'. Use index to choose a child from multi-child runs." })),
120
+ // Chain identifier for management (can't reuse 'chain' — that's the execution array)
121
+ chainName: _typebox.Type.Optional(_typebox.Type.String({
122
+ description: "Chain name for get/update/delete management actions"
123
+ })),
124
+ // Agent/chain configuration for create/update (nested to avoid conflicts with execution fields)
125
+ config: _typebox.Type.Optional(_typebox.Type.Unsafe({
126
+ anyOf: [
127
+ { type: "object", additionalProperties: true },
128
+ { type: "string" }],
129
+
130
+ description: "Agent or chain config for create/update. Agent: name, package (optional namespace; runtime name becomes package.name), description, scope ('user'|'project', default 'user'), systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, defaultContext ('fresh'|'fork'), model, tools (comma-separated), extensions (comma-separated), skills (comma-separated), thinking, output, reads, progress, maxSubagentDepth. Chain: name, package, description, scope, steps (array of {agent, task?, output?, outputMode?, reads?, model?, skill?, progress?}). Presence of 'steps' creates a chain instead of an agent. String values must be valid JSON."
131
+ })),
132
+ tasks: _typebox.Type.Optional(_typebox.Type.Array(TaskItem, { description: "PARALLEL mode: [{agent, task, count?, output?, outputMode?, reads?, progress?}, ...]" })),
133
+ concurrency: _typebox.Type.Optional(_typebox.Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or 4." })),
134
+ worktree: _typebox.Type.Optional(_typebox.Type.Boolean({
135
+ description: "Create isolated git worktrees for each parallel task. " +
136
+ "Prevents filesystem conflicts. Requires clean git state. " +
137
+ "Per-worktree diffs included in output."
138
+ })),
139
+ chain: _typebox.Type.Optional(_typebox.Type.Array(ChainItem, { description: "CHAIN mode: sequential pipeline where each step's response becomes {previous} for the next. Use {task}, {previous}, {chain_dir} in task templates." })),
140
+ context: _typebox.Type.Optional(_typebox.Type.String({
141
+ enum: ["fresh", "fork"],
142
+ description: "'fresh' or 'fork' to branch from parent session. If omitted, any requested agent with defaultContext: 'fork' makes the whole invocation forked; otherwise the default is 'fresh'."
143
+ })),
144
+ chainDir: _typebox.Type.Optional(_typebox.Type.String({ description: "Persistent directory for chain artifacts. Default: a user-scoped temp directory under <tmpdir>/ (auto-cleaned after 24h)" })),
145
+ async: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Run in background (default: false, or per config)" })),
146
+ agentScope: _typebox.Type.Optional(_typebox.Type.String({ description: "Agent discovery scope: 'user', 'project', or 'both' (default: 'both'; project wins on name collisions)" })),
147
+ cwd: _typebox.Type.Optional(_typebox.Type.String()),
148
+ artifacts: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Write debug artifacts (default: true)" })),
149
+ includeProgress: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Include full progress in result (default: false)" })),
150
+ share: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
151
+ sessionDir: _typebox.Type.Optional(
152
+ _typebox.Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" })
153
+ ),
154
+ // Clarification TUI
155
+ clarify: _typebox.Type.Optional(_typebox.Type.Boolean({ description: "Show TUI to preview/edit before execution (default: true for chains, false for single/parallel). Implies sync mode." })),
156
+ control: _typebox.Type.Optional(ControlOverrides),
157
+ // Solo agent overrides
158
+ output: _typebox.Type.Optional(_typebox.Type.Unsafe({
159
+ anyOf: [
160
+ { type: "string" },
161
+ { type: "boolean" }],
162
+
163
+ description: "Output file for single agent (string), or false to disable. Relative paths resolve against cwd."
164
+ })),
165
+ outputMode: _typebox.Type.Optional(OutputModeOverride),
166
+ skill: _typebox.Type.Optional(SkillOverride),
167
+ model: _typebox.Type.Optional(_typebox.Type.String({ description: "Override model for single agent (e.g. 'anthropic/claude-sonnet-4')" }))
168
+ }); /* v9-1f42c6bb141b9221 */
pip-tmp/jiti/foreground-chain-clarify.558277eb.mjs ADDED
@@ -0,0 +1,1333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.ChainClarifyComponent = void 0;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+ var _piTui = await jitiImport("@mariozechner/pi-tui");
11
+
12
+
13
+ var _modelFallback = await jitiImport("../shared/model-fallback.ts");
14
+ var _modelInfo = await jitiImport("../../shared/model-info.ts"); /**
15
+ * Chain Clarification TUI Component
16
+ *
17
+ * Shows templates and resolved behaviors for each step in a chain.
18
+ * Supports runtime editing of templates, output paths, reads lists, and progress toggle.
19
+ */
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+ function createEditorState(initial = "") {
43
+ return { buffer: initial, cursor: 0, viewportOffset: 0 };
44
+ }
45
+
46
+ function wrapText(text, width) {
47
+ if (width <= 0) return { lines: [text], starts: [0] };
48
+ if (text.length === 0) return { lines: [""], starts: [0] };
49
+
50
+ const lines = [];
51
+ const starts = [];
52
+ let offset = 0;
53
+ const segments = text.split("\n");
54
+ for (const [index, segment] of segments.entries()) {
55
+ if (segment.length === 0) {
56
+ starts.push(offset);
57
+ lines.push("");
58
+ } else {
59
+ let lineStart = 0;
60
+ let pos = 0;
61
+ let lineWidth = 0;
62
+ while (pos < segment.length) {
63
+ const char = String.fromCodePoint(segment.codePointAt(pos));
64
+ const charWidth = (0, _piTui.visibleWidth)(char);
65
+ if (lineWidth > 0 && lineWidth + charWidth > width) {
66
+ starts.push(offset + lineStart);
67
+ lines.push(segment.slice(lineStart, pos));
68
+ lineStart = pos;
69
+ lineWidth = 0;
70
+ continue;
71
+ }
72
+ pos += char.length;
73
+ lineWidth += charWidth;
74
+ }
75
+ starts.push(offset + lineStart);
76
+ lines.push(segment.slice(lineStart));
77
+ }
78
+ offset += segment.length + (index < segments.length - 1 ? 1 : 0);
79
+ }
80
+ if (!text.endsWith("\n") && text.length > 0 && (0, _piTui.visibleWidth)(lines[lines.length - 1] ?? "") === width) {
81
+ starts.push(text.length);
82
+ lines.push("");
83
+ }
84
+ return { lines, starts };
85
+ }
86
+
87
+ function getCursorDisplayPos(cursor, starts) {
88
+ for (let i = starts.length - 1; i >= 0; i--) {
89
+ if (cursor >= starts[i]) return { line: i, col: cursor - starts[i] };
90
+ }
91
+ return { line: 0, col: 0 };
92
+ }
93
+
94
+ function ensureCursorVisible(cursorLine, viewportHeight, currentOffset) {
95
+ if (cursorLine < currentOffset) return Math.max(0, cursorLine);
96
+ if (cursorLine >= currentOffset + viewportHeight) return Math.max(0, cursorLine - viewportHeight + 1);
97
+ return Math.max(0, currentOffset);
98
+ }
99
+
100
+ function isWordChar(ch) {
101
+ const code = ch.charCodeAt(0);
102
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
103
+ }
104
+
105
+ function wordBackward(buffer, cursor) {
106
+ let pos = cursor;
107
+ while (pos > 0 && !isWordChar(buffer[pos - 1])) pos--;
108
+ while (pos > 0 && isWordChar(buffer[pos - 1])) pos--;
109
+ return pos;
110
+ }
111
+
112
+ function wordForward(buffer, cursor) {
113
+ let pos = cursor;
114
+ while (pos < buffer.length && isWordChar(buffer[pos])) pos++;
115
+ while (pos < buffer.length && !isWordChar(buffer[pos])) pos++;
116
+ return pos;
117
+ }
118
+
119
+ function normalizeInsertText(data) {
120
+ let text = data.split("\x1b[200~").join("").split("\x1b[201~").join("");
121
+ text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
122
+ const newline = text.indexOf("\n");
123
+ if (newline !== -1) text = text.slice(0, newline);
124
+ text = text.replace(/\t/g, " ");
125
+ if (text.length === 0) return null;
126
+ for (let i = 0; i < text.length; i++) {
127
+ if (text.charCodeAt(i) < 32) return null;
128
+ }
129
+ return text;
130
+ }
131
+
132
+ function handleEditorInput(state, data, textWidth) {
133
+ if ((0, _piTui.matchesKey)(data, "escape") || (0, _piTui.matchesKey)(data, "ctrl+c") || (0, _piTui.matchesKey)(data, "return")) return null;
134
+
135
+ const { lines: wrapped, starts } = wrapText(state.buffer, textWidth);
136
+ const cursorPos = getCursorDisplayPos(state.cursor, starts);
137
+
138
+ if ((0, _piTui.matchesKey)(data, "alt+left") || (0, _piTui.matchesKey)(data, "ctrl+left")) return { ...state, cursor: wordBackward(state.buffer, state.cursor) };
139
+ if ((0, _piTui.matchesKey)(data, "alt+right") || (0, _piTui.matchesKey)(data, "ctrl+right")) return { ...state, cursor: wordForward(state.buffer, state.cursor) };
140
+ if ((0, _piTui.matchesKey)(data, "left")) return state.cursor > 0 ? { ...state, cursor: state.cursor - 1 } : state;
141
+ if ((0, _piTui.matchesKey)(data, "right")) return state.cursor < state.buffer.length ? { ...state, cursor: state.cursor + 1 } : state;
142
+ if ((0, _piTui.matchesKey)(data, "up") && cursorPos.line > 0) {
143
+ const targetLine = cursorPos.line - 1;
144
+ return { ...state, cursor: starts[targetLine] + Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0) };
145
+ }
146
+ if ((0, _piTui.matchesKey)(data, "down") && cursorPos.line < wrapped.length - 1) {
147
+ const targetLine = cursorPos.line + 1;
148
+ return { ...state, cursor: starts[targetLine] + Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0) };
149
+ }
150
+ if ((0, _piTui.matchesKey)(data, "home")) return { ...state, cursor: starts[cursorPos.line] };
151
+ if ((0, _piTui.matchesKey)(data, "end")) return { ...state, cursor: starts[cursorPos.line] + (wrapped[cursorPos.line]?.length ?? 0) };
152
+ if ((0, _piTui.matchesKey)(data, "ctrl+home")) return { ...state, cursor: 0 };
153
+ if ((0, _piTui.matchesKey)(data, "ctrl+end")) return { ...state, cursor: state.buffer.length };
154
+ if ((0, _piTui.matchesKey)(data, "alt+backspace")) {
155
+ const target = wordBackward(state.buffer, state.cursor);
156
+ return target === state.cursor ? state : { ...state, buffer: state.buffer.slice(0, target) + state.buffer.slice(state.cursor), cursor: target };
157
+ }
158
+ if ((0, _piTui.matchesKey)(data, "backspace")) {
159
+ return state.cursor > 0 ?
160
+ { ...state, buffer: state.buffer.slice(0, state.cursor - 1) + state.buffer.slice(state.cursor), cursor: state.cursor - 1 } :
161
+ state;
162
+ }
163
+ if ((0, _piTui.matchesKey)(data, "delete")) {
164
+ return state.cursor < state.buffer.length ?
165
+ { ...state, buffer: state.buffer.slice(0, state.cursor) + state.buffer.slice(state.cursor + 1) } :
166
+ state;
167
+ }
168
+
169
+ const insert = normalizeInsertText(data);
170
+ return insert ?
171
+ { ...state, buffer: state.buffer.slice(0, state.cursor) + insert + state.buffer.slice(state.cursor), cursor: state.cursor + insert.length } :
172
+ null;
173
+ }
174
+
175
+ function renderWithCursor(text, cursorPos) {
176
+ const before = text.slice(0, cursorPos);
177
+ const cursorChar = text[cursorPos] ?? " ";
178
+ const after = text.slice(cursorPos + 1);
179
+ return `${before}\x1b[7m${cursorChar}\x1b[27m${after}`;
180
+ }
181
+
182
+ function renderEditor(state, width, viewportHeight) {
183
+ const { lines: wrapped, starts } = wrapText(state.buffer, width);
184
+ const cursorPos = getCursorDisplayPos(state.cursor, starts);
185
+ const lines = [];
186
+ for (let i = 0; i < viewportHeight; i++) {
187
+ const lineIdx = state.viewportOffset + i;
188
+ let content = lineIdx < wrapped.length ? wrapped[lineIdx] ?? "" : "";
189
+ if (lineIdx === cursorPos.line) content = renderWithCursor(content, cursorPos.col);
190
+ lines.push(content);
191
+ }
192
+ return lines;
193
+ }
194
+
195
+ /**
196
+ * TUI component for chain clarification.
197
+ * Factory signature matches ctx.ui.custom: (tui, theme, kb, done) => Component
198
+ */
199
+ class ChainClarifyComponent {
200
+ width = 84;
201
+
202
+ selectedStep = 0;
203
+ editingStep = null;
204
+ editMode = "template";
205
+ editState = createEditorState();
206
+
207
+ EDIT_VIEWPORT_HEIGHT = 12;
208
+ behaviorOverrides = new Map();
209
+ modelSearchQuery = "";
210
+ modelSelectedIndex = 0;
211
+ filteredModels = [];
212
+ MODEL_SELECTOR_HEIGHT = 10;
213
+ thinkingSelectedIndex = 0;
214
+ skillSearchQuery = "";
215
+ skillSelectedNames = new Set();
216
+ skillCursorIndex = 0;
217
+ filteredSkills = [];
218
+ noticeMessage = null;
219
+ noticeMessageTimer = null;
220
+ /** Run in background (async) mode */
221
+ runInBackground = false;
222
+ tui;
223
+ theme;
224
+ agentConfigs;
225
+ templates;
226
+ originalTask;
227
+ chainDir;
228
+ resolvedBehaviors;
229
+ availableModels;
230
+ preferredProvider;
231
+ availableSkills;
232
+ done;
233
+ mode;
234
+
235
+ constructor(
236
+ tui,
237
+ theme,
238
+ agentConfigs,
239
+ templates,
240
+ originalTask,
241
+ chainDir,
242
+ resolvedBehaviors,
243
+ availableModels,
244
+ preferredProvider,
245
+ availableSkills,
246
+ done,
247
+ mode = 'chain')
248
+ {
249
+ this.tui = tui;
250
+ this.theme = theme;
251
+ this.agentConfigs = agentConfigs;
252
+ this.templates = templates;
253
+ this.originalTask = originalTask;
254
+ this.chainDir = chainDir;
255
+ this.resolvedBehaviors = resolvedBehaviors;
256
+ this.availableModels = availableModels;
257
+ this.preferredProvider = preferredProvider;
258
+ this.availableSkills = availableSkills;
259
+ this.done = done;
260
+ this.mode = mode;
261
+ this.filteredModels = [...availableModels];
262
+ this.filteredSkills = [...availableSkills];
263
+ }
264
+
265
+ // ─────────────────────────────────────────────────────────────────────────────
266
+ // Helper methods for rendering
267
+ // ─────────────────────────────────────────────────────────────────────────────
268
+
269
+ /** Pad string to specified visible width */
270
+ pad(s, len) {
271
+ const vis = (0, _piTui.visibleWidth)(s);
272
+ return s + " ".repeat(Math.max(0, len - vis));
273
+ }
274
+
275
+ /** Create a row with border characters */
276
+ row(content) {
277
+ const innerW = this.width - 2;
278
+ return this.theme.fg("border", "│") + this.pad(content, innerW) + this.theme.fg("border", "│");
279
+ }
280
+
281
+ /** Render centered header line with border */
282
+ renderHeader(text) {
283
+ const innerW = this.width - 2;
284
+ const padLen = Math.max(0, innerW - (0, _piTui.visibleWidth)(text));
285
+ const padLeft = Math.floor(padLen / 2);
286
+ const padRight = padLen - padLeft;
287
+ return (
288
+ this.theme.fg("border", "╭" + "─".repeat(padLeft)) +
289
+ this.theme.fg("accent", text) +
290
+ this.theme.fg("border", "─".repeat(padRight) + "╮"));
291
+
292
+ }
293
+
294
+ /** Render centered footer line with border */
295
+ renderFooter(text) {
296
+ const innerW = this.width - 2;
297
+ const padLen = Math.max(0, innerW - (0, _piTui.visibleWidth)(text));
298
+ const padLeft = Math.floor(padLen / 2);
299
+ const padRight = padLen - padLeft;
300
+ return (
301
+ this.theme.fg("border", "╰" + "─".repeat(padLeft)) +
302
+ this.theme.fg("dim", text) +
303
+ this.theme.fg("border", "─".repeat(padRight) + "╯"));
304
+
305
+ }
306
+
307
+ /** Exit edit mode and reset state */
308
+ exitEditMode() {
309
+ this.editingStep = null;
310
+ this.editState = createEditorState();
311
+ this.tui.requestRender();
312
+ }
313
+
314
+ // ─────────────────────────────────────────────────────────────────────────────
315
+ // Full edit mode methods
316
+ // ─────────────────────────────────────────────────────────────────────────────
317
+
318
+ /** Render the full-edit takeover view */
319
+ renderFullEditMode() {
320
+ const innerW = this.width - 2;
321
+ const textWidth = innerW - 2; // 1 char padding on each side
322
+ const lines = [];
323
+
324
+ const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
325
+ const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
326
+ this.editState = {
327
+ ...this.editState,
328
+ viewportOffset: ensureCursorVisible(
329
+ cursorPos.line,
330
+ this.EDIT_VIEWPORT_HEIGHT,
331
+ this.editState.viewportOffset
332
+ )
333
+ };
334
+
335
+ // Header (truncate agent name to prevent overflow)
336
+ const fieldName = this.editMode === "template" ? "task" : this.editMode;
337
+ const rawAgentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
338
+ const maxAgentLen = innerW - 30; // Reserve space for " Editing X (Step/Task N: ) "
339
+ const agentName = rawAgentName.length > maxAgentLen ?
340
+ rawAgentName.slice(0, maxAgentLen - 1) + "…" :
341
+ rawAgentName;
342
+ // Use mode-appropriate terminology
343
+ const stepLabel = this.mode === 'single' ?
344
+ agentName :
345
+ this.mode === 'parallel' ?
346
+ `Task ${this.editingStep + 1}: ${agentName}` :
347
+ `Step ${this.editingStep + 1}: ${agentName}`;
348
+ const headerText = ` Editing ${fieldName} (${stepLabel}) `;
349
+ lines.push(this.renderHeader(headerText));
350
+ lines.push(this.row(""));
351
+
352
+ const editorLines = renderEditor(this.editState, textWidth, this.EDIT_VIEWPORT_HEIGHT);
353
+ for (const line of editorLines) {
354
+ lines.push(this.row(` ${line}`));
355
+ }
356
+
357
+ const linesBelow = wrapped.length - this.editState.viewportOffset - this.EDIT_VIEWPORT_HEIGHT;
358
+ const hasMore = linesBelow > 0;
359
+ const hasLess = this.editState.viewportOffset > 0;
360
+ let scrollInfo = "";
361
+ if (hasLess) scrollInfo += "↑";
362
+ if (hasMore) scrollInfo += `↓ ${linesBelow}+`;
363
+
364
+ lines.push(this.row(""));
365
+
366
+ const footerText = scrollInfo ?
367
+ ` [Esc] Done • [Ctrl+C] Discard • ${scrollInfo} ` :
368
+ " [Esc] Done • [Ctrl+C] Discard ";
369
+ lines.push(this.renderFooter(footerText));
370
+
371
+ return lines;
372
+ }
373
+
374
+ // ─────────────────────────────────────────────────────────────────────────────
375
+ // Behavior helpers
376
+ // ─────────────────────────────────────────────────────────────────────────────
377
+
378
+ /** Get effective behavior for a step (with user overrides applied) */
379
+ getEffectiveBehavior(stepIndex) {
380
+ const base = this.resolvedBehaviors[stepIndex];
381
+ const override = this.behaviorOverrides.get(stepIndex);
382
+ if (!override) return base;
383
+
384
+ return {
385
+ output: override.output !== undefined ? override.output : base.output,
386
+ outputMode: base.outputMode,
387
+ reads: override.reads !== undefined ? override.reads : base.reads,
388
+ progress: override.progress !== undefined ? override.progress : base.progress,
389
+ skills: override.skills !== undefined ? override.skills : base.skills,
390
+ model: override.model !== undefined ? override.model : base.model
391
+ };
392
+ }
393
+
394
+ /** Get the effective model for a step (override or agent default) */
395
+ getEffectiveModel(stepIndex) {
396
+ const override = this.behaviorOverrides.get(stepIndex);
397
+ if (override?.model) return this.resolveModelFullId(override.model);
398
+
399
+ const baseModel = this.resolvedBehaviors[stepIndex]?.model;
400
+ if (baseModel) return this.resolveModelFullId(baseModel);
401
+ return "default";
402
+ }
403
+
404
+ /** Resolve a model name to its full provider/model format */
405
+ resolveModelFullId(modelName) {
406
+ return (0, _modelFallback.resolveModelCandidate)(modelName, this.availableModels, this.preferredProvider) ?? modelName;
407
+ }
408
+
409
+ /** Update a behavior override for a step */
410
+ updateBehavior(stepIndex, field, value) {
411
+ const existing = this.behaviorOverrides.get(stepIndex) ?? {};
412
+ this.behaviorOverrides.set(stepIndex, { ...existing, [field]: value });
413
+ }
414
+
415
+ showNotice(text, type) {
416
+ this.noticeMessage = { text, type };
417
+ if (this.noticeMessageTimer) clearTimeout(this.noticeMessageTimer);
418
+ this.noticeMessageTimer = setTimeout(() => {
419
+ this.noticeMessage = null;
420
+ this.noticeMessageTimer = null;
421
+ this.tui.requestRender();
422
+ }, 2000);
423
+ this.tui.requestRender();
424
+ }
425
+
426
+ handleInput(data) {
427
+ if (this.editingStep !== null) {
428
+ if (this.editMode === "model") {
429
+ this.handleModelSelectorInput(data);
430
+ } else if (this.editMode === "thinking") {
431
+ this.handleThinkingSelectorInput(data);
432
+ } else if (this.editMode === "skills") {
433
+ this.handleSkillSelectorInput(data);
434
+ } else {
435
+ this.handleEditInput(data);
436
+ }
437
+ return;
438
+ }
439
+
440
+ if ((0, _piTui.matchesKey)(data, "escape") || (0, _piTui.matchesKey)(data, "ctrl+c")) {
441
+ this.done({ confirmed: false, templates: [], behaviorOverrides: [] });
442
+ return;
443
+ }
444
+
445
+ if ((0, _piTui.matchesKey)(data, "return")) {
446
+ const overrides = [];
447
+ for (let i = 0; i < this.agentConfigs.length; i++) {
448
+ overrides.push(this.behaviorOverrides.get(i));
449
+ }
450
+ this.done({ confirmed: true, templates: this.templates, behaviorOverrides: overrides, runInBackground: this.runInBackground });
451
+ return;
452
+ }
453
+
454
+ if ((0, _piTui.matchesKey)(data, "up")) {
455
+ this.selectedStep = Math.max(0, this.selectedStep - 1);
456
+ this.tui.requestRender();
457
+ return;
458
+ }
459
+
460
+ if ((0, _piTui.matchesKey)(data, "down")) {
461
+ const maxStep = Math.max(0, this.agentConfigs.length - 1);
462
+ this.selectedStep = Math.min(maxStep, this.selectedStep + 1);
463
+ this.tui.requestRender();
464
+ return;
465
+ }
466
+
467
+ if (data === "e") {
468
+ this.enterEditMode("template");
469
+ return;
470
+ }
471
+
472
+ if (data === "m") {
473
+ this.enterModelSelector();
474
+ return;
475
+ }
476
+
477
+ if (data === "t") {
478
+ this.enterThinkingSelector();
479
+ return;
480
+ }
481
+
482
+ if (data === "s") {
483
+ this.editingStep = this.selectedStep;
484
+ this.editMode = "skills";
485
+ this.skillSearchQuery = "";
486
+ this.skillCursorIndex = 0;
487
+ this.filteredSkills = [...this.availableSkills];
488
+ const current = this.getEffectiveBehavior(this.selectedStep).skills;
489
+ this.skillSelectedNames.clear();
490
+ if (current !== false && current.length > 0) {
491
+ current.forEach((skillName) => this.skillSelectedNames.add(skillName));
492
+ }
493
+ this.tui.requestRender();
494
+ return;
495
+ }
496
+
497
+ if (data === "w" && this.mode !== 'parallel') {
498
+ this.enterEditMode("output");
499
+ return;
500
+ }
501
+
502
+ if (data === "r" && this.mode === 'chain') {
503
+ this.enterEditMode("reads");
504
+ return;
505
+ }
506
+
507
+ if (data === "p" && this.mode === 'chain') {
508
+ const anyEnabled = this.agentConfigs.some((_, i) => this.getEffectiveBehavior(i).progress);
509
+ const newState = !anyEnabled;
510
+ for (let i = 0; i < this.agentConfigs.length; i++) {
511
+ this.updateBehavior(i, "progress", newState);
512
+ }
513
+ this.tui.requestRender();
514
+ return;
515
+ }
516
+
517
+ if (data === "b") {
518
+ this.runInBackground = !this.runInBackground;
519
+ this.tui.requestRender();
520
+ return;
521
+ }
522
+
523
+ }
524
+
525
+ enterEditMode(mode) {
526
+ this.editingStep = this.selectedStep;
527
+ this.editMode = mode;
528
+ let buffer = "";
529
+
530
+ if (mode === "template") {
531
+ const template = this.templates[this.selectedStep] ?? "";
532
+ buffer = template.split("\n")[0] ?? "";
533
+ } else if (mode === "output") {
534
+ const behavior = this.getEffectiveBehavior(this.selectedStep);
535
+ buffer = behavior.output === false ? "" : behavior.output || "";
536
+ } else if (mode === "reads") {
537
+ const behavior = this.getEffectiveBehavior(this.selectedStep);
538
+ buffer = behavior.reads === false ? "" : behavior.reads?.join(", ") || "";
539
+ }
540
+
541
+ this.editState = createEditorState(buffer);
542
+ this.tui.requestRender();
543
+ }
544
+
545
+ /** Enter model selector mode */
546
+ enterModelSelector() {
547
+ this.editingStep = this.selectedStep;
548
+ this.editMode = "model";
549
+ this.modelSearchQuery = "";
550
+ this.modelSelectedIndex = 0;
551
+ this.filteredModels = [...this.availableModels];
552
+ const currentModel = (0, _modelFallback.splitThinkingSuffix)(this.getEffectiveModel(this.selectedStep)).baseModel;
553
+ const currentIndex = this.filteredModels.findIndex((m) => m.fullId === currentModel || m.id === currentModel);
554
+ if (currentIndex >= 0) {
555
+ this.modelSelectedIndex = currentIndex;
556
+ }
557
+
558
+ this.tui.requestRender();
559
+ }
560
+
561
+ /** Filter models based on search query */
562
+ filterModels() {
563
+ const query = this.modelSearchQuery.toLowerCase();
564
+ if (!query) {
565
+ this.filteredModels = [...this.availableModels];
566
+ } else {
567
+ this.filteredModels = this.availableModels.filter((m) =>
568
+ m.fullId.toLowerCase().includes(query) ||
569
+ m.id.toLowerCase().includes(query) ||
570
+ m.provider.toLowerCase().includes(query)
571
+ );
572
+ }
573
+ this.modelSelectedIndex = Math.min(this.modelSelectedIndex, Math.max(0, this.filteredModels.length - 1));
574
+ }
575
+
576
+ handleModelSelectorInput(data) {
577
+ if ((0, _piTui.matchesKey)(data, "escape") || (0, _piTui.matchesKey)(data, "ctrl+c")) {
578
+ this.exitEditMode();
579
+ return;
580
+ }
581
+
582
+ if ((0, _piTui.matchesKey)(data, "return")) {
583
+ const selected = this.filteredModels[this.modelSelectedIndex];
584
+ if (selected) {
585
+ const { thinkingSuffix } = (0, _modelFallback.splitThinkingSuffix)(this.getEffectiveModel(this.editingStep));
586
+ const requestedLevel = thinkingSuffix.slice(1);
587
+ const selectedModel = (0, _modelInfo.findModelInfo)(selected.fullId, this.availableModels, this.preferredProvider);
588
+ const suffix = (0, _modelInfo.getSupportedThinkingLevels)(selectedModel).some((level) => level === requestedLevel) ? thinkingSuffix : "";
589
+ this.updateBehavior(this.editingStep, "model", `${selected.fullId}${suffix}`);
590
+ }
591
+ this.exitEditMode();
592
+ return;
593
+ }
594
+
595
+ if ((0, _piTui.matchesKey)(data, "up")) {
596
+ if (this.filteredModels.length > 0) {
597
+ this.modelSelectedIndex = this.modelSelectedIndex === 0 ?
598
+ this.filteredModels.length - 1 :
599
+ this.modelSelectedIndex - 1;
600
+ }
601
+ this.tui.requestRender();
602
+ return;
603
+ }
604
+
605
+ if ((0, _piTui.matchesKey)(data, "down")) {
606
+ if (this.filteredModels.length > 0) {
607
+ this.modelSelectedIndex = this.modelSelectedIndex === this.filteredModels.length - 1 ?
608
+ 0 :
609
+ this.modelSelectedIndex + 1;
610
+ }
611
+ this.tui.requestRender();
612
+ return;
613
+ }
614
+
615
+ if ((0, _piTui.matchesKey)(data, "backspace")) {
616
+ if (this.modelSearchQuery.length > 0) {
617
+ this.modelSearchQuery = this.modelSearchQuery.slice(0, -1);
618
+ this.filterModels();
619
+ }
620
+ this.tui.requestRender();
621
+ return;
622
+ }
623
+
624
+ if (data.length === 1 && data.charCodeAt(0) >= 32) {
625
+ this.modelSearchQuery += data;
626
+ this.filterModels();
627
+ this.tui.requestRender();
628
+ return;
629
+ }
630
+ }
631
+
632
+ getAvailableThinkingLevels(stepIndex) {
633
+ return (0, _modelInfo.getSupportedThinkingLevels)((0, _modelInfo.findModelInfo)(this.getEffectiveModel(stepIndex), this.availableModels, this.preferredProvider));
634
+ }
635
+
636
+ /** Enter thinking level selector mode */
637
+ enterThinkingSelector() {
638
+ if (!this.getEffectiveBehavior(this.selectedStep).model) {
639
+ this.showNotice("Select a model first", "error");
640
+ return;
641
+ }
642
+ this.editingStep = this.selectedStep;
643
+ this.editMode = "thinking";
644
+
645
+ const levels = this.getAvailableThinkingLevels(this.selectedStep);
646
+ const { thinkingSuffix } = (0, _modelFallback.splitThinkingSuffix)(this.getEffectiveModel(this.selectedStep));
647
+ const suffix = thinkingSuffix.slice(1);
648
+ const levelIdx = levels.findIndex((level) => level === suffix);
649
+ this.thinkingSelectedIndex = levelIdx >= 0 ? levelIdx : Math.max(0, levels.indexOf("off"));
650
+
651
+ this.tui.requestRender();
652
+ }
653
+
654
+ handleThinkingSelectorInput(data) {
655
+ if ((0, _piTui.matchesKey)(data, "escape") || (0, _piTui.matchesKey)(data, "ctrl+c")) {
656
+ this.exitEditMode();
657
+ return;
658
+ }
659
+
660
+ const levels = this.getAvailableThinkingLevels(this.editingStep);
661
+ if (levels.length === 0) return;
662
+
663
+ if ((0, _piTui.matchesKey)(data, "return")) {
664
+ const selectedLevel = levels[this.thinkingSelectedIndex] ?? "off";
665
+ this.applyThinkingLevel(selectedLevel);
666
+ this.exitEditMode();
667
+ return;
668
+ }
669
+
670
+ if ((0, _piTui.matchesKey)(data, "up")) {
671
+ this.thinkingSelectedIndex = this.thinkingSelectedIndex === 0 ?
672
+ levels.length - 1 :
673
+ this.thinkingSelectedIndex - 1;
674
+ this.tui.requestRender();
675
+ return;
676
+ }
677
+
678
+ if ((0, _piTui.matchesKey)(data, "down")) {
679
+ this.thinkingSelectedIndex = this.thinkingSelectedIndex === levels.length - 1 ?
680
+ 0 :
681
+ this.thinkingSelectedIndex + 1;
682
+ this.tui.requestRender();
683
+ return;
684
+ }
685
+ }
686
+
687
+ /** Apply thinking level to the current step's model */
688
+ applyThinkingLevel(level) {
689
+ const stepIndex = this.editingStep;
690
+ const currentModel = this.getEffectiveBehavior(stepIndex).model;
691
+ if (!currentModel) return;
692
+
693
+ const { baseModel } = (0, _modelFallback.splitThinkingSuffix)(currentModel);
694
+ const newModel = level === "off" ? baseModel : `${baseModel}:${level}`;
695
+ this.updateBehavior(stepIndex, "model", newModel);
696
+ }
697
+
698
+ filterSkills() {
699
+ const query = this.skillSearchQuery.toLowerCase();
700
+ if (!query) {
701
+ this.filteredSkills = [...this.availableSkills];
702
+ } else {
703
+ this.filteredSkills = this.availableSkills.filter((s) =>
704
+ s.name.toLowerCase().includes(query) || (
705
+ s.description?.toLowerCase().includes(query) ?? false)
706
+ );
707
+ }
708
+ this.skillCursorIndex = Math.min(this.skillCursorIndex, Math.max(0, this.filteredSkills.length - 1));
709
+ }
710
+
711
+ handleSkillSelectorInput(data) {
712
+ if ((0, _piTui.matchesKey)(data, "escape") || (0, _piTui.matchesKey)(data, "ctrl+c")) {
713
+ this.exitEditMode();
714
+ return;
715
+ }
716
+
717
+ if ((0, _piTui.matchesKey)(data, "return")) {
718
+ const selected = [...this.skillSelectedNames];
719
+ this.updateBehavior(this.editingStep, "skills", selected);
720
+ this.exitEditMode();
721
+ return;
722
+ }
723
+
724
+ if (data === " ") {
725
+ if (this.filteredSkills.length > 0) {
726
+ const skill = this.filteredSkills[this.skillCursorIndex];
727
+ if (skill) {
728
+ if (this.skillSelectedNames.has(skill.name)) {
729
+ this.skillSelectedNames.delete(skill.name);
730
+ } else {
731
+ this.skillSelectedNames.add(skill.name);
732
+ }
733
+ }
734
+ }
735
+ this.tui.requestRender();
736
+ return;
737
+ }
738
+
739
+ if ((0, _piTui.matchesKey)(data, "up")) {
740
+ if (this.filteredSkills.length > 0) {
741
+ this.skillCursorIndex = this.skillCursorIndex === 0 ?
742
+ this.filteredSkills.length - 1 :
743
+ this.skillCursorIndex - 1;
744
+ }
745
+ this.tui.requestRender();
746
+ return;
747
+ }
748
+
749
+ if ((0, _piTui.matchesKey)(data, "down")) {
750
+ if (this.filteredSkills.length > 0) {
751
+ this.skillCursorIndex = this.skillCursorIndex === this.filteredSkills.length - 1 ?
752
+ 0 :
753
+ this.skillCursorIndex + 1;
754
+ }
755
+ this.tui.requestRender();
756
+ return;
757
+ }
758
+
759
+ if ((0, _piTui.matchesKey)(data, "backspace")) {
760
+ if (this.skillSearchQuery.length > 0) {
761
+ this.skillSearchQuery = this.skillSearchQuery.slice(0, -1);
762
+ this.filterSkills();
763
+ }
764
+ this.tui.requestRender();
765
+ return;
766
+ }
767
+
768
+ if (data.length === 1 && data.charCodeAt(0) >= 32) {
769
+ this.skillSearchQuery += data;
770
+ this.filterSkills();
771
+ this.tui.requestRender();
772
+ return;
773
+ }
774
+ }
775
+
776
+ handleEditInput(data) {
777
+ const textWidth = this.width - 4; // Must match render: innerW - 2 = (width - 2) - 2
778
+ if ((0, _piTui.matchesKey)(data, "shift+up") || (0, _piTui.matchesKey)(data, "pageup")) {
779
+ const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
780
+ const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
781
+ const targetLine = Math.max(0, cursorPos.line - this.EDIT_VIEWPORT_HEIGHT);
782
+ const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
783
+ this.editState = { ...this.editState, cursor: starts[targetLine] + targetCol };
784
+ this.tui.requestRender();
785
+ return;
786
+ }
787
+
788
+ if ((0, _piTui.matchesKey)(data, "shift+down") || (0, _piTui.matchesKey)(data, "pagedown")) {
789
+ const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
790
+ const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
791
+ const targetLine = Math.min(wrapped.length - 1, cursorPos.line + this.EDIT_VIEWPORT_HEIGHT);
792
+ const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
793
+ this.editState = { ...this.editState, cursor: starts[targetLine] + targetCol };
794
+ this.tui.requestRender();
795
+ return;
796
+ }
797
+
798
+ if ((0, _piTui.matchesKey)(data, "tab")) return;
799
+
800
+ const nextState = handleEditorInput(this.editState, data, textWidth);
801
+ if (nextState) {
802
+ this.editState = nextState;
803
+ this.tui.requestRender();
804
+ return;
805
+ }
806
+
807
+ if ((0, _piTui.matchesKey)(data, "escape")) {
808
+ this.saveEdit();
809
+ this.exitEditMode();
810
+ return;
811
+ }
812
+
813
+ if ((0, _piTui.matchesKey)(data, "ctrl+c")) {
814
+ this.exitEditMode();
815
+ return;
816
+ }
817
+ }
818
+
819
+ saveEdit() {
820
+ const stepIndex = this.editingStep;
821
+
822
+ if (this.editMode === "template") {
823
+ // For template, preserve other lines if they existed
824
+ const original = this.templates[stepIndex] ?? "";
825
+ const originalLines = original.split("\n");
826
+ originalLines[0] = this.editState.buffer;
827
+ this.templates[stepIndex] = originalLines.join("\n");
828
+ } else if (this.editMode === "output") {
829
+ // Capture OLD output before updating (for downstream propagation)
830
+ const oldBehavior = this.getEffectiveBehavior(stepIndex);
831
+ const oldOutput = typeof oldBehavior.output === "string" ? oldBehavior.output : null;
832
+
833
+ // Empty string or whitespace means disable output
834
+ const trimmed = this.editState.buffer.trim();
835
+ const newOutput = trimmed === "" ? false : trimmed;
836
+ this.updateBehavior(stepIndex, "output", newOutput);
837
+
838
+ // Propagate output filename change to downstream steps' reads
839
+ if (oldOutput && typeof newOutput === "string" && oldOutput !== newOutput) {
840
+ this.propagateOutputChange(stepIndex, oldOutput, newOutput);
841
+ }
842
+ } else if (this.editMode === "reads") {
843
+ // Parse comma-separated list, empty means disable reads
844
+ const trimmed = this.editState.buffer.trim();
845
+ if (trimmed === "") {
846
+ this.updateBehavior(stepIndex, "reads", false);
847
+ } else {
848
+ const files = trimmed.split(",").map((f) => f.trim()).filter((f) => f !== "");
849
+ this.updateBehavior(stepIndex, "reads", files.length > 0 ? files : false);
850
+ }
851
+ }
852
+ }
853
+
854
+ /**
855
+ * When a step's output filename changes, update downstream steps that read from it.
856
+ * This maintains the chain dependency automatically.
857
+ */
858
+ propagateOutputChange(changedStepIndex, oldOutput, newOutput) {
859
+ // Check all downstream steps (steps that come after the changed step)
860
+ for (let i = changedStepIndex + 1; i < this.agentConfigs.length; i++) {
861
+ const behavior = this.getEffectiveBehavior(i);
862
+
863
+ // Skip if reads is disabled or empty
864
+ if (behavior.reads === false || !behavior.reads || behavior.reads.length === 0) {
865
+ continue;
866
+ }
867
+
868
+ // Check if this step reads the old output file
869
+ const readsArray = behavior.reads;
870
+ const oldIndex = readsArray.indexOf(oldOutput);
871
+
872
+ if (oldIndex !== -1) {
873
+ // Replace old filename with new filename in reads
874
+ const newReads = [...readsArray];
875
+ newReads[oldIndex] = newOutput;
876
+ this.updateBehavior(i, "reads", newReads);
877
+ }
878
+ }
879
+ }
880
+
881
+ render(_width) {
882
+ if (this.editingStep !== null) {
883
+ if (this.editMode === "model") {
884
+ return this.renderModelSelector();
885
+ }
886
+ if (this.editMode === "thinking") {
887
+ return this.renderThinkingSelector();
888
+ }
889
+ if (this.editMode === "skills") {
890
+ return this.renderSkillSelector();
891
+ }
892
+ return this.renderFullEditMode();
893
+ }
894
+ // Mode-based navigation rendering
895
+ switch (this.mode) {
896
+ case 'single':return this.renderSingleMode();
897
+ case 'parallel':return this.renderParallelMode();
898
+ case 'chain':return this.renderChainMode();
899
+ }
900
+ }
901
+
902
+ /** Render the model selector view */
903
+ renderModelSelector() {
904
+ const th = this.theme;
905
+ const lines = [];
906
+
907
+ // Header (mode-aware terminology)
908
+ const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
909
+ const stepLabel = this.mode === 'single' ?
910
+ agentName :
911
+ this.mode === 'parallel' ?
912
+ `Task ${this.editingStep + 1}: ${agentName}` :
913
+ `Step ${this.editingStep + 1}: ${agentName}`;
914
+ const headerText = ` Select Model (${stepLabel}) `;
915
+ lines.push(this.renderHeader(headerText));
916
+ lines.push(this.row(""));
917
+
918
+ const searchPrefix = th.fg("dim", "Search: ");
919
+ const cursor = "\x1b[7m \x1b[27m"; // Reverse video space for cursor
920
+ const searchDisplay = this.modelSearchQuery + cursor;
921
+ lines.push(this.row(` ${searchPrefix}${searchDisplay}`));
922
+ lines.push(this.row(""));
923
+
924
+ const currentModel = this.getEffectiveModel(this.editingStep);
925
+ const currentModelBase = (0, _modelFallback.splitThinkingSuffix)(currentModel).baseModel;
926
+ const currentLabel = th.fg("dim", "Current: ");
927
+ lines.push(this.row(` ${currentLabel}${th.fg("warning", currentModel)}`));
928
+ lines.push(this.row(""));
929
+
930
+ if (this.filteredModels.length === 0) {
931
+ lines.push(this.row(` ${th.fg("dim", "No matching models")}`));
932
+ } else {
933
+ const maxVisible = this.MODEL_SELECTOR_HEIGHT;
934
+ let startIdx = 0;
935
+
936
+ if (this.filteredModels.length > maxVisible) {
937
+ startIdx = Math.max(0, this.modelSelectedIndex - Math.floor(maxVisible / 2));
938
+ startIdx = Math.min(startIdx, this.filteredModels.length - maxVisible);
939
+ }
940
+
941
+ const endIdx = Math.min(startIdx + maxVisible, this.filteredModels.length);
942
+
943
+ if (startIdx > 0) {
944
+ lines.push(this.row(` ${th.fg("dim", ` ↑ ${startIdx} more`)}`));
945
+ }
946
+
947
+ for (let i = startIdx; i < endIdx; i++) {
948
+ const model = this.filteredModels[i];
949
+ const isSelected = i === this.modelSelectedIndex;
950
+ const isCurrent = model.fullId === currentModelBase || model.id === currentModelBase;
951
+ const prefix = isSelected ? th.fg("accent", "→ ") : " ";
952
+ const modelText = isSelected ? th.fg("accent", model.id) : model.id;
953
+ const providerBadge = th.fg("dim", ` [${model.provider}]`);
954
+ const currentBadge = isCurrent ? th.fg("success", " current") : "";
955
+
956
+ lines.push(this.row(` ${prefix}${modelText}${providerBadge}${currentBadge}`));
957
+ }
958
+
959
+ const remaining = this.filteredModels.length - endIdx;
960
+ if (remaining > 0) {
961
+ lines.push(this.row(` ${th.fg("dim", ` ↓ ${remaining} more`)}`));
962
+ }
963
+ }
964
+
965
+ const contentLines = lines.length;
966
+ const targetHeight = 18;
967
+ for (let i = contentLines; i < targetHeight; i++) {
968
+ lines.push(this.row(""));
969
+ }
970
+
971
+ const footerText = " [Enter] Select • [Esc] Cancel • Type to search ";
972
+ lines.push(this.renderFooter(footerText));
973
+
974
+ return lines;
975
+ }
976
+
977
+ /** Render the thinking level selector view */
978
+ renderThinkingSelector() {
979
+ const th = this.theme;
980
+ const lines = [];
981
+
982
+ const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
983
+ const stepLabel = this.mode === 'single' ?
984
+ agentName :
985
+ this.mode === 'parallel' ?
986
+ `Task ${this.editingStep + 1}: ${agentName}` :
987
+ `Step ${this.editingStep + 1}: ${agentName}`;
988
+ const headerText = ` Thinking Level (${stepLabel}) `;
989
+ lines.push(this.renderHeader(headerText));
990
+ lines.push(this.row(""));
991
+
992
+ const currentModel = this.getEffectiveModel(this.editingStep);
993
+ const currentLabel = th.fg("dim", "Model: ");
994
+ lines.push(this.row(` ${currentLabel}${th.fg("accent", currentModel)}`));
995
+ lines.push(this.row(""));
996
+
997
+ lines.push(this.row(` ${th.fg("dim", "Select thinking level (extended thinking budget):")}`));
998
+ lines.push(this.row(""));
999
+
1000
+ const levelDescriptions = {
1001
+ "off": "No extended thinking",
1002
+ "minimal": "Brief reasoning",
1003
+ "low": "Light reasoning",
1004
+ "medium": "Moderate reasoning",
1005
+ "high": "Deep reasoning",
1006
+ "xhigh": "Maximum reasoning (ultrathink)"
1007
+ };
1008
+
1009
+ const levels = this.getAvailableThinkingLevels(this.editingStep);
1010
+ if (levels.length === 0) {
1011
+ lines.push(this.row(` ${th.fg("dim", "No supported thinking levels")}`));
1012
+ } else {
1013
+ for (let i = 0; i < levels.length; i++) {
1014
+ const level = levels[i];
1015
+ const isSelected = i === this.thinkingSelectedIndex;
1016
+ const prefix = isSelected ? th.fg("accent", "→ ") : " ";
1017
+ const levelText = isSelected ? th.fg("accent", level) : level;
1018
+ const desc = th.fg("dim", ` - ${levelDescriptions[level]}`);
1019
+ lines.push(this.row(` ${prefix}${levelText}${desc}`));
1020
+ }
1021
+ }
1022
+
1023
+ const contentLines = lines.length;
1024
+ const targetHeight = 16;
1025
+ for (let i = contentLines; i < targetHeight; i++) {
1026
+ lines.push(this.row(""));
1027
+ }
1028
+
1029
+ const footerText = levels.length === 0 ?
1030
+ " [Esc] Cancel " :
1031
+ " [Enter] Select • [Esc] Cancel • ↑↓ Navigate ";
1032
+ lines.push(this.renderFooter(footerText));
1033
+
1034
+ return lines;
1035
+ }
1036
+
1037
+ renderSkillSelector() {
1038
+ const innerW = this.width - 2;
1039
+ const th = this.theme;
1040
+ const lines = [];
1041
+
1042
+ const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
1043
+ const stepLabel = this.mode === 'single' ?
1044
+ agentName :
1045
+ this.mode === 'parallel' ?
1046
+ `Task ${this.editingStep + 1}: ${agentName}` :
1047
+ `Step ${this.editingStep + 1}: ${agentName}`;
1048
+ lines.push(this.renderHeader(` Select Skills (${stepLabel}) `));
1049
+ lines.push(this.row(""));
1050
+
1051
+ const cursor = "\x1b[7m \x1b[27m";
1052
+ lines.push(this.row(` ${th.fg("dim", "Search: ")}${this.skillSearchQuery}${cursor}`));
1053
+ lines.push(this.row(""));
1054
+
1055
+ const selected = [...this.skillSelectedNames].join(", ") || th.fg("dim", "(none)");
1056
+ lines.push(this.row(` ${th.fg("dim", "Selected: ")}${(0, _piTui.truncateToWidth)(selected, innerW - 12)}`));
1057
+ lines.push(this.row(""));
1058
+
1059
+ const selectorHeight = 10;
1060
+ if (this.filteredSkills.length === 0) {
1061
+ lines.push(this.row(` ${th.fg("dim", "No matching skills")}`));
1062
+ } else {
1063
+ let startIdx = 0;
1064
+ if (this.filteredSkills.length > selectorHeight) {
1065
+ startIdx = Math.max(0, this.skillCursorIndex - Math.floor(selectorHeight / 2));
1066
+ startIdx = Math.min(startIdx, this.filteredSkills.length - selectorHeight);
1067
+ }
1068
+ const endIdx = Math.min(startIdx + selectorHeight, this.filteredSkills.length);
1069
+
1070
+ if (startIdx > 0) {
1071
+ lines.push(this.row(` ${th.fg("dim", ` ↑ ${startIdx} more`)}`));
1072
+ }
1073
+
1074
+ for (let i = startIdx; i < endIdx; i++) {
1075
+ const skill = this.filteredSkills[i];
1076
+ const isCursor = i === this.skillCursorIndex;
1077
+ const isSelected = this.skillSelectedNames.has(skill.name);
1078
+
1079
+ const prefix = isCursor ? th.fg("accent", "→ ") : " ";
1080
+ const checkbox = isSelected ? th.fg("success", "[x]") : "[ ]";
1081
+ const nameText = isCursor ? th.fg("accent", skill.name) : skill.name;
1082
+ const sourceBadge = th.fg("dim", ` [${skill.source}]`);
1083
+ const desc = skill.description ?
1084
+ th.fg("dim", ` - ${(0, _piTui.truncateToWidth)(skill.description, 25)}`) :
1085
+ "";
1086
+
1087
+ lines.push(this.row(` ${prefix}${checkbox} ${nameText}${sourceBadge}${desc}`));
1088
+ }
1089
+
1090
+ const remaining = this.filteredSkills.length - endIdx;
1091
+ if (remaining > 0) {
1092
+ lines.push(this.row(` ${th.fg("dim", ` ↓ ${remaining} more`)}`));
1093
+ }
1094
+ }
1095
+
1096
+ const targetHeight = 18;
1097
+ for (let i = lines.length; i < targetHeight; i++) {
1098
+ lines.push(this.row(""));
1099
+ }
1100
+
1101
+ lines.push(this.renderFooter(" [Enter] Confirm • [Space] Toggle • [Esc] Cancel "));
1102
+ return lines;
1103
+ }
1104
+
1105
+ getFooterText() {
1106
+ const bgLabel = this.runInBackground ? '[b]g:ON' : '[b]g';
1107
+ switch (this.mode) {
1108
+ case 'single':
1109
+ return ` [Enter] Run • [Esc] Cancel • e m t w s ${bgLabel} `;
1110
+ case 'parallel':
1111
+ return ` [Enter] Run • [Esc] Cancel • e m t s ${bgLabel} • ↑↓ Nav `;
1112
+ case 'chain':
1113
+ return ` [Enter] Run • [Esc] Cancel • e m t w r p s ${bgLabel} • ↑↓ Nav `;
1114
+ }
1115
+ }
1116
+
1117
+ appendNotice(lines) {
1118
+ if (!this.noticeMessage) return;
1119
+ const color = this.noticeMessage.type === "error" ? "error" : "success";
1120
+ lines.push(this.row(` ${this.theme.fg(color, this.noticeMessage.text)}`));
1121
+ }
1122
+
1123
+ renderSingleMode() {
1124
+ const innerW = this.width - 2;
1125
+ const th = this.theme;
1126
+ const lines = [];
1127
+
1128
+ const agentName = this.agentConfigs[0]?.name ?? "unknown";
1129
+ const maxHeaderLen = innerW - 4;
1130
+ const headerText = ` Agent: ${(0, _piTui.truncateToWidth)(agentName, maxHeaderLen - 9)} `;
1131
+ lines.push(this.renderHeader(headerText));
1132
+ lines.push(this.row(""));
1133
+
1134
+ const config = this.agentConfigs[0];
1135
+ const behavior = this.getEffectiveBehavior(0);
1136
+
1137
+ const stepLabel = config.name;
1138
+ lines.push(this.row(` ${th.fg("accent", "▶ " + stepLabel)}`));
1139
+
1140
+ const template = (this.templates[0] ?? "").split("\n")[0] ?? "";
1141
+ const taskLabel = th.fg("dim", "task: ");
1142
+ lines.push(this.row(` ${taskLabel}${(0, _piTui.truncateToWidth)(template, innerW - 12)}`));
1143
+
1144
+ const effectiveModel = this.getEffectiveModel(0);
1145
+ const override = this.behaviorOverrides.get(0);
1146
+ const isOverridden = override?.model !== undefined;
1147
+ const modelValue = isOverridden ?
1148
+ th.fg("warning", effectiveModel) + th.fg("dim", " ✎") :
1149
+ effectiveModel;
1150
+ const modelLabel = th.fg("dim", "model: ");
1151
+ lines.push(this.row(` ${modelLabel}${(0, _piTui.truncateToWidth)(modelValue, innerW - 13)}`));
1152
+
1153
+ const writesValue = behavior.output === false ?
1154
+ th.fg("dim", "(disabled)") :
1155
+ behavior.output || th.fg("dim", "(none)");
1156
+ const writesLabel = th.fg("dim", "writes: ");
1157
+ lines.push(this.row(` ${writesLabel}${(0, _piTui.truncateToWidth)(writesValue, innerW - 14)}`));
1158
+
1159
+ const skillsValue = behavior.skills === false ?
1160
+ th.fg("dim", "(disabled)") :
1161
+ behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)");
1162
+ const skillsLabel = th.fg("dim", "skills: ");
1163
+ lines.push(this.row(` ${skillsLabel}${(0, _piTui.truncateToWidth)(skillsValue, innerW - 14)}`));
1164
+
1165
+ lines.push(this.row(""));
1166
+
1167
+ this.appendNotice(lines);
1168
+ lines.push(this.renderFooter(this.getFooterText()));
1169
+
1170
+ return lines;
1171
+ }
1172
+
1173
+ renderParallelMode() {
1174
+ const innerW = this.width - 2;
1175
+ const th = this.theme;
1176
+ const lines = [];
1177
+
1178
+ const headerText = ` Parallel Tasks (${this.agentConfigs.length}) `;
1179
+ lines.push(this.renderHeader(headerText));
1180
+ lines.push(this.row(""));
1181
+
1182
+ for (let i = 0; i < this.agentConfigs.length; i++) {
1183
+ const config = this.agentConfigs[i];
1184
+ const isSelected = i === this.selectedStep;
1185
+
1186
+ const color = isSelected ? "accent" : "dim";
1187
+ const prefix = isSelected ? "▶ " : " ";
1188
+ const taskPrefix = `Task ${i + 1}: `;
1189
+ const maxNameLen = innerW - 4 - prefix.length - taskPrefix.length;
1190
+ const agentName = config.name.length > maxNameLen ?
1191
+ config.name.slice(0, maxNameLen - 1) + "…" :
1192
+ config.name;
1193
+ const taskLabel = `${taskPrefix}${agentName}`;
1194
+ lines.push(this.row(` ${th.fg(color, prefix + taskLabel)}`));
1195
+
1196
+ const template = (this.templates[i] ?? "").split("\n")[0] ?? "";
1197
+ const taskTextLabel = th.fg("dim", "task: ");
1198
+ lines.push(this.row(` ${taskTextLabel}${(0, _piTui.truncateToWidth)(template, innerW - 12)}`));
1199
+
1200
+ const effectiveModel = this.getEffectiveModel(i);
1201
+ const override = this.behaviorOverrides.get(i);
1202
+ const isOverridden = override?.model !== undefined;
1203
+ const modelValue = isOverridden ?
1204
+ th.fg("warning", effectiveModel) + th.fg("dim", " ✎") :
1205
+ effectiveModel;
1206
+ const modelLabel = th.fg("dim", "model: ");
1207
+ lines.push(this.row(` ${modelLabel}${(0, _piTui.truncateToWidth)(modelValue, innerW - 13)}`));
1208
+
1209
+ const behavior = this.getEffectiveBehavior(i);
1210
+ const skillsValue = behavior.skills === false ?
1211
+ th.fg("dim", "(disabled)") :
1212
+ behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)");
1213
+ const skillsLabel = th.fg("dim", "skills: ");
1214
+ lines.push(this.row(` ${skillsLabel}${(0, _piTui.truncateToWidth)(skillsValue, innerW - 14)}`));
1215
+
1216
+ lines.push(this.row(""));
1217
+ }
1218
+
1219
+ this.appendNotice(lines);
1220
+ lines.push(this.renderFooter(this.getFooterText()));
1221
+
1222
+ return lines;
1223
+ }
1224
+
1225
+ renderChainMode() {
1226
+ const innerW = this.width - 2;
1227
+ const th = this.theme;
1228
+ const lines = [];
1229
+
1230
+ const chainLabel = this.agentConfigs.map((c) => c.name).join(" → ");
1231
+ const maxHeaderLen = innerW - 4;
1232
+ const headerText = ` Chain: ${(0, _piTui.truncateToWidth)(chainLabel, maxHeaderLen - 9)} `;
1233
+ lines.push(this.renderHeader(headerText));
1234
+
1235
+ lines.push(this.row(""));
1236
+
1237
+ const taskPreview = (0, _piTui.truncateToWidth)(this.originalTask, innerW - 16);
1238
+ lines.push(this.row(` Original Task: ${taskPreview}`));
1239
+ const chainDirPreview = (0, _piTui.truncateToWidth)(this.chainDir ?? "", innerW - 12);
1240
+ lines.push(this.row(` Chain Dir: ${th.fg("dim", chainDirPreview)}`));
1241
+
1242
+ const progressEnabled = this.agentConfigs.some((_, i) => this.getEffectiveBehavior(i).progress);
1243
+ const progressValue = progressEnabled ? th.fg("success", "enabled") : th.fg("dim", "disabled");
1244
+ lines.push(this.row(` Progress: ${progressValue} ${th.fg("dim", "(press [p] to toggle)")}`));
1245
+ lines.push(this.row(""));
1246
+
1247
+ for (let i = 0; i < this.agentConfigs.length; i++) {
1248
+ const config = this.agentConfigs[i];
1249
+ const isSelected = i === this.selectedStep;
1250
+ const behavior = this.getEffectiveBehavior(i);
1251
+
1252
+ const color = isSelected ? "accent" : "dim";
1253
+ const prefix = isSelected ? "▶ " : " ";
1254
+ const stepPrefix = `Step ${i + 1}: `;
1255
+ const maxNameLen = innerW - 4 - prefix.length - stepPrefix.length;
1256
+ const agentName = config.name.length > maxNameLen ?
1257
+ config.name.slice(0, maxNameLen - 1) + "…" :
1258
+ config.name;
1259
+ const stepLabel = `${stepPrefix}${agentName}`;
1260
+ lines.push(
1261
+ this.row(` ${th.fg(color, prefix + stepLabel)}`)
1262
+ );
1263
+
1264
+ const template = (this.templates[i] ?? "").split("\n")[0] ?? "";
1265
+ const highlighted = template.
1266
+ replace(/\{task\}/g, th.fg("success", "{task}")).
1267
+ replace(/\{previous\}/g, th.fg("warning", "{previous}")).
1268
+ replace(/\{chain_dir\}/g, th.fg("accent", "{chain_dir}"));
1269
+
1270
+ const templateLabel = th.fg("dim", "task: ");
1271
+ lines.push(this.row(` ${templateLabel}${(0, _piTui.truncateToWidth)(highlighted, innerW - 12)}`));
1272
+
1273
+ const effectiveModel = this.getEffectiveModel(i);
1274
+ const override = this.behaviorOverrides.get(i);
1275
+ const isOverridden = override?.model !== undefined;
1276
+ const modelValue = isOverridden ?
1277
+ th.fg("warning", effectiveModel) + th.fg("dim", " ✎") :
1278
+ effectiveModel;
1279
+ const modelLabel = th.fg("dim", "model: ");
1280
+ lines.push(this.row(` ${modelLabel}${(0, _piTui.truncateToWidth)(modelValue, innerW - 13)}`));
1281
+
1282
+ const writesValue = behavior.output === false ?
1283
+ th.fg("dim", "(disabled)") :
1284
+ behavior.output || th.fg("dim", "(none)");
1285
+ const writesLabel = th.fg("dim", "writes: ");
1286
+ lines.push(this.row(` ${writesLabel}${(0, _piTui.truncateToWidth)(writesValue, innerW - 14)}`));
1287
+
1288
+ const readsValue = behavior.reads === false ?
1289
+ th.fg("dim", "(disabled)") :
1290
+ behavior.reads && behavior.reads.length > 0 ?
1291
+ behavior.reads.join(", ") :
1292
+ th.fg("dim", "(none)");
1293
+ const readsLabel = th.fg("dim", "reads: ");
1294
+ lines.push(this.row(` ${readsLabel}${(0, _piTui.truncateToWidth)(readsValue, innerW - 13)}`));
1295
+
1296
+ const skillsValue = behavior.skills === false ?
1297
+ th.fg("dim", "(disabled)") :
1298
+ behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)");
1299
+ const skillsLabel = th.fg("dim", "skills: ");
1300
+ lines.push(this.row(` ${skillsLabel}${(0, _piTui.truncateToWidth)(skillsValue, innerW - 14)}`));
1301
+
1302
+ if (progressEnabled) {
1303
+ const isFirstStep = i === 0;
1304
+ const progressAction = isFirstStep ?
1305
+ th.fg("success", "writes progress.md") :
1306
+ th.fg("accent", "reads progress.md");
1307
+ const progressLabel = th.fg("dim", "progress: ");
1308
+ lines.push(this.row(` ${progressLabel}${progressAction}`));
1309
+ }
1310
+
1311
+ if (i < this.agentConfigs.length - 1) {
1312
+ const nextStepUsePrevious = (this.templates[i + 1] ?? "").includes("{previous}");
1313
+ if (nextStepUsePrevious) {
1314
+ const indicator = th.fg("dim", " ↳ response → ") + th.fg("warning", "{previous}");
1315
+ lines.push(this.row(indicator));
1316
+ }
1317
+ }
1318
+
1319
+ lines.push(this.row(""));
1320
+ }
1321
+
1322
+ this.appendNotice(lines);
1323
+ lines.push(this.renderFooter(this.getFooterText()));
1324
+
1325
+ return lines;
1326
+ }
1327
+
1328
+ invalidate() {}
1329
+ dispose() {
1330
+ if (this.noticeMessageTimer) clearTimeout(this.noticeMessageTimer);
1331
+ this.noticeMessageTimer = null;
1332
+ }
1333
+ }exports.ChainClarifyComponent = ChainClarifyComponent; /* v9-86a1ae2c0ca16164 */
pip-tmp/jiti/foreground-chain-execution.e441430b.mjs ADDED
@@ -0,0 +1,932 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.executeChain = executeChain;
2
+
3
+
4
+
5
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
6
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
7
+
8
+
9
+
10
+ var _chainClarify = await jitiImport("./chain-clarify.ts");
11
+ var _modelInfo = await jitiImport("../../shared/model-info.ts");
12
+ var _settings = await jitiImport("../../shared/settings.ts");
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+ var _skills = await jitiImport("../../agents/skills.ts");
32
+ var _intercomBridge = await jitiImport("../../intercom/intercom-bridge.ts");
33
+ var _execution = await jitiImport("./execution.ts");
34
+ var _formatters = await jitiImport("../../shared/formatters.ts");
35
+ var _utils = await jitiImport("../../shared/utils.ts");
36
+ var _runHistory = await jitiImport("../shared/run-history.ts");
37
+ var _worktree = await jitiImport("../shared/worktree.ts");
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ var _types = await jitiImport("../../shared/types.ts");
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+ var _modelFallback = await jitiImport("../shared/model-fallback.ts");
60
+ var _singleOutput = await jitiImport("../shared/single-output.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /**
61
+ * Chain execution logic for subagent tool
62
+ */
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ function buildChainExecutionDetails(input) {
118
+ return (0, _utils.compactForegroundDetails)({
119
+ mode: "chain",
120
+ results: input.results,
121
+ progress: input.includeProgress ? input.allProgress : undefined,
122
+ artifacts: input.allArtifactPaths.length ? { dir: input.artifactsDir, files: input.allArtifactPaths } : undefined,
123
+ chainAgents: input.chainAgents,
124
+ totalSteps: input.totalSteps,
125
+ currentStepIndex: input.currentStepIndex
126
+ });
127
+ }
128
+
129
+ function buildChainExecutionErrorResult(message, input) {
130
+ return {
131
+ content: [{ type: "text", text: message }],
132
+ isError: true,
133
+ details: buildChainExecutionDetails(input)
134
+ };
135
+ }
136
+
137
+ function ensureParallelProgressFile(
138
+ chainDir,
139
+ progressCreated,
140
+ parallelBehaviors)
141
+ {
142
+ if (progressCreated || !parallelBehaviors.some((behavior) => behavior.progress)) {
143
+ return progressCreated;
144
+ }
145
+ (0, _settings.writeInitialProgressFile)(chainDir);
146
+ return true;
147
+ }
148
+
149
+ function appendParallelWorktreeSummary(
150
+ output,
151
+ worktreeSetup,
152
+ diffsDir,
153
+ agents)
154
+ {
155
+ if (!worktreeSetup) return output;
156
+ const diffs = (0, _worktree.diffWorktrees)(worktreeSetup, agents, diffsDir);
157
+ const diffSummary = (0, _worktree.formatWorktreeDiffSummary)(diffs);
158
+ if (!diffSummary) return output;
159
+ return `${output}\n\n${diffSummary}`;
160
+ }
161
+
162
+ async function runParallelChainTasks(input) {
163
+ const concurrency = input.step.concurrency ?? _types.MAX_CONCURRENCY;
164
+ const failFast = input.step.failFast ?? false;
165
+ let aborted = false;
166
+
167
+ const parallelResults = await (0, _utils.mapConcurrent)(
168
+ input.step.parallel,
169
+ concurrency,
170
+ async (task, taskIndex) => {
171
+ if (aborted && failFast) {
172
+ return {
173
+ agent: task.agent,
174
+ task: "(skipped)",
175
+ exitCode: -1,
176
+ messages: [],
177
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
178
+ error: "Skipped due to fail-fast"
179
+ };
180
+ }
181
+
182
+ const taskTemplate = input.parallelTemplates[taskIndex] ?? "{previous}";
183
+ const behavior = (0, _settings.suppressProgressForReadOnlyTask)(input.parallelBehaviors[taskIndex], taskTemplate, input.originalTask);
184
+ const templateHasPrevious = taskTemplate.includes("{previous}");
185
+ const { prefix, suffix } = (0, _settings.buildChainInstructions)(
186
+ behavior,
187
+ input.chainDir,
188
+ false,
189
+ templateHasPrevious ? undefined : input.prev
190
+ );
191
+
192
+ let taskStr = taskTemplate;
193
+ taskStr = taskStr.replace(/\{task\}/g, input.originalTask);
194
+ taskStr = taskStr.replace(/\{previous\}/g, input.prev);
195
+ taskStr = taskStr.replace(/\{chain_dir\}/g, input.chainDir);
196
+ const cleanTask = taskStr;
197
+ taskStr = prefix + taskStr + suffix;
198
+
199
+ const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
200
+ const effectiveModel =
201
+ (task.model ? (0, _modelFallback.resolveModelCandidate)(task.model, input.availableModels, input.ctx.model?.provider) : null) ??
202
+ (0, _modelFallback.resolveModelCandidate)(taskAgentConfig?.model, input.availableModels, input.ctx.model?.provider);
203
+ const maxSubagentDepth = (0, _types.resolveChildMaxSubagentDepth)(input.maxSubagentDepth, taskAgentConfig?.maxSubagentDepth);
204
+
205
+ const taskCwd = input.worktreeSetup ?
206
+ input.worktreeSetup.worktrees[taskIndex].agentCwd :
207
+ (0, _utils.resolveChildCwd)(input.cwd ?? input.ctx.cwd, task.cwd);
208
+
209
+ const outputPath = typeof behavior.output === "string" ?
210
+ path.isAbsolute(behavior.output) ? behavior.output : path.join(input.chainDir, behavior.output) :
211
+ undefined;
212
+ const interruptController = new AbortController();
213
+ if (input.foregroundControl) {
214
+ input.foregroundControl.currentAgent = task.agent;
215
+ input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
216
+ input.foregroundControl.currentActivityState = undefined;
217
+ input.foregroundControl.updatedAt = Date.now();
218
+ input.foregroundControl.interrupt = () => {
219
+ if (interruptController.signal.aborted) return false;
220
+ interruptController.abort();
221
+ input.foregroundControl.currentActivityState = undefined;
222
+ input.foregroundControl.updatedAt = Date.now();
223
+ return true;
224
+ };
225
+ }
226
+
227
+ const result = await (0, _execution.runSync)(input.ctx.cwd, input.agents, task.agent, taskStr, {
228
+ cwd: taskCwd,
229
+ signal: input.signal,
230
+ interruptSignal: interruptController.signal,
231
+ allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(_intercomBridge.INTERCOM_BRIDGE_MARKER) === true,
232
+ intercomEvents: input.intercomEvents,
233
+ runId: input.runId,
234
+ index: input.globalTaskIndex + taskIndex,
235
+ sessionDir: input.sessionDirForIndex(input.globalTaskIndex + taskIndex),
236
+ sessionFile: input.sessionFileForIndex?.(input.globalTaskIndex + taskIndex),
237
+ share: input.shareEnabled,
238
+ artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
239
+ artifactConfig: input.artifactConfig,
240
+ outputPath,
241
+ outputMode: behavior.outputMode,
242
+ maxSubagentDepth,
243
+ controlConfig: input.controlConfig,
244
+ onControlEvent: input.onControlEvent,
245
+ intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
246
+ orchestratorIntercomTarget: input.orchestratorIntercomTarget,
247
+ modelOverride: effectiveModel,
248
+ availableModels: input.availableModels,
249
+ preferredModelProvider: input.ctx.model?.provider,
250
+ skills: behavior.skills === false ? [] : behavior.skills,
251
+ onUpdate: input.onUpdate ?
252
+ (progressUpdate) => {
253
+ const stepResults = progressUpdate.details?.results || [];
254
+ const stepProgress = progressUpdate.details?.progress || [];
255
+ if (input.foregroundControl && stepProgress.length > 0) {
256
+ const current = stepProgress[0];
257
+ input.foregroundControl.currentAgent = task.agent;
258
+ input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
259
+ input.foregroundControl.currentActivityState = current?.activityState;
260
+ input.foregroundControl.lastActivityAt = current?.lastActivityAt;
261
+ input.foregroundControl.currentTool = current?.currentTool;
262
+ input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
263
+ input.foregroundControl.currentPath = current?.currentPath;
264
+ input.foregroundControl.turnCount = current?.turnCount;
265
+ input.foregroundControl.tokens = current?.tokens;
266
+ input.foregroundControl.toolCount = current?.toolCount;
267
+ input.foregroundControl.updatedAt = Date.now();
268
+ }
269
+ input.onUpdate?.({
270
+ ...progressUpdate,
271
+ details: {
272
+ mode: "chain",
273
+ results: input.results.concat(stepResults),
274
+ progress: input.allProgress.concat(stepProgress),
275
+ controlEvents: progressUpdate.details?.controlEvents,
276
+ chainAgents: input.chainAgents,
277
+ totalSteps: input.totalSteps,
278
+ currentStepIndex: input.stepIndex
279
+ }
280
+ });
281
+ } :
282
+ undefined
283
+ });
284
+ if (input.foregroundControl?.currentIndex === input.globalTaskIndex + taskIndex) {
285
+ input.foregroundControl.interrupt = undefined;
286
+ input.foregroundControl.updatedAt = Date.now();
287
+ }
288
+
289
+ if (result.exitCode !== 0 && failFast) {
290
+ aborted = true;
291
+ }
292
+ (0, _runHistory.recordRun)(task.agent, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0);
293
+ return result;
294
+ }
295
+ );
296
+
297
+ return parallelResults;
298
+ }
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+ /**
350
+ * Execute a chain of subagent steps
351
+ */
352
+ async function executeChain(params) {
353
+ const {
354
+ chain: chainSteps,
355
+ agents,
356
+ ctx,
357
+ signal,
358
+ runId,
359
+ cwd,
360
+ shareEnabled,
361
+ sessionDirForIndex,
362
+ sessionFileForIndex,
363
+ artifactsDir,
364
+ artifactConfig,
365
+ includeProgress,
366
+ clarify,
367
+ onUpdate,
368
+ onControlEvent,
369
+ controlConfig,
370
+ childIntercomTarget,
371
+ orchestratorIntercomTarget,
372
+ foregroundControl,
373
+ intercomEvents,
374
+ chainSkills: chainSkillsParam,
375
+ chainDir: chainDirBase
376
+ } = params;
377
+ const chainSkills = chainSkillsParam ?? [];
378
+
379
+ const allProgress = [];
380
+ const allArtifactPaths = [];
381
+
382
+ const chainAgents = chainSteps.map((step) =>
383
+ (0, _settings.isParallelStep)(step) ?
384
+ `[${step.parallel.map((t) => t.agent).join("+")}]` :
385
+ step.agent
386
+ );
387
+ const totalSteps = chainSteps.length;
388
+
389
+ const firstStep = chainSteps[0];
390
+ const originalTask = params.task ?? (
391
+ (0, _settings.isParallelStep)(firstStep) ? firstStep.parallel[0].task : firstStep.task);
392
+
393
+ const chainDir = (0, _settings.createChainDir)(runId, chainDirBase);
394
+ const hasParallelSteps = chainSteps.some(_settings.isParallelStep);
395
+ let templates = (0, _settings.resolveChainTemplates)(chainSteps);
396
+ const shouldClarify = clarify !== false && ctx.hasUI && !hasParallelSteps;
397
+ let tuiBehaviorOverrides;
398
+ const availableModels = ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo);
399
+ const availableSkills = (0, _skills.discoverAvailableSkills)(cwd ?? ctx.cwd);
400
+
401
+ if (shouldClarify) {
402
+ const seqSteps = chainSteps;
403
+ const agentConfigs = [];
404
+ for (const step of seqSteps) {
405
+ const config = agents.find((a) => a.name === step.agent);
406
+ if (!config) {
407
+ (0, _settings.removeChainDir)(chainDir);
408
+ return {
409
+ content: [{ type: "text", text: `Unknown agent: ${step.agent}` }],
410
+ isError: true,
411
+ details: { mode: "chain", results: [] }
412
+ };
413
+ }
414
+ agentConfigs.push(config);
415
+ }
416
+
417
+ const stepOverrides = seqSteps.map((step) => ({
418
+ output: step.output,
419
+ outputMode: step.outputMode,
420
+ reads: step.reads,
421
+ progress: step.progress,
422
+ skills: (0, _skills.normalizeSkillInput)(step.skill),
423
+ model: step.model
424
+ }));
425
+
426
+ const resolvedBehaviors = agentConfigs.map((config, i) =>
427
+ (0, _settings.resolveStepBehavior)(config, stepOverrides[i], chainSkills)
428
+ );
429
+ const flatTemplates = templates;
430
+
431
+ const result = await ctx.ui.custom(
432
+ (tui, theme, _kb, done) =>
433
+ new _chainClarify.ChainClarifyComponent(
434
+ tui,
435
+ theme,
436
+ agentConfigs,
437
+ flatTemplates,
438
+ originalTask,
439
+ chainDir,
440
+ resolvedBehaviors,
441
+ availableModels,
442
+ ctx.model?.provider,
443
+ availableSkills,
444
+ done
445
+ ),
446
+ {
447
+ overlay: true,
448
+ overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" }
449
+ }
450
+ );
451
+
452
+ if (!result || !result.confirmed) {
453
+ (0, _settings.removeChainDir)(chainDir);
454
+ return {
455
+ content: [{ type: "text", text: "Chain cancelled" }],
456
+ details: { mode: "chain", results: [] }
457
+ };
458
+ }
459
+
460
+ if (result.runInBackground) {
461
+ (0, _settings.removeChainDir)(chainDir);
462
+ const updatedChain = chainSteps.map((step, i) => {
463
+ if ((0, _settings.isParallelStep)(step)) return step;
464
+ const override = result.behaviorOverrides[i];
465
+ return {
466
+ ...step,
467
+ task: result.templates[i],
468
+ ...(override?.model ? { model: override.model } : {}),
469
+ ...(override?.output !== undefined ? { output: override.output } : {}),
470
+ ...("outputMode" in step && step.outputMode !== undefined ? { outputMode: step.outputMode } : {}),
471
+ ...(override?.reads !== undefined ? { reads: override.reads } : {}),
472
+ ...(override?.progress !== undefined ? { progress: override.progress } : {}),
473
+ ...(override?.skills !== undefined ? { skill: override.skills } : {})
474
+ };
475
+ });
476
+ return {
477
+ content: [{ type: "text", text: "Launching in background..." }],
478
+ details: { mode: "chain", results: [] },
479
+ requestedAsync: { chain: updatedChain, chainSkills }
480
+ };
481
+ }
482
+
483
+ templates = result.templates;
484
+ tuiBehaviorOverrides = result.behaviorOverrides;
485
+ }
486
+
487
+ const results = [];
488
+ let prev = "";
489
+ let globalTaskIndex = 0;
490
+ let progressCreated = false;
491
+
492
+ for (let stepIndex = 0; stepIndex < chainSteps.length; stepIndex++) {
493
+ const step = chainSteps[stepIndex];
494
+ const stepTemplates = templates[stepIndex];
495
+
496
+ if ((0, _settings.isParallelStep)(step)) {
497
+ const parallelTemplates = stepTemplates;
498
+ const parallelCwd = (0, _utils.resolveChildCwd)(cwd ?? ctx.cwd, step.cwd);
499
+ let worktreeSetup;
500
+ if (step.worktree) {
501
+ const worktreeTaskCwdConflict = (0, _worktree.findWorktreeTaskCwdConflict)(step.parallel, parallelCwd);
502
+ if (worktreeTaskCwdConflict) {
503
+ return buildChainExecutionErrorResult(
504
+ `parallel chain step ${stepIndex + 1}: ${(0, _worktree.formatWorktreeTaskCwdConflict)(worktreeTaskCwdConflict, parallelCwd)}`,
505
+ {
506
+ results,
507
+ includeProgress,
508
+ allProgress,
509
+ allArtifactPaths,
510
+ artifactsDir,
511
+ chainAgents,
512
+ totalSteps,
513
+ currentStepIndex: stepIndex
514
+ }
515
+ );
516
+ }
517
+ try {
518
+ worktreeSetup = (0, _worktree.createWorktrees)(parallelCwd, `${runId}-s${stepIndex}`, step.parallel.length, {
519
+ agents: step.parallel.map((task) => task.agent),
520
+ setupHook: params.worktreeSetupHook ?
521
+ { hookPath: params.worktreeSetupHook, timeoutMs: params.worktreeSetupHookTimeoutMs } :
522
+ undefined
523
+ });
524
+ } catch (error) {
525
+ const message = error instanceof Error ? error.message : String(error);
526
+ return buildChainExecutionErrorResult(message, {
527
+ results,
528
+ includeProgress,
529
+ allProgress,
530
+ allArtifactPaths,
531
+ artifactsDir,
532
+ chainAgents,
533
+ totalSteps,
534
+ currentStepIndex: stepIndex
535
+ });
536
+ }
537
+ }
538
+
539
+ try {
540
+ const agentNames = step.parallel.map((task) => task.agent);
541
+ const parallelBehaviors = (0, _settings.resolveParallelBehaviors)(step.parallel, agents, stepIndex, chainSkills).
542
+ map((behavior, taskIndex) => (0, _settings.suppressProgressForReadOnlyTask)(behavior, parallelTemplates[taskIndex] ?? step.parallel[taskIndex]?.task, originalTask));
543
+ for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
544
+ const behavior = parallelBehaviors[taskIndex];
545
+ const outputPath = typeof behavior.output === "string" ?
546
+ path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output) :
547
+ undefined;
548
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(behavior.outputMode, outputPath, `Parallel chain step ${stepIndex + 1} task ${taskIndex + 1} (${step.parallel[taskIndex].agent})`);
549
+ if (validationError) return buildChainExecutionErrorResult(validationError, {
550
+ results,
551
+ includeProgress,
552
+ allProgress,
553
+ allArtifactPaths,
554
+ artifactsDir,
555
+ chainAgents,
556
+ totalSteps,
557
+ currentStepIndex: stepIndex
558
+ });
559
+ }
560
+ progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
561
+ (0, _settings.createParallelDirs)(chainDir, stepIndex, step.parallel.length, agentNames);
562
+
563
+ const parallelResults = await runParallelChainTasks({
564
+ step,
565
+ parallelTemplates,
566
+ parallelBehaviors,
567
+ agents,
568
+ stepIndex,
569
+ availableModels,
570
+ chainDir,
571
+ prev,
572
+ originalTask,
573
+ ctx,
574
+ intercomEvents,
575
+ cwd,
576
+ runId,
577
+ globalTaskIndex,
578
+ sessionDirForIndex,
579
+ sessionFileForIndex,
580
+ shareEnabled,
581
+ artifactConfig,
582
+ artifactsDir,
583
+ signal,
584
+ onUpdate,
585
+ results,
586
+ allProgress,
587
+ chainAgents,
588
+ totalSteps,
589
+ controlConfig,
590
+ onControlEvent,
591
+ childIntercomTarget,
592
+ orchestratorIntercomTarget,
593
+ foregroundControl,
594
+ worktreeSetup,
595
+ maxSubagentDepth: params.maxSubagentDepth
596
+ });
597
+ globalTaskIndex += step.parallel.length;
598
+
599
+ for (const result of parallelResults) {
600
+ results.push(result);
601
+ if (result.progress) allProgress.push(result.progress);
602
+ if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
603
+ }
604
+
605
+ const interrupted = parallelResults.find((result) => result.interrupted);
606
+ if (interrupted) {
607
+ return {
608
+ content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
609
+ details: buildChainExecutionDetails({
610
+ results,
611
+ includeProgress,
612
+ allProgress,
613
+ allArtifactPaths,
614
+ artifactsDir,
615
+ chainAgents,
616
+ totalSteps,
617
+ currentStepIndex: stepIndex
618
+ })
619
+ };
620
+ }
621
+ const detachedIndexInStep = parallelResults.findIndex((result) => result.detached);
622
+ const detached = detachedIndexInStep >= 0 ? parallelResults[detachedIndexInStep] : undefined;
623
+ if (detached) {
624
+ return {
625
+ content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
626
+ details: buildChainExecutionDetails({
627
+ results,
628
+ includeProgress,
629
+ allProgress,
630
+ allArtifactPaths,
631
+ artifactsDir,
632
+ chainAgents,
633
+ totalSteps,
634
+ currentStepIndex: stepIndex
635
+ })
636
+ };
637
+ }
638
+
639
+ const failures = parallelResults.
640
+ map((result, originalIndex) => ({ ...result, originalIndex })).
641
+ filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
642
+ if (failures.length > 0) {
643
+ const failureSummary = failures.
644
+ map((failure) => `- Task ${failure.originalIndex + 1} (${failure.agent}): ${failure.error || "failed"}`).
645
+ join("\n");
646
+ const errorMsg = `Parallel step ${stepIndex + 1} failed:\n${failureSummary}`;
647
+ const summary = (0, _formatters.buildChainSummary)(chainSteps, results, chainDir, "failed", {
648
+ index: stepIndex,
649
+ error: errorMsg
650
+ });
651
+ return {
652
+ content: [{ type: "text", text: summary }],
653
+ isError: true,
654
+ details: buildChainExecutionDetails({
655
+ results,
656
+ includeProgress,
657
+ allProgress,
658
+ allArtifactPaths,
659
+ artifactsDir,
660
+ chainAgents,
661
+ totalSteps,
662
+ currentStepIndex: stepIndex
663
+ })
664
+ };
665
+ }
666
+
667
+ const taskResults = parallelResults.map((result, i) => {
668
+ const outputTarget = parallelBehaviors[i]?.output;
669
+ const outputTargetPath = typeof outputTarget === "string" ?
670
+ path.isAbsolute(outputTarget) ? outputTarget : path.join(chainDir, outputTarget) :
671
+ undefined;
672
+ return {
673
+ agent: result.agent,
674
+ taskIndex: i,
675
+ output: (0, _utils.getSingleResultOutput)(result),
676
+ exitCode: result.exitCode,
677
+ error: result.error,
678
+ outputTargetPath,
679
+ outputTargetExists: outputTargetPath ? fs.existsSync(outputTargetPath) : undefined
680
+ };
681
+ });
682
+ prev = (0, _settings.aggregateParallelOutputs)(taskResults);
683
+ prev = appendParallelWorktreeSummary(
684
+ prev,
685
+ worktreeSetup,
686
+ path.join(chainDir, "worktree-diffs", `step-${stepIndex}`),
687
+ agentNames
688
+ );
689
+ } finally {
690
+ if (worktreeSetup) (0, _worktree.cleanupWorktrees)(worktreeSetup);
691
+ }
692
+ } else {
693
+ const seqStep = step;
694
+ const stepTemplate = stepTemplates;
695
+
696
+ const agentConfig = agents.find((a) => a.name === seqStep.agent);
697
+ if (!agentConfig) {
698
+ (0, _settings.removeChainDir)(chainDir);
699
+ return {
700
+ content: [{ type: "text", text: `Unknown agent: ${seqStep.agent}` }],
701
+ isError: true,
702
+ details: { mode: "chain", results: [] }
703
+ };
704
+ }
705
+
706
+ const tuiOverride = tuiBehaviorOverrides?.[stepIndex];
707
+ const stepOverride = {
708
+ output: tuiOverride?.output !== undefined ? tuiOverride.output : seqStep.output,
709
+ outputMode: seqStep.outputMode,
710
+ reads: tuiOverride?.reads !== undefined ? tuiOverride.reads : seqStep.reads,
711
+ progress: tuiOverride?.progress !== undefined ? tuiOverride.progress : seqStep.progress,
712
+ skills:
713
+ tuiOverride?.skills !== undefined ?
714
+ tuiOverride.skills :
715
+ (0, _skills.normalizeSkillInput)(seqStep.skill)
716
+ };
717
+ const behavior = (0, _settings.suppressProgressForReadOnlyTask)((0, _settings.resolveStepBehavior)(agentConfig, stepOverride, chainSkills), stepTemplate, originalTask);
718
+
719
+ const isFirstProgress = behavior.progress && !progressCreated;
720
+ if (isFirstProgress) {
721
+ progressCreated = true;
722
+ }
723
+
724
+ const templateHasPrevious = stepTemplate.includes("{previous}");
725
+ const { prefix, suffix } = (0, _settings.buildChainInstructions)(
726
+ behavior,
727
+ chainDir,
728
+ isFirstProgress,
729
+ templateHasPrevious ? undefined : prev
730
+ );
731
+
732
+ let stepTask = stepTemplate;
733
+ stepTask = stepTask.replace(/\{task\}/g, originalTask);
734
+ stepTask = stepTask.replace(/\{previous\}/g, prev);
735
+ stepTask = stepTask.replace(/\{chain_dir\}/g, chainDir);
736
+ const cleanTask = stepTask;
737
+ stepTask = prefix + stepTask + suffix;
738
+
739
+ const effectiveModel =
740
+ tuiOverride?.model ?? (
741
+ seqStep.model ? (0, _modelFallback.resolveModelCandidate)(seqStep.model, availableModels, ctx.model?.provider) : null) ??
742
+ (0, _modelFallback.resolveModelCandidate)(agentConfig.model, availableModels, ctx.model?.provider);
743
+
744
+ const outputPath = typeof behavior.output === "string" ?
745
+ path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output) :
746
+ undefined;
747
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(behavior.outputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
748
+ if (validationError) {
749
+ return buildChainExecutionErrorResult(validationError, {
750
+ results,
751
+ includeProgress,
752
+ allProgress,
753
+ allArtifactPaths,
754
+ artifactsDir,
755
+ chainAgents,
756
+ totalSteps,
757
+ currentStepIndex: stepIndex
758
+ });
759
+ }
760
+ const maxSubagentDepth = (0, _types.resolveChildMaxSubagentDepth)(params.maxSubagentDepth, agentConfig.maxSubagentDepth);
761
+ const interruptController = new AbortController();
762
+ if (foregroundControl) {
763
+ foregroundControl.currentAgent = seqStep.agent;
764
+ foregroundControl.currentIndex = globalTaskIndex;
765
+ foregroundControl.currentActivityState = undefined;
766
+ foregroundControl.updatedAt = Date.now();
767
+ foregroundControl.interrupt = () => {
768
+ if (interruptController.signal.aborted) return false;
769
+ interruptController.abort();
770
+ foregroundControl.currentActivityState = undefined;
771
+ foregroundControl.updatedAt = Date.now();
772
+ return true;
773
+ };
774
+ }
775
+
776
+ const r = await (0, _execution.runSync)(ctx.cwd, agents, seqStep.agent, stepTask, {
777
+ cwd: (0, _utils.resolveChildCwd)(cwd ?? ctx.cwd, seqStep.cwd),
778
+ signal,
779
+ interruptSignal: interruptController.signal,
780
+ allowIntercomDetach: agentConfig.systemPrompt?.includes(_intercomBridge.INTERCOM_BRIDGE_MARKER) === true,
781
+ intercomEvents,
782
+ runId,
783
+ index: globalTaskIndex,
784
+ sessionDir: sessionDirForIndex(globalTaskIndex),
785
+ sessionFile: sessionFileForIndex?.(globalTaskIndex),
786
+ share: shareEnabled,
787
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
788
+ artifactConfig,
789
+ outputPath,
790
+ outputMode: behavior.outputMode,
791
+ maxSubagentDepth,
792
+ controlConfig,
793
+ onControlEvent,
794
+ intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
795
+ orchestratorIntercomTarget,
796
+ modelOverride: effectiveModel,
797
+ availableModels,
798
+ preferredModelProvider: ctx.model?.provider,
799
+ skills: behavior.skills === false ? [] : behavior.skills,
800
+ onUpdate: onUpdate ?
801
+ (p) => {
802
+ const stepResults = p.details?.results || [];
803
+ const stepProgress = p.details?.progress || [];
804
+ if (foregroundControl && stepProgress.length > 0) {
805
+ const current = stepProgress[0];
806
+ foregroundControl.currentAgent = seqStep.agent;
807
+ foregroundControl.currentIndex = globalTaskIndex;
808
+ foregroundControl.currentActivityState = current?.activityState;
809
+ foregroundControl.lastActivityAt = current?.lastActivityAt;
810
+ foregroundControl.currentTool = current?.currentTool;
811
+ foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
812
+ foregroundControl.currentPath = current?.currentPath;
813
+ foregroundControl.turnCount = current?.turnCount;
814
+ foregroundControl.tokens = current?.tokens;
815
+ foregroundControl.toolCount = current?.toolCount;
816
+ foregroundControl.updatedAt = Date.now();
817
+ }
818
+ onUpdate({
819
+ ...p,
820
+ details: {
821
+ mode: "chain",
822
+ results: results.concat(stepResults),
823
+ progress: allProgress.concat(stepProgress),
824
+ controlEvents: p.details?.controlEvents,
825
+ chainAgents,
826
+ totalSteps,
827
+ currentStepIndex: stepIndex
828
+ }
829
+ });
830
+ } :
831
+ undefined
832
+ });
833
+ if (foregroundControl?.currentIndex === globalTaskIndex) {
834
+ foregroundControl.interrupt = undefined;
835
+ foregroundControl.updatedAt = Date.now();
836
+ }
837
+ (0, _runHistory.recordRun)(seqStep.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
838
+
839
+ globalTaskIndex++;
840
+ results.push(r);
841
+ if (r.progress) allProgress.push(r.progress);
842
+ if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
843
+
844
+ if (r.interrupted) {
845
+ return {
846
+ content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${r.agent}). Waiting for explicit next action.` }],
847
+ details: buildChainExecutionDetails({
848
+ results,
849
+ includeProgress,
850
+ allProgress,
851
+ allArtifactPaths,
852
+ artifactsDir,
853
+ chainAgents,
854
+ totalSteps,
855
+ currentStepIndex: stepIndex
856
+ })
857
+ };
858
+ }
859
+ if (r.detached) {
860
+ return {
861
+ content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${r.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
862
+ details: buildChainExecutionDetails({
863
+ results,
864
+ includeProgress,
865
+ allProgress,
866
+ allArtifactPaths,
867
+ artifactsDir,
868
+ chainAgents,
869
+ totalSteps,
870
+ currentStepIndex: stepIndex
871
+ })
872
+ };
873
+ }
874
+
875
+ if (r.exitCode !== 0) {
876
+ const summary = (0, _formatters.buildChainSummary)(chainSteps, results, chainDir, "failed", {
877
+ index: stepIndex,
878
+ error: r.error || "Chain failed"
879
+ });
880
+ return {
881
+ content: [{ type: "text", text: summary }],
882
+ details: buildChainExecutionDetails({
883
+ results,
884
+ includeProgress,
885
+ allProgress,
886
+ allArtifactPaths,
887
+ artifactsDir,
888
+ chainAgents,
889
+ totalSteps,
890
+ currentStepIndex: stepIndex
891
+ }),
892
+ isError: true
893
+ };
894
+ }
895
+
896
+ if (behavior.output) {
897
+ try {
898
+ const expectedPath = path.isAbsolute(behavior.output) ?
899
+ behavior.output :
900
+ path.join(chainDir, behavior.output);
901
+ if (!fs.existsSync(expectedPath)) {
902
+ const dirFiles = fs.readdirSync(chainDir);
903
+ const mdFiles = dirFiles.filter((file) => file.endsWith(".md") && file !== "progress.md");
904
+ const warning = mdFiles.length > 0 ?
905
+ `Agent wrote to different file(s): ${mdFiles.join(", ")} instead of ${behavior.output}` :
906
+ `Agent did not create expected output file: ${behavior.output}`;
907
+ r.error = r.error ? `${r.error}\n${warning}` : warning;
908
+ }
909
+ } catch {
910
+
911
+ // Ignore validation errors; this diagnostic should not mask successful chain output.
912
+ }}
913
+
914
+ prev = (0, _utils.getSingleResultOutput)(r);
915
+ }
916
+ }
917
+
918
+ const summary = (0, _formatters.buildChainSummary)(chainSteps, results, chainDir, "completed");
919
+
920
+ return {
921
+ content: [{ type: "text", text: summary }],
922
+ details: buildChainExecutionDetails({
923
+ results,
924
+ includeProgress,
925
+ allProgress,
926
+ allArtifactPaths,
927
+ artifactsDir,
928
+ chainAgents,
929
+ totalSteps
930
+ })
931
+ };
932
+ } /* v9-cf844499abcaf24e */
pip-tmp/jiti/foreground-execution.7bdac23f.mjs ADDED
@@ -0,0 +1,902 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.runSync = runSync;
2
+
3
+
4
+
5
+ var _nodeChild_process = await jitiImport("node:child_process");
6
+ var _nodeFs = await jitiImport("node:fs");
7
+
8
+
9
+ var _artifacts = await jitiImport("../../shared/artifacts.ts");
10
+
11
+
12
+
13
+
14
+
15
+ var _types = await jitiImport("../../shared/types.ts");
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+ var _subagentControl = await jitiImport("../shared/subagent-control.ts");
30
+
31
+
32
+
33
+
34
+
35
+
36
+ var _utils = await jitiImport("../../shared/utils.ts");
37
+
38
+
39
+
40
+
41
+
42
+
43
+ var _skills = await jitiImport("../../agents/skills.ts");
44
+ var _completionGuard = await jitiImport("../shared/completion-guard.ts");
45
+ var _piSpawn = await jitiImport("../shared/pi-spawn.ts");
46
+ var _jsonlWriter = await jitiImport("../../shared/jsonl-writer.ts");
47
+ var _postExitStdioGuard = await jitiImport("../../shared/post-exit-stdio-guard.ts");
48
+ var _piArgs = await jitiImport("../shared/pi-args.ts");
49
+ var _singleOutput = await jitiImport("../shared/single-output.ts");
50
+ var _modelFallback = await jitiImport("../shared/model-fallback.ts");
51
+
52
+
53
+
54
+
55
+ var _longRunningGuard = await jitiImport("../shared/long-running-guard.ts"); /**
56
+ * Core execution logic for running subagents
57
+ */
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+ const artifactOutputByResult = new WeakMap();
68
+
69
+ function emptyUsage() {
70
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
71
+ }
72
+
73
+ function sumUsage(target, source) {
74
+ target.input += source.input;
75
+ target.output += source.output;
76
+ target.cacheRead += source.cacheRead;
77
+ target.cacheWrite += source.cacheWrite;
78
+ target.cost += source.cost;
79
+ target.turns += source.turns;
80
+ }
81
+
82
+ function appendRecentOutput(progress, lines) {
83
+ if (lines.length === 0) return;
84
+ progress.recentOutput.push(...lines.filter((line) => line.trim()));
85
+ if (progress.recentOutput.length > 50) {
86
+ progress.recentOutput.splice(0, progress.recentOutput.length - 50);
87
+ }
88
+ }
89
+
90
+ function snapshotProgress(progress) {
91
+ return {
92
+ ...progress,
93
+ skills: progress.skills ? [...progress.skills] : undefined,
94
+ recentTools: progress.recentTools.map((tool) => ({ ...tool })),
95
+ recentOutput: [...progress.recentOutput]
96
+ };
97
+ }
98
+
99
+ function snapshotResult(result, progress) {
100
+ return {
101
+ ...result,
102
+ messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
103
+ usage: { ...result.usage },
104
+ skills: result.skills ? [...result.skills] : undefined,
105
+ attemptedModels: result.attemptedModels ? [...result.attemptedModels] : undefined,
106
+ modelAttempts: result.modelAttempts ?
107
+ result.modelAttempts.map((attempt) => ({
108
+ ...attempt,
109
+ usage: attempt.usage ? { ...attempt.usage } : undefined
110
+ })) :
111
+ undefined,
112
+ controlEvents: result.controlEvents ? result.controlEvents.map((event) => ({ ...event })) : undefined,
113
+ progress,
114
+ progressSummary: result.progressSummary ? { ...result.progressSummary } : undefined,
115
+ artifactPaths: result.artifactPaths ? { ...result.artifactPaths } : undefined,
116
+ truncation: result.truncation ? { ...result.truncation } : undefined,
117
+ outputReference: result.outputReference ? { ...result.outputReference } : undefined
118
+ };
119
+ }
120
+
121
+ async function runSingleAttempt(
122
+ runtimeCwd,
123
+ agent,
124
+ task,
125
+ model,
126
+ options,
127
+ shared)
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+ {
138
+ const modelArg = (0, _piArgs.applyThinkingSuffix)(model, agent.thinking);
139
+ const { args, env: sharedEnv, tempDir } = (0, _piArgs.buildPiArgs)({
140
+ baseArgs: ["--mode", "json", "-p"],
141
+ task,
142
+ sessionEnabled: shared.sessionEnabled,
143
+ sessionDir: options.sessionDir,
144
+ sessionFile: options.sessionFile,
145
+ model,
146
+ thinking: agent.thinking,
147
+ systemPromptMode: agent.systemPromptMode,
148
+ inheritProjectContext: agent.inheritProjectContext,
149
+ inheritSkills: agent.inheritSkills,
150
+ tools: agent.tools,
151
+ extensions: agent.extensions,
152
+ systemPrompt: shared.systemPrompt,
153
+ mcpDirectTools: agent.mcpDirectTools,
154
+ promptFileStem: agent.name,
155
+ intercomSessionName: options.intercomSessionName,
156
+ orchestratorIntercomTarget: options.orchestratorIntercomTarget,
157
+ runId: options.runId,
158
+ childAgentName: agent.name,
159
+ childIndex: options.index ?? 0
160
+ });
161
+
162
+ const result = {
163
+ agent: agent.name,
164
+ task,
165
+ exitCode: 0,
166
+ messages: [],
167
+ usage: emptyUsage(),
168
+ model: modelArg,
169
+ artifactPaths: shared.artifactPaths,
170
+ skills: shared.resolvedSkillNames,
171
+ skillsWarning: shared.skillsWarning
172
+ };
173
+ const startTime = Date.now();
174
+ const controlConfig = options.controlConfig ?? _subagentControl.DEFAULT_CONTROL_CONFIG;
175
+ let interruptedByControl = false;
176
+ const allControlEvents = [];
177
+ let pendingControlEvents = [];
178
+ const emittedControlEventKeys = new Set();
179
+ const emitControlEvent = (event) => {
180
+ if (!(0, _subagentControl.shouldNotifyControlEvent)(controlConfig, event)) return;
181
+ if (!(0, _subagentControl.claimControlNotification)(controlConfig, event, emittedControlEventKeys)) return;
182
+ allControlEvents.push(event);
183
+ pendingControlEvents.push(event);
184
+ options.onControlEvent?.(event);
185
+ };
186
+
187
+ const progress = {
188
+ index: options.index ?? 0,
189
+ agent: agent.name,
190
+ status: "running",
191
+ task,
192
+ skills: shared.resolvedSkillNames,
193
+ recentTools: [],
194
+ recentOutput: [...shared.attemptNotes],
195
+ toolCount: 0,
196
+ tokens: 0,
197
+ durationMs: 0,
198
+ lastActivityAt: startTime
199
+ };
200
+ result.progress = progress;
201
+ const spawnEnv = { ...process.env, ...sharedEnv, ...(0, _types.getSubagentDepthEnv)(options.maxSubagentDepth) };
202
+ let observedMutationAttempt = false;
203
+
204
+ const exitCode = await new Promise((resolve) => {
205
+ const spawnSpec = (0, _piSpawn.getPiSpawnCommand)(args);
206
+ const proc = (0, _nodeChild_process.spawn)(spawnSpec.command, spawnSpec.args, {
207
+ cwd: options.cwd ?? runtimeCwd,
208
+ env: spawnEnv,
209
+ stdio: ["ignore", "pipe", "pipe"]
210
+ });
211
+ const jsonlWriter = (0, _jsonlWriter.createJsonlWriter)(shared.jsonlPath, proc.stdout);
212
+ let buf = "";
213
+ let processClosed = false;
214
+ let settled = false;
215
+ let detached = false;
216
+ let intercomStarted = false;
217
+ let removeAbortListener;
218
+ let removeInterruptListener;
219
+ let activityTimer;
220
+
221
+ const detachForIntercom = () => {
222
+ detached = true;
223
+ processClosed = true;
224
+ result.detached = true;
225
+ result.detachedReason = "intercom coordination";
226
+ progress.status = "detached";
227
+ progress.durationMs = Date.now() - startTime;
228
+ result.progressSummary = {
229
+ toolCount: progress.toolCount,
230
+ tokens: progress.tokens,
231
+ durationMs: progress.durationMs
232
+ };
233
+ finish(-2);
234
+ };
235
+
236
+ // If the child emits a terminal assistant stop but never exits,
237
+ // give it a short grace period to flush naturally, then clean it up.
238
+ const FINAL_STOP_GRACE_MS = 1000;
239
+ const HARD_KILL_MS = 3000;
240
+ let childExited = false;
241
+ let forcedTerminationSignal = false;
242
+ let cleanTerminalAssistantStopReceived = false;
243
+ let finalDrainTimer;
244
+ let finalHardKillTimer;
245
+ const clearFinalDrainTimers = () => {
246
+ if (finalDrainTimer) {
247
+ clearTimeout(finalDrainTimer);
248
+ finalDrainTimer = undefined;
249
+ }
250
+ if (finalHardKillTimer) {
251
+ clearTimeout(finalHardKillTimer);
252
+ finalHardKillTimer = undefined;
253
+ }
254
+ };
255
+ const startFinalDrain = () => {
256
+ if (childExited || finalDrainTimer || settled || processClosed || detached) return;
257
+ finalDrainTimer = setTimeout(() => {
258
+ if (settled || processClosed || detached) return;
259
+ const termSent = (0, _postExitStdioGuard.trySignalChild)(proc, "SIGTERM");
260
+ if (!termSent) return;
261
+ forcedTerminationSignal = true;
262
+ if (!cleanTerminalAssistantStopReceived) {
263
+ result.error = result.error ?? `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its final message. Forcing termination.`;
264
+ }
265
+ finalHardKillTimer = setTimeout(() => {
266
+ if (settled || processClosed || detached) return;
267
+ forcedTerminationSignal = (0, _postExitStdioGuard.trySignalChild)(proc, "SIGKILL") || forcedTerminationSignal;
268
+ }, HARD_KILL_MS);
269
+ finalHardKillTimer.unref?.();
270
+ }, FINAL_STOP_GRACE_MS);
271
+ finalDrainTimer.unref?.();
272
+ };
273
+
274
+ const unsubscribeIntercomDetach = options.intercomEvents?.on?.(_types.INTERCOM_DETACH_REQUEST_EVENT, (payload) => {
275
+ if (!options.allowIntercomDetach || detached || processClosed || !intercomStarted) return;
276
+ if (!payload || typeof payload !== "object") return;
277
+ const requestId = payload.requestId;
278
+ if (typeof requestId !== "string" || requestId.length === 0) return;
279
+ options.intercomEvents?.emit(_types.INTERCOM_DETACH_RESPONSE_EVENT, { requestId, accepted: true });
280
+ detachForIntercom();
281
+ });
282
+
283
+ const finish = (code) => {
284
+ if (settled) return;
285
+ settled = true;
286
+ clearFinalDrainTimers();
287
+ clearStdioGuard();
288
+ if (activityTimer) {
289
+ clearInterval(activityTimer);
290
+ activityTimer = undefined;
291
+ }
292
+ unsubscribeIntercomDetach?.();
293
+ removeAbortListener?.();
294
+ removeInterruptListener?.();
295
+ resolve(code);
296
+ };
297
+
298
+ const drainPendingControlEvents = () => {
299
+ if (pendingControlEvents.length === 0) return undefined;
300
+ const events = pendingControlEvents;
301
+ pendingControlEvents = [];
302
+ return events;
303
+ };
304
+
305
+ let activeLongRunningNotified = false;
306
+ let pendingToolResult;
307
+ const mutatingFailures = (0, _longRunningGuard.createMutatingFailureState)();
308
+ const mutatingFailureWindowMs = 5 * 60_000;
309
+ const currentToolDurationMs = (now) => progress.currentToolStartedAt ? Math.max(0, now - progress.currentToolStartedAt) : undefined;
310
+ const emitNeedsAttention = (now, input = {}) => {
311
+ if (!controlConfig.enabled) return false;
312
+ const previous = progress.activityState;
313
+ progress.activityState = "needs_attention";
314
+ const event = (0, _subagentControl.buildControlEvent)({
315
+ type: "needs_attention",
316
+ from: previous,
317
+ to: "needs_attention",
318
+ runId: options.runId,
319
+ agent: agent.name,
320
+ index: options.index,
321
+ ts: now,
322
+ lastActivityAt: progress.lastActivityAt,
323
+ message: input.message,
324
+ reason: input.reason ?? "idle",
325
+ turns: result.usage.turns,
326
+ tokens: progress.tokens,
327
+ toolCount: progress.toolCount,
328
+ currentTool: input.currentTool ?? progress.currentTool,
329
+ currentToolDurationMs: input.currentToolDurationMs ?? currentToolDurationMs(now),
330
+ currentPath: input.currentPath ?? progress.currentPath,
331
+ recentFailureSummary: input.recentFailureSummary
332
+ });
333
+ emitControlEvent(event);
334
+ return previous !== "needs_attention";
335
+ };
336
+ const emitActiveLongRunning = (now, reason) => {
337
+ if (!controlConfig.enabled || activeLongRunningNotified || progress.activityState === "needs_attention") return false;
338
+ activeLongRunningNotified = true;
339
+ const previous = progress.activityState;
340
+ progress.activityState = "active_long_running";
341
+ emitControlEvent((0, _subagentControl.buildControlEvent)({
342
+ type: "active_long_running",
343
+ from: previous,
344
+ to: "active_long_running",
345
+ runId: options.runId,
346
+ agent: agent.name,
347
+ index: options.index,
348
+ ts: now,
349
+ message: `${agent.name} is still active but long-running`,
350
+ reason,
351
+ turns: result.usage.turns,
352
+ tokens: progress.tokens,
353
+ toolCount: progress.toolCount,
354
+ currentTool: progress.currentTool,
355
+ currentToolDurationMs: currentToolDurationMs(now),
356
+ currentPath: progress.currentPath,
357
+ elapsedMs: now - startTime
358
+ }));
359
+ return true;
360
+ };
361
+ const updateActivityState = (now) => {
362
+ if (!controlConfig.enabled) return false;
363
+ const idleState = (0, _subagentControl.deriveActivityState)({
364
+ config: controlConfig,
365
+ startedAt: startTime,
366
+ lastActivityAt: progress.lastActivityAt,
367
+ now
368
+ });
369
+ if (idleState === "needs_attention") {
370
+ return progress.activityState === "needs_attention" ? false : emitNeedsAttention(now);
371
+ }
372
+ const activeReason = (0, _longRunningGuard.nextLongRunningTrigger)(controlConfig, {
373
+ startedAt: startTime,
374
+ now,
375
+ turns: result.usage.turns,
376
+ tokens: progress.tokens
377
+ });
378
+ return activeReason ? emitActiveLongRunning(now, activeReason) : false;
379
+ };
380
+
381
+
382
+ const emitUpdateSnapshot = (text) => {
383
+ if (!options.onUpdate || processClosed) return;
384
+ const progressSnapshot = snapshotProgress(progress);
385
+ const resultSnapshot = snapshotResult(result, progressSnapshot);
386
+ const controlEvents = drainPendingControlEvents();
387
+ options.onUpdate({
388
+ content: [{ type: "text", text }],
389
+ details: {
390
+ mode: "single",
391
+ results: [resultSnapshot],
392
+ progress: [progressSnapshot],
393
+ controlEvents
394
+ }
395
+ });
396
+ };
397
+
398
+ const fireUpdate = () => {
399
+ if (!options.onUpdate || processClosed) return;
400
+ progress.durationMs = Date.now() - startTime;
401
+ emitUpdateSnapshot((0, _utils.getFinalOutput)(result.messages) || "(running...)");
402
+ };
403
+
404
+ const processLine = (line) => {
405
+ if (!line.trim()) return;
406
+ jsonlWriter.writeLine(line);
407
+ let evt;
408
+ try {
409
+ evt = JSON.parse(line);
410
+ } catch {
411
+ // Non-JSON stdout lines are expected; only structured events are parsed.
412
+ return;
413
+ }
414
+
415
+ const now = Date.now();
416
+ progress.durationMs = now - startTime;
417
+ progress.lastActivityAt = now;
418
+ updateActivityState(now);
419
+
420
+ if (evt.type === "tool_execution_start") {
421
+ const toolArgs = evt.args && typeof evt.args === "object" && !Array.isArray(evt.args) ?
422
+ evt.args :
423
+ {};
424
+ if (options.allowIntercomDetach && (evt.toolName === "intercom" || evt.toolName === "contact_supervisor")) {
425
+ intercomStarted = true;
426
+ }
427
+ progress.toolCount++;
428
+ progress.currentTool = evt.toolName;
429
+ progress.currentToolArgs = (0, _utils.extractToolArgsPreview)(toolArgs);
430
+ progress.currentToolStartedAt = now;
431
+ progress.currentPath = (0, _longRunningGuard.resolveCurrentPath)(evt.toolName, toolArgs);
432
+ const mutates = (0, _longRunningGuard.isMutatingTool)(evt.toolName, toolArgs);
433
+ observedMutationAttempt = observedMutationAttempt || mutates;
434
+ pendingToolResult = { tool: evt.toolName ?? "tool", path: progress.currentPath, mutates, startedAt: now };
435
+ fireUpdate();
436
+ }
437
+
438
+ if (evt.type === "tool_execution_end") {
439
+ if (progress.currentTool) {
440
+ progress.recentTools.push({
441
+ tool: progress.currentTool,
442
+ args: progress.currentToolArgs || "",
443
+ endMs: now
444
+ });
445
+ }
446
+ progress.currentTool = undefined;
447
+ progress.currentToolArgs = undefined;
448
+ progress.currentToolStartedAt = undefined;
449
+ progress.currentPath = undefined;
450
+ fireUpdate();
451
+ }
452
+
453
+ if (evt.type === "message_end" && evt.message) {
454
+ result.messages.push(evt.message);
455
+ if (evt.message.role === "assistant") {
456
+ result.usage.turns++;
457
+ progress.turnCount = result.usage.turns;
458
+ const u = evt.message.usage;
459
+ if (u) {
460
+ result.usage.input += u.input || 0;
461
+ result.usage.output += u.output || 0;
462
+ result.usage.cacheRead += u.cacheRead || 0;
463
+ result.usage.cacheWrite += u.cacheWrite || 0;
464
+ result.usage.cost += u.cost?.total || 0;
465
+ progress.tokens = result.usage.input + result.usage.output;
466
+ }
467
+ if (!result.model && evt.message.model) result.model = evt.message.model;
468
+ if (evt.message.errorMessage) result.error = evt.message.errorMessage;
469
+ appendRecentOutput(progress, (0, _utils.extractTextFromContent)(evt.message.content).split("\n").slice(-10));
470
+ // Final assistant message: start the exit drain window.
471
+ const stopReason = evt.message.stopReason;
472
+ const hasToolCall = Array.isArray(evt.message.content) &&
473
+ evt.message.content.some((part) => part.type === "toolCall");
474
+ if (stopReason === "stop" && !hasToolCall) {
475
+ cleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;
476
+ startFinalDrain();
477
+ }
478
+ }
479
+ updateActivityState(now);
480
+ fireUpdate();
481
+ }
482
+
483
+ if (evt.type === "tool_result_end" && evt.message) {
484
+ result.messages.push(evt.message);
485
+ const resultText = (0, _utils.extractTextFromContent)(evt.message.content);
486
+ appendRecentOutput(progress, resultText.split("\n").slice(-10));
487
+ const toolSnapshot = pendingToolResult;
488
+ pendingToolResult = undefined;
489
+ if (toolSnapshot?.mutates && (0, _longRunningGuard.didMutatingToolFail)(resultText)) {
490
+ (0, _longRunningGuard.recordMutatingFailure)(mutatingFailures, {
491
+ tool: toolSnapshot.tool,
492
+ path: toolSnapshot.path,
493
+ error: resultText.split("\n").find((line) => line.trim())?.trim().slice(0, 180) ?? "mutating tool failed",
494
+ ts: now
495
+ }, mutatingFailureWindowMs);
496
+ if ((0, _longRunningGuard.shouldEscalateMutatingFailures)(mutatingFailures, controlConfig.failedToolAttemptsBeforeAttention)) {
497
+ emitNeedsAttention(now, {
498
+ message: `${agent.name} needs attention after repeated mutating tool failures`,
499
+ reason: "tool_failures",
500
+ currentTool: toolSnapshot.tool,
501
+ currentPath: toolSnapshot.path,
502
+ currentToolDurationMs: toolSnapshot.startedAt ? Math.max(0, now - toolSnapshot.startedAt) : undefined,
503
+ recentFailureSummary: (0, _longRunningGuard.summarizeRecentMutatingFailures)(mutatingFailures)
504
+ });
505
+ }
506
+ } else if (toolSnapshot?.mutates) {
507
+ (0, _longRunningGuard.resetMutatingFailureState)(mutatingFailures);
508
+ }
509
+ fireUpdate();
510
+ }
511
+ };
512
+
513
+ if (controlConfig.enabled) {
514
+ activityTimer = setInterval(() => {
515
+ if (processClosed || settled || detached) return;
516
+ const now = Date.now();
517
+ if (updateActivityState(now)) {
518
+ progress.durationMs = now - startTime;
519
+ fireUpdate();
520
+ }
521
+ }, 1000);
522
+ activityTimer.unref?.();
523
+ }
524
+
525
+ let stderrBuf = "";
526
+
527
+ const clearStdioGuard = (0, _postExitStdioGuard.attachPostExitStdioGuard)(proc, { idleMs: 2000, hardMs: 8000 });
528
+ proc.stdout.on("data", (d) => {
529
+ buf += d.toString();
530
+ const lines = buf.split("\n");
531
+ buf = lines.pop() || "";
532
+ lines.forEach(processLine);
533
+ });
534
+ proc.stderr.on("data", (d) => {
535
+ stderrBuf += d.toString();
536
+ });
537
+ proc.on("exit", () => {
538
+ childExited = true;
539
+ clearFinalDrainTimers();
540
+ });
541
+ proc.on("close", (code, signal) => {
542
+ clearFinalDrainTimers();
543
+ clearStdioGuard();
544
+ void jsonlWriter.close().catch(() => {
545
+
546
+ // JSONL artifact flush is best effort.
547
+ });(0, _piArgs.cleanupTempDir)(tempDir);
548
+ if (detached) {
549
+ finish(-2);
550
+ return;
551
+ }
552
+ processClosed = true;
553
+ if (buf.trim()) processLine(buf);
554
+ const forcedDrainAfterFinalSuccess = forcedTerminationSignal && cleanTerminalAssistantStopReceived && !result.error;
555
+ if (code !== 0 && stderrBuf.trim() && !result.error && !forcedDrainAfterFinalSuccess) {
556
+ result.error = stderrBuf.trim();
557
+ }
558
+ const finalCode = forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? code ?? 1 : code ?? 0;
559
+ finish(finalCode);
560
+ });
561
+ proc.on("error", (error) => {
562
+ clearFinalDrainTimers();
563
+ clearStdioGuard();
564
+ void jsonlWriter.close().catch(() => {
565
+
566
+ // JSONL artifact flush is best effort.
567
+ });(0, _piArgs.cleanupTempDir)(tempDir);
568
+ if (!result.error) {
569
+ result.error = error instanceof Error ? error.message : String(error);
570
+ }
571
+ finish(1);
572
+ });
573
+
574
+ if (options.signal) {
575
+ const kill = () => {
576
+ if (processClosed || detached) return;
577
+ if (options.allowIntercomDetach && intercomStarted && !detached) {
578
+ detachForIntercom();
579
+ return;
580
+ }
581
+ proc.kill("SIGTERM");
582
+ setTimeout(() => !proc.killed && proc.kill("SIGKILL"), 3000);
583
+ };
584
+ if (options.signal.aborted) kill();else
585
+ {
586
+ options.signal.addEventListener("abort", kill, { once: true });
587
+ removeAbortListener = () => options.signal?.removeEventListener("abort", kill);
588
+ }
589
+ }
590
+
591
+ if (options.interruptSignal) {
592
+ const interrupt = () => {
593
+ if (processClosed || detached || settled) return;
594
+ interruptedByControl = true;
595
+ progress.status = "running";
596
+ progress.durationMs = Date.now() - startTime;
597
+ result.interrupted = true;
598
+ result.finalOutput = "Interrupted. Waiting for explicit next action.";
599
+ progress.activityState = undefined;
600
+ fireUpdate();
601
+ (0, _postExitStdioGuard.trySignalChild)(proc, "SIGINT");
602
+ setTimeout(() => {
603
+ if (settled || processClosed || detached) return;
604
+ (0, _postExitStdioGuard.trySignalChild)(proc, "SIGTERM");
605
+ }, 1000).unref?.();
606
+ };
607
+ if (options.interruptSignal.aborted) interrupt();else
608
+ {
609
+ options.interruptSignal.addEventListener("abort", interrupt, { once: true });
610
+ removeInterruptListener = () => options.interruptSignal?.removeEventListener("abort", interrupt);
611
+ }
612
+ }
613
+ });
614
+ result.exitCode = exitCode;
615
+ if (interruptedByControl) {
616
+ result.exitCode = 0;
617
+ result.interrupted = true;
618
+ result.error = undefined;
619
+ result.finalOutput = result.finalOutput || "Interrupted. Waiting for explicit next action.";
620
+ result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
621
+ progress.activityState = undefined;
622
+ progress.durationMs = Date.now() - startTime;
623
+ result.progressSummary = {
624
+ toolCount: progress.toolCount,
625
+ tokens: progress.tokens,
626
+ durationMs: progress.durationMs
627
+ };
628
+ return result;
629
+ }
630
+ if (result.detached) {
631
+ result.exitCode = 0;
632
+ result.finalOutput = "Detached for intercom coordination.";
633
+ return result;
634
+ }
635
+
636
+ if (result.error && result.exitCode === 0) {
637
+ result.exitCode = 1;
638
+ }
639
+ if (result.exitCode === 0 && !result.error) {
640
+ const errInfo = (0, _utils.detectSubagentError)(result.messages);
641
+ if (errInfo.hasError) {
642
+ result.exitCode = errInfo.exitCode ?? 1;
643
+ result.error = errInfo.details ?
644
+ `${errInfo.errorType} failed (exit ${errInfo.exitCode}): ${errInfo.details}` :
645
+ `${errInfo.errorType} failed with exit code ${errInfo.exitCode}`;
646
+ }
647
+ }
648
+
649
+ progress.status = result.exitCode === 0 ? "completed" : "failed";
650
+ progress.durationMs = Date.now() - startTime;
651
+ if (result.error) {
652
+ progress.error = result.error;
653
+ if (progress.currentTool) {
654
+ progress.failedTool = progress.currentTool;
655
+ }
656
+ }
657
+
658
+ result.progressSummary = {
659
+ toolCount: progress.toolCount,
660
+ tokens: progress.tokens,
661
+ durationMs: progress.durationMs
662
+ };
663
+
664
+ let fullOutput = (0, _utils.getFinalOutput)(result.messages);
665
+ const completionGuard = result.exitCode === 0 && !result.error ?
666
+ (0, _completionGuard.evaluateCompletionMutationGuard)({ agent: agent.name, task, messages: result.messages }) :
667
+ undefined;
668
+ if (completionGuard?.triggered && !observedMutationAttempt) {
669
+ result.exitCode = 1;
670
+ result.error = "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes.";
671
+ progress.status = "failed";
672
+ progress.error = result.error;
673
+ emitControlEvent((0, _subagentControl.buildControlEvent)({
674
+ from: progress.activityState,
675
+ to: "needs_attention",
676
+ runId: options.runId ?? agent.name,
677
+ agent: agent.name,
678
+ index: options.index,
679
+ ts: Date.now(),
680
+ message: `${agent.name} completed without making edits for an implementation task`,
681
+ reason: "completion_guard"
682
+ }));
683
+ }
684
+ if (options.outputPath && result.exitCode === 0) {
685
+ const resolvedOutput = (0, _singleOutput.resolveSingleOutput)(options.outputPath, fullOutput, shared.outputSnapshot);
686
+ fullOutput = resolvedOutput.fullOutput;
687
+ result.savedOutputPath = resolvedOutput.savedPath;
688
+ result.outputSaveError = resolvedOutput.saveError;
689
+ if (resolvedOutput.savedPath) {
690
+ result.outputReference = (0, _singleOutput.formatSavedOutputReference)(resolvedOutput.savedPath, fullOutput);
691
+ }
692
+ }
693
+ artifactOutputByResult.set(result, fullOutput);
694
+ result.outputMode = options.outputMode ?? "inline";
695
+ result.finalOutput = options.outputMode === "file-only" && result.savedOutputPath && result.outputReference ?
696
+ result.outputReference.message :
697
+ fullOutput;
698
+ result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
699
+ if (options.onUpdate) {
700
+ const finalText = result.finalOutput || result.error || "(no output)";
701
+ const progressSnapshot = snapshotProgress(progress);
702
+ const resultSnapshot = snapshotResult(result, progressSnapshot);
703
+ options.onUpdate({
704
+ content: [{ type: "text", text: finalText }],
705
+ details: {
706
+ mode: "single",
707
+ results: [resultSnapshot],
708
+ progress: [progressSnapshot],
709
+ controlEvents: allControlEvents.length ? allControlEvents : undefined
710
+ }
711
+ });
712
+ }
713
+ return result;
714
+ }
715
+
716
+ /**
717
+ * Run a subagent synchronously (blocking until complete)
718
+ */
719
+ async function runSync(
720
+ runtimeCwd,
721
+ agents,
722
+ agentName,
723
+ task,
724
+ options)
725
+ {
726
+ const agent = agents.find((a) => a.name === agentName);
727
+ if (!agent) {
728
+ return {
729
+ agent: agentName,
730
+ task,
731
+ exitCode: 1,
732
+ messages: [],
733
+ usage: emptyUsage(),
734
+ error: `Unknown agent: ${agentName}`
735
+ };
736
+ }
737
+ const outputModeValidationError = (0, _singleOutput.validateFileOnlyOutputMode)(options.outputMode, options.outputPath, `Single run (${agentName})`);
738
+ if (outputModeValidationError) {
739
+ return {
740
+ agent: agentName,
741
+ task,
742
+ exitCode: 1,
743
+ messages: [],
744
+ usage: emptyUsage(),
745
+ outputMode: options.outputMode,
746
+ error: outputModeValidationError
747
+ };
748
+ }
749
+
750
+ const shareEnabled = options.share === true;
751
+ const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
752
+ const skillNames = options.skills ?? agent.skills ?? [];
753
+ const skillCwd = options.cwd ?? runtimeCwd;
754
+ const { resolved: resolvedSkills, missing: missingSkills } = (0, _skills.resolveSkillsWithFallback)(skillNames, skillCwd, runtimeCwd);
755
+ if (skillNames.some((skill) => skill.trim() === "pi-subagents") && missingSkills.includes("pi-subagents")) {
756
+ return {
757
+ agent: agentName,
758
+ task,
759
+ exitCode: 1,
760
+ messages: [],
761
+ usage: emptyUsage(),
762
+ error: "Skills not found: pi-subagents"
763
+ };
764
+ }
765
+ let systemPrompt = agent.systemPrompt?.trim() || "";
766
+ if (resolvedSkills.length > 0) {
767
+ const skillInjection = (0, _skills.buildSkillInjection)(resolvedSkills);
768
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillInjection}` : skillInjection;
769
+ }
770
+
771
+ const candidates = (0, _modelFallback.buildModelCandidates)(
772
+ options.modelOverride ?? agent.model,
773
+ agent.fallbackModels,
774
+ options.availableModels,
775
+ options.preferredModelProvider
776
+ );
777
+ const attemptedModels = [];
778
+ const modelAttempts = [];
779
+ const aggregateUsage = emptyUsage();
780
+ const attemptNotes = [];
781
+ let totalToolCount = 0;
782
+ let totalDurationMs = 0;
783
+
784
+ let artifactPathsResult;
785
+ let jsonlPath;
786
+ if (options.artifactsDir && options.artifactConfig?.enabled !== false) {
787
+ artifactPathsResult = (0, _artifacts.getArtifactPaths)(options.artifactsDir, options.runId, agentName, options.index);
788
+ (0, _artifacts.ensureArtifactsDir)(options.artifactsDir);
789
+ if (options.artifactConfig?.includeInput !== false) {
790
+ (0, _artifacts.writeArtifact)(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${task}`);
791
+ }
792
+ if (options.artifactConfig?.includeJsonl !== false) {
793
+ jsonlPath = artifactPathsResult.jsonlPath;
794
+ }
795
+ }
796
+
797
+ let lastResult;
798
+ const modelsToTry = candidates.length > 0 ? candidates : [undefined];
799
+ for (let i = 0; i < modelsToTry.length; i++) {
800
+ const candidate = modelsToTry[i];
801
+ if (candidate) attemptedModels.push(candidate);
802
+ const outputSnapshot = (0, _singleOutput.captureSingleOutputSnapshot)(options.outputPath);
803
+ const result = await runSingleAttempt(runtimeCwd, agent, task, candidate, options, {
804
+ sessionEnabled,
805
+ systemPrompt,
806
+ resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
807
+ skillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(", ")}` : undefined,
808
+ jsonlPath,
809
+ artifactPaths: artifactPathsResult,
810
+ attemptNotes,
811
+ outputSnapshot
812
+ });
813
+ lastResult = result;
814
+ sumUsage(aggregateUsage, result.usage);
815
+ totalToolCount += result.progressSummary?.toolCount ?? 0;
816
+ totalDurationMs += result.progressSummary?.durationMs ?? 0;
817
+ const attemptSucceeded = result.exitCode === 0 && !result.error;
818
+ const attempt = {
819
+ model: candidate ?? result.model ?? agent.model ?? "default",
820
+ success: attemptSucceeded,
821
+ exitCode: result.exitCode,
822
+ error: result.error,
823
+ usage: { ...result.usage }
824
+ };
825
+ modelAttempts.push(attempt);
826
+ if (attemptSucceeded) {
827
+ break;
828
+ }
829
+ if (!(0, _modelFallback.isRetryableModelFailure)(result.error) || i === modelsToTry.length - 1) {
830
+ break;
831
+ }
832
+ attemptNotes.push((0, _modelFallback.formatModelAttemptNote)(attempt, modelsToTry[i + 1]));
833
+ }
834
+
835
+ const result = lastResult ?? {
836
+ agent: agentName,
837
+ task,
838
+ exitCode: 1,
839
+ messages: [],
840
+ usage: emptyUsage(),
841
+ error: "Subagent did not produce a result."
842
+ };
843
+
844
+ result.usage = aggregateUsage;
845
+ result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
846
+ result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
847
+ result.progressSummary = {
848
+ toolCount: totalToolCount,
849
+ tokens: aggregateUsage.input + aggregateUsage.output,
850
+ durationMs: totalDurationMs
851
+ };
852
+ if (attemptNotes.length > 0 && result.progress) {
853
+ result.progress.recentOutput = [...attemptNotes, ...result.progress.recentOutput];
854
+ if (result.progress.recentOutput.length > 50) {
855
+ result.progress.recentOutput.splice(50);
856
+ }
857
+ }
858
+
859
+ if (artifactPathsResult && options.artifactConfig?.enabled !== false) {
860
+ result.artifactPaths = artifactPathsResult;
861
+ if (options.artifactConfig?.includeOutput !== false) {
862
+ (0, _artifacts.writeArtifact)(artifactPathsResult.outputPath, artifactOutputByResult.get(result) ?? result.finalOutput ?? "");
863
+ }
864
+ if (options.artifactConfig?.includeMetadata !== false) {
865
+ (0, _artifacts.writeMetadata)(artifactPathsResult.metadataPath, {
866
+ runId: options.runId,
867
+ agent: agentName,
868
+ task,
869
+ exitCode: result.exitCode,
870
+ usage: result.usage,
871
+ model: result.model,
872
+ attemptedModels: result.attemptedModels,
873
+ modelAttempts: result.modelAttempts,
874
+ durationMs: result.progressSummary?.durationMs,
875
+ toolCount: result.progressSummary?.toolCount,
876
+ error: result.error,
877
+ skills: result.skills,
878
+ skillsWarning: result.skillsWarning,
879
+ timestamp: Date.now()
880
+ });
881
+ }
882
+
883
+ if (options.maxOutput) {
884
+ const config = { ..._types.DEFAULT_MAX_OUTPUT, ...options.maxOutput };
885
+ const truncationResult = (0, _types.truncateOutput)(result.finalOutput ?? "", config, artifactPathsResult.outputPath);
886
+ if (truncationResult.truncated) result.truncation = truncationResult;
887
+ }
888
+ } else if (options.maxOutput) {
889
+ const config = { ..._types.DEFAULT_MAX_OUTPUT, ...options.maxOutput };
890
+ const truncationResult = (0, _types.truncateOutput)(result.finalOutput ?? "", config);
891
+ if (truncationResult.truncated) result.truncation = truncationResult;
892
+ }
893
+
894
+ if (options.sessionFile && ((0, _nodeFs.existsSync)(options.sessionFile) || result.messages?.length)) {
895
+ result.sessionFile = options.sessionFile;
896
+ } else if (shareEnabled && options.sessionDir) {
897
+ const sessionFile = (0, _utils.findLatestSessionFile)(options.sessionDir);
898
+ if (sessionFile) result.sessionFile = sessionFile;
899
+ }
900
+
901
+ return result;
902
+ } /* v9-3f2610b246127400 */
pip-tmp/jiti/foreground-subagent-executor.425806a0.mjs ADDED
@@ -0,0 +1,2232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.createSubagentExecutor = createSubagentExecutor;var _nodeCrypto = await jitiImport("node:crypto");
2
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
3
+ var path = _interopRequireWildcard(await jitiImport("node:path"));
4
+
5
+
6
+
7
+ var _artifacts = await jitiImport("../../shared/artifacts.ts");
8
+ var _chainClarify = await jitiImport("./chain-clarify.ts");
9
+ var _modelInfo = await jitiImport("../../shared/model-info.ts");
10
+ var _chainExecution = await jitiImport("./chain-execution.ts");
11
+ var _agentScope = await jitiImport("../../agents/agent-scope.ts");
12
+ var _agentManagement = await jitiImport("../../agents/agent-management.ts");
13
+ var _doctor = await jitiImport("../../extension/doctor.ts");
14
+ var _controlNotices = await jitiImport("../../extension/control-notices.ts");
15
+ var _execution = await jitiImport("./execution.ts");
16
+ var _modelFallback = await jitiImport("../shared/model-fallback.ts");
17
+ var _parallelUtils = await jitiImport("../shared/parallel-utils.ts");
18
+ var _runHistory = await jitiImport("../shared/run-history.ts");
19
+ var _settings = await jitiImport("../../shared/settings.ts");
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+ var _skills = await jitiImport("../../agents/skills.ts");
33
+ var _asyncExecution = await jitiImport("../background/async-execution.ts");
34
+ var _forkContext = await jitiImport("../../shared/fork-context.ts");
35
+ var _sessionIdentity = await jitiImport("../../shared/session-identity.ts");
36
+ var _intercomBridge = await jitiImport("../../intercom/intercom-bridge.ts");
37
+ var _subagentControl = await jitiImport("../shared/subagent-control.ts");
38
+ var _singleOutput = await jitiImport("../shared/single-output.ts");
39
+ var _utils = await jitiImport("../../shared/utils.ts");
40
+ var _resultIntercom = await jitiImport("../../intercom/result-intercom.ts");
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+ var _asyncResume = await jitiImport("../background/async-resume.ts");
49
+ var _runStatus = await jitiImport("../background/run-status.ts");
50
+ var _topLevelAsync = await jitiImport("../background/top-level-async.ts");
51
+ var _worktree = await jitiImport("../shared/worktree.ts");
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+ var _types = await jitiImport("../../shared/types.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+ const ASYNC_INTERRUPT_SIGNAL = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+ function resolveRequestedCwd(runtimeCwd, requestedCwd) {
164
+ return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
165
+ }
166
+
167
+ function getForegroundControl(state, runId) {
168
+ if (runId) return state.foregroundControls.get(runId);
169
+ if (state.lastForegroundControlId) {
170
+ const latest = state.foregroundControls.get(state.lastForegroundControlId);
171
+ if (latest) return latest;
172
+ }
173
+ let newest;
174
+ for (const control of state.foregroundControls.values()) {
175
+ if (!newest || control.updatedAt > newest.updatedAt) newest = control;
176
+ }
177
+ return newest;
178
+ }
179
+
180
+ function formatForegroundActivity(control) {
181
+ const facts = [];
182
+ if (control.currentTool && control.currentToolStartedAt) facts.push(`tool ${control.currentTool} for ${Math.floor(Math.max(0, Date.now() - control.currentToolStartedAt) / 1000)}s`);else
183
+ if (control.currentTool) facts.push(`tool ${control.currentTool}`);
184
+ if (control.currentPath) facts.push(`path ${control.currentPath}`);
185
+ if (control.turnCount !== undefined) facts.push(`${control.turnCount} turns`);
186
+ if (control.tokens !== undefined) facts.push(`${control.tokens} tokens`);
187
+ if (control.toolCount !== undefined) facts.push(`${control.toolCount} tools`);
188
+ if (!control.lastActivityAt) {
189
+ if (control.currentActivityState === "needs_attention") return ["needs attention", ...facts].join(" | ");
190
+ if (control.currentActivityState === "active_long_running") return ["active but long-running", ...facts].join(" | ");
191
+ return facts.length ? facts.join(" | ") : undefined;
192
+ }
193
+ const seconds = Math.floor(Math.max(0, Date.now() - control.lastActivityAt) / 1000);
194
+ if (control.currentActivityState === "needs_attention") return [`no activity for ${seconds}s`, ...facts].join(" | ");
195
+ if (control.currentActivityState === "active_long_running") return [`active but long-running; last activity ${seconds}s ago`, ...facts].join(" | ");
196
+ return [`active ${seconds}s ago`, ...facts].join(" | ");
197
+ }
198
+
199
+ function foregroundStatusResult(control) {
200
+ const activity = formatForegroundActivity(control);
201
+ const lines = [
202
+ `Run: ${control.runId}`,
203
+ "State: running",
204
+ `Mode: ${control.mode}`,
205
+ control.currentAgent ? `Current: ${control.currentAgent}${control.currentIndex !== undefined ? ` step ${control.currentIndex + 1}` : ""}` : undefined,
206
+ activity ? `Activity: ${activity}` : undefined].
207
+ filter((line) => Boolean(line));
208
+ return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
209
+ }
210
+
211
+ function rememberForegroundRun(state, input) {
212
+ state.foregroundRuns ??= new Map();
213
+ state.foregroundRuns.set(input.runId, {
214
+ runId: input.runId,
215
+ mode: input.mode,
216
+ cwd: input.cwd,
217
+ updatedAt: Date.now(),
218
+ children: input.results.map((result, index) => ({
219
+ agent: result.agent,
220
+ index,
221
+ status: (0, _resultIntercom.resolveSubagentResultStatus)({ exitCode: result.exitCode, interrupted: result.interrupted, detached: result.detached }),
222
+ ...(result.sessionFile ? { sessionFile: result.sessionFile } : {})
223
+ }))
224
+ });
225
+ while (state.foregroundRuns.size > 50) {
226
+ const oldest = [...state.foregroundRuns.values()].sort((left, right) => left.updatedAt - right.updatedAt)[0];
227
+ if (!oldest) break;
228
+ state.foregroundRuns.delete(oldest.runId);
229
+ }
230
+ }
231
+
232
+ function resolveForegroundResumeTarget(params, state) {
233
+ const requested = (params.id ?? params.runId)?.trim();
234
+ if (!requested || !state.foregroundRuns?.size) return undefined;
235
+ const direct = state.foregroundRuns.get(requested);
236
+ const matches = direct ? [direct] : [...state.foregroundRuns.values()].filter((run) => run.runId.startsWith(requested));
237
+ if (matches.length === 0) return undefined;
238
+ if (matches.length > 1) throw new Error(`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((run) => run.runId).join(", ")}. Provide a longer id.`);
239
+ const run = matches[0];
240
+ if (run.children.length > 1 && params.index === undefined) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Provide index to choose one.`);
241
+ const index = params.index ?? 0;
242
+ if (!Number.isInteger(index)) throw new Error(`Foreground run '${run.runId}' index must be an integer.`);
243
+ if (index < 0 || index >= run.children.length) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Index ${index} is out of range.`);
244
+ const child = run.children[index];
245
+ if (child.status === "detached") throw new Error(`Foreground run '${run.runId}' child ${index} is detached for intercom coordination and cannot be revived safely from the remembered foreground state. Reply to the supervisor request first; after the child exits, start a fresh follow-up if needed.`);
246
+ if (!child.sessionFile) throw new Error(`Foreground run '${run.runId}' child ${index} does not have a persisted session file to resume from.`);
247
+ if (path.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
248
+ const sessionFile = path.resolve(child.sessionFile);
249
+ if (!fs.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
250
+ return { runId: run.runId, mode: run.mode, state: "complete", agent: child.agent, index, intercomTarget: (0, _intercomBridge.resolveSubagentIntercomTarget)(run.runId, child.agent, index), cwd: run.cwd, sessionFile };
251
+ }
252
+
253
+
254
+
255
+
256
+
257
+ function isAsyncRunNotFound(error) {
258
+ return error instanceof Error && error.message.startsWith("Async run not found.");
259
+ }
260
+
261
+ function isResumeAmbiguity(error) {
262
+ return error instanceof Error && /Ambiguous .*run id prefix/.test(error.message);
263
+ }
264
+
265
+ function resumeTargetExact(target, requested) {
266
+ return target?.runId === requested;
267
+ }
268
+
269
+ function escapeRegExp(value) {
270
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
271
+ }
272
+
273
+ function isExactResumeError(error, source, requested) {
274
+ if (!(error instanceof Error) || !requested) return false;
275
+ return new RegExp(`\\b${source} run '${escapeRegExp(requested)}'`, "i").test(error.message);
276
+ }
277
+
278
+ function resolveResumeTarget(params, state) {
279
+ const requested = (params.id ?? params.runId)?.trim() ?? "";
280
+ let foregroundTarget;
281
+ let foregroundError;
282
+ let asyncTarget;
283
+ let asyncError;
284
+
285
+ try {
286
+ const target = resolveForegroundResumeTarget(params, state);
287
+ if (target) foregroundTarget = { kind: "revive", source: "foreground", ...target };
288
+ } catch (error) {
289
+ foregroundError = error;
290
+ }
291
+ try {
292
+ asyncTarget = { source: "async", ...(0, _asyncResume.resolveAsyncResumeTarget)(params) };
293
+ } catch (error) {
294
+ asyncError = error;
295
+ }
296
+
297
+ if (foregroundTarget && asyncTarget) {
298
+ const foregroundExact = resumeTargetExact(foregroundTarget, requested);
299
+ const asyncExact = resumeTargetExact(asyncTarget, requested);
300
+ if (foregroundExact && !asyncExact) return foregroundTarget;
301
+ if (asyncExact && !foregroundExact) return asyncTarget;
302
+ throw new Error(`Resume id '${requested}' is ambiguous between foreground run '${foregroundTarget.runId}' and async run '${asyncTarget.runId}'. Provide a full run id.`);
303
+ }
304
+ if (foregroundTarget) {
305
+ if (isExactResumeError(asyncError, "async", requested)) throw asyncError;
306
+ if (isResumeAmbiguity(asyncError) && !resumeTargetExact(foregroundTarget, requested)) throw asyncError;
307
+ return foregroundTarget;
308
+ }
309
+ if (asyncTarget) {
310
+ if (isExactResumeError(foregroundError, "foreground", requested)) throw foregroundError;
311
+ if (isResumeAmbiguity(foregroundError) && !resumeTargetExact(asyncTarget, requested)) throw foregroundError;
312
+ return asyncTarget;
313
+ }
314
+ if (foregroundError && !isAsyncRunNotFound(asyncError)) throw foregroundError;
315
+ if (foregroundError) throw foregroundError;
316
+ if (asyncError) throw asyncError;
317
+ throw new Error("Run not found. Provide id or runId.");
318
+ }
319
+
320
+ function getAsyncInterruptTarget(state, runId) {
321
+ if (runId) {
322
+ const direct = state.asyncJobs.get(runId);
323
+ if (direct) return { asyncId: direct.asyncId, asyncDir: direct.asyncDir };
324
+ }
325
+ let newest;
326
+ for (const job of state.asyncJobs.values()) {
327
+ if (job.status !== "running") continue;
328
+ if (!newest || (job.updatedAt ?? 0) > newest.updatedAt) {
329
+ newest = { asyncId: job.asyncId, asyncDir: job.asyncDir, updatedAt: job.updatedAt ?? 0 };
330
+ }
331
+ }
332
+ return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
333
+ }
334
+
335
+ function emitControlNotification(input)
336
+
337
+
338
+
339
+
340
+ {
341
+ if (!(0, _subagentControl.shouldNotifyControlEvent)(input.controlConfig, input.event)) return;
342
+ const childIntercomTarget = input.intercomBridge.active ?
343
+ (0, _intercomBridge.resolveSubagentIntercomTarget)(input.event.runId, input.event.agent, input.event.index) :
344
+ undefined;
345
+ const payload = {
346
+ event: input.event,
347
+ source: "foreground",
348
+ childIntercomTarget,
349
+ noticeText: (0, _subagentControl.formatControlNoticeMessage)(input.event, childIntercomTarget)
350
+ };
351
+ if (input.controlConfig.notifyChannels.includes("event")) {
352
+ input.pi.events.emit(_types.SUBAGENT_CONTROL_EVENT, payload);
353
+ }
354
+ if (input.event.type !== "active_long_running" && input.controlConfig.notifyChannels.includes("intercom") && input.intercomBridge.active && input.intercomBridge.orchestratorTarget) {
355
+ input.pi.events.emit(_types.SUBAGENT_CONTROL_INTERCOM_EVENT, {
356
+ ...payload,
357
+ to: input.intercomBridge.orchestratorTarget,
358
+ message: (0, _subagentControl.formatControlIntercomMessage)(input.event, childIntercomTarget)
359
+ });
360
+ }
361
+ }
362
+
363
+ function interruptAsyncRun(state, runId) {
364
+ const target = getAsyncInterruptTarget(state, runId);
365
+ if (!target) return null;
366
+ const status = (0, _utils.readStatus)(target.asyncDir);
367
+ if (!status || status.state !== "running" || typeof status.pid !== "number") {
368
+ return {
369
+ content: [{ type: "text", text: `No running async run with an interrupt-capable pid was found for '${runId ?? "current"}'.` }],
370
+ isError: true,
371
+ details: { mode: "management", results: [] }
372
+ };
373
+ }
374
+ try {
375
+ process.kill(status.pid, ASYNC_INTERRUPT_SIGNAL);
376
+ const tracked = state.asyncJobs.get(target.asyncId);
377
+ if (tracked) {
378
+ tracked.activityState = undefined;
379
+ tracked.updatedAt = Date.now();
380
+ }
381
+ return {
382
+ content: [{ type: "text", text: `Interrupt requested for async run ${target.asyncId}.` }],
383
+ details: { mode: "management", results: [] }
384
+ };
385
+ } catch (error) {
386
+ const message = error instanceof Error ? error.message : String(error);
387
+ return {
388
+ content: [{ type: "text", text: `Failed to interrupt async run ${target.asyncId}: ${message}` }],
389
+ isError: true,
390
+ details: { mode: "management", results: [] }
391
+ };
392
+ }
393
+ }
394
+
395
+ async function resumeAsyncRun(input)
396
+
397
+
398
+
399
+
400
+ {
401
+ const followUp = (input.params.message ?? input.params.task ?? "").trim();
402
+ if (!followUp) {
403
+ return {
404
+ content: [{ type: "text", text: "action='resume' requires message." }],
405
+ isError: true,
406
+ details: { mode: "management", results: [] }
407
+ };
408
+ }
409
+
410
+ let target;
411
+ try {
412
+ target = resolveResumeTarget(input.params, input.deps.state);
413
+ } catch (error) {
414
+ const message = error instanceof Error ? error.message : String(error);
415
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
416
+ }
417
+
418
+ if (target.kind === "live") {
419
+ const delivered = await (0, _resultIntercom.deliverSubagentIntercomMessageEvent)(
420
+ input.deps.pi.events,
421
+ target.intercomTarget,
422
+ `Follow-up for async run ${target.runId} (${target.agent}):\n\n${followUp}`,
423
+ 500,
424
+ { source: "async-resume", runId: target.runId, agent: target.agent, index: target.index }
425
+ );
426
+ if (delivered) {
427
+ return {
428
+ content: [{ type: "text", text: [`Delivered follow-up to live async child.`, `Run: ${target.runId}`, `Intercom target: ${target.intercomTarget}`].join("\n") }],
429
+ details: { mode: "management", results: [] }
430
+ };
431
+ }
432
+ return {
433
+ content: [{ type: "text", text: [`Async child appears live but its intercom target is not registered.`, `Run: ${target.runId}`, `Intercom target: ${target.intercomTarget}`, `Wait for completion, then retry action='resume'.`].join("\n") }],
434
+ isError: true,
435
+ details: { mode: "management", results: [] }
436
+ };
437
+ }
438
+
439
+ const { blocked, depth, maxDepth } = (0, _types.checkSubagentDepth)(input.deps.config.maxSubagentDepth);
440
+ if (blocked) {
441
+ return {
442
+ content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
443
+ isError: true,
444
+ details: { mode: "management", results: [] }
445
+ };
446
+ }
447
+
448
+ const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
449
+ input.deps.state.currentSessionId = (0, _sessionIdentity.resolveCurrentSessionId)(input.ctx.sessionManager);
450
+ const effectiveCwd = target.cwd ?? input.requestCwd;
451
+ const scope = (0, _agentScope.resolveExecutionAgentScope)(input.params.agentScope);
452
+ const discoveredAgents = input.deps.discoverAgents(effectiveCwd, scope).agents;
453
+ const sessionName = (0, _intercomBridge.resolveIntercomSessionTarget)(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
454
+ const intercomBridge = (0, _intercomBridge.resolveIntercomBridge)({
455
+ config: input.deps.config.intercomBridge,
456
+ context: input.params.context,
457
+ orchestratorTarget: sessionName,
458
+ cwd: effectiveCwd
459
+ });
460
+ const agents = intercomBridge.active ?
461
+ discoveredAgents.map((agent) => (0, _intercomBridge.applyIntercomBridgeToAgent)(agent, intercomBridge)) :
462
+ discoveredAgents;
463
+ const agentConfig = agents.find((agent) => agent.name === target.agent);
464
+ if (!agentConfig) {
465
+ return {
466
+ content: [{ type: "text", text: `Unknown agent for resume: ${target.agent}` }],
467
+ isError: true,
468
+ details: { mode: "management", results: [] }
469
+ };
470
+ }
471
+
472
+ const runId = (0, _nodeCrypto.randomUUID)().slice(0, 8);
473
+ const artifactConfig = { ..._types.DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false };
474
+ const availableModels = input.ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo);
475
+ const result = (0, _asyncExecution.executeAsyncSingle)(runId, {
476
+ agent: target.agent,
477
+ task: (0, _asyncResume.buildRevivedAsyncTask)(target, followUp),
478
+ agentConfig,
479
+ ctx: {
480
+ pi: input.deps.pi,
481
+ cwd: input.requestCwd,
482
+ currentSessionId: input.deps.state.currentSessionId,
483
+ currentModelProvider: input.ctx.model?.provider
484
+ },
485
+ cwd: effectiveCwd,
486
+ maxOutput: input.params.maxOutput,
487
+ artifactsDir: input.deps.tempArtifactsDir,
488
+ artifactConfig,
489
+ shareEnabled: input.params.share === true,
490
+ sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
491
+ sessionFile: target.sessionFile,
492
+ maxSubagentDepth: (0, _types.resolveCurrentMaxSubagentDepth)(input.deps.config.maxSubagentDepth),
493
+ worktreeSetupHook: input.deps.config.worktreeSetupHook,
494
+ worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
495
+ controlConfig: (0, _subagentControl.resolveControlConfig)(input.deps.config.control, input.params.control),
496
+ controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
497
+ childIntercomTarget: intercomBridge.active ? (agent, index) => (0, _intercomBridge.resolveSubagentIntercomTarget)(runId, agent, index) : undefined,
498
+ availableModels
499
+ });
500
+ if (result.isError) return result;
501
+
502
+ const revivedId = result.details.asyncId ?? runId;
503
+ const revivedTarget = intercomBridge.active ? (0, _intercomBridge.resolveSubagentIntercomTarget)(revivedId, target.agent, 0) : undefined;
504
+ const sourceLabel = target.source === "foreground" ? "foreground" : "async";
505
+ const lines = [
506
+ `Revived ${sourceLabel} subagent from ${target.runId}.`,
507
+ `Revived run: ${revivedId}`,
508
+ `Agent: ${target.agent}`,
509
+ `Session: ${target.sessionFile}`,
510
+ result.details.asyncDir ? `Async dir: ${result.details.asyncDir}` : undefined,
511
+ revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
512
+ `Status if needed: subagent({ action: "status", id: "${revivedId}" })`].
513
+ filter((line) => Boolean(line));
514
+ return { content: [{ type: "text", text: (0, _asyncExecution.formatAsyncStartedMessage)(lines.join("\n")) }], details: result.details };
515
+ }
516
+
517
+ function resultSummaryForIntercom(result) {
518
+ const output = (0, _utils.getSingleResultOutput)(result);
519
+ if (result.exitCode !== 0 && result.error) {
520
+ return output ? `${result.error}\n\nOutput:\n${output}` : result.error;
521
+ }
522
+ return output || result.error || "(no output)";
523
+ }
524
+
525
+ function createForegroundControlNotifier(data, deps) {
526
+ return (event) => emitControlNotification({
527
+ pi: deps.pi,
528
+ controlConfig: data.controlConfig,
529
+ intercomBridge: data.intercomBridge,
530
+ event
531
+ });
532
+ }
533
+
534
+ async function emitForegroundResultIntercom(input)
535
+
536
+
537
+
538
+
539
+
540
+
541
+ {
542
+ if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
543
+ const children = input.results.flatMap((result, index) => result.detached ? [] : [{
544
+ agent: result.agent,
545
+ status: (0, _resultIntercom.resolveSubagentResultStatus)({
546
+ exitCode: result.exitCode,
547
+ interrupted: result.interrupted,
548
+ detached: result.detached
549
+ }),
550
+ summary: resultSummaryForIntercom(result),
551
+ index,
552
+ artifactPath: result.artifactPaths?.outputPath,
553
+ sessionPath: result.sessionFile,
554
+ intercomTarget: (0, _intercomBridge.resolveSubagentIntercomTarget)(input.runId, result.agent, index)
555
+ }]);
556
+ if (children.length === 0) return null;
557
+ const payload = (0, _resultIntercom.buildSubagentResultIntercomPayload)({
558
+ to: input.intercomBridge.orchestratorTarget,
559
+ runId: input.runId,
560
+ mode: input.mode,
561
+ source: "foreground",
562
+ children,
563
+ ...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {})
564
+ });
565
+ const delivered = await (0, _resultIntercom.deliverSubagentResultIntercomEvent)(input.pi.events, payload);
566
+ if (!delivered) return null;
567
+ return payload;
568
+ }
569
+
570
+ async function maybeBuildForegroundIntercomReceipt(input)
571
+
572
+
573
+
574
+
575
+
576
+ {
577
+ const payload = await emitForegroundResultIntercom({
578
+ pi: input.pi,
579
+ intercomBridge: input.intercomBridge,
580
+ runId: input.runId,
581
+ mode: input.mode,
582
+ results: input.details.results,
583
+ ...(typeof input.details.totalSteps === "number" ? { chainSteps: input.details.totalSteps } : {})
584
+ });
585
+ if (!payload) return null;
586
+ return {
587
+ text: (0, _resultIntercom.formatSubagentResultReceipt)({ mode: input.mode, runId: input.runId, payload }),
588
+ details: (0, _resultIntercom.stripDetailsOutputsForIntercomReceipt)(input.details)
589
+ };
590
+ }
591
+
592
+ function validateExecutionInput(
593
+ params,
594
+ agents,
595
+ hasChain,
596
+ hasTasks,
597
+ hasSingle,
598
+ allowClarifyTaskPrompt)
599
+ {
600
+ if (Number(hasChain) + Number(hasTasks) + Number(hasSingle) !== 1) {
601
+ return {
602
+ content: [
603
+ {
604
+ type: "text",
605
+ text: `Provide exactly one mode. Agents: ${agents.map((a) => a.name).join(", ") || "none"}`
606
+ }],
607
+
608
+ isError: true,
609
+ details: { mode: "single", results: [] }
610
+ };
611
+ }
612
+
613
+ if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
614
+ return {
615
+ content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
616
+ isError: true,
617
+ details: { mode: "single", results: [] }
618
+ };
619
+ }
620
+
621
+ if (hasTasks && params.tasks) {
622
+ for (let i = 0; i < params.tasks.length; i++) {
623
+ const task = params.tasks[i];
624
+ if (!agents.find((agent) => agent.name === task.agent)) {
625
+ return {
626
+ content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${i + 1})` }],
627
+ isError: true,
628
+ details: { mode: "parallel", results: [] }
629
+ };
630
+ }
631
+ }
632
+ }
633
+
634
+ if (hasChain && params.chain) {
635
+ if (params.chain.length === 0) {
636
+ return {
637
+ content: [{ type: "text", text: "Chain must have at least one step" }],
638
+ isError: true,
639
+ details: { mode: "chain", results: [] }
640
+ };
641
+ }
642
+ const firstStep = params.chain[0];
643
+ if ((0, _settings.isParallelStep)(firstStep)) {
644
+ const missingTaskIndex = firstStep.parallel.findIndex((t) => !t.task);
645
+ if (missingTaskIndex !== -1) {
646
+ return {
647
+ content: [{ type: "text", text: `First parallel step: task ${missingTaskIndex + 1} must have a task (no previous output to reference)` }],
648
+ isError: true,
649
+ details: { mode: "chain", results: [] }
650
+ };
651
+ }
652
+ } else if (!firstStep.task && !params.task && !allowClarifyTaskPrompt) {
653
+ return {
654
+ content: [{ type: "text", text: "First step in chain must have a task" }],
655
+ isError: true,
656
+ details: { mode: "chain", results: [] }
657
+ };
658
+ }
659
+ for (let i = 0; i < params.chain.length; i++) {
660
+ const step = params.chain[i];
661
+ const stepAgents = (0, _settings.getStepAgents)(step);
662
+ for (const agentName of stepAgents) {
663
+ if (!agents.find((a) => a.name === agentName)) {
664
+ return {
665
+ content: [{ type: "text", text: `Unknown agent: ${agentName} (step ${i + 1})` }],
666
+ isError: true,
667
+ details: { mode: "chain", results: [] }
668
+ };
669
+ }
670
+ }
671
+ if ((0, _settings.isParallelStep)(step) && step.parallel.length === 0) {
672
+ return {
673
+ content: [{ type: "text", text: `Parallel step ${i + 1} must have at least one task` }],
674
+ isError: true,
675
+ details: { mode: "chain", results: [] }
676
+ };
677
+ }
678
+ }
679
+ }
680
+
681
+ return null;
682
+ }
683
+
684
+ function getRequestedModeLabel(params) {
685
+ if ((params.chain?.length ?? 0) > 0) return "chain";
686
+ if ((params.tasks?.length ?? 0) > 0) return "parallel";
687
+ if (params.agent) return "single";
688
+ return "single";
689
+ }
690
+
691
+ function applyAgentDefaultContext(params, agents) {
692
+ if (params.context !== undefined) return params;
693
+ const byName = new Map(agents.map((agent) => [agent.name, agent]));
694
+ const names = [];
695
+ if (params.agent) names.push(params.agent);
696
+ for (const task of params.tasks ?? []) names.push(task.agent);
697
+ for (const step of params.chain ?? []) names.push(...(0, _settings.getStepAgents)(step));
698
+ return names.some((name) => byName.get(name)?.defaultContext === "fork") ?
699
+ { ...params, context: "fork" } :
700
+ params;
701
+ }
702
+
703
+ function buildRequestedModeError(params, message) {
704
+ return withForkContext(
705
+ {
706
+ content: [{ type: "text", text: message }],
707
+ isError: true,
708
+ details: { mode: getRequestedModeLabel(params), results: [] }
709
+ },
710
+ params.context
711
+ );
712
+ }
713
+
714
+ function expandTopLevelTaskCounts(tasks) {
715
+ const expanded = [];
716
+ for (let taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
717
+ const task = tasks[taskIndex];
718
+ const rawCount = task.count;
719
+ if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
720
+ return { error: `tasks[${taskIndex}].count must be an integer >= 1` };
721
+ }
722
+ const { count, ...concreteTask } = task;
723
+ for (let repeat = 0; repeat < (rawCount ?? 1); repeat++) {
724
+ expanded.push({ ...concreteTask });
725
+ }
726
+ }
727
+ return { tasks: expanded };
728
+ }
729
+
730
+ function expandChainParallelCounts(chain) {
731
+ const expandedChain = [];
732
+ for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
733
+ const step = chain[stepIndex];
734
+ if (!(0, _settings.isParallelStep)(step)) {
735
+ expandedChain.push(step);
736
+ continue;
737
+ }
738
+ const expandedParallel = [];
739
+ for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
740
+ const task = step.parallel[taskIndex];
741
+ const rawCount = task.count;
742
+ if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
743
+ return { error: `chain[${stepIndex}].parallel[${taskIndex}].count must be an integer >= 1` };
744
+ }
745
+ const { count, ...concreteTask } = task;
746
+ for (let repeat = 0; repeat < (rawCount ?? 1); repeat++) {
747
+ expandedParallel.push({ ...concreteTask });
748
+ }
749
+ }
750
+ expandedChain.push({ ...step, parallel: expandedParallel });
751
+ }
752
+ return { chain: expandedChain };
753
+ }
754
+
755
+ function normalizeRepeatedParallelCounts(params) {
756
+ if (params.tasks) {
757
+ const expandedTasks = expandTopLevelTaskCounts(params.tasks);
758
+ if (expandedTasks.error) {
759
+ return { error: buildRequestedModeError(params, expandedTasks.error) };
760
+ }
761
+ return { params: { ...params, tasks: expandedTasks.tasks } };
762
+ }
763
+ if (params.chain) {
764
+ const expandedChain = expandChainParallelCounts(params.chain);
765
+ if (expandedChain.error) {
766
+ return { error: buildRequestedModeError(params, expandedChain.error) };
767
+ }
768
+ return { params: { ...params, chain: expandedChain.chain } };
769
+ }
770
+ return { params };
771
+ }
772
+
773
+ function withForkContext(
774
+ result,
775
+ context)
776
+ {
777
+ if (context !== "fork" || !result.details) return result;
778
+ return {
779
+ ...result,
780
+ details: {
781
+ ...result.details,
782
+ context: "fork"
783
+ }
784
+ };
785
+ }
786
+
787
+ function toExecutionErrorResult(params, error) {
788
+ const message = error instanceof Error ? error.message : String(error);
789
+ return withForkContext(
790
+ {
791
+ content: [{ type: "text", text: message }],
792
+ isError: true,
793
+ details: { mode: getRequestedModeLabel(params), results: [] }
794
+ },
795
+ params.context
796
+ );
797
+ }
798
+
799
+ function collectChainSessionFiles(
800
+ chain,
801
+ sessionFileForIndex)
802
+ {
803
+ const sessionFiles = [];
804
+ let flatIndex = 0;
805
+ for (const step of chain) {
806
+ if ((0, _settings.isParallelStep)(step)) {
807
+ for (let i = 0; i < step.parallel.length; i++) {
808
+ sessionFiles.push(sessionFileForIndex(flatIndex));
809
+ flatIndex++;
810
+ }
811
+ continue;
812
+ }
813
+ sessionFiles.push(sessionFileForIndex(flatIndex));
814
+ flatIndex++;
815
+ }
816
+ return sessionFiles;
817
+ }
818
+
819
+ function wrapChainTasksForFork(chain, context) {
820
+ if (context !== "fork") return chain;
821
+ return chain.map((step, stepIndex) => {
822
+ if ((0, _settings.isParallelStep)(step)) {
823
+ return {
824
+ ...step,
825
+ parallel: step.parallel.map((task) => ({
826
+ ...task,
827
+ task: (0, _types.wrapForkTask)(task.task ?? "{previous}")
828
+ }))
829
+ };
830
+ }
831
+ const sequential = step;
832
+ return {
833
+ ...sequential,
834
+ task: (0, _types.wrapForkTask)(sequential.task ?? (stepIndex === 0 ? "{task}" : "{previous}"))
835
+ };
836
+ });
837
+ }
838
+
839
+ function runAsyncPath(data, deps) {
840
+ const {
841
+ params,
842
+ effectiveCwd,
843
+ agents,
844
+ ctx,
845
+ shareEnabled,
846
+ sessionRoot,
847
+ sessionFileForIndex,
848
+ artifactConfig,
849
+ artifactsDir,
850
+ effectiveAsync,
851
+ controlConfig,
852
+ intercomBridge
853
+ } = data;
854
+ const hasChain = (params.chain?.length ?? 0) > 0;
855
+ const hasTasks = (params.tasks?.length ?? 0) > 0;
856
+ const hasSingle = !hasChain && !hasTasks && Boolean(params.agent);
857
+ if (!effectiveAsync) return null;
858
+
859
+ if (hasChain && params.chain) {
860
+ const chainWorktreeTaskCwdError = buildChainWorktreeTaskCwdError(params.chain, effectiveCwd);
861
+ if (chainWorktreeTaskCwdError) {
862
+ return {
863
+ content: [{ type: "text", text: chainWorktreeTaskCwdError }],
864
+ isError: true,
865
+ details: { mode: "chain", results: [] }
866
+ };
867
+ }
868
+ }
869
+
870
+ if (hasTasks && params.tasks) {
871
+ const maxParallelTasks = (0, _types.resolveTopLevelParallelMaxTasks)(deps.config.parallel?.maxTasks);
872
+ if (params.tasks.length > maxParallelTasks) {
873
+ return buildParallelModeError(`Max ${maxParallelTasks} tasks`);
874
+ }
875
+ if (params.worktree) {
876
+ const worktreeTaskCwdError = buildParallelWorktreeTaskCwdError(params.tasks, effectiveCwd);
877
+ if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
878
+ }
879
+ }
880
+
881
+ if (!(0, _asyncExecution.isAsyncAvailable)()) {
882
+ return {
883
+ content: [{ type: "text", text: "Async mode requires jiti for TypeScript execution but it could not be found. Install globally: npm install -g jiti" }],
884
+ isError: true,
885
+ details: { mode: "single", results: [] }
886
+ };
887
+ }
888
+ const id = (0, _nodeCrypto.randomUUID)();
889
+ const asyncCtx = {
890
+ pi: deps.pi,
891
+ cwd: ctx.cwd,
892
+ currentSessionId: deps.state.currentSessionId,
893
+ currentModelProvider: ctx.model?.provider
894
+ };
895
+ const availableModels = ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo);
896
+ const currentMaxSubagentDepth = (0, _types.resolveCurrentMaxSubagentDepth)(deps.config.maxSubagentDepth);
897
+ const currentProvider = ctx.model?.provider;
898
+ const controlIntercomTarget = intercomBridge.active ? intercomBridge.orchestratorTarget : undefined;
899
+ const childIntercomTarget = intercomBridge.active ? (agent, index) => (0, _intercomBridge.resolveSubagentIntercomTarget)(id, agent, index) : undefined;
900
+
901
+ if (hasTasks && params.tasks) {
902
+ const agentConfigs = params.tasks.map((task) => agents.find((agent) => agent.name === task.agent));
903
+ const modelOverrides = params.tasks.map((task, index) =>
904
+ (0, _modelFallback.resolveModelCandidate)(task.model ?? agentConfigs[index]?.model, availableModels, currentProvider)
905
+ );
906
+ const skillOverrides = params.tasks.map((task) => (0, _skills.normalizeSkillInput)(task.skill));
907
+ const parallelTasks = params.tasks.map((task, index) => ({
908
+ agent: task.agent,
909
+ task: params.context === "fork" ? (0, _types.wrapForkTask)(task.task) : task.task,
910
+ cwd: task.cwd,
911
+ ...(modelOverrides[index] ? { model: modelOverrides[index] } : {}),
912
+ ...(skillOverrides[index] !== undefined ? { skill: skillOverrides[index] } : {}),
913
+ ...(task.output === true ? agentConfigs[index]?.output ? { output: agentConfigs[index].output } : {} : task.output !== undefined ? { output: task.output } : {}),
914
+ ...(task.outputMode !== undefined ? { outputMode: task.outputMode } : {}),
915
+ ...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
916
+ ...(task.progress !== undefined ? { progress: task.progress } : {})
917
+ }));
918
+ return (0, _asyncExecution.executeAsyncChain)(id, {
919
+ chain: [{
920
+ parallel: parallelTasks,
921
+ concurrency: (0, _types.resolveTopLevelParallelConcurrency)(params.concurrency, deps.config.parallel?.concurrency),
922
+ worktree: params.worktree
923
+ }],
924
+ resultMode: "parallel",
925
+ agents,
926
+ ctx: asyncCtx,
927
+ availableModels,
928
+ cwd: effectiveCwd,
929
+ maxOutput: params.maxOutput,
930
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
931
+ artifactConfig,
932
+ shareEnabled,
933
+ sessionRoot,
934
+ chainSkills: [],
935
+ sessionFilesByFlatIndex: params.tasks.map((_, index) => sessionFileForIndex(index)),
936
+ maxSubagentDepth: currentMaxSubagentDepth,
937
+ worktreeSetupHook: deps.config.worktreeSetupHook,
938
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
939
+ controlConfig,
940
+ controlIntercomTarget,
941
+ childIntercomTarget
942
+ });
943
+ }
944
+
945
+ if (hasChain && params.chain) {
946
+ const normalized = (0, _skills.normalizeSkillInput)(params.skill);
947
+ const chainSkills = normalized === false ? [] : normalized ?? [];
948
+ const chain = wrapChainTasksForFork(params.chain, params.context);
949
+ return (0, _asyncExecution.executeAsyncChain)(id, {
950
+ chain,
951
+ task: params.task,
952
+ agents,
953
+ ctx: asyncCtx,
954
+ availableModels,
955
+ cwd: effectiveCwd,
956
+ maxOutput: params.maxOutput,
957
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
958
+ artifactConfig,
959
+ shareEnabled,
960
+ sessionRoot,
961
+ chainSkills,
962
+ sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForIndex),
963
+ maxSubagentDepth: currentMaxSubagentDepth,
964
+ worktreeSetupHook: deps.config.worktreeSetupHook,
965
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
966
+ controlConfig,
967
+ controlIntercomTarget,
968
+ childIntercomTarget
969
+ });
970
+ }
971
+
972
+ if (hasSingle) {
973
+ const a = agents.find((x) => x.name === params.agent);
974
+ if (!a) {
975
+ return {
976
+ content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
977
+ isError: true,
978
+ details: { mode: "single", results: [] }
979
+ };
980
+ }
981
+ const rawOutput = params.output !== undefined ? params.output : a.output;
982
+ const effectiveOutput = rawOutput === true ? a.output : rawOutput;
983
+ const effectiveOutputMode = params.outputMode ?? "inline";
984
+ const normalizedSkills = (0, _skills.normalizeSkillInput)(params.skill);
985
+ const skills = normalizedSkills === false ? [] : normalizedSkills;
986
+ const maxSubagentDepth = (0, _types.resolveChildMaxSubagentDepth)(currentMaxSubagentDepth, a.maxSubagentDepth);
987
+ const modelOverride = (0, _modelFallback.resolveModelCandidate)(params.model ?? a.model, availableModels, currentProvider);
988
+ return (0, _asyncExecution.executeAsyncSingle)(id, {
989
+ agent: params.agent,
990
+ task: params.context === "fork" ? (0, _types.wrapForkTask)(params.task ?? "") : params.task ?? "",
991
+ agentConfig: a,
992
+ ctx: asyncCtx,
993
+ availableModels,
994
+ cwd: effectiveCwd,
995
+ maxOutput: params.maxOutput,
996
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
997
+ artifactConfig,
998
+ shareEnabled,
999
+ sessionRoot,
1000
+ sessionFile: sessionFileForIndex(0),
1001
+ skills,
1002
+ output: effectiveOutput,
1003
+ outputMode: effectiveOutputMode,
1004
+ modelOverride,
1005
+ maxSubagentDepth,
1006
+ worktreeSetupHook: deps.config.worktreeSetupHook,
1007
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
1008
+ controlConfig,
1009
+ controlIntercomTarget,
1010
+ childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined
1011
+ });
1012
+ }
1013
+
1014
+ return null;
1015
+ }
1016
+
1017
+ async function runChainPath(data, deps) {
1018
+ const {
1019
+ params,
1020
+ effectiveCwd,
1021
+ agents,
1022
+ ctx,
1023
+ signal,
1024
+ runId,
1025
+ shareEnabled,
1026
+ sessionDirForIndex,
1027
+ sessionFileForIndex,
1028
+ artifactsDir,
1029
+ artifactConfig,
1030
+ onUpdate,
1031
+ sessionRoot,
1032
+ controlConfig
1033
+ } = data;
1034
+ const onControlEvent = createForegroundControlNotifier(data, deps);
1035
+ const childIntercomTarget = data.intercomBridge.active ? _intercomBridge.resolveSubagentIntercomTarget : undefined;
1036
+ const foregroundControl = deps.state.foregroundControls.get(runId);
1037
+ const normalized = (0, _skills.normalizeSkillInput)(params.skill);
1038
+ const chainSkills = normalized === false ? [] : normalized ?? [];
1039
+ const chain = wrapChainTasksForFork(params.chain, params.context);
1040
+ const currentMaxSubagentDepth = (0, _types.resolveCurrentMaxSubagentDepth)(deps.config.maxSubagentDepth);
1041
+ const chainResult = await (0, _chainExecution.executeChain)({
1042
+ chain,
1043
+ task: params.task,
1044
+ agents,
1045
+ ctx,
1046
+ intercomEvents: deps.pi.events,
1047
+ signal,
1048
+ runId,
1049
+ cwd: effectiveCwd,
1050
+ shareEnabled,
1051
+ sessionDirForIndex,
1052
+ sessionFileForIndex,
1053
+ artifactsDir,
1054
+ artifactConfig,
1055
+ includeProgress: params.includeProgress,
1056
+ clarify: params.clarify,
1057
+ onUpdate,
1058
+ onControlEvent,
1059
+ controlConfig,
1060
+ childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
1061
+ orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1062
+ foregroundControl,
1063
+ chainSkills,
1064
+ chainDir: params.chainDir,
1065
+ maxSubagentDepth: currentMaxSubagentDepth,
1066
+ worktreeSetupHook: deps.config.worktreeSetupHook,
1067
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs
1068
+ });
1069
+
1070
+ if (chainResult.requestedAsync) {
1071
+ if (!(0, _asyncExecution.isAsyncAvailable)()) {
1072
+ return {
1073
+ content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1074
+ isError: true,
1075
+ details: { mode: "chain", results: [] }
1076
+ };
1077
+ }
1078
+ const id = (0, _nodeCrypto.randomUUID)();
1079
+ const asyncCtx = {
1080
+ pi: deps.pi,
1081
+ cwd: ctx.cwd,
1082
+ currentSessionId: deps.state.currentSessionId,
1083
+ currentModelProvider: ctx.model?.provider
1084
+ };
1085
+ const asyncChain = wrapChainTasksForFork(chainResult.requestedAsync.chain, params.context);
1086
+ return (0, _asyncExecution.executeAsyncChain)(id, {
1087
+ chain: asyncChain,
1088
+ task: params.task,
1089
+ agents,
1090
+ ctx: asyncCtx,
1091
+ availableModels: ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo),
1092
+ cwd: effectiveCwd,
1093
+ maxOutput: params.maxOutput,
1094
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1095
+ artifactConfig,
1096
+ shareEnabled,
1097
+ sessionRoot,
1098
+ chainSkills: chainResult.requestedAsync.chainSkills,
1099
+ sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForIndex),
1100
+ maxSubagentDepth: currentMaxSubagentDepth,
1101
+ worktreeSetupHook: deps.config.worktreeSetupHook,
1102
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
1103
+ controlConfig,
1104
+ controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1105
+ childIntercomTarget: data.intercomBridge.active ? (agent, index) => (0, _intercomBridge.resolveSubagentIntercomTarget)(id, agent, index) : undefined
1106
+ });
1107
+ }
1108
+
1109
+ const chainDetails = chainResult.details ? (0, _utils.compactForegroundDetails)({ ...chainResult.details, runId }) : undefined;
1110
+ if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
1111
+ const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached) ?
1112
+ await maybeBuildForegroundIntercomReceipt({
1113
+ pi: deps.pi,
1114
+ intercomBridge: data.intercomBridge,
1115
+ runId,
1116
+ mode: "chain",
1117
+ details: chainDetails
1118
+ }) :
1119
+ null;
1120
+ if (intercomReceipt) {
1121
+ return {
1122
+ ...chainResult,
1123
+ content: [{ type: "text", text: intercomReceipt.text }],
1124
+ details: intercomReceipt.details
1125
+ };
1126
+ }
1127
+
1128
+ return chainDetails ? { ...chainResult, details: chainDetails } : chainResult;
1129
+ }
1130
+
1131
+
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+
1148
+
1149
+
1150
+
1151
+
1152
+
1153
+
1154
+
1155
+
1156
+
1157
+
1158
+
1159
+
1160
+
1161
+
1162
+
1163
+ function buildParallelModeError(message) {
1164
+ return {
1165
+ content: [{ type: "text", text: message }],
1166
+ isError: true,
1167
+ details: { mode: "parallel", results: [] }
1168
+ };
1169
+ }
1170
+
1171
+ function createParallelWorktreeSetup(
1172
+ enabled,
1173
+ cwd,
1174
+ runId,
1175
+ tasks,
1176
+ setupHook,
1177
+ setupHookTimeoutMs)
1178
+ {
1179
+ if (!enabled) return {};
1180
+ try {
1181
+ return {
1182
+ setup: (0, _worktree.createWorktrees)(cwd, runId, tasks.length, {
1183
+ agents: tasks.map((task) => task.agent),
1184
+ setupHook: setupHook ?
1185
+ { hookPath: setupHook, timeoutMs: setupHookTimeoutMs } :
1186
+ undefined
1187
+ })
1188
+ };
1189
+ } catch (error) {
1190
+ const message = error instanceof Error ? error.message : String(error);
1191
+ return { errorResult: buildParallelModeError(message) };
1192
+ }
1193
+ }
1194
+
1195
+ function buildParallelWorktreeTaskCwdError(
1196
+ tasks,
1197
+ sharedCwd)
1198
+ {
1199
+ const conflict = (0, _worktree.findWorktreeTaskCwdConflict)(tasks, sharedCwd);
1200
+ if (!conflict) return undefined;
1201
+ return (0, _worktree.formatWorktreeTaskCwdConflict)(conflict, sharedCwd);
1202
+ }
1203
+
1204
+ function buildChainWorktreeTaskCwdError(chain, sharedCwd) {
1205
+ for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
1206
+ const step = chain[stepIndex];
1207
+ if (!(0, _settings.isParallelStep)(step) || !step.worktree) continue;
1208
+ const stepCwd = (0, _utils.resolveChildCwd)(sharedCwd, step.cwd);
1209
+ const conflict = (0, _worktree.findWorktreeTaskCwdConflict)(step.parallel, stepCwd);
1210
+ if (!conflict) continue;
1211
+ const detail = (0, _worktree.formatWorktreeTaskCwdConflict)(conflict, stepCwd);
1212
+ return `parallel chain step ${stepIndex + 1}: ${detail}`;
1213
+ }
1214
+ return undefined;
1215
+ }
1216
+
1217
+ function resolveParallelTaskCwd(
1218
+ task,
1219
+ paramsCwd,
1220
+ worktreeSetup,
1221
+ index)
1222
+ {
1223
+ if (worktreeSetup) return worktreeSetup.worktrees[index].agentCwd;
1224
+ return (0, _utils.resolveChildCwd)(paramsCwd, task.cwd);
1225
+ }
1226
+
1227
+ function buildParallelWorktreeSuffix(
1228
+ worktreeSetup,
1229
+ artifactsDir,
1230
+ tasks)
1231
+ {
1232
+ if (!worktreeSetup) return "";
1233
+ const diffsDir = path.join(artifactsDir, "worktree-diffs");
1234
+ const diffs = (0, _worktree.diffWorktrees)(worktreeSetup, tasks.map((task) => task.agent), diffsDir);
1235
+ return (0, _worktree.formatWorktreeDiffSummary)(diffs);
1236
+ }
1237
+
1238
+ function findDuplicateParallelOutputPath(input)
1239
+
1240
+
1241
+
1242
+
1243
+
1244
+ {
1245
+ const seen = new Map();
1246
+ for (let index = 0; index < input.tasks.length; index++) {
1247
+ const behavior = input.behaviors[index];
1248
+ if (!behavior?.output) continue;
1249
+ const task = input.tasks[index];
1250
+ const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
1251
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(behavior.output, input.ctxCwd, taskCwd);
1252
+ if (!outputPath) continue;
1253
+ const previous = seen.get(outputPath);
1254
+ if (previous) {
1255
+ return `Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${outputPath}. Use distinct output paths.`;
1256
+ }
1257
+ seen.set(outputPath, { index, agent: task.agent });
1258
+ }
1259
+ return undefined;
1260
+ }
1261
+
1262
+ async function runForegroundParallelTasks(input) {
1263
+ return (0, _utils.mapConcurrent)(input.tasks, input.concurrencyLimit, async (task, index) => {
1264
+ const behavior = input.behaviors[index];
1265
+ const effectiveSkills = behavior?.skills;
1266
+ const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
1267
+ const readInstructions = behavior ?
1268
+ (0, _settings.buildChainInstructions)({ ...behavior, output: false, progress: false }, taskCwd, false) :
1269
+ { prefix: "", suffix: "" };
1270
+ const progressInstructions = behavior ?
1271
+ (0, _settings.buildChainInstructions)({ ...behavior, output: false, reads: false }, input.paramsCwd, index === input.firstProgressIndex) :
1272
+ { prefix: "", suffix: "" };
1273
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(behavior?.output, input.ctx.cwd, taskCwd);
1274
+ const taskText = (0, _singleOutput.injectSingleOutputInstruction)(
1275
+ `${readInstructions.prefix}${input.taskTexts[index]}${progressInstructions.suffix}`,
1276
+ outputPath
1277
+ );
1278
+ const interruptController = new AbortController();
1279
+ if (input.foregroundControl) {
1280
+ input.foregroundControl.currentAgent = task.agent;
1281
+ input.foregroundControl.currentIndex = index;
1282
+ input.foregroundControl.currentActivityState = undefined;
1283
+ input.foregroundControl.updatedAt = Date.now();
1284
+ input.foregroundControl.interrupt = () => {
1285
+ if (interruptController.signal.aborted) return false;
1286
+ interruptController.abort();
1287
+ input.foregroundControl.currentActivityState = undefined;
1288
+ input.foregroundControl.updatedAt = Date.now();
1289
+ return true;
1290
+ };
1291
+ }
1292
+ const agentConfig = input.agents.find((agent) => agent.name === task.agent);
1293
+ return (0, _execution.runSync)(input.ctx.cwd, input.agents, task.agent, taskText, {
1294
+ cwd: taskCwd,
1295
+ signal: input.signal,
1296
+ interruptSignal: interruptController.signal,
1297
+ allowIntercomDetach: agentConfig?.systemPrompt?.includes(_intercomBridge.INTERCOM_BRIDGE_MARKER) === true,
1298
+ intercomEvents: input.intercomEvents,
1299
+ runId: input.runId,
1300
+ index,
1301
+ sessionDir: input.sessionDirForIndex(index),
1302
+ sessionFile: input.sessionFileForIndex(index),
1303
+ share: input.shareEnabled,
1304
+ artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
1305
+ artifactConfig: input.artifactConfig,
1306
+ maxOutput: input.maxOutput,
1307
+ outputPath,
1308
+ outputMode: behavior?.outputMode,
1309
+ maxSubagentDepth: input.maxSubagentDepths[index],
1310
+ controlConfig: input.controlConfig,
1311
+ onControlEvent: input.onControlEvent,
1312
+ intercomSessionName: input.childIntercomTarget?.(task.agent, index),
1313
+ orchestratorIntercomTarget: input.orchestratorIntercomTarget,
1314
+ modelOverride: input.modelOverrides[index],
1315
+ availableModels: input.availableModels,
1316
+ preferredModelProvider: input.ctx.model?.provider,
1317
+ skills: effectiveSkills === false ? [] : effectiveSkills,
1318
+ onUpdate: input.onUpdate ?
1319
+ (progressUpdate) => {
1320
+ const stepResults = progressUpdate.details?.results || [];
1321
+ const stepProgress = progressUpdate.details?.progress || [];
1322
+ if (input.foregroundControl && stepProgress.length > 0) {
1323
+ const current = stepProgress[0];
1324
+ input.foregroundControl.currentAgent = task.agent;
1325
+ input.foregroundControl.currentIndex = index;
1326
+ input.foregroundControl.currentActivityState = current?.activityState;
1327
+ input.foregroundControl.lastActivityAt = current?.lastActivityAt;
1328
+ input.foregroundControl.currentTool = current?.currentTool;
1329
+ input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
1330
+ input.foregroundControl.currentPath = current?.currentPath;
1331
+ input.foregroundControl.turnCount = current?.turnCount;
1332
+ input.foregroundControl.tokens = current?.tokens;
1333
+ input.foregroundControl.toolCount = current?.toolCount;
1334
+ input.foregroundControl.updatedAt = Date.now();
1335
+ }
1336
+ if (stepResults.length > 0) input.liveResults[index] = stepResults[0];
1337
+ if (stepProgress.length > 0) input.liveProgress[index] = stepProgress[0];
1338
+ const mergedResults = input.liveResults.filter((result) => result !== undefined);
1339
+ const mergedProgress = input.liveProgress.filter((progress) => progress !== undefined);
1340
+ input.onUpdate?.({
1341
+ content: progressUpdate.content,
1342
+ details: {
1343
+ mode: "parallel",
1344
+ results: mergedResults,
1345
+ progress: mergedProgress,
1346
+ controlEvents: progressUpdate.details?.controlEvents,
1347
+ totalSteps: input.tasks.length
1348
+ }
1349
+ });
1350
+ } :
1351
+ undefined
1352
+ }).finally(() => {
1353
+ if (input.foregroundControl?.currentIndex === index) {
1354
+ input.foregroundControl.interrupt = undefined;
1355
+ input.foregroundControl.updatedAt = Date.now();
1356
+ }
1357
+ });
1358
+ });
1359
+ }
1360
+
1361
+ async function runParallelPath(data, deps) {
1362
+ const {
1363
+ params,
1364
+ effectiveCwd,
1365
+ agents,
1366
+ ctx,
1367
+ signal,
1368
+ runId,
1369
+ sessionDirForIndex,
1370
+ sessionFileForIndex,
1371
+ shareEnabled,
1372
+ artifactConfig,
1373
+ artifactsDir,
1374
+ backgroundRequestedWhileClarifying,
1375
+ onUpdate,
1376
+ sessionRoot,
1377
+ controlConfig
1378
+ } = data;
1379
+ const onControlEvent = createForegroundControlNotifier(data, deps);
1380
+ const childIntercomTarget = data.intercomBridge.active ? _intercomBridge.resolveSubagentIntercomTarget : undefined;
1381
+ const allProgress = [];
1382
+ const allArtifactPaths = [];
1383
+ const tasks = params.tasks;
1384
+ const maxParallelTasks = (0, _types.resolveTopLevelParallelMaxTasks)(deps.config.parallel?.maxTasks);
1385
+ const parallelConcurrency = (0, _types.resolveTopLevelParallelConcurrency)(params.concurrency, deps.config.parallel?.concurrency);
1386
+
1387
+ if (tasks.length > maxParallelTasks)
1388
+ return {
1389
+ content: [{ type: "text", text: `Max ${maxParallelTasks} tasks` }],
1390
+ isError: true,
1391
+ details: { mode: "parallel", results: [] }
1392
+ };
1393
+
1394
+ const agentConfigs = [];
1395
+ for (const t of tasks) {
1396
+ const config = agents.find((a) => a.name === t.agent);
1397
+ if (!config) {
1398
+ return {
1399
+ content: [{ type: "text", text: `Unknown agent: ${t.agent}` }],
1400
+ isError: true,
1401
+ details: { mode: "parallel", results: [] }
1402
+ };
1403
+ }
1404
+ agentConfigs.push(config);
1405
+ }
1406
+
1407
+ const currentMaxSubagentDepth = (0, _types.resolveCurrentMaxSubagentDepth)(deps.config.maxSubagentDepth);
1408
+ const maxSubagentDepths = agentConfigs.map((config) =>
1409
+ (0, _types.resolveChildMaxSubagentDepth)(currentMaxSubagentDepth, config.maxSubagentDepth)
1410
+ );
1411
+
1412
+ if (params.worktree) {
1413
+ const worktreeTaskCwdError = buildParallelWorktreeTaskCwdError(tasks, effectiveCwd);
1414
+ if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
1415
+ }
1416
+
1417
+ const currentProvider = ctx.model?.provider;
1418
+ const availableModels = ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo);
1419
+ let taskTexts = tasks.map((t) => t.task);
1420
+ const skillOverrides = tasks.map((t) =>
1421
+ (0, _skills.normalizeSkillInput)(t.skill)
1422
+ );
1423
+ const behaviorOverrides = tasks.map((task, index) => ({
1424
+ ...(task.output !== undefined ? { output: task.output === true ? agentConfigs[index]?.output ?? false : task.output } : {}),
1425
+ ...(task.outputMode !== undefined ? { outputMode: task.outputMode } : {}),
1426
+ ...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
1427
+ ...(task.progress !== undefined ? { progress: task.progress } : {}),
1428
+ ...(skillOverrides[index] !== undefined ? { skills: skillOverrides[index] } : {}),
1429
+ ...(task.model ? { model: task.model } : {})
1430
+ }));
1431
+ const modelOverrides = tasks.map((_, i) =>
1432
+ (0, _modelFallback.resolveModelCandidate)(behaviorOverrides[i]?.model ?? agentConfigs[i]?.model, availableModels, currentProvider)
1433
+ );
1434
+
1435
+ if (params.clarify === true && ctx.hasUI) {
1436
+ const behaviors = agentConfigs.map((c, i) =>
1437
+ (0, _settings.resolveStepBehavior)(c, behaviorOverrides[i])
1438
+ );
1439
+ const availableSkills = (0, _skills.discoverAvailableSkills)(effectiveCwd);
1440
+
1441
+ const result = await ctx.ui.custom(
1442
+ (tui, theme, _kb, done) =>
1443
+ new _chainClarify.ChainClarifyComponent(
1444
+ tui, theme,
1445
+ agentConfigs,
1446
+ taskTexts,
1447
+ "",
1448
+ undefined,
1449
+ behaviors,
1450
+ availableModels,
1451
+ currentProvider,
1452
+ availableSkills,
1453
+ done,
1454
+ "parallel"
1455
+ ),
1456
+ { overlay: true, overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" } }
1457
+ );
1458
+
1459
+ if (!result || !result.confirmed) {
1460
+ return { content: [{ type: "text", text: "Cancelled" }], details: { mode: "parallel", results: [] } };
1461
+ }
1462
+
1463
+ taskTexts = result.templates;
1464
+ for (let i = 0; i < result.behaviorOverrides.length; i++) {
1465
+ const override = result.behaviorOverrides[i];
1466
+ if (override?.model) {
1467
+ modelOverrides[i] = override.model;
1468
+ behaviorOverrides[i].model = override.model;
1469
+ }
1470
+ if (override?.output !== undefined) behaviorOverrides[i].output = override.output;
1471
+ if (override?.reads !== undefined) behaviorOverrides[i].reads = override.reads;
1472
+ if (override?.progress !== undefined) behaviorOverrides[i].progress = override.progress;
1473
+ if (override?.skills !== undefined) {
1474
+ skillOverrides[i] = override.skills;
1475
+ behaviorOverrides[i].skills = override.skills;
1476
+ }
1477
+ }
1478
+
1479
+ if (result.runInBackground) {
1480
+ if (!(0, _asyncExecution.isAsyncAvailable)()) {
1481
+ return {
1482
+ content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1483
+ isError: true,
1484
+ details: { mode: "parallel", results: [] }
1485
+ };
1486
+ }
1487
+ const id = (0, _nodeCrypto.randomUUID)();
1488
+ const asyncCtx = {
1489
+ pi: deps.pi,
1490
+ cwd: ctx.cwd,
1491
+ currentSessionId: deps.state.currentSessionId,
1492
+ currentModelProvider: ctx.model?.provider
1493
+ };
1494
+ const parallelTasks = tasks.map((t, i) => {
1495
+ const taskText = params.context === "fork" ? (0, _types.wrapForkTask)(taskTexts[i]) : taskTexts[i];
1496
+ const progress = (0, _settings.taskDisallowsFileUpdates)(taskText) ? false : behaviorOverrides[i]?.progress;
1497
+ return {
1498
+ agent: t.agent,
1499
+ task: taskText,
1500
+ cwd: t.cwd,
1501
+ ...(modelOverrides[i] ? { model: modelOverrides[i] } : {}),
1502
+ ...(skillOverrides[i] !== undefined ? { skill: skillOverrides[i] } : {}),
1503
+ ...(behaviorOverrides[i]?.output !== undefined ? { output: behaviorOverrides[i].output } : {}),
1504
+ ...(behaviorOverrides[i]?.outputMode !== undefined ? { outputMode: behaviorOverrides[i].outputMode } : {}),
1505
+ ...(behaviorOverrides[i]?.reads !== undefined ? { reads: behaviorOverrides[i].reads } : {}),
1506
+ ...(progress !== undefined ? { progress } : {})
1507
+ };
1508
+ });
1509
+ return (0, _asyncExecution.executeAsyncChain)(id, {
1510
+ chain: [{ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree }],
1511
+ resultMode: "parallel",
1512
+ agents,
1513
+ ctx: asyncCtx,
1514
+ availableModels,
1515
+ cwd: effectiveCwd,
1516
+ maxOutput: params.maxOutput,
1517
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1518
+ artifactConfig,
1519
+ shareEnabled,
1520
+ sessionRoot,
1521
+ chainSkills: [],
1522
+ sessionFilesByFlatIndex: tasks.map((_, index) => sessionFileForIndex(index)),
1523
+ maxSubagentDepth: currentMaxSubagentDepth,
1524
+ worktreeSetupHook: deps.config.worktreeSetupHook,
1525
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
1526
+ controlConfig,
1527
+ controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1528
+ childIntercomTarget: data.intercomBridge.active ? (agent, index) => (0, _intercomBridge.resolveSubagentIntercomTarget)(id, agent, index) : undefined
1529
+ });
1530
+ }
1531
+ }
1532
+
1533
+ const behaviors = agentConfigs.map((config, index) => (0, _settings.suppressProgressForReadOnlyTask)((0, _settings.resolveStepBehavior)(config, behaviorOverrides[index]), taskTexts[index]));
1534
+ const firstProgressIndex = behaviors.findIndex((behavior) => behavior.progress);
1535
+ const liveResults = new Array(tasks.length).fill(undefined);
1536
+ const liveProgress = new Array(tasks.length).fill(undefined);
1537
+ const foregroundControl = deps.state.foregroundControls.get(runId);
1538
+ const { setup: worktreeSetup, errorResult } = createParallelWorktreeSetup(
1539
+ params.worktree,
1540
+ effectiveCwd,
1541
+ runId,
1542
+ tasks,
1543
+ deps.config.worktreeSetupHook,
1544
+ deps.config.worktreeSetupHookTimeoutMs
1545
+ );
1546
+ if (errorResult) return errorResult;
1547
+
1548
+ try {
1549
+ const duplicateOutputError = findDuplicateParallelOutputPath({
1550
+ tasks,
1551
+ behaviors,
1552
+ paramsCwd: effectiveCwd,
1553
+ ctxCwd: ctx.cwd,
1554
+ worktreeSetup
1555
+ });
1556
+ if (duplicateOutputError) return buildParallelModeError(duplicateOutputError);
1557
+ for (let index = 0; index < tasks.length; index++) {
1558
+ const taskCwd = resolveParallelTaskCwd(tasks[index], effectiveCwd, worktreeSetup, index);
1559
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(behaviors[index]?.output, ctx.cwd, taskCwd);
1560
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(behaviors[index]?.outputMode, outputPath, `Parallel task ${index + 1} (${tasks[index].agent})`);
1561
+ if (validationError) return buildParallelModeError(validationError);
1562
+ }
1563
+
1564
+ const parallelProgressPrecreated = firstProgressIndex !== -1;
1565
+ if (parallelProgressPrecreated) (0, _settings.writeInitialProgressFile)(effectiveCwd);
1566
+
1567
+ if (params.context === "fork") {
1568
+ for (let i = 0; i < taskTexts.length; i++) {
1569
+ taskTexts[i] = (0, _types.wrapForkTask)(taskTexts[i]);
1570
+ }
1571
+ }
1572
+
1573
+ const results = await runForegroundParallelTasks({
1574
+ tasks,
1575
+ taskTexts,
1576
+ agents,
1577
+ ctx,
1578
+ intercomEvents: deps.pi.events,
1579
+ signal,
1580
+ runId,
1581
+ sessionDirForIndex,
1582
+ sessionFileForIndex,
1583
+ shareEnabled,
1584
+ artifactConfig,
1585
+ artifactsDir,
1586
+ maxOutput: params.maxOutput,
1587
+ paramsCwd: effectiveCwd,
1588
+ availableModels,
1589
+ modelOverrides,
1590
+ behaviors,
1591
+ firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
1592
+ controlConfig,
1593
+ onControlEvent,
1594
+ childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
1595
+ orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1596
+ foregroundControl,
1597
+ concurrencyLimit: parallelConcurrency,
1598
+ maxSubagentDepths,
1599
+ liveResults,
1600
+ liveProgress,
1601
+ onUpdate,
1602
+ worktreeSetup
1603
+ });
1604
+ for (let i = 0; i < results.length; i++) {
1605
+ const run = results[i];
1606
+ (0, _runHistory.recordRun)(run.agent, taskTexts[i], run.exitCode, run.progressSummary?.durationMs ?? 0);
1607
+ }
1608
+
1609
+ for (const result of results) {
1610
+ if (result.progress) allProgress.push(result.progress);
1611
+ if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
1612
+ }
1613
+
1614
+ const interrupted = results.find((result) => result.interrupted);
1615
+ const details = (0, _utils.compactForegroundDetails)({
1616
+ mode: "parallel",
1617
+ runId,
1618
+ results,
1619
+ progress: params.includeProgress ? allProgress : undefined,
1620
+ artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined
1621
+ });
1622
+ rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, results: details.results });
1623
+ if (interrupted) {
1624
+ return {
1625
+ content: [{ type: "text", text: `Parallel run paused after interrupt (${interrupted.agent}). Waiting for explicit next action.` }],
1626
+ details
1627
+ };
1628
+ }
1629
+ const detachedIndex = results.findIndex((result) => result.detached);
1630
+ const detached = detachedIndex >= 0 ? results[detachedIndex] : undefined;
1631
+ if (detached) {
1632
+ return {
1633
+ content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
1634
+ details
1635
+ };
1636
+ }
1637
+
1638
+ const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1639
+ pi: deps.pi,
1640
+ intercomBridge: data.intercomBridge,
1641
+ runId,
1642
+ mode: "parallel",
1643
+ details
1644
+ });
1645
+ if (intercomReceipt) {
1646
+ return {
1647
+ content: [{ type: "text", text: intercomReceipt.text }],
1648
+ details: intercomReceipt.details
1649
+ };
1650
+ }
1651
+
1652
+ const worktreeSuffix = buildParallelWorktreeSuffix(worktreeSetup, artifactsDir, tasks);
1653
+ const ok = results.filter((result) => result.exitCode === 0).length;
1654
+ const downgradeNote = backgroundRequestedWhileClarifying ? " (background requested, but clarify kept this run foreground)" : "";
1655
+ const aggregatedOutput = (0, _parallelUtils.aggregateParallelOutputs)(
1656
+ results.map((result) => ({
1657
+ agent: result.agent,
1658
+ output: result.truncation?.text || (0, _utils.getSingleResultOutput)(result),
1659
+ exitCode: result.exitCode,
1660
+ error: result.error
1661
+ })),
1662
+ (i, agent) => `=== Task ${i + 1}: ${agent} ===`
1663
+ );
1664
+
1665
+ const summary = `${ok}/${results.length} succeeded${downgradeNote}`;
1666
+ const fullContent = worktreeSuffix ?
1667
+ `${summary}\n\n${aggregatedOutput}\n\n${worktreeSuffix}` :
1668
+ `${summary}\n\n${aggregatedOutput}`;
1669
+
1670
+ return {
1671
+ content: [{ type: "text", text: fullContent }],
1672
+ details
1673
+ };
1674
+ } finally {
1675
+ if (worktreeSetup) (0, _worktree.cleanupWorktrees)(worktreeSetup);
1676
+ }
1677
+ }
1678
+
1679
+ async function runSinglePath(data, deps) {
1680
+ const {
1681
+ params,
1682
+ effectiveCwd,
1683
+ agents,
1684
+ ctx,
1685
+ signal,
1686
+ runId,
1687
+ sessionDirForIndex,
1688
+ sessionFileForIndex,
1689
+ shareEnabled,
1690
+ artifactConfig,
1691
+ artifactsDir,
1692
+ onUpdate,
1693
+ sessionRoot,
1694
+ controlConfig
1695
+ } = data;
1696
+ const onControlEvent = createForegroundControlNotifier(data, deps);
1697
+ const childIntercomTarget = data.intercomBridge.active ? (0, _intercomBridge.resolveSubagentIntercomTarget)(runId, params.agent, 0) : undefined;
1698
+ const allProgress = [];
1699
+ const allArtifactPaths = [];
1700
+ const agentConfig = agents.find((a) => a.name === params.agent);
1701
+ if (!agentConfig) {
1702
+ return {
1703
+ content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
1704
+ isError: true,
1705
+ details: { mode: "single", results: [] }
1706
+ };
1707
+ }
1708
+
1709
+ const currentProvider = ctx.model?.provider;
1710
+ const availableModels = ctx.modelRegistry.getAvailable().map(_modelInfo.toModelInfo);
1711
+ let task = params.task ?? "";
1712
+ let modelOverride = (0, _modelFallback.resolveModelCandidate)(
1713
+ params.model ?? agentConfig.model,
1714
+ availableModels,
1715
+ currentProvider
1716
+ );
1717
+ let skillOverride = (0, _skills.normalizeSkillInput)(params.skill);
1718
+ const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
1719
+ let effectiveOutput = rawOutput === true ? agentConfig.output : rawOutput;
1720
+ const effectiveOutputMode = params.outputMode ?? "inline";
1721
+ const currentMaxSubagentDepth = (0, _types.resolveCurrentMaxSubagentDepth)(deps.config.maxSubagentDepth);
1722
+ const maxSubagentDepth = (0, _types.resolveChildMaxSubagentDepth)(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
1723
+
1724
+ if (params.clarify === true && ctx.hasUI) {
1725
+ const behavior = (0, _settings.resolveStepBehavior)(agentConfig, { output: effectiveOutput, skills: skillOverride });
1726
+ const availableSkills = (0, _skills.discoverAvailableSkills)(effectiveCwd);
1727
+
1728
+ const result = await ctx.ui.custom(
1729
+ (tui, theme, _kb, done) =>
1730
+ new _chainClarify.ChainClarifyComponent(
1731
+ tui, theme,
1732
+ [agentConfig],
1733
+ [task],
1734
+ task,
1735
+ undefined,
1736
+ [behavior],
1737
+ availableModels,
1738
+ currentProvider,
1739
+ availableSkills,
1740
+ done,
1741
+ "single"
1742
+ ),
1743
+ { overlay: true, overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" } }
1744
+ );
1745
+
1746
+ if (!result || !result.confirmed) {
1747
+ return { content: [{ type: "text", text: "Cancelled" }], details: { mode: "single", results: [] } };
1748
+ }
1749
+
1750
+ task = result.templates[0];
1751
+ const override = result.behaviorOverrides[0];
1752
+ if (override?.model) modelOverride = override.model;
1753
+ if (override?.output !== undefined) effectiveOutput = override.output;
1754
+ if (override?.skills !== undefined) skillOverride = override.skills;
1755
+
1756
+ if (result.runInBackground) {
1757
+ if (!(0, _asyncExecution.isAsyncAvailable)()) {
1758
+ return {
1759
+ content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1760
+ isError: true,
1761
+ details: { mode: "single", results: [] }
1762
+ };
1763
+ }
1764
+ const id = (0, _nodeCrypto.randomUUID)();
1765
+ const asyncCtx = {
1766
+ pi: deps.pi,
1767
+ cwd: ctx.cwd,
1768
+ currentSessionId: deps.state.currentSessionId,
1769
+ currentModelProvider: ctx.model?.provider
1770
+ };
1771
+ return (0, _asyncExecution.executeAsyncSingle)(id, {
1772
+ agent: params.agent,
1773
+ task: params.context === "fork" ? (0, _types.wrapForkTask)(task) : task,
1774
+ agentConfig,
1775
+ ctx: asyncCtx,
1776
+ availableModels,
1777
+ cwd: effectiveCwd,
1778
+ maxOutput: params.maxOutput,
1779
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1780
+ artifactConfig,
1781
+ shareEnabled,
1782
+ sessionRoot,
1783
+ sessionFile: sessionFileForIndex(0),
1784
+ skills: skillOverride === false ? [] : skillOverride,
1785
+ output: effectiveOutput,
1786
+ outputMode: effectiveOutputMode,
1787
+ modelOverride,
1788
+ maxSubagentDepth,
1789
+ worktreeSetupHook: deps.config.worktreeSetupHook,
1790
+ worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
1791
+ controlConfig,
1792
+ controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1793
+ childIntercomTarget: data.intercomBridge.active ? (agent, index) => (0, _intercomBridge.resolveSubagentIntercomTarget)(id, agent, index) : undefined
1794
+ });
1795
+ }
1796
+ }
1797
+
1798
+ if (params.context === "fork") {
1799
+ task = (0, _types.wrapForkTask)(task);
1800
+ }
1801
+ const cleanTask = task;
1802
+ const outputPath = (0, _singleOutput.resolveSingleOutputPath)(effectiveOutput, ctx.cwd, effectiveCwd);
1803
+ const validationError = (0, _singleOutput.validateFileOnlyOutputMode)(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
1804
+ if (validationError) {
1805
+ return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
1806
+ }
1807
+ task = (0, _singleOutput.injectSingleOutputInstruction)(task, outputPath);
1808
+
1809
+ let effectiveSkills;
1810
+ if (skillOverride === false) {
1811
+ effectiveSkills = [];
1812
+ } else {
1813
+ effectiveSkills = skillOverride;
1814
+ }
1815
+ const interruptController = new AbortController();
1816
+ const foregroundControl = deps.state.foregroundControls.get(runId);
1817
+ if (foregroundControl) {
1818
+ foregroundControl.currentAgent = params.agent;
1819
+ foregroundControl.currentIndex = 0;
1820
+ foregroundControl.currentActivityState = undefined;
1821
+ foregroundControl.updatedAt = Date.now();
1822
+ foregroundControl.interrupt = () => {
1823
+ if (interruptController.signal.aborted) return false;
1824
+ interruptController.abort();
1825
+ foregroundControl.currentActivityState = undefined;
1826
+ foregroundControl.updatedAt = Date.now();
1827
+ return true;
1828
+ };
1829
+ }
1830
+
1831
+ const forwardSingleUpdate = onUpdate ?
1832
+ (update) => {
1833
+ if (foregroundControl) {
1834
+ const firstProgress = update.details?.progress?.[0];
1835
+ foregroundControl.currentAgent = params.agent;
1836
+ foregroundControl.currentIndex = firstProgress?.index ?? 0;
1837
+ foregroundControl.currentActivityState = firstProgress?.activityState;
1838
+ foregroundControl.lastActivityAt = firstProgress?.lastActivityAt;
1839
+ foregroundControl.currentTool = firstProgress?.currentTool;
1840
+ foregroundControl.currentToolStartedAt = firstProgress?.currentToolStartedAt;
1841
+ foregroundControl.currentPath = firstProgress?.currentPath;
1842
+ foregroundControl.turnCount = firstProgress?.turnCount;
1843
+ foregroundControl.tokens = firstProgress?.tokens;
1844
+ foregroundControl.toolCount = firstProgress?.toolCount;
1845
+ foregroundControl.updatedAt = Date.now();
1846
+ }
1847
+ onUpdate(update);
1848
+ } :
1849
+ undefined;
1850
+
1851
+ const r = await (0, _execution.runSync)(ctx.cwd, agents, params.agent, task, {
1852
+ cwd: effectiveCwd,
1853
+ signal,
1854
+ interruptSignal: interruptController.signal,
1855
+ allowIntercomDetach: agentConfig.systemPrompt?.includes(_intercomBridge.INTERCOM_BRIDGE_MARKER) === true,
1856
+ intercomEvents: deps.pi.events,
1857
+ runId,
1858
+ sessionDir: sessionDirForIndex(0),
1859
+ sessionFile: sessionFileForIndex(0),
1860
+ share: shareEnabled,
1861
+ artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1862
+ artifactConfig,
1863
+ maxOutput: params.maxOutput,
1864
+ outputPath,
1865
+ outputMode: effectiveOutputMode,
1866
+ maxSubagentDepth,
1867
+ onUpdate: forwardSingleUpdate,
1868
+ controlConfig,
1869
+ onControlEvent,
1870
+ intercomSessionName: childIntercomTarget,
1871
+ orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1872
+ index: 0,
1873
+ modelOverride,
1874
+ availableModels,
1875
+ preferredModelProvider: currentProvider,
1876
+ skills: effectiveSkills
1877
+ });
1878
+ if (foregroundControl?.currentIndex === 0) {
1879
+ foregroundControl.interrupt = undefined;
1880
+ foregroundControl.currentActivityState = r.progress?.activityState;
1881
+ foregroundControl.lastActivityAt = r.progress?.lastActivityAt;
1882
+ foregroundControl.currentTool = r.progress?.currentTool;
1883
+ foregroundControl.currentToolStartedAt = r.progress?.currentToolStartedAt;
1884
+ foregroundControl.currentPath = r.progress?.currentPath;
1885
+ foregroundControl.turnCount = r.progress?.turnCount;
1886
+ foregroundControl.tokens = r.progress?.tokens;
1887
+ foregroundControl.toolCount = r.progress?.toolCount;
1888
+ foregroundControl.updatedAt = Date.now();
1889
+ }
1890
+ (0, _runHistory.recordRun)(params.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
1891
+
1892
+ if (r.progress) allProgress.push(r.progress);
1893
+ if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
1894
+
1895
+ const fullOutput = (0, _utils.getSingleResultOutput)(r);
1896
+ const finalizedOutput = (0, _singleOutput.finalizeSingleOutput)({
1897
+ fullOutput,
1898
+ truncatedOutput: r.truncation?.text,
1899
+ outputPath,
1900
+ outputMode: r.outputMode,
1901
+ exitCode: r.exitCode,
1902
+ savedPath: r.savedOutputPath,
1903
+ outputReference: r.outputReference,
1904
+ saveError: r.outputSaveError
1905
+ });
1906
+ const details = (0, _utils.compactForegroundDetails)({
1907
+ mode: "single",
1908
+ runId,
1909
+ results: [r],
1910
+ progress: params.includeProgress ? allProgress : undefined,
1911
+ artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1912
+ truncation: r.truncation
1913
+ });
1914
+ rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, results: details.results });
1915
+
1916
+ if (!r.detached && !r.interrupted) {
1917
+ const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1918
+ pi: deps.pi,
1919
+ intercomBridge: data.intercomBridge,
1920
+ runId,
1921
+ mode: "single",
1922
+ details
1923
+ });
1924
+ if (intercomReceipt) {
1925
+ return {
1926
+ content: [{ type: "text", text: intercomReceipt.text }],
1927
+ details: intercomReceipt.details,
1928
+ ...(r.exitCode !== 0 ? { isError: true } : {})
1929
+ };
1930
+ }
1931
+ }
1932
+
1933
+ if (r.detached) {
1934
+ return {
1935
+ content: [{ type: "text", text: `Detached for intercom coordination: ${params.agent}. Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
1936
+ details
1937
+ };
1938
+ }
1939
+
1940
+ if (r.interrupted) {
1941
+ return {
1942
+ content: [{ type: "text", text: `Run paused after interrupt (${params.agent}). Waiting for explicit next action.` }],
1943
+ details
1944
+ };
1945
+ }
1946
+
1947
+ if (r.exitCode !== 0)
1948
+ return {
1949
+ content: [{ type: "text", text: r.error || "Failed" }],
1950
+ details,
1951
+ isError: true
1952
+ };
1953
+ return {
1954
+ content: [{ type: "text", text: finalizedOutput.displayOutput || "(no output)" }],
1955
+ details
1956
+ };
1957
+ }
1958
+
1959
+ function createSubagentExecutor(deps)
1960
+
1961
+
1962
+
1963
+
1964
+
1965
+
1966
+
1967
+ {
1968
+ const execute = async (
1969
+ _id,
1970
+ params,
1971
+ signal,
1972
+ onUpdate,
1973
+ ctx) =>
1974
+ {
1975
+ deps.state.baseCwd = ctx.cwd;
1976
+ deps.state.foregroundRuns ??= new Map();
1977
+ deps.state.foregroundControls ??= new Map();
1978
+ deps.state.lastForegroundControlId ??= null;
1979
+ const requestCwd = resolveRequestedCwd(ctx.cwd, params.cwd);
1980
+ const paramsWithResolvedCwd = params.cwd === undefined ? params : { ...params, cwd: requestCwd };
1981
+ if (params.action) {
1982
+ if (params.action === "doctor") {
1983
+ let currentSessionFile = null;
1984
+ let currentSessionId = deps.state.currentSessionId;
1985
+ let sessionError;
1986
+ try {
1987
+ currentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
1988
+ currentSessionId = ctx.sessionManager.getSessionId();
1989
+ } catch (error) {
1990
+ sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
1991
+ }
1992
+ let orchestratorTarget;
1993
+ try {
1994
+ orchestratorTarget = (0, _intercomBridge.resolveIntercomSessionTarget)(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
1995
+ } catch {}
1996
+ return {
1997
+ content: [{
1998
+ type: "text",
1999
+ text: (0, _doctor.buildDoctorReport)({
2000
+ cwd: requestCwd,
2001
+ config: deps.config,
2002
+ state: deps.state,
2003
+ context: paramsWithResolvedCwd.context,
2004
+ requestedSessionDir: paramsWithResolvedCwd.sessionDir,
2005
+ currentSessionFile,
2006
+ currentSessionId,
2007
+ orchestratorTarget,
2008
+ sessionError,
2009
+ expandTilde: deps.expandTilde
2010
+ })
2011
+ }],
2012
+ details: { mode: "management", results: [] }
2013
+ };
2014
+ }
2015
+ if (params.action === "status") {
2016
+ const foreground = getForegroundControl(deps.state, paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId);
2017
+ if (foreground) return foregroundStatusResult(foreground);
2018
+ return (0, _runStatus.inspectSubagentStatus)(paramsWithResolvedCwd);
2019
+ }
2020
+ if (params.action === "resume") {
2021
+ return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
2022
+ }
2023
+ if (params.action === "interrupt") {
2024
+ const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
2025
+ const foreground = getForegroundControl(deps.state, targetRunId);
2026
+ if (foreground?.interrupt) {
2027
+ const interrupted = foreground.interrupt();
2028
+ if (interrupted) {
2029
+ foreground.updatedAt = Date.now();
2030
+ foreground.currentActivityState = undefined;
2031
+ return {
2032
+ content: [{ type: "text", text: `Interrupt requested for foreground run ${foreground.runId}.` }],
2033
+ details: { mode: "management", results: [] }
2034
+ };
2035
+ }
2036
+ return {
2037
+ content: [{ type: "text", text: `Foreground run ${foreground.runId} has no active child step to interrupt.` }],
2038
+ isError: true,
2039
+ details: { mode: "management", results: [] }
2040
+ };
2041
+ }
2042
+ const asyncInterruptResult = interruptAsyncRun(deps.state, targetRunId);
2043
+ if (asyncInterruptResult) return asyncInterruptResult;
2044
+ return {
2045
+ content: [{ type: "text", text: "No interrupt-capable run found in this session." }],
2046
+ isError: true,
2047
+ details: { mode: "management", results: [] }
2048
+ };
2049
+ }
2050
+ if (!_types.SUBAGENT_ACTIONS.includes(params.action)) {
2051
+ return {
2052
+ content: [{ type: "text", text: `Unknown action: ${params.action}. Valid: ${_types.SUBAGENT_ACTIONS.join(", ")}` }],
2053
+ isError: true,
2054
+ details: { mode: "management", results: [] }
2055
+ };
2056
+ }
2057
+ return (0, _agentManagement.handleManagementAction)(params.action, paramsWithResolvedCwd, { ...ctx, cwd: requestCwd });
2058
+ }
2059
+
2060
+ const { blocked, depth, maxDepth } = (0, _types.checkSubagentDepth)(deps.config.maxSubagentDepth);
2061
+ if (blocked) {
2062
+ return {
2063
+ content: [
2064
+ {
2065
+ type: "text",
2066
+ text:
2067
+ `Nested subagent call blocked (depth=${depth}, max=${maxDepth}). ` +
2068
+ "You are running at the maximum subagent nesting depth. " +
2069
+ "Complete your current task directly without delegating to further subagents."
2070
+ }],
2071
+
2072
+ isError: true,
2073
+ details: { mode: "single", results: [] }
2074
+ };
2075
+ }
2076
+
2077
+ const normalized = normalizeRepeatedParallelCounts(paramsWithResolvedCwd);
2078
+ if (normalized.error) return normalized.error;
2079
+ const normalizedParams = normalized.params;
2080
+
2081
+ let effectiveParams = (0, _topLevelAsync.applyForceTopLevelAsyncOverride)(
2082
+ normalizedParams,
2083
+ depth,
2084
+ deps.config.forceTopLevelAsync === true
2085
+ );
2086
+
2087
+ const scope = (0, _agentScope.resolveExecutionAgentScope)(effectiveParams.agentScope);
2088
+ const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
2089
+ const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
2090
+ deps.state.currentSessionId = (0, _sessionIdentity.resolveCurrentSessionId)(ctx.sessionManager);
2091
+ const discoveredAgents = deps.discoverAgents(effectiveCwd, scope).agents;
2092
+ effectiveParams = applyAgentDefaultContext(effectiveParams, discoveredAgents);
2093
+ const sessionName = (0, _intercomBridge.resolveIntercomSessionTarget)(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
2094
+ const intercomBridge = (0, _intercomBridge.resolveIntercomBridge)({
2095
+ config: deps.config.intercomBridge,
2096
+ context: effectiveParams.context,
2097
+ orchestratorTarget: sessionName,
2098
+ cwd: effectiveCwd
2099
+ });
2100
+ const agents = intercomBridge.active ?
2101
+ discoveredAgents.map((agent) => (0, _intercomBridge.applyIntercomBridgeToAgent)(agent, intercomBridge)) :
2102
+ discoveredAgents;
2103
+ const runId = (0, _nodeCrypto.randomUUID)().slice(0, 8);
2104
+ const shareEnabled = effectiveParams.share === true;
2105
+ const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
2106
+ const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
2107
+ const hasSingle = !hasChain && !hasTasks && Boolean(effectiveParams.agent);
2108
+ const allowClarifyTaskPrompt = hasChain &&
2109
+ effectiveParams.clarify === true &&
2110
+ ctx.hasUI &&
2111
+ !(effectiveParams.chain?.some(_settings.isParallelStep) ?? false);
2112
+
2113
+ const validationError = validateExecutionInput(
2114
+ effectiveParams,
2115
+ agents,
2116
+ hasChain,
2117
+ hasTasks,
2118
+ hasSingle,
2119
+ allowClarifyTaskPrompt
2120
+ );
2121
+ if (validationError) return validationError;
2122
+
2123
+ let sessionFileForIndex = () => undefined;
2124
+ try {
2125
+ sessionFileForIndex = (0, _forkContext.createForkContextResolver)(ctx.sessionManager, effectiveParams.context).sessionFileForIndex;
2126
+ } catch (error) {
2127
+ return toExecutionErrorResult(effectiveParams, error);
2128
+ }
2129
+ const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
2130
+ const backgroundRequestedWhileClarifying = hasTasks && requestedAsync && effectiveParams.clarify === true;
2131
+ const effectiveAsync = requestedAsync && (
2132
+ hasChain ? effectiveParams.clarify === false : effectiveParams.clarify !== true);
2133
+ const controlConfig = (0, _subagentControl.resolveControlConfig)(deps.config.control, effectiveParams.control);
2134
+
2135
+ const artifactConfig = {
2136
+ ..._types.DEFAULT_ARTIFACT_CONFIG,
2137
+ enabled: effectiveParams.artifacts !== false
2138
+ };
2139
+ const artifactsDir = effectiveAsync ? deps.tempArtifactsDir : (0, _artifacts.getArtifactsDir)(parentSessionFile);
2140
+
2141
+ let sessionRoot;
2142
+ if (effectiveParams.sessionDir) {
2143
+ sessionRoot = path.resolve(deps.expandTilde(effectiveParams.sessionDir));
2144
+ } else {
2145
+ const baseSessionRoot = deps.config.defaultSessionDir ?
2146
+ path.resolve(deps.expandTilde(deps.config.defaultSessionDir)) :
2147
+ deps.getSubagentSessionRoot(parentSessionFile);
2148
+ sessionRoot = path.join(baseSessionRoot, runId);
2149
+ }
2150
+ try {
2151
+ fs.mkdirSync(sessionRoot, { recursive: true });
2152
+ } catch (error) {
2153
+ const message = error instanceof Error ? error.message : String(error);
2154
+ return toExecutionErrorResult(
2155
+ effectiveParams,
2156
+ new Error(`Failed to create session directory '${sessionRoot}': ${message}`)
2157
+ );
2158
+ }
2159
+ const sessionDirForIndex = (idx) =>
2160
+ path.join(sessionRoot, `run-${idx ?? 0}`);
2161
+ const childSessionFileForIndex = (idx) =>
2162
+ sessionFileForIndex(idx) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
2163
+
2164
+ const onUpdateWithContext = onUpdate ?
2165
+ (r) => onUpdate(withForkContext(r, effectiveParams.context)) :
2166
+ undefined;
2167
+
2168
+ const execData = {
2169
+ params: effectiveParams,
2170
+ effectiveCwd,
2171
+ ctx,
2172
+ signal,
2173
+ onUpdate: onUpdateWithContext,
2174
+ agents,
2175
+ runId,
2176
+ shareEnabled,
2177
+ sessionRoot,
2178
+ sessionDirForIndex,
2179
+ sessionFileForIndex: childSessionFileForIndex,
2180
+ artifactConfig,
2181
+ artifactsDir,
2182
+ backgroundRequestedWhileClarifying,
2183
+ effectiveAsync,
2184
+ controlConfig,
2185
+ intercomBridge
2186
+ };
2187
+
2188
+ const foregroundMode = hasChain ? "chain" : hasTasks ? "parallel" : "single";
2189
+ const foregroundControl = effectiveAsync ?
2190
+ undefined :
2191
+ {
2192
+ runId,
2193
+ mode: foregroundMode,
2194
+ startedAt: Date.now(),
2195
+ updatedAt: Date.now(),
2196
+ currentAgent: undefined,
2197
+ currentIndex: undefined,
2198
+ currentActivityState: undefined,
2199
+ interrupt: undefined
2200
+ };
2201
+ if (foregroundControl) {
2202
+ deps.state.foregroundControls.set(runId, foregroundControl);
2203
+ deps.state.lastForegroundControlId = runId;
2204
+ }
2205
+
2206
+ try {
2207
+ const asyncResult = runAsyncPath(execData, deps);
2208
+ if (asyncResult) return withForkContext(asyncResult, effectiveParams.context);
2209
+ if (hasChain && effectiveParams.chain) return withForkContext(await runChainPath(execData, deps), effectiveParams.context);
2210
+ if (hasTasks && effectiveParams.tasks) return withForkContext(await runParallelPath(execData, deps), effectiveParams.context);
2211
+ if (hasSingle) return withForkContext(await runSinglePath(execData, deps), effectiveParams.context);
2212
+ } catch (error) {
2213
+ return toExecutionErrorResult(effectiveParams, error);
2214
+ } finally {
2215
+ if (foregroundControl) {
2216
+ (0, _controlNotices.clearPendingForegroundControlNotices)(deps.state, runId);
2217
+ deps.state.foregroundControls.delete(runId);
2218
+ if (deps.state.lastForegroundControlId === runId) {
2219
+ deps.state.lastForegroundControlId = null;
2220
+ }
2221
+ }
2222
+ }
2223
+
2224
+ return withForkContext({
2225
+ content: [{ type: "text", text: "Invalid params" }],
2226
+ isError: true,
2227
+ details: { mode: "single", results: [] }
2228
+ }, effectiveParams.context);
2229
+ };
2230
+
2231
+ return { execute };
2232
+ } /* v9-c7a1289a50ce9121 */
pip-tmp/jiti/intercom-intercom-bridge.88630197.mjs ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.INTERCOM_BRIDGE_MARKER = void 0;exports.applyIntercomBridgeToAgent = applyIntercomBridgeToAgent;exports.diagnoseIntercomBridge = diagnoseIntercomBridge;exports.resolveIntercomBridge = resolveIntercomBridge;exports.resolveIntercomBridgeMode = resolveIntercomBridgeMode;exports.resolveIntercomSessionTarget = resolveIntercomSessionTarget;exports.resolveSubagentIntercomTarget = resolveSubagentIntercomTarget;var _nodeChild_process = await jitiImport("node:child_process");
2
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
3
+ var os = _interopRequireWildcard(await jitiImport("node:os"));
4
+ var path = _interopRequireWildcard(await jitiImport("node:path"));function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
5
+
6
+
7
+
8
+ const PI_INTERCOM_PACKAGE_NAME = "pi-intercom";
9
+ const CONFIG_DIR = ".pi";
10
+
11
+ function defaultAgentDir() {
12
+ return path.join(os.homedir(), ".pi", "agent");
13
+ }
14
+
15
+ function defaultIntercomExtensionDir(agentDir = defaultAgentDir()) {
16
+ return path.join(agentDir, "extensions", PI_INTERCOM_PACKAGE_NAME);
17
+ }
18
+
19
+ function defaultIntercomConfigPath(agentDir = defaultAgentDir()) {
20
+ return path.join(agentDir, "intercom", "config.json");
21
+ }
22
+
23
+ function defaultSubagentConfigDir(agentDir = defaultAgentDir()) {
24
+ return path.join(agentDir, "extensions", "subagent");
25
+ }
26
+
27
+ const DEFAULT_INTERCOM_TARGET_PREFIX = "subagent-chat";
28
+ const INTERCOM_BRIDGE_MARKER = exports.INTERCOM_BRIDGE_MARKER = "Intercom orchestration channel:";
29
+ const DEFAULT_INTERCOM_BRIDGE_TEMPLATE = `The inherited thread is reference-only. Do not continue that conversation or send questions, status updates, or completion handoffs to the supervisor in normal assistant text.
30
+
31
+ Use contact_supervisor first. It resolves the supervisor session "{orchestratorTarget}" and run metadata automatically.
32
+ - Need a decision, blocked, approval, or product/API/scope ambiguity: contact_supervisor({ reason: "need_decision", message: "<question>" })
33
+ - After contact_supervisor with reason "need_decision", stay alive and continue only after the reply arrives. Do not finish your final response with a choose-one question.
34
+ - Do not ask for clarification when the only conflict is review-only/no-edit versus progress-writing or artifact-writing instructions. Review-only/no-edit wins; leave files unchanged and mention the conflict in your final result only if it matters.
35
+ - Meaningful progress or unexpected discoveries that change the plan: contact_supervisor({ reason: "progress_update", message: "UPDATE: <summary>" })
36
+ - Generic intercom is lower-level plumbing/fallback only: intercom({ action: "ask", to: "{orchestratorTarget}", message: "<question>" })
37
+
38
+ Do not use contact_supervisor or intercom for routine completion handoffs. If no coordination is needed, return a focused task result.`;
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+ function resolveIntercomSessionTarget(sessionName, sessionId) {
74
+ const trimmedName = sessionName?.trim();
75
+ if (trimmedName) return trimmedName;
76
+ const normalizedSessionId = sessionId.startsWith("session-") ? sessionId.slice("session-".length) : sessionId;
77
+ return `${DEFAULT_INTERCOM_TARGET_PREFIX}-${normalizedSessionId.slice(0, 8)}`;
78
+ }
79
+
80
+ function sanitizeIntercomTargetPart(value) {
81
+ return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
82
+ }
83
+
84
+ function resolveSubagentIntercomTarget(runId, agent, index) {
85
+ const stepSuffix = index !== undefined ? `-${index + 1}` : "";
86
+ return `subagent-${sanitizeIntercomTargetPart(agent)}-${sanitizeIntercomTargetPart(runId)}${stepSuffix}`;
87
+ }
88
+
89
+ function resolveIntercomBridgeMode(value) {
90
+ if (value === "off" || value === "always" || value === "fork-only") return value;
91
+ return "always";
92
+ }
93
+
94
+ function resolveIntercomBridgeConfig(value) {
95
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
96
+ return {
97
+ mode: "always",
98
+ instructionFile: ""
99
+ };
100
+ }
101
+ return {
102
+ mode: resolveIntercomBridgeMode(value.mode),
103
+ instructionFile: typeof value.instructionFile === "string" ? value.instructionFile : ""
104
+ };
105
+ }
106
+
107
+ function intercomConfigStatus(configPath) {
108
+ if (!fs.existsSync(configPath)) return { enabled: true };
109
+ try {
110
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
111
+ return { enabled: parsed.enabled !== false };
112
+ } catch (error) {
113
+ return { enabled: true, error };
114
+ }
115
+ }
116
+
117
+ function readJsonBestEffort(filePath) {
118
+ try {
119
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
120
+ } catch (error) {
121
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
122
+ if (code !== "ENOENT") console.warn(`Failed to read JSON from '${filePath}'.`, error);
123
+ return null;
124
+ }
125
+ }
126
+
127
+ function packageHasPiExtension(packageRoot) {
128
+ if (!fs.existsSync(packageRoot)) return false;
129
+ const pkg = readJsonBestEffort(path.join(packageRoot, "package.json"));
130
+ if (pkg && typeof pkg === "object" && !Array.isArray(pkg)) {
131
+ const pi = pkg.pi;
132
+ if (pi && typeof pi === "object" && !Array.isArray(pi)) {
133
+ const extensions = pi.extensions;
134
+ return Array.isArray(extensions) && extensions.some((entry) => typeof entry === "string" && entry.trim() !== "");
135
+ }
136
+ }
137
+ return fs.existsSync(path.join(packageRoot, "extensions"));
138
+ }
139
+
140
+ function isSafePackagePath(value) {
141
+ return value.length > 0 &&
142
+ !path.isAbsolute(value) &&
143
+ value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
144
+ }
145
+
146
+ function parseNpmPackageName(source) {
147
+ const spec = source.slice(4).trim();
148
+ if (!spec) return undefined;
149
+ const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
150
+ const packageName = match?.[1] ?? spec;
151
+ return isSafePackagePath(packageName) ? packageName : undefined;
152
+ }
153
+
154
+ function packageEntrySource(entry) {
155
+ if (typeof entry === "string") return entry;
156
+ if (entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.source === "string") {
157
+ return entry.source;
158
+ }
159
+ return undefined;
160
+ }
161
+
162
+ function packageEntryAllowsExtensions(entry) {
163
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
164
+ const extensions = entry.extensions;
165
+ return !Array.isArray(extensions) || extensions.length > 0;
166
+ }
167
+
168
+ function findNearestProjectConfigDir(cwd) {
169
+ let current = path.resolve(cwd);
170
+ while (true) {
171
+ const configDir = path.join(current, CONFIG_DIR);
172
+ if (fs.existsSync(path.join(configDir, "settings.json"))) return configDir;
173
+ const parent = path.dirname(current);
174
+ if (parent === current) return undefined;
175
+ current = parent;
176
+ }
177
+ }
178
+
179
+ let cachedGlobalNpmRoot;
180
+
181
+ function getGlobalNpmRoot() {
182
+ if (cachedGlobalNpmRoot !== undefined) return cachedGlobalNpmRoot;
183
+ try {
184
+ cachedGlobalNpmRoot = (0, _nodeChild_process.execSync)("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
185
+ return cachedGlobalNpmRoot;
186
+ } catch {
187
+ cachedGlobalNpmRoot = null;
188
+ return null;
189
+ }
190
+ }
191
+
192
+ function configuredPiIntercomPackageDir(input, agentDir) {
193
+ const cwd = path.resolve(input.cwd ?? process.cwd());
194
+ const projectConfigDir = findNearestProjectConfigDir(cwd);
195
+ const settingsFiles = [
196
+ ...(projectConfigDir ? [{ file: path.join(projectConfigDir, "settings.json"), configDir: projectConfigDir, scope: "project" }] : []),
197
+ { file: path.join(agentDir, "settings.json"), configDir: agentDir, scope: "user" }];
198
+
199
+ const globalNpmRoot = input.globalNpmRoot === undefined ? getGlobalNpmRoot() : input.globalNpmRoot;
200
+
201
+ for (const { file, configDir, scope } of settingsFiles) {
202
+ const settings = readJsonBestEffort(file);
203
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) continue;
204
+ const packages = settings.packages;
205
+ if (!Array.isArray(packages)) continue;
206
+
207
+ for (const entry of packages) {
208
+ if (!packageEntryAllowsExtensions(entry)) continue;
209
+ const source = packageEntrySource(entry)?.trim();
210
+ if (!source?.startsWith("npm:")) continue;
211
+ const packageName = parseNpmPackageName(source);
212
+ if (packageName !== PI_INTERCOM_PACKAGE_NAME) continue;
213
+ const candidates = scope === "project" ?
214
+ [path.join(configDir, "npm", "node_modules", packageName)] :
215
+ [
216
+ ...(globalNpmRoot ? [path.join(globalNpmRoot, packageName)] : []),
217
+ path.join(agentDir, "npm", "node_modules", packageName)];
218
+
219
+ const packageRoot = candidates.find(packageHasPiExtension);
220
+ if (packageRoot) return path.resolve(packageRoot);
221
+ }
222
+ }
223
+ return undefined;
224
+ }
225
+
226
+ function resolveIntercomExtensionDir(input, agentDir) {
227
+ const legacyDir = path.resolve(input.extensionDir ?? defaultIntercomExtensionDir(agentDir));
228
+ if (fs.existsSync(legacyDir)) return legacyDir;
229
+ return configuredPiIntercomPackageDir(input, agentDir) ?? legacyDir;
230
+ }
231
+
232
+ function extensionSandboxAllowsIntercom(extensions, extensionDir) {
233
+ if (extensions === undefined) return true;
234
+
235
+ const intercomDir = path.resolve(extensionDir).replaceAll("\\", "/").toLowerCase();
236
+ for (const entry of extensions) {
237
+ const normalized = entry.trim().replaceAll("\\", "/").toLowerCase();
238
+ if (normalized === "pi-intercom") return true;
239
+ if (normalized === intercomDir) return true;
240
+ if (normalized.startsWith(`${intercomDir}/`)) return true;
241
+ if (normalized.endsWith("/pi-intercom")) return true;
242
+ if (normalized.includes("/pi-intercom/")) return true;
243
+ }
244
+ return false;
245
+ }
246
+
247
+ function expandTilde(filePath) {
248
+ return filePath.startsWith("~/") ? path.join(os.homedir(), filePath.slice(2)) : filePath;
249
+ }
250
+
251
+ function resolveInstructionTemplate(instructionFile, settingsDir) {
252
+ if (!instructionFile) return DEFAULT_INTERCOM_BRIDGE_TEMPLATE;
253
+ const expandedPath = expandTilde(instructionFile);
254
+ const resolvedPath = path.isAbsolute(expandedPath) ?
255
+ expandedPath :
256
+ path.resolve(settingsDir, expandedPath);
257
+ try {
258
+ return fs.readFileSync(resolvedPath, "utf-8");
259
+ } catch (error) {
260
+ console.warn(`Failed to read intercom bridge instructionFile at '${resolvedPath}'. Using default instructions.`, error);
261
+ return DEFAULT_INTERCOM_BRIDGE_TEMPLATE;
262
+ }
263
+ }
264
+
265
+ function buildIntercomBridgeInstruction(orchestratorTarget, template) {
266
+ const instruction = template.replaceAll("{orchestratorTarget}", orchestratorTarget).trim();
267
+ if (instruction.startsWith(INTERCOM_BRIDGE_MARKER)) return instruction;
268
+ return `${INTERCOM_BRIDGE_MARKER}
269
+ ${instruction}`;
270
+ }
271
+
272
+ function diagnoseIntercomBridge(input) {
273
+ const config = resolveIntercomBridgeConfig(input.config);
274
+ const mode = config.mode;
275
+ const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
276
+ const extensionDir = resolveIntercomExtensionDir(input, agentDir);
277
+ const orchestratorTarget = input.orchestratorTarget?.trim();
278
+ const configPath = path.resolve(input.configPath ?? defaultIntercomConfigPath(agentDir));
279
+ const wantsIntercom = mode !== "off" && !(mode === "fork-only" && input.context !== "fork");
280
+ const piIntercomAvailable = fs.existsSync(extensionDir);
281
+ let configStatus;
282
+ let reason;
283
+ if (mode === "off") reason = "bridge mode is off";else
284
+ if (mode === "fork-only" && input.context !== "fork") reason = "bridge mode is fork-only and context is not fork";else
285
+ if (!orchestratorTarget) reason = "orchestrator target is not available";else
286
+ if (!piIntercomAvailable) reason = "pi-intercom extension was not found";else
287
+ {
288
+ configStatus = intercomConfigStatus(configPath);
289
+ if (!configStatus.enabled) reason = "intercom config is disabled";
290
+ }
291
+ let intercomConfigError;
292
+ if (configStatus?.error) {
293
+ const error = configStatus.error;
294
+ intercomConfigError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
295
+ }
296
+
297
+ return {
298
+ active: reason === undefined,
299
+ mode,
300
+ wantsIntercom,
301
+ piIntercomAvailable,
302
+ extensionDir,
303
+ configPath,
304
+ ...(orchestratorTarget ? { orchestratorTarget } : {}),
305
+ ...(reason ? { reason } : {}),
306
+ ...(configStatus ? { intercomConfigEnabled: configStatus.enabled } : {}),
307
+ ...(intercomConfigError ? { intercomConfigError } : {})
308
+ };
309
+ }
310
+
311
+ function resolveIntercomBridge(input) {
312
+ const config = resolveIntercomBridgeConfig(input.config);
313
+ const mode = config.mode;
314
+ const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
315
+ const extensionDir = resolveIntercomExtensionDir(input, agentDir);
316
+ const orchestratorTarget = input.orchestratorTarget?.trim();
317
+ const settingsDir = path.resolve(input.settingsDir ?? defaultSubagentConfigDir(agentDir));
318
+ const defaultInstruction = buildIntercomBridgeInstruction(
319
+ orchestratorTarget || "{orchestratorTarget}",
320
+ DEFAULT_INTERCOM_BRIDGE_TEMPLATE
321
+ );
322
+
323
+ if (mode === "off") {
324
+ return { active: false, mode, extensionDir, instruction: defaultInstruction };
325
+ }
326
+ if (mode === "fork-only" && input.context !== "fork") {
327
+ return { active: false, mode, extensionDir, instruction: defaultInstruction };
328
+ }
329
+ if (!orchestratorTarget) {
330
+ return { active: false, mode, extensionDir, instruction: defaultInstruction };
331
+ }
332
+ if (!fs.existsSync(extensionDir)) {
333
+ return { active: false, mode, extensionDir, instruction: defaultInstruction };
334
+ }
335
+
336
+ const configPath = path.resolve(input.configPath ?? defaultIntercomConfigPath(agentDir));
337
+ const intercomStatus = intercomConfigStatus(configPath);
338
+ if (intercomStatus.error) console.warn(`Failed to parse intercom config at '${configPath}'. Assuming enabled.`, intercomStatus.error);
339
+ if (!intercomStatus.enabled) {
340
+ return { active: false, mode, extensionDir, instruction: defaultInstruction };
341
+ }
342
+
343
+ const instruction = buildIntercomBridgeInstruction(
344
+ orchestratorTarget,
345
+ resolveInstructionTemplate(config.instructionFile, settingsDir)
346
+ );
347
+
348
+ return {
349
+ active: true,
350
+ mode,
351
+ orchestratorTarget,
352
+ extensionDir,
353
+ instruction
354
+ };
355
+ }
356
+
357
+ function applyIntercomBridgeToAgent(agent, bridge) {
358
+ if (!bridge.active || !bridge.orchestratorTarget) return agent;
359
+ if (!extensionSandboxAllowsIntercom(agent.extensions, bridge.extensionDir)) return agent;
360
+
361
+ const bridgeTools = ["intercom", "contact_supervisor"];
362
+ const tools = agent.tools ?
363
+ [...agent.tools, ...bridgeTools.filter((tool) => !agent.tools?.includes(tool))] :
364
+ agent.tools;
365
+ const instruction = bridge.instruction;
366
+ const trimmedPrompt = agent.systemPrompt?.trim() || "";
367
+ const systemPrompt = trimmedPrompt.includes(INTERCOM_BRIDGE_MARKER) ?
368
+ trimmedPrompt :
369
+ trimmedPrompt ?
370
+ `${trimmedPrompt}\n\n${instruction}` :
371
+ instruction;
372
+
373
+ if (tools === agent.tools && systemPrompt === agent.systemPrompt) return agent;
374
+ return {
375
+ ...agent,
376
+ tools,
377
+ systemPrompt
378
+ };
379
+ } /* v9-8debbb1836ab02f2 */
pip-tmp/jiti/intercom-result-intercom.32a45520.mjs ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.buildSubagentResultIntercomPayload = buildSubagentResultIntercomPayload;exports.deliverSubagentIntercomMessageEvent = deliverSubagentIntercomMessageEvent;exports.deliverSubagentResultIntercomEvent = deliverSubagentResultIntercomEvent;exports.formatSubagentResultReceipt = formatSubagentResultReceipt;exports.resolveSubagentResultStatus = resolveSubagentResultStatus;exports.stripDetailsOutputsForIntercomReceipt = stripDetailsOutputsForIntercomReceipt;var _nodeCrypto = await jitiImport("node:crypto");
2
+ var fs = _interopRequireWildcard(await jitiImport("node:fs"));
3
+ var _types = await jitiImport("../shared/types.ts");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+ function resolveSubagentResultStatus(input)
16
+
17
+
18
+
19
+
20
+
21
+ {
22
+ if (input.detached) return "detached";
23
+ if (input.interrupted || input.state === "paused") return "paused";
24
+ if (typeof input.success === "boolean") return input.success ? "completed" : "failed";
25
+ if (input.state === "complete") return "completed";
26
+ if (input.state === "failed") return "failed";
27
+ if (typeof input.exitCode === "number") return input.exitCode === 0 ? "completed" : "failed";
28
+ return "failed";
29
+ }
30
+
31
+ function countStatuses(children) {
32
+ const counts = {
33
+ completed: 0,
34
+ failed: 0,
35
+ paused: 0,
36
+ detached: 0
37
+ };
38
+ for (const child of children) {
39
+ counts[child.status] += 1;
40
+ }
41
+ return counts;
42
+ }
43
+
44
+ function formatStatusCounts(counts) {
45
+ const parts = [
46
+ counts.completed ? `${counts.completed} completed` : undefined,
47
+ counts.failed ? `${counts.failed} failed` : undefined,
48
+ counts.paused ? `${counts.paused} paused` : undefined,
49
+ counts.detached ? `${counts.detached} detached` : undefined].
50
+ filter((part) => Boolean(part));
51
+ return parts.length ? parts.join(", ") : "0 results";
52
+ }
53
+
54
+ function resolveGroupedStatus(children) {
55
+ const counts = countStatuses(children);
56
+ if (counts.failed > 0) return "failed";
57
+ if (counts.paused > 0) return "paused";
58
+ if (counts.completed > 0) return "completed";
59
+ if (counts.detached > 0) return "detached";
60
+ return "failed";
61
+ }
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+ function asyncResumeGuidance(input)
75
+
76
+
77
+
78
+ {
79
+ if (input.source !== "async" || !input.asyncId) return undefined;
80
+ const resumable = input.children.filter((child) => typeof child.sessionPath === "string" && fs.existsSync(child.sessionPath));
81
+ if (input.children.length === 1 && resumable.length === 1) {
82
+ return `Revive: subagent({ action: "resume", id: "${input.asyncId}", message: "..." })`;
83
+ }
84
+ if (resumable.length > 0) {
85
+ const firstIndex = resumable[0]?.index ?? input.children.indexOf(resumable[0]);
86
+ return `Revive child: subagent({ action: "resume", id: "${input.asyncId}", index: ${firstIndex}, message: "..." })`;
87
+ }
88
+ return "Resume: unavailable; no child session file was persisted.";
89
+ }
90
+
91
+ function formatSubagentResultIntercomMessage(input)
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ {
101
+ const counts = countStatuses(input.children);
102
+ const lines = [
103
+ "subagent results",
104
+ "",
105
+ `Run: ${input.runId}`,
106
+ `Mode: ${input.mode}`,
107
+ `Status: ${input.status}`,
108
+ `Children: ${formatStatusCounts(counts)}`];
109
+
110
+ if (input.mode === "chain" && typeof input.chainSteps === "number") {
111
+ lines.push(`Chain steps: ${input.chainSteps}`);
112
+ }
113
+ if (input.asyncId) lines.push(`Async id: ${input.asyncId}`);
114
+ if (input.asyncDir) lines.push(`Async dir: ${input.asyncDir}`);
115
+ const resumeGuidance = asyncResumeGuidance(input);
116
+ if (resumeGuidance) lines.push(resumeGuidance);
117
+ if (input.children.some((child) => child.intercomTarget)) {
118
+ lines.push("");
119
+ lines.push(input.source === "async" ?
120
+ "Previous intercom targets below identify child sessions used while they were running. Inspect artifacts or session logs if resume is unavailable." :
121
+ "Intercom targets below identify child sessions used while they were running; completed child sessions may no longer be reachable. Inspect artifacts or session logs for follow-up.");
122
+ }
123
+
124
+ for (let index = 0; index < input.children.length; index++) {
125
+ const child = input.children[index];
126
+ lines.push("");
127
+ lines.push(`${index + 1}. ${child.agent} — ${child.status}`);
128
+ if (child.intercomTarget) lines.push(`${input.source === "async" ? "Previous intercom target" : "Run intercom target"}: ${child.intercomTarget}`);
129
+ if (child.artifactPath) lines.push(`Output artifact: ${child.artifactPath}`);
130
+ if (child.sessionPath) lines.push(`Session: ${child.sessionPath}`);
131
+ lines.push("Summary:");
132
+ lines.push(child.summary);
133
+ }
134
+
135
+ return lines.join("\n");
136
+ }
137
+
138
+ function buildSubagentResultIntercomPayload(input) {
139
+ const children = input.children.map((child) => ({
140
+ ...child,
141
+ summary: child.summary.trim() || "(no output)"
142
+ }));
143
+ const status = resolveGroupedStatus(children);
144
+ const summary = formatStatusCounts(countStatuses(children));
145
+ const firstChild = children[0];
146
+ const payload = {
147
+ to: input.to,
148
+ runId: input.runId,
149
+ mode: input.mode,
150
+ status,
151
+ summary,
152
+ source: input.source,
153
+ children,
154
+ ...(input.asyncId ? { asyncId: input.asyncId } : {}),
155
+ ...(input.asyncDir ? { asyncDir: input.asyncDir } : {}),
156
+ ...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {}),
157
+ ...(firstChild?.agent ? { agent: firstChild.agent } : {}),
158
+ ...(firstChild?.index !== undefined ? { index: firstChild.index } : {}),
159
+ ...(firstChild?.artifactPath ? { artifactPath: firstChild.artifactPath } : {}),
160
+ ...(firstChild?.sessionPath ? { sessionPath: firstChild.sessionPath } : {}),
161
+ message: ""
162
+ };
163
+ payload.message = formatSubagentResultIntercomMessage(payload);
164
+ return payload;
165
+ }
166
+
167
+ async function deliverSubagentResultIntercomEvent(
168
+ events,
169
+ payload,
170
+ timeoutMs = 500)
171
+ {
172
+ return deliverSubagentIntercomMessageEvent(events, payload.to, payload.message, timeoutMs, payload);
173
+ }
174
+
175
+ async function deliverSubagentIntercomMessageEvent(
176
+ events,
177
+ to,
178
+ message,
179
+ timeoutMs = 500,
180
+ extra = {})
181
+ {
182
+ if (typeof events.on !== "function" || typeof events.emit !== "function") return false;
183
+ const requestId = typeof extra.requestId === "string" ? extra.requestId : (0, _nodeCrypto.randomUUID)();
184
+ return new Promise((resolve) => {
185
+ let settled = false;
186
+ let unsubscribe;
187
+ let timer;
188
+ const finish = (delivered) => {
189
+ if (settled) return;
190
+ settled = true;
191
+ if (timer) clearTimeout(timer);
192
+ unsubscribe?.();
193
+ resolve(delivered);
194
+ };
195
+ unsubscribe = events.on(_types.SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT, (data) => {
196
+ if (!data || typeof data !== "object") return;
197
+ const delivery = data;
198
+ if (delivery.requestId !== requestId) return;
199
+ finish(delivery.delivered === true);
200
+ });
201
+ timer = setTimeout(() => finish(false), timeoutMs);
202
+ try {
203
+ events.emit(_types.SUBAGENT_RESULT_INTERCOM_EVENT, { ...extra, to, message, requestId });
204
+ } catch {
205
+ finish(false);
206
+ }
207
+ });
208
+ }
209
+
210
+ function stripSingleResultOutputs(result) {
211
+ return {
212
+ ...result,
213
+ messages: undefined,
214
+ finalOutput: undefined,
215
+ truncation: undefined
216
+ };
217
+ }
218
+
219
+ function stripDetailsOutputsForIntercomReceipt(details) {
220
+ return {
221
+ ...details,
222
+ results: details.results.map(stripSingleResultOutputs)
223
+ };
224
+ }
225
+
226
+ function formatSubagentResultReceipt(input)
227
+
228
+
229
+
230
+ {
231
+ const counts = countStatuses(input.payload.children);
232
+ const modeLabel = input.mode === "single" ?
233
+ "single subagent result" :
234
+ input.mode === "parallel" ?
235
+ "parallel subagent results" :
236
+ "chain subagent results";
237
+ const lines = [
238
+ `Delivered ${modeLabel} via intercom.`,
239
+ `Run: ${input.runId}`,
240
+ `Children: ${formatStatusCounts(counts)}`];
241
+
242
+
243
+ const artifacts = input.payload.children.filter((child) => typeof child.artifactPath === "string");
244
+ if (artifacts.length > 0) {
245
+ lines.push("Artifacts:");
246
+ for (const child of artifacts) {
247
+ lines.push(`- ${child.agent} [${child.status}]: ${child.artifactPath}`);
248
+ }
249
+ }
250
+
251
+ const intercomTargets = input.payload.children.filter((child) => typeof child.intercomTarget === "string");
252
+ if (intercomTargets.length > 0) {
253
+ lines.push("Run intercom targets (may be inactive after completion):");
254
+ for (const child of intercomTargets) {
255
+ lines.push(`- ${child.agent} [${child.status}]: ${child.intercomTarget}`);
256
+ }
257
+ }
258
+
259
+ const sessions = input.payload.children.filter((child) => typeof child.sessionPath === "string");
260
+ if (sessions.length > 0) {
261
+ lines.push("Sessions:");
262
+ for (const child of sessions) {
263
+ lines.push(`- ${child.agent} [${child.status}]: ${child.sessionPath}`);
264
+ }
265
+ }
266
+
267
+ lines.push("Full grouped output was sent over intercom.");
268
+ return lines.join("\n");
269
+ } /* v9-427c8e4be18cce04 */
pip-tmp/jiti/pi-web-access-activity.4dfe2849.mjs ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.activityMonitor = exports.ActivityMonitor = void 0; // Types
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+ class ActivityMonitor {
27
+ entries = [];
28
+ maxEntries = 10;
29
+ listeners = new Set();
30
+ rateLimitInfo = { used: 0, max: 10, oldestTimestamp: null, windowMs: 60000 };
31
+ nextId = 1;
32
+
33
+ logStart(partial) {
34
+ const id = `act-${this.nextId++}`;
35
+ const entry = {
36
+ ...partial,
37
+ id,
38
+ startTime: Date.now(),
39
+ status: null
40
+ };
41
+ this.entries.push(entry);
42
+ if (this.entries.length > this.maxEntries) {
43
+ this.entries.shift();
44
+ }
45
+ this.notify();
46
+ return id;
47
+ }
48
+
49
+ logComplete(id, status) {
50
+ const entry = this.entries.find((e) => e.id === id);
51
+ if (entry) {
52
+ entry.endTime = Date.now();
53
+ entry.status = status;
54
+ this.notify();
55
+ }
56
+ }
57
+
58
+ logError(id, error) {
59
+ const entry = this.entries.find((e) => e.id === id);
60
+ if (entry) {
61
+ entry.endTime = Date.now();
62
+ entry.error = error;
63
+ this.notify();
64
+ }
65
+ }
66
+
67
+ getEntries() {
68
+ return this.entries;
69
+ }
70
+
71
+ getRateLimitInfo() {
72
+ return this.rateLimitInfo;
73
+ }
74
+
75
+ updateRateLimit(info) {
76
+ this.rateLimitInfo = info;
77
+ this.notify();
78
+ }
79
+
80
+ onUpdate(callback) {
81
+ this.listeners.add(callback);
82
+ return () => this.listeners.delete(callback);
83
+ }
84
+
85
+ clear() {
86
+ this.entries = [];
87
+ this.rateLimitInfo = { used: 0, max: 10, oldestTimestamp: null, windowMs: 60000 };
88
+ this.notify();
89
+ }
90
+
91
+ notify() {
92
+ for (const cb of this.listeners) {
93
+ try {
94
+ cb();
95
+ } catch {
96
+ }
97
+ }
98
+ }
99
+ }exports.ActivityMonitor = ActivityMonitor;
100
+
101
+ const activityMonitor = exports.activityMonitor = new ActivityMonitor(); /* v9-be7b38195f5d2adb */
pip-tmp/jiti/pi-web-access-chrome-cookies.ad56e10d.mjs ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.getGoogleCookies = getGoogleCookies;var _nodeChild_process = await jitiImport("node:child_process");
2
+ var _nodeCrypto = await jitiImport("node:crypto");
3
+ var _nodeFs = await jitiImport("node:fs");
4
+ var _nodeOs = await jitiImport("node:os");
5
+ var _nodePath = await jitiImport("node:path");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);}
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ const GOOGLE_ORIGINS = [
18
+ "https://gemini.google.com",
19
+ "https://accounts.google.com",
20
+ "https://www.google.com"];
21
+
22
+
23
+ const ALL_COOKIE_NAMES = new Set([
24
+ "__Secure-1PSID",
25
+ "__Secure-1PSIDTS",
26
+ "__Secure-1PSIDCC",
27
+ "__Secure-1PAPISID",
28
+ "NID",
29
+ "AEC",
30
+ "SOCS",
31
+ "__Secure-BUCKET",
32
+ "__Secure-ENID",
33
+ "SID",
34
+ "HSID",
35
+ "SSID",
36
+ "APISID",
37
+ "SAPISID",
38
+ "__Secure-3PSID",
39
+ "__Secure-3PSIDTS",
40
+ "__Secure-3PAPISID",
41
+ "SIDCC"]
42
+ );
43
+
44
+ const MACOS_BROWSER_CONFIGS = [
45
+ {
46
+ name: "Helium",
47
+ baseDir: "Library/Application Support/net.imput.helium",
48
+ keychainService: "Helium Storage Key",
49
+ keychainAccount: "Helium"
50
+ },
51
+ {
52
+ name: "Chrome",
53
+ baseDir: "Library/Application Support/Google/Chrome",
54
+ keychainService: "Chrome Safe Storage",
55
+ keychainAccount: "Chrome"
56
+ },
57
+ {
58
+ name: "Arc",
59
+ baseDir: "Library/Application Support/Arc/User Data",
60
+ keychainService: "Arc Safe Storage",
61
+ keychainAccount: "Arc"
62
+ }];
63
+
64
+
65
+ const LINUX_BROWSER_CONFIGS = [
66
+ { name: "Chromium", baseDir: ".config/chromium", secretToolApp: "chromium" },
67
+ { name: "Chrome", baseDir: ".config/google-chrome", secretToolApp: "chrome" }];
68
+
69
+
70
+ async function getGoogleCookies(
71
+ options)
72
+ {
73
+ const currentPlatform = (0, _nodeOs.platform)();
74
+ const configs = currentPlatform === "darwin" ?
75
+ MACOS_BROWSER_CONFIGS :
76
+ currentPlatform === "linux" ?
77
+ LINUX_BROWSER_CONFIGS :
78
+ [];
79
+ if (configs.length === 0) return null;
80
+
81
+ const warnings = [];
82
+ const profile = options?.profile ?? "Default";
83
+ const hosts = GOOGLE_ORIGINS.map((origin) => new URL(origin).hostname);
84
+
85
+ for (const config of configs) {
86
+ const cookiesPath = (0, _nodePath.join)((0, _nodeOs.homedir)(), config.baseDir, profile, "Cookies");
87
+ if (!(0, _nodeFs.existsSync)(cookiesPath)) continue;
88
+
89
+ const password = await readBrowserPassword(config, currentPlatform);
90
+ if (!password) {
91
+ warnings.push(`Could not read ${config.name} cookie encryption password`);
92
+ continue;
93
+ }
94
+
95
+ const key = (0, _nodeCrypto.pbkdf2Sync)(password, "saltysalt", currentPlatform === "darwin" ? 1003 : 1, 16, "sha1");
96
+ const tempDir = (0, _nodeFs.mkdtempSync)((0, _nodePath.join)((0, _nodeOs.tmpdir)(), "pi-chrome-cookies-"));
97
+
98
+ try {
99
+ const tempDb = (0, _nodePath.join)(tempDir, "Cookies");
100
+ (0, _nodeFs.copyFileSync)(cookiesPath, tempDb);
101
+ copySidecar(cookiesPath, tempDb, "-wal");
102
+ copySidecar(cookiesPath, tempDb, "-shm");
103
+
104
+ const metaVersion = await readMetaVersion(tempDb);
105
+ const stripHash = metaVersion >= 24;
106
+ const rows = await queryCookieRows(tempDb, hosts);
107
+ if (!rows) {
108
+ warnings.push(`Failed to query ${config.name} cookie database`);
109
+ continue;
110
+ }
111
+
112
+ const cookies = {};
113
+ for (const row of rows) {
114
+ const name = row.name;
115
+ if (!ALL_COOKIE_NAMES.has(name)) continue;
116
+ if (cookies[name]) continue;
117
+
118
+ let value = typeof row.value === "string" && row.value.length > 0 ? row.value : null;
119
+ if (!value) {
120
+ const encrypted = row.encrypted_value;
121
+ if (encrypted instanceof Uint8Array) {
122
+ value = decryptCookieValue(encrypted, key, stripHash);
123
+ }
124
+ }
125
+ if (value) cookies[name] = value;
126
+ }
127
+
128
+ if (options?.requiredCookies?.length && !options.requiredCookies.every((name) => Boolean(cookies[name]))) {
129
+ continue;
130
+ }
131
+
132
+ return { cookies, warnings };
133
+ } finally {
134
+ (0, _nodeFs.rmSync)(tempDir, { recursive: true, force: true });
135
+ }
136
+ }
137
+
138
+ return null;
139
+ }
140
+
141
+ function decryptCookieValue(encrypted, key, stripHash) {
142
+ const buf = Buffer.from(encrypted);
143
+ if (buf.length < 3) return null;
144
+
145
+ const prefix = buf.subarray(0, 3).toString("utf8");
146
+ if (!/^v\d\d$/.test(prefix)) return null;
147
+
148
+ const ciphertext = buf.subarray(3);
149
+ if (!ciphertext.length) return "";
150
+
151
+ try {
152
+ const iv = Buffer.alloc(16, 0x20);
153
+ const decipher = (0, _nodeCrypto.createDecipheriv)("aes-128-cbc", key, iv);
154
+ decipher.setAutoPadding(false);
155
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
156
+ const unpadded = removePkcs7Padding(plaintext);
157
+ const bytes = stripHash && unpadded.length >= 32 ? unpadded.subarray(32) : unpadded;
158
+ const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
159
+ let i = 0;
160
+ while (i < decoded.length && decoded.charCodeAt(i) < 0x20) i++;
161
+ return decoded.slice(i);
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
166
+
167
+ function removePkcs7Padding(buf) {
168
+ if (!buf.length) return buf;
169
+ const padding = buf[buf.length - 1];
170
+ if (!padding || padding > 16) return buf;
171
+ return buf.subarray(0, buf.length - padding);
172
+ }
173
+
174
+ function readBrowserPassword(
175
+ config,
176
+ currentPlatform)
177
+ {
178
+ if (currentPlatform === "darwin") {
179
+ if (!config.keychainAccount || !config.keychainService) return Promise.resolve(null);
180
+ return readKeychainPassword(config.keychainAccount, config.keychainService);
181
+ }
182
+ if (currentPlatform === "linux") {
183
+ return readLinuxPassword(config.secretToolApp);
184
+ }
185
+ return Promise.resolve(null);
186
+ }
187
+
188
+ function readKeychainPassword(account, service) {
189
+ return new Promise((resolve) => {
190
+ (0, _nodeChild_process.execFile)(
191
+ "security",
192
+ ["find-generic-password", "-w", "-a", account, "-s", service],
193
+ { timeout: 5000 },
194
+ (err, stdout) => {
195
+ if (err) {resolve(null);return;}
196
+ resolve(stdout.trim() || null);
197
+ }
198
+ );
199
+ });
200
+ }
201
+
202
+ function readLinuxPassword(secretToolApp) {
203
+ if (!secretToolApp) return Promise.resolve("peanuts");
204
+
205
+ return new Promise((resolve) => {
206
+ (0, _nodeChild_process.execFile)(
207
+ "secret-tool",
208
+ ["lookup", "application", secretToolApp],
209
+ { timeout: 5000 },
210
+ (err, stdout) => {
211
+ if (err) {
212
+ // KDE Wallet users fall through to peanuts intentionally.
213
+ resolve("peanuts");
214
+ return;
215
+ }
216
+ resolve(stdout.trim() || "peanuts");
217
+ }
218
+ );
219
+ });
220
+ }
221
+
222
+ let sqliteModule = null;
223
+
224
+ async function importSqlite() {
225
+ if (sqliteModule) return sqliteModule;
226
+ const orig = process.emitWarning.bind(process);
227
+ process.emitWarning = (warning, ...args) => {
228
+ const msg = typeof warning === "string" ? warning : warning?.message ?? "";
229
+ if (msg.includes("SQLite is an experimental feature")) return;
230
+ return orig(warning, ...args);
231
+ };
232
+ try {
233
+ sqliteModule = await Promise.resolve().then(() => jitiImport("node:sqlite").then((m) => _interopRequireWildcard(m)));
234
+ return sqliteModule;
235
+ } catch {
236
+ return null;
237
+ } finally {
238
+ process.emitWarning = orig;
239
+ }
240
+ }
241
+
242
+ function supportsReadBigInts() {
243
+ const [major, minor] = process.versions.node.split(".").map(Number);
244
+ if (major > 24) return true;
245
+ if (major < 24) return false;
246
+ return minor >= 4;
247
+ }
248
+
249
+ async function readMetaVersion(dbPath) {
250
+ const sqlite = await importSqlite();
251
+ if (!sqlite) return 0;
252
+ const opts = { readOnly: true };
253
+ if (supportsReadBigInts()) opts.readBigInts = true;
254
+ const db = new sqlite.DatabaseSync(dbPath, opts);
255
+ try {
256
+ const rows = db.prepare("SELECT value FROM meta WHERE key = 'version'").all();
257
+ const val = rows[0]?.value;
258
+ if (typeof val === "number") return Math.floor(val);
259
+ if (typeof val === "bigint") return Number(val);
260
+ if (typeof val === "string") return parseInt(val, 10) || 0;
261
+ return 0;
262
+ } catch {
263
+ return 0;
264
+ } finally {
265
+ db.close();
266
+ }
267
+ }
268
+
269
+ async function queryCookieRows(
270
+ dbPath,
271
+ hosts)
272
+ {
273
+ const sqlite = await importSqlite();
274
+ if (!sqlite) return null;
275
+
276
+ const clauses = [];
277
+ for (const host of hosts) {
278
+ for (const candidate of expandHosts(host)) {
279
+ const esc = candidate.replaceAll("'", "''");
280
+ clauses.push(`host_key = '${esc}'`);
281
+ clauses.push(`host_key = '.${esc}'`);
282
+ clauses.push(`host_key LIKE '%.${esc}'`);
283
+ }
284
+ }
285
+ const where = clauses.join(" OR ");
286
+
287
+ const opts = { readOnly: true };
288
+ if (supportsReadBigInts()) opts.readBigInts = true;
289
+ const db = new sqlite.DatabaseSync(dbPath, opts);
290
+ try {
291
+ return db.
292
+ prepare(
293
+ `SELECT name, value, host_key, encrypted_value FROM cookies WHERE (${where}) ORDER BY expires_utc DESC`
294
+ ).
295
+ all();
296
+ } catch {
297
+ return null;
298
+ } finally {
299
+ db.close();
300
+ }
301
+ }
302
+
303
+ function expandHosts(host) {
304
+ const parts = host.split(".").filter(Boolean);
305
+ if (parts.length <= 1) return [host];
306
+ const candidates = new Set();
307
+ candidates.add(host);
308
+ for (let i = 1; i <= parts.length - 2; i++) {
309
+ const c = parts.slice(i).join(".");
310
+ if (c) candidates.add(c);
311
+ }
312
+ return Array.from(candidates);
313
+ }
314
+
315
+ function copySidecar(srcDb, targetDb, suffix) {
316
+ const sidecar = `${srcDb}${suffix}`;
317
+ if (!(0, _nodeFs.existsSync)(sidecar)) return;
318
+ try {
319
+ (0, _nodeFs.copyFileSync)(sidecar, `${targetDb}${suffix}`);
320
+ } catch {
321
+ }
322
+ } /* v9-cf2975e0a9fd33bd */
pip-tmp/jiti/pi-web-access-code-search.f93c7d37.mjs ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.executeCodeSearch = executeCodeSearch;var _activity = await jitiImport("./activity.js");
2
+ var _exa = await jitiImport("./exa.js");
3
+
4
+ const CODE_CONTEXT_TOOL = "get_code_context_exa";
5
+ const WEB_SEARCH_TOOL = "web_search_exa";
6
+ const DEFAULT_MAX_TOKENS = 5000;
7
+
8
+ let codeContextToolMissing = false;
9
+
10
+ function isMissingMcpToolError(message) {
11
+ const normalized = message.toLowerCase();
12
+ return normalized.includes("tool") && normalized.includes("not found");
13
+ }
14
+
15
+ function buildFallbackQuery(query) {
16
+ const normalized = query.toLowerCase();
17
+ const hasCodeTerms = /\b(api|code|docs?|documentation|example|github|implementation|library|source|stackoverflow|stack overflow)\b/.test(normalized);
18
+ return hasCodeTerms ? query : `${query} code examples documentation GitHub Stack Overflow official docs`;
19
+ }
20
+
21
+ function maxTokensToResultCount(maxTokens) {
22
+ return Math.min(20, Math.max(5, Math.ceil(maxTokens / 1000)));
23
+ }
24
+
25
+ function trimApproxTokens(text, maxTokens) {
26
+ const maxCharacters = Math.max(1000, maxTokens * 4);
27
+ if (text.length <= maxCharacters) return text;
28
+ return `${text.slice(0, maxCharacters).trimEnd()}\n\n[Truncated by code_search to approximately ${maxTokens} tokens.]`;
29
+ }
30
+
31
+ async function executeFallbackSearch(query, maxTokens, signal) {
32
+ const text = await (0, _exa.callExaMcp)(
33
+ WEB_SEARCH_TOOL,
34
+ {
35
+ query: buildFallbackQuery(query),
36
+ numResults: maxTokensToResultCount(maxTokens),
37
+ livecrawl: "fallback",
38
+ type: "auto",
39
+ contextMaxCharacters: Math.min(50000, Math.max(1000, maxTokens * 4))
40
+ },
41
+ signal
42
+ );
43
+ return trimApproxTokens(text, maxTokens);
44
+ }
45
+
46
+ async function executeCodeSearch(
47
+ _toolCallId,
48
+ params,
49
+ signal)
50
+
51
+
52
+
53
+ {
54
+ const query = params.query.trim();
55
+ if (!query) {
56
+ return {
57
+ content: [{ type: "text", text: "Error: No query provided." }],
58
+ details: { query: "", maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS, error: "No query provided" }
59
+ };
60
+ }
61
+
62
+ const maxTokens = params.maxTokens ?? DEFAULT_MAX_TOKENS;
63
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query });
64
+
65
+ try {
66
+ let mode = "web-search-fallback";
67
+ let text;
68
+
69
+ if (codeContextToolMissing) {
70
+ text = await executeFallbackSearch(query, maxTokens, signal);
71
+ } else {
72
+ try {
73
+ text = await (0, _exa.callExaMcp)(
74
+ CODE_CONTEXT_TOOL,
75
+ {
76
+ query,
77
+ tokensNum: maxTokens
78
+ },
79
+ signal
80
+ );
81
+ mode = "code-context";
82
+ } catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ if (!isMissingMcpToolError(message)) throw err;
85
+ codeContextToolMissing = true;
86
+ text = await executeFallbackSearch(query, maxTokens, signal);
87
+ }
88
+ }
89
+
90
+ _activity.activityMonitor.logComplete(activityId, 200);
91
+ return {
92
+ content: [{ type: "text", text }],
93
+ details: { query, maxTokens, mode }
94
+ };
95
+ } catch (err) {
96
+ const message = err instanceof Error ? err.message : String(err);
97
+ if (message.toLowerCase().includes("abort")) {
98
+ _activity.activityMonitor.logComplete(activityId, 0);
99
+ throw err;
100
+ }
101
+ _activity.activityMonitor.logError(activityId, message);
102
+ return {
103
+ content: [{ type: "text", text: `Error: ${message}` }],
104
+ details: { query, maxTokens, error: message }
105
+ };
106
+ }
107
+ } /* v9-7cb6dc1a8d8e5070 */
pip-tmp/jiti/pi-web-access-curator-page.f382e376.mjs ADDED
The diff for this file is too large to render. See raw diff
 
pip-tmp/jiti/pi-web-access-curator-server.2a16f6fa.mjs ADDED
@@ -0,0 +1,605 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.startCuratorServer = startCuratorServer;var _nodeHttp = _interopRequireDefault(await jitiImport("node:http"));
2
+ var _curatorPage = await jitiImport("./curator-page.js");function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}
3
+
4
+
5
+ const STALE_THRESHOLD_MS = 30000;
6
+ const WATCHDOG_INTERVAL_MS = 5000;
7
+ const MAX_BODY_SIZE = 64 * 1024;
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+ function sendJson(res, status, payload) {
49
+ res.writeHead(status, {
50
+ "Content-Type": "application/json",
51
+ "Cache-Control": "no-store"
52
+ });
53
+ res.end(JSON.stringify(payload));
54
+ }
55
+
56
+ function parseJSONBody(req) {
57
+ return new Promise((resolve, reject) => {
58
+ let body = "";
59
+ let size = 0;
60
+ req.on("data", (chunk) => {
61
+ size += chunk.length;
62
+ if (size > MAX_BODY_SIZE) {
63
+ req.destroy();
64
+ reject(new Error("Request body too large"));
65
+ return;
66
+ }
67
+ body += chunk.toString();
68
+ });
69
+ req.on("end", () => {
70
+ try {
71
+ resolve(JSON.parse(body));
72
+ } catch (err) {
73
+ const message = err instanceof Error ? err.message : String(err);
74
+ reject(new Error(`Invalid JSON: ${message}`));
75
+ }
76
+ });
77
+ req.on("error", reject);
78
+ });
79
+ }
80
+
81
+ async function parseBodyOrSend(req, res) {
82
+ try {
83
+ return await parseJSONBody(req);
84
+ } catch (err) {
85
+ const message = err instanceof Error ? err.message : "Invalid body";
86
+ const status = message === "Request body too large" ? 413 : 400;
87
+ sendJson(res, status, { ok: false, error: message });
88
+ return null;
89
+ }
90
+ }
91
+
92
+ function normalizeSelectedIndices(
93
+ value,
94
+ options)
95
+ {
96
+ if (!Array.isArray(value)) {
97
+ return { ok: false, error: "Invalid selection" };
98
+ }
99
+
100
+ if (!options.allowEmpty && value.length === 0) {
101
+ return { ok: false, error: "Invalid selection" };
102
+ }
103
+
104
+ const normalized = [];
105
+ const seen = new Set();
106
+ for (const item of value) {
107
+ if (typeof item !== "number" || !Number.isInteger(item) || item < 0) {
108
+ return { ok: false, error: "Invalid selection" };
109
+ }
110
+ if (item >= options.maxExclusive) {
111
+ return { ok: false, error: "Invalid selection" };
112
+ }
113
+ if (seen.has(item)) {
114
+ continue;
115
+ }
116
+ seen.add(item);
117
+ normalized.push(item);
118
+ }
119
+
120
+ if (!options.allowEmpty && normalized.length === 0) {
121
+ return { ok: false, error: "Invalid selection" };
122
+ }
123
+
124
+ return { ok: true, indices: normalized };
125
+ }
126
+
127
+ function normalizeSummaryMeta(value) {
128
+ if (!value || typeof value !== "object") return null;
129
+ const meta = value;
130
+
131
+ const model = meta.model;
132
+ if (model !== null && typeof model !== "string") return null;
133
+
134
+ const durationMs = meta.durationMs;
135
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0) return null;
136
+
137
+ const tokenEstimate = meta.tokenEstimate;
138
+ if (typeof tokenEstimate !== "number" || !Number.isFinite(tokenEstimate) || tokenEstimate < 0) return null;
139
+
140
+ const fallbackUsed = meta.fallbackUsed;
141
+ if (typeof fallbackUsed !== "boolean") return null;
142
+
143
+ const fallbackReason = meta.fallbackReason;
144
+ if (fallbackReason !== undefined && typeof fallbackReason !== "string") return null;
145
+
146
+ const edited = meta.edited;
147
+ if (edited !== undefined && typeof edited !== "boolean") return null;
148
+
149
+ return {
150
+ model,
151
+ durationMs,
152
+ tokenEstimate,
153
+ fallbackUsed,
154
+ fallbackReason,
155
+ edited
156
+ };
157
+ }
158
+
159
+ function startCuratorServer(
160
+ options,
161
+ callbacks)
162
+ {
163
+ const {
164
+ queries,
165
+ sessionToken,
166
+ timeout,
167
+ availableProviders,
168
+ defaultProvider,
169
+ summaryModels,
170
+ defaultSummaryModel
171
+ } = options;
172
+ let browserConnected = false;
173
+ let lastHeartbeatAt = Date.now();
174
+ let completed = false;
175
+ let watchdog = null;
176
+ let state = "SEARCHING";
177
+ let sseResponse = null;
178
+ const sseBuffer = [];
179
+ let nextQueryIndex = queries.length;
180
+ let summarizeAbortController = null;
181
+ let summarizeRequestSeq = 0;
182
+
183
+ let sseKeepalive = null;
184
+
185
+ const abortInFlightSummarize = () => {
186
+ if (!summarizeAbortController) return;
187
+ summarizeAbortController.abort();
188
+ summarizeAbortController = null;
189
+ };
190
+
191
+ const markCompleted = () => {
192
+ if (completed) return false;
193
+ completed = true;
194
+ state = "COMPLETED";
195
+ if (watchdog) {
196
+ clearInterval(watchdog);
197
+ watchdog = null;
198
+ }
199
+ if (sseKeepalive) {
200
+ clearInterval(sseKeepalive);
201
+ sseKeepalive = null;
202
+ }
203
+ abortInFlightSummarize();
204
+ if (sseResponse) {
205
+ try {sseResponse.end();} catch {}
206
+ sseResponse = null;
207
+ }
208
+ return true;
209
+ };
210
+
211
+ const touchHeartbeat = () => {
212
+ lastHeartbeatAt = Date.now();
213
+ browserConnected = true;
214
+ };
215
+
216
+ function validateToken(body, res) {
217
+ if (!body || typeof body !== "object") {
218
+ sendJson(res, 400, { ok: false, error: "Invalid body" });
219
+ return false;
220
+ }
221
+ if (body.token !== sessionToken) {
222
+ sendJson(res, 403, { ok: false, error: "Invalid session" });
223
+ return false;
224
+ }
225
+ return true;
226
+ }
227
+
228
+ function isAvailableProvider(provider) {
229
+ if (provider === "perplexity") return availableProviders.perplexity;
230
+ if (provider === "exa") return availableProviders.exa;
231
+ if (provider === "gemini") return availableProviders.gemini;
232
+ return false;
233
+ }
234
+
235
+ function sendSSE(event, data) {
236
+ const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
237
+ const res = sseResponse;
238
+ if (res && !res.writableEnded && res.socket && !res.socket.destroyed) {
239
+ try {res.write(payload);return;} catch {}
240
+ }
241
+ sseBuffer.push(payload);
242
+ }
243
+
244
+ const pageHtml = (0, _curatorPage.generateCuratorPage)(
245
+ queries,
246
+ sessionToken,
247
+ timeout,
248
+ availableProviders,
249
+ defaultProvider,
250
+ summaryModels,
251
+ defaultSummaryModel
252
+ );
253
+
254
+ const server = _nodeHttp.default.createServer(async (req, res) => {
255
+ try {
256
+ const method = req.method || "GET";
257
+ const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
258
+
259
+ if (method === "GET" && url.pathname === "/") {
260
+ const token = url.searchParams.get("session");
261
+ if (token !== sessionToken) {
262
+ res.writeHead(403, { "Content-Type": "text/plain" });
263
+ res.end("Invalid session");
264
+ return;
265
+ }
266
+ touchHeartbeat();
267
+ res.writeHead(200, {
268
+ "Content-Type": "text/html; charset=utf-8",
269
+ "Cache-Control": "no-store"
270
+ });
271
+ res.end(pageHtml);
272
+ return;
273
+ }
274
+
275
+ if (method === "GET" && url.pathname === "/events") {
276
+ const token = url.searchParams.get("session");
277
+ if (token !== sessionToken) {
278
+ res.writeHead(403, { "Content-Type": "text/plain" });
279
+ res.end("Invalid session");
280
+ return;
281
+ }
282
+ if (state === "COMPLETED") {
283
+ sendJson(res, 409, { ok: false, error: "No events available" });
284
+ return;
285
+ }
286
+ if (sseResponse) {
287
+ try {sseResponse.end();} catch {}
288
+ }
289
+ res.writeHead(200, {
290
+ "Content-Type": "text/event-stream",
291
+ "Cache-Control": "no-cache",
292
+ Connection: "keep-alive",
293
+ "X-Accel-Buffering": "no"
294
+ });
295
+ res.flushHeaders();
296
+ if (res.socket) res.socket.setNoDelay(true);
297
+ sseResponse = res;
298
+ if (sseBuffer.length > 0) {
299
+ const pending = sseBuffer.splice(0, sseBuffer.length);
300
+ for (let i = 0; i < pending.length; i++) {
301
+ const msg = pending[i];
302
+ try {
303
+ res.write(msg);
304
+ } catch {
305
+ sseBuffer.unshift(...pending.slice(i));
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ if (sseKeepalive) clearInterval(sseKeepalive);
311
+ sseKeepalive = setInterval(() => {
312
+ if (sseResponse) {
313
+ try {sseResponse.write(":keepalive\n\n");} catch {}
314
+ }
315
+ }, 15000);
316
+ req.on("close", () => {
317
+ if (sseResponse === res) sseResponse = null;
318
+ });
319
+ return;
320
+ }
321
+
322
+ if (method === "POST" && url.pathname === "/heartbeat") {
323
+ const body = await parseBodyOrSend(req, res);
324
+ if (!body) return;
325
+ if (!validateToken(body, res)) return;
326
+ touchHeartbeat();
327
+ sendJson(res, 200, { ok: true });
328
+ return;
329
+ }
330
+
331
+ if (method === "POST" && url.pathname === "/provider") {
332
+ const body = await parseBodyOrSend(req, res);
333
+ if (!body) return;
334
+ if (!validateToken(body, res)) return;
335
+ const { provider } = body;
336
+ if (typeof provider !== "string" || provider.length === 0) {
337
+ sendJson(res, 400, { ok: false, error: "Invalid provider" });
338
+ return;
339
+ }
340
+ if (!isAvailableProvider(provider)) {
341
+ sendJson(res, 400, { ok: false, error: `Provider unavailable: ${provider}` });
342
+ return;
343
+ }
344
+ setImmediate(() => callbacks.onProviderChange(provider));
345
+ sendJson(res, 200, { ok: true });
346
+ return;
347
+ }
348
+
349
+ if (method === "POST" && url.pathname === "/search") {
350
+ const body = await parseBodyOrSend(req, res);
351
+ if (!body) return;
352
+ if (!validateToken(body, res)) return;
353
+ if (state === "COMPLETED") {
354
+ sendJson(res, 409, { ok: false, error: "Session closed" });
355
+ return;
356
+ }
357
+ const { query, provider } = body;
358
+ if (typeof query !== "string" || query.trim().length === 0) {
359
+ sendJson(res, 400, { ok: false, error: "Invalid query" });
360
+ return;
361
+ }
362
+ if (provider !== undefined) {
363
+ if (typeof provider !== "string" || provider.length === 0) {
364
+ sendJson(res, 400, { ok: false, error: "Invalid provider" });
365
+ return;
366
+ }
367
+ if (!isAvailableProvider(provider)) {
368
+ sendJson(res, 400, { ok: false, error: `Provider unavailable: ${provider}` });
369
+ return;
370
+ }
371
+ }
372
+ const qi = nextQueryIndex++;
373
+ touchHeartbeat();
374
+ try {
375
+ const result = await callbacks.onAddSearch(query.trim(), qi, provider);
376
+ sendJson(res, 200, {
377
+ ok: true,
378
+ queryIndex: qi,
379
+ answer: result.answer,
380
+ results: result.results,
381
+ provider: result.provider
382
+ });
383
+ } catch (err) {
384
+ const message = err instanceof Error ? err.message : "Search failed";
385
+ sendJson(res, 200, {
386
+ ok: true,
387
+ queryIndex: qi,
388
+ error: message,
389
+ provider: typeof provider === "string" && provider.length > 0 ? provider : undefined
390
+ });
391
+ }
392
+ return;
393
+ }
394
+
395
+ if (method === "POST" && url.pathname === "/summarize") {
396
+ const body = await parseBodyOrSend(req, res);
397
+ if (!body) return;
398
+ if (!validateToken(body, res)) return;
399
+ if (state === "COMPLETED") {
400
+ sendJson(res, 409, { ok: false, error: "Session closed" });
401
+ return;
402
+ }
403
+
404
+ const parsed = normalizeSelectedIndices(body.selected, {
405
+ allowEmpty: false,
406
+ maxExclusive: nextQueryIndex
407
+ });
408
+ if (!parsed.ok) {
409
+ sendJson(res, 400, { ok: false, error: parsed.error });
410
+ return;
411
+ }
412
+
413
+ let model;
414
+ const bodyModel = body.model;
415
+ if (bodyModel !== undefined) {
416
+ if (typeof bodyModel !== "string") {
417
+ sendJson(res, 400, { ok: false, error: "Invalid model" });
418
+ return;
419
+ }
420
+ const trimmedModel = bodyModel.trim();
421
+ model = trimmedModel.length > 0 ? trimmedModel : undefined;
422
+ }
423
+
424
+ const bodyFeedback = body.feedback;
425
+ const feedback = typeof bodyFeedback === "string" && bodyFeedback.trim().length > 0 ?
426
+ bodyFeedback.trim() :
427
+ undefined;
428
+
429
+ abortInFlightSummarize();
430
+ const controller = new AbortController();
431
+ summarizeAbortController = controller;
432
+ const requestId = ++summarizeRequestSeq;
433
+
434
+ try {
435
+ const result = await callbacks.onSummarize(parsed.indices, controller.signal, model, feedback);
436
+ if (requestId !== summarizeRequestSeq || state === "COMPLETED") {
437
+ sendJson(res, 409, { ok: false, error: "Summarize request superseded" });
438
+ return;
439
+ }
440
+ sendJson(res, 200, {
441
+ ok: true,
442
+ summary: result.summary,
443
+ meta: result.meta
444
+ });
445
+ } catch (err) {
446
+ const message = err instanceof Error ? err.message : "Summary generation failed";
447
+ const status = controller.signal.aborted ? 409 : 500;
448
+ sendJson(res, status, { ok: false, error: message });
449
+ } finally {
450
+ if (summarizeAbortController === controller) {
451
+ summarizeAbortController = null;
452
+ }
453
+ }
454
+ return;
455
+ }
456
+
457
+ if (method === "POST" && url.pathname === "/rewrite") {
458
+ const body = await parseBodyOrSend(req, res);
459
+ if (!body) return;
460
+ if (!validateToken(body, res)) return;
461
+ if (state === "COMPLETED") {
462
+ sendJson(res, 409, { ok: false, error: "Session closed" });
463
+ return;
464
+ }
465
+ const { query } = body;
466
+ if (typeof query !== "string" || query.trim().length === 0) {
467
+ sendJson(res, 400, { ok: false, error: "Invalid query" });
468
+ return;
469
+ }
470
+ const controller = new AbortController();
471
+ req.on("close", () => controller.abort());
472
+ touchHeartbeat();
473
+ try {
474
+ const rewritten = await callbacks.onRewriteQuery(query.trim(), controller.signal);
475
+ sendJson(res, 200, { ok: true, query: rewritten });
476
+ } catch (err) {
477
+ const message = err instanceof Error ? err.message : "Rewrite failed";
478
+ const status = controller.signal.aborted ? 409 : 500;
479
+ sendJson(res, status, { ok: false, error: message });
480
+ }
481
+ return;
482
+ }
483
+
484
+ if (method === "POST" && url.pathname === "/submit") {
485
+ const body = await parseBodyOrSend(req, res);
486
+ if (!body) return;
487
+ if (!validateToken(body, res)) return;
488
+
489
+ const parsed = normalizeSelectedIndices(body.selected, {
490
+ allowEmpty: true,
491
+ maxExclusive: nextQueryIndex
492
+ });
493
+ if (!parsed.ok) {
494
+ sendJson(res, 400, { ok: false, error: parsed.error });
495
+ return;
496
+ }
497
+
498
+ let summary;
499
+ const bodySummary = body.summary;
500
+ if (bodySummary !== undefined) {
501
+ if (typeof bodySummary !== "string") {
502
+ sendJson(res, 400, { ok: false, error: "Invalid summary" });
503
+ return;
504
+ }
505
+ const trimmedSummary = bodySummary.trim();
506
+ summary = trimmedSummary.length > 0 ? trimmedSummary : undefined;
507
+ }
508
+
509
+ let summaryMeta;
510
+ const bodySummaryMeta = body.summaryMeta;
511
+ if (bodySummaryMeta !== undefined) {
512
+ const parsedSummaryMeta = normalizeSummaryMeta(bodySummaryMeta);
513
+ if (!parsedSummaryMeta) {
514
+ sendJson(res, 400, { ok: false, error: "Invalid summaryMeta" });
515
+ return;
516
+ }
517
+ summaryMeta = parsedSummaryMeta;
518
+ }
519
+
520
+ if (state !== "SEARCHING" && state !== "RESULT_SELECTION") {
521
+ sendJson(res, 409, { ok: false, error: "Cannot submit in current state" });
522
+ return;
523
+ }
524
+ if (!markCompleted()) {
525
+ sendJson(res, 409, { ok: false, error: "Session closed" });
526
+ return;
527
+ }
528
+ const rawResults = body.rawResults === true;
529
+ sendJson(res, 200, { ok: true });
530
+ setImmediate(() => callbacks.onSubmit({ selectedQueryIndices: parsed.indices, summary, summaryMeta, rawResults }));
531
+ return;
532
+ }
533
+
534
+ if (method === "POST" && url.pathname === "/cancel") {
535
+ const body = await parseBodyOrSend(req, res);
536
+ if (!body) return;
537
+ if (!validateToken(body, res)) return;
538
+ if (!markCompleted()) {
539
+ sendJson(res, 200, { ok: true });
540
+ return;
541
+ }
542
+ const { reason } = body;
543
+ sendJson(res, 200, { ok: true });
544
+ const cancelReason = reason === "timeout" ? "timeout" : "user";
545
+ setImmediate(() => callbacks.onCancel(cancelReason));
546
+ return;
547
+ }
548
+
549
+ res.writeHead(404, { "Content-Type": "text/plain" });
550
+ res.end("Not found");
551
+ } catch (err) {
552
+ const message = err instanceof Error ? err.message : "Server error";
553
+ sendJson(res, 500, { ok: false, error: message });
554
+ }
555
+ });
556
+
557
+ return new Promise((resolve, reject) => {
558
+ const onError = (err) => {
559
+ reject(new Error(`Curator server failed to start: ${err.message}`));
560
+ };
561
+
562
+ server.once("error", onError);
563
+ server.listen(0, "127.0.0.1", () => {
564
+ server.off("error", onError);
565
+ const addr = server.address();
566
+ if (!addr || typeof addr === "string") {
567
+ reject(new Error("Curator server: invalid address"));
568
+ return;
569
+ }
570
+ const url = `http://localhost:${addr.port}/?session=${sessionToken}`;
571
+
572
+ watchdog = setInterval(() => {
573
+ if (completed || !browserConnected) return;
574
+ if (Date.now() - lastHeartbeatAt <= STALE_THRESHOLD_MS) return;
575
+ if (!markCompleted()) return;
576
+ setImmediate(() => callbacks.onCancel("stale"));
577
+ }, WATCHDOG_INTERVAL_MS);
578
+
579
+ resolve({
580
+ server,
581
+ url,
582
+ close: () => {
583
+ const wasOpen = markCompleted();
584
+ try {server.close();} catch {}
585
+ if (wasOpen) {
586
+ setImmediate(() => callbacks.onCancel("stale"));
587
+ }
588
+ },
589
+ pushResult: (queryIndex, data) => {
590
+ if (completed) return;
591
+ sendSSE("result", { queryIndex, query: queries[queryIndex] ?? "", ...data });
592
+ },
593
+ pushError: (queryIndex, error, provider) => {
594
+ if (completed) return;
595
+ sendSSE("search-error", { queryIndex, query: queries[queryIndex] ?? "", error, provider });
596
+ },
597
+ searchesDone: () => {
598
+ if (completed) return;
599
+ sendSSE("done", {});
600
+ state = "RESULT_SELECTION";
601
+ }
602
+ });
603
+ });
604
+ });
605
+ } /* v9-ab24f6f4c86d3473 */
pip-tmp/jiti/pi-web-access-exa.b5c6a885.mjs ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.callExaMcp = callExaMcp;exports.hasExaApiKey = hasExaApiKey;exports.isExaAvailable = isExaAvailable;exports.searchWithExa = searchWithExa;var _nodeFs = await jitiImport("node:fs");
2
+ var _nodeOs = await jitiImport("node:os");
3
+ var _nodePath = await jitiImport("node:path");
4
+ var _activity = await jitiImport("./activity.js");
5
+
6
+
7
+
8
+ const EXA_ANSWER_URL = "https://api.exa.ai/answer";
9
+ const EXA_SEARCH_URL = "https://api.exa.ai/search";
10
+ const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
11
+ const CONFIG_PATH = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi", "web-search.json");
12
+ const USAGE_PATH = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi", "exa-usage.json");
13
+
14
+ const MONTHLY_LIMIT = 1000;
15
+ const WARNING_THRESHOLD = 800;
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+ let cachedConfig = null;
63
+ let warnedMonth = null;
64
+
65
+ function loadConfig() {
66
+ if (cachedConfig) return cachedConfig;
67
+ if (!(0, _nodeFs.existsSync)(CONFIG_PATH)) {
68
+ cachedConfig = {};
69
+ return cachedConfig;
70
+ }
71
+
72
+ const raw = (0, _nodeFs.readFileSync)(CONFIG_PATH, "utf-8");
73
+ try {
74
+ cachedConfig = JSON.parse(raw);
75
+ return cachedConfig;
76
+ } catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
79
+ }
80
+ }
81
+
82
+ function normalizeApiKey(value) {
83
+ if (typeof value !== "string") return null;
84
+ const normalized = value.trim();
85
+ return normalized.length > 0 ? normalized : null;
86
+ }
87
+
88
+ function getApiKey() {
89
+ return normalizeApiKey(process.env.EXA_API_KEY) ?? normalizeApiKey(loadConfig().exaApiKey);
90
+ }
91
+
92
+ function getCurrentMonth() {
93
+ return new Date().toISOString().slice(0, 7);
94
+ }
95
+
96
+ function normalizeUsage(raw) {
97
+ const month = getCurrentMonth();
98
+ if (!raw || typeof raw !== "object") return { month, count: 0 };
99
+ const data = raw;
100
+ const parsedMonth = typeof data.month === "string" ? data.month : month;
101
+ const parsedCount = typeof data.count === "number" && Number.isFinite(data.count) ? data.count : 0;
102
+ if (parsedMonth !== month) return { month, count: 0 };
103
+ return { month: parsedMonth, count: Math.max(0, Math.floor(parsedCount)) };
104
+ }
105
+
106
+ function readUsage() {
107
+ if (!(0, _nodeFs.existsSync)(USAGE_PATH)) return { month: getCurrentMonth(), count: 0 };
108
+ const raw = (0, _nodeFs.readFileSync)(USAGE_PATH, "utf-8");
109
+ try {
110
+ return normalizeUsage(JSON.parse(raw));
111
+ } catch (err) {
112
+ const message = err instanceof Error ? err.message : String(err);
113
+ throw new Error(`Failed to parse ${USAGE_PATH}: ${message}`);
114
+ }
115
+ }
116
+
117
+ function writeUsage(usage) {
118
+ const dir = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi");
119
+ if (!(0, _nodeFs.existsSync)(dir)) (0, _nodeFs.mkdirSync)(dir, { recursive: true });
120
+ (0, _nodeFs.writeFileSync)(USAGE_PATH, JSON.stringify(usage, null, 2) + "\n");
121
+ }
122
+
123
+ function reserveRequestBudget() {
124
+ const usage = readUsage();
125
+
126
+ if (usage.count >= MONTHLY_LIMIT) {
127
+ return { exhausted: true };
128
+ }
129
+
130
+ const nextCount = usage.count + 1;
131
+ if (nextCount >= WARNING_THRESHOLD && warnedMonth !== usage.month) {
132
+ warnedMonth = usage.month;
133
+ console.error(`Exa usage warning: ${nextCount}/${MONTHLY_LIMIT} monthly requests used.`);
134
+ }
135
+
136
+ writeUsage({ month: usage.month, count: nextCount });
137
+ return null;
138
+ }
139
+
140
+ function requestSignal(signal) {
141
+ const timeout = AbortSignal.timeout(60000);
142
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
143
+ }
144
+
145
+ function recencyToStartDate(filter) {
146
+ const now = new Date();
147
+ const offsets = {
148
+ day: 1,
149
+ week: 7,
150
+ month: 30,
151
+ year: 365
152
+ };
153
+ const days = offsets[filter] ?? 0;
154
+ return new Date(now.getTime() - days * 86400000).toISOString();
155
+ }
156
+
157
+ function mapDomainFilter(domainFilter) {
158
+ if (!domainFilter?.length) return {};
159
+ const includeDomains = domainFilter.
160
+ filter((d) => !d.startsWith("-") && d.trim().length > 0).
161
+ map((d) => d.trim());
162
+ const excludeDomains = domainFilter.
163
+ filter((d) => d.startsWith("-")).
164
+ map((d) => d.slice(1).trim()).
165
+ filter(Boolean);
166
+ return {
167
+ ...(includeDomains.length ? { includeDomains } : {}),
168
+ ...(excludeDomains.length ? { excludeDomains } : {})
169
+ };
170
+ }
171
+
172
+ function normalizeHighlights(value) {
173
+ if (!Array.isArray(value)) return [];
174
+ return value.filter((item) => typeof item === "string" && item.trim().length > 0);
175
+ }
176
+
177
+ function buildAnswerFromSearchResults(results) {
178
+ if (!results?.length) return "";
179
+ const parts = [];
180
+ for (let i = 0; i < results.length; i++) {
181
+ const item = results[i];
182
+ if (!item?.url) continue;
183
+ const highlights = normalizeHighlights(item.highlights);
184
+ const content = highlights.length > 0 ?
185
+ highlights.join(" ") :
186
+ typeof item.text === "string" ? item.text.trim().slice(0, 1000) : "";
187
+ if (!content) continue;
188
+ const sourceTitle = item.title || `Source ${i + 1}`;
189
+ parts.push(`${content}\nSource: ${sourceTitle} (${item.url})`);
190
+ }
191
+ return parts.join("\n\n");
192
+ }
193
+
194
+ function mapResults(results) {
195
+ if (!Array.isArray(results)) return [];
196
+ const mapped = [];
197
+ for (let i = 0; i < results.length; i++) {
198
+ const item = results[i];
199
+ if (!item?.url) continue;
200
+ mapped.push({
201
+ title: item.title || `Source ${i + 1}`,
202
+ url: item.url,
203
+ snippet: ""
204
+ });
205
+ }
206
+ return mapped;
207
+ }
208
+
209
+ function mapInlineContent(results) {
210
+ if (!results?.length) return [];
211
+ return results.
212
+ filter((r) =>
213
+ !!r?.url && typeof r.text === "string" && r.text.length > 0).
214
+ map((r) => ({
215
+ url: r.url,
216
+ title: r.title || "",
217
+ content: r.text,
218
+ error: null
219
+ }));
220
+ }
221
+
222
+ async function callExaMcp(
223
+ toolName,
224
+ args,
225
+ signal)
226
+ {
227
+ const response = await fetch(EXA_MCP_URL, {
228
+ method: "POST",
229
+ headers: {
230
+ "Content-Type": "application/json",
231
+ "Accept": "application/json, text/event-stream"
232
+ },
233
+ body: JSON.stringify({
234
+ jsonrpc: "2.0",
235
+ id: 1,
236
+ method: "tools/call",
237
+ params: {
238
+ name: toolName,
239
+ arguments: args
240
+ }
241
+ }),
242
+ signal: requestSignal(signal)
243
+ });
244
+
245
+ if (!response.ok) {
246
+ const errorText = await response.text();
247
+ throw new Error(`Exa MCP error ${response.status}: ${errorText.slice(0, 300)}`);
248
+ }
249
+
250
+ const body = await response.text();
251
+ const dataLines = body.split("\n").filter((line) => line.startsWith("data:"));
252
+
253
+ let parsed = null;
254
+ for (const line of dataLines) {
255
+ const payload = line.slice(5).trim();
256
+ if (!payload) continue;
257
+ try {
258
+ const candidate = JSON.parse(payload);
259
+ if (candidate?.result || candidate?.error) {
260
+ parsed = candidate;
261
+ break;
262
+ }
263
+ } catch {
264
+ }
265
+ }
266
+
267
+ if (!parsed) {
268
+ try {
269
+ const candidate = JSON.parse(body);
270
+ if (candidate?.result || candidate?.error) {
271
+ parsed = candidate;
272
+ }
273
+ } catch {
274
+ }
275
+ }
276
+
277
+ if (!parsed) {
278
+ throw new Error("Exa MCP returned an empty response");
279
+ }
280
+
281
+ if (parsed.error) {
282
+ const code = typeof parsed.error.code === "number" ? ` ${parsed.error.code}` : "";
283
+ const message = parsed.error.message || "Unknown error";
284
+ throw new Error(`Exa MCP error${code}: ${message}`);
285
+ }
286
+
287
+ if (parsed.result?.isError) {
288
+ const message = parsed.result.content?.
289
+ find((item) => item.type === "text" && typeof item.text === "string")?.
290
+ text?.trim();
291
+ throw new Error(message || "Exa MCP returned an error");
292
+ }
293
+
294
+ const text = parsed.result?.content?.
295
+ find((item) => item.type === "text" && typeof item.text === "string" && item.text.trim().length > 0)?.
296
+ text;
297
+
298
+ if (!text) {
299
+ throw new Error("Exa MCP returned empty content");
300
+ }
301
+
302
+ return text;
303
+ }
304
+
305
+ function parseMcpResults(text) {
306
+ const blocks = text.split(/(?=^Title: )/m).filter((block) => block.trim().length > 0);
307
+ const parsed = blocks.map((block) => {
308
+ const title = block.match(/^Title: (.+)/m)?.[1]?.trim() ?? "";
309
+ const url = block.match(/^URL: (.+)/m)?.[1]?.trim() ?? "";
310
+ let content = "";
311
+ const textStart = block.indexOf("\nText: ");
312
+ if (textStart >= 0) {
313
+ content = block.slice(textStart + 7).trim();
314
+ } else {
315
+ const hlMatch = block.match(/\nHighlights:\s*\n/);
316
+ if (hlMatch?.index != null) {
317
+ content = block.slice(hlMatch.index + hlMatch[0].length).trim();
318
+ }
319
+ }
320
+ content = content.replace(/\n---\s*$/, "").trim();
321
+ return { title, url, content };
322
+ }).filter((result) => result.url.length > 0);
323
+ return parsed.length > 0 ? parsed : null;
324
+ }
325
+
326
+ function buildAnswerFromMcpResults(results) {
327
+ if (results.length === 0) return "";
328
+ const parts = [];
329
+ for (let i = 0; i < results.length; i++) {
330
+ const result = results[i];
331
+ const snippet = result.content.replace(/\s+/g, " ").trim().slice(0, 500);
332
+ if (!snippet) continue;
333
+ const sourceTitle = result.title || `Source ${i + 1}`;
334
+ parts.push(`${snippet}\nSource: ${sourceTitle} (${result.url})`);
335
+ }
336
+ return parts.join("\n\n");
337
+ }
338
+
339
+ function mapMcpInlineContent(results) {
340
+ return results.
341
+ filter((result) => result.content.length > 0).
342
+ map((result) => ({
343
+ url: result.url,
344
+ title: result.title,
345
+ content: result.content,
346
+ error: null
347
+ }));
348
+ }
349
+
350
+ function buildMcpQuery(query, options) {
351
+ const parts = [query];
352
+ if (options.domainFilter?.length) {
353
+ for (const d of options.domainFilter) {
354
+ parts.push(d.startsWith("-") ? `-site:${d.slice(1)}` : `site:${d}`);
355
+ }
356
+ }
357
+ if (options.recencyFilter) {
358
+ const now = new Date();
359
+ switch (options.recencyFilter) {
360
+ case "day":parts.push("past 24 hours");break;
361
+ case "week":parts.push("past week");break;
362
+ case "month":parts.push(`${now.toLocaleString("en", { month: "long" })} ${now.getFullYear()}`);break;
363
+ case "year":parts.push(String(now.getFullYear()));break;
364
+ }
365
+ }
366
+ return parts.join(" ");
367
+ }
368
+
369
+ async function searchWithExaMcp(query, options = {}) {
370
+ const enrichedQuery = buildMcpQuery(query, options);
371
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query: enrichedQuery });
372
+
373
+ try {
374
+ const text = await callExaMcp(
375
+ "web_search_exa",
376
+ {
377
+ query: enrichedQuery,
378
+ numResults: options.numResults ?? 5,
379
+ livecrawl: "fallback",
380
+ type: "auto",
381
+ contextMaxCharacters: options.includeContent ? 50000 : 3000
382
+ },
383
+ options.signal
384
+ );
385
+ const parsedResults = parseMcpResults(text);
386
+ _activity.activityMonitor.logComplete(activityId, 200);
387
+
388
+ if (!parsedResults) return null;
389
+
390
+ const response = {
391
+ answer: buildAnswerFromMcpResults(parsedResults),
392
+ results: parsedResults.map((result, index) => ({
393
+ title: result.title || `Source ${index + 1}`,
394
+ url: result.url,
395
+ snippet: ""
396
+ }))
397
+ };
398
+
399
+ if (options.includeContent) {
400
+ const inlineContent = mapMcpInlineContent(parsedResults);
401
+ if (inlineContent.length > 0) response.inlineContent = inlineContent;
402
+ }
403
+
404
+ return response;
405
+ } catch (err) {
406
+ const message = err instanceof Error ? err.message : String(err);
407
+ if (message.toLowerCase().includes("abort")) {
408
+ _activity.activityMonitor.logComplete(activityId, 0);
409
+ } else {
410
+ _activity.activityMonitor.logError(activityId, message);
411
+ }
412
+ throw err;
413
+ }
414
+ }
415
+
416
+ function isExaAvailable() {
417
+ if (getApiKey()) {
418
+ const usage = readUsage();
419
+ return usage.count < MONTHLY_LIMIT;
420
+ }
421
+ return true;
422
+ }
423
+
424
+ function hasExaApiKey() {
425
+ return !!getApiKey();
426
+ }
427
+
428
+ async function searchWithExa(query, options = {}) {
429
+ const apiKey = getApiKey();
430
+ if (!apiKey) {
431
+ return searchWithExaMcp(query, options);
432
+ }
433
+
434
+ const budget = reserveRequestBudget();
435
+ if (budget) return budget;
436
+
437
+ const useSearch = options.includeContent ||
438
+ !!options.recencyFilter ||
439
+ !!options.domainFilter?.length ||
440
+ !!(options.numResults && options.numResults !== 5);
441
+
442
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query });
443
+
444
+ try {
445
+ if (!useSearch) {
446
+ const response = await fetch(EXA_ANSWER_URL, {
447
+ method: "POST",
448
+ headers: {
449
+ "x-api-key": apiKey,
450
+ "Content-Type": "application/json"
451
+ },
452
+ body: JSON.stringify({
453
+ query,
454
+ text: true
455
+ }),
456
+ signal: requestSignal(options.signal)
457
+ });
458
+
459
+ if (!response.ok) {
460
+ const errorText = await response.text();
461
+ throw new Error(`Exa API error ${response.status}: ${errorText.slice(0, 300)}`);
462
+ }
463
+
464
+ const data = await response.json();
465
+ _activity.activityMonitor.logComplete(activityId, response.status);
466
+ return {
467
+ answer: data.answer || "",
468
+ results: mapResults(data.citations)
469
+ };
470
+ }
471
+
472
+ const startDate = options.recencyFilter ? recencyToStartDate(options.recencyFilter) : null;
473
+ const domainFilters = mapDomainFilter(options.domainFilter);
474
+ const response = await fetch(EXA_SEARCH_URL, {
475
+ method: "POST",
476
+ headers: {
477
+ "x-api-key": apiKey,
478
+ "Content-Type": "application/json"
479
+ },
480
+ body: JSON.stringify({
481
+ query,
482
+ type: "auto",
483
+ numResults: options.numResults ?? 5,
484
+ ...domainFilters,
485
+ ...(startDate ? { startPublishedDate: startDate } : {}),
486
+ contents: {
487
+ text: options.includeContent ? true : { maxCharacters: 3000 },
488
+ highlights: true
489
+ }
490
+ }),
491
+ signal: requestSignal(options.signal)
492
+ });
493
+
494
+ if (!response.ok) {
495
+ const errorText = await response.text();
496
+ throw new Error(`Exa API error ${response.status}: ${errorText.slice(0, 300)}`);
497
+ }
498
+
499
+ const data = await response.json();
500
+ _activity.activityMonitor.logComplete(activityId, response.status);
501
+
502
+ const mapped = {
503
+ answer: buildAnswerFromSearchResults(data.results),
504
+ results: mapResults(data.results)
505
+ };
506
+ if (options.includeContent) {
507
+ const inlineContent = mapInlineContent(data.results);
508
+ if (inlineContent.length > 0) mapped.inlineContent = inlineContent;
509
+ }
510
+ return mapped;
511
+ } catch (err) {
512
+ const message = err instanceof Error ? err.message : String(err);
513
+ if (message.toLowerCase().includes("abort")) {
514
+ _activity.activityMonitor.logComplete(activityId, 0);
515
+ } else {
516
+ _activity.activityMonitor.logError(activityId, message);
517
+ }
518
+ throw err;
519
+ }
520
+ } /* v9-af927f0725a120a3 */
pip-tmp/jiti/pi-web-access-extract.baf16c13.mjs ADDED
@@ -0,0 +1,641 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.extractContent = extractContent;exports.extractHeadingTitle = extractHeadingTitle;exports.fetchAllContent = fetchAllContent;var _readability = await jitiImport("@mozilla/readability");
2
+ var _linkedom = await jitiImport("linkedom");
3
+ var _turndown = _interopRequireDefault(await jitiImport("turndown"));
4
+ var _pLimit = _interopRequireDefault(await jitiImport("p-limit"));
5
+ var _activity = await jitiImport("./activity.js");
6
+ var _rscExtract = await jitiImport("./rsc-extract.js");
7
+ var _pdfExtract = await jitiImport("./pdf-extract.js");
8
+ var _githubExtract = await jitiImport("./github-extract.js");
9
+ var _youtubeExtract = await jitiImport("./youtube-extract.js");
10
+ var _geminiUrlContext = await jitiImport("./gemini-url-context.js");
11
+ var _videoExtract = await jitiImport("./video-extract.js");
12
+ var _utils = await jitiImport("./utils.js");function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}
13
+
14
+ const DEFAULT_TIMEOUT_MS = 30000;
15
+ const CONCURRENT_LIMIT = 3;
16
+
17
+ const NON_RECOVERABLE_ERRORS = ["Unsupported content type", "Response too large"];
18
+ const MIN_USEFUL_CONTENT = 500;
19
+
20
+ function errorMessage(err) {
21
+ return err instanceof Error ? err.message : String(err);
22
+ }
23
+
24
+ function isConfigParseError(err) {
25
+ return errorMessage(err).startsWith("Failed to parse ");
26
+ }
27
+
28
+ function isAbortError(err) {
29
+ return errorMessage(err).toLowerCase().includes("abort");
30
+ }
31
+
32
+ function abortedResult(url) {
33
+ return { url, title: "", content: "", error: "Aborted" };
34
+ }
35
+
36
+ const turndown = new _turndown.default({
37
+ headingStyle: "atx",
38
+ codeBlockStyle: "fenced"
39
+ });
40
+
41
+ const fetchLimit = (0, _pLimit.default)(CONCURRENT_LIMIT);
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+ const JINA_READER_BASE = "https://r.jina.ai/";
72
+ const JINA_TIMEOUT_MS = 30000;
73
+
74
+ async function extractWithJinaReader(
75
+ url,
76
+ signal)
77
+ {
78
+ const jinaUrl = JINA_READER_BASE + url;
79
+
80
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query: `jina: ${url}` });
81
+
82
+ try {
83
+ const res = await fetch(jinaUrl, {
84
+ headers: {
85
+ "Accept": "text/markdown",
86
+ "X-No-Cache": "true"
87
+ },
88
+ signal: AbortSignal.any([
89
+ AbortSignal.timeout(JINA_TIMEOUT_MS),
90
+ ...(signal ? [signal] : [])]
91
+ )
92
+ });
93
+
94
+ if (!res.ok) {
95
+ _activity.activityMonitor.logComplete(activityId, res.status);
96
+ return null;
97
+ }
98
+
99
+ const content = await res.text();
100
+ _activity.activityMonitor.logComplete(activityId, res.status);
101
+
102
+ const contentStart = content.indexOf("Markdown Content:");
103
+ if (contentStart < 0) {
104
+ return null;
105
+ }
106
+
107
+ const markdownPart = content.slice(contentStart + 17).trim(); // 17 = "Markdown Content:".length
108
+
109
+ // Check for failed JS rendering or minimal content
110
+ if (markdownPart.length < 100 ||
111
+ markdownPart.startsWith("Loading...") ||
112
+ markdownPart.startsWith("Please enable JavaScript")) {
113
+ return null;
114
+ }
115
+
116
+ const title = extractHeadingTitle(markdownPart) ?? (new URL(url).pathname.split("/").pop() || url);
117
+ return { url, title, content: markdownPart, error: null };
118
+ } catch (err) {
119
+ const message = err instanceof Error ? err.message : String(err);
120
+ if (message.toLowerCase().includes("abort")) {
121
+ _activity.activityMonitor.logComplete(activityId, 0);
122
+ } else {
123
+ _activity.activityMonitor.logError(activityId, message);
124
+ }
125
+ return null;
126
+ }
127
+ }
128
+
129
+ function parseTimestamp(ts) {
130
+ const num = Number(ts);
131
+ if (!isNaN(num) && num >= 0) return Math.floor(num);
132
+ const parts = ts.split(":").map(Number);
133
+ if (parts.some((p) => isNaN(p) || p < 0)) return null;
134
+ if (parts.length === 3) return Math.floor(parts[0] * 3600 + parts[1] * 60 + parts[2]);
135
+ if (parts.length === 2) return Math.floor(parts[0] * 60 + parts[1]);
136
+ return null;
137
+ }
138
+
139
+
140
+
141
+ function parseTimestampSpec(ts) {
142
+ const dashIdx = ts.indexOf("-", 1);
143
+ if (dashIdx > 0) {
144
+ const start = parseTimestamp(ts.slice(0, dashIdx));
145
+ const end = parseTimestamp(ts.slice(dashIdx + 1));
146
+ if (start !== null && end !== null && end > start) return { type: "range", start, end };
147
+ }
148
+ const seconds = parseTimestamp(ts);
149
+ return seconds !== null ? { type: "single", seconds } : null;
150
+ }
151
+
152
+ const DEFAULT_RANGE_FRAMES = 6;
153
+ const MIN_FRAME_INTERVAL = 5;
154
+
155
+ function computeRangeTimestamps(start, end, maxFrames = DEFAULT_RANGE_FRAMES) {
156
+ if (maxFrames <= 1) return [start];
157
+ const duration = end - start;
158
+ const idealInterval = duration / (maxFrames - 1);
159
+ if (idealInterval < MIN_FRAME_INTERVAL) {
160
+ const timestamps = [];
161
+ for (let t = start; t <= end && timestamps.length < maxFrames; t += MIN_FRAME_INTERVAL) {
162
+ timestamps.push(t);
163
+ }
164
+ return timestamps;
165
+ }
166
+ return Array.from({ length: maxFrames }, (_, i) => Math.round(start + i * idealInterval));
167
+ }
168
+
169
+ function buildFrameResult(
170
+ url, label, requestedCount,
171
+ frames, error, duration)
172
+ {
173
+ if (frames.length === 0) {
174
+ const msg = error ?? "Frame extraction failed";
175
+ return { url, title: `Frames ${label} (0/${requestedCount})`, content: msg, error: msg };
176
+ }
177
+ return {
178
+ url,
179
+ title: `Frames ${label} (${frames.length}/${requestedCount})`,
180
+ content: `${frames.length} frames extracted from ${label}`,
181
+ error: null,
182
+ frames,
183
+ duration
184
+ };
185
+ }
186
+
187
+ async function extractLocalFrames(
188
+ filePath, timestamps)
189
+ {
190
+ const results = await Promise.all(timestamps.map(async (t) => {
191
+ const frame = await (0, _videoExtract.extractVideoFrame)(filePath, t);
192
+ if ("error" in frame) return { error: frame.error };
193
+ return { ...frame, timestamp: (0, _utils.formatSeconds)(t) };
194
+ }));
195
+ const frames = results.filter((f) => "data" in f);
196
+ const firstError = results.find((f) => "error" in f);
197
+ return { frames, error: frames.length === 0 && firstError ? firstError.error : null };
198
+ }
199
+
200
+ function safeVideoInfo(url) {
201
+ try {
202
+ return { info: (0, _videoExtract.isVideoFile)(url) };
203
+ } catch (err) {
204
+ return { info: null, error: errorMessage(err) };
205
+ }
206
+ }
207
+
208
+ async function extractContent(
209
+ url,
210
+ signal,
211
+ options)
212
+ {
213
+ if (signal?.aborted) {
214
+ return { url, title: "", content: "", error: "Aborted" };
215
+ }
216
+
217
+ if (options?.frames && !options.timestamp) {
218
+ const frameCount = options.frames;
219
+ const ytInfo = (0, _youtubeExtract.isYouTubeURL)(url);
220
+ if (ytInfo.isYouTube && ytInfo.videoId) {
221
+ const streamInfo = await (0, _youtubeExtract.getYouTubeStreamInfo)(ytInfo.videoId);
222
+ if ("error" in streamInfo) {
223
+ return { url, title: "Frames", content: streamInfo.error, error: streamInfo.error };
224
+ }
225
+ if (streamInfo.duration === null) {
226
+ const error = "Cannot determine video duration. Use a timestamp range instead.";
227
+ return { url, title: "Frames", content: error, error };
228
+ }
229
+ const dur = Math.floor(streamInfo.duration);
230
+ const timestamps = computeRangeTimestamps(0, dur, frameCount);
231
+ const result = await (0, _youtubeExtract.extractYouTubeFrames)(ytInfo.videoId, timestamps, streamInfo);
232
+ const label = `${(0, _utils.formatSeconds)(0)}-${(0, _utils.formatSeconds)(dur)}`;
233
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error, streamInfo.duration);
234
+ }
235
+
236
+ const localVideo = safeVideoInfo(url);
237
+ if (localVideo.error) {
238
+ return { url, title: "", content: "", error: localVideo.error };
239
+ }
240
+ if (localVideo.info) {
241
+ const durationResult = await (0, _videoExtract.getLocalVideoDuration)(localVideo.info.absolutePath);
242
+ if (typeof durationResult !== "number") {
243
+ return { url, title: "Frames", content: durationResult.error, error: durationResult.error };
244
+ }
245
+ const dur = Math.floor(durationResult);
246
+ const timestamps = computeRangeTimestamps(0, dur, frameCount);
247
+ const result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);
248
+ const label = `${(0, _utils.formatSeconds)(0)}-${(0, _utils.formatSeconds)(dur)}`;
249
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error, durationResult);
250
+ }
251
+
252
+ return { url, title: "", content: "", error: "Frame extraction only works with YouTube and local video files" };
253
+ }
254
+
255
+ if (options?.timestamp) {
256
+ const spec = parseTimestampSpec(options.timestamp);
257
+ if (!spec) {
258
+ return {
259
+ url,
260
+ title: "",
261
+ content: "",
262
+ error: `Invalid timestamp format: "${options.timestamp}". Use "H:MM:SS", "MM:SS", "85", or "start-end".`
263
+ };
264
+ }
265
+
266
+ const frameCount = options.frames;
267
+ const ytInfo = (0, _youtubeExtract.isYouTubeURL)(url);
268
+ if (ytInfo.isYouTube && ytInfo.videoId) {
269
+ const streamInfo = await (0, _youtubeExtract.getYouTubeStreamInfo)(ytInfo.videoId);
270
+ if ("error" in streamInfo) {
271
+ if (spec.type === "range") {
272
+ const label = `${(0, _utils.formatSeconds)(spec.start)}-${(0, _utils.formatSeconds)(spec.end)}`;
273
+ return { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };
274
+ }
275
+ if (frameCount) {
276
+ const end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;
277
+ const label = `${(0, _utils.formatSeconds)(spec.seconds)}-${(0, _utils.formatSeconds)(end)}`;
278
+ return { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };
279
+ }
280
+ return { url, title: `Frame at ${options.timestamp}`, content: streamInfo.error, error: streamInfo.error };
281
+ }
282
+
283
+ if (spec.type === "range") {
284
+ const label = `${(0, _utils.formatSeconds)(spec.start)}-${(0, _utils.formatSeconds)(spec.end)}`;
285
+ if (streamInfo.duration !== null && spec.end > streamInfo.duration) {
286
+ const error = `Timestamp ${(0, _utils.formatSeconds)(spec.end)} exceeds video duration (${(0, _utils.formatSeconds)(Math.floor(streamInfo.duration))})`;
287
+ return { url, title: `Frames ${label}`, content: error, error };
288
+ }
289
+ const timestamps = frameCount ?
290
+ computeRangeTimestamps(spec.start, spec.end, frameCount) :
291
+ computeRangeTimestamps(spec.start, spec.end);
292
+ const result = await (0, _youtubeExtract.extractYouTubeFrames)(ytInfo.videoId, timestamps, streamInfo);
293
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);
294
+ }
295
+
296
+ if (frameCount) {
297
+ const end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;
298
+ const label = `${(0, _utils.formatSeconds)(spec.seconds)}-${(0, _utils.formatSeconds)(end)}`;
299
+ if (streamInfo.duration !== null && end > streamInfo.duration) {
300
+ const error = `Timestamp ${(0, _utils.formatSeconds)(end)} exceeds video duration (${(0, _utils.formatSeconds)(Math.floor(streamInfo.duration))})`;
301
+ return { url, title: `Frames ${label}`, content: error, error };
302
+ }
303
+ const timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);
304
+ const result = await (0, _youtubeExtract.extractYouTubeFrames)(ytInfo.videoId, timestamps, streamInfo);
305
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);
306
+ }
307
+
308
+ if (streamInfo.duration !== null && spec.seconds > streamInfo.duration) {
309
+ const error = `Timestamp ${(0, _utils.formatSeconds)(spec.seconds)} exceeds video duration (${(0, _utils.formatSeconds)(Math.floor(streamInfo.duration))})`;
310
+ return { url, title: `Frame at ${options.timestamp}`, content: error, error };
311
+ }
312
+ const frame = await (0, _youtubeExtract.extractYouTubeFrame)(ytInfo.videoId, spec.seconds, streamInfo);
313
+ if ("error" in frame) {
314
+ return { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };
315
+ }
316
+ return { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };
317
+ }
318
+
319
+ const localVideo = safeVideoInfo(url);
320
+ if (localVideo.error) {
321
+ return { url, title: "", content: "", error: localVideo.error };
322
+ }
323
+ if (localVideo.info) {
324
+ if (spec.type === "range") {
325
+ const timestamps = frameCount ?
326
+ computeRangeTimestamps(spec.start, spec.end, frameCount) :
327
+ computeRangeTimestamps(spec.start, spec.end);
328
+ const result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);
329
+ const label = `${(0, _utils.formatSeconds)(spec.start)}-${(0, _utils.formatSeconds)(spec.end)}`;
330
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error);
331
+ }
332
+
333
+ if (frameCount) {
334
+ const end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;
335
+ const timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);
336
+ const result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);
337
+ const label = `${(0, _utils.formatSeconds)(spec.seconds)}-${(0, _utils.formatSeconds)(end)}`;
338
+ return buildFrameResult(url, label, timestamps.length, result.frames, result.error);
339
+ }
340
+
341
+ const frame = await (0, _videoExtract.extractVideoFrame)(localVideo.info.absolutePath, spec.seconds);
342
+ if ("error" in frame) {
343
+ return { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };
344
+ }
345
+ return { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };
346
+ }
347
+
348
+ return { url, title: "", content: "", error: "Timestamp extraction only works with YouTube and local video files" };
349
+ }
350
+
351
+ const localVideo = safeVideoInfo(url);
352
+ if (localVideo.error) {
353
+ return { url, title: "", content: "", error: localVideo.error };
354
+ }
355
+ if (localVideo.info) {
356
+ try {
357
+ const result = await (0, _videoExtract.extractVideo)(localVideo.info, signal, options);
358
+ if (signal?.aborted) return abortedResult(url);
359
+ return result ?? { url, title: "", content: "", error: "Video analysis requires Gemini access. Either:\n 1. Sign into gemini.google.com in Chrome (free, uses cookies)\n 2. Set GEMINI_API_KEY in ~/.pi/web-search.json" };
360
+ } catch (err) {
361
+ if (isAbortError(err)) return abortedResult(url);
362
+ return { url, title: "", content: "", error: errorMessage(err) };
363
+ }
364
+ }
365
+
366
+ try {
367
+ new URL(url);
368
+ } catch {
369
+ return { url, title: "", content: "", error: "Invalid URL" };
370
+ }
371
+
372
+ try {
373
+ const ghResult = await (0, _githubExtract.extractGitHub)(url, signal, options?.forceClone);
374
+ if (ghResult) return ghResult;
375
+ if (signal?.aborted) return abortedResult(url);
376
+ } catch (err) {
377
+ const message = errorMessage(err);
378
+ if (isAbortError(err)) return abortedResult(url);
379
+ if (isConfigParseError(err)) {
380
+ return { url, title: "", content: "", error: message };
381
+ }
382
+ }
383
+
384
+ const ytInfo = (0, _youtubeExtract.isYouTubeURL)(url);
385
+ let youtubeEnabled = false;
386
+ try {
387
+ youtubeEnabled = (0, _youtubeExtract.isYouTubeEnabled)();
388
+ } catch (err) {
389
+ return { url, title: "", content: "", error: errorMessage(err) };
390
+ }
391
+ if (ytInfo.isYouTube && youtubeEnabled) {
392
+ try {
393
+ const ytResult = await (0, _youtubeExtract.extractYouTube)(url, signal, options?.prompt, options?.model);
394
+ if (ytResult) return ytResult;
395
+ if (signal?.aborted) return abortedResult(url);
396
+ } catch (err) {
397
+ const message = errorMessage(err);
398
+ if (isAbortError(err)) return abortedResult(url);
399
+ if (isConfigParseError(err)) {
400
+ return { url, title: "", content: "", error: message };
401
+ }
402
+ }
403
+ return {
404
+ url,
405
+ title: "",
406
+ content: "",
407
+ error: "Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY."
408
+ };
409
+ }
410
+
411
+ if (signal?.aborted) return abortedResult(url);
412
+
413
+ const httpResult = await extractViaHttp(url, signal, options);
414
+
415
+ if (signal?.aborted) return abortedResult(url);
416
+ if (!httpResult.error) return httpResult;
417
+ if (NON_RECOVERABLE_ERRORS.some((prefix) => httpResult.error.startsWith(prefix))) return httpResult;
418
+
419
+ const jinaResult = await extractWithJinaReader(url, signal);
420
+ if (jinaResult) return jinaResult;
421
+ if (signal?.aborted) return abortedResult(url);
422
+
423
+ let geminiResult = null;
424
+ try {
425
+ geminiResult = (await (0, _geminiUrlContext.extractWithUrlContext)(url, signal)) ?? (
426
+ await (0, _geminiUrlContext.extractWithGeminiWeb)(url, signal));
427
+ } catch (err) {
428
+ if (isAbortError(err)) return abortedResult(url);
429
+ if (isConfigParseError(err)) {
430
+ return { ...httpResult, error: errorMessage(err) };
431
+ }
432
+ }
433
+
434
+ if (geminiResult) return geminiResult;
435
+ if (signal?.aborted) return abortedResult(url);
436
+
437
+ const guidance = [
438
+ httpResult.error,
439
+ "",
440
+ "Fallback options:",
441
+ " \u2022 Set GEMINI_API_KEY in ~/.pi/web-search.json",
442
+ " \u2022 Sign into gemini.google.com in Chrome",
443
+ " \u2022 Use web_search to find content about this topic"].
444
+ join("\n");
445
+ return { ...httpResult, error: guidance };
446
+ }
447
+
448
+ function isLikelyJSRendered(html) {
449
+ // Extract body content
450
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
451
+ if (!bodyMatch) return false;
452
+
453
+ const bodyHtml = bodyMatch[1];
454
+
455
+ // Strip tags to get text content
456
+ const textContent = bodyHtml.
457
+ replace(/<script[\s\S]*?<\/script>/gi, "").
458
+ replace(/<style[\s\S]*?<\/style>/gi, "").
459
+ replace(/<[^>]+>/g, "").
460
+ replace(/\s+/g, " ").
461
+ trim();
462
+
463
+ // Count scripts
464
+ const scriptCount = (html.match(/<script/gi) || []).length;
465
+
466
+ // Heuristic: little text content but many scripts suggests JS rendering
467
+ return textContent.length < 500 && scriptCount > 3;
468
+ }
469
+
470
+ async function extractViaHttp(
471
+ url,
472
+ signal,
473
+ options)
474
+ {
475
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
476
+ const activityId = _activity.activityMonitor.logStart({ type: "fetch", url });
477
+
478
+ const controller = new AbortController();
479
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
480
+
481
+ const onAbort = () => controller.abort();
482
+ signal?.addEventListener("abort", onAbort);
483
+
484
+ try {
485
+ const response = await fetch(url, {
486
+ signal: controller.signal,
487
+ headers: {
488
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
489
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
490
+ "Accept-Language": "en-US,en;q=0.9",
491
+ "Cache-Control": "no-cache",
492
+ "Sec-Fetch-Dest": "document",
493
+ "Sec-Fetch-Mode": "navigate",
494
+ "Sec-Fetch-Site": "none",
495
+ "Sec-Fetch-User": "?1",
496
+ "Upgrade-Insecure-Requests": "1"
497
+ }
498
+ });
499
+
500
+ if (!response.ok) {
501
+ _activity.activityMonitor.logComplete(activityId, response.status);
502
+ return {
503
+ url,
504
+ title: "",
505
+ content: "",
506
+ error: `HTTP ${response.status}: ${response.statusText}`
507
+ };
508
+ }
509
+
510
+ const contentLengthHeader = response.headers.get("content-length");
511
+ const contentType = response.headers.get("content-type") || "";
512
+ const isPDFContent = (0, _pdfExtract.isPDF)(url, contentType);
513
+ const maxResponseSize = isPDFContent ? 20 * 1024 * 1024 : 5 * 1024 * 1024;
514
+ if (contentLengthHeader) {
515
+ const contentLength = parseInt(contentLengthHeader, 10);
516
+ if (contentLength > maxResponseSize) {
517
+ _activity.activityMonitor.logComplete(activityId, response.status);
518
+ return {
519
+ url,
520
+ title: "",
521
+ content: "",
522
+ error: `Response too large (${Math.round(contentLength / 1024 / 1024)}MB)`
523
+ };
524
+ }
525
+ }
526
+
527
+ if (isPDFContent) {
528
+ try {
529
+ const buffer = await response.arrayBuffer();
530
+ const result = await (0, _pdfExtract.extractPDFToMarkdown)(buffer, url);
531
+ _activity.activityMonitor.logComplete(activityId, response.status);
532
+ return {
533
+ url,
534
+ title: result.title,
535
+ content: `PDF extracted and saved to: ${result.outputPath}\n\nPages: ${result.pages}\nCharacters: ${result.chars}`,
536
+ error: null
537
+ };
538
+ } catch (err) {
539
+ const message = err instanceof Error ? err.message : String(err);
540
+ _activity.activityMonitor.logError(activityId, message);
541
+ return { url, title: "", content: "", error: `PDF extraction failed: ${message}` };
542
+ }
543
+ }
544
+
545
+ if (contentType.includes("application/octet-stream") ||
546
+ contentType.includes("image/") ||
547
+ contentType.includes("audio/") ||
548
+ contentType.includes("video/") ||
549
+ contentType.includes("application/zip")) {
550
+ _activity.activityMonitor.logComplete(activityId, response.status);
551
+ return {
552
+ url,
553
+ title: "",
554
+ content: "",
555
+ error: `Unsupported content type: ${contentType.split(";")[0]}`
556
+ };
557
+ }
558
+
559
+ const text = await response.text();
560
+ const isHTML = contentType.includes("text/html") || contentType.includes("application/xhtml+xml");
561
+
562
+ if (!isHTML) {
563
+ _activity.activityMonitor.logComplete(activityId, response.status);
564
+ const title = extractTextTitle(text, url);
565
+ return { url, title, content: text, error: null };
566
+ }
567
+
568
+ const { document } = (0, _linkedom.parseHTML)(text);
569
+ const reader = new _readability.Readability(document);
570
+ const article = reader.parse();
571
+
572
+ if (!article) {
573
+ const rscResult = (0, _rscExtract.extractRSCContent)(text);
574
+ if (rscResult) {
575
+ _activity.activityMonitor.logComplete(activityId, response.status);
576
+ return { url, title: rscResult.title, content: rscResult.content, error: null };
577
+ }
578
+
579
+ _activity.activityMonitor.logComplete(activityId, response.status);
580
+
581
+ // Provide more specific error message
582
+ const jsRendered = isLikelyJSRendered(text);
583
+ const errorMsg = jsRendered ?
584
+ "Page appears to be JavaScript-rendered (content loads dynamically)" :
585
+ "Could not extract readable content from HTML structure";
586
+
587
+ return {
588
+ url,
589
+ title: "",
590
+ content: "",
591
+ error: errorMsg
592
+ };
593
+ }
594
+
595
+ const markdown = turndown.turndown(article.content);
596
+ _activity.activityMonitor.logComplete(activityId, response.status);
597
+
598
+ if (markdown.length < MIN_USEFUL_CONTENT) {
599
+ return {
600
+ url,
601
+ title: article.title || "",
602
+ content: markdown,
603
+ error: isLikelyJSRendered(text) ?
604
+ "Page appears to be JavaScript-rendered (content loads dynamically)" :
605
+ "Extracted content appears incomplete"
606
+ };
607
+ }
608
+
609
+ return { url, title: article.title || "", content: markdown, error: null };
610
+ } catch (err) {
611
+ const message = err instanceof Error ? err.message : String(err);
612
+ if (message.toLowerCase().includes("abort")) {
613
+ _activity.activityMonitor.logComplete(activityId, 0);
614
+ } else {
615
+ _activity.activityMonitor.logError(activityId, message);
616
+ }
617
+ return { url, title: "", content: "", error: message };
618
+ } finally {
619
+ clearTimeout(timeoutId);
620
+ signal?.removeEventListener("abort", onAbort);
621
+ }
622
+ }
623
+
624
+ function extractHeadingTitle(text) {
625
+ const match = text.match(/^#{1,2}\s+(.+)/m);
626
+ if (!match) return null;
627
+ const cleaned = match[1].replace(/\*+/g, "").trim();
628
+ return cleaned || null;
629
+ }
630
+
631
+ function extractTextTitle(text, url) {
632
+ return extractHeadingTitle(text) ?? (new URL(url).pathname.split("/").pop() || url);
633
+ }
634
+
635
+ async function fetchAllContent(
636
+ urls,
637
+ signal,
638
+ options)
639
+ {
640
+ return Promise.all(urls.map((url) => fetchLimit(() => extractContent(url, signal, options))));
641
+ } /* v9-487b509b854a2127 */
pip-tmp/jiti/pi-web-access-gemini-api.b8e0a3b9.mjs ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.DEFAULT_MODEL = exports.API_BASE = void 0;exports.getApiKey = getApiKey;exports.isGeminiApiAvailable = isGeminiApiAvailable;exports.queryGeminiApiWithVideo = queryGeminiApiWithVideo;var _nodeFs = await jitiImport("node:fs");
2
+ var _nodeOs = await jitiImport("node:os");
3
+ var _nodePath = await jitiImport("node:path");
4
+
5
+ const API_BASE = exports.API_BASE = "https://generativelanguage.googleapis.com/v1beta";
6
+ const CONFIG_PATH = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi", "web-search.json");
7
+ const DEFAULT_MODEL = exports.DEFAULT_MODEL = "gemini-3-flash-preview";
8
+
9
+
10
+
11
+
12
+
13
+ let cachedConfig = null;
14
+
15
+ function loadConfig() {
16
+ if (cachedConfig) return cachedConfig;
17
+ if (!(0, _nodeFs.existsSync)(CONFIG_PATH)) {
18
+ cachedConfig = {};
19
+ return cachedConfig;
20
+ }
21
+
22
+ const raw = (0, _nodeFs.readFileSync)(CONFIG_PATH, "utf-8");
23
+ try {
24
+ cachedConfig = JSON.parse(raw);
25
+ return cachedConfig;
26
+ } catch (err) {
27
+ const message = err instanceof Error ? err.message : String(err);
28
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
29
+ }
30
+ }
31
+
32
+ function withTimeout(signal, timeoutMs) {
33
+ const timeout = AbortSignal.timeout(timeoutMs);
34
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
35
+ }
36
+
37
+ function normalizeApiKey(value) {
38
+ if (typeof value !== "string") return null;
39
+ const normalized = value.trim();
40
+ return normalized.length > 0 ? normalized : null;
41
+ }
42
+
43
+ function getApiKey() {
44
+ return normalizeApiKey(process.env.GEMINI_API_KEY) ?? normalizeApiKey(loadConfig().geminiApiKey);
45
+ }
46
+
47
+ function isGeminiApiAvailable() {
48
+ return getApiKey() !== null;
49
+ }
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+ async function queryGeminiApiWithVideo(
59
+ prompt,
60
+ videoUri,
61
+ options = {})
62
+ {
63
+ const apiKey = getApiKey();
64
+ if (!apiKey) throw new Error("GEMINI_API_KEY not configured");
65
+
66
+ const model = options.model ?? DEFAULT_MODEL;
67
+ const signal = withTimeout(options.signal, options.timeoutMs ?? 120000);
68
+ const url = `${API_BASE}/models/${model}:generateContent?key=${apiKey}`;
69
+
70
+ const fileData = { fileUri: videoUri };
71
+ if (options.mimeType) fileData.mimeType = options.mimeType;
72
+
73
+ const body = {
74
+ contents: [
75
+ {
76
+ parts: [
77
+ { fileData },
78
+ { text: prompt }]
79
+
80
+ }]
81
+
82
+ };
83
+
84
+ const res = await fetch(url, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify(body),
88
+ signal
89
+ });
90
+
91
+ if (!res.ok) {
92
+ const errorText = await res.text();
93
+ throw new Error(`Gemini API error ${res.status}: ${errorText.slice(0, 300)}`);
94
+ }
95
+
96
+ const data = await res.json();
97
+ const text = data.candidates?.[0]?.content?.parts?.
98
+ map((p) => p.text).
99
+ filter(Boolean).
100
+ join("\n");
101
+
102
+ if (!text) throw new Error("Gemini API returned empty response");
103
+ return text;
104
+ } /* v9-c8e90b8826d73142 */
pip-tmp/jiti/pi-web-access-gemini-search.037a514f.mjs ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.search = search;var _nodeFs = await jitiImport("node:fs");
2
+ var _nodeOs = await jitiImport("node:os");
3
+ var _nodePath = await jitiImport("node:path");
4
+ var _activity = await jitiImport("./activity.js");
5
+ var _geminiApi = await jitiImport("./gemini-api.js");
6
+ var _geminiWeb = await jitiImport("./gemini-web.js");
7
+ var _perplexity = await jitiImport("./perplexity.js");
8
+ var _exa = await jitiImport("./exa.js");
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ const CONFIG_PATH = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi", "web-search.json");
18
+
19
+ let cachedSearchConfig = null;
20
+
21
+ function getSearchConfig() {
22
+ if (cachedSearchConfig) return cachedSearchConfig;
23
+ if (!(0, _nodeFs.existsSync)(CONFIG_PATH)) {
24
+ cachedSearchConfig = { searchProvider: "auto", searchModel: undefined };
25
+ return cachedSearchConfig;
26
+ }
27
+
28
+ const rawText = (0, _nodeFs.readFileSync)(CONFIG_PATH, "utf-8");
29
+ let raw;
30
+
31
+
32
+
33
+
34
+ try {
35
+ raw = JSON.parse(rawText);
36
+
37
+
38
+
39
+
40
+ } catch (err) {
41
+ const message = err instanceof Error ? err.message : String(err);
42
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
43
+ }
44
+
45
+ cachedSearchConfig = {
46
+ searchProvider: normalizeSearchProvider(raw.searchProvider ?? raw.provider),
47
+ searchModel: normalizeSearchModel(raw.searchModel)
48
+ };
49
+ return cachedSearchConfig;
50
+ }
51
+
52
+ function normalizeSearchModel(value) {
53
+ if (typeof value !== "string") return undefined;
54
+ const normalized = value.trim();
55
+ return normalized.length > 0 ? normalized : undefined;
56
+ }
57
+
58
+ function normalizeSearchProvider(value) {
59
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
60
+ return normalized === "auto" || normalized === "perplexity" || normalized === "gemini" || normalized === "exa" ?
61
+ normalized :
62
+ "auto";
63
+ }
64
+
65
+
66
+
67
+
68
+
69
+
70
+ function errorMessage(err) {
71
+ return err instanceof Error ? err.message : String(err);
72
+ }
73
+
74
+ function isAbortError(err) {
75
+ return errorMessage(err).toLowerCase().includes("abort");
76
+ }
77
+
78
+ async function searchWithGemini(
79
+ query,
80
+ options,
81
+ strictErrors)
82
+ {
83
+ const errors = [];
84
+
85
+ try {
86
+ const apiResult = await searchWithGeminiApi(query, options);
87
+ if (apiResult) return apiResult;
88
+ } catch (err) {
89
+ if (isAbortError(err)) throw err;
90
+ errors.push(`Gemini API: ${errorMessage(err)}`);
91
+ }
92
+
93
+ try {
94
+ const webResult = await searchWithGeminiWeb(query, options);
95
+ if (webResult) return webResult;
96
+ } catch (err) {
97
+ if (isAbortError(err)) throw err;
98
+ errors.push(`Gemini Web: ${errorMessage(err)}`);
99
+ }
100
+
101
+ if (strictErrors && errors.length > 0) {
102
+ throw new Error(`Gemini search failed:\n - ${errors.join("\n - ")}`);
103
+ }
104
+
105
+ return null;
106
+ }
107
+
108
+ async function search(query, options = {}) {
109
+ const config = getSearchConfig();
110
+ const provider = options.provider ?? config.searchProvider;
111
+
112
+ if (provider === "perplexity") {
113
+ const result = await (0, _perplexity.searchWithPerplexity)(query, options);
114
+ return { ...result, provider: "perplexity" };
115
+ }
116
+
117
+ if (provider === "gemini") {
118
+ const result = await searchWithGemini(query, options, true);
119
+ if (result) return { ...result, provider: "gemini" };
120
+ throw new Error(
121
+ "Gemini search unavailable. Either:\n" +
122
+ " 1. Set GEMINI_API_KEY in ~/.pi/web-search.json\n" +
123
+ " 2. Sign into gemini.google.com in a supported Chromium-based browser"
124
+ );
125
+ }
126
+
127
+ if (provider === "exa") {
128
+ const exaApiKeyConfigured = (0, _exa.hasExaApiKey)();
129
+ try {
130
+ const result = await (0, _exa.searchWithExa)(query, options);
131
+ if (result && "exhausted" in result) {
132
+ throw new Error(
133
+ "Exa monthly free tier exhausted (1,000 requests). Resets next month.\n" +
134
+ " Use provider: 'perplexity' or 'gemini', or upgrade at exa.ai/pricing"
135
+ );
136
+ }
137
+ if (result && "answer" in result) return { ...result, provider: "exa" };
138
+ if (exaApiKeyConfigured) {
139
+ throw new Error("Exa search returned no results.");
140
+ }
141
+ } catch (err) {
142
+ const message = err instanceof Error ? err.message : String(err);
143
+ if (message.toLowerCase().includes("abort")) throw err;
144
+ if (exaApiKeyConfigured) throw err;
145
+ // No API key: allow provider fallback.
146
+ }
147
+ }
148
+
149
+ const fallbackErrors = [];
150
+
151
+ if (provider !== "exa" && (0, _exa.isExaAvailable)()) {
152
+ try {
153
+ const result = await (0, _exa.searchWithExa)(query, options);
154
+ if (result && "answer" in result) return { ...result, provider: "exa" };
155
+ } catch (err) {
156
+ if (isAbortError(err)) throw err;
157
+ fallbackErrors.push(`Exa: ${errorMessage(err)}`);
158
+ }
159
+ }
160
+
161
+ if ((0, _perplexity.isPerplexityAvailable)()) {
162
+ try {
163
+ const result = await (0, _perplexity.searchWithPerplexity)(query, options);
164
+ return { ...result, provider: "perplexity" };
165
+ } catch (err) {
166
+ if (isAbortError(err)) throw err;
167
+ fallbackErrors.push(`Perplexity: ${errorMessage(err)}`);
168
+ }
169
+ }
170
+
171
+ try {
172
+ const geminiResult = await searchWithGemini(query, options, false);
173
+ if (geminiResult) return { ...geminiResult, provider: "gemini" };
174
+ } catch (err) {
175
+ if (isAbortError(err)) throw err;
176
+ fallbackErrors.push(`Gemini: ${errorMessage(err)}`);
177
+ }
178
+
179
+ if (fallbackErrors.length > 0) {
180
+ throw new Error(`Auto provider search failed:\n - ${fallbackErrors.join("\n - ")}`);
181
+ }
182
+
183
+ throw new Error(
184
+ "No search provider available. Either:\n" +
185
+ " 1. Set perplexityApiKey in ~/.pi/web-search.json\n" +
186
+ " 2. Set EXA_API_KEY (or exaApiKey) in ~/.pi/web-search.json\n" +
187
+ " 3. Set GEMINI_API_KEY in ~/.pi/web-search.json\n" +
188
+ " 4. Sign into gemini.google.com in a supported Chromium-based browser"
189
+ );
190
+ }
191
+
192
+ async function searchWithGeminiApi(query, options = {}) {
193
+ const apiKey = (0, _geminiApi.getApiKey)();
194
+ if (!apiKey) return null;
195
+
196
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query });
197
+
198
+ try {
199
+ const model = getSearchConfig().searchModel ?? _geminiApi.DEFAULT_MODEL;
200
+ const body = {
201
+ contents: [{ parts: [{ text: query }] }],
202
+ tools: [{ google_search: {} }]
203
+ };
204
+
205
+ const res = await fetch(`${_geminiApi.API_BASE}/models/${model}:generateContent?key=${apiKey}`, {
206
+ method: "POST",
207
+ headers: { "Content-Type": "application/json" },
208
+ body: JSON.stringify(body),
209
+ signal: AbortSignal.any([
210
+ AbortSignal.timeout(60000),
211
+ ...(options.signal ? [options.signal] : [])]
212
+ )
213
+ });
214
+
215
+ if (!res.ok) {
216
+ const errorText = await res.text();
217
+ throw new Error(`Gemini API error ${res.status}: ${errorText.slice(0, 300)}`);
218
+ }
219
+
220
+ const data = await res.json();
221
+ _activity.activityMonitor.logComplete(activityId, res.status);
222
+
223
+ const answer = data.candidates?.[0]?.content?.parts?.
224
+ map((p) => p.text).filter(Boolean).join("\n") ?? "";
225
+
226
+ const metadata = data.candidates?.[0]?.groundingMetadata;
227
+ const results = await resolveGroundingChunks(metadata?.groundingChunks, options.signal);
228
+
229
+ if (!answer && results.length === 0) return null;
230
+ return { answer, results };
231
+ } catch (err) {
232
+ const message = err instanceof Error ? err.message : String(err);
233
+ if (message.toLowerCase().includes("abort")) {
234
+ _activity.activityMonitor.logComplete(activityId, 0);
235
+ } else {
236
+ _activity.activityMonitor.logError(activityId, message);
237
+ }
238
+ throw err;
239
+ }
240
+ }
241
+
242
+ async function searchWithGeminiWeb(query, options = {}) {
243
+ const cookies = await (0, _geminiWeb.isGeminiWebAvailable)();
244
+ if (!cookies) return null;
245
+
246
+ const prompt = buildSearchPrompt(query, options);
247
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query });
248
+
249
+ try {
250
+ const text = await (0, _geminiWeb.queryWithCookies)(prompt, cookies, {
251
+ model: "gemini-3-flash-preview",
252
+ signal: options.signal,
253
+ timeoutMs: 60000
254
+ });
255
+
256
+ _activity.activityMonitor.logComplete(activityId, 200);
257
+
258
+ const results = extractSourceUrls(text);
259
+ return { answer: text, results };
260
+ } catch (err) {
261
+ const message = err instanceof Error ? err.message : String(err);
262
+ if (message.toLowerCase().includes("abort")) {
263
+ _activity.activityMonitor.logComplete(activityId, 0);
264
+ } else {
265
+ _activity.activityMonitor.logError(activityId, message);
266
+ }
267
+ throw err;
268
+ }
269
+ }
270
+
271
+ function buildSearchPrompt(query, options) {
272
+ let prompt = `Search the web and answer the following question. Include source URLs for your claims.\nFormat your response as:\n1. A direct answer to the question\n2. Cited sources as markdown links\n\nQuestion: ${query}`;
273
+
274
+ if (options.recencyFilter) {
275
+ const labels = {
276
+ day: "past 24 hours",
277
+ week: "past week",
278
+ month: "past month",
279
+ year: "past year"
280
+ };
281
+ prompt += `\n\nOnly include results from the ${labels[options.recencyFilter]}.`;
282
+ }
283
+
284
+ if (options.domainFilter?.length) {
285
+ const includes = options.domainFilter.filter((d) => !d.startsWith("-"));
286
+ const excludes = options.domainFilter.filter((d) => d.startsWith("-")).map((d) => d.slice(1));
287
+ if (includes.length) prompt += `\n\nOnly cite sources from: ${includes.join(", ")}`;
288
+ if (excludes.length) prompt += `\n\nDo not cite sources from: ${excludes.join(", ")}`;
289
+ }
290
+
291
+ return prompt;
292
+ }
293
+
294
+ function extractSourceUrls(markdown) {
295
+ const results = [];
296
+ const seen = new Set();
297
+ const linkRegex = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
298
+ for (const match of markdown.matchAll(linkRegex)) {
299
+ const url = match[2];
300
+ if (seen.has(url)) continue;
301
+ seen.add(url);
302
+ results.push({ title: match[1], url, snippet: "" });
303
+ }
304
+ return results;
305
+ }
306
+
307
+ async function resolveGroundingChunks(
308
+ chunks,
309
+ signal)
310
+ {
311
+ if (!chunks?.length) return [];
312
+
313
+ const results = [];
314
+ for (const chunk of chunks) {
315
+ if (!chunk.web) continue;
316
+ const title = chunk.web.title || "";
317
+ let url = chunk.web.uri || "";
318
+
319
+ if (url.includes("vertexaisearch.cloud.google.com/grounding-api-redirect")) {
320
+ const resolved = await resolveRedirect(url, signal);
321
+ if (resolved) url = resolved;
322
+ }
323
+
324
+ if (url) results.push({ title, url, snippet: "" });
325
+ }
326
+ return results;
327
+ }
328
+
329
+ async function resolveRedirect(proxyUrl, signal) {
330
+ try {
331
+ const res = await fetch(proxyUrl, {
332
+ method: "HEAD",
333
+ redirect: "manual",
334
+ signal: AbortSignal.any([
335
+ AbortSignal.timeout(5000),
336
+ ...(signal ? [signal] : [])]
337
+ )
338
+ });
339
+ return res.headers.get("location") || null;
340
+ } catch {
341
+ return null;
342
+ }
343
+ } /* v9-e316b01b2036b15f */
pip-tmp/jiti/pi-web-access-gemini-url-context.ea2bdc30.mjs ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.extractWithGeminiWeb = extractWithGeminiWeb;exports.extractWithUrlContext = extractWithUrlContext;var _activity = await jitiImport("./activity.js");
2
+ var _geminiApi = await jitiImport("./gemini-api.js");
3
+ var _geminiWeb = await jitiImport("./gemini-web.js");
4
+ var _extract = await jitiImport("./extract.js");
5
+
6
+ const EXTRACTION_PROMPT = `Extract the complete readable content from this URL as clean markdown.
7
+ Include the page title, all text content, code blocks, and tables.
8
+ Do not summarize — extract the full content.
9
+
10
+ URL: `;
11
+
12
+ function shouldRethrow(err) {
13
+ const message = err instanceof Error ? err.message : String(err);
14
+ return message.startsWith("Failed to parse ");
15
+ }
16
+
17
+ async function extractWithUrlContext(
18
+ url,
19
+ signal)
20
+ {
21
+ const apiKey = (0, _geminiApi.getApiKey)();
22
+ if (!apiKey) return null;
23
+
24
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query: `url_context: ${url}` });
25
+
26
+ try {
27
+ const model = _geminiApi.DEFAULT_MODEL;
28
+ const body = {
29
+ contents: [{ parts: [{ text: EXTRACTION_PROMPT + url }] }],
30
+ tools: [{ url_context: {} }]
31
+ };
32
+
33
+ const res = await fetch(`${_geminiApi.API_BASE}/models/${model}:generateContent?key=${apiKey}`, {
34
+ method: "POST",
35
+ headers: { "Content-Type": "application/json" },
36
+ body: JSON.stringify(body),
37
+ signal: AbortSignal.any([
38
+ AbortSignal.timeout(60000),
39
+ ...(signal ? [signal] : [])]
40
+ )
41
+ });
42
+
43
+ if (!res.ok) {
44
+ _activity.activityMonitor.logComplete(activityId, res.status);
45
+ return null;
46
+ }
47
+
48
+ const data = await res.json();
49
+ _activity.activityMonitor.logComplete(activityId, res.status);
50
+
51
+ const metadata = data.candidates?.[0]?.url_context_metadata;
52
+ if (metadata?.url_metadata?.length) {
53
+ const status = metadata.url_metadata[0].url_retrieval_status;
54
+ if (status === "URL_RETRIEVAL_STATUS_UNSAFE" || status === "URL_RETRIEVAL_STATUS_ERROR") {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ const content = data.candidates?.[0]?.content?.parts?.
60
+ map((p) => p.text).filter(Boolean).join("\n") ?? "";
61
+
62
+ if (!content || content.length < 50) return null;
63
+
64
+ const title = extractTitleFromContent(content, url);
65
+ return { url, title, content, error: null };
66
+ } catch (err) {
67
+ if (shouldRethrow(err)) throw err;
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ if (message.toLowerCase().includes("abort")) {
70
+ _activity.activityMonitor.logComplete(activityId, 0);
71
+ } else {
72
+ _activity.activityMonitor.logError(activityId, message);
73
+ }
74
+ return null;
75
+ }
76
+ }
77
+
78
+ async function extractWithGeminiWeb(
79
+ url,
80
+ signal)
81
+ {
82
+ const cookies = await (0, _geminiWeb.isGeminiWebAvailable)();
83
+ if (!cookies) return null;
84
+
85
+ const activityId = _activity.activityMonitor.logStart({ type: "api", query: `gemini_web: ${url}` });
86
+
87
+ try {
88
+ const text = await (0, _geminiWeb.queryWithCookies)(EXTRACTION_PROMPT + url, cookies, {
89
+ model: "gemini-3-flash-preview",
90
+ signal,
91
+ timeoutMs: 60000
92
+ });
93
+
94
+ _activity.activityMonitor.logComplete(activityId, 200);
95
+
96
+ if (!text || text.length < 50) return null;
97
+
98
+ const title = extractTitleFromContent(text, url);
99
+ return { url, title, content: text, error: null };
100
+ } catch (err) {
101
+ if (shouldRethrow(err)) throw err;
102
+ const message = err instanceof Error ? err.message : String(err);
103
+ if (message.toLowerCase().includes("abort")) {
104
+ _activity.activityMonitor.logComplete(activityId, 0);
105
+ } else {
106
+ _activity.activityMonitor.logError(activityId, message);
107
+ }
108
+ return null;
109
+ }
110
+ }
111
+
112
+ function extractTitleFromContent(text, url) {
113
+ return (0, _extract.extractHeadingTitle)(text) ?? (new URL(url).pathname.split("/").pop() || url);
114
+ } /* v9-29a641a47ee49837 */
pip-tmp/jiti/pi-web-access-gemini-web-config.612faa3d.mjs ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.getChromeProfileFromConfig = getChromeProfileFromConfig;exports.isBrowserCookieAccessAllowed = isBrowserCookieAccessAllowed;exports.normalizeChromeProfile = normalizeChromeProfile;var _nodeFs = await jitiImport("node:fs");
2
+ var _nodeOs = await jitiImport("node:os");
3
+ var _nodePath = await jitiImport("node:path");
4
+
5
+ const CONFIG_PATH = (0, _nodePath.join)((0, _nodeOs.homedir)(), ".pi", "web-search.json");
6
+
7
+
8
+
9
+
10
+
11
+
12
+ let cachedConfig = null;
13
+
14
+ function normalizeChromeProfile(value) {
15
+ if (typeof value !== "string") return undefined;
16
+ const normalized = value.trim();
17
+ return normalized.length > 0 ? normalized : undefined;
18
+ }
19
+
20
+ function loadConfig() {
21
+ if (cachedConfig) return cachedConfig;
22
+ if (!(0, _nodeFs.existsSync)(CONFIG_PATH)) {
23
+ cachedConfig = {};
24
+ return cachedConfig;
25
+ }
26
+
27
+ const rawText = (0, _nodeFs.readFileSync)(CONFIG_PATH, "utf-8");
28
+ let raw;
29
+ try {
30
+ raw = JSON.parse(rawText);
31
+ } catch (err) {
32
+ const message = err instanceof Error ? err.message : String(err);
33
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
34
+ }
35
+
36
+ cachedConfig = {
37
+ chromeProfile: normalizeChromeProfile(raw.chromeProfile),
38
+ allowBrowserCookies: raw.allowBrowserCookies === true
39
+ };
40
+ return cachedConfig;
41
+ }
42
+
43
+ function getChromeProfileFromConfig() {
44
+ return loadConfig().chromeProfile;
45
+ }
46
+
47
+ function isBrowserCookieAccessAllowed() {
48
+ if (process.env.PI_ALLOW_BROWSER_COOKIES === "1" || process.env.FEYNMAN_ALLOW_BROWSER_COOKIES === "1") {
49
+ return true;
50
+ }
51
+ return loadConfig().allowBrowserCookies === true;
52
+ } /* v9-3c5f441b5ffff223 */