@shpitdev/convex-cliproxy-observability 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/README.md +3 -1
- package/dist/capture/index.d.ts +13 -3
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +83 -3
- package/dist/capture/index.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8 -1
- package/dist/client.js.map +1 -1
- package/dist/model-call/index.d.ts +12 -0
- package/dist/model-call/index.d.ts.map +1 -1
- package/dist/model-call/index.js.map +1 -1
- package/dist/protocols/checkpoint.d.ts +2 -0
- package/dist/protocols/checkpoint.d.ts.map +1 -1
- package/dist/protocols/checkpoint.js +37 -9
- package/dist/protocols/checkpoint.js.map +1 -1
- package/dist/protocols/framing.d.ts +2 -0
- package/dist/protocols/framing.d.ts.map +1 -1
- package/dist/protocols/framing.js +75 -0
- package/dist/protocols/framing.js.map +1 -1
- package/dist/protocols/index.d.ts +3 -1
- package/dist/protocols/index.d.ts.map +1 -1
- package/dist/protocols/index.js +4 -2
- package/dist/protocols/index.js.map +1 -1
- package/package.json +1 -1
- package/src/capture/index.ts +114 -6
- package/src/client.ts +8 -1
- package/src/model-call/index.ts +13 -0
- package/src/protocols/checkpoint.ts +38 -9
- package/src/protocols/framing.ts +68 -0
- package/src/protocols/index.ts +6 -2
|
@@ -52,6 +52,81 @@ export class SSEReader {
|
|
|
52
52
|
throw new Error("SSE frame limit");
|
|
53
53
|
return frames;
|
|
54
54
|
}
|
|
55
|
+
/** Derive frames from pinned stock pre-framer callbacks; stored bytes stay untouched. */
|
|
56
|
+
feedStock(bytes, protocol, observedAt) {
|
|
57
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
58
|
+
if (protocol === "chat_completions" && (this.pending[0] ?? bytes[0]) === 123) {
|
|
59
|
+
if (this.pending.length + bytes.length > this.maxBytes)
|
|
60
|
+
throw new Error("SSE frame limit");
|
|
61
|
+
const combined = new Uint8Array(this.pending.length + bytes.length);
|
|
62
|
+
combined.set(this.pending);
|
|
63
|
+
combined.set(bytes, this.pending.length);
|
|
64
|
+
this.pending = combined;
|
|
65
|
+
try {
|
|
66
|
+
const data = decoder.decode(combined);
|
|
67
|
+
JSON.parse(data);
|
|
68
|
+
this.pending = new Uint8Array();
|
|
69
|
+
return [{ data, observedAt }];
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (protocol === "responses" && this.pending.length && bytes.length) {
|
|
76
|
+
// Only an event-only field can gain a missing separator. A data: prefix
|
|
77
|
+
// inside fragmented JSON is content, not a new line.
|
|
78
|
+
const event = new TextDecoder().decode(this.pending);
|
|
79
|
+
if (/^event: *[a-zA-Z0-9][a-zA-Z0-9._:-]{0,126} *$/.test(event) &&
|
|
80
|
+
bytes[0] === 100 &&
|
|
81
|
+
bytes[1] === 97 &&
|
|
82
|
+
bytes[2] === 116 &&
|
|
83
|
+
bytes[3] === 97 &&
|
|
84
|
+
bytes[4] === 58) {
|
|
85
|
+
const line = new Uint8Array(bytes.length + 1);
|
|
86
|
+
line[0] = 10;
|
|
87
|
+
line.set(bytes, 1);
|
|
88
|
+
bytes = line;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const frames = this.feed(bytes, observedAt);
|
|
92
|
+
if (protocol !== "responses")
|
|
93
|
+
return frames;
|
|
94
|
+
let candidate;
|
|
95
|
+
try {
|
|
96
|
+
candidate = decoder.decode(this.pending);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return frames;
|
|
100
|
+
}
|
|
101
|
+
if (!candidate.trim()) {
|
|
102
|
+
this.pending = new Uint8Array();
|
|
103
|
+
return frames;
|
|
104
|
+
}
|
|
105
|
+
const data = [];
|
|
106
|
+
let event;
|
|
107
|
+
for (const line of candidate.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n")) {
|
|
108
|
+
if (line.startsWith("data:"))
|
|
109
|
+
data.push(line.slice(5).replace(/^ /, ""));
|
|
110
|
+
else if (line.startsWith("event:"))
|
|
111
|
+
event = line.slice(6).trim();
|
|
112
|
+
else if (line !== "" && !line.startsWith(":"))
|
|
113
|
+
return frames;
|
|
114
|
+
}
|
|
115
|
+
if (!data.length)
|
|
116
|
+
return frames;
|
|
117
|
+
const payload = data.join("\n");
|
|
118
|
+
if (payload !== "[DONE]") {
|
|
119
|
+
try {
|
|
120
|
+
JSON.parse(payload);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return frames;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.pending = new Uint8Array();
|
|
127
|
+
frames.push({ data: payload, event, observedAt });
|
|
128
|
+
return frames;
|
|
129
|
+
}
|
|
55
130
|
finish() {
|
|
56
131
|
return {
|
|
57
132
|
truncated: new TextDecoder("utf-8", { fatal: true }).decode(this.pending).trim().length > 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framing.js","sourceRoot":"","sources":["../../src/protocols/framing.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"framing.js","sourceRoot":"","sources":["../../src/protocols/framing.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ export type { SSEFrame, SSECheckpoint } from "./framing.js";
|
|
|
7
7
|
export type { CliproxyProjection, ProjectionState } from "./types.js";
|
|
8
8
|
export { assembleResponsesStream, assembleChatStream, assembleAnthropicStream };
|
|
9
9
|
export { nanoTime } from "./values.js";
|
|
10
|
-
export declare const PARSER_VERSION = "client-protocol-
|
|
10
|
+
export declare const PARSER_VERSION = "client-protocol-v2";
|
|
11
11
|
export declare function protocolForRoute(route: string): CliproxyProjection["protocol"];
|
|
12
12
|
/** Bounded replay helper for fixtures and selected-call export; never fetches data itself. */
|
|
13
13
|
export declare function projectCapturedPayloads(args: {
|
|
@@ -16,5 +16,7 @@ export declare function projectCapturedPayloads(args: {
|
|
|
16
16
|
response?: Uint8Array;
|
|
17
17
|
chunks?: readonly Uint8Array[];
|
|
18
18
|
complete?: boolean;
|
|
19
|
+
/** Set only for capturePolicy hook-body-v1, never historical canonical SSE. */
|
|
20
|
+
stockHookChunks?: boolean;
|
|
19
21
|
}): CliproxyProjection;
|
|
20
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC5D,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAM9E;AACD,8FAA8F;AAC9F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC5D,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAM9E;AACD,8FAA8F;AAC9F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,kBAAkB,CAkHrB"}
|
package/dist/protocols/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { requestInput, responseOutput } from "./output.js";
|
|
|
7
7
|
export { SSEReader } from "./framing.js";
|
|
8
8
|
export { assembleResponsesStream, assembleChatStream, assembleAnthropicStream };
|
|
9
9
|
export { nanoTime } from "./values.js";
|
|
10
|
-
export const PARSER_VERSION = "client-protocol-
|
|
10
|
+
export const PARSER_VERSION = "client-protocol-v2";
|
|
11
11
|
export function protocolForRoute(route) {
|
|
12
12
|
if (route === "POST /v1/messages" || route === "POST /v1/messages/count_tokens")
|
|
13
13
|
return "anthropic_messages";
|
|
@@ -65,7 +65,9 @@ export function projectCapturedPayloads(args) {
|
|
|
65
65
|
if (bytes > 16 * 1024 * 1024)
|
|
66
66
|
throw new Error("bounded replay limit; use event pages for larger calls");
|
|
67
67
|
try {
|
|
68
|
-
for (const frame of
|
|
68
|
+
for (const frame of args.stockHookChunks
|
|
69
|
+
? reader.feedStock(chunk, projection.protocol)
|
|
70
|
+
: reader.feed(chunk)) {
|
|
69
71
|
if (frame.data === "[DONE]") {
|
|
70
72
|
done = true;
|
|
71
73
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,CAAC;AACnD,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,KAAK,KAAK,mBAAmB,IAAI,KAAK,KAAK,gCAAgC;QAC7E,OAAO,oBAAoB,CAAC;IAC9B,IAAI,KAAK,KAAK,oBAAoB;QAAE,OAAO,WAAW,CAAC;IACvD,IAAI,KAAK,KAAK,2BAA2B;QAAE,OAAO,kBAAkB,CAAC;IACrE,OAAO,SAAS,CAAC;AACnB,CAAC;AACD,8FAA8F;AAC9F,MAAM,UAAU,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocols/index.ts"],"names":[],"mappings":"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"}
|
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.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"author": "Anand Pant",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": "github:anandpant/convex-components",
|
package/src/capture/index.ts
CHANGED
|
@@ -12,33 +12,50 @@ const routes = new Set([
|
|
|
12
12
|
export type CaptureObservationV1 = {
|
|
13
13
|
schemaVersion: 1;
|
|
14
14
|
pluginVersion: string;
|
|
15
|
-
|
|
15
|
+
/** Historical redacted observations retain their original policy label. */
|
|
16
|
+
redactionVersion?: string;
|
|
17
|
+
capturePolicy?: "hook-body-v1";
|
|
16
18
|
destinationId: string;
|
|
17
19
|
instanceId: string;
|
|
18
20
|
pluginBootId: string;
|
|
19
21
|
requestId: string;
|
|
20
22
|
sequence: number;
|
|
21
|
-
kind:
|
|
23
|
+
kind:
|
|
24
|
+
| "request"
|
|
25
|
+
| "request_after_auth"
|
|
26
|
+
| "response"
|
|
27
|
+
| "stream_init"
|
|
28
|
+
| "stream_chunk"
|
|
29
|
+
| "completion"
|
|
30
|
+
| "capture_health";
|
|
22
31
|
observedAt: string;
|
|
23
32
|
offsetNs: number;
|
|
24
33
|
route: string;
|
|
25
34
|
configRevision: string;
|
|
26
35
|
sourceFormat?: string;
|
|
27
36
|
requestedModel?: string;
|
|
37
|
+
executionModel?: string;
|
|
38
|
+
selectedAuthId?: string;
|
|
39
|
+
selectedAuthIndex?: string;
|
|
40
|
+
executionProtocol?: string;
|
|
41
|
+
metadataOmissions?: string[];
|
|
28
42
|
sourceTraceId?: string;
|
|
29
43
|
correlation?: ExactCorrelation;
|
|
30
44
|
correlationConflicts?: string[];
|
|
31
45
|
stockChunkIndex?: number;
|
|
32
46
|
body?: string;
|
|
33
47
|
observedBodyBytes?: number;
|
|
34
|
-
bodyFraming?: "stock_json_chunk" | "stock_sse_candidate";
|
|
48
|
+
bodyFraming?: "stock_json_chunk" | "stock_sse_candidate" | "stock_hook_chunk";
|
|
35
49
|
contentSha256: string;
|
|
36
50
|
contentBytes: number;
|
|
37
51
|
completionOutcome?: string;
|
|
38
52
|
executionStatusCode?: number;
|
|
39
53
|
executionStartedAt?: string;
|
|
40
54
|
executionCompletedAt?: string;
|
|
55
|
+
/** Exact bounded stock completion diagnostic; private event content. */
|
|
41
56
|
error?: string;
|
|
57
|
+
errorPresent?: boolean;
|
|
58
|
+
observedErrorBytes?: number;
|
|
42
59
|
gap?: string;
|
|
43
60
|
bodyFromSequence?: number;
|
|
44
61
|
droppedObservationsTotal?: number;
|
|
@@ -92,6 +109,51 @@ export async function validateObservation(
|
|
|
92
109
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
93
110
|
throw new Error("observation required");
|
|
94
111
|
const o = value as CaptureObservationV1;
|
|
112
|
+
const fields = new Set([
|
|
113
|
+
"schemaVersion",
|
|
114
|
+
"pluginVersion",
|
|
115
|
+
"redactionVersion",
|
|
116
|
+
"capturePolicy",
|
|
117
|
+
"destinationId",
|
|
118
|
+
"instanceId",
|
|
119
|
+
"pluginBootId",
|
|
120
|
+
"requestId",
|
|
121
|
+
"sequence",
|
|
122
|
+
"kind",
|
|
123
|
+
"observedAt",
|
|
124
|
+
"offsetNs",
|
|
125
|
+
"route",
|
|
126
|
+
"configRevision",
|
|
127
|
+
"sourceFormat",
|
|
128
|
+
"requestedModel",
|
|
129
|
+
"executionModel",
|
|
130
|
+
"executionProtocol",
|
|
131
|
+
"selectedAuthId",
|
|
132
|
+
"selectedAuthIndex",
|
|
133
|
+
"metadataOmissions",
|
|
134
|
+
"sourceTraceId",
|
|
135
|
+
"correlation",
|
|
136
|
+
"correlationConflicts",
|
|
137
|
+
"stockChunkIndex",
|
|
138
|
+
"body",
|
|
139
|
+
"observedBodyBytes",
|
|
140
|
+
"bodyFraming",
|
|
141
|
+
"contentSha256",
|
|
142
|
+
"contentBytes",
|
|
143
|
+
"completionOutcome",
|
|
144
|
+
"executionStatusCode",
|
|
145
|
+
"executionStartedAt",
|
|
146
|
+
"executionCompletedAt",
|
|
147
|
+
"error",
|
|
148
|
+
"errorPresent",
|
|
149
|
+
"observedErrorBytes",
|
|
150
|
+
"gap",
|
|
151
|
+
"bodyFromSequence",
|
|
152
|
+
"droppedObservationsTotal",
|
|
153
|
+
"scopeConflictsTotal",
|
|
154
|
+
]);
|
|
155
|
+
if (Object.keys(o).some((key) => !fields.has(key)))
|
|
156
|
+
throw new Error("unsupported observation field");
|
|
95
157
|
if (
|
|
96
158
|
o.schemaVersion !== 1 ||
|
|
97
159
|
o.destinationId !== expected.destinationId ||
|
|
@@ -119,7 +181,14 @@ export async function validateObservation(
|
|
|
119
181
|
)
|
|
120
182
|
throw new Error("invalid observation metadata");
|
|
121
183
|
if (
|
|
122
|
-
![
|
|
184
|
+
![
|
|
185
|
+
"request",
|
|
186
|
+
"request_after_auth",
|
|
187
|
+
"response",
|
|
188
|
+
"stream_init",
|
|
189
|
+
"stream_chunk",
|
|
190
|
+
"completion",
|
|
191
|
+
].includes(o.kind) ||
|
|
123
192
|
!/^[a-f0-9]{64}$/.test(o.contentSha256)
|
|
124
193
|
)
|
|
125
194
|
throw new Error("unsupported observation");
|
|
@@ -154,6 +223,10 @@ export async function validateObservation(
|
|
|
154
223
|
"redactionVersion",
|
|
155
224
|
"sourceFormat",
|
|
156
225
|
"requestedModel",
|
|
226
|
+
"executionModel",
|
|
227
|
+
"selectedAuthId",
|
|
228
|
+
"selectedAuthIndex",
|
|
229
|
+
"executionProtocol",
|
|
157
230
|
"sourceTraceId",
|
|
158
231
|
"gap",
|
|
159
232
|
"completionOutcome",
|
|
@@ -161,7 +234,33 @@ export async function validateObservation(
|
|
|
161
234
|
if (o[field] !== undefined && (typeof o[field] !== "string" || o[field]!.length > 256))
|
|
162
235
|
throw new Error("invalid optional metadata");
|
|
163
236
|
}
|
|
164
|
-
if (!o.pluginVersion || !o.redactionVersion
|
|
237
|
+
if (!o.pluginVersion || (!o.redactionVersion && o.capturePolicy !== "hook-body-v1"))
|
|
238
|
+
throw new Error("capture versions required");
|
|
239
|
+
if (
|
|
240
|
+
o.capturePolicy !== undefined &&
|
|
241
|
+
(o.capturePolicy !== "hook-body-v1" || o.redactionVersion !== undefined)
|
|
242
|
+
)
|
|
243
|
+
throw new Error("invalid capture policy");
|
|
244
|
+
if (
|
|
245
|
+
o.metadataOmissions !== undefined &&
|
|
246
|
+
(!Array.isArray(o.metadataOmissions) ||
|
|
247
|
+
o.metadataOmissions.length > 16 ||
|
|
248
|
+
o.metadataOmissions.some((x) => typeof x !== "string" || x.length > 64))
|
|
249
|
+
)
|
|
250
|
+
throw new Error("invalid metadata omissions");
|
|
251
|
+
if (o.error !== undefined && (typeof o.error !== "string" || o.error.length > 4096))
|
|
252
|
+
throw new Error("invalid error detail");
|
|
253
|
+
if (
|
|
254
|
+
(o.kind === "request_after_auth" || o.bodyFraming === "stock_hook_chunk") &&
|
|
255
|
+
o.capturePolicy !== "hook-body-v1"
|
|
256
|
+
)
|
|
257
|
+
throw new Error("raw hook policy required");
|
|
258
|
+
if (
|
|
259
|
+
o.capturePolicy === "hook-body-v1" &&
|
|
260
|
+
(o.bodyFromSequence !== undefined ||
|
|
261
|
+
(o.kind === "stream_chunk" && o.bodyFraming !== "stock_hook_chunk"))
|
|
262
|
+
)
|
|
263
|
+
throw new Error("raw hook provenance required");
|
|
165
264
|
if (
|
|
166
265
|
o.kind === "completion" &&
|
|
167
266
|
!["succeeded", "failed", "canceled", "rejected"].includes(o.completionOutcome ?? "")
|
|
@@ -169,7 +268,7 @@ export async function validateObservation(
|
|
|
169
268
|
throw new Error("invalid completion outcome");
|
|
170
269
|
if (
|
|
171
270
|
o.bodyFraming !== undefined &&
|
|
172
|
-
!["stock_json_chunk", "stock_sse_candidate"].includes(o.bodyFraming)
|
|
271
|
+
!["stock_json_chunk", "stock_sse_candidate", "stock_hook_chunk"].includes(o.bodyFraming)
|
|
173
272
|
)
|
|
174
273
|
throw new Error("invalid body framing");
|
|
175
274
|
if (
|
|
@@ -179,6 +278,15 @@ export async function validateObservation(
|
|
|
179
278
|
o.observedBodyBytes > MAX_ENVELOPE_BYTES)
|
|
180
279
|
)
|
|
181
280
|
throw new Error("invalid observed byte count");
|
|
281
|
+
if (o.errorPresent !== undefined && typeof o.errorPresent !== "boolean")
|
|
282
|
+
throw new Error("invalid error presence");
|
|
283
|
+
if (
|
|
284
|
+
o.observedErrorBytes !== undefined &&
|
|
285
|
+
(!Number.isSafeInteger(o.observedErrorBytes) ||
|
|
286
|
+
o.observedErrorBytes < 0 ||
|
|
287
|
+
o.observedErrorBytes > MAX_ENVELOPE_BYTES)
|
|
288
|
+
)
|
|
289
|
+
throw new Error("invalid observed diagnostic size");
|
|
182
290
|
const body = decodeBody(o);
|
|
183
291
|
if ((await sha256(body)) !== o.contentSha256) throw new Error("content digest mismatch");
|
|
184
292
|
return {
|
package/src/client.ts
CHANGED
|
@@ -105,6 +105,7 @@ export function initialCall(
|
|
|
105
105
|
parserVersion: PARSER_VERSION,
|
|
106
106
|
pluginVersion: o.pluginVersion,
|
|
107
107
|
redactionVersion: o.redactionVersion,
|
|
108
|
+
capturePolicy: o.capturePolicy,
|
|
108
109
|
lastObservedAt: o.observedAt,
|
|
109
110
|
},
|
|
110
111
|
receivedAt,
|
|
@@ -266,7 +267,13 @@ export async function handleCliproxyCaptureRequest(
|
|
|
266
267
|
});
|
|
267
268
|
// Scheduling failure returns retryable 503 after durable receipt; retry is idempotent.
|
|
268
269
|
await options.scheduleProjection(ctx, callId);
|
|
269
|
-
return Response.json({
|
|
270
|
+
return Response.json({
|
|
271
|
+
...receipt,
|
|
272
|
+
destinationId: options.destinationId,
|
|
273
|
+
deploymentId: options.deploymentId,
|
|
274
|
+
rawCommitted: true,
|
|
275
|
+
projectionCommitted: false,
|
|
276
|
+
});
|
|
270
277
|
} catch (error) {
|
|
271
278
|
const message = error instanceof Error ? error.message : "";
|
|
272
279
|
return new Response(
|
package/src/model-call/index.ts
CHANGED
|
@@ -54,6 +54,11 @@ export type ModelCallV1 = {
|
|
|
54
54
|
parentSpanId?: string;
|
|
55
55
|
requestModel?: string;
|
|
56
56
|
responseModel?: string;
|
|
57
|
+
/** Last observed after-auth execution, not inferred provider identity. */
|
|
58
|
+
executionModel?: string;
|
|
59
|
+
selectedAuthId?: string;
|
|
60
|
+
selectedAuthIndex?: string;
|
|
61
|
+
executionProtocol?: string;
|
|
57
62
|
providerName?: string;
|
|
58
63
|
streamed?: boolean;
|
|
59
64
|
operation: "generation" | "token_count" | "discovery" | "unknown";
|
|
@@ -85,6 +90,8 @@ export type ModelCallV1 = {
|
|
|
85
90
|
capture: {
|
|
86
91
|
raw: EvidenceState;
|
|
87
92
|
projection: EvidenceState;
|
|
93
|
+
projectionIssue?:
|
|
94
|
+
"malformed_payload" | "truncated_frame" | "frame_limit" | "unsupported_protocol";
|
|
88
95
|
usage: EvidenceState;
|
|
89
96
|
preHook: "unavailable";
|
|
90
97
|
projectedThroughSequence: number;
|
|
@@ -94,6 +101,7 @@ export type ModelCallV1 = {
|
|
|
94
101
|
parserVersion: string;
|
|
95
102
|
pluginVersion?: string;
|
|
96
103
|
redactionVersion?: string;
|
|
104
|
+
capturePolicy?: "hook-body-v1";
|
|
97
105
|
lastObservedAt?: string;
|
|
98
106
|
};
|
|
99
107
|
receivedAt: number;
|
|
@@ -111,6 +119,11 @@ export type OpenRouterSpanInput = {
|
|
|
111
119
|
parentSpanId?: string;
|
|
112
120
|
requestModel?: string;
|
|
113
121
|
responseModel?: string;
|
|
122
|
+
/** Last observed after-auth execution, not inferred provider identity. */
|
|
123
|
+
executionModel?: string;
|
|
124
|
+
selectedAuthId?: string;
|
|
125
|
+
selectedAuthIndex?: string;
|
|
126
|
+
executionProtocol?: string;
|
|
114
127
|
providerName?: string;
|
|
115
128
|
streamed?: boolean;
|
|
116
129
|
startTimeUnixNano?: string;
|
|
@@ -18,6 +18,8 @@ export type ProjectionCheckpoint = {
|
|
|
18
18
|
terminal?: string;
|
|
19
19
|
usageFinal?: boolean;
|
|
20
20
|
invalid?: boolean;
|
|
21
|
+
truncated?: boolean;
|
|
22
|
+
frameLimit?: boolean;
|
|
21
23
|
unsupported?: boolean;
|
|
22
24
|
rawGap?: boolean;
|
|
23
25
|
streamed?: boolean;
|
|
@@ -192,6 +194,14 @@ export function applyObservation(
|
|
|
192
194
|
}
|
|
193
195
|
const body = decodeBody(o);
|
|
194
196
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
197
|
+
if (o.kind === "request_after_auth" || o.kind === "stream_init") {
|
|
198
|
+
call.selectedAuthId = o.selectedAuthId;
|
|
199
|
+
call.selectedAuthIndex = o.selectedAuthIndex;
|
|
200
|
+
}
|
|
201
|
+
if (o.kind === "request_after_auth") {
|
|
202
|
+
call.executionModel = o.executionModel;
|
|
203
|
+
call.executionProtocol = o.executionProtocol;
|
|
204
|
+
}
|
|
195
205
|
if (o.kind === "request") {
|
|
196
206
|
state.inputSeen = true;
|
|
197
207
|
if (o.requestedModel) call.requestModel = o.requestedModel.slice(0, 256);
|
|
@@ -221,7 +231,9 @@ export function applyObservation(
|
|
|
221
231
|
}
|
|
222
232
|
if ((o.kind === "stream_chunk" || o.kind === "completion") && body.length) {
|
|
223
233
|
const reader = new SSEReader(state.sse);
|
|
224
|
-
for (const frame of
|
|
234
|
+
for (const frame of o.bodyFraming === "stock_hook_chunk"
|
|
235
|
+
? reader.feedStock(body, call.clientProtocol, o.observedAt)
|
|
236
|
+
: reader.feed(body, o.observedAt)) {
|
|
225
237
|
if (frame.data === "[DONE]") {
|
|
226
238
|
if (call.clientProtocol === "chat_completions" && state.chatFinished)
|
|
227
239
|
state.terminal = "completed";
|
|
@@ -234,13 +246,14 @@ export function applyObservation(
|
|
|
234
246
|
}
|
|
235
247
|
state.sse = reader.checkpoint();
|
|
236
248
|
}
|
|
237
|
-
} catch {
|
|
249
|
+
} catch (error) {
|
|
238
250
|
state.invalid = true;
|
|
251
|
+
if (error instanceof Error && error.message === "SSE frame limit") state.frameLimit = true;
|
|
239
252
|
}
|
|
240
253
|
}
|
|
241
254
|
if (o.kind === "completion") {
|
|
242
255
|
try {
|
|
243
|
-
if (new SSEReader(state.sse).finish().truncated) state.
|
|
256
|
+
if (new SSEReader(state.sse).finish().truncated) state.truncated = true;
|
|
244
257
|
} catch {
|
|
245
258
|
state.invalid = true;
|
|
246
259
|
}
|
|
@@ -275,7 +288,12 @@ export function applyObservation(
|
|
|
275
288
|
}
|
|
276
289
|
call.protocolTerminal = state.terminal;
|
|
277
290
|
const final =
|
|
278
|
-
!!state.terminal &&
|
|
291
|
+
!!state.terminal &&
|
|
292
|
+
state.usageFinal &&
|
|
293
|
+
!state.invalid &&
|
|
294
|
+
!state.truncated &&
|
|
295
|
+
!state.unsupported &&
|
|
296
|
+
!state.rawGap;
|
|
279
297
|
const projection: CliproxyProjection = {
|
|
280
298
|
version: 1,
|
|
281
299
|
protocol: call.clientProtocol ?? "unknown",
|
|
@@ -331,12 +349,23 @@ export function applyObservation(
|
|
|
331
349
|
call.capture.persistedThroughSequence = o.sequence;
|
|
332
350
|
call.capture.raw =
|
|
333
351
|
call.capture.terminalSequence === o.sequence && !state.rawGap ? "complete" : "partial";
|
|
352
|
+
call.capture.projectionIssue = state.frameLimit
|
|
353
|
+
? "frame_limit"
|
|
354
|
+
: state.invalid
|
|
355
|
+
? "malformed_payload"
|
|
356
|
+
: state.truncated
|
|
357
|
+
? "truncated_frame"
|
|
358
|
+
: state.unsupported
|
|
359
|
+
? "unsupported_protocol"
|
|
360
|
+
: undefined;
|
|
334
361
|
call.capture.projection = state.invalid
|
|
335
362
|
? "invalid"
|
|
336
|
-
: state.
|
|
337
|
-
? "
|
|
338
|
-
: state.
|
|
339
|
-
? "
|
|
340
|
-
:
|
|
363
|
+
: state.truncated
|
|
364
|
+
? "partial"
|
|
365
|
+
: state.unsupported
|
|
366
|
+
? "unsupported"
|
|
367
|
+
: state.terminal
|
|
368
|
+
? "complete"
|
|
369
|
+
: "partial";
|
|
341
370
|
call.capture.usage = call.usage.length ? (final ? "complete" : "partial") : "unavailable";
|
|
342
371
|
}
|
package/src/protocols/framing.ts
CHANGED
|
@@ -49,6 +49,74 @@ export class SSEReader {
|
|
|
49
49
|
if (this.pending.length > this.maxBytes) throw new Error("SSE frame limit");
|
|
50
50
|
return frames;
|
|
51
51
|
}
|
|
52
|
+
/** Derive frames from pinned stock pre-framer callbacks; stored bytes stay untouched. */
|
|
53
|
+
feedStock(bytes: Uint8Array, protocol?: string, observedAt?: string): SSEFrame[] {
|
|
54
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
55
|
+
if (protocol === "chat_completions" && (this.pending[0] ?? bytes[0]) === 123) {
|
|
56
|
+
if (this.pending.length + bytes.length > this.maxBytes) throw new Error("SSE frame limit");
|
|
57
|
+
const combined = new Uint8Array(this.pending.length + bytes.length);
|
|
58
|
+
combined.set(this.pending);
|
|
59
|
+
combined.set(bytes, this.pending.length);
|
|
60
|
+
this.pending = combined;
|
|
61
|
+
try {
|
|
62
|
+
const data = decoder.decode(combined);
|
|
63
|
+
JSON.parse(data);
|
|
64
|
+
this.pending = new Uint8Array();
|
|
65
|
+
return [{ data, observedAt }];
|
|
66
|
+
} catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (protocol === "responses" && this.pending.length && bytes.length) {
|
|
71
|
+
// Only an event-only field can gain a missing separator. A data: prefix
|
|
72
|
+
// inside fragmented JSON is content, not a new line.
|
|
73
|
+
const event = new TextDecoder().decode(this.pending);
|
|
74
|
+
if (
|
|
75
|
+
/^event: *[a-zA-Z0-9][a-zA-Z0-9._:-]{0,126} *$/.test(event) &&
|
|
76
|
+
bytes[0] === 100 &&
|
|
77
|
+
bytes[1] === 97 &&
|
|
78
|
+
bytes[2] === 116 &&
|
|
79
|
+
bytes[3] === 97 &&
|
|
80
|
+
bytes[4] === 58
|
|
81
|
+
) {
|
|
82
|
+
const line = new Uint8Array(bytes.length + 1);
|
|
83
|
+
line[0] = 10;
|
|
84
|
+
line.set(bytes, 1);
|
|
85
|
+
bytes = line;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const frames = this.feed(bytes, observedAt);
|
|
89
|
+
if (protocol !== "responses") return frames;
|
|
90
|
+
let candidate: string;
|
|
91
|
+
try {
|
|
92
|
+
candidate = decoder.decode(this.pending);
|
|
93
|
+
} catch {
|
|
94
|
+
return frames;
|
|
95
|
+
}
|
|
96
|
+
if (!candidate.trim()) {
|
|
97
|
+
this.pending = new Uint8Array();
|
|
98
|
+
return frames;
|
|
99
|
+
}
|
|
100
|
+
const data: string[] = [];
|
|
101
|
+
let event: string | undefined;
|
|
102
|
+
for (const line of candidate.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n")) {
|
|
103
|
+
if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /, ""));
|
|
104
|
+
else if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
105
|
+
else if (line !== "" && !line.startsWith(":")) return frames;
|
|
106
|
+
}
|
|
107
|
+
if (!data.length) return frames;
|
|
108
|
+
const payload = data.join("\n");
|
|
109
|
+
if (payload !== "[DONE]") {
|
|
110
|
+
try {
|
|
111
|
+
JSON.parse(payload);
|
|
112
|
+
} catch {
|
|
113
|
+
return frames;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
this.pending = new Uint8Array();
|
|
117
|
+
frames.push({ data: payload, event, observedAt });
|
|
118
|
+
return frames;
|
|
119
|
+
}
|
|
52
120
|
finish(): { truncated: boolean } {
|
|
53
121
|
return {
|
|
54
122
|
truncated: new TextDecoder("utf-8", { fatal: true }).decode(this.pending).trim().length > 0,
|
package/src/protocols/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type { SSEFrame, SSECheckpoint } from "./framing.js";
|
|
|
10
10
|
export type { CliproxyProjection, ProjectionState } from "./types.js";
|
|
11
11
|
export { assembleResponsesStream, assembleChatStream, assembleAnthropicStream };
|
|
12
12
|
export { nanoTime } from "./values.js";
|
|
13
|
-
export const PARSER_VERSION = "client-protocol-
|
|
13
|
+
export const PARSER_VERSION = "client-protocol-v2";
|
|
14
14
|
export function protocolForRoute(route: string): CliproxyProjection["protocol"] {
|
|
15
15
|
if (route === "POST /v1/messages" || route === "POST /v1/messages/count_tokens")
|
|
16
16
|
return "anthropic_messages";
|
|
@@ -25,6 +25,8 @@ export function projectCapturedPayloads(args: {
|
|
|
25
25
|
response?: Uint8Array;
|
|
26
26
|
chunks?: readonly Uint8Array[];
|
|
27
27
|
complete?: boolean;
|
|
28
|
+
/** Set only for capturePolicy hook-body-v1, never historical canonical SSE. */
|
|
29
|
+
stockHookChunks?: boolean;
|
|
28
30
|
}): CliproxyProjection {
|
|
29
31
|
const projection: CliproxyProjection = {
|
|
30
32
|
version: 1,
|
|
@@ -68,7 +70,9 @@ export function projectCapturedPayloads(args: {
|
|
|
68
70
|
if (bytes > 16 * 1024 * 1024)
|
|
69
71
|
throw new Error("bounded replay limit; use event pages for larger calls");
|
|
70
72
|
try {
|
|
71
|
-
for (const frame of
|
|
73
|
+
for (const frame of args.stockHookChunks
|
|
74
|
+
? reader.feedStock(chunk, projection.protocol)
|
|
75
|
+
: reader.feed(chunk)) {
|
|
72
76
|
if (frame.data === "[DONE]") {
|
|
73
77
|
done = true;
|
|
74
78
|
continue;
|