@open-cr-agent/cloud-contract 0.0.0-stage → 0.5.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.
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ /** The providers the gateway knows today; GET /api/providers lists them with their paths. */
3
+ export declare const PROVIDER_IDS: readonly ["openai", "anthropic", "openrouter", "gemini", "deepseek", "mistral", "groq", "xai", "together", "fireworks", "cerebras", "moonshot", "moonshot-cn", "kimi-coding", "zai", "zhipu", "qwen", "qwen-cn", "minimax", "minimax-cn", "siliconflow", "siliconflow-cn", "volcengine", "qianfan", "hunyuan", "stepfun", "baichuan", "longcat", "huggingface", "cohere", "vercel", "deepinfra", "perplexity", "venice", "novita", "nebius", "sambanova", "inference-net", "chutes", "upstage", "ai21", "ionos", "scaleway", "featherless", "opencode-zen"];
4
+ export type ProviderId = (typeof PROVIDER_IDS)[number];
5
+ export declare const PROVIDER_ID: RegExp;
6
+ export declare const MODEL_ID: RegExp;
7
+ /** A path the gateway forwards, as GET /api/providers lists it. */
8
+ export declare const GATEWAY_PATH: RegExp;
9
+ export declare const CLOUD_PREFIX = "ocra-";
10
+ /** Whether `model` is ocra-<provider>/<model> with a provider id and model id ocra Cloud may use. */
11
+ export declare function isCloudModel(model: string): boolean;
12
+ /** How a provider takes a reasoning effort; unlisted, OpenAI's `reasoning_effort`. */
13
+ export declare const EFFORT_STYLES: readonly ["openai", "openrouter"];
14
+ export type EffortStyle = (typeof EFFORT_STYLES)[number];
15
+ export declare const providersAnswerSchema: z.ZodObject<{
16
+ providers: z.ZodArray<z.ZodUnknown>;
17
+ }, z.core.$strip>;
18
+ export declare const providerEntrySchema: z.ZodObject<{
19
+ name: z.ZodString;
20
+ paths: z.ZodArray<z.ZodUnknown>;
21
+ effort: z.ZodOptional<z.ZodUnknown>;
22
+ }, z.core.$strip>;
23
+ export type ProviderEntry = z.infer<typeof providerEntrySchema>;
24
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1,80 @@
1
+ import { z } from "zod";
2
+ // The model providers ocra Cloud's gateway forwards to (ADR-0024). A model
3
+ // named ocra-<provider>/<model> goes through the gateway with the key the
4
+ // account stored for <provider>.
5
+ /** The providers the gateway knows today; GET /api/providers lists them with their paths. */
6
+ export const PROVIDER_IDS = [
7
+ "openai",
8
+ "anthropic",
9
+ "openrouter",
10
+ "gemini",
11
+ "deepseek",
12
+ "mistral",
13
+ "groq",
14
+ "xai",
15
+ "together",
16
+ "fireworks",
17
+ "cerebras",
18
+ "moonshot",
19
+ "moonshot-cn",
20
+ "kimi-coding",
21
+ "zai",
22
+ "zhipu",
23
+ "qwen",
24
+ "qwen-cn",
25
+ "minimax",
26
+ "minimax-cn",
27
+ "siliconflow",
28
+ "siliconflow-cn",
29
+ "volcengine",
30
+ "qianfan",
31
+ "hunyuan",
32
+ "stepfun",
33
+ "baichuan",
34
+ "longcat",
35
+ "huggingface",
36
+ "cohere",
37
+ "vercel",
38
+ "deepinfra",
39
+ "perplexity",
40
+ "venice",
41
+ "novita",
42
+ "nebius",
43
+ "sambanova",
44
+ "inference-net",
45
+ "chutes",
46
+ "upstage",
47
+ "ai21",
48
+ "ionos",
49
+ "scaleway",
50
+ "featherless",
51
+ "opencode-zen",
52
+ ];
53
+ // What a provider id and a model id may look like. A client checks names
54
+ // against these, not against PROVIDER_IDS, so a provider the gateway adds
55
+ // later works without a new client.
56
+ export const PROVIDER_ID = /^[a-z][a-z0-9-]{0,39}$/;
57
+ export const MODEL_ID = /^[\w./:@-]{1,200}$/;
58
+ /** A path the gateway forwards, as GET /api/providers lists it. */
59
+ export const GATEWAY_PATH = /^\/[\w./-]+$/;
60
+ export const CLOUD_PREFIX = "ocra-";
61
+ /** Whether `model` is ocra-<provider>/<model> with a provider id and model id ocra Cloud may use. */
62
+ export function isCloudModel(model) {
63
+ const slash = model.indexOf("/");
64
+ return (slash > 0 &&
65
+ model.startsWith(CLOUD_PREFIX) &&
66
+ PROVIDER_ID.test(model.slice(CLOUD_PREFIX.length, slash)) &&
67
+ MODEL_ID.test(model.slice(slash + 1)));
68
+ }
69
+ /** How a provider takes a reasoning effort; unlisted, OpenAI's `reasoning_effort`. */
70
+ export const EFFORT_STYLES = ["openai", "openrouter"];
71
+ // GET /api/providers. An entry that does not fit is left out on its own;
72
+ // paths are checked one by one where they are used, and an effort style a
73
+ // client does not know is refused there.
74
+ export const providersAnswerSchema = z.object({ providers: z.array(z.unknown()) });
75
+ export const providerEntrySchema = z.object({
76
+ name: z.string(),
77
+ paths: z.array(z.unknown()),
78
+ effort: z.unknown().optional(),
79
+ });
80
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1,7 @@
1
+ export declare const REDACTED = "[redacted]";
2
+ /** The text with secret-looking tokens replaced, and whether any was. */
3
+ export declare function redact(text: string): {
4
+ text: string;
5
+ redacted: boolean;
6
+ };
7
+ //# sourceMappingURL=redact.d.ts.map
package/dist/redact.js ADDED
@@ -0,0 +1,54 @@
1
+ // Replaces secret-looking tokens in a shared finding (ADR-0028, 2): the CLI
2
+ // before the finding leaves the machine, ocra Cloud again on what it
3
+ // receives. fixtures/redaction-vectors.json pins what both must catch and
4
+ // keep. It catches patterns, not every secret.
5
+ export const REDACTED = "[redacted]";
6
+ const PATTERNS = [
7
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g,
8
+ /\b(AKIA|ASIA)[0-9A-Z]{16}\b/g,
9
+ /\b(ghp|gho|ghs|ghu|ghr)_[A-Za-z0-9]{20,}\b/g,
10
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
11
+ /\bglpat-[A-Za-z0-9_-]{20,}\b/g,
12
+ /\bsk-(ant-|or-|proj-)?[A-Za-z0-9_-]{20,}\b/g,
13
+ /\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g,
14
+ /\bAIza[0-9A-Za-z_-]{35}\b/g,
15
+ /\bnpm_[A-Za-z0-9]{36}\b/g,
16
+ /\bhttps:\/\/hooks\.slack\.com\/(services|workflows|triggers)\/[A-Za-z0-9/_-]+/g,
17
+ /\bhttps:\/\/(ptb\.|canary\.)?discord(app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_-]+/g,
18
+ ];
19
+ // The password in a URL's userinfo: postgres://user:password@host.
20
+ const USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]*:)([^\s/@]+)@/gi;
21
+ // A quoted value assigned to a name that says it is secret:
22
+ // password = "…", "apiKey": "…", DB_PASSWORD: '…'.
23
+ const ASSIGNED = /\b([A-Za-z0-9_.-]*(?:passw(?:or)?d|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|credentials?)["']?\s*[:=]\s*)(["'`])([^"'`\n]{4,})\2/gi;
24
+ // A long hex run with letters and digits: a key or token in hex.
25
+ const HEX = /\b[0-9a-f]{32,}\b/gi;
26
+ const isHexKey = (s) => /[0-9]/.test(s) && /[a-f]/i.test(s);
27
+ // A long base64-like run that looks random: high entropy and a digit.
28
+ // Identifiers stay under the entropy bar; a path or URL path (every segment
29
+ // lowercase, or letters only) is left alone.
30
+ const LONG_RUN = /[A-Za-z0-9+/_-]{32,}={0,2}/g;
31
+ function entropy(s) {
32
+ const counts = new Map();
33
+ for (const ch of s)
34
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
35
+ let bits = 0;
36
+ for (const n of counts.values())
37
+ bits -= (n / s.length) * Math.log2(n / s.length);
38
+ return bits;
39
+ }
40
+ const isPath = (s) => s.includes("/") &&
41
+ s.split("/").every((seg) => /^[a-z0-9_-]*$/.test(seg) || /^[A-Za-z]+$/.test(seg));
42
+ const looksRandom = (s) => /\d/.test(s) && entropy(s) >= 4.3 && !isPath(s);
43
+ /** The text with secret-looking tokens replaced, and whether any was. */
44
+ export function redact(text) {
45
+ let out = text;
46
+ for (const p of PATTERNS)
47
+ out = out.replace(p, REDACTED);
48
+ out = out.replace(USERINFO, (_m, head) => `${head}${REDACTED}@`);
49
+ out = out.replace(ASSIGNED, (_m, head, q) => `${head}${q}${REDACTED}${q}`);
50
+ out = out.replace(HEX, (m) => (isHexKey(m) ? REDACTED : m));
51
+ out = out.replace(LONG_RUN, (m) => (looksRandom(m) ? REDACTED : m));
52
+ return { text: out, redacted: out !== text };
53
+ }
54
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1,158 @@
1
+ import { z } from "zod";
2
+ export declare const severityCountsSchema: z.ZodCatch<z.ZodObject<{
3
+ critical: z.ZodCatch<z.ZodNumber>;
4
+ warning: z.ZodCatch<z.ZodNumber>;
5
+ suggestion: z.ZodCatch<z.ZodNumber>;
6
+ }, z.core.$strip>>;
7
+ export declare const reviewerCountsSchema: z.ZodObject<{
8
+ tasks: z.ZodCatch<z.ZodNumber>;
9
+ failedTasks: z.ZodCatch<z.ZodNumber>;
10
+ findings: z.ZodCatch<z.ZodObject<{
11
+ critical: z.ZodCatch<z.ZodNumber>;
12
+ warning: z.ZodCatch<z.ZodNumber>;
13
+ suggestion: z.ZodCatch<z.ZodNumber>;
14
+ }, z.core.$strip>>;
15
+ costUsd: z.ZodCatch<z.ZodNumber>;
16
+ fixed: z.ZodCatch<z.ZodNumber>;
17
+ dismissed: z.ZodCatch<z.ZodNumber>;
18
+ }, z.core.$strip>;
19
+ export type ReviewerCounts = z.input<typeof reviewerCountsSchema>;
20
+ /** A finding's stable id across runs. */
21
+ export declare const FINGERPRINT: RegExp;
22
+ /**
23
+ * A finding as the CLI shares it when the account shares findings
24
+ * (ADR-0028, 2): already redacted and bounded (redact, MAX_FIELD), which
25
+ * the server does again. One that fails is left out on its own.
26
+ */
27
+ export declare const sharedFindingSchema: z.ZodObject<{
28
+ fingerprint: z.ZodString;
29
+ reviewer: z.ZodString;
30
+ severity: z.ZodEnum<{
31
+ critical: "critical";
32
+ suggestion: "suggestion";
33
+ warning: "warning";
34
+ }>;
35
+ category: z.ZodCatch<z.ZodNullable<z.ZodString>>;
36
+ verification: z.ZodCatch<z.ZodNullable<z.ZodEnum<{
37
+ confirmed: "confirmed";
38
+ uncertain: "uncertain";
39
+ unchecked: "unchecked";
40
+ }>>>;
41
+ file: z.ZodString;
42
+ lineStart: z.ZodCatch<z.ZodNullable<z.ZodNumber>>;
43
+ lineEnd: z.ZodCatch<z.ZodNullable<z.ZodNumber>>;
44
+ title: z.ZodString;
45
+ body: z.ZodString;
46
+ suggestion: z.ZodCatch<z.ZodNullable<z.ZodString>>;
47
+ code: z.ZodCatch<z.ZodNullable<z.ZodString>>;
48
+ redacted: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
49
+ truncated: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
50
+ }, z.core.$strip>;
51
+ export type SharedFinding = z.input<typeof sharedFindingSchema>;
52
+ /**
53
+ * The review's counts, and the shared findings in `findingList`. A finding
54
+ * or a reviewer's counts that do not fit read as null; the server keeps
55
+ * the first MAX_FINDINGS findings and the first MAX_REVIEWERS reviewers
56
+ * under a REVIEWER_ID, and answers what it left out as `dropped`.
57
+ */
58
+ export declare const reviewUploadSchema: z.ZodObject<{
59
+ runId: z.ZodString;
60
+ repoHash: z.ZodString;
61
+ source: z.ZodEnum<{
62
+ github: "github";
63
+ gitlab: "gitlab";
64
+ local: "local";
65
+ }>;
66
+ tier: z.ZodCatch<z.ZodNullable<z.ZodEnum<{
67
+ full: "full";
68
+ lite: "lite";
69
+ trivial: "trivial";
70
+ }>>>;
71
+ verdict: z.ZodCatch<z.ZodNullable<z.ZodEnum<{
72
+ approved: "approved";
73
+ approved_with_comments: "approved_with_comments";
74
+ minor_issues: "minor_issues";
75
+ significant_concerns: "significant_concerns";
76
+ }>>>;
77
+ complete: z.ZodCatch<z.ZodBoolean>;
78
+ findings: z.ZodCatch<z.ZodObject<{
79
+ critical: z.ZodCatch<z.ZodNumber>;
80
+ warning: z.ZodCatch<z.ZodNumber>;
81
+ suggestion: z.ZodCatch<z.ZodNumber>;
82
+ }, z.core.$strip>>;
83
+ files: z.ZodCatch<z.ZodObject<{
84
+ reviewed: z.ZodCatch<z.ZodNumber>;
85
+ notReviewed: z.ZodCatch<z.ZodNumber>;
86
+ }, z.core.$strip>>;
87
+ tasks: z.ZodCatch<z.ZodObject<{
88
+ completed: z.ZodCatch<z.ZodNumber>;
89
+ failed: z.ZodCatch<z.ZodNumber>;
90
+ }, z.core.$strip>>;
91
+ usage: z.ZodCatch<z.ZodObject<{
92
+ inputTokens: z.ZodCatch<z.ZodNumber>;
93
+ outputTokens: z.ZodCatch<z.ZodNumber>;
94
+ costUsd: z.ZodCatch<z.ZodNumber>;
95
+ }, z.core.$strip>>;
96
+ durationMs: z.ZodCatch<z.ZodNumber>;
97
+ ocraVersion: z.ZodCatch<z.ZodNullable<z.ZodString>>;
98
+ reviewers: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodCatch<z.ZodNullable<z.ZodObject<{
99
+ tasks: z.ZodCatch<z.ZodNumber>;
100
+ failedTasks: z.ZodCatch<z.ZodNumber>;
101
+ findings: z.ZodCatch<z.ZodObject<{
102
+ critical: z.ZodCatch<z.ZodNumber>;
103
+ warning: z.ZodCatch<z.ZodNumber>;
104
+ suggestion: z.ZodCatch<z.ZodNumber>;
105
+ }, z.core.$strip>>;
106
+ costUsd: z.ZodCatch<z.ZodNumber>;
107
+ fixed: z.ZodCatch<z.ZodNumber>;
108
+ dismissed: z.ZodCatch<z.ZodNumber>;
109
+ }, z.core.$strip>>>>>>;
110
+ verification: z.ZodCatch<z.ZodOptional<z.ZodObject<{
111
+ confirmed: z.ZodOptional<z.ZodCatch<z.ZodNumber>>;
112
+ uncertain: z.ZodOptional<z.ZodCatch<z.ZodNumber>>;
113
+ unchecked: z.ZodOptional<z.ZodCatch<z.ZodNumber>>;
114
+ }, z.core.$strip>>>;
115
+ outcomes: z.ZodCatch<z.ZodOptional<z.ZodObject<{
116
+ fixed: z.ZodCatch<z.ZodNumber>;
117
+ dismissed: z.ZodCatch<z.ZodNumber>;
118
+ }, z.core.$strip>>>;
119
+ findingList: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCatch<z.ZodNullable<z.ZodObject<{
120
+ fingerprint: z.ZodString;
121
+ reviewer: z.ZodString;
122
+ severity: z.ZodEnum<{
123
+ critical: "critical";
124
+ suggestion: "suggestion";
125
+ warning: "warning";
126
+ }>;
127
+ category: z.ZodCatch<z.ZodNullable<z.ZodString>>;
128
+ verification: z.ZodCatch<z.ZodNullable<z.ZodEnum<{
129
+ confirmed: "confirmed";
130
+ uncertain: "uncertain";
131
+ unchecked: "unchecked";
132
+ }>>>;
133
+ file: z.ZodString;
134
+ lineStart: z.ZodCatch<z.ZodNullable<z.ZodNumber>>;
135
+ lineEnd: z.ZodCatch<z.ZodNullable<z.ZodNumber>>;
136
+ title: z.ZodString;
137
+ body: z.ZodString;
138
+ suggestion: z.ZodCatch<z.ZodNullable<z.ZodString>>;
139
+ code: z.ZodCatch<z.ZodNullable<z.ZodString>>;
140
+ redacted: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
141
+ truncated: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
142
+ }, z.core.$strip>>>>>>;
143
+ }, z.core.$strip>;
144
+ export type ReviewUpload = z.input<typeof reviewUploadSchema>;
145
+ /** What the server took: how many findings it kept, and what it left out. */
146
+ export declare const uploadAnswerSchema: z.ZodObject<{
147
+ id: z.ZodCatch<z.ZodOptional<z.ZodString>>;
148
+ duplicate: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
149
+ findings: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
150
+ truncated: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
151
+ dropped: z.ZodCatch<z.ZodOptional<z.ZodObject<{
152
+ findings: z.ZodOptional<z.ZodCatch<z.ZodNumber>>;
153
+ reviewers: z.ZodOptional<z.ZodCatch<z.ZodNumber>>;
154
+ }, z.core.$strip>>>;
155
+ findingsSkipped: z.ZodCatch<z.ZodOptional<z.ZodLiteral<"account_limit">>>;
156
+ }, z.core.$strip>;
157
+ export type UploadAnswer = z.input<typeof uploadAnswerSchema>;
158
+ //# sourceMappingURL=upload.d.ts.map
package/dist/upload.js ADDED
@@ -0,0 +1,102 @@
1
+ import { z } from "zod";
2
+ import { REPO_HASH, REVIEWER_ID, reviewSourceSchema, riskTierSchema, severitySchema, verdictSchema, verificationSchema, } from "./vocabulary.js";
3
+ // POST /api/reviews: what a signed-in CLI sends after a review (ADR-0024,
4
+ // ADR-0028), and what ocra Cloud answers.
5
+ //
6
+ // Each schema's input type is what a client sends; its output is what the
7
+ // server keeps. Past the identifying fields, a value the server cannot use
8
+ // becomes its default instead of refusing the upload, so a newer client (a
9
+ // new tier, say) still has its counts kept.
10
+ const count = z.number().int().min(0).max(1e12).catch(0);
11
+ const costUsd = z.number().min(0).lt(1e6).catch(0);
12
+ export const severityCountsSchema = z
13
+ .object({ critical: count, warning: count, suggestion: count })
14
+ .catch({ critical: 0, warning: 0, suggestion: 0 });
15
+ export const reviewerCountsSchema = z.object({
16
+ tasks: count,
17
+ failedTasks: count,
18
+ findings: severityCountsSchema,
19
+ costUsd,
20
+ fixed: count,
21
+ dismissed: count,
22
+ });
23
+ /** A finding's stable id across runs. */
24
+ export const FINGERPRINT = /^[\w:.-]{4,128}$/;
25
+ const line = z.number().int().min(0).lt(10_000_000).nullable().catch(null);
26
+ /**
27
+ * A finding as the CLI shares it when the account shares findings
28
+ * (ADR-0028, 2): already redacted and bounded (redact, MAX_FIELD), which
29
+ * the server does again. One that fails is left out on its own.
30
+ */
31
+ export const sharedFindingSchema = z.object({
32
+ fingerprint: z.string().regex(FINGERPRINT),
33
+ reviewer: z.string().regex(REVIEWER_ID),
34
+ severity: severitySchema,
35
+ category: z.string().nullable().catch(null),
36
+ verification: verificationSchema.nullable().catch(null),
37
+ file: z.string().min(1).max(1024),
38
+ lineStart: line,
39
+ lineEnd: line,
40
+ title: z.string(),
41
+ body: z.string(),
42
+ suggestion: z.string().nullable().catch(null),
43
+ code: z.string().nullable().catch(null),
44
+ // Set when the CLI replaced a secret-looking string or cut a long field.
45
+ redacted: z.literal(true).optional().catch(undefined),
46
+ truncated: z.literal(true).optional().catch(undefined),
47
+ });
48
+ /**
49
+ * The review's counts, and the shared findings in `findingList`. A finding
50
+ * or a reviewer's counts that do not fit read as null; the server keeps
51
+ * the first MAX_FINDINGS findings and the first MAX_REVIEWERS reviewers
52
+ * under a REVIEWER_ID, and answers what it left out as `dropped`.
53
+ */
54
+ export const reviewUploadSchema = z.object({
55
+ runId: z.string().regex(/^[\w.-]{1,64}$/),
56
+ repoHash: z.string().regex(REPO_HASH),
57
+ source: reviewSourceSchema,
58
+ tier: riskTierSchema.nullable().catch(null),
59
+ verdict: verdictSchema.nullable().catch(null),
60
+ // As the exit code reads it: incomplete when files went unreviewed or a
61
+ // critical finding could not be verified.
62
+ complete: z.boolean().catch(true),
63
+ findings: severityCountsSchema,
64
+ files: z.object({ reviewed: count, notReviewed: count }).catch({ reviewed: 0, notReviewed: 0 }),
65
+ tasks: z.object({ completed: count, failed: count }).catch({ completed: 0, failed: 0 }),
66
+ usage: z
67
+ .object({ inputTokens: count, outputTokens: count, costUsd })
68
+ .catch({ inputTokens: 0, outputTokens: 0, costUsd: 0 }),
69
+ durationMs: count,
70
+ ocraVersion: z
71
+ .string()
72
+ .regex(/^[\w.+-]{1,32}$/)
73
+ .nullable()
74
+ .catch(null),
75
+ // Added by ADR-0028; optional so a server reading older uploads still can.
76
+ reviewers: z
77
+ .record(z.string(), reviewerCountsSchema.nullable().catch(null))
78
+ .optional()
79
+ .catch(undefined),
80
+ verification: z
81
+ .object({ confirmed: count, uncertain: count, unchecked: count })
82
+ .partial()
83
+ .optional()
84
+ .catch(undefined),
85
+ outcomes: z.object({ fixed: count, dismissed: count }).optional().catch(undefined),
86
+ findingList: z.array(sharedFindingSchema.nullable().catch(null)).optional().catch(undefined),
87
+ });
88
+ /** What the server took: how many findings it kept, and what it left out. */
89
+ export const uploadAnswerSchema = z.object({
90
+ id: z.string().optional().catch(undefined),
91
+ // A retry of a run already uploaded; nothing new was kept.
92
+ duplicate: z.literal(true).optional().catch(undefined),
93
+ findings: z.number().int().positive().optional().catch(undefined),
94
+ truncated: z.literal(true).optional().catch(undefined),
95
+ dropped: z
96
+ .object({ findings: count.optional(), reviewers: count.optional() })
97
+ .optional()
98
+ .catch(undefined),
99
+ // The account holds as many findings as it may; the counts were kept.
100
+ findingsSkipped: z.literal("account_limit").optional().catch(undefined),
101
+ });
102
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ export declare const VERDICTS: readonly ["approved", "approved_with_comments", "minor_issues", "significant_concerns"];
3
+ export declare const verdictSchema: z.ZodEnum<{
4
+ approved: "approved";
5
+ approved_with_comments: "approved_with_comments";
6
+ minor_issues: "minor_issues";
7
+ significant_concerns: "significant_concerns";
8
+ }>;
9
+ export type Verdict = z.infer<typeof verdictSchema>;
10
+ /** The review's risk tier, as a report records it. */
11
+ export declare const RISK_TIERS: readonly ["trivial", "lite", "full"];
12
+ export declare const riskTierSchema: z.ZodEnum<{
13
+ full: "full";
14
+ lite: "lite";
15
+ trivial: "trivial";
16
+ }>;
17
+ export type RiskTier = z.infer<typeof riskTierSchema>;
18
+ export declare const SEVERITIES: readonly ["critical", "warning", "suggestion"];
19
+ export declare const severitySchema: z.ZodEnum<{
20
+ critical: "critical";
21
+ suggestion: "suggestion";
22
+ warning: "warning";
23
+ }>;
24
+ export type Severity = z.infer<typeof severitySchema>;
25
+ export declare const VERIFICATIONS: readonly ["confirmed", "uncertain", "unchecked"];
26
+ export declare const verificationSchema: z.ZodEnum<{
27
+ confirmed: "confirmed";
28
+ uncertain: "uncertain";
29
+ unchecked: "unchecked";
30
+ }>;
31
+ export type Verification = z.infer<typeof verificationSchema>;
32
+ /** Where a review ran. */
33
+ export declare const REVIEW_SOURCES: readonly ["local", "github", "gitlab"];
34
+ export declare const reviewSourceSchema: z.ZodEnum<{
35
+ github: "github";
36
+ gitlab: "gitlab";
37
+ local: "local";
38
+ }>;
39
+ export type ReviewSource = z.infer<typeof reviewSourceSchema>;
40
+ /** The model tiers, strongest first (ADR-0025). */
41
+ export declare const MODEL_TIERS: readonly ["top", "standard", "light"];
42
+ export declare const modelTierSchema: z.ZodEnum<{
43
+ light: "light";
44
+ standard: "standard";
45
+ top: "top";
46
+ }>;
47
+ export type ModelTier = z.infer<typeof modelTierSchema>;
48
+ /** The agents that are not reviewers (ADR-0025). */
49
+ export declare const AGENT_ROLES: readonly ["verifier", "judge", "helper"];
50
+ export declare const agentRoleSchema: z.ZodEnum<{
51
+ helper: "helper";
52
+ judge: "judge";
53
+ verifier: "verifier";
54
+ }>;
55
+ export type AgentRole = z.infer<typeof agentRoleSchema>;
56
+ /** How much a model reasons before it answers (ADR-0025). */
57
+ export declare const EFFORTS: readonly ["none", "minimal", "low", "medium", "high"];
58
+ export declare const effortSchema: z.ZodEnum<{
59
+ high: "high";
60
+ low: "low";
61
+ medium: "medium";
62
+ minimal: "minimal";
63
+ none: "none";
64
+ }>;
65
+ export type Effort = z.infer<typeof effortSchema>;
66
+ /** The built-in runtimes an account may name; a plugin runtime would load code. */
67
+ export declare const RUNTIMES: readonly ["direct", "opencode"];
68
+ export declare const runtimeSchema: z.ZodEnum<{
69
+ direct: "direct";
70
+ opencode: "opencode";
71
+ }>;
72
+ export type Runtime = z.infer<typeof runtimeSchema>;
73
+ /**
74
+ * A reviewer id as the engine makes them: built-in ids, SARIF tool slugs
75
+ * (which may start with a digit) and plugin reviewers. Bounded, and without
76
+ * spaces or punctuation that could mean something else.
77
+ */
78
+ export declare const REVIEWER_ID: RegExp;
79
+ /** A repository as the CLI uploads it: a salted SHA-256, never its name (ADR-0028). */
80
+ export declare const REPO_HASH: RegExp;
81
+ //# sourceMappingURL=vocabulary.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ // The words both sides use. The engine's core declares the same values for
3
+ // its own domain (core depends on nothing); the cli's tests check the two
4
+ // agree, so a new verdict or tier cannot reach one side only.
5
+ export const VERDICTS = [
6
+ "approved",
7
+ "approved_with_comments",
8
+ "minor_issues",
9
+ "significant_concerns",
10
+ ];
11
+ export const verdictSchema = z.enum(VERDICTS);
12
+ /** The review's risk tier, as a report records it. */
13
+ export const RISK_TIERS = ["trivial", "lite", "full"];
14
+ export const riskTierSchema = z.enum(RISK_TIERS);
15
+ export const SEVERITIES = ["critical", "warning", "suggestion"];
16
+ export const severitySchema = z.enum(SEVERITIES);
17
+ export const VERIFICATIONS = ["confirmed", "uncertain", "unchecked"];
18
+ export const verificationSchema = z.enum(VERIFICATIONS);
19
+ /** Where a review ran. */
20
+ export const REVIEW_SOURCES = ["local", "github", "gitlab"];
21
+ export const reviewSourceSchema = z.enum(REVIEW_SOURCES);
22
+ /** The model tiers, strongest first (ADR-0025). */
23
+ export const MODEL_TIERS = ["top", "standard", "light"];
24
+ export const modelTierSchema = z.enum(MODEL_TIERS);
25
+ /** The agents that are not reviewers (ADR-0025). */
26
+ export const AGENT_ROLES = ["verifier", "judge", "helper"];
27
+ export const agentRoleSchema = z.enum(AGENT_ROLES);
28
+ /** How much a model reasons before it answers (ADR-0025). */
29
+ export const EFFORTS = ["none", "minimal", "low", "medium", "high"];
30
+ export const effortSchema = z.enum(EFFORTS);
31
+ /** The built-in runtimes an account may name; a plugin runtime would load code. */
32
+ export const RUNTIMES = ["direct", "opencode"];
33
+ export const runtimeSchema = z.enum(RUNTIMES);
34
+ /**
35
+ * A reviewer id as the engine makes them: built-in ids, SARIF tool slugs
36
+ * (which may start with a digit) and plugin reviewers. Bounded, and without
37
+ * spaces or punctuation that could mean something else.
38
+ */
39
+ export const REVIEWER_ID = /^[A-Za-z0-9][\w.:@-]{0,99}$/;
40
+ /** A repository as the CLI uploads it: a salted SHA-256, never its name (ADR-0028). */
41
+ export const REPO_HASH = /^[0-9a-f]{64}$/;
42
+ //# sourceMappingURL=vocabulary.js.map
@@ -0,0 +1,27 @@
1
+ {
2
+ "about": "GET /api/memory as ocra Cloud answers it. The ocra CLI applies every entry.",
3
+ "answer": {
4
+ "entries": [
5
+ {
6
+ "id": "5f0a6c1e-2b7d-4c3e-8a9f-1d2e3f4a5b6c",
7
+ "repoHash": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
8
+ "repo": "8d969eef",
9
+ "fingerprint": "3f9a1c7e5b2d8a40",
10
+ "file": "src/db.ts",
11
+ "title": "SQL built from input",
12
+ "reason": "The id is validated by the router before it gets here.",
13
+ "createdAt": "2026-10-04T12:00:00.000Z"
14
+ },
15
+ {
16
+ "id": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
17
+ "repoHash": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
18
+ "repo": "8d969eef",
19
+ "fingerprint": "a07c4e9b1d3f5a62",
20
+ "file": "src/retry.ts",
21
+ "title": "Retry without a limit",
22
+ "reason": "Bounded by the caller's timeout.",
23
+ "createdAt": "2026-10-03T08:30:00.000Z"
24
+ }
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "about": "GET /api/preferences as ocra Cloud answers it once every part is saved. The ocra CLI reads all of it as account settings; ocra Cloud saves it with PUT /api/preferences and answers it back unchanged. Its models name providers with an OpenAI-compatible chat route, the only ones ocra Cloud accepts.",
3
+ "answer": {
4
+ "runtime": "direct",
5
+ "models": {
6
+ "top": ["ocra-openrouter/anthropic/claude-opus-4.1"],
7
+ "standard": ["ocra-openrouter/openai/gpt-5-mini", "ocra-deepseek/deepseek-chat"],
8
+ "light": ["ocra-gemini/gemini-2.5-flash"]
9
+ },
10
+ "agents": {
11
+ "effort": { "top": "high", "light": "none" },
12
+ "reviewers": {
13
+ "security": {
14
+ "enabled": true,
15
+ "models": ["ocra-openrouter/anthropic/claude-opus-4.1"],
16
+ "effort": "high"
17
+ },
18
+ "style": { "enabled": false }
19
+ },
20
+ "roles": { "judge": { "models": ["ocra-openai/gpt-5"], "effort": "medium" } }
21
+ },
22
+ "settings": {
23
+ "ultra": true,
24
+ "concurrency": 4,
25
+ "taskTimeoutMinutes": 10,
26
+ "runTimeoutMinutes": 30,
27
+ "maxCostUsd": 2.5,
28
+ "maxTasks": 40,
29
+ "verify": true,
30
+ "judge": true,
31
+ "sampling": { "temperature": 0.2, "seed": 7 },
32
+ "include": ["src/**"],
33
+ "exclude": ["**/*.snap"],
34
+ "rules": [
35
+ { "path": "src/db/**", "rule": "Queries go through the scoped() helper." },
36
+ { "path": ["web/**/*.tsx", "web/**/*.ts"], "rule": "No inline styles." }
37
+ ],
38
+ "plugins": ["@acme/ocra-plugin-sql"],
39
+ "pluginSettings": { "@acme/ocra-plugin-sql": { "dialect": "sqlite" } }
40
+ },
41
+ "updatedAt": "2026-10-04T12:00:00.000Z",
42
+ "version": "0b6c1f0e-5d1e-4a8e-9d0e-6f6d5f4c3b2a"
43
+ }
44
+ }