chainlesschain 0.45.70 → 0.45.74
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 +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/Analytics-B4OM8S8X.css +1 -0
- package/src/assets/web-panel/assets/Analytics-sBrYoc3A.js +3 -0
- package/src/assets/web-panel/assets/AppLayout-BhJ3YFWt.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-Cr2lWhF-.css +1 -0
- package/src/assets/web-panel/assets/Backup-D68fenbD.js +1 -0
- package/src/assets/web-panel/assets/Backup-fZqtfC1m.css +1 -0
- package/src/assets/web-panel/assets/{Chat-DXtvKoM0.js → Chat-DaxTP3x8.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-BJ4ODHOy.js → Cron-CNs03iHJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BZd4wDPQ.js → Dashboard-CjlX4CrX.js} +2 -2
- package/src/assets/web-panel/assets/Git-CCMVr3Y8.js +2 -0
- package/src/assets/web-panel/assets/Git-DGcuBXST.css +1 -0
- package/src/assets/web-panel/assets/{Logs-CSeKZEG_.js → Logs-BY6A0UNG.js} +2 -2
- package/src/assets/web-panel/assets/{McpTools-BYQAK11r.js → McpTools-CrBVYlg6.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-gkUAPyuZ.js → Memory-CWx3SpUt.js} +2 -2
- package/src/assets/web-panel/assets/{Notes-bjNrQgAo.js → Notes-1LcGD49x.js} +2 -2
- package/src/assets/web-panel/assets/Organization-DdOOM4ic.css +1 -0
- package/src/assets/web-panel/assets/Organization-Dx2DhbkM.js +4 -0
- package/src/assets/web-panel/assets/P2P-B16fjqfJ.js +2 -0
- package/src/assets/web-panel/assets/P2P-OEzOeMZX.css +1 -0
- package/src/assets/web-panel/assets/Permissions-BQbC9FzG.js +4 -0
- package/src/assets/web-panel/assets/Permissions-C9WlkGl-.css +1 -0
- package/src/assets/web-panel/assets/Projects-CjhZbNYm.js +2 -0
- package/src/assets/web-panel/assets/Projects-DxKelI5h.css +1 -0
- package/src/assets/web-panel/assets/Providers-BEakqcO5.css +1 -0
- package/src/assets/web-panel/assets/Providers-ivOAQtHM.js +2 -0
- package/src/assets/web-panel/assets/RssFeed-BlFC20eg.css +1 -0
- package/src/assets/web-panel/assets/RssFeed-BrsErdrU.js +3 -0
- package/src/assets/web-panel/assets/Security-DnEvJU5h.js +4 -0
- package/src/assets/web-panel/assets/Security-Dwxw7rfP.css +1 -0
- package/src/assets/web-panel/assets/{Services-CS0oMdxh.js → Services-7jQywNbl.js} +2 -2
- package/src/assets/web-panel/assets/Skills-BCvgBkD3.js +1 -0
- package/src/assets/web-panel/assets/{Tasks-qULws8pc.js → Tasks-CmJBC1cf.js} +1 -1
- package/src/assets/web-panel/assets/Templates-DOY_oZnm.css +1 -0
- package/src/assets/web-panel/assets/Templates-RXT8-DNk.js +1 -0
- package/src/assets/web-panel/assets/Wallet-3iYASEx_.js +4 -0
- package/src/assets/web-panel/assets/Wallet-DnIumafl.css +1 -0
- package/src/assets/web-panel/assets/WebAuthn-CNPl2VQR.css +1 -0
- package/src/assets/web-panel/assets/WebAuthn-s3Hzd9db.js +5 -0
- package/src/assets/web-panel/assets/{antd-CJSBocer.js → antd-gZyc63Qr.js} +114 -114
- package/src/assets/web-panel/assets/chat-BmwHBi9M.js +1 -0
- package/src/assets/web-panel/assets/index-DrmEk9S3.js +2 -0
- package/src/assets/web-panel/assets/{markdown-Bo5cVN4u.js → markdown-Bv7nG63L.js} +1 -1
- package/src/assets/web-panel/assets/ws-CU7Gvoom.js +1 -0
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/doctor.js +33 -151
- package/src/commands/mcp.js +1 -1
- package/src/commands/plugin.js +1 -1
- package/src/commands/session.js +106 -7
- package/src/commands/status.js +39 -69
- package/src/gateways/ws/session-protocol.js +1 -1
- package/src/gateways/ws/ws-agent-handler.js +484 -0
- package/src/gateways/ws/ws-server.js +758 -4
- package/src/gateways/ws/ws-session-gateway.js +1432 -1
- package/src/harness/mcp-client.js +417 -0
- package/src/harness/mock-llm-provider.js +167 -0
- package/src/harness/plugin-manager.js +434 -0
- package/src/lib/agent-core.js +25 -1902
- package/src/lib/hashline.js +208 -0
- package/src/lib/jsonl-session-store.js +11 -0
- package/src/lib/mcp-client.js +14 -412
- package/src/lib/plugin-manager.js +29 -428
- package/src/lib/prompt-compressor.js +11 -0
- package/src/lib/session-hooks.js +61 -0
- package/src/lib/skill-loader.js +4 -0
- package/src/lib/skill-mcp.js +190 -0
- package/src/lib/workflow-state-reader.js +94 -0
- package/src/lib/ws-agent-handler.js +8 -472
- package/src/lib/ws-server.js +12 -756
- package/src/lib/ws-session-manager.js +8 -1417
- package/src/repl/agent-repl.js +27 -3
- package/src/runtime/agent-core.js +1760 -0
- package/src/runtime/agent-runtime.js +3 -1
- package/src/runtime/coding-agent-contract-shared.cjs +496 -0
- package/src/runtime/coding-agent-contract.js +49 -229
- package/src/runtime/coding-agent-policy.cjs +54 -5
- package/src/runtime/diagnostics.js +317 -0
- package/src/runtime/index.js +3 -0
- package/src/tools/index.js +3 -0
- package/src/tools/legacy-agent-tools.js +5 -0
- package/src/assets/web-panel/assets/AppLayout-B_tkw3Pn.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-CFP4dGIJ.css +0 -1
- package/src/assets/web-panel/assets/Providers-Brm-S_hS.css +0 -1
- package/src/assets/web-panel/assets/Providers-Dbf57Tbv.js +0 -1
- package/src/assets/web-panel/assets/Skills-B2fgruv8.js +0 -1
- package/src/assets/web-panel/assets/chat-DnH09sSR.js +0 -1
- package/src/assets/web-panel/assets/index-IK-oro0g.js +0 -2
- package/src/assets/web-panel/assets/ws-DjelKkD6.js +0 -1
|
@@ -1,193 +1,18 @@
|
|
|
1
1
|
import { ToolRegistry, createDefaultToolRegistry } from "../tools/registry.js";
|
|
2
|
-
import
|
|
2
|
+
import sharedContract from "./coding-agent-contract-shared.cjs";
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
CODING_AGENT_EXTENSION_TOOL_NAMES,
|
|
6
|
+
CODING_AGENT_MVP_TOOL_NAMES,
|
|
7
|
+
getCodingAgentFunctionToolDefinition,
|
|
8
|
+
getCodingAgentFunctionToolDefinitions,
|
|
9
|
+
getCodingAgentToolContract,
|
|
10
|
+
getCodingAgentToolContracts,
|
|
11
|
+
getCodingAgentToolPolicy,
|
|
12
|
+
isCodingAgentMvpTool,
|
|
13
|
+
listCodingAgentToolNames,
|
|
14
|
+
} = sharedContract;
|
|
3
15
|
|
|
4
|
-
const { TOOL_POLICY_METADATA } = sharedCodingAgentPolicy;
|
|
5
|
-
|
|
6
|
-
const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
7
|
-
{
|
|
8
|
-
name: "read_file",
|
|
9
|
-
title: "Read File",
|
|
10
|
-
kind: "filesystem",
|
|
11
|
-
tier: "mvp",
|
|
12
|
-
...TOOL_POLICY_METADATA.read_file,
|
|
13
|
-
permissions: {
|
|
14
|
-
level: "readonly",
|
|
15
|
-
scopes: ["filesystem:read"],
|
|
16
|
-
},
|
|
17
|
-
telemetry: {
|
|
18
|
-
category: "filesystem",
|
|
19
|
-
tags: ["tool:read_file", "contract:coding-agent", "tier:mvp"],
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "write_file",
|
|
24
|
-
title: "Write File",
|
|
25
|
-
kind: "filesystem",
|
|
26
|
-
tier: "mvp",
|
|
27
|
-
...TOOL_POLICY_METADATA.write_file,
|
|
28
|
-
permissions: {
|
|
29
|
-
level: "elevated",
|
|
30
|
-
scopes: ["filesystem:write"],
|
|
31
|
-
},
|
|
32
|
-
telemetry: {
|
|
33
|
-
category: "filesystem",
|
|
34
|
-
tags: ["tool:write_file", "contract:coding-agent", "tier:mvp"],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "edit_file",
|
|
39
|
-
title: "Edit File",
|
|
40
|
-
kind: "filesystem",
|
|
41
|
-
tier: "mvp",
|
|
42
|
-
...TOOL_POLICY_METADATA.edit_file,
|
|
43
|
-
permissions: {
|
|
44
|
-
level: "elevated",
|
|
45
|
-
scopes: ["filesystem:write"],
|
|
46
|
-
},
|
|
47
|
-
telemetry: {
|
|
48
|
-
category: "filesystem",
|
|
49
|
-
tags: ["tool:edit_file", "contract:coding-agent", "tier:mvp"],
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "run_shell",
|
|
54
|
-
title: "Run Shell",
|
|
55
|
-
kind: "shell",
|
|
56
|
-
tier: "mvp",
|
|
57
|
-
...TOOL_POLICY_METADATA.run_shell,
|
|
58
|
-
runtimeDescriptor: "shell",
|
|
59
|
-
permissions: {
|
|
60
|
-
level: "elevated",
|
|
61
|
-
scopes: ["process:spawn", "filesystem:workspace"],
|
|
62
|
-
},
|
|
63
|
-
telemetry: {
|
|
64
|
-
category: "shell",
|
|
65
|
-
tags: ["tool:run_shell", "contract:coding-agent", "tier:mvp"],
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "git",
|
|
70
|
-
title: "Git",
|
|
71
|
-
kind: "git",
|
|
72
|
-
tier: "mvp",
|
|
73
|
-
...TOOL_POLICY_METADATA.git,
|
|
74
|
-
runtimeDescriptor: "git",
|
|
75
|
-
permissions: {
|
|
76
|
-
level: "elevated",
|
|
77
|
-
scopes: ["process:spawn", "filesystem:workspace", "vcs:git"],
|
|
78
|
-
},
|
|
79
|
-
telemetry: {
|
|
80
|
-
category: "git",
|
|
81
|
-
tags: ["tool:git", "contract:coding-agent", "tier:mvp"],
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
name: "search_files",
|
|
86
|
-
title: "Search Files",
|
|
87
|
-
kind: "filesystem",
|
|
88
|
-
tier: "mvp",
|
|
89
|
-
...TOOL_POLICY_METADATA.search_files,
|
|
90
|
-
permissions: {
|
|
91
|
-
level: "readonly",
|
|
92
|
-
scopes: ["filesystem:read", "search:content"],
|
|
93
|
-
},
|
|
94
|
-
telemetry: {
|
|
95
|
-
category: "filesystem",
|
|
96
|
-
tags: ["tool:search_files", "contract:coding-agent", "tier:mvp"],
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "list_dir",
|
|
101
|
-
title: "List Directory",
|
|
102
|
-
kind: "filesystem",
|
|
103
|
-
tier: "mvp",
|
|
104
|
-
...TOOL_POLICY_METADATA.list_dir,
|
|
105
|
-
permissions: {
|
|
106
|
-
level: "readonly",
|
|
107
|
-
scopes: ["filesystem:read"],
|
|
108
|
-
},
|
|
109
|
-
telemetry: {
|
|
110
|
-
category: "filesystem",
|
|
111
|
-
tags: ["tool:list_dir", "contract:coding-agent", "tier:mvp"],
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: "run_skill",
|
|
116
|
-
title: "Run Skill",
|
|
117
|
-
kind: "skill",
|
|
118
|
-
tier: "extension",
|
|
119
|
-
...TOOL_POLICY_METADATA.run_skill,
|
|
120
|
-
permissions: {
|
|
121
|
-
level: "standard",
|
|
122
|
-
scopes: ["skill:invoke"],
|
|
123
|
-
},
|
|
124
|
-
telemetry: {
|
|
125
|
-
category: "skill",
|
|
126
|
-
tags: ["tool:run_skill", "contract:coding-agent", "tier:extension"],
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
name: "list_skills",
|
|
131
|
-
title: "List Skills",
|
|
132
|
-
kind: "skill",
|
|
133
|
-
tier: "extension",
|
|
134
|
-
...TOOL_POLICY_METADATA.list_skills,
|
|
135
|
-
permissions: {
|
|
136
|
-
level: "readonly",
|
|
137
|
-
scopes: ["skill:read"],
|
|
138
|
-
},
|
|
139
|
-
telemetry: {
|
|
140
|
-
category: "skill",
|
|
141
|
-
tags: ["tool:list_skills", "contract:coding-agent", "tier:extension"],
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: "run_code",
|
|
146
|
-
title: "Run Code",
|
|
147
|
-
kind: "code",
|
|
148
|
-
tier: "extension",
|
|
149
|
-
...TOOL_POLICY_METADATA.run_code,
|
|
150
|
-
permissions: {
|
|
151
|
-
level: "elevated",
|
|
152
|
-
scopes: ["process:spawn", "filesystem:workspace", "runtime:script"],
|
|
153
|
-
},
|
|
154
|
-
telemetry: {
|
|
155
|
-
category: "code",
|
|
156
|
-
tags: ["tool:run_code", "contract:coding-agent", "tier:extension"],
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
name: "spawn_sub_agent",
|
|
161
|
-
title: "Spawn Sub Agent",
|
|
162
|
-
kind: "agent",
|
|
163
|
-
tier: "extension",
|
|
164
|
-
...TOOL_POLICY_METADATA.spawn_sub_agent,
|
|
165
|
-
permissions: {
|
|
166
|
-
level: "elevated",
|
|
167
|
-
scopes: ["agent:spawn"],
|
|
168
|
-
},
|
|
169
|
-
telemetry: {
|
|
170
|
-
category: "agent",
|
|
171
|
-
tags: ["tool:spawn_sub_agent", "contract:coding-agent", "tier:extension"],
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
]);
|
|
175
|
-
|
|
176
|
-
export const CODING_AGENT_MVP_TOOL_NAMES = Object.freeze(
|
|
177
|
-
CODING_AGENT_TOOL_CONTRACTS.filter((tool) => tool.tier === "mvp").map(
|
|
178
|
-
(tool) => tool.name,
|
|
179
|
-
),
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
export const CODING_AGENT_EXTENSION_TOOL_NAMES = Object.freeze(
|
|
183
|
-
CODING_AGENT_TOOL_CONTRACTS.filter((tool) => tool.tier === "extension").map(
|
|
184
|
-
(tool) => tool.name,
|
|
185
|
-
),
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const TOOL_CONTRACT_MAP = new Map(
|
|
189
|
-
CODING_AGENT_TOOL_CONTRACTS.map((tool) => [tool.name, tool]),
|
|
190
|
-
);
|
|
191
16
|
const runtimeRegistry = createDefaultToolRegistry();
|
|
192
17
|
|
|
193
18
|
function cloneValue(value) {
|
|
@@ -216,44 +41,17 @@ function getFallbackToolContract(name) {
|
|
|
216
41
|
};
|
|
217
42
|
}
|
|
218
43
|
|
|
219
|
-
export
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}).map(cloneValue);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export function listCodingAgentToolNames({ tier = null } = {}) {
|
|
234
|
-
return getCodingAgentToolContracts({ tier }).map((tool) => tool.name);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function isCodingAgentMvpTool(name) {
|
|
238
|
-
return CODING_AGENT_MVP_TOOL_NAMES.includes(name);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function getCodingAgentToolPolicy(name) {
|
|
242
|
-
const tool = TOOL_CONTRACT_MAP.get(name);
|
|
243
|
-
if (!tool) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return {
|
|
248
|
-
tier: tool.tier,
|
|
249
|
-
riskLevel: tool.riskLevel,
|
|
250
|
-
availableInPlanMode: tool.availableInPlanMode,
|
|
251
|
-
planModeBehavior: tool.planModeBehavior || "standard",
|
|
252
|
-
requiresPlanApproval: tool.requiresPlanApproval,
|
|
253
|
-
approvalFlow: tool.approvalFlow,
|
|
254
|
-
permissions: cloneValue(tool.permissions),
|
|
255
|
-
};
|
|
256
|
-
}
|
|
44
|
+
export {
|
|
45
|
+
CODING_AGENT_EXTENSION_TOOL_NAMES,
|
|
46
|
+
CODING_AGENT_MVP_TOOL_NAMES,
|
|
47
|
+
getCodingAgentFunctionToolDefinition,
|
|
48
|
+
getCodingAgentFunctionToolDefinitions,
|
|
49
|
+
getCodingAgentToolContract,
|
|
50
|
+
getCodingAgentToolContracts,
|
|
51
|
+
getCodingAgentToolPolicy,
|
|
52
|
+
isCodingAgentMvpTool,
|
|
53
|
+
listCodingAgentToolNames,
|
|
54
|
+
};
|
|
257
55
|
|
|
258
56
|
export function mapCodingAgentToolDefinition(definition = {}, options = {}) {
|
|
259
57
|
const fn = definition.function || {};
|
|
@@ -269,8 +67,14 @@ export function mapCodingAgentToolDefinition(definition = {}, options = {}) {
|
|
|
269
67
|
name,
|
|
270
68
|
title: contract.title || name,
|
|
271
69
|
kind: contract.kind,
|
|
272
|
-
description: fn.description || "",
|
|
273
|
-
schema: fn.parameters ||
|
|
70
|
+
description: fn.description || contract.description || "",
|
|
71
|
+
schema: fn.parameters ||
|
|
72
|
+
(contract.inputSchema
|
|
73
|
+
? cloneValue(contract.inputSchema)
|
|
74
|
+
: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {},
|
|
77
|
+
}),
|
|
274
78
|
permissions: cloneValue(contract.permissions),
|
|
275
79
|
telemetry: cloneValue(contract.telemetry),
|
|
276
80
|
source: options.source || "agent-core",
|
|
@@ -286,9 +90,25 @@ export function createCodingAgentToolRegistry(definitions = [], options = {}) {
|
|
|
286
90
|
}
|
|
287
91
|
|
|
288
92
|
export function getCodingAgentRuntimeDescriptor(toolName) {
|
|
289
|
-
const descriptorName =
|
|
93
|
+
const descriptorName = getCodingAgentToolContract(toolName)?.runtimeDescriptor;
|
|
290
94
|
if (!descriptorName) {
|
|
291
95
|
return null;
|
|
292
96
|
}
|
|
293
97
|
return runtimeRegistry.get(descriptorName) || null;
|
|
294
98
|
}
|
|
99
|
+
|
|
100
|
+
export function getCodingAgentRuntimeDescriptorByCommand(command) {
|
|
101
|
+
const trimmed = String(command || "").trim();
|
|
102
|
+
if (!trimmed) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const parts = trimmed.split(/\s+/);
|
|
107
|
+
if (parts[0] === "git") {
|
|
108
|
+
return runtimeRegistry.get("git") || null;
|
|
109
|
+
}
|
|
110
|
+
if (parts[0] === "mcp" || parts.includes("mcp")) {
|
|
111
|
+
return runtimeRegistry.get("mcp") || null;
|
|
112
|
+
}
|
|
113
|
+
return runtimeRegistry.get("shell") || null;
|
|
114
|
+
}
|
|
@@ -74,6 +74,16 @@ const TOOL_POLICY_METADATA = Object.freeze({
|
|
|
74
74
|
approvalFlow: "plan",
|
|
75
75
|
isReadOnly: false,
|
|
76
76
|
},
|
|
77
|
+
edit_file_hashed: {
|
|
78
|
+
riskLevel: RISK_LEVELS.MEDIUM,
|
|
79
|
+
category: TOOL_CATEGORIES.WRITE,
|
|
80
|
+
availableInPlanMode: false,
|
|
81
|
+
planModeBehavior: "blocked",
|
|
82
|
+
requiresPlanApproval: true,
|
|
83
|
+
requiresConfirmation: false,
|
|
84
|
+
approvalFlow: "plan",
|
|
85
|
+
isReadOnly: false,
|
|
86
|
+
},
|
|
77
87
|
write_file: {
|
|
78
88
|
riskLevel: RISK_LEVELS.MEDIUM,
|
|
79
89
|
category: TOOL_CATEGORIES.WRITE,
|
|
@@ -193,14 +203,53 @@ function resolveToolPolicy(toolName, descriptor = null) {
|
|
|
193
203
|
base.riskLevel = normalizeRiskLevel(descriptor.riskLevel, base.riskLevel);
|
|
194
204
|
}
|
|
195
205
|
|
|
206
|
+
if (descriptor?.category) {
|
|
207
|
+
base.category = descriptor.category;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (typeof descriptor?.availableInPlanMode === "boolean") {
|
|
211
|
+
base.availableInPlanMode = descriptor.availableInPlanMode;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (descriptor?.planModeBehavior) {
|
|
215
|
+
base.planModeBehavior = descriptor.planModeBehavior;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (typeof descriptor?.requiresPlanApproval === "boolean") {
|
|
219
|
+
base.requiresPlanApproval = descriptor.requiresPlanApproval;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (typeof descriptor?.requiresConfirmation === "boolean") {
|
|
223
|
+
base.requiresConfirmation = descriptor.requiresConfirmation;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (descriptor?.approvalFlow) {
|
|
227
|
+
base.approvalFlow = descriptor.approvalFlow;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (Array.isArray(descriptor?.readOnlySubcommands)) {
|
|
231
|
+
base.readOnlySubcommands = [...descriptor.readOnlySubcommands];
|
|
232
|
+
}
|
|
233
|
+
|
|
196
234
|
if (descriptor?.isReadOnly === true) {
|
|
197
235
|
base.isReadOnly = true;
|
|
198
236
|
base.riskLevel = RISK_LEVELS.LOW;
|
|
199
|
-
base.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
237
|
+
base.category = descriptor?.category || TOOL_CATEGORIES.READ;
|
|
238
|
+
if (typeof descriptor?.availableInPlanMode !== "boolean") {
|
|
239
|
+
base.availableInPlanMode = true;
|
|
240
|
+
}
|
|
241
|
+
if (!descriptor?.planModeBehavior) {
|
|
242
|
+
base.planModeBehavior = "allow";
|
|
243
|
+
}
|
|
244
|
+
if (typeof descriptor?.requiresPlanApproval !== "boolean") {
|
|
245
|
+
base.requiresPlanApproval = false;
|
|
246
|
+
}
|
|
247
|
+
if (typeof descriptor?.requiresConfirmation !== "boolean") {
|
|
248
|
+
base.requiresConfirmation = false;
|
|
249
|
+
}
|
|
250
|
+
if (!descriptor?.approvalFlow) {
|
|
251
|
+
base.approvalFlow = "auto";
|
|
252
|
+
}
|
|
204
253
|
}
|
|
205
254
|
|
|
206
255
|
return base;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime diagnostics — machine-readable doctor/status reports.
|
|
3
|
+
*
|
|
4
|
+
* Canonical data collectors for `chainlesschain doctor` and
|
|
5
|
+
* `chainlesschain status`. These return plain JSON-serializable
|
|
6
|
+
* objects that both the human-readable CLI renderers and external
|
|
7
|
+
* consumers (IDE integrations, monitoring) can rely on.
|
|
8
|
+
*
|
|
9
|
+
* Introduced as part of the CLI Runtime Convergence roadmap (Phase 5,
|
|
10
|
+
* 2026-04-09) per ADR decision D6 (machine-readable doctor/status JSON).
|
|
11
|
+
*
|
|
12
|
+
* Refs: docs/implementation-plans/CLI_RUNTIME_CONVERGENCE_ADR.md
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execSync } from "node:child_process";
|
|
16
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
17
|
+
import { createConnection } from "node:net";
|
|
18
|
+
import semver from "semver";
|
|
19
|
+
|
|
20
|
+
import { MIN_NODE_VERSION, DEFAULT_PORTS, VERSION } from "../constants.js";
|
|
21
|
+
import { getHomeDir, getConfigPath, getBinDir } from "../lib/paths.js";
|
|
22
|
+
import {
|
|
23
|
+
isDockerAvailable,
|
|
24
|
+
isDockerComposeAvailable,
|
|
25
|
+
getServiceStatus,
|
|
26
|
+
findComposeFile,
|
|
27
|
+
} from "../lib/service-manager.js";
|
|
28
|
+
import { loadConfig } from "../lib/config-manager.js";
|
|
29
|
+
import { isAppRunning, getAppPid } from "../lib/process-manager.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Probe a TCP port. Resolves true if something is listening.
|
|
33
|
+
*/
|
|
34
|
+
export function checkPort(port, host = "127.0.0.1", timeoutMs = 1000) {
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
const socket = createConnection({ port, host, timeout: timeoutMs });
|
|
37
|
+
socket.on("connect", () => {
|
|
38
|
+
socket.destroy();
|
|
39
|
+
resolve(true);
|
|
40
|
+
});
|
|
41
|
+
socket.on("error", () => resolve(false));
|
|
42
|
+
socket.on("timeout", () => {
|
|
43
|
+
socket.destroy();
|
|
44
|
+
resolve(false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readdirSafe(dir) {
|
|
50
|
+
try {
|
|
51
|
+
return readdirSync(dir);
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function safeExec(cmd) {
|
|
58
|
+
try {
|
|
59
|
+
return execSync(cmd, { encoding: "utf-8" }).trim();
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Collect a machine-readable doctor report.
|
|
67
|
+
*
|
|
68
|
+
* Shape:
|
|
69
|
+
* {
|
|
70
|
+
* schema: "chainlesschain.doctor.v1",
|
|
71
|
+
* version: string, // CLI version
|
|
72
|
+
* generatedAt: string, // ISO timestamp
|
|
73
|
+
* checks: Array<{ id, name, ok, optional, detail }>,
|
|
74
|
+
* ports: Array<{ name, port, open }>,
|
|
75
|
+
* disk: { homeDir, freeGB } | null,
|
|
76
|
+
* summary: { total, passed, failed, criticalFailed }
|
|
77
|
+
* }
|
|
78
|
+
*/
|
|
79
|
+
export async function collectDoctorReport() {
|
|
80
|
+
const checks = [];
|
|
81
|
+
|
|
82
|
+
// Node.js
|
|
83
|
+
const nodeVersion = process.versions.node;
|
|
84
|
+
const nodeOk = semver.gte(nodeVersion, MIN_NODE_VERSION);
|
|
85
|
+
checks.push({
|
|
86
|
+
id: "node",
|
|
87
|
+
name: `Node.js ${nodeVersion}`,
|
|
88
|
+
ok: nodeOk,
|
|
89
|
+
optional: false,
|
|
90
|
+
detail: nodeOk ? "" : `Requires >=${MIN_NODE_VERSION}`,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// npm
|
|
94
|
+
const npmVersion = safeExec("npm --version");
|
|
95
|
+
checks.push({
|
|
96
|
+
id: "npm",
|
|
97
|
+
name: npmVersion ? `npm ${npmVersion}` : "npm",
|
|
98
|
+
ok: Boolean(npmVersion),
|
|
99
|
+
optional: false,
|
|
100
|
+
detail: npmVersion ? "" : "Not found",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Docker
|
|
104
|
+
const dockerOk = isDockerAvailable();
|
|
105
|
+
checks.push({
|
|
106
|
+
id: "docker",
|
|
107
|
+
name: "Docker",
|
|
108
|
+
ok: dockerOk,
|
|
109
|
+
optional: true,
|
|
110
|
+
detail: dockerOk ? "" : "Not installed (optional)",
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const composeOk = isDockerComposeAvailable();
|
|
114
|
+
checks.push({
|
|
115
|
+
id: "docker-compose",
|
|
116
|
+
name: "Docker Compose",
|
|
117
|
+
ok: composeOk,
|
|
118
|
+
optional: true,
|
|
119
|
+
detail: composeOk ? "" : "Not installed (optional)",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Git
|
|
123
|
+
const gitVersion = safeExec("git --version");
|
|
124
|
+
checks.push({
|
|
125
|
+
id: "git",
|
|
126
|
+
name: gitVersion || "Git",
|
|
127
|
+
ok: Boolean(gitVersion),
|
|
128
|
+
optional: false,
|
|
129
|
+
detail: gitVersion ? "" : "Not found",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Config directory
|
|
133
|
+
const homeDir = getHomeDir();
|
|
134
|
+
const homeOk = existsSync(homeDir);
|
|
135
|
+
checks.push({
|
|
136
|
+
id: "config-dir",
|
|
137
|
+
name: `Config dir: ${homeDir}`,
|
|
138
|
+
ok: homeOk,
|
|
139
|
+
optional: false,
|
|
140
|
+
detail: homeOk ? "" : 'Run "chainlesschain setup"',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Config file
|
|
144
|
+
const configPath = getConfigPath();
|
|
145
|
+
const configOk = existsSync(configPath);
|
|
146
|
+
checks.push({
|
|
147
|
+
id: "config-file",
|
|
148
|
+
name: "Config file",
|
|
149
|
+
ok: configOk,
|
|
150
|
+
optional: false,
|
|
151
|
+
detail: configOk ? "" : 'Run "chainlesschain setup"',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Desktop binary
|
|
155
|
+
const binDir = getBinDir();
|
|
156
|
+
const hasBin = existsSync(binDir) && readdirSafe(binDir).length > 0;
|
|
157
|
+
checks.push({
|
|
158
|
+
id: "desktop-binary",
|
|
159
|
+
name: "Desktop binary",
|
|
160
|
+
ok: hasBin,
|
|
161
|
+
optional: true,
|
|
162
|
+
detail: hasBin
|
|
163
|
+
? ""
|
|
164
|
+
: 'Run "chainlesschain setup" or "chainlesschain update"',
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Setup completed
|
|
168
|
+
let setupCompleted = false;
|
|
169
|
+
try {
|
|
170
|
+
setupCompleted = Boolean(loadConfig().setupCompleted);
|
|
171
|
+
} catch {
|
|
172
|
+
// loadConfig may throw if config missing
|
|
173
|
+
}
|
|
174
|
+
checks.push({
|
|
175
|
+
id: "setup-completed",
|
|
176
|
+
name: "Setup completed",
|
|
177
|
+
ok: setupCompleted,
|
|
178
|
+
optional: false,
|
|
179
|
+
detail: setupCompleted ? "" : 'Run "chainlesschain setup"',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Port scan
|
|
183
|
+
const ports = [];
|
|
184
|
+
for (const [name, port] of Object.entries(DEFAULT_PORTS)) {
|
|
185
|
+
const open = await checkPort(port);
|
|
186
|
+
ports.push({ name, port, open });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Disk (Node 22+ statfsSync)
|
|
190
|
+
let disk = null;
|
|
191
|
+
try {
|
|
192
|
+
const { statfsSync } = await import("node:fs");
|
|
193
|
+
if (typeof statfsSync === "function") {
|
|
194
|
+
const stats = statfsSync(homeDir);
|
|
195
|
+
const freeGB = (stats.bavail * stats.bsize) / (1024 * 1024 * 1024);
|
|
196
|
+
disk = { homeDir, freeGB: Number(freeGB.toFixed(2)) };
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
// statfsSync unavailable on some platforms
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const failed = checks.filter((c) => !c.ok);
|
|
203
|
+
const criticalFailed = failed.filter((c) => !c.optional);
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
schema: "chainlesschain.doctor.v1",
|
|
207
|
+
version: VERSION,
|
|
208
|
+
generatedAt: new Date().toISOString(),
|
|
209
|
+
checks,
|
|
210
|
+
ports,
|
|
211
|
+
disk,
|
|
212
|
+
summary: {
|
|
213
|
+
total: checks.length,
|
|
214
|
+
passed: checks.length - failed.length,
|
|
215
|
+
failed: failed.length,
|
|
216
|
+
criticalFailed: criticalFailed.length,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Collect a machine-readable status report.
|
|
223
|
+
*
|
|
224
|
+
* Shape:
|
|
225
|
+
* {
|
|
226
|
+
* schema: "chainlesschain.status.v1",
|
|
227
|
+
* version: string,
|
|
228
|
+
* generatedAt: string,
|
|
229
|
+
* app: { running, pid },
|
|
230
|
+
* setup: { completed, completedAt, edition, llm: { provider, model } } | null,
|
|
231
|
+
* docker: {
|
|
232
|
+
* available,
|
|
233
|
+
* composePath: string | null,
|
|
234
|
+
* services: Array<{ name, state }> | null,
|
|
235
|
+
* note: string | null
|
|
236
|
+
* },
|
|
237
|
+
* ports: Array<{ name, port, open }>
|
|
238
|
+
* }
|
|
239
|
+
*/
|
|
240
|
+
export async function collectStatusReport() {
|
|
241
|
+
let config = {};
|
|
242
|
+
try {
|
|
243
|
+
config = loadConfig();
|
|
244
|
+
} catch {
|
|
245
|
+
// loadConfig may throw if config missing
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// App
|
|
249
|
+
const running = isAppRunning();
|
|
250
|
+
const app = {
|
|
251
|
+
running,
|
|
252
|
+
pid: running ? getAppPid() : null,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Setup
|
|
256
|
+
const setup = config.setupCompleted
|
|
257
|
+
? {
|
|
258
|
+
completed: true,
|
|
259
|
+
completedAt: config.completedAt || null,
|
|
260
|
+
edition: config.edition || null,
|
|
261
|
+
llm: config.llm
|
|
262
|
+
? {
|
|
263
|
+
provider: config.llm.provider || null,
|
|
264
|
+
model: config.llm.model || null,
|
|
265
|
+
}
|
|
266
|
+
: null,
|
|
267
|
+
}
|
|
268
|
+
: { completed: false };
|
|
269
|
+
|
|
270
|
+
// Docker services
|
|
271
|
+
const dockerAvailable = isDockerAvailable();
|
|
272
|
+
const docker = {
|
|
273
|
+
available: dockerAvailable,
|
|
274
|
+
composePath: null,
|
|
275
|
+
services: null,
|
|
276
|
+
note: null,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
if (!dockerAvailable) {
|
|
280
|
+
docker.note = "Docker not available";
|
|
281
|
+
} else {
|
|
282
|
+
const composePath = findComposeFile([process.cwd(), "backend/docker"]);
|
|
283
|
+
if (!composePath) {
|
|
284
|
+
docker.note = "docker-compose.yml not found";
|
|
285
|
+
} else {
|
|
286
|
+
docker.composePath = composePath;
|
|
287
|
+
const raw = getServiceStatus(composePath);
|
|
288
|
+
if (Array.isArray(raw)) {
|
|
289
|
+
docker.services = raw.map((svc) => ({
|
|
290
|
+
name: svc.Service || svc.Name || null,
|
|
291
|
+
state: svc.State || null,
|
|
292
|
+
}));
|
|
293
|
+
} else if (raw) {
|
|
294
|
+
docker.note = String(raw);
|
|
295
|
+
} else {
|
|
296
|
+
docker.note = "No services running";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Ports
|
|
302
|
+
const ports = [];
|
|
303
|
+
for (const [name, port] of Object.entries(DEFAULT_PORTS)) {
|
|
304
|
+
const open = await checkPort(port);
|
|
305
|
+
ports.push({ name, port, open });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
schema: "chainlesschain.status.v1",
|
|
310
|
+
version: VERSION,
|
|
311
|
+
generatedAt: new Date().toISOString(),
|
|
312
|
+
app,
|
|
313
|
+
setup,
|
|
314
|
+
docker,
|
|
315
|
+
ports,
|
|
316
|
+
};
|
|
317
|
+
}
|
package/src/runtime/index.js
CHANGED
|
@@ -15,7 +15,10 @@ export {
|
|
|
15
15
|
CODING_AGENT_MVP_TOOL_NAMES,
|
|
16
16
|
CODING_AGENT_EXTENSION_TOOL_NAMES,
|
|
17
17
|
createCodingAgentToolRegistry,
|
|
18
|
+
getCodingAgentFunctionToolDefinition,
|
|
19
|
+
getCodingAgentFunctionToolDefinitions,
|
|
18
20
|
getCodingAgentRuntimeDescriptor,
|
|
21
|
+
getCodingAgentRuntimeDescriptorByCommand,
|
|
19
22
|
getCodingAgentToolContract,
|
|
20
23
|
getCodingAgentToolContracts,
|
|
21
24
|
getCodingAgentToolPolicy,
|