@smithers-orchestrator/agents 0.22.0 → 0.24.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 (34) hide show
  1. package/package.json +10 -4
  2. package/src/AmpAgent.js +26 -19
  3. package/src/AntigravityAgent.js +53 -18
  4. package/src/AntigravityAgentOptions.ts +45 -4
  5. package/src/BaseCliAgent/AgentGenerateOptions.ts +12 -0
  6. package/src/BaseCliAgent/BaseCliAgent.js +78 -8
  7. package/src/BaseCliAgent/RunCommandResult.ts +4 -0
  8. package/src/BaseCliAgent/runCommandEffect.js +3 -2
  9. package/src/BaseCliAgent/taskContextEnv.js +31 -0
  10. package/src/ClaudeCodeAgent.js +19 -1
  11. package/src/ForgeAgent.js +26 -19
  12. package/src/HermesAgent.js +1 -1
  13. package/src/VibeAgent.js +214 -0
  14. package/src/VibeAgentOptions.ts +11 -0
  15. package/src/agent-contract/createSmithersAgentContract.js +1 -0
  16. package/src/agent-contract/renderSmithersAgentPromptGuidance.js +4 -0
  17. package/src/capability-registry/AgentCapabilityRegistry.ts +1 -1
  18. package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +4 -1
  19. package/src/cli-capabilities/CliAgentCapabilityReportEntry.ts +2 -0
  20. package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +48 -1
  21. package/src/cli-capabilities/getCliAgentCapabilityReport.js +24 -0
  22. package/src/cli-capabilities/index.js +5 -0
  23. package/src/cli-surface/CliAgentSurfaceTypes.ts +34 -0
  24. package/src/cli-surface/cliAgentSurfaceManifest.js +490 -0
  25. package/src/cli-surface/index.js +5 -0
  26. package/src/diagnostics/getDiagnosticStrategy.js +1 -1
  27. package/src/index.d.ts +707 -386
  28. package/src/index.js +21 -0
  29. package/src/mcp/McpServerConfig.ts +19 -0
  30. package/src/mcp/McpToolset.ts +17 -0
  31. package/src/mcp/McpToolsetOptions.ts +16 -0
  32. package/src/mcp/createMcpToolset.d.ts +12 -0
  33. package/src/mcp/createMcpToolset.js +83 -0
  34. package/src/sanitizeForOpenAI.js +20 -17
