@haikeilabs/agentware 0.1.0 → 0.2.1
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 +19 -0
- package/dist/executor/agent_loop.d.ts +50 -0
- package/dist/executor/agent_loop.d.ts.map +1 -0
- package/dist/executor/agent_loop.js +255 -0
- package/dist/executor/executor.d.ts.map +1 -1
- package/dist/executor/executor.js +6 -2
- package/dist/executor/index.d.ts +2 -1
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/kei/index.d.ts +2 -0
- package/dist/kei/index.d.ts.map +1 -0
- package/dist/kei/index.js +1 -0
- package/dist/kei/runtimeLink.d.ts +281 -0
- package/dist/kei/runtimeLink.d.ts.map +1 -0
- package/dist/kei/runtimeLink.js +591 -0
- package/dist/llm/backend.d.ts +7 -0
- package/dist/llm/backend.d.ts.map +1 -1
- package/dist/llm/index.d.ts +2 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -0
- package/dist/llm/openai_backend.d.ts +51 -0
- package/dist/llm/openai_backend.d.ts.map +1 -0
- package/dist/llm/openai_backend.js +203 -0
- package/dist/llm/response.d.ts +5 -0
- package/dist/llm/response.d.ts.map +1 -1
- package/dist/middleware/guardrails/response_validator.d.ts +1 -1
- package/dist/middleware/guardrails/response_validator.d.ts.map +1 -1
- package/dist/middleware/guardrails/response_validator.js +1 -1
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/inference.d.ts +8 -0
- package/dist/middleware/inference.d.ts.map +1 -1
- package/dist/middleware/inference.js +73 -31
- package/dist/reasoning/adapter.d.ts +33 -0
- package/dist/reasoning/adapter.d.ts.map +1 -0
- package/dist/reasoning/adapter.js +365 -0
- package/dist/reasoning/errors.d.ts +28 -0
- package/dist/reasoning/errors.d.ts.map +1 -0
- package/dist/reasoning/errors.js +57 -0
- package/dist/reasoning/index.d.ts +6 -0
- package/dist/reasoning/index.d.ts.map +1 -0
- package/dist/reasoning/index.js +4 -0
- package/dist/reasoning/options.d.ts +18 -0
- package/dist/reasoning/options.d.ts.map +1 -0
- package/dist/reasoning/options.js +18 -0
- package/dist/reasoning/types.d.ts +44 -0
- package/dist/reasoning/types.d.ts.map +1 -0
- package/dist/reasoning/types.js +32 -0
- package/dist/toolformat/formatter.d.ts +3 -3
- package/dist/toolformat/formatter.d.ts.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/registry.d.ts +4 -4
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/tool.d.ts +8 -1
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/tools/tool.js +7 -0
- package/package.json +17 -1
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeLink contract: the harness → Agentware → kei-connector-runtime heartbeat.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript mirror of the Go reference `go/kei/runtimelink` for
|
|
5
|
+
* `docs/specs/runtime-heartbeat-liveness.md` (HAI-141). Contract types only:
|
|
6
|
+
* configuration, status, identity, failure classes, the child's JSONL wire
|
|
7
|
+
* events, and redacted lifecycle events. It never spawns a process, opens a
|
|
8
|
+
* network connection, or reads a credential value. The only heartbeat path is
|
|
9
|
+
* harness → RuntimeLink → kei-connector-runtime; nothing here talks to the
|
|
10
|
+
* catalog.
|
|
11
|
+
*
|
|
12
|
+
* All three SDK languages share `testing/contracts/runtime-link`. Durations
|
|
13
|
+
* are milliseconds here (`intervalMs`), seconds in Python, and
|
|
14
|
+
* `time.Duration` in Go.
|
|
15
|
+
*/
|
|
16
|
+
/** The child stdout event version ("v") this SDK accepts. */
|
|
17
|
+
export const RUNTIME_LINK_CONTRACT_VERSION = 1;
|
|
18
|
+
/** Bound on one child stdout line, excluding the newline. */
|
|
19
|
+
export const MAX_CHILD_LINE_BYTES = 4096;
|
|
20
|
+
/** Capacity of the RuntimeLink event ring. */
|
|
21
|
+
export const RUNTIME_LINK_EVENT_BUFFER_SIZE = 64;
|
|
22
|
+
export const RUNTIME_LINK_SDK_LANG = "typescript";
|
|
23
|
+
export const HARNESS_KINDS = [
|
|
24
|
+
"assistant",
|
|
25
|
+
"pde",
|
|
26
|
+
"chat-discord",
|
|
27
|
+
"chat-slack",
|
|
28
|
+
"chat-teams",
|
|
29
|
+
"cli",
|
|
30
|
+
];
|
|
31
|
+
export const LINK_STATES = [
|
|
32
|
+
"disabled",
|
|
33
|
+
"starting",
|
|
34
|
+
"connected",
|
|
35
|
+
"degraded",
|
|
36
|
+
"reconnecting",
|
|
37
|
+
"terminal",
|
|
38
|
+
"stopped",
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Closed failure taxonomy (spec §6.3). Separates harness↔runtime failures
|
|
42
|
+
* from runtime↔catalog failures so alerts route to the right owner.
|
|
43
|
+
*/
|
|
44
|
+
export const FAILURE_CLASSES = [
|
|
45
|
+
"config_invalid",
|
|
46
|
+
"runtime_unavailable",
|
|
47
|
+
"runtime_crashloop",
|
|
48
|
+
"runtime_unresponsive",
|
|
49
|
+
"contract_mismatch",
|
|
50
|
+
"catalog_unreachable",
|
|
51
|
+
"catalog_timeout",
|
|
52
|
+
"catalog_error",
|
|
53
|
+
"catalog_backpressure",
|
|
54
|
+
"installation_unauthorized",
|
|
55
|
+
"installation_stale",
|
|
56
|
+
"installation_offline",
|
|
57
|
+
"audit_backlog",
|
|
58
|
+
"legacy_runtime_unverified",
|
|
59
|
+
];
|
|
60
|
+
export const BEAT_OUTCOMES = [
|
|
61
|
+
"ok",
|
|
62
|
+
"catalog_unreachable",
|
|
63
|
+
"catalog_timeout",
|
|
64
|
+
"catalog_error",
|
|
65
|
+
"catalog_backpressure",
|
|
66
|
+
"catalog_rejected",
|
|
67
|
+
"unauthorized",
|
|
68
|
+
];
|
|
69
|
+
export const LIFECYCLE_EVENT_NAMES = [
|
|
70
|
+
"runtime.link.started",
|
|
71
|
+
"runtime.link.connected",
|
|
72
|
+
"runtime.link.degraded",
|
|
73
|
+
"runtime.link.reconnecting",
|
|
74
|
+
"runtime.link.terminal",
|
|
75
|
+
"runtime.link.stopped",
|
|
76
|
+
];
|
|
77
|
+
export const SDK_LANGS = ["go", "python", "typescript"];
|
|
78
|
+
/** The exact environment a runtime child may receive; never `process.env`. */
|
|
79
|
+
export const CHILD_ENV_ALLOWLIST = [
|
|
80
|
+
"KEI_RUNTIME_TOKEN",
|
|
81
|
+
"KEI_RUNTIME_CONTROL_PLANE_URL",
|
|
82
|
+
"KEI_HARNESS_KIND",
|
|
83
|
+
"KEI_HARNESS_VERSION",
|
|
84
|
+
"KEI_DEPLOYMENT_ENV",
|
|
85
|
+
"KEI_AGENTWARE_SDK_LANG",
|
|
86
|
+
"KEI_AGENTWARE_SDK_VERSION",
|
|
87
|
+
"KEI_HEARTBEAT_RUN_ID",
|
|
88
|
+
"PATH",
|
|
89
|
+
"HOME",
|
|
90
|
+
"TZ",
|
|
91
|
+
];
|
|
92
|
+
function isOneOf(set, value) {
|
|
93
|
+
return (typeof value === "string" && set.includes(value));
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Configuration (spec §3.1, §3.2)
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/** Environment variables. KEI_PROXY_* keep their legacy names (runtime contract). */
|
|
99
|
+
export const RUNTIME_LINK_ENV = {
|
|
100
|
+
enabled: "KEI_RUNTIME_ENABLED",
|
|
101
|
+
token: "KEI_RUNTIME_TOKEN",
|
|
102
|
+
legacyToken: "KEI_HARNESS_TOKEN",
|
|
103
|
+
controlPlaneUrl: "KEI_RUNTIME_CONTROL_PLANE_URL",
|
|
104
|
+
proxyPath: "KEI_PROXY_PATH",
|
|
105
|
+
proxySha: "KEI_PROXY_SHA",
|
|
106
|
+
interval: "KEI_HEARTBEAT_INTERVAL",
|
|
107
|
+
timeout: "KEI_HEARTBEAT_TIMEOUT",
|
|
108
|
+
restartMin: "KEI_HEARTBEAT_RESTART_MIN",
|
|
109
|
+
restartMax: "KEI_HEARTBEAT_RESTART_MAX",
|
|
110
|
+
stableSeconds: "KEI_HEARTBEAT_STABLE_SECONDS",
|
|
111
|
+
logCount: "KEI_HEARTBEAT_LOG_COUNT",
|
|
112
|
+
harnessKind: "KEI_HARNESS_KIND",
|
|
113
|
+
harnessVersion: "KEI_HARNESS_VERSION",
|
|
114
|
+
deploymentEnv: "KEI_DEPLOYMENT_ENV",
|
|
115
|
+
};
|
|
116
|
+
/** The distribution keeps the kei-proxy name; the repo is kei-connector-runtime. */
|
|
117
|
+
export const DEFAULT_RUNTIME_BINARY_PATH = "kei-proxy";
|
|
118
|
+
/**
|
|
119
|
+
* Bounds. Interval is clamped; everything else is rejected when out of range
|
|
120
|
+
* so a typo fails closed instead of silently changing behavior.
|
|
121
|
+
*/
|
|
122
|
+
export const RUNTIME_LINK_BOUNDS = {
|
|
123
|
+
minIntervalMs: 15_000,
|
|
124
|
+
maxIntervalMs: 300_000,
|
|
125
|
+
maxGraceMs: 300_000,
|
|
126
|
+
minRestartMs: 1_000,
|
|
127
|
+
maxRestartMs: 3_600_000,
|
|
128
|
+
minStableResetMs: 1_000,
|
|
129
|
+
maxStableResetMs: 3_600_000,
|
|
130
|
+
minStopTimeoutMs: 1_000,
|
|
131
|
+
maxStopTimeoutMs: 60_000,
|
|
132
|
+
maxLogCount: 100,
|
|
133
|
+
};
|
|
134
|
+
export const RUNTIME_LINK_CONFIG_ERROR_CODES = [
|
|
135
|
+
"invalid_value",
|
|
136
|
+
"out_of_range",
|
|
137
|
+
"beat_timeout",
|
|
138
|
+
"harness_kind",
|
|
139
|
+
"envelope",
|
|
140
|
+
"legacy_token",
|
|
141
|
+
"token_missing",
|
|
142
|
+
"control_plane_url",
|
|
143
|
+
"binary",
|
|
144
|
+
];
|
|
145
|
+
/**
|
|
146
|
+
* Invalid RuntimeLink configuration. Carries the offending field and a stable
|
|
147
|
+
* code, never the offending value, so a misplaced secret cannot leak.
|
|
148
|
+
*/
|
|
149
|
+
export class RuntimeLinkConfigError extends Error {
|
|
150
|
+
code;
|
|
151
|
+
field;
|
|
152
|
+
constructor(code, field) {
|
|
153
|
+
super(`runtimeLink: invalid config: ${field}: ${code}`);
|
|
154
|
+
this.code = code;
|
|
155
|
+
this.field = field;
|
|
156
|
+
this.name = "RuntimeLinkConfigError";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** The spec defaults with the link disabled. */
|
|
160
|
+
export function defaultRuntimeLinkConfig() {
|
|
161
|
+
return {
|
|
162
|
+
enabled: false,
|
|
163
|
+
binary: { path: DEFAULT_RUNTIME_BINARY_PATH, sha256: "" },
|
|
164
|
+
controlPlaneUrl: "",
|
|
165
|
+
token: { env: RUNTIME_LINK_ENV.token },
|
|
166
|
+
intervalMs: 60_000,
|
|
167
|
+
beatTimeoutMs: 10_000,
|
|
168
|
+
graceMs: 15_000,
|
|
169
|
+
restart: { minMs: 1_000, maxMs: 300_000, stableResetMs: 300_000 },
|
|
170
|
+
stopTimeoutMs: 10_000,
|
|
171
|
+
logCount: 3,
|
|
172
|
+
harness: { kind: "", version: "", deploymentEnv: "" },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const VERSION_RE = /^[A-Za-z0-9._+-]{1,64}$/;
|
|
176
|
+
const ENV_NAME_RE = /^[A-Za-z0-9._-]{1,32}$/;
|
|
177
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
178
|
+
const ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
179
|
+
const REASON_RE = /^[a-z0-9_]{1,64}$/;
|
|
180
|
+
const TIMESTAMP_RE = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
181
|
+
const ENV_INT_RE = /^[0-9]{1,9}$/;
|
|
182
|
+
/**
|
|
183
|
+
* Clamp `intervalMs` to [15s, 300s] and validate every other field, throwing
|
|
184
|
+
* RuntimeLinkConfigError on the first violation. A disabled link (local-only
|
|
185
|
+
* mode) is valid, but its declared envelope and timings are still checked so
|
|
186
|
+
* typos fail loudly.
|
|
187
|
+
*/
|
|
188
|
+
export function normalizeRuntimeLinkConfig(cfg) {
|
|
189
|
+
const b = RUNTIME_LINK_BOUNDS;
|
|
190
|
+
const h = cfg.harness;
|
|
191
|
+
if ((h.kind && !isOneOf(HARNESS_KINDS, h.kind)) || (cfg.enabled && !h.kind)) {
|
|
192
|
+
throw new RuntimeLinkConfigError("harness_kind", "harness.kind");
|
|
193
|
+
}
|
|
194
|
+
if (h.version && !VERSION_RE.test(h.version)) {
|
|
195
|
+
throw new RuntimeLinkConfigError("envelope", "harness.version");
|
|
196
|
+
}
|
|
197
|
+
if (h.deploymentEnv && !ENV_NAME_RE.test(h.deploymentEnv)) {
|
|
198
|
+
throw new RuntimeLinkConfigError("envelope", "harness.deploymentEnv");
|
|
199
|
+
}
|
|
200
|
+
if (cfg.enabled) {
|
|
201
|
+
if (cfg.token.env === RUNTIME_LINK_ENV.legacyToken) {
|
|
202
|
+
throw new RuntimeLinkConfigError("legacy_token", "token");
|
|
203
|
+
}
|
|
204
|
+
if (cfg.token.env !== RUNTIME_LINK_ENV.token) {
|
|
205
|
+
throw new RuntimeLinkConfigError("invalid_value", "token");
|
|
206
|
+
}
|
|
207
|
+
validateControlPlaneUrl(cfg.controlPlaneUrl);
|
|
208
|
+
if (!cfg.binary.path || cfg.binary.path.includes("\0")) {
|
|
209
|
+
throw new RuntimeLinkConfigError("binary", "binary.path");
|
|
210
|
+
}
|
|
211
|
+
if (!cfg.binary.sha256 && h.deploymentEnv === "prod") {
|
|
212
|
+
throw new RuntimeLinkConfigError("binary", "binary.sha256");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (cfg.binary.sha256 && !SHA256_RE.test(cfg.binary.sha256)) {
|
|
216
|
+
throw new RuntimeLinkConfigError("binary", "binary.sha256");
|
|
217
|
+
}
|
|
218
|
+
const intervalMs = Math.min(Math.max(cfg.intervalMs, b.minIntervalMs), b.maxIntervalMs);
|
|
219
|
+
if (!(cfg.beatTimeoutMs > 0) || 2 * cfg.beatTimeoutMs >= intervalMs) {
|
|
220
|
+
throw new RuntimeLinkConfigError("beat_timeout", "beatTimeoutMs");
|
|
221
|
+
}
|
|
222
|
+
if (!inRange(cfg.graceMs, 0, b.maxGraceMs)) {
|
|
223
|
+
throw new RuntimeLinkConfigError("out_of_range", "graceMs");
|
|
224
|
+
}
|
|
225
|
+
const r = cfg.restart;
|
|
226
|
+
if (!(r.minMs >= b.minRestartMs) ||
|
|
227
|
+
!(r.maxMs <= b.maxRestartMs) ||
|
|
228
|
+
r.minMs > r.maxMs) {
|
|
229
|
+
throw new RuntimeLinkConfigError("out_of_range", "restart");
|
|
230
|
+
}
|
|
231
|
+
if (!inRange(r.stableResetMs, b.minStableResetMs, b.maxStableResetMs)) {
|
|
232
|
+
throw new RuntimeLinkConfigError("out_of_range", "restart.stableResetMs");
|
|
233
|
+
}
|
|
234
|
+
if (!inRange(cfg.stopTimeoutMs, b.minStopTimeoutMs, b.maxStopTimeoutMs)) {
|
|
235
|
+
throw new RuntimeLinkConfigError("out_of_range", "stopTimeoutMs");
|
|
236
|
+
}
|
|
237
|
+
if (!Number.isInteger(cfg.logCount) ||
|
|
238
|
+
!inRange(cfg.logCount, 0, b.maxLogCount)) {
|
|
239
|
+
throw new RuntimeLinkConfigError("out_of_range", "logCount");
|
|
240
|
+
}
|
|
241
|
+
return { ...cfg, intervalMs };
|
|
242
|
+
}
|
|
243
|
+
function inRange(v, lo, hi) {
|
|
244
|
+
return v >= lo && v <= hi;
|
|
245
|
+
}
|
|
246
|
+
function validateControlPlaneUrl(raw) {
|
|
247
|
+
let ok = false;
|
|
248
|
+
try {
|
|
249
|
+
const u = new URL(raw);
|
|
250
|
+
ok =
|
|
251
|
+
(u.protocol === "http:" || u.protocol === "https:") &&
|
|
252
|
+
u.hostname !== "" &&
|
|
253
|
+
u.username === "" &&
|
|
254
|
+
u.password === "";
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
ok = false;
|
|
258
|
+
}
|
|
259
|
+
if (!ok)
|
|
260
|
+
throw new RuntimeLinkConfigError("control_plane_url", "controlPlaneUrl");
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Build a normalized config from the spec §3.2 variables. Non-empty fields of
|
|
264
|
+
* `harness` override the KEI_HARNESS_* variables. The runtime token is only
|
|
265
|
+
* checked for presence; its value is never retained.
|
|
266
|
+
*/
|
|
267
|
+
export function runtimeLinkConfigFromEnv(env = process.env, harness = {}) {
|
|
268
|
+
const E = RUNTIME_LINK_ENV;
|
|
269
|
+
const get = (key) => (env[key] ?? "").trim();
|
|
270
|
+
const seconds = (key) => {
|
|
271
|
+
const raw = get(key);
|
|
272
|
+
if (raw === "")
|
|
273
|
+
return undefined;
|
|
274
|
+
if (!ENV_INT_RE.test(raw))
|
|
275
|
+
throw new RuntimeLinkConfigError("invalid_value", key);
|
|
276
|
+
return Number(raw) * 1000;
|
|
277
|
+
};
|
|
278
|
+
const cfg = defaultRuntimeLinkConfig();
|
|
279
|
+
cfg.intervalMs = seconds(E.interval) ?? cfg.intervalMs;
|
|
280
|
+
cfg.beatTimeoutMs = seconds(E.timeout) ?? cfg.beatTimeoutMs;
|
|
281
|
+
cfg.restart.minMs = seconds(E.restartMin) ?? cfg.restart.minMs;
|
|
282
|
+
cfg.restart.maxMs = seconds(E.restartMax) ?? cfg.restart.maxMs;
|
|
283
|
+
cfg.restart.stableResetMs =
|
|
284
|
+
seconds(E.stableSeconds) ?? cfg.restart.stableResetMs;
|
|
285
|
+
const logCount = get(E.logCount);
|
|
286
|
+
if (logCount !== "") {
|
|
287
|
+
if (!ENV_INT_RE.test(logCount))
|
|
288
|
+
throw new RuntimeLinkConfigError("invalid_value", E.logCount);
|
|
289
|
+
cfg.logCount = Number(logCount);
|
|
290
|
+
}
|
|
291
|
+
const hasToken = get(E.token) !== "";
|
|
292
|
+
if (!hasToken && get(E.legacyToken) !== "") {
|
|
293
|
+
throw new RuntimeLinkConfigError("legacy_token", E.legacyToken);
|
|
294
|
+
}
|
|
295
|
+
cfg.enabled = hasToken;
|
|
296
|
+
const enabled = get(E.enabled).toLowerCase();
|
|
297
|
+
if (enabled !== "") {
|
|
298
|
+
if (enabled === "true" || enabled === "1") {
|
|
299
|
+
if (!hasToken)
|
|
300
|
+
throw new RuntimeLinkConfigError("token_missing", E.token);
|
|
301
|
+
cfg.enabled = true;
|
|
302
|
+
}
|
|
303
|
+
else if (enabled === "false" || enabled === "0") {
|
|
304
|
+
cfg.enabled = false;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
throw new RuntimeLinkConfigError("invalid_value", E.enabled);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
cfg.controlPlaneUrl = get(E.controlPlaneUrl);
|
|
311
|
+
cfg.binary = {
|
|
312
|
+
path: get(E.proxyPath) || DEFAULT_RUNTIME_BINARY_PATH,
|
|
313
|
+
sha256: get(E.proxySha),
|
|
314
|
+
};
|
|
315
|
+
cfg.harness = {
|
|
316
|
+
kind: harness.kind || get(E.harnessKind),
|
|
317
|
+
version: harness.version || get(E.harnessVersion),
|
|
318
|
+
deploymentEnv: harness.deploymentEnv || get(E.deploymentEnv),
|
|
319
|
+
};
|
|
320
|
+
return normalizeRuntimeLinkConfig(cfg);
|
|
321
|
+
}
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Backoff and cancellation
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
const MAX_JITTER_SAMPLE = 1 - Number.EPSILON / 2;
|
|
326
|
+
/**
|
|
327
|
+
* Full-jitter restart delay for attempt n (0-based):
|
|
328
|
+
* `floor(r × min(maxMs, minMs × 2^n))`. `r` is clamped to [0, 1); negative
|
|
329
|
+
* attempts count as 0.
|
|
330
|
+
*/
|
|
331
|
+
export function backoffDelayMs(backoff, attempt, r) {
|
|
332
|
+
const n = Math.min(Math.max(Math.trunc(attempt), 0), 30);
|
|
333
|
+
const capMs = Math.min(backoff.maxMs, backoff.minMs * 2 ** n);
|
|
334
|
+
const sample = Math.min(Math.max(r, 0), MAX_JITTER_SAMPLE);
|
|
335
|
+
return Math.floor(sample * capMs);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Sleep for the jittered delay, or reject with the signal's abort reason as
|
|
339
|
+
* soon as `signal` aborts, so a stop during backoff never waits out the delay.
|
|
340
|
+
*/
|
|
341
|
+
export function waitBackoff(backoff, attempt, options = {}) {
|
|
342
|
+
const { signal, random = Math.random } = options;
|
|
343
|
+
return new Promise((resolve, reject) => {
|
|
344
|
+
const abortReason = () => signal?.reason ?? new Error("aborted");
|
|
345
|
+
if (signal?.aborted) {
|
|
346
|
+
reject(abortReason());
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const onAbort = () => {
|
|
350
|
+
clearTimeout(timer);
|
|
351
|
+
reject(abortReason());
|
|
352
|
+
};
|
|
353
|
+
const timer = setTimeout(() => {
|
|
354
|
+
signal?.removeEventListener("abort", onAbort);
|
|
355
|
+
resolve();
|
|
356
|
+
}, backoffDelayMs(backoff, attempt, random()));
|
|
357
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/** A child line the SDK must drop and count. */
|
|
361
|
+
export class ChildLineError extends Error {
|
|
362
|
+
kind;
|
|
363
|
+
constructor(kind, detail) {
|
|
364
|
+
super(`runtimeLink: child line ${kind}: ${detail}`);
|
|
365
|
+
this.kind = kind;
|
|
366
|
+
this.name = "ChildLineError";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const MAX_SEQ = Number.MAX_SAFE_INTEGER;
|
|
370
|
+
const MAX_LATENCY_MS = 3_600_000;
|
|
371
|
+
const MAX_NEXT_IN_MS = 3_600_000;
|
|
372
|
+
class FieldReader {
|
|
373
|
+
raw;
|
|
374
|
+
constructor(raw) {
|
|
375
|
+
this.raw = raw;
|
|
376
|
+
}
|
|
377
|
+
text(key, required) {
|
|
378
|
+
if (!Object.hasOwn(this.raw, key)) {
|
|
379
|
+
if (required)
|
|
380
|
+
throw new ChildLineError("malformed", key);
|
|
381
|
+
return "";
|
|
382
|
+
}
|
|
383
|
+
const v = this.raw[key];
|
|
384
|
+
if (typeof v !== "string")
|
|
385
|
+
throw new ChildLineError("malformed", key);
|
|
386
|
+
return v;
|
|
387
|
+
}
|
|
388
|
+
id(key, required) {
|
|
389
|
+
const v = this.text(key, required);
|
|
390
|
+
if ((v !== "" || required) && !ID_RE.test(v))
|
|
391
|
+
throw new ChildLineError("malformed", key);
|
|
392
|
+
return v;
|
|
393
|
+
}
|
|
394
|
+
timestamp(key) {
|
|
395
|
+
const v = this.text(key, true);
|
|
396
|
+
if (!TIMESTAMP_RE.test(v))
|
|
397
|
+
throw new ChildLineError("malformed", key);
|
|
398
|
+
return v;
|
|
399
|
+
}
|
|
400
|
+
integer(key, required, lo, hi) {
|
|
401
|
+
if (!Object.hasOwn(this.raw, key)) {
|
|
402
|
+
if (required)
|
|
403
|
+
throw new ChildLineError("malformed", key);
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
const v = this.raw[key];
|
|
407
|
+
if (typeof v !== "number" || !Number.isSafeInteger(v) || v < lo || v > hi) {
|
|
408
|
+
throw new ChildLineError("malformed", key);
|
|
409
|
+
}
|
|
410
|
+
return v;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const encoder = new TextEncoder();
|
|
414
|
+
const decoder = new TextDecoder();
|
|
415
|
+
/**
|
|
416
|
+
* Parse one line of child stdout (a trailing "\n" / "\r\n" is ignored). Only
|
|
417
|
+
* allowlisted fields survive; payloads, results, reasoning, tokens, or any
|
|
418
|
+
* other key the runtime might emit are dropped. Throws ChildLineError for
|
|
419
|
+
* lines the SDK must drop and count.
|
|
420
|
+
*/
|
|
421
|
+
export function parseChildLine(line) {
|
|
422
|
+
let text = typeof line === "string" ? line : decoder.decode(line);
|
|
423
|
+
if (text.endsWith("\n"))
|
|
424
|
+
text = text.slice(0, -1);
|
|
425
|
+
if (text.endsWith("\r"))
|
|
426
|
+
text = text.slice(0, -1);
|
|
427
|
+
if (encoder.encode(text).length > MAX_CHILD_LINE_BYTES) {
|
|
428
|
+
throw new ChildLineError("line_too_long", "line");
|
|
429
|
+
}
|
|
430
|
+
let raw;
|
|
431
|
+
try {
|
|
432
|
+
raw = JSON.parse(text);
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
throw new ChildLineError("malformed", "not json");
|
|
436
|
+
}
|
|
437
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
438
|
+
throw new ChildLineError("malformed", "not an object");
|
|
439
|
+
}
|
|
440
|
+
const obj = raw;
|
|
441
|
+
if (obj.v !== RUNTIME_LINK_CONTRACT_VERSION) {
|
|
442
|
+
throw new ChildLineError("contract_mismatch", "v");
|
|
443
|
+
}
|
|
444
|
+
if (typeof obj.event !== "string")
|
|
445
|
+
throw new ChildLineError("malformed", "event");
|
|
446
|
+
const f = new FieldReader(obj);
|
|
447
|
+
switch (obj.event) {
|
|
448
|
+
case "identity":
|
|
449
|
+
return {
|
|
450
|
+
kind: "identity",
|
|
451
|
+
identity: {
|
|
452
|
+
runId: f.id("run_id", true),
|
|
453
|
+
installationId: f.id("installation_id", true),
|
|
454
|
+
orgId: f.id("org_id", true),
|
|
455
|
+
workspaceId: f.id("workspace_id", false),
|
|
456
|
+
platform: f.id("platform", false),
|
|
457
|
+
status: f.id("status", false),
|
|
458
|
+
bindingStatus: f.id("binding_status", false),
|
|
459
|
+
runtimeVersion: f.id("runtime_version", false),
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
case "beat": {
|
|
463
|
+
const runId = f.id("run_id", true);
|
|
464
|
+
const seq = f.integer("seq", false, 0, MAX_SEQ);
|
|
465
|
+
const at = f.timestamp("at");
|
|
466
|
+
const outcome = f.text("outcome", true);
|
|
467
|
+
const latencyMs = f.integer("latency_ms", false, 0, MAX_LATENCY_MS);
|
|
468
|
+
const nextInMs = f.integer("next_in_ms", false, 0, MAX_NEXT_IN_MS);
|
|
469
|
+
const httpStatus = f.integer("http_status", false, 0, 599);
|
|
470
|
+
if (httpStatus > 0 && httpStatus < 100)
|
|
471
|
+
throw new ChildLineError("malformed", "http_status");
|
|
472
|
+
if (!isOneOf(BEAT_OUTCOMES, outcome))
|
|
473
|
+
throw new ChildLineError("contract_mismatch", "outcome");
|
|
474
|
+
return {
|
|
475
|
+
kind: "beat",
|
|
476
|
+
beat: { runId, seq, at, outcome, httpStatus, latencyMs, nextInMs },
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
case "terminal": {
|
|
480
|
+
const runId = f.id("run_id", true);
|
|
481
|
+
const reason = f.text("reason", true);
|
|
482
|
+
if (!REASON_RE.test(reason))
|
|
483
|
+
throw new ChildLineError("malformed", "reason");
|
|
484
|
+
return { kind: "terminal", terminal: { runId, reason } };
|
|
485
|
+
}
|
|
486
|
+
default:
|
|
487
|
+
return { kind: "ignored" };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// ---------------------------------------------------------------------------
|
|
491
|
+
// Redacted lifecycle events (spec §5.3)
|
|
492
|
+
// ---------------------------------------------------------------------------
|
|
493
|
+
/** A lifecycle event has a value outside its closed set or bounds. */
|
|
494
|
+
export class InvalidLinkEventError extends Error {
|
|
495
|
+
constructor(detail) {
|
|
496
|
+
super(`runtimeLink: invalid lifecycle event: ${detail}`);
|
|
497
|
+
this.name = "InvalidLinkEventError";
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function isCount(v) {
|
|
501
|
+
return typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
|
|
502
|
+
}
|
|
503
|
+
/** Throws InvalidLinkEventError unless every closed set and bound holds. */
|
|
504
|
+
export function validateLinkEvent(ev) {
|
|
505
|
+
const h = ev.harness;
|
|
506
|
+
const ok = isOneOf(LIFECYCLE_EVENT_NAMES, ev.name) &&
|
|
507
|
+
isOneOf(LINK_STATES, ev.state) &&
|
|
508
|
+
(ev.reason === "" || isOneOf(FAILURE_CLASSES, ev.reason)) &&
|
|
509
|
+
ev.at instanceof Date &&
|
|
510
|
+
!Number.isNaN(ev.at.getTime()) &&
|
|
511
|
+
(ev.runId === "" || ID_RE.test(ev.runId)) &&
|
|
512
|
+
ID_RE.test(ev.sdkInstanceId) &&
|
|
513
|
+
isOneOf(HARNESS_KINDS, h.kind) &&
|
|
514
|
+
isOneOf(SDK_LANGS, h.sdkLang) &&
|
|
515
|
+
(h.version === "" || VERSION_RE.test(h.version)) &&
|
|
516
|
+
(h.deploymentEnv === "" || ENV_NAME_RE.test(h.deploymentEnv)) &&
|
|
517
|
+
(h.sdkVersion === "" || VERSION_RE.test(h.sdkVersion)) &&
|
|
518
|
+
isCount(ev.seq) &&
|
|
519
|
+
isCount(ev.restarts) &&
|
|
520
|
+
isCount(ev.consecutiveFails);
|
|
521
|
+
if (!ok)
|
|
522
|
+
throw new InvalidLinkEventError("value outside contract");
|
|
523
|
+
return ev;
|
|
524
|
+
}
|
|
525
|
+
function str(v, fallback = "") {
|
|
526
|
+
if (v === undefined)
|
|
527
|
+
return fallback;
|
|
528
|
+
if (typeof v !== "string")
|
|
529
|
+
throw new InvalidLinkEventError("expected string");
|
|
530
|
+
return v;
|
|
531
|
+
}
|
|
532
|
+
function parseEventTimestamp(v) {
|
|
533
|
+
const m = typeof v === "string" ? TIMESTAMP_RE.exec(v) : null;
|
|
534
|
+
if (!m)
|
|
535
|
+
throw new InvalidLinkEventError("at");
|
|
536
|
+
const fraction = (m[2] ?? ".000").slice(0, 4);
|
|
537
|
+
return new Date(`${m[1]}${fraction}${m[3]}`);
|
|
538
|
+
}
|
|
539
|
+
/** Build an event from allowlisted wire keys only; everything else is dropped. */
|
|
540
|
+
export function linkEventFromWire(raw) {
|
|
541
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
542
|
+
throw new InvalidLinkEventError("not an object");
|
|
543
|
+
}
|
|
544
|
+
const o = raw;
|
|
545
|
+
const hRaw = o.harness ?? {};
|
|
546
|
+
if (hRaw === null || typeof hRaw !== "object" || Array.isArray(hRaw)) {
|
|
547
|
+
throw new InvalidLinkEventError("harness");
|
|
548
|
+
}
|
|
549
|
+
const h = hRaw;
|
|
550
|
+
return validateLinkEvent({
|
|
551
|
+
name: str(o.name),
|
|
552
|
+
at: parseEventTimestamp(o.at),
|
|
553
|
+
state: str(o.state),
|
|
554
|
+
reason: str(o.reason),
|
|
555
|
+
runId: str(o.run_id),
|
|
556
|
+
seq: (o.seq ?? 0),
|
|
557
|
+
sdkInstanceId: str(o.sdk_instance_id),
|
|
558
|
+
harness: {
|
|
559
|
+
kind: str(h.kind),
|
|
560
|
+
version: str(h.version),
|
|
561
|
+
deploymentEnv: str(h.deployment_env),
|
|
562
|
+
sdkLang: str(h.sdk_lang),
|
|
563
|
+
sdkVersion: str(h.sdk_version),
|
|
564
|
+
},
|
|
565
|
+
restarts: (o.restarts ?? 0),
|
|
566
|
+
consecutiveFails: (o.consecutive_fails ?? 0),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
/** Validate and emit exactly the allowlisted keys; `at` is UTC with millisecond precision. */
|
|
570
|
+
export function linkEventToWire(ev) {
|
|
571
|
+
validateLinkEvent(ev);
|
|
572
|
+
const h = ev.harness;
|
|
573
|
+
return {
|
|
574
|
+
name: ev.name,
|
|
575
|
+
at: ev.at.toISOString(),
|
|
576
|
+
state: ev.state,
|
|
577
|
+
reason: ev.reason,
|
|
578
|
+
run_id: ev.runId,
|
|
579
|
+
seq: ev.seq,
|
|
580
|
+
sdk_instance_id: ev.sdkInstanceId,
|
|
581
|
+
harness: {
|
|
582
|
+
kind: h.kind,
|
|
583
|
+
version: h.version,
|
|
584
|
+
deployment_env: h.deploymentEnv,
|
|
585
|
+
sdk_lang: h.sdkLang,
|
|
586
|
+
sdk_version: h.sdkVersion,
|
|
587
|
+
},
|
|
588
|
+
restarts: ev.restarts,
|
|
589
|
+
consecutive_fails: ev.consecutiveFails,
|
|
590
|
+
};
|
|
591
|
+
}
|
package/dist/llm/backend.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Message } from "./request.js";
|
|
2
|
+
import type { ToolDefinition } from "./request.js";
|
|
2
3
|
import type { Response } from "./response.js";
|
|
3
4
|
export interface Backend {
|
|
4
5
|
complete(messages: Message[]): Response;
|
|
@@ -6,4 +7,10 @@ export interface Backend {
|
|
|
6
7
|
modelName(): string;
|
|
7
8
|
contextWindowSize(): number;
|
|
8
9
|
}
|
|
10
|
+
export interface AsyncBackend {
|
|
11
|
+
complete(messages: Message[], tools?: ToolDefinition[]): Promise<Response>;
|
|
12
|
+
supportsNativeToolCalling(): boolean;
|
|
13
|
+
modelName(): string;
|
|
14
|
+
contextWindowSize(): number;
|
|
15
|
+
}
|
|
9
16
|
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/llm/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IACxC,yBAAyB,IAAI,OAAO,CAAC;IACrC,SAAS,IAAI,MAAM,CAAC;IACpB,iBAAiB,IAAI,MAAM,CAAC;CAC7B"}
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/llm/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IACxC,yBAAyB,IAAI,OAAO,CAAC;IACrC,SAAS,IAAI,MAAM,CAAC;IACpB,iBAAiB,IAAI,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3E,yBAAyB,IAAI,OAAO,CAAC;IACrC,SAAS,IAAI,MAAM,CAAC;IACpB,iBAAiB,IAAI,MAAM,CAAC;CAC7B"}
|
package/dist/llm/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { Backend } from "./backend.js";
|
|
1
|
+
export { Backend, AsyncBackend } from "./backend.js";
|
|
2
2
|
export { Message, Role, Request, ToolDefinition } from "./request.js";
|
|
3
3
|
export { Response, ToolCall, TokenUsage } from "./response.js";
|
|
4
|
+
export { OpenAIBackend, OpenAIBackendConfig, OpenAIError, FetchFn, FetchLikeInit, FetchLikeResponse, parseToolArguments, } from "./openai_backend.js";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/llm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC"}
|
package/dist/llm/index.js
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Message, ToolDefinition } from "./request.js";
|
|
2
|
+
import type { Response } from "./response.js";
|
|
3
|
+
import type { AsyncBackend } from "./backend.js";
|
|
4
|
+
export interface FetchLikeResponse {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
status: number;
|
|
7
|
+
text(): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export interface FetchLikeInit {
|
|
10
|
+
method?: string;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
body?: string;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export type FetchFn = (url: string, init?: FetchLikeInit) => Promise<FetchLikeResponse>;
|
|
16
|
+
export declare class OpenAIError extends Error {
|
|
17
|
+
readonly status?: number;
|
|
18
|
+
constructor(message: string, status?: number);
|
|
19
|
+
}
|
|
20
|
+
export interface OpenAIBackendConfig {
|
|
21
|
+
model: string;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
fetchFn?: FetchFn;
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
maxResponseBytes?: number;
|
|
27
|
+
maxToolArgBytes?: number;
|
|
28
|
+
contextWindowSize?: number;
|
|
29
|
+
temperature?: number;
|
|
30
|
+
maxTokens?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function parseToolArguments(raw: string | null | undefined, maxBytes: number): Record<string, unknown>;
|
|
33
|
+
export declare class OpenAIBackend implements AsyncBackend {
|
|
34
|
+
private readonly model;
|
|
35
|
+
private readonly baseUrl;
|
|
36
|
+
private readonly apiKey;
|
|
37
|
+
private readonly fetchFn;
|
|
38
|
+
private readonly timeoutMs;
|
|
39
|
+
private readonly maxResponseBytes;
|
|
40
|
+
private readonly maxToolArgBytes;
|
|
41
|
+
private readonly contextWindow;
|
|
42
|
+
private readonly temperature?;
|
|
43
|
+
private readonly maxTokens?;
|
|
44
|
+
constructor(config: OpenAIBackendConfig);
|
|
45
|
+
complete(messages: Message[], tools?: ToolDefinition[]): Promise<Response>;
|
|
46
|
+
private parseCompletion;
|
|
47
|
+
supportsNativeToolCalling(): boolean;
|
|
48
|
+
modelName(): string;
|
|
49
|
+
contextWindowSize(): number;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=openai_backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai_backend.d.ts","sourceRoot":"","sources":["../../src/llm/openai_backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAwB,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,aAAa,KACjB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhC,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAK7C;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwCD,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAkDD,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;gBAExB,MAAM,EAAE,mBAAmB;IAajC,QAAQ,CACZ,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,CAAC,EAAE,cAAc,EAAE,GACvB,OAAO,CAAC,QAAQ,CAAC;IA2EpB,OAAO,CAAC,eAAe;IA2CvB,yBAAyB,IAAI,OAAO;IAIpC,SAAS,IAAI,MAAM;IAInB,iBAAiB,IAAI,MAAM;CAG5B"}
|