@ixo/editor 5.11.0 → 5.13.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/dist/{chunk-GCR6VY7O.mjs → chunk-JAB2IBVH.mjs} +1299 -31
- package/dist/chunk-JAB2IBVH.mjs.map +1 -0
- package/dist/{chunk-RAEW6M7A.mjs → chunk-S77KHEUD.mjs} +1187 -796
- package/dist/chunk-S77KHEUD.mjs.map +1 -0
- package/dist/chunk-ZEKC5FGC.mjs +63 -0
- package/dist/chunk-ZEKC5FGC.mjs.map +1 -0
- package/dist/core/index.d.ts +25 -5
- package/dist/core/index.mjs +33 -29
- package/dist/core/index.mjs.map +1 -1
- package/dist/{graphql-client-DP4rq5no.d.ts → graphql-client-BjgWToqz.d.ts} +7 -2
- package/dist/{index-C0ikjICf.d.ts → index-1Y4fIZa7.d.ts} +39 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +20 -19
- package/dist/index.mjs.map +1 -1
- package/dist/mantine/index.d.ts +5 -4
- package/dist/mantine/index.mjs +4 -2
- package/dist/{store-BT916StR.d.ts → store-CEGM2mJP.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-7ZKV7F6Z.mjs +0 -824
- package/dist/chunk-7ZKV7F6Z.mjs.map +0 -1
- package/dist/chunk-GCR6VY7O.mjs.map +0 -1
- package/dist/chunk-RAEW6M7A.mjs.map +0 -1
|
@@ -543,7 +543,14 @@ registerAction({
|
|
|
543
543
|
// events-and-triggers plan.
|
|
544
544
|
eligibleForEventTrigger: true,
|
|
545
545
|
run: async (inputs, ctx) => {
|
|
546
|
-
const
|
|
546
|
+
const endpoint = inputs.endpoint ?? inputs.url;
|
|
547
|
+
const url = typeof endpoint === "string" ? endpoint : "";
|
|
548
|
+
if (!url) {
|
|
549
|
+
throw new Error("HTTP request action requires an endpoint or url input");
|
|
550
|
+
}
|
|
551
|
+
const method = typeof inputs.method === "string" ? inputs.method : "GET";
|
|
552
|
+
const headers = inputs.headers && typeof inputs.headers === "object" && !Array.isArray(inputs.headers) ? inputs.headers : {};
|
|
553
|
+
const body = inputs.body;
|
|
547
554
|
const requestFn = ctx.services.http?.request;
|
|
548
555
|
if (requestFn) {
|
|
549
556
|
const result = await requestFn({ url, method, headers, body });
|
|
@@ -2572,14 +2579,14 @@ var tempDomainCreatorSurvey = {
|
|
|
2572
2579
|
function resolveEntityTypeFromSchema(schemaType) {
|
|
2573
2580
|
const normalized = schemaType.replace(/^schema:/i, "").toLowerCase();
|
|
2574
2581
|
const schemaToEntity = {
|
|
2575
|
-
organization: "dao",
|
|
2576
|
-
nonprofit: "dao",
|
|
2577
|
-
corporation: "dao",
|
|
2578
|
-
governmentorganization: "dao"
|
|
2582
|
+
organization: "dao/pod",
|
|
2583
|
+
nonprofit: "dao/pod",
|
|
2584
|
+
corporation: "dao/pod",
|
|
2585
|
+
governmentorganization: "dao/pod"
|
|
2579
2586
|
};
|
|
2580
2587
|
if (schemaToEntity[normalized]) return schemaToEntity[normalized];
|
|
2581
|
-
const allowed = /* @__PURE__ */ new Set(["dao", "protocol", "oracle", "investment", "project", "asset"]);
|
|
2582
|
-
return allowed.has(normalized) ? normalized : "dao";
|
|
2588
|
+
const allowed = /* @__PURE__ */ new Set(["dao/pod", "protocol", "oracle", "investment", "project", "asset"]);
|
|
2589
|
+
return allowed.has(normalized) ? normalized : "dao/pod";
|
|
2583
2590
|
}
|
|
2584
2591
|
function parseJsonInput(value) {
|
|
2585
2592
|
if (!value) return null;
|
|
@@ -2718,7 +2725,7 @@ registerAction({
|
|
|
2718
2725
|
if (!domainCardData?.credentialSubject?.name) {
|
|
2719
2726
|
throw new Error("domainCardData is missing or invalid (credentialSubject.name required)");
|
|
2720
2727
|
}
|
|
2721
|
-
const entityType = String(inputs.entityType || "").trim() || (domainCardData.credentialSubject?.type?.[0] ? resolveEntityTypeFromSchema(domainCardData.credentialSubject.type[0]) : "dao");
|
|
2728
|
+
const entityType = String(inputs.entityType || "").trim() || (domainCardData.credentialSubject?.type?.[0] ? resolveEntityTypeFromSchema(domainCardData.credentialSubject.type[0]) : "dao/pod");
|
|
2722
2729
|
const issuerDid = handlers.getEntityDid?.() || handlers.getCurrentUser?.()?.address;
|
|
2723
2730
|
if (!issuerDid) throw new Error("Unable to determine issuer DID");
|
|
2724
2731
|
const entityDidPlaceholder = "did:ixo:entity:pending";
|
|
@@ -5143,6 +5150,22 @@ function toSupportedDID(did) {
|
|
|
5143
5150
|
}
|
|
5144
5151
|
return void 0;
|
|
5145
5152
|
}
|
|
5153
|
+
function normalizeDid(did) {
|
|
5154
|
+
if (did.startsWith("did:")) return did;
|
|
5155
|
+
if (!did.startsWith("@did-")) return did;
|
|
5156
|
+
const [localpart] = did.split(":");
|
|
5157
|
+
const parts = localpart.split("-");
|
|
5158
|
+
if (parts.length < 3 || parts[0] !== "@did") return did;
|
|
5159
|
+
return `did:${parts[1]}:${parts.slice(2).join("-")}`;
|
|
5160
|
+
}
|
|
5161
|
+
function toInvocationAudienceDID(did) {
|
|
5162
|
+
const normalized = normalizeDid(did);
|
|
5163
|
+
const supported = toSupportedDID(normalized);
|
|
5164
|
+
if (!supported) {
|
|
5165
|
+
throw new Error(`Invalid DID "${did}", must start with 'did:'`);
|
|
5166
|
+
}
|
|
5167
|
+
return supported;
|
|
5168
|
+
}
|
|
5146
5169
|
function toUcantoCapabilities(capabilities) {
|
|
5147
5170
|
return capabilities.map((cap) => ({
|
|
5148
5171
|
can: cap.can,
|
|
@@ -5151,13 +5174,14 @@ function toUcantoCapabilities(capabilities) {
|
|
|
5151
5174
|
}));
|
|
5152
5175
|
}
|
|
5153
5176
|
async function getSigner(handlers, did, didType, entityRoomId, pin) {
|
|
5154
|
-
const
|
|
5177
|
+
const normalizedDid = normalizeDid(did);
|
|
5178
|
+
const supportedDid = toSupportedDID(normalizedDid);
|
|
5155
5179
|
if (handlers.getPrivateKey) {
|
|
5156
|
-
const privateKey = await handlers.getPrivateKey({ did, didType, entityRoomId, pin });
|
|
5180
|
+
const privateKey = await handlers.getPrivateKey({ did: normalizedDid, didType, entityRoomId, pin });
|
|
5157
5181
|
return parseSigner(privateKey, supportedDid);
|
|
5158
5182
|
}
|
|
5159
5183
|
if (handlers.getMnemonic) {
|
|
5160
|
-
const mnemonic = await handlers.getMnemonic({ did, didType, entityRoomId, pin });
|
|
5184
|
+
const mnemonic = await handlers.getMnemonic({ did: normalizedDid, didType, entityRoomId, pin });
|
|
5161
5185
|
const result = await signerFromMnemonic(mnemonic, supportedDid);
|
|
5162
5186
|
return result.signer;
|
|
5163
5187
|
}
|
|
@@ -5168,6 +5192,7 @@ function getCidFromDelegation(delegation) {
|
|
|
5168
5192
|
}
|
|
5169
5193
|
var createUcanService = (config) => {
|
|
5170
5194
|
const { delegationStore, invocationStore, handlers, flowOwnerDid } = config;
|
|
5195
|
+
const normalizedFlowOwnerDid = normalizeDid(flowOwnerDid);
|
|
5171
5196
|
const delegationCache = /* @__PURE__ */ new Map();
|
|
5172
5197
|
const isConfigured = () => {
|
|
5173
5198
|
const hasSessionHandlers = !!(handlers.createSignerSession && handlers.signWithSession);
|
|
@@ -5202,11 +5227,12 @@ var createUcanService = (config) => {
|
|
|
5202
5227
|
};
|
|
5203
5228
|
const createRootDelegation = async (params) => {
|
|
5204
5229
|
const { flowOwnerDid: ownerDid, issuerType, entityRoomId, flowUri: uri, pin } = params;
|
|
5205
|
-
const
|
|
5230
|
+
const normalizedOwnerDid = normalizeDid(ownerDid);
|
|
5231
|
+
const signer = await getSigner(handlers, normalizedOwnerDid, issuerType, entityRoomId, pin);
|
|
5206
5232
|
const capabilities = [{ can: "flow/*", with: uri }];
|
|
5207
5233
|
const delegation = await ucanCreateDelegation({
|
|
5208
5234
|
issuer: signer,
|
|
5209
|
-
audience:
|
|
5235
|
+
audience: toInvocationAudienceDID(normalizedOwnerDid),
|
|
5210
5236
|
capabilities,
|
|
5211
5237
|
proofs: []
|
|
5212
5238
|
});
|
|
@@ -5215,8 +5241,8 @@ var createUcanService = (config) => {
|
|
|
5215
5241
|
const storedDelegation = {
|
|
5216
5242
|
cid,
|
|
5217
5243
|
delegation: serialized,
|
|
5218
|
-
issuerDid:
|
|
5219
|
-
audienceDid:
|
|
5244
|
+
issuerDid: normalizedOwnerDid,
|
|
5245
|
+
audienceDid: normalizedOwnerDid,
|
|
5220
5246
|
capabilities,
|
|
5221
5247
|
createdAt: Date.now(),
|
|
5222
5248
|
format: "car",
|
|
@@ -5229,12 +5255,14 @@ var createUcanService = (config) => {
|
|
|
5229
5255
|
};
|
|
5230
5256
|
const createDelegation = async (params) => {
|
|
5231
5257
|
const { issuerDid, issuerType, entityRoomId, audience, capabilities, proofs = [], expiration, pin } = params;
|
|
5232
|
-
const
|
|
5258
|
+
const normalizedIssuerDid = normalizeDid(issuerDid);
|
|
5259
|
+
const normalizedAudienceDid = normalizeDid(audience);
|
|
5260
|
+
const signer = await getSigner(handlers, normalizedIssuerDid, issuerType, entityRoomId, pin);
|
|
5233
5261
|
const proofCids = proofs.length > 0 ? proofs : [delegationStore.getRootCid()].filter(Boolean);
|
|
5234
5262
|
const proofDelegations = await getProofDelegations(proofCids);
|
|
5235
5263
|
const delegation = await ucanCreateDelegation({
|
|
5236
5264
|
issuer: signer,
|
|
5237
|
-
audience,
|
|
5265
|
+
audience: toInvocationAudienceDID(normalizedAudienceDid),
|
|
5238
5266
|
capabilities: toUcantoCapabilities(capabilities),
|
|
5239
5267
|
proofs: proofDelegations,
|
|
5240
5268
|
expiration: expiration ? Math.floor(expiration / 1e3) : void 0
|
|
@@ -5244,8 +5272,8 @@ var createUcanService = (config) => {
|
|
|
5244
5272
|
const storedDelegation = {
|
|
5245
5273
|
cid,
|
|
5246
5274
|
delegation: serialized,
|
|
5247
|
-
issuerDid,
|
|
5248
|
-
audienceDid:
|
|
5275
|
+
issuerDid: normalizedIssuerDid,
|
|
5276
|
+
audienceDid: normalizedAudienceDid,
|
|
5249
5277
|
capabilities,
|
|
5250
5278
|
expiration,
|
|
5251
5279
|
createdAt: Date.now(),
|
|
@@ -5319,7 +5347,7 @@ var createUcanService = (config) => {
|
|
|
5319
5347
|
for (let i = 0; i < proofChain.length - 1; i++) {
|
|
5320
5348
|
const current = proofChain[i];
|
|
5321
5349
|
const parent = proofChain[i + 1];
|
|
5322
|
-
if (parent.audienceDid !== current.issuerDid) {
|
|
5350
|
+
if (normalizeDid(parent.audienceDid) !== normalizeDid(current.issuerDid)) {
|
|
5323
5351
|
return {
|
|
5324
5352
|
valid: false,
|
|
5325
5353
|
error: `Chain broken: ${parent.cid} audience (${parent.audienceDid}) != ${current.cid} issuer (${current.issuerDid})`
|
|
@@ -5327,7 +5355,7 @@ var createUcanService = (config) => {
|
|
|
5327
5355
|
}
|
|
5328
5356
|
}
|
|
5329
5357
|
const root = proofChain[proofChain.length - 1];
|
|
5330
|
-
if (root.issuerDid !==
|
|
5358
|
+
if (normalizeDid(root.issuerDid) !== normalizedFlowOwnerDid) {
|
|
5331
5359
|
return { valid: false, error: `Root issuer ${root.issuerDid} is not flow owner ${flowOwnerDid}` };
|
|
5332
5360
|
}
|
|
5333
5361
|
return { valid: true, proofChain };
|
|
@@ -5352,7 +5380,7 @@ var createUcanService = (config) => {
|
|
|
5352
5380
|
};
|
|
5353
5381
|
const invocation = await ucanCreateInvocation({
|
|
5354
5382
|
issuer: signer,
|
|
5355
|
-
audience:
|
|
5383
|
+
audience: toInvocationAudienceDID(normalizedFlowOwnerDid),
|
|
5356
5384
|
capability: ucantoCapability,
|
|
5357
5385
|
proofs: proofDelegations
|
|
5358
5386
|
});
|
|
@@ -5868,13 +5896,12 @@ var VERSION_MANIFEST = {
|
|
|
5868
5896
|
"1.0.0": {
|
|
5869
5897
|
version: "1.0.0",
|
|
5870
5898
|
label: "UCAN Required",
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
authorizationFn: "v1",
|
|
5899
|
+
ucanRequired: true,
|
|
5900
|
+
delegationRootRequired: true,
|
|
5901
|
+
whitelistOnlyAllowed: false,
|
|
5902
|
+
unrestrictedAllowed: false,
|
|
5903
|
+
executionPath: "invocation",
|
|
5904
|
+
authorizationFn: "v2",
|
|
5878
5905
|
allowedAuthModes: ["capability"],
|
|
5879
5906
|
ui: {
|
|
5880
5907
|
showDelegationPanel: true,
|
|
@@ -7571,6 +7598,1218 @@ function setRootMetadata(yDoc, root, plan, creatorDid, docId) {
|
|
|
7571
7598
|
});
|
|
7572
7599
|
}
|
|
7573
7600
|
|
|
7601
|
+
// src/core/lib/flowAgent/store.ts
|
|
7602
|
+
import * as Y6 from "yjs";
|
|
7603
|
+
|
|
7604
|
+
// src/core/lib/flowAgent/utils.ts
|
|
7605
|
+
function stableStringify(value) {
|
|
7606
|
+
if (value === void 0) return "null";
|
|
7607
|
+
if (value === null || typeof value !== "object") {
|
|
7608
|
+
return JSON.stringify(value);
|
|
7609
|
+
}
|
|
7610
|
+
if (Array.isArray(value)) {
|
|
7611
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
7612
|
+
}
|
|
7613
|
+
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
7614
|
+
return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
|
|
7615
|
+
}
|
|
7616
|
+
function fnv1a322(input) {
|
|
7617
|
+
let hash = 2166136261;
|
|
7618
|
+
for (let i = 0; i < input.length; i++) {
|
|
7619
|
+
hash ^= input.charCodeAt(i);
|
|
7620
|
+
hash = Math.imul(hash, 16777619);
|
|
7621
|
+
}
|
|
7622
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
7623
|
+
}
|
|
7624
|
+
function createIntentHash(intent) {
|
|
7625
|
+
return fnv1a322(stableStringify(intent));
|
|
7626
|
+
}
|
|
7627
|
+
function parseJsonProp(value, fallback) {
|
|
7628
|
+
if (value == null || value === "") return fallback;
|
|
7629
|
+
if (typeof value === "object") return value;
|
|
7630
|
+
if (typeof value !== "string") return fallback;
|
|
7631
|
+
try {
|
|
7632
|
+
const parsed = JSON.parse(value);
|
|
7633
|
+
return parsed;
|
|
7634
|
+
} catch {
|
|
7635
|
+
return fallback;
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
function getBlockActionType(block) {
|
|
7639
|
+
const props = block?.props;
|
|
7640
|
+
const actionType = props?.actionType;
|
|
7641
|
+
return typeof actionType === "string" && actionType.length > 0 ? actionType : void 0;
|
|
7642
|
+
}
|
|
7643
|
+
function getBlockTitle(block) {
|
|
7644
|
+
const props = block?.props;
|
|
7645
|
+
const title = props?.title;
|
|
7646
|
+
return typeof title === "string" && title.length > 0 ? title : void 0;
|
|
7647
|
+
}
|
|
7648
|
+
function getBlockType(block) {
|
|
7649
|
+
const type = block?.type;
|
|
7650
|
+
return typeof type === "string" && type.length > 0 ? type : "block";
|
|
7651
|
+
}
|
|
7652
|
+
function getBlockProps(block) {
|
|
7653
|
+
return block?.props || {};
|
|
7654
|
+
}
|
|
7655
|
+
function getBlockId(block) {
|
|
7656
|
+
const id = block?.id;
|
|
7657
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
7658
|
+
}
|
|
7659
|
+
function getAssigneeDid(block) {
|
|
7660
|
+
const props = getBlockProps(block);
|
|
7661
|
+
const assignment = parseJsonProp(props.assignment, {});
|
|
7662
|
+
const did = assignment?.assignedActor?.did;
|
|
7663
|
+
return typeof did === "string" && did.length > 0 ? did : void 0;
|
|
7664
|
+
}
|
|
7665
|
+
function getDueAt(block) {
|
|
7666
|
+
const props = getBlockProps(block);
|
|
7667
|
+
const ttlAbsoluteDueDate = props.ttlAbsoluteDueDate;
|
|
7668
|
+
if (typeof ttlAbsoluteDueDate !== "string" || ttlAbsoluteDueDate.length === 0) return void 0;
|
|
7669
|
+
const parsed = new Date(ttlAbsoluteDueDate).getTime();
|
|
7670
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
7671
|
+
}
|
|
7672
|
+
function capabilityForCommand(can, flowUri, nodeId) {
|
|
7673
|
+
return {
|
|
7674
|
+
can,
|
|
7675
|
+
with: `${flowUri}:${nodeId}`
|
|
7676
|
+
};
|
|
7677
|
+
}
|
|
7678
|
+
|
|
7679
|
+
// src/core/lib/flowAgent/store.ts
|
|
7680
|
+
var FLOW_AGENT_OUTBOX_KEY = "agentOutbox";
|
|
7681
|
+
var FLOW_AGENT_LEASES_KEY = "agentLeases";
|
|
7682
|
+
var FLOW_AGENT_AUDIT_SCOPE = "__flow_agent__";
|
|
7683
|
+
function getFlowAgentMaps(yDoc) {
|
|
7684
|
+
return {
|
|
7685
|
+
outbox: yDoc.getMap(FLOW_AGENT_OUTBOX_KEY),
|
|
7686
|
+
leases: yDoc.getMap(FLOW_AGENT_LEASES_KEY)
|
|
7687
|
+
};
|
|
7688
|
+
}
|
|
7689
|
+
function computeAgentCommandId(params) {
|
|
7690
|
+
const intentHash = createIntentHash(params);
|
|
7691
|
+
return `${params.flowId}:${params.nodeId}:${intentHash}`;
|
|
7692
|
+
}
|
|
7693
|
+
function queueAgentCommand(yDoc, command) {
|
|
7694
|
+
const { outbox } = getFlowAgentMaps(yDoc);
|
|
7695
|
+
const existing = outbox.get(command.id);
|
|
7696
|
+
if (existing) {
|
|
7697
|
+
if (existing.type === "assign_actor" && existing.status === "failed") {
|
|
7698
|
+
const retried = {
|
|
7699
|
+
...existing,
|
|
7700
|
+
status: "queued",
|
|
7701
|
+
error: void 0,
|
|
7702
|
+
lease: void 0,
|
|
7703
|
+
reason: command.reason,
|
|
7704
|
+
payload: command.payload,
|
|
7705
|
+
capability: command.capability,
|
|
7706
|
+
actorDid: command.actorDid,
|
|
7707
|
+
updatedAt: command.updatedAt
|
|
7708
|
+
};
|
|
7709
|
+
outbox.set(command.id, retried);
|
|
7710
|
+
try {
|
|
7711
|
+
appendAgentLedgerEvent(yDoc, {
|
|
7712
|
+
type: "agent.command",
|
|
7713
|
+
flowId: retried.flowId,
|
|
7714
|
+
nodeId: retried.nodeId,
|
|
7715
|
+
commandId: retried.id,
|
|
7716
|
+
actorDid: retried.actorDid,
|
|
7717
|
+
timestamp: retried.updatedAt,
|
|
7718
|
+
details: {
|
|
7719
|
+
commandType: retried.type,
|
|
7720
|
+
status: retried.status,
|
|
7721
|
+
idempotencyKey: retried.idempotencyKey,
|
|
7722
|
+
reason: retried.reason,
|
|
7723
|
+
retry: true
|
|
7724
|
+
}
|
|
7725
|
+
});
|
|
7726
|
+
} catch (error) {
|
|
7727
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
7728
|
+
if (!message.includes("Unexpected content type") && !message.includes("already been defined with a different constructor")) {
|
|
7729
|
+
throw error;
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
return { command: retried, created: true };
|
|
7733
|
+
}
|
|
7734
|
+
return { command: existing, created: false };
|
|
7735
|
+
}
|
|
7736
|
+
outbox.set(command.id, command);
|
|
7737
|
+
try {
|
|
7738
|
+
appendAgentLedgerEvent(yDoc, {
|
|
7739
|
+
type: "agent.command",
|
|
7740
|
+
flowId: command.flowId,
|
|
7741
|
+
nodeId: command.nodeId,
|
|
7742
|
+
commandId: command.id,
|
|
7743
|
+
actorDid: command.actorDid,
|
|
7744
|
+
timestamp: command.createdAt,
|
|
7745
|
+
details: {
|
|
7746
|
+
commandType: command.type,
|
|
7747
|
+
status: command.status,
|
|
7748
|
+
idempotencyKey: command.idempotencyKey,
|
|
7749
|
+
reason: command.reason
|
|
7750
|
+
}
|
|
7751
|
+
});
|
|
7752
|
+
} catch (error) {
|
|
7753
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
7754
|
+
if (!message.includes("Unexpected content type") && !message.includes("already been defined with a different constructor")) {
|
|
7755
|
+
throw error;
|
|
7756
|
+
}
|
|
7757
|
+
}
|
|
7758
|
+
return { command, created: true };
|
|
7759
|
+
}
|
|
7760
|
+
function readQueuedAgentCommands(yDoc) {
|
|
7761
|
+
const { outbox } = getFlowAgentMaps(yDoc);
|
|
7762
|
+
const commands = [];
|
|
7763
|
+
outbox.forEach((command) => {
|
|
7764
|
+
if (command.status === "queued" || command.status === "leased" || command.status === "running") {
|
|
7765
|
+
commands.push(command);
|
|
7766
|
+
}
|
|
7767
|
+
});
|
|
7768
|
+
commands.sort((a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id));
|
|
7769
|
+
return commands;
|
|
7770
|
+
}
|
|
7771
|
+
function updateAgentCommand(yDoc, commandId, patch) {
|
|
7772
|
+
const { outbox } = getFlowAgentMaps(yDoc);
|
|
7773
|
+
const current = outbox.get(commandId);
|
|
7774
|
+
if (!current) return null;
|
|
7775
|
+
const next = { ...current, ...patch };
|
|
7776
|
+
outbox.set(commandId, next);
|
|
7777
|
+
return next;
|
|
7778
|
+
}
|
|
7779
|
+
function appendAgentLedgerEvent(yDoc, event) {
|
|
7780
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
7781
|
+
let arr = auditMap.get(FLOW_AGENT_AUDIT_SCOPE);
|
|
7782
|
+
if (!arr) {
|
|
7783
|
+
arr = new Y6.Array();
|
|
7784
|
+
auditMap.set(FLOW_AGENT_AUDIT_SCOPE, arr);
|
|
7785
|
+
}
|
|
7786
|
+
const fullEvent = {
|
|
7787
|
+
id: event.id || `${event.type}:${event.commandId || event.nodeId || "flow"}:${createIntentHash(event.details)}:${event.timestamp}`,
|
|
7788
|
+
type: event.type,
|
|
7789
|
+
flowId: event.flowId,
|
|
7790
|
+
actorDid: event.actorDid,
|
|
7791
|
+
timestamp: event.timestamp,
|
|
7792
|
+
details: JSON.parse(stableStringify(event.details))
|
|
7793
|
+
};
|
|
7794
|
+
if (event.nodeId) fullEvent.nodeId = event.nodeId;
|
|
7795
|
+
if (event.commandId) fullEvent.commandId = event.commandId;
|
|
7796
|
+
arr.push([
|
|
7797
|
+
{
|
|
7798
|
+
id: fullEvent.id,
|
|
7799
|
+
type: fullEvent.type,
|
|
7800
|
+
details: fullEvent,
|
|
7801
|
+
meta: {
|
|
7802
|
+
timestamp: new Date(fullEvent.timestamp).toISOString(),
|
|
7803
|
+
userId: fullEvent.actorDid,
|
|
7804
|
+
editable: false
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
]);
|
|
7808
|
+
return fullEvent;
|
|
7809
|
+
}
|
|
7810
|
+
function readAgentLedgerEvents(yDoc, eventType) {
|
|
7811
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
7812
|
+
const arr = auditMap.get(FLOW_AGENT_AUDIT_SCOPE);
|
|
7813
|
+
if (!arr) return [];
|
|
7814
|
+
const events = [];
|
|
7815
|
+
arr.forEach((value) => {
|
|
7816
|
+
if (!value || typeof value !== "object") return;
|
|
7817
|
+
const entry = value;
|
|
7818
|
+
if (!entry.details) return;
|
|
7819
|
+
if (eventType && entry.details.type !== eventType) return;
|
|
7820
|
+
events.push(entry.details);
|
|
7821
|
+
});
|
|
7822
|
+
events.sort((a, b) => a.timestamp - b.timestamp || a.id.localeCompare(b.id));
|
|
7823
|
+
return events;
|
|
7824
|
+
}
|
|
7825
|
+
|
|
7826
|
+
// src/core/lib/flowAgent/policy.ts
|
|
7827
|
+
var COMMAND_CAPABILITIES = {
|
|
7828
|
+
diagnose_blocker: "flow/observe",
|
|
7829
|
+
assign_actor: "flow/node/assign",
|
|
7830
|
+
notify_actor: "flow/notify",
|
|
7831
|
+
execute_action: "flow/node/execute",
|
|
7832
|
+
validate_external_state: "flow/mutation/execute",
|
|
7833
|
+
submit_claim: "flow/claim/submit",
|
|
7834
|
+
watch_udid: "flow/observe",
|
|
7835
|
+
archive_flow: "flow/archive",
|
|
7836
|
+
propose_config_change: "flow/config/propose"
|
|
7837
|
+
};
|
|
7838
|
+
function requiredCapabilityForCommand(type, flowUri, nodeId) {
|
|
7839
|
+
return capabilityForCommand(COMMAND_CAPABILITIES[type], flowUri, nodeId);
|
|
7840
|
+
}
|
|
7841
|
+
function isCapabilityMatch(granted, required) {
|
|
7842
|
+
return canMatches(granted.can, required.can) && resourceMatches(granted.with, required.with);
|
|
7843
|
+
}
|
|
7844
|
+
function canMatches(granted, required) {
|
|
7845
|
+
if (granted === "*" || granted === required) return true;
|
|
7846
|
+
if (granted.endsWith("/*")) {
|
|
7847
|
+
const prefix = granted.slice(0, -1);
|
|
7848
|
+
return required.startsWith(prefix);
|
|
7849
|
+
}
|
|
7850
|
+
return false;
|
|
7851
|
+
}
|
|
7852
|
+
function resourceMatches(granted, required) {
|
|
7853
|
+
if (granted === "*" || granted === required) return true;
|
|
7854
|
+
if (granted.endsWith("*")) {
|
|
7855
|
+
const prefix = granted.slice(0, -1);
|
|
7856
|
+
return required.startsWith(prefix);
|
|
7857
|
+
}
|
|
7858
|
+
return false;
|
|
7859
|
+
}
|
|
7860
|
+
function evaluateFlowAgentPolicy({
|
|
7861
|
+
actorDid,
|
|
7862
|
+
commandType,
|
|
7863
|
+
flowUri,
|
|
7864
|
+
nodeId,
|
|
7865
|
+
delegationStore,
|
|
7866
|
+
delegations,
|
|
7867
|
+
now = Date.now()
|
|
7868
|
+
}) {
|
|
7869
|
+
const required = requiredCapabilityForCommand(commandType, flowUri, nodeId);
|
|
7870
|
+
const candidates = delegations || delegationStore?.getByAudience(actorDid) || [];
|
|
7871
|
+
for (const delegation of candidates) {
|
|
7872
|
+
if (delegation.audienceDid !== actorDid) continue;
|
|
7873
|
+
if (delegation.expiration != null && delegation.expiration <= now) continue;
|
|
7874
|
+
if (!delegation.capabilities.some((capability) => isCapabilityMatch(capability, required))) continue;
|
|
7875
|
+
return {
|
|
7876
|
+
allowed: true,
|
|
7877
|
+
reason: "Allowed by UCAN delegation",
|
|
7878
|
+
capability: required,
|
|
7879
|
+
proofCids: [delegation.cid, ...delegation.proofCids]
|
|
7880
|
+
};
|
|
7881
|
+
}
|
|
7882
|
+
return {
|
|
7883
|
+
allowed: false,
|
|
7884
|
+
reason: `Actor ${actorDid} lacks ${required.can} on ${required.with}`,
|
|
7885
|
+
capability: required,
|
|
7886
|
+
proofCids: []
|
|
7887
|
+
};
|
|
7888
|
+
}
|
|
7889
|
+
|
|
7890
|
+
// src/core/lib/flowAgent/commands.ts
|
|
7891
|
+
function createAgentCommand({ type, flowId, flowUri, nodeId, actor, reason, payload = {}, now = Date.now() }) {
|
|
7892
|
+
const capability = requiredCapabilityForCommand(type, flowUri, nodeId);
|
|
7893
|
+
const id = computeAgentCommandId({ flowId, nodeId, type, payload });
|
|
7894
|
+
return {
|
|
7895
|
+
id,
|
|
7896
|
+
type,
|
|
7897
|
+
flowId,
|
|
7898
|
+
flowUri,
|
|
7899
|
+
nodeId,
|
|
7900
|
+
actorDid: actor.did,
|
|
7901
|
+
status: "queued",
|
|
7902
|
+
capability,
|
|
7903
|
+
idempotencyKey: `${id}:${createIntentHash({ capability, payload })}`,
|
|
7904
|
+
createdAt: now,
|
|
7905
|
+
updatedAt: now,
|
|
7906
|
+
reason,
|
|
7907
|
+
payload
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7910
|
+
function validateAgentCommand(command) {
|
|
7911
|
+
if (!command.id) return { valid: false, error: "Command id is required" };
|
|
7912
|
+
if (!command.flowId) return { valid: false, error: "flowId is required" };
|
|
7913
|
+
if (!command.flowUri) return { valid: false, error: "flowUri is required" };
|
|
7914
|
+
if (!command.nodeId) return { valid: false, error: "nodeId is required" };
|
|
7915
|
+
if (!command.actorDid) return { valid: false, error: "actorDid is required" };
|
|
7916
|
+
if (!command.capability?.can || !command.capability?.with) return { valid: false, error: "capability is required" };
|
|
7917
|
+
if (!command.idempotencyKey) return { valid: false, error: "idempotencyKey is required" };
|
|
7918
|
+
if (isExternalMutation(command.type) && !command.payload.validator) {
|
|
7919
|
+
return {
|
|
7920
|
+
valid: false,
|
|
7921
|
+
error: `${command.type} requires payload.validator for post-action read-back validation`
|
|
7922
|
+
};
|
|
7923
|
+
}
|
|
7924
|
+
if (command.type === "propose_config_change" && !command.payload.proposal) {
|
|
7925
|
+
return { valid: false, error: "Config changes must be expressed as payload.proposal" };
|
|
7926
|
+
}
|
|
7927
|
+
return { valid: true };
|
|
7928
|
+
}
|
|
7929
|
+
function isExternalMutation(type) {
|
|
7930
|
+
return type === "validate_external_state";
|
|
7931
|
+
}
|
|
7932
|
+
|
|
7933
|
+
// src/core/lib/flowAgent/context.ts
|
|
7934
|
+
function buildFlowAgentContext({ yDoc, flowId, flowUri = `ixo:flow:${flowId}`, actor, blocks, editor, now }) {
|
|
7935
|
+
return {
|
|
7936
|
+
yDoc,
|
|
7937
|
+
flowId,
|
|
7938
|
+
flowUri,
|
|
7939
|
+
actor,
|
|
7940
|
+
...blocks ? { blocks } : {},
|
|
7941
|
+
...editor ? { editor } : {},
|
|
7942
|
+
...now ? { now } : {}
|
|
7943
|
+
};
|
|
7944
|
+
}
|
|
7945
|
+
|
|
7946
|
+
// src/core/lib/flowAgent/lease.ts
|
|
7947
|
+
var DEFAULT_FLOW_AGENT_LEASE_TTL_MS = 6e4;
|
|
7948
|
+
function acquireFlowAgentLease({
|
|
7949
|
+
leases,
|
|
7950
|
+
commandId,
|
|
7951
|
+
nodeId,
|
|
7952
|
+
actorDid,
|
|
7953
|
+
now = Date.now(),
|
|
7954
|
+
ttlMs = DEFAULT_FLOW_AGENT_LEASE_TTL_MS
|
|
7955
|
+
}) {
|
|
7956
|
+
const current = leases.get(commandId);
|
|
7957
|
+
if (current && current.expiresAt > now && current.actorDid !== actorDid) {
|
|
7958
|
+
return null;
|
|
7959
|
+
}
|
|
7960
|
+
const nextEpoch = current ? current.epoch + 1 : 1;
|
|
7961
|
+
const lease = {
|
|
7962
|
+
id: `${commandId}:lease:${nextEpoch}`,
|
|
7963
|
+
commandId,
|
|
7964
|
+
nodeId,
|
|
7965
|
+
actorDid,
|
|
7966
|
+
acquiredAt: now,
|
|
7967
|
+
expiresAt: now + ttlMs,
|
|
7968
|
+
epoch: nextEpoch
|
|
7969
|
+
};
|
|
7970
|
+
leases.set(commandId, lease);
|
|
7971
|
+
return lease;
|
|
7972
|
+
}
|
|
7973
|
+
function validateFlowAgentLease(leases, lease, now = Date.now()) {
|
|
7974
|
+
const current = leases.get(lease.commandId);
|
|
7975
|
+
return !!current && current.actorDid === lease.actorDid && current.epoch === lease.epoch && current.expiresAt > now;
|
|
7976
|
+
}
|
|
7977
|
+
function releaseFlowAgentLease(leases, lease) {
|
|
7978
|
+
const current = leases.get(lease.commandId);
|
|
7979
|
+
if (!current || current.actorDid !== lease.actorDid || current.epoch !== lease.epoch) return false;
|
|
7980
|
+
leases.delete(lease.commandId);
|
|
7981
|
+
return true;
|
|
7982
|
+
}
|
|
7983
|
+
function cleanupExpiredFlowAgentLeases(leases, now = Date.now()) {
|
|
7984
|
+
const expired = [];
|
|
7985
|
+
leases.forEach((lease, commandId) => {
|
|
7986
|
+
if (lease.expiresAt <= now) {
|
|
7987
|
+
expired.push(lease);
|
|
7988
|
+
leases.delete(commandId);
|
|
7989
|
+
}
|
|
7990
|
+
});
|
|
7991
|
+
return expired;
|
|
7992
|
+
}
|
|
7993
|
+
|
|
7994
|
+
// src/core/lib/flowAgent/state.ts
|
|
7995
|
+
function classifyBlockerCause(block, runtime) {
|
|
7996
|
+
const props = getBlockProps(block);
|
|
7997
|
+
if (runtime.error?.code === "missing_ucan") return "missing_ucan";
|
|
7998
|
+
if (runtime.error?.code === "validation_mismatch") return "validation_mismatch";
|
|
7999
|
+
if (runtime.error?.code === "external_confirmation_pending") return "external_confirmation_pending";
|
|
8000
|
+
if (runtime.error) return "service_error";
|
|
8001
|
+
const conditions = parseJsonProp(props.conditions, {});
|
|
8002
|
+
if (conditions.enabled && Array.isArray(conditions.conditions) && conditions.conditions.length > 0) {
|
|
8003
|
+
return "failed_upstream";
|
|
8004
|
+
}
|
|
8005
|
+
const inputs = parseJsonProp(props.inputs, {});
|
|
8006
|
+
const requiredInputs = parseJsonProp(props.requiredInputs, []);
|
|
8007
|
+
if (requiredInputs.some((key) => inputs[key] == null || inputs[key] === "")) {
|
|
8008
|
+
return "missing_input";
|
|
8009
|
+
}
|
|
8010
|
+
return void 0;
|
|
8011
|
+
}
|
|
8012
|
+
function classifyNodeState({ block, runtime, now, pendingInvocationCount = 0 }) {
|
|
8013
|
+
if (runtime.state === "completed" || runtime.state === "cancelled") return "Done";
|
|
8014
|
+
if (runtime.state === "failed" || runtime.error) return "Blocked";
|
|
8015
|
+
const dueAt = getDueAt(block);
|
|
8016
|
+
if (dueAt != null && dueAt <= now) return "Overdue";
|
|
8017
|
+
if (pendingInvocationCount > 0) return "Pending";
|
|
8018
|
+
return "Pending";
|
|
8019
|
+
}
|
|
8020
|
+
function snapshotNode(block, runtime, now, pendingInvocationCount = 0) {
|
|
8021
|
+
const nodeId = getBlockId(block);
|
|
8022
|
+
if (!nodeId) {
|
|
8023
|
+
throw new Error("Cannot snapshot a block without an id");
|
|
8024
|
+
}
|
|
8025
|
+
const publicState = classifyNodeState({ block, runtime, now, pendingInvocationCount });
|
|
8026
|
+
const dueAt = getDueAt(block);
|
|
8027
|
+
const assigneeDid = getAssigneeDid(block);
|
|
8028
|
+
const snapshot = {
|
|
8029
|
+
nodeId,
|
|
8030
|
+
blockType: getBlockType(block),
|
|
8031
|
+
runtime,
|
|
8032
|
+
publicState,
|
|
8033
|
+
pendingInvocationCount
|
|
8034
|
+
};
|
|
8035
|
+
const actionType = getBlockActionType(block);
|
|
8036
|
+
if (actionType) snapshot.actionType = actionType;
|
|
8037
|
+
const title = getBlockTitle(block);
|
|
8038
|
+
if (title) snapshot.title = title;
|
|
8039
|
+
if (publicState === "Blocked") {
|
|
8040
|
+
snapshot.blockerCause = classifyBlockerCause(block, runtime) || "unknown";
|
|
8041
|
+
}
|
|
8042
|
+
if (assigneeDid) snapshot.assigneeDid = assigneeDid;
|
|
8043
|
+
if (dueAt != null) snapshot.dueAt = dueAt;
|
|
8044
|
+
return snapshot;
|
|
8045
|
+
}
|
|
8046
|
+
|
|
8047
|
+
// src/core/lib/flowAgent/orchestrator.ts
|
|
8048
|
+
var BLOCKER_DIAGNOSIS_VERSION = 2;
|
|
8049
|
+
function getBlocks(context) {
|
|
8050
|
+
return context.blocks || context.editor?.document || [];
|
|
8051
|
+
}
|
|
8052
|
+
function getRuntime(yDoc, nodeId) {
|
|
8053
|
+
const runtime = yDoc.getMap("runtime");
|
|
8054
|
+
const value = runtime.get(nodeId);
|
|
8055
|
+
return value && typeof value === "object" ? value : {};
|
|
8056
|
+
}
|
|
8057
|
+
function validateInputs(block) {
|
|
8058
|
+
const props = getBlockProps(block);
|
|
8059
|
+
const inputs = parseJsonProp(props.inputs, {});
|
|
8060
|
+
const requiredInputs = parseJsonProp(props.requiredInputs, []);
|
|
8061
|
+
const missing = requiredInputs.filter((key) => inputs[key] == null || inputs[key] === "");
|
|
8062
|
+
if (missing.length > 0) {
|
|
8063
|
+
return { valid: false, reason: `Missing required inputs: ${missing.join(", ")}` };
|
|
8064
|
+
}
|
|
8065
|
+
return { valid: true };
|
|
8066
|
+
}
|
|
8067
|
+
function chooseActor(block, options) {
|
|
8068
|
+
const props = getBlockProps(block);
|
|
8069
|
+
const actionType = getBlockActionType(block);
|
|
8070
|
+
const requiredSkill = typeof props.requiredSkill === "string" ? props.requiredSkill : actionType;
|
|
8071
|
+
if (!options.candidateActors || options.candidateActors.length === 0) return void 0;
|
|
8072
|
+
if (!requiredSkill) return options.candidateActors[0];
|
|
8073
|
+
return options.candidateActors.find((actor) => actor.skills?.includes(requiredSkill)) || options.candidateActors[0];
|
|
8074
|
+
}
|
|
8075
|
+
function canQueueCommand(type, nodeId, context, options) {
|
|
8076
|
+
return evaluateFlowAgentPolicy({
|
|
8077
|
+
actorDid: context.actor.did,
|
|
8078
|
+
commandType: type,
|
|
8079
|
+
flowUri: context.flowUri,
|
|
8080
|
+
nodeId,
|
|
8081
|
+
delegationStore: options.delegationStore,
|
|
8082
|
+
delegations: options.delegations,
|
|
8083
|
+
now: context.now?.() || Date.now()
|
|
8084
|
+
}).allowed;
|
|
8085
|
+
}
|
|
8086
|
+
function safeAppendAgentLedgerEvent(yDoc, event) {
|
|
8087
|
+
try {
|
|
8088
|
+
appendAgentLedgerEvent(yDoc, event);
|
|
8089
|
+
} catch (error) {
|
|
8090
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
8091
|
+
if (message.includes("Unexpected content type") || message.includes("already been defined with a different constructor")) {
|
|
8092
|
+
return;
|
|
8093
|
+
}
|
|
8094
|
+
throw error;
|
|
8095
|
+
}
|
|
8096
|
+
}
|
|
8097
|
+
function queueIfAuthorized(context, options, type, nodeId, reason, payload) {
|
|
8098
|
+
const now = context.now?.() || Date.now();
|
|
8099
|
+
const policy = evaluateFlowAgentPolicy({
|
|
8100
|
+
actorDid: context.actor.did,
|
|
8101
|
+
commandType: type,
|
|
8102
|
+
flowUri: context.flowUri,
|
|
8103
|
+
nodeId,
|
|
8104
|
+
delegationStore: options.delegationStore,
|
|
8105
|
+
delegations: options.delegations,
|
|
8106
|
+
now
|
|
8107
|
+
});
|
|
8108
|
+
safeAppendAgentLedgerEvent(context.yDoc, {
|
|
8109
|
+
type: "agent.decision",
|
|
8110
|
+
flowId: context.flowId,
|
|
8111
|
+
nodeId,
|
|
8112
|
+
actorDid: context.actor.did,
|
|
8113
|
+
timestamp: now,
|
|
8114
|
+
details: {
|
|
8115
|
+
commandType: type,
|
|
8116
|
+
allowed: policy.allowed,
|
|
8117
|
+
reason: policy.reason,
|
|
8118
|
+
capability: policy.capability
|
|
8119
|
+
}
|
|
8120
|
+
});
|
|
8121
|
+
if (!policy.allowed) return null;
|
|
8122
|
+
const command = createAgentCommand({
|
|
8123
|
+
type,
|
|
8124
|
+
flowId: context.flowId,
|
|
8125
|
+
flowUri: context.flowUri,
|
|
8126
|
+
nodeId,
|
|
8127
|
+
actor: context.actor,
|
|
8128
|
+
reason,
|
|
8129
|
+
payload,
|
|
8130
|
+
now
|
|
8131
|
+
});
|
|
8132
|
+
const validation = validateAgentCommand(command);
|
|
8133
|
+
if (!validation.valid) {
|
|
8134
|
+
safeAppendAgentLedgerEvent(context.yDoc, {
|
|
8135
|
+
type: "agent.validation",
|
|
8136
|
+
flowId: context.flowId,
|
|
8137
|
+
nodeId,
|
|
8138
|
+
commandId: command.id,
|
|
8139
|
+
actorDid: context.actor.did,
|
|
8140
|
+
timestamp: now,
|
|
8141
|
+
details: { valid: false, error: validation.error }
|
|
8142
|
+
});
|
|
8143
|
+
return null;
|
|
8144
|
+
}
|
|
8145
|
+
return queueAgentCommand(context.yDoc, command).created ? command : null;
|
|
8146
|
+
}
|
|
8147
|
+
function planForSnapshot(context, options, block, snapshot) {
|
|
8148
|
+
const queued = [];
|
|
8149
|
+
const actionType = getBlockActionType(block);
|
|
8150
|
+
if (snapshot.publicState === "Done") {
|
|
8151
|
+
const runtime = snapshot.runtime;
|
|
8152
|
+
if (actionType === "qi/claim.submit" && runtime.output?.claimId && !runtime.output?.udid) {
|
|
8153
|
+
const command = queueIfAuthorized(context, options, "watch_udid", snapshot.nodeId, "Claim submitted but UDID is not yet observed", {
|
|
8154
|
+
claimId: runtime.output.claimId,
|
|
8155
|
+
timeoutMs: 864e5
|
|
8156
|
+
});
|
|
8157
|
+
if (command) queued.push(command);
|
|
8158
|
+
}
|
|
8159
|
+
return queued;
|
|
8160
|
+
}
|
|
8161
|
+
if (snapshot.publicState === "Overdue") {
|
|
8162
|
+
const command = queueIfAuthorized(context, options, "notify_actor", snapshot.nodeId, "Action node is overdue", {
|
|
8163
|
+
assigneeDid: snapshot.assigneeDid,
|
|
8164
|
+
dueAt: snapshot.dueAt,
|
|
8165
|
+
severity: "overdue"
|
|
8166
|
+
});
|
|
8167
|
+
if (command) queued.push(command);
|
|
8168
|
+
return queued;
|
|
8169
|
+
}
|
|
8170
|
+
if (snapshot.publicState === "Blocked") {
|
|
8171
|
+
const command = queueIfAuthorized(context, options, "diagnose_blocker", snapshot.nodeId, "Action node is blocked and requires diagnosis", {
|
|
8172
|
+
cause: snapshot.blockerCause || "unknown",
|
|
8173
|
+
error: snapshot.runtime.error,
|
|
8174
|
+
diagnosisVersion: BLOCKER_DIAGNOSIS_VERSION
|
|
8175
|
+
});
|
|
8176
|
+
if (command) queued.push(command);
|
|
8177
|
+
return queued;
|
|
8178
|
+
}
|
|
8179
|
+
const assigneeDid = getAssigneeDid(block);
|
|
8180
|
+
if (!assigneeDid) {
|
|
8181
|
+
const actor = chooseActor(block, options);
|
|
8182
|
+
const command = queueIfAuthorized(context, options, "assign_actor", snapshot.nodeId, "Action node has no assigned actor", {
|
|
8183
|
+
targetActorDid: actor?.did,
|
|
8184
|
+
targetMatrixUserId: actor?.matrixUserId,
|
|
8185
|
+
requiredActionType: actionType
|
|
8186
|
+
});
|
|
8187
|
+
if (command) queued.push(command);
|
|
8188
|
+
return queued;
|
|
8189
|
+
}
|
|
8190
|
+
const inputValidation = validateInputs(block);
|
|
8191
|
+
if (!inputValidation.valid) {
|
|
8192
|
+
const command = queueIfAuthorized(context, options, "diagnose_blocker", snapshot.nodeId, inputValidation.reason || "Action node inputs are not ready", {
|
|
8193
|
+
cause: "missing_input"
|
|
8194
|
+
});
|
|
8195
|
+
if (command) queued.push(command);
|
|
8196
|
+
return queued;
|
|
8197
|
+
}
|
|
8198
|
+
if (actionType === "qi/claim.submit") {
|
|
8199
|
+
const command = queueIfAuthorized(context, options, "submit_claim", snapshot.nodeId, "Claim action is pending and agent is authorized to submit", {
|
|
8200
|
+
actionType
|
|
8201
|
+
});
|
|
8202
|
+
if (command) queued.push(command);
|
|
8203
|
+
return queued;
|
|
8204
|
+
}
|
|
8205
|
+
if (actionType && canQueueCommand("execute_action", snapshot.nodeId, context, options)) {
|
|
8206
|
+
const command = queueIfAuthorized(context, options, "execute_action", snapshot.nodeId, "Action node is pending and executable", {
|
|
8207
|
+
actionType
|
|
8208
|
+
});
|
|
8209
|
+
if (command) queued.push(command);
|
|
8210
|
+
return queued;
|
|
8211
|
+
}
|
|
8212
|
+
const notifyCommand = queueIfAuthorized(context, options, "notify_actor", snapshot.nodeId, "Action node is pending but the Flow Agent cannot execute it", {
|
|
8213
|
+
assigneeDid,
|
|
8214
|
+
requiredActionType: actionType,
|
|
8215
|
+
severity: "attention"
|
|
8216
|
+
});
|
|
8217
|
+
if (notifyCommand) queued.push(notifyCommand);
|
|
8218
|
+
return queued;
|
|
8219
|
+
}
|
|
8220
|
+
function planRalphLoopCommands(context, options = {}) {
|
|
8221
|
+
const now = context.now?.() || Date.now();
|
|
8222
|
+
const blocks = getBlocks(context);
|
|
8223
|
+
const snapshots = [];
|
|
8224
|
+
const queuedCommands = [];
|
|
8225
|
+
for (const block of blocks) {
|
|
8226
|
+
const nodeId = getBlockId(block);
|
|
8227
|
+
if (!nodeId) continue;
|
|
8228
|
+
const pendingInvocationCount = readPendingInvocations(context.yDoc, nodeId).length;
|
|
8229
|
+
const snapshot = snapshotNode(block, getRuntime(context.yDoc, nodeId), now, pendingInvocationCount);
|
|
8230
|
+
snapshots.push(snapshot);
|
|
8231
|
+
queuedCommands.push(...planForSnapshot(context, options, block, snapshot));
|
|
8232
|
+
}
|
|
8233
|
+
const flowDone = snapshots.length > 0 && snapshots.every((snapshot) => snapshot.publicState === "Done");
|
|
8234
|
+
if (flowDone && options.archiveWhenDone !== false) {
|
|
8235
|
+
const archiveCommand = queueIfAuthorized(context, options, "archive_flow", context.flowId, "All Ralph loop action nodes are done", {
|
|
8236
|
+
restartIfRepeats: true
|
|
8237
|
+
});
|
|
8238
|
+
if (archiveCommand) queuedCommands.push(archiveCommand);
|
|
8239
|
+
}
|
|
8240
|
+
return { snapshots, queuedCommands, flowDone };
|
|
8241
|
+
}
|
|
8242
|
+
async function callExecutor(command, context, executor) {
|
|
8243
|
+
switch (command.type) {
|
|
8244
|
+
case "execute_action":
|
|
8245
|
+
return executor.executeAction ? executor.executeAction(command, context) : { commandId: command.id, success: false, error: "No executeAction handler configured" };
|
|
8246
|
+
case "assign_actor":
|
|
8247
|
+
return executor.assignActor ? executor.assignActor(command, context) : { commandId: command.id, success: false, error: "No assignActor handler configured" };
|
|
8248
|
+
case "notify_actor":
|
|
8249
|
+
return executor.notifyActor ? executor.notifyActor(command, context) : { commandId: command.id, success: false, error: "No notifyActor handler configured" };
|
|
8250
|
+
case "validate_external_state":
|
|
8251
|
+
return executor.validateExternalState ? executor.validateExternalState(command, context) : { commandId: command.id, success: false, error: "No validateExternalState handler configured" };
|
|
8252
|
+
case "submit_claim":
|
|
8253
|
+
return executor.submitClaim ? executor.submitClaim(command, context) : { commandId: command.id, success: false, error: "No submitClaim handler configured" };
|
|
8254
|
+
case "watch_udid":
|
|
8255
|
+
return executor.watchUdid ? executor.watchUdid(command, context) : { commandId: command.id, success: false, error: "No watchUdid handler configured" };
|
|
8256
|
+
case "archive_flow":
|
|
8257
|
+
return executor.archiveFlow ? executor.archiveFlow(command, context) : { commandId: command.id, success: false, error: "No archiveFlow handler configured" };
|
|
8258
|
+
case "propose_config_change":
|
|
8259
|
+
return executor.proposeConfigChange ? executor.proposeConfigChange(command, context) : { commandId: command.id, success: false, error: "No proposeConfigChange handler configured" };
|
|
8260
|
+
case "diagnose_blocker":
|
|
8261
|
+
return executor.diagnoseBlocker ? executor.diagnoseBlocker(command, context) : { commandId: command.id, success: false, error: "No diagnoseBlocker handler configured" };
|
|
8262
|
+
}
|
|
8263
|
+
}
|
|
8264
|
+
async function executeQueuedAgentCommands(context, options = {}) {
|
|
8265
|
+
const executor = options.executor;
|
|
8266
|
+
if (!executor) return [];
|
|
8267
|
+
const now = context.now?.() || Date.now();
|
|
8268
|
+
const { leases } = getFlowAgentMaps(context.yDoc);
|
|
8269
|
+
const results = [];
|
|
8270
|
+
for (const command of readQueuedAgentCommands(context.yDoc)) {
|
|
8271
|
+
const validation = validateAgentCommand(command);
|
|
8272
|
+
if (!validation.valid) {
|
|
8273
|
+
updateAgentCommand(context.yDoc, command.id, { status: "failed", error: validation.error, updatedAt: now });
|
|
8274
|
+
results.push({ commandId: command.id, success: false, error: validation.error });
|
|
8275
|
+
continue;
|
|
8276
|
+
}
|
|
8277
|
+
const policy = evaluateFlowAgentPolicy({
|
|
8278
|
+
actorDid: context.actor.did,
|
|
8279
|
+
commandType: command.type,
|
|
8280
|
+
flowUri: context.flowUri,
|
|
8281
|
+
nodeId: command.nodeId,
|
|
8282
|
+
delegationStore: options.delegationStore,
|
|
8283
|
+
delegations: options.delegations,
|
|
8284
|
+
now
|
|
8285
|
+
});
|
|
8286
|
+
if (!policy.allowed) {
|
|
8287
|
+
updateAgentCommand(context.yDoc, command.id, { status: "skipped", error: policy.reason, updatedAt: now });
|
|
8288
|
+
results.push({ commandId: command.id, success: false, error: policy.reason });
|
|
8289
|
+
continue;
|
|
8290
|
+
}
|
|
8291
|
+
const lease = acquireFlowAgentLease({
|
|
8292
|
+
leases,
|
|
8293
|
+
commandId: command.id,
|
|
8294
|
+
nodeId: command.nodeId,
|
|
8295
|
+
actorDid: context.actor.did,
|
|
8296
|
+
now,
|
|
8297
|
+
ttlMs: options.leaseTtlMs
|
|
8298
|
+
});
|
|
8299
|
+
if (!lease) {
|
|
8300
|
+
continue;
|
|
8301
|
+
}
|
|
8302
|
+
updateAgentCommand(context.yDoc, command.id, { status: "running", lease, updatedAt: now });
|
|
8303
|
+
try {
|
|
8304
|
+
const result = await callExecutor({ ...command, lease, status: "running" }, context, executor);
|
|
8305
|
+
if (!validateFlowAgentLease(leases, lease, context.now?.() || Date.now())) {
|
|
8306
|
+
const leaseError = "Lease expired or was superseded before command commit";
|
|
8307
|
+
updateAgentCommand(context.yDoc, command.id, { status: "failed", error: leaseError, updatedAt: context.now?.() || Date.now() });
|
|
8308
|
+
results.push({ commandId: command.id, success: false, error: leaseError });
|
|
8309
|
+
continue;
|
|
8310
|
+
}
|
|
8311
|
+
updateAgentCommand(context.yDoc, command.id, {
|
|
8312
|
+
status: result.success ? "confirmed" : "failed",
|
|
8313
|
+
error: result.error,
|
|
8314
|
+
updatedAt: context.now?.() || Date.now()
|
|
8315
|
+
});
|
|
8316
|
+
safeAppendAgentLedgerEvent(context.yDoc, {
|
|
8317
|
+
type: result.success ? "agent.validation" : "agent.escalation",
|
|
8318
|
+
flowId: context.flowId,
|
|
8319
|
+
nodeId: command.nodeId,
|
|
8320
|
+
commandId: command.id,
|
|
8321
|
+
actorDid: context.actor.did,
|
|
8322
|
+
timestamp: context.now?.() || Date.now(),
|
|
8323
|
+
details: {
|
|
8324
|
+
success: result.success,
|
|
8325
|
+
confirmed: result.confirmed,
|
|
8326
|
+
output: result.output,
|
|
8327
|
+
error: result.error
|
|
8328
|
+
}
|
|
8329
|
+
});
|
|
8330
|
+
results.push(result);
|
|
8331
|
+
} finally {
|
|
8332
|
+
releaseFlowAgentLease(leases, lease);
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
return results;
|
|
8336
|
+
}
|
|
8337
|
+
async function tickFlowAgent(context, options = {}) {
|
|
8338
|
+
const plan = planRalphLoopCommands(context, options);
|
|
8339
|
+
const executedCommands = await executeQueuedAgentCommands(context, options);
|
|
8340
|
+
return {
|
|
8341
|
+
flowDone: plan.flowDone,
|
|
8342
|
+
snapshots: plan.snapshots,
|
|
8343
|
+
queuedCommands: plan.queuedCommands,
|
|
8344
|
+
executedCommands
|
|
8345
|
+
};
|
|
8346
|
+
}
|
|
8347
|
+
|
|
8348
|
+
// src/core/lib/flowAgent/execution.ts
|
|
8349
|
+
var DEFAULT_SCHEMA_VERSION = "1.0.0";
|
|
8350
|
+
function safeAppendAgentLedgerEvent2(yDoc, event) {
|
|
8351
|
+
try {
|
|
8352
|
+
appendAgentLedgerEvent(yDoc, event);
|
|
8353
|
+
} catch (error) {
|
|
8354
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
8355
|
+
if (message.includes("Unexpected content type") || message.includes("already been defined with a different constructor")) {
|
|
8356
|
+
return;
|
|
8357
|
+
}
|
|
8358
|
+
throw error;
|
|
8359
|
+
}
|
|
8360
|
+
}
|
|
8361
|
+
function createYDocRuntimeManager(yDoc) {
|
|
8362
|
+
const runtime = yDoc.getMap("runtime");
|
|
8363
|
+
return {
|
|
8364
|
+
get: (nodeId) => {
|
|
8365
|
+
const value = runtime.get(nodeId);
|
|
8366
|
+
return value && typeof value === "object" ? { ...value } : {};
|
|
8367
|
+
},
|
|
8368
|
+
update: (nodeId, updates) => {
|
|
8369
|
+
const current = runtime.get(nodeId);
|
|
8370
|
+
runtime.set(nodeId, {
|
|
8371
|
+
...current && typeof current === "object" ? current : {},
|
|
8372
|
+
...updates
|
|
8373
|
+
});
|
|
8374
|
+
}
|
|
8375
|
+
};
|
|
8376
|
+
}
|
|
8377
|
+
function getRuntimeRecord(yDoc, nodeId) {
|
|
8378
|
+
const runtime = yDoc.getMap("runtime");
|
|
8379
|
+
const value = runtime.get(nodeId);
|
|
8380
|
+
return value && typeof value === "object" ? value : {};
|
|
8381
|
+
}
|
|
8382
|
+
function clearRuntimeBlocker(yDoc, nodeId, updates) {
|
|
8383
|
+
const runtime = yDoc.getMap("runtime");
|
|
8384
|
+
const current = getRuntimeRecord(yDoc, nodeId);
|
|
8385
|
+
const { state: _state, error: _error, ...rest } = current;
|
|
8386
|
+
runtime.set(nodeId, {
|
|
8387
|
+
...rest,
|
|
8388
|
+
...updates
|
|
8389
|
+
});
|
|
8390
|
+
}
|
|
8391
|
+
function findContextBlock(context, nodeId) {
|
|
8392
|
+
const block = (context.blocks || []).find((entry) => getBlockId(entry) === nodeId);
|
|
8393
|
+
return block && typeof block === "object" ? block : null;
|
|
8394
|
+
}
|
|
8395
|
+
function parseInputs2(value) {
|
|
8396
|
+
return parseJsonProp(value, {});
|
|
8397
|
+
}
|
|
8398
|
+
function getXmlAttribute(element, key) {
|
|
8399
|
+
return element.getAttribute(key);
|
|
8400
|
+
}
|
|
8401
|
+
function setXmlAttribute(element, key, value) {
|
|
8402
|
+
element.setAttribute(key, value);
|
|
8403
|
+
}
|
|
8404
|
+
function isRecord(value) {
|
|
8405
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
8406
|
+
}
|
|
8407
|
+
function isYXmlContainerLike(value) {
|
|
8408
|
+
return value != null && typeof value === "object" && typeof value.toArray === "function";
|
|
8409
|
+
}
|
|
8410
|
+
function isYXmlElementLike(value) {
|
|
8411
|
+
return isYXmlContainerLike(value) && typeof value.nodeName === "string" && typeof value.getAttribute === "function" && typeof value.setAttribute === "function";
|
|
8412
|
+
}
|
|
8413
|
+
function getElementBlockId(element) {
|
|
8414
|
+
const directId = getXmlAttribute(element, "id");
|
|
8415
|
+
if (typeof directId === "string" && directId.length > 0) return directId;
|
|
8416
|
+
const attrs = getXmlAttribute(element, "attrs");
|
|
8417
|
+
const attrsId = isRecord(attrs) ? attrs.id : void 0;
|
|
8418
|
+
return typeof attrsId === "string" && attrsId.length > 0 ? attrsId : void 0;
|
|
8419
|
+
}
|
|
8420
|
+
function updateXmlElementProps(element, blockId, propsPatch) {
|
|
8421
|
+
const attrs = getXmlAttribute(element, "attrs");
|
|
8422
|
+
if (isRecord(attrs)) {
|
|
8423
|
+
const existingProps = isRecord(attrs.props) ? attrs.props : {};
|
|
8424
|
+
setXmlAttribute(element, "attrs", {
|
|
8425
|
+
...attrs,
|
|
8426
|
+
id: typeof attrs.id === "string" ? attrs.id : blockId,
|
|
8427
|
+
props: {
|
|
8428
|
+
...existingProps,
|
|
8429
|
+
...propsPatch
|
|
8430
|
+
}
|
|
8431
|
+
});
|
|
8432
|
+
}
|
|
8433
|
+
for (const [key, value] of Object.entries(propsPatch)) {
|
|
8434
|
+
element.setAttribute(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
8435
|
+
}
|
|
8436
|
+
}
|
|
8437
|
+
function findXmlElementById(container, blockId) {
|
|
8438
|
+
for (const node of container.toArray()) {
|
|
8439
|
+
if (!isYXmlElementLike(node)) continue;
|
|
8440
|
+
const id = getElementBlockId(node);
|
|
8441
|
+
if (id === blockId) return node;
|
|
8442
|
+
const nested = findXmlElementById(node, blockId);
|
|
8443
|
+
if (nested) return nested;
|
|
8444
|
+
}
|
|
8445
|
+
return null;
|
|
8446
|
+
}
|
|
8447
|
+
function updateDocumentBlockProps(yDoc, blockId, propsPatch) {
|
|
8448
|
+
const target = findXmlElementById(yDoc.getXmlFragment("document"), blockId);
|
|
8449
|
+
if (!target) return false;
|
|
8450
|
+
updateXmlElementProps(target, blockId, propsPatch);
|
|
8451
|
+
const content = target.toArray().find((node) => isYXmlElementLike(node) && node.nodeName !== "blockGroup");
|
|
8452
|
+
if (content) {
|
|
8453
|
+
updateXmlElementProps(content, blockId, propsPatch);
|
|
8454
|
+
}
|
|
8455
|
+
return true;
|
|
8456
|
+
}
|
|
8457
|
+
function assignActorInYDoc(yDoc, command, context) {
|
|
8458
|
+
const targetActorDid = typeof command.payload.targetActorDid === "string" && command.payload.targetActorDid.length > 0 ? command.payload.targetActorDid : context.actor.did;
|
|
8459
|
+
const targetMatrixUserId = typeof command.payload.targetMatrixUserId === "string" ? command.payload.targetMatrixUserId : void 0;
|
|
8460
|
+
const assignment = {
|
|
8461
|
+
assignedActor: {
|
|
8462
|
+
did: targetActorDid,
|
|
8463
|
+
avatar: "",
|
|
8464
|
+
displayName: targetMatrixUserId || targetActorDid
|
|
8465
|
+
},
|
|
8466
|
+
assignedBy: {
|
|
8467
|
+
did: context.actor.did,
|
|
8468
|
+
avatar: "",
|
|
8469
|
+
displayName: context.actor.displayName || context.actor.matrixUserId || ""
|
|
8470
|
+
},
|
|
8471
|
+
assignedTimestamp: new Date(context.now?.() || Date.now()).toISOString()
|
|
8472
|
+
};
|
|
8473
|
+
const assignmentJson = JSON.stringify(assignment);
|
|
8474
|
+
const updatedDocument = updateDocumentBlockProps(yDoc, command.nodeId, {
|
|
8475
|
+
assignment: assignmentJson
|
|
8476
|
+
});
|
|
8477
|
+
if (!updatedDocument) {
|
|
8478
|
+
throw new Error(`Block ${command.nodeId} was not found for assignment`);
|
|
8479
|
+
}
|
|
8480
|
+
const runtime = createYDocRuntimeManager(yDoc);
|
|
8481
|
+
runtime.update(command.nodeId, {
|
|
8482
|
+
assignedActorDid: targetActorDid,
|
|
8483
|
+
assignedByDid: context.actor.did,
|
|
8484
|
+
assignedAt: assignment.assignedTimestamp,
|
|
8485
|
+
lastCommandId: command.id
|
|
8486
|
+
});
|
|
8487
|
+
return assignment;
|
|
8488
|
+
}
|
|
8489
|
+
async function executeAssignActorCommand(command, context, options) {
|
|
8490
|
+
const capability = { can: "flow/node/assign", with: `${context.flowUri}:${command.nodeId}` };
|
|
8491
|
+
const proofs = await options.ucanService.findValidProofs(context.actor.did, capability);
|
|
8492
|
+
if (!proofs.found || !proofs.proofCids) {
|
|
8493
|
+
return {
|
|
8494
|
+
commandId: command.id,
|
|
8495
|
+
success: false,
|
|
8496
|
+
error: proofs.error || "No valid delegation found for assignment"
|
|
8497
|
+
};
|
|
8498
|
+
}
|
|
8499
|
+
const result = await options.ucanService.executeWithInvocation(
|
|
8500
|
+
{
|
|
8501
|
+
invokerDid: context.actor.did,
|
|
8502
|
+
invokerType: options.actorType,
|
|
8503
|
+
entityRoomId: options.entityRoomId,
|
|
8504
|
+
capability,
|
|
8505
|
+
proofs: proofs.proofCids,
|
|
8506
|
+
pin: options.pin
|
|
8507
|
+
},
|
|
8508
|
+
async () => assignActorInYDoc(context.yDoc, command, context),
|
|
8509
|
+
context.flowId,
|
|
8510
|
+
command.nodeId
|
|
8511
|
+
);
|
|
8512
|
+
return {
|
|
8513
|
+
commandId: command.id,
|
|
8514
|
+
success: result.success,
|
|
8515
|
+
confirmed: result.success,
|
|
8516
|
+
output: result.actionResult && typeof result.actionResult === "object" ? { assignment: result.actionResult, invocationCid: result.invocationCid } : { invocationCid: result.invocationCid },
|
|
8517
|
+
error: result.error
|
|
8518
|
+
};
|
|
8519
|
+
}
|
|
8520
|
+
async function executeActionCommand(command, context, options) {
|
|
8521
|
+
const actionType = typeof command.payload.actionType === "string" ? command.payload.actionType : void 0;
|
|
8522
|
+
if (!actionType) {
|
|
8523
|
+
return { commandId: command.id, success: false, error: "execute_action command is missing payload.actionType" };
|
|
8524
|
+
}
|
|
8525
|
+
const action = getAction(actionType);
|
|
8526
|
+
if (!action) {
|
|
8527
|
+
return { commandId: command.id, success: false, error: `No registered action found for ${actionType}` };
|
|
8528
|
+
}
|
|
8529
|
+
const block = findContextBlock(context, command.nodeId);
|
|
8530
|
+
if (!block) {
|
|
8531
|
+
return { commandId: command.id, success: false, error: `Block ${command.nodeId} was not found in runtime context` };
|
|
8532
|
+
}
|
|
8533
|
+
const props = getBlockProps(block);
|
|
8534
|
+
const inputs = parseInputs2(props.inputs);
|
|
8535
|
+
const runtime = createYDocRuntimeManager(context.yDoc);
|
|
8536
|
+
const flowNode = buildFlowNodeFromBlock(block);
|
|
8537
|
+
const outcome = await executeNode({
|
|
8538
|
+
node: flowNode,
|
|
8539
|
+
actorDid: context.actor.did,
|
|
8540
|
+
actorType: options.actorType,
|
|
8541
|
+
entityRoomId: options.entityRoomId,
|
|
8542
|
+
context: {
|
|
8543
|
+
runtime,
|
|
8544
|
+
ucanService: options.ucanService,
|
|
8545
|
+
invocationStore: options.invocationStore,
|
|
8546
|
+
flowUri: context.flowUri,
|
|
8547
|
+
flowId: context.flowId,
|
|
8548
|
+
flowOwnerDid: "",
|
|
8549
|
+
schemaVersion: options.schemaVersion || DEFAULT_SCHEMA_VERSION,
|
|
8550
|
+
now: options.now || context.now
|
|
8551
|
+
},
|
|
8552
|
+
action: async () => {
|
|
8553
|
+
const result = await action.run(inputs, {
|
|
8554
|
+
actorDid: context.actor.did,
|
|
8555
|
+
flowId: context.flowId,
|
|
8556
|
+
flowUri: context.flowUri,
|
|
8557
|
+
nodeId: command.nodeId,
|
|
8558
|
+
flowNode,
|
|
8559
|
+
runtime,
|
|
8560
|
+
services: options.actionServices || {}
|
|
8561
|
+
});
|
|
8562
|
+
return { payload: result.output };
|
|
8563
|
+
},
|
|
8564
|
+
pin: options.pin
|
|
8565
|
+
});
|
|
8566
|
+
if (outcome.success && outcome.result) {
|
|
8567
|
+
runtime.update(command.nodeId, {
|
|
8568
|
+
state: "completed",
|
|
8569
|
+
output: outcome.result.payload || {},
|
|
8570
|
+
executedByDid: context.actor.did,
|
|
8571
|
+
executedAt: options.now?.() || context.now?.() || Date.now(),
|
|
8572
|
+
lastInvocationCid: outcome.invocationCid || outcome.capabilityId,
|
|
8573
|
+
lastCommandId: command.id
|
|
8574
|
+
});
|
|
8575
|
+
} else if (!outcome.success) {
|
|
8576
|
+
runtime.update(command.nodeId, {
|
|
8577
|
+
state: "failed",
|
|
8578
|
+
error: {
|
|
8579
|
+
message: outcome.error || "Unknown execution error",
|
|
8580
|
+
at: options.now?.() || context.now?.() || Date.now(),
|
|
8581
|
+
commandId: command.id
|
|
8582
|
+
},
|
|
8583
|
+
lastCommandId: command.id
|
|
8584
|
+
});
|
|
8585
|
+
}
|
|
8586
|
+
return {
|
|
8587
|
+
commandId: command.id,
|
|
8588
|
+
success: outcome.success,
|
|
8589
|
+
confirmed: outcome.success,
|
|
8590
|
+
output: {
|
|
8591
|
+
...outcome.result?.payload && typeof outcome.result.payload === "object" ? outcome.result.payload : {},
|
|
8592
|
+
...outcome.invocationCid ? { invocationCid: outcome.invocationCid } : {}
|
|
8593
|
+
},
|
|
8594
|
+
error: outcome.error
|
|
8595
|
+
};
|
|
8596
|
+
}
|
|
8597
|
+
function getRecoverableBlockerReason(runtime) {
|
|
8598
|
+
const error = runtime.error;
|
|
8599
|
+
const message = error && typeof error === "object" && typeof error.message === "string" ? error.message : typeof error === "string" ? error : void 0;
|
|
8600
|
+
if (!message) return null;
|
|
8601
|
+
if (message.includes("Invalid DID") && message.includes("must start with 'did:'")) {
|
|
8602
|
+
return "matrix_did_normalization";
|
|
8603
|
+
}
|
|
8604
|
+
if (message.includes("Failed to parse URL from undefined")) {
|
|
8605
|
+
return "http_endpoint_input_normalization";
|
|
8606
|
+
}
|
|
8607
|
+
if (message.includes("Unexpected content type") || message.includes("already been defined with a different constructor")) {
|
|
8608
|
+
return "audit_trail_shape";
|
|
8609
|
+
}
|
|
8610
|
+
return null;
|
|
8611
|
+
}
|
|
8612
|
+
function resetFailedActionCommands(context, nodeId, now) {
|
|
8613
|
+
const { outbox } = getFlowAgentMaps(context.yDoc);
|
|
8614
|
+
const resetIds = [];
|
|
8615
|
+
outbox.forEach((command) => {
|
|
8616
|
+
if (command.nodeId !== nodeId || command.type !== "execute_action" || command.status !== "failed") {
|
|
8617
|
+
return;
|
|
8618
|
+
}
|
|
8619
|
+
outbox.set(command.id, {
|
|
8620
|
+
...command,
|
|
8621
|
+
status: "queued",
|
|
8622
|
+
error: void 0,
|
|
8623
|
+
lease: void 0,
|
|
8624
|
+
updatedAt: now,
|
|
8625
|
+
reason: `Retry after blocker diagnosis: ${command.reason}`
|
|
8626
|
+
});
|
|
8627
|
+
resetIds.push(command.id);
|
|
8628
|
+
});
|
|
8629
|
+
return resetIds;
|
|
8630
|
+
}
|
|
8631
|
+
async function executeDiagnoseBlockerCommand(command, context, options) {
|
|
8632
|
+
const now = options.now?.() || context.now?.() || Date.now();
|
|
8633
|
+
const runtime = getRuntimeRecord(context.yDoc, command.nodeId);
|
|
8634
|
+
const recovery = getRecoverableBlockerReason(runtime);
|
|
8635
|
+
if (!recovery) {
|
|
8636
|
+
return {
|
|
8637
|
+
commandId: command.id,
|
|
8638
|
+
success: true,
|
|
8639
|
+
confirmed: true,
|
|
8640
|
+
output: {
|
|
8641
|
+
blockerCause: command.payload.cause || "unknown",
|
|
8642
|
+
recovery: "manual_intervention_required"
|
|
8643
|
+
}
|
|
8644
|
+
};
|
|
8645
|
+
}
|
|
8646
|
+
clearRuntimeBlocker(context.yDoc, command.nodeId, {
|
|
8647
|
+
diagnosedAt: now,
|
|
8648
|
+
diagnosedByDid: context.actor.did,
|
|
8649
|
+
lastDiagnosisCommandId: command.id,
|
|
8650
|
+
lastRecovery: recovery
|
|
8651
|
+
});
|
|
8652
|
+
const resetCommandIds = resetFailedActionCommands(context, command.nodeId, now);
|
|
8653
|
+
return {
|
|
8654
|
+
commandId: command.id,
|
|
8655
|
+
success: true,
|
|
8656
|
+
confirmed: true,
|
|
8657
|
+
output: {
|
|
8658
|
+
blockerCause: command.payload.cause || "unknown",
|
|
8659
|
+
recovery,
|
|
8660
|
+
resetCommandIds
|
|
8661
|
+
}
|
|
8662
|
+
};
|
|
8663
|
+
}
|
|
8664
|
+
async function executeFlowAgentCoreCommand(command, context, options) {
|
|
8665
|
+
switch (command.type) {
|
|
8666
|
+
case "assign_actor":
|
|
8667
|
+
return executeAssignActorCommand(command, context, options);
|
|
8668
|
+
case "execute_action":
|
|
8669
|
+
return executeActionCommand(command, context, options);
|
|
8670
|
+
case "diagnose_blocker":
|
|
8671
|
+
return executeDiagnoseBlockerCommand(command, context, options);
|
|
8672
|
+
default:
|
|
8673
|
+
return { commandId: command.id, success: false, error: `No editor-core executor for ${command.type}` };
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
async function callConfiguredExecutor(command, context, options) {
|
|
8677
|
+
if (!options.executor) return void 0;
|
|
8678
|
+
switch (command.type) {
|
|
8679
|
+
case "assign_actor":
|
|
8680
|
+
return options.executor.assignActor?.(command, context);
|
|
8681
|
+
case "execute_action":
|
|
8682
|
+
return options.executor.executeAction?.(command, context);
|
|
8683
|
+
case "diagnose_blocker":
|
|
8684
|
+
return options.executor.diagnoseBlocker?.(command, context);
|
|
8685
|
+
default:
|
|
8686
|
+
return void 0;
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8689
|
+
async function executeQueuedFlowAgentCoreCommands(context, options) {
|
|
8690
|
+
const now = options.now?.() || context.now?.() || Date.now();
|
|
8691
|
+
const { leases } = getFlowAgentMaps(context.yDoc);
|
|
8692
|
+
const results = [];
|
|
8693
|
+
for (const command of readQueuedAgentCommands(context.yDoc)) {
|
|
8694
|
+
if (options.commandTypes && !options.commandTypes.includes(command.type)) {
|
|
8695
|
+
continue;
|
|
8696
|
+
}
|
|
8697
|
+
const validation = validateAgentCommand(command);
|
|
8698
|
+
if (!validation.valid) {
|
|
8699
|
+
updateAgentCommand(context.yDoc, command.id, { status: "failed", error: validation.error, updatedAt: now });
|
|
8700
|
+
results.push({ commandId: command.id, success: false, error: validation.error });
|
|
8701
|
+
continue;
|
|
8702
|
+
}
|
|
8703
|
+
const policy = evaluateFlowAgentPolicy({
|
|
8704
|
+
actorDid: context.actor.did,
|
|
8705
|
+
commandType: command.type,
|
|
8706
|
+
flowUri: context.flowUri,
|
|
8707
|
+
nodeId: command.nodeId,
|
|
8708
|
+
delegationStore: options.delegationStore,
|
|
8709
|
+
delegations: options.delegations,
|
|
8710
|
+
now
|
|
8711
|
+
});
|
|
8712
|
+
if (!policy.allowed) {
|
|
8713
|
+
updateAgentCommand(context.yDoc, command.id, { status: "skipped", error: policy.reason, updatedAt: now });
|
|
8714
|
+
results.push({ commandId: command.id, success: false, error: policy.reason });
|
|
8715
|
+
continue;
|
|
8716
|
+
}
|
|
8717
|
+
const lease = acquireFlowAgentLease({
|
|
8718
|
+
leases,
|
|
8719
|
+
commandId: command.id,
|
|
8720
|
+
nodeId: command.nodeId,
|
|
8721
|
+
actorDid: context.actor.did,
|
|
8722
|
+
now,
|
|
8723
|
+
ttlMs: options.leaseTtlMs
|
|
8724
|
+
});
|
|
8725
|
+
if (!lease) continue;
|
|
8726
|
+
updateAgentCommand(context.yDoc, command.id, { status: "running", lease, updatedAt: now });
|
|
8727
|
+
try {
|
|
8728
|
+
const runningCommand = { ...command, lease, status: "running" };
|
|
8729
|
+
const result = await Promise.resolve(callConfiguredExecutor(runningCommand, context, options)) || await executeFlowAgentCoreCommand(runningCommand, context, options);
|
|
8730
|
+
const normalized = result || {
|
|
8731
|
+
commandId: command.id,
|
|
8732
|
+
success: false,
|
|
8733
|
+
error: `No executor configured for ${command.type}`
|
|
8734
|
+
};
|
|
8735
|
+
if (!validateFlowAgentLease(leases, lease, options.now?.() || context.now?.() || Date.now())) {
|
|
8736
|
+
const error = "Lease expired or was superseded before command commit";
|
|
8737
|
+
updateAgentCommand(context.yDoc, command.id, { status: "failed", error, updatedAt: options.now?.() || context.now?.() || Date.now() });
|
|
8738
|
+
results.push({ commandId: command.id, success: false, error });
|
|
8739
|
+
continue;
|
|
8740
|
+
}
|
|
8741
|
+
updateAgentCommand(context.yDoc, command.id, {
|
|
8742
|
+
status: normalized.success ? "confirmed" : "failed",
|
|
8743
|
+
error: normalized.error,
|
|
8744
|
+
updatedAt: options.now?.() || context.now?.() || Date.now()
|
|
8745
|
+
});
|
|
8746
|
+
safeAppendAgentLedgerEvent2(context.yDoc, {
|
|
8747
|
+
type: normalized.success ? "agent.validation" : "agent.escalation",
|
|
8748
|
+
flowId: context.flowId,
|
|
8749
|
+
nodeId: command.nodeId,
|
|
8750
|
+
commandId: command.id,
|
|
8751
|
+
actorDid: context.actor.did,
|
|
8752
|
+
timestamp: options.now?.() || context.now?.() || Date.now(),
|
|
8753
|
+
details: {
|
|
8754
|
+
success: normalized.success,
|
|
8755
|
+
confirmed: normalized.confirmed,
|
|
8756
|
+
output: normalized.output,
|
|
8757
|
+
error: normalized.error
|
|
8758
|
+
}
|
|
8759
|
+
});
|
|
8760
|
+
results.push(normalized);
|
|
8761
|
+
} finally {
|
|
8762
|
+
releaseFlowAgentLease(leases, lease);
|
|
8763
|
+
}
|
|
8764
|
+
}
|
|
8765
|
+
return results;
|
|
8766
|
+
}
|
|
8767
|
+
|
|
8768
|
+
// src/core/lib/flowAgent/service.ts
|
|
8769
|
+
var FlowAgentService = class {
|
|
8770
|
+
constructor(context, options = {}) {
|
|
8771
|
+
this.context = context;
|
|
8772
|
+
this.options = options;
|
|
8773
|
+
this.timer = null;
|
|
8774
|
+
this.running = false;
|
|
8775
|
+
}
|
|
8776
|
+
async tick() {
|
|
8777
|
+
if (this.running) {
|
|
8778
|
+
return {
|
|
8779
|
+
flowDone: false,
|
|
8780
|
+
snapshots: [],
|
|
8781
|
+
queuedCommands: [],
|
|
8782
|
+
executedCommands: []
|
|
8783
|
+
};
|
|
8784
|
+
}
|
|
8785
|
+
this.running = true;
|
|
8786
|
+
try {
|
|
8787
|
+
const result = await tickFlowAgent(this.context, this.options);
|
|
8788
|
+
await this.options.onTick?.(result);
|
|
8789
|
+
return result;
|
|
8790
|
+
} catch (error) {
|
|
8791
|
+
this.options.onError?.(error);
|
|
8792
|
+
throw error;
|
|
8793
|
+
} finally {
|
|
8794
|
+
this.running = false;
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
start() {
|
|
8798
|
+
if (this.timer) return;
|
|
8799
|
+
const intervalMs = this.options.intervalMs || 3e4;
|
|
8800
|
+
this.timer = setInterval(() => {
|
|
8801
|
+
this.tick().catch((error) => {
|
|
8802
|
+
this.options.onError?.(error);
|
|
8803
|
+
});
|
|
8804
|
+
}, intervalMs);
|
|
8805
|
+
}
|
|
8806
|
+
stop() {
|
|
8807
|
+
if (!this.timer) return;
|
|
8808
|
+
clearInterval(this.timer);
|
|
8809
|
+
this.timer = null;
|
|
8810
|
+
}
|
|
8811
|
+
};
|
|
8812
|
+
|
|
7574
8813
|
export {
|
|
7575
8814
|
resolveActionType,
|
|
7576
8815
|
registerAction,
|
|
@@ -7644,6 +8883,35 @@ export {
|
|
|
7644
8883
|
setActiveEditor,
|
|
7645
8884
|
getActiveEditor,
|
|
7646
8885
|
readFlow,
|
|
7647
|
-
setupFlowFromBaseUcan
|
|
8886
|
+
setupFlowFromBaseUcan,
|
|
8887
|
+
getFlowAgentMaps,
|
|
8888
|
+
computeAgentCommandId,
|
|
8889
|
+
queueAgentCommand,
|
|
8890
|
+
readQueuedAgentCommands,
|
|
8891
|
+
updateAgentCommand,
|
|
8892
|
+
appendAgentLedgerEvent,
|
|
8893
|
+
readAgentLedgerEvents,
|
|
8894
|
+
requiredCapabilityForCommand,
|
|
8895
|
+
isCapabilityMatch,
|
|
8896
|
+
canMatches,
|
|
8897
|
+
resourceMatches,
|
|
8898
|
+
evaluateFlowAgentPolicy,
|
|
8899
|
+
createAgentCommand,
|
|
8900
|
+
validateAgentCommand,
|
|
8901
|
+
isExternalMutation,
|
|
8902
|
+
buildFlowAgentContext,
|
|
8903
|
+
acquireFlowAgentLease,
|
|
8904
|
+
validateFlowAgentLease,
|
|
8905
|
+
releaseFlowAgentLease,
|
|
8906
|
+
cleanupExpiredFlowAgentLeases,
|
|
8907
|
+
classifyBlockerCause,
|
|
8908
|
+
classifyNodeState,
|
|
8909
|
+
snapshotNode,
|
|
8910
|
+
planRalphLoopCommands,
|
|
8911
|
+
executeQueuedAgentCommands,
|
|
8912
|
+
tickFlowAgent,
|
|
8913
|
+
executeFlowAgentCoreCommand,
|
|
8914
|
+
executeQueuedFlowAgentCoreCommands,
|
|
8915
|
+
FlowAgentService
|
|
7648
8916
|
};
|
|
7649
|
-
//# sourceMappingURL=chunk-
|
|
8917
|
+
//# sourceMappingURL=chunk-JAB2IBVH.mjs.map
|