@@ -0,0 +1,214 @@
1
+ import {
2
+ BaseCliAgent,
3
+ pushFlag,
4
+ isRecord,
5
+ asString,
6
+ createSyntheticIdGenerator,
7
+ } from "./BaseCliAgent/index.js";
8
+
9
+ /** @typedef {import("./BaseCliAgent/index.ts").BaseCliAgentOptions} BaseCliAgentOptions */
10
+ /** @typedef {import("./capability-registry/index.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
11
+ /** @typedef {import("./BaseCliAgent/index.ts").CliOutputInterpreter} CliOutputInterpreter */
12
+
13
+ /**
14
+ * @param {VibeAgentOptions} [opts]
15
+ * @returns {AgentCapabilityRegistry}
16
+ */
17
+ export function createVibeCapabilityRegistry(opts = {}) {
18
+ return {
19
+ version: 1,
20
+ engine: "vibe",
21
+ runtimeTools: {},
22
+ mcp: {
23
+ bootstrap: "project-config",
24
+ supportsProjectScope: true,
25
+ supportsUserScope: true,
26
+ },
27
+ skills: {
28
+ supportsSkills: true,
29
+ installMode: "plugin",
30
+ smithersSkillIds: [],
31
+ },
32
+ humanInteraction: {
33
+ supportsUiRequests: false,
34
+ methods: [],
35
+ },
36
+ builtIns: ["default"],
37
+ };
38
+ }
39
+
40
+ /**
41
+ * @typedef {BaseCliAgentOptions & {
42
+ * agent?: string;
43
+ * maxTurns?: number;
44
+ * maxPrice?: number;
45
+ * maxTokens?: number;
46
+ * enabledTools?: string[];
47
+ * sessionId?: string;
48
+ * continueSession?: boolean;
49
+ * }} VibeAgentOptions
50
+ */
51
+
52
+ export class VibeAgent extends BaseCliAgent {
53
+ /** @type {VibeAgentOptions} */
54
+ opts;
55
+ /** @type {AgentCapabilityRegistry} */
56
+ capabilities;
57
+ /** @type {"vibe"} */
58
+ cliEngine = "vibe";
59
+ /** @type {string | undefined} */
60
+ issuedSessionId;
61
+
62
+ /**
63
+ * @param {VibeAgentOptions} [opts]
64
+ */
65
+ constructor(opts = {}) {
66
+ super({ ...opts, yolo: opts.yolo ?? false });
67
+ this.opts = opts;
68
+ this.capabilities = createVibeCapabilityRegistry(opts);
69
+ }
70
+
71
+ /**
72
+ * @returns {CliOutputInterpreter}
73
+ */
74
+ createOutputInterpreter() {
75
+ let finalAnswer = "";
76
+ let didEmitStarted = false;
77
+ let didEmitCompleted = false;
78
+ const nextSyntheticId = createSyntheticIdGenerator();
79
+
80
+ /**
81
+ * @param {string} line
82
+ * @returns {import("./BaseCliAgent/index.ts").AgentCliEvent[]}
83
+ */
84
+ const parseLine = (line) => {
85
+ const trimmed = line.trim();
86
+ if (!trimmed) return [];
87
+
88
+ /** @type {Record<string, unknown>} */
89
+ let payload;
90
+ try {
91
+ payload = JSON.parse(trimmed);
92
+ } catch {
93
+ return [];
94
+ }
95
+
96
+ if (!isRecord(payload)) return [];
97
+ const role = asString(payload.role);
98
+ const content = asString(payload.content);
99
+ if (role !== "assistant" || !content) return [];
100
+
101
+ const events = [];
102
+
103
+ if (!didEmitStarted) {
104
+ didEmitStarted = true;
105
+ events.push({
106
+ type: "started",
107
+ engine: this.cliEngine,
108
+ title: "Vibe",
109
+ resume: this.issuedSessionId,
110
+ });
111
+ }
112
+
113
+ const id = nextSyntheticId("vibe-message");
114
+ finalAnswer += content;
115
+
116
+ events.push({
117
+ type: "action",
118
+ engine: this.cliEngine,
119
+ phase: "updated",
120
+ entryType: "message",
121
+ action: { id, kind: "turn", title: "assistant" },
122
+ message: content,
123
+ });
124
+
125
+ return events;
126
+ };
127
+
128
+ return {
129
+ onStdoutLine: parseLine,
130
+
131
+ onExit: (result) => {
132
+ if (didEmitCompleted) return [];
133
+ didEmitCompleted = true;
134
+
135
+ const ok = (result.exitCode ?? 0) === 0;
136
+
137
+ const events = [];
138
+ if (!didEmitStarted) {
139
+ events.push({
140
+ type: "started",
141
+ engine: this.cliEngine,
142
+ title: "Vibe",
143
+ resume: this.issuedSessionId,
144
+ });
145
+ }
146
+
147
+ events.push({
148
+ type: "completed",
149
+ engine: this.cliEngine,
150
+ ok,
151
+ answer: ok ? finalAnswer || undefined : undefined,
152
+ error: ok
153
+ ? undefined
154
+ : result.stderr?.trim() ||
155
+ `vibe exited with code ${result.exitCode ?? -1}`,
156
+ resume: this.issuedSessionId,
157
+ });
158
+
159
+ return events;
160
+ },
161
+ };
162
+ }
163
+
164
+ /**
165
+ * @param {{ prompt: string; systemPrompt?: string; cwd: string; options: any }} params
166
+ */
167
+ async buildCommand(params) {
168
+ const args = [];
169
+
170
+ const resumeSession = typeof params.options?.resumeSession === "string"
171
+ ? params.options.resumeSession
172
+ : undefined;
173
+ const effectiveSession = resumeSession ?? this.opts.sessionId;
174
+ this.issuedSessionId = effectiveSession;
175
+ if (this.opts.continueSession && !effectiveSession) {
176
+ args.push("-c");
177
+ }
178
+ pushFlag(args, "--resume", effectiveSession);
179
+
180
+ pushFlag(args, "--agent", this.opts.agent);
181
+ if (this.opts.maxTurns !== undefined) {
182
+ args.push("--max-turns", String(this.opts.maxTurns));
183
+ }
184
+ if (this.opts.maxPrice !== undefined) {
185
+ args.push("--max-price", String(this.opts.maxPrice));
186
+ }
187
+ if (this.opts.maxTokens !== undefined) {
188
+ args.push("--max-tokens", String(this.opts.maxTokens));
189
+ }
190
+ if (this.opts.enabledTools?.length) {
191
+ for (const tool of this.opts.enabledTools) {
192
+ args.push("--enabled-tools", tool);
193
+ }
194
+ }
195
+
196
+ args.push("--trust");
197
+ args.push("--output", "streaming");
198
+ pushFlag(args, "--workdir", params.cwd);
199
+
200
+ if (this.extraArgs?.length) args.push(...this.extraArgs);
201
+
202
+ const systemPrefix = params.systemPrompt
203
+ ? `${params.systemPrompt}\n\n`
204
+ : "";
205
+ const fullPrompt = `${systemPrefix}${params.prompt ?? ""}`;
206
+ pushFlag(args, "--prompt", fullPrompt);
207
+
208
+ return {
209
+ command: "vibe",
210
+ args,
211
+ outputFormat: "stream-json",
212
+ };
213
+ }
214
+ }
@@ -0,0 +1,11 @@
1
+ import type { BaseCliAgentOptions } from "./BaseCliAgent/BaseCliAgentOptions";
2
+
3
+ export type VibeAgentOptions = BaseCliAgentOptions & {
4
+ agent?: string;
5
+ maxTurns?: number;
6
+ maxPrice?: number;
7
+ maxTokens?: number;
8
+ enabledTools?: string[];
9
+ sessionId?: string;
10
+ continueSession?: boolean;
11
+ };
@@ -30,6 +30,7 @@ const WORKFLOW_TOOL_NAMES = new Set([
30
30
  ]);
