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.
Files changed (89) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/Analytics-B4OM8S8X.css +1 -0
  4. package/src/assets/web-panel/assets/Analytics-sBrYoc3A.js +3 -0
  5. package/src/assets/web-panel/assets/AppLayout-BhJ3YFWt.js +1 -0
  6. package/src/assets/web-panel/assets/AppLayout-Cr2lWhF-.css +1 -0
  7. package/src/assets/web-panel/assets/Backup-D68fenbD.js +1 -0
  8. package/src/assets/web-panel/assets/Backup-fZqtfC1m.css +1 -0
  9. package/src/assets/web-panel/assets/{Chat-DXtvKoM0.js → Chat-DaxTP3x8.js} +1 -1
  10. package/src/assets/web-panel/assets/{Cron-BJ4ODHOy.js → Cron-CNs03iHJ.js} +2 -2
  11. package/src/assets/web-panel/assets/{Dashboard-BZd4wDPQ.js → Dashboard-CjlX4CrX.js} +2 -2
  12. package/src/assets/web-panel/assets/Git-CCMVr3Y8.js +2 -0
  13. package/src/assets/web-panel/assets/Git-DGcuBXST.css +1 -0
  14. package/src/assets/web-panel/assets/{Logs-CSeKZEG_.js → Logs-BY6A0UNG.js} +2 -2
  15. package/src/assets/web-panel/assets/{McpTools-BYQAK11r.js → McpTools-CrBVYlg6.js} +2 -2
  16. package/src/assets/web-panel/assets/{Memory-gkUAPyuZ.js → Memory-CWx3SpUt.js} +2 -2
  17. package/src/assets/web-panel/assets/{Notes-bjNrQgAo.js → Notes-1LcGD49x.js} +2 -2
  18. package/src/assets/web-panel/assets/Organization-DdOOM4ic.css +1 -0
  19. package/src/assets/web-panel/assets/Organization-Dx2DhbkM.js +4 -0
  20. package/src/assets/web-panel/assets/P2P-B16fjqfJ.js +2 -0
  21. package/src/assets/web-panel/assets/P2P-OEzOeMZX.css +1 -0
  22. package/src/assets/web-panel/assets/Permissions-BQbC9FzG.js +4 -0
  23. package/src/assets/web-panel/assets/Permissions-C9WlkGl-.css +1 -0
  24. package/src/assets/web-panel/assets/Projects-CjhZbNYm.js +2 -0
  25. package/src/assets/web-panel/assets/Projects-DxKelI5h.css +1 -0
  26. package/src/assets/web-panel/assets/Providers-BEakqcO5.css +1 -0
  27. package/src/assets/web-panel/assets/Providers-ivOAQtHM.js +2 -0
  28. package/src/assets/web-panel/assets/RssFeed-BlFC20eg.css +1 -0
  29. package/src/assets/web-panel/assets/RssFeed-BrsErdrU.js +3 -0
  30. package/src/assets/web-panel/assets/Security-DnEvJU5h.js +4 -0
  31. package/src/assets/web-panel/assets/Security-Dwxw7rfP.css +1 -0
  32. package/src/assets/web-panel/assets/{Services-CS0oMdxh.js → Services-7jQywNbl.js} +2 -2
  33. package/src/assets/web-panel/assets/Skills-BCvgBkD3.js +1 -0
  34. package/src/assets/web-panel/assets/{Tasks-qULws8pc.js → Tasks-CmJBC1cf.js} +1 -1
  35. package/src/assets/web-panel/assets/Templates-DOY_oZnm.css +1 -0
  36. package/src/assets/web-panel/assets/Templates-RXT8-DNk.js +1 -0
  37. package/src/assets/web-panel/assets/Wallet-3iYASEx_.js +4 -0
  38. package/src/assets/web-panel/assets/Wallet-DnIumafl.css +1 -0
  39. package/src/assets/web-panel/assets/WebAuthn-CNPl2VQR.css +1 -0
  40. package/src/assets/web-panel/assets/WebAuthn-s3Hzd9db.js +5 -0
  41. package/src/assets/web-panel/assets/{antd-CJSBocer.js → antd-gZyc63Qr.js} +114 -114
  42. package/src/assets/web-panel/assets/chat-BmwHBi9M.js +1 -0
  43. package/src/assets/web-panel/assets/index-DrmEk9S3.js +2 -0
  44. package/src/assets/web-panel/assets/{markdown-Bo5cVN4u.js → markdown-Bv7nG63L.js} +1 -1
  45. package/src/assets/web-panel/assets/ws-CU7Gvoom.js +1 -0
  46. package/src/assets/web-panel/index.html +2 -2
  47. package/src/commands/doctor.js +33 -151
  48. package/src/commands/mcp.js +1 -1
  49. package/src/commands/plugin.js +1 -1
  50. package/src/commands/session.js +106 -7
  51. package/src/commands/status.js +39 -69
  52. package/src/gateways/ws/session-protocol.js +1 -1
  53. package/src/gateways/ws/ws-agent-handler.js +484 -0
  54. package/src/gateways/ws/ws-server.js +758 -4
  55. package/src/gateways/ws/ws-session-gateway.js +1432 -1
  56. package/src/harness/mcp-client.js +417 -0
  57. package/src/harness/mock-llm-provider.js +167 -0
  58. package/src/harness/plugin-manager.js +434 -0
  59. package/src/lib/agent-core.js +25 -1902
  60. package/src/lib/hashline.js +208 -0
  61. package/src/lib/jsonl-session-store.js +11 -0
  62. package/src/lib/mcp-client.js +14 -412
  63. package/src/lib/plugin-manager.js +29 -428
  64. package/src/lib/prompt-compressor.js +11 -0
  65. package/src/lib/session-hooks.js +61 -0
  66. package/src/lib/skill-loader.js +4 -0
  67. package/src/lib/skill-mcp.js +190 -0
  68. package/src/lib/workflow-state-reader.js +94 -0
  69. package/src/lib/ws-agent-handler.js +8 -472
  70. package/src/lib/ws-server.js +12 -756
  71. package/src/lib/ws-session-manager.js +8 -1417
  72. package/src/repl/agent-repl.js +27 -3
  73. package/src/runtime/agent-core.js +1760 -0
  74. package/src/runtime/agent-runtime.js +3 -1
  75. package/src/runtime/coding-agent-contract-shared.cjs +496 -0
  76. package/src/runtime/coding-agent-contract.js +49 -229
  77. package/src/runtime/coding-agent-policy.cjs +54 -5
  78. package/src/runtime/diagnostics.js +317 -0
  79. package/src/runtime/index.js +3 -0
  80. package/src/tools/index.js +3 -0
  81. package/src/tools/legacy-agent-tools.js +5 -0
  82. package/src/assets/web-panel/assets/AppLayout-B_tkw3Pn.js +0 -1
  83. package/src/assets/web-panel/assets/AppLayout-CFP4dGIJ.css +0 -1
  84. package/src/assets/web-panel/assets/Providers-Brm-S_hS.css +0 -1
  85. package/src/assets/web-panel/assets/Providers-Dbf57Tbv.js +0 -1
  86. package/src/assets/web-panel/assets/Skills-B2fgruv8.js +0 -1
  87. package/src/assets/web-panel/assets/chat-DnH09sSR.js +0 -1
  88. package/src/assets/web-panel/assets/index-IK-oro0g.js +0 -2
  89. package/src/assets/web-panel/assets/ws-DjelKkD6.js +0 -1
