@cueai/omni-reader-mcp 1.5.4 → 1.6.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.
- package/README.md +72 -39
- package/dist/artifact-store.d.ts +2 -0
- package/dist/artifact-store.js +45 -10
- package/dist/cli/agent-config.js +2 -2
- package/dist/cli/arguments.d.ts +8 -4
- package/dist/cli/arguments.js +62 -7
- package/dist/cli/config-inspection.d.ts +59 -0
- package/dist/cli/config-inspection.js +307 -0
- package/dist/cli/doctor.d.ts +7 -0
- package/dist/cli/doctor.js +37 -2
- package/dist/cli/setup.js +13 -2
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +4 -3
- package/dist/cube-client.js +1 -1
- package/dist/cursor.d.ts +4 -0
- package/dist/cursor.js +11 -13
- package/dist/index.js +10 -1
- package/dist/operation-journal.d.ts +4 -1
- package/dist/operation-journal.js +85 -15
- package/dist/operation-manager.d.ts +6 -2
- package/dist/operation-manager.js +246 -63
- package/dist/path-normalization.d.ts +5 -0
- package/dist/path-normalization.js +25 -0
- package/dist/path-security.d.ts +2 -1
- package/dist/path-security.js +49 -28
- package/dist/protocol.d.ts +10 -2
- package/dist/protocol.js +19 -7
- package/dist/result-contract.d.ts +46 -6
- package/dist/result-contract.js +118 -3
- package/dist/task-runtime.d.ts +3 -2
- package/dist/task-runtime.js +2 -2
- package/dist/tools.d.ts +4 -0
- package/dist/tools.js +37 -16
- package/package.json +14 -1
|
@@ -4,7 +4,7 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { OmniBridgeError } from "./errors.js";
|
|
6
6
|
import { GROUNDING_SCHEMA_VERSION, RESULT_BUNDLE_PROTOCOL_VERSION, normalizeRepresentation, } from "./protocol.js";
|
|
7
|
-
// D2-D item 4-6: v3 records persist keyed identities instead of unkeyed
|
|
7
|
+
// D2-D item 4-6: v3/v4 records persist keyed identities instead of unkeyed
|
|
8
8
|
// request/source hashes. requestIdentityHmac and sourceLocatorHmac are
|
|
9
9
|
// lowercase full hex of HMAC-SHA-256 over the journal secret with the exact
|
|
10
10
|
// domain prefixes below; they are never returned in tool output and never
|
|
@@ -43,6 +43,10 @@ const PERSISTED_V3_KEYS = [
|
|
|
43
43
|
"resultExpiresAt",
|
|
44
44
|
"errorCode",
|
|
45
45
|
];
|
|
46
|
+
const PERSISTED_V4_KEYS = [
|
|
47
|
+
...PERSISTED_V3_KEYS,
|
|
48
|
+
"result_delivery_effective",
|
|
49
|
+
];
|
|
46
50
|
const JOURNAL_STATES = new Set([
|
|
47
51
|
"CREATED",
|
|
48
52
|
"GRANT_PENDING",
|
|
@@ -81,6 +85,7 @@ const DELIVERY_STATES = new Set([
|
|
|
81
85
|
"pending",
|
|
82
86
|
"deleted_after_ack",
|
|
83
87
|
]);
|
|
88
|
+
const RESULT_DELIVERIES = new Set(["auto", "artifact"]);
|
|
84
89
|
const RECORD_FILE_PATTERN = /^[0-9a-f]{64}(?:\.issued)?\.json$/u;
|
|
85
90
|
const LOCK_STALE_MS = 30_000;
|
|
86
91
|
const HEX_64_PATTERN = /^[0-9a-f]{64}$/u;
|
|
@@ -255,6 +260,26 @@ function parseVersionThreeRecord(value) {
|
|
|
255
260
|
}
|
|
256
261
|
return value;
|
|
257
262
|
}
|
|
263
|
+
function parseVersionFourRecord(value) {
|
|
264
|
+
const keys = Object.keys(value);
|
|
265
|
+
if (keys.length !== PERSISTED_V4_KEYS.length ||
|
|
266
|
+
PERSISTED_V4_KEYS.some((key) => !(key in value)) ||
|
|
267
|
+
value.version !== 4 ||
|
|
268
|
+
typeof value.result_delivery_effective !== "string" ||
|
|
269
|
+
!RESULT_DELIVERIES.has(value.result_delivery_effective)) {
|
|
270
|
+
throw new Error("version-4 record fields are invalid");
|
|
271
|
+
}
|
|
272
|
+
const { result_delivery_effective, ...versionThreeFields } = value;
|
|
273
|
+
const validated = parseVersionThreeRecord({
|
|
274
|
+
...versionThreeFields,
|
|
275
|
+
version: 3,
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
...validated,
|
|
279
|
+
version: 4,
|
|
280
|
+
result_delivery_effective: result_delivery_effective,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
258
283
|
function parsePersistedRecord(value) {
|
|
259
284
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
260
285
|
throw new Error("record is not an object");
|
|
@@ -266,12 +291,14 @@ function parsePersistedRecord(value) {
|
|
|
266
291
|
return parseVersionTwoRecord(record);
|
|
267
292
|
if (record.version === 3)
|
|
268
293
|
return parseVersionThreeRecord(record);
|
|
294
|
+
if (record.version === 4)
|
|
295
|
+
return parseVersionFourRecord(record);
|
|
269
296
|
throw new Error("record version is invalid");
|
|
270
297
|
}
|
|
271
298
|
function migrateInMemory(stored) {
|
|
272
|
-
if (stored.version === 3) {
|
|
299
|
+
if (stored.version === 3 || stored.version === 4) {
|
|
273
300
|
return {
|
|
274
|
-
version:
|
|
301
|
+
version: 4,
|
|
275
302
|
clientRequestId: stored.clientRequestId,
|
|
276
303
|
requestIdentityHmac: stored.requestIdentityHmac,
|
|
277
304
|
sourceLocatorHmac: stored.sourceLocatorHmac,
|
|
@@ -282,6 +309,9 @@ function migrateInMemory(stored) {
|
|
|
282
309
|
detail: stored.detail,
|
|
283
310
|
groundingSchemaVersion: stored.groundingSchemaVersion,
|
|
284
311
|
bundleProtocolVersion: stored.bundleProtocolVersion,
|
|
312
|
+
resultDeliveryEffective: stored.version === 4
|
|
313
|
+
? stored.result_delivery_effective
|
|
314
|
+
: "auto",
|
|
285
315
|
operationId: stored.operationId,
|
|
286
316
|
operationToken: stored.operationToken,
|
|
287
317
|
state: stored.state,
|
|
@@ -308,7 +338,7 @@ function migrateInMemory(stored) {
|
|
|
308
338
|
// are recomputed only when safe canonical source facts reconstruct the
|
|
309
339
|
// stored unkeyed requestHash (see #reconstructLegacy).
|
|
310
340
|
return {
|
|
311
|
-
version:
|
|
341
|
+
version: 4,
|
|
312
342
|
clientRequestId: stored.clientRequestId,
|
|
313
343
|
requestIdentityHmac: null,
|
|
314
344
|
sourceLocatorHmac: null,
|
|
@@ -319,6 +349,7 @@ function migrateInMemory(stored) {
|
|
|
319
349
|
detail: "text",
|
|
320
350
|
groundingSchemaVersion: "none",
|
|
321
351
|
bundleProtocolVersion: "none",
|
|
352
|
+
resultDeliveryEffective: "auto",
|
|
322
353
|
operationId: stored.operationId,
|
|
323
354
|
operationToken: stored.operationToken,
|
|
324
355
|
state: stored.state,
|
|
@@ -341,7 +372,7 @@ function migrateInMemory(stored) {
|
|
|
341
372
|
};
|
|
342
373
|
}
|
|
343
374
|
return {
|
|
344
|
-
version:
|
|
375
|
+
version: 4,
|
|
345
376
|
clientRequestId: stored.clientRequestId,
|
|
346
377
|
requestIdentityHmac: null,
|
|
347
378
|
sourceLocatorHmac: null,
|
|
@@ -352,6 +383,7 @@ function migrateInMemory(stored) {
|
|
|
352
383
|
detail: "text",
|
|
353
384
|
groundingSchemaVersion: "none",
|
|
354
385
|
bundleProtocolVersion: "none",
|
|
386
|
+
resultDeliveryEffective: "auto",
|
|
355
387
|
operationId: stored.operationId,
|
|
356
388
|
operationToken: stored.operationToken,
|
|
357
389
|
state: stored.state,
|
|
@@ -428,6 +460,10 @@ function assertStableTransition(current, updated) {
|
|
|
428
460
|
throw bridgeError("JOURNAL_FACT_REGRESSION", "A confirmed local operation fact cannot be reverted.", false);
|
|
429
461
|
}
|
|
430
462
|
}
|
|
463
|
+
if (current.resultDeliveryEffective === "artifact" &&
|
|
464
|
+
updated.resultDeliveryEffective !== "artifact") {
|
|
465
|
+
throw bridgeError("JOURNAL_RESULT_DELIVERY_REGRESSION", "Artifact result delivery cannot be weakened to automatic delivery.", false);
|
|
466
|
+
}
|
|
431
467
|
}
|
|
432
468
|
function hmacEqual(left, right) {
|
|
433
469
|
if (left.length !== right.length)
|
|
@@ -451,19 +487,22 @@ export class OperationJournal {
|
|
|
451
487
|
async sourceLocatorHmac(sourceLocator) {
|
|
452
488
|
return this.#hmac(SOURCE_LOCATOR_DOMAIN, sourceLocator);
|
|
453
489
|
}
|
|
454
|
-
//
|
|
490
|
+
// v4 intent creation. canonicalIdentityJson is the exact canonical
|
|
455
491
|
// serialization of {source_kind, source_facts} (which includes the
|
|
456
492
|
// normalized representation tuple); the journal derives the keyed identity
|
|
457
493
|
// from it and persists only the HMAC, never the payload or any unkeyed hash.
|
|
458
|
-
async beginIntent(clientRequestId, canonicalIdentityJson, sourceKind = "local", sourceLocator = null, representation = TEXT_REPRESENTATION) {
|
|
494
|
+
async beginIntent(clientRequestId, canonicalIdentityJson, sourceKind = "local", sourceLocator = null, representation = TEXT_REPRESENTATION, resultDelivery = "auto") {
|
|
459
495
|
validateClientRequestId(clientRequestId);
|
|
496
|
+
if (!RESULT_DELIVERIES.has(resultDelivery)) {
|
|
497
|
+
throw bridgeError("INVALID_RESULT_DELIVERY", "The result delivery mode is invalid.", false);
|
|
498
|
+
}
|
|
460
499
|
const existing = await this.#readRecord(clientRequestId);
|
|
461
500
|
if (existing !== null) {
|
|
462
501
|
return this.#requireMatchingRequest(clientRequestId, canonicalIdentityJson, sourceLocator, existing);
|
|
463
502
|
}
|
|
464
503
|
const createdAt = this.#now().toISOString();
|
|
465
504
|
const created = {
|
|
466
|
-
version:
|
|
505
|
+
version: 4,
|
|
467
506
|
clientRequestId,
|
|
468
507
|
requestIdentityHmac: await this.#hmac(REQUEST_IDENTITY_DOMAIN, canonicalIdentityJson),
|
|
469
508
|
sourceLocatorHmac: sourceLocator === null
|
|
@@ -476,6 +515,7 @@ export class OperationJournal {
|
|
|
476
515
|
detail: representation.detail,
|
|
477
516
|
groundingSchemaVersion: representation.groundingSchemaVersion,
|
|
478
517
|
bundleProtocolVersion: representation.bundleProtocolVersion,
|
|
518
|
+
resultDeliveryEffective: resultDelivery,
|
|
479
519
|
operationId: null,
|
|
480
520
|
operationToken: null,
|
|
481
521
|
state: "CREATED",
|
|
@@ -507,7 +547,7 @@ export class OperationJournal {
|
|
|
507
547
|
}
|
|
508
548
|
// Migrate a legacy v1/v2 record using the caller's safe canonical source
|
|
509
549
|
// facts. When the facts reconstruct the stored unkeyed requestHash, the
|
|
510
|
-
// record migrates to
|
|
550
|
+
// record migrates to v4 (durably) with domain-separated HMACs and keeps its
|
|
511
551
|
// state; otherwise it becomes a terminal source-free recovery failure and
|
|
512
552
|
// never copies the unkeyed hashes. Returns null when no record exists.
|
|
513
553
|
async migrateLegacyRecord(clientRequestId, canonicalIdentityJson, sourceLocator = null) {
|
|
@@ -515,8 +555,9 @@ export class OperationJournal {
|
|
|
515
555
|
const stored = await this.#readRecord(clientRequestId);
|
|
516
556
|
if (stored === null)
|
|
517
557
|
return null;
|
|
518
|
-
if (stored.version === 3)
|
|
558
|
+
if (stored.version === 3 || stored.version === 4) {
|
|
519
559
|
return this.#recordToPublic(clientRequestId, stored);
|
|
560
|
+
}
|
|
520
561
|
const expected = `sha256:${createHash("sha256").update(canonicalIdentityJson, "utf8").digest("hex")}`;
|
|
521
562
|
if (stored.requestHash === expected) {
|
|
522
563
|
const migrated = await this.#reconstructLegacy(stored, canonicalIdentityJson, sourceLocator);
|
|
@@ -559,6 +600,31 @@ export class OperationJournal {
|
|
|
559
600
|
return updated;
|
|
560
601
|
});
|
|
561
602
|
}
|
|
603
|
+
async strengthenResultDelivery(clientRequestId, requested) {
|
|
604
|
+
validateClientRequestId(clientRequestId);
|
|
605
|
+
if (!RESULT_DELIVERIES.has(requested)) {
|
|
606
|
+
throw bridgeError("INVALID_RESULT_DELIVERY", "The result delivery mode is invalid.", false);
|
|
607
|
+
}
|
|
608
|
+
return this.#withRecordLock(clientRequestId, async () => {
|
|
609
|
+
const stored = await this.#readRecord(clientRequestId);
|
|
610
|
+
if (stored === null) {
|
|
611
|
+
throw bridgeError("JOURNAL_RECORD_NOT_FOUND", "The local operation journal record is missing.", true);
|
|
612
|
+
}
|
|
613
|
+
const current = await this.#recordToPublic(clientRequestId, stored);
|
|
614
|
+
if (requested === "auto" || current.resultDeliveryEffective === "artifact") {
|
|
615
|
+
return current;
|
|
616
|
+
}
|
|
617
|
+
const updated = {
|
|
618
|
+
...current,
|
|
619
|
+
resultDeliveryEffective: "artifact",
|
|
620
|
+
updatedAt: this.#now().toISOString(),
|
|
621
|
+
};
|
|
622
|
+
assertStableTransition(current, updated);
|
|
623
|
+
await this.#replaceRecord(await this.#persistedRecord(updated), this.#recordPath(clientRequestId));
|
|
624
|
+
await unlink(this.#legacyIssuedRecordPath(clientRequestId)).catch(() => undefined);
|
|
625
|
+
return updated;
|
|
626
|
+
});
|
|
627
|
+
}
|
|
562
628
|
async loadByRequestId(clientRequestId) {
|
|
563
629
|
validateClientRequestId(clientRequestId);
|
|
564
630
|
const persisted = await this.#readRecord(clientRequestId);
|
|
@@ -592,7 +658,7 @@ export class OperationJournal {
|
|
|
592
658
|
// the journal receives, so the keyed handle is derived over that value.
|
|
593
659
|
const createdAt = this.#now().toISOString();
|
|
594
660
|
const created = {
|
|
595
|
-
version:
|
|
661
|
+
version: 4,
|
|
596
662
|
clientRequestId,
|
|
597
663
|
requestIdentityHmac: await this.#hmac(REQUEST_IDENTITY_DOMAIN, requestHash),
|
|
598
664
|
sourceLocatorHmac: null,
|
|
@@ -603,6 +669,7 @@ export class OperationJournal {
|
|
|
603
669
|
detail: "text",
|
|
604
670
|
groundingSchemaVersion: "none",
|
|
605
671
|
bundleProtocolVersion: "none",
|
|
672
|
+
resultDeliveryEffective: "auto",
|
|
606
673
|
operationId: null,
|
|
607
674
|
operationToken: null,
|
|
608
675
|
state: "CREATED",
|
|
@@ -698,7 +765,7 @@ export class OperationJournal {
|
|
|
698
765
|
}
|
|
699
766
|
async #persistedRecord(record) {
|
|
700
767
|
return {
|
|
701
|
-
version:
|
|
768
|
+
version: 4,
|
|
702
769
|
clientRequestId: record.clientRequestId,
|
|
703
770
|
// A legacy record persisted before reconstruction (for example a
|
|
704
771
|
// source-free recovery failure) gets the deterministic keyed handle of
|
|
@@ -711,6 +778,7 @@ export class OperationJournal {
|
|
|
711
778
|
detail: record.detail,
|
|
712
779
|
groundingSchemaVersion: record.groundingSchemaVersion,
|
|
713
780
|
bundleProtocolVersion: record.bundleProtocolVersion,
|
|
781
|
+
result_delivery_effective: record.resultDeliveryEffective,
|
|
714
782
|
operationId: record.operationId,
|
|
715
783
|
operationToken: record.operationToken === null
|
|
716
784
|
? null
|
|
@@ -736,7 +804,7 @@ export class OperationJournal {
|
|
|
736
804
|
}
|
|
737
805
|
async #persistedFromInMemory(record) {
|
|
738
806
|
return {
|
|
739
|
-
version:
|
|
807
|
+
version: 4,
|
|
740
808
|
clientRequestId: record.clientRequestId,
|
|
741
809
|
requestIdentityHmac: record.requestIdentityHmac === null
|
|
742
810
|
? await this.#hmac(REQUEST_IDENTITY_DOMAIN, "")
|
|
@@ -746,6 +814,7 @@ export class OperationJournal {
|
|
|
746
814
|
detail: record.detail,
|
|
747
815
|
groundingSchemaVersion: record.groundingSchemaVersion,
|
|
748
816
|
bundleProtocolVersion: record.bundleProtocolVersion,
|
|
817
|
+
result_delivery_effective: record.resultDeliveryEffective,
|
|
749
818
|
operationId: record.operationId,
|
|
750
819
|
operationToken: record.operationToken,
|
|
751
820
|
state: record.state,
|
|
@@ -784,6 +853,7 @@ export class OperationJournal {
|
|
|
784
853
|
detail: migrated.detail,
|
|
785
854
|
groundingSchemaVersion: migrated.groundingSchemaVersion,
|
|
786
855
|
bundleProtocolVersion: migrated.bundleProtocolVersion,
|
|
856
|
+
resultDeliveryEffective: migrated.resultDeliveryEffective,
|
|
787
857
|
operationId: migrated.operationId,
|
|
788
858
|
operationToken,
|
|
789
859
|
uploadUrl: migrated.uploadUrl,
|
|
@@ -819,7 +889,7 @@ export class OperationJournal {
|
|
|
819
889
|
return migrated;
|
|
820
890
|
}
|
|
821
891
|
async #requireMatchingRequest(clientRequestId, canonicalIdentityJson, sourceLocator, persisted) {
|
|
822
|
-
if (persisted.version === 3) {
|
|
892
|
+
if (persisted.version === 3 || persisted.version === 4) {
|
|
823
893
|
const expected = await this.#hmacExisting(REQUEST_IDENTITY_DOMAIN, canonicalIdentityJson);
|
|
824
894
|
if (!hmacEqual(persisted.requestIdentityHmac, expected)) {
|
|
825
895
|
throw bridgeError("IDEMPOTENCY_COLLISION", "This local operation request identifier is already bound to different metadata.", false);
|
|
@@ -833,7 +903,7 @@ export class OperationJournal {
|
|
|
833
903
|
throw bridgeError("IDEMPOTENCY_COLLISION", "This local operation request identifier is already bound to different metadata.", false);
|
|
834
904
|
}
|
|
835
905
|
async #requireMatchingCubeRequest(clientRequestId, requestHash, persisted) {
|
|
836
|
-
if (persisted.version === 3) {
|
|
906
|
+
if (persisted.version === 3 || persisted.version === 4) {
|
|
837
907
|
const expected = await this.#hmacExisting(REQUEST_IDENTITY_DOMAIN, requestHash);
|
|
838
908
|
if (!hmacEqual(persisted.requestIdentityHmac, expected)) {
|
|
839
909
|
throw bridgeError("IDEMPOTENCY_COLLISION", "This local operation request identifier is already bound to different metadata.", false);
|
|
@@ -3,7 +3,7 @@ import type { CubeGrantClient } from "./cube-client.js";
|
|
|
3
3
|
import type { IiisClient, ResultRetentionSink } from "./iiis-client.js";
|
|
4
4
|
import type { JournalPatch, JournalRecord, JournalState, OperationJournal } from "./operation-journal.js";
|
|
5
5
|
import { type OpenAllowedFileOptions, type OpenedAllowedFile } from "./path-security.js";
|
|
6
|
-
import { type RepresentationIntent } from "./protocol.js";
|
|
6
|
+
import { type RepresentationIntent, type ResultDelivery } from "./protocol.js";
|
|
7
7
|
import type { RemoteOmniClient } from "./remote-client.js";
|
|
8
8
|
import { type ParseResult } from "./result-contract.js";
|
|
9
9
|
export interface SubmitOperationInput {
|
|
@@ -12,6 +12,7 @@ export interface SubmitOperationInput {
|
|
|
12
12
|
readonly clientRequestId: string;
|
|
13
13
|
readonly signal?: AbortSignal;
|
|
14
14
|
readonly context?: unknown;
|
|
15
|
+
readonly resultDelivery?: ResultDelivery;
|
|
15
16
|
readonly representation?: RepresentationIntent;
|
|
16
17
|
}
|
|
17
18
|
export interface OperationDriverUpdate {
|
|
@@ -38,8 +39,9 @@ export interface OperationManagerDriver {
|
|
|
38
39
|
result?(record: JournalRecord, signal: AbortSignal): Promise<ParseResult | void>;
|
|
39
40
|
}
|
|
40
41
|
export interface OperationManagerOptions {
|
|
41
|
-
readonly journal: Pick<OperationJournal, "beginIntent" | "migrateLegacyRecord" | "requestIdentityHmac" | "sourceLocatorHmac" | "transition" | "loadByRequestId" | "loadByOperationId" | "loadLatestByRequestIdentityHmac" | "loadLatestBySourceLocatorHmac" | "listRecoverable">;
|
|
42
|
+
readonly journal: Pick<OperationJournal, "beginIntent" | "migrateLegacyRecord" | "requestIdentityHmac" | "sourceLocatorHmac" | "transition" | "loadByRequestId" | "loadByOperationId" | "loadLatestByRequestIdentityHmac" | "loadLatestBySourceLocatorHmac" | "listRecoverable" | "strengthenResultDelivery">;
|
|
42
43
|
readonly driver: OperationManagerDriver;
|
|
44
|
+
readonly presentResult?: (record: JournalRecord, canonicalResult: ParseResult) => Promise<ParseResult>;
|
|
43
45
|
readonly now?: () => number;
|
|
44
46
|
readonly sleep?: (milliseconds: number) => Promise<void>;
|
|
45
47
|
}
|
|
@@ -61,6 +63,8 @@ export interface LocalRetention extends ResultRetentionSink {
|
|
|
61
63
|
}
|
|
62
64
|
export interface LocalArtifactStore {
|
|
63
65
|
createRetention(): LocalRetention;
|
|
66
|
+
preflightWrite?(): Promise<void>;
|
|
67
|
+
mintReadCursor?(resultId: string, byteOffset: number): Promise<string>;
|
|
64
68
|
read(resultId: string, cursor?: string, maxBytes?: number): Promise<ArtifactReadResult>;
|
|
65
69
|
readBundleDescriptor?(resultId: string): Promise<BundleLocalResult | null>;
|
|
66
70
|
}
|