@nklisch/pi-enhanced 0.2.7 → 0.3.1
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 +16 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-x64-gnu.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-plugins/README.md +4 -2
- package/node_modules/@nklisch/pi-plugins/dist/hooks.js +81 -8
- package/node_modules/@nklisch/pi-plugins/dist/hooks.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/mcp.js +32 -1
- package/node_modules/@nklisch/pi-plugins/dist/mcp.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.d.ts +10 -4
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js +144 -10
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js +40 -14
- package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/CHANGELOG.md +27 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +76 -47
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/public.d.ts +171 -140
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/settings.d.ts +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/FORK-MAINTENANCE.md +8 -7
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +12 -10
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +118 -104
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/comparison-with-upstream.md +3 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0004-reconsider-ui-direction.md +5 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/custom-agents.ts +31 -4
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/default-agents.ts +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/invocation-config.ts +7 -4
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/index.ts +18 -9
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/layered-settings.ts +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/child-lifecycle.ts +10 -63
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/concurrency-limiter.ts +122 -76
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/create-subagent-session.ts +3 -5
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/lifecycle-interceptor.ts +4 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/run-listeners.ts +2 -23
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-manager.ts +177 -252
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-session.ts +90 -280
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-state.ts +153 -239
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent.ts +569 -700
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/workspace.ts +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/notification.ts +78 -122
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/renderer.ts +15 -42
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/subagent-events-observer.ts +44 -95
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service-adapter.ts +157 -90
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service.ts +60 -68
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/conversation.ts +3 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/query-source.ts +28 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/query.ts +488 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/settings.ts +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/agent-tool.ts +171 -249
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-tool.ts +51 -94
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/helpers.ts +9 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/list-tool.ts +46 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/parent-tool-registry.ts +17 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/query-session-tool.ts +412 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/result-renderer.ts +27 -111
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/resume-tool.ts +105 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/spawn-config.ts +26 -8
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/steer-tool.ts +27 -111
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/stop-tool.ts +53 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/types.ts +11 -5
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/agent-widget.ts +33 -27
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/display.ts +6 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigation.ts +121 -10
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigator.ts +430 -112
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/subagents-settings.ts +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/widget-renderer.ts +8 -9
- package/node_modules/@nklisch/pi-plugins/package.json +2 -2
- package/package.json +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/background-spawner.ts +0 -81
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/foreground-runner.ts +0 -149
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-report.ts +0 -77
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* service-adapter.ts — Adapter that wraps SubagentManager to satisfy SubagentsService.
|
|
3
|
-
*
|
|
4
|
-
* Handles model resolution at the API boundary, record serialization
|
|
5
|
-
* (stripping non-serializable fields), and session gating.
|
|
6
|
-
*/
|
|
1
|
+
/** Adapter from the public service contract to the authoritative manager. */
|
|
7
2
|
|
|
8
3
|
import type { Model } from "@earendil-works/pi-ai";
|
|
9
4
|
import { resolveDispatchAgentType } from "#src/config/agent-type-resolution";
|
|
@@ -14,38 +9,45 @@ import type {
|
|
|
14
9
|
} from "#src/lifecycle/lifecycle-interceptor";
|
|
15
10
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
16
11
|
import type { WorkspaceProvider } from "#src/lifecycle/workspace";
|
|
17
|
-
import type {
|
|
12
|
+
import type { AgentSpawnConfig, DeliveryOutcome, ManagerSteerOutcome, ResumeOutcome, StopOutcome } from "#src/lifecycle/subagent-manager";
|
|
13
|
+
import type { Subagent } from "#src/lifecycle/subagent";
|
|
14
|
+
import type {
|
|
15
|
+
LaunchDelivery,
|
|
16
|
+
LaunchOptions,
|
|
17
|
+
ListOptions,
|
|
18
|
+
ResultDelivery,
|
|
19
|
+
ResumeDelivery,
|
|
20
|
+
StopDelivery,
|
|
21
|
+
SteerDelivery,
|
|
22
|
+
SubagentRecord,
|
|
23
|
+
SubagentsService,
|
|
24
|
+
} from "#src/service/service";
|
|
18
25
|
import { formatModelLabel } from "#src/session/model-label";
|
|
19
26
|
import { resolveEffectiveThinkingLevel } from "#src/session/thinking-level";
|
|
20
27
|
import type { ModelRegistry } from "#src/session/model-resolver";
|
|
21
28
|
import { resolveDefaultModel } from "#src/session/session-config";
|
|
22
|
-
import type { AgentInvocation, SessionContext,
|
|
29
|
+
import type { AgentInvocation, SessionContext, SubagentMode, ThinkingLevel } from "#src/types";
|
|
30
|
+
import { describeActivity } from "#src/ui/display";
|
|
23
31
|
|
|
24
|
-
/** Narrow interface for the SubagentManager — avoids coupling to the concrete class. */
|
|
25
32
|
export interface SubagentManagerLike {
|
|
26
|
-
|
|
33
|
+
launch(snapshot: ParentSnapshot, type: string, prompt: string, options: AgentSpawnConfig): Promise<DeliveryOutcome>;
|
|
34
|
+
resume(id: string, prompt: string, mode: SubagentMode, timeoutSeconds: number | undefined, signal?: AbortSignal): Promise<ResumeOutcome>;
|
|
35
|
+
stop(id: string, settlementTimeoutSeconds?: number): Promise<StopOutcome>;
|
|
36
|
+
steer(id: string, message: string): Promise<ManagerSteerOutcome>;
|
|
27
37
|
getRecord(id: string): Subagent | undefined;
|
|
28
38
|
listAgents(): Subagent[];
|
|
29
|
-
abort(id: string): boolean;
|
|
30
39
|
waitForAll(): Promise<void>;
|
|
31
40
|
hasRunning(): boolean;
|
|
32
41
|
registerWorkspaceProvider(provider: WorkspaceProvider): () => void;
|
|
33
|
-
registerLifecycleInterceptor(
|
|
34
|
-
interceptor: SubagentLifecycleInterceptor,
|
|
35
|
-
): SubagentLifecycleRegistration;
|
|
42
|
+
registerLifecycleInterceptor(interceptor: SubagentLifecycleInterceptor): SubagentLifecycleRegistration;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
/**
|
|
39
|
-
* Narrow runtime interface consumed by the service adapter.
|
|
40
|
-
* `SubagentRuntime` satisfies this structurally; tests use plain stubs.
|
|
41
|
-
*/
|
|
42
45
|
export interface ServiceRuntimeLike {
|
|
43
46
|
readonly currentCtx: SessionContext | undefined;
|
|
44
47
|
buildSnapshot(inheritContext: boolean): ParentSnapshot;
|
|
45
48
|
getSessionInfo(): { parentSessionFile: string; parentSessionId: string };
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
/** Adapter that wraps SubagentManager to satisfy SubagentsService. */
|
|
49
51
|
export class SubagentsServiceAdapter implements SubagentsService {
|
|
50
52
|
constructor(
|
|
51
53
|
private readonly manager: SubagentManagerLike,
|
|
@@ -55,139 +57,204 @@ export class SubagentsServiceAdapter implements SubagentsService {
|
|
|
55
57
|
private readonly settings?: { readonly fallbackSubagent?: string | false },
|
|
56
58
|
) {}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new Error("No active session — cannot spawn agents outside a session.");
|
|
61
|
-
}
|
|
62
|
-
|
|
60
|
+
async launch(type: string, prompt: string, options?: LaunchOptions): Promise<LaunchDelivery> {
|
|
61
|
+
const ctx = this.requireContext();
|
|
63
62
|
this.agentRegistry?.reload();
|
|
64
63
|
const resolution = this.agentRegistry
|
|
65
64
|
? resolveDispatchAgentType(type, this.agentRegistry, this.settings?.fallbackSubagent)
|
|
66
65
|
: { type, requestedType: type, fellBack: false };
|
|
67
66
|
if ("error" in resolution) throw new Error(resolution.error);
|
|
68
|
-
const resolvedType = resolution.type;
|
|
69
67
|
|
|
68
|
+
const resolvedType = resolution.type;
|
|
69
|
+
const agentConfig = this.agentRegistry?.resolveAgentConfig(resolvedType);
|
|
70
70
|
const model = options?.model
|
|
71
71
|
? this.resolveModelOption(options.model)
|
|
72
72
|
: this.resolveTypeDefaultModel(resolvedType);
|
|
73
|
+
const mode = normalizeMode(options?.mode ?? agentConfig?.mode);
|
|
73
74
|
const description = options?.description ?? prompt.slice(0, 80);
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
requestedThinking,
|
|
81
|
-
snapshot.thinkingLevel,
|
|
82
|
-
);
|
|
75
|
+
const thinking = options?.thinkingLevel ?? agentConfig?.thinking;
|
|
76
|
+
const maxTurns = normalizeMaxTurns(options?.maxTurns ?? agentConfig?.maxTurns);
|
|
77
|
+
const timeoutSeconds = options?.timeoutSeconds ?? agentConfig?.timeoutSeconds;
|
|
78
|
+
const inheritContext = options?.inheritContext ?? agentConfig?.inheritContext ?? false;
|
|
79
|
+
const snapshot = this.runtime.buildSnapshot(inheritContext);
|
|
80
|
+
const effectiveThinkingLevel = resolveEffectiveThinkingLevel(model, thinking, snapshot.thinkingLevel);
|
|
83
81
|
const invocation: AgentInvocation = {
|
|
84
82
|
modelName: formatModelLabel(model),
|
|
85
|
-
maxTurns
|
|
86
|
-
inheritContext
|
|
87
|
-
|
|
83
|
+
maxTurns,
|
|
84
|
+
inheritContext,
|
|
85
|
+
mode,
|
|
86
|
+
timeoutSeconds,
|
|
88
87
|
};
|
|
89
|
-
|
|
90
88
|
const parent = this.runtime.getSessionInfo();
|
|
91
|
-
|
|
89
|
+
|
|
90
|
+
const outcome = await this.manager.launch(snapshot, resolvedType, prompt, {
|
|
92
91
|
description,
|
|
93
92
|
model,
|
|
94
|
-
maxTurns
|
|
93
|
+
maxTurns,
|
|
95
94
|
thinkingLevel: effectiveThinkingLevel,
|
|
96
|
-
inheritContext
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
inheritContext,
|
|
96
|
+
mode,
|
|
97
|
+
timeoutSeconds: normalizeTimeout(timeoutSeconds),
|
|
99
98
|
origin: "service",
|
|
100
99
|
invocation,
|
|
100
|
+
signal: options?.signal,
|
|
101
101
|
lifecycleParentSession: parent.parentSessionId
|
|
102
|
-
? {
|
|
103
|
-
parentSessionFile: parent.parentSessionFile || undefined,
|
|
104
|
-
parentSessionId: parent.parentSessionId,
|
|
105
|
-
}
|
|
102
|
+
? { parentSessionFile: parent.parentSessionFile || undefined, parentSessionId: parent.parentSessionId }
|
|
106
103
|
: undefined,
|
|
107
104
|
});
|
|
105
|
+
if (outcome.kind === "joined") outcome.record.markConsumed();
|
|
106
|
+
return this.toLaunchDelivery(outcome);
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
async resume(
|
|
110
|
+
agentId: string,
|
|
111
|
+
prompt: string,
|
|
112
|
+
options?: Pick<LaunchOptions, "mode" | "timeoutSeconds" | "signal">,
|
|
113
|
+
): Promise<ResumeDelivery> {
|
|
114
|
+
const mode = options?.mode ?? "detached";
|
|
115
|
+
const outcome = await this.manager.resume(
|
|
116
|
+
agentId,
|
|
117
|
+
prompt,
|
|
118
|
+
mode,
|
|
119
|
+
normalizeTimeout(options?.timeoutSeconds),
|
|
120
|
+
options?.signal,
|
|
121
|
+
);
|
|
122
|
+
if (outcome.kind === "joined") {
|
|
123
|
+
outcome.record.markConsumed();
|
|
124
|
+
return { kind: "joined", record: toSubagentRecord(outcome.record) };
|
|
125
|
+
}
|
|
126
|
+
if (outcome.kind === "detached") return outcome;
|
|
127
|
+
return outcome;
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
async stop(agentId: string, settlementTimeoutSeconds = 5): Promise<StopDelivery> {
|
|
131
|
+
const timeout = normalizeSettlementTimeout(settlementTimeoutSeconds);
|
|
132
|
+
const outcome = await this.manager.stop(agentId, timeout);
|
|
133
|
+
if (outcome.kind === "not_found") return outcome;
|
|
134
|
+
if (outcome.kind === "already_terminal") return { ...outcome, record: toSubagentRecord(outcome.record) };
|
|
135
|
+
return { ...outcome, record: toSubagentRecord(outcome.record) };
|
|
117
136
|
}
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
async steer(agentId: string, message: string): Promise<SteerDelivery> {
|
|
139
|
+
const outcome = await this.manager.steer(agentId, message);
|
|
140
|
+
if (outcome.kind === "not_found") return outcome;
|
|
141
|
+
if (outcome.kind === "rejected") return { ...outcome, agentId };
|
|
142
|
+
return { ...outcome, agentId };
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
list(options?: ListOptions): SubagentRecord[] {
|
|
146
|
+
const state = options?.state ?? "all";
|
|
147
|
+
const limit = normalizeListLimit(options?.limit);
|
|
148
|
+
return this.manager.listAgents()
|
|
149
|
+
.filter((record) => state === "all" || (state === "active" ? record.isActive() : !record.isActive()))
|
|
150
|
+
.slice(0, limit)
|
|
151
|
+
.map(toSubagentRecord);
|
|
130
152
|
}
|
|
131
153
|
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
getResult(agentId: string): ResultDelivery {
|
|
155
|
+
const record = this.manager.getRecord(agentId);
|
|
156
|
+
if (!record) return { kind: "not_found", agentId };
|
|
157
|
+
if (!record.isActive()) record.markConsumed();
|
|
158
|
+
return { kind: "result", record: toSubagentRecord(record) };
|
|
134
159
|
}
|
|
135
160
|
|
|
136
|
-
|
|
137
|
-
|
|
161
|
+
getRecord(agentId: string): SubagentRecord | undefined {
|
|
162
|
+
const record = this.manager.getRecord(agentId);
|
|
163
|
+
return record ? toSubagentRecord(record) : undefined;
|
|
138
164
|
}
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
166
|
+
waitForAll(): Promise<void> { return this.manager.waitForAll(); }
|
|
167
|
+
hasRunning(): boolean { return this.manager.hasRunning(); }
|
|
168
|
+
registerWorkspaceProvider(provider: WorkspaceProvider): () => void { return this.manager.registerWorkspaceProvider(provider); }
|
|
169
|
+
registerLifecycleInterceptor(interceptor: SubagentLifecycleInterceptor): SubagentLifecycleRegistration { return this.manager.registerLifecycleInterceptor(interceptor); }
|
|
143
170
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return this.manager.registerLifecycleInterceptor(interceptor);
|
|
171
|
+
private requireContext(): SessionContext {
|
|
172
|
+
if (!this.runtime.currentCtx) throw new Error("No active session — cannot launch agents outside a session.");
|
|
173
|
+
return this.runtime.currentCtx;
|
|
148
174
|
}
|
|
149
175
|
|
|
150
|
-
|
|
151
|
-
private resolveModelOption(modelInput: string): Model<any> {
|
|
176
|
+
private resolveModelOption(input: string): Model<any> {
|
|
152
177
|
const registry = this.runtime.currentCtx?.modelRegistry;
|
|
153
|
-
if (!registry)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const resolved = this.resolveModel(modelInput, registry);
|
|
157
|
-
if (typeof resolved === "string") {
|
|
158
|
-
throw new Error(resolved);
|
|
159
|
-
}
|
|
178
|
+
if (!registry) throw new Error("No model registry available.");
|
|
179
|
+
const resolved = this.resolveModel(input, registry);
|
|
180
|
+
if (typeof resolved === "string") throw new Error(resolved);
|
|
160
181
|
return resolved;
|
|
161
182
|
}
|
|
162
183
|
|
|
163
|
-
/** Resolve the agent type's configured model with the same fallback used by session assembly. */
|
|
164
184
|
private resolveTypeDefaultModel(type: string): Model<any> {
|
|
165
185
|
const ctx = this.runtime.currentCtx!;
|
|
166
186
|
const configured = this.agentRegistry?.resolveAgentConfig(type).model;
|
|
167
187
|
if (!ctx.modelRegistry) return ctx.model as Model<any>;
|
|
168
188
|
return resolveDefaultModel(ctx.model, ctx.modelRegistry, configured) as Model<any>;
|
|
169
189
|
}
|
|
190
|
+
|
|
191
|
+
private toLaunchDelivery(outcome: DeliveryOutcome): LaunchDelivery {
|
|
192
|
+
return outcome.kind === "joined"
|
|
193
|
+
? { kind: "joined", record: toSubagentRecord(outcome.record) }
|
|
194
|
+
: outcome;
|
|
195
|
+
}
|
|
170
196
|
}
|
|
171
197
|
|
|
172
|
-
/**
|
|
173
|
-
* Convert an internal Subagent to a serializable SubagentRecord.
|
|
174
|
-
* Uses an explicit allowlist — new fields must be opted in.
|
|
175
|
-
*/
|
|
176
198
|
export function toSubagentRecord(record: Subagent): SubagentRecord {
|
|
199
|
+
const activeTools = [...record.activeTools.values()];
|
|
177
200
|
const out: SubagentRecord = {
|
|
178
201
|
id: record.id,
|
|
179
202
|
type: record.type,
|
|
180
203
|
description: record.description,
|
|
204
|
+
runId: record.runId,
|
|
205
|
+
mode: record.mode,
|
|
181
206
|
status: record.status,
|
|
207
|
+
stopRequested: record.stopRequested,
|
|
208
|
+
terminalReason: record.stateTerminalReason,
|
|
182
209
|
toolUses: record.toolUses,
|
|
183
210
|
startedAt: record.startedAt,
|
|
184
|
-
|
|
211
|
+
activeRuntimeMs: record.activeRuntimeMs,
|
|
212
|
+
modelLabel: record.modelLabel,
|
|
213
|
+
thinkingLevel: record.effectiveThinkingLevel,
|
|
214
|
+
activeTools,
|
|
215
|
+
currentActivity: describeActivity(record.activeTools, record.responseText),
|
|
216
|
+
lifetimeUsage: { ...record.lifetimeUsage },
|
|
185
217
|
compactionCount: record.compactionCount,
|
|
186
218
|
};
|
|
187
|
-
|
|
188
|
-
if (record.result !== undefined) out.result = record.result;
|
|
219
|
+
if (record.result !== undefined) out.result = boundResult(record.result, record.outputFile);
|
|
189
220
|
if (record.error !== undefined) out.error = record.error;
|
|
190
221
|
if (record.completedAt !== undefined) out.completedAt = record.completedAt;
|
|
191
|
-
|
|
222
|
+
if (record.outputFile !== undefined) out.outputFile = record.outputFile;
|
|
192
223
|
return out;
|
|
193
224
|
}
|
|
225
|
+
|
|
226
|
+
const MAX_RESULT_OUTPUT = 12_000;
|
|
227
|
+
function boundResult(result: string, outputFile: string | undefined): string {
|
|
228
|
+
return result.length > MAX_RESULT_OUTPUT
|
|
229
|
+
? result.slice(0, MAX_RESULT_OUTPUT) + `\n\nOutput truncated. Full transcript: ${outputFile ?? "unavailable"}`
|
|
230
|
+
: result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function normalizeMode(value: string | undefined): SubagentMode {
|
|
234
|
+
if (value == null) return "detached";
|
|
235
|
+
if (value !== "joined" && value !== "detached") throw new RangeError("mode must be joined or detached");
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeMaxTurns(value: number | undefined): number | undefined {
|
|
240
|
+
if (value == null) return undefined;
|
|
241
|
+
if (!Number.isInteger(value) || value < 0) throw new RangeError("maxTurns must be a non-negative integer");
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function normalizeTimeout(value: number | undefined): number | undefined {
|
|
246
|
+
if (value == null) return undefined;
|
|
247
|
+
if (!Number.isInteger(value) || value <= 0) throw new RangeError("timeoutSeconds must be a positive integer");
|
|
248
|
+
return value;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function normalizeSettlementTimeout(value: number): number {
|
|
252
|
+
if (!Number.isInteger(value) || value <= 0 || value > 30) throw new RangeError("settlementTimeoutSeconds must be an integer from 1 to 30");
|
|
253
|
+
return value;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeListLimit(value: number | undefined): number {
|
|
257
|
+
const limit = value ?? 20;
|
|
258
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new RangeError("limit must be an integer from 1 to 100");
|
|
259
|
+
return limit;
|
|
260
|
+
}
|
package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* service.ts — Public API surface for cross-extension access to subagents.
|
|
3
|
-
*
|
|
4
|
-
* Consumers declare this package as an optional peer dependency and use
|
|
5
|
-
* dynamic import to access the accessor functions:
|
|
6
|
-
*
|
|
7
|
-
* const { getSubagentsService } = await import("@nklisch/pi-subagents");
|
|
8
|
-
* const svc = getSubagentsService();
|
|
9
|
-
* svc?.spawn("Explore", "Check for stale TODOs");
|
|
10
|
-
*/
|
|
1
|
+
/** Public cross-extension contract for subagent execution control. */
|
|
11
2
|
|
|
12
3
|
import type {
|
|
13
4
|
SubagentLifecycleInterceptor,
|
|
14
5
|
SubagentLifecycleRegistration,
|
|
6
|
+
SubagentExecutionMode,
|
|
15
7
|
} from "#src/lifecycle/lifecycle-interceptor";
|
|
16
|
-
import type { SubagentStatus } from "#src/lifecycle/subagent";
|
|
8
|
+
import type { SubagentStatus, SubagentStopReason, SubagentTerminalReason } from "#src/lifecycle/subagent-state";
|
|
17
9
|
import type { LifetimeUsage } from "#src/lifecycle/usage";
|
|
18
10
|
import type {
|
|
19
11
|
Workspace,
|
|
@@ -22,7 +14,7 @@ import type {
|
|
|
22
14
|
WorkspacePrepareContext,
|
|
23
15
|
WorkspaceProvider,
|
|
24
16
|
} from "#src/lifecycle/workspace";
|
|
25
|
-
|
|
17
|
+
import type { ThinkingLevel } from "#src/types";
|
|
26
18
|
|
|
27
19
|
export {
|
|
28
20
|
MAX_LIFECYCLE_CONTINUATION_ROUNDS,
|
|
@@ -40,13 +32,7 @@ export {
|
|
|
40
32
|
type SubagentLifecycleStartContext,
|
|
41
33
|
type SubagentLifecycleStartDecision,
|
|
42
34
|
} from "#src/lifecycle/lifecycle-interceptor";
|
|
43
|
-
|
|
44
|
-
// here for the public API surface — mirrors the LifetimeUsage / workspace pattern.
|
|
45
|
-
export type { SubagentStatus } from "#src/lifecycle/subagent";
|
|
46
|
-
// Generative extension seam (ADR 0002, Phase 16 Step 2). The provider type
|
|
47
|
-
// and all four collaborator types it references are re-exported by name so
|
|
48
|
-
// consumers can import them directly rather than recovering them via
|
|
49
|
-
// indexed-access inference (e.g. `Parameters<WorkspaceProvider["prepare"]>[0]`).
|
|
35
|
+
export type { SubagentStatus, SubagentStopReason, SubagentTerminalReason } from "#src/lifecycle/subagent-state";
|
|
50
36
|
export type {
|
|
51
37
|
LifetimeUsage,
|
|
52
38
|
Workspace,
|
|
@@ -55,73 +41,86 @@ export type {
|
|
|
55
41
|
WorkspacePrepareContext,
|
|
56
42
|
WorkspaceProvider,
|
|
57
43
|
};
|
|
44
|
+
export type { SubagentExecutionMode as SubagentMode };
|
|
58
45
|
|
|
59
|
-
/** Serializable snapshot of an agent's state — no live session objects. */
|
|
60
46
|
export interface SubagentRecord {
|
|
61
47
|
id: string;
|
|
62
48
|
type: string;
|
|
63
49
|
description: string;
|
|
50
|
+
runId: number;
|
|
51
|
+
mode: SubagentExecutionMode;
|
|
64
52
|
status: SubagentStatus;
|
|
53
|
+
stopRequested: boolean;
|
|
54
|
+
terminalReason?: SubagentTerminalReason;
|
|
65
55
|
result?: string;
|
|
66
56
|
error?: string;
|
|
67
57
|
toolUses: number;
|
|
68
58
|
startedAt: number;
|
|
69
59
|
completedAt?: number;
|
|
60
|
+
activeRuntimeMs: number;
|
|
61
|
+
modelLabel: string;
|
|
62
|
+
thinkingLevel: ThinkingLevel;
|
|
63
|
+
activeTools: string[];
|
|
64
|
+
currentActivity: string;
|
|
70
65
|
lifetimeUsage: LifetimeUsage;
|
|
71
66
|
compactionCount: number;
|
|
67
|
+
outputFile?: string;
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
export interface SpawnOptions {
|
|
70
|
+
export interface LaunchOptions {
|
|
76
71
|
description?: string;
|
|
77
72
|
model?: string;
|
|
78
73
|
maxTurns?: number;
|
|
79
|
-
thinkingLevel?:
|
|
74
|
+
thinkingLevel?: ThinkingLevel;
|
|
80
75
|
inheritContext?: boolean;
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
mode?: SubagentExecutionMode;
|
|
77
|
+
timeoutSeconds?: number;
|
|
78
|
+
signal?: AbortSignal;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
export type LaunchDelivery =
|
|
82
|
+
| { kind: "detached"; agentId: string; runId: number }
|
|
83
|
+
| { kind: "joined"; record: SubagentRecord };
|
|
84
|
+
|
|
85
|
+
export type ResumeDelivery = LaunchDelivery |
|
|
86
|
+
{ kind: "not_found"; agentId: string } |
|
|
87
|
+
{ kind: "wrong_state"; agentId: string; status: SubagentStatus };
|
|
88
|
+
|
|
89
|
+
export type StopDelivery =
|
|
90
|
+
| { kind: "stopped"; agentId: string; runId: number; reason: SubagentStopReason; record: SubagentRecord }
|
|
91
|
+
| { kind: "stop_pending"; agentId: string; runId: number; reason: SubagentStopReason; record: SubagentRecord }
|
|
92
|
+
| { kind: "already_terminal"; agentId: string; runId: number; record: SubagentRecord }
|
|
93
|
+
| { kind: "not_found"; agentId: string };
|
|
94
|
+
|
|
95
|
+
export type SteerDelivery =
|
|
96
|
+
| { kind: "delivered" | "buffered"; agentId: string; runId: number }
|
|
97
|
+
| { kind: "rejected"; agentId: string; runId: number; status: SubagentStatus }
|
|
98
|
+
| { kind: "not_found"; agentId: string };
|
|
99
|
+
|
|
100
|
+
export type ResultDelivery =
|
|
101
|
+
| { kind: "result"; record: SubagentRecord }
|
|
102
|
+
| { kind: "not_found"; agentId: string };
|
|
103
|
+
|
|
104
|
+
export type SubagentListState = "active" | "terminal" | "all";
|
|
105
|
+
export interface ListOptions {
|
|
106
|
+
state?: SubagentListState;
|
|
107
|
+
limit?: number;
|
|
108
|
+
}
|
|
101
109
|
|
|
102
|
-
|
|
110
|
+
export interface SubagentsService {
|
|
111
|
+
launch(type: string, prompt: string, options?: LaunchOptions): Promise<LaunchDelivery>;
|
|
112
|
+
resume(agentId: string, prompt: string, options?: Pick<LaunchOptions, "mode" | "timeoutSeconds" | "signal">): Promise<ResumeDelivery>;
|
|
113
|
+
stop(agentId: string, settlementTimeoutSeconds?: number): Promise<StopDelivery>;
|
|
114
|
+
steer(agentId: string, message: string): Promise<SteerDelivery>;
|
|
115
|
+
list(options?: ListOptions): SubagentRecord[];
|
|
116
|
+
getResult(agentId: string): ResultDelivery;
|
|
117
|
+
getRecord(agentId: string): SubagentRecord | undefined;
|
|
103
118
|
waitForAll(): Promise<void>;
|
|
104
|
-
|
|
105
|
-
/** Whether any agents are running or queued. */
|
|
106
119
|
hasRunning(): boolean;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Register the single workspace provider that supplies a child's working
|
|
110
|
-
* directory plus bracketed setup/teardown. Throws if one is already
|
|
111
|
-
* registered. Returns a disposer that unregisters the provider.
|
|
112
|
-
*/
|
|
113
120
|
registerWorkspaceProvider(provider: WorkspaceProvider): () => void;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Register an ordered async lifecycle provider. It receives only immutable
|
|
117
|
-
* execution facts and prompt/result decisions, never a manager or session.
|
|
118
|
-
*/
|
|
119
|
-
registerLifecycleInterceptor(
|
|
120
|
-
interceptor: SubagentLifecycleInterceptor,
|
|
121
|
-
): SubagentLifecycleRegistration;
|
|
121
|
+
registerLifecycleInterceptor(interceptor: SubagentLifecycleInterceptor): SubagentLifecycleRegistration;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
/** Event channel constants for pi.events subscriptions. */
|
|
125
124
|
export const SUBAGENT_EVENTS = {
|
|
126
125
|
STARTED: "subagents:started",
|
|
127
126
|
COMPLETED: "subagents:completed",
|
|
@@ -132,24 +131,17 @@ export const SUBAGENT_EVENTS = {
|
|
|
132
131
|
STEERED: "subagents:steered",
|
|
133
132
|
} as const;
|
|
134
133
|
|
|
135
|
-
// ---- Accessor functions ----
|
|
136
|
-
|
|
137
134
|
const SERVICE_KEY = Symbol.for("@nklisch/pi-subagents:service");
|
|
138
135
|
|
|
139
|
-
/** Publish the SubagentsService on globalThis for cross-extension access. */
|
|
140
136
|
export function publishSubagentsService(service: SubagentsService): void {
|
|
141
137
|
(globalThis as Record<symbol, unknown>)[SERVICE_KEY] = service;
|
|
142
138
|
}
|
|
143
139
|
|
|
144
|
-
/** Retrieve the published SubagentsService, or undefined if not yet published. */
|
|
145
140
|
export function getSubagentsService(): SubagentsService | undefined {
|
|
146
|
-
return (globalThis as Record<symbol, unknown>)[SERVICE_KEY] as
|
|
147
|
-
| SubagentsService
|
|
148
|
-
| undefined;
|
|
141
|
+
return (globalThis as Record<symbol, unknown>)[SERVICE_KEY] as SubagentsService | undefined;
|
|
149
142
|
}
|
|
150
143
|
|
|
151
|
-
/** Remove the SubagentsService from globalThis (call on shutdown/reload). */
|
|
152
144
|
export function unpublishSubagentsService(): void {
|
|
153
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property
|
|
154
146
|
delete (globalThis as Record<symbol, unknown>)[SERVICE_KEY];
|
|
155
147
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* conversation.ts — Render a subagent session's messages as formatted text.
|
|
3
3
|
*
|
|
4
|
-
* Extracted
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Extracted into the session domain, alongside the other message-extraction
|
|
5
|
+
* helpers. Kept for read-only transcript/navigation surfaces; parent result
|
|
6
|
+
* delivery itself remains bounded.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Shared file adapter for query and transcript consumers. */
|
|
2
|
+
import {
|
|
3
|
+
buildSessionContext,
|
|
4
|
+
parseSessionEntries,
|
|
5
|
+
type SessionEntry,
|
|
6
|
+
} from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import type { SessionMessage } from "#src/types";
|
|
8
|
+
|
|
9
|
+
/** Parse exactly the same public Pi JSONL/context pipeline used by navigation. */
|
|
10
|
+
export function parseSessionFileMessages(
|
|
11
|
+
content: string,
|
|
12
|
+
): readonly SessionMessage[] {
|
|
13
|
+
const entries = parseSessionEntries(content);
|
|
14
|
+
const sessionEntries = entries.filter((entry): entry is SessionEntry => entry.type !== "session");
|
|
15
|
+
return buildSessionContext(sessionEntries).messages;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SessionFileReader {
|
|
19
|
+
readFile(path: string): string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readSessionFileMessages(
|
|
23
|
+
outputFile: string,
|
|
24
|
+
reader: SessionFileReader | ((path: string) => string),
|
|
25
|
+
): readonly SessionMessage[] {
|
|
26
|
+
const readFile = typeof reader === "function" ? reader : (path: string) => reader.readFile(path);
|
|
27
|
+
return parseSessionFileMessages(readFile(outputFile));
|
|
28
|
+
}
|