clawmatrix 0.1.23 → 0.2.1

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/src/types.ts CHANGED
@@ -12,11 +12,33 @@ export interface ClusterFrame {
12
12
  // ── Auth messages ──────────────────────────────────────────────────
13
13
  export interface AuthChallenge extends ClusterFrame {
14
14
  type: "auth_challenge";
15
- payload: { nonce: string };
15
+ payload: { nonce: string; publicKey?: string };
16
16
  }
17
17
 
18
+ /** Legacy plaintext auth (used when E2EE is disabled). */
18
19
  export interface AuthRequest extends ClusterFrame {
19
20
  type: "auth";
21
+ payload: {
22
+ nodeId: string;
23
+ sig: string;
24
+ publicKey?: string;
25
+ agents?: AgentInfo[];
26
+ models?: ModelInfo[];
27
+ tags?: string[];
28
+ deviceInfo?: DeviceInfo;
29
+ toolProxy?: ToolProxyInfo;
30
+ };
31
+ }
32
+
33
+ /** Phase 2 of E2EE handshake: client sends its public key (plaintext). */
34
+ export interface AuthKeyExchange extends ClusterFrame {
35
+ type: "auth_key_exchange";
36
+ payload: { publicKey: string };
37
+ }
38
+
39
+ /** Phase 3 of E2EE handshake: client sends encrypted HMAC + capabilities. */
40
+ export interface AuthVerify extends ClusterFrame {
41
+ type: "auth_verify";
20
42
  payload: {
21
43
  nodeId: string;
22
44
  sig: string;
@@ -115,6 +137,8 @@ export interface ModelResponse extends ClusterFrame {
115
137
  payload: {
116
138
  success: boolean;
117
139
  content?: string;
140
+ /** Reasoning/thinking content from models like DeepSeek. */
141
+ reasoning?: string;
118
142
  /** Full message object from upstream (choices[0].message or responses output).
119
143
  * Carries tool_calls, refusal, etc. that `content` alone cannot represent. */
120
144
  message?: unknown;
@@ -128,6 +152,8 @@ export interface ModelStreamChunk extends ClusterFrame {
128
152
  id: string;
129
153
  payload: {
130
154
  delta: string;
155
+ /** Reasoning/thinking delta from models like DeepSeek (reasoning_content). */
156
+ reasoningDelta?: string;
131
157
  /** Full delta object from upstream (choices[0].delta).
132
158
  * Carries tool_calls chunks that `delta` string cannot represent. */
133
159
  deltaObj?: unknown;
@@ -136,6 +162,14 @@ export interface ModelStreamChunk extends ClusterFrame {
136
162
  };
137
163
  }
138
164
 
165
+ // ── Image content ─────────────────────────────────────────────────
166
+ export interface ImageContent {
167
+ /** Base64-encoded image data (without data URI prefix) */
168
+ data: string;
169
+ /** MIME type, e.g. "image/png", "image/jpeg" */
170
+ mediaType: string;
171
+ }
172
+
139
173
  // ── Handoff ────────────────────────────────────────────────────────
140
174
  export type HandoffStatus = "working" | "input_required" | "completed" | "failed" | "canceled";
141
175
 
@@ -152,6 +186,8 @@ export interface HandoffRequest extends ClusterFrame {
152
186
  target: string;
153
187
  task: string;
154
188
  context?: string;
189
+ sessionId?: string; // Reuse session for multi-turn conversation
190
+ images?: ImageContent[];
155
191
  };
156
192
  }
157
193
 
@@ -162,6 +198,7 @@ export interface HandoffStreamChunk extends ClusterFrame {
162
198
  delta: string;
163
199
  done: boolean;
164
200
  artifacts?: Artifact[];
201
+ sessionId?: string; // included for session watchers (multi-device sync)
165
202
  };
166
203
  }
167
204
 
@@ -177,6 +214,7 @@ export interface HandoffResponse extends ClusterFrame {
177
214
  artifacts?: Artifact[];
178
215
  inputRequired?: boolean;
179
216
  handoffId?: string;
217
+ sessionId?: string; // For multi-turn conversation reuse
180
218
  };
181
219
  }
182
220
 
@@ -216,6 +254,7 @@ export interface HandoffInput extends ClusterFrame {
216
254
  id: string;
217
255
  payload: {
218
256
  message: string;
257
+ images?: ImageContent[];
219
258
  };
220
259
  }
221
260
 
@@ -248,6 +287,36 @@ export interface ToolProxyResponse extends ClusterFrame {
248
287
  };
249
288
  }
250
289
 
290
+ // ── Tool batch proxy ──────────────────────────────────────────────
291
+ export interface ToolBatchItem {
292
+ tool: string;
293
+ params: Record<string, unknown>;
294
+ }
295
+
296
+ export interface ToolBatchResultItem {
297
+ tool: string;
298
+ success: boolean;
299
+ result?: Record<string, unknown>;
300
+ error?: string;
301
+ }
302
+
303
+ export interface ToolBatchRequest extends ClusterFrame {
304
+ type: "tool_batch_req";
305
+ id: string;
306
+ payload: {
307
+ items: ToolBatchItem[];
308
+ stopOnError?: boolean; // default true
309
+ };
310
+ }
311
+
312
+ export interface ToolBatchResponse extends ClusterFrame {
313
+ type: "tool_batch_res";
314
+ id: string;
315
+ payload: {
316
+ results: ToolBatchResultItem[];
317
+ };
318
+ }
319
+
251
320
  // ── Device info ───────────────────────────────────────────────────
252
321
  export interface DeviceInfo {
253
322
  os: string; // e.g. "Darwin 24.6.0", "Linux 6.1.0"
@@ -285,6 +354,7 @@ export interface ModelInfo {
285
354
  id: string;
286
355
  provider: string;
287
356
  description?: string;
357
+ input?: ("text" | "image")[];
288
358
  compat?: ModelCompatInfo;
289
359
  }
290
360
 
@@ -294,6 +364,11 @@ export interface ToolProxyInfo {
294
364
  deny: string[];
295
365
  }
296
366
 
367
+ export interface AcpAgentInfo {
368
+ id: string;
369
+ description: string;
370
+ }
371
+
297
372
  export interface PeerInfo {
298
373
  nodeId: string;
299
374
  agents: AgentInfo[];
@@ -303,6 +378,8 @@ export interface PeerInfo {
303
378
  directPeers?: string[]; // nodeIds this node has direct connections to
304
379
  deviceInfo?: DeviceInfo; // system/hardware info
305
380
  toolProxy?: ToolProxyInfo;
381
+ acpAgents?: AcpAgentInfo[]; // ACP agents available on this node
382
+ latencyMs?: number; // heartbeat RTT (direct) or estimated round-trip (relay)
306
383
  }
307
384
 
308
385
  export interface NodeCapabilities {
@@ -312,6 +389,7 @@ export interface NodeCapabilities {
312
389
  tags: string[];
313
390
  deviceInfo?: DeviceInfo;
314
391
  toolProxy?: ToolProxyInfo;
392
+ acpAgents?: AcpAgentInfo[];
315
393
  }
316
394
 
317
395
  // ── Ingested events (from Shortcuts automations, etc.) ────────────
@@ -328,7 +406,368 @@ export interface IngestedEvent {
328
406
  export interface KnowledgeSyncFrame extends ClusterFrame {
329
407
  type: "knowledge_sync";
330
408
  payload: {
331
- data: string; // base64-encoded Automerge sync message
409
+ docId: string; // "" = registry, otherwise file relPath
410
+ data: string; // base64-encoded Automerge sync message
411
+ };
412
+ }
413
+
414
+ // ── Diagnostic (sentinel) ────────────────────────────────────────
415
+ export interface DiagnosticExec extends ClusterFrame {
416
+ type: "diagnostic_exec";
417
+ id: string;
418
+ payload: {
419
+ command: string;
420
+ timeout?: number; // seconds, default 30
421
+ };
422
+ }
423
+
424
+ export interface DiagnosticExecResponse extends ClusterFrame {
425
+ type: "diagnostic_exec_res";
426
+ id: string;
427
+ payload: {
428
+ success: boolean;
429
+ exitCode?: number;
430
+ stdout?: string;
431
+ stderr?: string;
432
+ error?: string;
433
+ };
434
+ }
435
+
436
+ export interface DiagnosticStatus extends ClusterFrame {
437
+ type: "diagnostic_status";
438
+ id: string;
439
+ }
440
+
441
+ export interface DiagnosticStatusResponse extends ClusterFrame {
442
+ type: "diagnostic_status_res";
443
+ id: string;
444
+ payload: {
445
+ gatewayAlive: boolean;
446
+ uptimeMs: number;
447
+ pid: number;
448
+ gatewayPid?: number;
449
+ };
450
+ }
451
+
452
+ // ── ACP proxy ─────────────────────────────────────────────────────
453
+ export type AcpSessionMode = "oneshot" | "persistent";
454
+
455
+ export interface AcpTaskRequest extends ClusterFrame {
456
+ type: "acp_req";
457
+ id: string;
458
+ payload: {
459
+ agent: string; // ACP harness: "claude", "codex", "gemini", etc.
460
+ task?: string; // prompt to execute (omit to create session without prompting)
461
+ sessionId?: string; // existing session ID for follow-up (persistent mode)
462
+ acpSessionId?: string; // ACP-level session ID for resume when sessionId is stale
463
+ mode?: AcpSessionMode; // default: "oneshot"
464
+ cwd?: string; // working directory on remote node
465
+ images?: ImageContent[];
466
+ };
467
+ }
468
+
469
+ export interface AcpStreamChunk extends ClusterFrame {
470
+ type: "acp_stream";
471
+ id: string;
472
+ payload: {
473
+ delta: string;
474
+ event?: string; // "agent_message_chunk" | "tool_call" | etc.
475
+ done: boolean;
476
+ sessionId?: string; // included for session watchers (multi-device sync)
477
+ };
478
+ }
479
+
480
+ export interface AcpTaskResponse extends ClusterFrame {
481
+ type: "acp_res";
482
+ id: string;
483
+ payload: {
484
+ success: boolean;
485
+ nodeId?: string;
486
+ agent?: string;
487
+ result?: string;
488
+ sessionId?: string; // returned for persistent sessions
489
+ acpSessionId?: string; // ACP-level session ID for future resume
490
+ stopReason?: string; // ACP stop reason
491
+ error?: string;
492
+ };
493
+ }
494
+
495
+ export interface AcpCloseRequest extends ClusterFrame {
496
+ type: "acp_close";
497
+ id: string;
498
+ payload: {
499
+ sessionId: string;
500
+ };
501
+ }
502
+
503
+ export interface AcpCloseResponse extends ClusterFrame {
504
+ type: "acp_close_res";
505
+ id: string;
506
+ payload: {
507
+ success: boolean;
508
+ error?: string;
509
+ };
510
+ }
511
+
512
+ // ── ACP session management ──────────────────────────────────────
513
+ export interface AcpSessionInfo {
514
+ sessionId: string;
515
+ cwd: string;
516
+ title?: string;
517
+ description?: string; // first user message (for display in session list)
518
+ updatedAt?: string;
519
+ agent?: string; // which ACP agent (claude, codex, etc.)
520
+ }
521
+
522
+ export interface AcpListRequest extends ClusterFrame {
523
+ type: "acp_list_req";
524
+ id: string;
525
+ payload: {
526
+ agent?: string; // filter by agent type
527
+ cwd?: string; // filter by working directory
528
+ };
529
+ }
530
+
531
+ export interface AcpListResponse extends ClusterFrame {
532
+ type: "acp_list_res";
533
+ id: string;
534
+ payload: {
535
+ success: boolean;
536
+ sessions?: AcpSessionInfo[];
537
+ error?: string;
538
+ };
539
+ }
540
+
541
+ export interface AcpResumeRequest extends ClusterFrame {
542
+ type: "acp_resume_req";
543
+ id: string;
544
+ payload: {
545
+ agent: string;
546
+ acpSessionId: string; // the ACP protocol session ID to resume
547
+ cwd: string;
548
+ };
549
+ }
550
+
551
+ export interface AcpResumeResponse extends ClusterFrame {
552
+ type: "acp_resume_res";
553
+ id: string;
554
+ payload: {
555
+ success: boolean;
556
+ sessionId?: string; // ClawMatrix session ID for follow-up prompts
557
+ error?: string;
558
+ };
559
+ }
560
+
561
+ export interface AcpCancelRequest extends ClusterFrame {
562
+ type: "acp_cancel";
563
+ id: string;
564
+ payload: {
565
+ sessionId: string; // ClawMatrix session ID
566
+ };
567
+ }
568
+
569
+ export interface AcpCancelResponse extends ClusterFrame {
570
+ type: "acp_cancel_res";
571
+ id: string;
572
+ payload: {
573
+ success: boolean;
574
+ error?: string;
575
+ };
576
+ }
577
+
578
+ export interface AcpModeInfo {
579
+ id: string;
580
+ name: string;
581
+ description?: string;
582
+ }
583
+
584
+ export interface AcpSetModeRequest extends ClusterFrame {
585
+ type: "acp_set_mode";
586
+ id: string;
587
+ payload: {
588
+ sessionId: string; // ClawMatrix session ID
589
+ modeId: string; // e.g. "code", "architect", "ask"
590
+ };
591
+ }
592
+
593
+ export interface AcpSetModeResponse extends ClusterFrame {
594
+ type: "acp_set_mode_res";
595
+ id: string;
596
+ payload: {
597
+ success: boolean;
598
+ error?: string;
599
+ };
600
+ }
601
+
602
+ export interface AcpGetModesRequest extends ClusterFrame {
603
+ type: "acp_get_modes";
604
+ id: string;
605
+ payload: {
606
+ sessionId: string;
607
+ };
608
+ }
609
+
610
+ export interface AcpGetModesResponse extends ClusterFrame {
611
+ type: "acp_get_modes_res";
612
+ id: string;
613
+ payload: {
614
+ success: boolean;
615
+ modes?: AcpModeInfo[];
616
+ currentModeId?: string;
617
+ error?: string;
618
+ };
619
+ }
620
+
621
+ // ── Chat history ──────────────────────────────────────────────────
622
+
623
+ export interface ChatHistoryMessage {
624
+ role: "user" | "assistant" | "system" | "tool";
625
+ text: string;
626
+ thinking?: string;
627
+ toolName?: string;
628
+ toolArgs?: string;
629
+ toolResult?: string;
630
+ images?: Array<{ data: string; mediaType: string }>;
631
+ timestamp?: number;
632
+ }
633
+
634
+ export interface ChatHistoryRequest extends ClusterFrame {
635
+ type: "chat_history_req";
636
+ id: string;
637
+ payload: {
638
+ sessionId: string;
639
+ limit?: number;
640
+ };
641
+ }
642
+
643
+ export interface ChatHistoryResponse extends ClusterFrame {
644
+ type: "chat_history_res";
645
+ id: string;
646
+ payload: {
647
+ success: boolean;
648
+ messages?: ChatHistoryMessage[];
649
+ error?: string;
650
+ };
651
+ }
652
+
653
+ // ── Task activity (Live Activity push to mobile nodes) ───────────
654
+
655
+ export type TaskActivityStatus = "started" | "progress" | "completed" | "failed";
656
+
657
+ export interface TaskActivityFrame extends ClusterFrame {
658
+ type: "task_activity";
659
+ payload: {
660
+ taskId: string;
661
+ taskType: "acp" | "handoff";
662
+ status: TaskActivityStatus;
663
+ agent: string;
664
+ nodeId: string; // node where task is running
665
+ title: string;
666
+ detail?: string;
667
+ progress?: number; // 0.0 - 1.0
668
+ startedAt: number;
669
+ elapsedMs?: number;
670
+ tool?: string; // current tool being executed
671
+ toolDone?: boolean;
672
+ };
673
+ }
674
+
675
+ // ── Peer approval ────────────────────────────────────────────────
676
+ export interface PeerApprovalNotify extends ClusterFrame {
677
+ type: "peer_approval_notify";
678
+ payload: {
679
+ approvalId: string;
680
+ nodeId: string;
681
+ deviceInfo?: DeviceInfo;
682
+ mode: "notify" | "required";
683
+ ip?: string;
684
+ /** Present when this is a resolution notification (approved/denied). */
685
+ resolution?: {
686
+ decision: "approve" | "deny";
687
+ resolvedBy: string;
688
+ };
689
+ };
690
+ }
691
+
692
+ export interface PeerApprovalRequest extends ClusterFrame {
693
+ type: "peer_approval_req";
694
+ id: string;
695
+ payload: {
696
+ approvalId: string;
697
+ nodeId: string;
698
+ deviceInfo?: DeviceInfo;
699
+ ip?: string;
700
+ };
701
+ }
702
+
703
+ export interface PeerApprovalResponse extends ClusterFrame {
704
+ type: "peer_approval_res";
705
+ id: string;
706
+ payload: {
707
+ approvalId: string;
708
+ decision: "approve" | "deny";
709
+ };
710
+ }
711
+
712
+ // ── Terminal (interactive PTY over WebSocket) ─────────────────────
713
+
714
+ export interface TerminalOpenRequest extends ClusterFrame {
715
+ type: "terminal_open";
716
+ id: string;
717
+ payload: {
718
+ shell?: string;
719
+ cols?: number;
720
+ rows?: number;
721
+ cwd?: string;
722
+ env?: Record<string, string>;
723
+ };
724
+ }
725
+
726
+ export interface TerminalOpenResponse extends ClusterFrame {
727
+ type: "terminal_open_res";
728
+ id: string;
729
+ payload: {
730
+ success: boolean;
731
+ sessionId?: string;
732
+ error?: string;
733
+ };
734
+ }
735
+
736
+ export interface TerminalData extends ClusterFrame {
737
+ type: "terminal_data";
738
+ id: string;
739
+ payload: {
740
+ sessionId: string;
741
+ data: string; // base64-encoded
742
+ direction: "input" | "output";
743
+ };
744
+ }
745
+
746
+ export interface TerminalResize extends ClusterFrame {
747
+ type: "terminal_resize";
748
+ id: string;
749
+ payload: {
750
+ sessionId: string;
751
+ cols: number;
752
+ rows: number;
753
+ };
754
+ }
755
+
756
+ export interface TerminalCloseRequest extends ClusterFrame {
757
+ type: "terminal_close";
758
+ id: string;
759
+ payload: {
760
+ sessionId: string;
761
+ exitCode?: number;
762
+ };
763
+ }
764
+
765
+ export interface TerminalCloseResponse extends ClusterFrame {
766
+ type: "terminal_close_res";
767
+ id: string;
768
+ payload: {
769
+ success: boolean;
770
+ error?: string;
332
771
  };
333
772
  }
334
773
 
@@ -336,6 +775,8 @@ export interface KnowledgeSyncFrame extends ClusterFrame {
336
775
  export type AnyClusterFrame =
337
776
  | AuthChallenge
338
777
  | AuthRequest
778
+ | AuthKeyExchange
779
+ | AuthVerify
339
780
  | AuthOk
340
781
  | AuthFail
341
782
  | PeerSync
@@ -357,4 +798,37 @@ export type AnyClusterFrame =
357
798
  | HandoffInput
358
799
  | ToolProxyRequest
359
800
  | ToolProxyResponse
360
- | KnowledgeSyncFrame;
801
+ | ToolBatchRequest
802
+ | ToolBatchResponse
803
+ | DiagnosticExec
804
+ | DiagnosticExecResponse
805
+ | DiagnosticStatus
806
+ | DiagnosticStatusResponse
807
+ | KnowledgeSyncFrame
808
+ | AcpTaskRequest
809
+ | AcpStreamChunk
810
+ | AcpTaskResponse
811
+ | AcpCloseRequest
812
+ | AcpCloseResponse
813
+ | AcpListRequest
814
+ | AcpListResponse
815
+ | AcpResumeRequest
816
+ | AcpResumeResponse
817
+ | AcpCancelRequest
818
+ | AcpCancelResponse
819
+ | AcpSetModeRequest
820
+ | AcpSetModeResponse
821
+ | AcpGetModesRequest
822
+ | AcpGetModesResponse
823
+ | ChatHistoryRequest
824
+ | ChatHistoryResponse
825
+ | PeerApprovalNotify
826
+ | PeerApprovalRequest
827
+ | PeerApprovalResponse
828
+ | TaskActivityFrame
829
+ | TerminalOpenRequest
830
+ | TerminalOpenResponse
831
+ | TerminalData
832
+ | TerminalResize
833
+ | TerminalCloseRequest
834
+ | TerminalCloseResponse;
package/src/web.ts CHANGED
@@ -288,14 +288,14 @@ export class WebHandler {
288
288
  } : undefined,
289
289
  clusterTools: [
290
290
  "cluster_handoff", "cluster_send", "cluster_peers",
291
- "cluster_exec", "cluster_read", "cluster_write", "cluster_tool",
291
+ "cluster_exec", "cluster_read", "cluster_write", "cluster_edit",
292
292
  ],
293
293
  };
294
294
 
295
295
  // All mesh peers share these cluster tools (they all run the ClawMatrix plugin)
296
296
  const CLUSTER_TOOLS = [
297
297
  "cluster_handoff", "cluster_send", "cluster_peers",
298
- "cluster_exec", "cluster_read", "cluster_write", "cluster_tool",
298
+ "cluster_exec", "cluster_read", "cluster_write", "cluster_edit",
299
299
  ];
300
300
 
301
301
  const peerNodes: Record<string, unknown>[] = peers.map((p) => ({