@tintinweb/pi-subagents 0.7.3 → 0.9.0
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 +31 -0
- package/README.md +22 -1
- package/dist/agent-manager.d.ts +2 -2
- package/dist/agent-runner.d.ts +3 -3
- package/dist/agent-runner.js +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/custom-agents.js +1 -1
- package/dist/default-agents.js +3 -3
- package/dist/enabled-models.d.ts +49 -0
- package/dist/enabled-models.js +145 -0
- package/dist/env.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +215 -84
- package/dist/output-file.d.ts +1 -1
- package/dist/schedule-store.d.ts +2 -0
- package/dist/schedule-store.js +12 -1
- package/dist/schedule.d.ts +1 -1
- package/dist/settings.d.ts +23 -0
- package/dist/settings.js +6 -1
- package/dist/skill-loader.js +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/ui/agent-widget.js +1 -1
- package/dist/ui/conversation-viewer.d.ts +2 -2
- package/dist/ui/conversation-viewer.js +1 -1
- package/dist/ui/schedule-menu.d.ts +1 -1
- package/package.json +4 -4
- package/src/agent-manager.ts +2 -2
- package/src/agent-runner.ts +3 -3
- package/src/context.ts +1 -1
- package/src/custom-agents.ts +1 -1
- package/src/default-agents.ts +3 -3
- package/src/enabled-models.ts +180 -0
- package/src/env.ts +1 -1
- package/src/index.ts +238 -85
- package/src/output-file.ts +1 -1
- package/src/schedule-store.ts +11 -1
- package/src/schedule.ts +1 -1
- package/src/settings.ts +28 -1
- package/src/skill-loader.ts +1 -1
- package/src/types.ts +2 -2
- package/src/ui/agent-widget.ts +1 -1
- package/src/ui/conversation-viewer.ts +2 -2
- package/src/ui/schedule-menu.ts +1 -1
package/src/agent-runner.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* agent-runner.ts — Core execution engine: creates sessions, runs agents, collects results.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { Model } from "@
|
|
6
|
-
import type { ExtensionContext } from "@
|
|
5
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
6
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import {
|
|
8
8
|
type AgentSession,
|
|
9
9
|
type AgentSessionEvent,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
getAgentDir,
|
|
14
14
|
SessionManager,
|
|
15
15
|
SettingsManager,
|
|
16
|
-
} from "@
|
|
16
|
+
} from "@earendil-works/pi-coding-agent";
|
|
17
17
|
import { getAgentConfig, getConfig, getMemoryToolNames, getReadOnlyMemoryToolNames, getToolNamesForType } from "./agent-types.js";
|
|
18
18
|
import { buildParentContext, extractText } from "./context.js";
|
|
19
19
|
import { DEFAULT_AGENTS } from "./default-agents.js";
|
package/src/context.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* context.ts — Extract parent conversation context for subagent inheritance.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionContext } from "@
|
|
5
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
/** Extract text from a message content block array. */
|
|
8
8
|
export function extractText(content: unknown[]): string {
|
package/src/custom-agents.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
6
6
|
import { basename, join } from "node:path";
|
|
7
|
-
import { getAgentDir, parseFrontmatter } from "@
|
|
7
|
+
import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { BUILTIN_TOOL_NAMES } from "./agent-types.js";
|
|
9
9
|
import type { AgentConfig, MemoryScope, ThinkingLevel } from "./types.js";
|
|
10
10
|
|
package/src/default-agents.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const DEFAULT_AGENTS: Map<string, AgentConfig> = new Map([
|
|
|
14
14
|
{
|
|
15
15
|
name: "general-purpose",
|
|
16
16
|
displayName: "Agent",
|
|
17
|
-
description: "General-purpose agent for complex, multi-step tasks",
|
|
17
|
+
description: "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.",
|
|
18
18
|
// builtinToolNames omitted — means "all available tools" (resolved at lookup time)
|
|
19
19
|
// inheritContext / runInBackground / isolated omitted — strategy fields, callers decide per-call.
|
|
20
20
|
// Setting them to false would lock callsite intent (see resolveAgentInvocationConfig in invocation-config.ts).
|
|
@@ -30,7 +30,7 @@ export const DEFAULT_AGENTS: Map<string, AgentConfig> = new Map([
|
|
|
30
30
|
{
|
|
31
31
|
name: "Explore",
|
|
32
32
|
displayName: "Explore",
|
|
33
|
-
description: "Fast
|
|
33
|
+
description: "Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.",
|
|
34
34
|
builtinToolNames: READ_ONLY_TOOLS,
|
|
35
35
|
extensions: true,
|
|
36
36
|
skills: true,
|
|
@@ -72,7 +72,7 @@ Use Bash ONLY for read-only operations: ls, git status, git log, git diff, find,
|
|
|
72
72
|
{
|
|
73
73
|
name: "Plan",
|
|
74
74
|
displayName: "Plan",
|
|
75
|
-
description: "Software architect for implementation
|
|
75
|
+
description: "Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.",
|
|
76
76
|
builtinToolNames: READ_ONLY_TOOLS,
|
|
77
77
|
extensions: true,
|
|
78
78
|
skills: true,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads `enabledModels` from pi's settings (global `<agentDir>/settings.json`
|
|
3
|
+
* + project-local `<cwd>/.pi/settings.json`, project wins) and resolves
|
|
4
|
+
* entries to concrete `provider/modelId` keys for scope validation.
|
|
5
|
+
*
|
|
6
|
+
* **Project overrides global**, mirroring pi's own `SettingsManager`
|
|
7
|
+
* deep-merge behavior and matching the precedence we use for our own
|
|
8
|
+
* `subagents.json` settings (see `src/settings.ts:loadSettings`). If
|
|
9
|
+
* project file has `enabledModels` set, it wholly replaces global's
|
|
10
|
+
* (array fields are replaced, not concatenated).
|
|
11
|
+
*
|
|
12
|
+
* **Limited subset of upstream's resolveModelScope.** We support exact
|
|
13
|
+
* `provider/modelId` matching only. Upstream (pi-coding-agent's
|
|
14
|
+
* `core/model-resolver.ts`) additionally supports glob patterns
|
|
15
|
+
* (`*sonnet*`, `anthropic/*`), bare model IDs without provider, and
|
|
16
|
+
* thinking-level suffixes (`provider/*:high`). Those forms are silently
|
|
17
|
+
* ignored here.
|
|
18
|
+
*
|
|
19
|
+
* In practice, pi's `/scoped-models` picker writes exact `provider/modelId`
|
|
20
|
+
* entries, so the limitation is invisible for users who configure scope
|
|
21
|
+
* through pi's UI. Hand-edited settings using globs or bare IDs will
|
|
22
|
+
* produce an empty allowed set (scope check becomes a no-op).
|
|
23
|
+
*
|
|
24
|
+
* Example:
|
|
25
|
+
* enabledModels = ["anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6"]
|
|
26
|
+
* → resolves to { "anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6" }
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
32
|
+
import type { ModelEntry } from "./model-resolver.js";
|
|
33
|
+
|
|
34
|
+
/** Minimal registry shape — only the methods resolveEnabledModels actually calls. */
|
|
35
|
+
export interface ModelRegistryRef {
|
|
36
|
+
getAll(): unknown[];
|
|
37
|
+
getAvailable?(): unknown[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Paths to pi's settings.json files: [project, global] (project takes precedence). */
|
|
41
|
+
function settingsPaths(cwd: string): [project: string, global: string] {
|
|
42
|
+
return [
|
|
43
|
+
join(cwd, ".pi", "settings.json"),
|
|
44
|
+
join(getAgentDir(), "settings.json"),
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Read `enabledModels` from a single settings.json file. Undefined when missing or absent. */
|
|
49
|
+
function readField(path: string): string[] | undefined {
|
|
50
|
+
if (!existsSync(path)) return undefined;
|
|
51
|
+
try {
|
|
52
|
+
const raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
53
|
+
if (Array.isArray(raw?.enabledModels)) return raw.enabledModels as string[];
|
|
54
|
+
} catch {
|
|
55
|
+
/* corrupt file — silent */
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read enabledModels from pi's settings — project-local overrides global.
|
|
62
|
+
* Mirrors pi's SettingsManager deep-merge for the `enabledModels` field
|
|
63
|
+
* (and matches our own loadSettings precedence in src/settings.ts).
|
|
64
|
+
* Returns undefined when neither file has the field.
|
|
65
|
+
*/
|
|
66
|
+
export function readEnabledModels(cwd: string): string[] | undefined {
|
|
67
|
+
const [project, global] = settingsPaths(cwd);
|
|
68
|
+
return readField(project) ?? readField(global);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve enabledModels patterns → Set<"provider/modelId"> (lowercase keys).
|
|
73
|
+
*
|
|
74
|
+
* Only exact `provider/modelId` patterns are matched (case-insensitive).
|
|
75
|
+
* Patterns without a slash, with glob characters, or with a `:thinking`
|
|
76
|
+
* suffix are silently dropped. See module-level docstring for rationale.
|
|
77
|
+
*
|
|
78
|
+
* Cache: keyed on JSON.stringify(patterns) + mtime/size of *both*
|
|
79
|
+
* project and global settings.json files. Re-resolves when either file
|
|
80
|
+
* changes or the patterns argument differs.
|
|
81
|
+
*
|
|
82
|
+
* Returns undefined when no patterns are provided or no patterns match
|
|
83
|
+
* (scope check becomes a no-op at the call site).
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
// Module-level cache — invalidated when either settings.json changes or patterns differ.
|
|
87
|
+
let cachedAllowed: Set<string> | undefined;
|
|
88
|
+
let cachedHash = "";
|
|
89
|
+
let cachedPatternsKey = "";
|
|
90
|
+
|
|
91
|
+
/** mtime+size hash of one file, or "missing" if absent. */
|
|
92
|
+
function hashOf(path: string): string {
|
|
93
|
+
try {
|
|
94
|
+
const s = statSync(path);
|
|
95
|
+
return `${s.mtimeMs}-${s.size}`;
|
|
96
|
+
} catch {
|
|
97
|
+
return "missing";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveEnabledModels(
|
|
102
|
+
patterns: string[] | undefined,
|
|
103
|
+
registry: ModelRegistryRef,
|
|
104
|
+
cwd: string = process.cwd(),
|
|
105
|
+
): Set<string> | undefined {
|
|
106
|
+
// Fast path: check cache (stat both project and global settings.json files)
|
|
107
|
+
const patternsKey = JSON.stringify(patterns);
|
|
108
|
+
const [project, global] = settingsPaths(cwd);
|
|
109
|
+
const fileHash = `${hashOf(project)};${hashOf(global)}`;
|
|
110
|
+
|
|
111
|
+
if (fileHash === cachedHash && patternsKey === cachedPatternsKey) {
|
|
112
|
+
return cachedAllowed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Cache miss — resolve
|
|
116
|
+
if (!patterns || patterns.length === 0) {
|
|
117
|
+
cachedHash = fileHash;
|
|
118
|
+
cachedPatternsKey = patternsKey;
|
|
119
|
+
cachedAllowed = undefined;
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const available = (registry.getAvailable?.() ?? registry.getAll()) as ModelEntry[];
|
|
124
|
+
const allowed = new Set<string>();
|
|
125
|
+
|
|
126
|
+
for (const pattern of patterns) {
|
|
127
|
+
const trimmed = pattern.trim();
|
|
128
|
+
if (!trimmed) continue; // skip empty/whitespace
|
|
129
|
+
resolveExact(trimmed, available, allowed);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = allowed.size > 0 ? allowed : undefined;
|
|
133
|
+
cachedHash = fileHash;
|
|
134
|
+
cachedPatternsKey = patternsKey;
|
|
135
|
+
cachedAllowed = result;
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* True when `model` is in the allowed set. Centralizes the key format
|
|
143
|
+
* (`provider/id` lowercase) so callers don't have to reproduce it —
|
|
144
|
+
* both set-building (resolveExact) and lookup go through `modelKey`.
|
|
145
|
+
*/
|
|
146
|
+
export function isModelInScope(
|
|
147
|
+
model: { provider: string; id: string },
|
|
148
|
+
allowed: Set<string>,
|
|
149
|
+
): boolean {
|
|
150
|
+
return allowed.has(modelKey(model));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Canonical lowercase `provider/id` key for the allowed set. */
|
|
154
|
+
function modelKey(model: { provider: string; id: string }): string {
|
|
155
|
+
return `${model.provider}/${model.id}`.toLowerCase();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve exact model pattern. Example: "google/gemma-4-31b-it".
|
|
160
|
+
*/
|
|
161
|
+
function resolveExact(
|
|
162
|
+
pattern: string,
|
|
163
|
+
available: ModelEntry[],
|
|
164
|
+
allowed: Set<string>,
|
|
165
|
+
): void {
|
|
166
|
+
// "provider/modelId" — exact (colon is part of id, not split)
|
|
167
|
+
const slashIdx = pattern.indexOf("/");
|
|
168
|
+
if (slashIdx === -1) return; // bare modelId not supported
|
|
169
|
+
|
|
170
|
+
const provider = pattern.slice(0, slashIdx).toLowerCase();
|
|
171
|
+
const modelId = pattern.slice(slashIdx + 1).toLowerCase();
|
|
172
|
+
const exact = available.find(
|
|
173
|
+
m => m.provider.toLowerCase() === provider && m.id.toLowerCase() === modelId,
|
|
174
|
+
);
|
|
175
|
+
if (exact) {
|
|
176
|
+
allowed.add(modelKey(exact));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
package/src/env.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* env.ts — Detect environment info (git, platform) for subagent system prompts.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionAPI } from "@
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import type { EnvInfo } from "./types.js";
|
|
7
7
|
|
|
8
8
|
export async function detectEnv(pi: ExtensionAPI, cwd: string): Promise<EnvInfo> {
|