@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,191 @@
|
|
|
1
|
+
/** CL-01 deterministic protocol conformance harness types (CL-00 contract). */
|
|
2
|
+
|
|
3
|
+
export const SYNTHETIC_MARKER = "ocx-lab-synthetic-v1";
|
|
4
|
+
|
|
5
|
+
export type EvidenceLayer = "protocol_conformance" | "live_route_compatibility" | "task_effectiveness";
|
|
6
|
+
|
|
7
|
+
export type VerificationRole = "required" | "supplemental" | "negative_control";
|
|
8
|
+
|
|
9
|
+
export type FailureClassification =
|
|
10
|
+
| "harness_failure"
|
|
11
|
+
| "timeout"
|
|
12
|
+
| "budget_exhausted"
|
|
13
|
+
| "protocol_failure"
|
|
14
|
+
| "capability_failure"
|
|
15
|
+
| "behavioral_failure"
|
|
16
|
+
| "authentication_blocked"
|
|
17
|
+
| "quota_blocked"
|
|
18
|
+
| "region_blocked"
|
|
19
|
+
| "network_failure"
|
|
20
|
+
| "provider_transient"
|
|
21
|
+
| "inconclusive";
|
|
22
|
+
|
|
23
|
+
export interface FixtureRecord {
|
|
24
|
+
id: string;
|
|
25
|
+
role: "client_request" | "upstream_response" | "adapter_vector" | "synthetic_tool";
|
|
26
|
+
mediaType: string;
|
|
27
|
+
bytesUtf8: string;
|
|
28
|
+
digest: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AssertionSpec {
|
|
32
|
+
id: string;
|
|
33
|
+
operator: string;
|
|
34
|
+
selector: string;
|
|
35
|
+
expected: unknown;
|
|
36
|
+
required: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ExpectedFailureSpec {
|
|
40
|
+
controlKind: "conformance_negative_control" | "capability_absence_control";
|
|
41
|
+
expectedClass: FailureClassification;
|
|
42
|
+
expectedCode: string;
|
|
43
|
+
assertionIds: string[];
|
|
44
|
+
onMatch: "pass" | "unsupported";
|
|
45
|
+
onMismatch: "fail" | "inconclusive";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface CaseRecord {
|
|
49
|
+
id: string;
|
|
50
|
+
suite: string;
|
|
51
|
+
capability: string;
|
|
52
|
+
verificationRole?: VerificationRole;
|
|
53
|
+
requirements: {
|
|
54
|
+
inboundProtocols: string[];
|
|
55
|
+
upstreamProtocols: string[];
|
|
56
|
+
surfaces: string[];
|
|
57
|
+
requiredClaims: string[];
|
|
58
|
+
requiredHarnessFeatures: string[];
|
|
59
|
+
platforms: string[];
|
|
60
|
+
routePreconditions: string[];
|
|
61
|
+
};
|
|
62
|
+
fixture: FixtureRecord;
|
|
63
|
+
initiatingRequest?: FixtureRecord;
|
|
64
|
+
assertions: AssertionSpec[];
|
|
65
|
+
expectedFailure?: ExpectedFailureSpec;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface FailureRule {
|
|
69
|
+
id: string;
|
|
70
|
+
match: string[];
|
|
71
|
+
classification: FailureClassification;
|
|
72
|
+
secondaryCode?: string;
|
|
73
|
+
verdictEffect: "none" | "degraded" | "unsupported";
|
|
74
|
+
retry: "never" | "bounded" | "after_precondition_change";
|
|
75
|
+
expected: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface CaseAuthority {
|
|
79
|
+
schemaVersion: number;
|
|
80
|
+
sourceCommit: string;
|
|
81
|
+
assertionDslVersion: string;
|
|
82
|
+
evidenceSchemaVersion: string;
|
|
83
|
+
failureRuleSets: Record<string, FailureRule[]>;
|
|
84
|
+
expectedFailureRuleTemplate: Pick<FailureRule, "id" | "match" | "retry" | "expected">;
|
|
85
|
+
manifestDefaults: {
|
|
86
|
+
version: string;
|
|
87
|
+
suiteVersion: string;
|
|
88
|
+
evidenceLayer: EvidenceLayer;
|
|
89
|
+
verificationRole: VerificationRole;
|
|
90
|
+
executionMode: string;
|
|
91
|
+
freshness: { maxAgeMs: number | null };
|
|
92
|
+
executionLimits: Record<string, number>;
|
|
93
|
+
artifactPolicy: Record<string, unknown>;
|
|
94
|
+
failureRuleSet: string;
|
|
95
|
+
};
|
|
96
|
+
cases: CaseRecord[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface NormalizedEvent {
|
|
100
|
+
event: string;
|
|
101
|
+
data: unknown;
|
|
102
|
+
ordinal: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ToolCallProjection {
|
|
106
|
+
id: string;
|
|
107
|
+
name: string;
|
|
108
|
+
arguments: unknown;
|
|
109
|
+
kind: "function" | "custom";
|
|
110
|
+
ordinal: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface McpCallProjection {
|
|
114
|
+
namespace: string;
|
|
115
|
+
name: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface NormalizedObservation {
|
|
119
|
+
client: {
|
|
120
|
+
request: { status: number; headers: Record<string, string>; json: unknown; rawBytes: number };
|
|
121
|
+
response: {
|
|
122
|
+
status: number;
|
|
123
|
+
headers: Record<string, string>;
|
|
124
|
+
json: unknown;
|
|
125
|
+
events: NormalizedEvent[];
|
|
126
|
+
toolCalls: ToolCallProjection[];
|
|
127
|
+
mcpCalls: McpCallProjection[];
|
|
128
|
+
terminal: string | null;
|
|
129
|
+
normalizedText: string;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
upstream: {
|
|
133
|
+
requests: Array<{ status: number; headers: Record<string, string>; json: unknown; rawBytes: number }>;
|
|
134
|
+
responses: unknown[];
|
|
135
|
+
};
|
|
136
|
+
process: { exitCode: number | null };
|
|
137
|
+
verifiers: Record<string, unknown>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface AssertionResult {
|
|
141
|
+
id: string;
|
|
142
|
+
operator: string;
|
|
143
|
+
required: boolean;
|
|
144
|
+
passed: boolean;
|
|
145
|
+
observedSummary: string;
|
|
146
|
+
reason?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ProtocolExecutionContextV1 {
|
|
150
|
+
inboundProtocol: string;
|
|
151
|
+
upstreamProtocol: string;
|
|
152
|
+
surface: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface ScenarioRunResult {
|
|
156
|
+
scenarioId: string;
|
|
157
|
+
suite: string;
|
|
158
|
+
startedAt: number;
|
|
159
|
+
completedAt: number;
|
|
160
|
+
passed: boolean;
|
|
161
|
+
classification: FailureClassification;
|
|
162
|
+
secondaryCode?: string;
|
|
163
|
+
assertionResults: AssertionResult[];
|
|
164
|
+
expectedFailureMatched?: boolean;
|
|
165
|
+
diagnostics: string[];
|
|
166
|
+
executionContext: ProtocolExecutionContextV1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** All eight live-route compatibility suites frozen by CL-03 Live V1. */
|
|
170
|
+
export const CL03_LIVE_SUITES = [
|
|
171
|
+
"responses-core",
|
|
172
|
+
"chat-core",
|
|
173
|
+
"anthropic-core",
|
|
174
|
+
"tools-core",
|
|
175
|
+
"codex-core",
|
|
176
|
+
"vision-core",
|
|
177
|
+
"reasoning-core",
|
|
178
|
+
"mcp-core",
|
|
179
|
+
] as const;
|
|
180
|
+
|
|
181
|
+
/** All eight protocol-conformance suites frozen by CL-00 Protocol V1. */
|
|
182
|
+
export const CL01_SUITES = [
|
|
183
|
+
"responses-core",
|
|
184
|
+
"chat-core",
|
|
185
|
+
"anthropic-core",
|
|
186
|
+
"tools-core",
|
|
187
|
+
"codex-core",
|
|
188
|
+
"vision-core",
|
|
189
|
+
"reasoning-core",
|
|
190
|
+
"mcp-core",
|
|
191
|
+
] as const;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** CL-02 projection / ledger constants frozen from CL-00 contracts. */
|
|
2
|
+
|
|
3
|
+
export const LAB_EVENT_SCHEMA_VERSION = 1;
|
|
4
|
+
export const LAB_PROJECTION_SPEC_VERSION = "cl-02.v1";
|
|
5
|
+
export const LAB_PRODUCER = "opencodex-lab";
|
|
6
|
+
export const LAB_PRODUCER_VERSION = "2.10.2";
|
|
7
|
+
|
|
8
|
+
export const MAX_INVALIDATION_TARGETS = 1024;
|
|
9
|
+
export const MAX_BYTES_PER_ARTIFACT = 256 * 1024;
|
|
10
|
+
export const MAX_AGGREGATE_ARTIFACT_BYTES = 1024 * 1024;
|
|
11
|
+
export const MAX_ARTIFACTS_PER_RUN = 16;
|
|
12
|
+
export const MAX_SERIALIZED_EVENT_BYTES = 64 * 1024;
|
|
13
|
+
export const MAX_SANITIZED_STRING_FIELD = 4 * 1024;
|
|
14
|
+
export const MAX_EVENT_NESTING_DEPTH = 8;
|
|
15
|
+
export const MAX_OBJECT_KEYS_PER_EVENT = 64;
|
|
16
|
+
export const MAX_ARRAY_ELEMENTS_PER_EVENT = 256;
|
|
17
|
+
|
|
18
|
+
export const OBSERVATION_LIMIT_NAMES = [
|
|
19
|
+
"totalTimeoutMs",
|
|
20
|
+
"connectTimeoutMs",
|
|
21
|
+
"firstByteTimeoutMs",
|
|
22
|
+
"inactivityTimeoutMs",
|
|
23
|
+
"maxRequests",
|
|
24
|
+
"maxInputBytes",
|
|
25
|
+
"maxOutputBytes",
|
|
26
|
+
"maxOutputTokens",
|
|
27
|
+
"maxToolCalls",
|
|
28
|
+
"maxArtifactBytes",
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
export type ObservationLimitName = (typeof OBSERVATION_LIMIT_NAMES)[number];
|
|
32
|
+
|
|
33
|
+
export const EVENT_KINDS = [
|
|
34
|
+
"observation",
|
|
35
|
+
"claim_snapshot",
|
|
36
|
+
"invalidation",
|
|
37
|
+
"purge_tombstone",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
export type LabEventKind = (typeof EVENT_KINDS)[number];
|
|
41
|
+
|
|
42
|
+
export const EVIDENCE_LAYERS = [
|
|
43
|
+
"protocol_conformance",
|
|
44
|
+
"live_route_compatibility",
|
|
45
|
+
"task_effectiveness",
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
export type EvidenceLayer = (typeof EVIDENCE_LAYERS)[number];
|
|
49
|
+
|
|
50
|
+
export const VERDICTS = [
|
|
51
|
+
"UNKNOWN",
|
|
52
|
+
"CLAIMED",
|
|
53
|
+
"PROBED",
|
|
54
|
+
"VERIFIED",
|
|
55
|
+
"DEGRADED",
|
|
56
|
+
"BLOCKED",
|
|
57
|
+
"UNSUPPORTED",
|
|
58
|
+
] as const;
|
|
59
|
+
|
|
60
|
+
export type CompatibilityVerdict = (typeof VERDICTS)[number];
|
|
61
|
+
|
|
62
|
+
export const INVALIDATION_REASONS = [
|
|
63
|
+
"harness_defect",
|
|
64
|
+
"fixture_defect",
|
|
65
|
+
"redaction_defect",
|
|
66
|
+
"integrity_defect",
|
|
67
|
+
"contract_artifact_missing",
|
|
68
|
+
"manual_correction",
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
export type InvalidationReason = (typeof INVALIDATION_REASONS)[number];
|
|
72
|
+
|
|
73
|
+
export const PURGE_ACTIONS = [
|
|
74
|
+
"ledger",
|
|
75
|
+
"sqlite",
|
|
76
|
+
"artifact",
|
|
77
|
+
"scratch",
|
|
78
|
+
"export",
|
|
79
|
+
] as const;
|
|
80
|
+
|
|
81
|
+
export type PurgeAction = (typeof PURGE_ACTIONS)[number];
|
|
82
|
+
|
|
83
|
+
export const CLAIM_SOURCE_KINDS = [
|
|
84
|
+
"provider_config",
|
|
85
|
+
"provider_registry",
|
|
86
|
+
"cached_catalog",
|
|
87
|
+
"native_metadata",
|
|
88
|
+
"adapter_inference",
|
|
89
|
+
] as const;
|
|
90
|
+
|
|
91
|
+
export type ClaimSourceKind = (typeof CLAIM_SOURCE_KINDS)[number];
|
|
92
|
+
|
|
93
|
+
export const ARTIFACT_CLASSES = [
|
|
94
|
+
"scenario_manifest",
|
|
95
|
+
"suite_manifest",
|
|
96
|
+
"fixture",
|
|
97
|
+
"claim_source_manifest",
|
|
98
|
+
"assertion_report",
|
|
99
|
+
"request_shape",
|
|
100
|
+
"response_shape",
|
|
101
|
+
"event_trace",
|
|
102
|
+
"error_taxonomy",
|
|
103
|
+
"verifier_summary",
|
|
104
|
+
] as const;
|
|
105
|
+
|
|
106
|
+
export type ArtifactClass = (typeof ARTIFACT_CLASSES)[number];
|
|
107
|
+
|
|
108
|
+
export const CONTRACT_ARTIFACT_CLASSES = [
|
|
109
|
+
"scenario_manifest",
|
|
110
|
+
"suite_manifest",
|
|
111
|
+
"fixture",
|
|
112
|
+
"claim_source_manifest",
|
|
113
|
+
] as const;
|
|
114
|
+
|
|
115
|
+
export type ContractArtifactClass = (typeof CONTRACT_ARTIFACT_CLASSES)[number];
|
|
116
|
+
|
|
117
|
+
export const EXECUTION_MODES = ["fixture", "live", "fabric"] as const;
|
|
118
|
+
export type ExecutionMode = (typeof EXECUTION_MODES)[number];
|
|
119
|
+
|
|
120
|
+
export const OUTCOMES = ["pass", "fail", "blocked", "inconclusive"] as const;
|
|
121
|
+
export type ObservationOutcome = (typeof OUTCOMES)[number];
|
|
122
|
+
|
|
123
|
+
export const CLAIM_POLARITIES = ["supported", "not_supported", "withdrawn"] as const;
|
|
124
|
+
export type ClaimPolarity = (typeof CLAIM_POLARITIES)[number];
|
|
125
|
+
|
|
126
|
+
export const ARTIFACT_FILENAME_EXT = ".bin";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash, createHmac } from "node:crypto";
|
|
2
|
+
import { jcsStringify } from "./conformance/jcs";
|
|
3
|
+
import {
|
|
4
|
+
fixtureDigest,
|
|
5
|
+
scenarioManifestDigest,
|
|
6
|
+
suiteManifestDigest,
|
|
7
|
+
} from "./conformance/digest";
|
|
8
|
+
|
|
9
|
+
export { fixtureDigest, scenarioManifestDigest, suiteManifestDigest, jcsStringify };
|
|
10
|
+
export { jcsEqual } from "./conformance/jcs";
|
|
11
|
+
|
|
12
|
+
/** Domain-separated SHA-256: UTF-8(domain) || NUL || payload. */
|
|
13
|
+
export function domainHash(domain: string, payload: Uint8Array | string): string {
|
|
14
|
+
const hash = createHash("sha256");
|
|
15
|
+
hash.update(new TextEncoder().encode(`${domain}\0`));
|
|
16
|
+
if (typeof payload === "string") hash.update(new TextEncoder().encode(payload));
|
|
17
|
+
else hash.update(payload);
|
|
18
|
+
return hash.digest("hex");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const LAB_DOMAIN = {
|
|
22
|
+
event: "ocx-lab:event:v1",
|
|
23
|
+
subject: "ocx-lab:subject:v1",
|
|
24
|
+
claimSourceManifest: "ocx-lab:claim-source-manifest:v1",
|
|
25
|
+
scenarioManifest: "ocx-lab:scenario-manifest:v1",
|
|
26
|
+
suiteManifest: "ocx-lab:suite-manifest:v1",
|
|
27
|
+
fixture: "ocx-lab:fixture:v1",
|
|
28
|
+
localFingerprint: "ocx-lab:local-fingerprint:v1",
|
|
29
|
+
artifactBytes: "ocx-lab:artifact-bytes:v1",
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
export function eventIdForPayload(eventWithoutId: unknown): string {
|
|
33
|
+
return domainHash(LAB_DOMAIN.event, jcsStringify(eventWithoutId));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function subjectIdForSubject(subject: unknown): string {
|
|
37
|
+
return domainHash(LAB_DOMAIN.subject, jcsStringify(subject));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function claimSourceManifestDigest(manifest: unknown): string {
|
|
41
|
+
return domainHash(LAB_DOMAIN.claimSourceManifest, jcsStringify(manifest));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Content digest for already-redacted artifact bytes (not contract fixture domain). */
|
|
45
|
+
export function artifactBytesDigest(bytes: Uint8Array): string {
|
|
46
|
+
return domainHash(LAB_DOMAIN.artifactBytes, bytes);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function localFingerprint(
|
|
50
|
+
fieldName: string,
|
|
51
|
+
value: unknown,
|
|
52
|
+
salt: Uint8Array | string,
|
|
53
|
+
): string {
|
|
54
|
+
const key = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
|
|
55
|
+
const preimage = `${LAB_DOMAIN.localFingerprint}\0${fieldName}\0`;
|
|
56
|
+
return createHmac("sha256", key)
|
|
57
|
+
.update(new TextEncoder().encode(preimage))
|
|
58
|
+
.update(new TextEncoder().encode(jcsStringify(value)))
|
|
59
|
+
.digest("hex");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isSha256Hex(value: string): boolean {
|
|
63
|
+
return /^[0-9a-f]{64}$/.test(value);
|
|
64
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_ARRAY_ELEMENTS_PER_EVENT,
|
|
3
|
+
MAX_EVENT_NESTING_DEPTH,
|
|
4
|
+
MAX_OBJECT_KEYS_PER_EVENT,
|
|
5
|
+
MAX_SANITIZED_STRING_FIELD,
|
|
6
|
+
} from "../constants";
|
|
7
|
+
import { LabValidationError } from "./errors";
|
|
8
|
+
|
|
9
|
+
const FORBIDDEN_KEY_RE =
|
|
10
|
+
/(?:^|_)(?:secret|token|apikey|api_key|password|credential|authorization|cookie|bearer|prompt|repository|filepath|file_path|baseurl|base_url|hostname|rawrequest|raw_request)(?:$|_)/i;
|
|
11
|
+
|
|
12
|
+
const FORBIDDEN_EXACT_KEYS = new Set([
|
|
13
|
+
"authorization",
|
|
14
|
+
"apiKey",
|
|
15
|
+
"api_key",
|
|
16
|
+
"password",
|
|
17
|
+
"secret",
|
|
18
|
+
"token",
|
|
19
|
+
"cookie",
|
|
20
|
+
"cookies",
|
|
21
|
+
"headers",
|
|
22
|
+
"prompt",
|
|
23
|
+
"path",
|
|
24
|
+
"filepath",
|
|
25
|
+
"filePath",
|
|
26
|
+
"repository",
|
|
27
|
+
"repo",
|
|
28
|
+
"baseUrl",
|
|
29
|
+
"url",
|
|
30
|
+
"rawRequest",
|
|
31
|
+
"rawBytes",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const RAW_POSIX_PATH_RE =
|
|
35
|
+
/(?:^|[^A-Za-z0-9._~/])\/(?:(?=$|[^A-Za-z0-9._~/])|(?!\/)(?![ \t\r\n])(?:\/|[^/\0\r\n]+)+\/?(?=$|[^A-Za-z0-9._~/]))/u;
|
|
36
|
+
|
|
37
|
+
function fieldPath(base: string, key: string | number): string {
|
|
38
|
+
return base ? `${base}.${String(key)}` : String(key);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function assertAllowedValueType(value: unknown, path: string): void {
|
|
42
|
+
if (value === null) return;
|
|
43
|
+
const t = typeof value;
|
|
44
|
+
if (t === "string" || t === "boolean") return;
|
|
45
|
+
if (t === "number") {
|
|
46
|
+
if (!Number.isFinite(value as number)) {
|
|
47
|
+
throw new LabValidationError("non_finite", `${path} must be finite number`);
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(value) || (t === "object" && value !== null)) return;
|
|
52
|
+
throw new LabValidationError("unsupported_type", `${path} has unsupported type`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function assertKeyAllowed(key: string, path: string): void {
|
|
56
|
+
if (FORBIDDEN_EXACT_KEYS.has(key) || FORBIDDEN_KEY_RE.test(key)) {
|
|
57
|
+
throw new LabValidationError("forbidden_field", `${path} forbidden`);
|
|
58
|
+
}
|
|
59
|
+
if (key.includes("\0")) {
|
|
60
|
+
throw new LabValidationError("nul_forbidden", `${path} contains NUL`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Recursive deterministic CL-00 structural/privacy ceilings before JSONL admission. */
|
|
65
|
+
export function enforceEventStructureLimits(
|
|
66
|
+
value: unknown,
|
|
67
|
+
path = "",
|
|
68
|
+
depth = 0,
|
|
69
|
+
): void {
|
|
70
|
+
assertAllowedValueType(value, path || "event");
|
|
71
|
+
if (value === null) return;
|
|
72
|
+
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
const bytes = new TextEncoder().encode(value).byteLength;
|
|
75
|
+
if (bytes > MAX_SANITIZED_STRING_FIELD) {
|
|
76
|
+
throw new LabValidationError("field_too_large", `${path} exceeds ${MAX_SANITIZED_STRING_FIELD} bytes`);
|
|
77
|
+
}
|
|
78
|
+
if (/sk-[a-z0-9]{10,}/i.test(value) || /Bearer\s+\S+/i.test(value)) {
|
|
79
|
+
throw new LabValidationError("secret_pattern", `${path} contains secret-shaped data`);
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
/^[A-Za-z]:\\/.test(value) ||
|
|
83
|
+
RAW_POSIX_PATH_RE.test(value) ||
|
|
84
|
+
value.includes("\\Users\\")
|
|
85
|
+
) {
|
|
86
|
+
throw new LabValidationError("raw_path", `${path} contains raw filesystem path`);
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof value === "number" || typeof value === "boolean") return;
|
|
92
|
+
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
if (depth >= MAX_EVENT_NESTING_DEPTH) {
|
|
95
|
+
throw new LabValidationError("nesting_depth", `${path} exceeds nesting depth ${MAX_EVENT_NESTING_DEPTH}`);
|
|
96
|
+
}
|
|
97
|
+
if (value.length > MAX_ARRAY_ELEMENTS_PER_EVENT) {
|
|
98
|
+
throw new LabValidationError("array_too_large", `${path} exceeds ${MAX_ARRAY_ELEMENTS_PER_EVENT} elements`);
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < value.length; i++) {
|
|
101
|
+
enforceEventStructureLimits(value[i], fieldPath(path, i), depth + 1);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (depth >= MAX_EVENT_NESTING_DEPTH) {
|
|
107
|
+
throw new LabValidationError("nesting_depth", `${path} exceeds nesting depth ${MAX_EVENT_NESTING_DEPTH}`);
|
|
108
|
+
}
|
|
109
|
+
const keys = Object.keys(value as Record<string, unknown>);
|
|
110
|
+
if (keys.length > MAX_OBJECT_KEYS_PER_EVENT) {
|
|
111
|
+
throw new LabValidationError("too_many_keys", `${path} exceeds ${MAX_OBJECT_KEYS_PER_EVENT} keys`);
|
|
112
|
+
}
|
|
113
|
+
for (const key of keys) {
|
|
114
|
+
assertKeyAllowed(key, fieldPath(path, key));
|
|
115
|
+
enforceEventStructureLimits((value as Record<string, unknown>)[key], fieldPath(path, key), depth + 1);
|
|
116
|
+
}
|
|
117
|
+
}
|