omnius 1.0.698 → 1.0.699

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
@@ -68841,7 +68841,7 @@ var init_playwright_browser = __esm({
68841
68841
  if (!["web_serial", "web_usb", "camera", "microphone"].includes(capability)) {
68842
68842
  return fail("hardware_capability must be one of web_serial, web_usb, camera, or microphone", start2);
68843
68843
  }
68844
- const availability = await browserState().page.evaluate((requested) => {
68844
+ const availability2 = await browserState().page.evaluate((requested) => {
68845
68845
  const runtimeNavigator = navigator;
68846
68846
  const hasApi = requested === "web_serial" ? Boolean(runtimeNavigator.serial) : requested === "web_usb" ? Boolean(runtimeNavigator.usb) : Boolean(runtimeNavigator.mediaDevices);
68847
68847
  return {
@@ -68851,7 +68851,7 @@ var init_playwright_browser = __esm({
68851
68851
  };
68852
68852
  }, capability);
68853
68853
  const prerequisite = capability === "web_serial" ? "a physical serial device plus a secure-context user gesture" : capability === "web_usb" ? "a physical USB device plus a secure-context user gesture" : `a granted ${capability} permission and a real capture device`;
68854
- const reason = availability.hasApi && availability.secureContext ? `${capability} API is exposed, but this session has not selected or been granted ${prerequisite}.` : `${capability} cannot be exercised: ${availability.hasApi ? "the page is not a secure context" : "the browser API is unavailable"}; it still requires ${prerequisite}.`;
68854
+ const reason = availability2.hasApi && availability2.secureContext ? `${capability} API is exposed, but this session has not selected or been granted ${prerequisite}.` : `${capability} cannot be exercised: ${availability2.hasApi ? "the page is not a secure context" : "the browser API is unavailable"}; it still requires ${prerequisite}.`;
68855
68855
  const event = createEvidenceEvent({
68856
68856
  sessionId: browserState().playwrightSessionId ?? void 0,
68857
68857
  tool: "playwright_browser",
@@ -68867,9 +68867,9 @@ var init_playwright_browser = __esm({
68867
68867
  },
68868
68868
  metadata: {
68869
68869
  capability,
68870
- secureContext: availability.secureContext,
68871
- browserApiAvailable: availability.hasApi,
68872
- userActivation: availability.userActivation,
68870
+ secureContext: availability2.secureContext,
68871
+ browserApiAvailable: availability2.hasApi,
68872
+ userActivation: availability2.userActivation,
68873
68873
  prerequisite
68874
68874
  }
68875
68875
  });
@@ -68880,9 +68880,9 @@ var init_playwright_browser = __esm({
68880
68880
  `capability=${capability}`,
68881
68881
  "outcome=blocked",
68882
68882
  `reason=${reason}`,
68883
- `secure_context=${availability.secureContext}`,
68884
- `browser_api_available=${availability.hasApi}`,
68885
- `user_activation=${availability.userActivation}`,
68883
+ `secure_context=${availability2.secureContext}`,
68884
+ `browser_api_available=${availability2.hasApi}`,
68885
+ `user_activation=${availability2.userActivation}`,
68886
68886
  `next=${event.verification?.nextSuggestedObservation}`
68887
68887
  ].join("\n"),
68888
68888
  evidenceEvents: [event],
@@ -74084,118 +74084,168 @@ var init_memoryGraph = __esm({
74084
74084
  });
74085
74085
 
74086
74086
  // packages/memory/dist/embeddings.js
74087
+ function canonicalModel(model) {
74088
+ const name10 = model.trim();
74089
+ return name10.slice(name10.lastIndexOf("/") + 1).includes(":") ? name10 : `${name10}:latest`;
74090
+ }
74091
+ function resolveConfig(config) {
74092
+ const configuredModel = config?.model ?? (process.env["OMNIUS_EMBEDDING_MODEL"]?.trim() || process.env["EMBED_MODEL"]?.trim() || "nomic-embed-text");
74093
+ const model = configuredModel.trim();
74094
+ if (!model)
74095
+ throw new Error("Embedding model must be nonempty");
74096
+ const endpoint = new URL(config?.baseUrl ?? "http://localhost:11434");
74097
+ endpoint.pathname = endpoint.pathname.replace(/\/+$/, "");
74098
+ if (endpoint.search || endpoint.hash || !["http:", "https:"].includes(endpoint.protocol)) {
74099
+ throw new Error("Invalid embedding endpoint");
74100
+ }
74101
+ const timeoutMs = config?.timeoutMs ?? 3e4;
74102
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0)
74103
+ throw new Error("Invalid embedding timeout");
74104
+ return { baseUrl: endpoint.toString().replace(/\/+$/, ""), model, timeoutMs, signal: config?.signal };
74105
+ }
74087
74106
  function embeddingRequestSignal(timeoutMs, external) {
74088
74107
  const timeout2 = AbortSignal.timeout(timeoutMs);
74089
74108
  return external ? AbortSignal.any([external, timeout2]) : timeout2;
74090
74109
  }
74091
- async function generateEmbedding(text5, config) {
74092
- const cfg = { ...DEFAULT_CONFIG3, ...config };
74093
- if (cfg.signal?.aborted)
74094
- return null;
74110
+ function availabilityEntry(cfg) {
74111
+ const key2 = JSON.stringify([cfg.baseUrl, canonicalModel(cfg.model)]);
74112
+ let entry = availability.get(key2);
74113
+ if (!entry) {
74114
+ if (availability.size >= MAX_AVAILABILITY_ENTRIES)
74115
+ availability.delete(availability.keys().next().value);
74116
+ entry = { blockedUntil: 0, readyUntil: 0 };
74117
+ }
74118
+ availability.delete(key2);
74119
+ availability.set(key2, entry);
74120
+ return entry;
74121
+ }
74122
+ async function waitForAvailability(pending2, signal) {
74123
+ signal.throwIfAborted();
74124
+ let onAbort = () => {
74125
+ };
74095
74126
  try {
74096
- const url = `${cfg.baseUrl}/api/embed`;
74097
- const resp = await fetch(url, {
74127
+ await Promise.race([
74128
+ pending2,
74129
+ new Promise((_resolve, reject) => {
74130
+ onAbort = () => reject(signal.reason);
74131
+ signal.addEventListener("abort", onAbort, { once: true });
74132
+ })
74133
+ ]);
74134
+ signal.throwIfAborted();
74135
+ } finally {
74136
+ signal.removeEventListener("abort", onAbort);
74137
+ }
74138
+ }
74139
+ async function requestEmbeddings(texts, config, single) {
74140
+ const unavailable = () => texts.map(() => null);
74141
+ if (texts.length === 0 || config?.signal?.aborted)
74142
+ return unavailable();
74143
+ let finishDiscovery;
74144
+ let entry;
74145
+ try {
74146
+ const cfg = resolveConfig(config);
74147
+ const signal = embeddingRequestSignal(cfg.timeoutMs * (single ? 1 : 2), cfg.signal);
74148
+ entry = availabilityEntry(cfg);
74149
+ while (true) {
74150
+ signal.throwIfAborted();
74151
+ if (entry.blockedUntil > Date.now())
74152
+ return unavailable();
74153
+ if (entry.pending) {
74154
+ await waitForAvailability(entry.pending, signal);
74155
+ continue;
74156
+ }
74157
+ if (entry.readyUntil <= Date.now()) {
74158
+ entry.pending = new Promise((resolve108) => {
74159
+ finishDiscovery = resolve108;
74160
+ });
74161
+ }
74162
+ break;
74163
+ }
74164
+ const resp = await fetch(`${cfg.baseUrl}/api/embed`, {
74098
74165
  method: "POST",
74099
74166
  headers: { "Content-Type": "application/json" },
74100
- body: JSON.stringify({
74101
- model: cfg.model,
74102
- input: text5
74103
- }),
74104
- signal: embeddingRequestSignal(cfg.timeoutMs, cfg.signal)
74167
+ body: JSON.stringify({ model: cfg.model, input: single ? texts[0] : texts }),
74168
+ signal
74105
74169
  });
74170
+ signal.throwIfAborted();
74106
74171
  if (!resp.ok) {
74107
74172
  if (resp.status === 404) {
74108
- const pulled = await pullEmbeddingModel(cfg.baseUrl, cfg.model, cfg.timeoutMs, cfg.signal);
74109
- if (!pulled)
74110
- return null;
74111
- return generateEmbedding(text5, config);
74173
+ entry.blockedUntil = Date.now() + AVAILABILITY_TTL_MS;
74174
+ entry.readyUntil = 0;
74112
74175
  }
74113
- return null;
74176
+ await resp.body?.cancel();
74177
+ return unavailable();
74114
74178
  }
74115
74179
  const data = await resp.json();
74116
- const rawVector = data.embeddings?.[0] ?? data.embedding;
74117
- if (!rawVector || rawVector.length === 0)
74118
- return null;
74119
- return {
74120
- vector: new Float32Array(rawVector),
74121
- model: cfg.model,
74122
- dimensions: rawVector.length
74123
- };
74180
+ signal.throwIfAborted();
74181
+ if (!data || typeof data !== "object")
74182
+ return unavailable();
74183
+ const payload = data;
74184
+ const raw = payload.embeddings ?? (single && payload.embedding ? [payload.embedding] : void 0);
74185
+ if (!Array.isArray(raw) || raw.length !== texts.length)
74186
+ return unavailable();
74187
+ const vectors = [];
74188
+ for (const vector of raw) {
74189
+ if (!Array.isArray(vector) || vector.length === 0 || vector.some((value2) => typeof value2 !== "number" || !Number.isFinite(value2)))
74190
+ return unavailable();
74191
+ const typed = new Float32Array(vector);
74192
+ if (typed.some((value2) => !Number.isFinite(value2)) || !typed.some((value2) => value2 !== 0) || vectors[0] && typed.length !== vectors[0].length)
74193
+ return unavailable();
74194
+ vectors.push(typed);
74195
+ }
74196
+ entry.blockedUntil = 0;
74197
+ entry.readyUntil = Date.now() + AVAILABILITY_TTL_MS;
74198
+ return vectors.map((vector) => ({ vector, model: cfg.model, dimensions: vector.length }));
74124
74199
  } catch {
74125
- return null;
74200
+ return unavailable();
74201
+ } finally {
74202
+ if (finishDiscovery) {
74203
+ entry.pending = void 0;
74204
+ finishDiscovery();
74205
+ }
74126
74206
  }
74127
74207
  }
74208
+ async function generateEmbedding(text5, config) {
74209
+ return (await requestEmbeddings([text5], config, true))[0] ?? null;
74210
+ }
74128
74211
  async function generateEmbeddingBatch(texts, config) {
74129
- const cfg = { ...DEFAULT_CONFIG3, ...config };
74130
- if (cfg.signal?.aborted)
74131
- return texts.map(() => null);
74132
- try {
74133
- const url = `${cfg.baseUrl}/api/embed`;
74134
- const resp = await fetch(url, {
74135
- method: "POST",
74136
- headers: { "Content-Type": "application/json" },
74137
- body: JSON.stringify({
74138
- model: cfg.model,
74139
- input: texts
74140
- }),
74141
- signal: embeddingRequestSignal(cfg.timeoutMs * 2, cfg.signal)
74142
- // longer timeout for batch
74143
- });
74144
- if (!resp.ok)
74145
- return texts.map(() => null);
74146
- const data = await resp.json();
74147
- if (!data.embeddings)
74148
- return texts.map(() => null);
74149
- return data.embeddings.map((vec) => vec && vec.length > 0 ? {
74150
- vector: new Float32Array(vec),
74151
- model: cfg.model,
74152
- dimensions: vec.length
74153
- } : null);
74154
- } catch {
74155
- return texts.map(() => null);
74156
- }
74212
+ return requestEmbeddings(texts, config, false);
74157
74213
  }
74158
74214
  async function checkEmbeddingAvailable(config) {
74159
- const cfg = { ...DEFAULT_CONFIG3, ...config };
74160
- if (cfg.signal?.aborted)
74215
+ if (config?.signal?.aborted)
74161
74216
  return false;
74162
74217
  try {
74163
- const resp = await fetch(`${cfg.baseUrl}/api/tags`, {
74164
- signal: embeddingRequestSignal(5e3, cfg.signal)
74165
- });
74166
- if (!resp.ok)
74218
+ const cfg = resolveConfig(config);
74219
+ const signal = embeddingRequestSignal(5e3, cfg.signal);
74220
+ const resp = await fetch(`${cfg.baseUrl}/api/tags`, { signal });
74221
+ if (!resp.ok) {
74222
+ await resp.body?.cancel();
74167
74223
  return false;
74224
+ }
74168
74225
  const data = await resp.json();
74169
- return data.models?.some((m2) => m2.name.startsWith(cfg.model)) ?? false;
74170
- } catch {
74171
- return false;
74172
- }
74173
- }
74174
- async function pullEmbeddingModel(baseUrl3, model, timeoutMs, signal) {
74175
- if (signal?.aborted)
74176
- return false;
74177
- try {
74178
- const resp = await fetch(`${baseUrl3}/api/pull`, {
74179
- method: "POST",
74180
- headers: { "Content-Type": "application/json" },
74181
- body: JSON.stringify({ name: model, stream: false }),
74182
- signal: embeddingRequestSignal(timeoutMs * 10, signal)
74183
- // pulling can take minutes
74226
+ signal.throwIfAborted();
74227
+ if (!data || typeof data !== "object")
74228
+ return false;
74229
+ const models = data.models;
74230
+ if (!Array.isArray(models))
74231
+ return false;
74232
+ return models.some((item) => {
74233
+ if (!item || typeof item !== "object")
74234
+ return false;
74235
+ const name10 = item.name;
74236
+ return typeof name10 === "string" && canonicalModel(name10) === canonicalModel(cfg.model);
74184
74237
  });
74185
- return resp.ok;
74186
74238
  } catch {
74187
74239
  return false;
74188
74240
  }
74189
74241
  }
74190
- var DEFAULT_CONFIG3;
74242
+ var AVAILABILITY_TTL_MS, MAX_AVAILABILITY_ENTRIES, availability;
74191
74243
  var init_embeddings = __esm({
74192
74244
  "packages/memory/dist/embeddings.js"() {
74193
74245
  "use strict";
74194
- DEFAULT_CONFIG3 = {
74195
- baseUrl: "http://localhost:11434",
74196
- model: "nomic-embed-text",
74197
- timeoutMs: 3e4
74198
- };
74246
+ AVAILABILITY_TTL_MS = 3e4;
74247
+ MAX_AVAILABILITY_ENTRIES = 128;
74248
+ availability = /* @__PURE__ */ new Map();
74199
74249
  }
74200
74250
  });
74201
74251
 
@@ -74247,7 +74297,18 @@ var init_memoryIngestion = __esm({
74247
74297
  let graphNodes = 0;
74248
74298
  let graphEdges = 0;
74249
74299
  if (input.embedNow) {
74250
- embeddingsStored = await this.embedChunks(chunks);
74300
+ const result = await this.embedChunks(chunks);
74301
+ embeddingsStored = result.stored;
74302
+ if (input.queueEmbedding !== false) {
74303
+ for (const chunk of result.missing) {
74304
+ this.store.enqueueJob({
74305
+ jobType: "embed_chunk",
74306
+ itemId: chunk.itemId,
74307
+ chunkId: chunk.id,
74308
+ payload: { embeddingSetSlug: this.store.getDefaultEmbeddingSet().slug }
74309
+ });
74310
+ }
74311
+ }
74251
74312
  }
74252
74313
  if (input.maintainGraphNow) {
74253
74314
  const graph = maintainMemoryGraph(this.store, item);
@@ -74279,7 +74340,11 @@ var init_memoryIngestion = __esm({
74279
74340
  const chunk = this.store.getChunk(job.chunkId);
74280
74341
  if (!chunk)
74281
74342
  throw new Error(`Missing chunk ${job.chunkId}`);
74282
- embeddedChunks += await this.embedChunks([chunk], String(job.payload["embeddingSetSlug"] ?? "default-full"));
74343
+ const result = await this.embedChunks([chunk], String(job.payload["embeddingSetSlug"] ?? this.store.getDefaultEmbeddingSet().slug));
74344
+ embeddedChunks += result.stored;
74345
+ if (result.missing.length > 0) {
74346
+ throw new Error("Embedding unavailable or invalid; no vector was stored for the pending chunk");
74347
+ }
74283
74348
  } else if (job.jobType === "maintain_graph") {
74284
74349
  if (!job.itemId)
74285
74350
  throw new Error("Graph job missing item_id");
@@ -74298,7 +74363,7 @@ var init_memoryIngestion = __esm({
74298
74363
  }
74299
74364
  return { claimed: jobs.length, completed, failed, embeddedChunks, graphItems };
74300
74365
  }
74301
- async refreshEmbeddings(limit2 = 200, embeddingSetSlug = "default-full") {
74366
+ async refreshEmbeddings(limit2 = 200, embeddingSetSlug = this.store.getDefaultEmbeddingSet().slug) {
74302
74367
  const chunks = this.store.chunksMissingEmbedding(embeddingSetSlug, limit2);
74303
74368
  for (const chunk of chunks) {
74304
74369
  this.store.enqueueJob({
@@ -74310,23 +74375,28 @@ var init_memoryIngestion = __esm({
74310
74375
  }
74311
74376
  return { queued: chunks.length };
74312
74377
  }
74313
- async embedChunks(chunks, embeddingSetSlug = "default-full") {
74378
+ async embedChunks(chunks, embeddingSetSlug = this.store.getDefaultEmbeddingSet().slug) {
74314
74379
  if (chunks.length === 0)
74315
- return 0;
74380
+ return { stored: 0, missing: [] };
74316
74381
  const batch2 = chunks.length > 1 ? await generateEmbeddingBatch(chunks.map((chunk) => chunk.text), this.options.embedding) : [await generateEmbedding(chunks[0].text, this.options.embedding)];
74317
74382
  let stored = 0;
74383
+ const missing2 = [];
74318
74384
  for (let index = 0; index < chunks.length; index++) {
74319
74385
  const result = batch2[index];
74320
74386
  const chunk = chunks[index];
74321
- if (!result || !chunk)
74387
+ if (!chunk)
74388
+ continue;
74389
+ if (!result) {
74390
+ missing2.push(chunk);
74322
74391
  continue;
74392
+ }
74323
74393
  this.store.storeChunkEmbedding(chunk.id, result.vector, {
74324
74394
  embeddingSetSlug,
74325
74395
  model: result.model
74326
74396
  });
74327
74397
  stored++;
74328
74398
  }
74329
- return stored;
74399
+ return { stored, missing: missing2 };
74330
74400
  }
74331
74401
  handleJobFailure(job, error) {
74332
74402
  const maxAttempts = Math.max(1, this.options.maxAttempts ?? 3);
@@ -75197,7 +75267,7 @@ function findNeighbors(episode, candidates, topK, minSimilarity) {
75197
75267
  return scored.slice(0, topK);
75198
75268
  }
75199
75269
  function linkEpisode(episode, episodeStore, graph, config) {
75200
- const cfg = { ...DEFAULT_CONFIG4, ...config };
75270
+ const cfg = { ...DEFAULT_CONFIG3, ...config };
75201
75271
  const result = { episodeId: episode.id, linkedTo: [], linksCreated: 0 };
75202
75272
  if (!hasAssociativeEmbedding(episode))
75203
75273
  return result;
@@ -75244,11 +75314,11 @@ function batchLink(episodeStore, graph, config) {
75244
75314
  }
75245
75315
  return { processed, linksCreated: totalLinks };
75246
75316
  }
75247
- var DEFAULT_CONFIG4;
75317
+ var DEFAULT_CONFIG3;
75248
75318
  var init_zettelkasten = __esm({
75249
75319
  "packages/memory/dist/zettelkasten.js"() {
75250
75320
  "use strict";
75251
- DEFAULT_CONFIG4 = {
75321
+ DEFAULT_CONFIG3 = {
75252
75322
  topK: 3,
75253
75323
  minSimilarity: 0.3,
75254
75324
  evolutionThreshold: 0.6,
@@ -75648,7 +75718,7 @@ function extractQueryEntities(query) {
75648
75718
  return entities;
75649
75719
  }
75650
75720
  function personalizedPageRank(graph, seedNodeIds, config) {
75651
- const cfg = { ...DEFAULT_CONFIG5, ...config };
75721
+ const cfg = { ...DEFAULT_CONFIG4, ...config };
75652
75722
  const scores = /* @__PURE__ */ new Map();
75653
75723
  if (seedNodeIds.length === 0)
75654
75724
  return scores;
@@ -75699,7 +75769,7 @@ function personalizedPageRank(graph, seedNodeIds, config) {
75699
75769
  return scores;
75700
75770
  }
75701
75771
  function retrieveByPPR(query, graph, episodeStore, config) {
75702
- const cfg = { ...DEFAULT_CONFIG5, ...config };
75772
+ const cfg = { ...DEFAULT_CONFIG4, ...config };
75703
75773
  const queryEntities = extractQueryEntities(query);
75704
75774
  if (queryEntities.length === 0) {
75705
75775
  return { episodes: [], queryEntities: [], seedNodes: [], iterations: 0 };
@@ -75794,7 +75864,7 @@ function retrieveByPPR(query, graph, episodeStore, config) {
75794
75864
  iterations: cfg.maxIterations
75795
75865
  };
75796
75866
  }
75797
- var DEFAULT_CONFIG5;
75867
+ var DEFAULT_CONFIG4;
75798
75868
  var init_pprRetrieval = __esm({
75799
75869
  "packages/memory/dist/pprRetrieval.js"() {
75800
75870
  "use strict";
@@ -75802,7 +75872,7 @@ var init_pprRetrieval = __esm({
75802
75872
  init_homeostaticRegulation();
75803
75873
  init_socialInfluence();
75804
75874
  init_embodiedTrace();
75805
- DEFAULT_CONFIG5 = {
75875
+ DEFAULT_CONFIG4 = {
75806
75876
  damping: 0.5,
75807
75877
  maxIterations: 50,
75808
75878
  convergenceThreshold: 1e-6,
@@ -82669,11 +82739,11 @@ var init_embeddingDrift = __esm({
82669
82739
  });
82670
82740
 
82671
82741
  // packages/memory/dist/scoring.js
82672
- var DEFAULT_CONFIG6, MemoryScorer;
82742
+ var DEFAULT_CONFIG5, MemoryScorer;
82673
82743
  var init_scoring = __esm({
82674
82744
  "packages/memory/dist/scoring.js"() {
82675
82745
  "use strict";
82676
- DEFAULT_CONFIG6 = {
82746
+ DEFAULT_CONFIG5 = {
82677
82747
  recencyHalfLifeMs: 7 * 24 * 60 * 60 * 1e3,
82678
82748
  // 7 days
82679
82749
  minScore: 0.05,
@@ -82684,7 +82754,7 @@ var init_scoring = __esm({
82684
82754
  MemoryScorer = class {
82685
82755
  config;
82686
82756
  constructor(config) {
82687
- this.config = { ...DEFAULT_CONFIG6, ...config };
82757
+ this.config = { ...DEFAULT_CONFIG5, ...config };
82688
82758
  }
82689
82759
  /**
82690
82760
  * Score a single memory entry.
@@ -178360,7 +178430,7 @@ var require_axios = __commonJS({
178360
178430
  });
178361
178431
  return config;
178362
178432
  }
178363
- var resolveConfig = (config) => {
178433
+ var resolveConfig2 = (config) => {
178364
178434
  const newConfig = mergeConfig3({}, config);
178365
178435
  let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
178366
178436
  newConfig.headers = headers = AxiosHeaders$1.from(headers);
@@ -178404,7 +178474,7 @@ var require_axios = __commonJS({
178404
178474
  var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
178405
178475
  var xhrAdapter = isXHRAdapterSupported && function(config) {
178406
178476
  return new Promise(function dispatchXhrRequest(resolve108, reject) {
178407
- const _config3 = resolveConfig(config);
178477
+ const _config3 = resolveConfig2(config);
178408
178478
  let requestData = _config3.data;
178409
178479
  const requestHeaders = AxiosHeaders$1.from(_config3.headers).normalize();
178410
178480
  let { responseType, onUploadProgress, onDownloadProgress } = _config3;
@@ -178759,7 +178829,7 @@ var require_axios = __commonJS({
178759
178829
  headers,
178760
178830
  withCredentials = "same-origin",
178761
178831
  fetchOptions
178762
- } = resolveConfig(config);
178832
+ } = resolveConfig2(config);
178763
178833
  let _fetch = envFetch || fetch;
178764
178834
  responseType = responseType ? (responseType + "").toLowerCase() : "text";
178765
178835
  let composedSignal = composeSignals$1(
@@ -353754,8 +353824,8 @@ var init_ocr_pdf = __esm({
353754
353824
  });
353755
353825
  }
353756
353826
  const required = ["ocrmypdf", "tesseract", "gs"];
353757
- const availability = await Promise.all(required.map(async (command) => ({ command, available: await commandExists(command) })));
353758
- const missing2 = availability.filter((entry) => !entry.available).map((entry) => entry.command);
353827
+ const availability2 = await Promise.all(required.map(async (command) => ({ command, available: await commandExists(command) })));
353828
+ const missing2 = availability2.filter((entry) => !entry.available).map((entry) => entry.command);
353759
353829
  if (missing2.length) {
353760
353830
  return terminalOcrFailure({
353761
353831
  sourcePath: rawInput,
@@ -669705,8 +669775,10 @@ ${loadPrompt("agentic/system-small.md")}`;
669705
669775
  // window catches oscillation patterns (success → ENOENT → success → ENOENT)
669706
669776
  // that the consecutive counter misses.
669707
669777
  _consecutiveEnoent = 0;
669778
+ // Lifecycle summaries are a run-level fallback. Model replies, however,
669779
+ // must be delivered once for each response, including answers after tool work.
669708
669780
  _assistantTextEmitted = false;
669709
- // WO-E26: prevent double-emit
669781
+ _modelTextEmittedThisResponse = false;
669710
669782
  _pendingStreamAssistantText = null;
669711
669783
  /**
669712
669784
  * Bounded pause envelope for one open stream. It stores one finalized raw
@@ -673299,6 +673371,7 @@ ${read3.content}`;
673299
673371
  } else if (requireCanonical) {
673300
673372
  throw new Error("outbound request has no canonical projection");
673301
673373
  }
673374
+ this._modelTextEmittedThisResponse = false;
673302
673375
  const textProtocol = request.responseFormat || request.response_format ? void 0 : textProtocolOverride ?? (this.options.textToolMode ? "json" : resolveModelProfile(this.backend.model ?? "").toolCallFormat === "hermes-xml" ? "hermes-xml" : void 0);
673303
673376
  const streaming = this.options.streamEnabled && this.hasStreamingSupport();
673304
673377
  const backend = streaming ? { chatCompletion: (value2) => this.streamingRequest(value2, turn, textProtocol) } : this.backend;
@@ -683071,20 +683144,26 @@ Only the exact pending declared read tickets may execute. Old-plan mutations and
683071
683144
  flushPendingStreamAssistantText() {
683072
683145
  const pending2 = this._pendingStreamAssistantText;
683073
683146
  this._pendingStreamAssistantText = null;
683074
- if (!pending2 || this.aborted || this._assistantTextEmitted)
683147
+ if (pending2)
683148
+ this.emitModelAssistantText(pending2.content, pending2.turn);
683149
+ }
683150
+ /** The stream and batch consumers may see the same response; publish it once. */
683151
+ emitModelAssistantText(content, turn) {
683152
+ if (!content || this.aborted || this._modelTextEmittedThisResponse)
683075
683153
  return;
683076
683154
  const phase = this._interruptionLifecycle?.snapshot().phase;
683077
683155
  if (phase === "stopping" || phase === "stopped")
683078
683156
  return;
683079
683157
  this._observeExpectedOutcomeEffect("visible_response");
683158
+ this._modelTextEmittedThisResponse = true;
683159
+ this._assistantTextEmitted = true;
683080
683160
  this.emit({
683081
683161
  type: "assistant_text",
683082
- content: pending2.content,
683162
+ content,
683083
683163
  source: "model_visible_text",
683084
- turn: pending2.turn,
683164
+ turn,
683085
683165
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
683086
683166
  });
683087
- this._assistantTextEmitted = true;
683088
683167
  }
683089
683168
  /** Close a stream held at pause without retaining an event-per-token queue. */
683090
683169
  flushPendingPausedStreamTerminal() {
@@ -685361,6 +685440,7 @@ ${dynamicProjectContext}`
685361
685440
  let acceptedTaskCompleteOverride;
685362
685441
  let bruteForceCycle = 0;
685363
685442
  this._assistantTextEmitted = false;
685443
+ this._modelTextEmittedThisResponse = false;
685364
685444
  this._pendingStreamAssistantText = null;
685365
685445
  this._pendingPausedStreamTerminal = null;
685366
685446
  this._expectedOutcomeObservedEffects.clear();
@@ -691825,16 +691905,7 @@ Only call task_complete when the task is actually complete and the evidence is f
691825
691905
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
691826
691906
  });
691827
691907
  const cleanNonStream = content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
691828
- if (cleanNonStream && !this._assistantTextEmitted) {
691829
- this._observeExpectedOutcomeEffect("visible_response");
691830
- this.emit({
691831
- type: "assistant_text",
691832
- content: cleanNonStream,
691833
- turn,
691834
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
691835
- });
691836
- this._assistantTextEmitted = true;
691837
- }
691908
+ this.emitModelAssistantText(cleanNonStream, turn);
691838
691909
  if (this.options.acceptTextOnlyCompletion && expectedOutcomeAllowsTextOnlyCompletion(this.options.expectedOutcomeContract) && cleanNonStream) {
691839
691910
  completed = true;
691840
691911
  summary = cleanNonStream;
@@ -692464,6 +692535,7 @@ ${this.options.maxTurns && this.options.maxTurns > 0 ? `You have ${this.options.
692464
692535
  turn,
692465
692536
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
692466
692537
  });
692538
+ this.emitModelAssistantText(content.replace(/<think>[\s\S]*?<\/think>/g, "").trim(), turn);
692467
692539
  if (/task.?complete|all tests pass/i.test(content)) {
692468
692540
  const completionArgs = { summary: content };
692469
692541
  const open8 = this.getOpenTodoItems();
@@ -724637,15 +724709,15 @@ function loadLiveSensorConfig(repoRoot) {
724637
724709
  try {
724638
724710
  const parsed = JSON.parse(readFileSync121(liveConfigPath(repoRoot), "utf8"));
724639
724711
  return {
724640
- ...DEFAULT_CONFIG7,
724712
+ ...DEFAULT_CONFIG6,
724641
724713
  ...parsed,
724642
724714
  cameraOrientation: sanitizeCameraOrientation(parsed.cameraOrientation),
724643
724715
  cameraStreams: sanitizeCameraStreams(parsed.cameraStreams),
724644
- videoIntervalMs: Math.max(MIN_VIDEO_INTERVAL_MS, Number(parsed.videoIntervalMs ?? DEFAULT_CONFIG7.videoIntervalMs)),
724645
- audioIntervalMs: Math.max(MIN_AUDIO_INTERVAL_MS, Number(parsed.audioIntervalMs ?? DEFAULT_CONFIG7.audioIntervalMs))
724716
+ videoIntervalMs: Math.max(MIN_VIDEO_INTERVAL_MS, Number(parsed.videoIntervalMs ?? DEFAULT_CONFIG6.videoIntervalMs)),
724717
+ audioIntervalMs: Math.max(MIN_AUDIO_INTERVAL_MS, Number(parsed.audioIntervalMs ?? DEFAULT_CONFIG6.audioIntervalMs))
724646
724718
  };
724647
724719
  } catch {
724648
- return { ...DEFAULT_CONFIG7 };
724720
+ return { ...DEFAULT_CONFIG6 };
724649
724721
  }
724650
724722
  }
724651
724723
  function readLiveSensorSnapshot(repoRoot) {
@@ -725180,7 +725252,7 @@ function formatLiveStatus(snapshot) {
725180
725252
  }
725181
725253
  return lines.join("\n");
725182
725254
  }
725183
- var MIN_VIDEO_INTERVAL_MS, LOW_LATENCY_VIDEO_INTERVAL_MS, MIN_AUDIO_INTERVAL_MS, LOW_LATENCY_AUDIO_INTERVAL_MS, AUDIO_ACTIVITY_INTERVAL_MS, AUDIO_ACTIVITY_SAMPLE_SEC, DEFAULT_CAMERA_RESOLUTION, DEFAULT_CAMERA_FPS, DEFAULT_CONFIG7, managers, DASHBOARD_ANSI_STICKY_PATTERN, _liveDashboardMaximizedCamera, LiveSensorManager;
725255
+ var MIN_VIDEO_INTERVAL_MS, LOW_LATENCY_VIDEO_INTERVAL_MS, MIN_AUDIO_INTERVAL_MS, LOW_LATENCY_AUDIO_INTERVAL_MS, AUDIO_ACTIVITY_INTERVAL_MS, AUDIO_ACTIVITY_SAMPLE_SEC, DEFAULT_CAMERA_RESOLUTION, DEFAULT_CAMERA_FPS, DEFAULT_CONFIG6, managers, DASHBOARD_ANSI_STICKY_PATTERN, _liveDashboardMaximizedCamera, LiveSensorManager;
725184
725256
  var init_live_sensors = __esm({
725185
725257
  "packages/cli/src/tui/live-sensors.ts"() {
725186
725258
  init_dist5();
@@ -725198,7 +725270,7 @@ var init_live_sensors = __esm({
725198
725270
  AUDIO_ACTIVITY_SAMPLE_SEC = 0.2;
725199
725271
  DEFAULT_CAMERA_RESOLUTION = "1920x1080";
725200
725272
  DEFAULT_CAMERA_FPS = 8;
725201
- DEFAULT_CONFIG7 = {
725273
+ DEFAULT_CONFIG6 = {
725202
725274
  videoEnabled: false,
725203
725275
  audioEnabled: false,
725204
725276
  audioOutputEnabled: false,
@@ -755162,12 +755234,12 @@ async function repairExpandedVariantIfStale(variantModel, fallbackBaseModel, bac
755162
755234
  if (!baseModel) {
755163
755235
  return { repaired: false, currentNumCtx: state5.currentNumCtx, baseModel: null, resolvedModel: variantName };
755164
755236
  }
755165
- const canonicalModel = expandedModelName(baseModel);
755237
+ const canonicalModel2 = expandedModelName(baseModel);
755166
755238
  const hasCtx = state5.currentNumCtx > 0;
755167
755239
  const tooSmall = hasCtx && state5.currentNumCtx < targetNumCtx;
755168
755240
  const tooLarge = hasCtx && state5.currentNumCtx > Math.floor(targetNumCtx * 1.1);
755169
755241
  const needsCtxRepair = !hasCtx || tooSmall || tooLarge;
755170
- const needsCanonicalVariant = variantName !== canonicalModel;
755242
+ const needsCanonicalVariant = variantName !== canonicalModel2;
755171
755243
  let repaired = false;
755172
755244
  if (needsCtxRepair) {
755173
755245
  const repairedCurrent = await createExpandedVariantNamedAsync(variantName, baseModel, specs, sizeGB, kvBytesPerToken, archMax);
@@ -755175,13 +755247,13 @@ async function repairExpandedVariantIfStale(variantModel, fallbackBaseModel, bac
755175
755247
  }
755176
755248
  let resolvedModel = variantName;
755177
755249
  if (needsCanonicalVariant) {
755178
- const canonicalCreated = await createExpandedVariantNamedAsync(canonicalModel, baseModel, specs, sizeGB, kvBytesPerToken, archMax);
755250
+ const canonicalCreated = await createExpandedVariantNamedAsync(canonicalModel2, baseModel, specs, sizeGB, kvBytesPerToken, archMax);
755179
755251
  if (canonicalCreated) {
755180
755252
  repaired = true;
755181
755253
  resolvedModel = canonicalCreated;
755182
755254
  }
755183
755255
  } else {
755184
- resolvedModel = canonicalModel;
755256
+ resolvedModel = canonicalModel2;
755185
755257
  }
755186
755258
  return { repaired, currentNumCtx: state5.currentNumCtx, baseModel, resolvedModel };
755187
755259
  }
@@ -845415,8 +845487,8 @@ This is an independent background session started from /background.`
845415
845487
  }
845416
845488
  const startupChecksPromise = (async () => {
845417
845489
  if (!isResumed && !isFirstRun()) {
845418
- const availability = await checkModelAvailability(currentConfig);
845419
- if (availability.status === "unavailable" && currentConfig.backendType === "ollama") {
845490
+ const availability2 = await checkModelAvailability(currentConfig);
845491
+ if (availability2.status === "unavailable" && currentConfig.backendType === "ollama") {
845420
845492
  writeContent(() => {
845421
845493
  renderWarning(
845422
845494
  `Model "${currentConfig.model}" not available. Use /model to pick one.`
@@ -849913,8 +849985,8 @@ ${c3.dim("(Use /quit to exit)")}
849913
849985
  async function shouldRunFirstRunSetup(config, firstRun = isFirstRun()) {
849914
849986
  if (config.backendType !== "ollama") return false;
849915
849987
  if (!firstRun) return false;
849916
- const availability = await checkModelAvailability(config);
849917
- return availability.status === "unavailable";
849988
+ const availability2 = await checkModelAvailability(config);
849989
+ return availability2.status === "unavailable";
849918
849990
  }
849919
849991
  async function runWithTUI(task, config, repoPath2, callbacks) {
849920
849992
  const repoRoot = resolve101(repoPath2 ?? cwd());
@@ -35169,6 +35169,81 @@
35169
35169
  }
35170
35170
  ]
35171
35171
  },
35172
+ {
35173
+ "id": "guide.work-orders-runtime-health-remediation-wo-49-authored-final-delivery-uppercase",
35174
+ "kind": "guide",
35175
+ "title": "WO-49: Preserve the authored answer after tool work",
35176
+ "summary": "Status: repository repair verified; scoped Git delivery in progress.",
35177
+ "keywords": [
35178
+ "work",
35179
+ "orders",
35180
+ "runtime",
35181
+ "health",
35182
+ "remediation",
35183
+ "WO",
35184
+ "49",
35185
+ "authored",
35186
+ "final",
35187
+ "delivery",
35188
+ "md"
35189
+ ],
35190
+ "maturity": "internal",
35191
+ "audiences": [
35192
+ "maintainer",
35193
+ "large-context-agent"
35194
+ ],
35195
+ "layer": "documentation",
35196
+ "interfaces": [
35197
+ {
35198
+ "type": "file",
35199
+ "target": "docs/work-orders/runtime-health-remediation/WO-49-authored-final-delivery.md"
35200
+ }
35201
+ ],
35202
+ "references": [
35203
+ {
35204
+ "type": "documentation",
35205
+ "target": "docs/work-orders/runtime-health-remediation/WO-49-authored-final-delivery.md",
35206
+ "relation": "canonical-artifact"
35207
+ }
35208
+ ]
35209
+ },
35210
+ {
35211
+ "id": "guide.work-orders-runtime-health-remediation-wo-50-embedding-unavailability-uppercase",
35212
+ "kind": "guide",
35213
+ "title": "WO-50: Recover honestly from missing embeddings",
35214
+ "summary": "Status: source repair in progress; requested Nomic re-pull completed.",
35215
+ "keywords": [
35216
+ "work",
35217
+ "orders",
35218
+ "runtime",
35219
+ "health",
35220
+ "remediation",
35221
+ "WO",
35222
+ "50",
35223
+ "embedding",
35224
+ "unavailability",
35225
+ "md"
35226
+ ],
35227
+ "maturity": "internal",
35228
+ "audiences": [
35229
+ "maintainer",
35230
+ "large-context-agent"
35231
+ ],
35232
+ "layer": "documentation",
35233
+ "interfaces": [
35234
+ {
35235
+ "type": "file",
35236
+ "target": "docs/work-orders/runtime-health-remediation/WO-50-embedding-unavailability.md"
35237
+ }
35238
+ ],
35239
+ "references": [
35240
+ {
35241
+ "type": "documentation",
35242
+ "target": "docs/work-orders/runtime-health-remediation/WO-50-embedding-unavailability.md",
35243
+ "relation": "canonical-artifact"
35244
+ }
35245
+ ]
35246
+ },
35172
35247
  {
35173
35248
  "id": "guide.work-orders-telegram-dmn-wo-22-dmn-outreach-and-learning-uppercase",
35174
35249
  "kind": "guide",
package/docs/DISCOVERY.md CHANGED
@@ -594,6 +594,8 @@ Daemon equivalents are `GET /v1/discovery/bootstrap`, `GET /v1/discovery?q=<inte
594
594
  | `guide.work-orders-runtime-health-remediation-wo-46-shell-result-authority-uppercase` | WO-46: Preserve process authority for shell observations | Status: repository repair complete and delivered to origin/main. Publication and live validation remain with the user. |
595
595
  | `guide.work-orders-runtime-health-remediation-wo-47-diagnostic-execution-contract-uppercase` | WO-47: Validate and execute diagnostics without blocking the agent | Status: repository repair complete and delivered to origin/main. Publication and live validation remain with the user. |
596
596
  | `guide.work-orders-runtime-health-remediation-wo-48-conversation-result-evidence-uppercase` | WO-48: Retain command evidence in conversation results | Status: repository repair complete and delivered to origin/main. Publication and live validation remain with the user. |
597
+ | `guide.work-orders-runtime-health-remediation-wo-49-authored-final-delivery-uppercase` | WO-49: Preserve the authored answer after tool work | Status: repository repair verified; scoped Git delivery in progress. |
598
+ | `guide.work-orders-runtime-health-remediation-wo-50-embedding-unavailability-uppercase` | WO-50: Recover honestly from missing embeddings | Status: source repair in progress; requested Nomic re-pull completed. |
597
599
  | `guide.work-orders-telegram-dmn-wo-22-dmn-outreach-and-learning-uppercase` | WO-22: DMN outreach, DM sharing, and outcome learning | On 2026-09-03 at 17:07 PDT the bot posted in the OMNIUS group without being addressed. The operator asked whether this was self-induced reflection. |
598
600
  | `guide.work-orders-telegram-dropbear-context-rca-workorder` | Telegram Dropbear Context Engineering RCA Work Order | Observed run: /home/roko/Documents/Projects/Adjacent/telegramtest/.omnius, run id 1782873796963-i5r7mv. |
599
601
  | `guide.work-orders-wo-am-gaps-uppercase` | Associative Memory Gap Work Orders | Generated: 2026-04-13 Source: Deep audit of multimodal associative memory systems Status: READY FOR IMPLEMENTATION |
@@ -4,6 +4,13 @@
4
4
  **Checked-item rule:** code, focused tests, and named evidence must all exist
5
5
  **Last reconciled:** 2026-09-05
6
6
 
7
+ ## September 5 runtime 1.0.698 delivery and Ollama follow-up
8
+
9
+ - [ ] [WO-49: authored final delivery](WO-49-authored-final-delivery.md): generated answer lost after earlier prose and tool work; source repair and real-runner Telegram regression in progress.
10
+ - [ ] [WO-50: embedding unavailability](WO-50-embedding-unavailability.md): bounded missing-model recovery, truthful vector/job outcomes and exact model identity in progress. User-requested Nomic re-pull succeeded; installation metadata verified without inference.
11
+
12
+ The separate 14:43 PDT router failure was a managed Ollama lane exiting mid-stream, followed by connection refused and recovery. The external broker does not retain its exit status/stderr, so the underlying exit cause remains unresolved; WO-50 records the evidence and external observability follow-up.
13
+
7
14
  ## September 5 post-publication field review
8
15
 
9
16
  [Runtime 1.0.697 field review](LIVE-TELEGRAM-LOG-REVIEW-2026-09-05.md): a useful assessment final was delivered at 13:41 PDT, supported by TypeScript and 21 passing tests. At the reviewed checkpoint, the next implementation run was active. No hard blocker was established; source-read false failures, malformed diagnostic input handling and conversation-mode report coverage triggered the repairs below. Native feature workflow selection and continuous typing are not established by this sample.
@@ -0,0 +1,39 @@
1
+ # WO-49: Preserve the authored answer after tool work
2
+
3
+ Status: repository repair verified; scoped Git delivery in progress.
4
+
5
+ ## Field evidence and cause
6
+
7
+ Runtime 1.0.698, September 5, 2026, run `telegram-64ac9937dc7647a8-1788644624446-1` completed after 13 turns and 16 tools. Its last context dump (`2026-09-05T21-52-49-830Z-main-395f119a1f.json`, message 31) contains a 1,249-character tool-free answer with the tunnel URL and endpoint instructions. The next message asks for `task_complete`; that call supplies an internal summary but no optional `user_reply`. Telegram instead delivered a 1,735-character mechanical command report at 14:54:31 PDT. The final prompt still had 82.9% free context: this was delivery loss, not context exhaustion.
8
+
9
+ The runner uses one run-wide `_assistantTextEmitted` flag to suppress both duplicate response delivery and later independent replies. Earlier prose consumes the flag. Telegram correctly invalidates earlier candidates after tool work, but never receives the later answer's `model_visible_text` event. A subsequent summary-only completion clears the stream buffer; the report is the only eligible final. Nonstream replies also lack the source tag required by the terminal reply contract.
10
+
11
+ Selected evidence is copied with SHA-256 manifests under `/tmp/omnius-run-1788644624446-audit/` and `/tmp/omnius-ollama-failure-rurz8d5c/`. Source logs remain untouched. These local captures are diagnostic evidence, not repository test fixtures.
12
+
13
+ ## Repair ownership
14
+
15
+ - `packages/orchestrator/src/agenticRunner.ts`: separate per-response model delivery from run-wide lifecycle-summary suppression; preserve safe pause/Stop boundaries; tag actual authored text in both loops and transports.
16
+ - `packages/orchestrator/tests/`: regression coverage for distinct model responses, streaming deduplication, interruption and both loops.
17
+ - `packages/cli/tests/telegram-streamed-terminal-answer.test.ts` and `telegram-terminal-delivery.test.ts`: actual runner-to-Telegram field sequence plus exact terminal identity and stale-answer controls.
18
+
19
+ ## Acceptance
20
+
21
+ - [x] Earlier prose and tool work cannot suppress a later authored answer.
22
+ - [x] A separate summary-only completion turn retains that answer; no raw-command fallback replaces it.
23
+ - [x] Stream and batch paths tag actual model text consistently; a response emits exactly once.
24
+ - [x] Both loops retain interruption ownership; pause holds output, resume emits once, Stop rejects late output.
25
+ - [x] Intervening work and task replacement invalidate stale answers; unvalidated URLs remain subject to the existing evidence gate.
26
+ - [ ] Record verification and scoped commit/push.
27
+
28
+ ## Limits
29
+
30
+ The copied `•` bullets were not reproduced in repository formatting or JSON encoding: stored pre-format text uses ASCII hyphens, and the formatter emits a correct Unicode bullet. Do not invent an encoding repair without a failing byte-level case. Retaining the useful answer addresses the observed command dump; this order does not grant prose or shell stdout new completion or link-validation authority.
31
+
32
+
33
+ ## Verification
34
+
35
+ - Before the source rebuild, the actual Telegram regression failed in both streamed and batch modes with the same mechanical command report as the field run. Its intervening-work negative control already passed. Baseline: `/tmp/omnius-terminal-stream-before.log`.
36
+ - After repair, all three actual Telegram cases pass; four additional actual-runner cases cover main/brute loops and stream/batch transport, including repeated turn zero in the fallback loop and exactly-once authored events. Logs: `/tmp/omnius-terminal-stream-after.log`, `/tmp/omnius-assistant-text-emission.log`.
37
+ - `OMNIUS_SQLITE_TESTS=1 pnpm --dir packages/orchestrator exec vitest run tests/agenticRunner-interruption.test.ts tests/structured-tool-authority.test.ts tests/agenticRunner.test.ts tests/conversation-report-runner.test.ts --maxWorkers=4 --minWorkers=1`: 276 passed across four suites. Combined with the four new producer cases: 280 distinct orchestrator checks. Log: `/tmp/omnius-wo49-orchestrator-focused.log`.
38
+ - `pnpm --dir packages/cli exec vitest run tests/telegram-terminal-delivery.test.ts tests/telegram-streamed-terminal-answer.test.ts tests/telegram-conversation-evidence.test.ts tests/telegram-stream-timeout.test.ts --maxWorkers=4 --minWorkers=1`: 58 passed across four suites. Log: `/tmp/omnius-wo49-cli-focused.log`.
39
+ - Orchestrator build passed (`/tmp/omnius-wo49-orchestrator-build.log`). All inference and Telegram delivery in these tests were mocked. Publication and live acceptance remain with the user.
@@ -0,0 +1,33 @@
1
+ # WO-50: Recover honestly from missing embeddings
2
+
3
+ Status: source repair in progress; requested Nomic re-pull completed.
4
+
5
+ ## Field evidence and operational remedy
6
+
7
+ The Ollama broker recorded 102 missing-model errors for `nomic-embed-text:latest` between 14:45 and 15:09:55 PDT on September 5, 2026. Omnius is configured for `http://localhost:11434`, with no embedding model override in the running process. The errors return HTTP 404 from `/api/embed`. This reduces semantic retrieval; stored text and lexical retrieval are still available. The exact caller of every request in the bursts has not been established.
8
+
9
+ At the user's explicit request, `POST /api/pull` for the exact tag returned `success` at 15:19:57 PDT. Subsequent `/api/tags` and `/api/show` metadata confirmed `nomic-embed-text:latest`, embedding capability, 137M/F16, 768 dimensions, digest `0a109f422b47e3a30ba2b10eca18548e944e8a23073ee3f3e947efcf3c45e59f`. No inference or model-load probe was sent; no services were restarted. Successful installation metadata does not establish live embedding execution.
10
+
11
+ Passive journal follow-up through 15:25:50 PDT then recorded 51 `/api/embed` HTTP 200 responses and zero embedding 404s after the pull. The broker logged Nomic lanes ready at 15:22:07 and 15:22:12 for the existing workload. This is observed server recovery, not an agent-issued inference test or proof that every earlier missing embedding job was repaired. Capture and SHA-256 manifest: `/tmp/omnius-nomic-repull-20260905/`.
12
+
13
+ ## Confirmed source defects and repair ownership
14
+
15
+ - `packages/memory/src/embeddings.ts`: a single missing-model response triggers an implicit pull and recursive retry; batch requests lack bounded availability backoff. Replace this with exact endpoint/model scoped availability caching and independent cancellation. Preserve the null failure contract, validate vectors, and honor configured model identity without substituting models. Model checks must match exact tags.
16
+ - `packages/memory/src/memoryIngestion.ts`: jobs currently complete even when no embedding is stored. Use existing durable retry/backoff for failed embedding work; retain raw text and lexical search.
17
+ - `packages/indexer/src/ollamaEmbeddings.ts`: any failed batch causes per-input retries and fabricated zero vectors. Report generation failure truthfully, reject malformed vectors, and limit legacy API fallback to an actual unsupported endpoint.
18
+ - Matching hermetic tests in memory/indexer, plus dependent builds and relevant retrieval tests.
19
+
20
+ ## Acceptance
21
+
22
+ - [ ] Missing-model bursts make bounded requests, with no implicit downloads or recursive retries.
23
+ - [ ] Exact endpoint/model identity, TTL recovery and independent cancellation are tested.
24
+ - [ ] Configuration honors the selected model; availability checks cannot match a different tag by prefix.
25
+ - [ ] Failed generation cannot fabricate vectors or complete embedding jobs; text remains recoverable.
26
+ - [ ] Compatibility handling cannot fan one model failure into one request per input.
27
+ - [ ] Record verification and scoped commit/push.
28
+
29
+ ## Separate chat-server incident
30
+
31
+ This embedding defect did not cause the observed router HTTP 503. At 14:43:43.457 PDT the broker logged an incomplete `/api/chat` read; at 14:43:43.469 its fresh retry hit connection refused. At 14:43:44.583 it reported `lane-121` stopped unexpectedly. The lane had been ready since 14:25:42 for `robit/qwen3.8-27b-obliterated-e03:27b` on GPU UUID `GPU-170a99ee-850f-2182-1050-4e8d3c87b6b0`. The broker served chat again at 14:44:09.532, and the main run subsequently produced its answer.
32
+
33
+ No OOM kill, GPU Xid, segfault or coredump was found in the narrow incident journal. The external broker discards child stdout/stderr and omits PID/exit status in its prune warning (`/usr/local/libexec/ollama-unify-gpu-negotiator`, child spawn and `_prune_dead_lanes_locked`). Its exact exit mechanism remains unknown. An external broker follow-up needs retained child stderr and return-code logging to distinguish crash, signal and deliberate exit; this Omnius repair does not claim to fix that unknown cause. Hashed journal captures are under `/tmp/omnius-ollama-failure-aki8oz5k/`; the earlier incident window is under `/tmp/omnius-ollama-early-failure-_nsbctcb/`.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.698",
3
+ "version": "1.0.699",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.698",
9
+ "version": "1.0.699",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.698",
3
+ "version": "1.0.699",
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",