@canonmsg/core 0.20.1 → 0.22.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/dist/approval-format.d.ts +1 -0
- package/dist/approval-format.js +1 -0
- package/dist/approval-manager.js +63 -17
- package/dist/approval-types.d.ts +2 -0
- package/dist/approval-types.js +2 -0
- package/dist/client.d.ts +57 -0
- package/dist/client.js +29 -0
- package/dist/runtime-cards.d.ts +9 -1
- package/dist/runtime-cards.js +5 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ export declare function buildApprovalRequest(approvalId: string, toolName: strin
|
|
|
5
5
|
riskLevel?: 'normal' | 'destructive';
|
|
6
6
|
risk?: ApprovalRisk;
|
|
7
7
|
category?: ApprovalRequestCategory;
|
|
8
|
+
responseUserId?: string;
|
|
8
9
|
runtimeId?: string;
|
|
9
10
|
turnId?: string;
|
|
10
11
|
native?: ApprovalNativeRequestMetadata;
|
package/dist/approval-format.js
CHANGED
|
@@ -64,6 +64,7 @@ export function buildApprovalRequest(approvalId, toolName, toolInput, opts) {
|
|
|
64
64
|
const metadata = {
|
|
65
65
|
type: 'approval_request',
|
|
66
66
|
approvalId,
|
|
67
|
+
...(opts.responseUserId ? { responseUserId: opts.responseUserId.slice(0, 128) } : {}),
|
|
67
68
|
toolName,
|
|
68
69
|
toolSummary: summary,
|
|
69
70
|
...(opts.category ? { category: opts.category } : {}),
|
package/dist/approval-manager.js
CHANGED
|
@@ -50,10 +50,11 @@ export class ApprovalManager {
|
|
|
50
50
|
}
|
|
51
51
|
const approvalId = generateApprovalId();
|
|
52
52
|
const expiresAt = new Date(Date.now() + this.config.timeoutSeconds * 1000).toISOString();
|
|
53
|
-
const {
|
|
53
|
+
const { metadata } = buildApprovalRequest(approvalId, toolName, toolInput, {
|
|
54
54
|
riskLevel: opts?.riskLevel,
|
|
55
55
|
risk: opts?.risk,
|
|
56
56
|
category: opts?.category,
|
|
57
|
+
responseUserId: this.ownerId,
|
|
57
58
|
runtimeId: opts?.runtimeId,
|
|
58
59
|
turnId: opts?.turnId,
|
|
59
60
|
native: opts?.native,
|
|
@@ -62,25 +63,47 @@ export class ApprovalManager {
|
|
|
62
63
|
expiresAt,
|
|
63
64
|
redactPatterns: this.config.redactPatterns,
|
|
64
65
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
await this.client.createRuntimeApprovalRequest({
|
|
67
|
+
conversationId,
|
|
68
|
+
approvalId,
|
|
69
|
+
toolName,
|
|
70
|
+
toolSummary: metadata.toolSummary,
|
|
71
|
+
expiresAt: Date.parse(expiresAt),
|
|
72
|
+
responseUserId: this.ownerId,
|
|
73
|
+
...(opts?.riskLevel ? { riskLevel: opts.riskLevel } : {}),
|
|
74
|
+
...(opts?.risk ? { risk: opts.risk } : {}),
|
|
75
|
+
...(opts?.category ? { category: opts.category } : {}),
|
|
76
|
+
...(opts?.runtimeId ? { runtimeId: opts.runtimeId } : {}),
|
|
77
|
+
...(opts?.turnId ? { turnId: opts.turnId } : {}),
|
|
78
|
+
...(metadata.native ? { native: metadata.native } : {}),
|
|
79
|
+
...(metadata.details ? { details: metadata.details } : {}),
|
|
80
|
+
...(opts?.allowSessionRule === false ? { allowSessionRule: false } : {}),
|
|
69
81
|
});
|
|
70
82
|
// Wait for reply or timeout
|
|
71
83
|
return new Promise((resolve) => {
|
|
72
|
-
|
|
84
|
+
let settled = false;
|
|
85
|
+
let timer;
|
|
86
|
+
const finish = (result, reason) => {
|
|
87
|
+
if (settled)
|
|
88
|
+
return;
|
|
89
|
+
settled = true;
|
|
90
|
+
clearTimeout(timer);
|
|
73
91
|
this.pending.delete(approvalId);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
if (reason === 'timeout') {
|
|
93
|
+
const msg = buildApprovalOutcome(approvalId, toolName, metadata.toolSummary, 'deny', 'timeout');
|
|
94
|
+
this.client.sendMessage(conversationId, msg, {
|
|
95
|
+
metadata: {
|
|
96
|
+
type: 'approval_outcome',
|
|
97
|
+
approvalId,
|
|
98
|
+
decision: 'deny',
|
|
99
|
+
reason: 'timeout',
|
|
100
|
+
},
|
|
101
|
+
}).catch(() => { });
|
|
102
|
+
}
|
|
103
|
+
resolve(result);
|
|
104
|
+
};
|
|
105
|
+
timer = setTimeout(() => {
|
|
106
|
+
finish({ decision: 'deny' }, 'timeout');
|
|
84
107
|
}, this.config.timeoutSeconds * 1000);
|
|
85
108
|
this.pending.set(approvalId, {
|
|
86
109
|
approvalId,
|
|
@@ -88,9 +111,32 @@ export class ApprovalManager {
|
|
|
88
111
|
toolName,
|
|
89
112
|
toolSummary: metadata.toolSummary,
|
|
90
113
|
allowSessionRule: opts?.allowSessionRule !== false,
|
|
91
|
-
resolve,
|
|
114
|
+
resolve: (result) => finish(result, 'replied'),
|
|
92
115
|
timer,
|
|
93
116
|
});
|
|
117
|
+
const poll = async () => {
|
|
118
|
+
while (this.pending.has(approvalId)) {
|
|
119
|
+
try {
|
|
120
|
+
const response = await this.client.consumeRuntimeApprovalResponse({
|
|
121
|
+
conversationId,
|
|
122
|
+
approvalId,
|
|
123
|
+
});
|
|
124
|
+
if (response.status === 'allow' || response.status === 'deny') {
|
|
125
|
+
this.resolveApproval(approvalId, response.status, response.sessionRule, conversationId);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (response.status === 'timeout') {
|
|
129
|
+
finish({ decision: 'deny' }, 'timeout');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Keep polling through transient API errors until the local timeout.
|
|
135
|
+
}
|
|
136
|
+
await new Promise((resume) => setTimeout(resume, 1000));
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
void poll();
|
|
94
140
|
});
|
|
95
141
|
}
|
|
96
142
|
/**
|
package/dist/approval-types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface ApprovalRequestMetadata {
|
|
2
2
|
type: 'approval_request';
|
|
3
3
|
approvalId: string;
|
|
4
|
+
/** Canon user who should answer this card. UI hint only; reply APIs still enforce authorization. */
|
|
5
|
+
responseUserId?: string;
|
|
4
6
|
toolName: string;
|
|
5
7
|
/** Pre-computed, redacted summary — raw toolInput is never stored */
|
|
6
8
|
toolSummary: string;
|
package/dist/approval-types.js
CHANGED
|
@@ -172,6 +172,7 @@ export function parseApprovalRequestMetadata(value) {
|
|
|
172
172
|
const expiresMs = Date.parse(expiresAt);
|
|
173
173
|
if (!Number.isFinite(expiresMs))
|
|
174
174
|
return null;
|
|
175
|
+
const responseUserId = normalizeString(value.responseUserId, 128) ?? undefined;
|
|
175
176
|
const riskLevel = value.riskLevel === 'destructive' || value.riskLevel === 'normal'
|
|
176
177
|
? value.riskLevel
|
|
177
178
|
: undefined;
|
|
@@ -184,6 +185,7 @@ export function parseApprovalRequestMetadata(value) {
|
|
|
184
185
|
return {
|
|
185
186
|
type: 'approval_request',
|
|
186
187
|
approvalId,
|
|
188
|
+
...(responseUserId ? { responseUserId } : {}),
|
|
187
189
|
toolName,
|
|
188
190
|
toolSummary,
|
|
189
191
|
...(category ? { category } : {}),
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type CanonMessage, type CanonConversation, type CanonContact, type CanonContactRequest, type CanonMessagesPage, type CanonResolveAdmissionResult, type AgentContext, type AddMemberResult, type CreateContactRequestResult, type MediaAttachment, type SendMessageOptions, type CreateConversationOptions, type RegistrationStatus, type SetStreamingOptions } from './types.js';
|
|
2
2
|
import type { RuntimeInputKind } from './runtime-cards.js';
|
|
3
|
+
import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRisk, SessionRule } from './approval-types.js';
|
|
4
|
+
import type { RuntimeInputChoice, RuntimeInputNativeMetadata } from './runtime-cards.js';
|
|
3
5
|
import type { SendContextualMessageOptions, SendContextualMessageResult } from './self-context.js';
|
|
4
6
|
import type { InboundDisposition } from './turn-protocol.js';
|
|
5
7
|
/**
|
|
@@ -59,10 +61,19 @@ export declare class CanonClient {
|
|
|
59
61
|
inputId: string;
|
|
60
62
|
kind: RuntimeInputKind;
|
|
61
63
|
expiresAt: number;
|
|
64
|
+
title?: string;
|
|
65
|
+
prompt?: string;
|
|
66
|
+
choices?: RuntimeInputChoice[];
|
|
67
|
+
secretName?: string;
|
|
68
|
+
native?: RuntimeInputNativeMetadata;
|
|
69
|
+
sensitive?: boolean;
|
|
70
|
+
responseUserId?: string;
|
|
71
|
+
turnId?: string;
|
|
62
72
|
}): Promise<{
|
|
63
73
|
success: true;
|
|
64
74
|
inputId: string;
|
|
65
75
|
expiresAt: number;
|
|
76
|
+
messageId?: string;
|
|
66
77
|
}>;
|
|
67
78
|
consumeRuntimeInputResponse(options: {
|
|
68
79
|
conversationId: string;
|
|
@@ -86,6 +97,52 @@ export declare class CanonClient {
|
|
|
86
97
|
inputId: string;
|
|
87
98
|
kind: RuntimeInputKind;
|
|
88
99
|
}>;
|
|
100
|
+
createRuntimeApprovalRequest(options: {
|
|
101
|
+
conversationId: string;
|
|
102
|
+
approvalId?: string;
|
|
103
|
+
toolName: string;
|
|
104
|
+
toolSummary: string;
|
|
105
|
+
expiresAt: number;
|
|
106
|
+
responseUserId?: string;
|
|
107
|
+
riskLevel?: 'normal' | 'destructive';
|
|
108
|
+
risk?: ApprovalRisk;
|
|
109
|
+
category?: ApprovalRequestCategory;
|
|
110
|
+
runtimeId?: string;
|
|
111
|
+
turnId?: string;
|
|
112
|
+
native?: ApprovalNativeRequestMetadata;
|
|
113
|
+
details?: ApprovalRequestDetail[];
|
|
114
|
+
allowSessionRule?: boolean;
|
|
115
|
+
}): Promise<{
|
|
116
|
+
success: true;
|
|
117
|
+
approvalId: string;
|
|
118
|
+
expiresAt: number;
|
|
119
|
+
messageId?: string;
|
|
120
|
+
}>;
|
|
121
|
+
consumeRuntimeApprovalResponse(options: {
|
|
122
|
+
conversationId: string;
|
|
123
|
+
approvalId: string;
|
|
124
|
+
cancel?: boolean;
|
|
125
|
+
}): Promise<{
|
|
126
|
+
status: 'pending';
|
|
127
|
+
approvalId: string;
|
|
128
|
+
expiresAt?: number;
|
|
129
|
+
} | {
|
|
130
|
+
status: 'allow';
|
|
131
|
+
approvalId: string;
|
|
132
|
+
sessionRule?: SessionRule;
|
|
133
|
+
} | {
|
|
134
|
+
status: 'deny';
|
|
135
|
+
approvalId: string;
|
|
136
|
+
sessionRule?: SessionRule;
|
|
137
|
+
} | {
|
|
138
|
+
status: 'timeout';
|
|
139
|
+
approvalId: string;
|
|
140
|
+
}>;
|
|
141
|
+
updateRuntimeStatus(options: {
|
|
142
|
+
runtime: string;
|
|
143
|
+
hostMode?: boolean;
|
|
144
|
+
runtimeDescriptor?: Record<string, unknown>;
|
|
145
|
+
}): Promise<void>;
|
|
89
146
|
static register(baseUrl: string | undefined, body: {
|
|
90
147
|
name: string;
|
|
91
148
|
description: string;
|
package/dist/client.js
CHANGED
|
@@ -339,6 +339,35 @@ export class CanonClient {
|
|
|
339
339
|
throw new CanonApiError(res.status, await res.text());
|
|
340
340
|
return res.json();
|
|
341
341
|
}
|
|
342
|
+
async createRuntimeApprovalRequest(options) {
|
|
343
|
+
const res = await fetch(`${this.baseUrl}/runtime-approval/request`, {
|
|
344
|
+
method: 'POST',
|
|
345
|
+
headers: this.authHeaders(),
|
|
346
|
+
body: JSON.stringify(options),
|
|
347
|
+
});
|
|
348
|
+
if (!res.ok)
|
|
349
|
+
throw new CanonApiError(res.status, await res.text());
|
|
350
|
+
return res.json();
|
|
351
|
+
}
|
|
352
|
+
async consumeRuntimeApprovalResponse(options) {
|
|
353
|
+
const res = await fetch(`${this.baseUrl}/runtime-approval/consume`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: this.authHeaders(),
|
|
356
|
+
body: JSON.stringify(options),
|
|
357
|
+
});
|
|
358
|
+
if (!res.ok)
|
|
359
|
+
throw new CanonApiError(res.status, await res.text());
|
|
360
|
+
return res.json();
|
|
361
|
+
}
|
|
362
|
+
async updateRuntimeStatus(options) {
|
|
363
|
+
const res = await fetch(`${this.baseUrl}/runtime/status`, {
|
|
364
|
+
method: 'POST',
|
|
365
|
+
headers: this.authHeaders(),
|
|
366
|
+
body: JSON.stringify(options),
|
|
367
|
+
});
|
|
368
|
+
if (!res.ok)
|
|
369
|
+
throw new CanonApiError(res.status, await res.text());
|
|
370
|
+
}
|
|
342
371
|
// ── Static unauthenticated registration endpoints ────────────────────
|
|
343
372
|
static async register(baseUrl, body) {
|
|
344
373
|
const url = baseUrl || DEFAULT_BASE_URL;
|
package/dist/runtime-cards.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface RuntimeQuestionDefinition {
|
|
|
11
11
|
export interface ClaudeQuestionMetadata {
|
|
12
12
|
type: 'claude_question';
|
|
13
13
|
questionId: string;
|
|
14
|
+
/** Canon user who should answer this card. UI hint only; reply APIs still enforce authorization. */
|
|
15
|
+
responseUserId?: string;
|
|
14
16
|
questions: RuntimeQuestionDefinition[];
|
|
15
17
|
}
|
|
16
18
|
export interface ClaudeQuestionReplyMetadata {
|
|
@@ -21,6 +23,8 @@ export interface ClaudeQuestionReplyMetadata {
|
|
|
21
23
|
export interface PlanApprovalMetadata {
|
|
22
24
|
type: 'plan_approval';
|
|
23
25
|
planId: string;
|
|
26
|
+
/** Canon user who should answer this card. UI hint only; reply APIs still enforce authorization. */
|
|
27
|
+
responseUserId?: string;
|
|
24
28
|
title?: string;
|
|
25
29
|
summary?: string;
|
|
26
30
|
body?: string;
|
|
@@ -54,6 +58,8 @@ export interface RuntimeInputNativeMetadata {
|
|
|
54
58
|
export interface RuntimeInputRequestMetadata {
|
|
55
59
|
type: 'runtime_input_request';
|
|
56
60
|
inputId: string;
|
|
61
|
+
/** Canon user who should answer this card. UI hint only; reply APIs still enforce authorization. */
|
|
62
|
+
responseUserId?: string;
|
|
57
63
|
kind: RuntimeInputKind;
|
|
58
64
|
prompt: string;
|
|
59
65
|
title?: string;
|
|
@@ -78,7 +84,9 @@ export interface RuntimeInputOutcomeMetadata {
|
|
|
78
84
|
status: RuntimeInputResolutionStatus;
|
|
79
85
|
reason?: 'submitted' | 'cancelled' | 'timeout' | 'expired' | 'interrupted';
|
|
80
86
|
}
|
|
81
|
-
export declare function buildQuestionRequest(questionId: string, questions: RuntimeQuestionDefinition[]
|
|
87
|
+
export declare function buildQuestionRequest(questionId: string, questions: RuntimeQuestionDefinition[], options?: {
|
|
88
|
+
responseUserId?: string;
|
|
89
|
+
}): {
|
|
82
90
|
text: string;
|
|
83
91
|
metadata: ClaudeQuestionMetadata;
|
|
84
92
|
};
|
package/dist/runtime-cards.js
CHANGED
|
@@ -68,7 +68,7 @@ function assertNoSensitiveRuntimeInputPayload(value) {
|
|
|
68
68
|
&& !('secret' in value)
|
|
69
69
|
&& !('rawValue' in value);
|
|
70
70
|
}
|
|
71
|
-
export function buildQuestionRequest(questionId, questions) {
|
|
71
|
+
export function buildQuestionRequest(questionId, questions, options) {
|
|
72
72
|
const text = questions.length === 1
|
|
73
73
|
? questions[0]?.question || 'Question'
|
|
74
74
|
: `Please answer ${questions.length} questions.`;
|
|
@@ -77,6 +77,7 @@ export function buildQuestionRequest(questionId, questions) {
|
|
|
77
77
|
metadata: {
|
|
78
78
|
type: 'claude_question',
|
|
79
79
|
questionId,
|
|
80
|
+
...(options?.responseUserId ? { responseUserId: options.responseUserId.slice(0, 128) } : {}),
|
|
80
81
|
questions,
|
|
81
82
|
},
|
|
82
83
|
};
|
|
@@ -124,6 +125,7 @@ export function buildRuntimeInputRequest(inputId, input) {
|
|
|
124
125
|
metadata: {
|
|
125
126
|
type: 'runtime_input_request',
|
|
126
127
|
inputId,
|
|
128
|
+
...(input.responseUserId ? { responseUserId: input.responseUserId.slice(0, 128) } : {}),
|
|
127
129
|
kind: input.kind,
|
|
128
130
|
prompt: input.prompt.slice(0, 1000),
|
|
129
131
|
title: title.slice(0, 120),
|
|
@@ -171,6 +173,7 @@ export function parseRuntimeInputRequestMetadata(value) {
|
|
|
171
173
|
if (!assertNoSensitiveRuntimeInputPayload(value))
|
|
172
174
|
return null;
|
|
173
175
|
const inputId = normalizeString(value.inputId, 128);
|
|
176
|
+
const responseUserId = normalizeString(value.responseUserId, 128) ?? undefined;
|
|
174
177
|
const kind = normalizeRuntimeInputKind(value.kind);
|
|
175
178
|
const prompt = normalizeString(value.prompt, 1000);
|
|
176
179
|
const expiresAt = normalizeString(value.expiresAt, 128);
|
|
@@ -186,6 +189,7 @@ export function parseRuntimeInputRequestMetadata(value) {
|
|
|
186
189
|
return {
|
|
187
190
|
type: 'runtime_input_request',
|
|
188
191
|
inputId,
|
|
192
|
+
...(responseUserId ? { responseUserId } : {}),
|
|
189
193
|
kind,
|
|
190
194
|
prompt,
|
|
191
195
|
...(title ? { title } : {}),
|