@ixo/editor 6.30.3 → 6.31.1
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/action-manifest.json +16745 -3218
- package/dist/{chunk-OAAEXL7Q.js → chunk-4475SO6R.js} +2 -2
- package/dist/{chunk-XYAKFAZ3.js → chunk-FQAKOGKS.js} +2626 -942
- package/dist/chunk-FQAKOGKS.js.map +1 -0
- package/dist/{chunk-LY32OATI.js → chunk-TBCPHCFA.js} +2704 -466
- package/dist/chunk-TBCPHCFA.js.map +1 -0
- package/dist/{store-whqrkEVn.d.ts → connections-B24zj0RI.d.ts} +340 -323
- package/dist/core/index.d.ts +314 -251
- package/dist/core/index.js +455 -2
- package/dist/core/index.js.map +1 -1
- package/dist/{graphql-client-BV2YEKAc.d.ts → graphql-client-BICFLCX8.d.ts} +6 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mantine/index.d.ts +5 -5
- package/dist/mantine/index.js +2 -2
- package/dist/mantine/index.js.map +1 -1
- package/dist/{nodeContext-C-BsYuYi.d.ts → nodeContext-CV9D4s3X.d.ts} +1 -1
- package/dist/{index-CEIunGZv.d.ts → runtime-DFPayq89.d.ts} +9366 -8691
- package/package.json +7 -5
- package/style.css.d.ts +5 -5
- package/dist/chunk-LY32OATI.js.map +0 -1
- package/dist/chunk-XYAKFAZ3.js.map +0 -1
- /package/dist/{chunk-OAAEXL7Q.js.map → chunk-4475SO6R.js.map} +0 -0
package/dist/core/index.js
CHANGED
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
swapFlowBlocks,
|
|
15
15
|
updateNodeRuntime,
|
|
16
16
|
upsertFlowParticipant
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-4475SO6R.js";
|
|
18
18
|
import {
|
|
19
|
+
ACTION_MANIFEST_VERSION,
|
|
20
|
+
ACTION_REGISTRY_VERSION,
|
|
19
21
|
ExplicitRunRequiredError,
|
|
20
22
|
FLOW_CONNECTIONS_MAP_KEY,
|
|
21
23
|
FLOW_CONNECTION_BINDINGS_MAP_KEY,
|
|
@@ -26,6 +28,8 @@ import {
|
|
|
26
28
|
LATEST_VERSION,
|
|
27
29
|
LEGACY_RUN_STORAGE_VERSION,
|
|
28
30
|
MAX_RUN_ACTION_OUTPUT_BYTES,
|
|
31
|
+
MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT,
|
|
32
|
+
MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES,
|
|
29
33
|
MULTI_RUN_STORAGE_VERSION,
|
|
30
34
|
NODE_CONTEXT_MAP_NAME,
|
|
31
35
|
NODE_CONTEXT_OVERRIDES_MAP_NAME,
|
|
@@ -48,6 +52,7 @@ import {
|
|
|
48
52
|
XERO_WORK_ITEMS_MAP_NAME,
|
|
49
53
|
XERO_WORK_TERMINAL_MAP_NAME,
|
|
50
54
|
acquireFlowAgentLease,
|
|
55
|
+
actionManifestIssues,
|
|
51
56
|
actionTypeToCan,
|
|
52
57
|
adoptLegacyRuntime,
|
|
53
58
|
appendAgentLedgerEvent,
|
|
@@ -70,6 +75,7 @@ import {
|
|
|
70
75
|
canToActionType,
|
|
71
76
|
canToType,
|
|
72
77
|
cancelRun,
|
|
78
|
+
canonicalActionJson,
|
|
73
79
|
capabilityPatternCoversCan,
|
|
74
80
|
classifyBlockerCause,
|
|
75
81
|
classifyNodeState,
|
|
@@ -123,6 +129,7 @@ import {
|
|
|
123
129
|
getAction,
|
|
124
130
|
getActionByCan,
|
|
125
131
|
getActionForBlock,
|
|
132
|
+
getActionPresentation,
|
|
126
133
|
getActiveEditor,
|
|
127
134
|
getAliasEntries,
|
|
128
135
|
getAllActions,
|
|
@@ -237,6 +244,7 @@ import {
|
|
|
237
244
|
setMultiRunStorage,
|
|
238
245
|
setTempUcanEnforcementDisabled,
|
|
239
246
|
setupFlowFromBaseUcan,
|
|
247
|
+
sha256Digest,
|
|
240
248
|
snapshotInputRefs,
|
|
241
249
|
snapshotNode,
|
|
242
250
|
startRun,
|
|
@@ -256,18 +264,441 @@ import {
|
|
|
256
264
|
validateActionProof,
|
|
257
265
|
validateAgentCommand,
|
|
258
266
|
validateFlowAgentLease,
|
|
267
|
+
validateTopicSemanticRecord,
|
|
268
|
+
validateTopicSemanticRecordBatch,
|
|
259
269
|
verifyCompletion,
|
|
260
270
|
writeActionState,
|
|
261
271
|
writeCompiledBlocksToFragment,
|
|
262
272
|
writeRunRecordAndReconcile,
|
|
263
273
|
xeroOracleActivityForBlock,
|
|
264
274
|
xeroOracleActivityForBlockForEditor
|
|
265
|
-
} from "../chunk-
|
|
275
|
+
} from "../chunk-TBCPHCFA.js";
|
|
266
276
|
import {
|
|
267
277
|
computeCID,
|
|
268
278
|
computeJsonCID
|
|
269
279
|
} from "../chunk-ZPMRM6DA.js";
|
|
270
280
|
|
|
281
|
+
// src/core/lib/topicActions/topicActionBridge.ts
|
|
282
|
+
var TopicRevisionConflictError = class extends Error {
|
|
283
|
+
constructor(currentRevision) {
|
|
284
|
+
super(`Topic revision conflict; current revision is ${currentRevision}`);
|
|
285
|
+
this.currentRevision = currentRevision;
|
|
286
|
+
this.name = "TopicRevisionConflictError";
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
function mergeOutboxProgress(current, next) {
|
|
290
|
+
return {
|
|
291
|
+
...current,
|
|
292
|
+
...next,
|
|
293
|
+
attempts: Math.max(current.attempts, next.attempts),
|
|
294
|
+
receiptDelivery: next.receiptDelivery ?? current.receiptDelivery,
|
|
295
|
+
noticeDelivery: next.noticeDelivery ?? current.noticeDelivery
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
var InMemoryTopicActionOutboxStore = class {
|
|
299
|
+
constructor() {
|
|
300
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
301
|
+
this.delivered = /* @__PURE__ */ new Set();
|
|
302
|
+
this.sequences = /* @__PURE__ */ new Map();
|
|
303
|
+
}
|
|
304
|
+
async nextSequence(topicId, requestId) {
|
|
305
|
+
const key = `${topicId}|${requestId}`;
|
|
306
|
+
const sequence = (this.sequences.get(key) || 0) + 1;
|
|
307
|
+
this.sequences.set(key, sequence);
|
|
308
|
+
return sequence;
|
|
309
|
+
}
|
|
310
|
+
async enqueue(entry) {
|
|
311
|
+
if (this.entries.has(entry.deduplicationKey) || this.delivered.has(entry.deduplicationKey)) return "duplicate";
|
|
312
|
+
this.entries.set(entry.deduplicationKey, structuredClone(entry));
|
|
313
|
+
return "queued";
|
|
314
|
+
}
|
|
315
|
+
async pending() {
|
|
316
|
+
return [...this.entries.values()].map((entry) => structuredClone(entry));
|
|
317
|
+
}
|
|
318
|
+
async update(entry) {
|
|
319
|
+
if (this.delivered.has(entry.deduplicationKey)) return;
|
|
320
|
+
const current = this.entries.get(entry.deduplicationKey);
|
|
321
|
+
this.entries.set(entry.deduplicationKey, structuredClone(current ? mergeOutboxProgress(current, entry) : entry));
|
|
322
|
+
}
|
|
323
|
+
async markDelivered(deduplicationKey) {
|
|
324
|
+
this.entries.delete(deduplicationKey);
|
|
325
|
+
this.delivered.add(deduplicationKey);
|
|
326
|
+
}
|
|
327
|
+
async has(deduplicationKey) {
|
|
328
|
+
return this.entries.has(deduplicationKey) || this.delivered.has(deduplicationKey);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
var YjsTopicActionOutboxStore = class {
|
|
332
|
+
constructor(yDoc) {
|
|
333
|
+
this.yDoc = yDoc;
|
|
334
|
+
this.entries = yDoc.getMap("topicActionReceiptOutbox");
|
|
335
|
+
this.delivered = yDoc.getMap("topicActionReceiptDelivered");
|
|
336
|
+
this.sequences = yDoc.getMap("topicActionReceiptSequences");
|
|
337
|
+
}
|
|
338
|
+
async nextSequence(topicId, requestId) {
|
|
339
|
+
const key = `${topicId}|${requestId}`;
|
|
340
|
+
let sequence = 0;
|
|
341
|
+
this.yDoc.transact(() => {
|
|
342
|
+
sequence = (this.sequences.get(key) || 0) + 1;
|
|
343
|
+
this.sequences.set(key, sequence);
|
|
344
|
+
}, "topic-action-sequence");
|
|
345
|
+
return sequence;
|
|
346
|
+
}
|
|
347
|
+
async enqueue(entry) {
|
|
348
|
+
if (this.entries.has(entry.deduplicationKey) || this.delivered.has(entry.deduplicationKey)) return "duplicate";
|
|
349
|
+
this.entries.set(entry.deduplicationKey, structuredClone(entry));
|
|
350
|
+
return "queued";
|
|
351
|
+
}
|
|
352
|
+
async pending() {
|
|
353
|
+
return [...this.entries.values()].filter((entry) => !this.delivered.has(entry.deduplicationKey)).map((entry) => structuredClone(entry));
|
|
354
|
+
}
|
|
355
|
+
async update(entry) {
|
|
356
|
+
this.yDoc.transact(() => {
|
|
357
|
+
if (this.delivered.has(entry.deduplicationKey)) return;
|
|
358
|
+
const current = this.entries.get(entry.deduplicationKey);
|
|
359
|
+
this.entries.set(entry.deduplicationKey, structuredClone(current ? mergeOutboxProgress(current, entry) : entry));
|
|
360
|
+
}, "topic-action-outbox-progress");
|
|
361
|
+
}
|
|
362
|
+
async markDelivered(deduplicationKey, operationId, topicRevision) {
|
|
363
|
+
this.yDoc.transact(() => {
|
|
364
|
+
this.entries.delete(deduplicationKey);
|
|
365
|
+
this.delivered.set(deduplicationKey, { operationId, topicRevision });
|
|
366
|
+
}, "topic-action-delivered");
|
|
367
|
+
}
|
|
368
|
+
async has(deduplicationKey) {
|
|
369
|
+
return this.entries.has(deduplicationKey) || this.delivered.has(deduplicationKey);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
function readPath(value, path) {
|
|
373
|
+
return path.split(".").reduce((current, segment) => current && typeof current === "object" ? current[segment] : void 0, value);
|
|
374
|
+
}
|
|
375
|
+
function safeSummary(value, sensitivePaths) {
|
|
376
|
+
if (!value) return void 0;
|
|
377
|
+
const sensitiveRoots = new Set(sensitivePaths.map((path) => path.split(".")[0]));
|
|
378
|
+
const summary = {};
|
|
379
|
+
for (const key of Object.keys(value).sort()) {
|
|
380
|
+
if (Object.keys(summary).length >= 20 || sensitiveRoots.has(key)) continue;
|
|
381
|
+
const item = value[key];
|
|
382
|
+
if (typeof item === "string") summary[key] = item.length <= 240 ? item : `${item.slice(0, 237)}\u2026`;
|
|
383
|
+
else if (typeof item === "number" || typeof item === "boolean" || item === null) summary[key] = item;
|
|
384
|
+
else if (Array.isArray(item)) summary[key] = { count: item.length };
|
|
385
|
+
else if (item && typeof item === "object") summary[key] = { digest: sha256Digest(item) };
|
|
386
|
+
}
|
|
387
|
+
return Object.keys(summary).length > 0 ? summary : void 0;
|
|
388
|
+
}
|
|
389
|
+
function proofFor(action, output) {
|
|
390
|
+
if (!output || action.proof === "none") return void 0;
|
|
391
|
+
if ("validate" in action.proof) return { kind: "custom", digest: sha256Digest(output) };
|
|
392
|
+
const values = action.proof.fields.map((path) => readPath(output, path)).filter((value) => value !== void 0 && value !== null && value !== "");
|
|
393
|
+
const refs = values.filter((value) => typeof value === "string" && value.length <= 500);
|
|
394
|
+
return { kind: "fields", digest: sha256Digest(values), ...refs.length > 0 ? { refs: [...new Set(refs)].slice(0, 50) } : {} };
|
|
395
|
+
}
|
|
396
|
+
function localPhaseDeduplicationKey(receipt) {
|
|
397
|
+
return sha256Digest({
|
|
398
|
+
topicId: receipt.topicId,
|
|
399
|
+
requestId: receipt.requestId,
|
|
400
|
+
executionId: receipt.runtime.executionId,
|
|
401
|
+
status: receipt.status,
|
|
402
|
+
idempotencyKey: receipt.idempotencyKey
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
function externalReceiptDeduplicationKey(receipt) {
|
|
406
|
+
return sha256Digest({ topicId: receipt.topicId, requestId: receipt.requestId, receiptId: receipt.receiptId, sequence: receipt.sequence });
|
|
407
|
+
}
|
|
408
|
+
function topicSemanticRecordsDigest(records) {
|
|
409
|
+
return sha256Digest(records);
|
|
410
|
+
}
|
|
411
|
+
function validateSemanticRecords(action, records) {
|
|
412
|
+
if (!action.topic) throw new Error("Action has no Topic policy");
|
|
413
|
+
const result = validateTopicSemanticRecordBatch(records, action.topic);
|
|
414
|
+
if (!result.valid) throw new Error(`Invalid Topic semantic record (${result.code})`);
|
|
415
|
+
}
|
|
416
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "rejected", "cancelled", "needs_verification"]);
|
|
417
|
+
var MAX_RECEIPT_DELIVERY_ATTEMPTS = 3;
|
|
418
|
+
function statusWording(status) {
|
|
419
|
+
switch (status) {
|
|
420
|
+
case "succeeded":
|
|
421
|
+
return "completed successfully";
|
|
422
|
+
case "failed":
|
|
423
|
+
return "failed";
|
|
424
|
+
case "rejected":
|
|
425
|
+
return "was rejected";
|
|
426
|
+
case "cancelled":
|
|
427
|
+
return "was cancelled";
|
|
428
|
+
case "needs_verification":
|
|
429
|
+
return "completed and needs verification";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function topicActionThreadUpdateBody(action, status, semanticRecords) {
|
|
433
|
+
const recordText = semanticRecords.length > 0 ? ` ${semanticRecords.map((record) => `${record.displayName}: ${record.description}`).join(" ")}` : "";
|
|
434
|
+
return `${action.displayName} ${statusWording(status)}. ${action.description}${recordText} This update does not change the Topic, contract, outcome, claim, or settlement lifecycle.`;
|
|
435
|
+
}
|
|
436
|
+
function topicActionThreadUpdateId(input) {
|
|
437
|
+
return sha256Digest(input);
|
|
438
|
+
}
|
|
439
|
+
function buildTopicActionThreadUpdate(receipt, action, semanticRecords) {
|
|
440
|
+
if (!TERMINAL_STATUSES.has(receipt.status)) return void 0;
|
|
441
|
+
const status = receipt.status;
|
|
442
|
+
const definitions = new Map((action.topic?.semanticRecordTypes || []).map((definition) => [definition.type, definition]));
|
|
443
|
+
const presentedRecords = semanticRecords.map((record) => {
|
|
444
|
+
const definition = definitions.get(record.type);
|
|
445
|
+
return {
|
|
446
|
+
recordId: record.id,
|
|
447
|
+
type: record.type,
|
|
448
|
+
version: record.version,
|
|
449
|
+
displayName: definition.displayName,
|
|
450
|
+
description: definition.description
|
|
451
|
+
};
|
|
452
|
+
});
|
|
453
|
+
const body = topicActionThreadUpdateBody(action, status, presentedRecords);
|
|
454
|
+
const semanticRecordReferences = presentedRecords.map(({ recordId, type, version }) => ({ recordId, type, version }));
|
|
455
|
+
return {
|
|
456
|
+
version: 1,
|
|
457
|
+
updateId: topicActionThreadUpdateId({
|
|
458
|
+
topicId: receipt.topicId,
|
|
459
|
+
requestId: receipt.requestId,
|
|
460
|
+
receiptId: receipt.receiptId,
|
|
461
|
+
status,
|
|
462
|
+
semanticRecords: semanticRecordReferences
|
|
463
|
+
}),
|
|
464
|
+
topicId: receipt.topicId,
|
|
465
|
+
requestId: receipt.requestId,
|
|
466
|
+
receipt: { receiptId: receipt.receiptId },
|
|
467
|
+
action: { type: action.type, displayName: action.displayName, description: action.description },
|
|
468
|
+
status,
|
|
469
|
+
occurredAt: receipt.occurredAt,
|
|
470
|
+
semanticRecords: presentedRecords,
|
|
471
|
+
body,
|
|
472
|
+
lifecycleEffect: "none"
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function topicActionThreadUpdateTransactionId(update) {
|
|
476
|
+
return `topic-action-update-${update.updateId.slice("sha256:".length)}`;
|
|
477
|
+
}
|
|
478
|
+
var TopicActionBridge = class {
|
|
479
|
+
constructor(sink, signer, store = new InMemoryTopicActionOutboxStore(), verifier, now = () => /* @__PURE__ */ new Date()) {
|
|
480
|
+
this.sink = sink;
|
|
481
|
+
this.signer = signer;
|
|
482
|
+
this.store = store;
|
|
483
|
+
this.verifier = verifier;
|
|
484
|
+
this.now = now;
|
|
485
|
+
this.deliveries = /* @__PURE__ */ new Map();
|
|
486
|
+
}
|
|
487
|
+
async recordFlowPhase(params) {
|
|
488
|
+
const action = getAction(params.actionType);
|
|
489
|
+
if (!action) return { state: "rejected", error: `Unknown Action '${params.actionType}'` };
|
|
490
|
+
const manifestEntry = generateActionManifest().actions.find((entry) => entry.type === action.type);
|
|
491
|
+
if (!manifestEntry) return { state: "rejected", error: `Action '${action.type}' is absent from Manifest v4` };
|
|
492
|
+
const baseKind = params.topic.kind.source === "standard" ? params.topic.kind.kind : params.topic.kind.baseKind;
|
|
493
|
+
if (!action.topic?.supportedBaseKinds.includes(baseKind)) return { state: "rejected", error: `Action '${action.type}' does not support Topic Kind '${baseKind}'` };
|
|
494
|
+
const semanticRecords = params.semanticRecords || [];
|
|
495
|
+
try {
|
|
496
|
+
validateSemanticRecords(action, semanticRecords);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
return { state: "rejected", error: error instanceof Error ? error.message : "Invalid Topic semantic records" };
|
|
499
|
+
}
|
|
500
|
+
const stablePhaseKey = localPhaseDeduplicationKey({
|
|
501
|
+
topicId: params.topic.topicId,
|
|
502
|
+
requestId: params.topic.requestId,
|
|
503
|
+
status: params.status,
|
|
504
|
+
idempotencyKey: params.topic.idempotencyKey,
|
|
505
|
+
runtime: { executionId: params.executionId }
|
|
506
|
+
});
|
|
507
|
+
if (await this.store.has(stablePhaseKey)) return { state: "duplicate" };
|
|
508
|
+
const sequence = await this.store.nextSequence(params.topic.topicId, params.topic.requestId);
|
|
509
|
+
const occurredAt = params.occurredAt || this.now().toISOString();
|
|
510
|
+
const idempotencyKey = params.topic.idempotencyKey;
|
|
511
|
+
const inputSummary = safeSummary(params.input, action.sensitiveInputPaths || []);
|
|
512
|
+
const outputSummary = safeSummary(params.output, action.sensitiveOutputPaths || []);
|
|
513
|
+
const proof = proofFor(action, params.output);
|
|
514
|
+
const unsigned = {
|
|
515
|
+
version: 2,
|
|
516
|
+
receiptId: sha256Digest({ topicId: params.topic.topicId, requestId: params.topic.requestId, executionId: params.executionId, sequence, status: params.status }),
|
|
517
|
+
sequence,
|
|
518
|
+
requestId: params.topic.requestId,
|
|
519
|
+
topicId: params.topic.topicId,
|
|
520
|
+
topicRevision: params.topic.topicRevision,
|
|
521
|
+
action: { type: action.type, can: manifestEntry.can, registryVersion: ACTION_REGISTRY_VERSION, contractDigest: manifestEntry.contractDigest },
|
|
522
|
+
runtime: {
|
|
523
|
+
provider: params.topic.executorProvider,
|
|
524
|
+
...params.topic.bindingId ? { bindingId: params.topic.bindingId } : {},
|
|
525
|
+
...params.flowUri ? { flowUri: params.flowUri } : {},
|
|
526
|
+
...params.sessionRunId ? { sessionRunId: params.sessionRunId } : {},
|
|
527
|
+
...params.nodeId ? { nodeId: params.nodeId } : {},
|
|
528
|
+
executionId: params.executionId
|
|
529
|
+
},
|
|
530
|
+
actorDid: params.actorDid,
|
|
531
|
+
executorDid: params.executorDid,
|
|
532
|
+
status: params.status,
|
|
533
|
+
idempotencyKey,
|
|
534
|
+
occurredAt,
|
|
535
|
+
authorization: {
|
|
536
|
+
capability: params.topic.topicCapabilityReference,
|
|
537
|
+
...params.invocationReference ? { invocation: params.invocationReference } : {},
|
|
538
|
+
...params.topic.confirmationReference ? { confirmation: params.topic.confirmationReference } : {}
|
|
539
|
+
},
|
|
540
|
+
input: { digest: sha256Digest(params.input), ...inputSummary ? { summary: inputSummary } : {} },
|
|
541
|
+
semanticRecordsDigest: topicSemanticRecordsDigest(semanticRecords),
|
|
542
|
+
...params.output ? { output: { digest: sha256Digest(params.output), ...outputSummary ? { summary: outputSummary } : {} } } : {},
|
|
543
|
+
...proof ? { proof } : {},
|
|
544
|
+
...params.evidenceReferences?.length ? { evidenceRefs: [...new Set(params.evidenceReferences)].slice(0, 100) } : {},
|
|
545
|
+
...params.traceReference ? { traceRef: params.traceReference } : {},
|
|
546
|
+
...params.error ? { error: { ...params.error.code ? { code: params.error.code.slice(0, 80) } : {}, message: params.error.message.slice(0, 2e3) } } : {}
|
|
547
|
+
};
|
|
548
|
+
const signed = await this.signer.sign(unsigned);
|
|
549
|
+
return this.queueAndFlush({ ...unsigned, ...signed }, action, semanticRecords, params.topic.topicRevision, stablePhaseKey);
|
|
550
|
+
}
|
|
551
|
+
async ingestExternalReceipt(receipt, semanticRecords = []) {
|
|
552
|
+
const action = getAction(receipt.action.type);
|
|
553
|
+
const manifest = generateActionManifest();
|
|
554
|
+
const manifestEntry = action && manifest.actions.find((entry) => entry.type === action.type);
|
|
555
|
+
if (!action || !manifestEntry) return { state: "rejected", error: "Receipt names an unknown Action" };
|
|
556
|
+
if (receipt.action.contractDigest !== manifestEntry.contractDigest || receipt.action.can !== manifestEntry.can || receipt.action.registryVersion !== manifest.registryVersion) {
|
|
557
|
+
return { state: "rejected", error: "Receipt Action contract does not match the live manifest" };
|
|
558
|
+
}
|
|
559
|
+
if (!this.verifier) return { state: "rejected", error: "No external receipt verifier is configured" };
|
|
560
|
+
const verification = await this.verifier.verify(receipt, {
|
|
561
|
+
audience: receipt.topicId,
|
|
562
|
+
capability: "topic/record-action",
|
|
563
|
+
actionType: receipt.action.type,
|
|
564
|
+
topicId: receipt.topicId
|
|
565
|
+
});
|
|
566
|
+
if (!verification.valid || verification.revoked)
|
|
567
|
+
return { state: "rejected", error: verification.reason || (verification.revoked ? "Receipt capability was revoked" : "Receipt signature is invalid") };
|
|
568
|
+
if (verification.audience && verification.audience !== receipt.topicId) return { state: "rejected", error: "Receipt has the wrong audience" };
|
|
569
|
+
if (verification.capabilities && !verification.capabilities.includes("topic/record-action"))
|
|
570
|
+
return { state: "rejected", error: "Receipt does not grant Topic receipt recording" };
|
|
571
|
+
if (verification.expiresAt && Date.parse(verification.expiresAt) <= this.now().getTime()) return { state: "rejected", error: "Receipt capability is expired" };
|
|
572
|
+
if (Date.parse(receipt.occurredAt) > this.now().getTime() + 5 * 6e4) return { state: "rejected", error: "Receipt occurrence time is too far in the future" };
|
|
573
|
+
try {
|
|
574
|
+
validateSemanticRecords(action, semanticRecords);
|
|
575
|
+
} catch (error) {
|
|
576
|
+
return { state: "rejected", error: error instanceof Error ? error.message : "Invalid Topic semantic records" };
|
|
577
|
+
}
|
|
578
|
+
if (receipt.semanticRecordsDigest !== topicSemanticRecordsDigest(semanticRecords)) {
|
|
579
|
+
return { state: "rejected", error: "Receipt semantic record digest does not match its records" };
|
|
580
|
+
}
|
|
581
|
+
return this.queueAndFlush(receipt, action, semanticRecords, receipt.topicRevision, externalReceiptDeduplicationKey(receipt));
|
|
582
|
+
}
|
|
583
|
+
async flush() {
|
|
584
|
+
const results = [];
|
|
585
|
+
for (const entry of await this.store.pending()) results.push(await this.deliver(entry));
|
|
586
|
+
return results;
|
|
587
|
+
}
|
|
588
|
+
async queueAndFlush(receipt, action, semanticRecords, writeRevision, key) {
|
|
589
|
+
if (await this.store.has(key)) return { state: "duplicate", receiptId: receipt.receiptId };
|
|
590
|
+
const queued = await this.store.enqueue({
|
|
591
|
+
deduplicationKey: key,
|
|
592
|
+
receipt,
|
|
593
|
+
semanticRecords,
|
|
594
|
+
writeRevision,
|
|
595
|
+
queuedAt: this.now().toISOString(),
|
|
596
|
+
attempts: 0,
|
|
597
|
+
threadUpdate: buildTopicActionThreadUpdate(receipt, action, semanticRecords)
|
|
598
|
+
});
|
|
599
|
+
if (queued === "duplicate") return { state: "duplicate", receiptId: receipt.receiptId };
|
|
600
|
+
const entry = (await this.store.pending()).find((candidate) => candidate.deduplicationKey === key);
|
|
601
|
+
return entry ? this.deliver(entry) : { state: "duplicate", receiptId: receipt.receiptId };
|
|
602
|
+
}
|
|
603
|
+
deliver(entry) {
|
|
604
|
+
const inFlight = this.deliveries.get(entry.deduplicationKey);
|
|
605
|
+
if (inFlight) return inFlight;
|
|
606
|
+
const delivery = this.deliverOnce(entry).finally(() => this.deliveries.delete(entry.deduplicationKey));
|
|
607
|
+
this.deliveries.set(entry.deduplicationKey, delivery);
|
|
608
|
+
return delivery;
|
|
609
|
+
}
|
|
610
|
+
async deliverOnce(entry) {
|
|
611
|
+
let current = { ...entry, attempts: entry.attempts + 1 };
|
|
612
|
+
try {
|
|
613
|
+
if (!current.receiptDelivery) {
|
|
614
|
+
const result = await this.sink.appendReceipt({ receipt: current.receipt, semanticRecords: current.semanticRecords, previousRevision: current.writeRevision });
|
|
615
|
+
current = {
|
|
616
|
+
...current,
|
|
617
|
+
receiptDelivery: result,
|
|
618
|
+
writeRevision: result.topicRevision,
|
|
619
|
+
threadUpdate: current.threadUpdate ? { ...current.threadUpdate, receipt: { ...current.threadUpdate.receipt, operationId: result.operationId } } : void 0,
|
|
620
|
+
lastError: void 0
|
|
621
|
+
};
|
|
622
|
+
await this.store.update(current);
|
|
623
|
+
}
|
|
624
|
+
if (current.threadUpdate && !current.noticeDelivery) {
|
|
625
|
+
const notice = await this.sink.appendThreadUpdate({
|
|
626
|
+
update: current.threadUpdate,
|
|
627
|
+
transactionId: topicActionThreadUpdateTransactionId(current.threadUpdate)
|
|
628
|
+
});
|
|
629
|
+
current = { ...current, noticeDelivery: notice, lastError: void 0 };
|
|
630
|
+
await this.store.update(current);
|
|
631
|
+
}
|
|
632
|
+
const delivery = current.receiptDelivery;
|
|
633
|
+
await this.store.markDelivered(current.deduplicationKey, delivery.operationId, delivery.topicRevision);
|
|
634
|
+
return {
|
|
635
|
+
state: "delivered",
|
|
636
|
+
receiptId: current.receipt.receiptId,
|
|
637
|
+
operationId: delivery.operationId,
|
|
638
|
+
topicRevision: delivery.topicRevision,
|
|
639
|
+
...current.noticeDelivery ? { threadEventId: current.noticeDelivery.eventId } : {}
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
if (!current.receiptDelivery && error instanceof TopicRevisionConflictError) {
|
|
643
|
+
current = { ...current, writeRevision: error.currentRevision, lastError: error.message };
|
|
644
|
+
await this.store.update(current);
|
|
645
|
+
if (current.attempts < MAX_RECEIPT_DELIVERY_ATTEMPTS) return this.deliverOnce(current);
|
|
646
|
+
}
|
|
647
|
+
current = { ...current, lastError: error instanceof Error ? error.message : "Topic write-back failed" };
|
|
648
|
+
await this.store.update(current);
|
|
649
|
+
return {
|
|
650
|
+
state: "queued",
|
|
651
|
+
receiptId: current.receipt.receiptId,
|
|
652
|
+
...current.receiptDelivery ? { operationId: current.receiptDelivery.operationId, topicRevision: current.receiptDelivery.topicRevision } : {},
|
|
653
|
+
error: current.lastError
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
function topicActionReceiptSigningPayload(receipt) {
|
|
659
|
+
return canonicalActionJson(receipt);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/core/lib/topicActions/externalExecutorAdapter.ts
|
|
663
|
+
var ManifestBoundTopicExecutorAdapter = class {
|
|
664
|
+
constructor(provider, executor, bridge) {
|
|
665
|
+
this.provider = provider;
|
|
666
|
+
this.executor = executor;
|
|
667
|
+
this.bridge = bridge;
|
|
668
|
+
}
|
|
669
|
+
async execute(invocation) {
|
|
670
|
+
this.validateInvocation(invocation);
|
|
671
|
+
const result = await this.executor.invoke(structuredClone(invocation));
|
|
672
|
+
this.validateResult(invocation, result);
|
|
673
|
+
return this.bridge.ingestExternalReceipt(result.receipt, result.semanticRecords || []);
|
|
674
|
+
}
|
|
675
|
+
validateInvocation(invocation) {
|
|
676
|
+
if (invocation.version !== 1 || invocation.executorProvider !== this.provider) throw new Error(`Invocation is not addressed to ${this.provider}`);
|
|
677
|
+
if (!invocation.requestId || !invocation.idempotencyKey || !invocation.topicId || !invocation.topicRevision) throw new Error("External Topic invocation is incomplete");
|
|
678
|
+
if (!invocation.actorDid.startsWith("did:") || !invocation.executorDid.startsWith("did:")) throw new Error("External Topic invocation requires actor and executor DIDs");
|
|
679
|
+
if (!invocation.topicCapabilityReference || !invocation.input.digest.startsWith("sha256:")) throw new Error("External Topic invocation lacks capability or safe input digest");
|
|
680
|
+
const manifest = generateActionManifest();
|
|
681
|
+
const action = manifest.actions.find((entry) => entry.type === invocation.actionType);
|
|
682
|
+
if (!action || action.hidden || action.deprecated) throw new Error(`Action '${invocation.actionType}' is unavailable to external executors`);
|
|
683
|
+
if (action.contractDigest !== invocation.actionContractDigest) throw new Error(`Action '${invocation.actionType}' contract digest does not match Manifest v4`);
|
|
684
|
+
}
|
|
685
|
+
validateResult(invocation, result) {
|
|
686
|
+
const receipt = result.receipt;
|
|
687
|
+
if (receipt.topicId !== invocation.topicId || receipt.topicRevision !== invocation.topicRevision || receipt.requestId !== invocation.requestId || receipt.idempotencyKey !== invocation.idempotencyKey) {
|
|
688
|
+
throw new Error("External receipt does not correlate to its invocation");
|
|
689
|
+
}
|
|
690
|
+
if (receipt.runtime.provider !== this.provider || receipt.action.type !== invocation.actionType || receipt.action.contractDigest !== invocation.actionContractDigest) {
|
|
691
|
+
throw new Error("External receipt provider or Action contract does not match its invocation");
|
|
692
|
+
}
|
|
693
|
+
if (receipt.actorDid !== invocation.actorDid || receipt.executorDid !== invocation.executorDid)
|
|
694
|
+
throw new Error("External receipt actor or executor DID does not match its invocation");
|
|
695
|
+
if (receipt.input.digest !== invocation.input.digest) throw new Error("External receipt input digest does not match its invocation");
|
|
696
|
+
if (receipt.authorization.capability !== invocation.topicCapabilityReference) throw new Error("External receipt uses a different Topic capability reference");
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var createQiForgeTopicExecutorAdapter = (executor, bridge) => new ManifestBoundTopicExecutorAdapter("qiforge", executor, bridge);
|
|
700
|
+
var createMcpTopicExecutorAdapter = (executor, bridge) => new ManifestBoundTopicExecutorAdapter("mcp", executor, bridge);
|
|
701
|
+
|
|
271
702
|
// src/core/templates/oracleInitFlow.ts
|
|
272
703
|
var oracleInitSurveySchema = {
|
|
273
704
|
title: "Oracle Configuration",
|
|
@@ -648,6 +1079,8 @@ function createOracleInitFlowTemplate() {
|
|
|
648
1079
|
return { metadata, nodes };
|
|
649
1080
|
}
|
|
650
1081
|
export {
|
|
1082
|
+
ACTION_MANIFEST_VERSION,
|
|
1083
|
+
ACTION_REGISTRY_VERSION,
|
|
651
1084
|
ExplicitRunRequiredError,
|
|
652
1085
|
FLOW_CONNECTIONS_MAP_KEY,
|
|
653
1086
|
FLOW_CONNECTION_BINDINGS_MAP_KEY,
|
|
@@ -655,11 +1088,15 @@ export {
|
|
|
655
1088
|
FlowAgentService,
|
|
656
1089
|
FlowNotMigratedError,
|
|
657
1090
|
INVOCATIONS_MAP_KEY,
|
|
1091
|
+
InMemoryTopicActionOutboxStore,
|
|
658
1092
|
InvalidConditionConfigError,
|
|
659
1093
|
LATEST_VERSION,
|
|
660
1094
|
LEGACY_RUN_STORAGE_VERSION,
|
|
661
1095
|
MAX_RUN_ACTION_OUTPUT_BYTES,
|
|
1096
|
+
MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT,
|
|
1097
|
+
MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES,
|
|
662
1098
|
MULTI_RUN_STORAGE_VERSION,
|
|
1099
|
+
ManifestBoundTopicExecutorAdapter,
|
|
663
1100
|
NODE_CONTEXT_MAP_NAME,
|
|
664
1101
|
NODE_CONTEXT_OVERRIDES_MAP_NAME,
|
|
665
1102
|
PROOF_MISSING_CODE,
|
|
@@ -673,6 +1110,8 @@ export {
|
|
|
673
1110
|
STEP_COMPLETED_EVENT,
|
|
674
1111
|
STEP_COMPLETED_EVENT_NAME,
|
|
675
1112
|
TerminalRunExecutionError,
|
|
1113
|
+
TopicActionBridge,
|
|
1114
|
+
TopicRevisionConflictError,
|
|
676
1115
|
UnknownRunError,
|
|
677
1116
|
VERSION_MANIFEST,
|
|
678
1117
|
XERO_ORACLE_WORKING_STATUSES,
|
|
@@ -680,7 +1119,9 @@ export {
|
|
|
680
1119
|
XERO_TOOLKIT,
|
|
681
1120
|
XERO_WORK_ITEMS_MAP_NAME,
|
|
682
1121
|
XERO_WORK_TERMINAL_MAP_NAME,
|
|
1122
|
+
YjsTopicActionOutboxStore,
|
|
683
1123
|
acquireFlowAgentLease,
|
|
1124
|
+
actionManifestIssues,
|
|
684
1125
|
actionTypeToCan,
|
|
685
1126
|
addFlowNode,
|
|
686
1127
|
adoptLegacyRuntime,
|
|
@@ -698,6 +1139,7 @@ export {
|
|
|
698
1139
|
buildFlowNodeFromBlock,
|
|
699
1140
|
buildRunDefinitionSnapshot,
|
|
700
1141
|
buildServicesFromHandlers,
|
|
1142
|
+
buildTopicActionThreadUpdate,
|
|
701
1143
|
buildXeroInvoiceWorkKey,
|
|
702
1144
|
buildXeroPaymentWorkKey,
|
|
703
1145
|
canMatches,
|
|
@@ -722,9 +1164,11 @@ export {
|
|
|
722
1164
|
countPendingInvocations,
|
|
723
1165
|
createAgentCommand,
|
|
724
1166
|
createInvocationStore,
|
|
1167
|
+
createMcpTopicExecutorAdapter,
|
|
725
1168
|
createMemoryInvocationStore,
|
|
726
1169
|
createMemoryUcanDelegationStore,
|
|
727
1170
|
createOracleInitFlowTemplate,
|
|
1171
|
+
createQiForgeTopicExecutorAdapter,
|
|
728
1172
|
createReference,
|
|
729
1173
|
createRunEventIdempotencyKey,
|
|
730
1174
|
createRunId,
|
|
@@ -760,6 +1204,7 @@ export {
|
|
|
760
1204
|
getAction,
|
|
761
1205
|
getActionByCan,
|
|
762
1206
|
getActionForBlock,
|
|
1207
|
+
getActionPresentation,
|
|
763
1208
|
getActiveEditor,
|
|
764
1209
|
getAliasEntries,
|
|
765
1210
|
getAllActions,
|
|
@@ -885,6 +1330,7 @@ export {
|
|
|
885
1330
|
setMultiRunStorage,
|
|
886
1331
|
setTempUcanEnforcementDisabled,
|
|
887
1332
|
setupFlowFromBaseUcan,
|
|
1333
|
+
sha256Digest,
|
|
888
1334
|
snapshotInputRefs,
|
|
889
1335
|
snapshotNode,
|
|
890
1336
|
startRun,
|
|
@@ -893,6 +1339,11 @@ export {
|
|
|
893
1339
|
tickFlowAgent,
|
|
894
1340
|
toEvaluatorOperator,
|
|
895
1341
|
toRunJsonValue,
|
|
1342
|
+
topicActionReceiptSigningPayload,
|
|
1343
|
+
topicActionThreadUpdateBody,
|
|
1344
|
+
topicActionThreadUpdateId,
|
|
1345
|
+
topicActionThreadUpdateTransactionId,
|
|
1346
|
+
topicSemanticRecordsDigest,
|
|
896
1347
|
typeToCan,
|
|
897
1348
|
updateAgentCommand,
|
|
898
1349
|
updateNodeRuntime,
|
|
@@ -907,6 +1358,8 @@ export {
|
|
|
907
1358
|
validateActionProof,
|
|
908
1359
|
validateAgentCommand,
|
|
909
1360
|
validateFlowAgentLease,
|
|
1361
|
+
validateTopicSemanticRecord,
|
|
1362
|
+
validateTopicSemanticRecordBatch,
|
|
910
1363
|
verifyCompletion,
|
|
911
1364
|
writeActionState,
|
|
912
1365
|
writeCompiledBlocksToFragment,
|