@jopqior/pi-subagents 1.0.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 +2705 -0
- package/LICENSE +21 -0
- package/README.md +503 -0
- package/dist/public.d.ts +331 -0
- package/dist/settings.d.ts +82 -0
- package/docs/architecture/architecture.md +1566 -0
- package/docs/architecture/client-server-opportunities.md +127 -0
- package/docs/architecture/history/phase-1-api-boundary.md +8 -0
- package/docs/architecture/history/phase-10-structural-decomposition.md +141 -0
- package/docs/architecture/history/phase-11-closure-to-class.md +100 -0
- package/docs/architecture/history/phase-12-complexity-test-fixtures.md +55 -0
- package/docs/architecture/history/phase-13-remaining-smells.md +88 -0
- package/docs/architecture/history/phase-14-strip-policy.md +49 -0
- package/docs/architecture/history/phase-15-domain-model-evolution.md +73 -0
- package/docs/architecture/history/phase-16-invert-dependencies.md +144 -0
- package/docs/architecture/history/phase-17-core-consolidation.md +214 -0
- package/docs/architecture/history/phase-18-reconsider-ui.md +166 -0
- package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
- package/docs/architecture/history/phase-2-remove-scheduling.md +9 -0
- package/docs/architecture/history/phase-20-result-delivery.md +245 -0
- package/docs/architecture/history/phase-21-classification-model-boundary.md +107 -0
- package/docs/architecture/history/phase-3-remove-rpc-groupjoin.md +11 -0
- package/docs/architecture/history/phase-4-implement-service.md +8 -0
- package/docs/architecture/history/phase-5-decompose-index.md +42 -0
- package/docs/architecture/history/phase-7-encapsulation.md +173 -0
- package/docs/architecture/history/phase-8-testability.md +103 -0
- package/docs/architecture/history/phase-9-observation-ctx.md +122 -0
- package/docs/comparison-with-upstream.md +77 -0
- package/docs/configuration.md +364 -0
- package/docs/decisions/0001-deferred-patches.md +80 -0
- package/docs/decisions/0002-extensions-on-a-minimal-core.md +125 -0
- package/docs/decisions/0003-publish-bundled-type-declarations.md +71 -0
- package/docs/decisions/0004-reconsider-ui-direction.md +279 -0
- package/docs/decisions/0005-subagent-record-admission-policy.md +106 -0
- package/docs/decisions/0006-inherited-prompt-is-identity-only.md +104 -0
- package/docs/decisions/0007-transcript-viewer-is-not-an-overlay.md +228 -0
- package/docs/decisions/0008-inherited-region-is-shared-parts.md +81 -0
- package/docs/decisions/0009-portable-inheritance-is-provider-scoped.md +116 -0
- package/package.json +91 -0
- package/src/config/agent-types.ts +135 -0
- package/src/config/custom-agents.ts +151 -0
- package/src/config/default-agents.ts +121 -0
- package/src/config/invocation-config.ts +167 -0
- package/src/config/thinking-level.ts +58 -0
- package/src/debug.ts +14 -0
- package/src/handlers/index.ts +3 -0
- package/src/handlers/interrupt.ts +58 -0
- package/src/handlers/lifecycle.ts +71 -0
- package/src/handlers/widget-events.ts +49 -0
- package/src/index.ts +292 -0
- package/src/layered-settings.ts +105 -0
- package/src/lifecycle/child-lifecycle.ts +115 -0
- package/src/lifecycle/child-shutdown.ts +105 -0
- package/src/lifecycle/concurrency-limiter.ts +55 -0
- package/src/lifecycle/create-subagent-session.ts +335 -0
- package/src/lifecycle/parent-snapshot.ts +119 -0
- package/src/lifecycle/run-listeners.ts +37 -0
- package/src/lifecycle/selection-scope.ts +116 -0
- package/src/lifecycle/spawn-selection.ts +259 -0
- package/src/lifecycle/subagent-manager.ts +546 -0
- package/src/lifecycle/subagent-session.ts +347 -0
- package/src/lifecycle/subagent-state.ts +404 -0
- package/src/lifecycle/subagent.ts +885 -0
- package/src/lifecycle/turn-limits.ts +13 -0
- package/src/lifecycle/usage.ts +60 -0
- package/src/lifecycle/workspace-bracket.ts +76 -0
- package/src/lifecycle/workspace.ts +46 -0
- package/src/observation/composite-subagent-observer.ts +74 -0
- package/src/observation/notification.ts +430 -0
- package/src/observation/outcome-delivery.ts +239 -0
- package/src/observation/record-observer.ts +78 -0
- package/src/observation/renderer.ts +161 -0
- package/src/observation/subagent-events-observer.ts +148 -0
- package/src/runtime.ts +137 -0
- package/src/service/service-adapter.ts +201 -0
- package/src/service/service.ts +246 -0
- package/src/session/ask-parent-tool.ts +69 -0
- package/src/session/content-items.ts +53 -0
- package/src/session/context.ts +80 -0
- package/src/session/conversation.ts +49 -0
- package/src/session/env.ts +40 -0
- package/src/session/model-resolver.ts +126 -0
- package/src/session/notify-parent-tool.ts +83 -0
- package/src/session/package-exclusions.ts +75 -0
- package/src/session/prompts.ts +231 -0
- package/src/session/provider-inheritance.ts +56 -0
- package/src/session/selection-catalogue.ts +143 -0
- package/src/session/session-config.ts +202 -0
- package/src/session/session-dir.ts +38 -0
- package/src/settings.ts +447 -0
- package/src/tools/agent-tool.ts +305 -0
- package/src/tools/background-spawner.ts +83 -0
- package/src/tools/foreground-runner.ts +159 -0
- package/src/tools/get-result-renderer.ts +119 -0
- package/src/tools/get-result-report.ts +84 -0
- package/src/tools/get-result-tool.ts +192 -0
- package/src/tools/helpers.ts +118 -0
- package/src/tools/result-renderer.ts +153 -0
- package/src/tools/spawn-config.ts +192 -0
- package/src/tools/steer-tool.ts +109 -0
- package/src/types.ts +143 -0
- package/src/ui/agent-widget.ts +333 -0
- package/src/ui/bounded-lines.ts +45 -0
- package/src/ui/display.ts +180 -0
- package/src/ui/glyphs.ts +62 -0
- package/src/ui/session-navigation.ts +150 -0
- package/src/ui/session-navigator.ts +255 -0
- package/src/ui/subagents-settings.ts +179 -0
- package/src/ui/transcript-content.ts +374 -0
- package/src/ui/widget-renderer.ts +301 -0
package/dist/public.d.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { Model, Api } from '@earendil-works/pi-ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* thinking-level.ts — The thinking-level vocabulary both spawn doors validate against.
|
|
5
|
+
*
|
|
6
|
+
* Pi accepts `off` alongside pi-ai's `ThinkingLevel`, but does not export the
|
|
7
|
+
* combined list: `THINKING_LEVEL_OPTIONS` lives in `@earendil-works/pi-coding-agent`'s
|
|
8
|
+
* internals and is absent from its public entry, so this package carries its own.
|
|
9
|
+
*
|
|
10
|
+
* An unrecognized level is not ignored by the SDK — `clampThinkingLevel` misses it in
|
|
11
|
+
* the ordered table and falls to the first supported level, which is always `off`. A
|
|
12
|
+
* typo therefore disables thinking entirely, which is why both doors reject rather
|
|
13
|
+
* than pass the value through (Refs #834).
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Every level Pi accepts, in ascending order of effort.
|
|
17
|
+
*
|
|
18
|
+
* The `satisfies` clause rejects an entry the installed SDK does not declare, and the
|
|
19
|
+
* parity tests in `test/config/thinking-level.test.ts` check both directions at runtime.
|
|
20
|
+
* One drift escapes them: a level the SDK adds *and* gates behind a `thinkingLevelMap`
|
|
21
|
+
* key, the way `xhigh` and `max` are gated, is invisible until a caller names it.
|
|
22
|
+
*/
|
|
23
|
+
declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
24
|
+
/** A thinking level as an agent file or a spawn caller may spell it. */
|
|
25
|
+
type SubagentThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* types.ts — Type definitions for the subagent system.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** Agent type: any string name (built-in defaults or user-defined). */
|
|
32
|
+
type SubagentType = string;
|
|
33
|
+
|
|
34
|
+
/** usage.ts — Token usage: shapes, accumulator operators, session-stats readers. */
|
|
35
|
+
/**
|
|
36
|
+
* Lifetime usage components, accumulated via `message_end` events. Survives
|
|
37
|
+
* compaction (which replaces session.state.messages and would reset any
|
|
38
|
+
* stats-derived sum). cacheRead is excluded because each turn's cacheRead is
|
|
39
|
+
* the cumulative cached prefix re-read on that one call — summing across
|
|
40
|
+
* turns counts the prefix N times. See issue #38.
|
|
41
|
+
*/
|
|
42
|
+
type LifetimeUsage = {
|
|
43
|
+
input: number;
|
|
44
|
+
output: number;
|
|
45
|
+
cacheWrite: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* subagent-state.ts — SubagentState value object: lifecycle status, metrics, and live activity.
|
|
50
|
+
*
|
|
51
|
+
* Owns the passive, readable state of a subagent — status, result, error,
|
|
52
|
+
* timestamps, stats (toolUses, lifetimeUsage, compactionCount), and live-activity
|
|
53
|
+
* fields (turnCount, activeTools, responseText) — together with the transition
|
|
54
|
+
* methods (markRunning, markCompleted, …), accumulation methods
|
|
55
|
+
* (incrementToolUses, addUsage, incrementCompactions), and live-activity
|
|
56
|
+
* transition methods (incrementTurnCount, addActiveTool, removeActiveTool,
|
|
57
|
+
* resetResponseText, appendResponseText) that mutate them.
|
|
58
|
+
*
|
|
59
|
+
* State is encapsulated behind getters; external code reads through them but
|
|
60
|
+
* mutates only via the transition/accumulation methods. The value object owns
|
|
61
|
+
* all of its own mutations — no field is written from outside.
|
|
62
|
+
*
|
|
63
|
+
* Subagent holds one of these privately and delegates its getters and mutation
|
|
64
|
+
* methods to it. Extracting it lets the lifecycle state machine and the
|
|
65
|
+
* session-event observer be unit-tested without constructing an executor.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
type SubagentStatus = "queued" | "running" | "completed" | "steered" | "aborted" | "stopped" | "error";
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* workspace.ts — The single generative extension seam (ADR 0002, Phase 16 Step 2).
|
|
72
|
+
*
|
|
73
|
+
* "Where does a child run, and what brackets the run?" is a strategy (git
|
|
74
|
+
* worktree, container, tmpdir, remote sandbox), not core behavior. The core
|
|
75
|
+
* needs only a working directory plus a disposal hook; the default — the
|
|
76
|
+
* parent's cwd, with no setup/teardown — is always correct.
|
|
77
|
+
*
|
|
78
|
+
* Unlike the observational lifecycle events in child-lifecycle.ts, this is a
|
|
79
|
+
* *generative* seam: a registered provider returns a value the core consumes
|
|
80
|
+
* synchronously at run-start. The core has no knowledge of git or worktrees.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
/** Context the core hands a provider when a child run starts. */
|
|
84
|
+
interface WorkspacePrepareContext {
|
|
85
|
+
agentId: string;
|
|
86
|
+
agentType: SubagentType;
|
|
87
|
+
baseCwd: string;
|
|
88
|
+
}
|
|
89
|
+
/** Outcome the core reports to a workspace when the run ends. */
|
|
90
|
+
interface WorkspaceDisposeOutcome {
|
|
91
|
+
status: SubagentStatus;
|
|
92
|
+
description: string;
|
|
93
|
+
}
|
|
94
|
+
/** What dispose may hand back for the core to fold into the child result. */
|
|
95
|
+
interface WorkspaceDisposeResult {
|
|
96
|
+
/** Appended verbatim to the child's result text — the provider owns the wording. */
|
|
97
|
+
resultAddendum?: string;
|
|
98
|
+
}
|
|
99
|
+
/** A prepared working directory plus its bracketed teardown. Born complete. */
|
|
100
|
+
interface Workspace {
|
|
101
|
+
/** The working directory — already exists when the workspace is handed back. */
|
|
102
|
+
readonly cwd: string;
|
|
103
|
+
dispose(outcome: WorkspaceDisposeOutcome): WorkspaceDisposeResult | undefined;
|
|
104
|
+
}
|
|
105
|
+
/** The single generative seam: supplies a child's workspace. */
|
|
106
|
+
interface WorkspaceProvider {
|
|
107
|
+
prepare(ctx: WorkspacePrepareContext): Promise<Workspace | undefined>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* subagent.ts — Subagent class: identity, lifecycle status, and per-subagent behavior.
|
|
112
|
+
*
|
|
113
|
+
* Status/stats are delegated to the SubagentState value object; listener
|
|
114
|
+
* lifecycle to RunListeners; workspace prepare/dispose to WorkspaceBracket.
|
|
115
|
+
* Behavior (abort, steer buffering) lives here rather than on SubagentManager.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Why a resume of an agent would be refused.
|
|
120
|
+
*
|
|
121
|
+
* One vocabulary for a fact three record-level conditions used to answer
|
|
122
|
+
* separately: the resume door decided it from `isSessionReady()`,
|
|
123
|
+
* `sessionReleased`, and `workspaceDisposed`, while the result carriers never
|
|
124
|
+
* consulted any of them and advertised the resume regardless.
|
|
125
|
+
*
|
|
126
|
+
* `still-running` is the one transient member: it is a refusal of *now* rather
|
|
127
|
+
* than of ever, and the carriers word it accordingly.
|
|
128
|
+
*/
|
|
129
|
+
type ResumeRefusal = "still-running" | "no-session" | "session-released" | "workspace-disposed";
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* subagent-manager.ts - Tracks subagents, background execution, resume support.
|
|
133
|
+
*
|
|
134
|
+
* Background agents are subject to a configurable concurrency limit (default: 4).
|
|
135
|
+
* Excess agents are scheduled on a ConcurrencyLimiter and auto-started as running
|
|
136
|
+
* agents complete. Foreground agents bypass the limiter (they block the parent anyway).
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Why a resume was refused, across every front door.
|
|
141
|
+
*
|
|
142
|
+
* Widens the record's own vocabulary by the one refusal that is not a fact
|
|
143
|
+
* about a record: an id no record answers to.
|
|
144
|
+
*/
|
|
145
|
+
type ResumeRefusalReason = ResumeRefusal | "unknown-agent";
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* service.ts — Public API surface for cross-extension access to subagents.
|
|
149
|
+
*
|
|
150
|
+
* Consumers declare this package as an optional peer dependency and use
|
|
151
|
+
* dynamic import to access the accessor functions:
|
|
152
|
+
*
|
|
153
|
+
* const { getSubagentsService } = await import("@jopqior/pi-subagents");
|
|
154
|
+
* const svc = getSubagentsService();
|
|
155
|
+
* svc?.spawn("Explore", "Check for stale TODOs");
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Serializable by-value snapshot of an agent's state.
|
|
160
|
+
*
|
|
161
|
+
* Produced by this package and read by consumers — not a contract third
|
|
162
|
+
* parties implement, so a new field is a minor release. What earns a field a
|
|
163
|
+
* place here (and what the snapshot deliberately withholds) is decided in
|
|
164
|
+
* `docs/decisions/0005-subagent-record-admission-policy.md`.
|
|
165
|
+
*/
|
|
166
|
+
interface SubagentRecord {
|
|
167
|
+
id: string;
|
|
168
|
+
type: string;
|
|
169
|
+
description: string;
|
|
170
|
+
status: SubagentStatus;
|
|
171
|
+
/** Scheduling and announcement mode, resolved once at the manager choke point. */
|
|
172
|
+
isBackground: boolean;
|
|
173
|
+
result?: string;
|
|
174
|
+
/** The question the agent ended its turn with, when it declared one. */
|
|
175
|
+
pendingQuestion?: string;
|
|
176
|
+
error?: string;
|
|
177
|
+
toolUses: number;
|
|
178
|
+
/** Turns consumed so far; starts at 1. */
|
|
179
|
+
turnCount: number;
|
|
180
|
+
/** Turn ceiling for this run, when one was set. */
|
|
181
|
+
maxTurns?: number;
|
|
182
|
+
startedAt: number;
|
|
183
|
+
completedAt?: number;
|
|
184
|
+
lifetimeUsage: LifetimeUsage;
|
|
185
|
+
compactionCount: number;
|
|
186
|
+
/** Path to the agent's session JSONL, once the session exists. */
|
|
187
|
+
outputFile?: string;
|
|
188
|
+
}
|
|
189
|
+
/** Options for resuming an agent via the service. */
|
|
190
|
+
interface ResumeOptions {
|
|
191
|
+
/**
|
|
192
|
+
* Declare that the caller will deliver the resumed outcome to the parent,
|
|
193
|
+
* suppressing the completion nudge for it. Omitted, the resumed outcome is
|
|
194
|
+
* announced exactly as a background completion is.
|
|
195
|
+
*/
|
|
196
|
+
claimOutcome?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Cancels the resumed turn loop. `abort(id)` does not reach it: a resume does
|
|
199
|
+
* not run under the record's own abort controller.
|
|
200
|
+
*/
|
|
201
|
+
signal?: AbortSignal;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* What a resume attempt produced.
|
|
205
|
+
*
|
|
206
|
+
* A resumed run that *failed* is still `resumed` — the snapshot carries
|
|
207
|
+
* `status: "error"` and the message. `refused` means no turn loop ran.
|
|
208
|
+
*/
|
|
209
|
+
type ResumeResult = {
|
|
210
|
+
kind: "resumed";
|
|
211
|
+
record: SubagentRecord;
|
|
212
|
+
} | {
|
|
213
|
+
kind: "refused";
|
|
214
|
+
reason: ResumeRefusalReason;
|
|
215
|
+
};
|
|
216
|
+
/** Options for spawning an agent via the service. */
|
|
217
|
+
interface SpawnOptions {
|
|
218
|
+
description?: string;
|
|
219
|
+
model?: string;
|
|
220
|
+
maxTurns?: number;
|
|
221
|
+
thinkingLevel?: string;
|
|
222
|
+
inheritContext?: boolean;
|
|
223
|
+
foreground?: boolean;
|
|
224
|
+
bypassQueue?: boolean;
|
|
225
|
+
}
|
|
226
|
+
/** The pair a human selected for one new run — the authority for model and thinking. */
|
|
227
|
+
interface SpawnSelection {
|
|
228
|
+
readonly model: Model<Api>;
|
|
229
|
+
/** Mandatory and never `undefined`: `off` through `max`, including `off`. */
|
|
230
|
+
readonly thinkingLevel: SubagentThinkingLevel;
|
|
231
|
+
}
|
|
232
|
+
/** What a provider is asked, for one admitted new run about to create a child session. */
|
|
233
|
+
interface SpawnSelectionRequest {
|
|
234
|
+
/** Identifies the request in the root UI. */
|
|
235
|
+
readonly agentId: string;
|
|
236
|
+
readonly agentType: string;
|
|
237
|
+
readonly description: string;
|
|
238
|
+
/** The authenticated available models of the session whose manager is spawning — the actual choices. */
|
|
239
|
+
readonly availableModels: readonly Model<Api>[];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Asks for the model and thinking level a new run should use.
|
|
243
|
+
*
|
|
244
|
+
* Resolves with the selected pair, or `undefined` for user cancellation —
|
|
245
|
+
* never an approval that silently keeps inherited values. Infrastructure
|
|
246
|
+
* failures (no UI, unavailable catalogue) reject rather than resolve.
|
|
247
|
+
*/
|
|
248
|
+
interface SpawnSelectionProvider {
|
|
249
|
+
select(request: SpawnSelectionRequest, signal: AbortSignal): Promise<SpawnSelection | undefined>;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The outcome of registering a selection provider.
|
|
253
|
+
*
|
|
254
|
+
* `owned` — this session is the root of its subagent tree and now holds the
|
|
255
|
+
* lease; `dispose()` revokes it (idempotent) and a new session is required to
|
|
256
|
+
* register again.
|
|
257
|
+
*
|
|
258
|
+
* `inherited` — this session is a descendant: the supplied provider is NOT
|
|
259
|
+
* installed and the root's ownership is untouched (including when the
|
|
260
|
+
* inherited lease is already closed); `dispose()` is a no-op.
|
|
261
|
+
*/
|
|
262
|
+
type SpawnSelectionRegistration = {
|
|
263
|
+
readonly kind: "owned";
|
|
264
|
+
dispose(): void;
|
|
265
|
+
} | {
|
|
266
|
+
readonly kind: "inherited";
|
|
267
|
+
dispose(): void;
|
|
268
|
+
};
|
|
269
|
+
/** The public service contract for cross-extension subagent access. */
|
|
270
|
+
interface SubagentsService {
|
|
271
|
+
/** Spawn an agent. Returns the agent ID immediately. */
|
|
272
|
+
spawn(type: string, prompt: string, options?: SpawnOptions): string;
|
|
273
|
+
/** Get a snapshot of an agent's current state. */
|
|
274
|
+
getRecord(id: string): SubagentRecord | undefined;
|
|
275
|
+
/** List all tracked agents, most recent first. */
|
|
276
|
+
listAgents(): SubagentRecord[];
|
|
277
|
+
/** Abort a running or queued agent. Returns false if not found. */
|
|
278
|
+
abort(id: string): boolean;
|
|
279
|
+
/** Send a steering message to a running agent. */
|
|
280
|
+
steer(id: string, message: string): Promise<boolean>;
|
|
281
|
+
/**
|
|
282
|
+
* Resume a settled agent with a new prompt, continuing its session.
|
|
283
|
+
*
|
|
284
|
+
* Resolves when the resumed run reaches a terminal state, carrying the
|
|
285
|
+
* terminal snapshot — a caller that does not need the outcome can ignore the
|
|
286
|
+
* promise. A refusal resolves promptly instead: the checks are synchronous
|
|
287
|
+
* and no turn loop is started.
|
|
288
|
+
*/
|
|
289
|
+
resume(id: string, prompt: string, options?: ResumeOptions): Promise<ResumeResult>;
|
|
290
|
+
/** Wait for all running and queued agents to complete. */
|
|
291
|
+
waitForAll(): Promise<void>;
|
|
292
|
+
/** Whether any agents are running or queued. */
|
|
293
|
+
hasRunning(): boolean;
|
|
294
|
+
/**
|
|
295
|
+
* Register the single workspace provider that supplies a child's working
|
|
296
|
+
* directory plus bracketed setup/teardown. Throws if one is already
|
|
297
|
+
* registered. Returns a disposer that unregisters the provider.
|
|
298
|
+
*/
|
|
299
|
+
registerWorkspaceProvider(provider: WorkspaceProvider): () => void;
|
|
300
|
+
/**
|
|
301
|
+
* Register the per-spawn selection provider this session's subagent tree
|
|
302
|
+
* will consult before creating any new child session.
|
|
303
|
+
*
|
|
304
|
+
* On the tree's root session: installs the provider (throws if one is
|
|
305
|
+
* already registered, or once this session's scope is closed) and returns an
|
|
306
|
+
* `owned` registration whose disposer revokes it. On a descendant session:
|
|
307
|
+
* installs nothing and returns `inherited` — the root's chooser serves the
|
|
308
|
+
* whole tree.
|
|
309
|
+
*/
|
|
310
|
+
registerSpawnSelectionProvider(provider: SpawnSelectionProvider): SpawnSelectionRegistration;
|
|
311
|
+
}
|
|
312
|
+
/** Event channel constants for pi.events subscriptions. */
|
|
313
|
+
declare const SUBAGENT_EVENTS: {
|
|
314
|
+
readonly STARTED: "subagents:started";
|
|
315
|
+
readonly COMPLETED: "subagents:completed";
|
|
316
|
+
readonly FAILED: "subagents:failed";
|
|
317
|
+
readonly RESUMING: "subagents:resuming";
|
|
318
|
+
readonly RESUMED: "subagents:resumed";
|
|
319
|
+
readonly COMPACTED: "subagents:compacted";
|
|
320
|
+
readonly CREATED: "subagents:created";
|
|
321
|
+
readonly STEERED: "subagents:steered";
|
|
322
|
+
};
|
|
323
|
+
/** Publish the SubagentsService on globalThis for cross-extension access. */
|
|
324
|
+
declare function publishSubagentsService(service: SubagentsService): void;
|
|
325
|
+
/** Retrieve the published SubagentsService, or undefined if not yet published. */
|
|
326
|
+
declare function getSubagentsService(): SubagentsService | undefined;
|
|
327
|
+
/** Remove the SubagentsService from globalThis (call on shutdown/reload). */
|
|
328
|
+
declare function unpublishSubagentsService(): void;
|
|
329
|
+
|
|
330
|
+
export { SUBAGENT_EVENTS, getSubagentsService, publishSubagentsService, unpublishSubagentsService };
|
|
331
|
+
export type { LifetimeUsage, ResumeOptions, ResumeRefusal, ResumeRefusalReason, ResumeResult, SpawnOptions, SpawnSelection, SpawnSelectionProvider, SpawnSelectionRegistration, SpawnSelectionRequest, SubagentRecord, SubagentStatus, SubagentsService, Workspace, WorkspaceDisposeOutcome, WorkspaceDisposeResult, WorkspacePrepareContext, WorkspaceProvider };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic layered settings loader for `@gotgenes/pi-*` extensions.
|
|
3
|
+
*
|
|
4
|
+
* Extensions that store configuration in JSON files under a global agent
|
|
5
|
+
* directory and a per-project `.pi/` folder share the same three-step idiom:
|
|
6
|
+
*
|
|
7
|
+
* 1. Read the global file (`<agentDir>/<filename>`).
|
|
8
|
+
* 2. Read the project file (`<cwd>/.pi/<filename>`).
|
|
9
|
+
* 3. Merge them — project wins on conflicts — and return the result.
|
|
10
|
+
*
|
|
11
|
+
* Both layers are optional: a missing file is silent (`{}`), and a file that
|
|
12
|
+
* cannot be parsed warns to stderr and is treated as absent so startup
|
|
13
|
+
* proceeds normally.
|
|
14
|
+
*
|
|
15
|
+
* ## Usage
|
|
16
|
+
*
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { loadLayeredSettings, type LayeredSettingsSource } from "@jopqior/pi-subagents/settings";
|
|
19
|
+
*
|
|
20
|
+
* interface MyConfig { enabled?: boolean; limit?: number }
|
|
21
|
+
*
|
|
22
|
+
* function sanitize(raw: unknown): Partial<MyConfig> {
|
|
23
|
+
* if (!raw || typeof raw !== "object") return {};
|
|
24
|
+
* const r = raw as Record<string, unknown>;
|
|
25
|
+
* const out: Partial<MyConfig> = {};
|
|
26
|
+
* if (typeof r.enabled === "boolean") out.enabled = r.enabled;
|
|
27
|
+
* if (typeof r.limit === "number") out.limit = r.limit;
|
|
28
|
+
* return out;
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* const config = loadLayeredSettings<MyConfig>({
|
|
32
|
+
* agentDir, // e.g. from the Pi runtime env — the agent home directory
|
|
33
|
+
* cwd, // project root — project file is at <cwd>/.pi/<filename>
|
|
34
|
+
* filename: "my-extension.json",
|
|
35
|
+
* sanitize,
|
|
36
|
+
* warnLabel: "my-extension",
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Parameters for one layered settings load: describes where the files live,
|
|
44
|
+
* how to validate their contents, and what label to use in warnings.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
interface LayeredSettingsSource<T> {
|
|
49
|
+
/** Directory holding the global settings file (typically the Pi agent dir). */
|
|
50
|
+
agentDir: string;
|
|
51
|
+
/** Project root; the project file lives at `<cwd>/.pi/<filename>`. */
|
|
52
|
+
cwd: string;
|
|
53
|
+
/** Base filename for both layers, e.g. `"subagents.json"`. */
|
|
54
|
+
filename: string;
|
|
55
|
+
/**
|
|
56
|
+
* Validate and coerce parsed JSON into a partial settings object.
|
|
57
|
+
* Unknown or invalid fields should be silently dropped — return `{}` for
|
|
58
|
+
* unrecognised shapes. Never throw.
|
|
59
|
+
*/
|
|
60
|
+
sanitize: (raw: unknown) => Partial<T>;
|
|
61
|
+
/**
|
|
62
|
+
* Short label used in the malformed-file warning prefix,
|
|
63
|
+
* e.g. `"pi-subagents"` → `"[pi-subagents] Ignoring malformed settings at …"`.
|
|
64
|
+
*/
|
|
65
|
+
warnLabel: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Load merged layered settings: global provides defaults, project overrides.
|
|
69
|
+
*
|
|
70
|
+
* - A missing file is silent — returns `{}` for that layer.
|
|
71
|
+
* - A file that exists but cannot be parsed warns to stderr and returns `{}` for
|
|
72
|
+
* that layer, so startup proceeds normally.
|
|
73
|
+
* - The two layers are merged with a shallow spread; project keys win.
|
|
74
|
+
*
|
|
75
|
+
* Throws nothing. All error conditions produce a warning and fall back to `{}`.
|
|
76
|
+
*
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
declare function loadLayeredSettings<T>(source: LayeredSettingsSource<T>): Partial<T>;
|
|
80
|
+
|
|
81
|
+
export { loadLayeredSettings };
|
|
82
|
+
export type { LayeredSettingsSource };
|