@tintinweb/pi-subagents 0.6.1 → 0.6.3
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 +14 -0
- package/README.md +0 -1
- package/dist/agent-runner.js +7 -13
- package/dist/default-agents.js +2 -9
- package/dist/output-file.d.ts +7 -0
- package/dist/output-file.js +22 -2
- package/package.json +5 -5
- package/src/agent-runner.ts +6 -13
- package/src/default-agents.ts +2 -9
- package/src/output-file.ts +21 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.3] - 2026-04-28
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **`run_in_background: true` (and `inherit_context`, `isolated`) silently ignored on default agents** ([#37](https://github.com/tintinweb/pi-subagents/issues/37) — thanks [@kylesnowschwartz](https://github.com/kylesnowschwartz) for the diagnosis). The three built-in defaults (`general-purpose`, `Explore`, `Plan`) baked `runInBackground: false`, `inheritContext: false`, and `isolated: false` into their configs. `resolveAgentInvocationConfig` uses `agentConfig?.field ?? params.field ?? false`, and `??` only falls through on `null`/`undefined` — so an explicit `false` from the agent config silently won over the caller's `true`. Calling `Agent({ subagent_type: "general-purpose", run_in_background: true })` returned the result inline instead of backgrounding, blocking the parent UI for the agent's full runtime. Fix drops the three lines from each default (and from the unreachable defensive fallback in `agent-runner.ts`) — the type already declared each as `field?: boolean` with JSDoc *"undefined = caller decides"*, so the runtime now matches the documented contract. **Behavior:** custom agents that explicitly set these fields in frontmatter still lock as before (the v0.5.1 "frontmatter is authoritative" guarantee is preserved); the fix only stops *defaults* from spuriously claiming an opinion on callsite-strategy fields they don't actually have. The unreachable fallback now spreads `DEFAULT_AGENTS.get("general-purpose")` instead of duplicating the config inline, so future drift is impossible.
|
|
14
|
+
|
|
15
|
+
## [0.6.2] - 2026-04-28
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **`Agent` tool fails on Windows with `ENOENT` creating output directory** ([#27](https://github.com/tintinweb/pi-subagents/issues/27) — thanks [@sixnathan](https://github.com/sixnathan) for the diagnosis). The cwd-encoding regex in `output-file.ts` only handled POSIX `/` separators, so on Windows `cwd = "C:\\Users\\foo\\project"` survived unchanged and `path.join(tmpRoot, encoded, …)` produced an invalid nested-absolute path. Now extracts a small `encodeCwd()` helper that handles both `/` and `\\` separators, strips the Windows drive-letter prefix, and preserves UNC server/share segments. The `chmodSync(root, 0o700)` call is also wrapped in a try/catch that swallows errors only on Windows (where chmod is a no-op and can throw on some filesystems); on Unix the error still propagates so umask-defeating `0o700` enforcement is preserved.
|
|
19
|
+
|
|
10
20
|
## [0.6.1] - 2026-04-25
|
|
11
21
|
|
|
12
22
|
### Added
|
|
@@ -369,6 +379,10 @@ Initial release.
|
|
|
369
379
|
- **Thinking level** — per-agent extended thinking control
|
|
370
380
|
- **`/agent` and `/agents` commands**
|
|
371
381
|
|
|
382
|
+
[0.6.3]: https://github.com/tintinweb/pi-subagents/compare/v0.6.2...v0.6.3
|
|
383
|
+
[0.6.2]: https://github.com/tintinweb/pi-subagents/compare/v0.6.1...v0.6.2
|
|
384
|
+
[0.6.1]: https://github.com/tintinweb/pi-subagents/compare/v0.6.0...v0.6.1
|
|
385
|
+
[0.6.0]: https://github.com/tintinweb/pi-subagents/compare/v0.5.2...v0.6.0
|
|
372
386
|
[0.5.2]: https://github.com/tintinweb/pi-subagents/compare/v0.5.1...v0.5.2
|
|
373
387
|
[0.5.1]: https://github.com/tintinweb/pi-subagents/compare/v0.5.0...v0.5.1
|
|
374
388
|
[0.5.0]: https://github.com/tintinweb/pi-subagents/compare/v0.4.9...v0.5.0
|
package/README.md
CHANGED
|
@@ -166,7 +166,6 @@ All fields are optional — sensible defaults for everything.
|
|
|
166
166
|
| `prompt_mode` | `replace` | `replace`: body is the full system prompt (no AGENTS.md / CLAUDE.md inheritance). `append`: body appended to parent's prompt (agent acts as a "parent twin" — inherits parent's AGENTS.md / CLAUDE.md) |
|
|
167
167
|
| `inherit_context` | `false` | Fork parent conversation into agent |
|
|
168
168
|
| `run_in_background` | `false` | Run in background by default |
|
|
169
|
-
| `isolation` | — | `worktree`: run in a temporary git worktree for full repo isolation |
|
|
170
169
|
| `isolated` | `false` | No extension/MCP tools, only built-in |
|
|
171
170
|
| `enabled` | `true` | Set to `false` to disable an agent (useful for hiding a default agent per-project) |
|
|
172
171
|
|
package/dist/agent-runner.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
|
|
5
5
|
import { getAgentConfig, getConfig, getMemoryToolNames, getReadOnlyMemoryToolNames, getToolNamesForType } from "./agent-types.js";
|
|
6
6
|
import { buildParentContext, extractText } from "./context.js";
|
|
7
|
+
import { DEFAULT_AGENTS } from "./default-agents.js";
|
|
7
8
|
import { detectEnv } from "./env.js";
|
|
8
9
|
import { buildMemoryBlock, buildReadOnlyMemoryBlock } from "./memory.js";
|
|
9
10
|
import { buildAgentPrompt } from "./prompts.js";
|
|
@@ -139,19 +140,12 @@ export async function runAgent(ctx, type, prompt, options) {
|
|
|
139
140
|
systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras);
|
|
140
141
|
}
|
|
141
142
|
else {
|
|
142
|
-
// Unknown type fallback: general-purpose (defensive —
|
|
143
|
-
// since index.ts resolves unknown types
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
promptMode: "append",
|
|
149
|
-
extensions: true,
|
|
150
|
-
skills: true,
|
|
151
|
-
inheritContext: false,
|
|
152
|
-
runInBackground: false,
|
|
153
|
-
isolated: false,
|
|
154
|
-
}, effectiveCwd, env, parentSystemPrompt, extras);
|
|
143
|
+
// Unknown type fallback: spread the canonical general-purpose config (defensive —
|
|
144
|
+
// unreachable in practice since index.ts resolves unknown types before calling runAgent).
|
|
145
|
+
const fallback = DEFAULT_AGENTS.get("general-purpose");
|
|
146
|
+
if (!fallback)
|
|
147
|
+
throw new Error(`No fallback config available for unknown type "${type}"`);
|
|
148
|
+
systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras);
|
|
155
149
|
}
|
|
156
150
|
// When skills is string[], we've already preloaded them into the prompt.
|
|
157
151
|
// Still pass noSkills: true since we don't need the skill loader to load them again.
|
package/dist/default-agents.js
CHANGED
|
@@ -12,13 +12,12 @@ export const DEFAULT_AGENTS = new Map([
|
|
|
12
12
|
displayName: "Agent",
|
|
13
13
|
description: "General-purpose agent for complex, multi-step tasks",
|
|
14
14
|
// builtinToolNames omitted — means "all available tools" (resolved at lookup time)
|
|
15
|
+
// inheritContext / runInBackground / isolated omitted — strategy fields, callers decide per-call.
|
|
16
|
+
// Setting them to false would lock callsite intent (see resolveAgentInvocationConfig in invocation-config.ts).
|
|
15
17
|
extensions: true,
|
|
16
18
|
skills: true,
|
|
17
19
|
systemPrompt: "",
|
|
18
20
|
promptMode: "append",
|
|
19
|
-
inheritContext: false,
|
|
20
|
-
runInBackground: false,
|
|
21
|
-
isolated: false,
|
|
22
21
|
isDefault: true,
|
|
23
22
|
},
|
|
24
23
|
],
|
|
@@ -61,9 +60,6 @@ Use Bash ONLY for read-only operations: ls, git status, git log, git diff, find,
|
|
|
61
60
|
- Do not use emojis
|
|
62
61
|
- Be thorough and precise`,
|
|
63
62
|
promptMode: "replace",
|
|
64
|
-
inheritContext: false,
|
|
65
|
-
runInBackground: false,
|
|
66
|
-
isolated: false,
|
|
67
63
|
isDefault: true,
|
|
68
64
|
},
|
|
69
65
|
],
|
|
@@ -117,9 +113,6 @@ You are STRICTLY PROHIBITED from:
|
|
|
117
113
|
List 3-5 files most critical for implementing this plan:
|
|
118
114
|
- /absolute/path/to/file.ts - [Brief reason]`,
|
|
119
115
|
promptMode: "replace",
|
|
120
|
-
inheritContext: false,
|
|
121
|
-
runInBackground: false,
|
|
122
|
-
isolated: false,
|
|
123
116
|
isDefault: true,
|
|
124
117
|
},
|
|
125
118
|
],
|
package/dist/output-file.d.ts
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* matching Claude Code's task output file format.
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentSession } from "@mariozechner/pi-coding-agent";
|
|
8
|
+
/**
|
|
9
|
+
* Encode a cwd path as a filesystem-safe directory name. Handles:
|
|
10
|
+
* - POSIX: "/home/user/project" → "home-user-project"
|
|
11
|
+
* - Windows: "C:\Users\foo\project" → "Users-foo-project"
|
|
12
|
+
* - UNC: "\\\\server\\share\\project" → "server-share-project"
|
|
13
|
+
*/
|
|
14
|
+
export declare function encodeCwd(cwd: string): string;
|
|
8
15
|
/** Create the output file path, ensuring the directory exists.
|
|
9
16
|
* Mirrors Claude Code's layout: /tmp/{prefix}-{uid}/{encoded-cwd}/{sessionId}/tasks/{agentId}.output */
|
|
10
17
|
export declare function createOutputFilePath(cwd: string, agentId: string, sessionId: string): string;
|
package/dist/output-file.js
CHANGED
|
@@ -7,13 +7,33 @@
|
|
|
7
7
|
import { appendFileSync, chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
+
/**
|
|
11
|
+
* Encode a cwd path as a filesystem-safe directory name. Handles:
|
|
12
|
+
* - POSIX: "/home/user/project" → "home-user-project"
|
|
13
|
+
* - Windows: "C:\Users\foo\project" → "Users-foo-project"
|
|
14
|
+
* - UNC: "\\\\server\\share\\project" → "server-share-project"
|
|
15
|
+
*/
|
|
16
|
+
export function encodeCwd(cwd) {
|
|
17
|
+
return cwd
|
|
18
|
+
.replace(/[/\\]/g, "-") // both separators → dash
|
|
19
|
+
.replace(/^[A-Za-z]:-/, "") // strip Windows drive prefix ("C:-")
|
|
20
|
+
.replace(/^-+/, ""); // strip leading dashes (POSIX root, UNC)
|
|
21
|
+
}
|
|
10
22
|
/** Create the output file path, ensuring the directory exists.
|
|
11
23
|
* Mirrors Claude Code's layout: /tmp/{prefix}-{uid}/{encoded-cwd}/{sessionId}/tasks/{agentId}.output */
|
|
12
24
|
export function createOutputFilePath(cwd, agentId, sessionId) {
|
|
13
|
-
const encoded = cwd
|
|
25
|
+
const encoded = encodeCwd(cwd);
|
|
14
26
|
const root = join(tmpdir(), `pi-subagents-${process.getuid?.() ?? 0}`);
|
|
15
27
|
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
16
|
-
|
|
28
|
+
// chmod is a no-op on Windows and throws on some Windows filesystems.
|
|
29
|
+
// On Unix we still want to enforce 0o700 past umask, so only swallow on Windows.
|
|
30
|
+
try {
|
|
31
|
+
chmodSync(root, 0o700);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (process.platform !== "win32")
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
17
37
|
const dir = join(root, encoded, sessionId, "tasks");
|
|
18
38
|
mkdirSync(dir, { recursive: true });
|
|
19
39
|
return join(dir, `${agentId}.output`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tintinweb/pi-subagents",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "A pi extension extension that brings smart Claude Code-style autonomous sub-agents to pi.",
|
|
5
5
|
"author": "tintinweb",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"autonomous"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@mariozechner/pi-ai": "^0.70.
|
|
25
|
-
"@mariozechner/pi-coding-agent": "^0.70.
|
|
26
|
-
"@mariozechner/pi-tui": "^0.70.
|
|
24
|
+
"@mariozechner/pi-ai": "^0.70.5",
|
|
25
|
+
"@mariozechner/pi-coding-agent": "^0.70.5",
|
|
26
|
+
"@mariozechner/pi-tui": "^0.70.5",
|
|
27
27
|
"@sinclair/typebox": "latest"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@biomejs/biome": "^2.3.5",
|
|
40
40
|
"@types/node": "^25.5.0",
|
|
41
|
-
"typescript": "^
|
|
41
|
+
"typescript": "^6.0.0",
|
|
42
42
|
"vitest": "^4.0.18"
|
|
43
43
|
},
|
|
44
44
|
"pi": {
|
package/src/agent-runner.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from "@mariozechner/pi-coding-agent";
|
|
17
17
|
import { getAgentConfig, getConfig, getMemoryToolNames, getReadOnlyMemoryToolNames, getToolNamesForType } from "./agent-types.js";
|
|
18
18
|
import { buildParentContext, extractText } from "./context.js";
|
|
19
|
+
import { DEFAULT_AGENTS } from "./default-agents.js";
|
|
19
20
|
import { detectEnv } from "./env.js";
|
|
20
21
|
import { buildMemoryBlock, buildReadOnlyMemoryBlock } from "./memory.js";
|
|
21
22
|
import { buildAgentPrompt, type PromptExtras } from "./prompts.js";
|
|
@@ -212,19 +213,11 @@ export async function runAgent(
|
|
|
212
213
|
if (agentConfig) {
|
|
213
214
|
systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras);
|
|
214
215
|
} else {
|
|
215
|
-
// Unknown type fallback: general-purpose (defensive —
|
|
216
|
-
// since index.ts resolves unknown types
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
systemPrompt: "",
|
|
221
|
-
promptMode: "append",
|
|
222
|
-
extensions: true,
|
|
223
|
-
skills: true,
|
|
224
|
-
inheritContext: false,
|
|
225
|
-
runInBackground: false,
|
|
226
|
-
isolated: false,
|
|
227
|
-
}, effectiveCwd, env, parentSystemPrompt, extras);
|
|
216
|
+
// Unknown type fallback: spread the canonical general-purpose config (defensive —
|
|
217
|
+
// unreachable in practice since index.ts resolves unknown types before calling runAgent).
|
|
218
|
+
const fallback = DEFAULT_AGENTS.get("general-purpose");
|
|
219
|
+
if (!fallback) throw new Error(`No fallback config available for unknown type "${type}"`);
|
|
220
|
+
systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras);
|
|
228
221
|
}
|
|
229
222
|
|
|
230
223
|
// When skills is string[], we've already preloaded them into the prompt.
|
package/src/default-agents.ts
CHANGED
|
@@ -16,13 +16,12 @@ export const DEFAULT_AGENTS: Map<string, AgentConfig> = new Map([
|
|
|
16
16
|
displayName: "Agent",
|
|
17
17
|
description: "General-purpose agent for complex, multi-step tasks",
|
|
18
18
|
// builtinToolNames omitted — means "all available tools" (resolved at lookup time)
|
|
19
|
+
// inheritContext / runInBackground / isolated omitted — strategy fields, callers decide per-call.
|
|
20
|
+
// Setting them to false would lock callsite intent (see resolveAgentInvocationConfig in invocation-config.ts).
|
|
19
21
|
extensions: true,
|
|
20
22
|
skills: true,
|
|
21
23
|
systemPrompt: "",
|
|
22
24
|
promptMode: "append",
|
|
23
|
-
inheritContext: false,
|
|
24
|
-
runInBackground: false,
|
|
25
|
-
isolated: false,
|
|
26
25
|
isDefault: true,
|
|
27
26
|
},
|
|
28
27
|
],
|
|
@@ -65,9 +64,6 @@ Use Bash ONLY for read-only operations: ls, git status, git log, git diff, find,
|
|
|
65
64
|
- Do not use emojis
|
|
66
65
|
- Be thorough and precise`,
|
|
67
66
|
promptMode: "replace",
|
|
68
|
-
inheritContext: false,
|
|
69
|
-
runInBackground: false,
|
|
70
|
-
isolated: false,
|
|
71
67
|
isDefault: true,
|
|
72
68
|
},
|
|
73
69
|
],
|
|
@@ -121,9 +117,6 @@ You are STRICTLY PROHIBITED from:
|
|
|
121
117
|
List 3-5 files most critical for implementing this plan:
|
|
122
118
|
- /absolute/path/to/file.ts - [Brief reason]`,
|
|
123
119
|
promptMode: "replace",
|
|
124
|
-
inheritContext: false,
|
|
125
|
-
runInBackground: false,
|
|
126
|
-
isolated: false,
|
|
127
120
|
isDefault: true,
|
|
128
121
|
},
|
|
129
122
|
],
|
package/src/output-file.ts
CHANGED
|
@@ -10,13 +10,32 @@ import { tmpdir } from "node:os";
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import type { AgentSession, AgentSessionEvent } from "@mariozechner/pi-coding-agent";
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Encode a cwd path as a filesystem-safe directory name. Handles:
|
|
15
|
+
* - POSIX: "/home/user/project" → "home-user-project"
|
|
16
|
+
* - Windows: "C:\Users\foo\project" → "Users-foo-project"
|
|
17
|
+
* - UNC: "\\\\server\\share\\project" → "server-share-project"
|
|
18
|
+
*/
|
|
19
|
+
export function encodeCwd(cwd: string): string {
|
|
20
|
+
return cwd
|
|
21
|
+
.replace(/[/\\]/g, "-") // both separators → dash
|
|
22
|
+
.replace(/^[A-Za-z]:-/, "") // strip Windows drive prefix ("C:-")
|
|
23
|
+
.replace(/^-+/, ""); // strip leading dashes (POSIX root, UNC)
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
/** Create the output file path, ensuring the directory exists.
|
|
14
27
|
* Mirrors Claude Code's layout: /tmp/{prefix}-{uid}/{encoded-cwd}/{sessionId}/tasks/{agentId}.output */
|
|
15
28
|
export function createOutputFilePath(cwd: string, agentId: string, sessionId: string): string {
|
|
16
|
-
const encoded = cwd
|
|
29
|
+
const encoded = encodeCwd(cwd);
|
|
17
30
|
const root = join(tmpdir(), `pi-subagents-${process.getuid?.() ?? 0}`);
|
|
18
31
|
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
19
|
-
|
|
32
|
+
// chmod is a no-op on Windows and throws on some Windows filesystems.
|
|
33
|
+
// On Unix we still want to enforce 0o700 past umask, so only swallow on Windows.
|
|
34
|
+
try {
|
|
35
|
+
chmodSync(root, 0o700);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (process.platform !== "win32") throw err;
|
|
38
|
+
}
|
|
20
39
|
const dir = join(root, encoded, sessionId, "tasks");
|
|
21
40
|
mkdirSync(dir, { recursive: true });
|
|
22
41
|
return join(dir, `${agentId}.output`);
|