@cr1ms0n/pi-subagent 0.8.1 → 0.8.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 +18 -1
- package/README.md +19 -3
- package/docs/UI-OVERHAUL.md +4 -4
- package/docs/UX.md +5 -5
- package/package.json +1 -1
- package/skills/subagent/SKILL.md +6 -4
- package/src/agents.ts +2 -5
- package/src/backend.ts +3 -0
- package/src/config.ts +4 -6
- package/src/extension.ts +3 -3
- package/src/model-policy.ts +19 -7
- package/src/notifications.ts +2 -2
- package/src/policy.ts +3 -2
- package/src/schema.ts +1 -1
- package/src/thinking.ts +6 -0
- package/src/types.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.3 — 2026-09-10
|
|
4
|
+
|
|
5
|
+
### Model-policy thinking defaults
|
|
6
|
+
|
|
7
|
+
- Allow `modelPolicy.default` and named-agent routes in `~/.pi/subagent.json`
|
|
8
|
+
to specify an optional `thinking` default (`off`, `minimal`, `low`, `medium`,
|
|
9
|
+
`high`, or `xhigh`). Explicit task, agent, and profile defaults still win;
|
|
10
|
+
unsupported backends reject resolved thinking instead of silently ignoring it.
|
|
11
|
+
|
|
3
12
|
## 0.8.1 — @cr1ms0n community fork (2026-09-08)
|
|
4
13
|
|
|
5
14
|
- Require an explicit model matching user-owned default/agent model policy.
|
|
@@ -12,7 +21,15 @@
|
|
|
12
21
|
The entries below are retained upstream history from @parke.dev/pi-subagent.
|
|
13
22
|
|
|
14
23
|
|
|
15
|
-
##
|
|
24
|
+
## 0.8.2 — 2026-09-10
|
|
25
|
+
|
|
26
|
+
### Background completion delivery
|
|
27
|
+
|
|
28
|
+
- Automatic terminal notifications for `async: true` runs now use Pi's `steer`
|
|
29
|
+
delivery queue with `triggerTurn: true`, making completion messages available
|
|
30
|
+
before the parent's next LLM call. Foreground runs, explicit management
|
|
31
|
+
actions, message content, batching, persistence, and usage accounting are
|
|
32
|
+
unchanged.
|
|
16
33
|
|
|
17
34
|
### Machine-wide worktree GC
|
|
18
35
|
|
package/README.md
CHANGED
|
@@ -238,14 +238,29 @@ fallback lists are ignored for model selection. The minimal template is:
|
|
|
238
238
|
```json
|
|
239
239
|
{
|
|
240
240
|
"modelPolicy": {
|
|
241
|
-
"default": {
|
|
241
|
+
"default": {
|
|
242
|
+
"model": "<provider/model-id>",
|
|
243
|
+
"fallbackModels": [],
|
|
244
|
+
"thinking": "medium"
|
|
245
|
+
},
|
|
242
246
|
"agents": {
|
|
243
|
-
"<agent-name>": {
|
|
247
|
+
"<agent-name>": {
|
|
248
|
+
"model": "<provider/model-id>",
|
|
249
|
+
"fallbackModels": [],
|
|
250
|
+
"thinking": "high"
|
|
251
|
+
}
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
254
|
}
|
|
247
255
|
```
|
|
248
256
|
|
|
257
|
+
`thinking` is optional and accepts `off`, `minimal`, `low`, `medium`, `high`,
|
|
258
|
+
or `xhigh`. It is a route default, not a strict policy value. Resolution order
|
|
259
|
+
is: explicit task `thinking` > agent frontmatter `thinking` > profile
|
|
260
|
+
`taskDefaults.<profile>.thinking` > the selected `modelPolicy` route's
|
|
261
|
+
`thinking` > the parent session's thinking level. Omitting it preserves the
|
|
262
|
+
existing behavior.
|
|
263
|
+
|
|
249
264
|
Every new `task`/`tasks[]` item must pass the exact mapped `model`. Omit
|
|
250
265
|
`fallback_models` to use the configured list; when supplied it must match the
|
|
251
266
|
configured order exactly. Management actions remain available when this file is
|
|
@@ -319,7 +334,8 @@ and file changes are picked up within seconds — no restart needed.
|
|
|
319
334
|
`taskDefaults` in `~/.pi/subagent.json` remains available for non-model
|
|
320
335
|
fields such as thinking, budgets, and retry counts. Its legacy `model` and
|
|
321
336
|
`fallbackModels` fields are ignored; model routing belongs only to
|
|
322
|
-
`modelPolicy`.
|
|
337
|
+
`modelPolicy`. A profile `thinking` value overrides the selected route default.
|
|
338
|
+
Invalid fields are dropped field-by-field.
|
|
323
339
|
|
|
324
340
|
Notes on behavior:
|
|
325
341
|
|
package/docs/UI-OVERHAUL.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
> the plan: the inline spinner uses a wall-clock frame (Pi's working indicator
|
|
6
6
|
> drives repaints, so no timer is owned); the ledger command landed as
|
|
7
7
|
> `/subagent-cost` via `ctx.ui.notify`. Phase 4 shipped as the background-only
|
|
8
|
-
> ambient widget plus batched `
|
|
9
|
-
>
|
|
10
|
-
>
|
|
8
|
+
> ambient widget plus batched `steer` completion notifications with flush-time
|
|
9
|
+
> wait-suppression (see docs/UX.md). The `description` param is the task label
|
|
10
|
+
> across all surfaces. This document is retained as design rationale.
|
|
11
11
|
|
|
12
12
|
Goal: remove the duplicated cost footer, make inline chat rendering clean and
|
|
13
13
|
smooth, and align every surface with Pi's native conventions. Informed by a
|
|
@@ -137,7 +137,7 @@ Files: `src/extension.ts`, new `src/notifications.ts`.
|
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
Cleared when no background runs. Config: `widget: "background" | "off"`.
|
|
140
|
-
- **Completion notifications** via `pi.sendMessage(…, { deliverAs: "
|
|
140
|
+
- **Completion notifications** via `pi.sendMessage(…, { deliverAs: "steer",
|
|
141
141
|
triggerTurn: true })` + `registerMessageRenderer`: themed compact box for the
|
|
142
142
|
human (state, stats, first-line preview, artifact/session pointers),
|
|
143
143
|
structured payload for the model. Replaces the bare `ctx.ui.notify` toast.
|
package/docs/UX.md
CHANGED
|
@@ -66,11 +66,11 @@ Cleared when the last background run settles. Spinner and elapsed animate on
|
|
|
66
66
|
a 250ms interval that exists only while background runs are live.
|
|
67
67
|
|
|
68
68
|
### Completion notifications (background runs only)
|
|
69
|
-
When an async run reaches a terminal state, a `
|
|
70
|
-
`subagent-completion`)
|
|
71
|
-
The human sees a themed compact box (state
|
|
72
|
-
preview, artifact pointers); the LLM sees plain
|
|
73
|
-
`wait { id }` pointer.
|
|
69
|
+
When an async run reaches a terminal state, a `steer` message (custom type
|
|
70
|
+
`subagent-completion`) is queued for the parent LLM before its next LLM call,
|
|
71
|
+
so it can react without polling. The human sees a themed compact box (state
|
|
72
|
+
glyph, label, stats, one-line preview, artifact pointers); the LLM sees plain
|
|
73
|
+
text with run ids and a `wait { id }` pointer.
|
|
74
74
|
|
|
75
75
|
- Successes within a short window batch into one message (no fanout spam);
|
|
76
76
|
failures bypass batching and flush immediately, carrying held successes.
|
package/package.json
CHANGED
package/skills/subagent/SKILL.md
CHANGED
|
@@ -97,7 +97,9 @@ Every new task must pass a `model` that exactly matches the current
|
|
|
97
97
|
`taskDefaults.model`, and parent-session model inheritance are ignored. An
|
|
98
98
|
agent route replaces the default route, and configured fallback order is
|
|
99
99
|
immutable. Omit `fallback_models` to use the route; if supplied, it must match
|
|
100
|
-
exactly.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
exactly. An optional route `thinking` value (`off`, `minimal`, `low`, `medium`,
|
|
101
|
+
`high`, or `xhigh`) is a default; explicit task, agent, and profile
|
|
102
|
+
`taskDefaults.thinking` values override it. Management actions do not require
|
|
103
|
+
model. The extension re-reads this policy on each dispatch and injects it into
|
|
104
|
+
the parent prompt. If the policy is missing or invalid, management remains
|
|
105
|
+
available but new spawns and synthesis are rejected.
|
package/src/agents.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { TaskProfile } from "./types.js";
|
|
5
|
-
import type
|
|
5
|
+
import { isThinkingLevel, type ThinkingLevel } from "./thinking.js";
|
|
6
6
|
import { isPlausibleSchema } from "./structured.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -23,7 +23,6 @@ import { isPlausibleSchema } from "./structured.js";
|
|
|
23
23
|
* model policy is the only source used for new spawns.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
27
26
|
const PROFILES = ["explore", "review", "general"] as const;
|
|
28
27
|
|
|
29
28
|
export interface AgentDefinition {
|
|
@@ -178,9 +177,7 @@ export function parseAgentFile(name: string, raw: string, source: string, scope:
|
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
const thinking = frontmatter.thinking
|
|
182
|
-
? (frontmatter.thinking as ThinkingLevel)
|
|
183
|
-
: undefined;
|
|
180
|
+
const thinking = isThinkingLevel(frontmatter.thinking) ? frontmatter.thinking : undefined;
|
|
184
181
|
const profile = frontmatter.profile && (PROFILES as readonly string[]).includes(frontmatter.profile)
|
|
185
182
|
? (frontmatter.profile as TaskProfile)
|
|
186
183
|
: undefined;
|
package/src/backend.ts
CHANGED
|
@@ -139,6 +139,9 @@ export function checkCapabilities(
|
|
|
139
139
|
`backend '${backend}' cannot restrict the child's tools, so profile '${spec.profile ?? "explore"}' cannot be enforced; use profile:'general' with an explicitly writable backend, or the pi backend`,
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
+
if (spec.thinking !== undefined && !capabilities.thinking) {
|
|
143
|
+
problems.push(`backend '${backend}' does not support thinking level '${spec.thinking}'; omit thinking or use the pi backend`);
|
|
144
|
+
}
|
|
142
145
|
if (spec.outputSchema && !capabilities.outputSchema) {
|
|
143
146
|
problems.push(`backend '${backend}' does not support output_schema; drop it`);
|
|
144
147
|
}
|
package/src/config.ts
CHANGED
|
@@ -3,9 +3,9 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { TaskProfile } from "./types.js";
|
|
5
5
|
import { MODEL_POLICY_CONFIG_FILE, parseModelPolicy, type ModelPolicySnapshot } from "./model-policy.js";
|
|
6
|
+
import { isThinkingLevel, type ThinkingLevel } from "./thinking.js";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
8
|
+
export type { ThinkingLevel } from "./thinking.js";
|
|
9
9
|
|
|
10
10
|
const WIDGET_MODES = ["background", "off"] as const;
|
|
11
11
|
export type WidgetMode = (typeof WIDGET_MODES)[number];
|
|
@@ -83,7 +83,7 @@ export interface SubagentConfig {
|
|
|
83
83
|
*/
|
|
84
84
|
widget: WidgetMode;
|
|
85
85
|
/**
|
|
86
|
-
* Batched completion
|
|
86
|
+
* Batched completion messages for async runs. `"off"` disables the
|
|
87
87
|
* CompletionBatcher so the parent is not notified on finish.
|
|
88
88
|
*/
|
|
89
89
|
notifications: NotificationMode;
|
|
@@ -139,9 +139,7 @@ function prune<T extends object>(value: Partial<T>): Partial<T> {
|
|
|
139
139
|
function sanitizeTaskDefaults(raw: unknown): TaskDefaults | undefined {
|
|
140
140
|
if (!raw || typeof raw !== "object") return undefined;
|
|
141
141
|
const value = raw as Record<string, unknown>;
|
|
142
|
-
const thinking =
|
|
143
|
-
? (value.thinking as ThinkingLevel)
|
|
144
|
-
: undefined;
|
|
142
|
+
const thinking = isThinkingLevel(value.thinking) ? value.thinking : undefined;
|
|
145
143
|
const fallbackModels = Array.isArray(value.fallbackModels)
|
|
146
144
|
? value.fallbackModels.map(nonEmptyString).filter((model): model is string => !!model)
|
|
147
145
|
: undefined;
|
package/src/extension.ts
CHANGED
|
@@ -566,7 +566,7 @@ async function runSynthesis(
|
|
|
566
566
|
tools: ["read"],
|
|
567
567
|
model: approved.route.model,
|
|
568
568
|
fallbackModels: [...approved.route.fallbackModels],
|
|
569
|
-
thinking: "low",
|
|
569
|
+
thinking: approved.route.thinking ?? "low",
|
|
570
570
|
timeoutMs: Math.min(runtime.config.defaultTimeoutMs, 5 * 60_000),
|
|
571
571
|
maxTurns: 8,
|
|
572
572
|
},
|
|
@@ -763,7 +763,7 @@ export default function registerSubagent(pi: ExtensionAPI): void {
|
|
|
763
763
|
runtime.unsubscribeLedger = runtime.registry.subscribe((event) => {
|
|
764
764
|
if (event.sessionKey === runtime.key) runtime.ledgerDirty = true;
|
|
765
765
|
});
|
|
766
|
-
// Background-run completion notifications: batched
|
|
766
|
+
// Background-run completion notifications: batched steer messages so
|
|
767
767
|
// the parent LLM reacts without polling. Foreground runs deliver inline.
|
|
768
768
|
// `notifications: "off"` skips construction; subscribe still uses optional chain.
|
|
769
769
|
if (runtime.config.notifications !== "off") {
|
|
@@ -806,7 +806,7 @@ export default function registerSubagent(pi: ExtensionAPI): void {
|
|
|
806
806
|
].join("\n"),
|
|
807
807
|
display: true,
|
|
808
808
|
details,
|
|
809
|
-
}, { deliverAs: "
|
|
809
|
+
}, { deliverAs: "steer", triggerTurn: true });
|
|
810
810
|
});
|
|
811
811
|
}
|
|
812
812
|
|
package/src/model-policy.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { THINKING_LEVELS, isThinkingLevel, type ThinkingLevel } from "./thinking.js";
|
|
4
5
|
|
|
5
6
|
export const MODEL_POLICY_CONFIG_FILE = path.join(os.homedir(), ".pi", "subagent.json");
|
|
6
7
|
|
|
@@ -8,6 +9,8 @@ export interface ModelRoute {
|
|
|
8
9
|
model: string;
|
|
9
10
|
/** Immutable order owned by the user configuration. */
|
|
10
11
|
fallbackModels: readonly string[];
|
|
12
|
+
/** Optional route default; callers may override it with task/agent/profile settings. */
|
|
13
|
+
thinking?: ThinkingLevel;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export interface ModelPolicySnapshot {
|
|
@@ -42,7 +45,7 @@ function route(value: unknown, pathName: string, source: string): ModelRoute {
|
|
|
42
45
|
invalid(source, `${pathName} must be an object`);
|
|
43
46
|
}
|
|
44
47
|
const record = value as Record<string, unknown>;
|
|
45
|
-
const unknown = Object.keys(record).filter((key) => key !== "model" && key !== "fallbackModels");
|
|
48
|
+
const unknown = Object.keys(record).filter((key) => key !== "model" && key !== "fallbackModels" && key !== "thinking");
|
|
46
49
|
if (unknown.length) invalid(source, `${pathName} has unknown field(s): ${unknown.join(", ")}`);
|
|
47
50
|
const model = modelId(record.model, pathName, source);
|
|
48
51
|
const rawFallbacks = record.fallbackModels === undefined ? [] : record.fallbackModels;
|
|
@@ -50,7 +53,15 @@ function route(value: unknown, pathName: string, source: string): ModelRoute {
|
|
|
50
53
|
const fallbackModels = rawFallbacks.map((value, index) => modelId(value, `${pathName}.fallbackModels[${index}]`, source));
|
|
51
54
|
if (new Set(fallbackModels).size !== fallbackModels.length) invalid(source, `${pathName}.fallbackModels must not contain duplicates`);
|
|
52
55
|
if (fallbackModels.includes(model)) invalid(source, `${pathName}.fallbackModels must not repeat the primary model`);
|
|
53
|
-
|
|
56
|
+
const thinking = record.thinking;
|
|
57
|
+
if (thinking !== undefined && !isThinkingLevel(thinking)) {
|
|
58
|
+
invalid(source, `${pathName}.thinking must be one of: ${THINKING_LEVELS.join(", ")}`);
|
|
59
|
+
}
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
model,
|
|
62
|
+
fallbackModels: Object.freeze([...fallbackModels]),
|
|
63
|
+
...(thinking === undefined ? {} : { thinking }),
|
|
64
|
+
});
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
/** Parse only the modelPolicy subtree; no provider catalog or credentials are read. */
|
|
@@ -131,14 +142,15 @@ export function formatModelPolicyPrompt(policy: ModelPolicySnapshot | undefined,
|
|
|
131
142
|
const lines = [
|
|
132
143
|
"## Subagent model policy",
|
|
133
144
|
"Every new task/tasks[] spawn must pass model explicitly and it must exactly match this mapping. Management actions do not need model.",
|
|
134
|
-
`default (agentless and unmapped agents): model=${policy.default.model}; fallback_models=[${policy.default.fallbackModels.join(", ")}]`,
|
|
145
|
+
`default (agentless and unmapped agents): model=${policy.default.model}; fallback_models=[${policy.default.fallbackModels.join(", ")}]; thinking=${policy.default.thinking ?? "(unset)"}`,
|
|
135
146
|
];
|
|
136
147
|
for (const [agent, route] of [...policy.agents.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
137
|
-
lines.push(`agent:${agent}: model=${route.model}; fallback_models=[${route.fallbackModels.join(", ")}]`);
|
|
148
|
+
lines.push(`agent:${agent}: model=${route.model}; fallback_models=[${route.fallbackModels.join(", ")}]; thinking=${route.thinking ?? "(unset)"}`);
|
|
138
149
|
}
|
|
139
150
|
lines.push(
|
|
140
151
|
"If fallback_models is omitted, the configured list is used. If it is supplied, it must match the configured list exactly, including order.",
|
|
141
|
-
"
|
|
152
|
+
"thinking is a route default: explicit task thinking, agent thinking, and profile taskDefaults.thinking override it; parent thinking is used only when the route is unset.",
|
|
153
|
+
"Do not use model/fallback_models from agent frontmatter, taskDefaults, or the parent session; those legacy model fields are ignored.",
|
|
142
154
|
);
|
|
143
155
|
return lines.join("\n");
|
|
144
156
|
}
|
|
@@ -146,8 +158,8 @@ export function formatModelPolicyPrompt(policy: ModelPolicySnapshot | undefined,
|
|
|
146
158
|
export function modelPolicyTemplate(): string {
|
|
147
159
|
return JSON.stringify({
|
|
148
160
|
modelPolicy: {
|
|
149
|
-
default: { model: "<provider/model-id>", fallbackModels: [] },
|
|
150
|
-
agents: { "<agent-name>": { model: "<provider/model-id>", fallbackModels: [] } },
|
|
161
|
+
default: { model: "<provider/model-id>", fallbackModels: [], thinking: "medium" },
|
|
162
|
+
agents: { "<agent-name>": { model: "<provider/model-id>", fallbackModels: [], thinking: "high" } },
|
|
151
163
|
},
|
|
152
164
|
}, null, 2);
|
|
153
165
|
}
|
package/src/notifications.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Background-run completion notifications.
|
|
3
3
|
*
|
|
4
4
|
* When an async run reaches a terminal state, the parent LLM is notified with
|
|
5
|
-
* a
|
|
6
|
-
*
|
|
5
|
+
* a steer message (queued for delivery before the next LLM call) so it can
|
|
6
|
+
* react without polling status/wait. Matches the notification-as-delivery
|
|
7
7
|
* semantics of `wait`: whichever path delivers first wins via markDelivered.
|
|
8
8
|
*
|
|
9
9
|
* Batching: successes completing within a short window group into a single
|
package/src/policy.ts
CHANGED
|
@@ -199,6 +199,7 @@ function normalizeTask(
|
|
|
199
199
|
// file > per-profile config defaults > parent inheritance. Model routing was
|
|
200
200
|
// validated above and is exclusively owned by modelPolicy.
|
|
201
201
|
const profileDefaults: TaskDefaults = defaults.taskDefaults?.[profile] ?? {};
|
|
202
|
+
const effectiveThinking = item.thinking ?? agent?.thinking ?? profileDefaults.thinking ?? modelRoute.thinking ?? parent.thinking;
|
|
202
203
|
const label = item.description?.trim()
|
|
203
204
|
? item.description.trim().slice(0, 60)
|
|
204
205
|
: agent
|
|
@@ -223,7 +224,7 @@ function normalizeTask(
|
|
|
223
224
|
// restricts: profile 'general' inherits the parent set and does not
|
|
224
225
|
// promise a read-only sandbox.
|
|
225
226
|
tools: profile === "general" ? undefined : resolved.tools,
|
|
226
|
-
thinking:
|
|
227
|
+
thinking: effectiveThinking,
|
|
227
228
|
outputSchema: item.output_schema ?? agent?.outputSchema,
|
|
228
229
|
profile,
|
|
229
230
|
canWrite: resolved.canWrite,
|
|
@@ -244,7 +245,7 @@ function normalizeTask(
|
|
|
244
245
|
// Model routing is exclusively owned by modelPolicy. Legacy agent,
|
|
245
246
|
// taskDefaults, and parent-session model fields never participate.
|
|
246
247
|
model: item.model!.trim(),
|
|
247
|
-
thinking:
|
|
248
|
+
thinking: effectiveThinking,
|
|
248
249
|
tools: resolved.tools,
|
|
249
250
|
profile,
|
|
250
251
|
cwd,
|
package/src/schema.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const TaskFields = {
|
|
|
31
31
|
description: Type.Optional(Type.String({ description: "Short human label (3-5 words) shown in UIs and result indexes." })),
|
|
32
32
|
system_prompt: Type.Optional(Type.String({ description: "Extra system prompt appended to the child's prompt (does not replace it)." })),
|
|
33
33
|
model: Type.Optional(Type.String({ description: "**REQUIRED for every spawn call (task/tasks).** Exact model id from modelPolicy, in provider/model-id form. Calls without an explicit model are rejected; agent-file model, taskDefaults, and parent-session inheritance are ignored. Management actions (status/wait/cancel/steer/diff/apply/discard) do not need it." })),
|
|
34
|
-
thinking: Type.Optional({ ...ThinkingLevel, description: "Reasoning effort for the child. Defaults to profile
|
|
34
|
+
thinking: Type.Optional({ ...ThinkingLevel, description: "Reasoning effort for the child. Defaults to agent thinking, profile taskDefaults.thinking, modelPolicy route thinking, then the parent's level." }),
|
|
35
35
|
tools: Type.Optional(Type.Array(Type.String(), { description: "Optional tool allowlist. explore/review profiles reject write-capable tools." })),
|
|
36
36
|
profile: Type.Optional({ ...Profile, description: "Capability profile: explore/review are strictly read-only; general inherits the parent's active tools and may write." }),
|
|
37
37
|
cwd: Type.Optional(Type.String({ description: "Working directory for the child process." })),
|
package/src/thinking.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
2
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
3
|
+
|
|
4
|
+
export function isThinkingLevel(value: unknown): value is ThinkingLevel {
|
|
5
|
+
return typeof value === "string" && (THINKING_LEVELS as readonly string[]).includes(value);
|
|
6
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Message } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ThinkingLevel } from "./thinking.js";
|
|
2
3
|
|
|
3
4
|
export type RunMode = "single" | "parallel";
|
|
4
5
|
export type RunState = "queued" | "running" | "completed" | "partial" | "failed" | "cancelled" | "lost" | "timeout";
|
|
@@ -44,7 +45,7 @@ export interface TaskSpec {
|
|
|
44
45
|
label?: string;
|
|
45
46
|
systemPrompt?: string;
|
|
46
47
|
model?: string;
|
|
47
|
-
thinking?:
|
|
48
|
+
thinking?: ThinkingLevel;
|
|
48
49
|
tools?: string[];
|
|
49
50
|
profile: TaskProfile;
|
|
50
51
|
canWrite?: boolean;
|