omnius 1.0.598 → 1.0.600

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.
package/dist/index.js CHANGED
@@ -630870,6 +630870,23 @@ function mergeRanges(ranges) {
630870
630870
  function rangeLabel(ranges) {
630871
630871
  return ranges.map((r2) => r2.end === Infinity || r2.start === 0 ? "whole file" : `lines ${r2.start}-${r2.end}`).join(", ");
630872
630872
  }
630873
+ function sameRange(a2, b) {
630874
+ return a2.start === b.start && a2.end === b.end;
630875
+ }
630876
+ function rangeCovers(observed, requested) {
630877
+ const requestedWhole = requested.start === 0 || requested.end === Infinity;
630878
+ const observedWhole = observed.start === 0 || observed.end === Infinity;
630879
+ if (requestedWhole)
630880
+ return observedWhole;
630881
+ if (observedWhole)
630882
+ return true;
630883
+ return observed.start <= requested.start && requested.end <= observed.end;
630884
+ }
630885
+ function rangeWidth(range) {
630886
+ if (range.start === 0 || range.end === Infinity)
630887
+ return Infinity;
630888
+ return Math.max(0, range.end - range.start + 1);
630889
+ }
630873
630890
  function looksLikePartialCapture(content) {
630874
630891
  return /\[(?:oversize\s+(?:output|llmContent)|Tool output truncated|GREP_PARTIAL)\b/i.test(content);
630875
630892
  }
@@ -630895,16 +630912,36 @@ var init_evidenceLedger = __esm({
630895
630912
  "use strict";
630896
630913
  EvidenceLedger = class {
630897
630914
  entries = /* @__PURE__ */ new Map();
630915
+ /** Exact replay bodies, independently range-bound from aggregate receipts. */
630916
+ readBodies = /* @__PURE__ */ new Map();
630917
+ recordBody(path16, body, reset) {
630918
+ const prior = reset ? [] : this.readBodies.get(path16) ?? [];
630919
+ const retained = prior.filter((candidate) => !sameRange(candidate.range, body.range));
630920
+ retained.push(body);
630921
+ retained.sort((a2, b) => b.lastReadTurn - a2.lastReadTurn);
630922
+ this.readBodies.set(path16, retained.slice(0, 16));
630923
+ }
630898
630924
  /** Record a successful read. `fileVersion` is the file's current writeCount. */
630899
630925
  recordRead(input) {
630900
- const { path: path16, content, range, fileVersion, turn } = input;
630926
+ const { path: path16, content, fileVersion, turn } = input;
630901
630927
  if (!path16 || !content)
630902
630928
  return;
630929
+ const reportedRange = input.artifactMaterialization?.sourceRange;
630930
+ const materializedRange = reportedRange && typeof reportedRange.endLine === "number" ? {
630931
+ startLine: reportedRange.startLine,
630932
+ endLine: reportedRange.endLine
630933
+ } : void 0;
630934
+ const requestedWhole = input.range.start === 0 || input.range.end === Infinity;
630935
+ const range = !requestedWhole && materializedRange ? {
630936
+ start: materializedRange.startLine,
630937
+ end: materializedRange.endLine
630938
+ } : { ...input.range };
630903
630939
  const inferredPartial = input.artifactMaterialization?.status === "partial" || input.fidelity === "partial" || looksLikePartialCapture(content);
630904
630940
  const built = input.fidelity === "extract" ? { text: content, fidelity: "extract" } : inferredPartial ? { text: content, fidelity: "partial" } : { text: content, fidelity: "full" };
630905
630941
  const materialization = input.artifactMaterialization ?? (inferredPartial ? inferredPartialMaterialization(content) : void 0);
630906
630942
  const existing = this.entries.get(path16);
630907
- if (existing && existing.readVersion === fileVersion && !existing.stale) {
630943
+ const sameRevision = existing && existing.readVersion === fileVersion && !existing.stale && (!existing.contentHash || !input.contentHash || existing.contentHash === input.contentHash);
630944
+ if (existing && sameRevision) {
630908
630945
  if (built.fidelity === "extract" && existing.fidelity === "full") {
630909
630946
  const keepExistingCompleteExtract = existing.derivedExtract?.complete === true && input.extractComplete !== true;
630910
630947
  this.entries.set(path16, {
@@ -630926,6 +630963,14 @@ var init_evidenceLedger = __esm({
630926
630963
  return;
630927
630964
  }
630928
630965
  if (built.fidelity === "partial" && existing.fidelity === "full") {
630966
+ this.recordBody(path16, {
630967
+ range,
630968
+ content: built.text,
630969
+ ...input.contentHash ? { contentHash: input.contentHash } : {},
630970
+ fidelity: "partial",
630971
+ lastReadTurn: turn,
630972
+ ...materialization ? { artifactMaterialization: materialization } : {}
630973
+ }, false);
630929
630974
  this.entries.set(path16, {
630930
630975
  ...existing,
630931
630976
  lastReadTurn: turn,
@@ -630934,9 +630979,20 @@ var init_evidenceLedger = __esm({
630934
630979
  return;
630935
630980
  }
630936
630981
  const keepNew = existing.fidelity === "partial" && built.fidelity !== "partial" || built.text.length >= existing.content.length;
630982
+ if (built.fidelity !== "extract") {
630983
+ this.recordBody(path16, {
630984
+ range,
630985
+ content: built.text,
630986
+ ...input.contentHash ? { contentHash: input.contentHash } : {},
630987
+ fidelity: built.fidelity,
630988
+ lastReadTurn: turn,
630989
+ ...materialization ? { artifactMaterialization: materialization } : {}
630990
+ }, false);
630991
+ }
630937
630992
  this.entries.set(path16, {
630938
630993
  ...existing,
630939
630994
  content: keepNew ? built.text : existing.content,
630995
+ contentRange: keepNew ? range : existing.contentRange,
630940
630996
  contentHash: input.contentHash ?? existing.contentHash,
630941
630997
  fidelity: keepNew ? built.fidelity : existing.fidelity,
630942
630998
  ...keepNew && materialization ? { artifactMaterialization: materialization } : existing.artifactMaterialization ? { artifactMaterialization: existing.artifactMaterialization } : {},
@@ -630948,12 +631004,25 @@ var init_evidenceLedger = __esm({
630948
631004
  });
630949
631005
  return;
630950
631006
  }
631007
+ if (built.fidelity !== "extract") {
631008
+ this.recordBody(path16, {
631009
+ range,
631010
+ content: built.text,
631011
+ ...input.contentHash ? { contentHash: input.contentHash } : {},
631012
+ fidelity: built.fidelity,
631013
+ lastReadTurn: turn,
631014
+ ...materialization ? { artifactMaterialization: materialization } : {}
631015
+ }, true);
631016
+ } else {
631017
+ this.readBodies.delete(path16);
631018
+ }
630951
631019
  this.entries.set(path16, {
630952
631020
  path: path16,
630953
631021
  contentHash: input.contentHash,
630954
631022
  readVersion: fileVersion,
630955
631023
  mtimeMs: input.mtimeMs ?? 0,
630956
631024
  lastReadTurn: turn,
631025
+ contentRange: range,
630957
631026
  ranges: [range],
630958
631027
  content: built.text,
630959
631028
  fidelity: built.fidelity,
@@ -630974,6 +631043,19 @@ var init_evidenceLedger = __esm({
630974
631043
  const e2 = this.entries.get(path16);
630975
631044
  return !!e2 && !e2.stale && e2.fidelity !== "partial";
630976
631045
  }
631046
+ /**
631047
+ * Resolve a replay body only when that body's own provenance fully covers
631048
+ * the requested range. Aggregate `EvidenceEntry.ranges` are receipts only.
631049
+ */
631050
+ resolveFreshRead(input) {
631051
+ const entry = this.entries.get(input.path);
631052
+ if (!entry || entry.stale)
631053
+ return void 0;
631054
+ if (input.contentHash && input.contentHash !== entry.contentHash) {
631055
+ return void 0;
631056
+ }
631057
+ return (this.readBodies.get(input.path) ?? []).filter((body) => body.fidelity === "full" && (!input.contentHash || body.contentHash === input.contentHash) && rangeCovers(body.range, input.requestedRange)).sort((a2, b) => rangeWidth(a2.range) - rangeWidth(b.range) || b.lastReadTurn - a2.lastReadTurn)[0];
631058
+ }
630977
631059
  /**
630978
631060
  * Validate evidence freshness against the real filesystem via stat().
630979
631061
  * If the file's mtime has changed since we last read it (external mutation
@@ -631022,6 +631104,7 @@ var init_evidenceLedger = __esm({
631022
631104
  const cap = Math.max(1, Math.min(200, Math.floor(maxEntries)));
631023
631105
  return [...this.entries.values()].sort((a2, b) => b.lastReadTurn - a2.lastReadTurn).slice(0, cap).map((entry) => ({
631024
631106
  ...entry,
631107
+ contentRange: { ...entry.contentRange },
631025
631108
  ranges: entry.ranges.map((range) => ({ ...range })),
631026
631109
  ...entry.derivedExtract ? {
631027
631110
  derivedExtract: {
@@ -631053,6 +631136,7 @@ var init_evidenceLedger = __esm({
631053
631136
  }
631054
631137
  clear() {
631055
631138
  this.entries.clear();
631139
+ this.readBodies.clear();
631056
631140
  }
631057
631141
  /**
631058
631142
  * Render the evidence block for the ACTIVE CONTEXT FRAME. Freshest reads
@@ -631077,8 +631161,10 @@ var init_evidenceLedger = __esm({
631077
631161
  let used = lines.join("\n").length;
631078
631162
  for (const e2 of sorted) {
631079
631163
  const selectedExtract = !e2.stale && e2.derivedExtract?.complete === true ? e2.derivedExtract : void 0;
631080
- const renderedRanges = selectedExtract?.ranges ?? e2.ranges;
631081
- const header = e2.stale ? `▸ ${e2.path} @v${e2.readVersion} [STALE — changed since read; re-read needed] (${rangeLabel(e2.ranges)})` : selectedExtract ? `▸ ${e2.path} @v${e2.readVersion} [DERIVED EXTRACT — contract complete; canonical full source retained] (${rangeLabel(renderedRanges)}):` : e2.fidelity === "partial" ? `▸ ${e2.path} @v${e2.readVersion} [PARTIAL — successful output was capped; do not infer omitted text is absent] (${rangeLabel(e2.ranges)})` : `▸ ${e2.path} @v${e2.readVersion} [FRESH${e2.contentHash ? ` sha256=${e2.contentHash}` : ""}] (${rangeLabel(e2.ranges)}):`;
631164
+ const renderedRanges = selectedExtract?.ranges ?? [e2.contentRange];
631165
+ const coverage = rangeLabel(e2.ranges);
631166
+ const bodyRange = rangeLabel(renderedRanges);
631167
+ const header = e2.stale ? `▸ ${e2.path} @v${e2.readVersion} [STALE — changed since read; re-read needed] (${rangeLabel(e2.ranges)})` : selectedExtract ? `▸ ${e2.path} @v${e2.readVersion} [DERIVED EXTRACT — contract complete; canonical full source retained] (${rangeLabel(renderedRanges)}):` : e2.fidelity === "partial" ? `▸ ${e2.path} @v${e2.readVersion} [PARTIAL — successful output was capped; do not infer omitted text is absent] (body ${bodyRange}; coverage ${coverage})` : `▸ ${e2.path} @v${e2.readVersion} [FRESH${e2.contentHash ? ` sha256=${e2.contentHash}` : ""}] (body ${bodyRange}; coverage ${coverage}):`;
631082
631168
  if (compact6) {
631083
631169
  const receipt2 = `- evidence_id=file:${e2.path}@v${e2.readVersion} outcome=${e2.stale ? "stale" : "fresh"} fidelity=${e2.fidelity} sha256=${e2.contentHash ?? "unknown"} ranges=${rangeLabel(renderedRanges)}`;
631084
631170
  if (used + receipt2.length + 1 <= maxChars) {
@@ -651412,20 +651498,6 @@ ${blob}
651412
651498
  const p2 = pathForResult(idx);
651413
651499
  if (p2 && latestIdxForPath.get(p2) === idx)
651414
651500
  continue;
651415
- if (p2 && content.length >= 100) {
651416
- try {
651417
- this._evidenceLedger.recordRead({
651418
- path: this._normalizeEvidencePath(p2),
651419
- content,
651420
- contentHash: this._extractFileReadSha256(content) ?? void 0,
651421
- range: { start: 0, end: Number.POSITIVE_INFINITY },
651422
- fileVersion: this._worldFacts.files.get(p2)?.writeCount ?? 0,
651423
- mtimeMs: this._statMtimeMsForToolPath(p2),
651424
- turn: this._taskState?.toolCallCount ?? 0
651425
- });
651426
- } catch {
651427
- }
651428
- }
651429
651501
  const meta = metaForResult(idx);
651430
651502
  let stub;
651431
651503
  if (p2) {
@@ -651585,20 +651657,6 @@ ${blob}
651585
651657
  if (covered)
651586
651658
  return cov.fingerprint;
651587
651659
  }
651588
- if (!iv.whole && Number.isFinite(iv.end)) {
651589
- let best = null;
651590
- const requestedLength = Math.max(1, iv.end - iv.start + 1);
651591
- for (const cov of intervals) {
651592
- if (cov.whole || !Number.isFinite(cov.end))
651593
- continue;
651594
- const overlap = Math.max(0, Math.min(iv.end, cov.end) - Math.max(iv.start, cov.start) + 1);
651595
- if (overlap / requestedLength >= 0.8 && (!best || overlap > best.overlap)) {
651596
- best = { fingerprint: cov.fingerprint, overlap };
651597
- }
651598
- }
651599
- if (best)
651600
- return best.fingerprint;
651601
- }
651602
651660
  return exactFingerprint;
651603
651661
  }
651604
651662
  /** Register a read's region under the fingerprint that cached its result. */
@@ -652779,16 +652837,12 @@ ${notice}`;
652779
652837
  this._emitSteeringLifecycle(record, gatesOldPlan ? `admitted at turn ${turn}; old-plan actions are gated pending structured reconciliation` : `admitted at turn ${turn}; awaiting structured reconciliation`);
652780
652838
  }
652781
652839
  }
652782
- _canonicalReadEvidencePayload(canonicalPath2, contentHash2) {
652783
- const evidence = this._evidenceLedger.get(canonicalPath2);
652784
- if (evidence && !evidence.stale && evidence.contentHash === contentHash2 && evidence.fidelity === "full" && this._mustHonorExplicitFullFileRead(Buffer.byteLength(evidence.content, "utf8"), evidence.content.split("\n").length, this._branchRoutingContextWindow())) {
652785
- return evidence.content;
652786
- }
652787
- const branch = this._branchEvidenceByPath.get(canonicalPath2);
652788
- if (branch && branch.contentHash === contentHash2 && branch.taskEpoch === this._taskEpoch && branch.contractComplete) {
652789
- return branch.output;
652790
- }
652791
- return null;
652840
+ _canonicalReadEvidencePayload(canonicalPath2, contentHash2, requestedRange) {
652841
+ return this._evidenceLedger.resolveFreshRead({
652842
+ path: canonicalPath2,
652843
+ requestedRange,
652844
+ contentHash: contentHash2
652845
+ })?.content ?? null;
652792
652846
  }
652793
652847
  _rehydrateResolvedReadEvidence(input) {
652794
652848
  return [
@@ -658897,27 +658951,26 @@ ${cachedResult}`,
658897
658951
  let branchEvidenceResult = null;
658898
658952
  if (normalizedDispatchName === "file_read" && this._fileReadMode(tc.arguments) !== "extract") {
658899
658953
  const exactReadFingerprint = this._buildToolFingerprint("file_read", tc.arguments);
658954
+ const exactReadInterval = this._readIntervalFromArgs("file_read", tc.arguments);
658955
+ const requestedReadRange = exactReadInterval && !exactReadInterval.whole ? {
658956
+ start: exactReadInterval.start,
658957
+ end: exactReadInterval.end
658958
+ } : {
658959
+ start: 0,
658960
+ end: Number.POSITIVE_INFINITY
658961
+ };
658900
658962
  const exactReadKey = this._resolveReadCoverageFingerprint("file_read", tc.arguments, exactReadFingerprint);
658901
658963
  const priorRead = exactFileReadObservations.get(exactReadKey);
658902
658964
  if (priorRead) {
658903
658965
  const currentRead = this._fileReadDiskIdentity(tc.arguments);
658904
658966
  if (currentRead && currentRead.contentHash === priorRead.contentHash) {
658905
658967
  exactFileReadObservations.set(exactReadKey, currentRead);
658906
- const canonicalPayload = this._canonicalReadEvidencePayload(currentRead.canonicalPath, currentRead.contentHash);
658968
+ const canonicalPayload = this._canonicalReadEvidencePayload(currentRead.canonicalPath, currentRead.contentHash, requestedReadRange);
658907
658969
  if (canonicalPayload) {
658908
- const evidence = this._evidenceLedger.get(currentRead.canonicalPath);
658909
- const fullSource = evidence?.fidelity === "full" && evidence.content === canonicalPayload;
658910
- const delivered = fullSource ? canonicalPayload : this._rehydrateResolvedReadEvidence({
658911
- fingerprint: exactReadKey,
658912
- canonicalPath: currentRead.canonicalPath,
658913
- contentHash: currentRead.contentHash,
658914
- payload: canonicalPayload,
658915
- turn
658916
- });
658917
658970
  branchEvidenceResult = {
658918
658971
  success: true,
658919
- output: delivered,
658920
- llmContent: delivered,
658972
+ output: canonicalPayload,
658973
+ llmContent: canonicalPayload,
658921
658974
  beforeHash: currentRead.contentHash,
658922
658975
  runtimeAuthored: true,
658923
658976
  noop: true,
@@ -658930,16 +658983,15 @@ ${cachedResult}`,
658930
658983
  this.emit({
658931
658984
  type: "status",
658932
658985
  toolName: normalizedDispatchName,
658933
- content: fullSource ? `Exact unchanged file_read served as canonical full source: ${currentRead.path} sha256=${currentRead.contentHash.slice(0, 12)}` : `Exact unchanged file_read reused complete branch evidence: ${currentRead.path} sha256=${currentRead.contentHash.slice(0, 12)}`,
658986
+ content: `Unchanged file_read served from range-safe canonical source: ${currentRead.path} sha256=${currentRead.contentHash.slice(0, 12)}`,
658934
658987
  turn,
658935
658988
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
658936
658989
  });
658937
658990
  } else {
658938
- exactFileReadObservations.delete(exactReadKey);
658939
658991
  this.emit({
658940
658992
  type: "status",
658941
658993
  toolName: normalizedDispatchName,
658942
- content: `Exact read cache lacks model-visible source; executing requested read: ${currentRead.path}`,
658994
+ content: `Read cache has no body covering the requested range; executing requested read: ${currentRead.path}`,
658943
658995
  turn,
658944
658996
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
658945
658997
  });
@@ -659265,6 +659317,14 @@ ${diagnostic}`;
659265
659317
  const rLimit = typeof tc.arguments?.["limit"] === "number" ? tc.arguments["limit"] : void 0;
659266
659318
  const start3 = rOffset === void 0 && rLimit === void 0 ? 0 : Math.max(1, rOffset ?? 1);
659267
659319
  const end = rOffset === void 0 && rLimit === void 0 ? Number.POSITIVE_INFINITY : rLimit !== void 0 ? Math.max(1, rOffset ?? 1) + Math.max(0, rLimit) - 1 : Number.POSITIVE_INFINITY;
659320
+ const requestedWhole = start3 === 0;
659321
+ const reportedRange = result.artifactMaterialization?.sourceRange;
659322
+ const materializedRange = reportedRange && typeof reportedRange.endLine === "number" ? {
659323
+ startLine: reportedRange.startLine,
659324
+ endLine: reportedRange.endLine
659325
+ } : void 0;
659326
+ const effectiveStart = !requestedWhole && materializedRange ? materializedRange.startLine : start3;
659327
+ const effectiveEnd = !requestedWhole && materializedRange ? materializedRange.endLine : end;
659268
659328
  this._evidenceLedger.recordRead({
659269
659329
  path: this._normalizeEvidencePath(p2),
659270
659330
  content: result.output,
@@ -659283,8 +659343,8 @@ ${diagnostic}`;
659283
659343
  path: this._normalizeEvidencePath(p2),
659284
659344
  contentHash: result.beforeHash,
659285
659345
  sourceRange: {
659286
- startLine: start3 || 1,
659287
- endLine: end === Number.POSITIVE_INFINITY ? Number.MAX_SAFE_INTEGER : end
659346
+ startLine: effectiveStart || 1,
659347
+ endLine: effectiveEnd === Number.POSITIVE_INFINITY ? Number.MAX_SAFE_INTEGER : effectiveEnd
659288
659348
  },
659289
659349
  turn
659290
659350
  });
@@ -659295,8 +659355,14 @@ ${diagnostic}`;
659295
659355
  this._inlineReadArtifacts.delete(oldest);
659296
659356
  }
659297
659357
  }
659298
- if (result.partial !== true && result.artifactMaterialization?.status !== "partial") {
659299
- this._registerReadCoverage("file_read", tc.arguments, toolFingerprint);
659358
+ if (result.runtimeAuthored !== true && result.partial !== true && result.artifactMaterialization?.status !== "partial" && this._fileReadMode(tc.arguments) !== "extract") {
659359
+ const coverageArgs = !requestedWhole && materializedRange ? {
659360
+ ...tc.arguments,
659361
+ offset: effectiveStart,
659362
+ limit: Math.max(0, effectiveEnd - effectiveStart + 1)
659363
+ } : tc.arguments;
659364
+ const exactCoverageFingerprint = this._buildToolFingerprint("file_read", coverageArgs);
659365
+ this._registerReadCoverage("file_read", coverageArgs, this._resolveReadCoverageFingerprint("file_read", coverageArgs, exactCoverageFingerprint));
659300
659366
  }
659301
659367
  }
659302
659368
  if (this._fileSummaryStore && result.success && result.output && result.output.length > 100) {
@@ -678083,6 +678149,7 @@ __export(daemon_exports, {
678083
678149
  getDaemonStatus: () => getDaemonStatus,
678084
678150
  getLocalCliVersion: () => getLocalCliVersion,
678085
678151
  isDaemonRunning: () => isDaemonRunning,
678152
+ reclaimOwnedDaemonListener: () => reclaimOwnedDaemonListener,
678086
678153
  recoverDaemonVersionBoundedly: () => recoverDaemonVersionBoundedly,
678087
678154
  releaseDaemonEndpointClaim: () => releaseDaemonEndpointClaim,
678088
678155
  releaseDaemonEndpointForCurrentProcess: () => releaseDaemonEndpointForCurrentProcess,
@@ -678094,6 +678161,7 @@ import { spawn as spawn28 } from "node:child_process";
678094
678161
  import { existsSync as existsSync116, readFileSync as readFileSync94, writeFileSync as writeFileSync60, mkdirSync as mkdirSync69, unlinkSync as unlinkSync22, openSync as openSync4, closeSync as closeSync4, writeSync as writeSync3, statSync as statSync46, renameSync as renameSync17 } from "node:fs";
678095
678162
  import { join as join128 } from "node:path";
678096
678163
  import { homedir as homedir39 } from "node:os";
678164
+ import { createServer as createNetServer } from "node:net";
678097
678165
  import { fileURLToPath as fileURLToPath18 } from "node:url";
678098
678166
  import { dirname as dirname42 } from "node:path";
678099
678167
  function getDaemonPort() {
@@ -678353,55 +678421,105 @@ ${fuser.stderr ?? ""}`;
678353
678421
  return null;
678354
678422
  }
678355
678423
  }
678356
- function processCommandLine(pid) {
678357
- try {
678358
- return readFileSync94(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
678359
- } catch {
678360
- return "";
678361
- }
678362
- }
678363
- function isOwnedOmniusDaemon(pid, daemonPid) {
678364
- if (pid === daemonPid) return true;
678365
- if (listProcessLeases({ includeInactive: false }).some((lease) => lease.pid === pid && lease.ownerKind === "daemon")) {
678366
- return true;
678367
- }
678368
- const command = processCommandLine(pid);
678369
- return /(?:^|[\\/\s])omnius(?:[\\/\s]|$)|omnius-daemon|[\\/]dist[\\/]index\.js\b|launcher\.cjs\b/i.test(command);
678370
- }
678371
- async function ownedDaemonPids(port) {
678372
- const daemonPid = getDaemonPid();
678373
- const candidates = /* @__PURE__ */ new Set();
678374
- if (daemonPid) candidates.add(daemonPid);
678375
- const holders = await portHolderPids(port);
678376
- for (const pid of holders ?? []) candidates.add(pid);
678377
- return [...candidates].filter((pid) => processIsAlive(pid) && isOwnedOmniusDaemon(pid, daemonPid));
678424
+ async function daemonPortIsFree(port) {
678425
+ return new Promise((resolve87) => {
678426
+ const probe = createNetServer();
678427
+ let settled = false;
678428
+ const finish = (free) => {
678429
+ if (settled) return;
678430
+ settled = true;
678431
+ resolve87(free);
678432
+ };
678433
+ probe.once("error", () => finish(false));
678434
+ probe.once("listening", () => {
678435
+ probe.close(() => finish(true));
678436
+ });
678437
+ probe.listen(port, "127.0.0.1");
678438
+ probe.unref();
678439
+ });
678378
678440
  }
678379
678441
  async function waitForDaemonStopped(port, attempts = DAEMON_GRACEFUL_STOP_ATTEMPTS) {
678380
678442
  for (let attempt = 0; attempt < attempts; attempt++) {
678381
678443
  const healthy = await isDaemonRunning(port);
678382
678444
  const holders = await portHolderPids(port);
678383
- if (!healthy && (holders === null || holders.length === 0)) return true;
678445
+ const portFree = holders === null ? await daemonPortIsFree(port) : holders.length === 0;
678446
+ if (!healthy && portFree) return true;
678384
678447
  await delay3(500);
678385
678448
  }
678386
678449
  return false;
678387
678450
  }
678388
- async function gracefullyStopOwnedDaemon(port) {
678389
- const pids = await ownedDaemonPids(port);
678390
- for (const pid of pids) {
678391
- try {
678392
- process.kill(pid, "SIGTERM");
678393
- } catch {
678451
+ async function reclaimOwnedDaemonListener(port = getDaemonPort(), dependencies = DEFAULT_RECLAIM_DEPENDENCIES) {
678452
+ const holders = await dependencies.holderPids(port);
678453
+ if (holders === null) {
678454
+ if (await dependencies.portIsFree(port)) {
678455
+ return { ok: true, action: "free", port, clearedPids: [], blockedPids: [] };
678394
678456
  }
678457
+ return {
678458
+ ok: false,
678459
+ action: "blocked",
678460
+ port,
678461
+ clearedPids: [],
678462
+ blockedPids: [],
678463
+ reason: "Could not identify the process holding the daemon port"
678464
+ };
678395
678465
  }
678396
- if (await waitForDaemonStopped(port)) return true;
678397
- const survivors = await ownedDaemonPids(port);
678398
- for (const pid of survivors) {
678399
- try {
678400
- process.kill(pid, "SIGKILL");
678401
- } catch {
678466
+ const uniqueHolders = [...new Set(holders.filter((pid) => pid > 0 && pid !== process.pid))];
678467
+ if (uniqueHolders.length === 0) {
678468
+ if (await dependencies.portIsFree(port)) {
678469
+ return { ok: true, action: "free", port, clearedPids: [], blockedPids: [] };
678470
+ }
678471
+ return {
678472
+ ok: false,
678473
+ action: "blocked",
678474
+ port,
678475
+ clearedPids: [],
678476
+ blockedPids: [],
678477
+ reason: "Daemon port is occupied but its holder could not be verified"
678478
+ };
678479
+ }
678480
+ const ownerId = `daemon:${port}`;
678481
+ const activeLeases = dependencies.leases().filter(
678482
+ (lease) => lease.status === "active" && lease.ownerKind === "daemon" && lease.ownerId === ownerId && uniqueHolders.includes(lease.pid)
678483
+ );
678484
+ const leasesByPid = new Map(activeLeases.map((lease) => [lease.pid, lease]));
678485
+ const blockedPids = uniqueHolders.filter((pid) => !leasesByPid.has(pid));
678486
+ if (blockedPids.length > 0) {
678487
+ return {
678488
+ ok: false,
678489
+ action: "blocked",
678490
+ port,
678491
+ clearedPids: [],
678492
+ blockedPids,
678493
+ reason: "Port holder is not an identity-verified Omnius daemon lease"
678494
+ };
678495
+ }
678496
+ const clearedPids = [];
678497
+ for (const pid of uniqueHolders) {
678498
+ const lease = leasesByPid.get(pid);
678499
+ const stopped = await dependencies.stopLease(lease.leaseId);
678500
+ if (stopped.action !== "killed" && stopped.action !== "dead") {
678501
+ return {
678502
+ ok: false,
678503
+ action: "failed",
678504
+ port,
678505
+ clearedPids,
678506
+ blockedPids: [pid],
678507
+ reason: stopped.reason || "Daemon lease identity could not be verified"
678508
+ };
678402
678509
  }
678510
+ clearedPids.push(pid);
678403
678511
  }
678404
- return waitForDaemonStopped(port, 10);
678512
+ if (!await dependencies.waitForFree(port)) {
678513
+ return {
678514
+ ok: false,
678515
+ action: "failed",
678516
+ port,
678517
+ clearedPids,
678518
+ blockedPids: [],
678519
+ reason: "Verified daemon stopped but the port did not become free"
678520
+ };
678521
+ }
678522
+ return { ok: true, action: "cleared", port, clearedPids, blockedPids: [] };
678405
678523
  }
678406
678524
  async function restartDaemon(port, expectedVersion) {
678407
678525
  const p2 = port ?? getDaemonPort();
@@ -678417,9 +678535,15 @@ async function restartDaemon(port, expectedVersion) {
678417
678535
  if (started.ok && (await waitForDaemonReady(p2, expectedVersion)).ok) return true;
678418
678536
  }
678419
678537
  await runUserSystemctl(["stop", "omnius-daemon.service"]);
678420
- if (!await waitForDaemonStopped(p2)) return false;
678538
+ if (!await waitForDaemonStopped(p2)) {
678539
+ const reclaimed = await reclaimOwnedDaemonListener(p2);
678540
+ if (!reclaimed.ok) return false;
678541
+ }
678542
+ }
678543
+ if (await isDaemonRunning(p2)) {
678544
+ const reclaimed = await reclaimOwnedDaemonListener(p2);
678545
+ if (!reclaimed.ok) return false;
678421
678546
  }
678422
- if (await isDaemonRunning(p2) && !await gracefullyStopOwnedDaemon(p2)) return false;
678423
678547
  const pid = await startDaemon(p2);
678424
678548
  if (!pid) return false;
678425
678549
  return (await waitForDaemonReady(p2, expectedVersion)).ok;
@@ -678496,7 +678620,7 @@ async function startDaemon(port = getDaemonPort()) {
678496
678620
  outFd = openSync4(join128(OMNIUS_DIR, "daemon.log"), "a");
678497
678621
  errFd = openSync4(join128(OMNIUS_DIR, "daemon.err.log"), "a");
678498
678622
  const leaseId = newProcessLeaseId("daemon");
678499
- const ownerId = `daemon:${getDaemonPort()}`;
678623
+ const ownerId = `daemon:${daemonPort}`;
678500
678624
  const child = spawn28(daemonCommand.command, daemonCommand.args, {
678501
678625
  detached: true,
678502
678626
  stdio: ["ignore", outFd, errFd],
@@ -678670,17 +678794,17 @@ async function ensureDaemonVersion(expectedVersion = getLocalCliVersion(), port
678670
678794
  }
678671
678795
  return finish(true, "unchanged", await getDaemonReportedVersion(port));
678672
678796
  }
678673
- const stalePid = getDaemonPid();
678674
- if (stalePid) {
678675
- try {
678676
- process.kill(stalePid, "SIGTERM");
678677
- } catch {
678678
- }
678679
- try {
678680
- unlinkSync22(PID_FILE);
678681
- } catch {
678682
- }
678797
+ const reclaimed = await reclaimOwnedDaemonListener(port);
678798
+ if (!reclaimed.ok) {
678799
+ return finish(
678800
+ false,
678801
+ "failed",
678802
+ null,
678803
+ 0,
678804
+ process.platform === "linux" && reclaimed.action === "blocked"
678805
+ );
678683
678806
  }
678807
+ getDaemonPid();
678684
678808
  const pid = await startDaemon(port);
678685
678809
  if (!pid) {
678686
678810
  for (let i2 = 0; i2 < 20; i2++) {
@@ -678728,7 +678852,7 @@ async function getDaemonStatus() {
678728
678852
  }
678729
678853
  return { running, pid, port, uptime: uptime2, pidFile: PID_FILE };
678730
678854
  }
678731
- var OMNIUS_DIR, PID_FILE, DEFAULT_PORT2, LOCK_INITIALIZATION_GRACE_MS, DAEMON_VERSION_RECOVERY_ATTEMPTS, DAEMON_GRACEFUL_STOP_ATTEMPTS;
678855
+ var OMNIUS_DIR, PID_FILE, DEFAULT_PORT2, LOCK_INITIALIZATION_GRACE_MS, DAEMON_VERSION_RECOVERY_ATTEMPTS, DAEMON_GRACEFUL_STOP_ATTEMPTS, DEFAULT_RECLAIM_DEPENDENCIES;
678732
678856
  var init_daemon = __esm({
678733
678857
  "packages/cli/src/daemon.ts"() {
678734
678858
  init_dist5();
@@ -678738,6 +678862,16 @@ var init_daemon = __esm({
678738
678862
  LOCK_INITIALIZATION_GRACE_MS = 5e3;
678739
678863
  DAEMON_VERSION_RECOVERY_ATTEMPTS = 2;
678740
678864
  DAEMON_GRACEFUL_STOP_ATTEMPTS = 20;
678865
+ DEFAULT_RECLAIM_DEPENDENCIES = {
678866
+ holderPids: (port) => portHolderPids(port),
678867
+ portIsFree: (port) => daemonPortIsFree(port),
678868
+ leases: () => listProcessLeases({ includeInactive: false }),
678869
+ stopLease: (leaseId) => stopProcessLease(leaseId, {
678870
+ reason: "stale daemon listener reclaim",
678871
+ termGraceMs: 1e3
678872
+ }),
678873
+ waitForFree: (port) => waitForDaemonStopped(port)
678874
+ };
678741
678875
  }
678742
678876
  });
678743
678877
 
@@ -678757,6 +678891,7 @@ __export(tray_exports, {
678757
678891
  runTrayForeground: () => runTrayForeground,
678758
678892
  startTray: () => startTray,
678759
678893
  stopTray: () => stopTray,
678894
+ trayRequiresRestart: () => trayRequiresRestart,
678760
678895
  traySupport: () => traySupport,
678761
678896
  trayUpdatePresentation: () => trayUpdatePresentation,
678762
678897
  uninstallTrayAutostart: () => uninstallTrayAutostart,
@@ -679149,7 +679284,7 @@ start "" /b ${argv.map(windowsCmdQuote).join(" ")}\r
679149
679284
  }
679150
679285
  throw new Error(`Autostart is not supported on ${platform8}`);
679151
679286
  }
679152
- function installTrayAutostart(endpoint) {
679287
+ function writeTrayAutostart(endpoint) {
679153
679288
  const launch = resolveTrayLaunchCommand();
679154
679289
  if (!launch) throw new Error("Could not resolve the installed Omnius CLI entrypoint");
679155
679290
  const paths = resolveTrayPaths();
@@ -679159,11 +679294,15 @@ function installTrayAutostart(endpoint) {
679159
679294
  buildAutostartContent(process.platform, launch, endpoint, assetPath("online")),
679160
679295
  { encoding: "utf8", mode: process.platform === "win32" ? 448 : 384 }
679161
679296
  );
679297
+ return paths.autostartFile;
679298
+ }
679299
+ function installTrayAutostart(endpoint) {
679300
+ const autostartFile = writeTrayAutostart(endpoint);
679162
679301
  if (process.platform === "darwin" && typeof process.getuid === "function") {
679163
679302
  spawnSync9("launchctl", ["bootout", `gui/${process.getuid()}/ai.omnius.tray`], { stdio: "ignore" });
679164
- spawnSync9("launchctl", ["bootstrap", `gui/${process.getuid()}`, paths.autostartFile], { stdio: "ignore" });
679303
+ spawnSync9("launchctl", ["bootstrap", `gui/${process.getuid()}`, autostartFile], { stdio: "ignore" });
679165
679304
  }
679166
- return paths.autostartFile;
679305
+ return autostartFile;
679167
679306
  }
679168
679307
  function uninstallTrayAutostart() {
679169
679308
  const paths = resolveTrayPaths();
@@ -679295,11 +679434,15 @@ async function getTrayStatus(explicitEndpoint) {
679295
679434
  }
679296
679435
  const state = readProcessState(paths.stateFile);
679297
679436
  const support = traySupport();
679437
+ const requestedEndpoint = resolveTrayEndpoint(explicitEndpoint);
679298
679438
  return {
679299
679439
  running,
679300
679440
  ready: running && Boolean(state?.ready),
679301
679441
  pid: running ? pid : null,
679302
- endpoint: state?.endpoint || resolveTrayEndpoint(explicitEndpoint),
679442
+ // A stopped indicator's cached state must not override an explicitly
679443
+ // requested endpoint. This previously kept resurrecting an obsolete
679444
+ // custom port (for example 11535) after the daemon had returned to 11435.
679445
+ endpoint: running || !explicitEndpoint ? state?.endpoint || requestedEndpoint : requestedEndpoint,
679303
679446
  registered: existsSync117(paths.autostartFile),
679304
679447
  registrationFile: paths.autostartFile,
679305
679448
  ...state?.health ? { health: state.health } : {},
@@ -679308,16 +679451,26 @@ async function getTrayStatus(explicitEndpoint) {
679308
679451
  ...state?.error ? { error: state.error } : {}
679309
679452
  };
679310
679453
  }
679454
+ function trayRequiresRestart(existing, endpoint, liveHealth) {
679455
+ return existing.endpoint !== endpoint || !existing.ready || Boolean(existing.error) || existing.health?.kind !== liveHealth.kind || existing.health?.version !== liveHealth.version;
679456
+ }
679311
679457
  async function startTray(explicitEndpoint) {
679312
- const existing = await getTrayStatus(explicitEndpoint);
679313
- if (existing.running) return existing;
679458
+ const endpoint = resolveTrayEndpoint(explicitEndpoint);
679459
+ const existing = await getTrayStatus(endpoint);
679460
+ if (existing.running) {
679461
+ const liveHealth = await pollTrayHealth(endpoint);
679462
+ if (!trayRequiresRestart(existing, endpoint, liveHealth)) {
679463
+ return { ...existing, health: liveHealth };
679464
+ }
679465
+ await stopTray();
679466
+ }
679314
679467
  const support = traySupport();
679315
679468
  if (!support.supported) throw new Error(support.reason || "Tray is not supported on this host");
679316
679469
  verifyTrayHelper();
679317
679470
  const launch = resolveTrayLaunchCommand();
679318
679471
  if (!launch) throw new Error("Could not resolve the installed Omnius CLI entrypoint");
679319
- const endpoint = resolveTrayEndpoint(explicitEndpoint);
679320
679472
  const paths = resolveTrayPaths();
679473
+ if (existing.registered) writeTrayAutostart(endpoint);
679321
679474
  mkdirSync70(paths.stateDir, { recursive: true, mode: 448 });
679322
679475
  mkdirSync70(paths.runtimeDir, { recursive: true, mode: 448 });
679323
679476
  try {
@@ -724408,7 +724561,15 @@ var indicator_command_exports = {};
724408
724561
  __export(indicator_command_exports, {
724409
724562
  runIndicatorCommand: () => runIndicatorCommand
724410
724563
  });
724411
- function formatIndicatorStatus(status) {
724564
+ async function waitForOnlineHealth(endpoint, operations, attempts = 5) {
724565
+ let health = await operations.health(endpoint);
724566
+ for (let attempt = 1; health.kind !== "online" && attempt < attempts; attempt++) {
724567
+ await operations.wait(200);
724568
+ health = await operations.health(endpoint);
724569
+ }
724570
+ return health;
724571
+ }
724572
+ function formatIndicatorStatus(status, daemon) {
724412
724573
  const state = status.running ? status.ready ? "ready" : "starting" : "stopped";
724413
724574
  const pid = status.pid ? ` (PID ${status.pid})` : "";
724414
724575
  const health = status.health?.label ?? "not checked";
@@ -724419,6 +724580,12 @@ function formatIndicatorStatus(status) {
724419
724580
  `Endpoint: ${status.endpoint}.`,
724420
724581
  `Autostart: ${autostart}.`
724421
724582
  ];
724583
+ if (daemon) {
724584
+ const version5 = daemon.observedVersion ?? daemon.expectedVersion;
724585
+ parts.push(
724586
+ `Daemon: ${daemon.action}${version5 ? ` (v${version5})` : ""} on port ${daemon.port}.`
724587
+ );
724588
+ }
724422
724589
  if (status.error) parts.push(`Error: ${status.error}`);
724423
724590
  return parts.join(" ");
724424
724591
  }
@@ -724431,9 +724598,42 @@ async function runIndicatorCommand(rawAction, operations = DEFAULT_OPERATIONS) {
724431
724598
  };
724432
724599
  }
724433
724600
  try {
724434
- const status = action === "status" ? await operations.status() : await operations.start();
724435
- const level = status.error ? "error" : status.running && status.ready ? "info" : status.running ? "warning" : action === "start" ? "error" : "warning";
724436
- return { level, message: formatIndicatorStatus(status), status };
724601
+ if (action === "status") {
724602
+ const cached2 = await operations.status();
724603
+ const health2 = await operations.health(cached2.endpoint);
724604
+ const status2 = { ...cached2, health: health2 };
724605
+ const level2 = status2.error ? "error" : status2.running && status2.ready && health2.kind === "online" ? "info" : "warning";
724606
+ return { level: level2, message: formatIndicatorStatus(status2), status: status2 };
724607
+ }
724608
+ const daemon = await operations.ensureDaemon();
724609
+ if (!daemon.ok) {
724610
+ const observed = daemon.observedVersion ? `; observed v${daemon.observedVersion}` : "";
724611
+ const migration = daemon.requiresPrivilegedMigration ? " Privileged service migration is required; run /daemon takeover." : "";
724612
+ return {
724613
+ level: "error",
724614
+ message: `Indicator not started: daemon reconciliation failed on port ${daemon.port}${observed}.${migration}`,
724615
+ daemon
724616
+ };
724617
+ }
724618
+ const endpoint = `http://127.0.0.1:${daemon.port}`;
724619
+ const daemonHealth = await waitForOnlineHealth(endpoint, operations);
724620
+ if (daemonHealth.kind !== "online") {
724621
+ return {
724622
+ level: "error",
724623
+ message: `Indicator not started: daemon did not become online at ${endpoint}. ` + daemonHealth.tooltip,
724624
+ daemon
724625
+ };
724626
+ }
724627
+ const started = await operations.start(endpoint);
724628
+ const health = await waitForOnlineHealth(endpoint, operations);
724629
+ const status = { ...started, endpoint, health };
724630
+ const level = status.error ? "error" : status.running && status.ready && health.kind === "online" ? "info" : "error";
724631
+ return {
724632
+ level,
724633
+ message: formatIndicatorStatus(status, daemon),
724634
+ status,
724635
+ daemon
724636
+ };
724437
724637
  } catch (error) {
724438
724638
  return {
724439
724639
  level: "error",
@@ -724445,9 +724645,13 @@ var DEFAULT_OPERATIONS;
724445
724645
  var init_indicator_command = __esm({
724446
724646
  "packages/cli/src/tui/indicator-command.ts"() {
724447
724647
  init_tray();
724648
+ init_daemon();
724448
724649
  DEFAULT_OPERATIONS = {
724449
- start: () => startTray(),
724450
- status: () => getTrayStatus()
724650
+ ensureDaemon: () => ensureDaemonVersion(),
724651
+ start: (endpoint) => startTray(endpoint),
724652
+ status: () => getTrayStatus(),
724653
+ health: (endpoint) => pollTrayHealth(endpoint),
724654
+ wait: (ms) => new Promise((resolve87) => setTimeout(resolve87, ms))
724451
724655
  };
724452
724656
  }
724453
724657
  });
@@ -25,6 +25,12 @@ From an active Omnius TUI, use the local slash command instead:
25
25
  start/stop/restart lifecycle remain available through the top-level
26
26
  `omnius tray` command.
27
27
 
28
+ `/indicator` first reconciles the current Omnius daemon on its configured
29
+ loopback port (11435 by default), waits for verified online health, repairs a
30
+ stale tray endpoint/autostart registration, and only then starts the native
31
+ indicator. Untracked processes occupying the port are reported as blockers and
32
+ are never terminated automatically.
33
+
28
34
  The indicator polls the daemon's loopback-only `GET /health` route. It does not
29
35
  load a model or contact the configured inference backend. If the daemon uses a
30
36
  non-default port, Omnius discovers `OMNIUS_HOST`/`OMNIUS_PORT` from the current
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.598",
3
+ "version": "1.0.600",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.598",
9
+ "version": "1.0.600",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
@@ -7675,9 +7675,9 @@
7675
7675
  "license": "ISC"
7676
7676
  },
7677
7677
  "node_modules/ws": {
7678
- "version": "8.21.1",
7679
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
7680
- "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
7678
+ "version": "8.21.2",
7679
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.2.tgz",
7680
+ "integrity": "sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==",
7681
7681
  "license": "MIT",
7682
7682
  "engines": {
7683
7683
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.598",
3
+ "version": "1.0.600",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/library.js",