openmerit 0.1.5 → 0.1.6-preview.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +33 -9
  3. package/dist/core/src/index.d.ts +12 -1
  4. package/dist/core/src/index.js +552 -40
  5. package/dist/pi/src/index.d.ts +17 -0
  6. package/dist/pi/src/index.js +448 -77
  7. package/dist/pi/src/scheduler.d.ts +11 -0
  8. package/dist/pi/src/scheduler.js +137 -0
  9. package/dist/pi/src/wakeup.d.ts +2 -0
  10. package/dist/pi/src/wakeup.js +108 -0
  11. package/dist/protocol/src/index.d.ts +86 -4
  12. package/dist/protocol/src/index.js +1 -1
  13. package/dist/protocol/src/schemas.d.ts +128 -2
  14. package/dist/protocol/src/schemas.js +57 -1
  15. package/dist/terminal/public/app.js +297 -0
  16. package/dist/terminal/public/brands/anthropic.png +0 -0
  17. package/dist/terminal/public/brands/baai.png +0 -0
  18. package/dist/terminal/public/brands/baseten.png +0 -0
  19. package/dist/terminal/public/brands/cerebras.png +0 -0
  20. package/dist/terminal/public/brands/cohere.png +0 -0
  21. package/dist/terminal/public/brands/deepseek.ico +0 -0
  22. package/dist/terminal/public/brands/google.png +0 -0
  23. package/dist/terminal/public/brands/groq.ico +0 -0
  24. package/dist/terminal/public/brands/lm-studio.png +0 -0
  25. package/dist/terminal/public/brands/meta.ico +0 -0
  26. package/dist/terminal/public/brands/mistral.png +0 -0
  27. package/dist/terminal/public/brands/nomic.png +0 -0
  28. package/dist/terminal/public/brands/ollama.png +0 -0
  29. package/dist/terminal/public/brands/openai.png +0 -0
  30. package/dist/terminal/public/brands/openrouter.png +0 -0
  31. package/dist/terminal/public/brands/qwen.png +0 -0
  32. package/dist/terminal/public/brands/vllm.ico +0 -0
  33. package/dist/terminal/public/brands/vllm.png +0 -0
  34. package/dist/terminal/public/favicon.svg +1 -0
  35. package/dist/terminal/public/flow.css +1 -0
  36. package/dist/terminal/public/flow.js +770 -0
  37. package/dist/terminal/public/index.html +21 -0
  38. package/dist/terminal/public/styles.css +779 -0
  39. package/dist/terminal/src/activity-merge.mjs +64 -0
  40. package/dist/terminal/src/browser.mjs +29 -0
  41. package/dist/terminal/src/cli.mjs +60 -0
  42. package/dist/terminal/src/collect.mjs +311 -0
  43. package/dist/terminal/src/discovery.mjs +93 -0
  44. package/dist/terminal/src/hardware.mjs +57 -0
  45. package/dist/terminal/src/project-activity.mjs +156 -0
  46. package/dist/terminal/src/sample.mjs +171 -0
  47. package/dist/terminal/src/server.mjs +56 -0
  48. package/dist/terminal/src/services.mjs +62 -0
  49. package/dist/terminal/src/topology.mjs +30 -0
  50. package/docs/adapter-guide.md +24 -7
  51. package/docs/architecture.md +8 -4
  52. package/docs/automation.md +10 -2
  53. package/docs/budgets.md +37 -0
  54. package/docs/commands.md +85 -0
  55. package/docs/demo-backfill.md +29 -0
  56. package/docs/demo-fieldkit.md +47 -0
  57. package/docs/demo-placement.md +30 -0
  58. package/docs/demo-spam.md +15 -0
  59. package/docs/demo-support.md +42 -0
  60. package/docs/demo.md +57 -0
  61. package/docs/first-trial.md +60 -0
  62. package/docs/getting-started.md +18 -8
  63. package/docs/index.md +40 -0
  64. package/docs/inference-terminal.md +439 -0
  65. package/docs/lifecycle.md +9 -9
  66. package/docs/memo.md +126 -0
  67. package/docs/metrics-and-evidence.md +11 -3
  68. package/docs/operations.md +12 -3
  69. package/docs/pi-extension.md +17 -7
  70. package/docs/roadmap.md +4 -2
  71. package/docs/security.md +15 -1
  72. package/docs/site-artwork-linocut.md +23 -0
  73. package/docs/site-artwork-miniature-diverse.md +28 -0
  74. package/docs/site-artwork-miniature.md +26 -0
  75. package/docs/site-demo.md +177 -0
  76. package/docs/site-design.md +94 -0
  77. package/docs/site-documentation.md +83 -0
  78. package/docs/site-dynamic-og.md +35 -0
  79. package/docs/site-faq-maintenance.md +115 -0
  80. package/docs/site-hero-resolution.md +60 -0
  81. package/docs/site-illustration-sequences.md +227 -0
  82. package/docs/site-inference-terminal.md +203 -0
  83. package/docs/site-memo.md +39 -0
  84. package/docs/site-og-image.md +38 -0
  85. package/docs/site-og-workshop.md +21 -0
  86. package/docs/site-section-artwork.md +56 -0
  87. package/docs/site-skill-review.md +57 -0
  88. package/docs/site-terminal-preview.md +85 -0
  89. package/docs/testing.md +85 -3
  90. package/docs/troubleshooting.md +55 -0
  91. package/package.json +48 -7
