@cueai/omni-reader-mcp 1.5.5 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +106 -46
  2. package/dist/artifact-store.d.ts +2 -0
  3. package/dist/artifact-store.js +45 -10
  4. package/dist/capabilities.d.ts +129 -2
  5. package/dist/capabilities.js +122 -19
  6. package/dist/cli/agent-config.js +2 -2
  7. package/dist/cli/arguments.d.ts +8 -4
  8. package/dist/cli/arguments.js +62 -7
  9. package/dist/cli/config-inspection.d.ts +59 -0
  10. package/dist/cli/config-inspection.js +307 -0
  11. package/dist/cli/doctor.d.ts +7 -0
  12. package/dist/cli/doctor.js +37 -2
  13. package/dist/cli/setup.js +13 -2
  14. package/dist/constants.d.ts +6 -1
  15. package/dist/constants.js +9 -4
  16. package/dist/cube-client.d.ts +4 -1
  17. package/dist/cube-client.js +286 -32
  18. package/dist/cursor.d.ts +4 -0
  19. package/dist/cursor.js +11 -13
  20. package/dist/errors.d.ts +1 -0
  21. package/dist/errors.js +15 -0
  22. package/dist/iiis-client.d.ts +33 -2
  23. package/dist/iiis-client.js +368 -40
  24. package/dist/index.js +10 -1
  25. package/dist/operation-journal.d.ts +17 -1
  26. package/dist/operation-journal.js +260 -15
  27. package/dist/operation-manager.d.ts +6 -2
  28. package/dist/operation-manager.js +448 -89
  29. package/dist/path-normalization.d.ts +5 -0
  30. package/dist/path-normalization.js +25 -0
  31. package/dist/path-security.d.ts +2 -1
  32. package/dist/path-security.js +49 -28
  33. package/dist/protocol.d.ts +10 -2
  34. package/dist/protocol.js +23 -7
  35. package/dist/remote-client.js +3 -13
  36. package/dist/result-contract.d.ts +76 -32
  37. package/dist/result-contract.js +121 -5
  38. package/dist/task-runtime.d.ts +3 -2
  39. package/dist/task-runtime.js +2 -2
  40. package/dist/tools.d.ts +4 -0
  41. package/dist/tools.js +41 -31
  42. package/package.json +1 -1
@@ -4,8 +4,8 @@ import { OmniBridgeError } from "./errors.js";
4
4
  import { LEGACY_RECOVERY_FAILURE_CODE } from "./operation-journal.js";
5
5
  import { openAllowedFile, } from "./path-security.js";
6
6
  import { NOOP_PROGRESS } from "./progress.js";
7
- import { normalizeRepresentation } from "./protocol.js";
8
- import { localResultToResultField } from "./result-contract.js";
7
+ import { normalizeRepresentation, } from "./protocol.js";
8
+ import { LOCAL_ARTIFACT_RETENTION_WARNING, createFlatLocalResultCache, localResultToResultField, presentRetainedResult, } from "./result-contract.js";
9
9
  const TERMINAL_STATES = new Set([
10
10
  "COMPLETED",
11
11
  "FAILED",
@@ -36,6 +36,78 @@ function managerError(code, message, facts = {}) {
36
36
  retryable: facts.retryable ?? false,
37
37
  });
38
38
  }
