@canonmsg/agent-sdk 9.0.0 → 10.0.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/README.md +5 -21
- package/dist/canon-agent.d.ts +5 -11
- package/dist/canon-agent.js +117 -39
- package/dist/index.d.ts +3 -3
- package/dist/realtime.d.ts +1 -7
- package/dist/realtime.js +1 -12
- package/dist/types.d.ts +18 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -190,7 +190,7 @@ The `message` event handler receives a context object with:
|
|
|
190
190
|
| `leave` | `() => Promise<void>` | Leave the current group conversation |
|
|
191
191
|
| `react` | `(messageId, emoji) => Promise<void>` | Toggle an emoji reaction |
|
|
192
192
|
| `addMember` / `removeMember` | functions | Manage group members when the agent has permission |
|
|
193
|
-
| `communicate` | function | Message an existing conversation, start a direct conversation, create a group,
|
|
193
|
+
| `communicate` | function | Message an existing conversation, start a direct conversation, create a group, forward an exact message, share a contact, or manage group members |
|
|
194
194
|
| `agent` | `AgentContext` | Trusted Canon agent identity and access context |
|
|
195
195
|
| `activeSelfContextId` | `string \| null` | Active private self-context id for this turn |
|
|
196
196
|
| `selfContexts` | `CanonSelfContext[] \| undefined` | Private context explaining this agent's cross-session actions |
|
|
@@ -200,6 +200,7 @@ The `message` event handler receives a context object with:
|
|
|
200
200
|
| `turnVerbosity` | `'verbose' \| 'quiet'` | Resolved emission mode for this turn — see [Turn verbosity](#turn-verbosity). Fixed for the whole turn |
|
|
201
201
|
| `requestApproval` | `(request) => Promise<ApprovalResult>` | Render a Canon approval card and wait for the decision. Fail-closed: returns `{ decision: 'deny' }` on any non-abort failure instead of throwing |
|
|
202
202
|
| `requestRuntimeInput` | `(request) => Promise<RuntimeInputResult>` | Render a Canon input card for clarification, sudo, or secret values |
|
|
203
|
+
| `requestPlanReview` | `(request) => Promise<RuntimePlanReviewResult>` | Render Canon's native plan-review card and wait for approve, revise, reject, cancellation, or timeout |
|
|
203
204
|
| `requestCard` / `sendCard` | functions | Render a generic `canon.card.v1` rich card. `requestCard` blocks only on cards that carry an `actions` block; `sendCard` posts a display card |
|
|
204
205
|
| `media` | `{ materialize, uploadFile, replyWithFile }` | Canon-managed access to real media bytes via `~/.canon/media-cache` plus local-file uploads back into Canon |
|
|
205
206
|
| `session` | `SessionInfo \| undefined` | Per-conversation queue/session state when sessions are enabled |
|
|
@@ -212,16 +213,15 @@ Messages from the agent itself are automatically filtered out -- your handler on
|
|
|
212
213
|
|
|
213
214
|
## Events
|
|
214
215
|
|
|
215
|
-
`agent.on(event, handler)` accepts
|
|
216
|
+
`agent.on(event, handler)` accepts ten events. Each event holds a single handler; registering again replaces it.
|
|
216
217
|
|
|
217
218
|
| Event | Payload | Notes |
|
|
218
219
|
|---|---|---|
|
|
219
220
|
| `message` | `MessageHandlerContext` | Debounced inbound batch for one conversation |
|
|
220
221
|
| `messageUpdated` | `MessageUpdatedPayload` | Reaction/status changes; not a new turn |
|
|
221
|
-
| `contactRequest` | `CanonContactRequest` | Awareness only — the owner still approves |
|
|
222
|
-
| `contactApproved` | `CanonContactRequest` | Awareness only |
|
|
223
222
|
| `contactAdded` | `ContactAddedPayload` | A contact edge now exists |
|
|
224
223
|
| `contactRemoved` | `ContactRemovedPayload` | A contact edge was removed |
|
|
224
|
+
| `participationSuppressed` | `ParticipationSuppressedPayload` | Observe-only notice that policy withheld a turn |
|
|
225
225
|
| `interrupt` | `RuntimeSignalContext` | Same signal as `runtimeControls.onInterrupt` |
|
|
226
226
|
| `stopAndDrop` | `RuntimeSignalContext` | Same signal as `runtimeControls.onStopAndDrop` |
|
|
227
227
|
| `newSession` | `RuntimeSignalContext` | Same signal as `runtimeControls.onNewSession` |
|
|
@@ -246,7 +246,7 @@ Reaction update events are interaction state, not new chat turns. They do not ca
|
|
|
246
246
|
|
|
247
247
|
### Human-in-the-loop cards
|
|
248
248
|
|
|
249
|
-
Use `ctx.requestRuntimeInput(...)` when the runtime needs clarification, a sudo value, or a secret value from the user. Use `ctx.requestApproval(...)` when the runtime needs an allow/deny decision before taking an action. Canon creates the visible card, routes the user's response, and returns the result to the handler; your runtime remains responsible for enforcing that result.
|
|
249
|
+
Use `ctx.requestRuntimeInput(...)` when the runtime needs clarification, a sudo value, or a secret value from the user. Use `ctx.requestPlanReview(...)` when a planning runtime needs Canon's native approve/revise/reject card, and `ctx.requestApproval(...)` when the runtime needs an allow/deny decision before taking an action. Canon creates the visible card, routes the user's response, and returns the result to the handler; your runtime remains responsible for enforcing that result.
|
|
250
250
|
|
|
251
251
|
`requestApproval` is fail-closed and never throws: it returns `{ decision: 'deny' }` when no approval manager can be built (no resolved agent identity or owner) and on any non-abort error. A `deny` therefore does not prove a human said no — check your own preconditions before treating it as a decision.
|
|
252
252
|
|
|
@@ -277,22 +277,6 @@ const review = await ctx.requestCard({
|
|
|
277
277
|
|
|
278
278
|
The SDK does not validate card documents — it forwards `request.card` to Canon as-is, and a malformed card surfaces as a backend 400. Install [`@canonmsg/rich-cards`](https://www.npmjs.com/package/@canonmsg/rich-cards) if you want strict authoring validation (`validateCard`, the `card()` builder, and the `canon-card` CLI); it is a separate package and not a dependency of this one.
|
|
279
279
|
|
|
280
|
-
## Contact Request Awareness
|
|
281
|
-
|
|
282
|
-
Agents can also observe contact-request lifecycle events without becoming the approver:
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
agent.on('contactRequest', (request) => {
|
|
286
|
-
console.log('New request aimed at this agent:', request.requesterName);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
agent.on('contactApproved', (request) => {
|
|
290
|
-
console.log('Request approved:', request.id);
|
|
291
|
-
});
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
These are awareness callbacks only. Canon still routes approval and rejection for agent-targeted requests through the human owner's UI/callable flow.
|
|
295
|
-
|
|
296
280
|
### Turn-aware example
|
|
297
281
|
|
|
298
282
|
```typescript
|
package/dist/canon-agent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AddMemberResult, type CanonContact, type CommunicateInput, type CommunicateResult, type CanonConversation, type CanonConversationsPage, type CanonConversationsPageOptions, type CreateGroupOptions, type CreateGroupResult, type CanonRuntimeActivityItem, type CanonRuntimeCommandDescriptor, type CanonRuntimeFact, type CanonRuntimePrimitiveId, type ClearRuntimeActivityOptions, type CanonVoiceSession, type CanonVoiceSessionToken, type CreateVoiceSessionOptions, type VoiceSessionEventPayload } from '@canonmsg/core';
|
|
2
|
-
import type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, MessageHandler, MessageUpdatedHandler, ParticipationSuppressedHandler,
|
|
2
|
+
import type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, MessageHandler, MessageUpdatedHandler, ParticipationSuppressedHandler, RuntimeSignalHandler, RuntimePrimitiveHandler } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Contact-graph operations exposed under `agent.contacts`. Wraps the REST
|
|
5
5
|
* endpoints in CanonClient — the same surface a human user would hit through
|
|
@@ -40,8 +40,6 @@ export declare class CanonAgent {
|
|
|
40
40
|
private realtimeManager;
|
|
41
41
|
private sessionManager;
|
|
42
42
|
private handler;
|
|
43
|
-
private contactRequestHandler;
|
|
44
|
-
private contactApprovedHandler;
|
|
45
43
|
private contactAddedHandler;
|
|
46
44
|
private contactRemovedHandler;
|
|
47
45
|
private messageUpdatedHandler;
|
|
@@ -101,8 +99,6 @@ export declare class CanonAgent {
|
|
|
101
99
|
private filterApprovalReplyMessages;
|
|
102
100
|
on(event: 'message', handler: MessageHandler): void;
|
|
103
101
|
on(event: 'messageUpdated', handler: MessageUpdatedHandler): void;
|
|
104
|
-
on(event: 'contactRequest', handler: ContactRequestHandler): void;
|
|
105
|
-
on(event: 'contactApproved', handler: ContactRequestHandler): void;
|
|
106
102
|
on(event: 'contactAdded', handler: ContactAddedHandler): void;
|
|
107
103
|
on(event: 'contactRemoved', handler: ContactRemovedHandler): void;
|
|
108
104
|
/**
|
|
@@ -151,12 +147,11 @@ export declare class CanonAgent {
|
|
|
151
147
|
* Outcome depends on the target's `groupJoinPolicy` and the relationship
|
|
152
148
|
* graph:
|
|
153
149
|
* - `{ status: 'added' }` — the member was added immediately.
|
|
154
|
-
* - `{ status: 'pending', requestId
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* (you can listen for `contact.approved` SSE events to know when).
|
|
150
|
+
* - `{ status: 'pending', requestId }` — the target needs policy approval.
|
|
151
|
+
* The server created one `group_invite`; membership activates after approval
|
|
152
|
+
* The approved membership arrives through the normal conversation update.
|
|
158
153
|
*
|
|
159
|
-
* Throws `CanonApiError` for hard failures (block, inactive,
|
|
154
|
+
* Throws `CanonApiError` for hard failures (block, inactive, closed policy,
|
|
160
155
|
* member cap, requester not authorized).
|
|
161
156
|
*/
|
|
162
157
|
addMember(conversationId: string, userId: string): Promise<AddMemberResult>;
|
|
@@ -165,7 +160,6 @@ export declare class CanonAgent {
|
|
|
165
160
|
url: string;
|
|
166
161
|
attachment: import('@canonmsg/core').MediaAttachment;
|
|
167
162
|
}>;
|
|
168
|
-
private handleContactRequestEvent;
|
|
169
163
|
private handleContactGraphEvent;
|
|
170
164
|
private handleParticipationSuppressedEvent;
|
|
171
165
|
private handleMessageUpdatedEvent;
|
package/dist/canon-agent.js
CHANGED
|
@@ -245,6 +245,33 @@ function isAbortLikeError(error) {
|
|
|
245
245
|
return true;
|
|
246
246
|
return typeof record.message === 'string' && /\babort(?:ed)?\b/i.test(record.message);
|
|
247
247
|
}
|
|
248
|
+
function linkAbortSignals(turnSignal, requestSignal) {
|
|
249
|
+
if (!requestSignal || requestSignal === turnSignal) {
|
|
250
|
+
return { signal: turnSignal, dispose: () => { } };
|
|
251
|
+
}
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
const abortFrom = (source) => {
|
|
254
|
+
if (!controller.signal.aborted)
|
|
255
|
+
controller.abort(source.reason);
|
|
256
|
+
};
|
|
257
|
+
const onTurnAbort = () => abortFrom(turnSignal);
|
|
258
|
+
const onRequestAbort = () => abortFrom(requestSignal);
|
|
259
|
+
if (turnSignal.aborted)
|
|
260
|
+
abortFrom(turnSignal);
|
|
261
|
+
else
|
|
262
|
+
turnSignal.addEventListener('abort', onTurnAbort, { once: true });
|
|
263
|
+
if (requestSignal.aborted)
|
|
264
|
+
abortFrom(requestSignal);
|
|
265
|
+
else
|
|
266
|
+
requestSignal.addEventListener('abort', onRequestAbort, { once: true });
|
|
267
|
+
return {
|
|
268
|
+
signal: controller.signal,
|
|
269
|
+
dispose: () => {
|
|
270
|
+
turnSignal.removeEventListener('abort', onTurnAbort);
|
|
271
|
+
requestSignal.removeEventListener('abort', onRequestAbort);
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
248
275
|
export class CanonAgent {
|
|
249
276
|
options;
|
|
250
277
|
runtimeConnection;
|
|
@@ -254,8 +281,6 @@ export class CanonAgent {
|
|
|
254
281
|
realtimeManager = null;
|
|
255
282
|
sessionManager = null;
|
|
256
283
|
handler = null;
|
|
257
|
-
contactRequestHandler = null;
|
|
258
|
-
contactApprovedHandler = null;
|
|
259
284
|
contactAddedHandler = null;
|
|
260
285
|
contactRemovedHandler = null;
|
|
261
286
|
messageUpdatedHandler = null;
|
|
@@ -457,14 +482,6 @@ export class CanonAgent {
|
|
|
457
482
|
this.messageUpdatedHandler = handler;
|
|
458
483
|
return;
|
|
459
484
|
}
|
|
460
|
-
if (event === 'contactRequest') {
|
|
461
|
-
this.contactRequestHandler = handler;
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
if (event === 'contactApproved') {
|
|
465
|
-
this.contactApprovedHandler = handler;
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
485
|
if (event === 'contactAdded') {
|
|
469
486
|
this.contactAddedHandler = handler;
|
|
470
487
|
return;
|
|
@@ -607,7 +624,6 @@ export class CanonAgent {
|
|
|
607
624
|
const runtimeState = this.createRuntimeStatePublisher();
|
|
608
625
|
for (const id of this.cachedConversationIds) {
|
|
609
626
|
runtimeState?.writeSessionState(id, {
|
|
610
|
-
cwd: process.cwd(),
|
|
611
627
|
isActive: true,
|
|
612
628
|
...(this.options.clientType ? { clientType: this.options.clientType } : {}),
|
|
613
629
|
}).catch(() => { });
|
|
@@ -640,14 +656,6 @@ export class CanonAgent {
|
|
|
640
656
|
this.agentContext = ctx;
|
|
641
657
|
this.ensureApprovalManager(ctx);
|
|
642
658
|
});
|
|
643
|
-
rtm.setContactRequestHandlers({
|
|
644
|
-
onContactRequest: (request) => {
|
|
645
|
-
void this.handleContactRequestEvent(this.contactRequestHandler, request);
|
|
646
|
-
},
|
|
647
|
-
onContactApproved: (request) => {
|
|
648
|
-
void this.handleContactRequestEvent(this.contactApprovedHandler, request);
|
|
649
|
-
},
|
|
650
|
-
});
|
|
651
659
|
rtm.setContactGraphHandlers({
|
|
652
660
|
onContactAdded: (payload) => {
|
|
653
661
|
void this.handleContactGraphEvent(this.contactAddedHandler, payload);
|
|
@@ -726,12 +734,11 @@ export class CanonAgent {
|
|
|
726
734
|
* Outcome depends on the target's `groupJoinPolicy` and the relationship
|
|
727
735
|
* graph:
|
|
728
736
|
* - `{ status: 'added' }` — the member was added immediately.
|
|
729
|
-
* - `{ status: 'pending', requestId
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* (you can listen for `contact.approved` SSE events to know when).
|
|
737
|
+
* - `{ status: 'pending', requestId }` — the target needs policy approval.
|
|
738
|
+
* The server created one `group_invite`; membership activates after approval
|
|
739
|
+
* The approved membership arrives through the normal conversation update.
|
|
733
740
|
*
|
|
734
|
-
* Throws `CanonApiError` for hard failures (block, inactive,
|
|
741
|
+
* Throws `CanonApiError` for hard failures (block, inactive, closed policy,
|
|
735
742
|
* member cap, requester not authorized).
|
|
736
743
|
*/
|
|
737
744
|
async addMember(conversationId, userId) {
|
|
@@ -743,16 +750,6 @@ export class CanonAgent {
|
|
|
743
750
|
async uploadMedia(conversationId, data, mimeType, fileName) {
|
|
744
751
|
return this.apiClient.uploadMedia(conversationId, data, mimeType, fileName);
|
|
745
752
|
}
|
|
746
|
-
async handleContactRequestEvent(handler, request) {
|
|
747
|
-
if (!handler)
|
|
748
|
-
return;
|
|
749
|
-
try {
|
|
750
|
-
await handler(request);
|
|
751
|
-
}
|
|
752
|
-
catch (error) {
|
|
753
|
-
console.error('[canon-sdk] Contact-request handler failed:', error instanceof Error ? error.message : error);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
753
|
async handleContactGraphEvent(handler, payload) {
|
|
757
754
|
if (!handler)
|
|
758
755
|
return;
|
|
@@ -1219,6 +1216,7 @@ export class CanonAgent {
|
|
|
1219
1216
|
// The freshest message in the batch is the turn's trigger — same convention
|
|
1220
1217
|
// as the provenance lookup for turn verbosity below.
|
|
1221
1218
|
const triggeringMessageId = messages[messages.length - 1]?.id;
|
|
1219
|
+
const triggeringReplyAuthority = messages[messages.length - 1]?.replyAuthority;
|
|
1222
1220
|
const agentId = this.agentId;
|
|
1223
1221
|
const runtimeState = this.createRuntimeStatePublisher();
|
|
1224
1222
|
const queueDepth = () => this.sessionManager?.getQueueDepth(conversationId) ?? 0;
|
|
@@ -1493,9 +1491,12 @@ export class CanonAgent {
|
|
|
1493
1491
|
const activeSelfContextId = selfContexts.length > 0 ? resolvedActiveSelfContextId : null;
|
|
1494
1492
|
const withActiveSelfContext = (options) => {
|
|
1495
1493
|
const base = { ...(options ?? {}) };
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1494
|
+
const withContext = base.selfContextId !== undefined || !activeSelfContextId
|
|
1495
|
+
? base
|
|
1496
|
+
: { ...base, selfContextId: activeSelfContextId };
|
|
1497
|
+
return withContext.replyAuthority !== undefined || !triggeringReplyAuthority
|
|
1498
|
+
? withContext
|
|
1499
|
+
: { ...withContext, replyAuthority: triggeringReplyAuthority };
|
|
1499
1500
|
};
|
|
1500
1501
|
// Core's chunked sender walks the parts in a plain loop and knows nothing
|
|
1501
1502
|
// about this turn's abort signal, so a stop landing after part 1 would
|
|
@@ -1706,6 +1707,7 @@ export class CanonAgent {
|
|
|
1706
1707
|
};
|
|
1707
1708
|
const requestRuntimeInput = async (request) => {
|
|
1708
1709
|
throwIfAborted();
|
|
1710
|
+
const linkedSignal = linkAbortSignals(abortController.signal, request.signal);
|
|
1709
1711
|
const inputId = safeRuntimeInputId(request.inputId, request.kind);
|
|
1710
1712
|
const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
|
|
1711
1713
|
const expiresAtMs = Date.now() + timeoutMs;
|
|
@@ -1761,7 +1763,7 @@ export class CanonAgent {
|
|
|
1761
1763
|
result = await this.ensureRuntimeRequestManager().request('input', conversationId, { kind: request.kind }, {
|
|
1762
1764
|
requestId: inputId,
|
|
1763
1765
|
expiresAt: expiresAtMs,
|
|
1764
|
-
signal:
|
|
1766
|
+
signal: linkedSignal.signal,
|
|
1765
1767
|
});
|
|
1766
1768
|
const outcome = buildRuntimeInputOutcome(inputId, result.status, {
|
|
1767
1769
|
kind: request.kind,
|
|
@@ -1787,7 +1789,7 @@ export class CanonAgent {
|
|
|
1787
1789
|
return result;
|
|
1788
1790
|
}
|
|
1789
1791
|
catch (error) {
|
|
1790
|
-
if (abortController.signal.aborted || isAbortLikeError(error)) {
|
|
1792
|
+
if (abortController.signal.aborted || request.signal?.aborted || isAbortLikeError(error)) {
|
|
1791
1793
|
if (requestCreated) {
|
|
1792
1794
|
// Abort landing before the manager wired its cancel (e.g. during
|
|
1793
1795
|
// the pre-request writeTurn/typing round-trips) leaves the
|
|
@@ -1810,6 +1812,81 @@ export class CanonAgent {
|
|
|
1810
1812
|
await resumeTurnFromWaiting();
|
|
1811
1813
|
return result;
|
|
1812
1814
|
}
|
|
1815
|
+
finally {
|
|
1816
|
+
linkedSignal.dispose();
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
const requestPlanReview = async (request) => {
|
|
1820
|
+
throwIfAborted();
|
|
1821
|
+
const linkedSignal = linkAbortSignals(abortController.signal, request.signal);
|
|
1822
|
+
const planId = request.planId && RUNTIME_INPUT_ID_PATTERN.test(request.planId)
|
|
1823
|
+
? request.planId
|
|
1824
|
+
: randomUUID();
|
|
1825
|
+
const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
|
|
1826
|
+
const expiresAtMs = Date.now() + timeoutMs;
|
|
1827
|
+
const responseUserId = normalizeResponseUserId(request.responseUserId)
|
|
1828
|
+
?? triggeringHumanId
|
|
1829
|
+
?? normalizeResponseUserId(agent.ownerId);
|
|
1830
|
+
let created = false;
|
|
1831
|
+
let result = { status: 'timeout', planId };
|
|
1832
|
+
shouldPersistTurnState = true;
|
|
1833
|
+
try {
|
|
1834
|
+
result = await this.ensureRuntimeRequestManager().request('plan', conversationId, {
|
|
1835
|
+
...(request.title ? { title: request.title } : {}),
|
|
1836
|
+
...(request.summary ? { summary: request.summary } : {}),
|
|
1837
|
+
...(request.body ? { body: request.body } : {}),
|
|
1838
|
+
...(request.allowedPrompts ? { allowedPrompts: request.allowedPrompts } : {}),
|
|
1839
|
+
...(responseUserId ? { responseUserId } : {}),
|
|
1840
|
+
turnId: request.turnId ?? turnId,
|
|
1841
|
+
}, {
|
|
1842
|
+
requestId: planId,
|
|
1843
|
+
expiresAt: expiresAtMs,
|
|
1844
|
+
responderPolicy: 'infer',
|
|
1845
|
+
signal: linkedSignal.signal,
|
|
1846
|
+
onCreated: async () => {
|
|
1847
|
+
created = true;
|
|
1848
|
+
try {
|
|
1849
|
+
await turnOutput.addBlock({
|
|
1850
|
+
id: `plan:${planId}`,
|
|
1851
|
+
kind: 'input',
|
|
1852
|
+
status: 'pending',
|
|
1853
|
+
title: request.title ?? 'Plan review',
|
|
1854
|
+
summary: request.summary ?? 'Review requested',
|
|
1855
|
+
});
|
|
1856
|
+
await turnOutput.waitingInput();
|
|
1857
|
+
}
|
|
1858
|
+
catch { }
|
|
1859
|
+
await writeTurn('waiting_input');
|
|
1860
|
+
try {
|
|
1861
|
+
await this.typingSignals.clear(conversationId);
|
|
1862
|
+
}
|
|
1863
|
+
catch { }
|
|
1864
|
+
},
|
|
1865
|
+
});
|
|
1866
|
+
throwIfAborted();
|
|
1867
|
+
shouldPersistTurnState = false;
|
|
1868
|
+
try {
|
|
1869
|
+
await turnOutput.completeBlock(`plan:${planId}`, {
|
|
1870
|
+
summary: `Plan ${result.status}`,
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
catch { }
|
|
1874
|
+
await resumeTurnFromWaiting();
|
|
1875
|
+
return result;
|
|
1876
|
+
}
|
|
1877
|
+
catch (error) {
|
|
1878
|
+
if (abortController.signal.aborted || request.signal?.aborted || isAbortLikeError(error)) {
|
|
1879
|
+
throw error;
|
|
1880
|
+
}
|
|
1881
|
+
shouldPersistTurnState = false;
|
|
1882
|
+
if (!created)
|
|
1883
|
+
throw error;
|
|
1884
|
+
await resumeTurnFromWaiting();
|
|
1885
|
+
return result;
|
|
1886
|
+
}
|
|
1887
|
+
finally {
|
|
1888
|
+
linkedSignal.dispose();
|
|
1889
|
+
}
|
|
1813
1890
|
};
|
|
1814
1891
|
const sendCard = async (request) => {
|
|
1815
1892
|
throwIfAborted();
|
|
@@ -2020,6 +2097,7 @@ export class CanonAgent {
|
|
|
2020
2097
|
turnVerbosity,
|
|
2021
2098
|
requestApproval,
|
|
2022
2099
|
requestRuntimeInput,
|
|
2100
|
+
requestPlanReview,
|
|
2023
2101
|
requestCard,
|
|
2024
2102
|
sendCard,
|
|
2025
2103
|
abortSignal: abortController.signal,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { CanonAgent } from './canon-agent.js';
|
|
2
2
|
export type { AgentContactsAPI, AgentConversationsAPI, 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, redactSecrets, } from '@canonmsg/core';
|
|
4
|
-
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeTurnModeActivation, CanonRuntimeTurnModeDescriptor, CanonRuntimeTurnModeScope, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ParticipationSuppressedPayload, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, SessionRule, } from '@canonmsg/core';
|
|
4
|
+
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeTurnModeActivation, CanonRuntimeTurnModeDescriptor, CanonRuntimeTurnModeScope, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ParticipationSuppressedPayload, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimePlanRequestPayload, RuntimePlanRequestResult, SessionRule, } from '@canonmsg/core';
|
|
5
5
|
export { SessionManager } from './session-manager.js';
|
|
6
6
|
export { DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES, DEFAULT_MEDIA_CACHE_DIR, DEFAULT_MEDIA_MATERIALIZATION_BYTES, MAX_ANTHROPIC_IMAGE_RAW_BYTES, MAX_ANTHROPIC_REQUEST_BYTES, MAX_CANON_MEDIA_BYTES, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, toAnthropicImageBlocksWithinBudget, uploadMediaFile, } from './media.js';
|
|
7
7
|
export type { AnthropicImageBlock, AnthropicImageBudgetOptions, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, MaterializedCanonReplyContext, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
|
|
8
8
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
9
9
|
export type { CanonAgentTurnVerbosityOption } from './turn-verbosity-option.js';
|
|
10
|
-
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange,
|
|
11
|
-
export type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler,
|
|
10
|
+
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, DirectConversationSelection, CanonConversationsPage, CanonConversationsPageOptions, CanonReplyContext, CanonSelfContext, CanonTurnContextV2, CanonRuntimeDescriptor, MessageUpdatedPayload, SendMessageOptions, CreateGroupOptions, CreateGroupResult, TurnVerbosity, TurnVerbosityConfig, } from '@canonmsg/core';
|
|
11
|
+
export type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, FinalMessageResult, MessageHandler, MessageHandlerContext, MessageUpdatedHandler, ParticipationSuppressedHandler, ProgressMessageOptions, ProgressMessageResult, RuntimeApprovalRequest, RuntimeInputRequest, RuntimeInputResult, RuntimePlanReviewRequest, RuntimePlanReviewResult, RuntimeControlSurface, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentContext, type ContactAddedPayload, type
|
|
1
|
+
import { type AgentContext, type ContactAddedPayload, type ContactRemovedPayload, type ConversationUpdatedPayload, type MessageUpdatedPayload, type ParticipationSuppressedPayload, type VoiceSessionEventPayload } from '@canonmsg/core';
|
|
2
2
|
import { Debouncer } from './debouncer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Wraps @canonmsg/core's CanonStream with SDK-specific features:
|
|
@@ -15,8 +15,6 @@ export declare class RealtimeManager {
|
|
|
15
15
|
private lastSseErrorAt;
|
|
16
16
|
private suppressedSseErrorCount;
|
|
17
17
|
private onAgentContext;
|
|
18
|
-
private onContactRequest;
|
|
19
|
-
private onContactApproved;
|
|
20
18
|
private onContactAdded;
|
|
21
19
|
private onContactRemoved;
|
|
22
20
|
private onConversationUpdated;
|
|
@@ -35,10 +33,6 @@ export declare class RealtimeManager {
|
|
|
35
33
|
private pruneRecentInboundMessageIds;
|
|
36
34
|
private logSseError;
|
|
37
35
|
setOnAgentContext(cb: (ctx: AgentContext) => void): void;
|
|
38
|
-
setContactRequestHandlers(handlers: {
|
|
39
|
-
onContactRequest?: (payload: ContactRequestPayload) => void;
|
|
40
|
-
onContactApproved?: (payload: ContactApprovedPayload) => void;
|
|
41
|
-
}): void;
|
|
42
36
|
setContactGraphHandlers(handlers: {
|
|
43
37
|
onContactAdded?: (payload: ContactAddedPayload) => void;
|
|
44
38
|
onContactRemoved?: (payload: ContactRemovedPayload) => void;
|
package/dist/realtime.js
CHANGED
|
@@ -16,8 +16,6 @@ export class RealtimeManager {
|
|
|
16
16
|
lastSseErrorAt = 0;
|
|
17
17
|
suppressedSseErrorCount = 0;
|
|
18
18
|
onAgentContext = null;
|
|
19
|
-
onContactRequest = null;
|
|
20
|
-
onContactApproved = null;
|
|
21
19
|
onContactAdded = null;
|
|
22
20
|
onContactRemoved = null;
|
|
23
21
|
onConversationUpdated = null;
|
|
@@ -86,6 +84,7 @@ export class RealtimeManager {
|
|
|
86
84
|
createdAt: m.createdAt ?? new Date().toISOString(),
|
|
87
85
|
...(m.contactCard ? { contactCard: m.contactCard } : {}),
|
|
88
86
|
...(m.metadata ? { metadata: m.metadata } : {}),
|
|
87
|
+
...(payload.replyAuthority ? { replyAuthority: payload.replyAuthority } : {}),
|
|
89
88
|
};
|
|
90
89
|
this.debouncer.add(payload.conversationId, message, payload.provenance ?? null);
|
|
91
90
|
},
|
|
@@ -99,12 +98,6 @@ export class RealtimeManager {
|
|
|
99
98
|
onAgentContext: (ctx) => {
|
|
100
99
|
this.onAgentContext?.(ctx);
|
|
101
100
|
},
|
|
102
|
-
onContactRequest: (payload) => {
|
|
103
|
-
this.onContactRequest?.(payload);
|
|
104
|
-
},
|
|
105
|
-
onContactApproved: (payload) => {
|
|
106
|
-
this.onContactApproved?.(payload);
|
|
107
|
-
},
|
|
108
101
|
onContactAdded: (payload) => {
|
|
109
102
|
this.onContactAdded?.(payload);
|
|
110
103
|
},
|
|
@@ -173,10 +166,6 @@ export class RealtimeManager {
|
|
|
173
166
|
setOnAgentContext(cb) {
|
|
174
167
|
this.onAgentContext = cb;
|
|
175
168
|
}
|
|
176
|
-
setContactRequestHandlers(handlers) {
|
|
177
|
-
this.onContactRequest = handlers.onContactRequest ?? null;
|
|
178
|
-
this.onContactApproved = handlers.onContactApproved ?? null;
|
|
179
|
-
}
|
|
180
169
|
setContactGraphHandlers(handlers) {
|
|
181
170
|
this.onContactAdded = handlers.onContactAdded ?? null;
|
|
182
171
|
this.onContactRemoved = handlers.onContactRemoved ?? null;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AddMemberResult,
|
|
2
|
-
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, CanonReplyContext, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, ResumableMediaUploadResult, SendMessageOptions, TurnOutputBlock, TurnOutputBlockInput } from '@canonmsg/core';
|
|
1
|
+
export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, CanonReplyContext, CanonContact, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, CreateGroupOptions, CreateGroupResult, SendMessageOptions, TurnLifecycleState, TurnOutputBlock, TurnOutputBlockInput, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimePlanRequestPayload, RuntimePlanRequestResult, RuntimeCardNativeMetadata, RuntimeCardV1, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, SessionRule, ApprovalResult, } from '@canonmsg/core';
|
|
2
|
+
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, CanonReplyContext, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimePlanRequestPayload, RuntimePlanRequestResult, RuntimeCardNativeMetadata, RuntimeCardV1, ResumableMediaUploadResult, SendMessageOptions, TurnOutputBlock, TurnOutputBlockInput } from '@canonmsg/core';
|
|
3
3
|
import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
|
|
4
4
|
export interface ProgressMessageOptions extends SendMessageOptions {
|
|
5
5
|
/**
|
|
@@ -119,6 +119,8 @@ export interface RuntimeInputRequest {
|
|
|
119
119
|
native?: RuntimeInputNativeMetadata;
|
|
120
120
|
inputId?: string;
|
|
121
121
|
timeoutMs?: number;
|
|
122
|
+
/** Aborts the pending input request without waiting for the enclosing turn to stop. */
|
|
123
|
+
signal?: AbortSignal;
|
|
122
124
|
/** Human conversation member who should answer. Secret and sudo prompts remain owner-only. */
|
|
123
125
|
responseUserId?: string;
|
|
124
126
|
}
|
|
@@ -128,6 +130,14 @@ export interface RuntimeInputResult {
|
|
|
128
130
|
answers?: RuntimeInputAnswers;
|
|
129
131
|
inputId: string;
|
|
130
132
|
}
|
|
133
|
+
/** Native Canon plan-review card backed by Core's existing runtime-plan interaction. */
|
|
134
|
+
export interface RuntimePlanReviewRequest extends RuntimePlanRequestPayload {
|
|
135
|
+
planId?: string;
|
|
136
|
+
timeoutMs?: number;
|
|
137
|
+
/** Aborts the pending plan review without waiting for the enclosing turn to stop. */
|
|
138
|
+
signal?: AbortSignal;
|
|
139
|
+
}
|
|
140
|
+
export type RuntimePlanReviewResult = RuntimePlanRequestResult;
|
|
131
141
|
export interface RuntimeCardRequest {
|
|
132
142
|
card: RuntimeCardV1;
|
|
133
143
|
cardId?: string;
|
|
@@ -210,6 +220,12 @@ export interface MessageHandlerContext {
|
|
|
210
220
|
* are never persisted in Canon message metadata.
|
|
211
221
|
*/
|
|
212
222
|
requestRuntimeInput: (request: RuntimeInputRequest) => Promise<RuntimeInputResult>;
|
|
223
|
+
/**
|
|
224
|
+
* Present a native Canon plan-review card and wait for approve, revise,
|
|
225
|
+
* reject, cancellation, or timeout. Canon owns presentation and response
|
|
226
|
+
* routing; the runtime remains responsible for enforcing the decision.
|
|
227
|
+
*/
|
|
228
|
+
requestPlanReview: (request: RuntimePlanReviewRequest) => Promise<RuntimePlanReviewResult>;
|
|
213
229
|
/**
|
|
214
230
|
* Ask the triggering human to review/respond to a generic rich card. The
|
|
215
231
|
* visible card document is redacted for presentation; raw response values
|
|
@@ -329,7 +345,6 @@ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
|
|
|
329
345
|
*/
|
|
330
346
|
turnVerbosity?: import('./turn-verbosity-option.js').CanonAgentTurnVerbosityOption;
|
|
331
347
|
}
|
|
332
|
-
export type ContactRequestHandler = (request: import('@canonmsg/core').CanonContactRequest) => void | Promise<void>;
|
|
333
348
|
export type ContactAddedHandler = (contact: import('@canonmsg/core').ContactAddedPayload) => void | Promise<void>;
|
|
334
349
|
export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRemovedPayload) => void | Promise<void>;
|
|
335
350
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
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": "^
|
|
31
|
+
"@canonmsg/core": "^12.0.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|