@canonmsg/agent-sdk 2.1.0 → 2.1.2

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 readonly typingSignals;
58
59
  private sseConnectedLogged;
59
60
  constructor(options: CanonAgentOptions);
60
61
  private ensureApprovalManager;
@@ -1,4 +1,4 @@
1
- import { ApprovalManager, CanonClient, buildCanonGroupContext, createTurnOutputController, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, selectActiveSelfContexts, } from '@canonmsg/core';
1
+ import { ApprovalManager, CanonClient, buildCanonGroupContext, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, selectActiveSelfContexts, } from '@canonmsg/core';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { AuthManager } from './auth.js';
4
4
  import { Debouncer } from './debouncer.js';
@@ -259,6 +259,7 @@ export class CanonAgent {
259
259
  activeTurns = new Map();
260
260
  conversationMemberIds = new Map();
261
261
  pendingMembershipChanges = new Map();
262
+ typingSignals;
262
263
  sseConnectedLogged = false;
263
264
  constructor(options) {
264
265
  this.options = {
@@ -271,6 +272,11 @@ export class CanonAgent {
271
272
  ...options,
272
273
  };
273
274
  this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
275
+ this.typingSignals = createTypingStatusPublisher({
276
+ setTyping: (conversationId, typing, status) => status
277
+ ? this.apiClient.setTyping(conversationId, typing, status)
278
+ : this.apiClient.setTyping(conversationId, typing),
279
+ });
274
280
  this.authManager = new AuthManager(this.apiClient);
275
281
  this.debouncer = new Debouncer(this.options.debounceMs);
276
282
  const apiClient = this.apiClient;
@@ -1002,7 +1008,7 @@ export class CanonAgent {
1002
1008
  }
1003
1009
  await Promise.all([
1004
1010
  this.apiClient.clearStreaming(conversationId).catch(() => { }),
1005
- this.apiClient.setTyping(conversationId, false).catch(() => { }),
1011
+ this.typingSignals.clear(conversationId).catch(() => { }),
1006
1012
  ]);
1007
1013
  }
1008
1014
  abortActiveTurns(conversationId) {
@@ -1149,14 +1155,13 @@ export class CanonAgent {
1149
1155
  };
1150
1156
  // Show thinking indicator and keep it alive (5s client-side expiry)
1151
1157
  try {
1152
- await this.apiClient.setTyping(conversationId, true, 'thinking');
1158
+ await this.typingSignals.start(conversationId, 'thinking');
1153
1159
  }
1154
1160
  catch {
1155
1161
  // Non-critical
1156
1162
  }
1157
1163
  await setLiveState('thinking', 'Thinking...', 'thinking');
1158
1164
  const thinkingKeepalive = setInterval(() => {
1159
- this.apiClient.setTyping(conversationId, true, 'thinking').catch(() => { });
1160
1165
  if (turnState === 'thinking') {
1161
1166
  turnOutput.setStatus('thinking', 'Thinking...').catch(() => { });
1162
1167
  }
@@ -1189,7 +1194,7 @@ export class CanonAgent {
1189
1194
  const replyFinal = async (text, options) => {
1190
1195
  throwIfAborted();
1191
1196
  try {
1192
- await this.apiClient.setTyping(conversationId, true, 'typing');
1197
+ await this.typingSignals.start(conversationId, 'typing');
1193
1198
  }
1194
1199
  catch { }
1195
1200
  throwIfAborted();
@@ -1206,7 +1211,7 @@ export class CanonAgent {
1206
1211
  });
1207
1212
  await sleep(FINAL_MESSAGE_HANDOFF_MS);
1208
1213
  try {
1209
- await this.apiClient.setTyping(conversationId, false);
1214
+ await this.typingSignals.clear(conversationId);
1210
1215
  }
1211
1216
  catch { }
1212
1217
  return result;
@@ -1355,7 +1360,7 @@ export class CanonAgent {
1355
1360
  catch { }
1356
1361
  await writeTurn('waiting_input');
1357
1362
  try {
1358
- await this.apiClient.setTyping(conversationId, false);
1363
+ await this.typingSignals.clear(conversationId);
1359
1364
  }
1360
1365
  catch { }
1361
1366
  const result = await manager.requestApproval(conversationId, request.toolName, request.toolInput ?? {}, {
@@ -1379,7 +1384,7 @@ export class CanonAgent {
1379
1384
  }
1380
1385
  catch { }
1381
1386
  try {
1382
- await this.apiClient.setTyping(conversationId, true, 'thinking');
1387
+ await this.typingSignals.start(conversationId, 'thinking');
1383
1388
  }
1384
1389
  catch { }
1385
1390
  await setLiveState('thinking', 'Thinking...', 'thinking');
@@ -1446,7 +1451,7 @@ export class CanonAgent {
1446
1451
  catch { }
1447
1452
  await writeTurn('waiting_input');
1448
1453
  try {
1449
- await this.apiClient.setTyping(conversationId, false);
1454
+ await this.typingSignals.clear(conversationId);
1450
1455
  }
1451
1456
  catch { }
1452
1457
  while (Date.now() < expiresAtMs) {
@@ -1520,7 +1525,7 @@ export class CanonAgent {
1520
1525
  }
1521
1526
  catch { }
1522
1527
  try {
1523
- await this.apiClient.setTyping(conversationId, true, 'thinking');
1528
+ await this.typingSignals.start(conversationId, 'thinking');
1524
1529
  }
1525
1530
  catch { }
1526
1531
  await setLiveState('thinking', 'Thinking...', 'thinking');
@@ -1532,6 +1537,7 @@ export class CanonAgent {
1532
1537
  throw error;
1533
1538
  }
1534
1539
  if (!requestCreated) {
1540
+ shouldPersistTurnState = false;
1535
1541
  throw error;
1536
1542
  }
1537
1543
  shouldPersistTurnState = false;
@@ -1619,7 +1625,7 @@ export class CanonAgent {
1619
1625
  catch { }
1620
1626
  await writeTurn('waiting_input');
1621
1627
  try {
1622
- await this.apiClient.setTyping(conversationId, false);
1628
+ await this.typingSignals.clear(conversationId);
1623
1629
  }
1624
1630
  catch { }
1625
1631
  while (Date.now() < expiresAtMs) {
@@ -1695,7 +1701,7 @@ export class CanonAgent {
1695
1701
  }
1696
1702
  catch { }
1697
1703
  try {
1698
- await this.apiClient.setTyping(conversationId, true, 'thinking');
1704
+ await this.typingSignals.start(conversationId, 'thinking');
1699
1705
  }
1700
1706
  catch { }
1701
1707
  await setLiveState('thinking', 'Thinking...', 'thinking');
@@ -1718,6 +1724,10 @@ export class CanonAgent {
1718
1724
  }
1719
1725
  throw error;
1720
1726
  }
1727
+ if (!requestCreated) {
1728
+ shouldPersistTurnState = false;
1729
+ throw error;
1730
+ }
1721
1731
  shouldPersistTurnState = false;
1722
1732
  return result;
1723
1733
  }
@@ -1726,7 +1736,7 @@ export class CanonAgent {
1726
1736
  const replyWithFile = async (filePath, text = '', options) => {
1727
1737
  throwIfAborted();
1728
1738
  try {
1729
- await this.apiClient.setTyping(conversationId, true, 'typing');
1739
+ await this.typingSignals.start(conversationId, 'typing');
1730
1740
  }
1731
1741
  catch { }
1732
1742
  throwIfAborted();
@@ -1756,7 +1766,7 @@ export class CanonAgent {
1756
1766
  }
1757
1767
  finally {
1758
1768
  try {
1759
- await this.apiClient.setTyping(conversationId, false);
1769
+ await this.typingSignals.clear(conversationId);
1760
1770
  }
1761
1771
  catch { }
1762
1772
  }
@@ -1876,7 +1886,7 @@ export class CanonAgent {
1876
1886
  catch { }
1877
1887
  await writeTurn('waiting_input');
1878
1888
  try {
1879
- await this.apiClient.setTyping(conversationId, false);
1889
+ await this.typingSignals.clear(conversationId);
1880
1890
  }
1881
1891
  catch { }
1882
1892
  if (text) {
@@ -1923,7 +1933,7 @@ export class CanonAgent {
1923
1933
  clearInterval(thinkingKeepalive);
1924
1934
  // Always clear typing when done
1925
1935
  try {
1926
- await this.apiClient.setTyping(conversationId, false);
1936
+ await this.typingSignals.clear(conversationId);
1927
1937
  }
1928
1938
  catch { }
1929
1939
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
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": "^1.2.0"
31
+ "@canonmsg/core": "^1.3.1"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"