@bitkyc08/opencodex 2.11.1 → 2.12.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/README.md +1 -1
- package/bin/ocx.mjs +4 -0
- package/gui/dist/assets/index-BNVYzdn0.css +1 -0
- package/gui/dist/assets/index-Cw1_mxO8.js +70 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/command-code.ts +94 -5
- package/src/adapters/cursor/live-transport.ts +14 -1
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google.ts +24 -7
- package/src/adapters/openai-chat.ts +233 -305
- package/src/adapters/openai-responses.ts +62 -0
- package/src/adapters/tool-catalog-nudge.ts +2 -11
- package/src/claude/outbound.ts +42 -14
- package/src/cli/claude.ts +61 -9
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +8 -1
- package/src/cli/config-command.ts +4 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/help.ts +15 -0
- package/src/cli/index.ts +27 -11
- package/src/cli/lab.ts +367 -0
- package/src/cli/provider.ts +9 -1
- package/src/clients/config-export.ts +100 -3
- package/src/codex/app-server-processes.ts +11 -8
- package/src/codex/auth-api.ts +92 -28
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/catalog/metadata.ts +7 -3
- package/src/codex/catalog/provider-fetch.ts +191 -47
- package/src/codex/catalog.ts +2 -2
- package/src/codex/history-job.ts +83 -18
- package/src/codex/history-migration-guardian.ts +9 -16
- package/src/codex/history-provider.ts +178 -1
- package/src/codex/history-transition.ts +5 -5
- package/src/codex/history-worker.ts +22 -2
- package/src/codex/native-profile-store.ts +1 -1
- package/src/codex/plan.ts +15 -0
- package/src/codex/quota.ts +6 -6
- package/src/codex/routing.ts +3 -3
- package/src/codex/subagent-model-fallback.ts +246 -22
- package/src/codex/user-identity.ts +63 -12
- package/src/config.ts +265 -32
- package/src/images/loop.ts +3 -1
- package/src/images/plan.ts +23 -13
- package/src/integrations/omp-yaml-source.ts +225 -0
- package/src/integrations/ownership.ts +5 -5
- package/src/integrations/state.ts +45 -6
- package/src/integrations/writer.ts +38 -3
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +24 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +160 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +57 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +191 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +228 -0
- package/src/lab/events/validate.ts +780 -0
- package/src/lab/index.ts +36 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +241 -0
- package/src/lab/ledger/store.ts +263 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +311 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +116 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +344 -0
- package/src/lab/observe/from-live.ts +103 -0
- package/src/lab/paths.ts +65 -0
- package/src/lab/projection/rebuild.ts +477 -0
- package/src/lab/projection/schema.ts +122 -0
- package/src/lab/projection/verdicts.ts +438 -0
- package/src/lab/projection/verification.ts +332 -0
- package/src/lab/query/catalog.ts +72 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/index.ts +34 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +81 -0
- package/src/lab/subject/route-subject.ts +73 -0
- package/src/lib/bounded-body.ts +104 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +45 -0
- package/src/lib/pinned-http.ts +36 -7
- package/src/oauth/index.ts +20 -1
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/login-cli.ts +38 -4
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/codex-capacity.ts +7 -4
- package/src/providers/command-code-efforts.ts +5 -2
- package/src/providers/derive.ts +105 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/openai-tiers.ts +59 -3
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/quota.ts +67 -21
- package/src/providers/registry.ts +118 -5
- package/src/router.ts +15 -1
- package/src/routing/analytics.ts +8 -7
- package/src/routing/evaluator.ts +51 -5
- package/src/routing/profile.ts +10 -1
- package/src/routing/trace.ts +42 -6
- package/src/server/auth-cors.ts +15 -0
- package/src/server/background-lifecycle.ts +169 -0
- package/src/server/chat-completions.ts +18 -3
- package/src/server/claude-messages.ts +1 -0
- package/src/server/images.ts +78 -11
- package/src/server/index.ts +72 -18
- package/src/server/lifecycle.ts +6 -0
- package/src/server/live.ts +5 -1
- package/src/server/management/agent-settings-routes.ts +41 -24
- package/src/server/management/body.ts +6 -0
- package/src/server/management/config-routes.ts +51 -8
- package/src/server/management/lab-routes.ts +388 -0
- package/src/server/management/logs-usage-routes.ts +19 -1
- package/src/server/management/model-rows.ts +25 -11
- package/src/server/management/native-integration-routes.ts +3 -3
- package/src/server/management/provider-routes.ts +5 -0
- package/src/server/management/shared.ts +8 -1
- package/src/server/management/usage-summary-cache.ts +2 -0
- package/src/server/management/vision-sidecar-options.ts +125 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/request-decompress.ts +106 -5
- package/src/server/responses/collaboration.ts +4 -1
- package/src/server/responses/core.ts +117 -31
- package/src/server/responses-json-events.ts +50 -12
- package/src/server/search.ts +23 -7
- package/src/server/ws-bridge.ts +3 -1
- package/src/service.ts +40 -20
- package/src/types.ts +74 -0
- package/src/update/job.ts +10 -2
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +9 -0
- package/src/usage/log.ts +9 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/describe.ts +2 -2
- package/src/vision/eligibility.ts +229 -0
- package/src/vision/index.ts +20 -2
- package/src/web-search/executor.ts +2 -2
- package/src/web-search/index.ts +4 -2
- package/src/web-search/loop.ts +3 -1
- package/src/web-search/parse.ts +72 -3
- package/gui/dist/assets/index-BA1dgo4q.css +0 -1
- package/gui/dist/assets/index-DDZpgzKk.js +0 -70
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LAB_QUERY_DEFAULT_PAGE_SIZE,
|
|
3
|
+
LAB_QUERY_MAX_PAGE_SIZE,
|
|
4
|
+
} from "./constants";
|
|
5
|
+
import {
|
|
6
|
+
closeLabReadConnection,
|
|
7
|
+
countTable,
|
|
8
|
+
openLabReadConnection,
|
|
9
|
+
type LabReadConnection,
|
|
10
|
+
} from "./connection";
|
|
11
|
+
import {
|
|
12
|
+
assertCursorFilters,
|
|
13
|
+
artifactCursor,
|
|
14
|
+
decodeLabCursor,
|
|
15
|
+
encodeLabCursor,
|
|
16
|
+
eventCursor,
|
|
17
|
+
filterKeyFor,
|
|
18
|
+
observationCursor,
|
|
19
|
+
subjectCursor,
|
|
20
|
+
verdictCursor,
|
|
21
|
+
} from "./cursor";
|
|
22
|
+
import {
|
|
23
|
+
mapArtifactRow,
|
|
24
|
+
mapEventListRow,
|
|
25
|
+
mapObservationRow,
|
|
26
|
+
mapSubjectJson,
|
|
27
|
+
mapSubjectListRow,
|
|
28
|
+
mapVerdictRow,
|
|
29
|
+
parseEventPayloadToDto,
|
|
30
|
+
} from "./dto-map";
|
|
31
|
+
import { InvalidCursorError } from "./errors";
|
|
32
|
+
import { queryLabCatalog } from "./catalog";
|
|
33
|
+
import type {
|
|
34
|
+
ArtifactFilters,
|
|
35
|
+
ArtifactMetadataDto,
|
|
36
|
+
EventFilters,
|
|
37
|
+
EventListItemDto,
|
|
38
|
+
LabEventDto,
|
|
39
|
+
LabStatusDto,
|
|
40
|
+
ObservationDto,
|
|
41
|
+
ObservationFilters,
|
|
42
|
+
PaginatedResult,
|
|
43
|
+
SubjectDto,
|
|
44
|
+
SubjectListItemDto,
|
|
45
|
+
VerdictDto,
|
|
46
|
+
VerdictFilters,
|
|
47
|
+
} from "./types";
|
|
48
|
+
|
|
49
|
+
function clampLimit(limit: number | undefined): number {
|
|
50
|
+
if (limit === undefined) return LAB_QUERY_DEFAULT_PAGE_SIZE;
|
|
51
|
+
return Math.min(Math.max(limit, 1), LAB_QUERY_MAX_PAGE_SIZE);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function queryLabStatus(configDir?: string): LabStatusDto {
|
|
55
|
+
try {
|
|
56
|
+
const conn = openLabReadConnection(configDir);
|
|
57
|
+
try {
|
|
58
|
+
return {
|
|
59
|
+
projectionAvailable: true,
|
|
60
|
+
sqliteSchemaVersion: conn.schemaVersion,
|
|
61
|
+
projectionSpecVersion: conn.projectionSpecVersion,
|
|
62
|
+
builtAtMs: conn.builtAtMs,
|
|
63
|
+
eventCount: countTable(conn, "events"),
|
|
64
|
+
subjectCount: countTable(conn, "subjects"),
|
|
65
|
+
observationCount: countTable(conn, "observations"),
|
|
66
|
+
claimCount: countTable(conn, "claims"),
|
|
67
|
+
verdictCount: countTable(conn, "verdicts"),
|
|
68
|
+
artifactCount: countTable(conn, "artifacts"),
|
|
69
|
+
corruptionCount: countTable(conn, "corruption"),
|
|
70
|
+
};
|
|
71
|
+
} finally {
|
|
72
|
+
closeLabReadConnection(conn);
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
76
|
+
const code = String((err as { code: unknown }).code);
|
|
77
|
+
if (code === "lab_projection_incompatible") {
|
|
78
|
+
return { projectionAvailable: false, projectionIncompatible: true };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { projectionAvailable: false };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function withConnection<T>(configDir: string | undefined, fn: (conn: LabReadConnection) => T): T {
|
|
86
|
+
const conn = openLabReadConnection(configDir);
|
|
87
|
+
try {
|
|
88
|
+
return fn(conn);
|
|
89
|
+
} finally {
|
|
90
|
+
closeLabReadConnection(conn);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function queryLabVerdicts(
|
|
95
|
+
filters: VerdictFilters = {},
|
|
96
|
+
cursorRaw?: string | null,
|
|
97
|
+
limit?: number,
|
|
98
|
+
configDir?: string,
|
|
99
|
+
): PaginatedResult<VerdictDto> {
|
|
100
|
+
const pageSize = clampLimit(limit);
|
|
101
|
+
const filterRecord: Record<string, unknown> = { ...filters };
|
|
102
|
+
const decoded = cursorRaw ? decodeLabCursor(cursorRaw) : null;
|
|
103
|
+
if (cursorRaw && !decoded) throw new InvalidCursorError();
|
|
104
|
+
if (decoded) assertCursorFilters(decoded, filterRecord, "verdicts");
|
|
105
|
+
|
|
106
|
+
return withConnection(configDir, (conn) => {
|
|
107
|
+
const where: string[] = [];
|
|
108
|
+
const params: Array<string | number> = [];
|
|
109
|
+
if (filters.subjectId) {
|
|
110
|
+
where.push("subject_id = ?");
|
|
111
|
+
params.push(filters.subjectId);
|
|
112
|
+
}
|
|
113
|
+
if (filters.layer) {
|
|
114
|
+
where.push("evidence_layer = ?");
|
|
115
|
+
params.push(filters.layer);
|
|
116
|
+
}
|
|
117
|
+
if (filters.suiteId) {
|
|
118
|
+
where.push("suite_id = ?");
|
|
119
|
+
params.push(filters.suiteId);
|
|
120
|
+
}
|
|
121
|
+
if (filters.verdict) {
|
|
122
|
+
where.push("verdict = ?");
|
|
123
|
+
params.push(filters.verdict);
|
|
124
|
+
}
|
|
125
|
+
if (filters.from !== undefined) {
|
|
126
|
+
where.push("as_of >= ?");
|
|
127
|
+
params.push(filters.from);
|
|
128
|
+
}
|
|
129
|
+
if (filters.to !== undefined) {
|
|
130
|
+
where.push("as_of <= ?");
|
|
131
|
+
params.push(filters.to);
|
|
132
|
+
}
|
|
133
|
+
if (decoded && decoded.k === "verdicts") {
|
|
134
|
+
where.push("(as_of < ? OR (as_of = ? AND projection_key > ?))");
|
|
135
|
+
params.push(decoded.a, decoded.a, decoded.p);
|
|
136
|
+
}
|
|
137
|
+
const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
138
|
+
const rows = conn.db
|
|
139
|
+
.query(
|
|
140
|
+
`SELECT projection_key, subject_id, evidence_layer, suite_id, suite_version,
|
|
141
|
+
suite_manifest_digest, projection_spec_version, verdict, as_of,
|
|
142
|
+
scenario_manifest_digests_json, claim_source_digest,
|
|
143
|
+
contributing_event_ids_json, contradicting_event_ids_json, notes_json
|
|
144
|
+
FROM verdicts ${whereSql}
|
|
145
|
+
ORDER BY as_of DESC, projection_key ASC
|
|
146
|
+
LIMIT ?`,
|
|
147
|
+
)
|
|
148
|
+
.all(...params, pageSize + 1) as Array<Record<string, unknown>>;
|
|
149
|
+
|
|
150
|
+
const hasMore = rows.length > pageSize;
|
|
151
|
+
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
152
|
+
const items = pageRows.map(mapVerdictRow);
|
|
153
|
+
let nextCursor: string | undefined;
|
|
154
|
+
if (hasMore && pageRows.length > 0) {
|
|
155
|
+
const last = pageRows[pageRows.length - 1]!;
|
|
156
|
+
nextCursor = encodeLabCursor(
|
|
157
|
+
verdictCursor(Number(last.as_of), String(last.projection_key), filterRecord),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
return { items, hasMore, ...(nextCursor ? { nextCursor } : {}) };
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function queryLabSubjects(
|
|
165
|
+
kind?: string,
|
|
166
|
+
cursorRaw?: string | null,
|
|
167
|
+
limit?: number,
|
|
168
|
+
configDir?: string,
|
|
169
|
+
): PaginatedResult<SubjectListItemDto> {
|
|
170
|
+
const pageSize = clampLimit(limit);
|
|
171
|
+
const filterRecord: Record<string, unknown> = { kind };
|
|
172
|
+
const decoded = cursorRaw ? decodeLabCursor(cursorRaw) : null;
|
|
173
|
+
if (cursorRaw && !decoded) throw new InvalidCursorError();
|
|
174
|
+
if (decoded) assertCursorFilters(decoded, filterRecord, "subjects");
|
|
175
|
+
|
|
176
|
+
return withConnection(configDir, (conn) => {
|
|
177
|
+
const where: string[] = [];
|
|
178
|
+
const params: Array<string | number> = [];
|
|
179
|
+
if (kind) {
|
|
180
|
+
where.push("subject_kind = ?");
|
|
181
|
+
params.push(kind);
|
|
182
|
+
}
|
|
183
|
+
if (decoded && decoded.k === "subjects") {
|
|
184
|
+
where.push("subject_id > ?");
|
|
185
|
+
params.push(decoded.s);
|
|
186
|
+
}
|
|
187
|
+
const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
188
|
+
const rows = conn.db
|
|
189
|
+
.query(`SELECT subject_id, subject_kind FROM subjects ${whereSql} ORDER BY subject_id ASC LIMIT ?`)
|
|
190
|
+
.all(...params, pageSize + 1) as Array<Record<string, unknown>>;
|
|
191
|
+
|
|
192
|
+
const hasMore = rows.length > pageSize;
|
|
193
|
+
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
194
|
+
const items = pageRows.map(mapSubjectListRow);
|
|
195
|
+
let nextCursor: string | undefined;
|
|
196
|
+
if (hasMore && pageRows.length > 0) {
|
|
197
|
+
const last = pageRows[pageRows.length - 1]!;
|
|
198
|
+
nextCursor = encodeLabCursor(subjectCursor(String(last.subject_id), filterRecord));
|
|
199
|
+
}
|
|
200
|
+
return { items, hasMore, ...(nextCursor ? { nextCursor } : {}) };
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function queryLabSubjectById(subjectId: string, configDir?: string): SubjectDto | null {
|
|
205
|
+
return withConnection(configDir, (conn) => {
|
|
206
|
+
const row = conn.db
|
|
207
|
+
.query("SELECT subject_json FROM subjects WHERE subject_id = ?")
|
|
208
|
+
.get(subjectId) as { subject_json: string } | null;
|
|
209
|
+
if (!row) return null;
|
|
210
|
+
return mapSubjectJson(row.subject_json);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function queryLabObservations(
|
|
215
|
+
filters: ObservationFilters = {},
|
|
216
|
+
cursorRaw?: string | null,
|
|
217
|
+
limit?: number,
|
|
218
|
+
configDir?: string,
|
|
219
|
+
): PaginatedResult<ObservationDto> {
|
|
220
|
+
const pageSize = clampLimit(limit);
|
|
221
|
+
const filterRecord: Record<string, unknown> = { ...filters };
|
|
222
|
+
const decoded = cursorRaw ? decodeLabCursor(cursorRaw) : null;
|
|
223
|
+
if (cursorRaw && !decoded) throw new InvalidCursorError();
|
|
224
|
+
if (decoded) assertCursorFilters(decoded, filterRecord, "observations");
|
|
225
|
+
|
|
226
|
+
return withConnection(configDir, (conn) => {
|
|
227
|
+
const where: string[] = [];
|
|
228
|
+
const params: Array<string | number> = [];
|
|
229
|
+
if (filters.subjectId) {
|
|
230
|
+
where.push("o.subject_id = ?");
|
|
231
|
+
params.push(filters.subjectId);
|
|
232
|
+
}
|
|
233
|
+
if (filters.layer) {
|
|
234
|
+
where.push("o.evidence_layer = ?");
|
|
235
|
+
params.push(filters.layer);
|
|
236
|
+
}
|
|
237
|
+
if (filters.suiteId) {
|
|
238
|
+
where.push("o.suite_id = ?");
|
|
239
|
+
params.push(filters.suiteId);
|
|
240
|
+
}
|
|
241
|
+
if (filters.scenarioId) {
|
|
242
|
+
where.push("o.scenario_id = ?");
|
|
243
|
+
params.push(filters.scenarioId);
|
|
244
|
+
}
|
|
245
|
+
if (filters.outcome) {
|
|
246
|
+
where.push("o.outcome = ?");
|
|
247
|
+
params.push(filters.outcome);
|
|
248
|
+
}
|
|
249
|
+
if (filters.executionMode) {
|
|
250
|
+
where.push("o.execution_mode = ?");
|
|
251
|
+
params.push(filters.executionMode);
|
|
252
|
+
}
|
|
253
|
+
if (filters.from !== undefined) {
|
|
254
|
+
where.push("o.completed_at >= ?");
|
|
255
|
+
params.push(filters.from);
|
|
256
|
+
}
|
|
257
|
+
if (filters.to !== undefined) {
|
|
258
|
+
where.push("o.completed_at <= ?");
|
|
259
|
+
params.push(filters.to);
|
|
260
|
+
}
|
|
261
|
+
if (decoded && decoded.k === "observations") {
|
|
262
|
+
where.push("(o.completed_at < ? OR (o.completed_at = ? AND o.event_id < ?))");
|
|
263
|
+
params.push(decoded.c, decoded.c, decoded.e);
|
|
264
|
+
}
|
|
265
|
+
const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
266
|
+
const rows = conn.db
|
|
267
|
+
.query(
|
|
268
|
+
`SELECT o.event_id, o.subject_id, o.evidence_layer, o.suite_id, o.suite_version,
|
|
269
|
+
o.suite_manifest_digest, o.scenario_id, o.scenario_version, o.scenario_manifest_digest,
|
|
270
|
+
o.outcome, o.completed_at, o.execution_mode,
|
|
271
|
+
e.excluded, e.exclusion_reason
|
|
272
|
+
FROM observations o
|
|
273
|
+
JOIN events e ON e.event_id = o.event_id
|
|
274
|
+
${whereSql}
|
|
275
|
+
ORDER BY o.completed_at DESC, o.event_id DESC
|
|
276
|
+
LIMIT ?`,
|
|
277
|
+
)
|
|
278
|
+
.all(...params, pageSize + 1) as Array<Record<string, unknown>>;
|
|
279
|
+
|
|
280
|
+
const hasMore = rows.length > pageSize;
|
|
281
|
+
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
282
|
+
const items = pageRows.map((row) =>
|
|
283
|
+
mapObservationRow(row, Number(row.excluded) === 1, row.exclusion_reason ? String(row.exclusion_reason) : null),
|
|
284
|
+
);
|
|
285
|
+
let nextCursor: string | undefined;
|
|
286
|
+
if (hasMore && pageRows.length > 0) {
|
|
287
|
+
const last = pageRows[pageRows.length - 1]!;
|
|
288
|
+
nextCursor = encodeLabCursor(
|
|
289
|
+
observationCursor(Number(last.completed_at), String(last.event_id), filterRecord),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return { items, hasMore, ...(nextCursor ? { nextCursor } : {}) };
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function queryLabEvents(
|
|
297
|
+
filters: EventFilters = {},
|
|
298
|
+
cursorRaw?: string | null,
|
|
299
|
+
limit?: number,
|
|
300
|
+
configDir?: string,
|
|
301
|
+
): PaginatedResult<EventListItemDto> {
|
|
302
|
+
const pageSize = clampLimit(limit);
|
|
303
|
+
const filterRecord: Record<string, unknown> = { ...filters };
|
|
304
|
+
const decoded = cursorRaw ? decodeLabCursor(cursorRaw) : null;
|
|
305
|
+
if (cursorRaw && !decoded) throw new InvalidCursorError();
|
|
306
|
+
if (decoded) assertCursorFilters(decoded, filterRecord, "events");
|
|
307
|
+
|
|
308
|
+
return withConnection(configDir, (conn) => {
|
|
309
|
+
const where: string[] = [];
|
|
310
|
+
const params: Array<string | number> = [];
|
|
311
|
+
if (filters.eventKind) {
|
|
312
|
+
where.push("e.event_kind = ?");
|
|
313
|
+
params.push(filters.eventKind);
|
|
314
|
+
}
|
|
315
|
+
if (filters.from !== undefined) {
|
|
316
|
+
where.push("e.recorded_at >= ?");
|
|
317
|
+
params.push(filters.from);
|
|
318
|
+
}
|
|
319
|
+
if (filters.to !== undefined) {
|
|
320
|
+
where.push("e.recorded_at <= ?");
|
|
321
|
+
params.push(filters.to);
|
|
322
|
+
}
|
|
323
|
+
if (filters.excluded !== undefined) {
|
|
324
|
+
where.push("e.excluded = ?");
|
|
325
|
+
params.push(filters.excluded ? 1 : 0);
|
|
326
|
+
}
|
|
327
|
+
if (filters.subjectId) {
|
|
328
|
+
where.push(
|
|
329
|
+
"(EXISTS (SELECT 1 FROM observations ox WHERE ox.event_id = e.event_id AND ox.subject_id = ?) OR EXISTS (SELECT 1 FROM claims cx WHERE cx.event_id = e.event_id AND cx.subject_id = ?))",
|
|
330
|
+
);
|
|
331
|
+
params.push(filters.subjectId, filters.subjectId);
|
|
332
|
+
}
|
|
333
|
+
if (decoded && decoded.k === "events") {
|
|
334
|
+
where.push("(e.recorded_at < ? OR (e.recorded_at = ? AND e.event_id < ?))");
|
|
335
|
+
params.push(decoded.r, decoded.r, decoded.e);
|
|
336
|
+
}
|
|
337
|
+
const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
338
|
+
const rows = conn.db
|
|
339
|
+
.query(
|
|
340
|
+
`SELECT e.event_id, e.event_kind, e.recorded_at, e.excluded, e.exclusion_reason
|
|
341
|
+
FROM events e
|
|
342
|
+
${whereSql}
|
|
343
|
+
ORDER BY e.recorded_at DESC, e.event_id DESC
|
|
344
|
+
LIMIT ?`,
|
|
345
|
+
)
|
|
346
|
+
.all(...params, pageSize + 1) as Array<Record<string, unknown>>;
|
|
347
|
+
|
|
348
|
+
const hasMore = rows.length > pageSize;
|
|
349
|
+
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
350
|
+
const items = pageRows.map(mapEventListRow);
|
|
351
|
+
let nextCursor: string | undefined;
|
|
352
|
+
if (hasMore && pageRows.length > 0) {
|
|
353
|
+
const last = pageRows[pageRows.length - 1]!;
|
|
354
|
+
nextCursor = encodeLabCursor(
|
|
355
|
+
eventCursor(Number(last.recorded_at), String(last.event_id), filterRecord),
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
return { items, hasMore, ...(nextCursor ? { nextCursor } : {}) };
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function queryLabEventById(eventId: string, configDir?: string): LabEventDto | null {
|
|
363
|
+
return withConnection(configDir, (conn) => {
|
|
364
|
+
const row = conn.db
|
|
365
|
+
.query(
|
|
366
|
+
"SELECT event_id, payload_json, excluded, exclusion_reason FROM events WHERE event_id = ?",
|
|
367
|
+
)
|
|
368
|
+
.get(eventId) as { payload_json: string; excluded: number; exclusion_reason: string | null } | null;
|
|
369
|
+
if (!row) return null;
|
|
370
|
+
const excluded = Number(row.excluded) === 1;
|
|
371
|
+
const exclusionReason = row.exclusion_reason ? String(row.exclusion_reason) : null;
|
|
372
|
+
return parseEventPayloadToDto(row.payload_json, excluded, exclusionReason);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function queryLabArtifacts(
|
|
377
|
+
filters: ArtifactFilters = {},
|
|
378
|
+
cursorRaw?: string | null,
|
|
379
|
+
limit?: number,
|
|
380
|
+
configDir?: string,
|
|
381
|
+
): PaginatedResult<ArtifactMetadataDto> {
|
|
382
|
+
const pageSize = clampLimit(limit);
|
|
383
|
+
const filterRecord: Record<string, unknown> = { ...filters };
|
|
384
|
+
const decoded = cursorRaw ? decodeLabCursor(cursorRaw) : null;
|
|
385
|
+
if (cursorRaw && !decoded) throw new InvalidCursorError();
|
|
386
|
+
if (decoded) assertCursorFilters(decoded, filterRecord, "artifacts");
|
|
387
|
+
|
|
388
|
+
return withConnection(configDir, (conn) => {
|
|
389
|
+
const where: string[] = [];
|
|
390
|
+
const params: Array<string | number> = [];
|
|
391
|
+
if (filters.status) {
|
|
392
|
+
where.push("status = ?");
|
|
393
|
+
params.push(filters.status);
|
|
394
|
+
}
|
|
395
|
+
if (filters.artifactClass) {
|
|
396
|
+
where.push("artifact_class = ?");
|
|
397
|
+
params.push(filters.artifactClass);
|
|
398
|
+
}
|
|
399
|
+
if (decoded && decoded.k === "artifacts") {
|
|
400
|
+
where.push("digest > ?");
|
|
401
|
+
params.push(decoded.d);
|
|
402
|
+
}
|
|
403
|
+
const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
404
|
+
const rows = conn.db
|
|
405
|
+
.query(
|
|
406
|
+
`SELECT digest, artifact_class, media_type, byte_count, status, last_error
|
|
407
|
+
FROM artifacts ${whereSql} ORDER BY digest ASC LIMIT ?`,
|
|
408
|
+
)
|
|
409
|
+
.all(...params, pageSize + 1) as Array<Record<string, unknown>>;
|
|
410
|
+
|
|
411
|
+
const hasMore = rows.length > pageSize;
|
|
412
|
+
const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
|
|
413
|
+
const items = pageRows.map(mapArtifactRow);
|
|
414
|
+
let nextCursor: string | undefined;
|
|
415
|
+
if (hasMore && pageRows.length > 0) {
|
|
416
|
+
const last = pageRows[pageRows.length - 1]!;
|
|
417
|
+
nextCursor = encodeLabCursor(artifactCursor(String(last.digest), filterRecord));
|
|
418
|
+
}
|
|
419
|
+
return { items, hasMore, ...(nextCursor ? { nextCursor } : {}) };
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function queryLabArtifactByDigest(digest: string, configDir?: string): ArtifactMetadataDto | null {
|
|
424
|
+
return withConnection(configDir, (conn) => {
|
|
425
|
+
const row = conn.db
|
|
426
|
+
.query(
|
|
427
|
+
"SELECT digest, artifact_class, media_type, byte_count, status, last_error FROM artifacts WHERE digest = ?",
|
|
428
|
+
)
|
|
429
|
+
.get(digest) as Record<string, unknown> | null;
|
|
430
|
+
if (!row) return null;
|
|
431
|
+
return mapArtifactRow(row);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function queryLabCatalogEntries(
|
|
436
|
+
filters: { layer?: string; suiteId?: string } = {},
|
|
437
|
+
): ReturnType<typeof queryLabCatalog> {
|
|
438
|
+
return queryLabCatalog({
|
|
439
|
+
layer: filters.layer as import("../constants").EvidenceLayer | undefined,
|
|
440
|
+
suiteId: filters.suiteId,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export { filterKeyFor };
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ArtifactClass,
|
|
3
|
+
CompatibilityVerdict,
|
|
4
|
+
EvidenceLayer,
|
|
5
|
+
ExecutionMode,
|
|
6
|
+
InvalidationReason,
|
|
7
|
+
LabEventKind,
|
|
8
|
+
ObservationOutcome,
|
|
9
|
+
PurgeAction,
|
|
10
|
+
} from "../constants";
|
|
11
|
+
|
|
12
|
+
export interface LabStatusDto {
|
|
13
|
+
projectionAvailable: boolean;
|
|
14
|
+
projectionIncompatible?: boolean;
|
|
15
|
+
sqliteSchemaVersion?: number;
|
|
16
|
+
projectionSpecVersion?: string;
|
|
17
|
+
builtAtMs?: number;
|
|
18
|
+
eventCount?: number;
|
|
19
|
+
subjectCount?: number;
|
|
20
|
+
observationCount?: number;
|
|
21
|
+
claimCount?: number;
|
|
22
|
+
verdictCount?: number;
|
|
23
|
+
artifactCount?: number;
|
|
24
|
+
corruptionCount?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VerdictDto {
|
|
28
|
+
projectionKey: string;
|
|
29
|
+
subjectId: string;
|
|
30
|
+
evidenceLayer: EvidenceLayer;
|
|
31
|
+
suiteId: string;
|
|
32
|
+
suiteVersion: string;
|
|
33
|
+
suiteManifestDigest: string;
|
|
34
|
+
projectionSpecVersion: string;
|
|
35
|
+
verdict: CompatibilityVerdict;
|
|
36
|
+
asOf: number;
|
|
37
|
+
scenarioManifestDigests: string[];
|
|
38
|
+
claimSourceDigest: string | null;
|
|
39
|
+
contributingEventIds: string[];
|
|
40
|
+
contradictingEventIds: string[];
|
|
41
|
+
notes: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ProtocolSubjectDto {
|
|
45
|
+
subjectKind: "protocol";
|
|
46
|
+
subjectSchemaVersion: 1;
|
|
47
|
+
opencodexCompatibilityVersion: string;
|
|
48
|
+
effectiveAdapter: string;
|
|
49
|
+
inboundProtocol: string;
|
|
50
|
+
upstreamProtocol: string;
|
|
51
|
+
surface: string;
|
|
52
|
+
behaviorFingerprint: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface RouteDependencyDto {
|
|
56
|
+
role: string;
|
|
57
|
+
providerId: string;
|
|
58
|
+
providerInstanceFingerprint: string;
|
|
59
|
+
clientModelId: string;
|
|
60
|
+
upstreamModelId: string;
|
|
61
|
+
effectiveAdapter: string;
|
|
62
|
+
upstreamProtocol: string;
|
|
63
|
+
endpointFingerprint: string;
|
|
64
|
+
behaviorFingerprint: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RouteSubjectDto {
|
|
68
|
+
subjectKind: "route";
|
|
69
|
+
subjectSchemaVersion: 1;
|
|
70
|
+
providerId: string;
|
|
71
|
+
providerInstanceFingerprint: string;
|
|
72
|
+
clientModelId: string;
|
|
73
|
+
upstreamModelId: string;
|
|
74
|
+
effectiveAdapter: string;
|
|
75
|
+
inboundProtocol: string;
|
|
76
|
+
upstreamProtocol: string;
|
|
77
|
+
surface: string;
|
|
78
|
+
opencodexCompatibilityVersion: string;
|
|
79
|
+
behaviorFingerprint: string;
|
|
80
|
+
endpointFingerprint: string;
|
|
81
|
+
dependencies: RouteDependencyDto[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface TaskSubjectDto {
|
|
85
|
+
subjectKind: "task";
|
|
86
|
+
subjectSchemaVersion: 1;
|
|
87
|
+
routeSubject: RouteSubjectDto;
|
|
88
|
+
taskClassId: string;
|
|
89
|
+
taskClassVersion: string;
|
|
90
|
+
taskFixtureDigest: string;
|
|
91
|
+
verifierManifestDigest: string;
|
|
92
|
+
fabricCompatibilityVersion: string;
|
|
93
|
+
sandboxProfileDigest: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type SubjectDto = ProtocolSubjectDto | RouteSubjectDto | TaskSubjectDto;
|
|
97
|
+
|
|
98
|
+
export interface SubjectListItemDto {
|
|
99
|
+
subjectId: string;
|
|
100
|
+
subjectKind: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ObservationDto {
|
|
104
|
+
eventId: string;
|
|
105
|
+
subjectId: string;
|
|
106
|
+
evidenceLayer: EvidenceLayer;
|
|
107
|
+
suiteId: string;
|
|
108
|
+
suiteVersion: string;
|
|
109
|
+
suiteManifestDigest: string;
|
|
110
|
+
scenarioId: string;
|
|
111
|
+
scenarioVersion: string;
|
|
112
|
+
scenarioManifestDigest: string;
|
|
113
|
+
outcome: ObservationOutcome;
|
|
114
|
+
completedAt: number;
|
|
115
|
+
executionMode: ExecutionMode;
|
|
116
|
+
excluded: boolean;
|
|
117
|
+
exclusionReason: string | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ObservationEventDto {
|
|
121
|
+
eventKind: "observation";
|
|
122
|
+
eventId: string;
|
|
123
|
+
recordedAt: number;
|
|
124
|
+
producer: string;
|
|
125
|
+
producerVersion: string;
|
|
126
|
+
evidenceLayer: EvidenceLayer;
|
|
127
|
+
scenarioId: string;
|
|
128
|
+
scenarioVersion: string;
|
|
129
|
+
scenarioManifestDigest: string;
|
|
130
|
+
suiteId: string;
|
|
131
|
+
suiteVersion: string;
|
|
132
|
+
suiteManifestDigest: string;
|
|
133
|
+
subjectId: string;
|
|
134
|
+
startedAt: number;
|
|
135
|
+
completedAt: number;
|
|
136
|
+
executionMode: ExecutionMode;
|
|
137
|
+
attempt: number;
|
|
138
|
+
outcome: ObservationOutcome;
|
|
139
|
+
excluded: boolean;
|
|
140
|
+
exclusionReason: string | null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface ClaimSnapshotEventDto {
|
|
144
|
+
eventKind: "claim_snapshot";
|
|
145
|
+
eventId: string;
|
|
146
|
+
recordedAt: number;
|
|
147
|
+
producer: string;
|
|
148
|
+
producerVersion: string;
|
|
149
|
+
subjectId: string;
|
|
150
|
+
capability: string;
|
|
151
|
+
polarity: string;
|
|
152
|
+
sourceManifestDigest: string;
|
|
153
|
+
effectiveAt: number;
|
|
154
|
+
excluded: boolean;
|
|
155
|
+
exclusionReason: string | null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface InvalidationEventDto {
|
|
159
|
+
eventKind: "invalidation";
|
|
160
|
+
eventId: string;
|
|
161
|
+
recordedAt: number;
|
|
162
|
+
producer: string;
|
|
163
|
+
producerVersion: string;
|
|
164
|
+
reason: InvalidationReason;
|
|
165
|
+
targetEventIds: string[];
|
|
166
|
+
excluded: boolean;
|
|
167
|
+
exclusionReason: string | null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface PurgeTombstoneEventDto {
|
|
171
|
+
eventKind: "purge_tombstone";
|
|
172
|
+
eventId: string;
|
|
173
|
+
recordedAt: number;
|
|
174
|
+
producer: string;
|
|
175
|
+
producerVersion: string;
|
|
176
|
+
targetEventIds: string[];
|
|
177
|
+
targetArtifactDigests: string[];
|
|
178
|
+
purgeActions: PurgeAction[];
|
|
179
|
+
excluded: boolean;
|
|
180
|
+
exclusionReason: string | null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export type LabEventDto =
|
|
184
|
+
| ObservationEventDto
|
|
185
|
+
| ClaimSnapshotEventDto
|
|
186
|
+
| InvalidationEventDto
|
|
187
|
+
| PurgeTombstoneEventDto;
|
|
188
|
+
|
|
189
|
+
export interface EventListItemDto {
|
|
190
|
+
eventId: string;
|
|
191
|
+
eventKind: LabEventKind;
|
|
192
|
+
recordedAt: number;
|
|
193
|
+
excluded: boolean;
|
|
194
|
+
exclusionReason: string | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface ArtifactMetadataDto {
|
|
198
|
+
digest: string;
|
|
199
|
+
artifactClass: ArtifactClass | null;
|
|
200
|
+
mediaType: string | null;
|
|
201
|
+
byteCount: number | null;
|
|
202
|
+
status: "present" | "corrupt" | "purged_unavailable";
|
|
203
|
+
lastError: string | null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface CatalogScenarioDto {
|
|
207
|
+
scenarioId: string;
|
|
208
|
+
scenarioVersion: string;
|
|
209
|
+
evidenceLayer: EvidenceLayer;
|
|
210
|
+
suiteId: string;
|
|
211
|
+
suiteVersion: string;
|
|
212
|
+
capability: string;
|
|
213
|
+
verificationRole: string;
|
|
214
|
+
requirements: {
|
|
215
|
+
inboundProtocols: string[];
|
|
216
|
+
upstreamProtocols: string[];
|
|
217
|
+
surfaces: string[];
|
|
218
|
+
};
|
|
219
|
+
freshness: Record<string, unknown>;
|
|
220
|
+
scenarioManifestDigest: string;
|
|
221
|
+
suiteManifestDigest: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface PaginatedResult<T> {
|
|
225
|
+
items: T[];
|
|
226
|
+
nextCursor?: string;
|
|
227
|
+
hasMore: boolean;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface VerdictFilters {
|
|
231
|
+
subjectId?: string;
|
|
232
|
+
layer?: EvidenceLayer;
|
|
233
|
+
suiteId?: string;
|
|
234
|
+
verdict?: CompatibilityVerdict;
|
|
235
|
+
from?: number;
|
|
236
|
+
to?: number;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface ObservationFilters {
|
|
240
|
+
subjectId?: string;
|
|
241
|
+
layer?: EvidenceLayer;
|
|
242
|
+
suiteId?: string;
|
|
243
|
+
scenarioId?: string;
|
|
244
|
+
outcome?: ObservationOutcome;
|
|
245
|
+
executionMode?: ExecutionMode;
|
|
246
|
+
from?: number;
|
|
247
|
+
to?: number;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface EventFilters {
|
|
251
|
+
eventKind?: LabEventKind;
|
|
252
|
+
subjectId?: string;
|
|
253
|
+
from?: number;
|
|
254
|
+
to?: number;
|
|
255
|
+
excluded?: boolean;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface ArtifactFilters {
|
|
259
|
+
status?: "present" | "corrupt" | "purged_unavailable";
|
|
260
|
+
artifactClass?: ArtifactClass;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface CatalogFilters {
|
|
264
|
+
layer?: EvidenceLayer;
|
|
265
|
+
suiteId?: string;
|
|
266
|
+
}
|