@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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +65 -0
  3. package/src/AgentLike.ts +28 -0
  4. package/src/AmpAgent.js +232 -0
  5. package/src/AmpAgentOptions.ts +26 -0
  6. package/src/AnthropicAgent.js +54 -0
  7. package/src/AnthropicAgentOptions.ts +8 -0
  8. package/src/BaseCliAgent/AgentCliActionKind.ts +10 -0
  9. package/src/BaseCliAgent/AgentCliEvent.ts +44 -0
  10. package/src/BaseCliAgent/BaseCliAgent.js +874 -0
  11. package/src/BaseCliAgent/BaseCliAgentOptions.ts +13 -0
  12. package/src/BaseCliAgent/CliOutputInterpreter.ts +8 -0
  13. package/src/BaseCliAgent/CliUsageInfo.ts +7 -0
  14. package/src/BaseCliAgent/CodexConfigOverrides.ts +3 -0
  15. package/src/BaseCliAgent/PiExtensionUiRequest.ts +10 -0
  16. package/src/BaseCliAgent/PiExtensionUiResponse.ts +7 -0
  17. package/src/BaseCliAgent/RunCommandResult.ts +5 -0
  18. package/src/BaseCliAgent/buildGenerateResult.js +57 -0
  19. package/src/BaseCliAgent/combineNonEmpty.js +8 -0
  20. package/src/BaseCliAgent/createAgentStdoutTextEmitter.js +198 -0
  21. package/src/BaseCliAgent/extractPrompt.js +88 -0
  22. package/src/BaseCliAgent/extractTextFromJsonValue.js +46 -0
  23. package/src/BaseCliAgent/index.js +32 -0
  24. package/src/BaseCliAgent/normalizeCodexConfig.js +22 -0
  25. package/src/BaseCliAgent/parseHelpers.js +111 -0
  26. package/src/BaseCliAgent/pushFlag.js +18 -0
  27. package/src/BaseCliAgent/pushList.js +10 -0
  28. package/src/BaseCliAgent/resolveTimeouts.js +24 -0
  29. package/src/BaseCliAgent/runCommandEffect.js +32 -0
  30. package/src/BaseCliAgent/runRpcCommandEffect.js +365 -0
  31. package/src/BaseCliAgent/truncateToBytes.js +13 -0
  32. package/src/BaseCliAgent/tryParseJson.js +18 -0
  33. package/src/ClaudeCodeAgent.js +455 -0
  34. package/src/ClaudeCodeAgentOptions.ts +52 -0
  35. package/src/CodexAgent.js +593 -0
  36. package/src/CodexAgentOptions.ts +23 -0
  37. package/src/ForgeAgent.js +128 -0
  38. package/src/ForgeAgentOptions.ts +14 -0
  39. package/src/GeminiAgent.js +273 -0
  40. package/src/GeminiAgentOptions.ts +20 -0
  41. package/src/KimiAgent.js +260 -0
  42. package/src/KimiAgentOptions.ts +21 -0
  43. package/src/OpenAIAgent.js +54 -0
  44. package/src/OpenAIAgentOptions.ts +8 -0
  45. package/src/PiAgent.js +468 -0
  46. package/src/PiAgentOptions.ts +40 -0
  47. package/src/SdkAgentOptions.ts +16 -0
  48. package/src/agent-contract/SmithersAgentContract.ts +10 -0
  49. package/src/agent-contract/SmithersAgentContractTool.ts +8 -0
  50. package/src/agent-contract/SmithersAgentToolCategory.ts +6 -0
  51. package/src/agent-contract/SmithersListedTool.ts +4 -0
  52. package/src/agent-contract/SmithersToolSurface.ts +1 -0
  53. package/src/agent-contract/createSmithersAgentContract.js +188 -0
  54. package/src/agent-contract/index.js +10 -0
  55. package/src/agent-contract/renderSmithersAgentPromptGuidance.js +81 -0
  56. package/src/capability-registry/AgentCapabilityRegistry.ts +22 -0
  57. package/src/capability-registry/AgentToolDescriptor.ts +4 -0
  58. package/src/capability-registry/hashCapabilityRegistry.js +43 -0
  59. package/src/capability-registry/index.js +8 -0
  60. package/src/capability-registry/normalizeCapabilityRegistry.js +52 -0
  61. package/src/capability-registry/normalizeCapabilityStringList.js +9 -0
  62. package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +6 -0
  63. package/src/cli-capabilities/CliAgentCapabilityDoctorReport.ts +18 -0
  64. package/src/cli-capabilities/CliAgentCapabilityReportEntry.ts +9 -0
  65. package/src/cli-capabilities/formatCliAgentCapabilityDoctorReport.js +24 -0
  66. package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +92 -0
  67. package/src/cli-capabilities/getCliAgentCapabilityReport.js +52 -0
  68. package/src/cli-capabilities/index.js +11 -0
  69. package/src/diagnostics/DiagnosticCheck.ts +11 -0
  70. package/src/diagnostics/DiagnosticCheckId.ts +4 -0
  71. package/src/diagnostics/DiagnosticContext.ts +4 -0
  72. package/src/diagnostics/DiagnosticReport.ts +9 -0
  73. package/src/diagnostics/enrichReportWithErrorAnalysis.js +34 -0
  74. package/src/diagnostics/formatDiagnosticSummary.js +17 -0
  75. package/src/diagnostics/getDiagnosticStrategy.js +503 -0
  76. package/src/diagnostics/index.js +13 -0
  77. package/src/diagnostics/launchDiagnostics.js +16 -0
  78. package/src/diagnostics/runDiagnostics.js +52 -0
  79. package/src/index.d.ts +872 -0
  80. package/src/index.js +39 -0
  81. package/src/resolveSdkModel.js +9 -0
  82. package/src/sanitizeForOpenAI.js +47 -0
  83. package/src/streamResultToGenerateResult.js +70 -0
  84. 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,7 @@
1
+ export type CliUsageInfo = {
2
+ inputTokens?: number;
3
+ outputTokens?: number;
4
+ cacheReadTokens?: number;
5
+ cacheWriteTokens?: number;
6
+ reasoningTokens?: number;
7
+ };
@@ -0,0 +1,3 @@
1
+ export type CodexConfigOverrides =
2
+ | Record<string, string | number | boolean | object | null>
3
+ | string[];
@@ -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,7 @@
1
+ export type PiExtensionUiResponse = {
2
+ type: "extension_ui_response";
3
+ id: string;
4
+ value?: string;
5
+ cancelled?: boolean;
6
+ [key: string]: unknown;
7
+ };
@@ -0,0 +1,5 @@
1
+ export type RunCommandResult = {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number | null;
5
+ };
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @param {string[]} args
3
+ * @param {string} flag
4
+ * @param {string[]} [values]
5
+ */
6
+ export function pushList(args, flag, values) {
7
+ if (!values || values.length === 0)
8
+ return;
9
+ args.push(flag, ...values.map(String));
10
+ }