Edwin Jose Palathinkal commited on
Commit ·
f9ede04
1
Parent(s): 7d14ffe
Remove redundant README files - using single README.md for both platforms
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- .gitignore +0 -3
- CHANGELOG.md +0 -56
- README.md.git +0 -159
- README.md.tmp +0 -114
- README_HF.md +0 -114
- convert_checkpoint.py +0 -29
- namer/__init__.py +1 -24
- namer/modeling_namer.py +0 -342
- pip-tmp/jiti/agents-agent-management.2587ecf7.mjs +643 -0
- pip-tmp/jiti/agents-agent-scope.515c0bc4.mjs +6 -0
- pip-tmp/jiti/agents-agent-selection.a867544e.mjs +23 -0
- pip-tmp/jiti/agents-agent-serializer.61dec55f.mjs +84 -0
- pip-tmp/jiti/agents-agents.f0509ee2.mjs +808 -0
- pip-tmp/jiti/agents-chain-serializer.a832da0e.mjs +137 -0
- pip-tmp/jiti/agents-frontmatter.6976eaf1.mjs +29 -0
- pip-tmp/jiti/agents-identity.568fcc02.mjs +30 -0
- pip-tmp/jiti/agents-skills.af023ec8.mjs +630 -0
- pip-tmp/jiti/background-async-execution.0e72180b.mjs +578 -0
- pip-tmp/jiti/background-async-job-tracker.6fb217e9.mjs +267 -0
- pip-tmp/jiti/background-async-resume.8fb594f6.mjs +332 -0
- pip-tmp/jiti/background-async-status.51801569.mjs +292 -0
- pip-tmp/jiti/background-completion-dedupe.b4a5ebb9.mjs +63 -0
- pip-tmp/jiti/background-notify.20a22f54.mjs +108 -0
- pip-tmp/jiti/background-parallel-groups.e70893a1.mjs +45 -0
- pip-tmp/jiti/background-result-watcher.056baed0.mjs +250 -0
- pip-tmp/jiti/background-run-status.37d535df.mjs +190 -0
- pip-tmp/jiti/background-stale-run-reconciler.52d9f1fc.mjs +291 -0
- pip-tmp/jiti/background-top-level-async.2257c9e0.mjs +13 -0
- pip-tmp/jiti/extension-control-notices.20f5052e.mjs +92 -0
- pip-tmp/jiti/extension-doctor.43600346.mjs +199 -0
- pip-tmp/jiti/extension-index.80330c2b.mjs +585 -0
- pip-tmp/jiti/extension-schemas.9e6ee9e3.mjs +168 -0
- pip-tmp/jiti/foreground-chain-clarify.558277eb.mjs +1333 -0
- pip-tmp/jiti/foreground-chain-execution.e441430b.mjs +932 -0
- pip-tmp/jiti/foreground-execution.7bdac23f.mjs +902 -0
- pip-tmp/jiti/foreground-subagent-executor.425806a0.mjs +2232 -0
- pip-tmp/jiti/intercom-intercom-bridge.88630197.mjs +379 -0
- pip-tmp/jiti/intercom-result-intercom.32a45520.mjs +269 -0
- pip-tmp/jiti/pi-web-access-activity.4dfe2849.mjs +101 -0
- pip-tmp/jiti/pi-web-access-chrome-cookies.ad56e10d.mjs +322 -0
- pip-tmp/jiti/pi-web-access-code-search.f93c7d37.mjs +107 -0
- pip-tmp/jiti/pi-web-access-curator-page.f382e376.mjs +0 -0
- pip-tmp/jiti/pi-web-access-curator-server.2a16f6fa.mjs +605 -0
- pip-tmp/jiti/pi-web-access-exa.b5c6a885.mjs +520 -0
- pip-tmp/jiti/pi-web-access-extract.baf16c13.mjs +641 -0
- pip-tmp/jiti/pi-web-access-gemini-api.b8e0a3b9.mjs +104 -0
- pip-tmp/jiti/pi-web-access-gemini-search.037a514f.mjs +343 -0
- pip-tmp/jiti/pi-web-access-gemini-url-context.ea2bdc30.mjs +114 -0
- 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 |
-
[](https://huggingface.co/edwinhere/namer)
|
| 15 |
-
[](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.
|
| 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 */
|