@polderlabs/bizar-omp 0.8.1 → 0.9.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 (47) hide show
  1. package/README.md +284 -338
  2. package/dist/cli/doctor.d.ts +4 -4
  3. package/dist/core/types.d.ts +29 -0
  4. package/dist/core/types.d.ts.map +1 -1
  5. package/dist/extension.d.ts.map +1 -1
  6. package/dist/extension.js +47 -11
  7. package/dist/extension.js.map +1 -1
  8. package/dist/omp/compatibility.d.ts +15 -5
  9. package/dist/omp/compatibility.d.ts.map +1 -1
  10. package/dist/omp/compatibility.js +12 -2
  11. package/dist/omp/compatibility.js.map +1 -1
  12. package/dist/omp/dispatch-lifecycle.d.ts +14 -0
  13. package/dist/omp/dispatch-lifecycle.d.ts.map +1 -1
  14. package/dist/omp/dispatch-lifecycle.js +10 -1
  15. package/dist/omp/dispatch-lifecycle.js.map +1 -1
  16. package/dist/omp/omb-sessions.d.ts +8 -1
  17. package/dist/omp/omb-sessions.d.ts.map +1 -1
  18. package/dist/omp/omb-sessions.js +20 -17
  19. package/dist/omp/omb-sessions.js.map +1 -1
  20. package/dist/omp/rpc.d.ts +3 -0
  21. package/dist/omp/rpc.d.ts.map +1 -1
  22. package/dist/omp/session-ownership.d.ts +64 -0
  23. package/dist/omp/session-ownership.d.ts.map +1 -0
  24. package/dist/omp/session-ownership.js +269 -0
  25. package/dist/omp/session-ownership.js.map +1 -0
  26. package/dist/omp/task-model-selector.d.ts +58 -0
  27. package/dist/omp/task-model-selector.d.ts.map +1 -0
  28. package/dist/omp/task-model-selector.js +144 -0
  29. package/dist/omp/task-model-selector.js.map +1 -0
  30. package/dist/scripts/emit-compatibility-receipt.js +13 -3
  31. package/dist/scripts/emit-compatibility-receipt.js.map +1 -1
  32. package/docs/assets/bizar-omp-banner.svg +51 -18
  33. package/docs/compatibility/baseline.json +9 -8
  34. package/docs/compatibility/supported-surfaces.json +11 -3
  35. package/docs/releases/0.9.0.md +35 -0
  36. package/docs/releases/support-matrix.md +1 -1
  37. package/package.json +1 -1
  38. package/skills/omp-native-development/SKILL.md +5 -3
  39. package/skills/omp-native-development/assets/tests/acceptance-matrix.json +417 -0
  40. package/skills/omp-native-development/references/accuracy-and-versioning.md +1 -1
  41. package/skills/omp-native-development/references/bizar-integration-contract.md +1 -1
  42. package/skills/omp-native-development/references/developer-handoff.md +3 -1
  43. package/skills/omp-native-development/references/execution-and-lifecycle.md +4 -0
  44. package/skills/omp-native-development/references/native-validation-matrix.md +25 -0
  45. package/skills/omp-native-development/references/sessions-sdk-rpc.md +17 -0
  46. package/skills/omp-native-development/references/settings-providers-security.md +11 -0
  47. package/skills/omp-native-development/references/source-manifest.json +20 -4