39
+ function journalIntegrityError(message, record) {
40
+ return managerError("JOURNAL_INTEGRITY_FAILED", message, record === undefined
41
+ ? {}
42
+ : {
43
+ operationCreated: record.operationId !== null,
44
+ fileUploaded: record.fileUploaded,
45
+ parserStarted: record.parserStarted,
46
+ billed: record.billed,
47
+ contentReleased: record.contentReleased,
48
+ });
49
+ }
50
+ function isBillingProfile(profile) {
51
+ return profile === "omni.direct_text_billing.v1"
52
+ || profile === "omni.direct_grounding_billing.v1";
53
+ }
54
+ function toJournalBilling(billing) {
55
+ return {
56
+ creditsCharged: billing.credits_charged,
57
+ creditsRemaining: billing.credits_remaining,
58
+ };
59
+ }
60
+ function toWireBilling(billing) {
61
+ return {
62
+ credits_charged: billing.creditsCharged,
63
+ credits_remaining: billing.creditsRemaining,
64
+ };
65
+ }
66
+ function resultBilling(result) {
67
+ if (result.status === "completed"
68
+ || result.status === "cleanup_pending"
69
+ || result.status === "canceled"
70
+ || result.status === "expired") {
71
+ return result.billing ?? null;
72
+ }
73
+ return null;
74
+ }
75
+ function projectBilling(result, billing) {
76
+ if (billing === null
77
+ || (result.status !== "completed"
78
+ && result.status !== "cleanup_pending"
79
+ && result.status !== "canceled"
80
+ && result.status !== "expired")) {
81
+ return result;
82
+ }
83
+ return { ...result, billing };
84
+ }
85
+ function requireWireBillingForSettledProfile(profile, billed, billing) {
86
+ if (!isBillingProfile(profile))
87
+ return billing ?? undefined;
88
+ if (!billed && billing !== null) {
89
+ throw journalIntegrityError("An unbilled direct terminal carries billing facts.");
90
+ }
91
+ if (billed && billing === null) {
92
+ throw journalIntegrityError("New-profile settlement billing is missing.");
93
+ }
94
+ return billing ?? undefined;
95
+ }
96
+ function assertDirectTerminalSnapshot(profile, snapshot) {
97
+ if (!isBillingProfile(profile))
98
+ return;
99
+ if ((snapshot.directProfile ?? null) !== profile) {
100
+ throw journalIntegrityError("IIIS changed the selected direct profile.");
101
+ }
102
+ if (snapshot.status === "CANCELED"
103
+ || snapshot.status === "FAILED"
104
+ || snapshot.status === "UNSUPPORTED"
105
+ || snapshot.status === "SETTLEMENT_DENIED") {
106
+ if (snapshot.billed || snapshot.contentReleased || snapshot.billing !== null) {
107
+ throw journalIntegrityError("A canceled or failed direct-v5 operation is not strictly uncharged.");
108
+ }
109
+ }
110
+ }
39
111
  function canonicalValue(value) {
40
112
  if (value === null || typeof value === "string" || typeof value === "boolean") {
41
113
  return value;
@@ -162,6 +234,7 @@ function resultFromRecord(record) {
162
234
  status: "expired",
163
235
  operation_id: record.operationId,
164
236
  requires_user_confirmation: true,
237
+ ...(record.billing === null ? {} : { billing: toWireBilling(record.billing) }),
165
238
  };
166
239
  }
167
240
  if (record.state === "CANCELED") {
@@ -170,6 +243,7 @@ function resultFromRecord(record) {
170
243
  status: "canceled",
171
244
  operation_id: record.operationId,
172
245
  ...(cleanupDeadline === null ? {} : { cleanup_deadline: cleanupDeadline }),
246
+ ...(record.billing === null ? {} : { billing: toWireBilling(record.billing) }),
173
247
  data_handling: recordDataHandling(record),
174
248
  };
175
249
  }
@@ -188,6 +262,7 @@ function resultFromRecord(record) {
188
262
  return {
189
263
  status: "cleanup_pending",
190
264
  operation_id: record.operationId,
265
+ ...(record.billing === null ? {} : { billing: toWireBilling(record.billing) }),
191
266
  cleanup_deadline: record.resultExpiresAt ?? record.expiresAt ?? new Date(0).toISOString(),
192
267
  data_handling: recordDataHandling(record),
193
268
  };
@@ -217,30 +292,52 @@ function resultFromRecord(record) {
217
292
  export class OperationManager {
218
293
  #journal;
219
294
  #driver;
295
+ #presentResult;
220
296
  #now;
221
297
  #sleep;
222
298
  #submissions = new Map();
299
+ #submissionDelivery = new Map();
223
300
  #executions = new Map();
224
301
  #results = new Map();
225
302
  constructor(options) {
226
303
  this.#journal = options.journal;
227
304
  this.#driver = options.driver;
305
+ this.#presentResult = options.presentResult ?? (async (_record, result) => result);
228
306
  this.#now = options.now ?? Date.now;
229
307
  this.#sleep = options.sleep ?? defaultSleep;
230
308
  }
309
+ async #strengthenResultDelivery(record, requested) {
310
+ if (requested === "auto" || record.resultDeliveryEffective === "artifact") {
311
+ return record;
312
+ }
313
+ if (record.requestIdentityHmac === null
314
+ && record.errorCode === LEGACY_RECOVERY_FAILURE_CODE) {
315
+ return record;
316
+ }
317
+ return this.#journal.strengthenResultDelivery(record.clientRequestId, requested);
318
+ }
231
319
  async submit(input) {
232
320
  const requestHash = operationRequestHash(input);
321
+ const requestedDelivery = input.resultDelivery ?? "auto";
322
+ if (requestedDelivery === "artifact"
323
+ || this.#submissionDelivery.get(requestHash) === undefined) {
324
+ this.#submissionDelivery.set(requestHash, requestedDelivery);
325
+ }
233
326
  const active = this.#submissions.get(requestHash);
234
- if (active !== undefined)
235
- return active;
327
+ if (active !== undefined) {
328
+ const record = await active;
329
+ return this.#strengthenResultDelivery(record, requestedDelivery);
330
+ }
236
331
  const pending = this.#submit(input);
237
332
  this.#submissions.set(requestHash, pending);
238
333
  try {
239
- return await pending;
334
+ const record = await pending;
335
+ return await this.#strengthenResultDelivery(record, this.#submissionDelivery.get(requestHash) ?? requestedDelivery);
240
336
  }
241
337
  finally {
242
338
  if (this.#submissions.get(requestHash) === pending) {
243
339
  this.#submissions.delete(requestHash);
340
+ this.#submissionDelivery.delete(requestHash);
244
341
  }
245
342
  }
246
343
  }
@@ -282,7 +379,7 @@ export class OperationManager {
282
379
  effectiveInput = { ...input, clientRequestId: matching.clientRequestId };
283
380
  }
284
381
  else {
285
- record = await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation);
382
+ record = await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation, input.resultDelivery ?? "auto");
286
383
  }
287
384
  }
288
385
  else if (record.requestIdentityHmac === null) {
@@ -291,18 +388,26 @@ export class OperationManager {
291
388
  // recovery failure. It can never be resumed as grounded/layout.
292
389
  const migrated = await this.#journal.migrateLegacyRecord(input.clientRequestId, identityJson, sourceLocator);
293
390
  record = migrated === null
294
- ? await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation)
391
+ ? await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation, input.resultDelivery ?? "auto")
295
392
  : migrated;
296
393
  if (TERMINAL_STATES.has(record.state))
297
394
  return record;
298
395
  }
299
396
  else {
300
- record = await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation);
397
+ record = await this.#journal.beginIntent(input.clientRequestId, identityJson, input.sourceKind, sourceLocator, representation, input.resultDelivery ?? "auto");
301
398
  }
399
+ record = await this.#strengthenResultDelivery(record, input.resultDelivery ?? "auto");
302
400
  const signal = effectiveInput.signal ?? new AbortController().signal;
303
401
  if (TERMINAL_STATES.has(record.state))
304
402
  return record;
305
403
  if (record.state !== "CREATED") {
404
+ if (record.sourceKind === "url"
405
+ && record.operationId !== null) {
406
+ // The remote server already owns this operation. A repeated parse call
407
+ // would create or attach to a second remote submission; callers must
408
+ // reuse the journaled operation and continue through status instead.
409
+ return record;
410
+ }
306
411
  if (record.operationId !== null && this.#executions.has(record.operationId)) {
307
412
  return record;
308
413
  }
@@ -334,6 +439,45 @@ export class OperationManager {
334
439
  throw structured;
335
440
  }
336
441
  }