@@ -0,0 +1,11 @@
1
+ export interface PiWakeupOptions {
2
+ readonly projectRoot: string;
3
+ readonly extensionPath: string;
4
+ readonly provider: string;
5
+ readonly modelId: string;
6
+ }
7
+ export declare function piWakeupLabel(projectRoot: string): string;
8
+ export declare function piWakeupPlistPath(projectRoot: string): string;
9
+ export declare function renderPiWakeupPlist(options: PiWakeupOptions, piPath: string, runnerPath: string): string;
10
+ export declare function provisionPiWakeup(options: PiWakeupOptions): Promise<string>;
11
+ export declare function removePiWakeup(projectRoot: string): Promise<boolean>;
@@ -0,0 +1,137 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { constants } from "node:fs";
4
+ import { access, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { basename, dirname, extname, join, resolve } from "node:path";
7
+ import { promisify } from "node:util";
8
+ const execFileAsync = promisify(execFile);
9
+ const POLL_SECONDS = 300;
10
+ function projectHash(projectRoot) {
11
+ return createHash("sha256").update(resolve(projectRoot)).digest("hex").slice(0, 16);
12
+ }
13
+ export function piWakeupLabel(projectRoot) {
14
+ return `com.openmerit.pi.${projectHash(projectRoot)}`;
15
+ }
16
+ export function piWakeupPlistPath(projectRoot) {
17
+ return join(homedir(), "Library", "LaunchAgents", `${piWakeupLabel(projectRoot)}.plist`);
18
+ }
19
+ function managedMarker(projectRoot) {
20
+ return `<!-- openmerit-managed:${projectHash(projectRoot)} -->`;
21
+ }
22
+ function xml(value) {
23
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
24
+ .replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
25
+ }
26
+ async function executable(name) {
27
+ for (const directory of (process.env.PATH ?? "").split(":")) {
28
+ if (!directory)
29
+ continue;
30
+ const path = join(directory, name);
31
+ try {
32
+ await access(path, constants.X_OK);
33
+ return path;
34
+ }
35
+ catch { /* try the next PATH entry */ }
36
+ }
37
+ throw new Error(`${name} is not on PATH`);
38
+ }
39
+ export function renderPiWakeupPlist(options, piPath, runnerPath) {
40
+ const args = [process.execPath, runnerPath, resolve(options.projectRoot), piPath,
41
+ resolve(options.extensionPath), options.provider, options.modelId];
42
+ const argumentsXml = args.map((argument) => ` <string>${xml(argument)}</string>`).join("\n");
43
+ const path = [dirname(process.execPath), dirname(piPath), "/usr/bin", "/bin", "/usr/sbin", "/sbin"].join(":");
44
+ return `<?xml version="1.0" encoding="UTF-8"?>
45
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
46
+ ${managedMarker(options.projectRoot)}
47
+ <plist version="1.0">
48
+ <dict>
49
+ <key>Label</key><string>${xml(piWakeupLabel(options.projectRoot))}</string>
50
+ <key>ProgramArguments</key><array>
51
+ ${argumentsXml}
52
+ </array>
53
+ <key>WorkingDirectory</key><string>${xml(resolve(options.projectRoot))}</string>
54
+ <key>EnvironmentVariables</key><dict>
55
+ <key>PATH</key><string>${xml(path)}</string>
56
+ </dict>
57
+ <key>RunAtLoad</key><true/>
58
+ <key>StartInterval</key><integer>${POLL_SECONDS}</integer>
59
+ </dict>
60
+ </plist>
61
+ `;
62
+ }
63
+ async function loaded(label) {
64
+ try {
65
+ await execFileAsync("launchctl", ["print", `gui/${process.getuid?.()}/${label}`]);
66
+ return true;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
72
+ export async function provisionPiWakeup(options) {
73
+ if (process.platform !== "darwin" || process.getuid === undefined) {
74
+ throw new Error("Persistent Pi wakeups require a macOS user LaunchAgent.");
75
+ }
76
+ if (!options.provider || !options.modelId)
77
+ throw new Error("Select an authenticated Pi model before scheduling.");
78
+ const piPath = await executable("pi");
79
+ const auth = await execFileAsync(piPath, ["auth", "check", "--model", `${options.provider}/${options.modelId}`], {
80
+ env: {
81
+ HOME: homedir(),
82
+ PATH: [dirname(process.execPath), dirname(piPath), "/usr/bin", "/bin", "/usr/sbin", "/sbin"].join(":"),
83
+ },
84
+ });
85
+ if (auth.stdout.trim() !== "ready") {
86
+ throw new Error(`Pi authentication is not ready for ${options.provider}/${options.modelId} in the background environment.`);
87
+ }
88
+ const extensionPath = resolve(options.extensionPath);
89
+ const runnerPath = join(dirname(extensionPath), `wakeup${extname(extensionPath)}`);
90
+ await access(runnerPath, constants.R_OK);
91
+ const path = piWakeupPlistPath(options.projectRoot);
92
+ const content = renderPiWakeupPlist({ ...options, extensionPath }, piPath, runnerPath);
93
+ let prior;
94
+ try {
95
+ prior = await readFile(path, "utf8");
96
+ }
97
+ catch (error) {
98
+ if (error.code !== "ENOENT")
99
+ throw error;
100
+ }
101
+ if (prior && !prior.includes(managedMarker(options.projectRoot))) {
102
+ throw new Error(`Refusing to replace unmanaged LaunchAgent ${basename(path)}.`);
103
+ }
104
+ const label = piWakeupLabel(options.projectRoot);
105
+ if (prior === content && await loaded(label))
106
+ return path;
107
+ if (await loaded(label))
108
+ await execFileAsync("launchctl", ["bootout", `gui/${process.getuid()}/${label}`]);
109
+ await mkdir(dirname(path), { recursive: true });
110
+ const temporary = `${path}.${process.pid}.tmp`;
111
+ await writeFile(temporary, content, { mode: 0o600 });
112
+ await rename(temporary, path);
113
+ await execFileAsync("launchctl", ["bootstrap", `gui/${process.getuid()}`, path]);
114
+ return path;
115
+ }
116
+ export async function removePiWakeup(projectRoot) {
117
+ if (process.platform !== "darwin" || process.getuid === undefined)
118
+ return false;
119
+ const path = piWakeupPlistPath(projectRoot);
120
+ let content;
121
+ try {
122
+ content = await readFile(path, "utf8");
123
+ }
124
+ catch (error) {
125
+ if (error.code === "ENOENT")
126
+ return false;
127
+ throw error;
128
+ }
129
+ if (!content.includes(managedMarker(projectRoot))) {
130
+ throw new Error(`Refusing to remove unmanaged LaunchAgent ${basename(path)}.`);
131
+ }
132
+ const label = piWakeupLabel(projectRoot);
133
+ if (await loaded(label))
134
+ await execFileAsync("launchctl", ["bootout", `gui/${process.getuid()}/${label}`]);
135
+ await unlink(path);
136
+ return true;
137
+ }
@@ -0,0 +1,2 @@
1
+ import type { OpenMeritProjectState } from "../../protocol/src/index.ts";
2
+ export declare function isPiWakeupDue(state: OpenMeritProjectState, now?: string): boolean;
@@ -0,0 +1,108 @@
1
+ import { spawn } from "node:child_process";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { buildAutomationPlan, OpenMeritCoordinator, ProjectStore } from "../../core/src/index.js";
6
+ const wakeupDescriptor = {
7
+ id: "pi-launchagent", name: "Pi LaunchAgent", version: "0.87", protocolVersions: ["0.1"],
8
+ capabilities: [], executionModes: ["non_interactive"],
9
+ structuredOutput: { schemaDialect: "https://json-schema.org/draft/2020-12/schema", presentation: "adapter_validation", enforcement: "adapter" },
10
+ automation: {
11
+ supportedSignals: ["product_task_completed", "metric_window_available", "scheduled_tick", "model_catalog_changed", "verification_window_completed"],
12
+ persistentScheduling: true, backgroundExecution: true, wakeupProvisioning: "infrastructure_change",
13
+ },
14
+ };
15
+ export function isPiWakeupDue(state, now = new Date().toISOString()) {
16
+ const execution = state.cycle.policy?.checkPolicy.execution;
17
+ if (execution?.mode !== "persistent" || !execution.infrastructureChangesAllowed || state.activeIntent || state.automation.paused)
18
+ return false;
19
+ if (state.automation.pendingIntentKind)
20
+ return true;
21
+ const at = buildAutomationPlan(state, wakeupDescriptor).nextWakeupAt;
22
+ return at !== undefined && Date.parse(at) <= Date.parse(now);
23
+ }
24
+ async function run() {
25
+ const [projectRoot, piPath, extensionPath, provider, modelId] = process.argv.slice(2);
26
+ if (!projectRoot || !piPath || !extensionPath || !provider || !modelId)
27
+ process.exitCode = 2;
28
+ if (process.exitCode)
29
+ return;
30
+ const store = new ProjectStore(projectRoot);
31
+ const state = await store.readState();
32
+ if (!state || !isPiWakeupDue(state))
33
+ return;
34
+ const startedAt = new Date().toISOString();
35
+ const coordinator = new OpenMeritCoordinator(store, {
36
+ descriptor: wakeupDescriptor,
37
+ async dispatch() { return { accepted: false, reason: "The wakeup runner does not dispatch harness work directly." }; },
38
+ });
39
+ const check = state.automation.pendingIntentKind === "run_assessment"
40
+ ? await coordinator.assessBaseline(startedAt)
41
+ : state.automation.pendingIntentKind
42
+ ? undefined
43
+ : await coordinator.recordAutomationSignal({
44
+ protocolVersion: "0.1", id: `wakeup-${crypto.randomUUID()}`,
45
+ type: "scheduled_tick", occurredAt: startedAt, targetId: state.cycle.targetId,
46
+ });
47
+ if (check && check.decision.action !== "issue_intent") {
48
+ await writeFile(join(store.directory, "scheduler-status.json"), JSON.stringify({
49
+ schemaVersion: 1, startedAt, completedAt: new Date().toISOString(), exitCode: 0,
50
+ decision: check.decision.reason,
51
+ }, null, 2) + "\n", { mode: 0o600 });
52
+ return;
53
+ }
54
+ const configuredTimeout = Number(process.env.OPENMERIT_WAKEUP_TIMEOUT_MS);
55
+ const runLimitMs = Number.isFinite(configuredTimeout) && configuredTimeout >= 5_000
56
+ ? Math.min(configuredTimeout, 15 * 60_000)
57
+ : 15 * 60_000;
58
+ const deadline = Date.now() + runLimitMs;
59
+ const invokePi = async (prompt) => {
60
+ const child = spawn(piPath, [
61
+ "--no-extensions", "--extension", extensionPath, "--provider", provider, "--model", modelId,
62
+ ...(process.env.OPENMERIT_WAKEUP_DEBUG === "1" ? ["--mode", "json"] : []),
63
+ "--no-session", "--print", prompt,
64
+ ], { cwd: projectRoot, stdio: process.env.OPENMERIT_WAKEUP_DEBUG === "1" ? "inherit" : "ignore", env: process.env });
65
+ return new Promise((resolve, reject) => {
66
+ let timedOut = false;
67
+ const timeout = setTimeout(() => {
68
+ timedOut = true;
69
+ child.kill("SIGTERM");
70
+ setTimeout(() => child.kill("SIGKILL"), 5_000).unref();
71
+ }, Math.max(1, deadline - Date.now()));
72
+ child.once("error", (error) => { clearTimeout(timeout); reject(error); });
73
+ child.once("exit", (code) => { clearTimeout(timeout); resolve(timedOut ? 124 : code ?? 1); });
74
+ });
75
+ };
76
+ let exitCode = await invokePi("Run the due OpenMerit scheduled check for this configured project. " +
77
+ "On startup, OpenMerit may create an active intent in .openmerit/state.json. Read that intent and .openmerit/config.json, " +
78
+ "perform only its authorized work, and call openmerit_complete_intent exactly once with verified evidence. " +
79
+ "For baseline assessment, if no application metric windows exist, report insufficiency promptly; do not wait for new data or invent samples. " +
80
+ "If evidence is unavailable, report failure honestly. Never change Pi's model or an unauthorized application route.");
81
+ const issued = await store.readState();
82
+ if (exitCode === 0 && issued?.activeIntent && !state.activeIntent && Date.now() < deadline) {
83
+ const intent = issued.activeIntent;
84
+ exitCode = await invokePi(`Complete the already active OpenMerit ${intent.kind} intent ${intent.id}. ` +
85
+ "Read .openmerit/config.json and the project evidence. Follow the requested outcome and constraints below. " +
86
+ "Use the registered openmerit_complete_intent tool exactly once, with verified evidence; if evidence is unavailable, report failure honestly. " +
87
+ "Never change Pi's model or the application model unless this intent explicitly authorizes that target-bound change.\n\n" +
88
+ JSON.stringify(intent));
89
+ if ((await store.readState())?.activeIntent?.id === intent.id)
90
+ exitCode = 1;
91
+ }
92
+ const finalState = await store.readState();
93
+ if (finalState?.activeIntent && exitCode === 0)
94
+ exitCode = 1;
95
+ if (finalState?.automation.pendingIntentKind && exitCode === 0)
96
+ exitCode = 1;
97
+ await writeFile(join(store.directory, "scheduler-status.json"), JSON.stringify({
98
+ schemaVersion: 1, startedAt, completedAt: new Date().toISOString(), exitCode,
99
+ ...(finalState?.activeIntent ? { activeIntentId: finalState.activeIntent.id } : {}),
100
+ }, null, 2) + "\n", { mode: 0o600 });
101
+ process.exitCode = exitCode;
102
+ }
103
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
104
+ run().catch((error) => {
105
+ process.stderr.write(`OpenMerit Pi wakeup failed: ${error instanceof Error ? error.message : String(error)}\n`);
106
+ process.exitCode = 1;
107
+ });
108
+ }
@@ -1,5 +1,5 @@
1
1
  export declare const PROTOCOL_VERSION: "0.1";
2
- export { CandidateAssessmentSchema, CandidateDescriptorSchema, completionToolSchemaFor, EvidenceReferenceSchema, FrontierRecommendationSchema, FrontierSnapshotSchema, INTENT_OUTPUT_SCHEMAS, intentOutputSchemaFor, MetricObjectiveSchema, MetricRecordSchema, ObservabilityCoverageSchema, OPENMERIT_RESULT_SCHEMA_VERSION, OPENMERIT_SCHEMA_DIALECT, SupervisedGraduationPolicySchema, TaskProfileSchema, validateIntentOutputSchema, } from "./schemas.ts";
2
+ export { ApplicationLlmTargetSchema, CandidateAssessmentSchema, CandidateDescriptorSchema, completionToolSchemaFor, EvidenceReferenceSchema, FrontierRecommendationSchema, FrontierSnapshotSchema, INTENT_OUTPUT_SCHEMAS, intentOutputSchemaFor, MetricObjectiveSchema, MetricRecordSchema, ObservabilityCoverageSchema, OPENMERIT_RESULT_SCHEMA_VERSION, OPENMERIT_SCHEMA_DIALECT, SupervisedGraduationPolicySchema, TaskProfileSchema, validateIntentOutputSchema, } from "./schemas.ts";
3
3
  export type JsonPrimitive = string | number | boolean | null;
4
4
  export type JsonValue = JsonPrimitive | {
5
5
  readonly [key: string]: JsonValue;
@@ -88,6 +88,7 @@ export interface OpenMeritIntent {
88
88
  readonly protocolVersion: typeof PROTOCOL_VERSION;
89
89
  readonly id: string;
90
90
  readonly kind: IntentKind;
91
+ readonly targetId?: string;
91
92
  readonly taskProfileId: string;
92
93
  readonly authorizationPolicyId: string;
93
94
  readonly requestedOutcome: string;
@@ -161,11 +162,13 @@ export interface MetricDefinition {
161
162
  }
162
163
  export declare const METRIC_CATALOG: readonly MetricDefinition[];
163
164
  export interface MetricRecord {
165
+ readonly targetId: string;
164
166
  readonly metricId: MetricId;
165
167
  readonly state: MetricState;
166
168
  readonly scope: MetricScope;
167
169
  readonly direction: MetricDirection;
168
170
  readonly method: MeasurementMethod;
171
+ readonly aggregation: "single_value" | "sum" | "mean" | "rate" | "distribution" | "percentile";
169
172
  readonly value?: number;
170
173
  readonly unit: string;
171
174
  readonly sampleCount: number;
@@ -186,22 +189,41 @@ export interface MetricObjective {
186
189
  readonly required: boolean;
187
190
  readonly minimumSamples: number;
188
191
  readonly tolerance: number;
192
+ readonly samplingPlan: {
193
+ readonly strategy: "single_run" | "repeated_same_input" | "representative_task_set" | "repeated_representative_task_set" | "load_test";
194
+ readonly representativeCaseCount: number;
195
+ readonly repetitionsPerCase: number;
196
+ readonly aggregation: "single_value" | "mean" | "rate" | "distribution" | "percentile";
197
+ readonly rationale: string;
198
+ };
189
199
  readonly constraint?: {
190
200
  readonly operator: "at_least" | "at_most";
191
201
  readonly value: number;
192
202
  };
193
203
  }
204
+ /** A user-confirmed application LLM route. The coding harness itself is never an evaluation target. */
205
+ export interface ApplicationLlmTarget {
206
+ readonly id: string;
207
+ readonly kind: "application_llm_call";
208
+ readonly name: string;
209
+ readonly applicationId: string;
210
+ readonly callSites: readonly string[];
211
+ readonly routeKey: string;
212
+ readonly confirmedAt: string;
213
+ }
194
214
  export interface TaskProfile {
195
215
  readonly id: string;
196
216
  readonly revision: number;
197
217
  readonly name: string;
198
218
  readonly goal: string;
219
+ readonly applicationTarget: ApplicationLlmTarget;
199
220
  readonly objectives: readonly MetricObjective[];
200
221
  readonly createdAt: string;
201
222
  readonly confirmedAt?: string;
202
223
  }
203
224
  export type ObservabilityReadinessStatus = "ready" | "incomplete";
204
225
  export interface ObservabilityCoverage {
226
+ readonly targetId: string;
205
227
  readonly status: ObservabilityReadinessStatus;
206
228
  readonly requiredMetricIds: readonly MetricId[];
207
229
  readonly coveredMetricIds: readonly MetricId[];
@@ -210,6 +232,7 @@ export interface ObservabilityCoverage {
210
232
  }
211
233
  export interface CandidateAssessment {
212
234
  readonly id: string;
235
+ readonly targetId: string;
213
236
  readonly taskProfileId: string;
214
237
  readonly taskProfileRevision: number;
215
238
  readonly candidateId: string;
@@ -230,6 +253,7 @@ export interface DominanceComparison {
230
253
  }
231
254
  export interface FrontierSnapshot {
232
255
  readonly id: string;
256
+ readonly targetId: string;
233
257
  readonly taskProfileId: string;
234
258
  readonly taskProfileRevision: number;
235
259
  readonly productRevision: string;
@@ -243,6 +267,7 @@ export interface FrontierSnapshot {
243
267
  readonly calculationEvidence: readonly EvidenceReference[];
244
268
  }
245
269
  export interface FrontierRecommendation {
270
+ readonly targetId: string;
246
271
  readonly frontierSnapshotId: string;
247
272
  readonly selectedCandidateId: string;
248
273
  readonly currentCandidateId: string;
@@ -250,50 +275,82 @@ export interface FrontierRecommendation {
250
275
  readonly preferenceEvidence: readonly EvidenceReference[];
251
276
  }
252
277
  export interface CandidateDescriptor {
278
+ readonly targetId: string;
253
279
  readonly candidateId: string;
254
280
  readonly modelId: string;
255
281
  readonly modelVersion?: string;
256
282
  readonly reasons: readonly string[];
257
283
  }
284
+ /** Research priors only. These are never MetricRecords or frontier evidence. */
285
+ export interface PreliminaryModelLead {
286
+ readonly targetId: string;
287
+ readonly modelId: string;
288
+ readonly modelVersion?: string;
289
+ readonly estimatedPrice: {
290
+ readonly currency: string;
291
+ readonly inputPerMillionTokens: number;
292
+ readonly outputPerMillionTokens: number;
293
+ readonly sourceUri: string;
294
+ readonly checkedAt: string;
295
+ };
296
+ readonly reputation: {
297
+ readonly summary: string;
298
+ readonly sourceUri: string;
299
+ readonly checkedAt: string;
300
+ };
301
+ }
258
302
  export interface IntentOutputMap {
259
303
  readonly establish_evals: {
260
304
  readonly taskProfile: TaskProfile;
305
+ /** The application model configured before any challenger trial. */
306
+ readonly incumbent: CandidateDescriptor;
261
307
  readonly policy: SupervisedGraduationPolicy;
262
308
  readonly observability: ObservabilityCoverage;
309
+ readonly preliminaryModelLeads?: readonly PreliminaryModelLead[];
263
310
  };
264
311
  readonly instrument_observability: {
312
+ readonly targetId: string;
265
313
  readonly coveredMetricIds: readonly MetricId[];
266
314
  readonly missingMetricIds: readonly MetricId[];
267
315
  };
268
316
  readonly run_assessment: {
317
+ readonly targetId: string;
269
318
  readonly baselineEvidenceSufficient: boolean;
270
- readonly metrics?: readonly MetricRecord[];
319
+ readonly metrics: readonly MetricRecord[];
271
320
  };
272
321
  readonly discover_candidates: {
322
+ readonly targetId: string;
273
323
  readonly candidates: readonly CandidateDescriptor[];
274
324
  };
275
325
  readonly run_challenger_trials: {
326
+ readonly targetId: string;
276
327
  readonly assessments: readonly CandidateAssessment[];
328
+ readonly experimentManifest: EvidenceReference;
277
329
  };
278
330
  readonly calculate_frontier: {
331
+ readonly targetId: string;
279
332
  readonly assessments: readonly CandidateAssessment[];
280
333
  readonly frontierSnapshot: FrontierSnapshot;
281
334
  readonly recommendation?: FrontierRecommendation;
282
335
  };
283
336
  readonly investigate_regression: {
337
+ readonly targetId: string;
284
338
  readonly regressionDetected: boolean;
285
339
  readonly affectedMetricIds: readonly MetricId[];
286
340
  readonly likelyCause?: string;
287
341
  };
288
342
  readonly apply_model_swap: {
343
+ readonly targetId: string;
289
344
  readonly appliedCandidateId: string;
290
345
  readonly previousCandidateId?: string;
291
346
  };
292
347
  readonly verify_model_swap: {
348
+ readonly targetId: string;
293
349
  readonly verified: boolean;
294
350
  readonly regressionDetected: boolean;
295
351
  };
296
352
  readonly rollback_model_swap: {
353
+ readonly targetId: string;
297
354
  readonly restoredCandidateId: string;
298
355
  };
299
356
  }
@@ -304,6 +361,7 @@ export declare const REQUIRED_CAPABILITIES: Readonly<Record<IntentKind, readonly
304
361
  export type ImprovementStage = "unconfigured" | "establishing_evidence" | "collecting_baseline" | "baseline_ready" | "discovering_candidates" | "candidates_ready" | "running_challenger_trials" | "challengers_ready" | "calculating_frontier" | "frontier_ready" | "awaiting_swap_approval" | "swap_approved" | "applying_swap" | "swap_applied" | "verifying_swap" | "swap_verified" | "verification_failed" | "rolling_back" | "monitoring";
305
362
  export interface ImprovementCycleState {
306
363
  readonly id: string;
364
+ readonly targetId?: string;
307
365
  readonly taskProfileId?: string;
308
366
  readonly stage: ImprovementStage;
309
367
  readonly activeCandidateId?: string;
@@ -319,14 +377,20 @@ export interface OpenMeritProjectConfig {
319
377
  readonly activeTaskProfileId?: string;
320
378
  readonly taskProfiles: readonly TaskProfile[];
321
379
  readonly policy?: SupervisedGraduationPolicy;
380
+ readonly preliminaryModelLeads?: readonly PreliminaryModelLead[];
322
381
  }
323
382
  export interface OpenMeritProjectState {
324
383
  readonly schemaVersion: 1;
325
384
  readonly cycle: ImprovementCycleState;
326
385
  readonly activeIntent?: OpenMeritIntent;
327
386
  readonly lastResult?: IntentResult;
387
+ /** Durable handoffs between discovery, trials, and frontier calculation. */
388
+ readonly candidates?: readonly CandidateDescriptor[];
389
+ readonly assessments?: readonly CandidateAssessment[];
390
+ readonly experimentManifest?: EvidenceReference;
328
391
  readonly observability?: ObservabilityCoverage;
329
392
  readonly automation: {
393
+ readonly paused?: boolean;
330
394
  readonly setupNudgeShown: boolean;
331
395
  readonly observedCompletedTasks: number;
332
396
  readonly lastAssessmentTaskCount: number;
@@ -336,6 +400,7 @@ export interface OpenMeritProjectState {
336
400
  readonly pendingIntentKind?: IntentKind;
337
401
  readonly pendingIntentReason?: string;
338
402
  readonly lastAssessmentAt?: string;
403
+ readonly baselineStartedAt?: string;
339
404
  readonly lastReassessmentTaskCount?: number;
340
405
  readonly lastReassessmentAt?: string;
341
406
  readonly lastRegressionCheckTaskCount?: number;
@@ -344,20 +409,37 @@ export interface OpenMeritProjectState {
344
409
  readonly swapAppliedAt?: string;
345
410
  readonly lastVerificationAt?: string;
346
411
  readonly latestMetricValues?: Readonly<Partial<Record<MetricId, number>>>;
412
+ /** Latest reported aggregate application-run window per metric; overlapping windows are never summed. */
413
+ readonly baselineMetricWindows?: readonly MetricRecord[];
414
+ /** Spend from controlled challenger trials; ordinary product work is excluded. */
415
+ readonly evaluationSpend?: number;
347
416
  };
348
417
  readonly updatedAt: string;
349
418
  }
350
- export type AuditEventType = "project_initialized" | "intent_requested" | "intent_progressed" | "intent_completed" | "requirements_confirmation_required" | "swap_confirmation_required" | "frontier_verified" | "frontier_rejected" | "cycle_advanced" | "setup_nudged" | "task_observed" | "model_catalog_changed" | "automation_signal_recorded" | "automation_signal_duplicate" | "automation_check_due" | "observability_readiness_checked";
419
+ export type AuditEventType = "project_initialized" | "intent_requested" | "intent_progressed" | "intent_completed" | "requirements_confirmation_required" | "swap_confirmation_required" | "frontier_verified" | "frontier_rejected" | "cycle_advanced" | "setup_nudged" | "task_observed" | "model_catalog_changed" | "automation_signal_recorded" | "automation_signal_duplicate" | "automation_check_due" | "baseline_sufficiency_checked" | "observability_readiness_checked";
351
420
  interface AutomationSignalBase {
352
421
  readonly protocolVersion: typeof PROTOCOL_VERSION;
353
422
  readonly id: string;
354
423
  readonly occurredAt: string;
424
+ readonly targetId: string;
355
425
  readonly evidence?: readonly EvidenceReference[];
356
426
  }
357
427
  export type AutomationSignal = (AutomationSignalBase & {
358
428
  readonly type: "product_task_completed";
429
+ /** The model used by the application target, never the coding harness model. */
359
430
  readonly modelId: string;
360
- readonly contextTokens?: number | null;
431
+ /** Provider telemetry captured from the application LLM call. */
432
+ readonly telemetry?: {
433
+ readonly startedAt: string;
434
+ readonly completedAt: string;
435
+ readonly durationMs: number;
436
+ readonly inputTokens: number;
437
+ readonly outputTokens: number;
438
+ readonly cacheReadTokens: number;
439
+ readonly cacheWriteTokens: number;
440
+ readonly cost: number;
441
+ readonly currency: "USD";
442
+ };
361
443
  }) | (AutomationSignalBase & {
362
444
  readonly type: "metric_window_available";
363
445
  readonly metrics: readonly MetricRecord[];
@@ -1,5 +1,5 @@
1
1
  export const PROTOCOL_VERSION = "0.1";
2
- export { CandidateAssessmentSchema, CandidateDescriptorSchema, completionToolSchemaFor, EvidenceReferenceSchema, FrontierRecommendationSchema, FrontierSnapshotSchema, INTENT_OUTPUT_SCHEMAS, intentOutputSchemaFor, MetricObjectiveSchema, MetricRecordSchema, ObservabilityCoverageSchema, OPENMERIT_RESULT_SCHEMA_VERSION, OPENMERIT_SCHEMA_DIALECT, SupervisedGraduationPolicySchema, TaskProfileSchema, validateIntentOutputSchema, } from "./schemas.js";
2
+ export { ApplicationLlmTargetSchema, CandidateAssessmentSchema, CandidateDescriptorSchema, completionToolSchemaFor, EvidenceReferenceSchema, FrontierRecommendationSchema, FrontierSnapshotSchema, INTENT_OUTPUT_SCHEMAS, intentOutputSchemaFor, MetricObjectiveSchema, MetricRecordSchema, ObservabilityCoverageSchema, OPENMERIT_RESULT_SCHEMA_VERSION, OPENMERIT_SCHEMA_DIALECT, SupervisedGraduationPolicySchema, TaskProfileSchema, validateIntentOutputSchema, } from "./schemas.js";
3
3
  export const METRIC_CATALOG = [
4
4
  { id: "task_quality", label: "Task quality", direction: "maximize", defaultUnit: "score", requiresRepeatedRuns: false },
5
5
  { id: "task_success", label: "Task success rate", direction: "maximize", defaultUnit: "ratio", requiresRepeatedRuns: true },