paseo-plugin-zcode-provider 0.0.0-stage → 0.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.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +29 -0
  3. package/README.md +98 -2
  4. package/client/diagnostics.tsx +397 -0
  5. package/client/setup.tsx +347 -0
  6. package/icon.svg +1 -0
  7. package/index.client.tsx +22 -0
  8. package/index.server.ts +21 -0
  9. package/package.json +75 -4
  10. package/paseo-plugin.json +6 -0
  11. package/server/attachments.ts +150 -0
  12. package/server/build-info.ts +2 -0
  13. package/server/conversation.ts +319 -0
  14. package/server/diagnostics.ts +235 -0
  15. package/server/discovery/discover.ts +225 -0
  16. package/server/discovery/manifest.ts +36 -0
  17. package/server/discovery/types.ts +30 -0
  18. package/server/errors.ts +36 -0
  19. package/server/host/bridge.ts +463 -0
  20. package/server/host/schemas.ts +119 -0
  21. package/server/logger.ts +19 -0
  22. package/server/mapping.ts +270 -0
  23. package/server/models.ts +68 -0
  24. package/server/persistence.ts +146 -0
  25. package/server/presentation.ts +428 -0
  26. package/server/provider.ts +831 -0
  27. package/server/runtime/managed.ts +501 -0
  28. package/server/runtime/pins.ts +72 -0
  29. package/server/runtime-setup.ts +102 -0
  30. package/server/session-types.ts +49 -0
  31. package/server/session.ts +866 -0
  32. package/server/status.ts +89 -0
  33. package/server/timeline.ts +18 -0
  34. package/server/vendor/zcode/LICENSE +202 -0
  35. package/server/vendor/zcode/NOTICE.md +70 -0
  36. package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
  37. package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
  38. package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
  39. package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
  40. package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
  41. package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
  42. package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
  43. package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
  44. package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
  45. package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
  46. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
  47. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
  48. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
  49. package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
  50. package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
  51. package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
  52. package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
  53. package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
  54. package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
  55. package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
  56. package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
  57. package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
  58. package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
  59. package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
  60. package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
  61. package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
  62. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
  63. package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
  64. package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
  65. package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
  66. package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
  67. package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
  68. package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
  69. package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
  70. package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
  71. package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
  72. package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
  73. package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
  74. package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
  75. package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
  76. package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
  77. package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
  78. package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
  79. package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
  80. package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
  81. package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
  82. package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
  83. package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
  84. package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
  85. package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
  86. package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
  87. package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
  88. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
  89. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
  90. package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
  91. package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
  92. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
  93. package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
  94. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
  95. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
  96. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
  97. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
  98. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
  99. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
  100. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
  101. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
  102. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
  103. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
  104. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
  105. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
  106. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
  107. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
  108. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
  109. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
  110. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
  111. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
  112. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
  113. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
  114. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
  115. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
  116. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
  117. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
  118. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
  119. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
  120. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
  121. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
  122. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
  123. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
  124. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
  125. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
  126. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
  127. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
  128. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
  129. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
  130. package/server/vendor/zcode/provenance.json +405 -0
  131. package/shared/diagnostics.ts +52 -0
  132. package/shared/runtime-setup.ts +75 -0
  133. package/vendor/acp-registry/LICENSE +176 -0
  134. package/vendor/paseo/LICENSE +211 -0
