@love-moon/conductor-sdk 0.2.38 → 0.2.40

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/dist/client.d.ts CHANGED
@@ -26,6 +26,8 @@ export interface ConductorClientConnectOptions {
26
26
  onDisconnected?: (event: WebSocketDisconnectEvent) => void;
27
27
  onPong?: (event: WebSocketPongEvent) => void;
28
28
  onStopTask?: (event: StopTaskEvent) => Promise<void> | void;
29
+ onInterruptTurn?: (event: InterruptTurnEvent) => Promise<boolean | void> | boolean | void;
30
+ onRefreshSession?: (event: RefreshSessionEvent) => Promise<boolean | void> | boolean | void;
29
31
  }
30
32
  interface ConductorClientInit {
31
33
  config: ConductorConfig;
@@ -41,12 +43,27 @@ interface ConductorClientInit {
41
43
  downstreamCursorStore: DownstreamCursorStore;
42
44
  agentHost: string;
43
45
  onStopTask?: (event: StopTaskEvent) => Promise<void> | void;
46
+ onInterruptTurn?: (event: InterruptTurnEvent) => Promise<boolean | void> | boolean | void;
47
+ onRefreshSession?: (event: RefreshSessionEvent) => Promise<boolean | void> | boolean | void;
44
48
  }
45
49
  export interface StopTaskEvent {
46
50
  taskId: string;
47
51
  requestId?: string;
48
52
  reason?: string;
49
53
  }
54
+ export interface InterruptTurnEvent {
55
+ taskId: string;
56
+ requestId?: string;
57
+ reason?: string;
58
+ targetReplyTo: string;
59
+ }
60
+ export interface RefreshSessionEvent {
61
+ taskId: string;
62
+ requestId?: string;
63
+ reason?: string;
64
+ sessionId: string;
65
+ sessionFilePath?: string;
66
+ }
50
67
  export interface FlushPendingUpstreamEventsOptions {
51
68
  timeoutMs?: number;
52
69
  retryIntervalMs?: number;
@@ -64,6 +81,8 @@ export declare class ConductorClient {
64
81
  private downstreamCursorStore;
65
82
  private readonly agentHost;
66
83
  private readonly onStopTask?;
84
+ private readonly onInterruptTurn?;
85
+ private readonly onRefreshSession?;
67
86
  private deliveryScopeId;
68
87
  private closed;
69
88
  private durableOutboxFlushPromise;
@@ -116,6 +135,10 @@ export declare class ConductorClient {
116
135
  private extractDownstreamCommandContext;
117
136
  private handleStopTaskCommand;
118
137
  private invokeStopTaskHandler;
138
+ private handleInterruptTurnCommand;
139
+ private handleRefreshSessionCommand;
140
+ private invokeInterruptTurnHandler;
141
+ private invokeRefreshSessionHandler;
119
142
  private persistAndCommitUpstreamEvent;
120
143
  private requestDurableOutboxFlush;
121
144
  private hasEarlierPendingSdkMessage;
package/dist/client.js CHANGED
@@ -23,6 +23,8 @@ export class ConductorClient {
23
23
  downstreamCursorStore;
24
24
  agentHost;
25
25
  onStopTask;
26
+ onInterruptTurn;
27
+ onRefreshSession;
26
28
  deliveryScopeId;
27
29
  closed = false;
28
30
  durableOutboxFlushPromise = null;
@@ -41,6 +43,8 @@ export class ConductorClient {
41
43
  this.downstreamCursorStore = init.downstreamCursorStore;
42
44
  this.agentHost = init.agentHost;
43
45
  this.onStopTask = init.onStopTask;
46
+ this.onInterruptTurn = init.onInterruptTurn;
47
+ this.onRefreshSession = init.onRefreshSession;
44
48
  this.deliveryScopeId = init.deliveryScopeId;
45
49
  this.wsClient.registerHandler(this.handleBackendEvent);
46
50
  }
@@ -83,6 +87,8 @@ export class ConductorClient {
83
87
  downstreamCursorStore,
84
88
  agentHost,
85
89
  onStopTask: options.onStopTask,
90
+ onInterruptTurn: options.onInterruptTurn,
91
+ onRefreshSession: options.onRefreshSession,
86
92
  });
87
93
  await client.wsClient.connect();
88
94
  if (client.shouldAutoFlushDurableOutbox()) {
@@ -533,6 +539,21 @@ export class ConductorClient {
533
539
  };
534
540
  }
535
541
  handleBackendEvent = async (payload) => {
542
+ if (typeof payload?.type === 'string' && payload.type === 'refresh_session') {
543
+ void this.handleRefreshSessionCommand(payload).catch((error) => {
544
+ const data = payload?.payload && typeof payload.payload === 'object'
545
+ ? payload.payload
546
+ : null;
547
+ const taskId = typeof data?.task_id === 'string' ? data.task_id.trim() : '';
548
+ const message = error instanceof Error ? error.message : String(error);
549
+ console.warn(`[sdk] refresh_session dispatch failed${taskId ? ` for task ${taskId}` : ''}: ${message}`);
550
+ });
551
+ return;
552
+ }
553
+ if (typeof payload?.type === 'string' && payload.type === 'interrupt_turn') {
554
+ await this.handleInterruptTurnCommand(payload);
555
+ return;
556
+ }
536
557
  const command = this.extractDownstreamCommandContext(payload);
537
558
  if (command?.eventType === 'stop_task') {
538
559
  await this.handleStopTaskCommand(payload, command);
@@ -630,6 +651,92 @@ export class ConductorClient {
630
651
  return false;
631
652
  }
632
653
  }
654
+ async handleInterruptTurnCommand(payload) {
655
+ const accepted = await this.invokeInterruptTurnHandler(payload);
656
+ await this.maybeAckInboundCommand(payload, { accepted });
657
+ }
658
+ async handleRefreshSessionCommand(payload) {
659
+ const accepted = await this.invokeRefreshSessionHandler(payload);
660
+ await this.maybeAckInboundCommand(payload, { accepted });
661
+ }
662
+ async invokeInterruptTurnHandler(payload) {
663
+ const data = payload?.payload && typeof payload.payload === 'object'
664
+ ? payload.payload
665
+ : null;
666
+ if (!data) {
667
+ return false;
668
+ }
669
+ const taskId = typeof data.task_id === 'string' ? data.task_id.trim() : '';
670
+ const requestId = typeof data.request_id === 'string' ? data.request_id.trim() : '';
671
+ const reason = typeof data.reason === 'string' ? data.reason.trim() : '';
672
+ const targetReplyTo = typeof data.target_reply_to === 'string'
673
+ ? data.target_reply_to.trim()
674
+ : typeof data.targetReplyTo === 'string'
675
+ ? data.targetReplyTo.trim()
676
+ : '';
677
+ if (!taskId || !targetReplyTo) {
678
+ return false;
679
+ }
680
+ if (!this.onInterruptTurn) {
681
+ return false;
682
+ }
683
+ try {
684
+ const accepted = await this.onInterruptTurn({
685
+ taskId,
686
+ requestId: requestId || undefined,
687
+ reason: reason || undefined,
688
+ targetReplyTo,
689
+ });
690
+ return accepted !== false;
691
+ }
692
+ catch (error) {
693
+ const message = error instanceof Error ? error.message : String(error);
694
+ console.warn(`[sdk] interrupt_turn callback failed for task ${taskId}: ${message}`);
695
+ return false;
696
+ }
697
+ }
698
+ async invokeRefreshSessionHandler(payload) {
699
+ const data = payload?.payload && typeof payload.payload === 'object'
700
+ ? payload.payload
701
+ : null;
702
+ if (!data) {
703
+ return false;
704
+ }
705
+ const taskId = typeof data.task_id === 'string' ? data.task_id.trim() : '';
706
+ const requestId = typeof data.request_id === 'string' ? data.request_id.trim() : '';
707
+ const reason = typeof data.reason === 'string' ? data.reason.trim() : '';
708
+ const sessionId = typeof data.session_id === 'string'
709
+ ? data.session_id.trim()
710
+ : typeof data.sessionId === 'string'
711
+ ? data.sessionId.trim()
712
+ : '';
713
+ const sessionFilePath = typeof data.session_file_path === 'string'
714
+ ? data.session_file_path.trim()
715
+ : typeof data.sessionFilePath === 'string'
716
+ ? data.sessionFilePath.trim()
717
+ : '';
718
+ if (!taskId || !sessionId) {
719
+ return false;
720
+ }
721
+ if (!this.onRefreshSession) {
722
+ return false;
723
+ }
724
+ try {
725
+ const accepted = await this.onRefreshSession({
726
+ taskId,
727
+ requestId: requestId || undefined,
728
+ reason: reason || undefined,
729
+ sessionId,
730
+ sessionFilePath: sessionFilePath || undefined,
731
+ });
732
+ return accepted !== false;
733
+ }
734
+ catch (error) {
735
+ const message = error instanceof Error ? error.message : String(error);
736
+ console.warn(`[sdk] refresh_session callback failed for task ${taskId}: ${message}`);
737
+ return false;
738
+ }
739
+ }
633
740
  async persistAndCommitUpstreamEvent(entry) {
634
741
  const stored = this.upstreamOutbox.upsert(entry);
635
742
  if (stored.eventType === 'sdk_message' && this.hasEarlierPendingSdkMessage(stored.stableId)) {
@@ -796,7 +903,10 @@ export class ConductorClient {
796
903
  }
797
904
  async maybeAckInboundCommand(payload, options = {}) {
798
905
  const eventType = typeof payload?.type === 'string' ? payload.type : '';
799
- if (eventType !== 'task_user_message' && eventType !== 'task_action') {
906
+ if (eventType !== 'task_user_message' &&
907
+ eventType !== 'task_action' &&
908
+ eventType !== 'interrupt_turn' &&
909
+ eventType !== 'refresh_session') {
800
910
  return;
801
911
  }
802
912
  const data = payload?.payload && typeof payload.payload === 'object'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-sdk",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,5 +27,5 @@
27
27
  "typescript": "^5.6.3",
28
28
  "vitest": "^2.1.4"
29
29
  },
30
- "gitCommitId": "6bf5515"
30
+ "gitCommitId": "e08f8b6"
31
31
  }