@selesai/code 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +7 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/defaults/models.json +46 -55
- package/dist/defaults/settings.json +7 -8
- package/dist/extensions/agent-browser.test.ts +178 -2
- package/dist/extensions/agent-browser.ts +2 -0
- package/dist/extensions/copy-turn.test.ts +229 -0
- package/dist/extensions/copy-turn.ts +11 -0
- package/dist/extensions/grep-app/index.test.ts +356 -1
- package/dist/extensions/grep-app/index.ts +6 -1
- package/dist/extensions/handoff-new.test.ts +351 -161
- package/dist/extensions/handoff-new.ts +3 -0
- package/dist/extensions/inline-skills.test.ts +93 -0
- package/dist/extensions/inline-skills.ts +3 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +26 -3
- package/dist/extensions/pi-subagents/README.md +2 -0
- package/dist/extensions/pi-subagents/agents/builder.md +5 -2
- package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +54 -2
- package/dist/extensions/pi-subagents/docs/observability.md +1 -1
- package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agents.ts +9 -3
- package/dist/extensions/pi-subagents/src/extension/config.ts +12 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +40 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +104 -14
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +5 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +3 -9
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +2 -2
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -1
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +4 -7
- package/dist/extensions/pi-subagents/src/missions/store.ts +4 -4
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +374 -0
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +117 -29
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +14 -5
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +33 -15
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +104 -0
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +17 -3
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +19 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +84 -36
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +37 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +135 -22
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +171 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +653 -196
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +13 -4
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +286 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +23 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +44 -1
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +19 -3
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +17 -5
- package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +108 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -1
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +7 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +226 -13
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +22 -5
- package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +96 -6
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +28 -5
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +8 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +83 -2
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +211 -15
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +41 -3
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +314 -10
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +317 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +21 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +16 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +75 -4
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +31 -5
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +139 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +171 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +30 -4
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +83 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +55 -2
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +9 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +39 -0
- package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +61 -0
- package/dist/extensions/ponytail/index.js +11 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
- package/dist/extensions/ponytail/test/extension.test.js +274 -140
- package/dist/extensions/ponytail/test/helpers.test.js +280 -92
- package/dist/extensions/question/index.ts +18 -0
- package/dist/extensions/question/question-list.ts +22 -0
- package/dist/extensions/question/tests/batch.test.ts +103 -65
- package/dist/extensions/question/tests/helpers.test.ts +68 -35
- package/dist/extensions/question/tests/question-list.test.ts +506 -204
- package/dist/extensions/question/tests/row-layout.test.ts +180 -114
- package/dist/extensions/question/tests/schemas.test.ts +42 -19
- package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
- package/dist/extensions/question/tests/wizard.test.ts +771 -50
- package/dist/extensions/rtk.test.ts +180 -1
- package/dist/extensions/tokenin-onboarding.ts +3 -0
- package/dist/extensions/undo.test.ts +720 -0
- package/dist/extensions/undo.ts +6 -0
- package/dist/extensions/web-agent-onboarding.test.ts +415 -0
- package/dist/extensions/workflow/extension.ts +3 -3
- package/dist/extensions/workflow/modes.ts +41 -19
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.js +48 -1
- package/dist/skills/pi-subagents/references/execution-controls.md +1 -1
- package/docs/plans/workflow-autoloop-reference.md +1 -2
- package/docs/plans/workflow-handoff-carryover.md +108 -0
- package/docs/quickstart.md +27 -43
- package/docs/settings.md +4 -0
- package/docs/usage.md +1 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to `@selesai/code` will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.6] - 2026-08-15
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Auto-relaunching workflows.** The four `/workflow-*` modes (task, prototype, quicktype, loop) now run fully unattended: when a scripted workflow exhausts its per-run fan-out budget before the goal is clean, the async executor mints a fresh budget and re-runs the same script with the same mission and progress file, up to `config.maxWorkflowAutoRelaunches` (default 12; `0` = unlimited). Only `budget` results relaunch — real child failures still surface immediately. `AsyncStatus` gained `workflowRelaunchCount`.
|
|
11
|
+
- **Packaged prompts now load.** The pi-subagents extension registers `resources_discover`, so the bundled prompts (`review-loop.md`, `parallel-*.md`, `gather-context-and-clarify.md`) are actually discoverable. The `workflow-drive.md` prompt was removed as superseded by auto-relaunch.
|
|
12
|
+
- **Goal visibility on workflow launches.** Each `/workflow-*` launch emits its goal at start (visible in `subagent status` workflow emits) and names the auto-created mission after the goal (`mission.list` / `mission.show`).
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Bounded build→review→fix rounds.** The workflow auto-loop now instructs the builder to implement one small, self-contained slice per round ("do not attempt the whole plan") instead of the entire plan at once, with per-run timeouts (build/fix 45m, review 15m).
|
|
16
|
+
- **Progress-file scoped reviews.** Builder appends a `## Round N` ledger entry (files, summary, validation) to `.pi-subagents/progress/<mode>.md`; the commentator scopes its review to the latest round entry plus the immediately preceding fix entry, falling back to the full uncommitted diff when the file is missing or empty. Blocking reviews must list a `Remaining work:` section; the next build round picks up from it.
|
|
17
|
+
- **Previous review feedback now reaches the next builder round** (previously it was dropped).
|
|
18
|
+
|
|
19
|
+
## [0.8.5] - 2026-08-14
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **`/settings-factory-reset` slash command.** Restores `~/.selesai/agent/settings.json` to the bundled factory defaults after a warning and confirmation. A backup is saved as `settings.json.bak` before the reset, credentials (`auth.json`), sessions, extensions, skills, and themes are untouched, and settings reload live without a restart.
|
|
23
|
+
- **Install from the public repository.** `npm install -g github:SelesaiInTech/selesai-code` now works: the package gained a `prepare` script that builds `dist` from source on install.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- **Simplified install guide.** The documented install command is now plain `npm install -g @selesai/code`. The previous `--ignore-scripts` advice was actively harmful: `@ast-grep/cli` (a runtime dependency) requires its `postinstall` to fetch its platform binary. Quickstart, README, and doc-web install pages updated, and quickstart no longer references the upstream `pi` binary or `~/.pi` paths.
|
|
27
|
+
- **Bumped default `retry.maxRetries` from 5 to 7** in bundled defaults.
|
|
28
|
+
|
|
7
29
|
## [0.8.4] - 2026-08-13
|
|
8
30
|
|
|
9
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ The goal is simple: make a capable coding agent useful out of the box, maximize
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install -g
|
|
10
|
+
npm install -g @selesai/code
|
|
11
11
|
selesai
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -17,6 +17,12 @@ For a one-off run using the latest published package:
|
|
|
17
17
|
npx @selesai/code
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
To install directly from the public repository (builds the latest `main` from source):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g github:SelesaiInTech/selesai-code
|
|
24
|
+
```
|
|
25
|
+
|
|
20
26
|
The published npm package is **`@selesai/code`** and its executable is **`selesai`**. Selesai uses `~/.selesai/agent` for user state and `.selesai/` for project-local settings and resources.
|
|
21
27
|
|
|
22
28
|
Start with a prompt:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { APP_NAME } from "../config.js";
|
|
2
2
|
export const BUILTIN_SLASH_COMMANDS = [
|
|
3
3
|
{ name: "settings", description: "Open settings menu" },
|
|
4
|
+
{ name: "settings-factory-reset", description: "Reset settings to factory defaults (with confirmation)" },
|
|
4
5
|
{ name: "model", description: "Select model (opens selector UI)", argumentHint: "<provider/model>" },
|
|
5
6
|
{ name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },
|
|
6
7
|
{ name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
|
|
@@ -15,13 +15,8 @@
|
|
|
15
15
|
"name": "DeepSeek V4 Flash",
|
|
16
16
|
"reasoning": true,
|
|
17
17
|
"input": ["text"],
|
|
18
|
-
"contextWindow":
|
|
19
|
-
"
|
|
20
|
-
"input": 0.15,
|
|
21
|
-
"output": 0.3,
|
|
22
|
-
"cacheRead": 0.003,
|
|
23
|
-
"cacheWrite": 0
|
|
24
|
-
},
|
|
18
|
+
"contextWindow": 512000,
|
|
19
|
+
"maxTokens": 64000,
|
|
25
20
|
"thinkingLevelMap": {
|
|
26
21
|
"minimal": null,
|
|
27
22
|
"low": null,
|
|
@@ -37,50 +32,40 @@
|
|
|
37
32
|
}
|
|
38
33
|
},
|
|
39
34
|
{
|
|
40
|
-
"id": "
|
|
41
|
-
"name": "
|
|
35
|
+
"id": "deepseek-v4-pro",
|
|
36
|
+
"name": "DeepSeek V4 pro",
|
|
42
37
|
"reasoning": true,
|
|
43
|
-
"input": ["text"
|
|
44
|
-
"contextWindow":
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
38
|
+
"input": ["text"],
|
|
39
|
+
"contextWindow": 512000,
|
|
40
|
+
"maxTokens": 64000,
|
|
41
|
+
"thinkingLevelMap": {
|
|
42
|
+
"minimal": null,
|
|
43
|
+
"low": null,
|
|
44
|
+
"medium": null,
|
|
45
|
+
"high": null,
|
|
46
|
+
"xhigh": null,
|
|
47
|
+
"max": "max"
|
|
50
48
|
},
|
|
51
49
|
"compat": {
|
|
52
50
|
"supportsDeveloperRole": false,
|
|
53
|
-
"
|
|
51
|
+
"requiresReasoningContentOnAssistantMessages": true,
|
|
52
|
+
"thinkingFormat": "deepseek"
|
|
54
53
|
}
|
|
55
54
|
},
|
|
56
55
|
{
|
|
57
|
-
"id": "kimi-
|
|
58
|
-
"name": "Kimi
|
|
56
|
+
"id": "kimi-k3",
|
|
57
|
+
"name": "Kimi K3",
|
|
59
58
|
"reasoning": true,
|
|
60
59
|
"input": ["text", "image"],
|
|
61
|
-
"contextWindow":
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
70
|
-
"supportsReasoningEffort": false
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"id": "qwen3.5-397b",
|
|
75
|
-
"name": "Qwen3.5 397B",
|
|
76
|
-
"reasoning": true,
|
|
77
|
-
"input": ["text"],
|
|
78
|
-
"contextWindow": 262128,
|
|
79
|
-
"cost": {
|
|
80
|
-
"input": 0.69,
|
|
81
|
-
"output": 4.14,
|
|
82
|
-
"cacheRead": 0.1725,
|
|
83
|
-
"cacheWrite": 0
|
|
60
|
+
"contextWindow": 512000,
|
|
61
|
+
"maxTokens": 64000,
|
|
62
|
+
"thinkingLevelMap": {
|
|
63
|
+
"minimal": null,
|
|
64
|
+
"low": "low",
|
|
65
|
+
"medium": null,
|
|
66
|
+
"high": "high",
|
|
67
|
+
"xhigh": null,
|
|
68
|
+
"max": "max"
|
|
84
69
|
},
|
|
85
70
|
"compat": {
|
|
86
71
|
"supportsDeveloperRole": false,
|
|
@@ -88,16 +73,19 @@
|
|
|
88
73
|
}
|
|
89
74
|
},
|
|
90
75
|
{
|
|
91
|
-
"id": "
|
|
92
|
-
"name": "
|
|
76
|
+
"id": "kimi-k2.7-code",
|
|
77
|
+
"name": "Kimi K2.7 Code",
|
|
93
78
|
"reasoning": true,
|
|
94
79
|
"input": ["text", "image"],
|
|
95
|
-
"contextWindow":
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
80
|
+
"contextWindow": 262128,
|
|
81
|
+
"maxTokens": 64000,
|
|
82
|
+
"thinkingLevelMap": {
|
|
83
|
+
"minimal": null,
|
|
84
|
+
"low": "low",
|
|
85
|
+
"medium": null,
|
|
86
|
+
"high": "high",
|
|
87
|
+
"xhigh": null,
|
|
88
|
+
"max": "max"
|
|
101
89
|
},
|
|
102
90
|
"compat": {
|
|
103
91
|
"supportsDeveloperRole": false,
|
|
@@ -110,11 +98,14 @@
|
|
|
110
98
|
"reasoning": true,
|
|
111
99
|
"input": ["text"],
|
|
112
100
|
"contextWindow": 1000000,
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
101
|
+
"maxTokens": 64000,
|
|
102
|
+
"thinkingLevelMap": {
|
|
103
|
+
"minimal": null,
|
|
104
|
+
"low": "low",
|
|
105
|
+
"medium": null,
|
|
106
|
+
"high": "high",
|
|
107
|
+
"xhigh": null,
|
|
108
|
+
"max": null
|
|
118
109
|
},
|
|
119
110
|
"compat": {
|
|
120
111
|
"supportsDeveloperRole": false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"autoHandoff": {
|
|
3
|
-
"enabled":
|
|
4
|
-
"thresholdTokens":
|
|
3
|
+
"enabled": true,
|
|
4
|
+
"thresholdTokens": 256000
|
|
5
5
|
},
|
|
6
6
|
"collapseChangelog": true,
|
|
7
7
|
"compaction": {
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
"keepRecentTokens": 32000,
|
|
10
10
|
"reserveTokens": 64000
|
|
11
11
|
},
|
|
12
|
-
"defaultModel": "
|
|
12
|
+
"defaultModel": "deepseek-v4-flash",
|
|
13
13
|
"defaultProvider": "tokenin",
|
|
14
|
-
"defaultThinkingLevel": "
|
|
14
|
+
"defaultThinkingLevel": "max",
|
|
15
15
|
"doubleEscapeAction": "tree",
|
|
16
16
|
"editorPaddingX": 0,
|
|
17
17
|
"outputPad": 0,
|
|
18
|
-
"showCacheMissNotices":
|
|
18
|
+
"showCacheMissNotices": false,
|
|
19
19
|
"followUpMode": "one-at-a-time",
|
|
20
20
|
"hideThinkingBlock": false,
|
|
21
21
|
"images": {
|
|
22
22
|
"autoResize": true
|
|
23
23
|
},
|
|
24
|
-
"lastChangelogVersion": "0.80.2",
|
|
25
24
|
"packages": [
|
|
26
25
|
"git:github.com/MasuRii/pi-tool-display",
|
|
27
26
|
"git:github.com/omaclaren/pi-markdown-preview"
|
|
@@ -29,7 +28,7 @@
|
|
|
29
28
|
"quietStartup": true,
|
|
30
29
|
"retry": {
|
|
31
30
|
"enabled": true,
|
|
32
|
-
"maxRetries":
|
|
31
|
+
"maxRetries": 7
|
|
33
32
|
},
|
|
34
33
|
"steeringMode": "all",
|
|
35
34
|
"terminal": {
|
|
@@ -39,7 +38,7 @@
|
|
|
39
38
|
"subagents": {
|
|
40
39
|
"agentOverrides": {
|
|
41
40
|
"architect": {
|
|
42
|
-
"model": "tokenin/
|
|
41
|
+
"model": "tokenin/deepseek-v4-pro:max"
|
|
43
42
|
},
|
|
44
43
|
"builder": {
|
|
45
44
|
"model": "tokenin/deepseek-v4-flash:max"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
5
|
import agentBrowserSetupExtension, {
|
|
6
6
|
DECLINED_MARKER_NAME,
|
|
7
7
|
ensureAgentBrowser,
|
|
@@ -192,4 +192,180 @@ describe("agent-browser setup extension", () => {
|
|
|
192
192
|
it("declined marker path lives in the agent dir", () => {
|
|
193
193
|
expect(getDeclinedMarkerPath()).toBe(join(tmpDir, DECLINED_MARKER_NAME));
|
|
194
194
|
});
|
|
195
|
+
|
|
196
|
+
it("skips probing when the decline marker exists", async () => {
|
|
197
|
+
const { pi, handlers, exec } = makePi();
|
|
198
|
+
await agentBrowserSetupExtension(pi);
|
|
199
|
+
const markerPath = getDeclinedMarkerPath();
|
|
200
|
+
mkdirSync(dirname(markerPath), { recursive: true });
|
|
201
|
+
writeFileSync(markerPath, "1", "utf-8");
|
|
202
|
+
try {
|
|
203
|
+
await getSessionStartHandler(handlers)({ type: "session_start", reason: "startup" }, makeCtx());
|
|
204
|
+
expect(exec).not.toHaveBeenCalled();
|
|
205
|
+
} finally {
|
|
206
|
+
rmSync(markerPath, { force: true });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("isAgentBrowserInstalled returns false when exec throws", async () => {
|
|
211
|
+
const { pi } = makePi(async () => {
|
|
212
|
+
throw new Error("spawn failed");
|
|
213
|
+
});
|
|
214
|
+
expect(await isAgentBrowserInstalled(pi)).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("isAgentBrowserInstalled returns false when the probe is killed", async () => {
|
|
218
|
+
const { pi } = makePi(async () => ({ code: 0, stdout: "", stderr: "", killed: true }));
|
|
219
|
+
expect(await isAgentBrowserInstalled(pi)).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("reports a timed-out npm install", async () => {
|
|
223
|
+
const execImpl = vi
|
|
224
|
+
.fn()
|
|
225
|
+
.mockResolvedValueOnce(fail) // probe: missing
|
|
226
|
+
.mockResolvedValueOnce({ code: 0, stdout: "", stderr: "", killed: true }); // npm killed
|
|
227
|
+
const { pi, handlers, exec } = makePi(execImpl as any);
|
|
228
|
+
await agentBrowserSetupExtension(pi);
|
|
229
|
+
const ctx = makeCtx();
|
|
230
|
+
await getSessionStartHandler(handlers)({ type: "session_start", reason: "startup" }, ctx);
|
|
231
|
+
|
|
232
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("timed out"), "error");
|
|
233
|
+
expect(exec).toHaveBeenCalledTimes(2);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("reports npm success but missing binary on PATH", async () => {
|
|
237
|
+
const execImpl = vi
|
|
238
|
+
.fn()
|
|
239
|
+
.mockResolvedValueOnce(fail) // probe: missing
|
|
240
|
+
.mockResolvedValueOnce(ok) // npm i -g agent-browser
|
|
241
|
+
.mockResolvedValueOnce(fail) // probe: still missing
|
|
242
|
+
.mockResolvedValueOnce(ok); // (unused)
|
|
243
|
+
const { pi, handlers, exec } = makePi(execImpl as any);
|
|
244
|
+
await agentBrowserSetupExtension(pi);
|
|
245
|
+
const ctx = makeCtx();
|
|
246
|
+
await getSessionStartHandler(handlers)({ type: "session_start", reason: "startup" }, ctx);
|
|
247
|
+
|
|
248
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("still not on PATH"), "warning");
|
|
249
|
+
expect(exec).toHaveBeenCalledTimes(3);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("warns when browser provisioning fails after install", async () => {
|
|
253
|
+
const execImpl = vi
|
|
254
|
+
.fn()
|
|
255
|
+
.mockResolvedValueOnce(fail) // probe: missing
|
|
256
|
+
.mockResolvedValueOnce(ok) // npm i -g agent-browser
|
|
257
|
+
.mockResolvedValueOnce(ok) // probe: now present
|
|
258
|
+
.mockResolvedValueOnce({ code: 1, stdout: "", stderr: "browser failed", killed: false }); // agent-browser install fails
|
|
259
|
+
const { pi, handlers, exec } = makePi(execImpl as any);
|
|
260
|
+
await agentBrowserSetupExtension(pi);
|
|
261
|
+
const ctx = makeCtx();
|
|
262
|
+
await getSessionStartHandler(handlers)({ type: "session_start", reason: "startup" }, ctx);
|
|
263
|
+
|
|
264
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("Run `agent-browser install`"), "info");
|
|
265
|
+
expect(exec).toHaveBeenCalledTimes(4);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("provisionBrowser returns false when exec throws", async () => {
|
|
269
|
+
const execImpl = vi
|
|
270
|
+
.fn()
|
|
271
|
+
.mockResolvedValueOnce(fail) // probe: missing
|
|
272
|
+
.mockResolvedValueOnce(ok) // npm i -g agent-browser
|
|
273
|
+
.mockResolvedValueOnce(ok) // probe: now present
|
|
274
|
+
.mockRejectedValueOnce(new Error("spawn failed")); // agent-browser install throws
|
|
275
|
+
const { pi, handlers, exec } = makePi(execImpl as any);
|
|
276
|
+
await agentBrowserSetupExtension(pi);
|
|
277
|
+
const ctx = makeCtx();
|
|
278
|
+
await getSessionStartHandler(handlers)({ type: "session_start", reason: "startup" }, ctx);
|
|
279
|
+
|
|
280
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("Run `agent-browser install`"), "info");
|
|
281
|
+
expect(exec).toHaveBeenCalledTimes(4);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("ensureAgentBrowser returns false when the user declines", async () => {
|
|
285
|
+
const execImpl = vi.fn().mockResolvedValue(fail);
|
|
286
|
+
const { pi } = makePi(execImpl as any);
|
|
287
|
+
const ctx = makeCtx({ confirmResult: false });
|
|
288
|
+
expect(await ensureAgentBrowser(pi, ctx)).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("ensureAgentBrowser returns true when already installed", async () => {
|
|
292
|
+
const { pi } = makePi(async () => ok);
|
|
293
|
+
expect(await ensureAgentBrowser(pi, makeCtx())).toBe(true);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("ensureAgentBrowser returns false without UI", async () => {
|
|
297
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
298
|
+
const execImpl = vi.fn().mockResolvedValue(fail);
|
|
299
|
+
const { pi } = makePi(execImpl as any);
|
|
300
|
+
expect(await ensureAgentBrowser(pi, makeCtx({ hasUI: false }))).toBe(false);
|
|
301
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining(INSTALL_COMMAND));
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("ensureAgentBrowser returns false when npm install fails", async () => {
|
|
305
|
+
const execImpl = vi
|
|
306
|
+
.fn()
|
|
307
|
+
.mockResolvedValueOnce(fail) // probe
|
|
308
|
+
.mockResolvedValueOnce({ code: 1, stdout: "", stderr: "EACCES", killed: false }); // npm fails
|
|
309
|
+
const { pi } = makePi(execImpl as any);
|
|
310
|
+
expect(await ensureAgentBrowser(pi, makeCtx())).toBe(false);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("npm install failure surfaces stdout when stderr is empty", async () => {
|
|
314
|
+
const execImpl = vi
|
|
315
|
+
.fn()
|
|
316
|
+
.mockResolvedValueOnce(fail) // probe
|
|
317
|
+
.mockResolvedValueOnce({ code: 1, stdout: "npm error log", stderr: "", killed: false }); // npm fails with stdout only
|
|
318
|
+
const { pi } = makePi(execImpl as any);
|
|
319
|
+
const ctx = makeCtx();
|
|
320
|
+
expect(await ensureAgentBrowser(pi, ctx)).toBe(false);
|
|
321
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("npm error log"), "error");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("npm install failure falls back to exit status when output is empty", async () => {
|
|
325
|
+
const execImpl = vi
|
|
326
|
+
.fn()
|
|
327
|
+
.mockResolvedValueOnce(fail) // probe
|
|
328
|
+
.mockResolvedValueOnce({ code: 1, stdout: "", stderr: "", killed: false }); // npm fails with no output
|
|
329
|
+
const { pi } = makePi(execImpl as any);
|
|
330
|
+
const ctx = makeCtx();
|
|
331
|
+
expect(await ensureAgentBrowser(pi, ctx)).toBe(false);
|
|
332
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(expect.stringContaining("exit status 1"), "error");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("notify is a no-op without UI even when the CLI is installed", async () => {
|
|
336
|
+
const { pi } = makePi(async () => ok);
|
|
337
|
+
const ctx = makeCtx({ hasUI: false });
|
|
338
|
+
expect(await ensureAgentBrowser(pi, ctx)).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("ensureAgentBrowser returns true after full install flow", async () => {
|
|
342
|
+
const execImpl = vi
|
|
343
|
+
.fn()
|
|
344
|
+
.mockResolvedValueOnce(fail) // probe: missing
|
|
345
|
+
.mockResolvedValueOnce(ok) // npm i -g agent-browser
|
|
346
|
+
.mockResolvedValueOnce(ok) // probe: now present
|
|
347
|
+
.mockResolvedValueOnce(ok); // agent-browser install
|
|
348
|
+
const { pi } = makePi(execImpl as any);
|
|
349
|
+
expect(await ensureAgentBrowser(pi, makeCtx())).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("cliCommand wraps args in cmd.exe on win32", () => {
|
|
353
|
+
const original = process.platform;
|
|
354
|
+
Object.defineProperty(process, "platform", { value: "win32" });
|
|
355
|
+
try {
|
|
356
|
+
const { ensureAgentBrowser: _e, ...rest } = {} as any;
|
|
357
|
+
// Re-import to pick up the platform at module scope is not needed;
|
|
358
|
+
// cliCommand is module-private, so exercise it through the public flow.
|
|
359
|
+
const execImpl = vi.fn().mockResolvedValue(ok);
|
|
360
|
+
const { pi } = makePi(execImpl as any);
|
|
361
|
+
return ensureAgentBrowser(pi, makeCtx()).then((result) => {
|
|
362
|
+
expect(result).toBe(true);
|
|
363
|
+
const call = execImpl.mock.calls[0];
|
|
364
|
+
expect(call[0]).toBe("cmd");
|
|
365
|
+
expect(call[1]).toEqual(["/c", "agent-browser", "--version"]);
|
|
366
|
+
});
|
|
367
|
+
} finally {
|
|
368
|
+
Object.defineProperty(process, "platform", { value: original });
|
|
369
|
+
}
|
|
370
|
+
});
|
|
195
371
|
});
|
|
@@ -75,6 +75,8 @@ async function runNpmInstall(pi: ExtensionAPI): Promise<string | null> {
|
|
|
75
75
|
const result = await pi.exec(command, args, { timeout: INSTALL_TIMEOUT_MS });
|
|
76
76
|
if (result.killed) return "npm install timed out";
|
|
77
77
|
if (result.code !== 0) {
|
|
78
|
+
// The exit-status fallback is always non-empty, so the final || is unreachable.
|
|
79
|
+
/* v8 ignore next 1 */
|
|
78
80
|
return (result.stderr || result.stdout || `exit status ${result.code}`).trim() || "npm install failed";
|
|
79
81
|
}
|
|
80
82
|
return null;
|