@@ -0,0 +1,214 @@
1
+ import type { RestrictedCelExpression } from "./parser.js";
2
+ import {
3
+ RestrictedCelError,
4
+ type JsonObject,
5
+ type JsonValue,
6
+ type RestrictedCelValue,
7
+ } from "./types.js";
8
+
9
+ export function evaluateRestrictedCel(
10
+ expression: RestrictedCelExpression,
11
+ input: RestrictedCelValue,
12
+ ): JsonValue {
13
+ assertRestrictedCelValue(input, expression.offset);
14
+ return freezeJson(evaluate(expression, input));
15
+ }
16
+
17
+ function evaluate(expression: RestrictedCelExpression, input: RestrictedCelValue): JsonValue {
18
+ switch (expression.type) {
19
+ case "literal":
20
+ return expression.value;
21
+ case "input":
22
+ return input;
23
+ case "array":
24
+ return expression.elements.map((element) => evaluate(element, input));
25
+ case "object": {
26
+ const result: Record<string, JsonValue> = Object.create(null) as Record<string, JsonValue>;
27
+ for (const entry of expression.entries) {
28
+ Object.defineProperty(result, entry.key, {
29
+ configurable: true,
30
+ enumerable: true,
31
+ value: evaluate(entry.value, input),
32
+ writable: true,
33
+ });
34
+ }
35
+ return result;
36
+ }
37
+ case "unary":
38
+ return evaluateUnary(
39
+ expression.operator,
40
+ evaluate(expression.operand, input),
41
+ expression.offset,
42
+ );
43
+ case "binary":
44
+ return evaluateBinary(expression, input);
45
+ case "conditional":
46
+ return requireBoolean(evaluate(expression.condition, input), expression.condition.offset)
47
+ ? evaluate(expression.whenTrue, input)
48
+ : evaluate(expression.whenFalse, input);
49
+ }
50
+ }
51
+
52
+ function evaluateUnary(operator: string, operand: JsonValue, offset: number): JsonValue {
53
+ if (operator === "!") return !requireBoolean(operand, offset);
54
+ const number = requireNumber(operand, offset);
55
+ return assertNumber(operator === "-" ? -number : number, offset);
56
+ }
57
+
58
+ function evaluateBinary(
59
+ expression: Extract<RestrictedCelExpression, { type: "binary" }>,
60
+ input: RestrictedCelValue,
61
+ ): JsonValue {
62
+ const left = evaluate(expression.left, input);
63
+ if (expression.operator === "&&") {
64
+ return requireBoolean(left, expression.left.offset)
65
+ ? requireBoolean(evaluate(expression.right, input), expression.right.offset)
66
+ : false;
67
+ }
68
+ if (expression.operator === "||") {
69
+ return requireBoolean(left, expression.left.offset)
70
+ ? true
71
+ : requireBoolean(evaluate(expression.right, input), expression.right.offset);
72
+ }
73
+
74
+ const right = evaluate(expression.right, input);
75
+ switch (expression.operator) {
76
+ case "==":
77
+ return jsonEquals(left, right);
78
+ case "!=":
79
+ return !jsonEquals(left, right);
80
+ case "+":
81
+ if (typeof left === "string" && typeof right === "string") return left + right;
82
+ return assertNumber(
83
+ requireNumber(left, expression.left.offset) + requireNumber(right, expression.right.offset),
84
+ expression.offset,
85
+ );
86
+ case "-":
87
+ return numericBinary(left, right, expression, (a, b) => a - b);
88
+ case "*":
89
+ return numericBinary(left, right, expression, (a, b) => a * b);
90
+ case "/":
91
+ return numericBinary(left, right, expression, (a, b) => a / b);
92
+ case "%":
93
+ return numericBinary(left, right, expression, (a, b) => a % b);
94
+ case "<":
95
+ case "<=":
96
+ case ">":
97
+ case ">=":
98
+ return compare(left, right, expression.operator, expression.offset);
99
+ default:
100
+ throw new RestrictedCelError(
101
+ `unsupported operator ${expression.operator}`,
102
+ expression.offset,
103
+ );
104
+ }
105
+ }
106
+
107
+ function numericBinary(
108
+ left: JsonValue,
109
+ right: JsonValue,
110
+ expression: Extract<RestrictedCelExpression, { type: "binary" }>,
111
+ operation: (left: number, right: number) => number,
112
+ ): number {
113
+ return assertNumber(
114
+ operation(
115
+ requireNumber(left, expression.left.offset),
116
+ requireNumber(right, expression.right.offset),
117
+ ),
118
+ expression.offset,
119
+ );
120
+ }
121
+
122
+ function compare(left: JsonValue, right: JsonValue, operator: string, offset: number): boolean {
123
+ if (typeof left !== typeof right || (typeof left !== "number" && typeof left !== "string")) {
124
+ throw new RestrictedCelError(
125
+ "comparison operands must have the same numeric or string type",
126
+ offset,
127
+ );
128
+ }
129
+ const comparison =
130
+ typeof left === "number" && typeof right === "number"
131
+ ? left < right
132
+ ? -1
133
+ : left > right
134
+ ? 1
135
+ : 0
136
+ : String(left) < String(right)
137
+ ? -1
138
+ : String(left) > String(right)
139
+ ? 1
140
+ : 0;
141
+ if (operator === "<") return comparison < 0;
142
+ if (operator === "<=") return comparison <= 0;
143
+ if (operator === ">") return comparison > 0;
144
+ return comparison >= 0;
145
+ }
146
+
147
+ function requireBoolean(value: JsonValue, offset: number): boolean {
148
+ if (typeof value !== "boolean") {
149
+ throw new RestrictedCelError("boolean operand required", offset);
150
+ }
151
+ return value;
152
+ }
153
+
154
+ function requireNumber(value: JsonValue, offset: number): number {
155
+ if (typeof value !== "number") {
156
+ throw new RestrictedCelError("numeric operand required", offset);
157
+ }
158
+ return value;
159
+ }
160
+
161
+ function assertNumber(value: number, offset: number): number {
162
+ if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
163
+ throw new RestrictedCelError("numeric result is not JSON-safe", offset);
164
+ }
165
+ return value;
166
+ }
167
+
168
+ function assertRestrictedCelValue(
169
+ value: unknown,
170
+ offset: number,
171
+ ): asserts value is RestrictedCelValue {
172
+ if (typeof value === "string") return;
173
+ if (typeof value === "number") {
174
+ assertNumber(value, offset);
175
+ return;
176
+ }
177
+ throw new RestrictedCelError("input value must be a string or number", offset);
178
+ }
179
+
180
+ function jsonEquals(left: JsonValue, right: JsonValue): boolean {
181
+ if (Object.is(left, right)) return true;
182
+ if (Array.isArray(left) || Array.isArray(right)) {
183
+ return (
184
+ Array.isArray(left) &&
185
+ Array.isArray(right) &&
186
+ left.length === right.length &&
187
+ left.every((entry, index) => jsonEquals(entry, right[index]!))
188
+ );
189
+ }
190
+ if (isJsonObject(left) && isJsonObject(right)) {
191
+ const leftKeys = Object.keys(left);
192
+ const rightKeys = Object.keys(right);
193
+ return (
194
+ leftKeys.length === rightKeys.length &&
195
+ leftKeys.every((key) => Object.hasOwn(right, key) && jsonEquals(left[key]!, right[key]!))
196
+ );
197
+ }
198
+ return false;
199
+ }
200
+
201
+ function freezeJson<T extends JsonValue>(value: T): T {
202
+ if (Array.isArray(value)) {
203
+ for (const entry of value) freezeJson(entry);
204
+ } else if (isJsonObject(value)) {
205
+ for (const entry of Object.values(value)) freezeJson(entry);
206
+ } else {
207
+ return value;
208
+ }
209
+ return Object.freeze(value);
210
+ }
211
+
212
+ function isJsonObject(value: JsonValue): value is JsonObject {
213
+ return value !== null && typeof value === "object" && !Array.isArray(value);
214
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./compiler.js";
2
+ export * from "./merge-patch.js";
3
+ export * from "./option-maps.js";
4
+ export * from "./tokenizer.js";
5
+ export * from "./types.js";
@@ -0,0 +1,97 @@
1
+ import { ModelOptionMapError, type JsonObject, type JsonValue } from "./types.js";
2
+
3
+ export interface NamedJsonMergePatch {
4
+ readonly option: string;
5
+ readonly patch: JsonObject;
6
+ }
7
+
8
+ interface OwnedPath {
9
+ readonly option: string;
10
+ readonly path: readonly string[];
11
+ }
12
+
13
+ export function applyOrderedJsonMergePatches(
14
+ body: JsonObject,
15
+ patches: readonly NamedJsonMergePatch[],
16
+ ): JsonObject {
17
+ const ownedPaths: OwnedPath[] = [];
18
+ let result = cloneJson(body) as JsonObject;
19
+ for (const namedPatch of patches) {
20
+ const paths = collectWrittenPaths(namedPatch.patch);
21
+ for (const path of paths) {
22
+ const conflict = ownedPaths.find((owned) => pathsOverlap(owned.path, path));
23
+ if (conflict) {
24
+ throw new ModelOptionMapError(
25
+ `Model option maps write conflicting JSON path ${formatPath(path)}: ${conflict.option} and ${namedPatch.option}`,
26
+ );
27
+ }
28
+ ownedPaths.push({ option: namedPatch.option, path });
29
+ }
30
+ result = mergeObject(result, namedPatch.patch);
31
+ }
32
+ return result;
33
+ }
34
+
35
+ function collectWrittenPaths(
36
+ patch: JsonObject,
37
+ prefix: readonly string[] = [],
38
+ ): readonly string[][] {
39
+ const paths: string[][] = [];
40
+ for (const [key, value] of Object.entries(patch)) {
41
+ const path = [...prefix, key];
42
+ if (isJsonObject(value) && Object.keys(value).length > 0) {
43
+ paths.push(...collectWrittenPaths(value, path));
44
+ } else {
45
+ paths.push(path);
46
+ }
47
+ }
48
+ return paths;
49
+ }
50
+
51
+ function pathsOverlap(left: readonly string[], right: readonly string[]): boolean {
52
+ const sharedLength = Math.min(left.length, right.length);
53
+ for (let index = 0; index < sharedLength; index += 1) {
54
+ if (left[index] !== right[index]) return false;
55
+ }
56
+ return true;
57
+ }
58
+
59
+ function formatPath(path: readonly string[]): string {
60
+ return path.length === 0 ? "$" : `$.${path.join(".")}`;
61
+ }
62
+
63
+ function mergeObject(target: JsonObject, patch: JsonObject): JsonObject {
64
+ const result = cloneJson(target) as Record<string, JsonValue>;
65
+ for (const [key, patchValue] of Object.entries(patch)) {
66
+ if (patchValue === null) {
67
+ delete result[key];
68
+ continue;
69
+ }
70
+ if (isJsonObject(patchValue)) {
71
+ const targetValue = result[key];
72
+ result[key] = mergeObject(isJsonObject(targetValue) ? targetValue : {}, patchValue);
73
+ continue;
74
+ }
75
+ result[key] = cloneJson(patchValue);
76
+ }
77
+ return result;
78
+ }
79
+
80
+ function cloneJson(value: JsonValue): JsonValue {
81
+ if (Array.isArray(value)) return value.map(cloneJson);
82
+ if (!isJsonObject(value)) return value;
83
+ const result: Record<string, JsonValue> = Object.create(null) as Record<string, JsonValue>;
84
+ for (const [key, entry] of Object.entries(value)) {
85
+ Object.defineProperty(result, key, {
86
+ configurable: true,
87
+ enumerable: true,
88
+ value: cloneJson(entry),
89
+ writable: true,
90
+ });
91
+ }
92
+ return result;
93
+ }
94
+
95
+ function isJsonObject(value: JsonValue | undefined): value is JsonObject {
96
+ return value !== null && typeof value === "object" && !Array.isArray(value);
97
+ }
@@ -0,0 +1,42 @@
1
+ import { compileModelOptionMap } from "./compiler.js";
2
+ import { applyOrderedJsonMergePatches, type NamedJsonMergePatch } from "./merge-patch.js";
3
+ import { ModelOptionMapError, type JsonObject, type ModelOptionMapProgram } from "./types.js";
4
+
5
+ export interface ModelOptionMapSpecs {
6
+ readonly reasoningLevel: { readonly map: string };
7
+ readonly maxOutputTokens: { readonly map: string };
8
+ }
9
+
10
+ export interface ModelOptionValues {
11
+ readonly reasoningLevel: string;
12
+ readonly maxOutputTokens: number;
13
+ }
14
+
15
+ export interface CompiledModelOptionMaps {
16
+ apply(body: JsonObject, values: ModelOptionValues): JsonObject;
17
+ }
18
+
19
+ /** Model 创建时编译一次;每个请求只绑定本轮冻结的 Option value。 */
20
+ export function compileModelOptionMaps(specs: ModelOptionMapSpecs): CompiledModelOptionMaps {
21
+ const reasoningLevel = compileModelOptionMap(specs.reasoningLevel.map, "reasoningLevel");
22
+ const maxOutputTokens = compileModelOptionMap(specs.maxOutputTokens.map, "maxOutputTokens");
23
+ return Object.freeze({
24
+ apply(body: JsonObject, values: ModelOptionValues): JsonObject {
25
+ const patches: NamedJsonMergePatch[] = [];
26
+ if (values.reasoningLevel === undefined) {
27
+ throw new ModelOptionMapError("reasoningLevel requires an effective value");
28
+ }
29
+ patches.push(optionPatch("reasoningLevel", reasoningLevel, values.reasoningLevel));
30
+ patches.push(optionPatch("maxOutputTokens", maxOutputTokens, values.maxOutputTokens));
31
+ return applyOrderedJsonMergePatches(body, patches);
32
+ },
33
+ });
34
+ }
35
+
36
+ function optionPatch(
37
+ option: string,
38
+ program: ModelOptionMapProgram,
39
+ value: string | number,
40
+ ): NamedJsonMergePatch {
41
+ return { option, patch: program.evaluate(value) };
42
+ }
@@ -0,0 +1,240 @@
1
+ import { RestrictedCelError, type ModelOptionName } from "./types.js";
2
+ import type { RestrictedCelToken } from "./tokenizer.js";
3
+
4
+ export type RestrictedCelExpression =
5
+ | {
6
+ readonly type: "literal";
7
+ readonly value: string | number | boolean | null;
8
+ readonly offset: number;
9
+ }
10
+ | { readonly type: "input"; readonly offset: number }
11
+ | {
12
+ readonly type: "array";
13
+ readonly elements: readonly RestrictedCelExpression[];
14
+ readonly offset: number;
15
+ }
16
+ | {
17
+ readonly type: "object";
18
+ readonly entries: readonly {
19
+ readonly key: string;
20
+ readonly value: RestrictedCelExpression;
21
+ readonly offset: number;
22
+ }[];
23
+ readonly offset: number;
24
+ }
25
+ | {
26
+ readonly type: "unary";
27
+ readonly operator: "!" | "-" | "+";
28
+ readonly operand: RestrictedCelExpression;
29
+ readonly offset: number;
30
+ }
31
+ | {
32
+ readonly type: "binary";
33
+ readonly operator: string;
34
+ readonly left: RestrictedCelExpression;
35
+ readonly right: RestrictedCelExpression;
36
+ readonly offset: number;
37
+ }
38
+ | {
39
+ readonly type: "conditional";
40
+ readonly condition: RestrictedCelExpression;
41
+ readonly whenTrue: RestrictedCelExpression;
42
+ readonly whenFalse: RestrictedCelExpression;
43
+ readonly offset: number;
44
+ };
45
+
46
+ export function parseRestrictedCel(
47
+ tokens: readonly RestrictedCelToken[],
48
+ variableName: ModelOptionName,
49
+ ): RestrictedCelExpression {
50
+ return new Parser(tokens, variableName).parse();
51
+ }
52
+
53
+ class Parser {
54
+ #index = 0;
55
+
56
+ constructor(
57
+ private readonly tokens: readonly RestrictedCelToken[],
58
+ private readonly variableName: ModelOptionName,
59
+ ) {}
60
+
61
+ parse(): RestrictedCelExpression {
62
+ const expression = this.parseConditional();
63
+ const trailing = this.current();
64
+ if (trailing.kind !== "eof") {
65
+ if (trailing.value === ".") {
66
+ throw new RestrictedCelError("member access is not supported", trailing.offset);
67
+ }
68
+ if (trailing.value === "(") {
69
+ throw new RestrictedCelError("function calls are not supported", trailing.offset);
70
+ }
71
+ throw new RestrictedCelError(
72
+ `unexpected token ${JSON.stringify(trailing.value)}`,
73
+ trailing.offset,
74
+ );
75
+ }
76
+ return expression;
77
+ }
78
+
79
+ private parseConditional(): RestrictedCelExpression {
80
+ const condition = this.parseLogicalOr();
81
+ if (!this.consume("?")) return condition;
82
+ const whenTrue = this.parseConditional();
83
+ this.expect(":");
84
+ const whenFalse = this.parseConditional();
85
+ return { type: "conditional", condition, whenTrue, whenFalse, offset: condition.offset };
86
+ }
87
+
88
+ private parseLogicalOr(): RestrictedCelExpression {
89
+ return this.parseBinary(() => this.parseLogicalAnd(), new Set(["||"]));
90
+ }
91
+
92
+ private parseLogicalAnd(): RestrictedCelExpression {
93
+ return this.parseBinary(() => this.parseEquality(), new Set(["&&"]));
94
+ }
95
+
96
+ private parseEquality(): RestrictedCelExpression {
97
+ return this.parseBinary(() => this.parseRelational(), new Set(["==", "!="]));
98
+ }
99
+
100
+ private parseRelational(): RestrictedCelExpression {
101
+ return this.parseBinary(() => this.parseAdditive(), new Set(["<", "<=", ">", ">="]));
102
+ }
103
+
104
+ private parseAdditive(): RestrictedCelExpression {
105
+ return this.parseBinary(() => this.parseMultiplicative(), new Set(["+", "-"]));
106
+ }
107
+
108
+ private parseMultiplicative(): RestrictedCelExpression {
109
+ return this.parseBinary(() => this.parseUnary(), new Set(["*", "/", "%"]));
110
+ }
111
+
112
+ private parseBinary(
113
+ parseOperand: () => RestrictedCelExpression,
114
+ operators: ReadonlySet<string>,
115
+ ): RestrictedCelExpression {
116
+ let expression = parseOperand();
117
+ while (this.current().kind === "operator" && operators.has(this.current().value)) {
118
+ const operator = this.advance();
119
+ expression = {
120
+ type: "binary",
121
+ operator: operator.value,
122
+ left: expression,
123
+ right: parseOperand(),
124
+ offset: operator.offset,
125
+ };
126
+ }
127
+ return expression;
128
+ }
129
+
130
+ private parseUnary(): RestrictedCelExpression {
131
+ const token = this.current();
132
+ if (
133
+ token.kind === "operator" &&
134
+ (token.value === "!" || token.value === "-" || token.value === "+")
135
+ ) {
136
+ this.advance();
137
+ return {
138
+ type: "unary",
139
+ operator: token.value,
140
+ operand: this.parseUnary(),
141
+ offset: token.offset,
142
+ };
143
+ }
144
+ return this.parsePrimary();
145
+ }
146
+
147
+ private parsePrimary(): RestrictedCelExpression {
148
+ const token = this.advance();
149
+ if (token.kind === "number") {
150
+ const value = Number(token.value);
151
+ if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
152
+ throw new RestrictedCelError("number literal is not JSON-safe", token.offset);
153
+ }
154
+ return { type: "literal", value, offset: token.offset };
155
+ }
156
+ if (token.kind === "string")
157
+ return { type: "literal", value: token.value, offset: token.offset };
158
+ if (token.kind === "identifier") {
159
+ if (this.current().value === "(") {
160
+ throw new RestrictedCelError("function calls are not supported", this.current().offset);
161
+ }
162
+ if (token.value === this.variableName) return { type: "input", offset: token.offset };
163
+ if (token.value === "true" || token.value === "false") {
164
+ return { type: "literal", value: token.value === "true", offset: token.offset };
165
+ }
166
+ if (token.value === "null") return { type: "literal", value: null, offset: token.offset };
167
+ throw new RestrictedCelError(
168
+ `unknown identifier ${JSON.stringify(token.value)}`,
169
+ token.offset,
170
+ );
171
+ }
172
+ if (token.value === "(") {
173
+ const expression = this.parseConditional();
174
+ this.expect(")");
175
+ return expression;
176
+ }
177
+ if (token.value === "[") return this.parseArray(token.offset);
178
+ if (token.value === "{") return this.parseObject(token.offset);
179
+ throw new RestrictedCelError(`unexpected token ${JSON.stringify(token.value)}`, token.offset);
180
+ }
181
+
182
+ private parseArray(offset: number): RestrictedCelExpression {
183
+ const elements: RestrictedCelExpression[] = [];
184
+ if (!this.consume("]")) {
185
+ do elements.push(this.parseConditional());
186
+ while (this.consume(","));
187
+ this.expect("]");
188
+ }
189
+ return { type: "array", elements: Object.freeze(elements), offset };
190
+ }
191
+
192
+ private parseObject(offset: number): RestrictedCelExpression {
193
+ const entries: { key: string; value: RestrictedCelExpression; offset: number }[] = [];
194
+ const keys = new Set<string>();
195
+ if (!this.consume("}")) {
196
+ do {
197
+ const key = this.advance();
198
+ if (key.kind !== "string") {
199
+ throw new RestrictedCelError("object keys must be string literals", key.offset);
200
+ }
201
+ if (keys.has(key.value)) {
202
+ throw new RestrictedCelError(
203
+ `duplicate object key ${JSON.stringify(key.value)}`,
204
+ key.offset,
205
+ );
206
+ }
207
+ keys.add(key.value);
208
+ this.expect(":");
209
+ entries.push({ key: key.value, value: this.parseConditional(), offset: key.offset });
210
+ } while (this.consume(","));
211
+ this.expect("}");
212
+ }
213
+ return { type: "object", entries: Object.freeze(entries), offset };
214
+ }
215
+
216
+ private consume(value: string): boolean {
217
+ if (this.current().value !== value) return false;
218
+ this.#index += 1;
219
+ return true;
220
+ }
221
+
222
+ private expect(value: string): RestrictedCelToken {
223
+ const token = this.current();
224
+ if (token.value !== value) {
225
+ throw new RestrictedCelError(`expected ${JSON.stringify(value)}`, token.offset);
226
+ }
227
+ this.#index += 1;
228
+ return token;
229
+ }
230
+
231
+ private advance(): RestrictedCelToken {
232
+ const token = this.current();
233
+ if (token.kind !== "eof") this.#index += 1;
234
+ return token;
235
+ }
236
+
237
+ private current(): RestrictedCelToken {
238
+ return this.tokens[this.#index] ?? this.tokens[this.tokens.length - 1]!;
239
+ }
240
+ }