@canonmsg/agent-sdk 11.2.1 → 12.1.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.
Files changed (181) hide show
  1. package/README.md +248 -523
  2. package/dist/chunks/chunk-7N6OPPW4.js +5430 -0
  3. package/dist/chunks/chunk-N722GWCP.js +4897 -0
  4. package/dist/chunks/chunk-QZ4PPRQW.js +14247 -0
  5. package/dist/chunks/mls-crypto-LMNQZOS6.js +10 -0
  6. package/dist/cli.js +572 -0
  7. package/dist/drivers/claude.d.ts +42 -0
  8. package/dist/drivers/code.d.ts +18 -0
  9. package/dist/drivers/codex.d.ts +46 -0
  10. package/dist/drivers/connector.d.ts +34 -0
  11. package/dist/index.d.ts +20 -25
  12. package/dist/index.js +91 -13
  13. package/dist/internal/backend-contracts/agentBehaviorPolicy.d.ts +122 -0
  14. package/dist/internal/backend-contracts/agentDirectory.d.ts +32 -0
  15. package/dist/internal/backend-contracts/agentOrigin.d.ts +23 -0
  16. package/dist/internal/backend-contracts/agentSearch.d.ts +17 -0
  17. package/dist/internal/backend-contracts/api-origins.generated.d.ts +4 -0
  18. package/dist/internal/backend-contracts/apiErrors.d.ts +1 -0
  19. package/dist/internal/backend-contracts/artifactActions.d.ts +8 -0
  20. package/dist/internal/backend-contracts/artifacts.d.ts +386 -0
  21. package/dist/internal/backend-contracts/clientFloor.d.ts +41 -0
  22. package/dist/internal/backend-contracts/communication.d.ts +146 -0
  23. package/dist/internal/backend-contracts/connectorRelay.d.ts +179 -0
  24. package/dist/internal/backend-contracts/conversationMembership.d.ts +28 -0
  25. package/dist/internal/backend-contracts/deliveryObservation.d.ts +23 -0
  26. package/dist/internal/backend-contracts/diffRedaction.d.ts +6 -0
  27. package/dist/internal/backend-contracts/endpoint-unread.d.ts +4 -0
  28. package/dist/internal/backend-contracts/endpoint.d.ts +120 -0
  29. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +48 -0
  30. package/dist/internal/backend-contracts/environment.d.ts +20 -0
  31. package/dist/internal/backend-contracts/externalInbound.d.ts +24 -0
  32. package/dist/internal/backend-contracts/firestoreValues.d.ts +9 -0
  33. package/dist/internal/backend-contracts/index.d.ts +54 -0
  34. package/dist/internal/backend-contracts/media-url.d.ts +20 -0
  35. package/dist/internal/backend-contracts/media.d.ts +51 -0
  36. package/dist/internal/backend-contracts/mediaDelivery.d.ts +46 -0
  37. package/dist/internal/backend-contracts/mediaTransferProtocol.d.ts +17 -0
  38. package/dist/internal/backend-contracts/membershipChange.d.ts +21 -0
  39. package/dist/internal/backend-contracts/message.d.ts +86 -0
  40. package/dist/internal/backend-contracts/messageText.d.ts +6 -0
  41. package/dist/internal/backend-contracts/mlsDevApplication.d.ts +161 -0
  42. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +78 -0
  43. package/dist/internal/backend-contracts/mlsDevCredentialReconciliation.d.ts +45 -0
  44. package/dist/internal/backend-contracts/mlsDevDeviceCustody.d.ts +52 -0
  45. package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +75 -0
  46. package/dist/internal/backend-contracts/mlsDevFeedReceipt.d.ts +43 -0
  47. package/dist/internal/backend-contracts/mlsDevGroup.d.ts +321 -0
  48. package/dist/internal/backend-contracts/mlsDevGroupFeed.d.ts +51 -0
  49. package/dist/internal/backend-contracts/mlsDevInteraction.d.ts +159 -0
  50. package/dist/internal/backend-contracts/mlsDevKeyPackage.d.ts +42 -0
  51. package/dist/internal/backend-contracts/mlsDevKeyPackages.d.ts +55 -0
  52. package/dist/internal/backend-contracts/mlsEnvironment.d.ts +35 -0
  53. package/dist/internal/backend-contracts/mlsPrincipalAlias.d.ts +23 -0
  54. package/dist/internal/backend-contracts/moderation.d.ts +7 -0
  55. package/dist/internal/backend-contracts/plaintextProcessors.d.ts +63 -0
  56. package/dist/internal/backend-contracts/preparedContent.d.ts +54 -0
  57. package/dist/internal/backend-contracts/replyAuthority.d.ts +14 -0
  58. package/dist/internal/backend-contracts/runtimeCardFields.d.ts +66 -0
  59. package/dist/internal/backend-contracts/runtimeCardStorage.d.ts +3 -0
  60. package/dist/internal/backend-contracts/runtimeDescriptor.d.ts +88 -0
  61. package/dist/internal/backend-contracts/selfContext.d.ts +9 -0
  62. package/dist/internal/backend-contracts/turnProtocol.d.ts +43 -0
  63. package/dist/internal/backend-contracts/upload-mime-types.d.ts +17 -0
  64. package/dist/internal/backend-contracts/verbContract.d.ts +856 -0
  65. package/dist/internal/backend-contracts/verbSchemas.d.ts +1717 -0
  66. package/dist/internal/backend-contracts/verbWire.d.ts +493 -0
  67. package/dist/internal/backend-contracts/webAuth.d.ts +8 -0
  68. package/dist/internal/core/agent-profiles.d.ts +65 -0
  69. package/dist/internal/core/approval-format.d.ts +22 -0
  70. package/dist/internal/core/approval-manager.d.ts +43 -0
  71. package/dist/internal/core/approval-types.d.ts +73 -0
  72. package/dist/internal/core/base-url.d.ts +1 -0
  73. package/dist/internal/core/cli-metadata.d.ts +13 -0
  74. package/dist/internal/core/client.d.ts +432 -0
  75. package/dist/internal/core/completed-output-journal.d.ts +39 -0
  76. package/dist/internal/core/constants.d.ts +1 -0
  77. package/dist/internal/core/endpoint-client.d.ts +56 -0
  78. package/dist/internal/core/execution-environment-mode.d.ts +6 -0
  79. package/dist/internal/core/gifs.d.ts +29 -0
  80. package/dist/internal/core/host-runtime.d.ts +53 -0
  81. package/dist/internal/core/index.d.ts +68 -0
  82. package/dist/internal/core/node-media.d.ts +6 -0
  83. package/dist/internal/core/policy.d.ts +2 -0
  84. package/dist/internal/core/registration-profile.d.ts +10 -0
  85. package/dist/internal/core/registration-session.d.ts +55 -0
  86. package/dist/internal/core/registration.d.ts +5 -0
  87. package/dist/internal/core/retry.d.ts +113 -0
  88. package/dist/internal/core/rtdb-rest.d.ts +141 -0
  89. package/dist/internal/core/runtime-cards.d.ts +286 -0
  90. package/dist/internal/core/runtime-descriptor.d.ts +16 -0
  91. package/dist/internal/core/runtime-endpoints.d.ts +26 -0
  92. package/dist/internal/core/runtime-heartbeat.d.ts +29 -0
  93. package/dist/internal/core/runtime-presentation.d.ts +9 -0
  94. package/dist/internal/core/runtime-request-manager.d.ts +283 -0
  95. package/dist/internal/core/runtime-state-publisher.d.ts +42 -0
  96. package/dist/internal/core/self-context.d.ts +22 -0
  97. package/dist/internal/core/silent-turn.d.ts +49 -0
  98. package/dist/internal/core/stream.d.ts +78 -0
  99. package/dist/internal/core/turn-protocol.d.ts +28 -0
  100. package/dist/internal/core/turn-verbosity.d.ts +85 -0
  101. package/dist/internal/core/types.d.ts +964 -0
  102. package/dist/internal/core/unified-diff.d.ts +35 -0
  103. package/dist/internal/endpoint/artifact-images.d.ts +60 -0
  104. package/dist/internal/endpoint/artifact-reducer.d.ts +35 -0
  105. package/dist/internal/endpoint/artifact-views.d.ts +113 -0
  106. package/dist/internal/endpoint/artifacts.d.ts +75 -0
  107. package/dist/internal/endpoint/delivery.d.ts +47 -0
  108. package/dist/internal/endpoint/engine.d.ts +249 -0
  109. package/dist/internal/endpoint/ephemeral.d.ts +8 -0
  110. package/dist/internal/endpoint/events.d.ts +32 -0
  111. package/dist/internal/endpoint/external-inbound.d.ts +36 -0
  112. package/dist/internal/endpoint/handoff.d.ts +50 -0
  113. package/dist/internal/endpoint/history.d.ts +22 -0
  114. package/dist/internal/endpoint/image-generation-errors.d.ts +2 -0
  115. package/dist/internal/endpoint/inbound.d.ts +29 -0
  116. package/dist/internal/endpoint/index.d.ts +32 -0
  117. package/dist/internal/endpoint/media.d.ts +177 -0
  118. package/dist/internal/endpoint/migration.d.ts +50 -0
  119. package/dist/internal/endpoint/mls-crypto.d.ts +32 -0
  120. package/dist/internal/endpoint/mls-dev-client.d.ts +52 -0
  121. package/dist/internal/endpoint/mls-dev-device.d.ts +69 -0
  122. package/dist/internal/endpoint/mls-dev-groups.d.ts +174 -0
  123. package/dist/internal/endpoint/mls-dev-interactions.d.ts +99 -0
  124. package/dist/internal/endpoint/mls-dev-key-package-service.d.ts +46 -0
  125. package/dist/internal/endpoint/mls-dev-key-packages.d.ts +62 -0
  126. package/dist/internal/endpoint/mls-dev-text.d.ts +67 -0
  127. package/dist/internal/endpoint/mls-dev-transport.d.ts +38 -0
  128. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +29 -0
  129. package/dist/internal/endpoint/node-mls-custody.d.ts +15 -0
  130. package/dist/internal/endpoint/node.d.ts +7 -0
  131. package/dist/internal/endpoint/observe-query.d.ts +17 -0
  132. package/dist/internal/endpoint/portable-node-mls-custody.d.ts +23 -0
  133. package/dist/internal/endpoint/queue.d.ts +22 -0
  134. package/dist/internal/endpoint/recovery.d.ts +27 -0
  135. package/dist/internal/endpoint/retention.d.ts +7 -0
  136. package/dist/internal/endpoint/store.d.ts +22 -0
  137. package/dist/internal/endpoint/sync.d.ts +33 -0
  138. package/dist/internal/endpoint/transient-publisher.d.ts +8 -0
  139. package/dist/internal/endpoint/transport.d.ts +63 -0
  140. package/dist/media.d.ts +1 -1
  141. package/dist/media.js +42 -541
  142. package/dist/runner/errands.d.ts +118 -0
  143. package/dist/runner/mls.d.ts +97 -0
  144. package/dist/runner/relay.d.ts +87 -0
  145. package/dist/runner/runner.d.ts +98 -0
  146. package/dist/runner/types.d.ts +215 -0
  147. package/package.json +18 -5
  148. package/dist/attached-session.d.ts +0 -58
  149. package/dist/attached-session.js +0 -638
  150. package/dist/bounded-file-read.d.ts +0 -14
  151. package/dist/bounded-file-read.js +0 -34
  152. package/dist/canon-agent.d.ts +0 -236
  153. package/dist/canon-agent.js +0 -2531
  154. package/dist/debouncer.d.ts +0 -18
  155. package/dist/debouncer.js +0 -133
  156. package/dist/inbound-handoff.d.ts +0 -6
  157. package/dist/inbound-handoff.js +0 -17
  158. package/dist/managed-session-host.d.ts +0 -27
  159. package/dist/managed-session-host.js +0 -195
  160. package/dist/managed-session-setup.d.ts +0 -99
  161. package/dist/managed-session-setup.js +0 -234
  162. package/dist/realtime.d.ts +0 -62
  163. package/dist/realtime.js +0 -193
  164. package/dist/runtime-card.d.ts +0 -48
  165. package/dist/runtime-card.js +0 -64
  166. package/dist/session-manager.d.ts +0 -77
  167. package/dist/session-manager.js +0 -285
  168. package/dist/turn-streaming-request.d.ts +0 -17
  169. package/dist/turn-streaming-request.js +0 -22
  170. package/dist/turn-verbosity-option.d.ts +0 -33
  171. package/dist/turn-verbosity-option.js +0 -23
  172. package/dist/types.d.ts +0 -387
  173. package/dist/types.js +0 -1
  174. package/dist/work-session-coordination.d.ts +0 -21
  175. package/dist/work-session-coordination.js +0 -53
  176. package/dist/work-session-host.d.ts +0 -31
  177. package/dist/work-session-host.js +0 -617
  178. package/dist/work-session-interactions.d.ts +0 -12
  179. package/dist/work-session-interactions.js +0 -173
  180. package/dist/work-session-state.d.ts +0 -36
  181. package/dist/work-session-state.js +0 -62
