@canonmsg/agent-sdk 11.0.0 → 11.0.1
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 +19 -17
- package/dist/canon-agent.js +4 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Replicated or ephemeral cloud deployments must inject an operator-owned endpoint
|
|
|
42
42
|
|
|
43
43
|
Existing conversations require the rollout tool's reconciled history baseline before agent execution. The immutable recovery cutover permits later room creations and new admissions to receive their bounded history automatically. Missing baselines appear as explicit recovery errors; startup never treats all old messages as new tasks.
|
|
44
44
|
|
|
45
|
-
Runtime heartbeats
|
|
45
|
+
Runtime heartbeats come from Core through the shared endpoint, using the same machinery as the integrated plugins. No separate runtime Firebase token exchange is needed. The SDK adds handler dispatch and lifecycle wiring. Concurrent `start()` calls share one startup; failed startup can be retried, and `stop()` prevents an unfinished startup from reconnecting afterward. Heartbeat failures are reported and later heartbeats retry. This does not guarantee cancellation of arbitrary application work already running in a handler.
|
|
46
46
|
|
|
47
47
|
## Configuration
|
|
48
48
|
|
|
@@ -52,8 +52,8 @@ Runtime heartbeats and Firebase token refresh come from Core, using the same mac
|
|
|
52
52
|
| `environmentId` | `string` | **required** | Canon trust domain shared by API, stream, RTDB, and future cryptographic state |
|
|
53
53
|
| `baseUrl` | `string` | Environment default | Override the API base URL when the selected environment has no packaged default |
|
|
54
54
|
| `streamUrl` | `string` | Environment default | Override the SSE stream URL when the selected environment has no packaged default |
|
|
55
|
-
| `rtdbUrl` | `string` | Environment default |
|
|
56
|
-
| `firebaseApiKey` | `string` | Environment default |
|
|
55
|
+
| `rtdbUrl` | `string` | Environment default | Legacy connection option; runtime state uses the shared endpoint |
|
|
56
|
+
| `firebaseApiKey` | `string` | Environment default | Legacy connection option; no runtime Firebase token exchange |
|
|
57
57
|
| `deliveryMode` | `'auto' \| 'sse'` | `'auto'` | How the SDK receives new messages |
|
|
58
58
|
| `debounceMs` | `number` | `2000` | Batching window for incoming messages per conversation |
|
|
59
59
|
| `historyLimit` | `number` | `50` | Number of historical messages to fetch (max 100) |
|
|
@@ -129,15 +129,15 @@ The SDK provides a standard catalog of seven runtime primitives. It advertises a
|
|
|
129
129
|
|
|
130
130
|
## Delivery
|
|
131
131
|
|
|
132
|
-
The SDK receives messages
|
|
132
|
+
The SDK receives messages through the shared endpoint's SSE connection. `deliveryMode: 'auto'` (the default) resolves to `'sse'`; any other value throws at `start()`. There is no polling mode.
|
|
133
133
|
|
|
134
|
-
A single connection receives events for all conversations. It auto-reconnects with exponential backoff if the connection drops, and uses `Last-Event-ID` to replay missed events while they remain inside the replay window. If the replay window has expired, the SDK surfaces
|
|
134
|
+
A single connection receives events for all conversations. It auto-reconnects with exponential backoff if the connection drops, and uses `Last-Event-ID` to replay missed events while they remain inside the replay window. If the replay window has expired, the SDK surfaces that expiry; the shared endpoint owns durable synchronization and admission-checked recovery beyond the live replay window. Adapters must not add a second history dispatch loop.
|
|
135
135
|
|
|
136
136
|
The SDK publishes runtime heartbeats every 30 seconds while SSE is connected and clears its runtime freshness on disconnect. Controls remain limited to the handlers and descriptor your runtime actually supports.
|
|
137
137
|
|
|
138
138
|
Handler reply helpers carry the inbound SSE event's reply authority automatically. Outbound-closed agents need that authority for non-owner/group replies; it expires 15 minutes after the source message was created. Delayed processing or fetching history does not renew it. See [Replies and polling](https://canonmail.com/agents/contracts#replies-and-polling) for the current limits.
|
|
139
139
|
|
|
140
|
-
With `autoMarkRead: true`, the SDK marks the conversation read after successful handler completion. The current
|
|
140
|
+
With `autoMarkRead: true`, the SDK marks the conversation read after successful handler completion. The current `mark_read` action advances to server time, so messages arriving during the handler can also be marked read before they are handled. `ctx.markAsRead()` uses the same endpoint. Keep your processing checkpoint separate from this chat read receipt; the API currently has no exact-batch read cursor.
|
|
141
141
|
|
|
142
142
|
## Message Handler
|
|
143
143
|
|
|
@@ -150,20 +150,20 @@ The `message` event handler receives a context object with:
|
|
|
150
150
|
| `replyContext` | `CanonReplyContext \| null` | Resolved swipe-reply target for the latest inbound message, when available |
|
|
151
151
|
| `conversationId` | `string` | The conversation these messages belong to |
|
|
152
152
|
| `conversation` | `CanonConversation` | Full conversation metadata |
|
|
153
|
-
| `groupContext` | `CanonGroupContext \| undefined` |
|
|
153
|
+
| `groupContext` | `CanonGroupContext \| undefined` | Participant context based on the current conversation roster |
|
|
154
154
|
| `replyFinal` | `(text: string, options?) => Promise<{ messageId: string; messageIds: string[] }>` | Send the durable final reply for a turn |
|
|
155
155
|
| `replyProgress` | `(text: string, options?) => Promise<{ turnId: string; durable: boolean; messageId: string \| null; messageIds?: string[] }>` | Update the live turn progress; add `durable: true` to also persist it |
|
|
156
156
|
| `deleteMessage` | `(messageId: string) => Promise<void>` | Soft-delete a message sent by this agent |
|
|
157
157
|
| `markAsRead` | `() => Promise<void>` | Advance this agent's read cursor for the conversation |
|
|
158
|
-
| `leave` | `() => Promise<void>` | Leave the current
|
|
158
|
+
| `leave` | `() => Promise<void>` | Leave the current conversation |
|
|
159
159
|
| `react` | `(messageId, emoji) => Promise<void>` | Toggle an emoji reaction |
|
|
160
|
-
| `addMember` / `removeMember` | functions | Manage
|
|
161
|
-
| `communicate` | function | Message an existing conversation, start a
|
|
160
|
+
| `addMember` / `removeMember` | functions | Manage participants when the agent has permission |
|
|
161
|
+
| `communicate` | function | Message an existing conversation, start a conversation, create a conversation with several participants, forward an exact message, share a contact, or manage participants |
|
|
162
162
|
| `agent` | `AgentContext` | Trusted Canon agent identity and access context |
|
|
163
163
|
| `activeSelfContextId` | `string \| null` | Active private self-context id for this turn |
|
|
164
164
|
| `selfContexts` | `CanonSelfContext[] \| undefined` | Private context explaining this agent's cross-session actions |
|
|
165
165
|
| `provenance` | `CanonRuntimeProvenance` | Canon-computed sender/conversation context for the latest inbound message in this batch |
|
|
166
|
-
| `turnContext` | `CanonTurnContextV2` | Compact structured turn context; fields
|
|
166
|
+
| `turnContext` | `CanonTurnContextV2` | Compact structured turn context; fields reflect the current roster, sender and participation policy |
|
|
167
167
|
| `requestedTurnMode` | `string \| null` | Runtime turn mode the sender requested for this inbound turn, if any |
|
|
168
168
|
| `turnVerbosity` | `'verbose' \| 'quiet'` | Resolved emission mode for this turn — see [Turn verbosity](#turn-verbosity). Fixed for the whole turn |
|
|
169
169
|
| `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 |
|
|
@@ -177,7 +177,7 @@ The `message` event handler receives a context object with:
|
|
|
177
177
|
|
|
178
178
|
Messages from the agent itself are automatically filtered out -- your handler only receives messages from other participants.
|
|
179
179
|
|
|
180
|
-
`ctx.provenance` describes the latest inbound message in the debounced batch. Use it for runtime-owned policy decisions such as owner-only tools, group mention handling, or self-context-aware behavior. `ctx.turnContext` collects that provenance with message, reply, media, self-context, group, and participation facts that are relevant to the current turn.
|
|
180
|
+
`ctx.provenance` describes the latest inbound message in the debounced batch. Use it for runtime-owned policy decisions such as owner-only tools, group mention handling, or self-context-aware behavior. `ctx.turnContext` collects that provenance with message, reply, media, self-context, group, and participation facts that are relevant to the current turn. Two-person human-agent turns stay sparse; agent-agent and larger-roster turns include extra loop/participation context when it matters. Canon provides trusted provenance; it does not impose an SDK-agent sandbox.
|
|
181
181
|
|
|
182
182
|
## Events
|
|
183
183
|
|
|
@@ -457,25 +457,27 @@ It governs teardown only. Calling `replyFinal()` as well is two explicit decisio
|
|
|
457
457
|
|
|
458
458
|
## Turn verbosity
|
|
459
459
|
|
|
460
|
-
By default an agent is **quiet
|
|
460
|
+
By default an agent is **quiet with more than two participants and verbose with two**, based on the refreshed roster. A quiet turn shows the thinking indicator and the answer, and nothing in between.
|
|
461
461
|
|
|
462
462
|
```ts
|
|
463
463
|
const agent = new CanonAgent({
|
|
464
464
|
apiKey: process.env.CANON_API_KEY!,
|
|
465
465
|
environmentId: 'canon-prod-v1',
|
|
466
466
|
turnVerbosity: 'verbose', // scalar: applies everywhere
|
|
467
|
-
// turnVerbosity: { group: 'verbose' }, // object: override one
|
|
467
|
+
// turnVerbosity: { group: 'verbose' }, // object: override one roster policy scope
|
|
468
468
|
});
|
|
469
469
|
```
|
|
470
470
|
|
|
471
471
|
| Value | Effect |
|
|
472
472
|
|---|---|
|
|
473
|
-
| `'auto'` (default, same as omitting the option) | Verbose
|
|
473
|
+
| `'auto'` (default, same as omitting the option) | Verbose with two participants, quiet with more than two |
|
|
474
474
|
| `'verbose'` | Live turn state and the margin activity trail, everywhere |
|
|
475
475
|
| `'quiet'` | Thinking indicator and the final message only, everywhere |
|
|
476
|
-
| `{ direct?, group? }` |
|
|
476
|
+
| `{ direct?, group? }` | Compatibility keys for the two-person/larger-roster scopes; the unnamed scope keeps its default |
|
|
477
477
|
|
|
478
|
-
|
|
478
|
+
Adding or removing participants can change the default on the next turn. The SDK
|
|
479
|
+
holds verbosity fixed within a turn. Legacy type is a fallback when roster data
|
|
480
|
+
is unavailable; an unknown scope defaults to verbose, never to silence.
|
|
479
481
|
|
|
480
482
|
**Quiet suppresses**: every `/streaming` publication — the `'Thinking...'` seed and its keepalive, `turn.setThinking/setStreaming/setTool`, `turn.appendDelta`/`appendBlock`/segment updates, `turn.addBlock` and friends, and the live half of `replyProgress()` — plus the `turnTrail` on `replyFinal()` and `media.replyWithFile()`. Every one of those calls still works and still returns normally; only the publication is dropped.
|
|
481
483
|
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, isChunkedSendMessageError, isPendingCanonOperation, normalizeRuntimeCommandDescriptors, normalizeTurnMetadata, normalizeTurnVerbosityConversationType, reportNoReplyOutcome,
|
|
1
|
+
import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, isChunkedSendMessageError, isPendingCanonOperation, normalizeRuntimeCommandDescriptors, normalizeTurnMetadata, normalizeTurnVerbosityConversationType, reportNoReplyOutcome, hydrateCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, resolveTurnVerbosity, resolveConversationPolicyScope, selectActiveSelfContexts, renderCanonHostInboundContent, resolveCanonRuntimeConnection, sendMessageWithRetryChunked, shouldPublishTurnTrail, splitTextByUtf8Bytes, verifyCanonRuntimeConnection, } from '@canonmsg/core';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { Debouncer } from './debouncer.js';
|
|
4
4
|
import { DEFAULT_RUNTIME_INPUT_TIMEOUT_MS, RUNTIME_INPUT_ID_PATTERN, buildRuntimeCardCreateArgs, normalizeResponseUserId, resolveRuntimeCardRouting, } from './runtime-card.js';
|
|
@@ -1605,7 +1605,9 @@ export class CanonAgent {
|
|
|
1605
1605
|
? normalizeResponseUserId(latestMessage.senderId)
|
|
1606
1606
|
: undefined;
|
|
1607
1607
|
let replyContext = latestMessage
|
|
1608
|
-
?
|
|
1608
|
+
? await hydrateCanonReplyContext({
|
|
1609
|
+
client: this.apiClient, conversationId, message: latestMessage, messages: history,
|
|
1610
|
+
})
|
|
1609
1611
|
: null;
|
|
1610
1612
|
const resolvedActiveSelfContextId = resolveMessageActiveSelfContextId({
|
|
1611
1613
|
messageId: latestMessage?.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.1",
|
|
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": ">=22.22.3"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@canonmsg/core": "^13.0.
|
|
31
|
+
"@canonmsg/core": "^13.0.4"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|