@canonmsg/agent-sdk 1.5.2 → 1.5.4
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/canon-agent.d.ts +1 -0
- package/dist/canon-agent.js +170 -4
- package/dist/index.d.ts +2 -2
- package/dist/realtime.d.ts +4 -0
- package/dist/realtime.js +20 -2
- package/dist/turn-filter.js +6 -1
- package/dist/types.d.ts +29 -2
- package/package.json +2 -2
package/dist/canon-agent.d.ts
CHANGED
|
@@ -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;
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApprovalManager, CanonClient, buildCanonGroupContext, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, selectActiveSelfContexts, } from '@canonmsg/core';
|
|
1
|
+
import { ApprovalManager, CanonClient, buildCanonGroupContext, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeInputOutcome, buildRuntimeInputRequest, 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';
|
|
@@ -7,6 +7,9 @@ import { SessionManager } from './session-manager.js';
|
|
|
7
7
|
const AGENT_RUNTIME_HEARTBEAT_MS = 30_000;
|
|
8
8
|
const RUNTIME_PRIMITIVE_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
9
9
|
const RUNTIME_PRIMITIVE_DEDUPE_MAX = 1_000;
|
|
10
|
+
const DEFAULT_RUNTIME_INPUT_TIMEOUT_MS = 5 * 60_000;
|
|
11
|
+
const RUNTIME_INPUT_POLL_MS = 1_000;
|
|
12
|
+
const RUNTIME_INPUT_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,160}$/;
|
|
10
13
|
const SDK_RUNTIME_CAPABILITIES = {
|
|
11
14
|
supportsInterrupt: false,
|
|
12
15
|
supportsInputInterrupt: false,
|
|
@@ -125,6 +128,24 @@ const STANDARD_PRIMITIVE_COMMANDS = {
|
|
|
125
128
|
function sleep(ms) {
|
|
126
129
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
127
130
|
}
|
|
131
|
+
function sleepWithAbort(ms, signal) {
|
|
132
|
+
if (signal.aborted)
|
|
133
|
+
return Promise.reject(createTurnAbortError());
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const timer = setTimeout(resolve, ms);
|
|
136
|
+
signal.addEventListener('abort', () => {
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
reject(createTurnAbortError());
|
|
139
|
+
}, { once: true });
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function safeRuntimeInputId(value, kind) {
|
|
143
|
+
const raw = value?.trim() || `${kind}_${randomUUID()}`;
|
|
144
|
+
const normalized = raw.replace(/[.#$\[\]\/\s]+/g, '_').slice(0, 160);
|
|
145
|
+
return RUNTIME_INPUT_ID_PATTERN.test(normalized)
|
|
146
|
+
? normalized
|
|
147
|
+
: `${kind}_${randomUUID()}`;
|
|
148
|
+
}
|
|
128
149
|
function isRuntimePrimitiveId(value) {
|
|
129
150
|
return value === 'runtime.status'
|
|
130
151
|
|| value === 'runtime.reasoning.set'
|
|
@@ -219,6 +240,7 @@ export class CanonAgent {
|
|
|
219
240
|
activeTurns = new Map();
|
|
220
241
|
conversationMemberIds = new Map();
|
|
221
242
|
pendingMembershipChanges = new Map();
|
|
243
|
+
sseConnectedLogged = false;
|
|
222
244
|
constructor(options) {
|
|
223
245
|
this.options = {
|
|
224
246
|
baseUrl: 'https://api-6m6mlelskq-uc.a.run.app',
|
|
@@ -226,6 +248,7 @@ export class CanonAgent {
|
|
|
226
248
|
debounceMs: 2000,
|
|
227
249
|
historyLimit: 50,
|
|
228
250
|
autoMarkRead: true,
|
|
251
|
+
runtimeControlSurface: 'agent',
|
|
229
252
|
...options,
|
|
230
253
|
};
|
|
231
254
|
this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
|
|
@@ -533,12 +556,17 @@ export class CanonAgent {
|
|
|
533
556
|
this.sessionManager?.dropQueuedMessage(payload.conversationId, payload.messageId);
|
|
534
557
|
});
|
|
535
558
|
rtm.setConnectionHandlers({
|
|
536
|
-
onConnected: () =>
|
|
559
|
+
onConnected: () => {
|
|
560
|
+
this.startRuntimeHeartbeat();
|
|
561
|
+
if (!this.sseConnectedLogged) {
|
|
562
|
+
this.sseConnectedLogged = true;
|
|
563
|
+
console.log('[canon-sdk] SSE stream connected');
|
|
564
|
+
}
|
|
565
|
+
},
|
|
537
566
|
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
538
567
|
});
|
|
539
568
|
this.realtimeManager = rtm;
|
|
540
569
|
await rtm.start();
|
|
541
|
-
console.log('[canon-sdk] SSE stream started');
|
|
542
570
|
}
|
|
543
571
|
async createConversation(options) {
|
|
544
572
|
return this.apiClient.createConversation(options);
|
|
@@ -995,7 +1023,7 @@ export class CanonAgent {
|
|
|
995
1023
|
return createRuntimeStatePublisher({
|
|
996
1024
|
agentId: this.agentId,
|
|
997
1025
|
clientType: this.options.clientType ?? 'generic',
|
|
998
|
-
hostMode:
|
|
1026
|
+
hostMode: this.options.runtimeControlSurface === 'host',
|
|
999
1027
|
});
|
|
1000
1028
|
}
|
|
1001
1029
|
requireRuntimeStatePublisher() {
|
|
@@ -1058,6 +1086,10 @@ export class CanonAgent {
|
|
|
1058
1086
|
if (!runtimeState || !agentId)
|
|
1059
1087
|
return;
|
|
1060
1088
|
turnState = state;
|
|
1089
|
+
const isOpenTurn = state === 'thinking'
|
|
1090
|
+
|| state === 'streaming'
|
|
1091
|
+
|| state === 'tool'
|
|
1092
|
+
|| state === 'waiting_input';
|
|
1061
1093
|
await Promise.resolve(runtimeState.writeTurnState(conversationId, {
|
|
1062
1094
|
turnId,
|
|
1063
1095
|
state,
|
|
@@ -1065,6 +1097,7 @@ export class CanonAgent {
|
|
|
1065
1097
|
currentSpeakerId: agentId,
|
|
1066
1098
|
capabilities: this.buildRuntimeCapabilities(),
|
|
1067
1099
|
openedAt: turnOpenedAt,
|
|
1100
|
+
...(isOpenTurn ? { turnUpdatedAt: Date.now() } : {}),
|
|
1068
1101
|
...(state === 'completed' || state === 'interrupted' || state === 'idle'
|
|
1069
1102
|
? { completedAt: { '.sv': 'timestamp' } }
|
|
1070
1103
|
: {}),
|
|
@@ -1326,6 +1359,138 @@ export class CanonAgent {
|
|
|
1326
1359
|
return { decision: 'deny' };
|
|
1327
1360
|
}
|
|
1328
1361
|
};
|
|
1362
|
+
const requestRuntimeInput = async (request) => {
|
|
1363
|
+
throwIfAborted();
|
|
1364
|
+
const inputId = safeRuntimeInputId(request.inputId, request.kind);
|
|
1365
|
+
const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
|
|
1366
|
+
const expiresAtMs = Date.now() + timeoutMs;
|
|
1367
|
+
const expiresAt = new Date(expiresAtMs).toISOString();
|
|
1368
|
+
let result = { status: 'timeout', inputId };
|
|
1369
|
+
let requestCreated = false;
|
|
1370
|
+
let requestResolved = false;
|
|
1371
|
+
const cancelPendingRequest = async () => {
|
|
1372
|
+
if (!requestCreated || requestResolved)
|
|
1373
|
+
return;
|
|
1374
|
+
try {
|
|
1375
|
+
await this.apiClient.consumeRuntimeInputResponse({
|
|
1376
|
+
conversationId,
|
|
1377
|
+
inputId,
|
|
1378
|
+
cancel: true,
|
|
1379
|
+
});
|
|
1380
|
+
requestResolved = true;
|
|
1381
|
+
}
|
|
1382
|
+
catch { }
|
|
1383
|
+
};
|
|
1384
|
+
shouldPersistTurnState = true;
|
|
1385
|
+
try {
|
|
1386
|
+
await this.apiClient.createRuntimeInputRequest({
|
|
1387
|
+
conversationId,
|
|
1388
|
+
inputId,
|
|
1389
|
+
kind: request.kind,
|
|
1390
|
+
expiresAt: expiresAtMs,
|
|
1391
|
+
});
|
|
1392
|
+
requestCreated = true;
|
|
1393
|
+
try {
|
|
1394
|
+
await this.apiClient.clearStreaming(conversationId);
|
|
1395
|
+
}
|
|
1396
|
+
catch { }
|
|
1397
|
+
await writeTurn('waiting_input');
|
|
1398
|
+
try {
|
|
1399
|
+
await this.apiClient.setTyping(conversationId, false);
|
|
1400
|
+
}
|
|
1401
|
+
catch { }
|
|
1402
|
+
const card = buildRuntimeInputRequest(inputId, {
|
|
1403
|
+
kind: request.kind,
|
|
1404
|
+
title: request.title,
|
|
1405
|
+
prompt: request.prompt,
|
|
1406
|
+
...(request.choices ? { choices: request.choices } : {}),
|
|
1407
|
+
...(request.secretName ? { secretName: request.secretName } : {}),
|
|
1408
|
+
...(request.native ? { native: request.native } : {}),
|
|
1409
|
+
expiresAt,
|
|
1410
|
+
...(request.sensitive !== undefined ? { sensitive: request.sensitive } : {}),
|
|
1411
|
+
});
|
|
1412
|
+
await this.apiClient.sendMessage(conversationId, card.text, {
|
|
1413
|
+
metadata: {
|
|
1414
|
+
...card.metadata,
|
|
1415
|
+
turnId: request.turnId ?? turnId,
|
|
1416
|
+
turnSemantics: 'control',
|
|
1417
|
+
replyBehavior: 'suppress_auto_reply',
|
|
1418
|
+
},
|
|
1419
|
+
});
|
|
1420
|
+
while (Date.now() < expiresAtMs) {
|
|
1421
|
+
throwIfAborted();
|
|
1422
|
+
try {
|
|
1423
|
+
const response = await this.apiClient.consumeRuntimeInputResponse({
|
|
1424
|
+
conversationId,
|
|
1425
|
+
inputId,
|
|
1426
|
+
});
|
|
1427
|
+
if (response.status === 'submitted') {
|
|
1428
|
+
requestResolved = true;
|
|
1429
|
+
result = {
|
|
1430
|
+
status: 'submitted',
|
|
1431
|
+
value: response.value,
|
|
1432
|
+
inputId,
|
|
1433
|
+
};
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
if (response.status === 'cancelled' || response.status === 'timeout') {
|
|
1437
|
+
requestResolved = true;
|
|
1438
|
+
result = { status: response.status, inputId };
|
|
1439
|
+
break;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
catch {
|
|
1443
|
+
// Transient consume failures should not leak sensitive input or
|
|
1444
|
+
// break the runtime; keep waiting until the explicit timeout.
|
|
1445
|
+
}
|
|
1446
|
+
await sleepWithAbort(Math.min(RUNTIME_INPUT_POLL_MS, Math.max(1, expiresAtMs - Date.now())), abortController.signal);
|
|
1447
|
+
}
|
|
1448
|
+
if (!requestResolved) {
|
|
1449
|
+
try {
|
|
1450
|
+
const response = await this.apiClient.consumeRuntimeInputResponse({
|
|
1451
|
+
conversationId,
|
|
1452
|
+
inputId,
|
|
1453
|
+
});
|
|
1454
|
+
if (response.status === 'submitted') {
|
|
1455
|
+
result = { status: 'submitted', value: response.value, inputId };
|
|
1456
|
+
}
|
|
1457
|
+
else if (response.status === 'cancelled' || response.status === 'timeout') {
|
|
1458
|
+
result = { status: response.status, inputId };
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
catch { }
|
|
1462
|
+
requestResolved = true;
|
|
1463
|
+
}
|
|
1464
|
+
const outcome = buildRuntimeInputOutcome(inputId, result.status, {
|
|
1465
|
+
kind: request.kind,
|
|
1466
|
+
reason: result.status,
|
|
1467
|
+
});
|
|
1468
|
+
await this.apiClient.sendMessage(conversationId, outcome.text, {
|
|
1469
|
+
metadata: {
|
|
1470
|
+
...outcome.metadata,
|
|
1471
|
+
turnId: request.turnId ?? turnId,
|
|
1472
|
+
turnSemantics: 'control',
|
|
1473
|
+
replyBehavior: 'suppress_auto_reply',
|
|
1474
|
+
},
|
|
1475
|
+
});
|
|
1476
|
+
throwIfAborted();
|
|
1477
|
+
shouldPersistTurnState = false;
|
|
1478
|
+
try {
|
|
1479
|
+
await this.apiClient.setTyping(conversationId, true, 'thinking');
|
|
1480
|
+
}
|
|
1481
|
+
catch { }
|
|
1482
|
+
await setLiveState('thinking', 'Thinking...', 'thinking');
|
|
1483
|
+
return result;
|
|
1484
|
+
}
|
|
1485
|
+
catch (error) {
|
|
1486
|
+
await cancelPendingRequest();
|
|
1487
|
+
if (abortController.signal.aborted || isAbortLikeError(error)) {
|
|
1488
|
+
throw error;
|
|
1489
|
+
}
|
|
1490
|
+
shouldPersistTurnState = false;
|
|
1491
|
+
return result;
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1329
1494
|
const uploadFile = (filePath, options) => uploadMediaFile(this.apiClient, conversationId, filePath, options);
|
|
1330
1495
|
const replyWithFile = async (filePath, text = '', options) => {
|
|
1331
1496
|
throwIfAborted();
|
|
@@ -1388,6 +1553,7 @@ export class CanonAgent {
|
|
|
1388
1553
|
selfContexts,
|
|
1389
1554
|
provenance,
|
|
1390
1555
|
requestApproval,
|
|
1556
|
+
requestRuntimeInput,
|
|
1391
1557
|
abortSignal: abortController.signal,
|
|
1392
1558
|
media: {
|
|
1393
1559
|
materialize: (message = hydratedMessages[hydratedMessages.length - 1], options) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { CanonAgent } from './canon-agent.js';
|
|
2
2
|
export type { AgentContactsAPI, AgentUsersAPI } from './canon-agent.js';
|
|
3
3
|
export { ApprovalManager, buildApprovalOutcome, buildApprovalReply, buildApprovalRequest, CanonApiError, DEFAULT_APPROVAL_CONFIG, generateApprovalId, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, parseApprovalReplyMetadata, parseApprovalRequestMetadata, parseSessionRule, parseTextApprovalReply, redactSecrets, } from '@canonmsg/core';
|
|
4
|
-
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, SessionRule, } from '@canonmsg/core';
|
|
4
|
+
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, SessionRule, } from '@canonmsg/core';
|
|
5
5
|
export { SessionManager } from './session-manager.js';
|
|
6
6
|
export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
|
|
7
7
|
export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, MaterializedCanonReplyContext, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
|
|
8
8
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
9
9
|
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonReplyContext, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
|
|
10
|
-
export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimeApprovalRequest, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
|
10
|
+
export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimeApprovalRequest, RuntimeInputRequest, RuntimeInputResult, RuntimeControlSurface, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/dist/turn-filter.js
CHANGED
|
@@ -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 {
|
|
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/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonMessage, CanonConversation, CanonReplyContext, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, SessionRule, ApprovalResult, } from '@canonmsg/core';
|
|
2
|
-
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CanonReplyContext, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
|
|
1
|
+
export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonMessage, CanonConversation, CanonReplyContext, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, SessionRule, ApprovalResult, } from '@canonmsg/core';
|
|
2
|
+
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CanonReplyContext, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
|
|
3
3
|
import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
|
|
4
4
|
export interface ProgressMessageOptions extends SendMessageOptions {
|
|
5
5
|
/**
|
|
@@ -56,6 +56,24 @@ export interface RuntimeApprovalRequest {
|
|
|
56
56
|
/** Whether an approval reply may set an in-memory session rule. */
|
|
57
57
|
allowSessionRule?: boolean;
|
|
58
58
|
}
|
|
59
|
+
export interface RuntimeInputRequest {
|
|
60
|
+
kind: RuntimeInputKind;
|
|
61
|
+
title: string;
|
|
62
|
+
prompt: string;
|
|
63
|
+
choices?: RuntimeInputChoice[];
|
|
64
|
+
secretName?: string;
|
|
65
|
+
sensitive?: boolean;
|
|
66
|
+
runtimeId?: string;
|
|
67
|
+
turnId?: string;
|
|
68
|
+
native?: RuntimeInputNativeMetadata;
|
|
69
|
+
inputId?: string;
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
}
|
|
72
|
+
export interface RuntimeInputResult {
|
|
73
|
+
status: 'submitted' | 'cancelled' | 'timeout';
|
|
74
|
+
value?: string;
|
|
75
|
+
inputId: string;
|
|
76
|
+
}
|
|
59
77
|
export interface MessageHandlerContext {
|
|
60
78
|
messages: CanonMessage[];
|
|
61
79
|
history: CanonMessage[];
|
|
@@ -109,6 +127,12 @@ export interface MessageHandlerContext {
|
|
|
109
127
|
* and enforce the returned decision in their own approval hook.
|
|
110
128
|
*/
|
|
111
129
|
requestApproval: (request: RuntimeApprovalRequest) => Promise<ApprovalResult>;
|
|
130
|
+
/**
|
|
131
|
+
* Ask the agent owner for runtime input such as clarification, sudo, or a
|
|
132
|
+
* secret. Sensitive values are returned only to the calling runtime and are
|
|
133
|
+
* never persisted in Canon message metadata.
|
|
134
|
+
*/
|
|
135
|
+
requestRuntimeInput: (request: RuntimeInputRequest) => Promise<RuntimeInputResult>;
|
|
112
136
|
/** Canon-managed local media access for the current conversation. */
|
|
113
137
|
media: {
|
|
114
138
|
materialize: (message?: CanonMessage, options?: Omit<MaterializeMediaOptions, 'agentId' | 'conversationId' | 'messageId'>) => Promise<MaterializedCanonAttachment[]>;
|
|
@@ -155,6 +179,7 @@ export interface RuntimeControlHandlers {
|
|
|
155
179
|
onStopAndDrop?: RuntimeSignalHandler;
|
|
156
180
|
onNewSession?: RuntimeSignalHandler;
|
|
157
181
|
}
|
|
182
|
+
export type RuntimeControlSurface = 'agent' | 'host';
|
|
158
183
|
export interface RuntimePrimitiveContext {
|
|
159
184
|
conversationId: string;
|
|
160
185
|
primitive: CanonRuntimePrimitiveId;
|
|
@@ -186,6 +211,8 @@ export interface CanonAgentOptions {
|
|
|
186
211
|
runtimeDescriptor?: import('@canonmsg/core').CanonRuntimeDescriptor;
|
|
187
212
|
/** Optional Canon runtime signal handlers. Enables interrupt controls when provided. */
|
|
188
213
|
runtimeControls?: RuntimeControlHandlers;
|
|
214
|
+
/** Runtime publishing surface. Use `host` when this agent owns live runtime controls. */
|
|
215
|
+
runtimeControlSurface?: RuntimeControlSurface;
|
|
189
216
|
/** Optional typed runtime primitive handlers. Enables descriptor-backed command controls when provided. */
|
|
190
217
|
runtimePrimitives?: RuntimePrimitiveHandlers;
|
|
191
218
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
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.
|
|
31
|
+
"@canonmsg/core": "^0.20.1"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|