@@ -1,476 +1,12 @@
1
1
  /**
2
- * WebSocket Agent Handler
2
+ * @deprecated canonical implementation lives in
3
+ * `../gateways/ws/ws-agent-handler.js` as of the CLI Runtime Convergence
4
+ * roadmap (Phase 6b, 2026-04-09). This file is retained as a re-export
5
+ * shim for backwards compatibility and will be removed once all external
6
+ * consumers have migrated.
3
7
  *
4
- * Handles agent session messages over WebSocket. Consumes agent-core's
5
- * agentLoop generator and routes events to the client via the interaction
6
- * adapter.
8
+ * Please import from `packages/cli/src/gateways/ws/ws-agent-handler.js`
9
+ * in new code.
7
10
  */
8
11
 
9
- import { agentLoop, formatToolArgs } from "./agent-core.js";
10
- import { detectTaskType, selectModelForTask } from "./task-model-selector.js";
11
- import { PlanState } from "./plan-mode.js";
12
- import { CLISlotFiller } from "./slot-filler.js";
13
- import { createAbortError, isAbortError } from "./abort-utils.js";
14
-
15
- export class WSAgentHandler {
16
- /**
17
- * @param {object} options
18
- * @param {import("./ws-session-manager.js").Session} options.session
19
- * @param {import("./interaction-adapter.js").WebSocketInteractionAdapter} options.interaction
20
- * @param {object} [options.db]
21
- */
22
- constructor({ session, interaction, db }) {
23
- this.session = session;
24
- this.interaction = interaction;
25
- this.db = db || null;
26
- this._processing = false;
27
- this._abortController = null;
28
- this._activeRequestId = null;
29
- }
30
-
31
- /**
32
- * Handle a user message — one turn of the agentic loop.
33
- *
34
- * @param {string} userMessage
35
- * @param {string} [requestId] - id from ws message for response correlation
36
- */
37
- async handleMessage(userMessage, requestId) {
38
- if (this._processing) {
39
- this.interaction.emit("error", {
40
- requestId,
41
- code: "BUSY",
42
- message: "Session is currently processing a message",
43
- });
44
- return;
45
- }
46
-
47
- this._processing = true;
48
- const abortController = new AbortController();
49
- this._abortController = abortController;
50
- this._activeRequestId = requestId || null;
51
-
52
- try {
53
- const { session } = this;
54
-
55
- // Add user message
56
- session.messages.push({ role: "user", content: userMessage });
57
-
58
- // Auto-select model based on task type
59
- let activeModel = session.model;
60
- const taskDetection = detectTaskType(userMessage);
61
- if (taskDetection.confidence > 0.3) {
62
- const recommended = selectModelForTask(
63
- session.provider,
64
- taskDetection.taskType,
65
- );
66
- if (recommended && recommended !== activeModel) {
67
- activeModel = recommended;
68
- this.interaction.emit("model-switch", {
69
- requestId,
70
- from: session.model,
71
- to: activeModel,
72
- reason: taskDetection.name,
73
- });
74
- }
75
- }
76
-
77
- // Create slot filler for interactive parameter collection
78
- const slotFiller = new CLISlotFiller({
79
- interaction: this.interaction,
80
- db: this.db,
81
- });
82
-
83
- // Run agent loop
84
- const loopOptions = {
85
- provider: session.provider,
86
- model: activeModel,
87
- baseUrl: session.baseUrl || "http://localhost:11434",
88
- apiKey: session.apiKey,
89
- contextEngine: session.contextEngine,
90
- hookDb: this.db,
91
- cwd: session.projectRoot,
92
- sessionId: session.id,
93
- planManager: session.planManager,
94
- enabledToolNames: session.enabledToolNames || null,
95
- hostManagedToolPolicy: session.hostManagedToolPolicy || null,
96
- extraToolDefinitions: session.externalToolDefinitions || [],
97
- externalToolDescriptors: session.externalToolDescriptors || {},
98
- externalToolExecutors: session.externalToolExecutors || {},
99
- mcpClient: session.mcpClient || null,
100
- slotFiller,
101
- interaction: this.interaction,
102
- signal: abortController.signal,
103
- };
104
-
105
- for await (const event of agentLoop(session.messages, loopOptions)) {
106
- switch (event.type) {
107
- case "slot-filling":
108
- this.interaction.emit("slot-filling", {
109
- requestId,
110
- slot: event.slot,
111
- question: event.question,
112
- });
113
- break;
114
-
115
- case "tool-executing":
116
- this.interaction.emit("tool-executing", {
117
- requestId,
118
- tool: event.tool,
119
- args: event.args,
120
- display: formatToolArgs(event.tool, event.args),
121
- });
122
- break;
123
-
124
- case "tool-result":
125
- this.interaction.emit("tool-result", {
126
- requestId,
127
- tool: event.tool,
128
- result: event.result,
129
- error: event.error,
130
- });
131
- break;
132
-
133
- case "response-complete":
134
- if (event.content) {
135
- session.messages.push({
136
- role: "assistant",
137
- content: event.content,
138
- });
139
- }
140
- this.interaction.emit("response-complete", {
141
- requestId,
142
- content: event.content,
143
- });
144
- break;
145
- }
146
- }
147
-
148
- // Update last activity
149
- session.lastActivity = new Date().toISOString();
150
- } catch (err) {
151
- if (isAbortError(err) || abortController.signal.aborted) {
152
- return;
153
- }
154
-
155
- this.interaction.emit("error", {
156
- requestId,
157
- code: "AGENT_ERROR",
158
- message: err.message,
159
- });
160
-
161
- // Record error in context engine
162
- if (this.session.contextEngine) {
163
- this.session.contextEngine.recordError({
164
- step: "ws-agent-loop",
165
- message: err.message,
166
- });
167
- }
168
- } finally {
169
- this._processing = false;
170
- if (this._abortController === abortController) {
171
- this._abortController = null;
172
- }
173
- if (this._activeRequestId === requestId) {
174
- this._activeRequestId = null;
175
- }
176
- }
177
- }
178
-
179
- async interrupt() {
180
- const wasProcessing = this._processing;
181
- const interruptedRequestId = this._activeRequestId || null;
182
- const reason = createAbortError("Session interrupted by client");
183
-
184
- if (this._abortController && !this._abortController.signal.aborted) {
185
- this._abortController.abort(reason);
186
- }
187
-
188
- if (typeof this.interaction?.rejectAllPending === "function") {
189
- this.interaction.rejectAllPending(reason);
190
- }
191
-
192
- return {
193
- sessionId: this.session?.id || null,
194
- interrupted: true,
195
- wasProcessing,
196
- interruptedRequestId,
197
- };
198
- }
199
-
200
- destroy() {
201
- const reason = createAbortError("Session closed");
202
- if (this._abortController && !this._abortController.signal.aborted) {
203
- this._abortController.abort(reason);
204
- }
205
- if (typeof this.interaction?.rejectAllPending === "function") {
206
- this.interaction.rejectAllPending(reason);
207
- }
208
- }
209
-
210
- /**
211
- * Handle slash commands within the session.
212
- *
213
- * @param {string} command - e.g. "/plan enter", "/model qwen2:7b"
214
- * @param {string} [requestId]
215
- */
216
- async handleSlashCommand(command, requestId) {
217
- const [cmd, ...args] = command.trim().split(/\s+/);
218
- const arg = args.join(" ").trim();
219
- const { session } = this;
220
-
221
- switch (cmd) {
222
- case "/model":
223
- if (arg) {
224
- session.model = arg;
225
- this.interaction.emit("command-response", {
226
- requestId,
227
- command: cmd,
228
- result: { model: arg },
229
- });
230
- } else {
231
- this.interaction.emit("command-response", {
232
- requestId,
233
- command: cmd,
234
- result: { model: session.model },
235
- });
236
- }
237
- break;
238
-
239
- case "/provider": {
240
- const supported = [
241
- "ollama",
242
- "anthropic",
243
- "openai",
244
- "deepseek",
245
- "dashscope",
246
- "mistral",
247
- "gemini",
248
- "volcengine",
249
- ];
250
- if (arg && supported.includes(arg)) {
251
- session.provider = arg;
252
- this.interaction.emit("command-response", {
253
- requestId,
254
- command: cmd,
255
- result: { provider: arg },
256
- });
257
- } else {
258
- this.interaction.emit("command-response", {
259
- requestId,
260
- command: cmd,
261
- result: { provider: session.provider, available: supported },
262
- });
263
- }
264
- break;
265
- }
266
-
267
- case "/clear":
268
- session.messages.length = 1; // Keep system prompt
269
- this.interaction.emit("command-response", {
270
- requestId,
271
- command: cmd,
272
- result: { cleared: true },
273
- });
274
- break;
275
-
276
- case "/compact":
277
- if (session.contextEngine && session.messages.length > 5) {
278
- const compacted = session.contextEngine.smartCompact(
279
- session.messages,
280
- );
281
- session.messages.length = 0;
282
- session.messages.push(...compacted);
283
- } else if (session.messages.length > 5) {
284
- const systemMsg = session.messages[0];
285
- const recent = session.messages.slice(-4);
286
- session.messages.length = 0;
287
- session.messages.push(systemMsg, ...recent);
288
- }
289
- this.interaction.emit("command-response", {
290
- requestId,
291
- command: cmd,
292
- result: { messageCount: session.messages.length },
293
- });
294
- break;
295
-
296
- case "/task":
297
- if (arg === "clear") {
298
- if (session.contextEngine) session.contextEngine.clearTask();
299
- this.interaction.emit("command-response", {
300
- requestId,
301
- command: cmd,
302
- result: { cleared: true },
303
- });
304
- } else if (arg) {
305
- if (session.contextEngine) session.contextEngine.setTask(arg);
306
- this.interaction.emit("command-response", {
307
- requestId,
308
- command: cmd,
309
- result: { task: arg },
310
- });
311
- } else {
312
- this.interaction.emit("command-response", {
313
- requestId,
314
- command: cmd,
315
- result: {
316
- task: session.contextEngine?.taskContext?.objective || null,
317
- },
318
- });
319
- }
320
- break;
321
-
322
- case "/stats":
323
- if (session.contextEngine) {
324
- const stats = session.contextEngine.getStats();
325
- this.interaction.emit("command-response", {
326
- requestId,
327
- command: cmd,
328
- result: stats,
329
- });
330
- } else {
331
- this.interaction.emit("command-response", {
332
- requestId,
333
- command: cmd,
334
- result: { error: "Context engine not available" },
335
- });
336
- }
337
- break;
338
-
339
- case "/session":
340
- this.interaction.emit("command-response", {
341
- requestId,
342
- command: cmd,
343
- result: {
344
- id: session.id,
345
- type: session.type,
346
- provider: session.provider,
347
- model: session.model,
348
- messageCount: session.messages.length,
349
- projectRoot: session.projectRoot,
350
- createdAt: session.createdAt,
351
- lastActivity: session.lastActivity,
352
- },
353
- });
354
- break;
355
-
356
- case "/plan":
357
- this._handlePlanCommand(arg, requestId);
358
- break;
359
-
360
- default:
361
- this.interaction.emit("command-response", {
362
- requestId,
363
- command: cmd,
364
- result: { error: `Unknown command: ${cmd}` },
365
- });
366
- }
367
- }
368
-
369
- /**
370
- * Handle /plan sub-commands.
371
- */
372
- _handlePlanCommand(subCmd, requestId) {
373
- const planManager = this.session.planManager;
374
-
375
- if (!subCmd || subCmd === "enter") {
376
- if (planManager.isActive()) {
377
- this.interaction.emit("command-response", {
378
- requestId,
379
- command: "/plan",
380
- result: { error: "Already in plan mode" },
381
- });
382
- } else {
383
- planManager.enterPlanMode({ title: "Agent Plan" });
384
- this.session.messages.push({
385
- role: "system",
386
- content:
387
- "[PLAN MODE ACTIVE] You are now in plan mode. You can read files, search, and analyze — but write/execute tools are blocked. Any blocked tool calls will be recorded as plan items. Analyze the task thoroughly, then the user will approve your plan.",
388
- });
389
- this.interaction.emit("command-response", {
390
- requestId,
391
- command: "/plan",
392
- result: { state: "analyzing", message: "Entered plan mode" },
393
- });
394
- }
395
- } else if (subCmd === "show") {
396
- if (!planManager.isActive()) {
397
- this.interaction.emit("command-response", {
398
- requestId,
399
- command: "/plan show",
400
- result: { error: "Not in plan mode" },
401
- });
402
- } else {
403
- this.interaction.emit("plan-ready", {
404
- requestId,
405
- summary: planManager.generatePlanSummary(),
406
- risk: planManager.getRiskAssessment(),
407
- items: planManager.currentPlan?.items || [],
408
- });
409
- }
410
- } else if (subCmd === "approve" || subCmd === "yes") {
411
- if (!planManager.isActive()) {
412
- this.interaction.emit("command-response", {
413
- requestId,
414
- command: "/plan approve",
415
- result: { error: "No plan to approve" },
416
- });
417
- } else if (
418
- !planManager.currentPlan ||
419
- planManager.currentPlan.items.length === 0
420
- ) {
421
- this.interaction.emit("command-response", {
422
- requestId,
423
- command: "/plan approve",
424
- result: { error: "Plan has no items" },
425
- });
426
- } else {
427
- planManager.approvePlan();
428
- this.session.messages.push({
429
- role: "system",
430
- content: `[PLAN APPROVED] The user has approved your plan with ${planManager.currentPlan.items.length} items. You can now use all tools including write_file, edit_file, run_shell, git, and run_skill. Execute the plan items in order.`,
431
- });
432
- this.interaction.emit("command-response", {
433
- requestId,
434
- command: "/plan approve",
435
- result: {
436
- state: PlanState.APPROVED,
437
- itemCount: planManager.currentPlan.items.length,
438
- },
439
- });
440
- }
441
- } else if (subCmd === "reject" || subCmd === "no") {
442
- if (planManager.isActive()) {
443
- planManager.rejectPlan("User rejected");
444
- this.interaction.emit("command-response", {
445
- requestId,
446
- command: "/plan reject",
447
- result: { state: PlanState.REJECTED },
448
- });
449
- } else {
450
- this.interaction.emit("command-response", {
451
- requestId,
452
- command: "/plan reject",
453
- result: { error: "No plan to reject" },
454
- });
455
- }
456
- } else if (subCmd === "exit") {
457
- if (planManager.isActive()) {
458
- planManager.exitPlanMode({ savePlan: true });
459
- }
460
- this.interaction.emit("command-response", {
461
- requestId,
462
- command: "/plan exit",
463
- result: { state: PlanState.INACTIVE },
464
- });
465
- } else {
466
- this.interaction.emit("command-response", {
467
- requestId,
468
- command: "/plan",
469
- result: {
470
- error: `Unknown /plan subcommand: ${subCmd}`,
471
- available: ["enter", "show", "approve", "reject", "exit"],
472
- },
473
- });
474
- }
475
- }
476
- }
12
+ export { WSAgentHandler } from "../gateways/ws/ws-agent-handler.js";