@@ -1,234 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { execFileSync } from 'node:child_process';
3
- import { existsSync, realpathSync } from 'node:fs';
4
- import { join, relative } from 'node:path';
5
- import { buildConversationWorktreeSpec, prepareConversationEnvironment, releaseConversationEnvironment, readLocalRuntimeEntry, saveRuntimeSessionState, } from '@canonmsg/core';
6
- const SETTING_KEYS = ['projectId', 'modelId', 'reasoningEffort', 'permissionMode', 'executionMode'];
7
- export const MANAGED_EXECUTION_MODE_LABELS = {
8
- worktree: 'Git worktree',
9
- locked: 'Project folder',
10
- };
11
- export function buildManagedSessionCatalog(input) {
12
- const defaults = {
13
- ...(input.projects[0] ? { projectId: input.projects[0].id } : {}),
14
- ...(input.defaultModelId ? { modelId: input.defaultModelId } : {}),
15
- ...(input.defaultReasoningEffort ? { reasoningEffort: input.defaultReasoningEffort } : {}),
16
- permissionMode: input.defaultPermissionMode, executionMode: input.defaultExecutionMode,
17
- };
18
- const catalog = {
19
- provider: input.provider, canCreate: false, canAttach: false, managedSetup: { version: 1 },
20
- projects: input.projects.map((project) => ({ id: project.id, label: project.label })),
21
- models: input.models.map((model) => ({ ...model })),
22
- ...(input.reasoningEfforts?.length ? { reasoningEfforts: input.reasoningEfforts.map((effort) => ({ ...effort })) } : {}),
23
- permissionModes: input.permissionModes.map((mode) => ({ ...mode })),
24
- executionModes: input.executionModes.map((id) => ({ id, label: MANAGED_EXECUTION_MODE_LABELS[id] })),
25
- defaults, sessions: [],
26
- };
27
- return { revision: createHash('sha256').update(JSON.stringify(catalog)).digest('hex').slice(0, 24), ...catalog };
28
- }
29
- /** The one saved setup for a conversation, or null; conflicting copies fail closed. */
30
- export function findManagedSessionState(runtimeId, conversationId, runtime) {
31
- const states = Object.values(readLocalRuntimeEntry(runtimeId)?.sessions ?? {})
32
- .filter((state) => state.conversationId === conversationId && state.codexManagedSetup);
33
- if (states.length > 1)
34
- throw new Error(`Conflicting saved ${runtime.label} setup for this conversation.`);
35
- return states[0] ?? null;
36
- }
37
- export function hasSavedNativeSession(runtimeId, conversationId, resumeField) {
38
- return Object.values(readLocalRuntimeEntry(runtimeId)?.sessions ?? {})
39
- .some((state) => state.conversationId === conversationId && Boolean(state[resumeField]));
40
- }
41
- /** Older records without ownership fields stay usable; a different owner generation does not. */
42
- export function savedSessionMatchesOwner(state, ownerId, ownershipVersion) {
43
- return (state.ownerId === undefined || ownerId === null || state.ownerId === ownerId)
44
- && (state.ownershipVersion === undefined || ownershipVersion === undefined
45
- || state.ownershipVersion === ownershipVersion);
46
- }
47
- export function inspectGitEnvironment(cwd) {
48
- const fields = {};
49
- try {
50
- const branch = execFileSync('git', ['symbolic-ref', '--quiet', '--short', 'HEAD'], { cwd, encoding: 'utf8', timeout: 3_000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
51
- if (branch)
52
- fields.branch = { value: branch, provenance: 'observed' };
53
- }
54
- catch { /* Detached HEAD has no branch to report. */ }
55
- try {
56
- const [gitDir, commonDir] = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-dir', '--git-common-dir'], { cwd, encoding: 'utf8', timeout: 3_000, stdio: ['ignore', 'pipe', 'ignore'] })
57
- .trim().split(/\r?\n/);
58
- if (gitDir && commonDir && realpathSync(gitDir) !== realpathSync(commonDir)) {
59
- const path = execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', timeout: 3_000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
60
- if (path)
61
- fields.worktreePath = { value: realpathSync(path), provenance: 'observed' };
62
- }
63
- }
64
- catch { /* An unavailable Git path has no worktree observation. */ }
65
- return fields;
66
- }
67
- function applyConfiguredFacts(fields, facts) {
68
- if (facts?.sandbox)
69
- fields.sandbox = { value: facts.sandbox, provenance: 'configured' };
70
- if (facts?.approvalPolicy)
71
- fields.approvalPolicy = { value: facts.approvalPolicy, provenance: 'configured' };
72
- if (facts?.nativeThreadBehavior) {
73
- fields.nativeThreadBehavior = { value: facts.nativeThreadBehavior, provenance: 'configured' };
74
- }
75
- }
76
- /** Private owner details for a running session: confirmed settings, then what the runtime and Git observe. */
77
- export function managedSessionFields(input) {
78
- const fields = {};
79
- for (const key of SETTING_KEYS) {
80
- if (input.settings[key])
81
- fields[key] = { value: input.settings[key], provenance: 'configured' };
82
- }
83
- if (input.projectLabel)
84
- fields.projectLabel = { value: input.projectLabel, provenance: 'configured' };
85
- fields.workingDirectory = { value: realpathSync(input.cwd), provenance: 'observed' };
86
- applyConfiguredFacts(fields, input.configured);
87
- if (input.observedModel)
88
- fields.modelId = { value: input.observedModel, provenance: 'observed' };
89
- if (input.observedEffort)
90
- fields.reasoningEffort = { value: input.observedEffort, provenance: 'observed' };
91
- Object.assign(fields, inspectGitEnvironment(input.cwd));
92
- return fields;
93
- }
94
- /** Private details for a running session nobody set up: what the host and Git observe. */
95
- export function observedSessionFields(input) {
96
- const fields = { workingDirectory: { value: input.cwd, provenance: 'observed' } };
97
- if (input.executionMode)
98
- fields.executionMode = { value: input.executionMode, provenance: 'observed' };
99
- if (input.project) {
100
- fields.projectId = { value: input.project.id, provenance: 'observed' };
101
- fields.projectLabel = { value: input.project.label, provenance: 'observed' };
102
- }
103
- if (input.observedModel)
104
- fields.modelId = { value: input.observedModel, provenance: 'observed' };
105
- if (input.observedEffort)
106
- fields.reasoningEffort = { value: input.observedEffort, provenance: 'observed' };
107
- Object.assign(fields, inspectGitEnvironment(input.cwd));
108
- return fields;
109
- }
110
- /** Read only: details for a saved session with no live native process, if its directory still exists. */
111
- export function savedSessionFields(input) {
112
- const { state } = input;
113
- const configured = state.codexManagedSetup?.settings;
114
- const project = input.projects.find((entry) => entry.cwd === state.baseCwd);
115
- let cwd;
116
- if (state.executionMode === 'locked' && existsSync(state.baseCwd))
117
- cwd = state.baseCwd;
118
- if (state.executionMode === 'worktree') {
119
- try {
120
- const spec = buildConversationWorktreeSpec({ agentId: input.agentId,
121
- conversationId: state.conversationId, workspaceCwd: state.baseCwd });
122
- const root = execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd: state.baseCwd, encoding: 'utf8', timeout: 3_000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
123
- const candidate = join(spec.worktreePath, relative(root, state.baseCwd));
124
- if (existsSync(candidate))
125
- cwd = candidate;
126
- }
127
- catch { /* Unavailable path stays unreported. */ }
128
- }
129
- const facts = configured ? input.describeConfigured?.(configured) : undefined;
130
- if (configured && cwd) {
131
- return managedSessionFields({ settings: configured, cwd, projectLabel: project?.label, configured: facts });
132
- }
133
- const fields = {};
134
- if (configured) {
135
- for (const key of SETTING_KEYS) {
136
- if (configured[key])
137
- fields[key] = { value: configured[key], provenance: 'configured' };
138
- }
139
- if (project?.label)
140
- fields.projectLabel = { value: project.label, provenance: 'configured' };
141
- applyConfiguredFacts(fields, facts);
142
- }
143
- else {
144
- if (state.executionMode)
145
- fields.executionMode = { value: state.executionMode, provenance: 'observed' };
146
- if (project) {
147
- fields.projectId = { value: project.id, provenance: 'observed' };
148
- fields.projectLabel = { value: project.label, provenance: 'observed' };
149
- }
150
- }
151
- if (cwd) {
152
- fields.workingDirectory = { value: realpathSync(cwd), provenance: 'observed' };
153
- Object.assign(fields, inspectGitEnvironment(cwd));
154
- }
155
- return fields;
156
- }
157
- /**
158
- * Idempotent configuration for `createCanonManagedSessionHost`. Validates the
159
- * owner's selection against the catalog, prepares the requested environment
160
- * (a requested worktree that cannot be created fails instead of falling back)
161
- * and saves the setup before the broker is acknowledged. A replayed request
162
- * returns the saved setup; a conversation that already has a native session or
163
- * a different confirmed setup keeps it.
164
- */
165
- export function configureManagedSession(input) {
166
- const { runtime } = input;
167
- const selection = input.request.selection;
168
- if (selection.mode !== 'configure' || input.catalog.managedSetup?.version !== 1)
169
- throw new Error('This host does not support the requested setup.');
170
- const existing = findManagedSessionState(input.runtimeId, input.request.conversationId, runtime);
171
- if (existing?.codexManagedSetup) {
172
- if (!savedSessionMatchesOwner(existing, input.ownerId ?? null, input.ownershipVersion))
173
- throw new Error(`The saved ${runtime.label} setup belongs to a previous owner generation.`);
174
- if (existing.codexManagedSetup.requestId !== input.request.requestId)
175
- throw new Error(`This conversation already has a confirmed ${runtime.label} setup.`);
176
- const settings = existing.codexManagedSetup.settings;
177
- const environment = prepareConversationEnvironment({ agentId: input.agentId,
178
- conversationId: input.request.conversationId, workspaceCwd: existing.baseCwd,
179
- allowWorktrees: existing.executionMode === 'worktree' });
180
- try {
181
- if (environment.mode !== existing.executionMode)
182
- throw new Error(`The saved ${runtime.label} execution environment is unavailable.`);
183
- return { settings,
184
- fields: managedSessionFields({ settings, cwd: environment.cwd,
185
- projectLabel: input.projects.find((project) => project.id === settings.projectId)?.label,
186
- configured: input.describeConfigured?.(settings) }),
187
- };
188
- }
189
- finally {
190
- releaseConversationEnvironment(environment);
191
- }
192
- }
193
- if (hasSavedNativeSession(input.runtimeId, input.request.conversationId, runtime.resumeField))
194
- throw new Error(`This conversation already has a native ${runtime.label} ${runtime.nativeSession}. Its settings are preserved.`);
195
- const project = input.projects.find((entry) => entry.id === selection.projectId);
196
- if (!project)
197
- throw new Error('The selected project is unavailable on this host.');
198
- const modelId = selection.modelId ?? input.catalog.defaults?.modelId;
199
- const model = input.catalog.models?.find((entry) => entry.id === modelId);
200
- if (modelId && !model)
201
- throw new Error('The selected model is unavailable on this host.');
202
- const reasoningEffort = selection.reasoningEffort ?? input.catalog.defaults?.reasoningEffort;
203
- if (reasoningEffort && !(model?.reasoningEfforts?.length ? model.reasoningEfforts : input.catalog.reasoningEfforts ?? [])
204
- .some((entry) => entry.id === reasoningEffort))
205
- throw new Error('The selected thinking level is unavailable for this model.');
206
- const permissionMode = selection.permissionMode ?? input.catalog.defaults?.permissionMode;
207
- const executionMode = selection.executionMode ?? input.catalog.defaults?.executionMode;
208
- if (!permissionMode || !input.catalog.permissionModes?.some((entry) => entry.id === permissionMode)
209
- || !executionMode || !input.catalog.executionModes?.some((entry) => entry.id === executionMode))
210
- throw new Error('The selected execution policy is unavailable on this host.');
211
- if (input.isPermissionModeApplicable && !input.isPermissionModeApplicable(permissionMode, modelId))
212
- throw new Error('The selected permission policy cannot be applied.');
213
- const environment = prepareConversationEnvironment({ agentId: input.agentId,
214
- conversationId: input.request.conversationId, workspaceCwd: project.cwd,
215
- allowWorktrees: executionMode === 'worktree' });
216
- try {
217
- if (environment.mode !== executionMode)
218
- throw new Error('The requested Git worktree could not be created. Select Project folder explicitly to use the base project.');
219
- const settings = { projectId: project.id,
220
- ...(modelId ? { modelId } : {}), ...(reasoningEffort ? { reasoningEffort } : {}),
221
- permissionMode, executionMode };
222
- const fields = managedSessionFields({ settings, cwd: environment.cwd, projectLabel: project.label,
223
- configured: input.describeConfigured?.(settings) });
224
- saveRuntimeSessionState(input.runtimeId, { conversationId: input.request.conversationId,
225
- baseCwd: environment.baseCwd, executionMode: executionMode,
226
- ...(input.ownerId ? { ownerId: input.ownerId } : {}),
227
- ...(input.ownershipVersion !== undefined ? { ownershipVersion: input.ownershipVersion } : {}),
228
- codexManagedSetup: { requestId: input.request.requestId, settings } });
229
- return { settings, fields };
230
- }
231
- finally {
232
- releaseConversationEnvironment(environment);
233
- }
234
- }
@@ -1,62 +0,0 @@
1
- import { type CanonEndpoint, type AgentContext, type ContactAddedPayload, type ContactRemovedPayload, type ConversationUpdatedPayload, type MessageUpdatedPayload, type ParticipationSuppressedPayload, type VoiceSessionEventPayload } from '@canonmsg/core';
2
- import { Debouncer } from './debouncer.js';
3
- /**
4
- * Wraps @canonmsg/core's CanonStream with SDK-specific features:
5
- * - Debouncer integration (message batching)
6
- * - Agent context callback
7
- * - Connection/status callbacks
8
- */
9
- export declare class RealtimeManager {
10
- private debouncer;
11
- private stream;
12
- private readonly endpoint;
13
- private readonly inbound;
14
- private lastSseErrorKey;
15
- private lastSseErrorAt;
16
- private suppressedSseErrorCount;
17
- private onAgentContext;
18
- private onContactAdded;
19
- private onContactRemoved;
20
- private onConversationUpdated;
21
- private onParticipationSuppressed;
22
- private onMessageUpdated;
23
- private onMessageDeleted;
24
- private onConnected;
25
- private onDisconnected;
26
- private onCallStarted;
27
- private onCallEnded;
28
- constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl: string | undefined, options: {
29
- enableVoiceEvents?: boolean;
30
- endpoint: CanonEndpoint;
31
- });
32
- private receive;
33
- private offer;
34
- private logSseError;
35
- setOnAgentContext(cb: (ctx: AgentContext) => void): void;
36
- setContactGraphHandlers(handlers: {
37
- onContactAdded?: (payload: ContactAddedPayload) => void;
38
- onContactRemoved?: (payload: ContactRemovedPayload) => void;
39
- }): void;
40
- setConversationUpdatedHandler(cb: (payload: ConversationUpdatedPayload) => void): void;
41
- /**
42
- * Observe-only notice that Canon's participation gate withheld a turn this
43
- * agent would otherwise have been dispatched. Never run a turn off it — it
44
- * exists so an agent can tell deliberate policy from a dead stream.
45
- */
46
- setParticipationSuppressedHandler(cb: (payload: ParticipationSuppressedPayload) => void): void;
47
- setMessageUpdatedHandler(cb: (payload: MessageUpdatedPayload) => void): void;
48
- setMessageDeletedHandler(cb: (payload: {
49
- conversationId: string;
50
- messageId: string;
51
- }) => void): void;
52
- setConnectionHandlers(handlers: {
53
- onConnected?: () => void;
54
- onDisconnected?: () => void;
55
- }): void;
56
- setCallHandlers(handlers: {
57
- onCallStarted?: (payload: VoiceSessionEventPayload) => void;
58
- onCallEnded?: (payload: VoiceSessionEventPayload) => void;
59
- }): void;
60
- start(): Promise<void>;
61
- stop(): void;
62
- }
package/dist/realtime.js DELETED
@@ -1,193 +0,0 @@
1
- import { CanonStream, } from '@canonmsg/core';
2
- /**
3
- * Wraps @canonmsg/core's CanonStream with SDK-specific features:
4
- * - Debouncer integration (message batching)
5
- * - Agent context callback
6
- * - Connection/status callbacks
7
- */
8
- export class RealtimeManager {
9
- debouncer;
10
- stream;
11
- endpoint;
12
- inbound;
13
- lastSseErrorKey = null;
14
- lastSseErrorAt = 0;
15
- suppressedSseErrorCount = 0;
16
- onAgentContext = null;
17
- onContactAdded = null;
18
- onContactRemoved = null;
19
- onConversationUpdated = null;
20
- onParticipationSuppressed = null;
21
- onMessageUpdated = null;
22
- onMessageDeleted = null;
23
- onConnected = null;
24
- onDisconnected = null;
25
- onCallStarted = null;
26
- onCallEnded = null;
27
- constructor(apiKey, debouncer, agentId, streamUrl, options) {
28
- this.debouncer = debouncer;
29
- this.endpoint = options.endpoint;
30
- this.inbound = this.endpoint.acceptInbound({ kind: 'message.created',
31
- offer: (event) => this.offer(event.data),
32
- onError: (error) => this.logSseError(error instanceof Error ? error : new Error(String(error))), });
33
- this.stream = new CanonStream({
34
- endpoint: this.endpoint,
35
- agentId,
36
- handler: {
37
- // The voice family is requested from handler PRESENCE at stream
38
- // construction, so these delegating closures exist only when the
39
- // agent registered call handlers before connect — otherwise every
40
- // SDK agent would cost the stream service a per-conversation
41
- // voiceSessions listener for nothing.
42
- ...(options?.enableVoiceEvents
43
- ? {
44
- onVoiceSessionStarted: (payload) => {
45
- this.onCallStarted?.(payload);
46
- },
47
- onVoiceSessionEnded: (payload) => {
48
- this.onCallEnded?.(payload);
49
- },
50
- }
51
- : {}),
52
- onMessage: (payload) => {
53
- void this.receive(payload).catch((error) => this.logSseError(error instanceof Error ? error : new Error(String(error))));
54
- },
55
- onMessageDeleted: (payload) => {
56
- this.debouncer.removeMessage(payload.conversationId, payload.messageId);
57
- this.onMessageDeleted?.(payload);
58
- },
59
- onMessageUpdated: (payload) => {
60
- this.onMessageUpdated?.(payload);
61
- },
62
- onAgentContext: (ctx) => {
63
- this.onAgentContext?.(ctx);
64
- },
65
- onContactAdded: (payload) => {
66
- this.onContactAdded?.(payload);
67
- },
68
- onContactRemoved: (payload) => {
69
- this.onContactRemoved?.(payload);
70
- },
71
- onConversationUpdated: (payload) => {
72
- void this.endpoint.applyConversationSnapshot({ id: payload.conversationId, ...payload.changes })
73
- .then(({ id: _id, ...changes }) => this.onConversationUpdated?.({ ...payload, changes }))
74
- .catch((error) => this.logSseError(error instanceof Error ? error : new Error(String(error))));
75
- },
76
- onParticipationSuppressed: (payload) => {
77
- this.onParticipationSuppressed?.(payload);
78
- },
79
- onConnected: () => {
80
- this.onConnected?.();
81
- },
82
- onDisconnected: () => {
83
- this.onDisconnected?.();
84
- },
85
- onReplayExpired: (payload) => {
86
- console.error(`[canon-sdk] SSE replay window expired${payload.lastAvailableId ? ` (oldest available event: ${payload.lastAvailableId})` : ''}; the endpoint is recovering the durable gap`);
87
- },
88
- onError: (err) => {
89
- this.logSseError(err);
90
- },
91
- },
92
- });
93
- }
94
- async receive(payload) {
95
- await this.inbound.receive({ id: `message:${payload.conversationId}:${payload.message.id}`,
96
- kind: 'message.created', conversationId: payload.conversationId, durable: true,
97
- data: payload });
98
- }
99
- async offer(payload) {
100
- const id = `message:${payload.conversationId}:${payload.message.id}`;
101
- const handoff = await this.endpoint.acquireInbound(id);
102
- if (!handoff)
103
- return;
104
- if (payload.turnDispatch && payload.turnDispatch.kind !== 'run_turn') {
105
- await handoff.cancel('observe-only');
106
- return;
107
- }
108
- const m = payload.message;
109
- const message = {
110
- id: m.id,
111
- senderId: m.senderId,
112
- ...(m.senderName ? { senderName: m.senderName } : {}),
113
- senderType: m.senderType ?? 'human',
114
- isOwner: m.isOwner ?? false,
115
- contentType: m.contentType ?? 'text',
116
- text: m.text ?? null,
117
- attachments: m.attachments ?? [],
118
- mentions: m.mentions ?? [],
119
- ...(m.reactions ? { reactions: m.reactions } : {}),
120
- replyTo: m.replyTo ?? null,
121
- replyToPosition: m.replyToPosition ?? null,
122
- ...(m.forwarded === true || m.forwardedFrom
123
- ? { forwarded: true }
124
- : {}),
125
- ...(m.forwardedFrom
126
- ? { forwardedFrom: m.forwardedFrom }
127
- : {}),
128
- status: 'sent',
129
- deleted: false,
130
- createdAt: m.createdAt ?? new Date().toISOString(),
131
- ...(m.contactCard ? { contactCard: m.contactCard } : {}),
132
- ...(m.metadata ? { metadata: m.metadata } : {}),
133
- ...(payload.replyAuthority ? { replyAuthority: payload.replyAuthority } : {}),
134
- };
135
- this.debouncer.add(payload.conversationId, message, payload.provenance ?? null, handoff);
136
- }
137
- logSseError(err) {
138
- const code = err.code;
139
- const key = `${typeof code === 'string' ? code : 'generic'}:${err.message}`;
140
- const now = Date.now();
141
- if (this.lastSseErrorKey === key && now - this.lastSseErrorAt < 60_000) {
142
- this.suppressedSseErrorCount += 1;
143
- return;
144
- }
145
- if (this.suppressedSseErrorCount > 0) {
146
- console.error(`[canon-sdk] SSE error repeated ${this.suppressedSseErrorCount} more time${this.suppressedSseErrorCount === 1 ? '' : 's'}`);
147
- this.suppressedSseErrorCount = 0;
148
- }
149
- this.lastSseErrorKey = key;
150
- this.lastSseErrorAt = now;
151
- console.error('[canon-sdk] SSE error:', err.message);
152
- }
153
- setOnAgentContext(cb) {
154
- this.onAgentContext = cb;
155
- }
156
- setContactGraphHandlers(handlers) {
157
- this.onContactAdded = handlers.onContactAdded ?? null;
158
- this.onContactRemoved = handlers.onContactRemoved ?? null;
159
- }
160
- setConversationUpdatedHandler(cb) {
161
- this.onConversationUpdated = cb;
162
- }
163
- /**
164
- * Observe-only notice that Canon's participation gate withheld a turn this
165
- * agent would otherwise have been dispatched. Never run a turn off it — it
166
- * exists so an agent can tell deliberate policy from a dead stream.
167
- */
168
- setParticipationSuppressedHandler(cb) {
169
- this.onParticipationSuppressed = cb;
170
- }
171
- setMessageUpdatedHandler(cb) {
172
- this.onMessageUpdated = cb;
173
- }
174
- setMessageDeletedHandler(cb) {
175
- this.onMessageDeleted = cb;
176
- }
177
- setConnectionHandlers(handlers) {
178
- this.onConnected = handlers.onConnected ?? null;
179
- this.onDisconnected = handlers.onDisconnected ?? null;
180
- }
181
- setCallHandlers(handlers) {
182
- this.onCallStarted = handlers.onCallStarted ?? null;
183
- this.onCallEnded = handlers.onCallEnded ?? null;
184
- }
185
- async start() {
186
- this.inbound.start();
187
- await this.stream.start();
188
- }
189
- stop() {
190
- void this.inbound.close();
191
- this.stream.stop();
192
- }
193
- }
@@ -1,48 +0,0 @@
1
- import type { RuntimeCardNativeMetadata, RuntimeCardV1 } from '@canonmsg/core';
2
- import type { RuntimeCardRequest } from './types';
3
- export declare const DEFAULT_RUNTIME_INPUT_TIMEOUT_MS: number;
4
- export declare const RUNTIME_INPUT_ID_PATTERN: RegExp;
5
- export declare function normalizeResponseUserId(value: string | undefined): string | undefined;
6
- /**
7
- * Resolve the routing prelude shared verbatim by `sendCard` (display) and
8
- * `requestCard` (interactive): the sanitized card id, the effective expiry,
9
- * and the request with an explicit responder folded in.
10
- *
11
- * `triggeringHumanId` must be passed in by the caller rather than re-derived
12
- * here — it is captured from the enclosing message handler's scope.
13
- */
14
- export declare function resolveRuntimeCardRouting(input: {
15
- request: RuntimeCardRequest;
16
- triggeringHumanId?: string;
17
- }): {
18
- cardId: string;
19
- expiresAtMs: number;
20
- routedRequest: RuntimeCardRequest;
21
- };
22
- /** Arguments passed to `CanonClient.createRuntimeCardRequest`. */
23
- export interface RuntimeCardCreateArgs {
24
- conversationId: string;
25
- card: RuntimeCardV1;
26
- cardId: string;
27
- expiresAt: number;
28
- responseUserId?: string;
29
- runtimeId?: string;
30
- turnId?: string;
31
- native?: RuntimeCardNativeMetadata;
32
- }
33
- /**
34
- * Build the `createRuntimeCardRequest` payload shared by `sendCard` (display)
35
- * and `requestCard` (interactive).
36
- *
37
- * `responseUserId` is OMITTED unless the developer supplied one, so the Canon
38
- * backend infers a reachable responder (the owner when they are a conversation
39
- * member, otherwise the sole other member). Pre-filling the owner here would
40
- * break agent-to-user DMs where the owner is not a member of the conversation.
41
- */
42
- export declare function buildRuntimeCardCreateArgs(input: {
43
- request: RuntimeCardRequest;
44
- conversationId: string;
45
- cardId: string;
46
- fallbackTurnId?: string;
47
- expiresAtMs: number;
48
- }): RuntimeCardCreateArgs;
@@ -1,64 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- export const DEFAULT_RUNTIME_INPUT_TIMEOUT_MS = 5 * 60_000;
3
- export const RUNTIME_INPUT_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,160}$/;
4
- function safeRuntimeCardId(value) {
5
- const raw = value?.trim() || `card_${randomUUID()}`;
6
- const normalized = raw.replace(/[.#$\[\]\/\s]+/g, '_').slice(0, 80);
7
- return RUNTIME_INPUT_ID_PATTERN.test(normalized)
8
- ? normalized
9
- : `card_${randomUUID()}`;
10
- }
11
- export function normalizeResponseUserId(value) {
12
- return value?.trim() || undefined;
13
- }
14
- /**
15
- * Resolve the routing prelude shared verbatim by `sendCard` (display) and
16
- * `requestCard` (interactive): the sanitized card id, the effective expiry,
17
- * and the request with an explicit responder folded in.
18
- *
19
- * `triggeringHumanId` must be passed in by the caller rather than re-derived
20
- * here — it is captured from the enclosing message handler's scope.
21
- */
22
- export function resolveRuntimeCardRouting(input) {
23
- const { request, triggeringHumanId } = input;
24
- const cardId = safeRuntimeCardId(request.cardId ?? request.card.cardId);
25
- const explicitExpiresAt = request.expiresAt instanceof Date
26
- ? request.expiresAt.getTime()
27
- : typeof request.expiresAt === 'number'
28
- ? request.expiresAt
29
- : typeof request.expiresAt === 'string'
30
- ? Date.parse(request.expiresAt)
31
- : null;
32
- const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
33
- const expiresAtMs = explicitExpiresAt && Number.isFinite(explicitExpiresAt)
34
- ? explicitExpiresAt
35
- : Date.now() + timeoutMs;
36
- const responseUserId = normalizeResponseUserId(request.responseUserId)
37
- ?? triggeringHumanId;
38
- const routedRequest = responseUserId
39
- ? { ...request, responseUserId }
40
- : request;
41
- return { cardId, expiresAtMs, routedRequest };
42
- }
43
- /**
44
- * Build the `createRuntimeCardRequest` payload shared by `sendCard` (display)
45
- * and `requestCard` (interactive).
46
- *
47
- * `responseUserId` is OMITTED unless the developer supplied one, so the Canon
48
- * backend infers a reachable responder (the owner when they are a conversation
49
- * member, otherwise the sole other member). Pre-filling the owner here would
50
- * break agent-to-user DMs where the owner is not a member of the conversation.
51
- */
52
- export function buildRuntimeCardCreateArgs(input) {
53
- const { request, conversationId, cardId, fallbackTurnId, expiresAtMs } = input;
54
- return {
55
- conversationId,
56
- cardId,
57
- card: { ...request.card, cardId },
58
- expiresAt: expiresAtMs,
59
- ...(request.responseUserId ? { responseUserId: request.responseUserId } : {}),
60
- ...(request.runtimeId ? { runtimeId: request.runtimeId } : {}),
61
- turnId: request.turnId ?? fallbackTurnId,
62
- ...(request.native ? { native: request.native } : {}),
63
- };
64
- }