@esso0428/pi-subagents 0.15.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 +638 -0
- package/CONTRIBUTING.md +68 -0
- package/LICENSE +21 -0
- package/README.md +745 -0
- package/SECURITY.md +95 -0
- package/dist/agent-manager.d.ts +144 -0
- package/dist/agent-manager.js +542 -0
- package/dist/agent-runner.d.ts +212 -0
- package/dist/agent-runner.js +850 -0
- package/dist/agent-types.d.ts +67 -0
- package/dist/agent-types.js +168 -0
- package/dist/context.d.ts +12 -0
- package/dist/context.js +56 -0
- package/dist/cross-extension-rpc.d.ts +46 -0
- package/dist/cross-extension-rpc.js +76 -0
- package/dist/custom-agents.d.ts +17 -0
- package/dist/custom-agents.js +156 -0
- package/dist/default-agents.d.ts +7 -0
- package/dist/default-agents.js +122 -0
- package/dist/enabled-models.d.ts +49 -0
- package/dist/enabled-models.js +145 -0
- package/dist/env.d.ts +6 -0
- package/dist/env.js +28 -0
- package/dist/group-join.d.ts +32 -0
- package/dist/group-join.js +116 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +2209 -0
- package/dist/invocation-config.d.ts +22 -0
- package/dist/invocation-config.js +15 -0
- package/dist/memory.d.ts +53 -0
- package/dist/memory.js +165 -0
- package/dist/model-resolver.d.ts +19 -0
- package/dist/model-resolver.js +80 -0
- package/dist/nico-overrides.d.ts +53 -0
- package/dist/nico-overrides.js +169 -0
- package/dist/output-file.d.ts +24 -0
- package/dist/output-file.js +101 -0
- package/dist/prompts.d.ts +32 -0
- package/dist/prompts.js +73 -0
- package/dist/schedule-store.d.ts +38 -0
- package/dist/schedule-store.js +155 -0
- package/dist/schedule.d.ts +109 -0
- package/dist/schedule.js +338 -0
- package/dist/settings.d.ts +141 -0
- package/dist/settings.js +162 -0
- package/dist/skill-loader.d.ts +24 -0
- package/dist/skill-loader.js +93 -0
- package/dist/status-note.d.ts +13 -0
- package/dist/status-note.js +24 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.js +5 -0
- package/dist/ui/agent-widget.d.ts +160 -0
- package/dist/ui/agent-widget.js +484 -0
- package/dist/ui/conversation-viewer.d.ts +57 -0
- package/dist/ui/conversation-viewer.js +354 -0
- package/dist/ui/fleet-list.d.ts +106 -0
- package/dist/ui/fleet-list.js +345 -0
- package/dist/ui/schedule-menu.d.ts +16 -0
- package/dist/ui/schedule-menu.js +95 -0
- package/dist/ui/viewer-keys.d.ts +20 -0
- package/dist/ui/viewer-keys.js +17 -0
- package/dist/usage.d.ts +50 -0
- package/dist/usage.js +49 -0
- package/dist/worktree.d.ts +45 -0
- package/dist/worktree.js +160 -0
- package/examples/agent-tool-description.md +42 -0
- package/package.json +56 -0
- package/src/agent-manager.ts +631 -0
- package/src/agent-runner.ts +1014 -0
- package/src/agent-types.ts +202 -0
- package/src/context.ts +58 -0
- package/src/cross-extension-rpc.ts +122 -0
- package/src/custom-agents.ts +167 -0
- package/src/default-agents.ts +126 -0
- package/src/enabled-models.ts +180 -0
- package/src/env.ts +33 -0
- package/src/group-join.ts +141 -0
- package/src/index.ts +2400 -0
- package/src/invocation-config.ts +40 -0
- package/src/memory.ts +179 -0
- package/src/model-resolver.ts +100 -0
- package/src/nico-overrides.ts +235 -0
- package/src/output-file.ts +110 -0
- package/src/prompts.ts +99 -0
- package/src/schedule-store.ts +153 -0
- package/src/schedule.ts +365 -0
- package/src/settings.ts +288 -0
- package/src/skill-loader.ts +102 -0
- package/src/status-note.ts +25 -0
- package/src/types.ts +208 -0
- package/src/ui/agent-widget.ts +566 -0
- package/src/ui/conversation-viewer.ts +362 -0
- package/src/ui/fleet-list.ts +380 -0
- package/src/ui/schedule-menu.ts +104 -0
- package/src/ui/viewer-keys.ts +39 -0
- package/src/usage.ts +60 -0
- package/src/worktree.ts +191 -0
- package/vitest.config.ts +18 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-runner.ts — Core execution engine: creates sessions, runs agents, collects results.
|
|
3
|
+
*/
|
|
4
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
5
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { type AgentSession, DefaultResourceLoader, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import type { SubagentType, ThinkingLevel } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Tool names registered by THIS extension. Single source of truth so the
|
|
10
|
+
* registration sites (index.ts) and the subagent exclusion list below can't
|
|
11
|
+
* drift apart. These are our own tools, not pi built-ins, so they can't be
|
|
12
|
+
* derived from pi — but they only need defining once.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SUBAGENT_TOOL_NAMES: {
|
|
15
|
+
readonly AGENT: "Agent";
|
|
16
|
+
readonly GET_RESULT: "get_subagent_result";
|
|
17
|
+
readonly STEER: "steer_subagent";
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Canonical name of an extension for `extensions: [...]` allowlist matching.
|
|
21
|
+
* Lowercased — extension names match case-insensitively so `extensions: [Mcp]`
|
|
22
|
+
* resolves the same as `[mcp]`. Tool names within `ext:foo/bar` are not affected.
|
|
23
|
+
* Directory extensions (`foo/index.ts`) resolve to the parent directory name;
|
|
24
|
+
* single-file extensions to the basename minus `.ts`/`.js`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function extensionCanonicalName(extPath: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* All names an extension answers to for allowlist matching (lowercased): its
|
|
29
|
+
* path-derived {@link extensionCanonicalName} plus, when a pi package manifest
|
|
30
|
+
* declares this entry, that package's unscoped short name (`@scope/foo` → `foo`).
|
|
31
|
+
* #143: an extension installed via `pi.extensions: ["./src/index.ts"]` would
|
|
32
|
+
* otherwise only ever match as `src` (the source directory), never by its
|
|
33
|
+
* package name. The path-derived name is preserved, so it keeps matching too.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extensionCanonicalNames(extPath: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Classify `extensions: string[]` frontmatter entries for the loader-level filter.
|
|
38
|
+
*
|
|
39
|
+
* An entry is a PATH iff it contains a path separator or starts with `~`; otherwise
|
|
40
|
+
* it is a NAME. `"*"` sets the wildcard flag (keep all default-discovered extensions).
|
|
41
|
+
*
|
|
42
|
+
* Path entries are resolved (`~` expanded, made absolute against `cwd`) into `paths`
|
|
43
|
+
* — and their canonical name is also added to `names`. The loader override matches
|
|
44
|
+
* everything by canonical name, so path-loaded extensions are matched via their name
|
|
45
|
+
* rather than their post-staging `Extension.path`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseExtensionsSpec(entries: string[], cwd: string): {
|
|
48
|
+
names: Set<string>;
|
|
49
|
+
paths: string[];
|
|
50
|
+
wildcard: boolean;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Parse raw `ext:` selector strings (from the `tools:` CSV) into the set of
|
|
54
|
+
* extension names to keep loaded and a per-extension tool-narrowing map.
|
|
55
|
+
*
|
|
56
|
+
* `ext:foo` → `extNames` has `foo`, no narrowing entry (all of foo's tools).
|
|
57
|
+
* `ext:foo/bar` → `extNames` has `foo`, `narrowing.foo` has `bar` (only `bar`).
|
|
58
|
+
* A name lands in `narrowing` only when a `/tool` form is seen, so a bare
|
|
59
|
+
* `ext:foo` alongside `ext:foo/bar` leaves narrowing in effect (narrowing wins).
|
|
60
|
+
* The split is on the first `/`; extension canonical names never contain `/`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseExtSelectors(entries: string[]): {
|
|
63
|
+
extNames: Set<string>;
|
|
64
|
+
narrowing: Map<string, Set<string>>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Keep a subagent's tool scope correct as extensions register tools over time.
|
|
68
|
+
*
|
|
69
|
+
* Extensions may call `registerTool` long after load — pi-mcp from `session_start`,
|
|
70
|
+
* context-mode from `before_agent_start` — so scope has to be re-derived rather than
|
|
71
|
+
* snapshotted. `registerTool` writes into the very `extension.tools` maps this reads,
|
|
72
|
+
* so `inScope()` sees late arrivals on the next call.
|
|
73
|
+
*
|
|
74
|
+
* Two enforcement points, because neither covers the whole picture:
|
|
75
|
+
*
|
|
76
|
+
* - `turn_end` re-narrows the ACTIVE set. pi emits `turn_end` immediately before
|
|
77
|
+
* `prepareNextTurn` re-snapshots `agent.state.tools`, and session listeners run
|
|
78
|
+
* synchronously, so the narrow lands in time for turns 2..N.
|
|
79
|
+
* - `beforeToolCall` blocks out-of-scope calls. Turn 1 cannot be narrowed at all:
|
|
80
|
+
* `before_agent_start` fires INSIDE `prompt()` and may widen the tool set, but
|
|
81
|
+
* `createContextSnapshot()` freezes that turn's tools immediately after — there
|
|
82
|
+
* is no hook in between. A call-time check is the only correct guard there.
|
|
83
|
+
*
|
|
84
|
+
* Both are installed on the session and deliberately NOT unsubscribed: they must
|
|
85
|
+
* outlive the `runAgent` call so resumed/steered turns stay scoped. pi's `dispose()`
|
|
86
|
+
* clears `_eventListeners`, so they die with the session rather than leaking.
|
|
87
|
+
*
|
|
88
|
+
* Only meaningful when extensions are loaded — under `noExtensions`/`isolated` the
|
|
89
|
+
* static `allowedToolNames` allowlist already gates the registry itself.
|
|
90
|
+
*/
|
|
91
|
+
export declare function installExtensionToolScope(session: AgentSession, ctx: {
|
|
92
|
+
loader: DefaultResourceLoader;
|
|
93
|
+
toolNames: string[];
|
|
94
|
+
disallowedSet: Set<string> | undefined;
|
|
95
|
+
extNames: Set<string>;
|
|
96
|
+
narrowing: Map<string, Set<string>>;
|
|
97
|
+
}): void;
|
|
98
|
+
/** Normalize max turns. undefined or 0 = unlimited, otherwise minimum 1. */
|
|
99
|
+
export declare function normalizeMaxTurns(n: number | undefined): number | undefined;
|
|
100
|
+
/** Get the default max turns value. undefined = unlimited. */
|
|
101
|
+
export declare function getDefaultMaxTurns(): number | undefined;
|
|
102
|
+
/** Set the default max turns value. undefined or 0 = unlimited, otherwise minimum 1. */
|
|
103
|
+
export declare function setDefaultMaxTurns(n: number | undefined): void;
|
|
104
|
+
/** Get the grace turns value. */
|
|
105
|
+
export declare function getGraceTurns(): number;
|
|
106
|
+
/** Set the grace turns value (minimum 1). */
|
|
107
|
+
export declare function setGraceTurns(n: number): void;
|
|
108
|
+
/** Info about a tool event in the subagent. */
|
|
109
|
+
export interface ToolActivity {
|
|
110
|
+
type: "start" | "end";
|
|
111
|
+
toolName: string;
|
|
112
|
+
}
|
|
113
|
+
export interface RunOptions {
|
|
114
|
+
/** ExtensionAPI instance — used for pi.exec() instead of execSync. */
|
|
115
|
+
pi: ExtensionAPI;
|
|
116
|
+
/** Manager-assigned id; suffixes session name to disambiguate parallel spawns (e.g. `Explore#a1b2c3d4`). */
|
|
117
|
+
agentId?: string;
|
|
118
|
+
model?: Model<any>;
|
|
119
|
+
maxTurns?: number;
|
|
120
|
+
signal?: AbortSignal;
|
|
121
|
+
isolated?: boolean;
|
|
122
|
+
inheritContext?: boolean;
|
|
123
|
+
thinkingLevel?: ThinkingLevel;
|
|
124
|
+
/** Override working directory (e.g. for worktree isolation). */
|
|
125
|
+
cwd?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Where .pi config is discovered (project extensions, skills, pi settings,
|
|
128
|
+
* agent memory). Default: same as the working directory. The manager sets
|
|
129
|
+
* this to the parent session's cwd when `SpawnOptions.cwd` points the
|
|
130
|
+
* working directory elsewhere — the agent works *there* but carries the
|
|
131
|
+
* parent project's config (the target's `.pi` extensions never execute).
|
|
132
|
+
*
|
|
133
|
+
* WARNING for future callers: if you pass `cwd` pointing at a directory the
|
|
134
|
+
* user didn't open, you almost certainly must pass `configCwd` too —
|
|
135
|
+
* omitting it makes the target's `.pi` extensions execute in this process.
|
|
136
|
+
* (Worktree isolation is the one intentional exception: its copy IS the
|
|
137
|
+
* parent's repo, so config resolving inside it is correct.)
|
|
138
|
+
*/
|
|
139
|
+
configCwd?: string;
|
|
140
|
+
/** Called on tool start/end with activity info. */
|
|
141
|
+
onToolActivity?: (activity: ToolActivity) => void;
|
|
142
|
+
/** Called on streaming text deltas from the assistant response. */
|
|
143
|
+
onTextDelta?: (delta: string, fullText: string) => void;
|
|
144
|
+
onSessionCreated?: (session: AgentSession) => void;
|
|
145
|
+
/** Called at the end of each agentic turn with the cumulative count. */
|
|
146
|
+
onTurnEnd?: (turnCount: number) => void;
|
|
147
|
+
/**
|
|
148
|
+
* Called once per assistant message_end with that message's usage delta.
|
|
149
|
+
* Lets callers maintain a lifetime accumulator that survives compaction
|
|
150
|
+
* (which replaces session.state.messages and resets stats-derived sums).
|
|
151
|
+
*/
|
|
152
|
+
onAssistantUsage?: (usage: {
|
|
153
|
+
input: number;
|
|
154
|
+
output: number;
|
|
155
|
+
cacheWrite: number;
|
|
156
|
+
}) => void;
|
|
157
|
+
/**
|
|
158
|
+
* Called when the session successfully compacts. `tokensBefore` is upstream's
|
|
159
|
+
* pre-compaction context size estimate. Aborted compactions don't fire.
|
|
160
|
+
*/
|
|
161
|
+
onCompaction?: (info: {
|
|
162
|
+
reason: "manual" | "threshold" | "overflow";
|
|
163
|
+
tokensBefore: number;
|
|
164
|
+
}) => void;
|
|
165
|
+
}
|
|
166
|
+
export interface RunResult {
|
|
167
|
+
responseText: string;
|
|
168
|
+
session: AgentSession;
|
|
169
|
+
/** True if the agent was hard-aborted (max_turns + grace exceeded). */
|
|
170
|
+
aborted: boolean;
|
|
171
|
+
/** True if the agent was steered to wrap up (hit soft turn limit) but finished in time. */
|
|
172
|
+
steered: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* A failure message for the run's FINAL assistant turn, when that turn failed:
|
|
175
|
+
* a provider error (stopReason "error"), or a "length" stop that produced no
|
|
176
|
+
* text (a silent max-token death). pi resolves an exhausted-retries failure
|
|
177
|
+
* normally instead of rejecting, so without this the manager would report such
|
|
178
|
+
* a run as completed — with an empty result, or worse, an earlier turn's text
|
|
179
|
+
* presented as the answer (#144). Undefined for a clean stop, or a "length"
|
|
180
|
+
* stop that produced text (a legitimate truncated answer).
|
|
181
|
+
*/
|
|
182
|
+
failure?: string;
|
|
183
|
+
}
|
|
184
|
+
export declare function runAgent(ctx: ExtensionContext, type: SubagentType, prompt: string, options: RunOptions): Promise<RunResult>;
|
|
185
|
+
/**
|
|
186
|
+
* Send a new prompt to an existing session (resume).
|
|
187
|
+
*/
|
|
188
|
+
export declare function resumeAgent(session: AgentSession, prompt: string, options?: {
|
|
189
|
+
onToolActivity?: (activity: ToolActivity) => void;
|
|
190
|
+
onAssistantUsage?: (usage: {
|
|
191
|
+
input: number;
|
|
192
|
+
output: number;
|
|
193
|
+
cacheWrite: number;
|
|
194
|
+
}) => void;
|
|
195
|
+
onCompaction?: (info: {
|
|
196
|
+
reason: "manual" | "threshold" | "overflow";
|
|
197
|
+
tokensBefore: number;
|
|
198
|
+
}) => void;
|
|
199
|
+
signal?: AbortSignal;
|
|
200
|
+
}): Promise<{
|
|
201
|
+
text: string;
|
|
202
|
+
failure?: string;
|
|
203
|
+
}>;
|
|
204
|
+
/**
|
|
205
|
+
* Send a steering message to a running subagent.
|
|
206
|
+
* The message will interrupt the agent after its current tool execution.
|
|
207
|
+
*/
|
|
208
|
+
export declare function steerAgent(session: AgentSession, message: string): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Get the subagent's conversation messages as formatted text.
|
|
211
|
+
*/
|
|
212
|
+
export declare function getAgentConversation(session: AgentSession): string;
|