@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type BaseCliAgentOptions = {
|
|
2
|
+
id?: string;
|
|
3
|
+
model?: string;
|
|
4
|
+
systemPrompt?: string;
|
|
5
|
+
instructions?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
yolo?: boolean;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
idleTimeoutMs?: number;
|
|
11
|
+
maxOutputBytes?: number;
|
|
12
|
+
extraArgs?: string[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentCliEvent } from "./AgentCliEvent";
|
|
2
|
+
import type { RunCommandResult } from "./RunCommandResult";
|
|
3
|
+
|
|
4
|
+
export type CliOutputInterpreter = {
|
|
5
|
+
onStdoutLine?: (line: string) => AgentCliEvent[] | AgentCliEvent | null | undefined;
|
|
6
|
+
onStderrLine?: (line: string) => AgentCliEvent[] | AgentCliEvent | null | undefined;
|
|
7
|
+
onExit?: (result: RunCommandResult) => AgentCliEvent[] | AgentCliEvent | null | undefined;
|
|
8
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// PiExtensionUiRequest is defined here because RunRpcCommandOptions references it.
|
|
2
|
+
// It is re-exported from PiAgent.ts for the public API barrel.
|
|
3
|
+
export type PiExtensionUiRequest = {
|
|
4
|
+
type: "extension_ui_request";
|
|
5
|
+
id: string;
|
|
6
|
+
method: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
/** @typedef {import("ai").GenerateTextResult} GenerateTextResult */
|
|
3
|
+
/** @typedef {import("ai").LanguageModelUsage} LanguageModelUsage */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} text
|
|
7
|
+
* @param {unknown} output
|
|
8
|
+
* @param {string} modelId
|
|
9
|
+
* @param {LanguageModelUsage} [usage]
|
|
10
|
+
* @returns {GenerateTextResult<Record<string, never>, unknown>}
|
|
11
|
+
*/
|
|
12
|
+
export function buildGenerateResult(text, output, modelId, usage) {
|
|
13
|
+
const finalUsage = usage ?? {
|
|
14
|
+
inputTokens: undefined,
|
|
15
|
+
inputTokenDetails: {
|
|
16
|
+
noCacheTokens: undefined,
|
|
17
|
+
cacheReadTokens: undefined,
|
|
18
|
+
cacheWriteTokens: undefined,
|
|
19
|
+
},
|
|
20
|
+
outputTokens: undefined,
|
|
21
|
+
outputTokenDetails: {
|
|
22
|
+
textTokens: undefined,
|
|
23
|
+
reasoningTokens: undefined,
|
|
24
|
+
},
|
|
25
|
+
totalTokens: undefined,
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text }],
|
|
29
|
+
text,
|
|
30
|
+
reasoning: [],
|
|
31
|
+
reasoningText: undefined,
|
|
32
|
+
files: [],
|
|
33
|
+
sources: [],
|
|
34
|
+
toolCalls: [],
|
|
35
|
+
staticToolCalls: [],
|
|
36
|
+
dynamicToolCalls: [],
|
|
37
|
+
toolResults: [],
|
|
38
|
+
staticToolResults: [],
|
|
39
|
+
dynamicToolResults: [],
|
|
40
|
+
finishReason: "stop",
|
|
41
|
+
rawFinishReason: undefined,
|
|
42
|
+
usage: finalUsage,
|
|
43
|
+
totalUsage: finalUsage,
|
|
44
|
+
warnings: undefined,
|
|
45
|
+
request: {},
|
|
46
|
+
response: {
|
|
47
|
+
id: randomUUID(),
|
|
48
|
+
timestamp: new Date(),
|
|
49
|
+
modelId,
|
|
50
|
+
messages: [],
|
|
51
|
+
},
|
|
52
|
+
providerMetadata: undefined,
|
|
53
|
+
steps: [],
|
|
54
|
+
experimental_output: output,
|
|
55
|
+
output: output,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Array<string | undefined>} parts
|
|
3
|
+
* @returns {string | undefined}
|
|
4
|
+
*/
|
|
5
|
+
export function combineNonEmpty(parts) {
|
|
6
|
+
const filtered = parts.map((part) => (part ?? "").trim()).filter(Boolean);
|
|
7
|
+
return filtered.length ? filtered.join("\n\n") : undefined;
|
|
8
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { extractTextFromJsonValue } from "./extractTextFromJsonValue.js";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {{ push: (chunk: string) => void; flush: (finalText?: string) => void; }} AgentStdoutTextEmitter
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{ outputFormat?: string; onText?: (text: string) => void; }} AgentStdoutTextEmitterOptions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {unknown} messages
|
|
11
|
+
* @returns {unknown | undefined}
|
|
12
|
+
*/
|
|
13
|
+
function extractLastAssistantMessage(messages) {
|
|
14
|
+
if (!Array.isArray(messages))
|
|
15
|
+
return undefined;
|
|
16
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
17
|
+
const message = messages[i];
|
|
18
|
+
if (message && typeof message === "object"
|
|
19
|
+
&& /** @type {Record<string, unknown>} */ (message).role === "assistant") {
|
|
20
|
+
return message;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @param {unknown} parsed
|
|
27
|
+
* @param {{ sawDeltaSinceBoundary: boolean }} state
|
|
28
|
+
* @returns {string[]}
|
|
29
|
+
*/
|
|
30
|
+
function extractCliStreamTextChunks(parsed, state) {
|
|
31
|
+
/** @type {string[]} */
|
|
32
|
+
const chunks = [];
|
|
33
|
+
/**
|
|
34
|
+
* @param {string | undefined} text
|
|
35
|
+
*/
|
|
36
|
+
const emitDelta = (text) => {
|
|
37
|
+
if (!text)
|
|
38
|
+
return;
|
|
39
|
+
state.sawDeltaSinceBoundary = true;
|
|
40
|
+
chunks.push(text);
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* @param {string | undefined} text
|
|
44
|
+
*/
|
|
45
|
+
const emitFinal = (text) => {
|
|
46
|
+
if (text && !state.sawDeltaSinceBoundary) {
|
|
47
|
+
chunks.push(text);
|
|
48
|
+
}
|
|
49
|
+
state.sawDeltaSinceBoundary = false;
|
|
50
|
+
};
|
|
51
|
+
if (!parsed || typeof parsed !== "object") {
|
|
52
|
+
return chunks;
|
|
53
|
+
}
|
|
54
|
+
const record = /** @type {Record<string, unknown>} */ (parsed);
|
|
55
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
56
|
+
const upperType = type.toUpperCase();
|
|
57
|
+
const delta = /** @type {Record<string, unknown> | undefined} */ (
|
|
58
|
+
record.delta && typeof record.delta === "object" ? record.delta : undefined
|
|
59
|
+
);
|
|
60
|
+
if (type === "content_block_delta" && delta?.type === "text_delta") {
|
|
61
|
+
emitDelta(typeof delta.text === "string" ? delta.text : undefined);
|
|
62
|
+
}
|
|
63
|
+
if (type === "message_update") {
|
|
64
|
+
const assistantEvent = /** @type {Record<string, unknown> | undefined} */ (
|
|
65
|
+
record.assistantMessageEvent && typeof record.assistantMessageEvent === "object"
|
|
66
|
+
? record.assistantMessageEvent
|
|
67
|
+
: undefined
|
|
68
|
+
);
|
|
69
|
+
if (assistantEvent?.type === "text_delta" &&
|
|
70
|
+
typeof assistantEvent.delta === "string") {
|
|
71
|
+
emitDelta(assistantEvent.delta);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (/delta/i.test(type) && type !== "content_block_delta" && type !== "message_update") {
|
|
75
|
+
if (typeof record.delta === "string") {
|
|
76
|
+
emitDelta(record.delta);
|
|
77
|
+
}
|
|
78
|
+
else if (typeof delta?.text === "string") {
|
|
79
|
+
emitDelta(delta.text);
|
|
80
|
+
}
|
|
81
|
+
else if (typeof record.text === "string") {
|
|
82
|
+
emitDelta(record.text);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (type === "message" && record.role === "assistant") {
|
|
86
|
+
emitFinal(extractTextFromJsonValue(record.content ?? record.message ?? record));
|
|
87
|
+
}
|
|
88
|
+
if (upperType === "MESSAGE" && record.role === "assistant") {
|
|
89
|
+
if (record.delta === true && typeof record.content === "string") {
|
|
90
|
+
emitDelta(record.content);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
emitFinal(extractTextFromJsonValue(record.content ?? record.message ?? record));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (record.role === "assistant" && typeof record.content === "string") {
|
|
97
|
+
emitFinal(record.content);
|
|
98
|
+
}
|
|
99
|
+
const message = /** @type {Record<string, unknown> | undefined} */ (
|
|
100
|
+
record.message && typeof record.message === "object" ? record.message : undefined
|
|
101
|
+
);
|
|
102
|
+
if (type === "assistant" && message?.role === "assistant") {
|
|
103
|
+
emitFinal(extractTextFromJsonValue(message));
|
|
104
|
+
}
|
|
105
|
+
if (type === "result") {
|
|
106
|
+
emitFinal(extractTextFromJsonValue(record.result ?? record.response ?? record.output ?? record));
|
|
107
|
+
}
|
|
108
|
+
if (type === "turn_end" && message?.role === "assistant") {
|
|
109
|
+
emitFinal(extractTextFromJsonValue(message));
|
|
110
|
+
}
|
|
111
|
+
if (type === "message_end" && message?.role === "assistant") {
|
|
112
|
+
emitFinal(extractTextFromJsonValue(message));
|
|
113
|
+
}
|
|
114
|
+
if (type === "agent_end") {
|
|
115
|
+
emitFinal(extractTextFromJsonValue(extractLastAssistantMessage(record.messages)));
|
|
116
|
+
}
|
|
117
|
+
if (type === "message_stop" ||
|
|
118
|
+
type === "turn.completed" ||
|
|
119
|
+
type === "turn_end" ||
|
|
120
|
+
type === "message_end" ||
|
|
121
|
+
type === "agent_end" ||
|
|
122
|
+
type === "result") {
|
|
123
|
+
state.sawDeltaSinceBoundary = false;
|
|
124
|
+
}
|
|
125
|
+
return chunks;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @param {AgentStdoutTextEmitterOptions} options
|
|
129
|
+
* @returns {AgentStdoutTextEmitter}
|
|
130
|
+
*/
|
|
131
|
+
export function createAgentStdoutTextEmitter(options) {
|
|
132
|
+
const { outputFormat, onText } = options;
|
|
133
|
+
let buffer = "";
|
|
134
|
+
let emittedAnyText = false;
|
|
135
|
+
const state = { sawDeltaSinceBoundary: false };
|
|
136
|
+
/**
|
|
137
|
+
* @param {string | undefined} text
|
|
138
|
+
*/
|
|
139
|
+
const emitText = (text) => {
|
|
140
|
+
if (!onText || !text)
|
|
141
|
+
return;
|
|
142
|
+
emittedAnyText = true;
|
|
143
|
+
onText(text);
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* @param {string} line
|
|
147
|
+
*/
|
|
148
|
+
const processLine = (line) => {
|
|
149
|
+
const trimmed = line.trim();
|
|
150
|
+
if (!trimmed)
|
|
151
|
+
return;
|
|
152
|
+
let parsed;
|
|
153
|
+
try {
|
|
154
|
+
parsed = JSON.parse(trimmed);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
for (const chunk of extractCliStreamTextChunks(parsed, state)) {
|
|
160
|
+
emitText(chunk);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
/**
|
|
165
|
+
* @param {string} chunk
|
|
166
|
+
*/
|
|
167
|
+
push(chunk) {
|
|
168
|
+
if (!onText || !chunk)
|
|
169
|
+
return;
|
|
170
|
+
if (!outputFormat || outputFormat === "text") {
|
|
171
|
+
emitText(chunk);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
buffer += chunk;
|
|
175
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
176
|
+
while (newlineIndex >= 0) {
|
|
177
|
+
const line = buffer.slice(0, newlineIndex);
|
|
178
|
+
processLine(line);
|
|
179
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
180
|
+
newlineIndex = buffer.indexOf("\n");
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
/**
|
|
184
|
+
* @param {string} [finalText]
|
|
185
|
+
*/
|
|
186
|
+
flush(finalText) {
|
|
187
|
+
if (!onText)
|
|
188
|
+
return;
|
|
189
|
+
if (outputFormat && outputFormat !== "text" && buffer.trim()) {
|
|
190
|
+
processLine(buffer);
|
|
191
|
+
}
|
|
192
|
+
buffer = "";
|
|
193
|
+
if (!emittedAnyText && finalText) {
|
|
194
|
+
emitText(finalText);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { extractTextFromJsonValue } from "./extractTextFromJsonValue.js";
|
|
2
|
+
/** @typedef {import("ai").ModelMessage} ModelMessage */
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {{ prompt: string; systemFromMessages?: string; }} PromptParts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {unknown} content
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function contentToText(content) {
|
|
12
|
+
if (typeof content === "string")
|
|
13
|
+
return content;
|
|
14
|
+
if (Array.isArray(content)) {
|
|
15
|
+
return content
|
|
16
|
+
.map((part) => {
|
|
17
|
+
if (typeof part === "string")
|
|
18
|
+
return part;
|
|
19
|
+
if (part && typeof part === "object") {
|
|
20
|
+
const partRecord = /** @type {Record<string, unknown>} */ (part);
|
|
21
|
+
if (typeof partRecord.text === "string")
|
|
22
|
+
return partRecord.text;
|
|
23
|
+
if (typeof partRecord.content === "string")
|
|
24
|
+
return partRecord.content;
|
|
25
|
+
}
|
|
26
|
+
return "";
|
|
27
|
+
})
|
|
28
|
+
.join("");
|
|
29
|
+
}
|
|
30
|
+
if (content == null)
|
|
31
|
+
return "";
|
|
32
|
+
return String(content);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @param {ReadonlyArray<ModelMessage>} messages
|
|
36
|
+
* @returns {PromptParts}
|
|
37
|
+
*/
|
|
38
|
+
function messagesToPrompt(messages) {
|
|
39
|
+
/** @type {string[]} */
|
|
40
|
+
const systemParts = [];
|
|
41
|
+
/** @type {string[]} */
|
|
42
|
+
const promptParts = [];
|
|
43
|
+
for (const msg of messages) {
|
|
44
|
+
const text = contentToText(msg.content);
|
|
45
|
+
if (!text)
|
|
46
|
+
continue;
|
|
47
|
+
const role = msg.role;
|
|
48
|
+
if (role === "system") {
|
|
49
|
+
systemParts.push(text);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (role) {
|
|
53
|
+
promptParts.push(`${String(role).toUpperCase()}: ${text}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
promptParts.push(text);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
prompt: promptParts.join("\n\n"),
|
|
61
|
+
systemFromMessages: systemParts.length
|
|
62
|
+
? systemParts.join("\n\n")
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @param {unknown} options
|
|
68
|
+
* @returns {PromptParts}
|
|
69
|
+
*/
|
|
70
|
+
export function extractPrompt(options) {
|
|
71
|
+
if (!options || typeof options !== "object")
|
|
72
|
+
return { prompt: "" };
|
|
73
|
+
const opts = /** @type {Record<string, unknown>} */ (options);
|
|
74
|
+
if ("prompt" in opts) {
|
|
75
|
+
const promptInput = opts.prompt;
|
|
76
|
+
if (typeof promptInput === "string") {
|
|
77
|
+
return { prompt: promptInput };
|
|
78
|
+
}
|
|
79
|
+
if (Array.isArray(promptInput)) {
|
|
80
|
+
return messagesToPrompt(/** @type {ModelMessage[]} */ (promptInput));
|
|
81
|
+
}
|
|
82
|
+
return { prompt: "" };
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(opts.messages)) {
|
|
85
|
+
return messagesToPrompt(/** @type {ModelMessage[]} */ (opts.messages));
|
|
86
|
+
}
|
|
87
|
+
return { prompt: "" };
|
|
88
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {unknown} value
|
|
3
|
+
* @returns {string | undefined}
|
|
4
|
+
*/
|
|
5
|
+
export function extractTextFromJsonValue(value) {
|
|
6
|
+
if (typeof value === "string")
|
|
7
|
+
return value;
|
|
8
|
+
if (!value || typeof value !== "object")
|
|
9
|
+
return undefined;
|
|
10
|
+
const record = /** @type {Record<string, unknown>} */ (value);
|
|
11
|
+
if (typeof record.text === "string")
|
|
12
|
+
return record.text;
|
|
13
|
+
if (typeof record.content === "string")
|
|
14
|
+
return record.content;
|
|
15
|
+
if (Array.isArray(record.content)) {
|
|
16
|
+
const parts = record.content
|
|
17
|
+
.map((part) => {
|
|
18
|
+
if (!part)
|
|
19
|
+
return "";
|
|
20
|
+
if (typeof part === "string")
|
|
21
|
+
return part;
|
|
22
|
+
if (typeof part !== "object")
|
|
23
|
+
return "";
|
|
24
|
+
const partRecord = /** @type {Record<string, unknown>} */ (part);
|
|
25
|
+
if (typeof partRecord.text === "string")
|
|
26
|
+
return partRecord.text;
|
|
27
|
+
if (typeof partRecord.content === "string")
|
|
28
|
+
return partRecord.content;
|
|
29
|
+
return "";
|
|
30
|
+
})
|
|
31
|
+
.join("");
|
|
32
|
+
if (parts.trim())
|
|
33
|
+
return parts;
|
|
34
|
+
}
|
|
35
|
+
if (record.response)
|
|
36
|
+
return extractTextFromJsonValue(record.response);
|
|
37
|
+
if (record.message)
|
|
38
|
+
return extractTextFromJsonValue(record.message);
|
|
39
|
+
if (record.result)
|
|
40
|
+
return extractTextFromJsonValue(record.result);
|
|
41
|
+
if (record.output)
|
|
42
|
+
return extractTextFromJsonValue(record.output);
|
|
43
|
+
if (record.data)
|
|
44
|
+
return extractTextFromJsonValue(record.data);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @smithers-type-exports-begin
|
|
2
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliActionEvent} AgentCliActionEvent */
|
|
3
|
+
/** @typedef {import("./AgentCliActionKind.ts").AgentCliActionKind} AgentCliActionKind */
|
|
4
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliActionPhase} AgentCliActionPhase */
|
|
5
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliCompletedEvent} AgentCliCompletedEvent */
|
|
6
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliEvent} AgentCliEvent */
|
|
7
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliEventLevel} AgentCliEventLevel */
|
|
8
|
+
/** @typedef {import("./AgentCliEvent.ts").AgentCliStartedEvent} AgentCliStartedEvent */
|
|
9
|
+
/** @typedef {import("./BaseCliAgentOptions.ts").BaseCliAgentOptions} BaseCliAgentOptions */
|
|
10
|
+
/** @typedef {import("./CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
|
|
11
|
+
/** @typedef {import("./CliUsageInfo.ts").CliUsageInfo} CliUsageInfo */
|
|
12
|
+
/** @typedef {import("./CodexConfigOverrides.ts").CodexConfigOverrides} CodexConfigOverrides */
|
|
13
|
+
/** @typedef {import("./PiExtensionUiRequest.ts").PiExtensionUiRequest} PiExtensionUiRequest */
|
|
14
|
+
/** @typedef {import("./PiExtensionUiResponse.ts").PiExtensionUiResponse} PiExtensionUiResponse */
|
|
15
|
+
/** @typedef {import("./RunCommandResult.ts").RunCommandResult} RunCommandResult */
|
|
16
|
+
// @smithers-type-exports-end
|
|
17
|
+
|
|
18
|
+
export { resolveTimeouts } from "./resolveTimeouts.js";
|
|
19
|
+
export { combineNonEmpty } from "./combineNonEmpty.js";
|
|
20
|
+
export { extractPrompt } from "./extractPrompt.js";
|
|
21
|
+
export { tryParseJson } from "./tryParseJson.js";
|
|
22
|
+
export { extractTextFromJsonValue } from "./extractTextFromJsonValue.js";
|
|
23
|
+
export { createAgentStdoutTextEmitter } from "./createAgentStdoutTextEmitter.js";
|
|
24
|
+
export { truncateToBytes } from "./truncateToBytes.js";
|
|
25
|
+
export { buildGenerateResult } from "./buildGenerateResult.js";
|
|
26
|
+
export { runCommandEffect } from "./runCommandEffect.js";
|
|
27
|
+
export { runRpcCommandEffect } from "./runRpcCommandEffect.js";
|
|
28
|
+
export { pushFlag } from "./pushFlag.js";
|
|
29
|
+
export { pushList } from "./pushList.js";
|
|
30
|
+
export { normalizeCodexConfig } from "./normalizeCodexConfig.js";
|
|
31
|
+
export { BaseCliAgent, extractUsageFromOutput, runAgentPromise } from "./BaseCliAgent.js";
|
|
32
|
+
export { isRecord, asString, asNumber, truncate, toolKindFromName, isLikelyRuntimeMetadata, shouldSurfaceUnparsedStdout, createSyntheticIdGenerator, } from "./parseHelpers.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
/** @typedef {import("./CodexConfigOverrides.ts").CodexConfigOverrides} CodexConfigOverrides */
|
|
3
|
+
/**
|
|
4
|
+
* @param {CodexConfigOverrides} [config]
|
|
5
|
+
* @returns {string[]}
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeCodexConfig(config) {
|
|
8
|
+
if (!config)
|
|
9
|
+
return [];
|
|
10
|
+
if (Array.isArray(config))
|
|
11
|
+
return config.map(String);
|
|
12
|
+
const entries = Object.entries(config);
|
|
13
|
+
return entries.map(([key, value]) => {
|
|
14
|
+
if (value === null)
|
|
15
|
+
return `${key}=null`;
|
|
16
|
+
if (typeof value === "string")
|
|
17
|
+
return `${key}=${value}`;
|
|
18
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
19
|
+
return `${key}=${value}`;
|
|
20
|
+
return `${key}=${JSON.stringify(value)}`;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
/** @typedef {import("./AgentCliActionKind.ts").AgentCliActionKind} AgentCliActionKind */
|
|
3
|
+
/**
|
|
4
|
+
* @param {unknown} value
|
|
5
|
+
* @returns {value is Record<string, unknown>}
|
|
6
|
+
*/
|
|
7
|
+
export function isRecord(value) {
|
|
8
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @param {unknown} value
|
|
12
|
+
* @returns {string | undefined}
|
|
13
|
+
*/
|
|
14
|
+
export function asString(value) {
|
|
15
|
+
return typeof value === "string" ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @param {unknown} value
|
|
19
|
+
* @returns {number | undefined}
|
|
20
|
+
*/
|
|
21
|
+
export function asNumber(value) {
|
|
22
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} value
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export function truncate(value, maxLength = 240) {
|
|
29
|
+
if (value.length <= maxLength) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
return `${value.slice(0, maxLength - 1)}…`;
|
|
33
|
+
}
|
|
34
|
+
const TOOL_KIND_KEYWORDS = [
|
|
35
|
+
[["bash", "shell", "command"], "command"],
|
|
36
|
+
[["search", "web"], "web_search"],
|
|
37
|
+
[["todo", "plan"], "todo_list"],
|
|
38
|
+
[["write", "edit", "file"], "file_change"],
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* @param {string | undefined} name
|
|
42
|
+
* @param {ReadonlyArray<readonly [string[], AgentCliActionKind]>} [extraRules]
|
|
43
|
+
* @returns {AgentCliActionKind}
|
|
44
|
+
*/
|
|
45
|
+
export function toolKindFromName(name, extraRules) {
|
|
46
|
+
const normalized = (name ?? "").toLowerCase();
|
|
47
|
+
if (!normalized)
|
|
48
|
+
return "tool";
|
|
49
|
+
const rules = extraRules
|
|
50
|
+
? [...TOOL_KIND_KEYWORDS, ...extraRules]
|
|
51
|
+
: TOOL_KIND_KEYWORDS;
|
|
52
|
+
for (const [keywords, kind] of rules) {
|
|
53
|
+
for (const keyword of keywords) {
|
|
54
|
+
if (normalized.includes(keyword)) {
|
|
55
|
+
return kind;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return "tool";
|
|
60
|
+
}
|
|
61
|
+
const RUNTIME_METADATA_MARKERS = [
|
|
62
|
+
"\"mcp_servers\"",
|
|
63
|
+
"\"slash_commands\"",
|
|
64
|
+
"\"permissionmode\"",
|
|
65
|
+
"\"claude_code_version\"",
|
|
66
|
+
"\"apikeysource\"",
|
|
67
|
+
"\"plugins\"",
|
|
68
|
+
"\"skills\"",
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} value
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
export function isLikelyRuntimeMetadata(value) {
|
|
75
|
+
const lower = value.toLowerCase();
|
|
76
|
+
let matchCount = 0;
|
|
77
|
+
for (const marker of RUNTIME_METADATA_MARKERS) {
|
|
78
|
+
if (lower.includes(marker)) {
|
|
79
|
+
matchCount += 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return matchCount >= 3;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} line
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
export function shouldSurfaceUnparsedStdout(line) {
|
|
89
|
+
if (isLikelyRuntimeMetadata(line)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (line.length > 220) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const lower = line.toLowerCase();
|
|
96
|
+
return (lower.includes("error") ||
|
|
97
|
+
lower.includes("failed") ||
|
|
98
|
+
lower.includes("denied") ||
|
|
99
|
+
lower.includes("exception") ||
|
|
100
|
+
lower.includes("timeout"));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @returns {(prefix: string) => string}
|
|
104
|
+
*/
|
|
105
|
+
export function createSyntheticIdGenerator() {
|
|
106
|
+
let counter = 0;
|
|
107
|
+
return (prefix) => {
|
|
108
|
+
counter += 1;
|
|
109
|
+
return `${prefix}-${counter}`;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {string[]} args
|
|
3
|
+
* @param {string} flag
|
|
4
|
+
* @param {string | number | boolean} [value]
|
|
5
|
+
*/
|
|
6
|
+
export function pushFlag(args, flag, value) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return;
|
|
9
|
+
if (value === true) {
|
|
10
|
+
args.push(flag);
|
|
11
|
+
}
|
|
12
|
+
else if (value === false) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
args.push(flag, String(value));
|
|
17
|
+
}
|
|
18
|
+
}
|