@canonmsg/agent-sdk 1.5.2 → 1.5.3

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.
@@ -55,6 +55,7 @@ export declare class CanonAgent {
55
55
  private readonly activeTurns;
56
56
  private readonly conversationMemberIds;
57
57
  private readonly pendingMembershipChanges;
58
+ private sseConnectedLogged;
58
59
  constructor(options: CanonAgentOptions);
59
60
  private ensureApprovalManager;
60
61
  private filterApprovalReplyMessages;
@@ -219,6 +219,7 @@ export class CanonAgent {
219
219
  activeTurns = new Map();
220
220
  conversationMemberIds = new Map();
221
221
  pendingMembershipChanges = new Map();
222
+ sseConnectedLogged = false;
222
223
  constructor(options) {
223
224
  this.options = {
224
225
  baseUrl: 'https://api-6m6mlelskq-uc.a.run.app',
@@ -533,12 +534,17 @@ export class CanonAgent {
533
534
  this.sessionManager?.dropQueuedMessage(payload.conversationId, payload.messageId);
534
535
  });
535
536
  rtm.setConnectionHandlers({
536
- onConnected: () => this.startRuntimeHeartbeat(),
537
+ onConnected: () => {
538
+ this.startRuntimeHeartbeat();
539
+ if (!this.sseConnectedLogged) {
540
+ this.sseConnectedLogged = true;
541
+ console.log('[canon-sdk] SSE stream connected');
542
+ }
543
+ },
537
544
  onDisconnected: () => this.stopRuntimeHeartbeat(),
538
545
  });
539
546
  this.realtimeManager = rtm;
540
547
  await rtm.start();
541
- console.log('[canon-sdk] SSE stream started');
542
548
  }
543
549
  async createConversation(options) {
544
550
  return this.apiClient.createConversation(options);
@@ -1058,6 +1064,10 @@ export class CanonAgent {
1058
1064
  if (!runtimeState || !agentId)
1059
1065
  return;
1060
1066
  turnState = state;
1067
+ const isOpenTurn = state === 'thinking'
1068
+ || state === 'streaming'
1069
+ || state === 'tool'
1070
+ || state === 'waiting_input';
1061
1071
  await Promise.resolve(runtimeState.writeTurnState(conversationId, {
1062
1072
  turnId,
1063
1073
  state,
@@ -1065,6 +1075,7 @@ export class CanonAgent {
1065
1075
  currentSpeakerId: agentId,
1066
1076
  capabilities: this.buildRuntimeCapabilities(),
1067
1077
  openedAt: turnOpenedAt,
1078
+ ...(isOpenTurn ? { turnUpdatedAt: Date.now() } : {}),
1068
1079
  ...(state === 'completed' || state === 'interrupted' || state === 'idle'
1069
1080
  ? { completedAt: { '.sv': 'timestamp' } }
1070
1081
  : {}),
@@ -10,6 +10,9 @@ export declare class RealtimeManager {
10
10
  private agentId;
11
11
  private stream;
12
12
  private running;
13
+ private lastSseErrorKey;
14
+ private lastSseErrorAt;
15
+ private suppressedSseErrorCount;
13
16
  private onAgentContext;
14
17
  private onContactRequest;
15
18
  private onContactApproved;
@@ -20,6 +23,7 @@ export declare class RealtimeManager {
20
23
  private onConnected;
21
24
  private onDisconnected;
22
25
  constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, apiClient?: CanonClient);
26
+ private logSseError;
23
27
  setOnAgentContext(cb: (ctx: AgentContext) => void): void;
24
28
  setContactRequestHandlers(handlers: {
25
29
  onContactRequest?: (payload: ContactRequestPayload) => void;
package/dist/realtime.js CHANGED
@@ -9,6 +9,9 @@ export class RealtimeManager {
9
9
  agentId;
10
10
  stream;
11
11
  running = false;
12
+ lastSseErrorKey = null;
13
+ lastSseErrorAt = 0;
14
+ suppressedSseErrorCount = 0;
12
15
  onAgentContext = null;
13
16
  onContactRequest = null;
14
17
  onContactApproved = null;
@@ -85,11 +88,27 @@ export class RealtimeManager {
85
88
  this.onDisconnected?.();
86
89
  },
87
90
  onError: (err) => {
88
- console.error('[canon-sdk] SSE error:', err.message);
91
+ this.logSseError(err);
89
92
  },
90
93
  },
91
94
  });
92
95
  }
96
+ logSseError(err) {
97
+ const code = err.code;
98
+ const key = `${typeof code === 'string' ? code : 'generic'}:${err.message}`;
99
+ const now = Date.now();
100
+ if (this.lastSseErrorKey === key && now - this.lastSseErrorAt < 60_000) {
101
+ this.suppressedSseErrorCount += 1;
102
+ return;
103
+ }
104
+ if (this.suppressedSseErrorCount > 0) {
105
+ console.error(`[canon-sdk] SSE error repeated ${this.suppressedSseErrorCount} more time${this.suppressedSseErrorCount === 1 ? '' : 's'}`);
106
+ this.suppressedSseErrorCount = 0;
107
+ }
108
+ this.lastSseErrorKey = key;
109
+ this.lastSseErrorAt = now;
110
+ console.error('[canon-sdk] SSE error:', err.message);
111
+ }
93
112
  setOnAgentContext(cb) {
94
113
  this.onAgentContext = cb;
95
114
  }
@@ -118,6 +137,5 @@ export class RealtimeManager {
118
137
  stop() {
119
138
  this.running = false;
120
139
  this.stream.stop();
121
- this.onDisconnected?.();
122
140
  }
123
141
  }
@@ -2,7 +2,12 @@ import { evaluateParticipationPolicy, normalizeTurnState, rtdbRead, shouldTrigge
2
2
  function normalizeRuntimeTurnState(value) {
3
3
  const turnState = normalizeTurnState(value);
4
4
  if (turnState) {
5
- return { state: turnState.state };
5
+ return {
6
+ state: turnState.state,
7
+ ...(turnState.openedAt !== undefined ? { openedAt: turnState.openedAt } : {}),
8
+ ...(turnState.updatedAt !== undefined ? { updatedAt: turnState.updatedAt } : {}),
9
+ ...(turnState.turnUpdatedAt !== undefined ? { turnUpdatedAt: turnState.turnUpdatedAt } : {}),
10
+ };
6
11
  }
7
12
  return null;
8
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Canon Agent SDK — build AI agents that participate in Canon conversations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "node": ">=18.0.0"
29
29
  },
30
30
  "dependencies": {
31
- "@canonmsg/core": "^0.19.3"
31
+ "@canonmsg/core": "^0.20.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"