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