442
+ async #bindResultBilling(record, result) {
443
+ const billing = resultBilling(result);
444
+ if (billing === null)
445
+ return record;
446
+ const latest = await this.#journal.loadByRequestId(record.clientRequestId) ?? record;
447
+ const directProfile = latest.sourceKind === "url" ? null : latest.directProfile;
448
+ if (latest.sourceKind === "local" && directProfile === null) {
449
+ throw journalIntegrityError("A local terminal carries billing without a saved direct profile.", latest);
450
+ }
451
+ return this.#journal.bindSettlementFacts(latest.clientRequestId, {
452
+ directProfile,
453
+ billing: toJournalBilling(billing),
454
+ });
455
+ }
456
+ async #normalizeResultBilling(record, result) {
457
+ const bound = await this.#bindResultBilling(record, result);
458
+ const billing = resultBilling(result)
459
+ ?? (bound.billing === null ? null : toWireBilling(bound.billing));
460
+ if (isBillingProfile(bound.directProfile)) {
461
+ if (result.status === "failed" || result.status === "canceled") {
462
+ if (bound.billed || bound.billing !== null || bound.contentReleased) {
463
+ throw journalIntegrityError("A failed or canceled direct-v5 terminal is not strictly uncharged.", bound);
464
+ }
465
+ }
466
+ else if (result.status === "completed"
467
+ || (result.status === "cleanup_pending" && result.result !== undefined)) {
468
+ requireWireBillingForSettledProfile(bound.directProfile, bound.billed, billing);
469
+ }
470
+ }
471
+ return projectBilling(result, billing);
472
+ }
473
+ async #present(record, canonicalResult) {
474
+ if (canonicalResult.status === "processing")
475
+ return canonicalResult;
476
+ const latest = record.operationId === null
477
+ ? await this.#journal.loadByRequestId(record.clientRequestId)
478
+ : await this.#journal.loadByOperationId(record.operationId);
479
+ return this.#presentResult(latest ?? record, canonicalResult);
480
+ }
337
481
  async submitResult(input, foregroundBudgetMs = FOREGROUND_BUDGET_MS) {
338
482
  const startedAt = this.#now();
339
483
  let record = await this.submit(input);
@@ -341,19 +485,20 @@ export class OperationManager {
341
485
  ? undefined
342
486
  : this.#results.get(record.operationId);
343
487
  if (existingResult !== undefined)
344
- return existingResult;
488
+ return this.#present(record, existingResult);
345
489
  const execution = record.operationId === null
346
490
  ? undefined
347
491
  : this.#executions.get(record.operationId);
348
492
  if (execution === undefined) {
349
- return TERMINAL_STATES.has(record.state) && record.operationId !== null
350
- ? this.statusResult(record.operationId, 0, input.signal)
351
- : resultFromRecord(record);
493
+ if (TERMINAL_STATES.has(record.state) && record.operationId !== null) {
494
+ return this.statusResult(record.operationId, 0, input.signal);
495
+ }
496
+ return this.#present(record, resultFromRecord(record));
352
497
  }
353
498
  const remaining = Math.max(0, foregroundBudgetMs - (this.#now() - startedAt));
354
499
  if (remaining === 0) {
355
500
  record = await this.#applyUpdate(record, execution.handle.snapshot());
356
- return resultFromRecord(record);
501
+ return this.#present(record, resultFromRecord(record));
357
502
  }
358
503
  const winner = await Promise.race([
359
504
  execution.settled.then((result) => ({ kind: "completed", result })),
@@ -361,76 +506,81 @@ export class OperationManager {
361
506
  ]);
362
507
  if (winner.kind === "completed") {
363
508
  return this.#now() - startedAt < foregroundBudgetMs
364
- ? winner.result
365
- : resultFromRecord(record);
509
+ ? this.#present(record, winner.result)
510
+ : this.#present(record, resultFromRecord(record));
366
511
  }
367
512
  const current = await this.#journal.loadByOperationId(record.operationId);
368
513
  if (current !== null && !TERMINAL_STATES.has(current.state)) {
369
514
  record = await this.#applyUpdate(current, execution.handle.snapshot());
370
515
  }
371
- else if (current !== null) {
372
- record = current;
373
- }
374
- return resultFromRecord(record);
516
+ return this.#present(record, resultFromRecord(record));
375
517
  }
376
518
  async statusResult(operationId, waitMs = 0, signal = new AbortController().signal) {
377
519
  const boundedWait = Math.min(STATUS_LONG_POLL_MAX_MS, Math.max(0, waitMs));
378
520
  let record = await this.#existingOperation(operationId);
379
521
  const cached = this.#results.get(operationId);
380
- if (cached !== undefined && TERMINAL_STATES.has(record.state))
381
- return cached;
522
+ if (cached !== undefined && TERMINAL_STATES.has(record.state)) {
523
+ return this.#present(record, cached);
524
+ }
382
525
  if (TERMINAL_STATES.has(record.state)) {
383
526
  const recovered = await this.#driver.result?.(record, signal);
384
527
  if (recovered !== undefined) {
385
- this.#results.set(operationId, recovered);
386
- return recovered;
528
+ const normalized = await this.#normalizeResultBilling(record, recovered);
529
+ this.#results.set(operationId, normalized);
530
+ return this.#present(record, normalized);
387
531
  }
388
- return resultFromRecord(record);
532
+ return this.#present(record, resultFromRecord(record));
389
533
  }
390
534
  if (record.resultExpiresAt !== null &&
391
535
  Date.parse(record.resultExpiresAt) <= this.#now()) {
392
536
  record = await this.#journal.transition(record.clientRequestId, record.state, "EXPIRED", { errorCode: "RESULT_EXPIRED" });
393
- return resultFromRecord(record);
537
+ return this.#present(record, resultFromRecord(record));
394
538
  }
395
539
  let execution = this.#executions.get(operationId);
396
540
  if (execution === undefined) {
397
541
  record = await this.status(operationId, boundedWait, signal);
398
542
  const statusResult = this.#results.get(operationId);
399
543
  if (statusResult !== undefined && TERMINAL_STATES.has(record.state)) {
400
- return statusResult;
544
+ return this.#present(record, statusResult);
401
545
  }
402
546
  execution = this.#executions.get(operationId);
403
- if (execution === undefined)
404
- return statusResult ?? resultFromRecord(record);
547
+ if (execution === undefined) {
548
+ return this.#present(record, statusResult ?? resultFromRecord(record));
549
+ }
405
550
  }
406
551
  if (!TERMINAL_STATES.has(record.state)) {
407
552
  record = await this.#applyUpdate(record, execution.handle.snapshot());
408
553
  }
409
- if (boundedWait === 0)
410
- return this.#results.get(operationId) ?? resultFromRecord(record);
554
+ if (boundedWait === 0) {
555
+ return this.#present(record, this.#results.get(operationId) ?? resultFromRecord(record));
556
+ }
411
557
  const winner = await Promise.race([
412
558
  execution.settled.then((value) => ({ kind: "completed", value })),
413
559
  this.#sleep(boundedWait).then(() => ({ kind: "timeout" })),
414
560
  ]);
415
561
  if (winner.kind === "completed")
416
- return winner.value;
562
+ return this.#present(record, winner.value);
417
563
  const current = await this.#existingOperation(operationId);
418
564
  record = TERMINAL_STATES.has(current.state)
419
565
  ? current
420
566
  : await this.#applyUpdate(current, execution.handle.snapshot());
421
- return this.#results.get(operationId) ?? resultFromRecord(record);
567
+ return this.#present(record, this.#results.get(operationId) ?? resultFromRecord(record));
422
568
  }
423
569
  async cancelResult(operationId, signal = new AbortController().signal) {
570
+ let record = await this.#existingOperation(operationId);
424
571
  const existing = this.#results.get(operationId);
425
- if (existing !== undefined && existing.status !== "processing")
426
- return existing;
572
+ if (existing !== undefined && existing.status !== "processing") {
573
+ return this.#present(record, existing);
574
+ }
427
575
  const execution = this.#executions.get(operationId);
428
576
  if (execution !== undefined) {
429
577
  await execution.handle.cancel();
430
- return execution.settled;
578
+ const settled = await execution.settled;
579
+ record = await this.#existingOperation(operationId);
580
+ return this.#present(record, settled);
431
581
  }
432
- const record = await this.cancel(operationId, signal);
433
- return this.#results.get(operationId) ?? resultFromRecord(record);
582
+ record = await this.cancel(operationId, signal);
583
+ return this.#present(record, this.#results.get(operationId) ?? resultFromRecord(record));
434
584
  }
435
585
  async status(operationId, waitMs, signal = new AbortController().signal) {
436
586
  const record = await this.#existingOperation(operationId);
@@ -448,7 +598,7 @@ export class OperationManager {
448
598
  return record;
449
599
  const updated = await this.#applyUpdate(record, update);
450
600
  if (update.result !== undefined && updated.operationId !== null) {
451
- this.#results.set(updated.operationId, update.result);
601
+ this.#results.set(updated.operationId, await this.#normalizeResultBilling(updated, update.result));
452
602
  }
453
603
  return updated;
454
604
  }
@@ -462,7 +612,7 @@ export class OperationManager {
462
612
  if (!isExecution(start)) {
463
613
  const updated = await this.#applyUpdate(record, start);
464
614
  if (start.result !== undefined && updated.operationId !== null) {
465
- this.#results.set(updated.operationId, start.result);
615
+ this.#results.set(updated.operationId, await this.#normalizeResultBilling(updated, start.result));
466
616
  }
467
617
  return updated;
468
618
  }
@@ -474,16 +624,17 @@ export class OperationManager {
474
624
  let managed;
475
625
  const settled = start.completed.then(async (completion) => {
476
626
  const current = await this.#journal.loadByOperationId(operationId);
477
- if (current !== null && !TERMINAL_STATES.has(current.state)) {
478
- await this.#applyUpdate(current, completion.update);
479
- }
480
- if (completion.result.status !== "processing") {
481
- this.#results.set(operationId, completion.result);
627
+ const updated = current !== null && !TERMINAL_STATES.has(current.state)
628
+ ? await this.#applyUpdate(current, completion.update)
629
+ : current ?? initial;
630
+ const normalized = await this.#normalizeResultBilling(updated, completion.result);
631
+ if (normalized.status !== "processing") {
632
+ this.#results.set(operationId, normalized);
482
633
  }
483
634
  else {
484
635
  this.#results.delete(operationId);
485
636
  }
486
- return completion.result;
637
+ return normalized;
487
638
  }).catch(async (error) => {
488
639
  const structured = error instanceof OmniBridgeError
489
640
  ? error
@@ -537,18 +688,10 @@ export class OperationManager {
537
688
  }
538
689
  #shouldReuseByRequestHash(record) {
539
690
  if (!TERMINAL_STATES.has(record.state)) {
540
- // Local uploads may resume mid-flight (restart recovery). URL recovery
541
- // is only valid BEFORE the remote operation exists (GRANT_PENDING — the
542
- // retry reuses the same request identity and the server's idempotency
543
- // key dedupes). Once a URL op is PROCESSING (operationId set), the
544
- // server owns it and a fresh parse creates a NEW operation id — reusing
545
- // the stale record pairs old op_id with the server's new one →
546
- // "changed the operation identifier" → REMOTE_PROTOCOL_ERROR
547
- // (dogfood 2026-08-06). Reuse URL non-terminal records only when no
548
- // operation id has been issued yet.
549
- if (record.sourceKind === "url") {
550
- return record.operationId === null;
551
- }
691
+ // Local uploads may resume mid-flight. URL request identities must also
692
+ // reuse the existing record: before an operation id exists, the retry
693
+ // keeps the same server idempotency key; after it exists, #submit returns
694
+ // the record without calling parse again and callers continue by status.
552
695
  return true;
553
696
  }
554
697
  if (record.state !== "COMPLETED" || record.operationId === null)
@@ -585,7 +728,9 @@ export class OperationManager {
585
728
  return record;
586
729
  }
587
730
  async #applyUpdate(record, update) {
588
- let current = record;
731
+ let current = update.result === undefined
732
+ ? record
733
+ : await this.#bindResultBilling(record, update.result);
589
734
  for (let attempt = 0; attempt < 4; attempt += 1) {
590
735
  try {
591
736
  return await this.#journal.transition(current.clientRequestId, current.state, update.state, update.patch ?? {});
@@ -663,18 +808,34 @@ export async function hydrateInlineUrlResult(result, artifactStore) {
663
808
  ...start,
664
809
  resultDigest: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
665
810
  });
666
- return { ...result, result: localResultToResultField(retention.result()) };
811
+ const local = retention.result();
812
+ return {
813
+ ...result,
814
+ result: localResultToResultField(local),
815
+ local_result_cache: createFlatLocalResultCache({
816
+ resultId: local.resultId,
817
+ resultBytes: local.resultBytes,
818
+ expiresAt: local.expiresAt,
819
+ }),
820
+ };
667
821
  }
668
822
  // hydrateInlineUrlResult is a pure local-storage optimization on top of content the caller
669
823
  // already has in full -- a local-disk failure here (e.g. disk full, permission error) must never
670
- // turn an otherwise-successful URL parse into a failure. This wrapper swallows any hydration
671
- // error and falls back to the original, un-hydrated result: the caller still gets their content;
672
- // they just lose local read_result/read_outline re-read capability for that particular result.
824
+ // turn an otherwise-successful URL parse into a failure. Preserve the delivered inline content
825
+ // and expose one exact warning instead of silently advertising unavailable local actions.
673
826
  export async function hydrateInlineUrlResultSafely(result, artifactStore) {
674
827
  try {
675
828
  return await hydrateInlineUrlResult(result, artifactStore);
676
829
  }
677
830
  catch {
831
+ if ((result.status === "completed" || result.status === "cleanup_pending")
832
+ && result.result?.kind === "inline") {
833
+ const { local_result_cache: _unavailableCache, ...delivered } = result;
834
+ return {
835
+ ...delivered,
836
+ delivery_warning: LOCAL_ARTIFACT_RETENTION_WARNING,
837
+ };
838
+ }
678
839
  return result;
679
840
  }
680
841
  }
@@ -723,6 +884,13 @@ function remoteFailure(result) {
723
884
  ...(error.constraints === undefined ? {} : { constraints: error.constraints }),
724
885
  });
725
886
  }
887
+ function retainedFlatResultId(result) {
888
+ if (result.status !== "completed" && result.status !== "cleanup_pending") {
889
+ return undefined;
890
+ }
891
+ const cache = result.local_result_cache;
892
+ return cache !== undefined && "result_id" in cache ? cache.result_id : undefined;
893
+ }
726
894
  function remoteResultUpdate(result, expectedOperationId) {
727
895
  if (result.status === "failed")
728
896
  throw remoteFailure(result);
@@ -770,6 +938,9 @@ function remoteResultUpdate(result, expectedOperationId) {
770
938
  processingCopy: "deleted",
771
939
  temporaryData: "deleted",
772
940
  deliveryResult: "deleted_after_ack",
941
+ ...(retainedFlatResultId(result) === undefined
942
+ ? {}
943
+ : { resultId: retainedFlatResultId(result) }),
773
944
  },
774
945
  result,
775
946
  };
@@ -793,6 +964,9 @@ function remoteResultUpdate(result, expectedOperationId) {
793
964
  temporaryData: result.data_handling.temporary_data,
794
965
  deliveryResult: "pending",
795
966
  resultExpiresAt: result.cleanup_deadline,
967
+ ...(retainedFlatResultId(result) === undefined
968
+ ? {}
969
+ : { resultId: retainedFlatResultId(result) }),
796
970
  },
797
971
  result,
798
972
  };
@@ -890,11 +1064,25 @@ function localResultValue(local) {
890
1064
  next_cursor: local.nextCursor,
891
1065
  };
892
1066
  }
893
- function completedLocalParse(local) {
1067
+ function localResultCache(local) {
1068
+ if (local.kind === "bundle") {
1069
+ return {
1070
+ expires_at: local.expiresAt,
1071
+ discard_action: "discard_result",
1072
+ };
1073
+ }
1074
+ return createFlatLocalResultCache({
1075
+ resultId: local.resultId,
1076
+ resultBytes: local.resultBytes,
1077
+ expiresAt: local.expiresAt,
1078
+ });
1079
+ }
1080
+ function completedLocalParse(local, billing) {
894
1081
  return {
895
1082
  status: "completed",
896
1083
  operation_id: local.operationId,
897
1084
  result: localResultValue(local),
1085
+ ...(billing === undefined ? {} : { billing }),
898
1086
  data_handling: {
899
1087
  processing_copy: "deleted",
900
1088
  temporary_data: "deleted",
@@ -902,19 +1090,15 @@ function completedLocalParse(local) {
902
1090
  original_source: "unchanged",
903
1091
  remote_content_retained: false,
904
1092
  },
905
- ...(local.kind === "artifact" || local.kind === "bundle" ? {
906
- local_result_cache: {
907
- expires_at: local.expiresAt,
908
- discard_action: "discard_result",
909
- },
910
- } : {}),
1093
+ local_result_cache: localResultCache(local),
911
1094
  };
912
1095
  }
913
- function cleanupPendingParse(local, cleanupDeadline) {
1096
+ function cleanupPendingParse(local, cleanupDeadline, billing) {
914
1097
  return {
915
1098
  status: "cleanup_pending",
916
1099
  operation_id: local.operationId,
917
1100
  result: localResultValue(local),
1101
+ ...(billing === undefined ? {} : { billing }),
918
1102
  cleanup_deadline: cleanupDeadline,
919
1103
  data_handling: {
920
1104
  processing_copy: "pending",
@@ -922,6 +1106,41 @@ function cleanupPendingParse(local, cleanupDeadline) {
922
1106
  delivery_result: "pending",
923
1107
  original_source: "unchanged",
924
1108
  },
1109
+ local_result_cache: localResultCache(local),
1110
+ };
1111
+ }
1112
+ function withRecoveredRemoteResult(result, local) {
1113
+ if (result.status !== "completed" && result.status !== "cleanup_pending") {
1114
+ return result;
1115
+ }
1116
+ return {
1117
+ ...result,
1118
+ result: localResultValue(local),
1119
+ local_result_cache: localResultCache(local),
1120
+ };
1121
+ }
1122
+ function recoveredRemoteParse(record, local) {
1123
+ if (record.operationId === null) {
1124
+ throw managerError("OPERATION_ID_UNAVAILABLE", "The remote parse operation identifier is unavailable.");
1125
+ }
1126
+ if (record.state === "CLEANUP_PENDING") {
1127
+ return {
1128
+ status: "cleanup_pending",
1129
+ operation_id: record.operationId,
1130
+ result: localResultValue(local),
1131
+ ...(record.billing === null ? {} : { billing: toWireBilling(record.billing) }),
1132
+ cleanup_deadline: record.resultExpiresAt ?? record.expiresAt ?? new Date(0).toISOString(),
1133
+ data_handling: recordDataHandling(record),
1134
+ local_result_cache: localResultCache(local),
1135
+ };
1136
+ }
1137
+ return {
1138
+ status: "completed",
1139
+ operation_id: record.operationId,
1140
+ result: localResultValue(local),
1141
+ ...(record.billing === null ? {} : { billing: toWireBilling(record.billing) }),
1142
+ data_handling: recordDataHandling(record),
1143
+ local_result_cache: localResultCache(local),
925
1144
  };
926
1145
  }
927
1146
  function canceledParse(operationId, cleanupDeadline) {
@@ -972,7 +1191,9 @@ function safeOperationStage(status, fallback) {
972
1191
  return "canceled";
973
1192
  if (status === "EXPIRED" || status === "DELIVERY_EXPIRED")
974
1193
  return "expired";
975
- if (status === "FAILED" || status === "SETTLEMENT_DENIED")
1194
+ if (status === "FAILED"
1195
+ || status === "UNSUPPORTED"
1196
+ || status === "SETTLEMENT_DENIED")
976
1197
  return "failed";
977
1198
  return safeJournalStage(fallback);
978
1199
  }
@@ -1011,6 +1232,23 @@ function requireBundleResult(retention) {
1011
1232
  }
1012
1233
  return retention.bundleResult();
1013
1234
  }
1235
+ async function requireExpectedBundleDetail(local, expectedDetail, retention) {
1236
+ if (local.kind === "bundle" && local.detail === expectedDetail)
1237
+ return local;
1238
+ try {
1239
+ await retention.abort();
1240
+ }
1241
+ catch {
1242
+ // Preserve the exact-detail protocol failure over best-effort local cleanup.
1243
+ }
1244
+ throw managerError("IIIS_INVALID_RESPONSE", "IIIS returned a result for a different representation detail.", {
1245
+ operationCreated: true,
1246
+ fileUploaded: true,
1247
+ parserStarted: true,
1248
+ billed: false,
1249
+ contentReleased: false,
1250
+ });
1251
+ }
1014
1252
  export function createLocalParseOperationManager(options) {
1015
1253
  const now = options.now ?? (() => new Date());
1016
1254
  const openFile = options.openFile ?? openAllowedFile;
@@ -1033,6 +1271,30 @@ export function createLocalParseOperationManager(options) {
1033
1271
  }
1034
1272
  throw managerError("JOURNAL_STATE_CONFLICT", "The operation changed repeatedly while saving a delivery checkpoint.", { operationCreated: true, retryable: true });
1035
1273
  }
1274
+ async function recoverArtifactAccess(record, action) {
1275
+ try {
1276
+ return await action();
1277
+ }
1278
+ catch (error) {
1279
+ const local = localFailure(error);
1280
+ throw new OmniBridgeError({
1281
+ code: local.code,
1282
+ message: local.message,
1283
+ failureScope: local.failureScope,
1284
+ sourceKind: local.sourceKind,
1285
+ userAction: local.userAction,
1286
+ requestId: local.requestId,
1287
+ operationCreated: local.operationCreated || record.operationId !== null,
1288
+ fileUploaded: local.fileUploaded || record.fileUploaded,
1289
+ parserStarted: local.parserStarted || record.parserStarted,
1290
+ billed: local.billed || record.billed,
1291
+ contentReleased: local.contentReleased || record.contentReleased,
1292
+ retryable: local.retryable,
1293
+ retryAfter: local.retryAfter,
1294
+ constraints: local.constraints,
1295
+ });
1296
+ }
1297
+ }
1036
1298
  // A retained logical bundle is reconstructed through its closed descriptor
1037
1299
  // and keeps the stored representation tuple; without the descriptor the
1038
1300
  // recovery fails safe instead of degrading the bundle to text.
@@ -1056,7 +1318,7 @@ export function createLocalParseOperationManager(options) {
1056
1318
  contentReleased: record.contentReleased,
1057
1319
  });
1058
1320
  }
1059
- const descriptor = await options.artifactStore.readBundleDescriptor(record.resultId);
1321
+ const descriptor = await recoverArtifactAccess(record, () => options.artifactStore.readBundleDescriptor(record.resultId));
1060
1322
  if (descriptor === null || descriptor.kind !== "bundle") {
1061
1323
  throw managerError("RESULT_NOT_AVAILABLE", "The retained local result bundle is unavailable.", {
1062
1324
  operationCreated: true,
@@ -1068,11 +1330,11 @@ export function createLocalParseOperationManager(options) {
1068
1330
  }
1069
1331
  return descriptor;
1070
1332
  }
1071
- const preview = await options.artifactStore.read(record.resultId, undefined, 2_048);
1333
+ const preview = await recoverArtifactAccess(record, () => options.artifactStore.read(record.resultId, undefined, 2_048));
1072
1334
  if (preview.resultBytes <= INLINE_RESULT_MAX_BYTES) {
1073
1335
  const recovered = preview.nextCursor === undefined
1074
1336
  ? preview
1075
- : await options.artifactStore.read(record.resultId, undefined, INLINE_RESULT_MAX_BYTES);
1337
+ : await recoverArtifactAccess(record, () => options.artifactStore.read(record.resultId, undefined, INLINE_RESULT_MAX_BYTES));
1076
1338
  if (recovered.nextCursor !== undefined) {
1077
1339
  throw managerError("LOCAL_RESULT_INTEGRITY_FAILED", "The retained inline result was not fully readable.", {
1078
1340
  operationCreated: true,
@@ -1101,6 +1363,16 @@ export function createLocalParseOperationManager(options) {
1101
1363
  ...(preview.nextCursor === undefined ? {} : { nextCursor: preview.nextCursor }),
1102
1364
  };
1103
1365
  }
1366
+ async function preflightArtifactDelivery(input, recovery) {
1367
+ const delivery = recovery?.resultDeliveryEffective ?? input.resultDelivery ?? "auto";
1368
+ if (delivery !== "artifact")
1369
+ return;
1370
+ if (options.artifactStore.preflightWrite === undefined
1371
+ || options.artifactStore.mintReadCursor === undefined) {
1372
+ throw managerError("ARTIFACT_PREFLIGHT_FAILED", "The local result store cannot provide artifact delivery.");
1373
+ }
1374
+ await options.artifactStore.preflightWrite();
1375
+ }
1104
1376
  async function startExecution(input, recovery) {
1105
1377
  // Recovery keeps the stored representation tuple exactly: status, download
1106
1378
  // (recoverLocalArtifact), and ACK all operate on the representation the
@@ -1136,6 +1408,7 @@ export function createLocalParseOperationManager(options) {
1136
1408
  throw managerError("SOURCE_CHANGED_DURING_SUBMISSION", "The local source changed before the upload operation was created.", { retryable: true });
1137
1409
  }
1138
1410
  try {
1411
+ await preflightArtifactDelivery(input, recovery);
1139
1412
  // D2-D Task 14: the normalized detail reaches the grant boundary so
1140
1413
  // CubeGrantClient selects the exact direct profile BEFORE constructing
1141
1414
  // the v3 request and requires the v3 response tuple before any upload.
@@ -1147,6 +1420,9 @@ export function createLocalParseOperationManager(options) {
1147
1420
  output: "markdown",
1148
1421
  ...(representation.detail === "text" ? {} : { detail: representation.detail }),
1149
1422
  }, input.clientRequestId, input.signal, { journal: false });
1423
+ if (isBillingProfile(granted.directProfile)) {
1424
+ await options.journal.bindDirectProfile(input.clientRequestId, granted.directProfile);
1425
+ }
1150
1426
  }
1151
1427
  catch (error) {
1152
1428
  await opened.close().catch(() => undefined);
@@ -1208,9 +1484,17 @@ export function createLocalParseOperationManager(options) {
1208
1484
  await (context?.progress ?? NOOP_PROGRESS).report(value, total, message, latestProgress ?? undefined);
1209
1485
  },
1210
1486
  };
1487
+ const expectedDetail = granted?.requestedDetail ?? representation.detail;
1488
+ if (expectedDetail !== representation.detail) {
1489
+ await opened?.close().catch(() => undefined);
1490
+ opened = undefined;
1491
+ throw managerError("CUBE_PROTOCOL_ERROR", "Cube returned a grant for a different representation detail.", { operationCreated: true });
1492
+ }
1211
1493
  const operation = {
1212
1494
  operationId,
1213
1495
  operationToken,
1496
+ directProfile: granted?.directProfile ?? recovery?.directProfile ?? null,
1497
+ expectedDetail,
1214
1498
  ...(granted === undefined || opened === undefined
1215
1499
  ? {}
1216
1500
  : {
@@ -1255,18 +1539,19 @@ export function createLocalParseOperationManager(options) {
1255
1539
  recovery?.state === "ACK_PENDING" ||
1256
1540
  recovery?.state === "CLEANUP_PENDING";
1257
1541
  let local;
1542
+ let released;
1258
1543
  if (deliveryRecovery && recovery.resultId !== null) {
1259
1544
  local = await recoverLocalArtifact(recovery);
1260
1545
  }
1261
1546
  else {
1262
1547
  if (deliveryRecovery) {
1263
- await options.iiisClient.downloadResult(operation, progress);
1548
+ released = await options.iiisClient.downloadResult(operation, progress);
1264
1549
  }
1265
1550
  else if (recovery?.state === "UPLOADING" || recovery?.state === "PROCESSING") {
1266
- await options.iiisClient.recoverAndWait(operation);
1551
+ released = await options.iiisClient.recoverAndWait(operation);
1267
1552
  }
1268
1553
  else {
1269
- await options.iiisClient.uploadAndWait(operation);
1554
+ released = await options.iiisClient.uploadAndWait(operation);
1270
1555
  }
1271
1556
  // Non-text results require the durable BundleLocalResult (both named
1272
1557
  // parts verified and fsynced); ACK_PENDING is persisted only after
@@ -1275,6 +1560,27 @@ export function createLocalParseOperationManager(options) {
1275
1560
  ? requireBundleResult(retention)
1276
1561
  : retention.result();
1277
1562
  }
1563
+ if (representation.detail !== "text") {
1564
+ local = await requireExpectedBundleDetail(local, representation.detail, retention);
1565
+ }
1566
+ const directProfile = released?.directProfile
1567
+ ?? recovery?.directProfile
1568
+ ?? operation.directProfile
1569
+ ?? null;
1570
+ if (released?.directProfile !== undefined
1571
+ && released.directProfile !== (operation.directProfile ?? null)) {
1572
+ throw journalIntegrityError("IIIS changed the selected direct profile.", recovery);
1573
+ }
1574
+ const recoveredBilling = recovery?.billing === null || recovery?.billing === undefined
1575
+ ? null
1576
+ : toWireBilling(recovery.billing);
1577
+ const wireBilling = requireWireBillingForSettledProfile(directProfile, true, released?.billing ?? recoveredBilling);
1578
+ if (wireBilling !== undefined) {
1579
+ await options.journal.bindSettlementFacts(input.clientRequestId, {
1580
+ directProfile,
1581
+ billing: toJournalBilling(wireBilling),
1582
+ });
1583
+ }
1278
1584
  const cleanupDeadline = recovery?.resultExpiresAt ?? new Date(now().getTime() + DELIVERY_TTL_SECONDS * 1000).toISOString();
1279
1585
  const resultPatch = {
1280
1586
  fileUploaded: true,
@@ -1325,7 +1631,7 @@ export function createLocalParseOperationManager(options) {
1325
1631
  deliveryResult: "deleted_after_ack",
1326
1632
  },
1327
1633
  },
1328
- result: completedLocalParse(local),
1634
+ result: completedLocalParse(local, wireBilling),
1329
1635
  };
1330
1636
  }
1331
1637
  catch (error) {
@@ -1349,6 +1655,7 @@ export function createLocalParseOperationManager(options) {
1349
1655
  result: processingParse(operationId, stage, latestPercent, confirmed.contentReleased === true),
1350
1656
  };
1351
1657
  }
1658
+ assertDirectTerminalSnapshot(operation.directProfile ?? null, inspected);
1352
1659
  const patch = {
1353
1660
  ...confirmed,
1354
1661
  fileUploaded: confirmed.fileUploaded === true || inspected.fileUploaded,
@@ -1388,7 +1695,9 @@ export function createLocalParseOperationManager(options) {
1388
1695
  },
1389
1696
  };
1390
1697
  }
1391
- if (inspected.status === "FAILED" || inspected.status === "SETTLEMENT_DENIED") {
1698
+ if (inspected.status === "FAILED"
1699
+ || inspected.status === "UNSUPPORTED"
1700
+ || inspected.status === "SETTLEMENT_DENIED") {
1392
1701
  const failed = managerError(inspected.status, `The Omni operation ended with status ${inspected.status}.`, {
1393
1702
  operationCreated: true,
1394
1703
  fileUploaded: patch.fileUploaded,
@@ -1444,12 +1753,14 @@ export function createLocalParseOperationManager(options) {
1444
1753
  state: "FAILED",
1445
1754
  patch: {
1446
1755
  ...failurePatch(stable),
1447
- // The ACK checkpoint may already have recorded a settled charge
1448
- // (snapshot.patch at :1751 carries resultPatch's billed fact).
1449
- // Never regress it assertStableTransition blocks true->false
1450
- // the charge happened; this failure is about the local artifact,
1451
- // not the settlement.
1452
- billed: checkpointBilled || stable.billed,
1756
+ // Recovery starts from persisted monotonic facts. A protocol
1757
+ // rejection cannot make a confirmed upload, parser start,
1758
+ // settlement, or release become false merely because the invalid
1759
+ // response itself was not trusted.
1760
+ fileUploaded: recovery?.fileUploaded === true || stable.fileUploaded,
1761
+ parserStarted: recovery?.parserStarted === true || stable.parserStarted,
1762
+ billed: checkpointBilled || recovery?.billed === true || stable.billed,
1763
+ contentReleased: recovery?.contentReleased === true || stable.contentReleased,
1453
1764
  },
1454
1765
  },
1455
1766
  result: {
@@ -1492,6 +1803,7 @@ export function createLocalParseOperationManager(options) {
1492
1803
  assertSameRepresentation(input, recovery);
1493
1804
  }
1494
1805
  const context = remoteContext(input.context);
1806
+ await preflightArtifactDelivery(input, recovery);
1495
1807
  const result = await options.remoteClient.parse(context.source, input.clientRequestId, input.signal ?? new AbortController().signal, representation.detail);
1496
1808
  const hydrated = await hydrateInlineUrlResultSafely(result, options.artifactStore);
1497
1809
  return remoteResultUpdate(hydrated, recovery?.operationId ?? undefined);
@@ -1507,9 +1819,30 @@ export function createLocalParseOperationManager(options) {
1507
1819
  if (record.operationId === null)
1508
1820
  return undefined;
1509
1821
  if (record.sourceKind === "url") {
1510
- if (options.remoteClient === undefined)
1511
- return undefined;
1822
+ if (options.remoteClient === undefined) {
1823
+ if (record.resultId === null || record.state !== "CLEANUP_PENDING") {
1824
+ return undefined;
1825
+ }
1826
+ return {
1827
+ state: record.state,
1828
+ result: recoveredRemoteParse(record, await recoverLocalArtifact(record)),
1829
+ };
1830
+ }
1512
1831
  const result = await options.remoteClient.status(record.operationId, waitMs, signal);
1832
+ if (record.resultId !== null && record.state === "CLEANUP_PENDING") {
1833
+ let local;
1834
+ try {
1835
+ local = await recoverLocalArtifact(record);
1836
+ }
1837
+ catch {
1838
+ // A missing or corrupt retained copy must not block authoritative
1839
+ // remote status recovery. Rehydrate the returned body when it is
1840
+ // available, preserving the existing safe warning behavior.
1841
+ }
1842
+ if (local !== undefined) {
1843
+ return remoteResultUpdate(withRecoveredRemoteResult(result, local), record.operationId);
1844
+ }
1845
+ }
1513
1846
  const hydrated = await hydrateInlineUrlResultSafely(result, options.artifactStore);
1514
1847
  return remoteResultUpdate(hydrated, record.operationId);
1515
1848
  }
@@ -1540,6 +1873,8 @@ export function createLocalParseOperationManager(options) {
1540
1873
  const operation = {
1541
1874
  operationId: record.operationId,
1542
1875
  operationToken: record.operationToken,
1876
+ directProfile: record.directProfile,
1877
+ expectedDetail: record.detail,
1543
1878
  retention: {
1544
1879
  async reset() { },
1545
1880
  async begin() { },
@@ -1552,6 +1887,7 @@ export function createLocalParseOperationManager(options) {
1552
1887
  const inspected = options.iiisClient.cancelOperation === undefined
1553
1888
  ? await options.iiisClient.inspectOperation(operation)
1554
1889
  : await options.iiisClient.cancelOperation(operation);
1890
+ assertDirectTerminalSnapshot(operation.directProfile ?? null, inspected);
1555
1891
  const patch = {
1556
1892
  fileUploaded: inspected.fileUploaded,
1557
1893
  parserStarted: inspected.parserStarted,
@@ -1585,7 +1921,9 @@ export function createLocalParseOperationManager(options) {
1585
1921
  },
1586
1922
  };
1587
1923
  }
1588
- if (inspected.status === "FAILED" || inspected.status === "SETTLEMENT_DENIED") {
1924
+ if (inspected.status === "FAILED"
1925
+ || inspected.status === "UNSUPPORTED"
1926
+ || inspected.status === "SETTLEMENT_DENIED") {
1589
1927
  return {
1590
1928
  state: "FAILED",
1591
1929
  patch: { ...patch, errorCode: inspected.status },
@@ -1611,7 +1949,16 @@ export function createLocalParseOperationManager(options) {
1611
1949
  if (record.operationId === null || record.state !== "COMPLETED")
1612
1950
  return undefined;
1613
1951
  if (record.sourceKind === "local" && record.resultId !== null) {
1614
- return completedLocalParse(await recoverLocalArtifact(record));
1952
+ return completedLocalParse(await recoverLocalArtifact(record), record.billing === null ? undefined : toWireBilling(record.billing));
1953
+ }
1954
+ if (record.sourceKind === "url" && record.resultId !== null) {
1955
+ try {
1956
+ return recoveredRemoteParse(record, await recoverLocalArtifact(record));
1957
+ }
1958
+ catch (error) {
1959
+ if (options.remoteClient === undefined)
1960
+ throw error;
1961
+ }
1615
1962
  }
1616
1963
  if (record.sourceKind === "url" && options.remoteClient !== undefined) {
1617
1964
  const result = await options.remoteClient.status(record.operationId, 0, signal);
@@ -1624,6 +1971,18 @@ export function createLocalParseOperationManager(options) {
1624
1971
  return new OperationManager({
1625
1972
  journal: options.journal,
1626
1973
  driver,
1974
+ presentResult: (record, canonicalResult) => presentRetainedResult(canonicalResult, record.resultDeliveryEffective, async (resultId, byteOffset) => {
1975
+ if (options.artifactStore.mintReadCursor === undefined) {
1976
+ throw managerError("LOCAL_RESULT_INTEGRITY_FAILED", "The retained result cursor capability is unavailable.", {
1977
+ operationCreated: record.operationId !== null,
1978
+ fileUploaded: record.fileUploaded,
1979
+ parserStarted: record.parserStarted,
1980
+ billed: record.billed,
1981
+ contentReleased: record.contentReleased,
1982
+ });
1983
+ }
1984
+ return options.artifactStore.mintReadCursor(resultId, byteOffset);
1985
+ }),
1627
1986
  now: () => now().getTime(),
1628
1987
  sleep: options.sleep,
1629
1988
  });