@roll-agent/protocol 0.6.0 → 0.7.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 +6 -5
- package/dist/app-output.d.ts +86 -0
- package/dist/app-output.js +178 -0
- package/dist/index.d.ts +4498 -761
- package/dist/index.js +222 -23
- package/dist/schema/roll-runtime-protocol-v1.5.schema.json +6871 -0
- package/dist/schema/roll-runtime-protocol-v1.schema.json +561 -11
- package/fixtures/v1.4/invalid-attachment-stage-sha256-request.json +14 -0
- package/fixtures/v1.4/invalid-turn-start-too-many-attachments-request.json +24 -0
- package/fixtures/v1.4/valid-attachment-chunk-request.json +12 -0
- package/fixtures/v1.4/valid-attachment-commit-request.json +10 -0
- package/fixtures/v1.4/valid-attachment-commit-response.json +16 -0
- package/fixtures/v1.4/valid-attachment-release-request.json +10 -0
- package/fixtures/v1.4/valid-attachment-stage-request.json +14 -0
- package/fixtures/v1.4/valid-attachment-stage-response.json +8 -0
- package/fixtures/v1.4/valid-initialize-response.json +34 -0
- package/fixtures/v1.4/valid-runtime-durable-event-notification.json +20 -0
- package/fixtures/v1.4/valid-thread-snapshot-attachment-part-response.json +44 -0
- package/fixtures/v1.4/valid-turn-start-with-attachments-request.json +14 -0
- package/fixtures/v1.5/invalid-operation-result-get-extra-request.json +12 -0
- package/fixtures/v1.5/valid-initialize-response.json +35 -0
- package/fixtures/v1.5/valid-operation-result-get-request.json +9 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -86,22 +86,23 @@ function handleEvent(event: RuntimeEventEnvelopeV13): void {
|
|
|
86
86
|
|
|
87
87
|
- `@roll-agent/protocol/schema` 与 `@roll-agent/protocol/schema/latest`:最新版本
|
|
88
88
|
JSON Schema Draft 2020-12 根 Schema;
|
|
89
|
-
- `@roll-agent/protocol/schema/1.3`、`/1.2`、`/1.1`、`/1.0`:严格按协商版本隔离的 Schema;
|
|
89
|
+
- `@roll-agent/protocol/schema/1.4`、`/1.3`、`/1.2`、`/1.1`、`/1.0`:严格按协商版本隔离的 Schema;
|
|
90
|
+
- `@roll-agent/protocol/fixtures/v1.4/*`:Protocol 1.4 attachment / turn.start / snapshot fixtures;
|
|
90
91
|
- `@roll-agent/protocol/fixtures/v1.3/*`:Protocol 1.3 durable event/replay fixtures;
|
|
91
92
|
- `@roll-agent/protocol/fixtures/v1/*`:冻结的 1.1/1.0 跨语言有效/无效消息 fixtures。
|
|
92
93
|
- `@roll-agent/protocol/fixtures/v1.2/*`:Protocol 1.2 capability/interaction fixtures。
|
|
93
94
|
|
|
94
95
|
协议版本与 npm 包版本相互独立。`RUNTIME_PROTOCOL_VERSION` 表示这个包提供的最新 wire
|
|
95
96
|
schema,并不代表调用方已实现对应 Client 能力。当前支持顺序为
|
|
96
|
-
`["1.3", "1.2", "1.1", "1.0"]`。`initialize` 请求保持旧 strict 形状;协商到 `"1.2"
|
|
97
|
-
或 `"1.
|
|
97
|
+
`["1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]`。`initialize` 请求保持旧 strict 形状;协商到 `"1.2"`、
|
|
98
|
+
`"1.3"` 或 `"1.4"` 后,
|
|
98
99
|
Client 必须用 `client.capabilities.set` 提交单调 `revision` 与当前 Handler methods,Runtime
|
|
99
100
|
返回 registry 交集后才进入 interaction-ready。ACK 可以为空或为请求集的任意子集,顺序按
|
|
100
101
|
Runtime registry;Client 必须按集合语义处理。未知的未来 method 名可安全发送但不会被接受。
|
|
101
102
|
|
|
102
|
-
广告 `"1.3"` 同时声明 Client 能接收至少 `17 MiB` 的单个 Runtime→Client NDJSON 帧:
|
|
103
|
+
广告 `"1.4"` 或 `"1.3"` 同时声明 Client 能接收至少 `17 MiB` 的单个 Runtime→Client NDJSON 帧:
|
|
103
104
|
durable record 的绝对上限为 `16 MiB`,额外 1 MiB 留给 envelope 与 JSON-RPC 元数据。本地
|
|
104
|
-
入站预算低于 17 MiB 的 Client
|
|
105
|
+
入站预算低于 17 MiB 的 Client 必须同时省略 `"1.4"` 与 `"1.3"`。这不会扩大 Client→Runtime 出站额度;
|
|
105
106
|
初始化后的出站上限仍取本地预算与 Runtime 返回的 `limits.maxFrameBytes` 的较小值。
|
|
106
107
|
官方 Node 恢复管理器的默认 replay 暂存窗口为 10,000 条 / 32 MiB。
|
|
107
108
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
/** Independent of a renderer and of the lossy model/display projections. */
|
|
3
|
+
export declare const APP_OUTPUT_META_KEY = "roll/appOutput";
|
|
4
|
+
export declare const APP_OUTPUT_STATUS_META_KEY = "roll/appOutputStatus";
|
|
5
|
+
export declare const APP_OUTPUT_LIMITS: {
|
|
6
|
+
readonly resultBytes: number;
|
|
7
|
+
readonly schemaBytes: number;
|
|
8
|
+
readonly threadBytes: number;
|
|
9
|
+
readonly threadRecords: 2000;
|
|
10
|
+
readonly maxAgeMs: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const appOutputDeclarationSchema: z.ZodReadonly<z.ZodObject<{
|
|
13
|
+
schemaId: z.ZodString;
|
|
14
|
+
schemaVersion: z.ZodNumber;
|
|
15
|
+
remoteReadable: z.ZodDefault<z.ZodBoolean>;
|
|
16
|
+
}, z.core.$strict>>;
|
|
17
|
+
export type AppOutputDeclaration = z.infer<typeof appOutputDeclarationSchema>;
|
|
18
|
+
export declare const appOutputContractSchema: z.ZodReadonly<z.ZodObject<{
|
|
19
|
+
outputSchema: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
20
|
+
schemaId: z.ZodString;
|
|
21
|
+
schemaVersion: z.ZodNumber;
|
|
22
|
+
remoteReadable: z.ZodDefault<z.ZodBoolean>;
|
|
23
|
+
}, z.core.$strict>>;
|
|
24
|
+
export type AppOutputContract = z.infer<typeof appOutputContractSchema>;
|
|
25
|
+
export declare const APP_OUTPUT_UNAVAILABLE_STATUSES: readonly ["not_provided", "invalid", "too_large", "expired", "denied"];
|
|
26
|
+
export declare const appOutputRejectionSchema: z.ZodReadonly<z.ZodObject<{
|
|
27
|
+
status: z.ZodLiteral<"rejected">;
|
|
28
|
+
reason: z.ZodEnum<{
|
|
29
|
+
credential_field: "credential_field";
|
|
30
|
+
credential_value: "credential_value";
|
|
31
|
+
}>;
|
|
32
|
+
field: z.ZodOptional<z.ZodString>;
|
|
33
|
+
}, z.core.$strict>>;
|
|
34
|
+
export type AppOutputRejection = z.infer<typeof appOutputRejectionSchema>;
|
|
35
|
+
export declare const appOutputResultSchema: z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
36
|
+
status: z.ZodLiteral<"rejected">;
|
|
37
|
+
reason: z.ZodEnum<{
|
|
38
|
+
credential_field: "credential_field";
|
|
39
|
+
credential_value: "credential_value";
|
|
40
|
+
}>;
|
|
41
|
+
field: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, z.core.$strict>>, z.ZodObject<{
|
|
43
|
+
status: z.ZodLiteral<"available">;
|
|
44
|
+
schemaId: z.ZodString;
|
|
45
|
+
schemaVersion: z.ZodNumber;
|
|
46
|
+
remoteReadable: z.ZodBoolean;
|
|
47
|
+
data: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
48
|
+
fallbackText: z.ZodString;
|
|
49
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
50
|
+
status: z.ZodEnum<{
|
|
51
|
+
not_provided: "not_provided";
|
|
52
|
+
invalid: "invalid";
|
|
53
|
+
too_large: "too_large";
|
|
54
|
+
expired: "expired";
|
|
55
|
+
denied: "denied";
|
|
56
|
+
}>;
|
|
57
|
+
}, z.core.$strict>]>>;
|
|
58
|
+
export type AppOutputResult = z.infer<typeof appOutputResultSchema>;
|
|
59
|
+
export declare const appOutputDescriptorSchema: z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
60
|
+
status: z.ZodLiteral<"rejected">;
|
|
61
|
+
reason: z.ZodEnum<{
|
|
62
|
+
credential_field: "credential_field";
|
|
63
|
+
credential_value: "credential_value";
|
|
64
|
+
}>;
|
|
65
|
+
field: z.ZodOptional<z.ZodString>;
|
|
66
|
+
}, z.core.$strict>>, z.ZodObject<{
|
|
67
|
+
status: z.ZodLiteral<"available">;
|
|
68
|
+
schemaId: z.ZodString;
|
|
69
|
+
schemaVersion: z.ZodNumber;
|
|
70
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
71
|
+
status: z.ZodEnum<{
|
|
72
|
+
not_provided: "not_provided";
|
|
73
|
+
invalid: "invalid";
|
|
74
|
+
too_large: "too_large";
|
|
75
|
+
expired: "expired";
|
|
76
|
+
denied: "denied";
|
|
77
|
+
}>;
|
|
78
|
+
}, z.core.$strict>]>>;
|
|
79
|
+
export type AppOutputDescriptor = z.infer<typeof appOutputDescriptorSchema>;
|
|
80
|
+
export declare function describeAppOutput(result: AppOutputResult): AppOutputDescriptor;
|
|
81
|
+
/** Validate portable, self-contained contracts without dereferencing network resources. */
|
|
82
|
+
export declare function validateAppOutputContract(value: unknown): AppOutputContract;
|
|
83
|
+
/** MCP uses isError for invalid output even when execution has completed. */
|
|
84
|
+
export declare const COMPLETED_APP_OUTPUT_STATUSES: readonly ["invalid", "too_large"];
|
|
85
|
+
export type CompletedAppOutputStatus = (typeof COMPLETED_APP_OUTPUT_STATUSES)[number];
|
|
86
|
+
export declare function getCompletedAppOutputStatus(value: unknown): CompletedAppOutputStatus | undefined;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
/** Independent of a renderer and of the lossy model/display projections. */
|
|
3
|
+
export const APP_OUTPUT_META_KEY = "roll/appOutput";
|
|
4
|
+
export const APP_OUTPUT_STATUS_META_KEY = "roll/appOutputStatus";
|
|
5
|
+
export const APP_OUTPUT_LIMITS = {
|
|
6
|
+
resultBytes: 256 * 1024,
|
|
7
|
+
schemaBytes: 32 * 1024,
|
|
8
|
+
threadBytes: 16 * 1024 * 1024,
|
|
9
|
+
threadRecords: 2000,
|
|
10
|
+
maxAgeMs: 30 * 24 * 60 * 60 * 1000,
|
|
11
|
+
};
|
|
12
|
+
const appOutputDeclarationObjectSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
schemaId: z.string().min(1).max(256),
|
|
15
|
+
schemaVersion: z.number().int().positive(),
|
|
16
|
+
remoteReadable: z.boolean().default(false),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
export const appOutputDeclarationSchema = appOutputDeclarationObjectSchema.readonly();
|
|
20
|
+
export const appOutputContractSchema = z
|
|
21
|
+
.object({
|
|
22
|
+
...appOutputDeclarationObjectSchema.shape,
|
|
23
|
+
outputSchema: z.record(z.string(), z.json()),
|
|
24
|
+
})
|
|
25
|
+
.strict()
|
|
26
|
+
.readonly();
|
|
27
|
+
export const APP_OUTPUT_UNAVAILABLE_STATUSES = [
|
|
28
|
+
"not_provided",
|
|
29
|
+
"invalid",
|
|
30
|
+
"too_large",
|
|
31
|
+
"expired",
|
|
32
|
+
"denied",
|
|
33
|
+
];
|
|
34
|
+
export const appOutputRejectionSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
status: z.literal("rejected"),
|
|
37
|
+
reason: z.enum(["credential_field", "credential_value"]),
|
|
38
|
+
field: z.string().max(64).optional(),
|
|
39
|
+
})
|
|
40
|
+
.strict()
|
|
41
|
+
.readonly();
|
|
42
|
+
export const appOutputResultSchema = z
|
|
43
|
+
.discriminatedUnion("status", [
|
|
44
|
+
appOutputRejectionSchema,
|
|
45
|
+
z
|
|
46
|
+
.object({
|
|
47
|
+
status: z.literal("available"),
|
|
48
|
+
schemaId: z.string().min(1).max(256),
|
|
49
|
+
schemaVersion: z.number().int().positive(),
|
|
50
|
+
remoteReadable: z.boolean(),
|
|
51
|
+
data: z.record(z.string(), z.json()),
|
|
52
|
+
fallbackText: z.string().max(4096),
|
|
53
|
+
})
|
|
54
|
+
.strict(),
|
|
55
|
+
z.object({ status: z.enum(APP_OUTPUT_UNAVAILABLE_STATUSES) }).strict(),
|
|
56
|
+
])
|
|
57
|
+
.readonly();
|
|
58
|
+
export const appOutputDescriptorSchema = z
|
|
59
|
+
.discriminatedUnion("status", [
|
|
60
|
+
appOutputRejectionSchema,
|
|
61
|
+
z
|
|
62
|
+
.object({
|
|
63
|
+
status: z.literal("available"),
|
|
64
|
+
schemaId: z.string().min(1).max(256),
|
|
65
|
+
schemaVersion: z.number().int().positive(),
|
|
66
|
+
})
|
|
67
|
+
.strict(),
|
|
68
|
+
z.object({ status: z.enum(APP_OUTPUT_UNAVAILABLE_STATUSES) }).strict(),
|
|
69
|
+
])
|
|
70
|
+
.readonly();
|
|
71
|
+
export function describeAppOutput(result) {
|
|
72
|
+
if (result.status === "rejected")
|
|
73
|
+
return result;
|
|
74
|
+
return result.status === "available"
|
|
75
|
+
? { status: result.status, schemaId: result.schemaId, schemaVersion: result.schemaVersion }
|
|
76
|
+
: { status: result.status };
|
|
77
|
+
}
|
|
78
|
+
/** Validate portable, self-contained contracts without dereferencing network resources. */
|
|
79
|
+
export function validateAppOutputContract(value) {
|
|
80
|
+
const contract = appOutputContractSchema.parse(value);
|
|
81
|
+
const schema = contract.outputSchema;
|
|
82
|
+
if (schema.type !== "object")
|
|
83
|
+
throw new Error("appOutput outputSchema must be an object schema");
|
|
84
|
+
if (new TextEncoder().encode(JSON.stringify(schema)).byteLength > APP_OUTPUT_LIMITS.schemaBytes) {
|
|
85
|
+
throw new Error("appOutput outputSchema exceeds 32 KiB");
|
|
86
|
+
}
|
|
87
|
+
const visit = (node) => {
|
|
88
|
+
if (typeof node !== "object" || node === null || Array.isArray(node))
|
|
89
|
+
return;
|
|
90
|
+
const record = { ...node };
|
|
91
|
+
if ("$dynamicRef" in record || "$recursiveRef" in record) {
|
|
92
|
+
throw new Error("appOutput supports only static local references");
|
|
93
|
+
}
|
|
94
|
+
if ("$ref" in record) {
|
|
95
|
+
const ref = record["$ref"];
|
|
96
|
+
if (typeof ref !== "string" || (ref !== "#" && !ref.startsWith("#/"))) {
|
|
97
|
+
throw new Error("appOutput outputSchema only supports local JSON Pointer references");
|
|
98
|
+
}
|
|
99
|
+
let target = schema;
|
|
100
|
+
for (const part of ref === "#" ? [] : ref.slice(2).split("/")) {
|
|
101
|
+
const key = decodeURIComponent(part).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
102
|
+
if (typeof target !== "object" || target === null || !Object.hasOwn(target, key)) {
|
|
103
|
+
throw new Error("appOutput outputSchema has an unresolved local reference");
|
|
104
|
+
}
|
|
105
|
+
target = Reflect.get(target, key);
|
|
106
|
+
}
|
|
107
|
+
if (typeof target !== "boolean" &&
|
|
108
|
+
(typeof target !== "object" || target === null || Array.isArray(target))) {
|
|
109
|
+
throw new Error("appOutput local reference must target a schema");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const key of [
|
|
113
|
+
"properties",
|
|
114
|
+
"patternProperties",
|
|
115
|
+
"$defs",
|
|
116
|
+
"definitions",
|
|
117
|
+
"dependentSchemas",
|
|
118
|
+
"dependencies",
|
|
119
|
+
]) {
|
|
120
|
+
const children = record[key];
|
|
121
|
+
if (typeof children === "object" && children !== null && !Array.isArray(children)) {
|
|
122
|
+
for (const child of Object.values(children))
|
|
123
|
+
visit(child);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
|
|
127
|
+
const children = record[key];
|
|
128
|
+
if (Array.isArray(children))
|
|
129
|
+
children.forEach(visit);
|
|
130
|
+
}
|
|
131
|
+
for (const key of [
|
|
132
|
+
"additionalProperties",
|
|
133
|
+
"additionalItems",
|
|
134
|
+
"contains",
|
|
135
|
+
"not",
|
|
136
|
+
"if",
|
|
137
|
+
"then",
|
|
138
|
+
"else",
|
|
139
|
+
"propertyNames",
|
|
140
|
+
"unevaluatedProperties",
|
|
141
|
+
"unevaluatedItems",
|
|
142
|
+
]) {
|
|
143
|
+
visit(record[key]);
|
|
144
|
+
}
|
|
145
|
+
const items = record["items"];
|
|
146
|
+
if (Array.isArray(items))
|
|
147
|
+
items.forEach(visit);
|
|
148
|
+
else
|
|
149
|
+
visit(items);
|
|
150
|
+
// A nested identifier can change the meaning of otherwise local references.
|
|
151
|
+
if (node !== schema && ("$id" in record || "id" in record)) {
|
|
152
|
+
throw new Error("appOutput does not support nested schema identifiers");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
visit(schema);
|
|
156
|
+
return contract;
|
|
157
|
+
}
|
|
158
|
+
/** MCP uses isError for invalid output even when execution has completed. */
|
|
159
|
+
export const COMPLETED_APP_OUTPUT_STATUSES = ["invalid", "too_large"];
|
|
160
|
+
export function getCompletedAppOutputStatus(value) {
|
|
161
|
+
if (typeof value !== "object" ||
|
|
162
|
+
value === null ||
|
|
163
|
+
!("isError" in value) ||
|
|
164
|
+
value.isError !== true ||
|
|
165
|
+
!("_meta" in value)) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
const meta = value._meta;
|
|
169
|
+
if (typeof meta !== "object" ||
|
|
170
|
+
meta === null ||
|
|
171
|
+
!("roll/executionStatus" in meta) ||
|
|
172
|
+
meta["roll/executionStatus"] !== "completed" ||
|
|
173
|
+
!(APP_OUTPUT_STATUS_META_KEY in meta)) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
const status = meta[APP_OUTPUT_STATUS_META_KEY];
|
|
177
|
+
return status === "invalid" || status === "too_large" ? status : undefined;
|
|
178
|
+
}
|