@m6d/cortex-server 2.0.0 → 2.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 +10 -0
- package/contracts/interactive.ts +53 -0
- package/contracts/rich-text.ts +207 -0
- package/contracts/runtime.ts +58 -1
- package/contracts/wire.ts +32 -0
- package/dist/contracts/interactive.d.ts +39 -0
- package/dist/contracts/rich-text.d.ts +32 -0
- package/dist/contracts/runtime.d.ts +119 -7
- package/dist/contracts/wire.d.ts +30 -0
- package/dist/src/lib/adapters/database/index.d.ts +7 -0
- package/dist/src/lib/adapters/database/message-content.d.ts +2 -0
- package/dist/src/lib/adapters/database/mssql/index.d.ts +1 -0
- package/dist/src/lib/adapters/database/mssql/messages.d.ts +1 -0
- package/dist/src/lib/adapters/database/postgres/index.d.ts +1 -0
- package/dist/src/lib/adapters/database/postgres/messages.d.ts +1 -0
- package/dist/src/lib/ai/cc-runtime.d.ts +5 -4
- package/dist/src/lib/ai/interactive.d.ts +62 -0
- package/dist/src/lib/ai/tools/search-common.d.ts +1 -1
- package/dist/src/lib/ai/turn-tools.d.ts +14 -0
- package/dist/src/lib/cc/client.d.ts +27 -3
- package/dist/src/lib/cc/config-cache.d.ts +2 -2
- package/dist/src/lib/cc/registry.d.ts +14 -1
- package/dist/src/lib/cc/types.d.ts +1 -1
- package/package.json +5 -2
- package/src/lib/adapters/database/index.ts +12 -0
- package/src/lib/adapters/database/mssql/messages.ts +28 -1
- package/src/lib/adapters/database/postgres/messages.ts +22 -1
- package/src/lib/ai/cc-runtime.ts +16 -1
- package/src/lib/ai/index.ts +41 -5
- package/src/lib/ai/interactive.ts +364 -0
- package/src/lib/ai/tools/search-tools.tool.ts +6 -2
- package/src/lib/ai/turn-tools.ts +23 -0
- package/src/lib/cc/client.ts +5 -1
- package/src/lib/cc/format.ts +8 -9
- package/src/lib/cc/registry.ts +16 -2
- package/src/lib/cc/types.ts +1 -0
- package/src/lib/routes/chat.ts +23 -0
|
@@ -3,14 +3,44 @@ import { z } from "zod";
|
|
|
3
3
|
export declare const AGENT_SLUG_PATTERN: RegExp;
|
|
4
4
|
/** Runtime contract §5: tool names become `tools.<name>()` sandbox bindings. */
|
|
5
5
|
export declare const TOOL_NAME_PATTERN: RegExp;
|
|
6
|
-
export declare const PROMPT_VARIABLES: readonly ["userName", "channel", "locale"];
|
|
6
|
+
export declare const PROMPT_VARIABLES: readonly ["userName", "channel", "locale", "utcTime", "timezone"];
|
|
7
|
+
/**
|
|
8
|
+
* One entry per prompt variable, driving editor autocomplete, validation, and
|
|
9
|
+
* runtime substitution alike. Adding a variable = extending the tuple above
|
|
10
|
+
* plus one entry here (the compiler enforces the pair); the consumer embedding
|
|
11
|
+
* the agent supplies the value in its session/request context under the same
|
|
12
|
+
* key. Descriptions are plain English on purpose: variables are code-like
|
|
13
|
+
* identifiers shown in an LTR suggestion list, not localized UI copy.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PROMPT_VARIABLE_DEFINITIONS: {
|
|
16
|
+
userName: {
|
|
17
|
+
description: string;
|
|
18
|
+
example: string;
|
|
19
|
+
};
|
|
20
|
+
channel: {
|
|
21
|
+
description: string;
|
|
22
|
+
example: string;
|
|
23
|
+
};
|
|
24
|
+
locale: {
|
|
25
|
+
description: string;
|
|
26
|
+
example: string;
|
|
27
|
+
};
|
|
28
|
+
utcTime: {
|
|
29
|
+
description: string;
|
|
30
|
+
example: string;
|
|
31
|
+
};
|
|
32
|
+
timezone: {
|
|
33
|
+
description: string;
|
|
34
|
+
example: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
7
37
|
export declare const RUNTIME_ERROR_KINDS: readonly ["timeout", "upstream_4xx", "upstream_5xx", "schema_mismatch", "rate_limited", "not_found", "unauthorized", "invalid_request", "egress_blocked", "internal"];
|
|
8
38
|
/** The ways an execute output may disagree with the tool's declared schema. */
|
|
9
39
|
export declare const JSON_SCHEMA_ISSUE_KINDS: readonly ["missing", "unexpected", "type_mismatch"];
|
|
10
40
|
export declare const agentSlugSchema: z.ZodString;
|
|
11
41
|
export declare const localeSchema: z.ZodEnum<{
|
|
12
|
-
en: "en";
|
|
13
42
|
ar: "ar";
|
|
43
|
+
en: "en";
|
|
14
44
|
}>;
|
|
15
45
|
export declare const catalogVersionSchema: z.ZodString;
|
|
16
46
|
export declare const sharedShapeSchema: z.ZodObject<{
|
|
@@ -30,6 +60,25 @@ export declare const knowledgeChunkSchema: z.ZodObject<{
|
|
|
30
60
|
service: z.ZodNullable<z.ZodString>;
|
|
31
61
|
}, z.core.$strip>;
|
|
32
62
|
}, z.core.$strip>;
|
|
63
|
+
/**
|
|
64
|
+
* Runtime contract §5.4: present on signatures of `interactive` tools — flows
|
|
65
|
+
* the end user completes in an embedded surface inside the chat widget. The
|
|
66
|
+
* tool's endpoint fields act as the *initiate* call (returns the embed URL);
|
|
67
|
+
* `hasVerify` marks a second, server-trusted call that settles the result.
|
|
68
|
+
*/
|
|
69
|
+
export declare const toolInteractionSchema: z.ZodObject<{
|
|
70
|
+
surface: z.ZodEnum<{
|
|
71
|
+
inline: "inline";
|
|
72
|
+
modal: "modal";
|
|
73
|
+
}>;
|
|
74
|
+
embedOrigin: z.ZodURL;
|
|
75
|
+
hasVerify: z.ZodBoolean;
|
|
76
|
+
resultDelivery: z.ZodEnum<{
|
|
77
|
+
agent: "agent";
|
|
78
|
+
endpoint: "endpoint";
|
|
79
|
+
both: "both";
|
|
80
|
+
}>;
|
|
81
|
+
}, z.core.$strip>;
|
|
33
82
|
export declare const toolSignatureSchema: z.ZodObject<{
|
|
34
83
|
toolId: z.ZodUUID;
|
|
35
84
|
name: z.ZodString;
|
|
@@ -39,6 +88,20 @@ export declare const toolSignatureSchema: z.ZodObject<{
|
|
|
39
88
|
signature: z.ZodString;
|
|
40
89
|
score: z.ZodNullable<z.ZodNumber>;
|
|
41
90
|
pinned: z.ZodBoolean;
|
|
91
|
+
interaction: z.ZodOptional<z.ZodObject<{
|
|
92
|
+
surface: z.ZodEnum<{
|
|
93
|
+
inline: "inline";
|
|
94
|
+
modal: "modal";
|
|
95
|
+
}>;
|
|
96
|
+
embedOrigin: z.ZodURL;
|
|
97
|
+
hasVerify: z.ZodBoolean;
|
|
98
|
+
resultDelivery: z.ZodEnum<{
|
|
99
|
+
agent: "agent";
|
|
100
|
+
endpoint: "endpoint";
|
|
101
|
+
both: "both";
|
|
102
|
+
}>;
|
|
103
|
+
}, z.core.$strip>>;
|
|
104
|
+
inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
42
105
|
}, z.core.$strip>;
|
|
43
106
|
export declare const serviceCardSchema: z.ZodObject<{
|
|
44
107
|
serviceId: z.ZodUUID;
|
|
@@ -54,11 +117,13 @@ export declare const runtimeAgentConfigSchema: z.ZodObject<{
|
|
|
54
117
|
userName: "userName";
|
|
55
118
|
channel: "channel";
|
|
56
119
|
locale: "locale";
|
|
120
|
+
utcTime: "utcTime";
|
|
121
|
+
timezone: "timezone";
|
|
57
122
|
}>>;
|
|
58
123
|
catalogBlurb: z.ZodString;
|
|
59
124
|
defaultLocale: z.ZodEnum<{
|
|
60
|
-
en: "en";
|
|
61
125
|
ar: "ar";
|
|
126
|
+
en: "en";
|
|
62
127
|
}>;
|
|
63
128
|
metaTools: z.ZodObject<{
|
|
64
129
|
searchKnowledge: z.ZodBoolean;
|
|
@@ -76,8 +141,8 @@ export declare const runtimeAgentConfigSchema: z.ZodObject<{
|
|
|
76
141
|
export declare const resolveRequestSchema: z.ZodObject<{
|
|
77
142
|
query: z.ZodString;
|
|
78
143
|
locale: z.ZodOptional<z.ZodEnum<{
|
|
79
|
-
en: "en";
|
|
80
144
|
ar: "ar";
|
|
145
|
+
en: "en";
|
|
81
146
|
}>>;
|
|
82
147
|
hints: z.ZodOptional<z.ZodObject<{
|
|
83
148
|
threadId: z.ZodOptional<z.ZodString>;
|
|
@@ -95,8 +160,8 @@ export declare const resolveResponseSchema: z.ZodObject<{
|
|
|
95
160
|
resolveId: z.ZodUUID;
|
|
96
161
|
catalogVersion: z.ZodString;
|
|
97
162
|
locale: z.ZodEnum<{
|
|
98
|
-
en: "en";
|
|
99
163
|
ar: "ar";
|
|
164
|
+
en: "en";
|
|
100
165
|
}>;
|
|
101
166
|
knowledge: z.ZodArray<z.ZodObject<{
|
|
102
167
|
chunkId: z.ZodUUID;
|
|
@@ -120,6 +185,20 @@ export declare const resolveResponseSchema: z.ZodObject<{
|
|
|
120
185
|
signature: z.ZodString;
|
|
121
186
|
score: z.ZodNullable<z.ZodNumber>;
|
|
122
187
|
pinned: z.ZodBoolean;
|
|
188
|
+
interaction: z.ZodOptional<z.ZodObject<{
|
|
189
|
+
surface: z.ZodEnum<{
|
|
190
|
+
inline: "inline";
|
|
191
|
+
modal: "modal";
|
|
192
|
+
}>;
|
|
193
|
+
embedOrigin: z.ZodURL;
|
|
194
|
+
hasVerify: z.ZodBoolean;
|
|
195
|
+
resultDelivery: z.ZodEnum<{
|
|
196
|
+
agent: "agent";
|
|
197
|
+
endpoint: "endpoint";
|
|
198
|
+
both: "both";
|
|
199
|
+
}>;
|
|
200
|
+
}, z.core.$strip>>;
|
|
201
|
+
inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
123
202
|
}, z.core.$strip>>;
|
|
124
203
|
services: z.ZodArray<z.ZodObject<{
|
|
125
204
|
serviceId: z.ZodUUID;
|
|
@@ -142,8 +221,8 @@ export declare const searchRequestSchema: z.ZodObject<{
|
|
|
142
221
|
query: z.ZodString;
|
|
143
222
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
144
223
|
locale: z.ZodOptional<z.ZodEnum<{
|
|
145
|
-
en: "en";
|
|
146
224
|
ar: "ar";
|
|
225
|
+
en: "en";
|
|
147
226
|
}>>;
|
|
148
227
|
threadId: z.ZodOptional<z.ZodString>;
|
|
149
228
|
service: z.ZodOptional<z.ZodString>;
|
|
@@ -159,6 +238,20 @@ export declare const searchToolsResponseSchema: z.ZodObject<{
|
|
|
159
238
|
signature: z.ZodString;
|
|
160
239
|
score: z.ZodNullable<z.ZodNumber>;
|
|
161
240
|
pinned: z.ZodBoolean;
|
|
241
|
+
interaction: z.ZodOptional<z.ZodObject<{
|
|
242
|
+
surface: z.ZodEnum<{
|
|
243
|
+
inline: "inline";
|
|
244
|
+
modal: "modal";
|
|
245
|
+
}>;
|
|
246
|
+
embedOrigin: z.ZodURL;
|
|
247
|
+
hasVerify: z.ZodBoolean;
|
|
248
|
+
resultDelivery: z.ZodEnum<{
|
|
249
|
+
agent: "agent";
|
|
250
|
+
endpoint: "endpoint";
|
|
251
|
+
both: "both";
|
|
252
|
+
}>;
|
|
253
|
+
}, z.core.$strip>>;
|
|
254
|
+
inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
162
255
|
}, z.core.$strip>>;
|
|
163
256
|
sharedShapes: z.ZodArray<z.ZodObject<{
|
|
164
257
|
ref: z.ZodString;
|
|
@@ -183,6 +276,20 @@ export declare const searchServicesResponseSchema: z.ZodObject<{
|
|
|
183
276
|
signature: z.ZodString;
|
|
184
277
|
score: z.ZodNullable<z.ZodNumber>;
|
|
185
278
|
pinned: z.ZodBoolean;
|
|
279
|
+
interaction: z.ZodOptional<z.ZodObject<{
|
|
280
|
+
surface: z.ZodEnum<{
|
|
281
|
+
inline: "inline";
|
|
282
|
+
modal: "modal";
|
|
283
|
+
}>;
|
|
284
|
+
embedOrigin: z.ZodURL;
|
|
285
|
+
hasVerify: z.ZodBoolean;
|
|
286
|
+
resultDelivery: z.ZodEnum<{
|
|
287
|
+
agent: "agent";
|
|
288
|
+
endpoint: "endpoint";
|
|
289
|
+
both: "both";
|
|
290
|
+
}>;
|
|
291
|
+
}, z.core.$strip>>;
|
|
292
|
+
inputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
186
293
|
}, z.core.$strip>>;
|
|
187
294
|
sharedShapes: z.ZodArray<z.ZodObject<{
|
|
188
295
|
ref: z.ZodString;
|
|
@@ -207,12 +314,16 @@ export declare const searchKnowledgeResponseSchema: z.ZodObject<{
|
|
|
207
314
|
}, z.core.$strip>;
|
|
208
315
|
export declare const executeRequestSchema: z.ZodObject<{
|
|
209
316
|
input: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
317
|
+
phase: z.ZodOptional<z.ZodEnum<{
|
|
318
|
+
verify: "verify";
|
|
319
|
+
initiate: "initiate";
|
|
320
|
+
}>>;
|
|
210
321
|
context: z.ZodOptional<z.ZodObject<{
|
|
211
322
|
threadId: z.ZodOptional<z.ZodString>;
|
|
212
323
|
userId: z.ZodOptional<z.ZodString>;
|
|
213
324
|
locale: z.ZodOptional<z.ZodEnum<{
|
|
214
|
-
en: "en";
|
|
215
325
|
ar: "ar";
|
|
326
|
+
en: "en";
|
|
216
327
|
}>>;
|
|
217
328
|
}, z.core.$strip>>;
|
|
218
329
|
}, z.core.$strip>;
|
|
@@ -270,6 +381,7 @@ export declare const runtimeErrorSchema: z.ZodObject<{
|
|
|
270
381
|
upstreamStatus: z.ZodOptional<z.ZodNumber>;
|
|
271
382
|
}, z.core.$strip>;
|
|
272
383
|
}, z.core.$strip>;
|
|
384
|
+
export type ToolInteraction = z.infer<typeof toolInteractionSchema>;
|
|
273
385
|
export type RuntimeAgentConfig = z.infer<typeof runtimeAgentConfigSchema>;
|
|
274
386
|
export type ResolveRequest = z.input<typeof resolveRequestSchema>;
|
|
275
387
|
export type ResolveResponse = z.infer<typeof resolveResponseSchema>;
|
package/dist/contracts/wire.d.ts
CHANGED
|
@@ -50,12 +50,28 @@ export type TokenUsage = {
|
|
|
50
50
|
};
|
|
51
51
|
total: number;
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* How to launch one interactive tool: stamped by the server, per tool name, on
|
|
55
|
+
* the metadata of an assistant message that may carry its pending call. Kept in
|
|
56
|
+
* the message so the binding survives server restarts while a run is parked.
|
|
57
|
+
*/
|
|
58
|
+
export type InteractiveToolBinding = {
|
|
59
|
+
toolId: string;
|
|
60
|
+
surface: "inline" | "modal";
|
|
61
|
+
embedOrigin: string;
|
|
62
|
+
hasVerify: boolean;
|
|
63
|
+
resultDelivery: "agent" | "endpoint" | "both";
|
|
64
|
+
};
|
|
53
65
|
export type MessageMetadata = {
|
|
54
66
|
modelId?: string;
|
|
55
67
|
providerMetadata?: unknown;
|
|
56
68
|
isAborted?: boolean;
|
|
57
69
|
tokenUsage?: TokenUsage;
|
|
58
70
|
attachments?: AttachmentSummary[];
|
|
71
|
+
interactiveTools?: Record<string, InteractiveToolBinding>;
|
|
72
|
+
/** Per tool call: the trusted reference its initiate call returned. A
|
|
73
|
+
* verified completion must match it — the browser's word is never enough. */
|
|
74
|
+
interactiveReferences?: Record<string, string>;
|
|
59
75
|
};
|
|
60
76
|
/**
|
|
61
77
|
* A stored message as it is persisted and served. TanStack AI's `UIMessage`
|
|
@@ -71,6 +87,20 @@ export type CortexMessage<TPart = unknown> = {
|
|
|
71
87
|
parts: TPart[];
|
|
72
88
|
metadata?: MessageMetadata;
|
|
73
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Response of `POST /chat/:chatId/tools/:toolCallId/initiate`. `interactive:
|
|
92
|
+
* false` means the pending call is not an interactive CC tool — the widget
|
|
93
|
+
* falls back to its default rendering for the call. The payload is for the
|
|
94
|
+
* widget only; it never reaches the LLM.
|
|
95
|
+
*/
|
|
96
|
+
export type InteractiveInitiateResult = {
|
|
97
|
+
interactive: false;
|
|
98
|
+
} | {
|
|
99
|
+
interactive: true;
|
|
100
|
+
embedUrl: string;
|
|
101
|
+
surface: "inline" | "modal";
|
|
102
|
+
embedOrigin: string;
|
|
103
|
+
};
|
|
74
104
|
export type ThreadCreatedEvent = {
|
|
75
105
|
type: "thread:created";
|
|
76
106
|
payload: {
|
|
@@ -34,6 +34,13 @@ export type DatabaseAdapter = {
|
|
|
34
34
|
upsert(threadId: string, messages: ChatMessage[], options?: {
|
|
35
35
|
replaceAttachments?: boolean;
|
|
36
36
|
}): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Atomically merges one anchored interactive reference into the
|
|
39
|
+
* message's metadata as a single-statement JSON merge — safe under
|
|
40
|
+
* concurrent writers across replicas, unlike a read-modify-write of
|
|
41
|
+
* the whole message.
|
|
42
|
+
*/
|
|
43
|
+
mergeInteractiveReference(threadId: string, messageId: string, toolCallId: string, reference: string): Promise<void>;
|
|
37
44
|
};
|
|
38
45
|
llmRequests: {
|
|
39
46
|
insert(requests: {
|
|
@@ -16,6 +16,8 @@ export declare function withOwnedAttachments(messages: ChatMessage[], replaceAtt
|
|
|
16
16
|
providerMetadata?: unknown;
|
|
17
17
|
isAborted?: boolean;
|
|
18
18
|
tokenUsage?: import("../../types").TokenUsage;
|
|
19
|
+
interactiveTools?: Record<string, import("../..").InteractiveToolBinding>;
|
|
20
|
+
interactiveReferences?: Record<string, string>;
|
|
19
21
|
};
|
|
20
22
|
id: string;
|
|
21
23
|
role: "system" | "user" | "assistant";
|
|
@@ -76,6 +76,7 @@ export declare function createMssqlAdapter(connectionString: string, storage?: S
|
|
|
76
76
|
upsert(threadId: string, messagesToUpsert: import("../../../types").ChatMessage[], options?: {
|
|
77
77
|
replaceAttachments?: boolean;
|
|
78
78
|
}): Promise<void>;
|
|
79
|
+
mergeInteractiveReference(threadId: string, messageId: string, toolCallId: string, reference: string): Promise<void>;
|
|
79
80
|
};
|
|
80
81
|
llmRequests: {
|
|
81
82
|
insert(requests: {
|
|
@@ -34,4 +34,5 @@ export declare function createMessagesRepository(db: MssqlDb): {
|
|
|
34
34
|
upsert(threadId: string, messagesToUpsert: ChatMessage[], options?: {
|
|
35
35
|
replaceAttachments?: boolean;
|
|
36
36
|
}): Promise<void>;
|
|
37
|
+
mergeInteractiveReference(threadId: string, messageId: string, toolCallId: string, reference: string): Promise<void>;
|
|
37
38
|
};
|
|
@@ -76,6 +76,7 @@ export declare function createPostgresAdapter(connectionString: string, storage?
|
|
|
76
76
|
upsert(threadId: string, messagesToUpsert: import("../../../types").ChatMessage[], options?: {
|
|
77
77
|
replaceAttachments?: boolean;
|
|
78
78
|
}): Promise<void>;
|
|
79
|
+
mergeInteractiveReference(threadId: string, messageId: string, toolCallId: string, reference: string): Promise<void>;
|
|
79
80
|
};
|
|
80
81
|
llmRequests: {
|
|
81
82
|
insert(requests: {
|
|
@@ -34,4 +34,5 @@ export declare function createMessagesRepository(db: PostgresDb): {
|
|
|
34
34
|
upsert(threadId: string, messagesToUpsert: ChatMessage[], options?: {
|
|
35
35
|
replaceAttachments?: boolean;
|
|
36
36
|
}): Promise<void>;
|
|
37
|
+
mergeInteractiveReference(threadId: string, messageId: string, toolCallId: string, reference: string): Promise<void>;
|
|
37
38
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ResolvedCortexAgentConfig } from "../config";
|
|
2
2
|
import type { Thread } from "../types";
|
|
3
3
|
import type { ControlCenterClient } from "../cc/client";
|
|
4
|
-
import type { CcToolRegistry } from "../cc/registry";
|
|
4
|
+
import type { CcInteractiveTool, CcToolRegistry } from "../cc/registry";
|
|
5
5
|
import type { ResolveResponse, RuntimeAgentConfig } from "../cc/types";
|
|
6
6
|
type CcRuntimeOptions = {
|
|
7
7
|
ccClient: ControlCenterClient | null;
|
|
@@ -31,9 +31,9 @@ export declare function createCcRuntime(options: CcRuntimeOptions): {
|
|
|
31
31
|
config: {
|
|
32
32
|
agentId: string;
|
|
33
33
|
systemPrompt: string;
|
|
34
|
-
promptVariables: ("userName" | "channel" | "locale")[];
|
|
34
|
+
promptVariables: ("userName" | "channel" | "locale" | "utcTime" | "timezone")[];
|
|
35
35
|
catalogBlurb: string;
|
|
36
|
-
defaultLocale: "
|
|
36
|
+
defaultLocale: "ar" | "en";
|
|
37
37
|
metaTools: {
|
|
38
38
|
searchKnowledge: boolean;
|
|
39
39
|
searchTools: boolean;
|
|
@@ -48,10 +48,11 @@ export declare function createCcRuntime(options: CcRuntimeOptions): {
|
|
|
48
48
|
publishedAt: string;
|
|
49
49
|
};
|
|
50
50
|
registry: CcToolRegistry;
|
|
51
|
+
interactiveTools: Map<string, CcInteractiveTool>;
|
|
51
52
|
threadId: string;
|
|
52
53
|
turnKey: string;
|
|
53
54
|
userId: string;
|
|
54
|
-
locale: "
|
|
55
|
+
locale: "ar" | "en";
|
|
55
56
|
endUserToken: string;
|
|
56
57
|
abortSignal: AbortSignal;
|
|
57
58
|
getStepIndex: () => number;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ResolvedCortexAgentConfig } from "../config";
|
|
2
|
+
import type { ChatMessage, Thread } from "../types";
|
|
3
|
+
import { ControlCenterClient } from "../cc/client";
|
|
4
|
+
import type { CcRuntime } from "../cc/registry";
|
|
5
|
+
/**
|
|
6
|
+
* The wire-shape bindings stamped on the turn's final assistant message, so a
|
|
7
|
+
* parked interactive call can be initiated and verified after any restart.
|
|
8
|
+
* Only names that actually won declaration merging are stamped — a call to a
|
|
9
|
+
* colliding consumer-owned tool must never be treated as interactive. Undefined
|
|
10
|
+
* when nothing qualifies, keeping metadata lean.
|
|
11
|
+
*/
|
|
12
|
+
export declare function interactiveToolBindings(cc: CcRuntime | undefined, takenNames: ReadonlySet<string>): {
|
|
13
|
+
[k: string]: {
|
|
14
|
+
surface: "inline" | "modal";
|
|
15
|
+
embedOrigin: string;
|
|
16
|
+
hasVerify: boolean;
|
|
17
|
+
resultDelivery: "agent" | "endpoint" | "both";
|
|
18
|
+
toolId: string;
|
|
19
|
+
};
|
|
20
|
+
} | undefined;
|
|
21
|
+
/** Null when the agent has no Control Center — every caller treats that as "skip". */
|
|
22
|
+
export declare function createCcClient(config: ResolvedCortexAgentConfig): ControlCenterClient | null;
|
|
23
|
+
/**
|
|
24
|
+
* `POST /chat/:chatId/tools/:toolCallId/initiate` — runs the interactive
|
|
25
|
+
* tool's initiate call (the tool's own endpoint) and hands the widget its
|
|
26
|
+
* embed payload. The model never sees this payload. The idempotency key is
|
|
27
|
+
* pinned to the tool call, so a reload mid-flow reuses the created session
|
|
28
|
+
* instead of opening a second one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function initiateInteractiveTool(options: {
|
|
31
|
+
config: ResolvedCortexAgentConfig;
|
|
32
|
+
thread: Thread;
|
|
33
|
+
userId: string;
|
|
34
|
+
token: string;
|
|
35
|
+
toolCallId: string;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
interactive: false;
|
|
38
|
+
embedUrl?: undefined;
|
|
39
|
+
surface?: undefined;
|
|
40
|
+
embedOrigin?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
interactive: true;
|
|
43
|
+
embedUrl: string;
|
|
44
|
+
surface: "inline" | "modal";
|
|
45
|
+
embedOrigin: string;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* The trust boundary for interactive results: whatever the client submitted
|
|
49
|
+
* for a bound tool call is replaced wholesale — cancelled/failed pass through
|
|
50
|
+
* normalized, completed is re-established by the tool's verify call when it
|
|
51
|
+
* has one, and result delivery masking is applied before the model reads it.
|
|
52
|
+
* Runs before the answering turn is persisted, so both the stored transcript
|
|
53
|
+
* and the model context carry only settled results.
|
|
54
|
+
*/
|
|
55
|
+
export declare function settleInteractiveToolResults(options: {
|
|
56
|
+
messages: ChatMessage[];
|
|
57
|
+
thread: Thread;
|
|
58
|
+
userId: string;
|
|
59
|
+
token: string;
|
|
60
|
+
config: ResolvedCortexAgentConfig;
|
|
61
|
+
ccClient: ControlCenterClient | null;
|
|
62
|
+
}): Promise<void>;
|
|
@@ -35,5 +35,19 @@ export declare function createToolInstrumentation(options: TurnToolsOptions & {
|
|
|
35
35
|
onBeforeToolCall(_ctx: import("@tanstack/ai").ChatMiddlewareContext<unknown>, { toolName, toolCallId, args }: import("@tanstack/ai").ToolCallHookContext): void;
|
|
36
36
|
onAfterToolCall(_ctx: import("@tanstack/ai").ChatMiddlewareContext<unknown>, { toolName, toolCallId }: import("@tanstack/ai").AfterToolCallInfo): void;
|
|
37
37
|
} | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Interactive CC tools are declared like the request's client tools: no
|
|
40
|
+
* executor, so the call streams to the widget as a tool-call part and the run
|
|
41
|
+
* parks until the widget answers. The declaration carries the tool's published
|
|
42
|
+
* input schema — function-calling models ignore schemas described only in
|
|
43
|
+
* prose — with a permissive object as the fallback for older Control Centers.
|
|
44
|
+
* Names the consumer already claimed are skipped: those calls belong to the
|
|
45
|
+
* consumer's tool, and must neither be declared nor stamped as interactive.
|
|
46
|
+
*/
|
|
47
|
+
export declare function interactiveToolDeclarations(cc: CcRuntime | undefined, takenNames: ReadonlySet<string>): {
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
parameters: Record<string, unknown>;
|
|
51
|
+
}[];
|
|
38
52
|
export declare function hasDefaultAttachmentInterceptor(config: ResolvedCortexAgentConfig): boolean;
|
|
39
53
|
export {};
|
|
@@ -8,6 +8,9 @@ export type ExecuteOptions = {
|
|
|
8
8
|
turnKey: string;
|
|
9
9
|
stepIndex: number;
|
|
10
10
|
callIndex: number;
|
|
11
|
+
/** Overrides the composed key — used where stability must outlive the turn
|
|
12
|
+
* counters, e.g. one interactive initiate per tool call across reloads. */
|
|
13
|
+
idempotencyKey?: string;
|
|
11
14
|
timeoutMs?: number;
|
|
12
15
|
abortSignal?: AbortSignal;
|
|
13
16
|
};
|
|
@@ -30,9 +33,9 @@ export declare class ControlCenterClient {
|
|
|
30
33
|
readonly config: {
|
|
31
34
|
agentId: string;
|
|
32
35
|
systemPrompt: string;
|
|
33
|
-
promptVariables: ("userName" | "channel" | "locale")[];
|
|
36
|
+
promptVariables: ("userName" | "channel" | "locale" | "utcTime" | "timezone")[];
|
|
34
37
|
catalogBlurb: string;
|
|
35
|
-
defaultLocale: "
|
|
38
|
+
defaultLocale: "ar" | "en";
|
|
36
39
|
metaTools: {
|
|
37
40
|
searchKnowledge: boolean;
|
|
38
41
|
searchTools: boolean;
|
|
@@ -51,7 +54,7 @@ export declare class ControlCenterClient {
|
|
|
51
54
|
resolve(agentId: string, request: ResolveRequest, abortSignal?: AbortSignal): Promise<{
|
|
52
55
|
resolveId: string;
|
|
53
56
|
catalogVersion: string;
|
|
54
|
-
locale: "
|
|
57
|
+
locale: "ar" | "en";
|
|
55
58
|
knowledge: {
|
|
56
59
|
chunkId: string;
|
|
57
60
|
text: string;
|
|
@@ -74,6 +77,13 @@ export declare class ControlCenterClient {
|
|
|
74
77
|
signature: string;
|
|
75
78
|
score: number | null;
|
|
76
79
|
pinned: boolean;
|
|
80
|
+
interaction?: {
|
|
81
|
+
surface: "inline" | "modal";
|
|
82
|
+
embedOrigin: string;
|
|
83
|
+
hasVerify: boolean;
|
|
84
|
+
resultDelivery: "agent" | "endpoint" | "both";
|
|
85
|
+
} | undefined;
|
|
86
|
+
inputSchema?: Record<string, unknown> | undefined;
|
|
77
87
|
}[];
|
|
78
88
|
services: {
|
|
79
89
|
serviceId: string;
|
|
@@ -103,6 +113,13 @@ export declare class ControlCenterClient {
|
|
|
103
113
|
signature: string;
|
|
104
114
|
score: number | null;
|
|
105
115
|
pinned: boolean;
|
|
116
|
+
interaction?: {
|
|
117
|
+
surface: "inline" | "modal";
|
|
118
|
+
embedOrigin: string;
|
|
119
|
+
hasVerify: boolean;
|
|
120
|
+
resultDelivery: "agent" | "endpoint" | "both";
|
|
121
|
+
} | undefined;
|
|
122
|
+
inputSchema?: Record<string, unknown> | undefined;
|
|
106
123
|
}[];
|
|
107
124
|
sharedShapes: {
|
|
108
125
|
ref: string;
|
|
@@ -127,6 +144,13 @@ export declare class ControlCenterClient {
|
|
|
127
144
|
signature: string;
|
|
128
145
|
score: number | null;
|
|
129
146
|
pinned: boolean;
|
|
147
|
+
interaction?: {
|
|
148
|
+
surface: "inline" | "modal";
|
|
149
|
+
embedOrigin: string;
|
|
150
|
+
hasVerify: boolean;
|
|
151
|
+
resultDelivery: "agent" | "endpoint" | "both";
|
|
152
|
+
} | undefined;
|
|
153
|
+
inputSchema?: Record<string, unknown> | undefined;
|
|
130
154
|
}[];
|
|
131
155
|
sharedShapes: {
|
|
132
156
|
ref: string;
|
|
@@ -2,9 +2,9 @@ import type { ControlCenterClient } from "./client";
|
|
|
2
2
|
export declare function getControlCenterConfig(cc: ControlCenterClient, agentId: string, abortSignal?: AbortSignal): Promise<{
|
|
3
3
|
agentId: string;
|
|
4
4
|
systemPrompt: string;
|
|
5
|
-
promptVariables: ("userName" | "channel" | "locale")[];
|
|
5
|
+
promptVariables: ("userName" | "channel" | "locale" | "utcTime" | "timezone")[];
|
|
6
6
|
catalogBlurb: string;
|
|
7
|
-
defaultLocale: "
|
|
7
|
+
defaultLocale: "ar" | "en";
|
|
8
8
|
metaTools: {
|
|
9
9
|
searchKnowledge: boolean;
|
|
10
10
|
searchTools: boolean;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import type { ControlCenterClient } from "./client";
|
|
2
|
-
import type { RuntimeAgentConfig } from "./types";
|
|
2
|
+
import type { RuntimeAgentConfig, ToolInteraction } from "./types";
|
|
3
3
|
export type CcToolBinding = {
|
|
4
4
|
toolId: string;
|
|
5
5
|
readOnly: boolean;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* An interactive tool resolved for this turn. Declared to the model as a real
|
|
9
|
+
* client-executed tool — never a sandbox binding — so its call streams to the
|
|
10
|
+
* widget as a tool-call part and parks the run until the flow settles.
|
|
11
|
+
*/
|
|
12
|
+
export type CcInteractiveTool = {
|
|
13
|
+
toolId: string;
|
|
14
|
+
signature: string;
|
|
15
|
+
interaction: ToolInteraction;
|
|
16
|
+
inputSchema?: Record<string, unknown>;
|
|
17
|
+
};
|
|
7
18
|
/**
|
|
8
19
|
* Turn-scoped and mutable: seeded from /resolve, extended by searchTools
|
|
9
20
|
* mid-turn so a tool discovered at step 3 is callable at step 4.
|
|
@@ -13,6 +24,7 @@ export declare function registerCcTools(registry: CcToolRegistry, tools: {
|
|
|
13
24
|
name: string;
|
|
14
25
|
toolId: string;
|
|
15
26
|
readOnly: boolean;
|
|
27
|
+
interaction?: ToolInteraction;
|
|
16
28
|
}[]): void;
|
|
17
29
|
export declare function recordRecentCcTool(threadId: string, name: string): void;
|
|
18
30
|
export declare function getRecentCcTools(threadId: string): string[];
|
|
@@ -22,6 +34,7 @@ export type CcRuntime = {
|
|
|
22
34
|
agentId: string;
|
|
23
35
|
config: RuntimeAgentConfig;
|
|
24
36
|
registry: CcToolRegistry;
|
|
37
|
+
interactiveTools: Map<string, CcInteractiveTool>;
|
|
25
38
|
threadId: string;
|
|
26
39
|
turnKey: string;
|
|
27
40
|
userId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from "zod";
|
|
2
|
-
export { AGENT_SLUG_PATTERN, executeResponseSchema, resolveResponseSchema, runtimeAgentConfigSchema, searchKnowledgeResponseSchema, searchServicesResponseSchema, searchToolsResponseSchema, type ExecuteRequest, type ResolveRequest, type ResolveResponse, type RuntimeAgentConfig, type SearchRequest, } from "../../../contracts/runtime";
|
|
2
|
+
export { AGENT_SLUG_PATTERN, executeResponseSchema, resolveResponseSchema, runtimeAgentConfigSchema, searchKnowledgeResponseSchema, searchServicesResponseSchema, searchToolsResponseSchema, type ExecuteRequest, type ResolveRequest, type ResolveResponse, type RuntimeAgentConfig, type SearchRequest, type ToolInteraction, } from "../../../contracts/runtime";
|
|
3
3
|
/**
|
|
4
4
|
* The contract's error envelope, hardened for this consumer: a newer runtime may
|
|
5
5
|
* answer with an error kind this build has never heard of, which must degrade to a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m6d/cortex-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Reusable AI agent chat server library for Hono + Bun",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -74,5 +74,8 @@
|
|
|
74
74
|
},
|
|
75
75
|
"publishConfig": {
|
|
76
76
|
"access": "public"
|
|
77
|
-
}
|
|
77
|
+
},
|
|
78
|
+
"releaseWatchPaths": [
|
|
79
|
+
"internal/contracts"
|
|
80
|
+
]
|
|
78
81
|
}
|
|
@@ -42,6 +42,18 @@ export type DatabaseAdapter = {
|
|
|
42
42
|
messages: ChatMessage[],
|
|
43
43
|
options?: { replaceAttachments?: boolean },
|
|
44
44
|
): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Atomically merges one anchored interactive reference into the
|
|
47
|
+
* message's metadata as a single-statement JSON merge — safe under
|
|
48
|
+
* concurrent writers across replicas, unlike a read-modify-write of
|
|
49
|
+
* the whole message.
|
|
50
|
+
*/
|
|
51
|
+
mergeInteractiveReference(
|
|
52
|
+
threadId: string,
|
|
53
|
+
messageId: string,
|
|
54
|
+
toolCallId: string,
|
|
55
|
+
reference: string,
|
|
56
|
+
): Promise<void>;
|
|
45
57
|
};
|
|
46
58
|
llmRequests: {
|
|
47
59
|
insert(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// fallow-ignore-file duplicate-export -- mssql twin of the postgres repository, same factory name by design
|
|
2
|
-
import { and, desc, eq, getColumns, inArray, type InferInsertModel } from "drizzle-orm";
|
|
2
|
+
import { and, desc, eq, getColumns, inArray, sql, type InferInsertModel } from "drizzle-orm";
|
|
3
3
|
import { threads, messages } from "@/db/schema.mssql";
|
|
4
4
|
import type { ChatMessage } from "@/types";
|
|
5
5
|
import type { DatabaseAdapter } from "@/adapters/database/index";
|
|
@@ -88,5 +88,32 @@ export function createMessagesRepository(db: MssqlDb) {
|
|
|
88
88
|
.execute();
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
|
+
|
|
92
|
+
async mergeInteractiveReference(threadId, messageId, toolCallId, reference) {
|
|
93
|
+
// JSON path keys are quoted strings; the id lands inside one.
|
|
94
|
+
const escapedKey = toolCallId.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
95
|
+
const anchorPath = `lax $.metadata.interactiveReferences."${escapedKey}"`;
|
|
96
|
+
await db
|
|
97
|
+
.update(messages)
|
|
98
|
+
.set({
|
|
99
|
+
// All JSON_QUERY reads see the row's pre-update value, so
|
|
100
|
+
// the statement is one atomic merge.
|
|
101
|
+
content: sql`JSON_MODIFY(
|
|
102
|
+
JSON_MODIFY(
|
|
103
|
+
JSON_MODIFY(
|
|
104
|
+
${messages.content},
|
|
105
|
+
'lax $.metadata',
|
|
106
|
+
JSON_QUERY(COALESCE(JSON_QUERY(${messages.content}, '$.metadata'), '{}'))
|
|
107
|
+
),
|
|
108
|
+
'lax $.metadata.interactiveReferences',
|
|
109
|
+
JSON_QUERY(COALESCE(JSON_QUERY(${messages.content}, '$.metadata.interactiveReferences'), '{}'))
|
|
110
|
+
),
|
|
111
|
+
${anchorPath},
|
|
112
|
+
${reference}
|
|
113
|
+
)`,
|
|
114
|
+
})
|
|
115
|
+
.where(and(eq(messages.id, messageId), eq(messages.threadId, threadId)))
|
|
116
|
+
.execute();
|
|
117
|
+
},
|
|
91
118
|
} satisfies DatabaseAdapter["messages"];
|
|
92
119
|
}
|