@warmdrift/kgauto-compiler 2.0.0-alpha.17 → 2.0.0-alpha.18
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/dist/chunk-VZGMWKRT.mjs +19 -0
- package/dist/glassbox/index.d.mts +4 -121
- package/dist/glassbox/index.d.ts +4 -121
- package/dist/glassbox/index.mjs +4 -14
- package/dist/glassbox-routes/index.d.mts +73 -0
- package/dist/glassbox-routes/index.d.ts +73 -0
- package/dist/glassbox-routes/index.js +560 -0
- package/dist/glassbox-routes/index.mjs +269 -0
- package/dist/types-DWF6mPGg.d.mts +122 -0
- package/dist/types-xeklorHU.d.ts +122 -0
- package/package.json +7 -2
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
subscribe
|
|
3
|
+
} from "../chunk-VZGMWKRT.mjs";
|
|
4
|
+
import "../chunk-NUTC7NUC.mjs";
|
|
5
|
+
|
|
6
|
+
// src/glassbox-routes/auth.ts
|
|
7
|
+
import { Buffer } from "buffer";
|
|
8
|
+
import { timingSafeEqual } from "crypto";
|
|
9
|
+
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
10
|
+
function jsonError(status, code) {
|
|
11
|
+
return new Response(JSON.stringify({ error: code }), {
|
|
12
|
+
status,
|
|
13
|
+
headers: JSON_HEADERS
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function tokensEqual(a, b) {
|
|
17
|
+
if (a.length !== b.length) return false;
|
|
18
|
+
const ab = Buffer.from(a, "utf8");
|
|
19
|
+
const bb = Buffer.from(b, "utf8");
|
|
20
|
+
if (ab.length !== bb.length) return false;
|
|
21
|
+
return timingSafeEqual(ab, bb);
|
|
22
|
+
}
|
|
23
|
+
function checkAuth(req, config) {
|
|
24
|
+
const authHeader = req.headers.get("Authorization") ?? "";
|
|
25
|
+
const match = /^Bearer\s+(.+)$/i.exec(authHeader);
|
|
26
|
+
const provided = match?.[1]?.trim() ?? "";
|
|
27
|
+
if (!provided || !tokensEqual(provided, config.installToken)) {
|
|
28
|
+
return jsonError(401, "unauthorized");
|
|
29
|
+
}
|
|
30
|
+
const origin = req.headers.get("Origin") ?? "";
|
|
31
|
+
const expected = `chrome-extension://${config.extensionId}`;
|
|
32
|
+
if (origin !== expected) {
|
|
33
|
+
return jsonError(403, "forbidden_origin");
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/glassbox-routes/proxy.ts
|
|
39
|
+
var JSON_HEADERS2 = {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"Cache-Control": "no-store"
|
|
42
|
+
};
|
|
43
|
+
var DEFAULT_LIMIT = 20;
|
|
44
|
+
var MAX_LIMIT = 100;
|
|
45
|
+
function jsonResponse(status, body) {
|
|
46
|
+
return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
|
|
47
|
+
}
|
|
48
|
+
function jsonError2(status, code) {
|
|
49
|
+
return jsonResponse(status, { error: code });
|
|
50
|
+
}
|
|
51
|
+
function applyScrub(row, scrub) {
|
|
52
|
+
if (!scrub || row == null || typeof row !== "object") return row;
|
|
53
|
+
try {
|
|
54
|
+
return scrub(row);
|
|
55
|
+
} catch {
|
|
56
|
+
return row;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function parseLimit(raw) {
|
|
60
|
+
if (!raw) return DEFAULT_LIMIT;
|
|
61
|
+
const n = Number.parseInt(raw, 10);
|
|
62
|
+
if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
|
|
63
|
+
return Math.min(n, MAX_LIMIT);
|
|
64
|
+
}
|
|
65
|
+
function createProxyHandler(config) {
|
|
66
|
+
const {
|
|
67
|
+
installToken,
|
|
68
|
+
extensionId,
|
|
69
|
+
brainEndpoint,
|
|
70
|
+
brainJwt,
|
|
71
|
+
appId,
|
|
72
|
+
scrub,
|
|
73
|
+
fetch: fetchImpl
|
|
74
|
+
} = config;
|
|
75
|
+
const doFetch = fetchImpl ?? ((...args) => globalThis.fetch(...args));
|
|
76
|
+
const base = brainEndpoint.replace(/\/+$/, "");
|
|
77
|
+
return async function proxy(req) {
|
|
78
|
+
const authFail = checkAuth(req, { installToken, extensionId });
|
|
79
|
+
if (authFail) return authFail;
|
|
80
|
+
const url = new URL(req.url);
|
|
81
|
+
const traceId = url.searchParams.get("traceId");
|
|
82
|
+
const limit = parseLimit(url.searchParams.get("limit"));
|
|
83
|
+
const qs = new URLSearchParams();
|
|
84
|
+
qs.set("app_id", `eq.${appId}`);
|
|
85
|
+
if (traceId) {
|
|
86
|
+
qs.set("trace_id", `eq.${traceId}`);
|
|
87
|
+
} else {
|
|
88
|
+
qs.set("order", "created_at.desc");
|
|
89
|
+
qs.set("limit", String(limit));
|
|
90
|
+
}
|
|
91
|
+
const brainUrl = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
|
|
92
|
+
let brainRes;
|
|
93
|
+
try {
|
|
94
|
+
brainRes = await doFetch(brainUrl, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: {
|
|
97
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
98
|
+
apikey: brainJwt,
|
|
99
|
+
Accept: "application/json"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch {
|
|
103
|
+
return jsonError2(502, "brain_unavailable");
|
|
104
|
+
}
|
|
105
|
+
if (brainRes.status === 401 || brainRes.status === 403) {
|
|
106
|
+
return jsonError2(500, "brain_auth_misconfig");
|
|
107
|
+
}
|
|
108
|
+
if (brainRes.status >= 500) {
|
|
109
|
+
return jsonError2(502, "brain_unavailable");
|
|
110
|
+
}
|
|
111
|
+
if (!brainRes.ok) {
|
|
112
|
+
return jsonError2(400, "bad_request");
|
|
113
|
+
}
|
|
114
|
+
let rows;
|
|
115
|
+
try {
|
|
116
|
+
rows = await brainRes.json();
|
|
117
|
+
} catch {
|
|
118
|
+
return jsonError2(502, "brain_unavailable");
|
|
119
|
+
}
|
|
120
|
+
if (!Array.isArray(rows)) {
|
|
121
|
+
return jsonError2(502, "brain_unavailable");
|
|
122
|
+
}
|
|
123
|
+
const scrubbed = rows.map((row) => applyScrub(row, scrub));
|
|
124
|
+
return jsonResponse(200, scrubbed);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/glassbox-routes/stream.ts
|
|
129
|
+
var SSE_HEADERS = {
|
|
130
|
+
"Content-Type": "text/event-stream",
|
|
131
|
+
"Cache-Control": "no-cache, no-transform",
|
|
132
|
+
Connection: "keep-alive",
|
|
133
|
+
"X-Accel-Buffering": "no"
|
|
134
|
+
};
|
|
135
|
+
var JSON_HEADERS3 = { "Content-Type": "application/json" };
|
|
136
|
+
function jsonError3(status, code) {
|
|
137
|
+
return new Response(JSON.stringify({ error: code }), {
|
|
138
|
+
status,
|
|
139
|
+
headers: JSON_HEADERS3
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function applyScrub2(event, scrub) {
|
|
143
|
+
if (!scrub) return event;
|
|
144
|
+
try {
|
|
145
|
+
const out = scrub(event);
|
|
146
|
+
if (out && typeof out === "object" && typeof out.kind === "string" && typeof out.at === "number") {
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
return event;
|
|
150
|
+
} catch {
|
|
151
|
+
return event;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function sseFrame(eventName, data) {
|
|
155
|
+
const safeName = eventName.replace(/[\r\n]/g, "");
|
|
156
|
+
return `event: ${safeName}
|
|
157
|
+
data: ${JSON.stringify(data)}
|
|
158
|
+
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
function createStreamHandler(config, subscribe2) {
|
|
162
|
+
const { installToken, extensionId, scrub } = config;
|
|
163
|
+
return async function stream(req) {
|
|
164
|
+
const authFail = checkAuth(req, { installToken, extensionId });
|
|
165
|
+
if (authFail) return authFail;
|
|
166
|
+
const url = new URL(req.url);
|
|
167
|
+
const traceId = url.searchParams.get("traceId");
|
|
168
|
+
if (!traceId) {
|
|
169
|
+
return jsonError3(400, "missing_trace_id");
|
|
170
|
+
}
|
|
171
|
+
const source = subscribe2(traceId);
|
|
172
|
+
const encoder = new TextEncoder();
|
|
173
|
+
let sourceReader;
|
|
174
|
+
let cancelled = false;
|
|
175
|
+
const body = new ReadableStream({
|
|
176
|
+
async start(controller) {
|
|
177
|
+
controller.enqueue(encoder.encode(sseFrame("ready", {})));
|
|
178
|
+
sourceReader = source.getReader();
|
|
179
|
+
const signal = req.signal;
|
|
180
|
+
if (signal) {
|
|
181
|
+
if (signal.aborted) {
|
|
182
|
+
cancelled = true;
|
|
183
|
+
await sourceReader.cancel();
|
|
184
|
+
try {
|
|
185
|
+
controller.close();
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
signal.addEventListener(
|
|
191
|
+
"abort",
|
|
192
|
+
() => {
|
|
193
|
+
cancelled = true;
|
|
194
|
+
sourceReader?.cancel().catch(() => {
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
controller.close();
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{ once: true }
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
while (!cancelled) {
|
|
206
|
+
const { value, done } = await sourceReader.read();
|
|
207
|
+
if (done) break;
|
|
208
|
+
const scrubbed = applyScrub2(value, scrub);
|
|
209
|
+
controller.enqueue(
|
|
210
|
+
encoder.encode(sseFrame(scrubbed.kind, scrubbed))
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
} finally {
|
|
215
|
+
try {
|
|
216
|
+
controller.close();
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
sourceReader?.releaseLock();
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
cancel() {
|
|
226
|
+
cancelled = true;
|
|
227
|
+
sourceReader?.cancel().catch(() => {
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
return new Response(body, { status: 200, headers: SSE_HEADERS });
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/glassbox-routes/index.ts
|
|
236
|
+
function requireString(name, value) {
|
|
237
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
238
|
+
throw new Error(`createGlassboxRoutes: ${name} is required`);
|
|
239
|
+
}
|
|
240
|
+
return value;
|
|
241
|
+
}
|
|
242
|
+
function createGlassboxRoutes(config) {
|
|
243
|
+
const installToken = requireString("installToken", config.installToken);
|
|
244
|
+
const extensionId = requireString("extensionId", config.extensionId);
|
|
245
|
+
const brainEndpoint = requireString("brainEndpoint", config.brainEndpoint);
|
|
246
|
+
const brainJwt = requireString("brainJwt", config.brainJwt);
|
|
247
|
+
const appId = requireString("appId", config.appId);
|
|
248
|
+
const proxy = createProxyHandler({
|
|
249
|
+
installToken,
|
|
250
|
+
extensionId,
|
|
251
|
+
brainEndpoint,
|
|
252
|
+
brainJwt,
|
|
253
|
+
appId,
|
|
254
|
+
scrub: config.scrub,
|
|
255
|
+
fetch: config.fetch
|
|
256
|
+
});
|
|
257
|
+
const stream = createStreamHandler(
|
|
258
|
+
{
|
|
259
|
+
installToken,
|
|
260
|
+
extensionId,
|
|
261
|
+
scrub: config.scrub
|
|
262
|
+
},
|
|
263
|
+
config.subscribe ?? subscribe
|
|
264
|
+
);
|
|
265
|
+
return { proxy, stream };
|
|
266
|
+
}
|
|
267
|
+
export {
|
|
268
|
+
createGlassboxRoutes
|
|
269
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { j as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, g as CallAttempt } from './ir-C3P4gDt0.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Glass-Box observability types (alpha.17).
|
|
5
|
+
*
|
|
6
|
+
* The substrate for kgauto's in-flight observability surface — a Chrome MV3
|
|
7
|
+
* side panel that renders compile + execute + advisor outputs in real-time.
|
|
8
|
+
* See design doc:
|
|
9
|
+
* ~/.gstack/projects/stue-kgauto/stgreen-claude-serene-williamson-51a105-design-20260518-175356.md
|
|
10
|
+
*
|
|
11
|
+
* Wire shape is intentionally minimal:
|
|
12
|
+
* { kind, at, data } — discriminated union by `kind`.
|
|
13
|
+
*
|
|
14
|
+
* Critical-path safety (L-086 derived): emit MUST NEVER fail the user's
|
|
15
|
+
* `call()` invocation. emit.ts wraps every adapter write in try/catch +
|
|
16
|
+
* silently drops on error. Consumers in Vercel Edge runtimes can lose one
|
|
17
|
+
* event without losing the response.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Discriminator. Six event kinds cover the v1 substrate. New kinds added
|
|
22
|
+
* additively; the side-panel renderer treats unknown kinds as "ignore".
|
|
23
|
+
*/
|
|
24
|
+
type GlassboxEventKind = 'compile.start' | 'compile.done' | 'execute.attempt' | 'execute.success' | 'advisory.fired' | 'fallback.walked';
|
|
25
|
+
/**
|
|
26
|
+
* Wire envelope. Every emit lands as one of these.
|
|
27
|
+
*
|
|
28
|
+
* - kind: discriminator
|
|
29
|
+
* - at: unix milliseconds (Date.now())
|
|
30
|
+
* - data: kind-specific payload
|
|
31
|
+
*
|
|
32
|
+
* `data` typed loosely as Record<string, unknown> for serialization-friendliness;
|
|
33
|
+
* the per-kind builders below populate it with the structured shape expected
|
|
34
|
+
* by the renderer. We deliberately do NOT typescript-narrow `data` by kind on
|
|
35
|
+
* the wire type — the side panel reads JSON from SSE and only the renderer
|
|
36
|
+
* needs the narrowed shape. Internal callers (emit.ts) get type assistance via
|
|
37
|
+
* the typed factory helpers in emit.ts.
|
|
38
|
+
*/
|
|
39
|
+
interface GlassboxEvent {
|
|
40
|
+
kind: GlassboxEventKind;
|
|
41
|
+
at: number;
|
|
42
|
+
data: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Per-kind data shapes. Surfaced as type aids; not enforced at the
|
|
46
|
+
* GlassboxEvent boundary (intentionally — see comment above).
|
|
47
|
+
*/
|
|
48
|
+
interface CompileStartData {
|
|
49
|
+
appId: string;
|
|
50
|
+
archetype: string;
|
|
51
|
+
models: string[];
|
|
52
|
+
}
|
|
53
|
+
interface CompileDoneData {
|
|
54
|
+
target: string;
|
|
55
|
+
provider: string;
|
|
56
|
+
fallbackChain: string[];
|
|
57
|
+
tokensIn: number;
|
|
58
|
+
estimatedCostUsd: number;
|
|
59
|
+
mutationsApplied: MutationApplied[];
|
|
60
|
+
advisories: BestPracticeAdvisory[];
|
|
61
|
+
}
|
|
62
|
+
interface ExecuteAttemptData {
|
|
63
|
+
model: string;
|
|
64
|
+
attemptIndex: number;
|
|
65
|
+
}
|
|
66
|
+
interface ExecuteSuccessData {
|
|
67
|
+
model: string;
|
|
68
|
+
tokensIn: number;
|
|
69
|
+
tokensOut: number;
|
|
70
|
+
latencyMs: number;
|
|
71
|
+
}
|
|
72
|
+
interface AdvisoryFiredData {
|
|
73
|
+
code: string;
|
|
74
|
+
message: string;
|
|
75
|
+
}
|
|
76
|
+
interface FallbackWalkedData {
|
|
77
|
+
from: string;
|
|
78
|
+
to: string;
|
|
79
|
+
reason: FallbackReason | 'unknown';
|
|
80
|
+
attempt: CallAttempt;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Pub/sub backend interface. Two adapters implement it: in-memory (dev /
|
|
84
|
+
* single-process) and Upstash Redis Streams (Vercel Edge multi-instance).
|
|
85
|
+
*
|
|
86
|
+
* The choice is made at module load (NOT per-call) based on env-var presence:
|
|
87
|
+
* if (UPSTASH_REDIS_URL && UPSTASH_REDIS_TOKEN) → Upstash
|
|
88
|
+
* else → in-memory
|
|
89
|
+
*
|
|
90
|
+
* Stream TTL: 60s after last event. After that, subscriber stream closes
|
|
91
|
+
* cleanly. The adapter owns its own TTL clock; subscribe() is agnostic.
|
|
92
|
+
*/
|
|
93
|
+
interface GlassboxPubSub {
|
|
94
|
+
/**
|
|
95
|
+
* Publish a single event for a traceId. Returns a promise that resolves
|
|
96
|
+
* after the event is durably appended (or rejects on adapter failure —
|
|
97
|
+
* callers in emit.ts always swallow rejections).
|
|
98
|
+
*/
|
|
99
|
+
publish(traceId: string, event: GlassboxEvent): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Subscribe to events for a traceId. Returns a ReadableStream that emits
|
|
102
|
+
* GlassboxEvent objects as they're published. The stream closes cleanly
|
|
103
|
+
* after the per-trace TTL elapses since the LAST event (rolling 60s).
|
|
104
|
+
*
|
|
105
|
+
* If the traceId has no publisher within 60s of subscription, the stream
|
|
106
|
+
* closes empty.
|
|
107
|
+
*/
|
|
108
|
+
subscribe(traceId: string): ReadableStream<GlassboxEvent>;
|
|
109
|
+
/**
|
|
110
|
+
* Test-only escape hatch. Clears any in-memory state. Upstash adapter
|
|
111
|
+
* no-ops (server-side state isn't accessible from here). Tests reach for
|
|
112
|
+
* this between cases to keep adapters hermetic.
|
|
113
|
+
*/
|
|
114
|
+
_reset?(): void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* TTL applied to a stream after each event. The design doc names 60s; this
|
|
118
|
+
* constant centralizes it so both adapters + tests agree.
|
|
119
|
+
*/
|
|
120
|
+
declare const GLASSBOX_STREAM_TTL_MS = 60000;
|
|
121
|
+
|
|
122
|
+
export { type AdvisoryFiredData as A, type CompileDoneData as C, type ExecuteAttemptData as E, type FallbackWalkedData as F, type GlassboxEvent as G, type CompileStartData as a, type ExecuteSuccessData as b, GLASSBOX_STREAM_TTL_MS as c, type GlassboxEventKind as d, type GlassboxPubSub as e };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { j as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, g as CallAttempt } from './ir-CFHU3BUT.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Glass-Box observability types (alpha.17).
|
|
5
|
+
*
|
|
6
|
+
* The substrate for kgauto's in-flight observability surface — a Chrome MV3
|
|
7
|
+
* side panel that renders compile + execute + advisor outputs in real-time.
|
|
8
|
+
* See design doc:
|
|
9
|
+
* ~/.gstack/projects/stue-kgauto/stgreen-claude-serene-williamson-51a105-design-20260518-175356.md
|
|
10
|
+
*
|
|
11
|
+
* Wire shape is intentionally minimal:
|
|
12
|
+
* { kind, at, data } — discriminated union by `kind`.
|
|
13
|
+
*
|
|
14
|
+
* Critical-path safety (L-086 derived): emit MUST NEVER fail the user's
|
|
15
|
+
* `call()` invocation. emit.ts wraps every adapter write in try/catch +
|
|
16
|
+
* silently drops on error. Consumers in Vercel Edge runtimes can lose one
|
|
17
|
+
* event without losing the response.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Discriminator. Six event kinds cover the v1 substrate. New kinds added
|
|
22
|
+
* additively; the side-panel renderer treats unknown kinds as "ignore".
|
|
23
|
+
*/
|
|
24
|
+
type GlassboxEventKind = 'compile.start' | 'compile.done' | 'execute.attempt' | 'execute.success' | 'advisory.fired' | 'fallback.walked';
|
|
25
|
+
/**
|
|
26
|
+
* Wire envelope. Every emit lands as one of these.
|
|
27
|
+
*
|
|
28
|
+
* - kind: discriminator
|
|
29
|
+
* - at: unix milliseconds (Date.now())
|
|
30
|
+
* - data: kind-specific payload
|
|
31
|
+
*
|
|
32
|
+
* `data` typed loosely as Record<string, unknown> for serialization-friendliness;
|
|
33
|
+
* the per-kind builders below populate it with the structured shape expected
|
|
34
|
+
* by the renderer. We deliberately do NOT typescript-narrow `data` by kind on
|
|
35
|
+
* the wire type — the side panel reads JSON from SSE and only the renderer
|
|
36
|
+
* needs the narrowed shape. Internal callers (emit.ts) get type assistance via
|
|
37
|
+
* the typed factory helpers in emit.ts.
|
|
38
|
+
*/
|
|
39
|
+
interface GlassboxEvent {
|
|
40
|
+
kind: GlassboxEventKind;
|
|
41
|
+
at: number;
|
|
42
|
+
data: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Per-kind data shapes. Surfaced as type aids; not enforced at the
|
|
46
|
+
* GlassboxEvent boundary (intentionally — see comment above).
|
|
47
|
+
*/
|
|
48
|
+
interface CompileStartData {
|
|
49
|
+
appId: string;
|
|
50
|
+
archetype: string;
|
|
51
|
+
models: string[];
|
|
52
|
+
}
|
|
53
|
+
interface CompileDoneData {
|
|
54
|
+
target: string;
|
|
55
|
+
provider: string;
|
|
56
|
+
fallbackChain: string[];
|
|
57
|
+
tokensIn: number;
|
|
58
|
+
estimatedCostUsd: number;
|
|
59
|
+
mutationsApplied: MutationApplied[];
|
|
60
|
+
advisories: BestPracticeAdvisory[];
|
|
61
|
+
}
|
|
62
|
+
interface ExecuteAttemptData {
|
|
63
|
+
model: string;
|
|
64
|
+
attemptIndex: number;
|
|
65
|
+
}
|
|
66
|
+
interface ExecuteSuccessData {
|
|
67
|
+
model: string;
|
|
68
|
+
tokensIn: number;
|
|
69
|
+
tokensOut: number;
|
|
70
|
+
latencyMs: number;
|
|
71
|
+
}
|
|
72
|
+
interface AdvisoryFiredData {
|
|
73
|
+
code: string;
|
|
74
|
+
message: string;
|
|
75
|
+
}
|
|
76
|
+
interface FallbackWalkedData {
|
|
77
|
+
from: string;
|
|
78
|
+
to: string;
|
|
79
|
+
reason: FallbackReason | 'unknown';
|
|
80
|
+
attempt: CallAttempt;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Pub/sub backend interface. Two adapters implement it: in-memory (dev /
|
|
84
|
+
* single-process) and Upstash Redis Streams (Vercel Edge multi-instance).
|
|
85
|
+
*
|
|
86
|
+
* The choice is made at module load (NOT per-call) based on env-var presence:
|
|
87
|
+
* if (UPSTASH_REDIS_URL && UPSTASH_REDIS_TOKEN) → Upstash
|
|
88
|
+
* else → in-memory
|
|
89
|
+
*
|
|
90
|
+
* Stream TTL: 60s after last event. After that, subscriber stream closes
|
|
91
|
+
* cleanly. The adapter owns its own TTL clock; subscribe() is agnostic.
|
|
92
|
+
*/
|
|
93
|
+
interface GlassboxPubSub {
|
|
94
|
+
/**
|
|
95
|
+
* Publish a single event for a traceId. Returns a promise that resolves
|
|
96
|
+
* after the event is durably appended (or rejects on adapter failure —
|
|
97
|
+
* callers in emit.ts always swallow rejections).
|
|
98
|
+
*/
|
|
99
|
+
publish(traceId: string, event: GlassboxEvent): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Subscribe to events for a traceId. Returns a ReadableStream that emits
|
|
102
|
+
* GlassboxEvent objects as they're published. The stream closes cleanly
|
|
103
|
+
* after the per-trace TTL elapses since the LAST event (rolling 60s).
|
|
104
|
+
*
|
|
105
|
+
* If the traceId has no publisher within 60s of subscription, the stream
|
|
106
|
+
* closes empty.
|
|
107
|
+
*/
|
|
108
|
+
subscribe(traceId: string): ReadableStream<GlassboxEvent>;
|
|
109
|
+
/**
|
|
110
|
+
* Test-only escape hatch. Clears any in-memory state. Upstash adapter
|
|
111
|
+
* no-ops (server-side state isn't accessible from here). Tests reach for
|
|
112
|
+
* this between cases to keep adapters hermetic.
|
|
113
|
+
*/
|
|
114
|
+
_reset?(): void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* TTL applied to a stream after each event. The design doc names 60s; this
|
|
118
|
+
* constant centralizes it so both adapters + tests agree.
|
|
119
|
+
*/
|
|
120
|
+
declare const GLASSBOX_STREAM_TTL_MS = 60000;
|
|
121
|
+
|
|
122
|
+
export { type AdvisoryFiredData as A, type CompileDoneData as C, type ExecuteAttemptData as E, type FallbackWalkedData as F, type GlassboxEvent as G, type CompileStartData as a, type ExecuteSuccessData as b, GLASSBOX_STREAM_TTL_MS as c, type GlassboxEventKind as d, type GlassboxPubSub as e };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warmdrift/kgauto-compiler",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.18",
|
|
4
4
|
"description": "Prompt compiler + central learning brain for multi-model AI apps. Swap models without rewriting prompts.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -25,11 +25,16 @@
|
|
|
25
25
|
"types": "./dist/glassbox/index.d.ts",
|
|
26
26
|
"import": "./dist/glassbox/index.mjs",
|
|
27
27
|
"require": "./dist/glassbox/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./glassbox-routes": {
|
|
30
|
+
"types": "./dist/glassbox-routes/index.d.ts",
|
|
31
|
+
"import": "./dist/glassbox-routes/index.mjs",
|
|
32
|
+
"require": "./dist/glassbox-routes/index.js"
|
|
28
33
|
}
|
|
29
34
|
},
|
|
30
35
|
"files": ["dist", "README.md"],
|
|
31
36
|
"scripts": {
|
|
32
|
-
"build": "tsup src/index.ts src/dialect.ts src/profiles.ts src/glassbox/index.ts --format cjs,esm --dts --clean",
|
|
37
|
+
"build": "tsup src/index.ts src/dialect.ts src/profiles.ts src/glassbox/index.ts src/glassbox-routes/index.ts --format cjs,esm --dts --clean",
|
|
33
38
|
"test": "vitest run",
|
|
34
39
|
"test:watch": "vitest",
|
|
35
40
|
"typecheck": "tsc --noEmit",
|