@@ -0,0 +1,269 @@
1
+ import { createHash } from "node:crypto";
2
+ import { closeSync, existsSync, mkdirSync, openSync, readSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { OwnershipError } from "../core/errors.js";
7
+ import { acquireLease } from "../storage/lease.js";
8
+ /**
9
+ * Session ownership, keyed the way OMP 18.5 keys it.
10
+ *
11
+ * OMP moved ownership from the journal's path to the session id in its header:
12
+ * `tryAcquireSessionLease(sessionId)` holds an OS-backed lease — an abstract
13
+ * Unix socket on Linux, a named mutex on Windows — under OMP's own
14
+ * session-owners directory. Because the key is the id rather than the path,
15
+ * every process that reaches one journal, through a hard link, a symlink, or
16
+ * after a move, meets the same lease; and a journal that has been replaced by a
17
+ * different session is detected instead of being written into.
18
+ *
19
+ * Bizar previously keyed a private lease on the journal's inode. That protects
20
+ * processes which cooperate with Bizar's own lock, but it is a different
21
+ * ownership domain from OMP's: a plain `omp` process never took it, so
22
+ * claiming "already open in another OMP or OMB process" asserted more than
23
+ * Bizar could prove.
24
+ *
25
+ * So this module keeps the two apart rather than conflating them:
26
+ *
27
+ * 1. It reads and validates the journal's native session id before any
28
+ * ownership operation, because an id is what both domains key on.
29
+ * 2. It takes OMP's **native** lease through OMP's own exported function when
30
+ * that runtime is reachable, so a plain OMP process holding the journal is
31
+ * observed instead of guessed at. Bizar does not reimplement OMP's lock
32
+ * format — an abstract socket cannot be reimplemented from the outside.
33
+ *
34
+ * The Bizar-private lease remains, but only for Bizar-owned state OMP itself
35
+ * does not own, and it is keyed by the same session id so both agree on which
36
+ * journal is being claimed.
37
+ *
38
+ * Acquisition is synchronous because every caller is synchronous — session
39
+ * start, session switch, delete, and migration all sit on synchronous
40
+ * extension handlers. The native module is therefore loaded with
41
+ * `createRequire` rather than a dynamic `import()`, which would only resolve
42
+ * later and leave the native path permanently unexercised.
43
+ */
44
+ /** Bytes read to find a header; the fixed-width title slot plus one line fits well inside. */
45
+ const HEADER_PREFIX_BYTES = 64 * 1024;
46
+ const NATIVE_SESSION_STORAGE = ["src", "session", "session-storage.ts"];
47
+ /**
48
+ * The session id in the header of the journal at `journalPath`.
49
+ *
50
+ * OMP writes a fixed-width title slot ahead of the JSONL entries, so the first
51
+ * line is not necessarily the header. This scans the bounded prefix for the
52
+ * first entry that is actually a session header, the same tolerance the
53
+ * repository's existing journal reader applies, rather than assuming a fixed
54
+ * line position. Anything that never yields a session header reports
55
+ * `undefined` instead of a guessed id.
56
+ */
57
+ export function readJournalSessionId(journalPath) {
58
+ let prefix;
59
+ try {
60
+ const fd = openSync(journalPath, "r");
61
+ try {
62
+ const buffer = Buffer.alloc(HEADER_PREFIX_BYTES);
63
+ prefix = buffer.toString("utf8", 0, readSync(fd, buffer, 0, buffer.length, 0));
64
+ }
65
+ finally {
66
+ closeSync(fd);
67
+ }
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ for (const line of prefix.split("\n")) {
73
+ const id = sessionIdFromLine(line);
74
+ if (id !== undefined)
75
+ return id;
76
+ }
77
+ return undefined;
78
+ }
79
+ function sessionIdFromLine(line) {
80
+ try {
81
+ const entry = JSON.parse(line);
82
+ if (entry === null || typeof entry !== "object")
83
+ return undefined;
84
+ const record = entry;
85
+ if (record.type !== "session" || typeof record.id !== "string" || !record.id.trim())
86
+ return undefined;
87
+ return record.id.trim();
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }
93
+ /**
94
+ * Where OMP's own package lives, or `undefined` when it is not reachable.
95
+ *
96
+ * An OMP installed as a self-contained binary exposes no importable module at
97
+ * all, which is a real supported shape rather than a failure.
98
+ */
99
+ function resolveOmpPackageRoot(env) {
100
+ const configured = env.BIZAR_OMP_PACKAGE_ROOT?.trim();
101
+ if (configured)
102
+ return resolve(configured);
103
+ try {
104
+ return dirname(createRequire(import.meta.url).resolve("@oh-my-pi/pi-coding-agent/package.json"));
105
+ }
106
+ catch {
107
+ // Not resolvable from Bizar; fall through to the executable's own tree.
108
+ }
109
+ for (const executable of [env.OMP_PATH?.trim(), "/usr/local/bin/omp", "/usr/bin/omp"].filter((value) => Boolean(value))) {
110
+ let dir = dirname(resolve(executable));
111
+ for (let depth = 0; depth < 6; depth += 1) {
112
+ const root = join(dir, "node_modules", "@oh-my-pi", "pi-coding-agent");
113
+ if (existsSync(join(root, "package.json")))
114
+ return root;
115
+ const parent = dirname(dir);
116
+ if (parent === dir)
117
+ break;
118
+ dir = parent;
119
+ }
120
+ }
121
+ return undefined;
122
+ }
123
+ /**
124
+ * Narrow a loaded module to the lease function, or `undefined` when the export
125
+ * is absent. A shape check is required because the module comes from outside
126
+ * this package and a missing export must degrade, not throw.
127
+ */
128
+ function asNativeSessionStorage(loaded) {
129
+ if (loaded === null || typeof loaded !== "object")
130
+ return undefined;
131
+ const candidate = loaded;
132
+ const acquire = candidate.tryAcquireSessionLease;
133
+ if (typeof acquire !== "function")
134
+ return undefined;
135
+ return { tryAcquireSessionLease: (sessionId) => acquire.call(loaded, sessionId) };
136
+ }
137
+ const nativeModuleCache = {};
138
+ /**
139
+ * Native leases this process already holds, so a second claim on the same
140
+ * session is a re-entry rather than self-collision.
141
+ *
142
+ * OMP keeps an equivalent process-local map, because one logical migration can
143
+ * legitimately need to claim the same session more than once — for example the
144
+ * source and its staged copy. Without this, Bizar would report its own
145
+ * re-entry as "another OMP process owns it".
146
+ */
147
+ const heldNativeLeases = new Map();
148
+ /**
149
+ * Take OMP's own ownership lease for `sessionId` through OMP's exported
150
+ * function, resolved from the installed OMP runtime.
151
+ *
152
+ * A runtime that is not reachable is reported as `unavailable`, never as
153
+ * "nobody owns it". That distinction matters: Bizar must not conclude a
154
+ * journal is free because it failed to find the module.
155
+ */
156
+ export function probeNativeSessionLease(sessionId, env = process.env) {
157
+ const root = resolveOmpPackageRoot(env);
158
+ const reentry = heldNativeLeases.get(sessionId);
159
+ if (reentry) {
160
+ reentry.depth += 1;
161
+ return { status: "acquired", release: () => releaseHeldNativeLease(sessionId, reentry) };
162
+ }
163
+ if (!root)
164
+ return { status: "unavailable", reason: "the installed OMP runtime exposes no importable session-storage module" };
165
+ if (nativeModuleCache.root !== root) {
166
+ nativeModuleCache.root = root;
167
+ try {
168
+ // A plain path, not a `file://` URL: Node's CommonJS resolver rejects a
169
+ // URL string here, and it loads a `.ts` source with its JavaScript loader.
170
+ const loaded = createRequire(import.meta.url)(join(root, ...NATIVE_SESSION_STORAGE));
171
+ nativeModuleCache.storage = asNativeSessionStorage(loaded);
172
+ }
173
+ catch {
174
+ nativeModuleCache.storage = undefined;
175
+ }
176
+ }
177
+ const storage = nativeModuleCache.storage;
178
+ if (!storage)
179
+ return { status: "unavailable", reason: "the installed OMP runtime exports no session ownership lease" };
180
+ let handle;
181
+ try {
182
+ handle = storage.tryAcquireSessionLease(sessionId);
183
+ }
184
+ catch (error) {
185
+ // OMP's own `claimSession` swallows a failed probe and carries on. Bizar's
186
+ // delete, migrate and resume callers depend on a throw to refuse, so a
187
+ // failed probe fails closed here instead of downgrading to the Bizar lock.
188
+ return { status: "probe-failed", reason: error instanceof Error ? error.message : String(error) };
189
+ }
190
+ if (handle === null)
191
+ return { status: "held-by-another-process" };
192
+ // Bound to its handle: OMP's FileLock.release reaches a native exclusive
193
+ // reference through `this`, so an unbound copy throws "Failed to unwrap
194
+ // exclusive reference of FileLock" instead of releasing the lease.
195
+ const held = { release: handle.release.bind(handle), depth: 1 };
196
+ heldNativeLeases.set(sessionId, held);
197
+ return { status: "acquired", release: () => releaseHeldNativeLease(sessionId, held) };
198
+ }
199
+ /**
200
+ * Test seam: drop every memoized module and held-claim so another runtime, or
201
+ * the next case, starts from a clean process-local state.
202
+ */
203
+ export function resetNativeSessionLeaseProbe() {
204
+ nativeModuleCache.root = undefined;
205
+ nativeModuleCache.storage = undefined;
206
+ heldNativeLeases.clear();
207
+ }
208
+ /** Drop one level of this process's claim, releasing OMP's lease at zero. */
209
+ function releaseHeldNativeLease(sessionId, held) {
210
+ // A double release must not drive the counter negative, which would leave
211
+ // the map claiming a claim that no longer exists.
212
+ if (held.depth <= 0)
213
+ return;
214
+ held.depth -= 1;
215
+ if (held.depth > 0)
216
+ return;
217
+ heldNativeLeases.delete(sessionId);
218
+ held.release();
219
+ }
220
+ /** Bizar's own cooperating-process lease, keyed by the same native session id. */
221
+ function sessionLeasePath(sessionId, env) {
222
+ const root = sessionLeaseRoot(env);
223
+ return join(root, `${createHash("sha256").update(`session:${sessionId}`).digest("hex")}.lock`);
224
+ }
225
+ /** Create Bizar's lease root, which `acquireLease` requires to already exist. */
226
+ function sessionLeaseRoot(env) {
227
+ const root = join(env.XDG_STATE_HOME || join(env.HOME || homedir(), ".local", "state"), "bizar-omp", "session-leases");
228
+ mkdirSync(root, { recursive: true, mode: 0o700 });
229
+ return root;
230
+ }
231
+ /**
232
+ * Claim exclusive write access to a native OMP journal.
233
+ *
234
+ * The journal's native session id is read first and is required: ownership in
235
+ * both domains is keyed on it, so a journal with no readable id cannot be
236
+ * proven to be the one Bizar recorded. Refusing there is deliberate — the
237
+ * alternative is mutating a file whose identity Bizar never established.
238
+ *
239
+ * When OMP's native lease is reachable it is taken too, so a plain `omp`
240
+ * process holding this journal blocks the operation. When it is not reachable
241
+ * the message names only Bizar-owned state, because that is all Bizar can
242
+ * actually prove at that moment.
243
+ */
244
+ export function acquireSessionOwnership(journalPath, env = process.env) {
245
+ const sessionId = readJournalSessionId(journalPath);
246
+ if (!sessionId)
247
+ throw new OwnershipError(`Refusing to modify a session journal with no readable native session id: ${journalPath}`);
248
+ const native = probeNativeSessionLease(sessionId, env);
249
+ if (native.status === "held-by-another-process") {
250
+ throw new OwnershipError("This OMP session is open in another OMP process; close it there before resuming or deleting it.");
251
+ }
252
+ if (native.status === "probe-failed") {
253
+ throw new OwnershipError(`Refusing to modify a session journal because OMP's ownership lease could not be probed: ${native.reason}`);
254
+ }
255
+ const lease = acquireLease(sessionLeasePath(sessionId, env), native.status === "acquired"
256
+ ? "This OMP session is already open in another OMP or OMB process; close it there before resuming."
257
+ : "This session is already open in another OMB process; close it there before resuming.");
258
+ if (native.status === "acquired") {
259
+ const releaseNative = native.release;
260
+ return { release() { try {
261
+ lease.release();
262
+ }
263
+ finally {
264
+ releaseNative();
265
+ } } };
266
+ }
267
+ return lease;
268
+ }
269
+ //# sourceMappingURL=session-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-ownership.js","sourceRoot":"","sources":["../../src/omp/session-ownership.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type { ObservedRouting, RoutingSource } from "../core/types.js";
2
+ export interface RequestedModelRouting {
3
+ /** Exact per-call selectors, in the order the caller supplied them. */
4
+ selectors: string[];
5
+ /** The native routing source Bizar admitted for this dispatch. */
6
+ source: RoutingSource;
7
+ /** The caller-supplied `effort`, kept separate from model identity. */
8
+ effort?: string;
9
+ }
10
+ /**
11
+ * Normalize a requested per-call `model` into the ordered selector list Bizar
12
+ * journals, or refuse a selector OMP documents as invalid.
13
+ *
14
+ * Returns an empty list when no selector was requested, which is the signal
15
+ * that the specialist's own frontmatter and exact-agent override remain
16
+ * authoritative.
17
+ */
18
+ export declare function normalizeRequestedModelSelectors(value: unknown): string[];
19
+ /**
20
+ * Build the routing record for one dispatch.
21
+ *
22
+ * Specialist identity and model routing stay separate fields: a
23
+ * `bizar-implementer` remains the implementer whichever selector it runs on.
24
+ * With no requested selector the source stays `specialist-default`, so the
25
+ * specialist's own model role keeps working exactly as before.
26
+ */
27
+ export declare function requestedRouting(input: {
28
+ model?: unknown;
29
+ effort?: unknown;
30
+ }): RequestedModelRouting;
31
+ /**
32
+ * Select the settled result belonging to one dispatch.
33
+ *
34
+ * A call settles into `details.results`, one entry per spawn, and each entry
35
+ * carries the wire `name` it was spawned under. Matching is by that identity,
36
+ * never by position: `admitNativeTasks` skips tasks that name no work item, so
37
+ * the admitted dispatches are fewer than the spawns and indexing by admission
38
+ * order would hand a dispatch whichever spawn happened to come first.
39
+ *
40
+ * A single-entry batch is used directly, because there is only one candidate
41
+ * and the host may omit `name`. A purely background call returns `results: []`
42
+ * and leaves its routing on the progress snapshot, so an absent match stays
43
+ * unobserved: recording another spawn's routing would be a false provenance
44
+ * claim, which is the exact failure this contract exists to prevent.
45
+ */
46
+ export declare function settledResultFor(details: unknown, workItemId: string): unknown;
47
+ /**
48
+ * Read the native routing OMP actually used for one settled spawn.
49
+ *
50
+ * These are the host's own flat fields on the settled result, siblings of
51
+ * `structuredOutput`, so the journal can distinguish the selector Bizar
52
+ * admitted from the model, identity, effort and fallback state OMP resolved.
53
+ * Every field is optional: the host omits routing it did not resolve, and an
54
+ * absent field must leave the dispatch on what was requested rather than
55
+ * inventing an observation.
56
+ */
57
+ export declare function readObservedRouting(result: unknown): ObservedRouting | undefined;
58
+ //# sourceMappingURL=task-model-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-model-selector.d.ts","sourceRoot":"","sources":["../../src/omp/task-model-selector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA2BvE,MAAM,WAAW,qBAAqB;IACpC,uEAAuE;IACvE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,aAAa,CAAC;IACtB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQzE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,qBAAqB,CAKpG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAehF"}
@@ -0,0 +1,144 @@
1
+ import { ContractError } from "../core/errors.js";
2
+ /**
3
+ * Per-call `model` selector policy for managed Bizar dispatches.
4
+ *
5
+ * OMP 18.5 made `model` a public, highest-precedence field on every `task`
6
+ * item: it outranks `task.agentModelOverrides[agentName]` and the agent
7
+ * definition's own `model`. A managed run that dropped the field would admit a
8
+ * dispatch, journal a Bizar-computed role, and then let the host execute a
9
+ * different model than the journal claims — a provenance failure, not just a
10
+ * missing feature.
11
+ *
12
+ * Bizar therefore records what was requested instead of ignoring it, and
13
+ * refuses only the selectors OMP documents as ambiguous or structurally empty.
14
+ *
15
+ * What this module deliberately does **not** do is resolve the selector.
16
+ * Deciding whether `provider/model:level` names a live model, whether the
17
+ * suffix is a valid thinking level, or whether it has working credentials all
18
+ * require OMP's model catalog and credential store. Re-deciding any of that
19
+ * here would create the second model resolver this package must not own; those
20
+ * cases fail natively at OMP preflight and Bizar reports the refusal as a
21
+ * failed dispatch rather than a settled one.
22
+ */
23
+ /** Selectors OMP documents as ambiguous and therefore refuses. */
24
+ const AMBIGUOUS_LITERALS = { default: true, inherit: true };
25
+ /**
26
+ * Whether one selector is the bare `default`/`inherit` literal OMP calls
27
+ * ambiguous, optionally carrying a thinking suffix.
28
+ *
29
+ * The suffix is stripped only to recognise that reserved word. A registered
30
+ * model id that legitimately ends in `:max` keeps its suffix and is left for
31
+ * OMP to resolve, because Bizar cannot tell an id from a selector without the
32
+ * catalog. `@default` is the documented way to inherit the parent session's
33
+ * model, so it is deliberately not ambiguous.
34
+ */
35
+ function isAmbiguousLiteral(selector) {
36
+ const trimmed = selector.trim();
37
+ const withoutSuffix = trimmed.includes(":") ? trimmed.slice(0, trimmed.lastIndexOf(":")) : trimmed;
38
+ return AMBIGUOUS_LITERALS[withoutSuffix] === true;
39
+ }
40
+ function normalizeSelector(entry, field) {
41
+ if (typeof entry !== "string")
42
+ throw new ContractError(`${field} must be a selector string, received ${entry === null ? "null" : typeof entry}`);
43
+ const trimmed = entry.trim();
44
+ // OMP rejects blank and separator-only selectors; mirroring that here keeps
45
+ // the refusal at admission instead of leaving a dispatch nothing can settle.
46
+ if (trimmed.replace(/[\s,]/g, "").length === 0)
47
+ throw new ContractError(`${field} was blank; use a concrete provider/model selector or a role alias such as @bizar_build`);
48
+ if (isAmbiguousLiteral(trimmed))
49
+ throw new ContractError(`${field} used the ambiguous literal "${trimmed}"; OMP refuses it at preflight. Use @default to inherit the parent session's model.`);
50
+ return trimmed;
51
+ }
52
+ /**
53
+ * Normalize a requested per-call `model` into the ordered selector list Bizar
54
+ * journals, or refuse a selector OMP documents as invalid.
55
+ *
56
+ * Returns an empty list when no selector was requested, which is the signal
57
+ * that the specialist's own frontmatter and exact-agent override remain
58
+ * authoritative.
59
+ */
60
+ export function normalizeRequestedModelSelectors(value) {
61
+ if (value === undefined || value === null)
62
+ return [];
63
+ if (Array.isArray(value)) {
64
+ if (value.length === 0)
65
+ throw new ContractError("model was given as an empty array; supply an ordered, non-empty selector list or omit the field");
66
+ return value.map((entry, index) => normalizeSelector(entry, `model[${index}]`));
67
+ }
68
+ if (typeof value === "string")
69
+ return [normalizeSelector(value, "model")];
70
+ throw new ContractError(`model must be a selector string or an ordered array of selectors, received ${typeof value}`);
71
+ }
72
+ /**
73
+ * Build the routing record for one dispatch.
74
+ *
75
+ * Specialist identity and model routing stay separate fields: a
76
+ * `bizar-implementer` remains the implementer whichever selector it runs on.
77
+ * With no requested selector the source stays `specialist-default`, so the
78
+ * specialist's own model role keeps working exactly as before.
79
+ */
80
+ export function requestedRouting(input) {
81
+ const selectors = normalizeRequestedModelSelectors(input.model);
82
+ const routing = { selectors, source: selectors.length > 0 ? "explicit-per-call" : "specialist-default" };
83
+ if (typeof input.effort === "string" && input.effort.trim())
84
+ routing.effort = input.effort.trim();
85
+ return routing;
86
+ }
87
+ /**
88
+ * Select the settled result belonging to one dispatch.
89
+ *
90
+ * A call settles into `details.results`, one entry per spawn, and each entry
91
+ * carries the wire `name` it was spawned under. Matching is by that identity,
92
+ * never by position: `admitNativeTasks` skips tasks that name no work item, so
93
+ * the admitted dispatches are fewer than the spawns and indexing by admission
94
+ * order would hand a dispatch whichever spawn happened to come first.
95
+ *
96
+ * A single-entry batch is used directly, because there is only one candidate
97
+ * and the host may omit `name`. A purely background call returns `results: []`
98
+ * and leaves its routing on the progress snapshot, so an absent match stays
99
+ * unobserved: recording another spawn's routing would be a false provenance
100
+ * claim, which is the exact failure this contract exists to prevent.
101
+ */
102
+ export function settledResultFor(details, workItemId) {
103
+ if (details === null || typeof details !== "object")
104
+ return undefined;
105
+ const results = details.results;
106
+ if (!Array.isArray(results))
107
+ return undefined;
108
+ if (results.length === 1)
109
+ return results[0];
110
+ const named = results.find(result => result !== null && typeof result === "object" && result.name === workItemId);
111
+ return named;
112
+ }
113
+ /**
114
+ * Read the native routing OMP actually used for one settled spawn.
115
+ *
116
+ * These are the host's own flat fields on the settled result, siblings of
117
+ * `structuredOutput`, so the journal can distinguish the selector Bizar
118
+ * admitted from the model, identity, effort and fallback state OMP resolved.
119
+ * Every field is optional: the host omits routing it did not resolve, and an
120
+ * absent field must leave the dispatch on what was requested rather than
121
+ * inventing an observation.
122
+ */
123
+ export function readObservedRouting(result) {
124
+ if (result === null || typeof result !== "object")
125
+ return undefined;
126
+ const record = result;
127
+ const observed = {};
128
+ for (const key of ["modelRole", "resolvedModel", "resolvedModelIdentity", "resolvedThinkingLevel", "resolvedModelRoute"]) {
129
+ const value = record[key];
130
+ if (typeof value === "string" && value.trim())
131
+ observed[key] = value.trim();
132
+ }
133
+ // A per-spawn override may be a single selector or the ordered list the
134
+ // caller supplied, so it is recorded as sent rather than collapsed.
135
+ const override = record.modelOverride;
136
+ if (typeof override === "string" && override.trim())
137
+ observed.modelOverride = override.trim();
138
+ else if (Array.isArray(override))
139
+ observed.modelOverride = override.map(selector => String(selector));
140
+ if (typeof record.resolvedModelIsFallback === "boolean")
141
+ observed.resolvedModelIsFallback = record.resolvedModelIsFallback;
142
+ return Object.keys(observed).length > 0 ? observed : undefined;
143
+ }
144
+ //# sourceMappingURL=task-model-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-model-selector.js","sourceRoot":"","sources":["../../src/omp/task-model-selector.ts"],"names":[],"mappings":"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"}
@@ -16,10 +16,21 @@ const root = resolve(here, "..", "..");
16
16
  const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
17
17
  if (typeof pkg.version !== "string")
18
18
  throw new Error("package.json has no version");
19
- /** Null rather than a guess: an unpacked tarball has no commit to report. */
19
+ /**
20
+ * Null by default, and the release path passes --no-commit for the same reason.
21
+ *
22
+ * A receipt that is committed into the repository cannot name the commit that
23
+ * contains it, so any value here refers to a *parent* -- a tree that does not
24
+ * contain the OMP baseline this receipt asserts. A reader who follows that
25
+ * commit lands on a tree contradicting the receipt. Provenance is therefore
26
+ * opt-in via --commit, for callers writing a throwaway file outside the repo.
27
+ */
28
+ const flags = new Set(["--no-commit", "--commit"]);
29
+ const target = process.argv.slice(2).find(arg => !flags.has(arg)) ?? join(root, "compatibility-receipt.json");
30
+ const wantsCommit = process.argv.includes("--commit") && !process.argv.includes("--no-commit");
20
31
  let commit = null;
21
32
  try {
22
- if (!process.argv.includes("--no-commit")) {
33
+ if (wantsCommit) {
23
34
  commit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8", timeout: 10_000 }).trim();
24
35
  }
25
36
  }
@@ -27,7 +38,6 @@ catch {
27
38
  commit = null;
28
39
  }
29
40
  const receipt = buildCompatibilityReceipt({ packageVersion: pkg.version, commit });
30
- const target = process.argv.slice(2).find(arg => arg !== "--no-commit") ?? join(root, "compatibility-receipt.json");
31
41
  writeFileSync(target, renderCompatibilityReceipt(receipt));
32
42
  process.stdout.write(`wrote ${target}\n`);
33
43
  //# sourceMappingURL=emit-compatibility-receipt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit-compatibility-receipt.js","sourceRoot":"","sources":["../../src/scripts/emit-compatibility-receipt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAExG,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,iCAAiC;AACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;AACjG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;IAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAEpF,6EAA6E;AAC7E,IAAI,MAAM,GAAkB,IAAI,CAAC;AACjC,IAAI,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1C,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/G,CAAC;AACH,CAAC;AAAC,MAAM,CAAC;IACP,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,OAAO,GAAG,yBAAyB,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACnF,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,aAAa,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,4BAA4B,CAAC,CAAC;AACpH,aAAa,CAAC,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"emit-compatibility-receipt.js","sourceRoot":"","sources":["../../src/scripts/emit-compatibility-receipt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAExG,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,iCAAiC;AACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;AACjG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;IAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAEpF;;;;;;;;GAQG;AACH,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;AACnD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,4BAA4B,CAAC,CAAC;AAC9G,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC/F,IAAI,MAAM,GAAkB,IAAI,CAAC;AACjC,IAAI,CAAC;IACH,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/G,CAAC;AACH,CAAC;AAAC,MAAM,CAAC;IACP,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,OAAO,GAAG,yBAAyB,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACnF,aAAa,CAAC,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC"}
@@ -1,20 +1,53 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 280" role="img" aria-labelledby="title desc">
2
- <title id="title">BizarHarness OMP</title>
3
- <desc id="desc">Autonomous engineering for oh-my-pi with evidence in the loop.</desc>
4
- <rect width="1200" height="280" rx="18" fill="#0b1220"/>
5
- <path d="M0 224h1200" stroke="#1e293b" stroke-width="2"/>
6
- <path d="M0 56h1200" stroke="#1e293b" stroke-width="2"/>
7
- <g transform="translate(72 72)">
8
- <rect width="88" height="88" rx="14" fill="#0f766e"/>
9
- <path d="M22 26h44v12H34v12h26v12H34v12h32v12H22z" fill="#ecfeff"/>
10
- <circle cx="74" cy="22" r="8" fill="#38bdf8"/>
11
- <circle cx="16" cy="74" r="8" fill="#fbbf24"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 360" role="img" aria-labelledby="title desc">
2
+ <title id="title">BizarOMP</title>
3
+ <desc id="desc">Native workflow, evidence, and session control for oh-my-pi.</desc>
4
+ <defs>
5
+ <pattern id="grid" width="32" height="32" patternUnits="userSpaceOnUse">
6
+ <path d="M32 0H0V32" fill="none" stroke="#171a21" stroke-width="1"/>
7
+ </pattern>
8
+ <clipPath id="frame"><rect x="1" y="1" width="1398" height="358" rx="18"/></clipPath>
9
+ </defs>
10
+ <rect width="1400" height="360" rx="18" fill="#07080a"/>
11
+ <g clip-path="url(#frame)">
12
+ <rect width="1400" height="360" fill="url(#grid)" opacity=".55"/>
13
+ <circle cx="232" cy="-34" r="248" fill="#ff2ea6" opacity=".055"/>
14
+ <circle cx="1268" cy="392" r="290" fill="#00b4ff" opacity=".05"/>
12
15
  </g>
13
- <text x="204" y="112" fill="#f8fafc" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="58" font-weight="700" letter-spacing="2">BIZAR // OMP</text>
14
- <text x="208" y="158" fill="#99f6e4" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="23" letter-spacing="1">AUTONOMOUS ENGINEERING, WITH EVIDENCE IN THE LOOP</text>
15
- <g fill="#38bdf8">
16
- <circle cx="210" cy="207" r="5"/><circle cx="232" cy="207" r="5"/><circle cx="254" cy="207" r="5"/>
16
+ <rect x="1" y="1" width="1398" height="358" rx="18" fill="none" stroke="#232833" stroke-width="2"/>
17
+ <g font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">
18
+ <g transform="translate(72 58)">
19
+ <rect width="48" height="48" rx="11" fill="#111319" stroke="#343946"/>
20
+ <path d="M14 16l9 8-9 8" fill="none" stroke="#ff2ea6" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
21
+ <path d="M28 32h8" stroke="#00b4ff" stroke-width="3" stroke-linecap="round"/>
22
+ <text x="68" y="19" fill="#8b93a7" font-size="13" font-weight="700" letter-spacing="2.2">POLDERLABS / OMP NATIVE</text>
23
+ <text x="68" y="42" fill="#5f6678" font-size="12" letter-spacing="1.4">OPERATOR WORKFLOW SYSTEM</text>
24
+ </g>
25
+ <text x="72" y="182" fill="#f3f4f7" font-size="62" font-weight="760" letter-spacing="-2.6">BIZAR</text>
26
+ <text x="282" y="182" fill="#ff2ea6" font-size="62" font-weight="760" letter-spacing="-2.6">OMP</text>
27
+ <text x="74" y="221" fill="#a9afbd" font-size="18" letter-spacing=".25">Native workflow, evidence, and session control for oh-my-pi.</text>
28
+ <g transform="translate(72 265)" font-size="12" font-weight="700" letter-spacing="1.1">
29
+ <rect width="112" height="31" rx="7" fill="#0f1116" stroke="#2c303b"/><text x="18" y="20" fill="#e7e9ee">SESSIONS</text>
30
+ <rect x="124" width="122" height="31" rx="7" fill="#0f1116" stroke="#2c303b"/><text x="142" y="20" fill="#e7e9ee">WORKFLOWS</text>
31
+ <rect x="258" width="118" height="31" rx="7" fill="#0f1116" stroke="#2c303b"/><text x="276" y="20" fill="#e7e9ee">EVIDENCE</text>
32
+ <rect x="388" width="98" height="31" rx="7" fill="#0f1116" stroke="#2c303b"/><text x="406" y="20" fill="#e7e9ee">MOBILE</text>
33
+ </g>
34
+ <g transform="translate(900 48)">
35
+ <rect width="424" height="264" rx="14" fill="#0d0f13" stroke="#2a2e38"/>
36
+ <path d="M0 44h424" stroke="#232833"/>
37
+ <circle cx="22" cy="22" r="4" fill="#ff2ea6"/><circle cx="38" cy="22" r="4" fill="#4b5262"/><circle cx="54" cy="22" r="4" fill="#4b5262"/>
38
+ <text x="80" y="27" fill="#7f8798" font-size="11" letter-spacing="1.5">OMB / SESSION CONTROL</text>
39
+ <text x="24" y="79" fill="#00b4ff" font-size="14">$</text><text x="43" y="79" fill="#f0f1f4" font-size="14">omb</text>
40
+ <text x="24" y="111" fill="#626a7c" font-size="11">HOST</text><text x="104" y="111" fill="#d8dbe2" font-size="12">local / project</text>
41
+ <text x="24" y="138" fill="#626a7c" font-size="11">SESSION</text><text x="104" y="138" fill="#d8dbe2" font-size="12">working</text><circle cx="184" cy="134" r="4" fill="#34d399"/>
42
+ <text x="24" y="165" fill="#626a7c" font-size="11">RUNTIME</text><text x="104" y="165" fill="#d8dbe2" font-size="12">OMP native</text>
43
+ <path d="M24 194h376" stroke="#232833"/>
44
+ <g transform="translate(24 214)">
45
+ <text x="0" y="0" fill="#9198a8" font-size="11">PLAN</text><path d="M42 -4h38" stroke="#343946"/><circle cx="88" cy="-4" r="3" fill="#ff2ea6"/>
46
+ <text x="104" y="0" fill="#9198a8" font-size="11">RUN</text><path d="M136 -4h38" stroke="#343946"/><circle cx="182" cy="-4" r="3" fill="#ff2ea6"/>
47
+ <text x="198" y="0" fill="#9198a8" font-size="11">VERIFY</text><path d="M250 -4h38" stroke="#343946"/><circle cx="296" cy="-4" r="3" fill="#00b4ff"/>
48
+ <text x="312" y="0" fill="#cdd1da" font-size="11">INTEGRATE</text>
49
+ </g>
50
+ <text x="24" y="247" fill="#5d6576" font-size="10" letter-spacing="1.2">OMP OWNS THE RUNTIME · BIZAR OWNS THE PROOF</text>
51
+ </g>
17
52
  </g>
18
- <text x="278" y="215" fill="#94a3b8" font-family="ui-monospace,SFMono-Regular,Menlo,monospace" font-size="18">workflows / sessions / verification / integration</text>
19
- <text x="1080" y="244" text-anchor="end" fill="#64748b" font-family="ui-monospace,SFMono-Regular,Menlo,monospace" font-size="14">POLDERLABS</text>
20
- </svg>
53
+ </svg>