31
31
  const APPROVAL_TOOL_NAMES = new Set([
32
32
  "approve",
33
+ "ask_human",
33
34
  "deny",
34
35
  "list_pending_approvals",
35
36
  "resolve_approval",
@@ -77,5 +77,9 @@ export function renderSmithersAgentPromptGuidance(contract, options = {}) {
77
77
  if (destructiveTools.length > 0) {
78
78
  lines.push(`Potentially destructive tools: ${joinToolNames(destructiveTools, prefix)}. Confirm intent before using them unless the user already asked for that action.`);
79
79
  }
80
+ const askHumanTool = contract.tools.find((tool) => tool.name === "ask_human");
81
+ if (askHumanTool) {
82
+ lines.push(`When you are blocked, uncertain, missing information, or about to take an irreversible or destructive action, you MUST call ${displayToolName(askHumanTool, prefix)} (or run \`smithers ask-human "<question>"\`) to ask a human and wait for the decision. Never guess or proceed on an assumption — if the request comes back blocked (cancelled/expired), stop and do not proceed.`);
83
+ }
80
84
  return lines.join("\n");
81
85
  }
@@ -2,7 +2,7 @@ import type { AgentToolDescriptor } from "./AgentToolDescriptor";
2
2
 
3
3
  export type AgentCapabilityRegistry = {
4
4
  version: 1;
5
- engine: "claude-code" | "codex" | "antigravity" | "gemini" | "kimi" | "pi" | "amp" | "forge" | "opencode";
5
+ engine: "claude-code" | "codex" | "antigravity" | "gemini" | "kimi" | "pi" | "amp" | "forge" | "opencode" | "vibe";
6
6
  runtimeTools: Record<string, AgentToolDescriptor>;
7
7
  mcp: {
8
8
  bootstrap: "inline-config" | "project-config" | "allow-list" | "unsupported";
@@ -1,8 +1,11 @@
1
1
  export type CliAgentCapabilityAdapterId =
2
2
  | "claude"
3
+ | "amp"
3
4
  | "antigravity"
4
5
  | "codex"
6
+ | "forge"
5
7
  | "gemini"
6
8
  | "kimi"
7
9
  | "opencode"
8
- | "pi";
10
+ | "pi"
11
+ | "vibe";
@@ -1,4 +1,5 @@
1
1
  import type { AgentCapabilityRegistry } from "../capability-registry";
2
+ import type { CliAgentSurfaceManifestEntry } from "../cli-surface/CliAgentSurfaceTypes";
2
3
  import type { CliAgentCapabilityAdapterId } from "./CliAgentCapabilityAdapterId";
3
4
 
4
5
  export type CliAgentCapabilityReportEntry = {
@@ -6,4 +7,5 @@ export type CliAgentCapabilityReportEntry = {
6
7
  binary: string;
7
8
  fingerprint: string;
8
9
  capabilities: AgentCapabilityRegistry;
10
+ surface: CliAgentSurfaceManifestEntry;
9
11
  };
@@ -1,5 +1,8 @@
1
1
  import { getCliAgentCapabilityReport } from "./getCliAgentCapabilityReport.js";
2
2
  /** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityDoctorReport} CliAgentCapabilityDoctorReport */
3
+ /** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityIssue} CliAgentCapabilityIssue */
4
+ /** @typedef {import("./CliAgentCapabilityReportEntry.ts").CliAgentCapabilityReportEntry} CliAgentCapabilityReportEntry */
5
+ /** @typedef {import("../capability-registry/AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
3
6
 
4
7
  /**
5
8
  * @param {AgentCapabilityRegistry} registry
@@ -71,12 +74,56 @@ function diagnoseCapabilityRegistry(registry) {
71
74
  }
72
75
  return issues;
73
76
  }
77
+
78
+ /**
79
+ * @param {CliAgentCapabilityReportEntry} entry
80
+ * @returns {CliAgentCapabilityIssue[]}
81
+ */
82
+ function diagnoseSurfaceContract(entry) {
83
+ const issues = [];
84
+ const emitted = new Set(entry.surface.emittedFlags);
85
+ for (const unsupported of entry.surface.unsupportedFlags) {
86
+ if (emitted.has(unsupported.flag)) {
87
+ issues.push({
88
+ code: "unsupported-flag-emitted",
89
+ message: `${entry.id} emits ${unsupported.flag}, but the surface manifest marks it unsupported.${unsupported.replacement ? ` Replacement: ${unsupported.replacement}.` : ""}`,
90
+ severity: "error",
91
+ });
92
+ }
93
+ }
94
+ if (entry.surface.binary !== entry.binary) {
95
+ issues.push({
96
+ code: "surface-binary-mismatch",
97
+ message: `${entry.id} capability binary is ${entry.binary}, but the surface manifest says ${entry.surface.binary}.`,
98
+ severity: "error",
99
+ });
100
+ }
101
+ if (entry.surface.docsUrls.length === 0) {
102
+ issues.push({
103
+ code: "missing-surface-docs",
104
+ message: `${entry.id} has no source URL recorded for its CLI surface.`,
105
+ severity: "warning",
106
+ });
107
+ }
108
+ if (entry.surface.resume.kind !== "none" && entry.surface.resume.emitted.length === 0) {
109
+ issues.push({
110
+ code: "missing-resume-emission",
111
+ message: `${entry.id} declares ${entry.surface.resume.kind} resume support but no emitted resume tokens.`,
112
+ severity: "warning",
113
+ });
114
+ }
115
+ return issues;
116
+ }
117
+
74
118
  /**
75
119
  * @returns {CliAgentCapabilityDoctorReport}
76
120
  */
77
121
  export function getCliAgentCapabilityDoctorReport() {
78
122
  const agents = getCliAgentCapabilityReport().map((entry) => {
79
- const issues = diagnoseCapabilityRegistry(entry.capabilities);
123
+ const issues = [
124
+ ...diagnoseCapabilityRegistry(entry.capabilities),
125
+ ...diagnoseSurfaceContract(entry),
126
+ ];
80
127
  return {
81
128
  ...entry,
82
129
  ok: issues.length === 0,
@@ -1,14 +1,23 @@
1
1
  import { hashCapabilityRegistry, normalizeCapabilityRegistry, } from "../capability-registry/index.js";
2
+ import { createAmpCapabilityRegistry } from "../AmpAgent.js";
2
3
  import { createAntigravityCapabilityRegistry } from "../AntigravityAgent.js";
3
4
  import { createClaudeCodeCapabilityRegistry } from "../ClaudeCodeAgent.js";
4
5
  import { createCodexCapabilityRegistry } from "../CodexAgent.js";
6
+ import { createForgeCapabilityRegistry } from "../ForgeAgent.js";
5
7
  import { createGeminiCapabilityRegistry } from "../GeminiAgent.js";
6
8
  import { createKimiCapabilityRegistry } from "../KimiAgent.js";
7
9
  import { createOpenCodeCapabilityRegistry } from "../OpenCodeAgent.js";
8
10
  import { createPiCapabilityRegistry } from "../PiAgent.js";
11
+ import { createVibeCapabilityRegistry } from "../VibeAgent.js";
12
+ import { getCliAgentSurfaceManifestEntry } from "../cli-surface/index.js";
9
13
  /** @typedef {import("./CliAgentCapabilityReportEntry.ts").CliAgentCapabilityReportEntry} CliAgentCapabilityReportEntry */
10
14
 
11
15
  const CLI_AGENT_CAPABILITY_ADAPTERS = [
16
+ {
17
+ id: "amp",
18
+ binary: "amp",
19
+ buildRegistry: () => createAmpCapabilityRegistry(),
20
+ },
12
21
  {
13
22
  id: "claude",
14
23
  binary: "claude",
@@ -29,6 +38,11 @@ const CLI_AGENT_CAPABILITY_ADAPTERS = [
29
38
  binary: "gemini",
30
39
  buildRegistry: () => createGeminiCapabilityRegistry(),
31
40
  },
41
+ {
42
+ id: "forge",
43
+ binary: "forge",
44
+ buildRegistry: () => createForgeCapabilityRegistry(),
45
+ },
32
46
  {
33
47
  id: "kimi",
34
48
  binary: "kimi",
@@ -44,6 +58,11 @@ const CLI_AGENT_CAPABILITY_ADAPTERS = [
44
58
  binary: "pi",
45
59
  buildRegistry: () => createPiCapabilityRegistry(),
46
60
  },
61
+ {
62
+ id: "vibe",
63
+ binary: "vibe",
64
+ buildRegistry: () => createVibeCapabilityRegistry(),
65
+ },
47
66
  ];
48
67
  /**
49
68
  * @returns {CliAgentCapabilityReportEntry[]}
@@ -54,11 +73,16 @@ export function getCliAgentCapabilityReport() {
54
73
  if (!capabilities) {
55
74
  throw new Error(`Capability registry missing for adapter ${adapter.id}`);
56
75
  }
76
+ const surface = getCliAgentSurfaceManifestEntry(adapter.id);
77
+ if (!surface) {
78
+ throw new Error(`CLI surface manifest missing for adapter ${adapter.id}`);
79
+ }
57
80
  return {
58
81
  id: adapter.id,
59
82
  binary: adapter.binary,
60
83
  fingerprint: hashCapabilityRegistry(capabilities),
61
84
  capabilities,
85
+ surface,
62
86
  };
63
87
  });
64
88
  }
@@ -9,3 +9,8 @@
9
9
  export { getCliAgentCapabilityReport } from "./getCliAgentCapabilityReport.js";
10
10
  export { getCliAgentCapabilityDoctorReport } from "./getCliAgentCapabilityDoctorReport.js";
11
11
  export { formatCliAgentCapabilityDoctorReport } from "./formatCliAgentCapabilityDoctorReport.js";
12
+ export {
13
+ CLI_AGENT_SURFACE_MANIFEST,
14
+ getCliAgentSurfaceManifestEntry,
15
+ listCliAgentSurfaceManifests,
16
+ } from "../cli-surface/index.js";
@@ -0,0 +1,34 @@
1
+ import type { CliAgentCapabilityAdapterId } from "../cli-capabilities/CliAgentCapabilityAdapterId";
2
+
3
+ export type CliAgentSurfaceOptionMapping = {
4
+ option: string;
5
+ flag?: string;
6
+ env?: string;
7
+ notes?: string;
8
+ };
9
+
10
+ export type CliAgentUnsupportedFlag = {
11
+ flag: string;
12
+ replacement?: string;
13
+ reason: string;
14
+ };
15
+
16
+ export type CliAgentSurfaceResumeContract = {
17
+ kind: "flag" | "subcommand" | "env" | "none";
18
+ emitted: string[];
19
+ notes: string;
20
+ };
21
+
22
+ export type CliAgentSurfaceManifestEntry = {
23
+ id: CliAgentCapabilityAdapterId;
24
+ displayName: string;
25
+ binary: string;
26
+ packageExport: string;
27
+ defaultOutputFormat: "text" | "json" | "stream-json" | "rpc";
28
+ docsUrls: string[];
29
+ emittedFlags: string[];
30
+ supportedFlags: string[];
31
+ unsupportedFlags: CliAgentUnsupportedFlag[];
32
+ optionMappings: CliAgentSurfaceOptionMapping[];
33
+ resume: CliAgentSurfaceResumeContract;
34
+ };