openmerit 0.1.4 → 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 (147) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +121 -386
  3. package/dist/core/src/index.d.ts +101 -0
  4. package/dist/core/src/index.js +1649 -0
  5. package/dist/core/src/store.d.ts +35 -0
  6. package/dist/core/src/store.js +102 -0
  7. package/dist/pi/src/index.d.ts +32 -0
  8. package/dist/pi/src/index.js +794 -0
  9. package/dist/pi/src/scheduler.d.ts +11 -0
  10. package/dist/pi/src/scheduler.js +137 -0
  11. package/dist/pi/src/wakeup.d.ts +2 -0
  12. package/dist/pi/src/wakeup.js +108 -0
  13. package/dist/protocol/src/index.d.ts +484 -0
  14. package/dist/protocol/src/index.js +47 -0
  15. package/dist/protocol/src/schemas.d.ts +576 -0
  16. package/dist/protocol/src/schemas.js +280 -0
  17. package/dist/terminal/public/app.js +297 -0
  18. package/dist/terminal/public/brands/anthropic.png +0 -0
  19. package/dist/terminal/public/brands/baai.png +0 -0
  20. package/dist/terminal/public/brands/baseten.png +0 -0
  21. package/dist/terminal/public/brands/cerebras.png +0 -0
  22. package/dist/terminal/public/brands/cohere.png +0 -0
  23. package/dist/terminal/public/brands/deepseek.ico +0 -0
  24. package/dist/terminal/public/brands/google.png +0 -0
  25. package/dist/terminal/public/brands/groq.ico +0 -0
  26. package/dist/terminal/public/brands/lm-studio.png +0 -0
  27. package/dist/terminal/public/brands/meta.ico +0 -0
  28. package/dist/terminal/public/brands/mistral.png +0 -0
  29. package/dist/terminal/public/brands/nomic.png +0 -0
  30. package/dist/terminal/public/brands/ollama.png +0 -0
  31. package/dist/terminal/public/brands/openai.png +0 -0
  32. package/dist/terminal/public/brands/openrouter.png +0 -0
  33. package/dist/terminal/public/brands/qwen.png +0 -0
  34. package/dist/terminal/public/brands/vllm.ico +0 -0
  35. package/dist/terminal/public/brands/vllm.png +0 -0
  36. package/dist/terminal/public/favicon.svg +1 -0
  37. package/dist/terminal/public/flow.css +1 -0
  38. package/dist/terminal/public/flow.js +770 -0
  39. package/dist/terminal/public/index.html +21 -0
  40. package/dist/terminal/public/styles.css +779 -0
  41. package/dist/terminal/src/activity-merge.mjs +64 -0
  42. package/dist/terminal/src/browser.mjs +29 -0
  43. package/dist/terminal/src/cli.mjs +60 -0
  44. package/dist/terminal/src/collect.mjs +311 -0
  45. package/dist/terminal/src/discovery.mjs +93 -0
  46. package/dist/terminal/src/hardware.mjs +57 -0
  47. package/dist/terminal/src/project-activity.mjs +156 -0
  48. package/dist/terminal/src/sample.mjs +171 -0
  49. package/dist/terminal/src/server.mjs +56 -0
  50. package/dist/terminal/src/services.mjs +62 -0
  51. package/dist/terminal/src/topology.mjs +30 -0
  52. package/docs/adapter-guide.md +189 -0
  53. package/docs/architecture.md +59 -0
  54. package/docs/automation.md +74 -0
  55. package/docs/budgets.md +37 -0
  56. package/docs/commands.md +85 -0
  57. package/docs/demo-backfill.md +29 -0
  58. package/docs/demo-fieldkit.md +47 -0
  59. package/docs/demo-placement.md +30 -0
  60. package/docs/demo-spam.md +15 -0
  61. package/docs/demo-support.md +42 -0
  62. package/docs/demo.md +57 -0
  63. package/docs/first-trial.md +60 -0
  64. package/docs/getting-started.md +65 -0
  65. package/docs/index.md +40 -0
  66. package/docs/inference-terminal.md +439 -0
  67. package/docs/lifecycle.md +30 -0
  68. package/docs/memo.md +126 -0
  69. package/docs/metrics-and-evidence.md +48 -0
  70. package/docs/operations.md +40 -0
  71. package/docs/pareto-spec.md +76 -0
  72. package/docs/pi-extension.md +54 -0
  73. package/docs/roadmap.md +28 -0
  74. package/docs/security.md +37 -0
  75. package/docs/site-artwork-linocut.md +23 -0
  76. package/docs/site-artwork-miniature-diverse.md +28 -0
  77. package/docs/site-artwork-miniature.md +26 -0
  78. package/docs/site-demo.md +177 -0
  79. package/docs/site-design.md +94 -0
  80. package/docs/site-documentation.md +83 -0
  81. package/docs/site-dynamic-og.md +35 -0
  82. package/docs/site-faq-maintenance.md +115 -0
  83. package/docs/site-hero-resolution.md +60 -0
  84. package/docs/site-illustration-sequences.md +227 -0
  85. package/docs/site-inference-terminal.md +203 -0
  86. package/docs/site-memo.md +39 -0
  87. package/docs/site-og-image.md +38 -0
  88. package/docs/site-og-workshop.md +21 -0
  89. package/docs/site-section-artwork.md +56 -0
  90. package/docs/site-skill-review.md +57 -0
  91. package/docs/site-terminal-preview.md +85 -0
  92. package/docs/testing.md +118 -0
  93. package/docs/troubleshooting.md +55 -0
  94. package/docs/ux-reference.md +32 -0
  95. package/package.json +74 -42
  96. package/benchmark/invoice_ocr/data/invoice_01_ground_truth.json +0 -38
  97. package/benchmark/invoice_ocr/data/invoice_01_row_2.jpg +0 -0
  98. package/benchmark/invoice_ocr/data/invoice_02_ground_truth.json +0 -32
  99. package/benchmark/invoice_ocr/data/invoice_02_row_5.jpg +0 -0
  100. package/benchmark/invoice_ocr/data/invoice_03_ground_truth.json +0 -26
  101. package/benchmark/invoice_ocr/data/invoice_03_row_6.jpg +0 -0
  102. package/benchmark/invoice_ocr/data/invoice_04_ground_truth.json +0 -26
  103. package/benchmark/invoice_ocr/data/invoice_04_row_7.jpg +0 -0
  104. package/benchmark/invoice_ocr/data/invoice_05_ground_truth.json +0 -38
  105. package/benchmark/invoice_ocr/data/invoice_05_row_947.jpg +0 -0
  106. package/benchmark/invoice_ocr/data/invoice_06_ground_truth.json +0 -38
  107. package/benchmark/invoice_ocr/data/invoice_06_row_948.jpg +0 -0
  108. package/benchmark/invoice_ocr/data/invoice_07_ground_truth.json +0 -20
  109. package/benchmark/invoice_ocr/data/invoice_07_row_949.jpg +0 -0
  110. package/benchmark/invoice_ocr/data/invoice_08_ground_truth.json +0 -38
  111. package/benchmark/invoice_ocr/data/invoice_08_row_1888.jpg +0 -0
  112. package/benchmark/invoice_ocr/data/invoice_09_ground_truth.json +0 -26
  113. package/benchmark/invoice_ocr/data/invoice_09_row_1890.jpg +0 -0
  114. package/benchmark/invoice_ocr/data/invoice_10_ground_truth.json +0 -20
  115. package/benchmark/invoice_ocr/data/invoice_10_row_1892.jpg +0 -0
  116. package/benchmark/invoice_ocr/data/manifest.json +0 -97
  117. package/dist/benchmarks.js +0 -98
  118. package/dist/catalog.js +0 -61
  119. package/dist/cli.js +0 -188
  120. package/dist/daemon.js +0 -407
  121. package/dist/diagnostics.js +0 -227
  122. package/dist/frontier.js +0 -56
  123. package/dist/harness.js +0 -1
  124. package/dist/integrations.js +0 -19
  125. package/dist/invoice-eval.js +0 -33
  126. package/dist/invoice-score.js +0 -124
  127. package/dist/judge.js +0 -43
  128. package/dist/llm.js +0 -207
  129. package/dist/pi-config.js +0 -46
  130. package/dist/pi-trials.js +0 -373
  131. package/dist/policy.js +0 -185
  132. package/dist/providers.js +0 -1
  133. package/dist/recommend.js +0 -76
  134. package/dist/routes.js +0 -74
  135. package/dist/standalone.js +0 -224
  136. package/dist/store.js +0 -89
  137. package/dist/strategist.js +0 -68
  138. package/dist/task-input.js +0 -54
  139. package/dist/traces.js +0 -127
  140. package/dist/trials.js +0 -140
  141. package/dist/types.js +0 -2
  142. package/examples/invoice-prompt.txt +0 -19
  143. package/examples/task.example.json +0 -7
  144. package/extension/openmerit.ts +0 -947
  145. package/instructions/OPENMERIT.md +0 -63
  146. package/instructions/openmerit.policy.json +0 -37
  147. package/rules.md +0 -43
@@ -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
+ }