@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,188 @@
|
|
|
1
|
+
import { renderSmithersAgentPromptGuidance } from "./renderSmithersAgentPromptGuidance.js";
|
|
2
|
+
/** @typedef {import("./SmithersListedTool.ts").SmithersListedTool} SmithersListedTool */
|
|
3
|
+
/** @typedef {import("./SmithersToolSurface.ts").SmithersToolSurface} SmithersToolSurface */
|
|
4
|
+
/** @typedef {import("./SmithersAgentContractTool.ts").SmithersAgentContractTool} SmithersAgentContractTool */
|
|
5
|
+
/** @typedef {import("./SmithersAgentToolCategory.ts").SmithersAgentToolCategory} SmithersAgentToolCategory */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {{ toolSurface?: SmithersToolSurface; serverName?: string; tools: SmithersListedTool[]; }} CreateSmithersAgentContractOptions
|
|
9
|
+
*/
|
|
10
|
+
/** @typedef {import("./SmithersAgentContract.ts").SmithersAgentContract} SmithersAgentContract */
|
|
11
|
+
|
|
12
|
+
const DEFAULT_SERVER_NAME = "smithers";
|
|
13
|
+
const TOOL_CATEGORY_ORDER = [
|
|
14
|
+
"workflows",
|
|
15
|
+
"runs",
|
|
16
|
+
"approvals",
|
|
17
|
+
"debug",
|
|
18
|
+
"admin",
|
|
19
|
+
];
|
|
20
|
+
const WORKFLOW_TOOL_NAMES = new Set([
|
|
21
|
+
"graph",
|
|
22
|
+
"list_workflows",
|
|
23
|
+
"run_workflow",
|
|
24
|
+
"up",
|
|
25
|
+
"workflow_create",
|
|
26
|
+
"workflow_doctor",
|
|
27
|
+
"workflow_list",
|
|
28
|
+
"workflow_path",
|
|
29
|
+
"workflow_run",
|
|
30
|
+
]);
|
|
31
|
+
const APPROVAL_TOOL_NAMES = new Set([
|
|
32
|
+
"approve",
|
|
33
|
+
"deny",
|
|
34
|
+
"list_pending_approvals",
|
|
35
|
+
"resolve_approval",
|
|
36
|
+
"signal",
|
|
37
|
+
]);
|
|
38
|
+
const RUN_TOOL_NAMES = new Set([
|
|
39
|
+
"cancel",
|
|
40
|
+
"down",
|
|
41
|
+
"events",
|
|
42
|
+
"explain_run",
|
|
43
|
+
"get_run",
|
|
44
|
+
"inspect",
|
|
45
|
+
"list_runs",
|
|
46
|
+
"logs",
|
|
47
|
+
"ps",
|
|
48
|
+
"run_workflow",
|
|
49
|
+
"supervise",
|
|
50
|
+
"watch_run",
|
|
51
|
+
"why",
|
|
52
|
+
]);
|
|
53
|
+
const DEBUG_TOOL_NAMES = new Set([
|
|
54
|
+
"chat",
|
|
55
|
+
"diff",
|
|
56
|
+
"fork",
|
|
57
|
+
"get_artifacts",
|
|
58
|
+
"get_chat_transcript",
|
|
59
|
+
"get_node_detail",
|
|
60
|
+
"get_run_events",
|
|
61
|
+
"hijack",
|
|
62
|
+
"list_artifacts",
|
|
63
|
+
"node",
|
|
64
|
+
"openapi_list",
|
|
65
|
+
"rag_ingest",
|
|
66
|
+
"rag_query",
|
|
67
|
+
"replay",
|
|
68
|
+
"revert",
|
|
69
|
+
"revert_attempt",
|
|
70
|
+
"retry-task",
|
|
71
|
+
"scores",
|
|
72
|
+
"timeline",
|
|
73
|
+
"timetravel",
|
|
74
|
+
]);
|
|
75
|
+
const DESTRUCTIVE_TOOL_NAMES = new Set([
|
|
76
|
+
"approve",
|
|
77
|
+
"cancel",
|
|
78
|
+
"cron_add",
|
|
79
|
+
"cron_rm",
|
|
80
|
+
"cron_start",
|
|
81
|
+
"deny",
|
|
82
|
+
"down",
|
|
83
|
+
"fork",
|
|
84
|
+
"replay",
|
|
85
|
+
"resolve_approval",
|
|
86
|
+
"revert",
|
|
87
|
+
"revert_attempt",
|
|
88
|
+
"retry-task",
|
|
89
|
+
"run_workflow",
|
|
90
|
+
"signal",
|
|
91
|
+
"timetravel",
|
|
92
|
+
"up",
|
|
93
|
+
"workflow_create",
|
|
94
|
+
"workflow_run",
|
|
95
|
+
]);
|
|
96
|
+
/**
|
|
97
|
+
* @param {string | null | undefined} description
|
|
98
|
+
*/
|
|
99
|
+
function normalizeDescription(description) {
|
|
100
|
+
return (description ?? "")
|
|
101
|
+
.replace(/\s+/g, " ")
|
|
102
|
+
.trim();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} name
|
|
106
|
+
* @returns {SmithersAgentToolCategory}
|
|
107
|
+
*/
|
|
108
|
+
function inferCategory(name) {
|
|
109
|
+
if (WORKFLOW_TOOL_NAMES.has(name) || name.startsWith("workflow_")) {
|
|
110
|
+
return "workflows";
|
|
111
|
+
}
|
|
112
|
+
if (APPROVAL_TOOL_NAMES.has(name)) {
|
|
113
|
+
return "approvals";
|
|
114
|
+
}
|
|
115
|
+
if (RUN_TOOL_NAMES.has(name)) {
|
|
116
|
+
return "runs";
|
|
117
|
+
}
|
|
118
|
+
if (DEBUG_TOOL_NAMES.has(name)) {
|
|
119
|
+
return "debug";
|
|
120
|
+
}
|
|
121
|
+
if (name.startsWith("memory_") || name.startsWith("cron_")) {
|
|
122
|
+
return "admin";
|
|
123
|
+
}
|
|
124
|
+
return "admin";
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* @param {string} name
|
|
128
|
+
* @param {string} description
|
|
129
|
+
*/
|
|
130
|
+
function isDestructive(name, description) {
|
|
131
|
+
return (DESTRUCTIVE_TOOL_NAMES.has(name) ||
|
|
132
|
+
description.toLowerCase().startsWith("destructive:"));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* @param {SmithersAgentContractTool[]} tools
|
|
136
|
+
*/
|
|
137
|
+
function sortTools(tools) {
|
|
138
|
+
return [...tools].sort((left, right) => {
|
|
139
|
+
const categoryDelta = TOOL_CATEGORY_ORDER.indexOf(left.category) -
|
|
140
|
+
TOOL_CATEGORY_ORDER.indexOf(right.category);
|
|
141
|
+
if (categoryDelta !== 0) {
|
|
142
|
+
return categoryDelta;
|
|
143
|
+
}
|
|
144
|
+
return left.name.localeCompare(right.name);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @param {SmithersAgentContract} contract
|
|
149
|
+
*/
|
|
150
|
+
function renderDocsGuidance(contract) {
|
|
151
|
+
const lines = [
|
|
152
|
+
`## Smithers ${contract.toolSurface} Tool Surface`,
|
|
153
|
+
"",
|
|
154
|
+
"| Tool | Category | Destructive | Description |",
|
|
155
|
+
"| --- | --- | --- | --- |",
|
|
156
|
+
];
|
|
157
|
+
for (const tool of contract.tools) {
|
|
158
|
+
lines.push(`| \`${tool.name}\` | ${tool.category} | ${tool.destructive ? "yes" : "no"} | ${tool.description || "No description provided."} |`);
|
|
159
|
+
}
|
|
160
|
+
return lines.join("\n");
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @param {CreateSmithersAgentContractOptions} options
|
|
164
|
+
* @returns {SmithersAgentContract}
|
|
165
|
+
*/
|
|
166
|
+
export function createSmithersAgentContract(options) {
|
|
167
|
+
const toolSurface = options.toolSurface ?? "semantic";
|
|
168
|
+
const serverName = options.serverName ?? DEFAULT_SERVER_NAME;
|
|
169
|
+
const tools = sortTools(options.tools.map((tool) => {
|
|
170
|
+
const description = normalizeDescription(tool.description);
|
|
171
|
+
return {
|
|
172
|
+
name: tool.name,
|
|
173
|
+
description,
|
|
174
|
+
destructive: isDestructive(tool.name, description),
|
|
175
|
+
category: inferCategory(tool.name),
|
|
176
|
+
};
|
|
177
|
+
}));
|
|
178
|
+
const contract = {
|
|
179
|
+
toolSurface,
|
|
180
|
+
serverName,
|
|
181
|
+
tools,
|
|
182
|
+
promptGuidance: "",
|
|
183
|
+
docsGuidance: "",
|
|
184
|
+
};
|
|
185
|
+
contract.promptGuidance = renderSmithersAgentPromptGuidance(contract);
|
|
186
|
+
contract.docsGuidance = renderDocsGuidance(contract);
|
|
187
|
+
return contract;
|
|
188
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @smithers-type-exports-begin
|
|
2
|
+
/** @typedef {import("./SmithersAgentContract.ts").SmithersAgentContract} SmithersAgentContract */
|
|
3
|
+
/** @typedef {import("./SmithersAgentContractTool.ts").SmithersAgentContractTool} SmithersAgentContractTool */
|
|
4
|
+
/** @typedef {import("./SmithersAgentToolCategory.ts").SmithersAgentToolCategory} SmithersAgentToolCategory */
|
|
5
|
+
/** @typedef {import("./SmithersListedTool.ts").SmithersListedTool} SmithersListedTool */
|
|
6
|
+
/** @typedef {import("./SmithersToolSurface.ts").SmithersToolSurface} SmithersToolSurface */
|
|
7
|
+
// @smithers-type-exports-end
|
|
8
|
+
|
|
9
|
+
export { renderSmithersAgentPromptGuidance } from "./renderSmithersAgentPromptGuidance.js";
|
|
10
|
+
export { createSmithersAgentContract } from "./createSmithersAgentContract.js";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
/** @typedef {import("./SmithersAgentContractTool.ts").SmithersAgentContractTool} SmithersAgentContractTool */
|
|
3
|
+
/** @typedef {import("./SmithersAgentToolCategory.ts").SmithersAgentToolCategory} SmithersAgentToolCategory */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{ available?: boolean; toolNamePrefix?: string; }} RenderGuidanceOptions
|
|
7
|
+
*/
|
|
8
|
+
/** @typedef {import("./SmithersAgentContract.ts").SmithersAgentContract} SmithersAgentContract */
|
|
9
|
+
const PROMPT_CATEGORY_ORDER = [
|
|
10
|
+
"workflows",
|
|
11
|
+
"runs",
|
|
12
|
+
"approvals",
|
|
13
|
+
"debug",
|
|
14
|
+
];
|
|
15
|
+
const CATEGORY_LABELS = {
|
|
16
|
+
workflows: "workflow discovery and launch",
|
|
17
|
+
runs: "run inspection and control",
|
|
18
|
+
approvals: "approval handling",
|
|
19
|
+
debug: "debugging and evidence gathering",
|
|
20
|
+
admin: "administration and maintenance",
|
|
21
|
+
};
|
|
22
|
+
const TOOL_CATEGORY_ORDER = [
|
|
23
|
+
"workflows",
|
|
24
|
+
"runs",
|
|
25
|
+
"approvals",
|
|
26
|
+
"debug",
|
|
27
|
+
"admin",
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* @param {Pick<SmithersAgentContractTool, "name">} tool
|
|
31
|
+
*/
|
|
32
|
+
function displayToolName(tool, prefix = "") {
|
|
33
|
+
return `\`${prefix}${tool.name}\``;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @param {SmithersAgentContractTool[]} tools
|
|
37
|
+
*/
|
|
38
|
+
function joinToolNames(tools, prefix = "") {
|
|
39
|
+
return tools.map((tool) => displayToolName(tool, prefix)).join(", ");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @param {SmithersAgentContractTool[]} tools
|
|
43
|
+
* @returns {Map<SmithersAgentToolCategory, SmithersAgentContractTool[]>}
|
|
44
|
+
*/
|
|
45
|
+
function groupToolsByCategory(tools) {
|
|
46
|
+
const grouped = new Map();
|
|
47
|
+
for (const category of TOOL_CATEGORY_ORDER) {
|
|
48
|
+
grouped.set(category, []);
|
|
49
|
+
}
|
|
50
|
+
for (const tool of tools) {
|
|
51
|
+
grouped.get(tool.category).push(tool);
|
|
52
|
+
}
|
|
53
|
+
return grouped;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* @param {SmithersAgentContract} contract
|
|
57
|
+
* @param {RenderGuidanceOptions} [options]
|
|
58
|
+
*/
|
|
59
|
+
export function renderSmithersAgentPromptGuidance(contract, options = {}) {
|
|
60
|
+
const grouped = groupToolsByCategory(contract.tools);
|
|
61
|
+
const prefix = options.toolNamePrefix ?? "";
|
|
62
|
+
const available = options.available ?? true;
|
|
63
|
+
const lines = [
|
|
64
|
+
available
|
|
65
|
+
? `You have access to the live Smithers ${contract.toolSurface} MCP surface on server "${contract.serverName}".`
|
|
66
|
+
: `The live Smithers ${contract.toolSurface} MCP contract for server "${contract.serverName}" is listed below, but MCP is disabled for this run.`,
|
|
67
|
+
"Only rely on the tool names listed here.",
|
|
68
|
+
];
|
|
69
|
+
for (const category of PROMPT_CATEGORY_ORDER) {
|
|
70
|
+
const tools = grouped.get(category) ?? [];
|
|
71
|
+
if (tools.length === 0) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
lines.push(`For ${CATEGORY_LABELS[category]}, use ${joinToolNames(tools, prefix)}.`);
|
|
75
|
+
}
|
|
76
|
+
const destructiveTools = contract.tools.filter((tool) => tool.destructive);
|
|
77
|
+
if (destructiveTools.length > 0) {
|
|
78
|
+
lines.push(`Potentially destructive tools: ${joinToolNames(destructiveTools, prefix)}. Confirm intent before using them unless the user already asked for that action.`);
|
|
79
|
+
}
|
|
80
|
+
return lines.join("\n");
|
|
81
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentToolDescriptor } from "./AgentToolDescriptor";
|
|
2
|
+
|
|
3
|
+
export type AgentCapabilityRegistry = {
|
|
4
|
+
version: 1;
|
|
5
|
+
engine: "claude-code" | "codex" | "gemini" | "kimi" | "pi" | "amp" | "forge";
|
|
6
|
+
runtimeTools: Record<string, AgentToolDescriptor>;
|
|
7
|
+
mcp: {
|
|
8
|
+
bootstrap: "inline-config" | "project-config" | "allow-list" | "unsupported";
|
|
9
|
+
supportsProjectScope: boolean;
|
|
10
|
+
supportsUserScope: boolean;
|
|
11
|
+
};
|
|
12
|
+
skills: {
|
|
13
|
+
supportsSkills: boolean;
|
|
14
|
+
installMode?: "files" | "dir" | "plugin";
|
|
15
|
+
smithersSkillIds: string[];
|
|
16
|
+
};
|
|
17
|
+
humanInteraction: {
|
|
18
|
+
supportsUiRequests: boolean;
|
|
19
|
+
methods: string[];
|
|
20
|
+
};
|
|
21
|
+
builtIns: string[];
|
|
22
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { normalizeCapabilityRegistry } from "./normalizeCapabilityRegistry.js";
|
|
3
|
+
/** @typedef {import("./AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {unknown} value
|
|
7
|
+
* @returns {StableJson}
|
|
8
|
+
*/
|
|
9
|
+
function toStableJson(value) {
|
|
10
|
+
if (value === null ||
|
|
11
|
+
typeof value === "boolean" ||
|
|
12
|
+
typeof value === "number" ||
|
|
13
|
+
typeof value === "string") {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map((entry) => toStableJson(entry));
|
|
18
|
+
}
|
|
19
|
+
if (!value || typeof value !== "object") {
|
|
20
|
+
return String(value);
|
|
21
|
+
}
|
|
22
|
+
return Object.fromEntries(Object.entries(value)
|
|
23
|
+
.filter(([, entry]) => entry !== undefined)
|
|
24
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
25
|
+
.map(([key, entry]) => [key, toStableJson(entry)]));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @param {unknown} value
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function stableStringify(value) {
|
|
32
|
+
return JSON.stringify(toStableJson(value));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @param {AgentCapabilityRegistry | null | undefined} registry
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
export function hashCapabilityRegistry(registry) {
|
|
39
|
+
const input = stableStringify({
|
|
40
|
+
capabilityRegistry: normalizeCapabilityRegistry(registry),
|
|
41
|
+
});
|
|
42
|
+
return createHash("sha256").update(input).digest("hex");
|
|
43
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @smithers-type-exports-begin
|
|
2
|
+
/** @typedef {import("./AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
3
|
+
/** @typedef {import("./AgentToolDescriptor.ts").AgentToolDescriptor} AgentToolDescriptor */
|
|
4
|
+
// @smithers-type-exports-end
|
|
5
|
+
|
|
6
|
+
export { normalizeCapabilityStringList } from "./normalizeCapabilityStringList.js";
|
|
7
|
+
export { normalizeCapabilityRegistry } from "./normalizeCapabilityRegistry.js";
|
|
8
|
+
export { hashCapabilityRegistry } from "./hashCapabilityRegistry.js";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { normalizeCapabilityStringList } from "./normalizeCapabilityStringList.js";
|
|
2
|
+
/** @typedef {import("./AgentToolDescriptor.ts").AgentToolDescriptor} AgentToolDescriptor */
|
|
3
|
+
|
|
4
|
+
/** @typedef {import("./AgentCapabilityRegistry.ts").AgentCapabilityRegistry} AgentCapabilityRegistry */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {AgentToolDescriptor | null | undefined} descriptor
|
|
8
|
+
* @returns {AgentToolDescriptor}
|
|
9
|
+
*/
|
|
10
|
+
function normalizeToolDescriptor(descriptor) {
|
|
11
|
+
return {
|
|
12
|
+
description: descriptor?.description?.trim() || undefined,
|
|
13
|
+
source: descriptor?.source,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* @param {AgentCapabilityRegistry | null | undefined} registry
|
|
18
|
+
* @returns {AgentCapabilityRegistry | null}
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeCapabilityRegistry(registry) {
|
|
21
|
+
if (!registry) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const runtimeTools = Object.fromEntries(Object.entries(registry.runtimeTools ?? {})
|
|
25
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
26
|
+
.map(([name, descriptor]) => [name, normalizeToolDescriptor(descriptor)]));
|
|
27
|
+
return {
|
|
28
|
+
version: 1,
|
|
29
|
+
engine: registry.engine,
|
|
30
|
+
runtimeTools,
|
|
31
|
+
mcp: {
|
|
32
|
+
bootstrap: registry.mcp.bootstrap,
|
|
33
|
+
supportsProjectScope: registry.mcp.supportsProjectScope,
|
|
34
|
+
supportsUserScope: registry.mcp.supportsUserScope,
|
|
35
|
+
},
|
|
36
|
+
skills: registry.skills.supportsSkills
|
|
37
|
+
? {
|
|
38
|
+
supportsSkills: true,
|
|
39
|
+
installMode: registry.skills.installMode,
|
|
40
|
+
smithersSkillIds: normalizeCapabilityStringList(registry.skills.smithersSkillIds),
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
supportsSkills: false,
|
|
44
|
+
smithersSkillIds: normalizeCapabilityStringList(registry.skills.smithersSkillIds),
|
|
45
|
+
},
|
|
46
|
+
humanInteraction: {
|
|
47
|
+
supportsUiRequests: registry.humanInteraction.supportsUiRequests,
|
|
48
|
+
methods: normalizeCapabilityStringList(registry.humanInteraction.methods),
|
|
49
|
+
},
|
|
50
|
+
builtIns: normalizeCapabilityStringList(registry.builtIns),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {readonly string[] | null | undefined} values
|
|
3
|
+
* @returns {string[]}
|
|
4
|
+
*/
|
|
5
|
+
export function normalizeCapabilityStringList(values) {
|
|
6
|
+
return [...new Set((values ?? [])
|
|
7
|
+
.map((value) => value.trim())
|
|
8
|
+
.filter(Boolean))].sort((left, right) => left.localeCompare(right));
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CliAgentCapabilityReportEntry } from "./CliAgentCapabilityReportEntry";
|
|
2
|
+
|
|
3
|
+
export type CliAgentCapabilityIssue = {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
severity: "error" | "warning";
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type CliAgentCapabilityDoctorEntry = CliAgentCapabilityReportEntry & {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
issues: CliAgentCapabilityIssue[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CliAgentCapabilityDoctorReport = {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
issueCount: number;
|
|
17
|
+
agents: CliAgentCapabilityDoctorEntry[];
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AgentCapabilityRegistry } from "../capability-registry";
|
|
2
|
+
import type { CliAgentCapabilityAdapterId } from "./CliAgentCapabilityAdapterId";
|
|
3
|
+
|
|
4
|
+
export type CliAgentCapabilityReportEntry = {
|
|
5
|
+
id: CliAgentCapabilityAdapterId;
|
|
6
|
+
binary: string;
|
|
7
|
+
fingerprint: string;
|
|
8
|
+
capabilities: AgentCapabilityRegistry;
|
|
9
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
/** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityDoctorReport} CliAgentCapabilityDoctorReport */
|
|
3
|
+
/**
|
|
4
|
+
* @param {CliAgentCapabilityDoctorReport} report
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function formatCliAgentCapabilityDoctorReport(report) {
|
|
8
|
+
if (report.ok) {
|
|
9
|
+
return `All ${report.agents.length} built-in CLI agent capability registries passed.`;
|
|
10
|
+
}
|
|
11
|
+
const lines = [
|
|
12
|
+
`Capability issues found: ${report.issueCount}`,
|
|
13
|
+
];
|
|
14
|
+
for (const agent of report.agents) {
|
|
15
|
+
if (agent.issues.length === 0) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
lines.push(`${agent.id} (${agent.capabilities.engine})`);
|
|
19
|
+
for (const issue of agent.issues) {
|
|
20
|
+
lines.push(` - [${issue.severity}] ${issue.code}: ${issue.message}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return lines.join("\n");
|
|
24
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getCliAgentCapabilityReport } from "./getCliAgentCapabilityReport.js";
|
|
2
|
+
/** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityDoctorReport} CliAgentCapabilityDoctorReport */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {AgentCapabilityRegistry} registry
|
|
6
|
+
* @returns {CliAgentCapabilityIssue[]}
|
|
7
|
+
*/
|
|
8
|
+
function diagnoseCapabilityRegistry(registry) {
|
|
9
|
+
const issues = [];
|
|
10
|
+
if (registry.version !== 1) {
|
|
11
|
+
issues.push({
|
|
12
|
+
code: "registry-version",
|
|
13
|
+
message: `Expected capability registry version 1, received ${registry.version}.`,
|
|
14
|
+
severity: "error",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (!registry.skills.supportsSkills) {
|
|
18
|
+
if (registry.skills.installMode) {
|
|
19
|
+
issues.push({
|
|
20
|
+
code: "skills-install-mode-without-support",
|
|
21
|
+
message: "Skills install mode is set even though the adapter declares no skills support.",
|
|
22
|
+
severity: "error",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (registry.skills.smithersSkillIds.length > 0) {
|
|
26
|
+
issues.push({
|
|
27
|
+
code: "skills-listed-without-support",
|
|
28
|
+
message: "Smithers skills are listed even though the adapter declares no skills support.",
|
|
29
|
+
severity: "error",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (!registry.skills.installMode) {
|
|
34
|
+
issues.push({
|
|
35
|
+
code: "missing-skills-install-mode",
|
|
36
|
+
message: "Adapters that support skills must declare an install mode.",
|
|
37
|
+
severity: "error",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!registry.humanInteraction.supportsUiRequests) {
|
|
41
|
+
if (registry.humanInteraction.methods.length > 0) {
|
|
42
|
+
issues.push({
|
|
43
|
+
code: "ui-methods-without-support",
|
|
44
|
+
message: "UI request methods are listed even though the adapter declares no UI request support.",
|
|
45
|
+
severity: "error",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (registry.humanInteraction.methods.length === 0) {
|
|
50
|
+
issues.push({
|
|
51
|
+
code: "missing-ui-methods",
|
|
52
|
+
message: "Adapters that support UI requests must declare at least one method.",
|
|
53
|
+
severity: "error",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (registry.mcp.bootstrap === "unsupported") {
|
|
57
|
+
if (registry.mcp.supportsProjectScope || registry.mcp.supportsUserScope) {
|
|
58
|
+
issues.push({
|
|
59
|
+
code: "unsupported-mcp-with-scope",
|
|
60
|
+
message: "Unsupported MCP adapters cannot advertise project or user scope support.",
|
|
61
|
+
severity: "error",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (!registry.mcp.supportsProjectScope && !registry.mcp.supportsUserScope) {
|
|
66
|
+
issues.push({
|
|
67
|
+
code: "mcp-bootstrap-without-scope",
|
|
68
|
+
message: "Supported MCP adapters should advertise at least one supported scope.",
|
|
69
|
+
severity: "warning",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return issues;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @returns {CliAgentCapabilityDoctorReport}
|
|
76
|
+
*/
|
|
77
|
+
export function getCliAgentCapabilityDoctorReport() {
|
|
78
|
+
const agents = getCliAgentCapabilityReport().map((entry) => {
|
|
79
|
+
const issues = diagnoseCapabilityRegistry(entry.capabilities);
|
|
80
|
+
return {
|
|
81
|
+
...entry,
|
|
82
|
+
ok: issues.length === 0,
|
|
83
|
+
issues,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
const issueCount = agents.reduce((count, entry) => count + entry.issues.length, 0);
|
|
87
|
+
return {
|
|
88
|
+
ok: issueCount === 0,
|
|
89
|
+
issueCount,
|
|
90
|
+
agents,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { hashCapabilityRegistry, normalizeCapabilityRegistry, } from "../capability-registry/index.js";
|
|
2
|
+
import { createClaudeCodeCapabilityRegistry } from "../ClaudeCodeAgent.js";
|
|
3
|
+
import { createCodexCapabilityRegistry } from "../CodexAgent.js";
|
|
4
|
+
import { createGeminiCapabilityRegistry } from "../GeminiAgent.js";
|
|
5
|
+
import { createKimiCapabilityRegistry } from "../KimiAgent.js";
|
|
6
|
+
import { createPiCapabilityRegistry } from "../PiAgent.js";
|
|
7
|
+
/** @typedef {import("./CliAgentCapabilityReportEntry.ts").CliAgentCapabilityReportEntry} CliAgentCapabilityReportEntry */
|
|
8
|
+
|
|
9
|
+
const CLI_AGENT_CAPABILITY_ADAPTERS = [
|
|
10
|
+
{
|
|
11
|
+
id: "claude",
|
|
12
|
+
binary: "claude",
|
|
13
|
+
buildRegistry: () => createClaudeCodeCapabilityRegistry(),
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "codex",
|
|
17
|
+
binary: "codex",
|
|
18
|
+
buildRegistry: () => createCodexCapabilityRegistry(),
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "gemini",
|
|
22
|
+
binary: "gemini",
|
|
23
|
+
buildRegistry: () => createGeminiCapabilityRegistry(),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "kimi",
|
|
27
|
+
binary: "kimi",
|
|
28
|
+
buildRegistry: () => createKimiCapabilityRegistry(),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "pi",
|
|
32
|
+
binary: "pi",
|
|
33
|
+
buildRegistry: () => createPiCapabilityRegistry(),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* @returns {CliAgentCapabilityReportEntry[]}
|
|
38
|
+
*/
|
|
39
|
+
export function getCliAgentCapabilityReport() {
|
|
40
|
+
return CLI_AGENT_CAPABILITY_ADAPTERS.map((adapter) => {
|
|
41
|
+
const capabilities = normalizeCapabilityRegistry(adapter.buildRegistry());
|
|
42
|
+
if (!capabilities) {
|
|
43
|
+
throw new Error(`Capability registry missing for adapter ${adapter.id}`);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
id: adapter.id,
|
|
47
|
+
binary: adapter.binary,
|
|
48
|
+
fingerprint: hashCapabilityRegistry(capabilities),
|
|
49
|
+
capabilities,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @smithers-type-exports-begin
|
|
2
|
+
/** @typedef {import("./CliAgentCapabilityAdapterId.ts").CliAgentCapabilityAdapterId} CliAgentCapabilityAdapterId */
|
|
3
|
+
/** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityDoctorEntry} CliAgentCapabilityDoctorEntry */
|
|
4
|
+
/** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityDoctorReport} CliAgentCapabilityDoctorReport */
|
|
5
|
+
/** @typedef {import("./CliAgentCapabilityDoctorReport.ts").CliAgentCapabilityIssue} CliAgentCapabilityIssue */
|
|
6
|
+
/** @typedef {import("./CliAgentCapabilityReportEntry.ts").CliAgentCapabilityReportEntry} CliAgentCapabilityReportEntry */
|
|
7
|
+
// @smithers-type-exports-end
|
|
8
|
+
|
|
9
|
+
export { getCliAgentCapabilityReport } from "./getCliAgentCapabilityReport.js";
|
|
10
|
+
export { getCliAgentCapabilityDoctorReport } from "./getCliAgentCapabilityDoctorReport.js";
|
|
11
|
+
export { formatCliAgentCapabilityDoctorReport } from "./formatCliAgentCapabilityDoctorReport.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DiagnosticCheckId } from "./DiagnosticCheckId";
|
|
2
|
+
|
|
3
|
+
export type DiagnosticCheckStatus = "pass" | "fail" | "skip" | "error";
|
|
4
|
+
|
|
5
|
+
export type DiagnosticCheck = {
|
|
6
|
+
id: DiagnosticCheckId;
|
|
7
|
+
status: DiagnosticCheckStatus;
|
|
8
|
+
message: string;
|
|
9
|
+
detail?: Record<string, unknown>;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
};
|