omnirush 0.8.1 → 0.8.2

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.
@@ -89,6 +89,9 @@ const ENVELOPE_WRITE_CHUNK_BYTES = 64 * 1024;
89
89
  const MAX_CHANGE_JOURNAL_ENTRIES = 512;
90
90
  const MAX_CHANGE_JOURNAL_BYTES = 768 * 1024;
91
91
  const MAX_SESSION_LEDGER_ENTRIES = 512;
92
+ const SESSION_LEDGER_LOCK_TIMEOUT_MS = 5_000;
93
+ const SESSION_LEDGER_LOCK_STALE_MS = 60_000;
94
+ const SESSION_LEDGER_LOCK_RETRY_MS = 25;
92
95
  const MAX_TOUCHED_PATHS = 128;
93
96
  const MAX_GIT_STATUS_ENTRIES = 500;
94
97
  const MAX_GIT_RECENT_COMMITS = 50;
@@ -2977,6 +2980,44 @@ async function writeFileAtomic(path: string, data: Buffer | string): Promise<voi
2977
2980
  }
2978
2981
  }
2979
2982
 
2983
+ async function withSessionLedgerLock<T>(lockPath: string, task: () => Promise<T>): Promise<T> {
2984
+ const started = Date.now();
2985
+ await mkdir(dirname(lockPath), { recursive: true, mode: 0o700 });
2986
+ let handle: Awaited<ReturnType<typeof open>> | null = null;
2987
+ while (!handle) {
2988
+ try {
2989
+ handle = await open(lockPath, "wx", 0o600);
2990
+ await handle.writeFile(`${process.pid}\n`);
2991
+ } catch (error) {
2992
+ await handle?.close().catch(() => undefined);
2993
+ handle = null;
2994
+ const code = typeof error === "object" && error !== null && "code" in error
2995
+ ? (error as { code?: string }).code
2996
+ : undefined;
2997
+ if (code !== "EEXIST") throw error;
2998
+ try {
2999
+ const info = await stat(lockPath);
3000
+ if (Date.now() - info.mtimeMs > SESSION_LEDGER_LOCK_STALE_MS) {
3001
+ await rm(lockPath, { force: true });
3002
+ continue;
3003
+ }
3004
+ } catch {
3005
+ // The owner may have released the lock between EEXIST and stat.
3006
+ }
3007
+ if (Date.now() - started >= SESSION_LEDGER_LOCK_TIMEOUT_MS) {
3008
+ throw new Error("timed out waiting for the session ledger lock");
3009
+ }
3010
+ await new Promise<void>((resolvePromise) => setTimeout(resolvePromise, SESSION_LEDGER_LOCK_RETRY_MS));
3011
+ }
3012
+ }
3013
+ try {
3014
+ return await task();
3015
+ } finally {
3016
+ await handle.close().catch(() => undefined);
3017
+ await rm(lockPath, { force: true }).catch(() => undefined);
3018
+ }
3019
+ }
3020
+
2980
3021
  function parseSpoolMeta(value: unknown): SpoolMeta | null {
2981
3022
  if (!value || typeof value !== "object") return null;
2982
3023
  const record = value as Partial<SpoolMeta>;
@@ -3105,19 +3146,27 @@ export class WorkspaceCollector {
3105
3146
  this.ledger = await readSessionLedger(this.ledgerPath);
3106
3147
  }
3107
3148
 
3108
- private async saveLedger(): Promise<void> {
3149
+ private async saveLedger(sessionId: string): Promise<void> {
3109
3150
  if (!this.ledgerPath) return;
3110
3151
  await this.ledgerReady;
3111
- const entries = Object.entries(this.ledger.sessions)
3112
- .sort(([, left], [, right]) => right.lastSeenAt.localeCompare(left.lastSeenAt))
3113
- .slice(0, MAX_SESSION_LEDGER_ENTRIES);
3114
- this.ledger.sessions = Object.fromEntries(entries);
3115
- const snapshot = JSON.stringify(this.ledger);
3152
+ const record = this.ledger.sessions[sessionId]
3153
+ ? JSON.parse(JSON.stringify(this.ledger.sessions[sessionId])) as SessionLedgerRecord
3154
+ : undefined;
3116
3155
  this.ledgerWriteTail = this.ledgerWriteTail
3117
3156
  .catch(() => undefined)
3118
3157
  .then(async () => {
3119
- await mkdir(dirname(this.ledgerPath!), { recursive: true, mode: 0o700 });
3120
- await writeFileAtomic(this.ledgerPath!, snapshot);
3158
+ await withSessionLedgerLock(`${this.ledgerPath!}.lock`, async () => {
3159
+ // Reload while holding the lock so concurrent processes merge their
3160
+ // session record instead of overwriting one another's progress.
3161
+ const merged = await readSessionLedger(this.ledgerPath);
3162
+ if (record) merged.sessions[sessionId] = record;
3163
+ const entries = Object.entries(merged.sessions)
3164
+ .sort(([, left], [, right]) => right.lastSeenAt.localeCompare(left.lastSeenAt))
3165
+ .slice(0, MAX_SESSION_LEDGER_ENTRIES);
3166
+ merged.sessions = Object.fromEntries(entries);
3167
+ this.ledger = merged;
3168
+ await writeFileAtomic(this.ledgerPath!, JSON.stringify(merged));
3169
+ });
3121
3170
  });
3122
3171
  await this.ledgerWriteTail;
3123
3172
  }
@@ -3158,13 +3207,13 @@ export class WorkspaceCollector {
3158
3207
  this.appendTrace(state, "session.resumed", { session_segment: state.segment, previous_segment: previous?.segment ?? null });
3159
3208
  }
3160
3209
  this.ledger.sessions[state.id] = this.ledgerRecord(state);
3161
- await this.saveLedger();
3210
+ await this.saveLedger(state.id);
3162
3211
  }
3163
3212
 
3164
3213
  private async persistSession(state: SessionState): Promise<void> {
3165
3214
  await this.ledgerReady;
3166
3215
  this.ledger.sessions[state.id] = this.ledgerRecord(state);
3167
- await this.saveLedger();
3216
+ await this.saveLedger(state.id);
3168
3217
  }
3169
3218
 
3170
3219
  private async recordLedgerOutcome(sessionId: string, outcome: "success" | "failure"): Promise<void> {
@@ -3188,7 +3237,7 @@ export class WorkspaceCollector {
3188
3237
  record.lastFailureAt = now;
3189
3238
  }
3190
3239
  record.lastSeenAt = now;
3191
- await this.saveLedger();
3240
+ await this.saveLedger(sessionId);
3192
3241
  }
3193
3242
 
3194
3243
  async sessionCheckpoint(sessionId: string): Promise<{ resumed: boolean; segment?: number; lastMessageId?: string }> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnirush",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Omnirush \u2014 free daily tokens for the most powerful coding model on earth.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -41,4 +41,4 @@
41
41
  "minimatch": "^10.2.6",
42
42
  "zod": "^4.6.5"
43
43
  }
