@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.
- package/README.md +248 -523
- package/dist/chunks/chunk-7N6OPPW4.js +5430 -0
- package/dist/chunks/chunk-N722GWCP.js +4897 -0
- package/dist/chunks/chunk-QZ4PPRQW.js +14247 -0
- package/dist/chunks/mls-crypto-LMNQZOS6.js +10 -0
- package/dist/cli.js +572 -0
- package/dist/drivers/claude.d.ts +42 -0
- package/dist/drivers/code.d.ts +18 -0
- package/dist/drivers/codex.d.ts +46 -0
- package/dist/drivers/connector.d.ts +34 -0
- package/dist/index.d.ts +20 -25
- package/dist/index.js +91 -13
- package/dist/internal/backend-contracts/agentBehaviorPolicy.d.ts +122 -0
- package/dist/internal/backend-contracts/agentDirectory.d.ts +32 -0
- package/dist/internal/backend-contracts/agentOrigin.d.ts +23 -0
- package/dist/internal/backend-contracts/agentSearch.d.ts +17 -0
- package/dist/internal/backend-contracts/api-origins.generated.d.ts +4 -0
- package/dist/internal/backend-contracts/apiErrors.d.ts +1 -0
- package/dist/internal/backend-contracts/artifactActions.d.ts +8 -0
- package/dist/internal/backend-contracts/artifacts.d.ts +386 -0
- package/dist/internal/backend-contracts/clientFloor.d.ts +41 -0
- package/dist/internal/backend-contracts/communication.d.ts +146 -0
- package/dist/internal/backend-contracts/connectorRelay.d.ts +179 -0
- package/dist/internal/backend-contracts/conversationMembership.d.ts +28 -0
- package/dist/internal/backend-contracts/deliveryObservation.d.ts +23 -0
- package/dist/internal/backend-contracts/diffRedaction.d.ts +6 -0
- package/dist/internal/backend-contracts/endpoint-unread.d.ts +4 -0
- package/dist/internal/backend-contracts/endpoint.d.ts +120 -0
- package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +48 -0
- package/dist/internal/backend-contracts/environment.d.ts +20 -0
- package/dist/internal/backend-contracts/externalInbound.d.ts +24 -0
- package/dist/internal/backend-contracts/firestoreValues.d.ts +9 -0
- package/dist/internal/backend-contracts/index.d.ts +54 -0
- package/dist/internal/backend-contracts/media-url.d.ts +20 -0
- package/dist/internal/backend-contracts/media.d.ts +51 -0
- package/dist/internal/backend-contracts/mediaDelivery.d.ts +46 -0
- package/dist/internal/backend-contracts/mediaTransferProtocol.d.ts +17 -0
- package/dist/internal/backend-contracts/membershipChange.d.ts +21 -0
- package/dist/internal/backend-contracts/message.d.ts +86 -0
- package/dist/internal/backend-contracts/messageText.d.ts +6 -0
- package/dist/internal/backend-contracts/mlsDevApplication.d.ts +161 -0
- package/dist/internal/backend-contracts/mlsDevCredential.d.ts +78 -0
- package/dist/internal/backend-contracts/mlsDevCredentialReconciliation.d.ts +45 -0
- package/dist/internal/backend-contracts/mlsDevDeviceCustody.d.ts +52 -0
- package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +75 -0
- package/dist/internal/backend-contracts/mlsDevFeedReceipt.d.ts +43 -0
- package/dist/internal/backend-contracts/mlsDevGroup.d.ts +321 -0
- package/dist/internal/backend-contracts/mlsDevGroupFeed.d.ts +51 -0
- package/dist/internal/backend-contracts/mlsDevInteraction.d.ts +159 -0
- package/dist/internal/backend-contracts/mlsDevKeyPackage.d.ts +42 -0
- package/dist/internal/backend-contracts/mlsDevKeyPackages.d.ts +55 -0
- package/dist/internal/backend-contracts/mlsEnvironment.d.ts +35 -0
- package/dist/internal/backend-contracts/mlsPrincipalAlias.d.ts +23 -0
- package/dist/internal/backend-contracts/moderation.d.ts +7 -0
- package/dist/internal/backend-contracts/plaintextProcessors.d.ts +63 -0
- package/dist/internal/backend-contracts/preparedContent.d.ts +54 -0
- package/dist/internal/backend-contracts/replyAuthority.d.ts +14 -0
- package/dist/internal/backend-contracts/runtimeCardFields.d.ts +66 -0
- package/dist/internal/backend-contracts/runtimeCardStorage.d.ts +3 -0
- package/dist/internal/backend-contracts/runtimeDescriptor.d.ts +88 -0
- package/dist/internal/backend-contracts/selfContext.d.ts +9 -0
- package/dist/internal/backend-contracts/turnProtocol.d.ts +43 -0
- package/dist/internal/backend-contracts/upload-mime-types.d.ts +17 -0
- package/dist/internal/backend-contracts/verbContract.d.ts +856 -0
- package/dist/internal/backend-contracts/verbSchemas.d.ts +1717 -0
- package/dist/internal/backend-contracts/verbWire.d.ts +493 -0
- package/dist/internal/backend-contracts/webAuth.d.ts +8 -0
- package/dist/internal/core/agent-profiles.d.ts +65 -0
- package/dist/internal/core/approval-format.d.ts +22 -0
- package/dist/internal/core/approval-manager.d.ts +43 -0
- package/dist/internal/core/approval-types.d.ts +73 -0
- package/dist/internal/core/base-url.d.ts +1 -0
- package/dist/internal/core/cli-metadata.d.ts +13 -0
- package/dist/internal/core/client.d.ts +432 -0
- package/dist/internal/core/completed-output-journal.d.ts +39 -0
- package/dist/internal/core/constants.d.ts +1 -0
- package/dist/internal/core/endpoint-client.d.ts +56 -0
- package/dist/internal/core/execution-environment-mode.d.ts +6 -0
- package/dist/internal/core/gifs.d.ts +29 -0
- package/dist/internal/core/host-runtime.d.ts +53 -0
- package/dist/internal/core/index.d.ts +68 -0
- package/dist/internal/core/node-media.d.ts +6 -0
- package/dist/internal/core/policy.d.ts +2 -0
- package/dist/internal/core/registration-profile.d.ts +10 -0
- package/dist/internal/core/registration-session.d.ts +55 -0
- package/dist/internal/core/registration.d.ts +5 -0
- package/dist/internal/core/retry.d.ts +113 -0
- package/dist/internal/core/rtdb-rest.d.ts +141 -0
- package/dist/internal/core/runtime-cards.d.ts +286 -0
- package/dist/internal/core/runtime-descriptor.d.ts +16 -0
- package/dist/internal/core/runtime-endpoints.d.ts +26 -0
- package/dist/internal/core/runtime-heartbeat.d.ts +29 -0
- package/dist/internal/core/runtime-presentation.d.ts +9 -0
- package/dist/internal/core/runtime-request-manager.d.ts +283 -0
- package/dist/internal/core/runtime-state-publisher.d.ts +42 -0
- package/dist/internal/core/self-context.d.ts +22 -0
- package/dist/internal/core/silent-turn.d.ts +49 -0
- package/dist/internal/core/stream.d.ts +78 -0
- package/dist/internal/core/turn-protocol.d.ts +28 -0
- package/dist/internal/core/turn-verbosity.d.ts +85 -0
- package/dist/internal/core/types.d.ts +964 -0
- package/dist/internal/core/unified-diff.d.ts +35 -0
- package/dist/internal/endpoint/artifact-images.d.ts +60 -0
- package/dist/internal/endpoint/artifact-reducer.d.ts +35 -0
- package/dist/internal/endpoint/artifact-views.d.ts +113 -0
- package/dist/internal/endpoint/artifacts.d.ts +75 -0
- package/dist/internal/endpoint/delivery.d.ts +47 -0
- package/dist/internal/endpoint/engine.d.ts +249 -0
- package/dist/internal/endpoint/ephemeral.d.ts +8 -0
- package/dist/internal/endpoint/events.d.ts +32 -0
- package/dist/internal/endpoint/external-inbound.d.ts +36 -0
- package/dist/internal/endpoint/handoff.d.ts +50 -0
- package/dist/internal/endpoint/history.d.ts +22 -0
- package/dist/internal/endpoint/image-generation-errors.d.ts +2 -0
- package/dist/internal/endpoint/inbound.d.ts +29 -0
- package/dist/internal/endpoint/index.d.ts +32 -0
- package/dist/internal/endpoint/media.d.ts +177 -0
- package/dist/internal/endpoint/migration.d.ts +50 -0
- package/dist/internal/endpoint/mls-crypto.d.ts +32 -0
- package/dist/internal/endpoint/mls-dev-client.d.ts +52 -0
- package/dist/internal/endpoint/mls-dev-device.d.ts +69 -0
- package/dist/internal/endpoint/mls-dev-groups.d.ts +174 -0
- package/dist/internal/endpoint/mls-dev-interactions.d.ts +99 -0
- package/dist/internal/endpoint/mls-dev-key-package-service.d.ts +46 -0
- package/dist/internal/endpoint/mls-dev-key-packages.d.ts +62 -0
- package/dist/internal/endpoint/mls-dev-text.d.ts +67 -0
- package/dist/internal/endpoint/mls-dev-transport.d.ts +38 -0
- package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +29 -0
- package/dist/internal/endpoint/node-mls-custody.d.ts +15 -0
- package/dist/internal/endpoint/node.d.ts +7 -0
- package/dist/internal/endpoint/observe-query.d.ts +17 -0
- package/dist/internal/endpoint/portable-node-mls-custody.d.ts +23 -0
- package/dist/internal/endpoint/queue.d.ts +22 -0
- package/dist/internal/endpoint/recovery.d.ts +27 -0
- package/dist/internal/endpoint/retention.d.ts +7 -0
- package/dist/internal/endpoint/store.d.ts +22 -0
- package/dist/internal/endpoint/sync.d.ts +33 -0
- package/dist/internal/endpoint/transient-publisher.d.ts +8 -0
- package/dist/internal/endpoint/transport.d.ts +63 -0
- package/dist/media.d.ts +1 -1
- package/dist/media.js +42 -541
- package/dist/runner/errands.d.ts +118 -0
- package/dist/runner/mls.d.ts +97 -0
- package/dist/runner/relay.d.ts +87 -0
- package/dist/runner/runner.d.ts +98 -0
- package/dist/runner/types.d.ts +215 -0
- package/package.json +18 -5
- package/dist/attached-session.d.ts +0 -58
- package/dist/attached-session.js +0 -638
- package/dist/bounded-file-read.d.ts +0 -14
- package/dist/bounded-file-read.js +0 -34
- package/dist/canon-agent.d.ts +0 -236
- package/dist/canon-agent.js +0 -2531
- package/dist/debouncer.d.ts +0 -18
- package/dist/debouncer.js +0 -133
- package/dist/inbound-handoff.d.ts +0 -6
- package/dist/inbound-handoff.js +0 -17
- package/dist/managed-session-host.d.ts +0 -27
- package/dist/managed-session-host.js +0 -195
- package/dist/managed-session-setup.d.ts +0 -99
- package/dist/managed-session-setup.js +0 -234
- package/dist/realtime.d.ts +0 -62
- package/dist/realtime.js +0 -193
- package/dist/runtime-card.d.ts +0 -48
- package/dist/runtime-card.js +0 -64
- package/dist/session-manager.d.ts +0 -77
- package/dist/session-manager.js +0 -285
- package/dist/turn-streaming-request.d.ts +0 -17
- package/dist/turn-streaming-request.js +0 -22
- package/dist/turn-verbosity-option.d.ts +0 -33
- package/dist/turn-verbosity-option.js +0 -23
- package/dist/types.d.ts +0 -387
- package/dist/types.js +0 -1
- package/dist/work-session-coordination.d.ts +0 -21
- package/dist/work-session-coordination.js +0 -53
- package/dist/work-session-host.d.ts +0 -31
- package/dist/work-session-host.js +0 -617
- package/dist/work-session-interactions.d.ts +0 -12
- package/dist/work-session-interactions.js +0 -173
- package/dist/work-session-state.d.ts +0 -36
- 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
|
-
}
|
package/dist/realtime.d.ts
DELETED
|
@@ -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
|
-
}
|
package/dist/runtime-card.d.ts
DELETED
|
@@ -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;
|
package/dist/runtime-card.js
DELETED
|
@@ -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
|
-
}
|