@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,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,4 @@
1
+ export type AgentToolDescriptor = {
2
+ description?: string;
3
+ source?: "builtin" | "mcp" | "extension" | "skill" | "runtime";
4
+ };
@@ -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,6 @@
1
+ export type CliAgentCapabilityAdapterId =
2
+ | "claude"
3
+ | "codex"
4
+ | "gemini"
5
+ | "kimi"
6
+ | "pi";
@@ -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
+ };
@@ -0,0 +1,4 @@
1
+ export type DiagnosticCheckId =
2
+ | "cli_installed"
3
+ | "api_key_valid"
4
+ | "rate_limit_status";
@@ -0,0 +1,4 @@
1
+ export type DiagnosticContext = {
2
+ env: Record<string, string>;
3
+ cwd: string;
4
+ };