@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,260 @@
1
+ import { mkdtempSync, cpSync, existsSync, rmSync } from "node:fs";
2
+ import { randomUUID } from "node:crypto";
3
+ import { join } from "node:path";
4
+ import { tmpdir, homedir } from "node:os";
5
+ import { BaseCliAgent, pushFlag, pushList, isRecord, asString, toolKindFromName, createSyntheticIdGenerator, } from "./BaseCliAgent/index.js";
6
+ import { normalizeCapabilityStringList, } from "./capability-registry/index.js";
7
+ /** @typedef {import("./BaseCliAgent/BaseCliAgentOptions.ts").BaseCliAgentOptions} BaseCliAgentOptions */
8
+ /** @typedef {import("./capability-registry/AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
9
+ /** @typedef {import("./BaseCliAgent/CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
10
+ /** @typedef {import("./KimiAgentOptions.ts").KimiAgentOptions} KimiAgentOptions */
11
+
12
+ function resolveKimiBuiltIns() {
13
+ return ["default"];
14
+ }
15
+ /**
16
+ * @param {KimiAgentOptions} [opts]
17
+ * @returns {AgentCapabilityRegistry}
18
+ */
19
+ export function createKimiCapabilityRegistry(opts = {}) {
20
+ return {
21
+ version: 1,
22
+ engine: "kimi",
23
+ runtimeTools: {},
24
+ mcp: {
25
+ bootstrap: "project-config",
26
+ supportsProjectScope: true,
27
+ supportsUserScope: true,
28
+ },
29
+ skills: {
30
+ supportsSkills: true,
31
+ installMode: "dir",
32
+ smithersSkillIds: normalizeCapabilityStringList(opts.skillsDir ? [`dir:${opts.skillsDir}`] : []),
33
+ },
34
+ humanInteraction: {
35
+ supportsUiRequests: false,
36
+ methods: [],
37
+ },
38
+ builtIns: resolveKimiBuiltIns(),
39
+ };
40
+ }
41
+ export class KimiAgent extends BaseCliAgent {
42
+ opts;
43
+ capabilities;
44
+ cliEngine = "kimi";
45
+ issuedSessionId;
46
+ /**
47
+ * @param {KimiAgentOptions} [opts]
48
+ */
49
+ constructor(opts = {}) {
50
+ super(opts);
51
+ this.opts = opts;
52
+ this.capabilities = createKimiCapabilityRegistry(opts);
53
+ }
54
+ /**
55
+ * @returns {CliOutputInterpreter}
56
+ */
57
+ createOutputInterpreter() {
58
+ let emittedStarted = false;
59
+ let didEmitCompleted = false;
60
+ let finalAnswer = "";
61
+ const nextSyntheticId = createSyntheticIdGenerator();
62
+ /**
63
+ * @param {string} line
64
+ * @returns {AgentCliEvent[]}
65
+ */
66
+ const parseLine = (line) => {
67
+ const trimmed = line.trim();
68
+ if (!trimmed)
69
+ return [];
70
+ let payload;
71
+ try {
72
+ payload = JSON.parse(trimmed);
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ if (!isRecord(payload))
78
+ return [];
79
+ const role = asString(payload.role);
80
+ const events = [];
81
+ if (!emittedStarted) {
82
+ emittedStarted = true;
83
+ events.push({
84
+ type: "started",
85
+ engine: this.cliEngine,
86
+ title: "Kimi",
87
+ resume: this.issuedSessionId,
88
+ });
89
+ }
90
+ if (role === "assistant") {
91
+ const content = asString(payload.content);
92
+ if (content) {
93
+ finalAnswer = content;
94
+ }
95
+ const toolCalls = Array.isArray(payload.tool_calls) ? payload.tool_calls : [];
96
+ for (const toolCall of toolCalls) {
97
+ if (!isRecord(toolCall))
98
+ continue;
99
+ const fn = isRecord(toolCall.function) ? toolCall.function : undefined;
100
+ const name = asString(fn?.name) ?? "tool";
101
+ const id = asString(toolCall.id) ?? nextSyntheticId("kimi-tool");
102
+ events.push({
103
+ type: "action",
104
+ engine: this.cliEngine,
105
+ phase: "started",
106
+ entryType: "thought",
107
+ action: {
108
+ id,
109
+ kind: toolKindFromName(name),
110
+ title: name,
111
+ detail: {
112
+ arguments: asString(fn?.arguments),
113
+ },
114
+ },
115
+ message: `Running ${name}`,
116
+ level: "info",
117
+ });
118
+ }
119
+ }
120
+ if (role === "tool") {
121
+ const id = asString(payload.tool_call_id) ?? nextSyntheticId("kimi-tool");
122
+ events.push({
123
+ type: "action",
124
+ engine: this.cliEngine,
125
+ phase: "completed",
126
+ entryType: "thought",
127
+ action: {
128
+ id,
129
+ kind: "tool",
130
+ title: "tool result",
131
+ detail: {},
132
+ },
133
+ message: asString(payload.content),
134
+ ok: true,
135
+ level: "info",
136
+ });
137
+ }
138
+ return events;
139
+ };
140
+ return {
141
+ onStdoutLine: parseLine,
142
+ onExit: (result) => {
143
+ if (didEmitCompleted)
144
+ return [];
145
+ didEmitCompleted = true;
146
+ return [{
147
+ type: "completed",
148
+ engine: this.cliEngine,
149
+ ok: !result.exitCode || result.exitCode === 0,
150
+ answer: finalAnswer || undefined,
151
+ error: result.exitCode && result.exitCode !== 0
152
+ ? result.stderr.trim() || `Kimi exited with code ${result.exitCode}`
153
+ : undefined,
154
+ resume: this.issuedSessionId,
155
+ }];
156
+ },
157
+ };
158
+ }
159
+ /**
160
+ * @param {{ prompt: string; systemPrompt?: string; cwd: string; options: any; }} params
161
+ */
162
+ async buildCommand(params) {
163
+ const args = [];
164
+ let commandEnv;
165
+ let cleanup;
166
+ // Isolate kimi metadata per invocation to avoid concurrent writes to
167
+ // ~/.kimi/kimi.json across parallel tasks. If caller explicitly provides
168
+ // KIMI_SHARE_DIR in opts.env, preserve that override.
169
+ if (!this.opts.env?.KIMI_SHARE_DIR) {
170
+ const defaultShareDir = process.env.KIMI_SHARE_DIR ?? join(homedir(), ".kimi");
171
+ const isolatedShareDir = mkdtempSync(join(tmpdir(), "kimi-share-"));
172
+ if (existsSync(defaultShareDir)) {
173
+ for (const name of ["config.toml", "credentials", "device_id", "latest_version.txt"]) {
174
+ const src = join(defaultShareDir, name);
175
+ if (existsSync(src)) {
176
+ try {
177
+ cpSync(src, join(isolatedShareDir, name), { recursive: true });
178
+ }
179
+ catch {
180
+ // Best-effort seed only; missing copy should not prevent execution.
181
+ }
182
+ }
183
+ }
184
+ }
185
+ commandEnv = { KIMI_SHARE_DIR: isolatedShareDir };
186
+ cleanup = async () => {
187
+ rmSync(isolatedShareDir, { recursive: true, force: true });
188
+ };
189
+ }
190
+ // Print mode is required for non-interactive execution
191
+ // Note: --print implicitly adds --yolo
192
+ args.push("--print");
193
+ // Output format — use text with --final-message-only to get only the
194
+ // model's final response without tool call outputs mixed in.
195
+ const outputFormat = this.opts.outputFormat ??
196
+ (params.options?.onEvent ? "stream-json" : "text");
197
+ pushFlag(args, "--output-format", outputFormat);
198
+ // When using text format, --final-message-only ensures we only get
199
+ // the model's final response, not intermediate tool output.
200
+ const finalMessageOnly = this.opts.finalMessageOnly ?? (outputFormat === "text");
201
+ if (finalMessageOnly)
202
+ args.push("--final-message-only");
203
+ // Other flags
204
+ const resumeSession = typeof params.options?.resumeSession === "string"
205
+ ? params.options.resumeSession
206
+ : undefined;
207
+ const sessionId = resumeSession ?? this.opts.session ?? randomUUID();
208
+ this.issuedSessionId = sessionId;
209
+ pushFlag(args, "--work-dir", this.opts.workDir ?? params.cwd);
210
+ pushFlag(args, "--session", sessionId);
211
+ if (this.opts.continue)
212
+ args.push("--continue");
213
+ pushFlag(args, "--model", this.opts.model ?? this.model);
214
+ const thinking = this.opts.thinking ?? true;
215
+ args.push(thinking ? "--thinking" : "--no-thinking");
216
+ if (this.opts.quiet)
217
+ args.push("--quiet");
218
+ pushFlag(args, "--agent", this.opts.agent);
219
+ pushFlag(args, "--agent-file", this.opts.agentFile);
220
+ pushList(args, "--mcp-config-file", this.opts.mcpConfigFile);
221
+ pushList(args, "--mcp-config", this.opts.mcpConfig);
222
+ pushFlag(args, "--skills-dir", this.opts.skillsDir);
223
+ pushFlag(args, "--max-steps-per-turn", this.opts.maxStepsPerTurn);
224
+ pushFlag(args, "--max-retries-per-step", this.opts.maxRetriesPerStep);
225
+ pushFlag(args, "--max-ralph-iterations", this.opts.maxRalphIterations);
226
+ if (this.opts.verbose)
227
+ args.push("--verbose");
228
+ if (this.opts.debug)
229
+ args.push("--debug");
230
+ if (this.extraArgs?.length)
231
+ args.push(...this.extraArgs);
232
+ // Build prompt with system prompt prepended
233
+ const systemPrefix = params.systemPrompt
234
+ ? `${params.systemPrompt}\n\n`
235
+ : "";
236
+ const jsonReminder = params.prompt?.includes("REQUIRED OUTPUT")
237
+ ? "\n\nREMINDER: Your response MUST end with a ```json code fence containing the required JSON object. Do NOT skip this step — the pipeline will reject your response without it.\n"
238
+ : "";
239
+ const fullPrompt = `${systemPrefix}${params.prompt ?? ""}${jsonReminder}`;
240
+ // Pass prompt via --prompt flag
241
+ pushFlag(args, "--prompt", fullPrompt);
242
+ return {
243
+ command: "kimi",
244
+ args,
245
+ outputFormat,
246
+ env: commandEnv,
247
+ cleanup,
248
+ stdoutBannerPatterns: [/^YOLO mode is enabled\b[^\n]*/gm],
249
+ stdoutErrorPatterns: [
250
+ /^LLM not set/i,
251
+ /^LLM not supported/i,
252
+ /^Max steps reached/i,
253
+ /^Interrupted by user$/i,
254
+ /^Unknown error:/i,
255
+ /^Error:/i,
256
+ ],
257
+ errorOnBannerOnly: true,
258
+ };
259
+ }
260
+ }
@@ -0,0 +1,21 @@
1
+ import type { BaseCliAgentOptions } from "./BaseCliAgent/BaseCliAgentOptions";
2
+
3
+ export type KimiAgentOptions = BaseCliAgentOptions & {
4
+ workDir?: string;
5
+ session?: string;
6
+ continue?: boolean;
7
+ thinking?: boolean;
8
+ outputFormat?: "text" | "stream-json";
9
+ finalMessageOnly?: boolean;
10
+ quiet?: boolean;
11
+ agent?: "default" | "okabe";
12
+ agentFile?: string;
13
+ mcpConfigFile?: string[];
14
+ mcpConfig?: string[];
15
+ skillsDir?: string;
16
+ maxStepsPerTurn?: number;
17
+ maxRetriesPerStep?: number;
18
+ maxRalphIterations?: number;
19
+ verbose?: boolean;
20
+ debug?: boolean;
21
+ };
@@ -0,0 +1,54 @@
1
+ import { openai } from "@ai-sdk/openai";
2
+ import { ToolLoopAgent, } from "ai";
3
+ import { resolveSdkModel } from "./resolveSdkModel.js";
4
+ import { streamResultToGenerateResult } from "./streamResultToGenerateResult.js";
5
+ /** @typedef {import("ai").AgentCallParameters} AgentCallParameters */
6
+
7
+ /**
8
+ * @template CALL_OPTIONS, TOOLS
9
+ * @typedef {AgentCallParameters<CALL_OPTIONS, TOOLS> & { onStdout?: (text: string) => void; onStderr?: (text: string) => void; onEvent?: (event: unknown) => Promise<void> | void; outputSchema?: import("zod").ZodTypeAny; resumeSession?: string; }} ExtendedGenerateArgs
10
+ */
11
+ /** @typedef {import("ai").GenerateTextResult} GenerateTextResult */
12
+ /**
13
+ * @template [CALL_OPTIONS=never], [TOOLS=import("ai").ToolSet]
14
+ * @typedef {import("./OpenAIAgentOptions.ts").OpenAIAgentOptions<CALL_OPTIONS, TOOLS>} OpenAIAgentOptions
15
+ */
16
+
17
+ export class OpenAIAgent extends ToolLoopAgent {
18
+ hijackEngine = "openai-sdk";
19
+ /**
20
+ * @param {OpenAIAgentOptions<CALL_OPTIONS, TOOLS>} opts
21
+ */
22
+ constructor(opts) {
23
+ const { model, ...rest } = opts;
24
+ super({
25
+ ...rest,
26
+ model: resolveSdkModel(model, openai),
27
+ });
28
+ }
29
+ /**
30
+ * @param {ExtendedGenerateArgs<CALL_OPTIONS, TOOLS>} args
31
+ * @returns {Promise<GenerateTextResult<TOOLS, never>>}
32
+ */
33
+ generate(args) {
34
+ const promptArgs = "messages" in args
35
+ ? { messages: args.messages }
36
+ : { prompt: args.prompt };
37
+ if (!args.onStdout) {
38
+ return super.generate({
39
+ options: args.options,
40
+ abortSignal: args.abortSignal,
41
+ ...promptArgs,
42
+ timeout: args.timeout,
43
+ onStepFinish: args.onStepFinish,
44
+ });
45
+ }
46
+ return super.stream({
47
+ options: args.options,
48
+ abortSignal: args.abortSignal,
49
+ ...promptArgs,
50
+ timeout: args.timeout,
51
+ onStepFinish: args.onStepFinish,
52
+ }).then((stream) => streamResultToGenerateResult(stream, args.onStdout));
53
+ }
54
+ }
@@ -0,0 +1,8 @@
1
+ import type { openai } from "@ai-sdk/openai";
2
+ import type { ToolSet } from "ai";
3
+ import type { SdkAgentOptions } from "./SdkAgentOptions";
4
+
5
+ export type OpenAIAgentOptions<
6
+ CALL_OPTIONS = never,
7
+ TOOLS extends ToolSet = {},
8
+ > = SdkAgentOptions<CALL_OPTIONS, TOOLS, ReturnType<typeof openai>>;