echoclaw-relay-agent 0.22.6 → 0.22.7

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.
@@ -44,10 +44,6 @@ export declare class InstallHandler {
44
44
  private _runToRequest;
45
45
  /** Whether workspace docs have been synced to OpenClaw. */
46
46
  private _workspaceSynced;
47
- /** ICP workspace orchestrator for engine-based flows. */
48
- private readonly _icpOrchestrator;
49
- /** Actions that should be routed through ICP workspace flow. */
50
- private static readonly ICP_ACTIONS;
51
47
  constructor(wsClient: OpenClawWsClient, chatHandler: ChatHandler, config?: InstallHandlerConfig);
52
48
  /** Set the callback for sending messages back to Desktop. */
53
49
  setSendBack(fn: (msg: Record<string, unknown>) => Promise<void>): void;
@@ -66,25 +62,6 @@ export declare class InstallHandler {
66
62
  handleAbort(requestId: string): Promise<void>;
67
63
  /** Disconnect and clean up all active installs. */
68
64
  cleanup(): void;
69
- /**
70
- * Handle an install request through the ICP workspace orchestrator.
71
- * On timeout, falls back to the chat-based flow.
72
- */
73
- private _handleViaIcp;
74
- /**
75
- * Build an IcpRequest from an InstallRequest payload.
76
- * Returns null if the payload can't be mapped to an ICP request.
77
- */
78
- private _buildIcpRequest;
79
- /**
80
- * Fall back to the existing chat-based flow.
81
- * Called when ICP times out or can't handle the request.
82
- */
83
- private _handleViaChatFallback;
84
- /**
85
- * Generate human-readable message for ICP progress phases.
86
- */
87
- private _icpPhaseMessage;
88
65
  /**
89
66
  * Handle a streaming chat event for an install run.
90
67
  * Called when a chat event arrives with a runId we're tracking.
@@ -23,8 +23,6 @@ import crypto from 'node:crypto';
23
23
  import { buildAppRequestPrompt } from '../gateway/AppRequestPrompt.js';
24
24
  import { StreamingMarkerParser } from './MarkerParser.js';
25
25
  import { INSTALL_ERROR_CODES, } from './types.js';
26
- import { IcpOrchestrator, } from '@echoclaw/icp-orchestrator';
27
- import { resolveInstallAction, } from '@echoclaw/claw-engine';
28
26
  // ── Constants ────────────────────────────────────────────────────
29
27
  const DEFAULT_TIMEOUT_MS = 120000;
30
28
  const DEFAULT_RPC_TIMEOUT_MS = 30000;
@@ -33,6 +31,11 @@ const DEFAULT_SESSION_KEY = 'main';
33
31
  const STATUS_THROTTLE_MS = 3000;
34
32
  // ── InstallHandler ───────────────────────────────────────────────
35
33
  export class InstallHandler {
34
+ // ── ICP (disabled until packages are on npm) ──
35
+ // private readonly _icpOrchestrator: IcpOrchestrator;
36
+ // private static readonly ICP_ACTIONS = new Set([
37
+ // 'install_with_config', 'complete_template', 'generate_fresh', 'adapt',
38
+ // ]);
36
39
  constructor(wsClient, chatHandler, config) {
37
40
  Object.defineProperty(this, "_wsClient", {
38
41
  enumerable: true,
@@ -92,23 +95,16 @@ export class InstallHandler {
92
95
  writable: true,
93
96
  value: false
94
97
  });
95
- /** ICP workspace orchestrator for engine-based flows. */
96
- Object.defineProperty(this, "_icpOrchestrator", {
97
- enumerable: true,
98
- configurable: true,
99
- writable: true,
100
- value: void 0
101
- });
102
98
  this._wsClient = wsClient;
103
99
  this._chatHandler = chatHandler;
104
100
  this._timeoutMs = config?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
105
101
  this._sessionKey = config?.sessionKey ?? DEFAULT_SESSION_KEY;
106
102
  this._rpcTimeoutMs = config?.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS;
107
- // Initialize ICP orchestrator
108
- this._icpOrchestrator = new IcpOrchestrator({
109
- outputTimeoutMs: this._timeoutMs,
110
- reviewTimeoutMs: this._timeoutMs,
111
- });
103
+ // ICP orchestrator init disabled until packages are on npm
104
+ // this._icpOrchestrator = new IcpOrchestrator({
105
+ // outputTimeoutMs: this._timeoutMs,
106
+ // reviewTimeoutMs: this._timeoutMs,
107
+ // });
112
108
  // Listen for chat events — only process runs we own
113
109
  this._wsClient.on('chat', (payload) => {
114
110
  const runId = payload?.runId;
@@ -163,41 +159,8 @@ export class InstallHandler {
163
159
  requestId,
164
160
  status: 'accepted',
165
161
  });
166
- // ── ICP workspace routing ────────────────────────────────────
167
- // Route eligible actions through ICP orchestrator (workspace-based).
168
- // Falls back to chat-based on timeout/failure.
169
- if (InstallHandler.ICP_ACTIONS.has(action) && this._workspaceSynced) {
170
- // Track in _activeInstalls to prevent duplicates and support abort
171
- const icpAbort = new AbortController();
172
- const icpInstall = {
173
- requestId,
174
- action,
175
- runId: null,
176
- parser: new StreamingMarkerParser(),
177
- startedAt: Date.now(),
178
- timeoutTimer: null,
179
- lastKnownLength: 0,
180
- legacy,
181
- lastStatusAt: 0,
182
- streamMarkers: [],
183
- hasConfirmedPlan: false,
184
- icpAbort,
185
- };
186
- this._activeInstalls.set(requestId, icpInstall);
187
- this._handleViaIcp(requestId, action, payload, icpAbort.signal).catch((err) => {
188
- // ICP flow threw unexpectedly — send failure to Desktop
189
- this._send({
190
- type: 'install_result',
191
- requestId,
192
- status: 'failed',
193
- error: err?.message || 'ICP orchestration failed unexpectedly',
194
- code: INSTALL_ERROR_CODES.ICP_FAILED,
195
- }).catch(() => { });
196
- this._activeInstalls.delete(requestId);
197
- });
198
- return;
199
- }
200
- // ── Chat-based flow (legacy / preview / execute-with-plan) ──
162
+ // ── ICP workspace routing (disabled until packages are on npm) ──
163
+ // if (InstallHandler.ICP_ACTIONS.has(action) && this._workspaceSynced) { ... }
201
164
  // Create active install tracker
202
165
  const install = {
203
166
  requestId,
@@ -211,7 +174,6 @@ export class InstallHandler {
211
174
  lastStatusAt: 0,
212
175
  streamMarkers: [],
213
176
  hasConfirmedPlan: !!(action === 'install' && payload.confirmedPlan),
214
- icpAbort: null,
215
177
  };
216
178
  this._activeInstalls.set(requestId, install);
217
179
  // Build prompt from the install/adapt/preview payload
@@ -274,19 +236,7 @@ export class InstallHandler {
274
236
  const install = this._activeInstalls.get(requestId);
275
237
  if (!install)
276
238
  return;
277
- // If this is an ICP flow, abort via AbortController
278
- if (install.icpAbort) {
279
- install.icpAbort.abort();
280
- this._send({
281
- type: 'install_result',
282
- requestId,
283
- status: 'cancelled',
284
- code: INSTALL_ERROR_CODES.CANCELLED,
285
- }).catch(() => { });
286
- this._activeInstalls.delete(requestId);
287
- return;
288
- }
289
- // Chat-based flow: try to abort the AI generation
239
+ // Try to abort the AI generation
290
240
  try {
291
241
  await this._wsClient.request('chat.abort', {
292
242
  sessionKey: this._sessionKey,
@@ -302,246 +252,14 @@ export class InstallHandler {
302
252
  }
303
253
  /** Disconnect and clean up all active installs. */
304
254
  cleanup() {
305
- for (const [requestId, install] of this._activeInstalls) {
306
- // Abort any active ICP workspace flows
307
- if (install.icpAbort) {
308
- install.icpAbort.abort();
309
- }
255
+ for (const [requestId] of this._activeInstalls) {
310
256
  this._cleanupInstall(requestId);
311
257
  }
312
258
  this._activeInstalls.clear();
313
259
  this._runToRequest.clear();
314
260
  }
315
- // ── Private: ICP Workspace Flow ─────────────────────────────────
316
- /**
317
- * Handle an install request through the ICP workspace orchestrator.
318
- * On timeout, falls back to the chat-based flow.
319
- */
320
- async _handleViaIcp(requestId, action, payload, signal) {
321
- // Build ICP request from install payload
322
- const icpRequest = this._buildIcpRequest(action, payload);
323
- if (!icpRequest) {
324
- // Can't build ICP request — fall back to chat
325
- await this._handleViaChatFallback(requestId, payload);
326
- return;
327
- }
328
- // Map ICP progress to InstallStatus
329
- const onProgress = (progress) => {
330
- const phaseMap = {
331
- writing_task: 'icp_writing_task',
332
- waiting_output: 'icp_waiting_output',
333
- validating: 'icp_validating',
334
- writing_review: 'icp_reviewing',
335
- waiting_revision: 'icp_reviewing',
336
- fallback: 'icp_fallback',
337
- delivering: 'icp_delivering',
338
- };
339
- const installPhase = phaseMap[progress.phase] ?? 'ai_processing';
340
- const message = progress.message ?? this._icpPhaseMessage(progress);
341
- this._sendStatus(requestId, installPhase, message).catch(() => { });
342
- };
343
- // Notify OpenClaw about new tasks/reviews via chat
344
- const notifyAI = async (taskId, content, isReview) => {
345
- try {
346
- const message = isReview
347
- ? `Review feedback written for task ${taskId}. Read the review file and fix the issues, then rewrite output/package.claw.json.`
348
- : `New task available: ${taskId}. Read the task file at claw-apps/${taskId}/task.md and follow the instructions.`;
349
- const response = await this._wsClient.request('chat.send', {
350
- sessionKey: this._sessionKey,
351
- message,
352
- idempotencyKey: crypto.randomUUID(),
353
- }, this._rpcTimeoutMs);
354
- // Register the runId so ChatHandler ignores events for this notification run
355
- const notifyRunId = response.payload?.runId;
356
- if (notifyRunId) {
357
- this._chatHandler.registerExternalRun(notifyRunId);
358
- // Auto-unregister after timeout — these are fire-and-forget notifications
359
- setTimeout(() => {
360
- this._chatHandler.unregisterExternalRun(notifyRunId);
361
- }, this._timeoutMs).unref?.();
362
- }
363
- }
364
- catch {
365
- // Notification failure is non-fatal — OpenClaw may still pick it up
366
- }
367
- };
368
- // Execute ICP flow
369
- const result = await this._icpOrchestrator.execute(icpRequest, {
370
- onProgress,
371
- notifyAI,
372
- signal,
373
- });
374
- // Guard: if aborted while awaiting ICP, don't send duplicate result
375
- if (!this._activeInstalls.has(requestId))
376
- return;
377
- // Handle result
378
- switch (result.status) {
379
- case 'success': {
380
- const pkg = result.output.package;
381
- this._send({
382
- type: 'install_result',
383
- requestId,
384
- status: 'success',
385
- app: {
386
- manifest: pkg.manifest,
387
- html: pkg.html ?? '',
388
- },
389
- }).catch(() => { });
390
- this._activeInstalls.delete(requestId);
391
- break;
392
- }
393
- case 'timeout': {
394
- // ICP timed out — remove ICP tracking, fall back to chat-based
395
- this._activeInstalls.delete(requestId);
396
- console.warn(`[InstallHandler] ICP timeout for ${requestId}, falling back to chat`);
397
- await this._handleViaChatFallback(requestId, payload);
398
- break;
399
- }
400
- case 'failed': {
401
- this._send({
402
- type: 'install_result',
403
- requestId,
404
- status: 'failed',
405
- error: result.error,
406
- code: INSTALL_ERROR_CODES.ICP_FAILED,
407
- }).catch(() => { });
408
- this._activeInstalls.delete(requestId);
409
- break;
410
- }
411
- default: {
412
- // Unknown status — send failure
413
- this._send({
414
- type: 'install_result',
415
- requestId,
416
- status: 'failed',
417
- error: `Unexpected ICP result status: ${result.status}`,
418
- code: INSTALL_ERROR_CODES.ICP_FAILED,
419
- }).catch(() => { });
420
- this._activeInstalls.delete(requestId);
421
- break;
422
- }
423
- }
424
- }
425
- /**
426
- * Build an IcpRequest from an InstallRequest payload.
427
- * Returns null if the payload can't be mapped to an ICP request.
428
- */
429
- _buildIcpRequest(action, payload) {
430
- if ((action === 'install_with_config' || action === 'complete_template') && payload.package) {
431
- const pkg = payload.package;
432
- const installAction = resolveInstallAction(pkg);
433
- if (!installAction.valid)
434
- return null;
435
- return { type: 'adapt', pkg, installAction };
436
- }
437
- if (action === 'generate_fresh' && payload.package) {
438
- const pkg = payload.package;
439
- // Prefer Desktop-provided userRequest; fall back to app name
440
- const userRequest = payload.userRequest
441
- ?? (typeof pkg.manifest?.name === 'string'
442
- ? `Create app: ${pkg.manifest.name}`
443
- : `Create app: ${pkg.manifest?.name?.en ?? 'New App'}`);
444
- return {
445
- type: 'create',
446
- userRequest,
447
- manifest: pkg.manifest,
448
- };
449
- }
450
- if (action === 'adapt' && payload.html && payload.manifest) {
451
- const manifest = payload.manifest;
452
- const pkg = {
453
- manifest,
454
- html: payload.html,
455
- prompt: { seed: { goal: `Adapt ${manifest.id ?? 'app'}` } },
456
- };
457
- const installAction = resolveInstallAction(pkg);
458
- if (!installAction.valid)
459
- return null;
460
- return { type: 'adapt', pkg, installAction };
461
- }
462
- return null;
463
- }
464
- /**
465
- * Fall back to the existing chat-based flow.
466
- * Called when ICP times out or can't handle the request.
467
- */
468
- async _handleViaChatFallback(requestId, payload) {
469
- const action = payload.action ?? 'install';
470
- // Create active install tracker (same as the normal chat flow)
471
- const install = {
472
- requestId,
473
- action,
474
- runId: null,
475
- parser: new StreamingMarkerParser(),
476
- startedAt: Date.now(),
477
- timeoutTimer: null,
478
- lastKnownLength: 0,
479
- legacy: false,
480
- lastStatusAt: 0,
481
- streamMarkers: [],
482
- hasConfirmedPlan: false,
483
- icpAbort: null,
484
- };
485
- this._activeInstalls.set(requestId, install);
486
- const promptPayload = this._buildPromptPayload(payload);
487
- const prompt = buildAppRequestPrompt(promptPayload, this._workspaceSynced);
488
- await this._sendStatus(requestId, 'sending', 'Falling back to chat-based flow...');
489
- try {
490
- const response = await this._wsClient.request('chat.send', {
491
- sessionKey: this._sessionKey,
492
- message: prompt,
493
- idempotencyKey: crypto.randomUUID(),
494
- }, this._rpcTimeoutMs);
495
- if (!response.ok) {
496
- this._completeInstall(requestId, {
497
- status: 'failed',
498
- error: response.error?.message || 'chat.send failed',
499
- code: INSTALL_ERROR_CODES.SEND_FAILED,
500
- });
501
- return;
502
- }
503
- const runId = response.payload?.runId;
504
- if (!runId) {
505
- this._completeInstall(requestId, {
506
- status: 'failed',
507
- error: 'No runId returned from chat.send',
508
- code: INSTALL_ERROR_CODES.SEND_FAILED,
509
- });
510
- return;
511
- }
512
- install.runId = runId;
513
- this._runToRequest.set(runId, requestId);
514
- this._chatHandler.registerExternalRun(runId);
515
- install.timeoutTimer = setTimeout(() => {
516
- this._handleTimeout(requestId);
517
- }, this._timeoutMs);
518
- if (install.timeoutTimer.unref)
519
- install.timeoutTimer.unref();
520
- await this._sendStatus(requestId, 'ai_processing', 'Waiting for AI response...');
521
- }
522
- catch (err) {
523
- this._completeInstall(requestId, {
524
- status: 'failed',
525
- error: err.message || 'Unknown error',
526
- code: INSTALL_ERROR_CODES.SEND_FAILED,
527
- });
528
- }
529
- }
530
- /**
531
- * Generate human-readable message for ICP progress phases.
532
- */
533
- _icpPhaseMessage(progress) {
534
- switch (progress.phase) {
535
- case 'writing_task': return 'Preparing task for AI...';
536
- case 'waiting_output': return 'Waiting for AI to complete task...';
537
- case 'validating': return `Validating output (round ${progress.round ?? 1})...`;
538
- case 'writing_review': return `Quality check failed, requesting revision (round ${progress.round}/${progress.maxRounds})...`;
539
- case 'waiting_revision': return `Waiting for AI revision (round ${progress.round}/${progress.maxRounds})...`;
540
- case 'fallback': return 'Applying auto-fix (degraded delivery)...';
541
- case 'delivering': return 'Quality check passed, delivering...';
542
- default: return 'Processing...';
543
- }
544
- }
261
+ // ── ICP Workspace Flow (disabled until @echoclaw/claw-engine & icp-orchestrator are on npm) ──
262
+ // See git history for full ICP integration code.
545
263
  // ── Private: Event Handling ────────────────────────────────────
546
264
  /**
547
265
  * Handle a streaming chat event for an install run.
@@ -927,15 +645,3 @@ export class InstallHandler {
927
645
  }
928
646
  }
929
647
  }
930
- /** Actions that should be routed through ICP workspace flow. */
931
- Object.defineProperty(InstallHandler, "ICP_ACTIONS", {
932
- enumerable: true,
933
- configurable: true,
934
- writable: true,
935
- value: new Set([
936
- 'install_with_config',
937
- 'complete_template',
938
- 'generate_fresh',
939
- 'adapt',
940
- ])
941
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoclaw-relay-agent",
3
- "version": "0.22.6",
3
+ "version": "0.22.7",
4
4
  "description": "EchoClaw Relay Connection — E2E encrypted relay transport, pairing, and session management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,8 +22,6 @@
22
22
  "dev": "tsx src/cli.ts"
23
23
  },
24
24
  "dependencies": {
25
- "@echoclaw/claw-engine": "workspace:*",
26
- "@echoclaw/icp-orchestrator": "workspace:*",
27
25
  "echoclaw-crypto": "0.3.1",
28
26
  "ws": "^8.18.0"
29
27
  },