@shpitdev/convex-cliproxy-observability 0.3.5 → 0.3.7

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 (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -2
  3. package/dist/capture/content-health.d.ts +29 -0
  4. package/dist/capture/content-health.d.ts.map +1 -0
  5. package/dist/capture/content-health.js +29 -0
  6. package/dist/capture/content-health.js.map +1 -0
  7. package/dist/capture/index.d.ts +26 -2
  8. package/dist/capture/index.d.ts.map +1 -1
  9. package/dist/capture/index.js +99 -7
  10. package/dist/capture/index.js.map +1 -1
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +3 -0
  13. package/dist/client.js.map +1 -1
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts.map +1 -1
  17. package/dist/component/ingest.d.ts +13 -0
  18. package/dist/component/ingest.d.ts.map +1 -1
  19. package/dist/component/ingest.js +4 -0
  20. package/dist/component/ingest.js.map +1 -1
  21. package/dist/model-call/index.d.ts +3 -1
  22. package/dist/model-call/index.d.ts.map +1 -1
  23. package/dist/model-call/index.js.map +1 -1
  24. package/dist/projection.d.ts +6 -3
  25. package/dist/projection.d.ts.map +1 -1
  26. package/dist/projection.js +55 -7
  27. package/dist/projection.js.map +1 -1
  28. package/dist/protocols/body-parts.d.ts +19 -0
  29. package/dist/protocols/body-parts.d.ts.map +1 -0
  30. package/dist/protocols/body-parts.js +56 -0
  31. package/dist/protocols/body-parts.js.map +1 -0
  32. package/dist/protocols/checkpoint.d.ts +16 -1
  33. package/dist/protocols/checkpoint.d.ts.map +1 -1
  34. package/dist/protocols/checkpoint.js +69 -24
  35. package/dist/protocols/checkpoint.js.map +1 -1
  36. package/dist/protocols/index.d.ts +3 -1
  37. package/dist/protocols/index.d.ts.map +1 -1
  38. package/dist/protocols/index.js +2 -0
  39. package/dist/protocols/index.js.map +1 -1
  40. package/dist/protocols/stock-block.d.ts +3 -0
  41. package/dist/protocols/stock-block.d.ts.map +1 -0
  42. package/dist/protocols/stock-block.js +17 -0
  43. package/dist/protocols/stock-block.js.map +1 -0
  44. package/package.json +1 -1
  45. package/src/capture/content-health.ts +32 -0
  46. package/src/capture/index.ts +139 -9
  47. package/src/client.ts +3 -0
  48. package/src/component/_generated/api.ts +5 -6
  49. package/src/component/_generated/component.ts +6 -6
  50. package/src/component/ingest.ts +3 -0
  51. package/src/model-call/index.ts +3 -1
  52. package/src/projection.ts +71 -16
  53. package/src/protocols/body-parts.ts +58 -0
  54. package/src/protocols/checkpoint.ts +76 -22
  55. package/src/protocols/index.ts +4 -1
  56. package/src/protocols/stock-block.ts +18 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Recover stock parser boundaries without altering the captured bytes. */
2
+ export declare function expandStockHookBlock(body: Uint8Array, lengths: readonly number[]): readonly Uint8Array[];
3
+ //# sourceMappingURL=stock-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-block.d.ts","sourceRoot":"","sources":["../../src/protocols/stock-block.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,SAAS,UAAU,EAAE,CAavB"}
@@ -0,0 +1,17 @@
1
+ /** Recover stock parser boundaries without altering the captured bytes. */
2
+ export function expandStockHookBlock(body, lengths) {
3
+ if (!Array.isArray(lengths) || lengths.length === 0 || lengths.length > 1024)
4
+ throw new Error("invalid stock hook block lengths");
5
+ const pieces = [];
6
+ let offset = 0;
7
+ for (const length of lengths) {
8
+ if (!Number.isSafeInteger(length) || length < 0 || length > body.byteLength - offset)
9
+ throw new Error("invalid stock hook block length");
10
+ pieces.push(body.subarray(offset, offset + length));
11
+ offset += length;
12
+ }
13
+ if (offset !== body.byteLength)
14
+ throw new Error("stock hook block byte mismatch");
15
+ return pieces;
16
+ }
17
+ //# sourceMappingURL=stock-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-block.js","sourceRoot":"","sources":["../../src/protocols/stock-block.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAClC,IAAgB,EAChB,OAA0B;IAE1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI;QAC1E,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC,UAAU,GAAG,MAAM;YAClF,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,MAAM,CAAC;IACnB,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAClF,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shpitdev/convex-cliproxy-observability",
3
3
  "description": "Private stock CLIProxy capture, protocol projections, and bounded model-call reads.",
4
- "version": "0.3.5",
4
+ "version": "0.3.7",
5
5
  "author": "Anand Pant",
6
6
  "license": "Apache-2.0",
7
7
  "repository": "github:anandpant/convex-components",
@@ -0,0 +1,32 @@
1
+ import { v } from "convex/values";
2
+
3
+ /** Local durable ACK counts. They do not claim remote persistence or projection. */
4
+ export const contentHealthValidator = v.object({
5
+ observedBytes: v.number(),
6
+ committedBytes: v.number(),
7
+ lostBytes: v.number(),
8
+ unconfirmedBytes: v.number(),
9
+ unknownLosses: v.number(),
10
+ /** Native 0.2.5: enrolled calls that could not start a scope. Optional for older senders. */
11
+ missedCalls: v.optional(v.number()),
12
+ streamCallbacks: v.number(),
13
+ incompleteCalls: v.number(),
14
+ capacityRejections: v.number(),
15
+ retainedChargedBytes: v.number(),
16
+ peakChargedBytes: v.number(),
17
+ });
18
+
19
+ export function validContentHealth(value: unknown): boolean {
20
+ if (value === undefined) return true;
21
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
22
+ const counters = value as Record<string, unknown>;
23
+ const fields = Object.keys(contentHealthValidator.fields);
24
+ return (
25
+ Object.keys(counters).every((key) => fields.includes(key)) &&
26
+ fields.every((key) => key === "missedCalls" || key in counters) &&
27
+ Object.values(counters).every(
28
+ (n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 0,
29
+ ) &&
30
+ (counters.retainedChargedBytes as number) <= (counters.peakChargedBytes as number)
31
+ );
32
+ }
@@ -1,6 +1,9 @@
1
+ import { expandStockHookBlock } from "../protocols/stock-block.js";
1
2
  import type { ExactCorrelation } from "../model-call/index.js";
2
3
  export const MAX_ENVELOPE_BYTES = 2 * 1024 * 1024;
3
4
  export const MAX_CONTENT_BYTES = 1024 * 1024;
5
+ /** One split body spans at most the producer's 256 shared data-record slots. */
6
+ export const MAX_BODY_PARTS = 256;
4
7
  const id = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$/;
5
8
  const routes = new Set([
6
9
  "GET /v1/models",
@@ -9,12 +12,26 @@ const routes = new Set([
9
12
  "POST /v1/responses",
10
13
  "POST /v1/chat/completions",
11
14
  ]);
15
+ /**
16
+ * Native 0.2.5 splits a body larger than `MAX_CONTENT_BYTES` into exact ordered
17
+ * parts with consecutive sequences. Every part but the last holds exactly
18
+ * `MAX_CONTENT_BYTES`; `offset` is `index * MAX_CONTENT_BYTES`. Concatenate in
19
+ * index order and verify `totalBytes`/`totalSha256` (see `/protocols`
20
+ * `BodyPartAssembler`). A split stream callback is one stock callback.
21
+ */
22
+ export type BodyPartV1 = {
23
+ index: number;
24
+ count: number;
25
+ offset: number;
26
+ totalBytes: number;
27
+ totalSha256: string;
28
+ };
12
29
  export type CaptureObservationV1 = {
13
30
  schemaVersion: 1;
14
31
  pluginVersion: string;
15
32
  /** Historical redacted observations retain their original policy label. */
16
33
  redactionVersion?: string;
17
- capturePolicy?: "hook-body-v1";
34
+ capturePolicy?: "hook-body-v1" | "hook-content-block-v1";
18
35
  destinationId: string;
19
36
  instanceId: string;
20
37
  pluginBootId: string;
@@ -45,7 +62,12 @@ export type CaptureObservationV1 = {
45
62
  stockChunkIndex?: number;
46
63
  body?: string;
47
64
  observedBodyBytes?: number;
48
- bodyFraming?: "stock_json_chunk" | "stock_sse_candidate" | "stock_hook_chunk";
65
+ bodyFraming?:
66
+ "stock_json_chunk" | "stock_sse_candidate" | "stock_hook_chunk" | "stock_hook_block";
67
+ stockHookChunkLengths?: number[];
68
+ bodyPart?: BodyPartV1;
69
+ lostContentBytes?: number;
70
+ captureIncomplete?: boolean;
49
71
  contentSha256: string;
50
72
  contentBytes: number;
51
73
  completionOutcome?: string;
@@ -135,6 +157,10 @@ export async function validateObservation(
135
157
  "correlation",
136
158
  "correlationConflicts",
137
159
  "stockChunkIndex",
160
+ "stockHookChunkLengths",
161
+ "bodyPart",
162
+ "lostContentBytes",
163
+ "captureIncomplete",
138
164
  "body",
139
165
  "observedBodyBytes",
140
166
  "bodyFraming",
@@ -234,11 +260,17 @@ export async function validateObservation(
234
260
  if (o[field] !== undefined && (typeof o[field] !== "string" || o[field]!.length > 256))
235
261
  throw new Error("invalid optional metadata");
236
262
  }
237
- if (!o.pluginVersion || (!o.redactionVersion && o.capturePolicy !== "hook-body-v1"))
263
+ if (
264
+ !o.pluginVersion ||
265
+ (!o.redactionVersion &&
266
+ o.capturePolicy !== "hook-body-v1" &&
267
+ o.capturePolicy !== "hook-content-block-v1")
268
+ )
238
269
  throw new Error("capture versions required");
239
270
  if (
240
271
  o.capturePolicy !== undefined &&
241
- (o.capturePolicy !== "hook-body-v1" || o.redactionVersion !== undefined)
272
+ (!["hook-body-v1", "hook-content-block-v1"].includes(o.capturePolicy) ||
273
+ o.redactionVersion !== undefined)
242
274
  )
243
275
  throw new Error("invalid capture policy");
244
276
  if (
@@ -248,11 +280,17 @@ export async function validateObservation(
248
280
  o.metadataOmissions.some((x) => typeof x !== "string" || x.length > 64))
249
281
  )
250
282
  throw new Error("invalid metadata omissions");
251
- if (o.error !== undefined && (typeof o.error !== "string" || o.error.length > 4096))
283
+ if (
284
+ o.error !== undefined &&
285
+ (typeof o.error !== "string" ||
286
+ new TextEncoder().encode(o.error).length >
287
+ (o.capturePolicy === "hook-content-block-v1" ? MAX_CONTENT_BYTES : 4096))
288
+ )
252
289
  throw new Error("invalid error detail");
253
290
  if (
254
291
  (o.kind === "request_after_auth" || o.bodyFraming === "stock_hook_chunk") &&
255
- o.capturePolicy !== "hook-body-v1"
292
+ o.capturePolicy !== "hook-body-v1" &&
293
+ o.capturePolicy !== "hook-content-block-v1"
256
294
  )
257
295
  throw new Error("raw hook policy required");
258
296
  if (
@@ -263,14 +301,52 @@ export async function validateObservation(
263
301
  throw new Error("raw hook provenance required");
264
302
  if (
265
303
  o.kind === "completion" &&
266
- !["succeeded", "failed", "canceled", "rejected"].includes(o.completionOutcome ?? "")
304
+ !["succeeded", "failed", "canceled", "rejected"].includes(o.completionOutcome ?? "") &&
305
+ !(
306
+ o.capturePolicy === "hook-content-block-v1" &&
307
+ o.captureIncomplete === true &&
308
+ o.completionOutcome === undefined &&
309
+ ["capture_shutdown_incomplete", "scope_expired"].includes(o.gap ?? "")
310
+ )
267
311
  )
268
312
  throw new Error("invalid completion outcome");
269
313
  if (
270
314
  o.bodyFraming !== undefined &&
271
- !["stock_json_chunk", "stock_sse_candidate", "stock_hook_chunk"].includes(o.bodyFraming)
315
+ !["stock_json_chunk", "stock_sse_candidate", "stock_hook_chunk", "stock_hook_block"].includes(
316
+ o.bodyFraming,
317
+ )
272
318
  )
273
319
  throw new Error("invalid body framing");
320
+ const body = decodeBody(o);
321
+ if (o.bodyPart !== undefined) validateBodyPart(o, body);
322
+ if (o.bodyFraming === "stock_hook_block") {
323
+ if (o.capturePolicy !== "hook-content-block-v1" || o.kind !== "stream_chunk")
324
+ throw new Error("stock block policy required");
325
+ expandStockHookBlock(body, o.stockHookChunkLengths ?? []);
326
+ } else if (o.stockHookChunkLengths !== undefined) {
327
+ throw new Error("unexpected stock block lengths");
328
+ }
329
+ if (
330
+ o.capturePolicy === "hook-content-block-v1" &&
331
+ (o.bodyFromSequence !== undefined ||
332
+ (o.kind === "stream_chunk" && o.bodyFraming !== "stock_hook_block"))
333
+ )
334
+ throw new Error("content block provenance required");
335
+ if (
336
+ o.lostContentBytes !== undefined &&
337
+ (o.capturePolicy !== "hook-content-block-v1" ||
338
+ !Number.isSafeInteger(o.lostContentBytes) ||
339
+ o.lostContentBytes < 0 ||
340
+ (o.lostContentBytes > 0 && o.captureIncomplete !== true))
341
+ )
342
+ throw new Error("invalid content loss count");
343
+ if (
344
+ o.captureIncomplete !== undefined &&
345
+ (o.capturePolicy !== "hook-content-block-v1" ||
346
+ typeof o.captureIncomplete !== "boolean" ||
347
+ (o.captureIncomplete && !o.gap))
348
+ )
349
+ throw new Error("invalid incomplete capture");
274
350
  if (
275
351
  o.observedBodyBytes !== undefined &&
276
352
  (!Number.isSafeInteger(o.observedBodyBytes) ||
@@ -287,7 +363,6 @@ export async function validateObservation(
287
363
  o.observedErrorBytes > MAX_ENVELOPE_BYTES)
288
364
  )
289
365
  throw new Error("invalid observed diagnostic size");
290
- const body = decodeBody(o);
291
366
  if ((await sha256(body)) !== o.contentSha256) throw new Error("content digest mismatch");
292
367
  return {
293
368
  observation: o,
@@ -298,6 +373,53 @@ export async function validateObservation(
298
373
  };
299
374
  }
300
375
 
376
+ const partKinds = new Set(["request", "request_after_auth", "response", "stream_chunk"]);
377
+ function validateBodyPart(o: CaptureObservationV1, body: Uint8Array) {
378
+ const part: unknown = o.bodyPart;
379
+ const keys = ["index", "count", "offset", "totalBytes", "totalSha256"];
380
+ if (
381
+ !part ||
382
+ typeof part !== "object" ||
383
+ Array.isArray(part) ||
384
+ Object.keys(part).length !== keys.length ||
385
+ !keys.every((key) => key in part)
386
+ )
387
+ throw new Error("invalid body part");
388
+ const { index, count, offset, totalBytes, totalSha256 } = part as BodyPartV1;
389
+ if (
390
+ o.capturePolicy !== "hook-content-block-v1" ||
391
+ !partKinds.has(o.kind) ||
392
+ ![index, count, offset, totalBytes].every(Number.isSafeInteger) ||
393
+ count < 2 ||
394
+ count > MAX_BODY_PARTS ||
395
+ index < 0 ||
396
+ index >= count ||
397
+ offset !== index * MAX_CONTENT_BYTES ||
398
+ totalBytes <= (count - 1) * MAX_CONTENT_BYTES ||
399
+ totalBytes > count * MAX_CONTENT_BYTES ||
400
+ body.byteLength !== Math.min(MAX_CONTENT_BYTES, totalBytes - offset) ||
401
+ typeof totalSha256 !== "string" ||
402
+ !/^[a-f0-9]{64}$/.test(totalSha256)
403
+ )
404
+ throw new Error("invalid body part");
405
+ }
406
+ type PartPosition = Pick<CaptureObservationV1, "kind" | "sequence" | "bodyPart">;
407
+ /** True when `next` is the part that must follow `previous` in one split body. */
408
+ export function continuesBodyPart(previous: PartPosition, next: PartPosition) {
409
+ const a = previous.bodyPart;
410
+ const b = next.bodyPart;
411
+ return (
412
+ !!a &&
413
+ !!b &&
414
+ next.kind === previous.kind &&
415
+ next.sequence === previous.sequence + 1 &&
416
+ b.index === a.index + 1 &&
417
+ b.count === a.count &&
418
+ b.totalBytes === a.totalBytes &&
419
+ b.totalSha256 === a.totalSha256
420
+ );
421
+ }
422
+
301
423
  /** One immutable NDJSON segment batches observations: no per-token Convex writes. */
302
424
  export type CaptureSegmentV1 = {
303
425
  schemaVersion: 1;
@@ -368,6 +490,14 @@ export async function validateSegment(
368
490
  )
369
491
  )
370
492
  throw new Error("inconsistent segment lifecycle");
493
+ // Parts of one split body are consecutive; neighbors within a segment must chain.
494
+ for (const [index, o] of observations.entries()) {
495
+ const previous = observations[index - 1];
496
+ if (!previous) continue;
497
+ const open = previous.bodyPart && previous.bodyPart.index < previous.bodyPart.count - 1;
498
+ if ((open || (o.bodyPart && o.bodyPart.index > 0)) && !continuesBodyPart(previous, o))
499
+ throw new Error("body part order mismatch");
500
+ }
371
501
  const callId = await callIdentity(first);
372
502
  const identity = await sha256(
373
503
  new TextEncoder().encode(
package/src/client.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { validContentHealth } from "./capture/content-health.js";
1
2
  import { validCapacity } from "./capture/capacity.js";
2
3
  import type {
3
4
  GenericActionCtx,
@@ -223,9 +224,11 @@ export async function handleCliproxyCaptureRequest(
223
224
  expiredScopesTotal: candidate.expiredScopesTotal,
224
225
  activeCalls: candidate.activeCalls,
225
226
  ...(candidate.capacity === undefined ? {} : { capacity: candidate.capacity }),
227
+ ...(candidate.content === undefined ? {} : { content: candidate.content }),
226
228
  };
227
229
  if (
228
230
  !validCapacity(candidate.capacity) ||
231
+ !validContentHealth(candidate.content) ||
229
232
  typeof args.startedAt !== "string" ||
230
233
  typeof args.observedAt !== "string" ||
231
234
  !Number.isFinite(Date.parse(args.startedAt)) ||
@@ -2,11 +2,7 @@
2
2
 
3
3
  import type * as ingest from "../ingest.js";
4
4
  import type * as queries from "../queries.js";
5
- import type {
6
- ApiFromModules,
7
- FilterApi,
8
- FunctionReference,
9
- } from "convex/server";
5
+ import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
10
6
  import { anyApi, componentsGeneric } from "convex/server";
11
7
 
12
8
  const fullApi: ApiFromModules<{
@@ -15,5 +11,8 @@ const fullApi: ApiFromModules<{
15
11
  }> = anyApi as any;
16
12
 
17
13
  export const api: FilterApi<typeof fullApi, FunctionReference<any, "public">> = anyApi as any;
18
- export const internal: FilterApi<typeof fullApi, FunctionReference<any, "internal">> = anyApi as any;
14
+ export const internal: FilterApi<
15
+ typeof fullApi,
16
+ FunctionReference<any, "internal">
17
+ > = anyApi as any;
19
18
  export const components = componentsGeneric() as unknown as {};
@@ -10,9 +10,9 @@ type ComponentQuery<Registered, Name extends string | undefined> =
10
10
  : never;
11
11
 
12
12
  type PublicQueries<Module, Name extends string | undefined> = {
13
- [Key in keyof Module as Module[Key] extends RegisteredQuery<"public", any, any>
14
- ? Key
15
- : never]: ComponentQuery<Module[Key], Name>;
13
+ [
14
+ Key in keyof Module as Module[Key] extends RegisteredQuery<"public", any, any> ? Key : never
15
+ ]: ComponentQuery<Module[Key], Name>;
16
16
  };
17
17
 
18
18
  type ComponentMutation<Registered, Name extends string | undefined> =
@@ -21,9 +21,9 @@ type ComponentMutation<Registered, Name extends string | undefined> =
21
21
  : never;
22
22
 
23
23
  type PublicMutations<Module, Name extends string | undefined> = {
24
- [Key in keyof Module as Module[Key] extends RegisteredMutation<"public", any, any>
25
- ? Key
26
- : never]: ComponentMutation<Module[Key], Name>;
24
+ [
25
+ Key in keyof Module as Module[Key] extends RegisteredMutation<"public", any, any> ? Key : never
26
+ ]: ComponentMutation<Module[Key], Name>;
27
27
  };
28
28
 
29
29
  export type ComponentApi<Name extends string | undefined = string | undefined> = {
@@ -1,3 +1,4 @@
1
+ import { contentHealthValidator, validContentHealth } from "../capture/content-health.js";
1
2
  import { capacityValidator, validCapacity } from "../capture/capacity.js";
2
3
  import { v } from "convex/values";
3
4
  import { mutation } from "./_generated/server.js";
@@ -268,10 +269,12 @@ export const recordHealth = mutation({
268
269
  expiredScopesTotal: v.number(),
269
270
  activeCalls: v.number(),
270
271
  capacity: v.optional(capacityValidator),
272
+ content: v.optional(contentHealthValidator),
271
273
  },
272
274
  handler: async (ctx, args) => {
273
275
  bounded(JSON.stringify(args), 4096);
274
276
  if (!validCapacity(args.capacity)) throw new Error("invalid capture capacity");
277
+ if (!validContentHealth(args.content)) throw new Error("invalid content health");
275
278
  const at = Date.parse(args.observedAt);
276
279
  if (!Number.isFinite(at) || !Number.isFinite(Date.parse(args.startedAt)))
277
280
  throw new Error("invalid health time");
@@ -126,7 +126,9 @@ export type ModelCallV1 = {
126
126
  parserVersion: string;
127
127
  pluginVersion?: string;
128
128
  redactionVersion?: string;
129
- capturePolicy?: "hook-body-v1";
129
+ capturePolicy?: "hook-body-v1" | "hook-content-block-v1";
130
+ incomplete?: boolean;
131
+ lostContentBytes?: number;
130
132
  lastObservedAt?: string;
131
133
  };
132
134
  receivedAt: number;
package/src/projection.ts CHANGED
@@ -1,19 +1,65 @@
1
1
  import type { GenericActionCtx, GenericDataModel } from "convex/server";
2
2
  import type { CliproxyObservability } from "./client.js";
3
3
  import { resolveCallBlob, type PrivateCaptureStorage } from "./content/index.js";
4
- import { sha256, type CaptureObservationV1 } from "./capture/index.js";
5
- import { applyObservation, type ProjectionCheckpoint } from "./protocols/checkpoint.js";
4
+ import { decodeBody, sha256, type CaptureObservationV1 } from "./capture/index.js";
5
+ import {
6
+ MAX_PROJECTED_SPLIT_BYTES,
7
+ applyObservation,
8
+ type ProjectionCheckpoint,
9
+ } from "./protocols/checkpoint.js";
10
+ import { BodyPartAssembler } from "./protocols/body-parts.js";
6
11
  import type { ModelCallV1 } from "./model-call/index.js";
12
+ type ProjectionContext = Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">;
13
+ type ProjectionOptions = {
14
+ client: CliproxyObservability;
15
+ storage: PrivateCaptureStorage;
16
+ destinationId: string;
17
+ callId: string;
18
+ };
19
+ const segmentLines = (content: Uint8Array) =>
20
+ new TextDecoder("utf-8", { fatal: true }).decode(content).split("\n").filter(Boolean);
21
+ /**
22
+ * A split body spans several immutable segments and projection pages. Re-read
23
+ * its owned segments from the first part and return the exact verified body.
24
+ */
25
+ async function reassembleSplitBody(
26
+ ctx: ProjectionContext,
27
+ options: ProjectionOptions,
28
+ fromSegment: number,
29
+ final: CaptureObservationV1,
30
+ ): Promise<Uint8Array> {
31
+ const parts = new BodyPartAssembler();
32
+ const first = final.sequence - final.bodyPart!.index;
33
+ let after = fromSegment - 1;
34
+ while (after < final.sequence) {
35
+ const page = await ctx.runQuery(options.client.component.queries.pageEventSegments, {
36
+ destinationId: options.destinationId,
37
+ callId: options.callId,
38
+ afterSequence: after,
39
+ limit: 8,
40
+ });
41
+ if (!page.segments.length || page.segments[0]!.sequence !== after + 1)
42
+ throw new Error("split body segment unavailable");
43
+ for (const segment of page.segments) {
44
+ if (segment.sequence > final.sequence) break;
45
+ const content = await resolveCallBlob(
46
+ options.storage,
47
+ [segment.reference],
48
+ segment.reference,
49
+ );
50
+ for (const line of segmentLines(content)) {
51
+ const o = JSON.parse(line) as CaptureObservationV1;
52
+ if (o.sequence < first || o.sequence > final.sequence) continue;
53
+ const whole = await parts.push(o, decodeBody(o));
54
+ if (whole) return whole;
55
+ }
56
+ after = segment.throughSequence;
57
+ }
58
+ }
59
+ throw new Error("split body incomplete");
60
+ }
7
61
  /** Project at most two immutable segments. Host schedules the next page when `more` is true. */
8
- export async function projectPendingSegments(
9
- ctx: Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">,
10
- options: {
11
- client: CliproxyObservability;
12
- storage: PrivateCaptureStorage;
13
- destinationId: string;
14
- callId: string;
15
- },
16
- ) {
62
+ export async function projectPendingSegments(ctx: ProjectionContext, options: ProjectionOptions) {
17
63
  const { client, destinationId, callId } = options;
18
64
  const state = await ctx.runQuery(client.component.queries.getProcessingState, {
19
65
  destinationId,
@@ -51,11 +97,20 @@ export async function projectPendingSegments(
51
97
  [segment.reference],
52
98
  segment.reference,
53
99
  );
54
- for (const line of new TextDecoder("utf-8", { fatal: true })
55
- .decode(content)
56
- .split("\n")
57
- .filter(Boolean))
58
- applyObservation(call, checkpoint, JSON.parse(line) as CaptureObservationV1);
100
+ for (const line of segmentLines(content)) {
101
+ const o = JSON.parse(line) as CaptureObservationV1;
102
+ const part = o.bodyPart;
103
+ if (part?.index === 0) checkpoint.bodyPartSegment = segment.sequence;
104
+ if (part && part.index === part.count - 1) {
105
+ if (checkpoint.bodyPartSegment === undefined) throw new Error("split body start missing");
106
+ const whole =
107
+ part.totalBytes > MAX_PROJECTED_SPLIT_BYTES
108
+ ? null
109
+ : await reassembleSplitBody(ctx, options, checkpoint.bodyPartSegment, o);
110
+ applyObservation(call, checkpoint, o, whole);
111
+ delete checkpoint.bodyPartSegment;
112
+ } else applyObservation(call, checkpoint, o);
113
+ }
59
114
  through = segment.throughSequence;
60
115
  }
61
116
  if (through === state.projectedThroughSequence)
@@ -0,0 +1,58 @@
1
+ import {
2
+ MAX_BODY_PARTS,
3
+ MAX_CONTENT_BYTES,
4
+ continuesBodyPart,
5
+ sha256,
6
+ type CaptureObservationV1,
7
+ } from "../capture/index.js";
8
+ import { expandStockHookBlock } from "./stock-block.js";
9
+
10
+ /**
11
+ * Reassembles native 0.2.5 split bodies from validated observations read in
12
+ * sequence order. `push` returns a whole observation's body unchanged, nothing
13
+ * for an earlier part, and the exact original body at the final part once its
14
+ * length and SHA-256 match. Interleaved, missing or reordered parts throw.
15
+ */
16
+ export class BodyPartAssembler {
17
+ #open?: { previous: CaptureObservationV1; body: Uint8Array; written: number };
18
+ /** `maxBytes` bounds the buffer one split body may allocate. */
19
+ constructor(readonly maxBytes = MAX_BODY_PARTS * MAX_CONTENT_BYTES) {}
20
+ /** A split body has started but its final part has not been pushed. */
21
+ get pending(): boolean {
22
+ return this.#open !== undefined;
23
+ }
24
+ async push(o: CaptureObservationV1, body: Uint8Array): Promise<Uint8Array | undefined> {
25
+ const part = o.bodyPart;
26
+ const open = this.#open;
27
+ if (!part) {
28
+ if (open) throw new Error("body part interrupted");
29
+ return body;
30
+ }
31
+ if (part.index === 0) {
32
+ if (open) throw new Error("body part interrupted");
33
+ if (part.totalBytes > this.maxBytes) throw new Error("body part read limit");
34
+ this.#open = { previous: o, body: new Uint8Array(part.totalBytes), written: 0 };
35
+ } else if (!open || !continuesBodyPart(open.previous, o)) {
36
+ throw new Error("body part order mismatch");
37
+ }
38
+ const current = this.#open!;
39
+ if (part.offset !== current.written || body.byteLength > part.totalBytes - current.written)
40
+ throw new Error("body part offset mismatch");
41
+ current.body.set(body, part.offset);
42
+ current.written += body.byteLength;
43
+ current.previous = o;
44
+ if (part.index < part.count - 1) return undefined;
45
+ this.#open = undefined;
46
+ if (current.written !== part.totalBytes || (await sha256(current.body)) !== part.totalSha256)
47
+ throw new Error("body part digest mismatch");
48
+ return current.body;
49
+ }
50
+ }
51
+
52
+ /** Stock callbacks in a block, or the one callback a split stream body carries. */
53
+ export function stockHookPieces(
54
+ o: Pick<CaptureObservationV1, "bodyPart" | "stockHookChunkLengths">,
55
+ body: Uint8Array,
56
+ ): readonly Uint8Array[] {
57
+ return o.bodyPart ? [body] : expandStockHookBlock(body, o.stockHookChunkLengths ?? []);
58
+ }