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