@smithers-orchestrator/agents 0.22.0 → 0.23.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/package.json +5 -4
- package/src/AmpAgent.js +26 -19
- package/src/AntigravityAgent.js +53 -18
- package/src/AntigravityAgentOptions.ts +45 -4
- package/src/BaseCliAgent/AgentGenerateOptions.ts +12 -0
- package/src/BaseCliAgent/BaseCliAgent.js +6 -1
- package/src/BaseCliAgent/taskContextEnv.js +31 -0
- package/src/ClaudeCodeAgent.js +19 -1
- package/src/ForgeAgent.js +26 -19
- package/src/HermesAgent.js +1 -1
- package/src/VibeAgent.js +214 -0
- package/src/VibeAgentOptions.ts +11 -0
- package/src/agent-contract/createSmithersAgentContract.js +1 -0
- package/src/agent-contract/renderSmithersAgentPromptGuidance.js +4 -0
- package/src/capability-registry/AgentCapabilityRegistry.ts +1 -1
- package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +4 -1
- package/src/cli-capabilities/CliAgentCapabilityReportEntry.ts +2 -0
- package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +48 -1
- package/src/cli-capabilities/getCliAgentCapabilityReport.js +24 -0
- package/src/cli-capabilities/index.js +5 -0
- package/src/cli-surface/CliAgentSurfaceTypes.ts +34 -0
- package/src/cli-surface/cliAgentSurfaceManifest.js +490 -0
- package/src/cli-surface/index.js +5 -0
- package/src/index.d.ts +707 -386
- package/src/index.js +21 -0
- package/src/mcp/McpServerConfig.ts +19 -0
- package/src/mcp/McpToolset.ts +17 -0
- package/src/mcp/createMcpToolset.js +94 -0
- package/src/sanitizeForOpenAI.js +20 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithers-orchestrator/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "AI SDK and CLI agent adapters for Smithers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,12 +47,13 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
49
49
|
"@ai-sdk/openai": "^3.0.53",
|
|
50
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
50
51
|
"ai": "^6.0.168",
|
|
51
52
|
"effect": "^3.21.1",
|
|
52
53
|
"zod": "^4.3.6",
|
|
53
|
-
"@smithers-orchestrator/
|
|
54
|
-
"@smithers-orchestrator/
|
|
55
|
-
"@smithers-orchestrator/observability": "0.
|
|
54
|
+
"@smithers-orchestrator/errors": "0.23.0",
|
|
55
|
+
"@smithers-orchestrator/driver": "0.23.0",
|
|
56
|
+
"@smithers-orchestrator/observability": "0.23.0"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@types/bun": "latest",
|
package/src/AmpAgent.js
CHANGED
|
@@ -6,6 +6,31 @@ import { BaseCliAgent, pushFlag, isRecord, asString, toolKindFromName, createSyn
|
|
|
6
6
|
/** @typedef {import("./capability-registry/AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
7
7
|
/** @typedef {import("./BaseCliAgent/CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @returns {AgentCapabilityRegistry}
|
|
11
|
+
*/
|
|
12
|
+
export function createAmpCapabilityRegistry() {
|
|
13
|
+
return {
|
|
14
|
+
version: 1,
|
|
15
|
+
engine: "amp",
|
|
16
|
+
runtimeTools: {},
|
|
17
|
+
mcp: {
|
|
18
|
+
bootstrap: "project-config",
|
|
19
|
+
supportsProjectScope: true,
|
|
20
|
+
supportsUserScope: false,
|
|
21
|
+
},
|
|
22
|
+
skills: {
|
|
23
|
+
supportsSkills: false,
|
|
24
|
+
smithersSkillIds: [],
|
|
25
|
+
},
|
|
26
|
+
humanInteraction: {
|
|
27
|
+
supportsUiRequests: false,
|
|
28
|
+
methods: [],
|
|
29
|
+
},
|
|
30
|
+
builtIns: ["default"],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
9
34
|
/**
|
|
10
35
|
* Agent implementation that wraps the 'amp' CLI executable.
|
|
11
36
|
* It translates generation requests into CLI arguments and executes the process.
|
|
@@ -23,25 +48,7 @@ export class AmpAgent extends BaseCliAgent {
|
|
|
23
48
|
constructor(opts = {}) {
|
|
24
49
|
super(opts);
|
|
25
50
|
this.opts = opts;
|
|
26
|
-
this.capabilities =
|
|
27
|
-
version: 1,
|
|
28
|
-
engine: "amp",
|
|
29
|
-
runtimeTools: {},
|
|
30
|
-
mcp: {
|
|
31
|
-
bootstrap: "project-config",
|
|
32
|
-
supportsProjectScope: true,
|
|
33
|
-
supportsUserScope: false,
|
|
34
|
-
},
|
|
35
|
-
skills: {
|
|
36
|
-
supportsSkills: false,
|
|
37
|
-
smithersSkillIds: [],
|
|
38
|
-
},
|
|
39
|
-
humanInteraction: {
|
|
40
|
-
supportsUiRequests: false,
|
|
41
|
-
methods: [],
|
|
42
|
-
},
|
|
43
|
-
builtIns: ["default"],
|
|
44
|
-
};
|
|
51
|
+
this.capabilities = createAmpCapabilityRegistry();
|
|
45
52
|
}
|
|
46
53
|
/**
|
|
47
54
|
* @returns {CliOutputInterpreter}
|
package/src/AntigravityAgent.js
CHANGED
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
import { BaseCliAgent, pushFlag, pushList, isRecord, asString, truncate, toolKindFromName, createSyntheticIdGenerator, } from "./BaseCliAgent/index.js";
|
|
2
|
+
import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
2
3
|
import { normalizeCapabilityStringList, } from "./capability-registry/index.js";
|
|
4
|
+
import { getCliAgentSurfaceManifestEntry } from "./cli-surface/index.js";
|
|
3
5
|
/** @typedef {import("./capability-registry/AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
4
6
|
/** @typedef {import("./BaseCliAgent/CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
|
|
5
7
|
/** @typedef {import("./AntigravityAgentOptions.ts").AntigravityAgentOptions} AntigravityAgentOptions */
|
|
6
8
|
|
|
9
|
+
const ANTIGRAVITY_SURFACE = getCliAgentSurfaceManifestEntry("antigravity");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} option
|
|
13
|
+
* @param {string} flag
|
|
14
|
+
* @returns {SmithersError}
|
|
15
|
+
*/
|
|
16
|
+
function unsupportedAntigravityOption(option, flag) {
|
|
17
|
+
const rule = ANTIGRAVITY_SURFACE?.unsupportedFlags.find((entry) => entry.flag === flag);
|
|
18
|
+
const replacement = rule?.replacement ? ` Use ${rule.replacement} instead.` : "";
|
|
19
|
+
const reason = rule?.reason ? ` ${rule.reason}` : "";
|
|
20
|
+
return new SmithersError("AGENT_CONFIG_INVALID", `AntigravityAgent option "${option}" maps to unsupported agy flag ${flag}.${reason}${replacement}`, {
|
|
21
|
+
agentEngine: "antigravity",
|
|
22
|
+
option,
|
|
23
|
+
flag,
|
|
24
|
+
replacement: rule?.replacement,
|
|
25
|
+
failureRetryable: false,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {AntigravityAgentOptions} opts
|
|
31
|
+
*/
|
|
32
|
+
function assertSupportedAntigravityOptions(opts) {
|
|
33
|
+
if (opts.debug)
|
|
34
|
+
throw unsupportedAntigravityOption("debug", "--debug");
|
|
35
|
+
if (opts.screenReader)
|
|
36
|
+
throw unsupportedAntigravityOption("screenReader", "--screen-reader");
|
|
37
|
+
if (opts.outputFormat !== undefined)
|
|
38
|
+
throw unsupportedAntigravityOption("outputFormat", "--output-format");
|
|
39
|
+
if (opts.listSessions)
|
|
40
|
+
throw unsupportedAntigravityOption("listSessions", "--list-sessions");
|
|
41
|
+
if (opts.deleteSession !== undefined)
|
|
42
|
+
throw unsupportedAntigravityOption("deleteSession", "--delete-session");
|
|
43
|
+
if (opts.extensions?.length)
|
|
44
|
+
throw unsupportedAntigravityOption("extensions", "--extensions");
|
|
45
|
+
if (opts.listExtensions)
|
|
46
|
+
throw unsupportedAntigravityOption("listExtensions", "--list-extensions");
|
|
47
|
+
}
|
|
48
|
+
|
|
7
49
|
/**
|
|
8
50
|
* @param {AntigravityAgentOptions} opts
|
|
9
51
|
*/
|
|
@@ -209,15 +251,13 @@ export class AntigravityAgent extends BaseCliAgent {
|
|
|
209
251
|
* @param {{ prompt: string; systemPrompt?: string; cwd: string; options: any; }} params
|
|
210
252
|
*/
|
|
211
253
|
async buildCommand(params) {
|
|
254
|
+
assertSupportedAntigravityOptions(this.opts);
|
|
212
255
|
const args = [];
|
|
213
256
|
const yoloEnabled = this.opts.dangerouslySkipPermissions ?? this.opts.yolo ?? this.yolo;
|
|
214
|
-
const outputFormat = this.opts.outputFormat ??
|
|
215
|
-
(params.options?.onEvent ? "stream-json" : "json");
|
|
216
257
|
const resumeSession = typeof params.options?.resumeSession === "string"
|
|
217
258
|
? params.options.resumeSession
|
|
218
|
-
: this.opts.resume;
|
|
219
|
-
|
|
220
|
-
args.push("--debug");
|
|
259
|
+
: this.opts.conversation ?? this.opts.resume;
|
|
260
|
+
args.push("--cwd", params.cwd);
|
|
221
261
|
pushFlag(args, "--model", this.opts.model ?? this.model);
|
|
222
262
|
if (this.opts.sandbox)
|
|
223
263
|
args.push("--sandbox");
|
|
@@ -232,18 +272,11 @@ export class AntigravityAgent extends BaseCliAgent {
|
|
|
232
272
|
pushList(args, "--allowed-tools", this.opts.allowedTools);
|
|
233
273
|
}
|
|
234
274
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (this.opts.listSessions)
|
|
240
|
-
args.push("--list-sessions");
|
|
241
|
-
pushFlag(args, "--delete-session", this.opts.deleteSession);
|
|
242
|
-
pushList(args, "--include-directories", this.opts.includeDirectories);
|
|
243
|
-
if (this.opts.screenReader)
|
|
244
|
-
args.push("--screen-reader");
|
|
275
|
+
if (this.opts.continue)
|
|
276
|
+
args.push("--continue");
|
|
277
|
+
pushFlag(args, "--conversation", resumeSession);
|
|
278
|
+
pushList(args, "--add-dir", this.opts.includeDirectories);
|
|
245
279
|
pushFlag(args, "--gemini_dir", this.opts.geminiDir ?? this.opts.configDir);
|
|
246
|
-
pushFlag(args, "--output-format", outputFormat);
|
|
247
280
|
if (this.extraArgs?.length)
|
|
248
281
|
args.push(...this.extraArgs);
|
|
249
282
|
const systemPrefix = params.systemPrompt
|
|
@@ -253,14 +286,16 @@ export class AntigravityAgent extends BaseCliAgent {
|
|
|
253
286
|
? "\n\nREMINDER: Your response MUST be ONLY the required raw JSON object. Do not include prose, markdown, or code fences. The first character must be `{` and the last character must be `}`.\n"
|
|
254
287
|
: "";
|
|
255
288
|
const fullPrompt = `${systemPrefix}${params.prompt ?? ""}${jsonReminder}`;
|
|
256
|
-
args.push("
|
|
289
|
+
args.push("-p", fullPrompt);
|
|
257
290
|
const accountEnv = {};
|
|
291
|
+
if (this.opts.geminiDir ?? this.opts.configDir)
|
|
292
|
+
accountEnv.GEMINI_DIR = this.opts.geminiDir ?? this.opts.configDir;
|
|
258
293
|
if (this.opts.apiKey)
|
|
259
294
|
accountEnv.GEMINI_API_KEY = this.opts.apiKey;
|
|
260
295
|
return {
|
|
261
296
|
command: this.opts.binary ?? "agy",
|
|
262
297
|
args,
|
|
263
|
-
outputFormat,
|
|
298
|
+
outputFormat: "text",
|
|
264
299
|
env: Object.keys(accountEnv).length > 0 ? accountEnv : undefined,
|
|
265
300
|
};
|
|
266
301
|
}
|
|
@@ -1,30 +1,71 @@
|
|
|
1
1
|
import type { BaseCliAgentOptions } from "./BaseCliAgent/BaseCliAgentOptions";
|
|
2
2
|
|
|
3
3
|
export type AntigravityAgentOptions = BaseCliAgentOptions & {
|
|
4
|
-
debug?: boolean;
|
|
5
4
|
model?: string;
|
|
6
5
|
sandbox?: boolean;
|
|
7
6
|
yolo?: boolean;
|
|
8
7
|
dangerouslySkipPermissions?: boolean;
|
|
9
8
|
allowedMcpServerNames?: string[];
|
|
10
9
|
allowedTools?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Antigravity renamed extensions to plugins and manages them via
|
|
12
|
+
* `agy plugin`; launch-time extension flags are rejected at runtime.
|
|
13
|
+
*/
|
|
11
14
|
extensions?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Use `agy plugin list` outside Smithers. This option is rejected
|
|
17
|
+
* at runtime because current `agy` builds no longer accept it during launch.
|
|
18
|
+
*/
|
|
12
19
|
listExtensions?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Native Antigravity conversation id. Smithers emits `--conversation`.
|
|
22
|
+
*/
|
|
23
|
+
conversation?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Continue the latest Antigravity conversation. Smithers emits `--continue`.
|
|
26
|
+
*/
|
|
27
|
+
continue?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Use `conversation`; Smithers still maps this to
|
|
30
|
+
* `--conversation` for compatibility.
|
|
31
|
+
*/
|
|
13
32
|
resume?: string;
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated Conversation listing is interactive via `/resume`; this option
|
|
35
|
+
* is rejected at runtime.
|
|
36
|
+
*/
|
|
14
37
|
listSessions?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Conversation deletion is not a supported non-interactive
|
|
40
|
+
* launch flag; this option is rejected at runtime.
|
|
41
|
+
*/
|
|
15
42
|
deleteSession?: string;
|
|
16
43
|
includeDirectories?: string[];
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Current `agy` builds do not expose `--screen-reader`; this
|
|
46
|
+
* option is rejected at runtime.
|
|
47
|
+
*/
|
|
17
48
|
screenReader?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated Current `agy` builds do not expose `--output-format`; Smithers
|
|
51
|
+
* reads Antigravity stdout as text.
|
|
52
|
+
*/
|
|
18
53
|
outputFormat?: "text" | "json" | "stream-json";
|
|
54
|
+
/**
|
|
55
|
+
* @deprecated Current `agy` builds do not expose `--debug`; this option is
|
|
56
|
+
* rejected at runtime.
|
|
57
|
+
*/
|
|
58
|
+
debug?: boolean;
|
|
19
59
|
/**
|
|
20
60
|
* Antigravity CLI binary to execute. The official CLI currently installs
|
|
21
61
|
* `agy`; this exists for test harnesses and future binary renames.
|
|
22
62
|
*/
|
|
23
63
|
binary?: string;
|
|
24
64
|
/**
|
|
25
|
-
* Path to an isolated Google CLI config root.
|
|
26
|
-
* Antigravity reads/writes
|
|
27
|
-
* user's default
|
|
65
|
+
* Path to an isolated Google CLI config root. Smithers passes it as
|
|
66
|
+
* `--gemini_dir` and `GEMINI_DIR` so Antigravity reads/writes
|
|
67
|
+
* `<configDir>/antigravity-cli/...` instead of the user's default
|
|
68
|
+
* `~/.gemini/antigravity-cli/...`.
|
|
28
69
|
*/
|
|
29
70
|
configDir?: string;
|
|
30
71
|
/**
|
|
@@ -20,5 +20,17 @@ export type AgentGenerateOptions = {
|
|
|
20
20
|
isRetry?: unknown;
|
|
21
21
|
retryAttempt?: unknown;
|
|
22
22
|
schemaRetry?: unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Run context for the task this agent invocation belongs to. Surfaced to the
|
|
25
|
+
* spawned agent process (and its subprocesses) as SMITHERS_RUN_ID / NODE_ID /
|
|
26
|
+
* ITERATION / ATTEMPT so the agent can address its own run — e.g. to raise a
|
|
27
|
+
* blocking `smithers ask-human` request.
|
|
28
|
+
*/
|
|
29
|
+
taskContext?: {
|
|
30
|
+
runId?: string;
|
|
31
|
+
nodeId?: string;
|
|
32
|
+
iteration?: number;
|
|
33
|
+
attempt?: number;
|
|
34
|
+
};
|
|
23
35
|
[key: string]: unknown;
|
|
24
36
|
};
|
|
@@ -14,6 +14,7 @@ import { extractTextFromJsonValue } from "./extractTextFromJsonValue.js";
|
|
|
14
14
|
import { createAgentStdoutTextEmitter } from "./createAgentStdoutTextEmitter.js";
|
|
15
15
|
import { buildGenerateResult } from "./buildGenerateResult.js";
|
|
16
16
|
import { runCommandEffect } from "./runCommandEffect.js";
|
|
17
|
+
import { taskContextEnv } from "./taskContextEnv.js";
|
|
17
18
|
/** @typedef {import("./AgentCliEvent.ts").AgentCliEvent} AgentCliEvent */
|
|
18
19
|
|
|
19
20
|
/** @typedef {import("./AgentGenerateOptions.ts").AgentGenerateOptions} AgentGenerateOptions */
|
|
@@ -628,7 +629,11 @@ export class BaseCliAgent {
|
|
|
628
629
|
idleMs: this.idleTimeoutMs,
|
|
629
630
|
});
|
|
630
631
|
const cwd = this.cwd ?? options?.rootDir ?? process.cwd();
|
|
631
|
-
const env = {
|
|
632
|
+
const env = {
|
|
633
|
+
...process.env,
|
|
634
|
+
...this.env,
|
|
635
|
+
...taskContextEnv(options?.taskContext),
|
|
636
|
+
};
|
|
632
637
|
const combinedSystem = combineNonEmpty([
|
|
633
638
|
this.systemPrompt,
|
|
634
639
|
systemFromMessages,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a task's run context into the `SMITHERS_*` environment variables that a
|
|
3
|
+
* Smithers-spawned agent — and any subprocess it runs, e.g. `smithers ask-human` —
|
|
4
|
+
* uses to identify the run/node it belongs to. Undefined/blank fields are omitted
|
|
5
|
+
* so we never clobber an inherited value with `"undefined"`.
|
|
6
|
+
*
|
|
7
|
+
* @param {{ runId?: string, nodeId?: string, iteration?: number, attempt?: number } | null | undefined} taskContext
|
|
8
|
+
* @returns {Record<string, string>}
|
|
9
|
+
*/
|
|
10
|
+
export function taskContextEnv(taskContext) {
|
|
11
|
+
if (!taskContext) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
/** @type {Record<string, string>} */
|
|
15
|
+
const env = {};
|
|
16
|
+
if (typeof taskContext.runId === "string" && taskContext.runId.length > 0) {
|
|
17
|
+
env.SMITHERS_RUN_ID = taskContext.runId;
|
|
18
|
+
}
|
|
19
|
+
if (typeof taskContext.nodeId === "string" && taskContext.nodeId.length > 0) {
|
|
20
|
+
env.SMITHERS_NODE_ID = taskContext.nodeId;
|
|
21
|
+
}
|
|
22
|
+
if (typeof taskContext.iteration === "number" &&
|
|
23
|
+
Number.isInteger(taskContext.iteration)) {
|
|
24
|
+
env.SMITHERS_ITERATION = String(taskContext.iteration);
|
|
25
|
+
}
|
|
26
|
+
if (typeof taskContext.attempt === "number" &&
|
|
27
|
+
Number.isInteger(taskContext.attempt)) {
|
|
28
|
+
env.SMITHERS_ATTEMPT = String(taskContext.attempt);
|
|
29
|
+
}
|
|
30
|
+
return env;
|
|
31
|
+
}
|
package/src/ClaudeCodeAgent.js
CHANGED
|
@@ -383,7 +383,7 @@ export class ClaudeCodeAgent extends BaseCliAgent {
|
|
|
383
383
|
args.push("--chrome");
|
|
384
384
|
if (this.opts.noChrome)
|
|
385
385
|
args.push("--no-chrome");
|
|
386
|
-
if (this.opts.continue)
|
|
386
|
+
if (this.opts.continue || params.options?.continueSession)
|
|
387
387
|
args.push("--continue");
|
|
388
388
|
if (this.opts.debug === true) {
|
|
389
389
|
args.push("--debug");
|
|
@@ -445,9 +445,27 @@ export class ClaudeCodeAgent extends BaseCliAgent {
|
|
|
445
445
|
args.push("--verbose");
|
|
446
446
|
if (this.extraArgs?.length)
|
|
447
447
|
args.push(...this.extraArgs);
|
|
448
|
+
// Durability: inject a PostToolUse hook that calls back into smithers for a
|
|
449
|
+
// strict Tier 1 snapshot at each file-edit / Bash boundary. Only when the
|
|
450
|
+
// engine passes a socket path (durability enabled); additive --settings.
|
|
451
|
+
const durabilitySocket = typeof params.options?.durabilitySocket === "string"
|
|
452
|
+
? params.options.durabilitySocket
|
|
453
|
+
: undefined;
|
|
454
|
+
if (durabilitySocket) {
|
|
455
|
+
args.push("--settings", JSON.stringify({
|
|
456
|
+
hooks: {
|
|
457
|
+
PostToolUse: [{
|
|
458
|
+
matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash",
|
|
459
|
+
hooks: [{ type: "command", command: "smithers snapshot-hook" }],
|
|
460
|
+
}],
|
|
461
|
+
},
|
|
462
|
+
}));
|
|
463
|
+
}
|
|
448
464
|
if (params.prompt)
|
|
449
465
|
args.push(params.prompt);
|
|
450
466
|
const accountEnv = {};
|
|
467
|
+
if (durabilitySocket)
|
|
468
|
+
accountEnv.SMITHERS_SNAPSHOT_SOCK = durabilitySocket;
|
|
451
469
|
if (this.opts.configDir)
|
|
452
470
|
accountEnv.CLAUDE_CONFIG_DIR = this.opts.configDir;
|
|
453
471
|
if (this.opts.apiKey)
|
package/src/ForgeAgent.js
CHANGED
|
@@ -5,6 +5,31 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
/** @typedef {import("./BaseCliAgent/CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
|
|
6
6
|
/** @typedef {import("./ForgeAgentOptions.ts").ForgeAgentOptions} ForgeAgentOptions */
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @returns {AgentCapabilityRegistry}
|
|
10
|
+
*/
|
|
11
|
+
export function createForgeCapabilityRegistry() {
|
|
12
|
+
return {
|
|
13
|
+
version: 1,
|
|
14
|
+
engine: "forge",
|
|
15
|
+
runtimeTools: {},
|
|
16
|
+
mcp: {
|
|
17
|
+
bootstrap: "unsupported",
|
|
18
|
+
supportsProjectScope: false,
|
|
19
|
+
supportsUserScope: false,
|
|
20
|
+
},
|
|
21
|
+
skills: {
|
|
22
|
+
supportsSkills: false,
|
|
23
|
+
smithersSkillIds: [],
|
|
24
|
+
},
|
|
25
|
+
humanInteraction: {
|
|
26
|
+
supportsUiRequests: false,
|
|
27
|
+
methods: [],
|
|
28
|
+
},
|
|
29
|
+
builtIns: ["default"],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
8
33
|
export class ForgeAgent extends BaseCliAgent {
|
|
9
34
|
opts;
|
|
10
35
|
/** @type {AgentCapabilityRegistry} */
|
|
@@ -17,25 +42,7 @@ export class ForgeAgent extends BaseCliAgent {
|
|
|
17
42
|
constructor(opts = {}) {
|
|
18
43
|
super(opts);
|
|
19
44
|
this.opts = opts;
|
|
20
|
-
this.capabilities =
|
|
21
|
-
version: 1,
|
|
22
|
-
engine: "forge",
|
|
23
|
-
runtimeTools: {},
|
|
24
|
-
mcp: {
|
|
25
|
-
bootstrap: "unsupported",
|
|
26
|
-
supportsProjectScope: false,
|
|
27
|
-
supportsUserScope: false,
|
|
28
|
-
},
|
|
29
|
-
skills: {
|
|
30
|
-
supportsSkills: false,
|
|
31
|
-
smithersSkillIds: [],
|
|
32
|
-
},
|
|
33
|
-
humanInteraction: {
|
|
34
|
-
supportsUiRequests: false,
|
|
35
|
-
methods: [],
|
|
36
|
-
},
|
|
37
|
-
builtIns: ["default"],
|
|
38
|
-
};
|
|
45
|
+
this.capabilities = createForgeCapabilityRegistry();
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
41
48
|
* @returns {CliOutputInterpreter}
|
package/src/HermesAgent.js
CHANGED
|
@@ -29,7 +29,7 @@ export class HermesAgent extends OpenAIAgent {
|
|
|
29
29
|
nativeStructuredOutput = false,
|
|
30
30
|
...rest
|
|
31
31
|
} = opts;
|
|
32
|
-
if (baseURL === undefined) {
|
|
32
|
+
if (baseURL === undefined || baseURL.trim() === "") {
|
|
33
33
|
throw new SmithersError(
|
|
34
34
|
"AGENT_CONFIG_INVALID",
|
|
35
35
|
"HermesAgent requires a baseURL (or the HERMES_BASE_URL env var) pointing at the Hermes OpenAI-compatible API, e.g. http://127.0.0.1:5123/v1.",
|
package/src/VibeAgent.js
ADDED
|
@@ -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
|
+
};
|