pi-gentic 0.1.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/README.md +499 -0
- package/dist/commands.d.ts +93 -0
- package/dist/commands.js +422 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +407 -0
- package/dist/core.d.ts +19 -0
- package/dist/core.js +125 -0
- package/dist/extension.d.ts +2 -0
- package/dist/extension.js +476 -0
- package/dist/orchestrator.d.ts +277 -0
- package/dist/orchestrator.js +633 -0
- package/dist/policy.d.ts +43 -0
- package/dist/policy.js +136 -0
- package/dist/prompt.d.ts +12 -0
- package/dist/prompt.js +226 -0
- package/dist/runs.d.ts +99 -0
- package/dist/runs.js +540 -0
- package/dist/runtime.d.ts +127 -0
- package/dist/runtime.js +360 -0
- package/dist/sessions.d.ts +56 -0
- package/dist/sessions.js +487 -0
- package/dist/ui.d.ts +108 -0
- package/dist/ui.js +957 -0
- package/dist/worktrees.d.ts +1 -0
- package/dist/worktrees.js +86 -0
- package/docs/assets/error-card.png +0 -0
- package/docs/assets/load-agent.png +0 -0
- package/docs/assets/orchestration-tree.png +0 -0
- package/docs/assets/send-background.png +0 -0
- package/docs/assets/send-foreground.png +0 -0
- package/package.json +58 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestration facade for Pi.
|
|
3
|
+
*
|
|
4
|
+
* The orchestrator coordinates policy, prompts, runtime sessions, and run
|
|
5
|
+
* delivery while keeping Pi-specific APIs behind this single class.
|
|
6
|
+
*/
|
|
7
|
+
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { buildReceiptText, buildReturnText, chooseBoolean, getErrorMessage, parseIntegerRadius, shortSessionId, } from "./core.js";
|
|
9
|
+
import { loadAvailableSkills, loadConfiguration } from "./config.js";
|
|
10
|
+
import { activeAgentName, appendActiveState, assertAvailableAgent, configuredDefaultAgent, filterAvailableAgents, getActiveState, nextAgentName, resolveSessionPolicy, shouldApplyDefaultAgent, } from "./policy.js";
|
|
11
|
+
import { buildResolvedSystemPrompt, mergeSkillEntries, parseSkillEntries, } from "./prompt.js";
|
|
12
|
+
import { abortAgentCall, activeVisibleContext, activeVisibleSession, applyInheritedModel, createLiveRuntime, findRuntimeSession, getRuntimeSession, listRuntimeSessions, persistSessionImmediately, pruneRuntimeSessions, registerAgentCall, resolveModelFromRegistry, setRuntimeSession, unregisterLiveRuntime, } from "./runtime.js";
|
|
13
|
+
import { abortActor, collectSessionActivities, createSessionActivityMonitor, deliverReturnToCaller, deliverSendContextToCaller, displayTargetAnswerIfVisible, mergeActivities, sendPendingText, sendStatusText, sessionRunOutcome, sessionStatus, shouldDeferSendCompletion, } from "./runs.js";
|
|
14
|
+
import { assignTreeDepths, buildSessionTree, cachedPersistedSessions, currentSessionSummary, enrichSessionSummaries, listSessionSummariesFast, resolveSessionReference, sessionDiscoveryScope, withRuntimeState, } from "./sessions.js";
|
|
15
|
+
import { setAgentLabel, setLiveCardDetails } from "./ui.js";
|
|
16
|
+
import { prepareWorktree } from "./worktrees.js";
|
|
17
|
+
/** Main application service used by commands, shortcuts, events, and tools. */
|
|
18
|
+
export class PiGenticOrchestrator {
|
|
19
|
+
pi;
|
|
20
|
+
currentAgentName;
|
|
21
|
+
constructor(pi) {
|
|
22
|
+
this.pi = pi;
|
|
23
|
+
this.currentAgentName = undefined;
|
|
24
|
+
}
|
|
25
|
+
load(ctx) {
|
|
26
|
+
return loadConfiguration({ cwd: ctx.cwd });
|
|
27
|
+
}
|
|
28
|
+
getActiveAgent(ctx, config = this.load(ctx)) {
|
|
29
|
+
const state = getActiveState(ctx.sessionManager);
|
|
30
|
+
return config.agents.find((agent) => agent.name === state.agentName);
|
|
31
|
+
}
|
|
32
|
+
resolvePolicy(ctx, config = this.load(ctx), state = getActiveState(ctx.sessionManager), resources = {}) {
|
|
33
|
+
const activeAgent = config.agents.find((agent) => agent.name === state.agentName);
|
|
34
|
+
return resolveSessionPolicy({
|
|
35
|
+
settings: config.settings,
|
|
36
|
+
activeAgent,
|
|
37
|
+
overrides: state.overrides,
|
|
38
|
+
allAgents: config.agents.map((agent) => agent.name),
|
|
39
|
+
allTools: resources.tools ?? this.pi.getAllTools().map((tool) => tool.name),
|
|
40
|
+
allSkills: resources.skills ?? currentSkillNames(ctx),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
availableAgents(ctx, config = this.load(ctx)) {
|
|
44
|
+
return filterAvailableAgents(config, this.resolvePolicy(ctx, config));
|
|
45
|
+
}
|
|
46
|
+
assertAgentAvailable(ctx, agentName, config = this.load(ctx)) {
|
|
47
|
+
const configured = config.agents.find((agent) => agent.name.toLowerCase() === String(agentName ?? "").toLowerCase());
|
|
48
|
+
if (!configured)
|
|
49
|
+
throw new Error(`Unknown agent "${agentName}". Available agents: ${config.agents.map((agent) => agent.name).join(", ") || "none"}.`);
|
|
50
|
+
return assertAvailableAgent(agentName, this.availableAgents(ctx, config));
|
|
51
|
+
}
|
|
52
|
+
/** Applies the current session policy to Pi tools, model, thinking, theme, and labels. */
|
|
53
|
+
async applyCurrentPolicy(ctx, options = {}) {
|
|
54
|
+
const config = this.load(ctx);
|
|
55
|
+
const state = getActiveState(ctx.sessionManager);
|
|
56
|
+
const policy = this.resolvePolicy(ctx, config, state, {
|
|
57
|
+
skills: skillContext(ctx).names,
|
|
58
|
+
});
|
|
59
|
+
this.currentAgentName = policy.agentName;
|
|
60
|
+
this.pi.setActiveTools(policy.resources.tools);
|
|
61
|
+
if (policy.model) {
|
|
62
|
+
const model = this.resolveModel(ctx, policy.model);
|
|
63
|
+
if (model)
|
|
64
|
+
await this.pi.setModel(model);
|
|
65
|
+
}
|
|
66
|
+
if (policy.thinking)
|
|
67
|
+
this.pi.setThinkingLevel(policy.thinking);
|
|
68
|
+
if (policy.theme && ctx.mode === "tui")
|
|
69
|
+
ctx.ui.setTheme(policy.theme);
|
|
70
|
+
this.setTitle(ctx, options.running === true);
|
|
71
|
+
this.setAgentWidget(ctx);
|
|
72
|
+
return { config, policy };
|
|
73
|
+
}
|
|
74
|
+
setTitle(ctx, running = false) {
|
|
75
|
+
const agent = activeAgentName(ctx.sessionManager);
|
|
76
|
+
if (agent)
|
|
77
|
+
ctx.ui.setTitle(`${running ? "●" : "○"} ${agent}`);
|
|
78
|
+
}
|
|
79
|
+
setAgentWidget(ctx) {
|
|
80
|
+
setAgentLabel(ctx, activeAgentName(ctx.sessionManager));
|
|
81
|
+
}
|
|
82
|
+
buildPromptAppend(ctx, event) {
|
|
83
|
+
const skills = skillContext(ctx, parseSkillEntries(event.systemPrompt));
|
|
84
|
+
const { config, policy, activeAgent } = this.applyPolicySnapshot(ctx, {
|
|
85
|
+
skills: skills.names,
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
systemPrompt: buildResolvedSystemPrompt({
|
|
89
|
+
baseSystemPrompt: event.systemPrompt,
|
|
90
|
+
config: { ...config, activeAgent },
|
|
91
|
+
policy,
|
|
92
|
+
skillEntries: skills.entries,
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
applyPolicySnapshot(ctx, resources = {}) {
|
|
97
|
+
const config = this.load(ctx);
|
|
98
|
+
const state = getActiveState(ctx.sessionManager);
|
|
99
|
+
const activeAgent = config.agents.find((agent) => agent.name === state.agentName);
|
|
100
|
+
const policy = this.resolvePolicy(ctx, config, state, {
|
|
101
|
+
...resources,
|
|
102
|
+
skills: resources.skills ?? skillContext(ctx).names,
|
|
103
|
+
});
|
|
104
|
+
this.currentAgentName = policy.agentName;
|
|
105
|
+
return { config, policy, activeAgent };
|
|
106
|
+
}
|
|
107
|
+
async loadDefaultAgent(ctx, event) {
|
|
108
|
+
const config = this.load(ctx);
|
|
109
|
+
const agentName = configuredDefaultAgent(config.settings);
|
|
110
|
+
if (!agentName || !shouldApplyDefaultAgent(event, ctx.sessionManager))
|
|
111
|
+
return undefined;
|
|
112
|
+
if (!config.agents.some((agent) => agent.name.toLowerCase() === agentName.toLowerCase())) {
|
|
113
|
+
ctx.ui.notify(`pi-gentic defaultAgent "${agentName}" is not configured.`, "warning");
|
|
114
|
+
await this.applyCurrentPolicy(ctx);
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return this.loadAgent(ctx, agentName, { enforceAccess: false });
|
|
118
|
+
}
|
|
119
|
+
async cycleAgent(ctx) {
|
|
120
|
+
const config = this.load(ctx);
|
|
121
|
+
const agentName = nextAgentName(activeAgentName(ctx.sessionManager), config.agents);
|
|
122
|
+
return this.loadAgent(ctx, agentName ?? "clear");
|
|
123
|
+
}
|
|
124
|
+
/** Stores a handoff in session history, then re-applies the resolved policy. */
|
|
125
|
+
async loadAgent(ctx, agentName, options = {}) {
|
|
126
|
+
const config = this.load(ctx);
|
|
127
|
+
if (!agentName || agentName === "clear") {
|
|
128
|
+
appendActiveState(ctx.sessionManager, {
|
|
129
|
+
agentName: undefined,
|
|
130
|
+
overrides: undefined,
|
|
131
|
+
});
|
|
132
|
+
const { policy } = await this.applyCurrentPolicy(ctx);
|
|
133
|
+
return {
|
|
134
|
+
text: "Cleared active agent.",
|
|
135
|
+
details: this.cardDetails("load", "done", {
|
|
136
|
+
agentName: "agentless",
|
|
137
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
138
|
+
configuration: compactPolicy(policy),
|
|
139
|
+
systemPrompt: this.resolvedPromptForCard(ctx, config, policy, undefined),
|
|
140
|
+
}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const agent = options.enforceAccess === false
|
|
144
|
+
? config.agents.find((item) => String(item.name).toLowerCase() ===
|
|
145
|
+
String(agentName).toLowerCase())
|
|
146
|
+
: this.assertAgentAvailable(ctx, agentName, config);
|
|
147
|
+
if (!agent)
|
|
148
|
+
throw new Error(`Unknown agent "${agentName}". Available agents: ${config.agents.map((item) => item.name).join(", ") || "none"}.`);
|
|
149
|
+
appendActiveState(ctx.sessionManager, {
|
|
150
|
+
agentName: agent.name,
|
|
151
|
+
overrides: options.overrides,
|
|
152
|
+
});
|
|
153
|
+
const { policy } = await this.applyCurrentPolicy(ctx);
|
|
154
|
+
return {
|
|
155
|
+
text: `Loaded ${agent.name} agent in session ${shortSessionId(ctx.sessionManager.getSessionId())}.`,
|
|
156
|
+
details: this.cardDetails("load", "done", {
|
|
157
|
+
agentName: agent.name,
|
|
158
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
159
|
+
configuration: compactPolicy(policy),
|
|
160
|
+
systemPrompt: this.resolvedPromptForCard(ctx, config, policy, agent),
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
resolvedPromptForCard(ctx, config, policy, activeAgent) {
|
|
165
|
+
const baseSystemPrompt = safeSystemPrompt(ctx);
|
|
166
|
+
return buildResolvedSystemPrompt({
|
|
167
|
+
baseSystemPrompt,
|
|
168
|
+
config: { ...config, activeAgent },
|
|
169
|
+
policy,
|
|
170
|
+
skillEntries: skillContext(ctx, parseSkillEntries(baseSystemPrompt))
|
|
171
|
+
.entries,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** Sends one message to a child or existing session and tracks the resulting run. */
|
|
175
|
+
async send(ctx, input, callbacks = {}) {
|
|
176
|
+
const config = this.load(ctx);
|
|
177
|
+
if (input.agent)
|
|
178
|
+
this.assertAgentAvailable(ctx, input.agent, config);
|
|
179
|
+
const callerState = getActiveState(ctx.sessionManager);
|
|
180
|
+
const callerAgent = callerState.agentName;
|
|
181
|
+
const defaults = this.resolvePolicy(ctx, config, callerState).agentsTool ?? {};
|
|
182
|
+
const targetAsync = input.sessionId
|
|
183
|
+
? true
|
|
184
|
+
: chooseBoolean(input.async, chooseBoolean(defaults.async, false));
|
|
185
|
+
const targetFork = chooseBoolean(input.fork, chooseBoolean(defaults.fork, false));
|
|
186
|
+
const cwd = await this.resolveSendCwd(ctx, {
|
|
187
|
+
...input,
|
|
188
|
+
cwd: input.cwd ?? defaults.cwd,
|
|
189
|
+
});
|
|
190
|
+
const invokeMeLater = chooseBoolean(input.invokeMeLater, targetAsync
|
|
191
|
+
? defaults.invokeMeLater?.async !== false
|
|
192
|
+
: defaults.invokeMeLater?.withSession !== false);
|
|
193
|
+
const startedAt = Date.now();
|
|
194
|
+
const target = await this.resolveTargetSession(ctx, { ...input, async: targetAsync, fork: targetFork, cwd }, config);
|
|
195
|
+
const targetSessionId = target.session.sessionManager.getSessionId();
|
|
196
|
+
const targetBusy = target.session.isStreaming === true;
|
|
197
|
+
const callerSessionManager = ctx.sessionManager;
|
|
198
|
+
const callerSessionId = callerSessionManager.getSessionId();
|
|
199
|
+
const callerCwd = ctx.cwd;
|
|
200
|
+
const details = this.cardDetails("send", targetBusy ? "queued" : "running", {
|
|
201
|
+
cardId: `send:${targetSessionId}:${startedAt}`,
|
|
202
|
+
async: targetAsync,
|
|
203
|
+
agentName: target.agentName,
|
|
204
|
+
sessionId: targetSessionId,
|
|
205
|
+
message: input.message,
|
|
206
|
+
queued: targetBusy,
|
|
207
|
+
startedAt,
|
|
208
|
+
updatedAt: startedAt,
|
|
209
|
+
activities: [],
|
|
210
|
+
});
|
|
211
|
+
const publish = (nextDetails, options = {}) => {
|
|
212
|
+
const liveDetails = setLiveCardDetails(nextDetails) ?? nextDetails;
|
|
213
|
+
if (options.refresh !== false)
|
|
214
|
+
callbacks.onRefresh?.(liveDetails);
|
|
215
|
+
if (options.notify === true)
|
|
216
|
+
callbacks.onUpdate?.({
|
|
217
|
+
content: [{ type: "text", text: sendStatusText(liveDetails) }],
|
|
218
|
+
details: liveDetails,
|
|
219
|
+
});
|
|
220
|
+
return liveDetails;
|
|
221
|
+
};
|
|
222
|
+
publish(details, { notify: true });
|
|
223
|
+
deliverSendContextToCaller({
|
|
224
|
+
pi: this.pi,
|
|
225
|
+
ctx,
|
|
226
|
+
target,
|
|
227
|
+
message: input.message,
|
|
228
|
+
async: targetAsync,
|
|
229
|
+
fork: targetFork,
|
|
230
|
+
});
|
|
231
|
+
const activeCall = registerAgentCall({
|
|
232
|
+
callerSessionId,
|
|
233
|
+
targetSessionId,
|
|
234
|
+
abort: async (options = {}) => {
|
|
235
|
+
target.lastAbort = {
|
|
236
|
+
actor: options.actor ?? abortActor(ctx),
|
|
237
|
+
at: Date.now(),
|
|
238
|
+
};
|
|
239
|
+
await target.session.abort();
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
const abortFromSignal = () => void abortAgentCall(activeCall.id, { actor: abortActor(ctx) });
|
|
243
|
+
callbacks.signal?.addEventListener?.("abort", abortFromSignal, {
|
|
244
|
+
once: true,
|
|
245
|
+
});
|
|
246
|
+
const run = async () => {
|
|
247
|
+
target.runStartedAt ??= startedAt;
|
|
248
|
+
const monitor = createSessionActivityMonitor(details, (nextDetails) => {
|
|
249
|
+
target.lastActivityAt = new Date(nextDetails.updatedAt ?? Date.now()).toISOString();
|
|
250
|
+
target.lastActivities =
|
|
251
|
+
nextDetails.activities ?? target.lastActivities ?? [];
|
|
252
|
+
return publish(nextDetails);
|
|
253
|
+
});
|
|
254
|
+
const unsubscribe = !targetBusy && typeof target.session.subscribe === "function"
|
|
255
|
+
? target.session.subscribe((event) => monitor.observe(event))
|
|
256
|
+
: undefined;
|
|
257
|
+
try {
|
|
258
|
+
const receipt = buildReceiptText(callerAgent, callerSessionId, input.message);
|
|
259
|
+
await target.session.prompt(receipt, target.session.isStreaming
|
|
260
|
+
? { streamingBehavior: "followUp" }
|
|
261
|
+
: undefined);
|
|
262
|
+
const outcome = sessionRunOutcome(target, { request: input.message });
|
|
263
|
+
const completed = outcome.status === "done"
|
|
264
|
+
? monitor.finish({
|
|
265
|
+
activities: mergeActivities(monitor.activities, collectSessionActivities(target.session)),
|
|
266
|
+
})
|
|
267
|
+
: monitor.stop(outcome.status, {
|
|
268
|
+
error: outcome.text,
|
|
269
|
+
activities: mergeActivities(monitor.activities, collectSessionActivities(target.session)),
|
|
270
|
+
});
|
|
271
|
+
target.lastActivities =
|
|
272
|
+
completed.activities ?? target.lastActivities ?? [];
|
|
273
|
+
target.runStartedAt = undefined;
|
|
274
|
+
if (outcome.status === "done")
|
|
275
|
+
await displayTargetAnswerIfVisible({
|
|
276
|
+
ctx: activeVisibleContext() ?? ctx,
|
|
277
|
+
target: getRuntimeSession(targetSessionId) ?? target,
|
|
278
|
+
targetSessionId,
|
|
279
|
+
text: outcome.text,
|
|
280
|
+
});
|
|
281
|
+
await deliverReturnToCaller({
|
|
282
|
+
pi: this.pi,
|
|
283
|
+
ctx: activeVisibleContext() ?? ctx,
|
|
284
|
+
callerSessionId,
|
|
285
|
+
callerSessionManager,
|
|
286
|
+
text: outcome.status === "done"
|
|
287
|
+
? buildReturnText(target.agentName, targetSessionId, outcome.text)
|
|
288
|
+
: outcome.text,
|
|
289
|
+
invoke: invokeMeLater,
|
|
290
|
+
persist: persistSessionImmediately,
|
|
291
|
+
visibleSession: activeVisibleSession(),
|
|
292
|
+
invokeInactiveCaller: (text) => this.invokeCallerSession({
|
|
293
|
+
callerSessionManager,
|
|
294
|
+
callerCwd,
|
|
295
|
+
text,
|
|
296
|
+
config,
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
299
|
+
return { answer: outcome.text, details: completed };
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
const outcome = sessionRunOutcome(target, {
|
|
303
|
+
request: input.message,
|
|
304
|
+
error,
|
|
305
|
+
});
|
|
306
|
+
const failed = monitor.stop(outcome.status, {
|
|
307
|
+
error: outcome.text,
|
|
308
|
+
activities: mergeActivities(monitor.activities, collectSessionActivities(target.session)),
|
|
309
|
+
});
|
|
310
|
+
target.lastActivities =
|
|
311
|
+
failed.activities ?? target.lastActivities ?? [];
|
|
312
|
+
target.runStartedAt = undefined;
|
|
313
|
+
await deliverReturnToCaller({
|
|
314
|
+
pi: this.pi,
|
|
315
|
+
ctx: activeVisibleContext() ?? ctx,
|
|
316
|
+
callerSessionId,
|
|
317
|
+
callerSessionManager,
|
|
318
|
+
text: outcome.text,
|
|
319
|
+
invoke: invokeMeLater,
|
|
320
|
+
persist: persistSessionImmediately,
|
|
321
|
+
visibleSession: activeVisibleSession(),
|
|
322
|
+
invokeInactiveCaller: (text) => this.invokeCallerSession({
|
|
323
|
+
callerSessionManager,
|
|
324
|
+
callerCwd,
|
|
325
|
+
text,
|
|
326
|
+
config,
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
return { answer: outcome.text, details: failed };
|
|
330
|
+
}
|
|
331
|
+
finally {
|
|
332
|
+
unsubscribe?.();
|
|
333
|
+
setRuntimeSession(targetSessionId, target);
|
|
334
|
+
callbacks.signal?.removeEventListener?.("abort", abortFromSignal);
|
|
335
|
+
activeCall.unregister();
|
|
336
|
+
if (target.session.isStreaming !== true)
|
|
337
|
+
unregisterLiveRuntime(targetSessionId);
|
|
338
|
+
pruneRuntimeSessions();
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
if (shouldDeferSendCompletion({
|
|
342
|
+
async: targetAsync,
|
|
343
|
+
awaitCompletion: callbacks.awaitCompletion,
|
|
344
|
+
})) {
|
|
345
|
+
void run()
|
|
346
|
+
.catch((error) => this.pi.sendMessage({
|
|
347
|
+
customType: "pi-gentic:card",
|
|
348
|
+
content: getErrorMessage(error),
|
|
349
|
+
display: true,
|
|
350
|
+
details: this.cardDetails("send", "error", {
|
|
351
|
+
...details,
|
|
352
|
+
error: getErrorMessage(error),
|
|
353
|
+
completedAt: Date.now(),
|
|
354
|
+
}),
|
|
355
|
+
}, { triggerTurn: false, deliverAs: "nextTurn" }))
|
|
356
|
+
.finally(() => callbacks.onSettled?.());
|
|
357
|
+
return {
|
|
358
|
+
text: sendPendingText({
|
|
359
|
+
async: targetAsync,
|
|
360
|
+
agentName: target.agentName,
|
|
361
|
+
sessionId: target.session.sessionManager.getSessionId(),
|
|
362
|
+
message: input.message,
|
|
363
|
+
details,
|
|
364
|
+
}),
|
|
365
|
+
details: this.cardDetails("send", details.status ?? "running", details),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const result = await run();
|
|
369
|
+
callbacks.onSettled?.();
|
|
370
|
+
return { text: result.answer, details: result.details };
|
|
371
|
+
}
|
|
372
|
+
async resolveSendCwd(ctx, input) {
|
|
373
|
+
if (input.worktree === undefined)
|
|
374
|
+
return input.cwd ?? ctx.cwd;
|
|
375
|
+
return this.prepareWorktree(ctx, input);
|
|
376
|
+
}
|
|
377
|
+
async prepareWorktree(ctx, input) {
|
|
378
|
+
return prepareWorktree({ ...input, repoCwd: ctx.cwd });
|
|
379
|
+
}
|
|
380
|
+
async resolveTargetSession(ctx, input, config) {
|
|
381
|
+
if (input.sessionId) {
|
|
382
|
+
const session = await this.getOrOpenSession(ctx, input.sessionId, input.cwd);
|
|
383
|
+
if (input.agent)
|
|
384
|
+
await this.loadAgentIntoSession(session.session, input.agent, input.overrides, config);
|
|
385
|
+
else if (input.overrides)
|
|
386
|
+
await this.applySessionOverrides(session.session, input.overrides, config);
|
|
387
|
+
return session;
|
|
388
|
+
}
|
|
389
|
+
const session = await this.createChildSession(ctx, input);
|
|
390
|
+
if (input.agent)
|
|
391
|
+
await this.loadAgentIntoSession(session.session, input.agent, input.overrides, config);
|
|
392
|
+
else if (input.overrides)
|
|
393
|
+
await this.applySessionOverrides(session.session, input.overrides, config);
|
|
394
|
+
else
|
|
395
|
+
await this.applyAgentlessPolicyToNewSession(session.session, config, ctx.model);
|
|
396
|
+
if (typeof input.agent === "string")
|
|
397
|
+
session.agentName = input.agent;
|
|
398
|
+
return session;
|
|
399
|
+
}
|
|
400
|
+
async createChildSession(ctx, input) {
|
|
401
|
+
let sessionManager;
|
|
402
|
+
const sessionDir = ctx.sessionManager.getSessionDir();
|
|
403
|
+
persistSessionImmediately(ctx.sessionManager);
|
|
404
|
+
const parentSession = ctx.sessionManager.getSessionFile();
|
|
405
|
+
if (input.fork && parentSession) {
|
|
406
|
+
sessionManager = SessionManager.forkFrom(parentSession, input.cwd ?? ctx.cwd, sessionDir);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
sessionManager = SessionManager.create(input.cwd ?? ctx.cwd, sessionDir, {
|
|
410
|
+
parentSession,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
if (typeof sessionManager.appendSessionInfo === "function")
|
|
414
|
+
sessionManager.appendSessionInfo(input.message);
|
|
415
|
+
persistSessionImmediately(sessionManager);
|
|
416
|
+
const runtimeHost = await createLiveRuntime({
|
|
417
|
+
cwd: input.cwd ?? ctx.cwd,
|
|
418
|
+
sessionManager,
|
|
419
|
+
});
|
|
420
|
+
const runtime = {
|
|
421
|
+
runtimeHost,
|
|
422
|
+
session: runtimeHost.session,
|
|
423
|
+
agentName: undefined,
|
|
424
|
+
parentSessionPath: parentSession,
|
|
425
|
+
lastMessage: input.message,
|
|
426
|
+
createdAt: new Date().toISOString(),
|
|
427
|
+
};
|
|
428
|
+
setRuntimeSession(runtimeHost.session.sessionManager.getSessionId(), runtime);
|
|
429
|
+
return runtime;
|
|
430
|
+
}
|
|
431
|
+
async getOrOpenSession(ctx, reference, cwd) {
|
|
432
|
+
const existing = findRuntimeSession((runtime) => matchesSession(runtime.session.sessionManager.getSessionId(), reference));
|
|
433
|
+
if (existing)
|
|
434
|
+
return existing;
|
|
435
|
+
const sessions = await SessionManager.list(cwd ?? ctx.cwd, ctx.sessionManager.getSessionDir());
|
|
436
|
+
const resolved = resolveSessionReference(sessions, reference);
|
|
437
|
+
const sessionManager = SessionManager.open(resolved.path, ctx.sessionManager.getSessionDir(), cwd);
|
|
438
|
+
const runtimeHost = await createLiveRuntime({
|
|
439
|
+
cwd: cwd ?? sessionManager.getCwd(),
|
|
440
|
+
sessionManager,
|
|
441
|
+
});
|
|
442
|
+
const state = getActiveState(sessionManager);
|
|
443
|
+
const runtime = {
|
|
444
|
+
runtimeHost,
|
|
445
|
+
session: runtimeHost.session,
|
|
446
|
+
agentName: state.agentName,
|
|
447
|
+
parentSessionPath: sessionManager.getHeader?.()?.parentSession,
|
|
448
|
+
};
|
|
449
|
+
setRuntimeSession(runtimeHost.session.sessionManager.getSessionId(), runtime);
|
|
450
|
+
return runtime;
|
|
451
|
+
}
|
|
452
|
+
async loadAgentIntoSession(session, agentName, overrides, config, accessContext) {
|
|
453
|
+
const agent = accessContext
|
|
454
|
+
? this.assertAgentAvailable(accessContext, agentName, config)
|
|
455
|
+
: config.agents.find((item) => item.name.toLowerCase() === agentName.toLowerCase());
|
|
456
|
+
if (!agent)
|
|
457
|
+
throw new Error(`Unknown agent "${agentName}".`);
|
|
458
|
+
appendActiveState(session.sessionManager, {
|
|
459
|
+
agentName: agent.name,
|
|
460
|
+
overrides,
|
|
461
|
+
});
|
|
462
|
+
await this.applyPolicyToAgentSession(session, config);
|
|
463
|
+
setRuntimeSession(session.sessionManager.getSessionId(), {
|
|
464
|
+
session,
|
|
465
|
+
agentName: agent.name,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
async applySessionOverrides(session, overrides, config) {
|
|
469
|
+
const state = getActiveState(session.sessionManager);
|
|
470
|
+
appendActiveState(session.sessionManager, {
|
|
471
|
+
agentName: state.agentName,
|
|
472
|
+
overrides,
|
|
473
|
+
});
|
|
474
|
+
return this.applyPolicyToAgentSession(session, config);
|
|
475
|
+
}
|
|
476
|
+
async applyPolicyToAgentSession(session, config) {
|
|
477
|
+
const policy = this.resolveAgentSessionPolicy(session, config);
|
|
478
|
+
session.setActiveToolsByName(policy.resources.tools);
|
|
479
|
+
if (policy.model) {
|
|
480
|
+
const model = resolveModelFromRegistry(session.modelRegistry, policy.model);
|
|
481
|
+
if (model)
|
|
482
|
+
await session.setModel(model);
|
|
483
|
+
}
|
|
484
|
+
if (policy.thinking)
|
|
485
|
+
session.setThinkingLevel(policy.thinking);
|
|
486
|
+
return policy;
|
|
487
|
+
}
|
|
488
|
+
async applyAgentlessPolicyToNewSession(session, config, inheritedModel) {
|
|
489
|
+
const policy = await this.applyPolicyToAgentSession(session, config);
|
|
490
|
+
await applyInheritedModel(session, policy, inheritedModel);
|
|
491
|
+
return policy;
|
|
492
|
+
}
|
|
493
|
+
resolveAgentSessionPolicy(session, config) {
|
|
494
|
+
const state = getActiveState(session.sessionManager);
|
|
495
|
+
const activeAgent = config.agents.find((agent) => agent.name === state.agentName);
|
|
496
|
+
return resolveSessionPolicy({
|
|
497
|
+
settings: config.settings,
|
|
498
|
+
activeAgent,
|
|
499
|
+
overrides: state.overrides,
|
|
500
|
+
allAgents: config.agents.map((agent) => agent.name),
|
|
501
|
+
allTools: session.getAllTools().map((tool) => tool.name),
|
|
502
|
+
allSkills: session.resourceLoader
|
|
503
|
+
.getSkills()
|
|
504
|
+
.skills.map((skill) => skill.name),
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
async status(ctx, sessionId) {
|
|
508
|
+
if (!sessionId)
|
|
509
|
+
throw new Error('Field "sessionId" is required for status.');
|
|
510
|
+
const runtime = await this.getOrOpenSession(ctx, sessionId);
|
|
511
|
+
return sessionStatus(runtime);
|
|
512
|
+
}
|
|
513
|
+
async abort(ctx, sessionId) {
|
|
514
|
+
if (!sessionId) {
|
|
515
|
+
ctx.abort();
|
|
516
|
+
return `Aborted session ${shortSessionId(ctx.sessionManager.getSessionId())}.`;
|
|
517
|
+
}
|
|
518
|
+
const runtime = await this.getOrOpenSession(ctx, sessionId);
|
|
519
|
+
runtime.lastAbort = { actor: abortActor(ctx), at: Date.now() };
|
|
520
|
+
await runtime.session.abort();
|
|
521
|
+
return `Aborted session ${shortSessionId(runtime.session.sessionManager.getSessionId())}.`;
|
|
522
|
+
}
|
|
523
|
+
async invokeCallerSession({ callerSessionManager, callerCwd, text, config }) {
|
|
524
|
+
const sessionId = callerSessionManager.getSessionId();
|
|
525
|
+
const existing = findRuntimeSession((runtime) => runtime.session.sessionManager.getSessionId() === sessionId);
|
|
526
|
+
const runtime = existing ??
|
|
527
|
+
(await this.createRuntimeForSessionManager(callerSessionManager, callerCwd));
|
|
528
|
+
await this.applyPolicyToAgentSession(runtime.session, config);
|
|
529
|
+
runtime.lastMessage = text;
|
|
530
|
+
runtime.lastActivityAt = new Date().toISOString();
|
|
531
|
+
void runtime.session
|
|
532
|
+
.prompt(text, runtime.session.isStreaming
|
|
533
|
+
? { streamingBehavior: "followUp" }
|
|
534
|
+
: undefined)
|
|
535
|
+
.catch((error) => {
|
|
536
|
+
runtime.lastActivityAt = new Date().toISOString();
|
|
537
|
+
runtime.session.sessionManager.appendCustomMessageEntry?.("pi-gentic:return-invoke-error", getErrorMessage(error), true, { kind: "returnInvokeError" });
|
|
538
|
+
persistSessionImmediately(runtime.session.sessionManager);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async createRuntimeForSessionManager(sessionManager, cwd) {
|
|
542
|
+
persistSessionImmediately(sessionManager);
|
|
543
|
+
const runtimeHost = await createLiveRuntime({
|
|
544
|
+
cwd: cwd ?? sessionManager.getCwd(),
|
|
545
|
+
sessionManager,
|
|
546
|
+
});
|
|
547
|
+
const state = getActiveState(sessionManager);
|
|
548
|
+
const runtime = {
|
|
549
|
+
runtimeHost,
|
|
550
|
+
session: runtimeHost.session,
|
|
551
|
+
agentName: state.agentName,
|
|
552
|
+
parentSessionPath: sessionManager.getHeader?.()?.parentSession,
|
|
553
|
+
createdAt: new Date().toISOString(),
|
|
554
|
+
};
|
|
555
|
+
setRuntimeSession(runtimeHost.session.sessionManager.getSessionId(), runtime);
|
|
556
|
+
return runtime;
|
|
557
|
+
}
|
|
558
|
+
/** Builds the orchestration tree visible to the agents tool and TUI picker. */
|
|
559
|
+
async discoverSessions(ctx, input) {
|
|
560
|
+
const policy = this.resolvePolicy(ctx, this.load(ctx));
|
|
561
|
+
const rx = parseIntegerRadius(input.rx, "rx", policy.agentsTool?.rx ?? 0);
|
|
562
|
+
const ry = parseIntegerRadius(input.ry, "ry", policy.agentsTool?.ry ?? 0);
|
|
563
|
+
const current = currentSessionSummary(ctx);
|
|
564
|
+
const sessionDir = ctx.sessionManager.getSessionDir();
|
|
565
|
+
const persisted = await cachedPersistedSessions(`${ctx.cwd ?? ""}\n${sessionDir ?? ""}`, () => listDiscoverySessionSources(ctx.cwd, sessionDir));
|
|
566
|
+
const tree = buildSessionTree(current, persisted, listRuntimeSessions());
|
|
567
|
+
const related = sessionDiscoveryScope(tree, current, {
|
|
568
|
+
rx,
|
|
569
|
+
ry,
|
|
570
|
+
all: input.all,
|
|
571
|
+
});
|
|
572
|
+
const enriched = enrichSessionSummaries(related, input.all ? 30 : 20);
|
|
573
|
+
return {
|
|
574
|
+
rx,
|
|
575
|
+
ry,
|
|
576
|
+
sessions: assignTreeDepths(enriched).map(withRuntimeState),
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
resolveModel(ctx, modelName) {
|
|
580
|
+
return resolveModelFromRegistry(ctx.modelRegistry, modelName);
|
|
581
|
+
}
|
|
582
|
+
cardDetails(kind, status, details = {}) {
|
|
583
|
+
return { kind, status, updatedAt: Date.now(), ...details };
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async function listDiscoverySessionSources(cwd, sessionDir) {
|
|
587
|
+
const fast = listSessionSummariesFast(sessionDir);
|
|
588
|
+
return fast.length > 0 ? fast : SessionManager.list(cwd, sessionDir);
|
|
589
|
+
}
|
|
590
|
+
function currentSkillNames(ctx) {
|
|
591
|
+
try {
|
|
592
|
+
return (ctx
|
|
593
|
+
.getSystemPromptOptions?.()
|
|
594
|
+
.skills?.map((skill) => skill.name)
|
|
595
|
+
.filter(Boolean) ?? []);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return [];
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function skillContext(ctx, parsedEntries = []) {
|
|
602
|
+
const entries = mergeSkillEntries(loadAvailableSkills({ cwd: ctx.cwd }), parsedEntries);
|
|
603
|
+
const names = entries.map((skill) => skill.name);
|
|
604
|
+
return { entries, names: names.length > 0 ? names : currentSkillNames(ctx) };
|
|
605
|
+
}
|
|
606
|
+
function safeSystemPrompt(ctx) {
|
|
607
|
+
try {
|
|
608
|
+
return ctx.getSystemPrompt?.() ?? "";
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
return "";
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function compactPolicy(policy) {
|
|
615
|
+
return {
|
|
616
|
+
model: policy.model,
|
|
617
|
+
thinking: policy.thinking,
|
|
618
|
+
theme: policy.theme,
|
|
619
|
+
tools: policy.resources.tools,
|
|
620
|
+
agents: policy.resources.agents,
|
|
621
|
+
skills: policy.resources.skills,
|
|
622
|
+
systemPromptFiles: policy.systemPromptFiles,
|
|
623
|
+
maxSubagentDepth: policy.maxSubagentDepth,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function matchesSession(sessionId, reference) {
|
|
627
|
+
const id = String(sessionId).toLowerCase();
|
|
628
|
+
const query = String(reference).toLowerCase();
|
|
629
|
+
return (id === query ||
|
|
630
|
+
id.startsWith(query) ||
|
|
631
|
+
id.includes(query) ||
|
|
632
|
+
shortSessionId(id) === query);
|
|
633
|
+
}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Combines defaults, the active agent, and runtime overrides into usable policy. */
|
|
2
|
+
export declare function resolveSessionPolicy({ settings, activeAgent, overrides, allAgents, allTools, allSkills, }: {
|
|
3
|
+
settings: any;
|
|
4
|
+
activeAgent: any;
|
|
5
|
+
overrides: any;
|
|
6
|
+
allAgents: any;
|
|
7
|
+
allTools: any;
|
|
8
|
+
allSkills: any;
|
|
9
|
+
}): {
|
|
10
|
+
agentName: any;
|
|
11
|
+
description: any;
|
|
12
|
+
instructions: any;
|
|
13
|
+
model: any;
|
|
14
|
+
thinking: any;
|
|
15
|
+
theme: any;
|
|
16
|
+
maxSubagentDepth: any;
|
|
17
|
+
agentsTool: any;
|
|
18
|
+
systemPromptFiles: string[];
|
|
19
|
+
resources: {
|
|
20
|
+
agents: string[];
|
|
21
|
+
tools: string[];
|
|
22
|
+
skills: string[];
|
|
23
|
+
};
|
|
24
|
+
recipe: {
|
|
25
|
+
agentReference: any;
|
|
26
|
+
overrides: any;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/** Reads the latest pi-gentic state entry from append-only session history. */
|
|
30
|
+
export declare function getActiveState(sessionManager: any): {
|
|
31
|
+
agentName: any;
|
|
32
|
+
overrides: any;
|
|
33
|
+
};
|
|
34
|
+
/** Writes a new durable active-agent recipe without mutating older entries. */
|
|
35
|
+
export declare function appendActiveState(sessionManager: any, state: any): void;
|
|
36
|
+
export declare const AGENT_CYCLE_SHORTCUT = "f7";
|
|
37
|
+
export declare function nextAgentName(currentAgentName: any, agents: any): any;
|
|
38
|
+
export declare function hasPersistedAgentState(sessionManager: any): any;
|
|
39
|
+
export declare function shouldApplyDefaultAgent(event: any, sessionManager: any): boolean;
|
|
40
|
+
export declare function configuredDefaultAgent(settings: any): any;
|
|
41
|
+
export declare function activeAgentName(sessionManager: any): any;
|
|
42
|
+
export declare function filterAvailableAgents(config: AnyRecord, policy: AnyRecord): AnyRecord[];
|
|
43
|
+
export declare function assertAvailableAgent(agentName: unknown, agents: AnyRecord[]): AnyRecord;
|