@smithers-orchestrator/agents 0.16.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/LICENSE +21 -0
- package/package.json +65 -0
- package/src/AgentLike.ts +28 -0
- package/src/AmpAgent.js +232 -0
- package/src/AmpAgentOptions.ts +26 -0
- package/src/AnthropicAgent.js +54 -0
- package/src/AnthropicAgentOptions.ts +8 -0
- package/src/BaseCliAgent/AgentCliActionKind.ts +10 -0
- package/src/BaseCliAgent/AgentCliEvent.ts +44 -0
- package/src/BaseCliAgent/BaseCliAgent.js +874 -0
- package/src/BaseCliAgent/BaseCliAgentOptions.ts +13 -0
- package/src/BaseCliAgent/CliOutputInterpreter.ts +8 -0
- package/src/BaseCliAgent/CliUsageInfo.ts +7 -0
- package/src/BaseCliAgent/CodexConfigOverrides.ts +3 -0
- package/src/BaseCliAgent/PiExtensionUiRequest.ts +10 -0
- package/src/BaseCliAgent/PiExtensionUiResponse.ts +7 -0
- package/src/BaseCliAgent/RunCommandResult.ts +5 -0
- package/src/BaseCliAgent/buildGenerateResult.js +57 -0
- package/src/BaseCliAgent/combineNonEmpty.js +8 -0
- package/src/BaseCliAgent/createAgentStdoutTextEmitter.js +198 -0
- package/src/BaseCliAgent/extractPrompt.js +88 -0
- package/src/BaseCliAgent/extractTextFromJsonValue.js +46 -0
- package/src/BaseCliAgent/index.js +32 -0
- package/src/BaseCliAgent/normalizeCodexConfig.js +22 -0
- package/src/BaseCliAgent/parseHelpers.js +111 -0
- package/src/BaseCliAgent/pushFlag.js +18 -0
- package/src/BaseCliAgent/pushList.js +10 -0
- package/src/BaseCliAgent/resolveTimeouts.js +24 -0
- package/src/BaseCliAgent/runCommandEffect.js +32 -0
- package/src/BaseCliAgent/runRpcCommandEffect.js +365 -0
- package/src/BaseCliAgent/truncateToBytes.js +13 -0
- package/src/BaseCliAgent/tryParseJson.js +18 -0
- package/src/ClaudeCodeAgent.js +455 -0
- package/src/ClaudeCodeAgentOptions.ts +52 -0
- package/src/CodexAgent.js +593 -0
- package/src/CodexAgentOptions.ts +23 -0
- package/src/ForgeAgent.js +128 -0
- package/src/ForgeAgentOptions.ts +14 -0
- package/src/GeminiAgent.js +273 -0
- package/src/GeminiAgentOptions.ts +20 -0
- package/src/KimiAgent.js +260 -0
- package/src/KimiAgentOptions.ts +21 -0
- package/src/OpenAIAgent.js +54 -0
- package/src/OpenAIAgentOptions.ts +8 -0
- package/src/PiAgent.js +468 -0
- package/src/PiAgentOptions.ts +40 -0
- package/src/SdkAgentOptions.ts +16 -0
- package/src/agent-contract/SmithersAgentContract.ts +10 -0
- package/src/agent-contract/SmithersAgentContractTool.ts +8 -0
- package/src/agent-contract/SmithersAgentToolCategory.ts +6 -0
- package/src/agent-contract/SmithersListedTool.ts +4 -0
- package/src/agent-contract/SmithersToolSurface.ts +1 -0
- package/src/agent-contract/createSmithersAgentContract.js +188 -0
- package/src/agent-contract/index.js +10 -0
- package/src/agent-contract/renderSmithersAgentPromptGuidance.js +81 -0
- package/src/capability-registry/AgentCapabilityRegistry.ts +22 -0
- package/src/capability-registry/AgentToolDescriptor.ts +4 -0
- package/src/capability-registry/hashCapabilityRegistry.js +43 -0
- package/src/capability-registry/index.js +8 -0
- package/src/capability-registry/normalizeCapabilityRegistry.js +52 -0
- package/src/capability-registry/normalizeCapabilityStringList.js +9 -0
- package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +6 -0
- package/src/cli-capabilities/CliAgentCapabilityDoctorReport.ts +18 -0
- package/src/cli-capabilities/CliAgentCapabilityReportEntry.ts +9 -0
- package/src/cli-capabilities/formatCliAgentCapabilityDoctorReport.js +24 -0
- package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +92 -0
- package/src/cli-capabilities/getCliAgentCapabilityReport.js +52 -0
- package/src/cli-capabilities/index.js +11 -0
- package/src/diagnostics/DiagnosticCheck.ts +11 -0
- package/src/diagnostics/DiagnosticCheckId.ts +4 -0
- package/src/diagnostics/DiagnosticContext.ts +4 -0
- package/src/diagnostics/DiagnosticReport.ts +9 -0
- package/src/diagnostics/enrichReportWithErrorAnalysis.js +34 -0
- package/src/diagnostics/formatDiagnosticSummary.js +17 -0
- package/src/diagnostics/getDiagnosticStrategy.js +503 -0
- package/src/diagnostics/index.js +13 -0
- package/src/diagnostics/launchDiagnostics.js +16 -0
- package/src/diagnostics/runDiagnostics.js +52 -0
- package/src/index.d.ts +872 -0
- package/src/index.js +39 -0
- package/src/resolveSdkModel.js +9 -0
- package/src/sanitizeForOpenAI.js +47 -0
- package/src/streamResultToGenerateResult.js +70 -0
- package/src/zodToOpenAISchema.js +16 -0
package/src/PiAgent.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
// @smithers-type-exports-begin
|
|
2
|
+
/** @typedef {import("./BaseCliAgent/PiExtensionUiRequest.ts").PiExtensionUiRequest} PiExtensionUiRequest */
|
|
3
|
+
/** @typedef {import("./BaseCliAgent/PiExtensionUiResponse.ts").PiExtensionUiResponse} PiExtensionUiResponse */
|
|
4
|
+
// @smithers-type-exports-end
|
|
5
|
+
|
|
6
|
+
import { Effect } from "effect";
|
|
7
|
+
import { BaseCliAgent, buildGenerateResult, combineNonEmpty, extractPrompt, extractTextFromJsonValue, pushFlag, resolveTimeouts, runAgentPromise, runRpcCommandEffect, tryParseJson, asString, truncate, toolKindFromName, } from "./BaseCliAgent/index.js";
|
|
8
|
+
import { normalizeCapabilityStringList, } from "./capability-registry/index.js";
|
|
9
|
+
import { toSmithersError } from "@smithers-orchestrator/errors/toSmithersError";
|
|
10
|
+
import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
11
|
+
import { enrichReportWithErrorAnalysis, launchDiagnostics } from "./diagnostics/index.js";
|
|
12
|
+
/** @typedef {import("./capability-registry/AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
13
|
+
/** @typedef {import("./BaseCliAgent/CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
|
|
14
|
+
/** @typedef {import("./BaseCliAgent/AgentCliEvent.ts").AgentCliEvent} AgentCliEvent */
|
|
15
|
+
/** @typedef {import("ai").GenerateTextResult<Record<string, never>, unknown>} GenerateTextResult */
|
|
16
|
+
/** @typedef {import("./PiAgentOptions.ts").PiAgentOptions} PiAgentOptions */
|
|
17
|
+
/** @typedef {"text" | "json" | "stream-json" | "rpc"} PiMode */
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* prompt?: unknown;
|
|
21
|
+
* messages?: unknown;
|
|
22
|
+
* onEvent?: (event: AgentCliEvent) => unknown;
|
|
23
|
+
* resumeSession?: unknown;
|
|
24
|
+
* rootDir?: string;
|
|
25
|
+
* timeout?: unknown;
|
|
26
|
+
* abortSignal?: AbortSignal;
|
|
27
|
+
* maxOutputBytes?: number;
|
|
28
|
+
* onStdout?: (text: string) => void;
|
|
29
|
+
* onStderr?: (text: string) => void;
|
|
30
|
+
* [key: string]: unknown;
|
|
31
|
+
* }} PiGenerateOptions
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {PiAgentOptions} opts
|
|
36
|
+
*/
|
|
37
|
+
function resolvePiBuiltIns(opts) {
|
|
38
|
+
if (opts.noTools) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return opts.tools?.length
|
|
42
|
+
? normalizeCapabilityStringList(opts.tools)
|
|
43
|
+
: ["default"];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @param {PiAgentOptions} [opts]
|
|
47
|
+
* @returns {AgentCapabilityRegistry}
|
|
48
|
+
*/
|
|
49
|
+
export function createPiCapabilityRegistry(opts = {}) {
|
|
50
|
+
return {
|
|
51
|
+
version: 1,
|
|
52
|
+
engine: "pi",
|
|
53
|
+
runtimeTools: {},
|
|
54
|
+
mcp: {
|
|
55
|
+
bootstrap: "unsupported",
|
|
56
|
+
supportsProjectScope: false,
|
|
57
|
+
supportsUserScope: false,
|
|
58
|
+
},
|
|
59
|
+
skills: {
|
|
60
|
+
supportsSkills: true,
|
|
61
|
+
installMode: "files",
|
|
62
|
+
smithersSkillIds: normalizeCapabilityStringList(opts.skill),
|
|
63
|
+
},
|
|
64
|
+
humanInteraction: {
|
|
65
|
+
supportsUiRequests: true,
|
|
66
|
+
methods: ["extension_ui_request"],
|
|
67
|
+
},
|
|
68
|
+
builtIns: resolvePiBuiltIns(opts),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export class PiAgent extends BaseCliAgent {
|
|
72
|
+
opts;
|
|
73
|
+
capabilities;
|
|
74
|
+
cliEngine = "pi";
|
|
75
|
+
issuedSessionRef;
|
|
76
|
+
/**
|
|
77
|
+
* @param {PiAgentOptions} [opts]
|
|
78
|
+
*/
|
|
79
|
+
constructor(opts = {}) {
|
|
80
|
+
super(opts);
|
|
81
|
+
this.opts = opts;
|
|
82
|
+
this.capabilities = createPiCapabilityRegistry(opts);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @param {PiGenerateOptions} [options]
|
|
86
|
+
* @returns {PiMode}
|
|
87
|
+
*/
|
|
88
|
+
resolveMode(options) {
|
|
89
|
+
if (this.opts.mode === "rpc")
|
|
90
|
+
return "rpc";
|
|
91
|
+
if (options?.onEvent)
|
|
92
|
+
return "json";
|
|
93
|
+
return this.opts.mode ?? "text";
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @param {{ prompt: string; cwd: string; options?: PiGenerateOptions; mode: PiMode; }} params
|
|
97
|
+
* @returns {string[]}
|
|
98
|
+
*/
|
|
99
|
+
buildArgs(params) {
|
|
100
|
+
const args = [];
|
|
101
|
+
const { systemFromMessages } = extractPrompt(params.options);
|
|
102
|
+
const resumeSession = typeof params.options?.resumeSession === "string"
|
|
103
|
+
? params.options.resumeSession
|
|
104
|
+
: undefined;
|
|
105
|
+
const effectiveSession = resumeSession ?? this.opts.session;
|
|
106
|
+
this.issuedSessionRef = effectiveSession;
|
|
107
|
+
if (params.mode === "text") {
|
|
108
|
+
if (this.opts.print !== false)
|
|
109
|
+
args.push("--print");
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
args.push("--mode", params.mode);
|
|
113
|
+
}
|
|
114
|
+
pushFlag(args, "--provider", this.opts.provider);
|
|
115
|
+
pushFlag(args, "--model", this.opts.model ?? this.model);
|
|
116
|
+
pushFlag(args, "--api-key", this.opts.apiKey);
|
|
117
|
+
pushFlag(args, "--system-prompt", this.systemPrompt);
|
|
118
|
+
pushFlag(args, "--append-system-prompt", combineNonEmpty([this.opts.appendSystemPrompt, systemFromMessages]));
|
|
119
|
+
if (this.opts.continue)
|
|
120
|
+
args.push("--continue");
|
|
121
|
+
if (this.opts.resume)
|
|
122
|
+
args.push("--resume");
|
|
123
|
+
pushFlag(args, "--session", effectiveSession);
|
|
124
|
+
pushFlag(args, "--session-dir", this.opts.sessionDir);
|
|
125
|
+
const needsDurableSession = Boolean(params.options?.onEvent || effectiveSession);
|
|
126
|
+
const hasSessionFlags = needsDurableSession ||
|
|
127
|
+
Boolean(this.opts.sessionDir || this.opts.continue || this.opts.resume);
|
|
128
|
+
if (!needsDurableSession && (this.opts.noSession ?? (!hasSessionFlags))) {
|
|
129
|
+
args.push("--no-session");
|
|
130
|
+
}
|
|
131
|
+
if (this.opts.models) {
|
|
132
|
+
const models = Array.isArray(this.opts.models)
|
|
133
|
+
? this.opts.models.join(",")
|
|
134
|
+
: this.opts.models;
|
|
135
|
+
args.push("--models", models);
|
|
136
|
+
}
|
|
137
|
+
if (this.opts.listModels !== undefined && this.opts.listModels !== false) {
|
|
138
|
+
if (typeof this.opts.listModels === "string") {
|
|
139
|
+
args.push("--list-models", this.opts.listModels);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
args.push("--list-models");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
pushFlag(args, "--export", this.opts.export);
|
|
146
|
+
if (this.opts.tools?.length) {
|
|
147
|
+
args.push("--tools", this.opts.tools.join(","));
|
|
148
|
+
}
|
|
149
|
+
if (this.opts.noTools)
|
|
150
|
+
args.push("--no-tools");
|
|
151
|
+
if (this.opts.extension) {
|
|
152
|
+
for (const value of this.opts.extension) {
|
|
153
|
+
args.push("--extension", value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (this.opts.noExtensions)
|
|
157
|
+
args.push("--no-extensions");
|
|
158
|
+
if (this.opts.skill) {
|
|
159
|
+
for (const value of this.opts.skill) {
|
|
160
|
+
args.push("--skill", value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (this.opts.noSkills)
|
|
164
|
+
args.push("--no-skills");
|
|
165
|
+
if (this.opts.promptTemplate) {
|
|
166
|
+
for (const value of this.opts.promptTemplate) {
|
|
167
|
+
args.push("--prompt-template", value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (this.opts.noPromptTemplates)
|
|
171
|
+
args.push("--no-prompt-templates");
|
|
172
|
+
if (this.opts.theme) {
|
|
173
|
+
for (const value of this.opts.theme) {
|
|
174
|
+
args.push("--theme", value);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (this.opts.noThemes)
|
|
178
|
+
args.push("--no-themes");
|
|
179
|
+
pushFlag(args, "--thinking", this.opts.thinking);
|
|
180
|
+
if (this.opts.verbose)
|
|
181
|
+
args.push("--verbose");
|
|
182
|
+
if (this.extraArgs?.length)
|
|
183
|
+
args.push(...this.extraArgs);
|
|
184
|
+
if (params.mode !== "rpc" && this.opts.files) {
|
|
185
|
+
for (const value of this.opts.files) {
|
|
186
|
+
args.push(`@${value}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (params.prompt) {
|
|
190
|
+
args.push(params.prompt);
|
|
191
|
+
}
|
|
192
|
+
return args;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* @returns {CliOutputInterpreter}
|
|
196
|
+
*/
|
|
197
|
+
createOutputInterpreter() {
|
|
198
|
+
let sessionId = this.issuedSessionRef;
|
|
199
|
+
let emittedStarted = false;
|
|
200
|
+
let finalAnswer = "";
|
|
201
|
+
/**
|
|
202
|
+
* @param {unknown} value
|
|
203
|
+
*/
|
|
204
|
+
const summarizeValue = (value) => {
|
|
205
|
+
if (value == null)
|
|
206
|
+
return undefined;
|
|
207
|
+
const text = extractTextFromJsonValue(value);
|
|
208
|
+
if (text)
|
|
209
|
+
return truncate(text, 400);
|
|
210
|
+
try {
|
|
211
|
+
return truncate(JSON.stringify(value), 400);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return truncate(String(value), 400);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* @param {Record<string, unknown>} [detail]
|
|
219
|
+
* @returns {AgentCliEvent[]}
|
|
220
|
+
*/
|
|
221
|
+
const startedEvents = (detail) => {
|
|
222
|
+
if (emittedStarted || !sessionId)
|
|
223
|
+
return [];
|
|
224
|
+
emittedStarted = true;
|
|
225
|
+
return [{
|
|
226
|
+
type: "started",
|
|
227
|
+
engine: this.cliEngine,
|
|
228
|
+
title: "PI",
|
|
229
|
+
resume: sessionId,
|
|
230
|
+
detail,
|
|
231
|
+
}];
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* @param {string} line
|
|
235
|
+
* @returns {AgentCliEvent[]}
|
|
236
|
+
*/
|
|
237
|
+
const parseLine = (line) => {
|
|
238
|
+
const trimmed = line.trim();
|
|
239
|
+
if (!trimmed)
|
|
240
|
+
return [];
|
|
241
|
+
let payload;
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(trimmed);
|
|
244
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
payload = parsed;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
const type = asString(payload.type);
|
|
253
|
+
if (!type)
|
|
254
|
+
return [];
|
|
255
|
+
if (type === "session") {
|
|
256
|
+
sessionId = asString(payload.id) ?? sessionId;
|
|
257
|
+
return startedEvents({
|
|
258
|
+
cwd: asString(payload.cwd),
|
|
259
|
+
version: payload.version,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (type === "message_update") {
|
|
263
|
+
const assistantEvent = payload.assistantMessageEvent;
|
|
264
|
+
if (assistantEvent?.type === "text_delta" && typeof assistantEvent.delta === "string") {
|
|
265
|
+
finalAnswer += assistantEvent.delta;
|
|
266
|
+
}
|
|
267
|
+
return startedEvents();
|
|
268
|
+
}
|
|
269
|
+
if (type === "message_end" || type === "turn_end") {
|
|
270
|
+
const message = payload.message;
|
|
271
|
+
if (message?.role === "assistant") {
|
|
272
|
+
const extracted = extractTextFromJsonValue(message);
|
|
273
|
+
if (extracted) {
|
|
274
|
+
finalAnswer = extracted;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return startedEvents();
|
|
278
|
+
}
|
|
279
|
+
if (type === "tool_execution_start") {
|
|
280
|
+
const toolName = asString(payload.toolName) ?? "tool";
|
|
281
|
+
const toolId = asString(payload.toolCallId) ?? toolName;
|
|
282
|
+
return [
|
|
283
|
+
...startedEvents(),
|
|
284
|
+
{
|
|
285
|
+
type: "action",
|
|
286
|
+
engine: this.cliEngine,
|
|
287
|
+
phase: "started",
|
|
288
|
+
entryType: "thought",
|
|
289
|
+
action: {
|
|
290
|
+
id: toolId,
|
|
291
|
+
kind: toolKindFromName(toolName),
|
|
292
|
+
title: toolName,
|
|
293
|
+
detail: {
|
|
294
|
+
args: payload.args,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
message: `Running ${toolName}`,
|
|
298
|
+
level: "info",
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
if (type === "tool_execution_update") {
|
|
303
|
+
const toolName = asString(payload.toolName) ?? "tool";
|
|
304
|
+
const toolId = asString(payload.toolCallId) ?? toolName;
|
|
305
|
+
return [
|
|
306
|
+
...startedEvents(),
|
|
307
|
+
{
|
|
308
|
+
type: "action",
|
|
309
|
+
engine: this.cliEngine,
|
|
310
|
+
phase: "updated",
|
|
311
|
+
entryType: "thought",
|
|
312
|
+
action: {
|
|
313
|
+
id: toolId,
|
|
314
|
+
kind: toolKindFromName(toolName),
|
|
315
|
+
title: toolName,
|
|
316
|
+
detail: {
|
|
317
|
+
args: payload.args,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
message: summarizeValue(payload.partialResult),
|
|
321
|
+
level: "info",
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
}
|
|
325
|
+
if (type === "tool_execution_end") {
|
|
326
|
+
const toolName = asString(payload.toolName) ?? "tool";
|
|
327
|
+
const toolId = asString(payload.toolCallId) ?? toolName;
|
|
328
|
+
const ok = payload.isError !== true;
|
|
329
|
+
return [
|
|
330
|
+
...startedEvents(),
|
|
331
|
+
{
|
|
332
|
+
type: "action",
|
|
333
|
+
engine: this.cliEngine,
|
|
334
|
+
phase: "completed",
|
|
335
|
+
entryType: "thought",
|
|
336
|
+
action: {
|
|
337
|
+
id: toolId,
|
|
338
|
+
kind: toolKindFromName(toolName),
|
|
339
|
+
title: toolName,
|
|
340
|
+
detail: {
|
|
341
|
+
result: summarizeValue(payload.result),
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
message: summarizeValue(payload.result),
|
|
345
|
+
ok,
|
|
346
|
+
level: ok ? "info" : "warning",
|
|
347
|
+
},
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
return startedEvents();
|
|
351
|
+
};
|
|
352
|
+
return {
|
|
353
|
+
onStdoutLine: parseLine,
|
|
354
|
+
onExit: (result) => {
|
|
355
|
+
const started = !emittedStarted && sessionId
|
|
356
|
+
? startedEvents()
|
|
357
|
+
: [];
|
|
358
|
+
return [
|
|
359
|
+
...started,
|
|
360
|
+
{
|
|
361
|
+
type: "completed",
|
|
362
|
+
engine: this.cliEngine,
|
|
363
|
+
ok: !result.exitCode || result.exitCode === 0,
|
|
364
|
+
answer: finalAnswer || undefined,
|
|
365
|
+
error: result.exitCode && result.exitCode !== 0
|
|
366
|
+
? result.stderr.trim() || `PI exited with code ${result.exitCode}`
|
|
367
|
+
: undefined,
|
|
368
|
+
resume: sessionId,
|
|
369
|
+
},
|
|
370
|
+
];
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* @param {PiGenerateOptions} [options]
|
|
376
|
+
* @returns {Promise<GenerateTextResult>}
|
|
377
|
+
*/
|
|
378
|
+
async generate(options) {
|
|
379
|
+
const mode = this.resolveMode(options);
|
|
380
|
+
// Non-RPC modes delegate to BaseCliAgent.generate() which handles
|
|
381
|
+
// metrics, diagnostics, and the full process lifecycle.
|
|
382
|
+
if (mode !== "rpc") {
|
|
383
|
+
return super.generate(options);
|
|
384
|
+
}
|
|
385
|
+
// RPC mode requires a custom transport (stdin/stdout JSON-RPC).
|
|
386
|
+
if (this.opts.files?.length) {
|
|
387
|
+
throw new SmithersError("AGENT_RPC_FILE_ARGS", "RPC mode does not support file arguments");
|
|
388
|
+
}
|
|
389
|
+
const { prompt } = extractPrompt(options);
|
|
390
|
+
const callTimeouts = resolveTimeouts(options?.timeout, {
|
|
391
|
+
totalMs: this.timeoutMs,
|
|
392
|
+
idleMs: this.idleTimeoutMs,
|
|
393
|
+
});
|
|
394
|
+
const cwd = this.cwd ?? options?.rootDir ?? process.cwd();
|
|
395
|
+
const env = { ...process.env, ...this.env };
|
|
396
|
+
const args = this.buildArgs({ prompt, cwd, options, mode });
|
|
397
|
+
const diagnosticsPromise = launchDiagnostics("pi", env, cwd);
|
|
398
|
+
const interpreter = this.createOutputInterpreter();
|
|
399
|
+
/**
|
|
400
|
+
* @param {AgentCliEvent[] | AgentCliEvent | null | undefined} payload
|
|
401
|
+
*/
|
|
402
|
+
const emitEvents = (payload) => {
|
|
403
|
+
if (!payload || !options?.onEvent)
|
|
404
|
+
return;
|
|
405
|
+
const events = Array.isArray(payload) ? payload : [payload];
|
|
406
|
+
for (const event of events) {
|
|
407
|
+
void Promise.resolve(options.onEvent(event)).catch(() => undefined);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* @param {unknown} err
|
|
412
|
+
*/
|
|
413
|
+
const diagnosticsEnrichment = (err) => Effect.tryPromise({
|
|
414
|
+
try: async () => {
|
|
415
|
+
if (!diagnosticsPromise)
|
|
416
|
+
return;
|
|
417
|
+
const report = await diagnosticsPromise.catch(() => null);
|
|
418
|
+
if (report && err instanceof SmithersError) {
|
|
419
|
+
enrichReportWithErrorAnalysis(report, err.message);
|
|
420
|
+
err.details = { ...err.details, diagnostics: report };
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
catch: (cause) => toSmithersError(cause, "enrich diagnostics"),
|
|
424
|
+
}).pipe(Effect.ignore);
|
|
425
|
+
const rpcProgram = Effect.gen(this, function* () {
|
|
426
|
+
const rpcResult = yield* runRpcCommandEffect("pi", args, {
|
|
427
|
+
cwd,
|
|
428
|
+
env,
|
|
429
|
+
prompt,
|
|
430
|
+
timeoutMs: callTimeouts.totalMs,
|
|
431
|
+
idleTimeoutMs: callTimeouts.idleMs,
|
|
432
|
+
signal: options?.abortSignal,
|
|
433
|
+
maxOutputBytes: this.maxOutputBytes ?? options?.maxOutputBytes,
|
|
434
|
+
onStdout: options?.onStdout,
|
|
435
|
+
onStderr: options?.onStderr,
|
|
436
|
+
onJsonEvent: (event) => emitEvents(interpreter.onStdoutLine?.(JSON.stringify(event))),
|
|
437
|
+
onExtensionUiRequest: this.opts.onExtensionUiRequest,
|
|
438
|
+
});
|
|
439
|
+
emitEvents(interpreter.onExit?.({
|
|
440
|
+
stdout: rpcResult.text,
|
|
441
|
+
stderr: rpcResult.stderr,
|
|
442
|
+
exitCode: rpcResult.exitCode,
|
|
443
|
+
}));
|
|
444
|
+
return buildGenerateResult(rpcResult.text, rpcResult.output, this.opts.model ?? "pi", rpcResult.usage);
|
|
445
|
+
}).pipe(Effect.tapError(diagnosticsEnrichment));
|
|
446
|
+
return runAgentPromise(rpcProgram);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* @param {{ prompt: string; systemPrompt?: string; cwd: string; options?: PiGenerateOptions; }} params
|
|
450
|
+
* @returns {Promise<{ command: string; args: string[]; stdin?: string; outputFormat?: string; outputFile?: string; cleanup?: () => Promise<void>; }>}
|
|
451
|
+
*/
|
|
452
|
+
async buildCommand(params) {
|
|
453
|
+
const mode = this.resolveMode(params.options);
|
|
454
|
+
if (mode === "rpc") {
|
|
455
|
+
throw new SmithersError("AGENT_BUILD_COMMAND", "Pi RPC mode uses the custom RPC transport");
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
command: "pi",
|
|
459
|
+
args: this.buildArgs({
|
|
460
|
+
prompt: params.prompt,
|
|
461
|
+
cwd: params.cwd,
|
|
462
|
+
options: params.options,
|
|
463
|
+
mode,
|
|
464
|
+
}),
|
|
465
|
+
outputFormat: mode,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BaseCliAgentOptions } from "./BaseCliAgent/BaseCliAgentOptions";
|
|
2
|
+
import type { PiExtensionUiRequest } from "./BaseCliAgent/PiExtensionUiRequest";
|
|
3
|
+
import type { PiExtensionUiResponse } from "./BaseCliAgent/PiExtensionUiResponse";
|
|
4
|
+
|
|
5
|
+
export type PiAgentOptions = BaseCliAgentOptions & {
|
|
6
|
+
provider?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
systemPrompt?: string;
|
|
10
|
+
appendSystemPrompt?: string;
|
|
11
|
+
mode?: "text" | "json" | "rpc";
|
|
12
|
+
print?: boolean;
|
|
13
|
+
continue?: boolean;
|
|
14
|
+
resume?: boolean;
|
|
15
|
+
session?: string;
|
|
16
|
+
sessionDir?: string;
|
|
17
|
+
noSession?: boolean;
|
|
18
|
+
models?: string | string[];
|
|
19
|
+
listModels?: boolean | string;
|
|
20
|
+
tools?: string[];
|
|
21
|
+
noTools?: boolean;
|
|
22
|
+
extension?: string[];
|
|
23
|
+
noExtensions?: boolean;
|
|
24
|
+
skill?: string[];
|
|
25
|
+
noSkills?: boolean;
|
|
26
|
+
promptTemplate?: string[];
|
|
27
|
+
noPromptTemplates?: boolean;
|
|
28
|
+
theme?: string[];
|
|
29
|
+
noThemes?: boolean;
|
|
30
|
+
thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
31
|
+
export?: string;
|
|
32
|
+
files?: string[];
|
|
33
|
+
verbose?: boolean;
|
|
34
|
+
onExtensionUiRequest?: (
|
|
35
|
+
request: PiExtensionUiRequest,
|
|
36
|
+
) =>
|
|
37
|
+
| Promise<PiExtensionUiResponse | null>
|
|
38
|
+
| PiExtensionUiResponse
|
|
39
|
+
| null;
|
|
40
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToolLoopAgentSettings,
|
|
3
|
+
ToolSet,
|
|
4
|
+
} from "ai";
|
|
5
|
+
|
|
6
|
+
export type SdkAgentOptions<
|
|
7
|
+
CALL_OPTIONS = never,
|
|
8
|
+
TOOLS extends ToolSet = {},
|
|
9
|
+
MODEL = any,
|
|
10
|
+
> = Omit<ToolLoopAgentSettings<CALL_OPTIONS, TOOLS>, "model"> & {
|
|
11
|
+
/**
|
|
12
|
+
* Either a provider model id string or a preconstructed AI SDK language model.
|
|
13
|
+
* Passing a model instance is mainly useful for tests and advanced provider setup.
|
|
14
|
+
*/
|
|
15
|
+
model: string | MODEL;
|
|
16
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SmithersToolSurface } from "./SmithersToolSurface";
|
|
2
|
+
import type { SmithersAgentContractTool } from "./SmithersAgentContractTool";
|
|
3
|
+
|
|
4
|
+
export type SmithersAgentContract = {
|
|
5
|
+
toolSurface: SmithersToolSurface;
|
|
6
|
+
serverName: string;
|
|
7
|
+
tools: SmithersAgentContractTool[];
|
|
8
|
+
promptGuidance: string;
|
|
9
|
+
docsGuidance: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type SmithersToolSurface = "raw" | "semantic";
|