@shpitdev/convex-cliproxy-observability 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +201 -0
- package/README.md +40 -0
- package/dist/capture/index.d.ts +79 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +182 -0
- package/dist/capture/index.js.map +1 -0
- package/dist/client.d.ts +129 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +232 -0
- package/dist/client.js.map +1 -0
- package/dist/component/_generated/api.d.ts +12 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +7 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +17 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +3 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +8 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +3 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +21 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +11 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +4 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +4 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/ingest.d.ts +64 -0
- package/dist/component/ingest.d.ts.map +1 -0
- package/dist/component/ingest.js +309 -0
- package/dist/component/ingest.js.map +1 -0
- package/dist/component/queries.d.ts +136 -0
- package/dist/component/queries.d.ts.map +1 -0
- package/dist/component/queries.js +219 -0
- package/dist/component/queries.js.map +1 -0
- package/dist/component/schema.d.ts +159 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +74 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/content/index.d.ts +8 -0
- package/dist/content/index.d.ts.map +1 -0
- package/dist/content/index.js +41 -0
- package/dist/content/index.js.map +1 -0
- package/dist/model-call/index.d.ts +120 -0
- package/dist/model-call/index.d.ts.map +1 -0
- package/dist/model-call/index.js +96 -0
- package/dist/model-call/index.js.map +1 -0
- package/dist/projection.d.ts +19 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +83 -0
- package/dist/projection.js.map +1 -0
- package/dist/protocols/anthropic-messages.d.ts +11 -0
- package/dist/protocols/anthropic-messages.d.ts.map +1 -0
- package/dist/protocols/anthropic-messages.js +114 -0
- package/dist/protocols/anthropic-messages.js.map +1 -0
- package/dist/protocols/chat-completions.d.ts +10 -0
- package/dist/protocols/chat-completions.d.ts.map +1 -0
- package/dist/protocols/chat-completions.js +89 -0
- package/dist/protocols/chat-completions.js.map +1 -0
- package/dist/protocols/checkpoint.d.ts +20 -0
- package/dist/protocols/checkpoint.d.ts.map +1 -0
- package/dist/protocols/checkpoint.js +318 -0
- package/dist/protocols/checkpoint.js.map +1 -0
- package/dist/protocols/content.d.ts +34 -0
- package/dist/protocols/content.d.ts.map +1 -0
- package/dist/protocols/content.js +40 -0
- package/dist/protocols/content.js.map +1 -0
- package/dist/protocols/framing.d.ts +20 -0
- package/dist/protocols/framing.d.ts.map +1 -0
- package/dist/protocols/framing.js +67 -0
- package/dist/protocols/framing.js.map +1 -0
- package/dist/protocols/index.d.ts +20 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +139 -0
- package/dist/protocols/index.js.map +1 -0
- package/dist/protocols/output.d.ts +4 -0
- package/dist/protocols/output.d.ts.map +1 -0
- package/dist/protocols/output.js +81 -0
- package/dist/protocols/output.js.map +1 -0
- package/dist/protocols/responses.d.ts +12 -0
- package/dist/protocols/responses.d.ts.map +1 -0
- package/dist/protocols/responses.js +189 -0
- package/dist/protocols/responses.js.map +1 -0
- package/dist/protocols/types.d.ts +27 -0
- package/dist/protocols/types.d.ts.map +1 -0
- package/dist/protocols/types.js +2 -0
- package/dist/protocols/types.js.map +1 -0
- package/dist/protocols/usage.d.ts +3 -0
- package/dist/protocols/usage.d.ts.map +1 -0
- package/dist/protocols/usage.js +27 -0
- package/dist/protocols/usage.js.map +1 -0
- package/dist/protocols/values.d.ts +10 -0
- package/dist/protocols/values.d.ts.map +1 -0
- package/dist/protocols/values.js +32 -0
- package/dist/protocols/values.js.map +1 -0
- package/dist/test.d.ts +155 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +8 -0
- package/dist/test.js.map +1 -0
- package/package.json +90 -0
- package/src/capture/index.ts +277 -0
- package/src/client.ts +286 -0
- package/src/component/_generated/api.ts +19 -0
- package/src/component/_generated/component.ts +32 -0
- package/src/component/_generated/dataModel.ts +15 -0
- package/src/component/_generated/server.ts +43 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/ingest.ts +328 -0
- package/src/component/queries.ts +248 -0
- package/src/component/schema.ts +73 -0
- package/src/content/index.ts +53 -0
- package/src/model-call/index.ts +224 -0
- package/src/projection.ts +99 -0
- package/src/protocols/anthropic-messages.ts +115 -0
- package/src/protocols/chat-completions.ts +94 -0
- package/src/protocols/checkpoint.ts +342 -0
- package/src/protocols/content.ts +41 -0
- package/src/protocols/framing.ts +62 -0
- package/src/protocols/index.ts +140 -0
- package/src/protocols/output.ts +78 -0
- package/src/protocols/responses.ts +188 -0
- package/src/protocols/types.ts +26 -0
- package/src/protocols/usage.ts +36 -0
- package/src/protocols/values.ts +35 -0
- package/src/test.ts +16 -0
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { GenericSchema, SchemaDefinition } from "convex/server";
|
|
2
|
+
import type { TestConvex } from "convex-test";
|
|
3
|
+
export declare function register(testConvex: TestConvex<SchemaDefinition<GenericSchema, boolean>>, name?: string): void;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
modules: Record<string, () => Promise<unknown>>;
|
|
6
|
+
register: typeof register;
|
|
7
|
+
schema: SchemaDefinition<{
|
|
8
|
+
calls: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
9
|
+
terminalSequence?: number | undefined;
|
|
10
|
+
projectionFailure?: {
|
|
11
|
+
reason: string;
|
|
12
|
+
at: number;
|
|
13
|
+
} | undefined;
|
|
14
|
+
requestId?: string | undefined;
|
|
15
|
+
runId?: string | undefined;
|
|
16
|
+
jobId?: string | undefined;
|
|
17
|
+
rootExecutionId?: string | undefined;
|
|
18
|
+
opencodeSessionId?: string | undefined;
|
|
19
|
+
destinationId: string;
|
|
20
|
+
callId: string;
|
|
21
|
+
instanceId: string;
|
|
22
|
+
pluginBootId: string;
|
|
23
|
+
executionId: string;
|
|
24
|
+
receivedAt: number;
|
|
25
|
+
revision: number;
|
|
26
|
+
projectedThroughSequence: number;
|
|
27
|
+
persistedThroughSequence: number;
|
|
28
|
+
summaryJson: string;
|
|
29
|
+
checkpointJson: string;
|
|
30
|
+
}, {
|
|
31
|
+
destinationId: import("convex/values").VString<string, "required">;
|
|
32
|
+
callId: import("convex/values").VString<string, "required">;
|
|
33
|
+
instanceId: import("convex/values").VString<string, "required">;
|
|
34
|
+
pluginBootId: import("convex/values").VString<string, "required">;
|
|
35
|
+
executionId: import("convex/values").VString<string, "required">;
|
|
36
|
+
receivedAt: import("convex/values").VFloat64<number, "required">;
|
|
37
|
+
revision: import("convex/values").VFloat64<number, "required">;
|
|
38
|
+
projectedThroughSequence: import("convex/values").VFloat64<number, "required">;
|
|
39
|
+
persistedThroughSequence: import("convex/values").VFloat64<number, "required">;
|
|
40
|
+
terminalSequence: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
41
|
+
summaryJson: import("convex/values").VString<string, "required">;
|
|
42
|
+
checkpointJson: import("convex/values").VString<string, "required">;
|
|
43
|
+
projectionFailure: import("convex/values").VObject<{
|
|
44
|
+
reason: string;
|
|
45
|
+
at: number;
|
|
46
|
+
} | undefined, {
|
|
47
|
+
reason: import("convex/values").VString<string, "required">;
|
|
48
|
+
at: import("convex/values").VFloat64<number, "required">;
|
|
49
|
+
}, "optional", "reason" | "at">;
|
|
50
|
+
requestId: import("convex/values").VString<string | undefined, "optional">;
|
|
51
|
+
runId: import("convex/values").VString<string | undefined, "optional">;
|
|
52
|
+
jobId: import("convex/values").VString<string | undefined, "optional">;
|
|
53
|
+
rootExecutionId: import("convex/values").VString<string | undefined, "optional">;
|
|
54
|
+
opencodeSessionId: import("convex/values").VString<string | undefined, "optional">;
|
|
55
|
+
}, "required", "destinationId" | "callId" | "instanceId" | "pluginBootId" | "executionId" | "receivedAt" | "revision" | "projectedThroughSequence" | "persistedThroughSequence" | "terminalSequence" | "summaryJson" | "checkpointJson" | "projectionFailure" | "requestId" | "runId" | "jobId" | "rootExecutionId" | "opencodeSessionId" | "projectionFailure.reason" | "projectionFailure.at">, {
|
|
56
|
+
by_call: ["destinationId", "callId", "_creationTime"];
|
|
57
|
+
by_received: ["destinationId", "receivedAt", "_creationTime"];
|
|
58
|
+
by_request: ["destinationId", "requestId", "receivedAt", "_creationTime"];
|
|
59
|
+
by_run: ["destinationId", "runId", "receivedAt", "_creationTime"];
|
|
60
|
+
by_job: ["destinationId", "jobId", "receivedAt", "_creationTime"];
|
|
61
|
+
by_root_execution: ["destinationId", "rootExecutionId", "receivedAt", "_creationTime"];
|
|
62
|
+
by_opencode_session: ["destinationId", "opencodeSessionId", "receivedAt", "_creationTime"];
|
|
63
|
+
}, {}, {}>;
|
|
64
|
+
segments: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
65
|
+
destinationId: string;
|
|
66
|
+
callId: string;
|
|
67
|
+
receivedAt: number;
|
|
68
|
+
reference: {
|
|
69
|
+
key: string;
|
|
70
|
+
sha256: string;
|
|
71
|
+
byteLength: number;
|
|
72
|
+
contentType: string;
|
|
73
|
+
};
|
|
74
|
+
sequence: number;
|
|
75
|
+
throughSequence: number;
|
|
76
|
+
identity: string;
|
|
77
|
+
digest: string;
|
|
78
|
+
}, {
|
|
79
|
+
destinationId: import("convex/values").VString<string, "required">;
|
|
80
|
+
callId: import("convex/values").VString<string, "required">;
|
|
81
|
+
sequence: import("convex/values").VFloat64<number, "required">;
|
|
82
|
+
throughSequence: import("convex/values").VFloat64<number, "required">;
|
|
83
|
+
identity: import("convex/values").VString<string, "required">;
|
|
84
|
+
digest: import("convex/values").VString<string, "required">;
|
|
85
|
+
reference: import("convex/values").VObject<{
|
|
86
|
+
key: string;
|
|
87
|
+
sha256: string;
|
|
88
|
+
byteLength: number;
|
|
89
|
+
contentType: string;
|
|
90
|
+
}, {
|
|
91
|
+
key: import("convex/values").VString<string, "required">;
|
|
92
|
+
sha256: import("convex/values").VString<string, "required">;
|
|
93
|
+
byteLength: import("convex/values").VFloat64<number, "required">;
|
|
94
|
+
contentType: import("convex/values").VString<string, "required">;
|
|
95
|
+
}, "required", "key" | "sha256" | "byteLength" | "contentType">;
|
|
96
|
+
receivedAt: import("convex/values").VFloat64<number, "required">;
|
|
97
|
+
}, "required", "destinationId" | "callId" | "receivedAt" | "reference" | "sequence" | "throughSequence" | "identity" | "digest" | "reference.key" | "reference.sha256" | "reference.byteLength" | "reference.contentType">, {
|
|
98
|
+
by_identity: ["destinationId", "identity", "_creationTime"];
|
|
99
|
+
by_sequence: ["destinationId", "callId", "sequence", "_creationTime"];
|
|
100
|
+
}, {}, {}>;
|
|
101
|
+
receipts: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
102
|
+
destinationId: string;
|
|
103
|
+
callId: string;
|
|
104
|
+
receivedAt: number;
|
|
105
|
+
sequence: number;
|
|
106
|
+
identity: string;
|
|
107
|
+
digest: string;
|
|
108
|
+
}, {
|
|
109
|
+
destinationId: import("convex/values").VString<string, "required">;
|
|
110
|
+
identity: import("convex/values").VString<string, "required">;
|
|
111
|
+
digest: import("convex/values").VString<string, "required">;
|
|
112
|
+
callId: import("convex/values").VString<string, "required">;
|
|
113
|
+
sequence: import("convex/values").VFloat64<number, "required">;
|
|
114
|
+
receivedAt: import("convex/values").VFloat64<number, "required">;
|
|
115
|
+
}, "required", "destinationId" | "callId" | "receivedAt" | "sequence" | "identity" | "digest">, {
|
|
116
|
+
by_identity: ["destinationId", "identity", "_creationTime"];
|
|
117
|
+
}, {}, {}>;
|
|
118
|
+
bootHealth: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
119
|
+
destinationId: string;
|
|
120
|
+
instanceId: string;
|
|
121
|
+
pluginBootId: string;
|
|
122
|
+
observedAt: number;
|
|
123
|
+
healthJson: string;
|
|
124
|
+
}, {
|
|
125
|
+
destinationId: import("convex/values").VString<string, "required">;
|
|
126
|
+
instanceId: import("convex/values").VString<string, "required">;
|
|
127
|
+
pluginBootId: import("convex/values").VString<string, "required">;
|
|
128
|
+
observedAt: import("convex/values").VFloat64<number, "required">;
|
|
129
|
+
healthJson: import("convex/values").VString<string, "required">;
|
|
130
|
+
}, "required", "destinationId" | "instanceId" | "pluginBootId" | "observedAt" | "healthJson">, {
|
|
131
|
+
by_boot: ["destinationId", "instanceId", "pluginBootId", "_creationTime"];
|
|
132
|
+
}, {}, {}>;
|
|
133
|
+
sourceStatus: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
134
|
+
healthJson?: string | undefined;
|
|
135
|
+
destinationId: string;
|
|
136
|
+
instanceId: string;
|
|
137
|
+
pluginBootId: string;
|
|
138
|
+
lastObservedAt: string;
|
|
139
|
+
lastReceivedAt: number;
|
|
140
|
+
coverage: string;
|
|
141
|
+
}, {
|
|
142
|
+
destinationId: import("convex/values").VString<string, "required">;
|
|
143
|
+
instanceId: import("convex/values").VString<string, "required">;
|
|
144
|
+
pluginBootId: import("convex/values").VString<string, "required">;
|
|
145
|
+
lastObservedAt: import("convex/values").VString<string, "required">;
|
|
146
|
+
lastReceivedAt: import("convex/values").VFloat64<number, "required">;
|
|
147
|
+
coverage: import("convex/values").VString<string, "required">;
|
|
148
|
+
healthJson: import("convex/values").VString<string | undefined, "optional">;
|
|
149
|
+
}, "required", "destinationId" | "instanceId" | "pluginBootId" | "healthJson" | "lastObservedAt" | "lastReceivedAt" | "coverage">, {
|
|
150
|
+
by_source: ["destinationId", "instanceId", "_creationTime"];
|
|
151
|
+
}, {}, {}>;
|
|
152
|
+
}, true>;
|
|
153
|
+
};
|
|
154
|
+
export default _default;
|
|
155
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,wBAAgB,QAAQ,CACtB,UAAU,EAAE,UAAU,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,EAChE,IAAI,SAA0B,QAG/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAED,wBAA6C"}
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
import schema from "./component/schema.js";
|
|
3
|
+
const modules = import.meta.glob("./component/**/*.ts");
|
|
4
|
+
export function register(testConvex, name = "cliproxyObservability") {
|
|
5
|
+
testConvex.registerComponent(name, schema, modules);
|
|
6
|
+
}
|
|
7
|
+
export default { modules, register, schema };
|
|
8
|
+
//# sourceMappingURL=test.js.map
|
package/dist/test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAIrC,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAE3C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAExD,MAAM,UAAU,QAAQ,CACtB,UAAgE,EAChE,IAAI,GAAG,uBAAuB;IAE9B,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shpitdev/convex-cliproxy-observability",
|
|
3
|
+
"description": "Private stock CLIProxy capture, protocol projections, and bounded model-call reads.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": "Anand Pant",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": "github:anandpant/convex-components",
|
|
8
|
+
"homepage": "https://github.com/anandpant/convex-components#readme",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/anandpant/convex-components/issues"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"convex",
|
|
14
|
+
"component",
|
|
15
|
+
"cliproxy",
|
|
16
|
+
"observability"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"files": [
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"exports": {
|
|
25
|
+
"./package.json": "./package.json",
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/client.d.ts",
|
|
28
|
+
"default": "./dist/client.js"
|
|
29
|
+
},
|
|
30
|
+
"./test": "./src/test.ts",
|
|
31
|
+
"./_generated/component.js": {
|
|
32
|
+
"types": "./dist/component/_generated/component.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./_generated/component": {
|
|
35
|
+
"types": "./dist/component/_generated/component.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./convex.config.js": {
|
|
38
|
+
"types": "./dist/component/convex.config.d.ts",
|
|
39
|
+
"default": "./dist/component/convex.config.js"
|
|
40
|
+
},
|
|
41
|
+
"./convex.config": {
|
|
42
|
+
"types": "./dist/component/convex.config.d.ts",
|
|
43
|
+
"default": "./dist/component/convex.config.js"
|
|
44
|
+
},
|
|
45
|
+
"./model-call": {
|
|
46
|
+
"types": "./dist/model-call/index.d.ts",
|
|
47
|
+
"default": "./dist/model-call/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./protocols": {
|
|
50
|
+
"types": "./dist/protocols/index.d.ts",
|
|
51
|
+
"default": "./dist/protocols/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./capture": {
|
|
54
|
+
"types": "./dist/capture/index.d.ts",
|
|
55
|
+
"default": "./dist/capture/index.js"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"types": "./dist/client.d.ts",
|
|
59
|
+
"module": "./dist/client.js",
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.14.0"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public",
|
|
65
|
+
"registry": "https://registry.npmjs.org/"
|
|
66
|
+
},
|
|
67
|
+
"sideEffects": false,
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsc6 --project tsconfig.build.json",
|
|
70
|
+
"build:clean": "rm -rf dist *.tsbuildinfo && pnpm build",
|
|
71
|
+
"codegen": "convex codegen --component-dir ./src/component",
|
|
72
|
+
"pack:check": "pnpm build:clean && pnpm pack --pack-destination .pack && node scripts/check-pack.mjs",
|
|
73
|
+
"prepare": "pnpm build",
|
|
74
|
+
"test": "vitest run --typecheck",
|
|
75
|
+
"typecheck": "tsc6 --noEmit && tsc6 --noEmit --project tsconfig.test.json"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"convex": "^1.42.2"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@edge-runtime/vm": "^5.0.0",
|
|
82
|
+
"convex": "^1.45.0",
|
|
83
|
+
"convex-test": "^0.0.56",
|
|
84
|
+
"vite": "^8.2.2",
|
|
85
|
+
"vitest": "^4.1.11"
|
|
86
|
+
},
|
|
87
|
+
"dependencies": {
|
|
88
|
+
"zod": "^4.4.3"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { ExactCorrelation } from "../model-call/index.js";
|
|
2
|
+
export const MAX_ENVELOPE_BYTES = 2 * 1024 * 1024;
|
|
3
|
+
export const MAX_CONTENT_BYTES = 1024 * 1024;
|
|
4
|
+
const id = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$/;
|
|
5
|
+
const routes = new Set([
|
|
6
|
+
"GET /v1/models",
|
|
7
|
+
"POST /v1/messages",
|
|
8
|
+
"POST /v1/messages/count_tokens",
|
|
9
|
+
"POST /v1/responses",
|
|
10
|
+
"POST /v1/chat/completions",
|
|
11
|
+
]);
|
|
12
|
+
export type CaptureObservationV1 = {
|
|
13
|
+
schemaVersion: 1;
|
|
14
|
+
pluginVersion: string;
|
|
15
|
+
redactionVersion: string;
|
|
16
|
+
destinationId: string;
|
|
17
|
+
instanceId: string;
|
|
18
|
+
pluginBootId: string;
|
|
19
|
+
requestId: string;
|
|
20
|
+
sequence: number;
|
|
21
|
+
kind: "request" | "response" | "stream_init" | "stream_chunk" | "completion" | "capture_health";
|
|
22
|
+
observedAt: string;
|
|
23
|
+
offsetNs: number;
|
|
24
|
+
route: string;
|
|
25
|
+
configRevision: string;
|
|
26
|
+
sourceFormat?: string;
|
|
27
|
+
requestedModel?: string;
|
|
28
|
+
sourceTraceId?: string;
|
|
29
|
+
correlation?: ExactCorrelation;
|
|
30
|
+
correlationConflicts?: string[];
|
|
31
|
+
stockChunkIndex?: number;
|
|
32
|
+
body?: string;
|
|
33
|
+
observedBodyBytes?: number;
|
|
34
|
+
bodyFraming?: "stock_json_chunk" | "stock_sse_candidate";
|
|
35
|
+
contentSha256: string;
|
|
36
|
+
contentBytes: number;
|
|
37
|
+
completionOutcome?: string;
|
|
38
|
+
executionStatusCode?: number;
|
|
39
|
+
executionStartedAt?: string;
|
|
40
|
+
executionCompletedAt?: string;
|
|
41
|
+
error?: string;
|
|
42
|
+
gap?: string;
|
|
43
|
+
bodyFromSequence?: number;
|
|
44
|
+
droppedObservationsTotal?: number;
|
|
45
|
+
scopeConflictsTotal?: number;
|
|
46
|
+
};
|
|
47
|
+
export async function sha256(bytes: Uint8Array): Promise<string> {
|
|
48
|
+
const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes));
|
|
49
|
+
return Array.from(new Uint8Array(digest), (v) => v.toString(16).padStart(2, "0")).join("");
|
|
50
|
+
}
|
|
51
|
+
export async function observationIdentity(o: CaptureObservationV1) {
|
|
52
|
+
return sha256(
|
|
53
|
+
new TextEncoder().encode(
|
|
54
|
+
JSON.stringify([
|
|
55
|
+
o.destinationId,
|
|
56
|
+
o.instanceId,
|
|
57
|
+
o.pluginBootId,
|
|
58
|
+
o.requestId,
|
|
59
|
+
o.sequence,
|
|
60
|
+
o.kind,
|
|
61
|
+
]),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
export async function callIdentity(
|
|
66
|
+
o: Pick<CaptureObservationV1, "destinationId" | "instanceId" | "pluginBootId" | "requestId">,
|
|
67
|
+
) {
|
|
68
|
+
return sha256(
|
|
69
|
+
new TextEncoder().encode(
|
|
70
|
+
JSON.stringify([o.destinationId, o.instanceId, o.pluginBootId, o.requestId]),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
export function decodeBody(o: CaptureObservationV1): Uint8Array {
|
|
75
|
+
const raw = o.body ?? "";
|
|
76
|
+
if (
|
|
77
|
+
raw.length > Math.ceil(MAX_CONTENT_BYTES / 3) * 4 ||
|
|
78
|
+
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(raw)
|
|
79
|
+
)
|
|
80
|
+
throw new Error("invalid body encoding");
|
|
81
|
+
const decoded = atob(raw);
|
|
82
|
+
if (decoded.length > MAX_CONTENT_BYTES || decoded.length !== o.contentBytes)
|
|
83
|
+
throw new Error("body size mismatch");
|
|
84
|
+
return Uint8Array.from(decoded, (c) => c.charCodeAt(0));
|
|
85
|
+
}
|
|
86
|
+
export async function validateObservation(
|
|
87
|
+
raw: Uint8Array,
|
|
88
|
+
expected: { destinationId: string; instanceIds: readonly string[] },
|
|
89
|
+
) {
|
|
90
|
+
if (raw.byteLength > MAX_ENVELOPE_BYTES) throw new Error("envelope limit");
|
|
91
|
+
const value: unknown = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
|
|
92
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
93
|
+
throw new Error("observation required");
|
|
94
|
+
const o = value as CaptureObservationV1;
|
|
95
|
+
if (
|
|
96
|
+
o.schemaVersion !== 1 ||
|
|
97
|
+
o.destinationId !== expected.destinationId ||
|
|
98
|
+
!expected.instanceIds.includes(o.instanceId)
|
|
99
|
+
)
|
|
100
|
+
throw new Error("scope mismatch");
|
|
101
|
+
for (const field of [
|
|
102
|
+
"destinationId",
|
|
103
|
+
"instanceId",
|
|
104
|
+
"pluginBootId",
|
|
105
|
+
"requestId",
|
|
106
|
+
"configRevision",
|
|
107
|
+
] as const) {
|
|
108
|
+
if (typeof o[field] !== "string" || !id.test(o[field])) throw new Error("invalid identity");
|
|
109
|
+
}
|
|
110
|
+
if (
|
|
111
|
+
!Number.isSafeInteger(o.sequence) ||
|
|
112
|
+
o.sequence < 1 ||
|
|
113
|
+
!Number.isSafeInteger(o.offsetNs) ||
|
|
114
|
+
o.offsetNs < 0 ||
|
|
115
|
+
!Number.isSafeInteger(o.contentBytes) ||
|
|
116
|
+
o.contentBytes < 0 ||
|
|
117
|
+
!routes.has(o.route) ||
|
|
118
|
+
!Number.isFinite(Date.parse(o.observedAt))
|
|
119
|
+
)
|
|
120
|
+
throw new Error("invalid observation metadata");
|
|
121
|
+
if (
|
|
122
|
+
!["request", "response", "stream_init", "stream_chunk", "completion"].includes(o.kind) ||
|
|
123
|
+
!/^[a-f0-9]{64}$/.test(o.contentSha256)
|
|
124
|
+
)
|
|
125
|
+
throw new Error("unsupported observation");
|
|
126
|
+
for (const [key, val] of Object.entries(o.correlation ?? {})) {
|
|
127
|
+
if (
|
|
128
|
+
![
|
|
129
|
+
"requestId",
|
|
130
|
+
"runId",
|
|
131
|
+
"jobId",
|
|
132
|
+
"traceId",
|
|
133
|
+
"rootExecutionId",
|
|
134
|
+
"opencodeSessionId",
|
|
135
|
+
"operationId",
|
|
136
|
+
"stepId",
|
|
137
|
+
"partId",
|
|
138
|
+
"attemptId",
|
|
139
|
+
].includes(key) ||
|
|
140
|
+
typeof val !== "string" ||
|
|
141
|
+
val.length > 256
|
|
142
|
+
)
|
|
143
|
+
throw new Error("invalid correlation");
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
o.correlationConflicts &&
|
|
147
|
+
(!Array.isArray(o.correlationConflicts) ||
|
|
148
|
+
o.correlationConflicts.length > 10 ||
|
|
149
|
+
o.correlationConflicts.some((x) => typeof x !== "string" || x.length > 64))
|
|
150
|
+
)
|
|
151
|
+
throw new Error("invalid correlation conflicts");
|
|
152
|
+
for (const field of [
|
|
153
|
+
"pluginVersion",
|
|
154
|
+
"redactionVersion",
|
|
155
|
+
"sourceFormat",
|
|
156
|
+
"requestedModel",
|
|
157
|
+
"sourceTraceId",
|
|
158
|
+
"gap",
|
|
159
|
+
"completionOutcome",
|
|
160
|
+
] as const) {
|
|
161
|
+
if (o[field] !== undefined && (typeof o[field] !== "string" || o[field]!.length > 256))
|
|
162
|
+
throw new Error("invalid optional metadata");
|
|
163
|
+
}
|
|
164
|
+
if (!o.pluginVersion || !o.redactionVersion) throw new Error("capture versions required");
|
|
165
|
+
if (
|
|
166
|
+
o.kind === "completion" &&
|
|
167
|
+
!["succeeded", "failed", "canceled", "rejected"].includes(o.completionOutcome ?? "")
|
|
168
|
+
)
|
|
169
|
+
throw new Error("invalid completion outcome");
|
|
170
|
+
if (
|
|
171
|
+
o.bodyFraming !== undefined &&
|
|
172
|
+
!["stock_json_chunk", "stock_sse_candidate"].includes(o.bodyFraming)
|
|
173
|
+
)
|
|
174
|
+
throw new Error("invalid body framing");
|
|
175
|
+
if (
|
|
176
|
+
o.observedBodyBytes !== undefined &&
|
|
177
|
+
(!Number.isSafeInteger(o.observedBodyBytes) ||
|
|
178
|
+
o.observedBodyBytes < 0 ||
|
|
179
|
+
o.observedBodyBytes > MAX_ENVELOPE_BYTES)
|
|
180
|
+
)
|
|
181
|
+
throw new Error("invalid observed byte count");
|
|
182
|
+
const body = decodeBody(o);
|
|
183
|
+
if ((await sha256(body)) !== o.contentSha256) throw new Error("content digest mismatch");
|
|
184
|
+
return {
|
|
185
|
+
observation: o,
|
|
186
|
+
body,
|
|
187
|
+
identity: await observationIdentity(o),
|
|
188
|
+
callId: await callIdentity(o),
|
|
189
|
+
digest: await sha256(raw),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** One immutable NDJSON segment batches observations: no per-token Convex writes. */
|
|
194
|
+
export type CaptureSegmentV1 = {
|
|
195
|
+
schemaVersion: 1;
|
|
196
|
+
operation: "segment";
|
|
197
|
+
destinationId: string;
|
|
198
|
+
instanceId: string;
|
|
199
|
+
pluginBootId: string;
|
|
200
|
+
requestId: string;
|
|
201
|
+
firstSequence: number;
|
|
202
|
+
throughSequence: number;
|
|
203
|
+
contentSha256: string;
|
|
204
|
+
contentBytes: number;
|
|
205
|
+
contentBase64: string;
|
|
206
|
+
};
|
|
207
|
+
export const MAX_SEGMENT_BYTES = 1408 * 1024;
|
|
208
|
+
export async function validateSegment(
|
|
209
|
+
raw: Uint8Array,
|
|
210
|
+
expected: { destinationId: string; instanceIds: readonly string[] },
|
|
211
|
+
) {
|
|
212
|
+
if (raw.length > MAX_ENVELOPE_BYTES) throw new Error("envelope limit");
|
|
213
|
+
const envelope = JSON.parse(
|
|
214
|
+
new TextDecoder("utf-8", { fatal: true }).decode(raw),
|
|
215
|
+
) as CaptureSegmentV1;
|
|
216
|
+
if (
|
|
217
|
+
envelope.schemaVersion !== 1 ||
|
|
218
|
+
envelope.operation !== "segment" ||
|
|
219
|
+
envelope.destinationId !== expected.destinationId ||
|
|
220
|
+
!expected.instanceIds.includes(envelope.instanceId) ||
|
|
221
|
+
!Number.isSafeInteger(envelope.firstSequence) ||
|
|
222
|
+
!Number.isSafeInteger(envelope.throughSequence) ||
|
|
223
|
+
envelope.firstSequence < 1 ||
|
|
224
|
+
envelope.throughSequence < envelope.firstSequence ||
|
|
225
|
+
envelope.throughSequence - envelope.firstSequence >= 256 ||
|
|
226
|
+
typeof envelope.contentBase64 !== "string" ||
|
|
227
|
+
envelope.contentBase64.length > Math.ceil(MAX_SEGMENT_BYTES / 3) * 4
|
|
228
|
+
)
|
|
229
|
+
throw new Error("invalid segment");
|
|
230
|
+
const content = Uint8Array.from(atob(envelope.contentBase64), (c) => c.charCodeAt(0));
|
|
231
|
+
if (
|
|
232
|
+
content.byteLength !== envelope.contentBytes ||
|
|
233
|
+
content.byteLength > MAX_SEGMENT_BYTES ||
|
|
234
|
+
(await sha256(content)) !== envelope.contentSha256
|
|
235
|
+
)
|
|
236
|
+
throw new Error("segment integrity failure");
|
|
237
|
+
const lines = new TextDecoder("utf-8", { fatal: true }).decode(content).split("\n");
|
|
238
|
+
if (lines.at(-1) === "") lines.pop();
|
|
239
|
+
if (lines.length !== envelope.throughSequence - envelope.firstSequence + 1)
|
|
240
|
+
throw new Error("segment sequence count mismatch");
|
|
241
|
+
const observations: CaptureObservationV1[] = [];
|
|
242
|
+
for (const [index, line] of lines.entries()) {
|
|
243
|
+
const { observation } = await validateObservation(new TextEncoder().encode(line), expected);
|
|
244
|
+
if (
|
|
245
|
+
observation.instanceId !== envelope.instanceId ||
|
|
246
|
+
observation.pluginBootId !== envelope.pluginBootId ||
|
|
247
|
+
observation.requestId !== envelope.requestId ||
|
|
248
|
+
observation.sequence !== envelope.firstSequence + index
|
|
249
|
+
)
|
|
250
|
+
throw new Error("segment identity mismatch");
|
|
251
|
+
observations.push(observation);
|
|
252
|
+
}
|
|
253
|
+
const first = observations[0]!;
|
|
254
|
+
if (
|
|
255
|
+
observations.some(
|
|
256
|
+
(o, index) =>
|
|
257
|
+
o.route !== first.route ||
|
|
258
|
+
o.configRevision !== first.configRevision ||
|
|
259
|
+
(o.kind === "completion" && index !== observations.length - 1),
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
throw new Error("inconsistent segment lifecycle");
|
|
263
|
+
const callId = await callIdentity(first);
|
|
264
|
+
const identity = await sha256(
|
|
265
|
+
new TextEncoder().encode(
|
|
266
|
+
JSON.stringify([
|
|
267
|
+
envelope.destinationId,
|
|
268
|
+
envelope.instanceId,
|
|
269
|
+
envelope.pluginBootId,
|
|
270
|
+
envelope.requestId,
|
|
271
|
+
envelope.firstSequence,
|
|
272
|
+
envelope.throughSequence,
|
|
273
|
+
]),
|
|
274
|
+
),
|
|
275
|
+
);
|
|
276
|
+
return { envelope, observations, content, callId, identity, digest: envelope.contentSha256 };
|
|
277
|
+
}
|