44
- }
44
+ }
package/src/bin.js CHANGED
@@ -577,7 +577,7 @@ async function cmdDoctor() {
577
577
  } else {
578
578
  console.log(
579
579
  `WARN agent core — installed core is v${coreVersion}, this omnirush expects v${own} ` +
580
- "(stale install: pi.dev update banners, missing omnirush provider). Fix: omnirush update",
580
+ "(stale install: agent update banners, missing omnirush provider). Fix: omnirush update",
581
581
  );
582
582
  }
583
583
  }
package/src/lib.js CHANGED
@@ -273,7 +273,7 @@ export function catalogFromGateway(payload, shipped = MODELS) {
273
273
  }
274
274
 
275
275
  /**
276
- * Prepend `--provider`/`--model` defaults for agent runs, without
276
+ * Prepend the product's provider/model/scope defaults for agent runs, without
277
277
  * duplicating flags the user already specified. Consumption rules mirror
278
278
  * pi's own parseArgs so a value token is never mistaken for a flag (e.g.
279
279
  * `omnirush -p "how do I use --model flags"` must not suppress defaults).
@@ -289,6 +289,7 @@ export function withModelDefaults(args, defaults) {
289
289
  }
290
290
  let hasProvider = false;
291
291
  let hasModel = false;
292
+ let hasModelsScope = false;
292
293
  for (let i = 0; i < args.length; i++) {
293
294
  const arg = args[i];
294
295
  if (arg === "--") break;
@@ -305,10 +306,17 @@ export function withModelDefaults(args, defaults) {
305
306
  hasModel = true;
306
307
  i++;
307
308
  }
309
+ } else if (arg === "--models") {
310
+ if (i + 1 < args.length) {
311
+ hasModelsScope = true;
312
+ i++;
313
+ }
308
314
  } else if (arg.startsWith("--provider=")) {
309
315
  hasProvider = true;
310
316
  } else if (arg.startsWith("--model=")) {
311
317
  hasModel = true;
318
+ } else if (arg.startsWith("--models=")) {
319
+ hasModelsScope = true;
312
320
  } else if (arg === "--print" || arg === "-p") {
313
321
  const next = args[i + 1];
314
322
  if (
@@ -334,6 +342,10 @@ export function withModelDefaults(args, defaults) {
334
342
  }
335
343
  }
336
344
  const out = [];
345
+ // The upstream core merges every configured provider into its registry.
346
+ // Keep BYOK configuration on disk, but scope this product's selector to
347
+ // Omnirush unless the caller explicitly opts into another scope.
348
+ if (!hasModelsScope) out.push("--models", `${DEFAULT_PROVIDER}/*`);
337
349
  if (!hasProvider) out.push("--provider", provider);
338
350
  if (!hasModel) out.push("--model", model);
339
351
  return [...out, ...args];