@axiom-lattice/protocols 4.10.0 → 5.0.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +113 -0
- package/dist/index.d.mts +506 -44
- package/dist/index.d.ts +506 -44
- package/dist/index.js +156 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +142 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AgentInterruptProtocol.ts +15 -0
- package/src/AgentLatticeProtocol.ts +0 -49
- package/src/AgentWebAppRuntimeProtocol.ts +2 -0
- package/src/ChannelAdapterProtocol.ts +17 -0
- package/src/ChannelReplyJobProtocol.ts +184 -0
- package/src/CheckpointStorageProtocol.ts +98 -0
- package/src/MessageProtocol.ts +8 -0
- package/src/ModelProviderProtocol.ts +65 -0
- package/src/RunCancelProtocol.ts +76 -0
- package/src/StoreProtocol.ts +181 -0
- package/src/ThreadStoreProtocol.ts +24 -0
- package/src/VectorStoreProviderProtocol.ts +9 -0
- package/src/__tests__/RunCancelProtocol.test.ts +148 -0
- package/src/__tests__/agent-web-app-types.test.ts +4 -0
- package/src/index.ts +5 -0
package/package.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized human-in-the-loop interrupt exposed by the Agent Server surface.
|
|
3
|
+
*/
|
|
4
|
+
export interface AgentInterrupt {
|
|
5
|
+
/** Stable id derived from the LangGraph interrupt id or task/index. */
|
|
6
|
+
interruptId: string;
|
|
7
|
+
/** Normalized interrupt kind. */
|
|
8
|
+
type: "clarify" | "task_review" | "custom";
|
|
9
|
+
/** Thread whose graph is interrupted. */
|
|
10
|
+
threadId: string;
|
|
11
|
+
/** Task id that owns the interrupt, when available. */
|
|
12
|
+
taskId?: string;
|
|
13
|
+
/** Raw or normalized interrupt payload. */
|
|
14
|
+
payload: unknown;
|
|
15
|
+
}
|
|
@@ -14,7 +14,6 @@ import { BaseLatticeProtocol } from "./BaseLatticeProtocol";
|
|
|
14
14
|
export enum AgentType {
|
|
15
15
|
REACT = "react",
|
|
16
16
|
DEEP_AGENT = "deep_agent",
|
|
17
|
-
TEAM = "team",
|
|
18
17
|
PROCESSING = "processing",
|
|
19
18
|
/** Remote A2A agent — delegates to an external A2A-compatible server */
|
|
20
19
|
A2A_REMOTE = "a2a_remote",
|
|
@@ -203,52 +202,6 @@ export interface ProcessingAgentConfig extends BaseAgentConfig {
|
|
|
203
202
|
internalSubAgents?: AgentConfig[];
|
|
204
203
|
}
|
|
205
204
|
|
|
206
|
-
/**
|
|
207
|
-
* Team teammate configuration -- describes an available teammate.
|
|
208
|
-
*/
|
|
209
|
-
export interface TeamTeammateConfig {
|
|
210
|
-
/** Unique name for this teammate (used as agent ID) */
|
|
211
|
-
name: string;
|
|
212
|
-
/** Role category (e.g. "research", "writing", "review") */
|
|
213
|
-
role: string;
|
|
214
|
-
/** Human-readable description of what this teammate does */
|
|
215
|
-
description: string;
|
|
216
|
-
/** Tool keys this teammate has access to */
|
|
217
|
-
tools?: string[];
|
|
218
|
-
/** Custom system prompt for this teammate */
|
|
219
|
-
prompt?: string;
|
|
220
|
-
/** Model key override for this teammate */
|
|
221
|
-
modelKey?: string;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* TEAM agent configuration -- a team lead that dynamically creates teammates.
|
|
226
|
-
* Teammates are created on-the-fly from create_team tool input (name, role, description).
|
|
227
|
-
*/
|
|
228
|
-
export interface TeamAgentConfig extends BaseAgentConfig {
|
|
229
|
-
type: AgentType.TEAM;
|
|
230
|
-
/** Tool keys available to the team lead */
|
|
231
|
-
tools?: string[];
|
|
232
|
-
/** Maximum number of teammates running concurrently */
|
|
233
|
-
maxConcurrency?: number;
|
|
234
|
-
/**
|
|
235
|
-
* Schedule lattice key for polling task list / mailbox.
|
|
236
|
-
* When set, teammates use ScheduleLattice for periodic polling instead of event-driven wait.
|
|
237
|
-
*/
|
|
238
|
-
scheduleLatticeKey?: string;
|
|
239
|
-
/** Poll interval in ms when using schedule lattice (default: 5000) */
|
|
240
|
-
pollIntervalMs?: number;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Type guard to check if config is TeamAgentConfig
|
|
245
|
-
*/
|
|
246
|
-
export function isTeamAgentConfig(
|
|
247
|
-
config: AgentConfig
|
|
248
|
-
): config is TeamAgentConfig {
|
|
249
|
-
return config.type === AgentType.TEAM;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
205
|
// ─── A2A_REMOTE Agent ──────────────────────────────────────────────────────
|
|
253
206
|
|
|
254
207
|
export interface LocalRuntimeConfig {
|
|
@@ -361,7 +314,6 @@ export function isWorkflowAgentConfig(
|
|
|
361
314
|
export type AgentConfig =
|
|
362
315
|
| ReactAgentConfig
|
|
363
316
|
| DeepAgentConfig
|
|
364
|
-
| TeamAgentConfig
|
|
365
317
|
| ProcessingAgentConfig
|
|
366
318
|
| A2ARemoteAgentConfig
|
|
367
319
|
| WorkflowAgentConfig
|
|
@@ -372,7 +324,6 @@ export type AgentConfig =
|
|
|
372
324
|
export type AgentConfigWithTools =
|
|
373
325
|
| ReactAgentConfig
|
|
374
326
|
| DeepAgentConfig
|
|
375
|
-
| TeamAgentConfig
|
|
376
327
|
| ProcessingAgentConfig
|
|
377
328
|
| A2ARemoteAgentConfig
|
|
378
329
|
| WorkflowAgentConfig
|
|
@@ -54,6 +54,13 @@ export interface OutboundStreamChunk {
|
|
|
54
54
|
text: string;
|
|
55
55
|
/** 是否结束(最后一次刷新必须为 `true`)。 */
|
|
56
56
|
done: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* 结束原因;仅对 `done: true` 有意义。未提供视为正常完成。
|
|
59
|
+
*
|
|
60
|
+
* 适配器可据此决定是否发送运行中途产生的附加内容(例如企业微信卡片):
|
|
61
|
+
* 失败/中断/超时的轮次不应把半成品当成正常结果展示。
|
|
62
|
+
*/
|
|
63
|
+
reason?: "completed" | "failed" | "interrupted" | "timeout";
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
export interface DispatchResult {
|
|
@@ -80,6 +87,16 @@ export type MessageMiddleware = (
|
|
|
80
87
|
export interface ChannelAdapter<TConfig = unknown> {
|
|
81
88
|
readonly channel: string;
|
|
82
89
|
readonly configSchema: z.ZodSchema<TConfig>;
|
|
90
|
+
/**
|
|
91
|
+
* Whether `sendReply` is safe to call from any Gateway replica.
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* Only adapters whose reply transport is stateless/HTTP (for example Lark)
|
|
95
|
+
* may set this. Connection-bound adapters (WeCom/WeChat/Room) leave it
|
|
96
|
+
* undefined: their replies depend on process-local sockets or context tokens,
|
|
97
|
+
* so the durable cross-replica reply path must not claim their jobs.
|
|
98
|
+
*/
|
|
99
|
+
readonly durableReply?: boolean;
|
|
83
100
|
receive(
|
|
84
101
|
rawPayload: unknown,
|
|
85
102
|
installation: ChannelInstallation,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/** Lifecycle of one durable channel reply delivery attempt. */
|
|
2
|
+
export type ChannelReplyJobStatus = "pending" | "delivered" | "failed";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A durable, cross-replica channel reply job.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* The row is keyed by `runId` but always read/written inside a tenant scope.
|
|
9
|
+
* Claiming is a lease-based CAS: only a pending, due row with no live lease can
|
|
10
|
+
* be claimed, and terminal writes must present the current lease owner.
|
|
11
|
+
*/
|
|
12
|
+
export interface ChannelReplyJob {
|
|
13
|
+
runId: string;
|
|
14
|
+
tenantId: string;
|
|
15
|
+
assistantId: string;
|
|
16
|
+
threadId: string;
|
|
17
|
+
adapterChannel: string;
|
|
18
|
+
channelInstallationId: string;
|
|
19
|
+
replyTarget: Record<string, unknown>;
|
|
20
|
+
inputMessageId: string;
|
|
21
|
+
/**
|
|
22
|
+
* Legacy chunk-buffer cursor captured before the run was created.
|
|
23
|
+
*
|
|
24
|
+
* @remarks Retained for schema compatibility. The durable dispatcher now
|
|
25
|
+
* reads final text from the thread checkpoint and does not use this field.
|
|
26
|
+
*/
|
|
27
|
+
streamCursor?: string | null;
|
|
28
|
+
status: ChannelReplyJobStatus;
|
|
29
|
+
attempts: number;
|
|
30
|
+
/** Epoch milliseconds. */
|
|
31
|
+
availableAt: number;
|
|
32
|
+
leaseOwner: string | null;
|
|
33
|
+
/** Epoch milliseconds. */
|
|
34
|
+
leaseExpiresAt: number | null;
|
|
35
|
+
lastError: string | null;
|
|
36
|
+
outputMessageId: string | null;
|
|
37
|
+
/** Epoch milliseconds. */
|
|
38
|
+
createdAt: number;
|
|
39
|
+
/** Epoch milliseconds. */
|
|
40
|
+
updatedAt: number;
|
|
41
|
+
/** Epoch milliseconds. */
|
|
42
|
+
deliveredAt: number | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Fields accepted when enqueuing a reply job. Status/lease/output fields are assigned by the store. */
|
|
46
|
+
export interface CreateChannelReplyJobInput {
|
|
47
|
+
runId: string;
|
|
48
|
+
tenantId: string;
|
|
49
|
+
assistantId: string;
|
|
50
|
+
threadId: string;
|
|
51
|
+
adapterChannel: string;
|
|
52
|
+
channelInstallationId: string;
|
|
53
|
+
replyTarget: Record<string, unknown>;
|
|
54
|
+
inputMessageId: string;
|
|
55
|
+
/**
|
|
56
|
+
* Legacy chunk-buffer cursor captured before the run was created.
|
|
57
|
+
*
|
|
58
|
+
* @remarks Retained for schema compatibility. The durable dispatcher now
|
|
59
|
+
* reads final text from the thread checkpoint and does not use this field.
|
|
60
|
+
*/
|
|
61
|
+
streamCursor?: string | null;
|
|
62
|
+
/** Earliest epoch-ms time the job may be claimed. */
|
|
63
|
+
availableAt: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Persistence boundary for durable channel reply jobs. */
|
|
67
|
+
export interface ChannelReplyJobStore {
|
|
68
|
+
/** Creates a pending job. */
|
|
69
|
+
create(input: CreateChannelReplyJobInput): Promise<ChannelReplyJob>;
|
|
70
|
+
/** Reads one job within a tenant scope. */
|
|
71
|
+
get(tenantId: string, runId: string): Promise<ChannelReplyJob | null>;
|
|
72
|
+
/** Lists claimable pending jobs that are due at `now`, oldest first. */
|
|
73
|
+
listPending(now: number, limit?: number): Promise<ChannelReplyJob[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Atomically leases a pending, due job that is unleased or has an expired
|
|
76
|
+
* lease. Returns the claimed job, or `null` when another owner won.
|
|
77
|
+
*/
|
|
78
|
+
claim(
|
|
79
|
+
tenantId: string,
|
|
80
|
+
runId: string,
|
|
81
|
+
owner: string,
|
|
82
|
+
leaseMs: number,
|
|
83
|
+
now?: number,
|
|
84
|
+
): Promise<ChannelReplyJob | null>;
|
|
85
|
+
/** Marks a job delivered when `owner` still holds the lease. */
|
|
86
|
+
markDelivered(
|
|
87
|
+
tenantId: string,
|
|
88
|
+
runId: string,
|
|
89
|
+
owner: string,
|
|
90
|
+
outputMessageId?: string | null,
|
|
91
|
+
now?: number,
|
|
92
|
+
): Promise<ChannelReplyJob | null>;
|
|
93
|
+
/** Returns a job to pending with an incremented attempt count. */
|
|
94
|
+
reschedule(
|
|
95
|
+
tenantId: string,
|
|
96
|
+
runId: string,
|
|
97
|
+
owner: string,
|
|
98
|
+
error: string,
|
|
99
|
+
availableAt: number,
|
|
100
|
+
now?: number,
|
|
101
|
+
): Promise<ChannelReplyJob | null>;
|
|
102
|
+
/** Marks a job terminally failed. */
|
|
103
|
+
markFailed(
|
|
104
|
+
tenantId: string,
|
|
105
|
+
runId: string,
|
|
106
|
+
owner: string,
|
|
107
|
+
error: string,
|
|
108
|
+
now?: number,
|
|
109
|
+
): Promise<ChannelReplyJob | null>;
|
|
110
|
+
/** Deletes one job. Returns whether a row was removed. */
|
|
111
|
+
delete(tenantId: string, runId: string): Promise<boolean>;
|
|
112
|
+
/**
|
|
113
|
+
* Deletes up to `limit` terminal (`delivered`/`failed`) jobs whose last update
|
|
114
|
+
* is strictly older than `before`.
|
|
115
|
+
*
|
|
116
|
+
* @param before Epoch milliseconds retention cutoff.
|
|
117
|
+
* @param limit Maximum rows to prune in this batch (normalized 1..200).
|
|
118
|
+
* @returns Number of rows removed.
|
|
119
|
+
*/
|
|
120
|
+
deleteTerminalBefore(before: number, limit?: number): Promise<number>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Smallest accepted page size for {@link ChannelReplyJobStore.listPending}. */
|
|
124
|
+
export const CHANNEL_REPLY_JOB_LIST_LIMIT_MIN = 1;
|
|
125
|
+
/** Largest accepted page size for {@link ChannelReplyJobStore.listPending}. */
|
|
126
|
+
export const CHANNEL_REPLY_JOB_LIST_LIMIT_MAX = 200;
|
|
127
|
+
/** Default page size for {@link ChannelReplyJobStore.listPending}. */
|
|
128
|
+
export const CHANNEL_REPLY_JOB_LIST_LIMIT_DEFAULT = 100;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Normalizes a caller-supplied page size into the shared 1..200 range.
|
|
132
|
+
*
|
|
133
|
+
* @remarks Zero and negative values clamp to 1; non-finite values fall back to
|
|
134
|
+
* the default. Stores must use this helper so InMemory and PostgreSQL agree.
|
|
135
|
+
*/
|
|
136
|
+
export function normalizeChannelReplyJobListLimit(limit?: number): number {
|
|
137
|
+
if (limit === undefined || !Number.isFinite(limit)) {
|
|
138
|
+
return CHANNEL_REPLY_JOB_LIST_LIMIT_DEFAULT;
|
|
139
|
+
}
|
|
140
|
+
return Math.min(
|
|
141
|
+
CHANNEL_REPLY_JOB_LIST_LIMIT_MAX,
|
|
142
|
+
Math.max(CHANNEL_REPLY_JOB_LIST_LIMIT_MIN, Math.trunc(limit)),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Rejects missing/blank required fields and malformed create payloads at runtime. */
|
|
147
|
+
export function assertValidCreateChannelReplyJobInput(
|
|
148
|
+
input: CreateChannelReplyJobInput,
|
|
149
|
+
): void {
|
|
150
|
+
if (input === null || typeof input !== "object") {
|
|
151
|
+
throw new Error("ChannelReplyJob create input is required");
|
|
152
|
+
}
|
|
153
|
+
const required: Array<[string, unknown]> = [
|
|
154
|
+
["runId", input.runId],
|
|
155
|
+
["tenantId", input.tenantId],
|
|
156
|
+
["assistantId", input.assistantId],
|
|
157
|
+
["threadId", input.threadId],
|
|
158
|
+
["adapterChannel", input.adapterChannel],
|
|
159
|
+
["channelInstallationId", input.channelInstallationId],
|
|
160
|
+
["inputMessageId", input.inputMessageId],
|
|
161
|
+
];
|
|
162
|
+
for (const [field, value] of required) {
|
|
163
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
164
|
+
throw new Error(`ChannelReplyJob requires a non-empty ${field}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (
|
|
168
|
+
typeof input.replyTarget !== "object" ||
|
|
169
|
+
input.replyTarget === null ||
|
|
170
|
+
Array.isArray(input.replyTarget)
|
|
171
|
+
) {
|
|
172
|
+
throw new Error("ChannelReplyJob requires a replyTarget object");
|
|
173
|
+
}
|
|
174
|
+
if (typeof input.availableAt !== "number" || !Number.isFinite(input.availableAt)) {
|
|
175
|
+
throw new Error("ChannelReplyJob requires a finite availableAt");
|
|
176
|
+
}
|
|
177
|
+
if (
|
|
178
|
+
input.streamCursor !== undefined &&
|
|
179
|
+
input.streamCursor !== null &&
|
|
180
|
+
typeof input.streamCursor !== "string"
|
|
181
|
+
) {
|
|
182
|
+
throw new Error("ChannelReplyJob streamCursor must be a string");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint storage usage for a single thread.
|
|
3
|
+
*
|
|
4
|
+
* This is a read-only accounting view over the checkpointer's persisted rows.
|
|
5
|
+
*/
|
|
6
|
+
export interface ThreadCheckpointStorage {
|
|
7
|
+
/** Thread whose checkpoint rows were measured. */
|
|
8
|
+
threadId: string;
|
|
9
|
+
/** Sum of checkpoint, channel-value, and pending-write bytes. */
|
|
10
|
+
totalBytes: number;
|
|
11
|
+
/** Persisted checkpoint rows (`checkpoints` table). */
|
|
12
|
+
checkpoints: { count: number; bytes: number };
|
|
13
|
+
/** Persisted channel values (`checkpoint_blobs` table). */
|
|
14
|
+
channelValues: { count: number; bytes: number };
|
|
15
|
+
/** Persisted pending writes (`checkpoint_writes` table). */
|
|
16
|
+
pendingWrites: { count: number; bytes: number };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read-only checkpoint storage accounting.
|
|
21
|
+
*
|
|
22
|
+
* Implementations must not create, copy, prune, or mutate checkpoint rows.
|
|
23
|
+
*/
|
|
24
|
+
export interface CheckpointStorage {
|
|
25
|
+
/**
|
|
26
|
+
* Measure the checkpoint storage used by one thread.
|
|
27
|
+
*
|
|
28
|
+
* @param threadId - Thread id whose checkpoint rows should be measured.
|
|
29
|
+
* @returns Counts and byte sizes grouped by checkpoint storage category.
|
|
30
|
+
*/
|
|
31
|
+
get(threadId: string): Promise<ThreadCheckpointStorage>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Checkpoint maintenance operations over persisted thread checkpoints.
|
|
36
|
+
*
|
|
37
|
+
* Implementations copy, delete, and prune raw checkpoint rows for a thread.
|
|
38
|
+
* Callers must verify thread ownership and refuse to run while the thread is
|
|
39
|
+
* active.
|
|
40
|
+
*/
|
|
41
|
+
export interface CheckpointMaintenance {
|
|
42
|
+
/**
|
|
43
|
+
* Copy every checkpoint row from a source thread to an empty target thread.
|
|
44
|
+
*
|
|
45
|
+
* @param sourceThreadId - Thread whose rows are copied.
|
|
46
|
+
* @param targetThreadId - Empty thread that receives the rows.
|
|
47
|
+
* @returns Resolves when the copy commits; an empty source is a no-op.
|
|
48
|
+
* @throws When the target already has checkpoint rows.
|
|
49
|
+
*/
|
|
50
|
+
copyThread(sourceThreadId: string, targetThreadId: string): Promise<void>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Delete every checkpoint row for a thread.
|
|
54
|
+
*
|
|
55
|
+
* @param threadId - Thread whose checkpoint rows are deleted.
|
|
56
|
+
* @returns Resolves when the deletion commits.
|
|
57
|
+
*/
|
|
58
|
+
deleteThread(threadId: string): Promise<void>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Delete the checkpoints that descend from a base checkpoint.
|
|
62
|
+
*
|
|
63
|
+
* Preserves the base checkpoint and every checkpoint that is not descended
|
|
64
|
+
* from it. Descendants are discovered by following both same-namespace
|
|
65
|
+
* `parent_checkpoint_id` edges and cross-namespace `metadata.parents` edges
|
|
66
|
+
* (the `{ "<parent checkpoint_ns>": "<parent checkpoint id>" }` map that
|
|
67
|
+
* LangGraph writes for subgraph checkpoints), recursively across all
|
|
68
|
+
* namespaces. Checkpoint-id ordering is not used. LangGraph keys the first
|
|
69
|
+
* superstep's writes of a resumed execution to the parent/base checkpoint, so
|
|
70
|
+
* writes keyed to the base itself belong to the rolled-back run and are
|
|
71
|
+
* deleted with descendant writes. Channel blobs are removed only when no
|
|
72
|
+
* retained checkpoint references them.
|
|
73
|
+
*
|
|
74
|
+
* @param threadId - Thread whose later checkpoint history is deleted.
|
|
75
|
+
* @param baseCheckpointId - Checkpoint to retain as the new tip, or `null` to
|
|
76
|
+
* delete every checkpoint for the thread. An empty string is invalid and must
|
|
77
|
+
* never be treated as the `null` wildcard.
|
|
78
|
+
* @returns Resolves when the deletion commits; no descendants is a no-op.
|
|
79
|
+
* @throws When the base checkpoint does not exist on the thread. Callers must
|
|
80
|
+
* fail closed rather than deleting an unknown range.
|
|
81
|
+
* @remarks The operation is transactional and idempotent so rollback recovery
|
|
82
|
+
* can retry it.
|
|
83
|
+
*/
|
|
84
|
+
deleteAfter(threadId: string, baseCheckpointId: string | null): Promise<void>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Retain only the latest N checkpoints per checkpoint namespace.
|
|
88
|
+
*
|
|
89
|
+
* @param threadId - Thread whose checkpoint history is pruned.
|
|
90
|
+
* @param options - Retention options.
|
|
91
|
+
* @param options.keepLatest - Number of latest checkpoints to retain per namespace.
|
|
92
|
+
* @returns Counts of deleted checkpoint, blob, and write rows.
|
|
93
|
+
*/
|
|
94
|
+
pruneThread(
|
|
95
|
+
threadId: string,
|
|
96
|
+
options: { keepLatest: number },
|
|
97
|
+
): Promise<{ checkpoints: number; blobs: number; writes: number }>;
|
|
98
|
+
}
|
package/src/MessageProtocol.ts
CHANGED
|
@@ -114,6 +114,14 @@ export type MessageChunkType = typeof MessageChunkTypes[keyof typeof MessageChun
|
|
|
114
114
|
|
|
115
115
|
export interface MessageChunk {
|
|
116
116
|
type: MessageChunkType;
|
|
117
|
+
/**
|
|
118
|
+
* Shared append-log cursor for this chunk.
|
|
119
|
+
*
|
|
120
|
+
* Set by streaming transports when the chunk is read from a cursor-addressable
|
|
121
|
+
* log (for example Redis Streams) so reconnects can resume via SSE
|
|
122
|
+
* `Last-Event-ID`. Omitted by in-memory buffers configured without a log.
|
|
123
|
+
*/
|
|
124
|
+
cursor?: string;
|
|
117
125
|
data: {
|
|
118
126
|
id: string;
|
|
119
127
|
content?: string;
|
|
@@ -34,6 +34,21 @@ export type ModelProviderApiStyle = "chat-completions" | "responses";
|
|
|
34
34
|
*/
|
|
35
35
|
export type ModelProviderLlmProvider = LLMConfig["provider"];
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* What a model can do.
|
|
39
|
+
*
|
|
40
|
+
* Lives on the **model** row, not the provider: one provider is a set of
|
|
41
|
+
* credentials plus one base URL, and a single DashScope-compatible endpoint
|
|
42
|
+
* serves chat, embedding and speech at once. Putting this on the provider would
|
|
43
|
+
* force a tenant to create three providers pointing at the same address with
|
|
44
|
+
* the same secret.
|
|
45
|
+
*
|
|
46
|
+
* `"asr"` is speech-to-text; the runtime is the `stt` lattice, not
|
|
47
|
+
* `models`. The kinds are deliberately independent: each has its own lattice
|
|
48
|
+
* manager, its own loader guard, and its own client type.
|
|
49
|
+
*/
|
|
50
|
+
export type ModelKind = "chat" | "embedding" | "asr";
|
|
51
|
+
|
|
37
52
|
/**
|
|
38
53
|
* Build the tenant-scoped model key.
|
|
39
54
|
*
|
|
@@ -360,6 +375,14 @@ export interface ProviderModelInput extends DiscoveredModel {
|
|
|
360
375
|
* or carry `modelKwargs` the server needs, without duplicating the provider.
|
|
361
376
|
*/
|
|
362
377
|
extra?: Record<string, unknown>;
|
|
378
|
+
/**
|
|
379
|
+
* What this model does.
|
|
380
|
+
*
|
|
381
|
+
* Omitted means "keep the stored kind"; a brand-new row defaults to
|
|
382
|
+
* `"chat"`. Discovery cannot tell the kinds apart from `/v1/models`, so the
|
|
383
|
+
* admin sets it.
|
|
384
|
+
*/
|
|
385
|
+
kind?: ModelKind;
|
|
363
386
|
/**
|
|
364
387
|
* Whether this is the tenant's default model.
|
|
365
388
|
*
|
|
@@ -390,6 +413,12 @@ export interface ModelSelection {
|
|
|
390
413
|
* lose the config it already had.
|
|
391
414
|
*/
|
|
392
415
|
extra?: Record<string, unknown>;
|
|
416
|
+
/**
|
|
417
|
+
* What this model does.
|
|
418
|
+
*
|
|
419
|
+
* Omitted means "leave the stored kind alone", the same rule as `extra`.
|
|
420
|
+
*/
|
|
421
|
+
kind?: ModelKind;
|
|
393
422
|
/**
|
|
394
423
|
* Whether this is the tenant's default model.
|
|
395
424
|
*
|
|
@@ -424,6 +453,13 @@ export interface ModelProviderModelEntry {
|
|
|
424
453
|
*/
|
|
425
454
|
modelId: string;
|
|
426
455
|
|
|
456
|
+
/**
|
|
457
|
+
* What this model does — chat, embedding or speech-to-text.
|
|
458
|
+
*
|
|
459
|
+
* Rows created before this column existed are all `"chat"`.
|
|
460
|
+
*/
|
|
461
|
+
kind: ModelKind;
|
|
462
|
+
|
|
427
463
|
/**
|
|
428
464
|
* Optional human-readable name from the upstream
|
|
429
465
|
*/
|
|
@@ -576,6 +612,12 @@ export interface TenantModelPublicEntry {
|
|
|
576
612
|
*/
|
|
577
613
|
modelKey: string;
|
|
578
614
|
|
|
615
|
+
/**
|
|
616
|
+
* What this model does. Pickers filter on it: a chat selector must not offer
|
|
617
|
+
* an embedding model, and the collection picker must not offer a chat model.
|
|
618
|
+
*/
|
|
619
|
+
kind: ModelKind;
|
|
620
|
+
|
|
579
621
|
/**
|
|
580
622
|
* Optional human-readable name
|
|
581
623
|
*/
|
|
@@ -703,6 +745,21 @@ export interface ModelProviderProbeRequest {
|
|
|
703
745
|
* Wire protocol to speak. Defaults to `openai-compatible`.
|
|
704
746
|
*/
|
|
705
747
|
protocol?: ModelProviderWireProtocol;
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* What kind of model to probe.
|
|
751
|
+
*
|
|
752
|
+
* A model-list probe proves the address and the credential, not that a model
|
|
753
|
+
* can actually run. For embeddings the meaningful check is one inference
|
|
754
|
+
* request, which is why the caller names the model and the kind.
|
|
755
|
+
*/
|
|
756
|
+
kind?: ModelKind;
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Model id to probe. Required when `kind` is `"embedding"`: the probe sends
|
|
760
|
+
* one tiny input to that model and reports the resulting vector's dimension.
|
|
761
|
+
*/
|
|
762
|
+
modelId?: string;
|
|
706
763
|
}
|
|
707
764
|
|
|
708
765
|
/**
|
|
@@ -736,6 +793,14 @@ export interface ModelProviderProbeResult {
|
|
|
736
793
|
* errors rather than as an unreachable probe.
|
|
737
794
|
*/
|
|
738
795
|
error?: ModelProviderUpstreamErrorCode;
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Dimension of the vector an embedding probe got back.
|
|
799
|
+
*
|
|
800
|
+
* Absent for a model-list probe, and for an upstream that answered but
|
|
801
|
+
* returned no usable vector.
|
|
802
|
+
*/
|
|
803
|
+
dimensions?: number;
|
|
739
804
|
}
|
|
740
805
|
|
|
741
806
|
/**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Upper bound for every contract string. These payloads travel between
|
|
5
|
+
* replicas and are persisted with A2A tasks, so unbounded input is rejected
|
|
6
|
+
* at the shared boundary instead of relying on transport limits.
|
|
7
|
+
*/
|
|
8
|
+
const MAX_FIELD_LENGTH = 64 * 1024;
|
|
9
|
+
|
|
10
|
+
const boundedString = z.string().min(1).max(MAX_FIELD_LENGTH);
|
|
11
|
+
|
|
12
|
+
/** Terminal cancellation commands delivered to the replica that currently owns a run. */
|
|
13
|
+
const RUN_CANCEL_REASONS = ["abort", "a2a.cancel", "shutdown"] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A cancellation signal routed to the current owner of a run.
|
|
17
|
+
*
|
|
18
|
+
* `runId` is mandatory: a signal without it cannot unambiguously identify the
|
|
19
|
+
* execution to stop, so it is rejected rather than applied to the thread.
|
|
20
|
+
*/
|
|
21
|
+
export interface RunCancelSignal {
|
|
22
|
+
commandId: string;
|
|
23
|
+
tenantId: string;
|
|
24
|
+
assistantId: string;
|
|
25
|
+
threadId: string;
|
|
26
|
+
runId: string;
|
|
27
|
+
reason: "abort" | "a2a.cancel" | "shutdown";
|
|
28
|
+
createdAt: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Identity of a human-in-the-loop interrupt answer, used for distributed
|
|
33
|
+
* dedupe so multiple replicas settle the same answer at most once.
|
|
34
|
+
*/
|
|
35
|
+
export interface InterruptAnswerKey {
|
|
36
|
+
tenantId: string;
|
|
37
|
+
assistantId: string;
|
|
38
|
+
threadId: string;
|
|
39
|
+
interruptId: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Durable A2A event delivery cursor stored with the shared A2A task.
|
|
44
|
+
* `lastEventId` is the last event already handed to the client.
|
|
45
|
+
*/
|
|
46
|
+
export interface A2AEventCursor {
|
|
47
|
+
taskId: string;
|
|
48
|
+
lastEventId: string;
|
|
49
|
+
updatedAt: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Strict validator for {@link RunCancelSignal}. */
|
|
53
|
+
export const runCancelSignalSchema: z.ZodType<RunCancelSignal> = z.object({
|
|
54
|
+
commandId: boundedString,
|
|
55
|
+
tenantId: boundedString,
|
|
56
|
+
assistantId: boundedString,
|
|
57
|
+
threadId: boundedString,
|
|
58
|
+
runId: boundedString,
|
|
59
|
+
reason: z.enum(RUN_CANCEL_REASONS),
|
|
60
|
+
createdAt: boundedString,
|
|
61
|
+
}).strict();
|
|
62
|
+
|
|
63
|
+
/** Strict validator for {@link InterruptAnswerKey}. */
|
|
64
|
+
export const interruptAnswerKeySchema: z.ZodType<InterruptAnswerKey> = z.object({
|
|
65
|
+
tenantId: boundedString,
|
|
66
|
+
assistantId: boundedString,
|
|
67
|
+
threadId: boundedString,
|
|
68
|
+
interruptId: boundedString,
|
|
69
|
+
}).strict();
|
|
70
|
+
|
|
71
|
+
/** Strict validator for {@link A2AEventCursor}. */
|
|
72
|
+
export const a2aEventCursorSchema: z.ZodType<A2AEventCursor> = z.object({
|
|
73
|
+
taskId: boundedString,
|
|
74
|
+
lastEventId: boundedString,
|
|
75
|
+
updatedAt: boundedString,
|
|
76
|
+
}).strict();
|