@everfir/az8-cli 0.3.0-preview.1 → 0.3.0-preview.3
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/CHANGELOG.md +47 -0
- package/README.md +27 -9
- package/RELEASE.md +1 -1
- package/dist/main.js +991 -153
- package/package.json +1 -1
- package/skills/az8-cli/SKILL.md +75 -12
package/dist/main.js
CHANGED
|
@@ -14175,11 +14175,47 @@ var CANVAS_OPERATION_REGISTRY = [
|
|
|
14175
14175
|
},
|
|
14176
14176
|
required: ["referenceViewNodeId", "viewNodeId"]
|
|
14177
14177
|
}),
|
|
14178
|
+
write2("insertPromptReference", "Bind one visible image, text, or video View Node and place its platform At token in the Prompt", "silent", {
|
|
14179
|
+
properties: {
|
|
14180
|
+
placement: {
|
|
14181
|
+
anyOf: [
|
|
14182
|
+
{ enum: ["start", "end"], type: "string" },
|
|
14183
|
+
{
|
|
14184
|
+
additionalProperties: false,
|
|
14185
|
+
properties: {
|
|
14186
|
+
anchor: {
|
|
14187
|
+
additionalProperties: false,
|
|
14188
|
+
properties: {
|
|
14189
|
+
occurrence: { minimum: 1, type: "integer" },
|
|
14190
|
+
text: stringSchema()
|
|
14191
|
+
},
|
|
14192
|
+
required: ["text"],
|
|
14193
|
+
type: "object"
|
|
14194
|
+
},
|
|
14195
|
+
kind: { enum: ["before", "after"], type: "string" }
|
|
14196
|
+
},
|
|
14197
|
+
required: ["anchor", "kind"],
|
|
14198
|
+
type: "object"
|
|
14199
|
+
}
|
|
14200
|
+
]
|
|
14201
|
+
},
|
|
14202
|
+
referenceViewNodeId: stringSchema(),
|
|
14203
|
+
viewNodeId: stringSchema()
|
|
14204
|
+
},
|
|
14205
|
+
required: ["referenceViewNodeId", "viewNodeId"]
|
|
14206
|
+
}),
|
|
14178
14207
|
write2("removeVisualReference", "Remove one Canvas View Node reference from a generation Draft", "silent", {
|
|
14179
14208
|
properties: { referenceId: stringSchema(), viewNodeId: stringSchema() },
|
|
14180
14209
|
required: ["referenceId", "viewNodeId"]
|
|
14181
14210
|
}),
|
|
14182
14211
|
write2("startGeneration", "Estimate current Draft inputs, start a Workflow Run, and acknowledge immediate Canvas writes", "barrier", { properties: { viewNodeId: stringSchema() }, required: ["viewNodeId"] }, "workflow-accepted"),
|
|
14212
|
+
write2("reconcileGenerationStart", "Reconcile one known Workflow Run with its target without starting another Run", "barrier", {
|
|
14213
|
+
properties: {
|
|
14214
|
+
viewNodeId: stringSchema(),
|
|
14215
|
+
workflowRunId: stringSchema()
|
|
14216
|
+
},
|
|
14217
|
+
required: ["viewNodeId", "workflowRunId"]
|
|
14218
|
+
}),
|
|
14183
14219
|
read("getWorkflowRun", "Read one Workflow Run", {
|
|
14184
14220
|
properties: { workflowRunId: stringSchema() },
|
|
14185
14221
|
required: ["workflowRunId"]
|
|
@@ -14246,10 +14282,25 @@ function sizeSchema() {
|
|
|
14246
14282
|
}
|
|
14247
14283
|
|
|
14248
14284
|
// ../../packages/project-canvas-runtime/dist/operation-usage.js
|
|
14285
|
+
function attachCanvasOperationIndeterminateResult(value, result3) {
|
|
14286
|
+
const cause = value instanceof Error ? value : new Error("Unexpected Canvas Operation failure.");
|
|
14287
|
+
const error51 = new Error(cause.message, { cause });
|
|
14288
|
+
return Object.assign(error51, {
|
|
14289
|
+
indeterminate: true,
|
|
14290
|
+
operationResult: structuredClone(result3),
|
|
14291
|
+
..."timedOut" in cause && cause.timedOut === true ? { timedOut: true } : {}
|
|
14292
|
+
});
|
|
14293
|
+
}
|
|
14249
14294
|
function attachCanvasOperationUsage(value, usage) {
|
|
14250
14295
|
const error51 = value instanceof Error ? value : new Error("Unexpected Canvas Operation failure.");
|
|
14251
14296
|
return Object.assign(error51, { operationUsage: usage });
|
|
14252
14297
|
}
|
|
14298
|
+
function readCanvasOperationIndeterminateResult(value) {
|
|
14299
|
+
if (typeof value !== "object" || value === null || !("operationResult" in value))
|
|
14300
|
+
return void 0;
|
|
14301
|
+
const result3 = value.operationResult;
|
|
14302
|
+
return typeof result3 === "object" && result3 !== null && !Array.isArray(result3) ? result3 : void 0;
|
|
14303
|
+
}
|
|
14253
14304
|
function readCanvasOperationUsage(value) {
|
|
14254
14305
|
if (typeof value !== "object" || value === null || !("operationUsage" in value))
|
|
14255
14306
|
return void 0;
|
|
@@ -15022,6 +15073,37 @@ function addVisualReferenceToDraft(draft, definition, reference, sourceDataType,
|
|
|
15022
15073
|
}
|
|
15023
15074
|
return appendPromptReferenceToken(nextDraft, slot.name, reference.id, reference.viewNodeId, now);
|
|
15024
15075
|
}
|
|
15076
|
+
function insertPromptReferenceIntoDraft(draft, definition, reference, sourceDataType, placement, now) {
|
|
15077
|
+
const withReference = addVisualReferenceToDraft(draft, definition, reference, sourceDataType, now);
|
|
15078
|
+
const binding = Object.entries(withReference.inputs.slots).flatMap(([slotName, values]) => values.flatMap((value, index) => value.kind === "view-node" && value.id === reference.id ? [{ index, slotName, value }] : []))[0];
|
|
15079
|
+
if (!binding)
|
|
15080
|
+
throw new Error(`Generation Reference ${reference.id} does not exist in the Draft.`);
|
|
15081
|
+
if (binding.value.sourceDataType !== "image" && binding.value.sourceDataType !== "text" && binding.value.sourceDataType !== "video") {
|
|
15082
|
+
throw new Error("Prompt References must resolve to image, text, or video content.");
|
|
15083
|
+
}
|
|
15084
|
+
const promptSlot = requirePromptSlot(definition);
|
|
15085
|
+
const withoutExistingToken = removePromptReferenceToken(withReference, reference.id);
|
|
15086
|
+
const promptValues = withoutExistingToken.inputs.slots[promptSlot.name] ?? [];
|
|
15087
|
+
const existingLiteral = promptValues.find((value) => value.kind === "literal" && value.assetType === "text");
|
|
15088
|
+
const promptInput = existingLiteral ?? literal(withoutExistingToken, promptSlot.name, "text", "");
|
|
15089
|
+
const prompt = promptInput.content;
|
|
15090
|
+
const token = `{{@${binding.value.sourceDataType}:${binding.slotName}:${binding.index}:${reference.id}|view-node|${encodeURIComponent(reference.viewNodeId)}}}`;
|
|
15091
|
+
const content = placement === "start" ? prompt.trim() ? `${token}
|
|
15092
|
+
${prompt}` : token : placement === "end" ? prompt.trim() ? `${prompt}${prompt.endsWith("\n") ? "" : "\n"}${token}` : token : insertAtPromptAnchor(prompt, token, placement);
|
|
15093
|
+
return {
|
|
15094
|
+
...structuredClone(withoutExistingToken),
|
|
15095
|
+
inputs: {
|
|
15096
|
+
slots: {
|
|
15097
|
+
...structuredClone(withoutExistingToken.inputs.slots),
|
|
15098
|
+
[promptSlot.name]: [
|
|
15099
|
+
{ ...promptInput, content },
|
|
15100
|
+
...promptValues.filter((value) => value.id !== promptInput.id)
|
|
15101
|
+
]
|
|
15102
|
+
}
|
|
15103
|
+
},
|
|
15104
|
+
updatedAt: now
|
|
15105
|
+
};
|
|
15106
|
+
}
|
|
15025
15107
|
function removeVisualReferenceFromDraft(draft, referenceId, now) {
|
|
15026
15108
|
let removed = false;
|
|
15027
15109
|
let removedTextReference = false;
|
|
@@ -15068,21 +15150,57 @@ function appendPromptReferenceToken(draft, slotName, referenceId, viewNodeId, no
|
|
|
15068
15150
|
};
|
|
15069
15151
|
}
|
|
15070
15152
|
function removePromptReferenceToken(draft, referenceId) {
|
|
15071
|
-
const pattern = new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN, "g");
|
|
15072
15153
|
const slots = Object.fromEntries(Object.entries(draft.inputs.slots).map(([slotName, values]) => [
|
|
15073
15154
|
slotName,
|
|
15074
15155
|
values.map((value) => {
|
|
15075
15156
|
if (value.kind !== "literal" || value.assetType !== "text")
|
|
15076
15157
|
return value;
|
|
15077
|
-
|
|
15078
|
-
const content = line.replace(pattern, (token, _type, _slot, _index, bindingId) => bindingId === referenceId ? "" : token);
|
|
15079
|
-
return content.trim() ? [content] : [];
|
|
15080
|
-
});
|
|
15081
|
-
return { ...value, content: lines.join("\n") };
|
|
15158
|
+
return { ...value, content: removeReferenceTokenFromContent(value.content, referenceId) };
|
|
15082
15159
|
})
|
|
15083
15160
|
]));
|
|
15084
15161
|
return { ...draft, inputs: { slots } };
|
|
15085
15162
|
}
|
|
15163
|
+
function insertAtPromptAnchor(prompt, token, placement) {
|
|
15164
|
+
const anchor = placement.anchor.text;
|
|
15165
|
+
if (!anchor.trim())
|
|
15166
|
+
throw new Error("placement.anchor.text must be non-empty.");
|
|
15167
|
+
const indexes = [];
|
|
15168
|
+
let from2 = 0;
|
|
15169
|
+
while (from2 <= prompt.length - anchor.length) {
|
|
15170
|
+
const index2 = prompt.indexOf(anchor, from2);
|
|
15171
|
+
if (index2 < 0)
|
|
15172
|
+
break;
|
|
15173
|
+
indexes.push(index2);
|
|
15174
|
+
from2 = index2 + Math.max(anchor.length, 1);
|
|
15175
|
+
}
|
|
15176
|
+
if (indexes.length === 0) {
|
|
15177
|
+
throw new Error(`Prompt anchor ${JSON.stringify(anchor)} does not exist.`);
|
|
15178
|
+
}
|
|
15179
|
+
const occurrence = placement.anchor.occurrence;
|
|
15180
|
+
if (occurrence === void 0 && indexes.length !== 1) {
|
|
15181
|
+
throw new Error(`Prompt anchor ${JSON.stringify(anchor)} is ambiguous; placement.anchor.occurrence is required.`);
|
|
15182
|
+
}
|
|
15183
|
+
const index = indexes[(occurrence ?? 1) - 1];
|
|
15184
|
+
if (index === void 0) {
|
|
15185
|
+
throw new Error(`Prompt anchor ${JSON.stringify(anchor)} does not have occurrence ${String(occurrence)}.`);
|
|
15186
|
+
}
|
|
15187
|
+
const insertionIndex = placement.kind === "before" ? index : index + anchor.length;
|
|
15188
|
+
return `${prompt.slice(0, insertionIndex)}${token}${prompt.slice(insertionIndex)}`;
|
|
15189
|
+
}
|
|
15190
|
+
function removeReferenceTokenFromContent(content, referenceId) {
|
|
15191
|
+
const matches2 = [...content.matchAll(new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN, "g"))].filter((match) => match[4] === referenceId);
|
|
15192
|
+
let next = content;
|
|
15193
|
+
for (const match of matches2.reverse()) {
|
|
15194
|
+
let start = match.index;
|
|
15195
|
+
let end = start + match[0].length;
|
|
15196
|
+
if (start === 0 && next[end] === "\n")
|
|
15197
|
+
end += 1;
|
|
15198
|
+
else if (end === next.length && next[start - 1] === "\n")
|
|
15199
|
+
start -= 1;
|
|
15200
|
+
next = `${next.slice(0, start)}${next.slice(end)}`;
|
|
15201
|
+
}
|
|
15202
|
+
return next;
|
|
15203
|
+
}
|
|
15086
15204
|
function createDefaultSlots(definition) {
|
|
15087
15205
|
return Object.fromEntries(definition.inputSchema.map((slot) => {
|
|
15088
15206
|
if (slot.defaultValue === void 0)
|
|
@@ -15322,7 +15440,8 @@ function createGenerationExecutionOperations(input) {
|
|
|
15322
15440
|
if (definitionChanges || Object.keys(editInput).length > 2) {
|
|
15323
15441
|
operations.push({ input: editInput, name: "editGenerationDraft" });
|
|
15324
15442
|
}
|
|
15325
|
-
const
|
|
15443
|
+
const promptSlotName = resolvePromptSlot(input.definition)?.name;
|
|
15444
|
+
const currentReferences = (definitionChanges ? [] : readGenerationDraftReferences(input.currentDraft)).filter((reference) => editInput.prompt === void 0 || reference.sourceSlotName !== promptSlotName);
|
|
15326
15445
|
const desiredKeys = new Set(input.assignments.map((item) => referenceKey(item.reference)));
|
|
15327
15446
|
for (const current of currentReferences) {
|
|
15328
15447
|
if (!current.draftInputId || desiredKeys.has(referenceKey(current)))
|
|
@@ -15823,7 +15942,14 @@ async function resolveEffectiveTextInputContent(input) {
|
|
|
15823
15942
|
if (!literal3)
|
|
15824
15943
|
return null;
|
|
15825
15944
|
const fragmentsByBindingId = /* @__PURE__ */ new Map();
|
|
15826
|
-
|
|
15945
|
+
const bindingsById = /* @__PURE__ */ new Map();
|
|
15946
|
+
for (const [slotName, references] of Object.entries(input.allReferencesBySlot)) {
|
|
15947
|
+
for (const reference of references) {
|
|
15948
|
+
if (reference.kind !== "literal")
|
|
15949
|
+
bindingsById.set(reference.id, { reference, slotName });
|
|
15950
|
+
}
|
|
15951
|
+
}
|
|
15952
|
+
for (const reference of Object.values(input.allReferencesBySlot).flat()) {
|
|
15827
15953
|
if (reference.kind !== "view-node" || reference.sourceDataType !== "text")
|
|
15828
15954
|
continue;
|
|
15829
15955
|
const content = await input.resolveViewNodeTextContent(reference.viewNodeId);
|
|
@@ -15831,8 +15957,17 @@ async function resolveEffectiveTextInputContent(input) {
|
|
|
15831
15957
|
fragmentsByBindingId.set(reference.id, content.trim());
|
|
15832
15958
|
}
|
|
15833
15959
|
const positionedBindingIds = /* @__PURE__ */ new Set();
|
|
15834
|
-
const positionedContent = literal3.content.replace(new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN2, "g"), (token,
|
|
15960
|
+
const positionedContent = literal3.content.replace(new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN2, "g"), (token, contentType, slotName, _rawIndex, bindingId, encodedViewNodeId) => {
|
|
15835
15961
|
if (!bindingId)
|
|
15962
|
+
throw new Error("Prompt contains an At token without a binding identity.");
|
|
15963
|
+
const binding = bindingsById.get(bindingId);
|
|
15964
|
+
if (!binding || binding.slotName !== slotName || binding.reference.sourceDataType !== contentType) {
|
|
15965
|
+
throw new Error(`Prompt At token ${bindingId} does not match its Generation binding.`);
|
|
15966
|
+
}
|
|
15967
|
+
if (encodedViewNodeId && (binding.reference.kind !== "view-node" || binding.reference.viewNodeId !== decodePromptViewNodeId(encodedViewNodeId))) {
|
|
15968
|
+
throw new Error(`Prompt At token ${bindingId} does not match its View Node.`);
|
|
15969
|
+
}
|
|
15970
|
+
if (contentType !== "text")
|
|
15836
15971
|
return token;
|
|
15837
15972
|
const content = fragmentsByBindingId.get(bindingId);
|
|
15838
15973
|
if (content === void 0)
|
|
@@ -15841,11 +15976,21 @@ async function resolveEffectiveTextInputContent(input) {
|
|
|
15841
15976
|
return content;
|
|
15842
15977
|
});
|
|
15843
15978
|
const trailingFragments = [...fragmentsByBindingId.entries()].flatMap(([bindingId, content]) => positionedBindingIds.has(bindingId) ? [] : [content]);
|
|
15844
|
-
|
|
15979
|
+
const unresolvedTextToken = [
|
|
15980
|
+
...positionedContent.matchAll(new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN2, "g"))
|
|
15981
|
+
].some((match) => match[1] === "text");
|
|
15982
|
+
if (unresolvedTextToken) {
|
|
15845
15983
|
throw new Error("Prompt contains an unresolved Text View Node reference.");
|
|
15846
15984
|
}
|
|
15847
15985
|
return appendUniqueTextFragments(positionedContent, trailingFragments);
|
|
15848
15986
|
}
|
|
15987
|
+
function decodePromptViewNodeId(value) {
|
|
15988
|
+
try {
|
|
15989
|
+
return decodeURIComponent(value);
|
|
15990
|
+
} catch {
|
|
15991
|
+
throw new Error("Prompt At token contains an invalid View Node identity.");
|
|
15992
|
+
}
|
|
15993
|
+
}
|
|
15849
15994
|
function appendUniqueTextFragments(base, fragments) {
|
|
15850
15995
|
let result3 = base;
|
|
15851
15996
|
for (const fragment of fragments) {
|
|
@@ -15865,6 +16010,7 @@ async function prepareGenerationInput(input) {
|
|
|
15865
16010
|
for (const slot of input.definition.inputSchema) {
|
|
15866
16011
|
const references = input.draft.inputs.slots[slot.name] ?? [];
|
|
15867
16012
|
const effectiveText = slot.name === promptSlot?.name ? await resolveEffectiveTextInputContent({
|
|
16013
|
+
allReferencesBySlot: input.draft.inputs.slots,
|
|
15868
16014
|
references,
|
|
15869
16015
|
resolveViewNodeTextContent: input.resolveViewNodeTextContent
|
|
15870
16016
|
}) : null;
|
|
@@ -15981,10 +16127,15 @@ var CoreNodeCreativeLoop = class {
|
|
|
15981
16127
|
return this.editDraft(input, timeoutMs);
|
|
15982
16128
|
if (name === "addVisualReference")
|
|
15983
16129
|
return this.addVisualReference(input, timeoutMs);
|
|
16130
|
+
if (name === "insertPromptReference")
|
|
16131
|
+
return this.insertPromptReference(input, timeoutMs);
|
|
15984
16132
|
if (name === "removeVisualReference")
|
|
15985
16133
|
return this.removeVisualReference(input, timeoutMs);
|
|
15986
16134
|
if (name === "startGeneration")
|
|
15987
16135
|
return this.startGeneration(input, timeoutMs);
|
|
16136
|
+
if (name === "reconcileGenerationStart") {
|
|
16137
|
+
return this.reconcileGenerationStart(input, timeoutMs);
|
|
16138
|
+
}
|
|
15988
16139
|
if (name === "getWorkflowRun")
|
|
15989
16140
|
return this.getWorkflowRun(input);
|
|
15990
16141
|
if (name === "waitWorkflowRun")
|
|
@@ -16011,6 +16162,18 @@ var CoreNodeCreativeLoop = class {
|
|
|
16011
16162
|
for (const id2 of ids)
|
|
16012
16163
|
await this.recoverWorkflowRun(id2, timeoutMs);
|
|
16013
16164
|
}
|
|
16165
|
+
observePendingWorkflowRuns(ids, timeoutMs) {
|
|
16166
|
+
for (const id2 of new Set(ids)) {
|
|
16167
|
+
if (!this.isPending(id2))
|
|
16168
|
+
continue;
|
|
16169
|
+
void this.recoverWorkflowRun(id2, timeoutMs).catch((error51) => {
|
|
16170
|
+
if (isSettlementWriteAttempt(error51))
|
|
16171
|
+
this.context.reportBackgroundError(error51);
|
|
16172
|
+
else
|
|
16173
|
+
this.scheduleWorkflowRunRecovery(id2, timeoutMs, error51);
|
|
16174
|
+
});
|
|
16175
|
+
}
|
|
16176
|
+
}
|
|
16014
16177
|
close() {
|
|
16015
16178
|
this.closed = true;
|
|
16016
16179
|
for (const subscription of this.subscriptions.values())
|
|
@@ -16160,23 +16323,26 @@ var CoreNodeCreativeLoop = class {
|
|
|
16160
16323
|
const draft = requireDraft(target);
|
|
16161
16324
|
const definition = await this.requireDefinition(draft.definitionId);
|
|
16162
16325
|
const referenceViewNodeId = requireString3(input.referenceViewNodeId, "referenceViewNodeId");
|
|
16163
|
-
const
|
|
16164
|
-
if (referenceViewNode.type !== "resource") {
|
|
16165
|
-
throw new Error("Generation References require a resource View Node.");
|
|
16166
|
-
}
|
|
16167
|
-
let sourceDataType = referenceViewNode.contentType ?? "";
|
|
16168
|
-
const referenceCoreNodeId = this.resolveViewNodeCoreNodeId(referenceViewNode);
|
|
16169
|
-
if (sourceDataType !== "text" || asRecord3(referenceViewNode.data).mode === "content") {
|
|
16170
|
-
if (!referenceCoreNodeId)
|
|
16171
|
-
throw new Error("Referenced View Node has no current Core Node.");
|
|
16172
|
-
const coreNode = await this.requireCoreNode(referenceCoreNodeId);
|
|
16173
|
-
sourceDataType = readCoreNodeDataType2(coreNode);
|
|
16174
|
-
}
|
|
16326
|
+
const sourceDataType = await this.resolveReferenceDataType(referenceViewNodeId);
|
|
16175
16327
|
const referenceId = `input_ref_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
16176
16328
|
const nextDraft = addVisualReferenceToDraft(draft, definition, { id: referenceId, kind: "view-node", viewNodeId: referenceViewNodeId }, sourceDataType, this.context.now());
|
|
16177
16329
|
await this.replaceDraft(target, nextDraft, "Add Visual Reference", timeoutMs);
|
|
16178
16330
|
return attention(target.id, { referenceId, viewNodeId: target.id });
|
|
16179
16331
|
}
|
|
16332
|
+
async insertPromptReference(input, timeoutMs) {
|
|
16333
|
+
assertOnlyKeys3(input, ["placement", "referenceViewNodeId", "viewNodeId"]);
|
|
16334
|
+
const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
|
|
16335
|
+
const draft = requireDraft(target);
|
|
16336
|
+
const definition = await this.requireDefinition(draft.definitionId);
|
|
16337
|
+
const referenceViewNodeId = requireString3(input.referenceViewNodeId, "referenceViewNodeId");
|
|
16338
|
+
const sourceDataType = await this.resolveReferenceDataType(referenceViewNodeId);
|
|
16339
|
+
const existing = Object.values(draft.inputs.slots).flat().find((reference) => reference.kind === "view-node" && reference.viewNodeId === referenceViewNodeId);
|
|
16340
|
+
const referenceId = existing?.id ?? `input_ref_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
16341
|
+
const placement = parsePromptReferencePlacement(input.placement);
|
|
16342
|
+
const nextDraft = insertPromptReferenceIntoDraft(draft, definition, { id: referenceId, kind: "view-node", viewNodeId: referenceViewNodeId }, sourceDataType, placement, this.context.now());
|
|
16343
|
+
await this.replaceDraft(target, nextDraft, "Insert Prompt Reference", timeoutMs);
|
|
16344
|
+
return attention(target.id, { placement, referenceId, viewNodeId: target.id });
|
|
16345
|
+
}
|
|
16180
16346
|
async removeVisualReference(input, timeoutMs) {
|
|
16181
16347
|
assertOnlyKeys3(input, ["referenceId", "viewNodeId"]);
|
|
16182
16348
|
const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
|
|
@@ -16187,7 +16353,8 @@ var CoreNodeCreativeLoop = class {
|
|
|
16187
16353
|
}
|
|
16188
16354
|
async startGeneration(input, timeoutMs) {
|
|
16189
16355
|
let estimate;
|
|
16190
|
-
let
|
|
16356
|
+
let acceptedRun;
|
|
16357
|
+
let acceptedTargetId;
|
|
16191
16358
|
try {
|
|
16192
16359
|
assertOnlyKeys3(input, ["viewNodeId"]);
|
|
16193
16360
|
const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
|
|
@@ -16200,6 +16367,10 @@ var CoreNodeCreativeLoop = class {
|
|
|
16200
16367
|
resolveViewNodeCoreNodeId: (id2) => this.resolveViewNodeCoreNodeId(this.requireViewNode(id2)),
|
|
16201
16368
|
resolveViewNodeTextContent: (id2) => this.resolveViewNodeTextContent(this.requireViewNode(id2))
|
|
16202
16369
|
});
|
|
16370
|
+
const targetSnapshot = structuredClone(target);
|
|
16371
|
+
const targetData = normalizeOptionalRecord(target.data, "View Node data");
|
|
16372
|
+
const targetHistory = readGenerationHistory(target.history);
|
|
16373
|
+
const effectivePrompt = readPrompt(prepared.effectiveDraft);
|
|
16203
16374
|
estimate = await this.systems.workflowRuntime.estimateRun({ definitionId: definition.id, inputs: prepared.estimateInputs }, { timeoutMs });
|
|
16204
16375
|
if (!estimate.canAfford)
|
|
16205
16376
|
throw new Error("Insufficient credits to start this generation.");
|
|
@@ -16218,7 +16389,8 @@ var CoreNodeCreativeLoop = class {
|
|
|
16218
16389
|
{ slotName: output.slotName, targetId: target.id, targetSlot: draft.id }
|
|
16219
16390
|
]
|
|
16220
16391
|
}, { timeoutMs });
|
|
16221
|
-
|
|
16392
|
+
acceptedRun = run;
|
|
16393
|
+
acceptedTargetId = target.id;
|
|
16222
16394
|
this.runs.set(run.id, run);
|
|
16223
16395
|
const timestamp = this.context.now();
|
|
16224
16396
|
const historyEntry = {
|
|
@@ -16234,15 +16406,15 @@ var CoreNodeCreativeLoop = class {
|
|
|
16234
16406
|
},
|
|
16235
16407
|
updatedAt: timestamp
|
|
16236
16408
|
};
|
|
16237
|
-
const { creationIntent: _creationIntent, ...data } =
|
|
16409
|
+
const { creationIntent: _creationIntent, ...data } = targetData;
|
|
16238
16410
|
const after = {
|
|
16239
|
-
...
|
|
16411
|
+
...targetSnapshot,
|
|
16240
16412
|
data: {
|
|
16241
16413
|
...data,
|
|
16242
|
-
...
|
|
16414
|
+
...effectivePrompt === null ? {} : { prompt: effectivePrompt }
|
|
16243
16415
|
},
|
|
16244
16416
|
draft: null,
|
|
16245
|
-
history: toDocumentJson([...
|
|
16417
|
+
history: toDocumentJson([...targetHistory, historyEntry]),
|
|
16246
16418
|
updatedAt: timestamp
|
|
16247
16419
|
};
|
|
16248
16420
|
await this.context.commit({
|
|
@@ -16264,8 +16436,15 @@ var CoreNodeCreativeLoop = class {
|
|
|
16264
16436
|
usage: estimatedUsage(estimate)
|
|
16265
16437
|
};
|
|
16266
16438
|
} catch (error51) {
|
|
16267
|
-
const
|
|
16268
|
-
|
|
16439
|
+
const failure = acceptedRun && acceptedTargetId ? attachCanvasOperationIndeterminateResult(error51, {
|
|
16440
|
+
canvasAcknowledged: false,
|
|
16441
|
+
reconciliationRequired: true,
|
|
16442
|
+
status: acceptedRun.status,
|
|
16443
|
+
viewNodeId: acceptedTargetId,
|
|
16444
|
+
workflowRunId: acceptedRun.id
|
|
16445
|
+
}) : error51;
|
|
16446
|
+
const usage = acceptedRun && estimate ? estimatedUsage(estimate) : isIndeterminateWrite(error51) ? unknownUsage(estimate) : notConsumedUsage(estimate);
|
|
16447
|
+
throw attachCanvasOperationUsage(failure, usage);
|
|
16269
16448
|
}
|
|
16270
16449
|
}
|
|
16271
16450
|
async getWorkflowRun(input) {
|
|
@@ -16273,6 +16452,133 @@ var CoreNodeCreativeLoop = class {
|
|
|
16273
16452
|
const run = await this.refreshRun(requireString3(input.workflowRunId, "workflowRunId"), 15e3);
|
|
16274
16453
|
return { result: { workflowRun: run } };
|
|
16275
16454
|
}
|
|
16455
|
+
async reconcileGenerationStart(input, timeoutMs) {
|
|
16456
|
+
assertOnlyKeys3(input, ["viewNodeId", "workflowRunId"]);
|
|
16457
|
+
const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
|
|
16458
|
+
const workflowRunId = requireString3(input.workflowRunId, "workflowRunId");
|
|
16459
|
+
const run = await this.systems.workflowRuntime.getRun(workflowRunId);
|
|
16460
|
+
if (!run)
|
|
16461
|
+
throw new Error(`Workflow Run ${workflowRunId} does not exist.`);
|
|
16462
|
+
this.recordRun(run);
|
|
16463
|
+
const sync = await this.systems.projectContent.syncProject(this.context.projectId, 0, {
|
|
16464
|
+
timeoutMs
|
|
16465
|
+
});
|
|
16466
|
+
this.projection.merge(sync.contents);
|
|
16467
|
+
const history = readGenerationHistory(target.history);
|
|
16468
|
+
const existing = history.find((entry) => entry.runtime.instanceIds.includes(run.id));
|
|
16469
|
+
if (existing) {
|
|
16470
|
+
if (existing.definitionId !== run.definitionId) {
|
|
16471
|
+
return reconciliationResult(target.id, run.id, "conflict", {
|
|
16472
|
+
reason: "The existing History entry and Workflow Run use different Definitions."
|
|
16473
|
+
});
|
|
16474
|
+
}
|
|
16475
|
+
const repairedPendingState = !isTerminalWorkflowRunStatus(run.status) && !this.isPending(run.id);
|
|
16476
|
+
if (isTerminalWorkflowRunStatus(run.status))
|
|
16477
|
+
await this.settleRun(run, timeoutMs);
|
|
16478
|
+
else if (repairedPendingState) {
|
|
16479
|
+
await this.context.commit({
|
|
16480
|
+
addPendingWorkflowInstanceIds: [run.id],
|
|
16481
|
+
after: [],
|
|
16482
|
+
before: [],
|
|
16483
|
+
history: "barrier",
|
|
16484
|
+
label: "Reconcile generation start",
|
|
16485
|
+
timeoutMs
|
|
16486
|
+
});
|
|
16487
|
+
this.watchRun(run.id, timeoutMs);
|
|
16488
|
+
}
|
|
16489
|
+
return reconciliationResult(target.id, run.id, "consistent", {
|
|
16490
|
+
repairedPendingState,
|
|
16491
|
+
workflowStatus: run.status
|
|
16492
|
+
});
|
|
16493
|
+
}
|
|
16494
|
+
const draft = readGenerationDraft(target.draft);
|
|
16495
|
+
if (!draft) {
|
|
16496
|
+
return reconciliationResult(target.id, run.id, "conflict", {
|
|
16497
|
+
reason: "The target has neither a matching History entry nor its original Draft.",
|
|
16498
|
+
workflowStatus: run.status
|
|
16499
|
+
});
|
|
16500
|
+
}
|
|
16501
|
+
if (draft.definitionId !== run.definitionId) {
|
|
16502
|
+
return reconciliationResult(target.id, run.id, "conflict", {
|
|
16503
|
+
reason: "The target Draft and Workflow Run use different Definitions.",
|
|
16504
|
+
workflowStatus: run.status
|
|
16505
|
+
});
|
|
16506
|
+
}
|
|
16507
|
+
if (!isTerminalWorkflowRunStatus(run.status)) {
|
|
16508
|
+
return reconciliationResult(target.id, run.id, "running-insufficient", {
|
|
16509
|
+
reason: "The running Workflow does not yet expose authoritative target output evidence. Wait for a terminal state; do not start another Run.",
|
|
16510
|
+
workflowStatus: run.status
|
|
16511
|
+
});
|
|
16512
|
+
}
|
|
16513
|
+
if (run.status !== "completed") {
|
|
16514
|
+
return reconciliationResult(target.id, run.id, "terminal", {
|
|
16515
|
+
reason: "The Workflow ended without a completed output, so no History entry was invented.",
|
|
16516
|
+
workflowStatus: run.status
|
|
16517
|
+
});
|
|
16518
|
+
}
|
|
16519
|
+
const submittedAt = parseWorkflowTimestamp(run.createdAt);
|
|
16520
|
+
if (submittedAt === null) {
|
|
16521
|
+
return reconciliationResult(target.id, run.id, "insufficient", {
|
|
16522
|
+
reason: "The completed Workflow Run has no trustworthy submission timestamp.",
|
|
16523
|
+
workflowStatus: run.status
|
|
16524
|
+
});
|
|
16525
|
+
}
|
|
16526
|
+
if ((draft.updatedAt ?? draft.createdAt) > submittedAt) {
|
|
16527
|
+
return reconciliationResult(target.id, run.id, "conflict", {
|
|
16528
|
+
reason: "The target Draft changed after the Workflow Run was submitted.",
|
|
16529
|
+
workflowStatus: run.status
|
|
16530
|
+
});
|
|
16531
|
+
}
|
|
16532
|
+
const definition = await this.requireDefinition(run.definitionId);
|
|
16533
|
+
const primaryOutput = requirePrimaryOutput(definition, target.contentType);
|
|
16534
|
+
const targetOutputs = sync.contents.filter((content) => content.sourceId === run.id || content.workflowInstanceId === run.id).flatMap((content) => content.outputs).filter((output) => output.targetId === target.id && output.targetSlot === draft.id && output.slotName === primaryOutput.slotName);
|
|
16535
|
+
if (targetOutputs.length === 0) {
|
|
16536
|
+
return reconciliationResult(target.id, run.id, "insufficient", {
|
|
16537
|
+
reason: "The completed Workflow Run has no Project Content output bound to this target and Draft.",
|
|
16538
|
+
workflowStatus: run.status
|
|
16539
|
+
});
|
|
16540
|
+
}
|
|
16541
|
+
const outputCoreNodeIds = new Set(run.outputs.map((output) => output.coreNodeId));
|
|
16542
|
+
if (outputCoreNodeIds.size > 0 && targetOutputs.every((output) => !outputCoreNodeIds.has(output.coreNodeId))) {
|
|
16543
|
+
return reconciliationResult(target.id, run.id, "conflict", {
|
|
16544
|
+
reason: "Workflow and Project Content outputs disagree.",
|
|
16545
|
+
workflowStatus: run.status
|
|
16546
|
+
});
|
|
16547
|
+
}
|
|
16548
|
+
const timestamp = this.context.now();
|
|
16549
|
+
const historyEntry = {
|
|
16550
|
+
...draft,
|
|
16551
|
+
runtime: {
|
|
16552
|
+
...run.definitionVersion === void 0 ? {} : { expectedDefinitionVersion: run.definitionVersion },
|
|
16553
|
+
instanceIds: [run.id],
|
|
16554
|
+
materializedInputs: inferReconciledMaterializedInputs(draft, run),
|
|
16555
|
+
originClientId: "unknown",
|
|
16556
|
+
submittedAt
|
|
16557
|
+
},
|
|
16558
|
+
updatedAt: timestamp
|
|
16559
|
+
};
|
|
16560
|
+
const { creationIntent: _creationIntent, ...data } = normalizeOptionalRecord(target.data, "View Node data");
|
|
16561
|
+
const prompt = readPrompt(draft);
|
|
16562
|
+
const after = {
|
|
16563
|
+
...structuredClone(target),
|
|
16564
|
+
data: { ...data, ...prompt === null ? {} : { prompt } },
|
|
16565
|
+
draft: null,
|
|
16566
|
+
history: toDocumentJson([...history, historyEntry]),
|
|
16567
|
+
updatedAt: timestamp
|
|
16568
|
+
};
|
|
16569
|
+
await this.context.commit({
|
|
16570
|
+
after: [after],
|
|
16571
|
+
before: [target],
|
|
16572
|
+
history: "barrier",
|
|
16573
|
+
label: "Reconcile generation start",
|
|
16574
|
+
...this.isPending(run.id) ? { removePendingWorkflowInstanceIds: [run.id] } : {},
|
|
16575
|
+
timeoutMs
|
|
16576
|
+
});
|
|
16577
|
+
return reconciliationResult(target.id, run.id, "repaired", {
|
|
16578
|
+
outputCoreNodeIds: [...new Set(targetOutputs.map((output) => output.coreNodeId))],
|
|
16579
|
+
workflowStatus: run.status
|
|
16580
|
+
});
|
|
16581
|
+
}
|
|
16276
16582
|
async waitWorkflowRun(input, timeoutMs) {
|
|
16277
16583
|
assertOnlyKeys3(input, ["workflowRunId"]);
|
|
16278
16584
|
const id2 = requireString3(input.workflowRunId, "workflowRunId");
|
|
@@ -16479,6 +16785,21 @@ var CoreNodeCreativeLoop = class {
|
|
|
16479
16785
|
this.definitions.set(id2, definition);
|
|
16480
16786
|
return definition;
|
|
16481
16787
|
}
|
|
16788
|
+
async resolveReferenceDataType(referenceViewNodeId) {
|
|
16789
|
+
const referenceViewNode = this.requireViewNode(referenceViewNodeId);
|
|
16790
|
+
if (referenceViewNode.type !== "resource") {
|
|
16791
|
+
throw new Error("Generation References require a resource View Node.");
|
|
16792
|
+
}
|
|
16793
|
+
let sourceDataType = referenceViewNode.contentType ?? "";
|
|
16794
|
+
const referenceCoreNodeId = this.resolveViewNodeCoreNodeId(referenceViewNode);
|
|
16795
|
+
if (sourceDataType !== "text" || asRecord3(referenceViewNode.data).mode === "content") {
|
|
16796
|
+
if (!referenceCoreNodeId)
|
|
16797
|
+
throw new Error("Referenced View Node has no current Core Node.");
|
|
16798
|
+
const coreNode = await this.requireCoreNode(referenceCoreNodeId);
|
|
16799
|
+
sourceDataType = readCoreNodeDataType2(coreNode);
|
|
16800
|
+
}
|
|
16801
|
+
return sourceDataType;
|
|
16802
|
+
}
|
|
16482
16803
|
requireTarget(id2) {
|
|
16483
16804
|
const viewNode = this.requireViewNode(id2);
|
|
16484
16805
|
requireSupportedContentType(viewNode.contentType);
|
|
@@ -16524,8 +16845,6 @@ var CoreNodeCreativeLoop = class {
|
|
|
16524
16845
|
data: {
|
|
16525
16846
|
...contentType === "text" ? { mode: "content" } : {},
|
|
16526
16847
|
...data,
|
|
16527
|
-
creator_id: this.context.actor.id,
|
|
16528
|
-
creator_name: this.context.actor.displayName,
|
|
16529
16848
|
...name ? { label: name } : {}
|
|
16530
16849
|
},
|
|
16531
16850
|
draft: null,
|
|
@@ -16563,6 +16882,14 @@ function createEstimatedCompletionTimes(estimate, submittedAt) {
|
|
|
16563
16882
|
function attention(viewNodeId, result3) {
|
|
16564
16883
|
return { attention: { kind: "view-nodes", viewNodeIds: [viewNodeId] }, result: result3 };
|
|
16565
16884
|
}
|
|
16885
|
+
function reconciliationResult(viewNodeId, workflowRunId, reconciliationStatus, detail) {
|
|
16886
|
+
return attention(viewNodeId, {
|
|
16887
|
+
...detail,
|
|
16888
|
+
reconciliationStatus,
|
|
16889
|
+
viewNodeId,
|
|
16890
|
+
workflowRunId
|
|
16891
|
+
});
|
|
16892
|
+
}
|
|
16566
16893
|
function requireDraft(viewNode) {
|
|
16567
16894
|
const draft = readGenerationDraft(viewNode.draft);
|
|
16568
16895
|
if (!draft)
|
|
@@ -16588,6 +16915,38 @@ function readPrompt(draft) {
|
|
|
16588
16915
|
}
|
|
16589
16916
|
return null;
|
|
16590
16917
|
}
|
|
16918
|
+
function inferReconciledMaterializedInputs(draft, run) {
|
|
16919
|
+
const materialized = {};
|
|
16920
|
+
for (const [slotName, bindings] of Object.entries(draft.inputs.slots)) {
|
|
16921
|
+
const references = bindings.filter((binding) => binding.kind !== "literal");
|
|
16922
|
+
if (references.length === 0)
|
|
16923
|
+
continue;
|
|
16924
|
+
const input = run.inputs[slotName];
|
|
16925
|
+
if (typeof input !== "object" || input === null || Array.isArray(input) || !("coreNodeIds" in input) || !Array.isArray(input.coreNodeIds)) {
|
|
16926
|
+
continue;
|
|
16927
|
+
}
|
|
16928
|
+
const coreNodeIds = input.coreNodeIds.filter((coreNodeId) => typeof coreNodeId === "string" && Boolean(coreNodeId));
|
|
16929
|
+
if (references.length === 1) {
|
|
16930
|
+
const reference = references[0];
|
|
16931
|
+
if (reference)
|
|
16932
|
+
materialized[slotName] = [{ coreNodeIds, inputRefId: reference.id }];
|
|
16933
|
+
continue;
|
|
16934
|
+
}
|
|
16935
|
+
if (references.length !== coreNodeIds.length)
|
|
16936
|
+
continue;
|
|
16937
|
+
materialized[slotName] = references.map((reference, index) => ({
|
|
16938
|
+
coreNodeIds: [coreNodeIds[index]],
|
|
16939
|
+
inputRefId: reference.id
|
|
16940
|
+
}));
|
|
16941
|
+
}
|
|
16942
|
+
return materialized;
|
|
16943
|
+
}
|
|
16944
|
+
function parseWorkflowTimestamp(value) {
|
|
16945
|
+
if (!value)
|
|
16946
|
+
return null;
|
|
16947
|
+
const timestamp = Date.parse(value);
|
|
16948
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
16949
|
+
}
|
|
16591
16950
|
function parseConfig2(value) {
|
|
16592
16951
|
const record2 = asRecord3(value);
|
|
16593
16952
|
for (const item of Object.values(record2)) {
|
|
@@ -16616,11 +16975,51 @@ function asRecord3(value) {
|
|
|
16616
16975
|
}
|
|
16617
16976
|
return value;
|
|
16618
16977
|
}
|
|
16978
|
+
function normalizeOptionalRecord(value, field) {
|
|
16979
|
+
if (value === void 0 || value === null)
|
|
16980
|
+
return {};
|
|
16981
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
16982
|
+
throw new Error(`${field} must be an object when present.`);
|
|
16983
|
+
}
|
|
16984
|
+
return structuredClone(value);
|
|
16985
|
+
}
|
|
16619
16986
|
function requireString3(value, field) {
|
|
16620
16987
|
if (typeof value !== "string" || !value.trim())
|
|
16621
16988
|
throw new Error(`${field} must be non-empty.`);
|
|
16622
16989
|
return value.trim();
|
|
16623
16990
|
}
|
|
16991
|
+
function parsePromptReferencePlacement(value) {
|
|
16992
|
+
if (value === void 0 || value === "end")
|
|
16993
|
+
return "end";
|
|
16994
|
+
if (value === "start")
|
|
16995
|
+
return "start";
|
|
16996
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
16997
|
+
throw new Error("placement must be start, end, or an anchor placement object.");
|
|
16998
|
+
}
|
|
16999
|
+
const placement = value;
|
|
17000
|
+
assertOnlyKeys3(placement, ["anchor", "kind"]);
|
|
17001
|
+
if (placement.kind !== "before" && placement.kind !== "after") {
|
|
17002
|
+
throw new Error("placement.kind must be before or after.");
|
|
17003
|
+
}
|
|
17004
|
+
if (typeof placement.anchor !== "object" || placement.anchor === null || Array.isArray(placement.anchor)) {
|
|
17005
|
+
throw new Error("placement.anchor must be an object.");
|
|
17006
|
+
}
|
|
17007
|
+
const anchor = placement.anchor;
|
|
17008
|
+
assertOnlyKeys3(anchor, ["occurrence", "text"]);
|
|
17009
|
+
const text = requirePlainString2(anchor.text, "placement.anchor.text");
|
|
17010
|
+
if (!text.trim())
|
|
17011
|
+
throw new Error("placement.anchor.text must be non-empty.");
|
|
17012
|
+
if (anchor.occurrence !== void 0 && (!Number.isInteger(anchor.occurrence) || anchor.occurrence < 1)) {
|
|
17013
|
+
throw new Error("placement.anchor.occurrence must be a positive integer.");
|
|
17014
|
+
}
|
|
17015
|
+
return {
|
|
17016
|
+
anchor: {
|
|
17017
|
+
...anchor.occurrence === void 0 ? {} : { occurrence: anchor.occurrence },
|
|
17018
|
+
text
|
|
17019
|
+
},
|
|
17020
|
+
kind: placement.kind
|
|
17021
|
+
};
|
|
17022
|
+
}
|
|
16624
17023
|
function requirePlainString2(value, field) {
|
|
16625
17024
|
if (typeof value !== "string")
|
|
16626
17025
|
throw new Error(`${field} must be a string.`);
|
|
@@ -16775,8 +17174,6 @@ var ExternalMediaImporter = class {
|
|
|
16775
17174
|
data: {
|
|
16776
17175
|
contentBindingError: null,
|
|
16777
17176
|
contentBindingStatus: "pending",
|
|
16778
|
-
creator_id: this.context.actor.id,
|
|
16779
|
-
creator_name: this.context.actor.displayName,
|
|
16780
17177
|
[IMPORT_INTENT_DATA_KEY]: structuredClone(intent),
|
|
16781
17178
|
label: intent.name
|
|
16782
17179
|
},
|
|
@@ -17086,9 +17483,12 @@ var MediaUploader = class {
|
|
|
17086
17483
|
this.systems = systems;
|
|
17087
17484
|
this.projection = systems.projectContentProjection;
|
|
17088
17485
|
}
|
|
17089
|
-
async execute(input, timeoutMs) {
|
|
17486
|
+
async execute(input, timeoutMs, onProgress) {
|
|
17090
17487
|
const request = normalizeUploadRequest(input);
|
|
17091
|
-
const
|
|
17488
|
+
const safeProgress = safeProgressListener(onProgress);
|
|
17489
|
+
const prepared = await this.systems.mediaUpload.prepare(request.source, {
|
|
17490
|
+
onProgress: safeProgress
|
|
17491
|
+
});
|
|
17092
17492
|
try {
|
|
17093
17493
|
const descriptor2 = requireValidDescriptor(prepared.descriptor);
|
|
17094
17494
|
const intent = await createIntent(request, descriptor2);
|
|
@@ -17122,9 +17522,10 @@ var MediaUploader = class {
|
|
|
17122
17522
|
let uploaded = false;
|
|
17123
17523
|
let coreNodeId;
|
|
17124
17524
|
try {
|
|
17125
|
-
const object2 = await prepared.upload({ timeoutMs });
|
|
17525
|
+
const object2 = await prepared.upload({ onProgress: safeProgress, timeoutMs });
|
|
17126
17526
|
uploaded = true;
|
|
17127
17527
|
const fileUrl = requireUploadedMediaUrl(object2.url);
|
|
17528
|
+
await safeProgress({ operation: "uploadMedia", stage: "creating-content" });
|
|
17128
17529
|
const completion = await this.systems.projectContent.completeMediaUploads(this.context.projectId, [
|
|
17129
17530
|
{
|
|
17130
17531
|
asset: {
|
|
@@ -17158,6 +17559,7 @@ var MediaUploader = class {
|
|
|
17158
17559
|
await this.updateBindingStatus(viewNode, "failed", errorMessage2(error51), timeoutMs);
|
|
17159
17560
|
throw error51;
|
|
17160
17561
|
}
|
|
17562
|
+
await safeProgress({ operation: "uploadMedia", stage: "updating-canvas" });
|
|
17161
17563
|
await this.updateBindingStatus(viewNode, "ready", null, timeoutMs);
|
|
17162
17564
|
return result2(viewNode.id, coreNodeId, intent, true, uploaded);
|
|
17163
17565
|
} finally {
|
|
@@ -17201,8 +17603,6 @@ var MediaUploader = class {
|
|
|
17201
17603
|
data: {
|
|
17202
17604
|
contentBindingError: null,
|
|
17203
17605
|
contentBindingStatus: "pending",
|
|
17204
|
-
creator_id: this.context.actor.id,
|
|
17205
|
-
creator_name: this.context.actor.displayName,
|
|
17206
17606
|
label: intent.name,
|
|
17207
17607
|
[UPLOAD_INTENT_DATA_KEY]: structuredClone(intent)
|
|
17208
17608
|
},
|
|
@@ -17263,6 +17663,14 @@ var MediaUploader = class {
|
|
|
17263
17663
|
return after;
|
|
17264
17664
|
}
|
|
17265
17665
|
};
|
|
17666
|
+
function safeProgressListener(listener) {
|
|
17667
|
+
return async (progress) => {
|
|
17668
|
+
try {
|
|
17669
|
+
await listener?.(progress);
|
|
17670
|
+
} catch {
|
|
17671
|
+
}
|
|
17672
|
+
};
|
|
17673
|
+
}
|
|
17266
17674
|
var MediaUploadIndeterminateError = class extends Error {
|
|
17267
17675
|
indeterminate = true;
|
|
17268
17676
|
timedOut = false;
|
|
@@ -17991,6 +18399,7 @@ var SemanticCanvasRuntime = class {
|
|
|
17991
18399
|
unsubscribeRemote;
|
|
17992
18400
|
commands;
|
|
17993
18401
|
contentProjection;
|
|
18402
|
+
creationProvenanceSuffix;
|
|
17994
18403
|
creativeLoop;
|
|
17995
18404
|
externalMediaImporter;
|
|
17996
18405
|
mediaUploader;
|
|
@@ -18000,12 +18409,12 @@ var SemanticCanvasRuntime = class {
|
|
|
18000
18409
|
constructor(options) {
|
|
18001
18410
|
this.options = options;
|
|
18002
18411
|
this.now = options.now ?? Date.now;
|
|
18412
|
+
this.creationProvenanceSuffix = normalizeCreationProvenanceSuffix(options.clientName);
|
|
18003
18413
|
this.commands = createProjectCanvasCommandExecutor(options.document, this.now);
|
|
18004
18414
|
this.contentProjection = new ProjectContentProjection(options.projectContents);
|
|
18005
18415
|
this.projectionRevision = options.initialRevision ?? 1;
|
|
18006
18416
|
this.snapshot = options.document.getSnapshot();
|
|
18007
18417
|
const operationContext = {
|
|
18008
|
-
actor: options.actor,
|
|
18009
18418
|
canWrite: options.role !== "viewer",
|
|
18010
18419
|
clientInstanceId: options.clientInstanceId ?? "unknown",
|
|
18011
18420
|
commit: async (input) => {
|
|
@@ -18031,6 +18440,8 @@ var SemanticCanvasRuntime = class {
|
|
|
18031
18440
|
this.unsubscribeRemote = options.document.subscribeRemoteChange(() => {
|
|
18032
18441
|
const previous = this.snapshot;
|
|
18033
18442
|
this.snapshot = options.document.getSnapshot();
|
|
18443
|
+
const previousPending = new Set(previous.pendingWorkflowInstanceIds);
|
|
18444
|
+
const addedPendingWorkflowInstanceIds = this.snapshot.pendingWorkflowInstanceIds.filter((id2) => !previousPending.has(id2));
|
|
18034
18445
|
const viewNodeChanges = diffSemanticViewNodes(previous.viewNodes, this.snapshot.viewNodes);
|
|
18035
18446
|
this.projectionRevision += 1;
|
|
18036
18447
|
this.emit({
|
|
@@ -18042,6 +18453,9 @@ var SemanticCanvasRuntime = class {
|
|
|
18042
18453
|
revision: this.projectionRevision
|
|
18043
18454
|
});
|
|
18044
18455
|
this.scheduleProjectContentRefresh();
|
|
18456
|
+
if (addedPendingWorkflowInstanceIds.length > 0) {
|
|
18457
|
+
this.creativeLoop?.observePendingWorkflowRuns(addedPendingWorkflowInstanceIds, 15e3);
|
|
18458
|
+
}
|
|
18045
18459
|
});
|
|
18046
18460
|
}
|
|
18047
18461
|
get revision() {
|
|
@@ -18124,7 +18538,7 @@ var SemanticCanvasRuntime = class {
|
|
|
18124
18538
|
this.listeners.add(listener);
|
|
18125
18539
|
return () => this.listeners.delete(listener);
|
|
18126
18540
|
}
|
|
18127
|
-
async execute(name, input, timeoutMs) {
|
|
18541
|
+
async execute(name, input, timeoutMs, options = {}) {
|
|
18128
18542
|
this.requireCapability(name);
|
|
18129
18543
|
if (name === "importExternalMedia") {
|
|
18130
18544
|
if (!this.externalMediaImporter)
|
|
@@ -18135,7 +18549,7 @@ var SemanticCanvasRuntime = class {
|
|
|
18135
18549
|
if (name === "uploadMedia") {
|
|
18136
18550
|
if (!this.mediaUploader)
|
|
18137
18551
|
throw new Error("Media upload system is unavailable.");
|
|
18138
|
-
const result3 = await this.mediaUploader.execute(input, timeoutMs);
|
|
18552
|
+
const result3 = await this.mediaUploader.execute(input, timeoutMs, options.onProgress);
|
|
18139
18553
|
return { ...result3, revision: this.projectionRevision };
|
|
18140
18554
|
}
|
|
18141
18555
|
if (isCreativeLoopOperation(name)) {
|
|
@@ -18342,15 +18756,18 @@ var SemanticCanvasRuntime = class {
|
|
|
18342
18756
|
};
|
|
18343
18757
|
}
|
|
18344
18758
|
async commit(entry, timeoutMs) {
|
|
18345
|
-
|
|
18346
|
-
this.
|
|
18759
|
+
const after = this.stampCreatedViewNodes(entry.after, entry.before);
|
|
18760
|
+
await this.applyHistoryState(after, entry.viewNodeIds, entry.label, timeoutMs);
|
|
18761
|
+
this.undoStack.push({ ...entry, after, kind: "reversible" });
|
|
18347
18762
|
this.redoStack.length = 0;
|
|
18348
18763
|
}
|
|
18349
18764
|
async applyOperationPlan(plan, timeoutMs) {
|
|
18350
18765
|
if (plan.changes.length > 0) {
|
|
18766
|
+
const before = plan.changes.map((change) => change.before);
|
|
18767
|
+
const after = this.stampCreatedViewNodes(plan.changes.map((change) => change.after), before);
|
|
18351
18768
|
const entry = {
|
|
18352
|
-
after
|
|
18353
|
-
before
|
|
18769
|
+
after,
|
|
18770
|
+
before,
|
|
18354
18771
|
kind: "reversible",
|
|
18355
18772
|
label: plan.label,
|
|
18356
18773
|
viewNodeIds: plan.changes.map((change) => change.viewNodeId)
|
|
@@ -18387,16 +18804,23 @@ var SemanticCanvasRuntime = class {
|
|
|
18387
18804
|
});
|
|
18388
18805
|
}
|
|
18389
18806
|
async applySemanticChanges(changes, label, timeoutMs, history, addPendingWorkflowInstanceIds, removePendingWorkflowInstanceIds) {
|
|
18807
|
+
const preparedChanges = changes.map((change) => ({
|
|
18808
|
+
...change,
|
|
18809
|
+
after: change.before === null ? this.stampCreatedViewNode(change.after) : structuredClone(change.after)
|
|
18810
|
+
}));
|
|
18390
18811
|
const previous = this.snapshot;
|
|
18391
18812
|
this.snapshot = await this.commands.changeViewNodes({
|
|
18392
18813
|
...addPendingWorkflowInstanceIds ? { addPendingWorkflowInstanceIds } : {},
|
|
18393
|
-
changes:
|
|
18814
|
+
changes: preparedChanges.map((change) => ({
|
|
18815
|
+
state: change.after,
|
|
18816
|
+
viewNodeId: change.viewNodeId
|
|
18817
|
+
})),
|
|
18394
18818
|
operationName: label,
|
|
18395
18819
|
...removePendingWorkflowInstanceIds ? { removePendingWorkflowInstanceIds } : {},
|
|
18396
18820
|
timeoutMs
|
|
18397
18821
|
});
|
|
18398
18822
|
this.projectionRevision += 1;
|
|
18399
|
-
const viewNodeIds =
|
|
18823
|
+
const viewNodeIds = preparedChanges.map((change) => change.viewNodeId);
|
|
18400
18824
|
const viewNodeChanges = diffSemanticViewNodes(previous.viewNodes, this.snapshot.viewNodes, viewNodeIds);
|
|
18401
18825
|
this.emit({
|
|
18402
18826
|
changedViewNodeIds: viewNodeIds,
|
|
@@ -18406,8 +18830,8 @@ var SemanticCanvasRuntime = class {
|
|
|
18406
18830
|
});
|
|
18407
18831
|
if (history === "undoable") {
|
|
18408
18832
|
this.undoStack.push({
|
|
18409
|
-
after:
|
|
18410
|
-
before:
|
|
18833
|
+
after: preparedChanges.map((change) => change.after),
|
|
18834
|
+
before: preparedChanges.map((change) => change.before),
|
|
18411
18835
|
kind: "reversible",
|
|
18412
18836
|
label,
|
|
18413
18837
|
viewNodeIds
|
|
@@ -18418,6 +18842,22 @@ var SemanticCanvasRuntime = class {
|
|
|
18418
18842
|
this.redoStack.length = 0;
|
|
18419
18843
|
}
|
|
18420
18844
|
}
|
|
18845
|
+
stampCreatedViewNodes(after, before) {
|
|
18846
|
+
return after.map((viewNode, index) => viewNode !== null && before[index] === null ? this.stampCreatedViewNode(viewNode) : structuredClone(viewNode));
|
|
18847
|
+
}
|
|
18848
|
+
stampCreatedViewNode(viewNode) {
|
|
18849
|
+
if (!this.creationProvenanceSuffix)
|
|
18850
|
+
return structuredClone(viewNode);
|
|
18851
|
+
const data = typeof viewNode.data === "object" && viewNode.data !== null && !Array.isArray(viewNode.data) ? structuredClone(viewNode.data) : {};
|
|
18852
|
+
delete data.creator_id;
|
|
18853
|
+
delete data.creator_name;
|
|
18854
|
+
data.createdByCli = this.creationProvenanceSuffix;
|
|
18855
|
+
if (viewNode.type === "note") {
|
|
18856
|
+
data.creator_id = this.options.actor.id;
|
|
18857
|
+
data.creator_name = `${this.options.actor.displayName} (${this.creationProvenanceSuffix})`;
|
|
18858
|
+
}
|
|
18859
|
+
return { ...structuredClone(viewNode), data };
|
|
18860
|
+
}
|
|
18421
18861
|
nextNotePosition() {
|
|
18422
18862
|
const count = this.snapshot.viewNodes.length;
|
|
18423
18863
|
return { x: 160 + count % 4 * 348, y: 160 + Math.floor(count / 4) * 348 };
|
|
@@ -18472,7 +18912,7 @@ function duplicateInputContainsResource(snapshot, input) {
|
|
|
18472
18912
|
return snapshot.viewNodes.some((viewNode) => ids.has(viewNode.id) && viewNode.type === "resource");
|
|
18473
18913
|
}
|
|
18474
18914
|
function isCreativeLoopOperation(name) {
|
|
18475
|
-
return name === "getCoreNode" || name === "placeCoreNode" || name === "createGenerationTarget" || name === "createMediaTarget" || name === "planGeneration" || name === "editGenerationDraft" || name === "addVisualReference" || name === "removeVisualReference" || name === "startGeneration" || name === "getWorkflowRun" || name === "waitWorkflowRun";
|
|
18915
|
+
return name === "getCoreNode" || name === "placeCoreNode" || name === "createGenerationTarget" || name === "createMediaTarget" || name === "planGeneration" || name === "editGenerationDraft" || name === "addVisualReference" || name === "insertPromptReference" || name === "removeVisualReference" || name === "startGeneration" || name === "reconcileGenerationStart" || name === "getWorkflowRun" || name === "waitWorkflowRun";
|
|
18476
18916
|
}
|
|
18477
18917
|
function createViewNodeId() {
|
|
18478
18918
|
return `view_node_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
@@ -18510,6 +18950,17 @@ function requireString6(value, field) {
|
|
|
18510
18950
|
throw new Error(`${field} must be a string.`);
|
|
18511
18951
|
return value;
|
|
18512
18952
|
}
|
|
18953
|
+
function normalizeCreationProvenanceSuffix(value) {
|
|
18954
|
+
if (value === void 0)
|
|
18955
|
+
return null;
|
|
18956
|
+
const normalized = value.trim();
|
|
18957
|
+
if (!normalized)
|
|
18958
|
+
throw new Error("clientName must be non-empty when provided.");
|
|
18959
|
+
if (normalized.includes("(") || normalized.includes(")")) {
|
|
18960
|
+
throw new Error("clientName must not contain parentheses.");
|
|
18961
|
+
}
|
|
18962
|
+
return normalized;
|
|
18963
|
+
}
|
|
18513
18964
|
|
|
18514
18965
|
// src/integrations/backend.ts
|
|
18515
18966
|
var BackendRequestError = class extends Error {
|
|
@@ -23176,10 +23627,12 @@ var HASH_BUFFER_BYTES = 256 * 1024;
|
|
|
23176
23627
|
function createNodeCanvasMediaUploadSystem(config2, cwd = process.cwd()) {
|
|
23177
23628
|
const backend = new Az8BackendClient(config2);
|
|
23178
23629
|
return {
|
|
23179
|
-
async prepare(sourceReference) {
|
|
23630
|
+
async prepare(sourceReference, options) {
|
|
23631
|
+
const report = createProgressReporter(options?.onProgress);
|
|
23632
|
+
await report({ bytesCompleted: 0, operation: "uploadMedia", stage: "inspecting" });
|
|
23180
23633
|
const source = await openUploadSource(sourceReference, cwd);
|
|
23181
23634
|
try {
|
|
23182
|
-
const descriptor2 = await inspectUploadSource(source.handle, source.absolutePath);
|
|
23635
|
+
const descriptor2 = await inspectUploadSource(source.handle, source.absolutePath, report);
|
|
23183
23636
|
return createPreparedUpload(config2, backend, source.handle, source.absolutePath, descriptor2);
|
|
23184
23637
|
} catch (error51) {
|
|
23185
23638
|
await source.handle.close().catch(() => void 0);
|
|
@@ -23213,7 +23666,7 @@ async function openUploadSource(sourceReference, cwd) {
|
|
|
23213
23666
|
}
|
|
23214
23667
|
return { absolutePath, handle };
|
|
23215
23668
|
}
|
|
23216
|
-
async function inspectUploadSource(handle, absolutePath) {
|
|
23669
|
+
async function inspectUploadSource(handle, absolutePath, report) {
|
|
23217
23670
|
const before = await handle.stat();
|
|
23218
23671
|
if (!Number.isSafeInteger(before.size) || before.size < 1) {
|
|
23219
23672
|
throw new Error("Media upload source must contain at least one byte.");
|
|
@@ -23232,6 +23685,7 @@ async function inspectUploadSource(handle, absolutePath) {
|
|
|
23232
23685
|
chunk2.copy(header, bytes, 0, Math.min(chunk2.byteLength, header.byteLength - bytes));
|
|
23233
23686
|
}
|
|
23234
23687
|
bytes += bytesRead;
|
|
23688
|
+
await report(uploadProgress("inspecting", bytes, before.size));
|
|
23235
23689
|
}
|
|
23236
23690
|
const after = await handle.stat();
|
|
23237
23691
|
if (after.size !== before.size || after.mtimeMs !== before.mtimeMs) {
|
|
@@ -23268,6 +23722,10 @@ function createPreparedUpload(config2, backend, handle, absolutePath, descriptor
|
|
|
23268
23722
|
if (closed) throw new Error("Prepared media upload is already closed.");
|
|
23269
23723
|
if (attempted) throw new Error("Prepared media upload can only be attempted once.");
|
|
23270
23724
|
attempted = true;
|
|
23725
|
+
await reportProgress(options?.onProgress, {
|
|
23726
|
+
operation: "uploadMedia",
|
|
23727
|
+
stage: "requesting-upload"
|
|
23728
|
+
});
|
|
23271
23729
|
const presign = await backend.request("core_nodes/upload/presign", {
|
|
23272
23730
|
json: { format: descriptor2.extension, type: descriptor2.mediaType },
|
|
23273
23731
|
method: "POST",
|
|
@@ -23276,12 +23734,20 @@ function createPreparedUpload(config2, backend, handle, absolutePath, descriptor
|
|
|
23276
23734
|
});
|
|
23277
23735
|
const uploadUrl = requireHttpUrl(presign.upload_url, "upload");
|
|
23278
23736
|
const fileUrl = requireHttpUrl(presign.file_url, "file");
|
|
23279
|
-
await putPreparedFile(
|
|
23737
|
+
await putPreparedFile(
|
|
23738
|
+
config2,
|
|
23739
|
+
handle,
|
|
23740
|
+
absolutePath,
|
|
23741
|
+
descriptor2,
|
|
23742
|
+
uploadUrl,
|
|
23743
|
+
options?.timeoutMs,
|
|
23744
|
+
createProgressReporter(options?.onProgress)
|
|
23745
|
+
);
|
|
23280
23746
|
return { url: fileUrl };
|
|
23281
23747
|
}
|
|
23282
23748
|
};
|
|
23283
23749
|
}
|
|
23284
|
-
async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploadUrl, timeoutMs) {
|
|
23750
|
+
async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploadUrl, timeoutMs, report) {
|
|
23285
23751
|
const originalStat = await handle.stat();
|
|
23286
23752
|
const uploadHandle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
23287
23753
|
let uploadStat;
|
|
@@ -23302,11 +23768,15 @@ async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploa
|
|
|
23302
23768
|
);
|
|
23303
23769
|
const hash2 = createHash("sha256");
|
|
23304
23770
|
let bytes = 0;
|
|
23771
|
+
await report(uploadProgress("uploading", 0, descriptor2.bytes));
|
|
23305
23772
|
const meter = new Transform({
|
|
23306
23773
|
transform(chunk2, _encoding, callback) {
|
|
23307
23774
|
hash2.update(chunk2);
|
|
23308
23775
|
bytes += chunk2.byteLength;
|
|
23309
|
-
|
|
23776
|
+
Promise.resolve(report(uploadProgress("uploading", bytes, descriptor2.bytes))).then(
|
|
23777
|
+
() => callback(null, chunk2),
|
|
23778
|
+
callback
|
|
23779
|
+
);
|
|
23310
23780
|
}
|
|
23311
23781
|
});
|
|
23312
23782
|
const source = uploadHandle.createReadStream({
|
|
@@ -23342,6 +23812,35 @@ async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploa
|
|
|
23342
23812
|
throw new Error("Media upload source changed before the upload completed.");
|
|
23343
23813
|
}
|
|
23344
23814
|
}
|
|
23815
|
+
function createProgressReporter(listener) {
|
|
23816
|
+
let lastStage = "";
|
|
23817
|
+
let lastReportedAt = 0;
|
|
23818
|
+
return async (progress) => {
|
|
23819
|
+
if (!listener) return;
|
|
23820
|
+
const now = Date.now();
|
|
23821
|
+
const complete = progress.bytesTotal !== void 0 && progress.bytesCompleted === progress.bytesTotal;
|
|
23822
|
+
const stageChanged = progress.stage !== lastStage;
|
|
23823
|
+
if (!stageChanged && !complete && now - lastReportedAt < 500) return;
|
|
23824
|
+
lastStage = progress.stage;
|
|
23825
|
+
lastReportedAt = now;
|
|
23826
|
+
await reportProgress(listener, progress);
|
|
23827
|
+
};
|
|
23828
|
+
}
|
|
23829
|
+
async function reportProgress(listener, progress) {
|
|
23830
|
+
try {
|
|
23831
|
+
await listener?.(progress);
|
|
23832
|
+
} catch {
|
|
23833
|
+
}
|
|
23834
|
+
}
|
|
23835
|
+
function uploadProgress(stage, bytesCompleted, bytesTotal) {
|
|
23836
|
+
return {
|
|
23837
|
+
bytesCompleted,
|
|
23838
|
+
bytesTotal,
|
|
23839
|
+
operation: "uploadMedia",
|
|
23840
|
+
percent: Math.min(100, Math.round(bytesCompleted / bytesTotal * 1e3) / 10),
|
|
23841
|
+
stage
|
|
23842
|
+
};
|
|
23843
|
+
}
|
|
23345
23844
|
function requireHttpUrl(value, kind) {
|
|
23346
23845
|
if (typeof value !== "string" || !value.trim()) {
|
|
23347
23846
|
throw new Error(`Media upload presign returned no ${kind} URL.`);
|
|
@@ -23410,8 +23909,19 @@ var COLLABORATION_SESSION_STATES = [
|
|
|
23410
23909
|
];
|
|
23411
23910
|
|
|
23412
23911
|
// src/canvas/watch/interest-registry.ts
|
|
23912
|
+
var SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY = "system.workflow-terminal";
|
|
23413
23913
|
var CollaborationInterestRegistry = class {
|
|
23414
23914
|
interests = /* @__PURE__ */ new Map();
|
|
23915
|
+
constructor(includeSystemWorkflowTerminal = false) {
|
|
23916
|
+
if (includeSystemWorkflowTerminal) {
|
|
23917
|
+
this.interests.set(SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY, {
|
|
23918
|
+
filter: { terminalOnly: true },
|
|
23919
|
+
key: SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY,
|
|
23920
|
+
kind: "workflow",
|
|
23921
|
+
system: true
|
|
23922
|
+
});
|
|
23923
|
+
}
|
|
23924
|
+
}
|
|
23415
23925
|
patch(input) {
|
|
23416
23926
|
const upserts = input.upsert ?? [];
|
|
23417
23927
|
const removals = input.remove ?? [];
|
|
@@ -23421,6 +23931,9 @@ var CollaborationInterestRegistry = class {
|
|
|
23421
23931
|
for (const entry of upserts) {
|
|
23422
23932
|
const key = requireKey(entry.key);
|
|
23423
23933
|
const current = next.get(key);
|
|
23934
|
+
if (current?.system) {
|
|
23935
|
+
throw interestError("system-interest-readonly", `System Interest ${key} cannot be changed.`);
|
|
23936
|
+
}
|
|
23424
23937
|
if (entry.patch.kind !== void 0 && !isInterestKind(entry.patch.kind)) {
|
|
23425
23938
|
throw interestError(
|
|
23426
23939
|
"interest-kind-invalid",
|
|
@@ -23441,6 +23954,9 @@ var CollaborationInterestRegistry = class {
|
|
|
23441
23954
|
}
|
|
23442
23955
|
for (const rawKey of removals) {
|
|
23443
23956
|
const key = requireKey(rawKey);
|
|
23957
|
+
if (next.get(key)?.system) {
|
|
23958
|
+
throw interestError("system-interest-readonly", `System Interest ${key} cannot be removed.`);
|
|
23959
|
+
}
|
|
23444
23960
|
if (next.delete(key)) changedKeys.push(key);
|
|
23445
23961
|
}
|
|
23446
23962
|
this.interests.clear();
|
|
@@ -23646,6 +24162,7 @@ function projectWorkflowRun(run, observedAt) {
|
|
|
23646
24162
|
status: run.status,
|
|
23647
24163
|
summary: {
|
|
23648
24164
|
definitionId: run.definitionId,
|
|
24165
|
+
workflowRunId: run.id,
|
|
23649
24166
|
...run.error ? {
|
|
23650
24167
|
error: {
|
|
23651
24168
|
...run.error.code ? { code: run.error.code } : {},
|
|
@@ -23655,6 +24172,7 @@ function projectWorkflowRun(run, observedAt) {
|
|
|
23655
24172
|
...run.progress === void 0 ? {} : { progress: run.progress },
|
|
23656
24173
|
...run.outputs.length > 0 ? {
|
|
23657
24174
|
outputs: run.outputs.map((output) => ({
|
|
24175
|
+
coreNodeId: output.coreNodeId,
|
|
23658
24176
|
role: output.role,
|
|
23659
24177
|
slotName: output.slotName,
|
|
23660
24178
|
...output.viewNodeId ? { viewNodeId: output.viewNodeId } : {}
|
|
@@ -23709,16 +24227,18 @@ function safeWorkflowMessage(value) {
|
|
|
23709
24227
|
|
|
23710
24228
|
// src/canvas/watch/watch.ts
|
|
23711
24229
|
var AgentCollaborationWatch = class {
|
|
23712
|
-
interests
|
|
24230
|
+
interests;
|
|
23713
24231
|
maxChangesPerDelivery;
|
|
23714
24232
|
maxPendingChanges;
|
|
23715
24233
|
maxRecentResults;
|
|
23716
24234
|
now;
|
|
23717
24235
|
pending = [];
|
|
23718
24236
|
recent = [];
|
|
24237
|
+
workflowWatermarks = /* @__PURE__ */ new Map();
|
|
23719
24238
|
activeWait = null;
|
|
23720
24239
|
continuityLost = false;
|
|
23721
24240
|
constructor(options = {}) {
|
|
24241
|
+
this.interests = new CollaborationInterestRegistry(options.includeSystemWorkflowTerminal);
|
|
23722
24242
|
this.maxChangesPerDelivery = positive(
|
|
23723
24243
|
options.maxChangesPerDelivery ?? 64,
|
|
23724
24244
|
"maxChangesPerDelivery"
|
|
@@ -23728,7 +24248,13 @@ var AgentCollaborationWatch = class {
|
|
|
23728
24248
|
this.now = options.now ?? Date.now;
|
|
23729
24249
|
}
|
|
23730
24250
|
patchInterests(input) {
|
|
23731
|
-
|
|
24251
|
+
const result3 = this.interests.patch(input);
|
|
24252
|
+
for (const key of result3.changedKeys) {
|
|
24253
|
+
for (const watermarkKey of this.workflowWatermarks.keys()) {
|
|
24254
|
+
if (watermarkKey.startsWith(`${key}\0`)) this.workflowWatermarks.delete(watermarkKey);
|
|
24255
|
+
}
|
|
24256
|
+
}
|
|
24257
|
+
return result3;
|
|
23732
24258
|
}
|
|
23733
24259
|
getInterests() {
|
|
23734
24260
|
return this.interests.getAll();
|
|
@@ -23752,10 +24278,24 @@ var AgentCollaborationWatch = class {
|
|
|
23752
24278
|
recordMany(facts) {
|
|
23753
24279
|
if (this.continuityLost) return;
|
|
23754
24280
|
for (const fact of facts) {
|
|
23755
|
-
for (const interest of this.interests.matching(fact))
|
|
24281
|
+
for (const interest of this.interests.matching(fact)) {
|
|
24282
|
+
this.recordWorkflowWatermark(interest.key, fact);
|
|
24283
|
+
this.enqueue(interest.key, fact);
|
|
24284
|
+
}
|
|
23756
24285
|
}
|
|
23757
24286
|
if (this.activeWait && (this.pending.length > 0 || this.continuityLost)) this.deliverActive();
|
|
23758
24287
|
}
|
|
24288
|
+
recordAuthoritativeWorkflow(fact) {
|
|
24289
|
+
if (fact.domain !== "workflow" || this.continuityLost) return;
|
|
24290
|
+
for (const interest of this.interests.matching(fact)) {
|
|
24291
|
+
const key = workflowWatermarkKey(interest.key, fact.ref);
|
|
24292
|
+
const fingerprint = workflowFingerprint(fact);
|
|
24293
|
+
if (this.workflowWatermarks.get(key) === fingerprint) continue;
|
|
24294
|
+
this.workflowWatermarks.set(key, fingerprint);
|
|
24295
|
+
this.enqueue(interest.key, fact);
|
|
24296
|
+
}
|
|
24297
|
+
if (this.activeWait && this.pending.length > 0) this.deliverActive();
|
|
24298
|
+
}
|
|
23759
24299
|
wait(options) {
|
|
23760
24300
|
const timeoutMs = positive(options.timeoutMs, "timeoutMs");
|
|
23761
24301
|
if (this.activeWait)
|
|
@@ -23789,6 +24329,11 @@ var AgentCollaborationWatch = class {
|
|
|
23789
24329
|
resynchronize() {
|
|
23790
24330
|
this.continuityLost = false;
|
|
23791
24331
|
this.pending.length = 0;
|
|
24332
|
+
this.workflowWatermarks.clear();
|
|
24333
|
+
}
|
|
24334
|
+
recordWorkflowWatermark(interest, fact) {
|
|
24335
|
+
if (fact.domain !== "workflow") return;
|
|
24336
|
+
this.workflowWatermarks.set(workflowWatermarkKey(interest, fact.ref), workflowFingerprint(fact));
|
|
23792
24337
|
}
|
|
23793
24338
|
enqueue(interest, fact) {
|
|
23794
24339
|
const terminalPending = this.pending.find(
|
|
@@ -23832,6 +24377,7 @@ var AgentCollaborationWatch = class {
|
|
|
23832
24377
|
merged: entry.merged,
|
|
23833
24378
|
quietForMs: Math.max(0, now - entry.lastObservedAt),
|
|
23834
24379
|
ref: entry.fact.ref,
|
|
24380
|
+
...entry.fact.domain === "canvas" ? { source: entry.fact.source } : {},
|
|
23835
24381
|
summary: structuredClone(entry.fact.summary)
|
|
23836
24382
|
}));
|
|
23837
24383
|
const result3 = {
|
|
@@ -23908,6 +24454,18 @@ function positive(value, field) {
|
|
|
23908
24454
|
function watchError(code, message) {
|
|
23909
24455
|
return Object.assign(new Error(message), { code });
|
|
23910
24456
|
}
|
|
24457
|
+
function workflowWatermarkKey(interest, runId) {
|
|
24458
|
+
return `${interest}\0${runId}`;
|
|
24459
|
+
}
|
|
24460
|
+
function workflowFingerprint(fact) {
|
|
24461
|
+
return JSON.stringify({
|
|
24462
|
+
changeKind: fact.changeKind,
|
|
24463
|
+
kind: fact.kind,
|
|
24464
|
+
status: fact.status,
|
|
24465
|
+
summary: fact.summary,
|
|
24466
|
+
terminal: fact.terminal
|
|
24467
|
+
});
|
|
24468
|
+
}
|
|
23911
24469
|
|
|
23912
24470
|
// src/canvas/session.ts
|
|
23913
24471
|
var CanvasSession = class {
|
|
@@ -23916,7 +24474,7 @@ var CanvasSession = class {
|
|
|
23916
24474
|
this.projectId = projectId;
|
|
23917
24475
|
this.dependencies = dependencies;
|
|
23918
24476
|
this.clientInstanceId = clientInstanceId;
|
|
23919
|
-
this.watch = dependencies.watch ?? new AgentCollaborationWatch();
|
|
24477
|
+
this.watch = dependencies.watch ?? new AgentCollaborationWatch({ includeSystemWorkflowTerminal: true });
|
|
23920
24478
|
this.health = {
|
|
23921
24479
|
connectionAttempts: 0,
|
|
23922
24480
|
connectionId: null,
|
|
@@ -23934,6 +24492,7 @@ var CanvasSession = class {
|
|
|
23934
24492
|
runtime = null;
|
|
23935
24493
|
unsubscribeRuntime = null;
|
|
23936
24494
|
observationListeners = /* @__PURE__ */ new Set();
|
|
24495
|
+
knownWorkflowRunIds = /* @__PURE__ */ new Set();
|
|
23937
24496
|
projectionRevision = 0;
|
|
23938
24497
|
actorId = null;
|
|
23939
24498
|
collaborationContext = null;
|
|
@@ -23983,6 +24542,7 @@ var CanvasSession = class {
|
|
|
23983
24542
|
const connectionId = this.health.connectionId;
|
|
23984
24543
|
const runtime = new SemanticCanvasRuntime({
|
|
23985
24544
|
actor: { displayName: actor.displayName, id: actor.accountId },
|
|
24545
|
+
clientName: normalizedClientName,
|
|
23986
24546
|
clientInstanceId: this.clientInstanceId,
|
|
23987
24547
|
document: document2,
|
|
23988
24548
|
foundationData,
|
|
@@ -24001,9 +24561,15 @@ var CanvasSession = class {
|
|
|
24001
24561
|
workflowRuntime
|
|
24002
24562
|
});
|
|
24003
24563
|
this.runtime = runtime;
|
|
24564
|
+
for (const workflowRunId of runtime.getSnapshot().pendingWorkflowInstanceIds) {
|
|
24565
|
+
this.knownWorkflowRunIds.add(workflowRunId);
|
|
24566
|
+
}
|
|
24004
24567
|
this.projectionRevision = runtime.revision;
|
|
24005
24568
|
this.unsubscribeRuntime = runtime.subscribe((observation) => {
|
|
24006
24569
|
this.projectionRevision = observation.revision;
|
|
24570
|
+
if (observation.workflowRun) {
|
|
24571
|
+
this.knownWorkflowRunIds.add(observation.workflowRun.id);
|
|
24572
|
+
}
|
|
24007
24573
|
this.watch.recordMany(projectCanvasObservation(observation, Date.now()));
|
|
24008
24574
|
const legacyObservation = {
|
|
24009
24575
|
...observation.changedViewNodeIds ? { changedViewNodeIds: observation.changedViewNodeIds } : {},
|
|
@@ -24022,13 +24588,60 @@ var CanvasSession = class {
|
|
|
24022
24588
|
throw error51;
|
|
24023
24589
|
}
|
|
24024
24590
|
}
|
|
24025
|
-
async execute(name, input, timeoutMs) {
|
|
24591
|
+
async execute(name, input, timeoutMs, options = {}) {
|
|
24026
24592
|
const runtime = this.requireRuntime();
|
|
24027
|
-
|
|
24593
|
+
if (typeof input.workflowRunId === "string" && input.workflowRunId) {
|
|
24594
|
+
this.knownWorkflowRunIds.add(input.workflowRunId);
|
|
24595
|
+
}
|
|
24596
|
+
const result3 = await runtime.execute(name, input, timeoutMs, options);
|
|
24597
|
+
if (typeof result3.result.workflowRunId === "string" && result3.result.workflowRunId) {
|
|
24598
|
+
this.knownWorkflowRunIds.add(result3.result.workflowRunId);
|
|
24599
|
+
}
|
|
24028
24600
|
this.projectionRevision = result3.revision;
|
|
24029
24601
|
if (result3.attention) this.publishAttention(result3.attention);
|
|
24030
24602
|
return result3;
|
|
24031
24603
|
}
|
|
24604
|
+
async catchUpWorkflowInterests(timeoutMs) {
|
|
24605
|
+
const runtime = this.requireRuntime();
|
|
24606
|
+
const deadline = Date.now() + timeoutMs;
|
|
24607
|
+
const knownWorkflowRunIds = [...this.knownWorkflowRunIds];
|
|
24608
|
+
const runIds = [
|
|
24609
|
+
...new Set(
|
|
24610
|
+
this.watch.getInterests().filter((interest) => interest.kind === "workflow").flatMap((interest) => interest.filter.runIds ?? knownWorkflowRunIds)
|
|
24611
|
+
)
|
|
24612
|
+
];
|
|
24613
|
+
for (const workflowRunId of runIds) {
|
|
24614
|
+
const remainingMs = deadline - Date.now();
|
|
24615
|
+
if (remainingMs < 1) break;
|
|
24616
|
+
try {
|
|
24617
|
+
const result3 = await runtime.execute("getWorkflowRun", { workflowRunId }, remainingMs);
|
|
24618
|
+
const workflowRun = result3.result.workflowRun;
|
|
24619
|
+
if (!isWorkflowRun(workflowRun)) continue;
|
|
24620
|
+
for (const fact of projectCanvasObservation(
|
|
24621
|
+
{ kind: "workflow", revision: result3.revision, workflowRun },
|
|
24622
|
+
Date.now()
|
|
24623
|
+
)) {
|
|
24624
|
+
this.watch.recordAuthoritativeWorkflow(fact);
|
|
24625
|
+
}
|
|
24626
|
+
} catch {
|
|
24627
|
+
}
|
|
24628
|
+
}
|
|
24629
|
+
}
|
|
24630
|
+
acknowledgeWatchDelivery(result3) {
|
|
24631
|
+
if (result3.status !== "ready") return;
|
|
24632
|
+
for (const change of result3.changes) {
|
|
24633
|
+
if (change.source !== "remote" || !change.kind.startsWith("note.") && !change.kind.startsWith("view-node.")) {
|
|
24634
|
+
continue;
|
|
24635
|
+
}
|
|
24636
|
+
const bounds = this.requireRuntime().resolveAttentionBounds({
|
|
24637
|
+
kind: "view-nodes",
|
|
24638
|
+
viewNodeIds: [change.ref]
|
|
24639
|
+
});
|
|
24640
|
+
if (!bounds) continue;
|
|
24641
|
+
this.publishAttentionBounds(bounds);
|
|
24642
|
+
return;
|
|
24643
|
+
}
|
|
24644
|
+
}
|
|
24032
24645
|
getCapabilities(detail) {
|
|
24033
24646
|
return this.requireRuntime().capabilities.map(
|
|
24034
24647
|
(descriptor2) => detail ? descriptor2 : {
|
|
@@ -24142,6 +24755,9 @@ function isIndeterminateWrite4(value) {
|
|
|
24142
24755
|
function isWorkflowRecoveryExhausted(value) {
|
|
24143
24756
|
return typeof value === "object" && value !== null && "workflowRecoveryExhausted" in value && value.workflowRecoveryExhausted === true;
|
|
24144
24757
|
}
|
|
24758
|
+
function isWorkflowRun(value) {
|
|
24759
|
+
return typeof value === "object" && value !== null && "id" in value && typeof value.id === "string" && "status" in value && typeof value.status === "string" && "definitionId" in value && typeof value.definitionId === "string" && "outputs" in value && Array.isArray(value.outputs);
|
|
24760
|
+
}
|
|
24145
24761
|
|
|
24146
24762
|
// src/canvas/client.ts
|
|
24147
24763
|
var CANVAS_QUERY_TARGETS = [
|
|
@@ -24226,7 +24842,9 @@ var CanvasClient = class {
|
|
|
24226
24842
|
};
|
|
24227
24843
|
this.receipts.push(receipt);
|
|
24228
24844
|
try {
|
|
24229
|
-
const operation = await this.session.execute(name, input.input, input.timeoutMs ?? 15e3
|
|
24845
|
+
const operation = await this.session.execute(name, input.input, input.timeoutMs ?? 15e3, {
|
|
24846
|
+
onProgress: input.onProgress
|
|
24847
|
+
});
|
|
24230
24848
|
Object.assign(receipt, {
|
|
24231
24849
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24232
24850
|
projectionRevisionAfter: operation.revision,
|
|
@@ -24236,6 +24854,7 @@ var CanvasClient = class {
|
|
|
24236
24854
|
});
|
|
24237
24855
|
return { receipt };
|
|
24238
24856
|
} catch (error51) {
|
|
24857
|
+
const operationResult2 = readCanvasOperationIndeterminateResult(error51);
|
|
24239
24858
|
const operationUsage = readCanvasOperationUsage(error51);
|
|
24240
24859
|
const indeterminate = isIndeterminateError(error51);
|
|
24241
24860
|
const timedOut = indeterminate && (error51 instanceof IndeterminateBackendWriteError && error51.timedOut || readTimedOut(error51) || error51 instanceof Error && /timed out/i.test(error51.message));
|
|
@@ -24247,6 +24866,7 @@ var CanvasClient = class {
|
|
|
24247
24866
|
message: safeCanvasMessage(error51),
|
|
24248
24867
|
outcome: indeterminate ? "indeterminate" : "failed"
|
|
24249
24868
|
},
|
|
24869
|
+
...operationResult2 ? { result: redactCanvasRecord(operationResult2) } : {},
|
|
24250
24870
|
status: timedOut ? "timed-out" : "failed",
|
|
24251
24871
|
...operationUsage ? { usage: operationUsage } : {}
|
|
24252
24872
|
});
|
|
@@ -24397,7 +25017,7 @@ import { chmod, unlink } from "node:fs/promises";
|
|
|
24397
25017
|
import net from "node:net";
|
|
24398
25018
|
var MAX_RPC_REQUEST_BYTES = 1024 * 1024;
|
|
24399
25019
|
var MAX_RPC_RESPONSE_BYTES = 16 * 1024 * 1024;
|
|
24400
|
-
async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal) {
|
|
25020
|
+
async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal, onProgress) {
|
|
24401
25021
|
return new Promise((resolve, reject) => {
|
|
24402
25022
|
const socket = net.createConnection(socketPath);
|
|
24403
25023
|
let source = "";
|
|
@@ -24427,14 +25047,23 @@ async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal)
|
|
|
24427
25047
|
finish(rpcError("session-rpc-too-large", "Agent Session RPC response is too large."));
|
|
24428
25048
|
return;
|
|
24429
25049
|
}
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
const
|
|
24434
|
-
|
|
24435
|
-
|
|
24436
|
-
|
|
24437
|
-
|
|
25050
|
+
while (!settled) {
|
|
25051
|
+
const newline = source.indexOf("\n");
|
|
25052
|
+
if (newline < 0) return;
|
|
25053
|
+
const line = source.slice(0, newline);
|
|
25054
|
+
source = source.slice(newline + 1);
|
|
25055
|
+
try {
|
|
25056
|
+
const message = JSON.parse(line);
|
|
25057
|
+
if ("type" in message && message.type === "progress") {
|
|
25058
|
+
void Promise.resolve(onProgress?.(message.progress)).catch(() => void 0);
|
|
25059
|
+
continue;
|
|
25060
|
+
}
|
|
25061
|
+
const response = message;
|
|
25062
|
+
if (!response.ok) finish(rpcError(response.error.code, response.error.message));
|
|
25063
|
+
else finish(void 0, response.value);
|
|
25064
|
+
} catch {
|
|
25065
|
+
finish(rpcError("invalid-session-rpc", "Agent Session RPC response is invalid."));
|
|
25066
|
+
}
|
|
24438
25067
|
}
|
|
24439
25068
|
});
|
|
24440
25069
|
socket.once("error", (error51) => finish(error51));
|
|
@@ -24473,7 +25102,10 @@ async function listenAgentSessionRpc(socketPath, handler) {
|
|
|
24473
25102
|
handled = true;
|
|
24474
25103
|
try {
|
|
24475
25104
|
const request = JSON.parse(source.slice(0, newline));
|
|
24476
|
-
const value = await handler(request, {
|
|
25105
|
+
const value = await handler(request, {
|
|
25106
|
+
reportProgress: (progress) => writeProgress(socket, progress),
|
|
25107
|
+
signal: abortController2.signal
|
|
25108
|
+
});
|
|
24477
25109
|
writeResponse(socket, { ok: true, value });
|
|
24478
25110
|
} catch (error51) {
|
|
24479
25111
|
writeResponse(
|
|
@@ -24505,6 +25137,13 @@ async function listenAgentSessionRpc(socketPath, handler) {
|
|
|
24505
25137
|
}
|
|
24506
25138
|
};
|
|
24507
25139
|
}
|
|
25140
|
+
function writeProgress(socket, progress) {
|
|
25141
|
+
if (socket.destroyed || !socket.writable) return Promise.resolve();
|
|
25142
|
+
return new Promise((resolve) => {
|
|
25143
|
+
socket.write(`${JSON.stringify({ progress, type: "progress" })}
|
|
25144
|
+
`, () => resolve());
|
|
25145
|
+
});
|
|
25146
|
+
}
|
|
24508
25147
|
function writeResponse(socket, response) {
|
|
24509
25148
|
if (socket.destroyed) return;
|
|
24510
25149
|
socket.end(`${JSON.stringify(response)}
|
|
@@ -24856,7 +25495,16 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
|
|
|
24856
25495
|
}
|
|
24857
25496
|
if (request.kind === "delivery.ack") {
|
|
24858
25497
|
const acknowledged = outstandingDelivery?.deliveryId === request.deliveryId;
|
|
24859
|
-
if (acknowledged)
|
|
25498
|
+
if (acknowledged) {
|
|
25499
|
+
const result3 = outstandingDelivery?.result;
|
|
25500
|
+
outstandingDelivery = void 0;
|
|
25501
|
+
if (result3) {
|
|
25502
|
+
try {
|
|
25503
|
+
client.session.acknowledgeWatchDelivery?.(result3);
|
|
25504
|
+
} catch {
|
|
25505
|
+
}
|
|
25506
|
+
}
|
|
25507
|
+
}
|
|
24860
25508
|
return { acknowledged };
|
|
24861
25509
|
}
|
|
24862
25510
|
if (request.kind === "interests.get") return watch.getInterests();
|
|
@@ -24882,7 +25530,10 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
|
|
|
24882
25530
|
}
|
|
24883
25531
|
if (request.kind === "watch.wait") {
|
|
24884
25532
|
if (outstandingDelivery) return outstandingDelivery;
|
|
24885
|
-
const
|
|
25533
|
+
const startedAt = Date.now();
|
|
25534
|
+
await client.session.catchUpWorkflowInterests?.(Math.min(request.timeoutMs, 15e3));
|
|
25535
|
+
const remainingMs = Math.max(1, request.timeoutMs - (Date.now() - startedAt));
|
|
25536
|
+
const result3 = await watch.wait({ signal: context.signal, timeoutMs: remainingMs });
|
|
24886
25537
|
if (result3.status !== "ready") return { result: result3 };
|
|
24887
25538
|
outstandingDelivery = { deliveryId: crypto.randomUUID(), result: result3 };
|
|
24888
25539
|
return outstandingDelivery;
|
|
@@ -24897,6 +25548,7 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
|
|
|
24897
25548
|
return await client.executeOperation({
|
|
24898
25549
|
input: request.input,
|
|
24899
25550
|
name: request.name,
|
|
25551
|
+
onProgress: context.reportProgress,
|
|
24900
25552
|
requestId: request.requestId,
|
|
24901
25553
|
...request.timeoutMs ? { timeoutMs: request.timeoutMs } : {}
|
|
24902
25554
|
});
|
|
@@ -24905,6 +25557,7 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
|
|
|
24905
25557
|
() => client.executeOperation({
|
|
24906
25558
|
input: request.input,
|
|
24907
25559
|
name: request.name,
|
|
25560
|
+
onProgress: context.reportProgress,
|
|
24908
25561
|
requestId: request.requestId,
|
|
24909
25562
|
...request.timeoutMs ? { timeoutMs: request.timeoutMs } : {}
|
|
24910
25563
|
})
|
|
@@ -24949,6 +25602,8 @@ async function createCanvasClient(spec, dependencies) {
|
|
|
24949
25602
|
executeOperation: (input) => client.executeOperation(input),
|
|
24950
25603
|
query: (input) => client.query(input),
|
|
24951
25604
|
session: {
|
|
25605
|
+
acknowledgeWatchDelivery: (result3) => session.acknowledgeWatchDelivery(result3),
|
|
25606
|
+
catchUpWorkflowInterests: (timeoutMs) => session.catchUpWorkflowInterests(timeoutMs),
|
|
24952
25607
|
getContext: () => session.getCollaborationContext(),
|
|
24953
25608
|
getHealth: () => session.getHealth(),
|
|
24954
25609
|
getIdentity: () => session.getIdentity(),
|
|
@@ -25351,6 +26006,11 @@ var CanvasProtocolServer = class {
|
|
|
25351
26006
|
const outcome = await this.client.executeOperation({
|
|
25352
26007
|
input: request.payload.input,
|
|
25353
26008
|
name: request.payload.name,
|
|
26009
|
+
onProgress: (progress) => this.output.emitMandatory({
|
|
26010
|
+
payload: progress,
|
|
26011
|
+
requestId: request.requestId,
|
|
26012
|
+
type: "operationProgress"
|
|
26013
|
+
}),
|
|
25354
26014
|
requestId: request.requestId,
|
|
25355
26015
|
...request.payload.timeoutMs ? { timeoutMs: request.payload.timeoutMs } : {}
|
|
25356
26016
|
});
|
|
@@ -28990,6 +29650,9 @@ async function runCanvasOneShot(config2, command, dependencies = {}) {
|
|
|
28990
29650
|
input: command.input,
|
|
28991
29651
|
name: command.name,
|
|
28992
29652
|
requestId: command.requestId,
|
|
29653
|
+
...dependencies.onProgress ? {
|
|
29654
|
+
onProgress: (progress) => dependencies.onProgress?.({ ...progress, requestId: command.requestId })
|
|
29655
|
+
} : {},
|
|
28993
29656
|
...command.timeoutMs ? { timeoutMs: command.timeoutMs } : {}
|
|
28994
29657
|
});
|
|
28995
29658
|
return operationResult(command, outcome);
|
|
@@ -29175,18 +29838,21 @@ function resolveDownloadableCoreNodeMedia(coreNode) {
|
|
|
29175
29838
|
|
|
29176
29839
|
// src/media/download.ts
|
|
29177
29840
|
var DEFAULT_RESOLUTION_TIMEOUT_MS = 15e3;
|
|
29841
|
+
var DEFAULT_DOWNLOAD_ATTEMPTS = 3;
|
|
29178
29842
|
var FILE_TYPE_HEADER_BYTES2 = 64 * 1024;
|
|
29179
29843
|
var MediaDownloadError = class extends Error {
|
|
29180
|
-
constructor(code, message, exitCode, retryable) {
|
|
29844
|
+
constructor(code, message, exitCode, retryable, details = {}) {
|
|
29181
29845
|
super(message);
|
|
29182
29846
|
this.code = code;
|
|
29183
29847
|
this.exitCode = exitCode;
|
|
29184
29848
|
this.retryable = retryable;
|
|
29849
|
+
this.details = details;
|
|
29185
29850
|
this.name = "MediaDownloadError";
|
|
29186
29851
|
}
|
|
29187
29852
|
code;
|
|
29188
29853
|
exitCode;
|
|
29189
29854
|
retryable;
|
|
29855
|
+
details;
|
|
29190
29856
|
};
|
|
29191
29857
|
async function runCanvasMediaDownload(config2, command, dependencies = {}) {
|
|
29192
29858
|
const requestedAbsolutePath = path6.resolve(dependencies.cwd ?? process.cwd(), command.outputPath);
|
|
@@ -29201,42 +29867,80 @@ async function runCanvasMediaDownload(config2, command, dependencies = {}) {
|
|
|
29201
29867
|
} catch (error51) {
|
|
29202
29868
|
return failureResult2(command, error51, requestedAbsolutePath);
|
|
29203
29869
|
}
|
|
29204
|
-
|
|
29205
|
-
|
|
29206
|
-
|
|
29207
|
-
|
|
29208
|
-
|
|
29209
|
-
|
|
29210
|
-
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29216
|
-
|
|
29217
|
-
|
|
29218
|
-
|
|
29219
|
-
media = await resolveMediaSource(client, command, dependencies.randomUUID);
|
|
29220
|
-
} catch (error51) {
|
|
29221
|
-
return failureResult2(command, error51, outputTarget.absolutePath);
|
|
29222
|
-
} finally {
|
|
29223
|
-
client?.close();
|
|
29870
|
+
const maximumAttempts = dependencies.maxAttempts ?? DEFAULT_DOWNLOAD_ATTEMPTS;
|
|
29871
|
+
if (!Number.isInteger(maximumAttempts) || maximumAttempts < 1 || maximumAttempts > 3) {
|
|
29872
|
+
return failureResult2(
|
|
29873
|
+
command,
|
|
29874
|
+
new MediaDownloadError(
|
|
29875
|
+
"invalid-download-attempts",
|
|
29876
|
+
"Download attempt limit must be between 1 and 3.",
|
|
29877
|
+
1,
|
|
29878
|
+
false,
|
|
29879
|
+
{ phase: "setup" }
|
|
29880
|
+
),
|
|
29881
|
+
outputTarget.absolutePath,
|
|
29882
|
+
0,
|
|
29883
|
+
0
|
|
29884
|
+
);
|
|
29224
29885
|
}
|
|
29225
|
-
|
|
29226
|
-
|
|
29227
|
-
|
|
29228
|
-
|
|
29229
|
-
|
|
29230
|
-
|
|
29231
|
-
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
|
|
29886
|
+
const deadlineAt = command.timeoutMs ? Date.now() + command.timeoutMs : null;
|
|
29887
|
+
let lastError;
|
|
29888
|
+
for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
|
|
29889
|
+
try {
|
|
29890
|
+
throwIfCancelled(dependencies.signal);
|
|
29891
|
+
const remainingMs = remainingDownloadMs(deadlineAt);
|
|
29892
|
+
const attemptCommand = {
|
|
29893
|
+
...command,
|
|
29894
|
+
...remainingMs === void 0 ? {} : { timeoutMs: remainingMs }
|
|
29895
|
+
};
|
|
29896
|
+
const media = await resolveMediaForAttempt(config2, attemptCommand, dependencies);
|
|
29897
|
+
const file2 = await transferMedia(config2, media, outputTarget, attemptCommand, dependencies);
|
|
29898
|
+
return {
|
|
29899
|
+
exitCode: 0,
|
|
29900
|
+
output: {
|
|
29901
|
+
attempts: { completed: attempt, maximum: maximumAttempts },
|
|
29902
|
+
file: file2,
|
|
29903
|
+
format: "az8.canvas.media-download.v1",
|
|
29904
|
+
media: { coreNodeId: media.coreNodeId, mediaType: media.mediaType },
|
|
29905
|
+
projectId: command.projectId,
|
|
29906
|
+
source: command.source
|
|
29907
|
+
}
|
|
29908
|
+
};
|
|
29909
|
+
} catch (error51) {
|
|
29910
|
+
lastError = error51;
|
|
29911
|
+
const failure = normalizeDownloadError(error51);
|
|
29912
|
+
if (!failure.retryable || failure.code === "download-cancelled" || failure.code === "download-timed-out" || attempt === maximumAttempts) {
|
|
29913
|
+
return failureResult2(command, failure, outputTarget.absolutePath, attempt, maximumAttempts);
|
|
29235
29914
|
}
|
|
29236
|
-
|
|
29237
|
-
|
|
29238
|
-
|
|
29915
|
+
try {
|
|
29916
|
+
await waitForDownloadRetry(
|
|
29917
|
+
downloadRetryDelay(attempt, dependencies.random?.() ?? Math.random()),
|
|
29918
|
+
deadlineAt,
|
|
29919
|
+
dependencies
|
|
29920
|
+
);
|
|
29921
|
+
} catch (retryError) {
|
|
29922
|
+
return failureResult2(
|
|
29923
|
+
command,
|
|
29924
|
+
retryError,
|
|
29925
|
+
outputTarget.absolutePath,
|
|
29926
|
+
attempt,
|
|
29927
|
+
maximumAttempts
|
|
29928
|
+
);
|
|
29929
|
+
}
|
|
29930
|
+
}
|
|
29239
29931
|
}
|
|
29932
|
+
return failureResult2(
|
|
29933
|
+
command,
|
|
29934
|
+
lastError,
|
|
29935
|
+
outputTarget.absolutePath,
|
|
29936
|
+
maximumAttempts,
|
|
29937
|
+
maximumAttempts
|
|
29938
|
+
);
|
|
29939
|
+
}
|
|
29940
|
+
function downloadRetryDelay(attempt, random) {
|
|
29941
|
+
const boundedRandom = Number.isFinite(random) ? Math.max(0, Math.min(1, random)) : 0.5;
|
|
29942
|
+
const base = Math.min(250 * 2 ** (attempt - 1), 1e3);
|
|
29943
|
+
return Math.min(1e3, Math.round(base * (0.75 + boundedRandom * 0.5)));
|
|
29240
29944
|
}
|
|
29241
29945
|
function formatCanvasMediaDownloadResult(result3) {
|
|
29242
29946
|
return `${JSON.stringify(result3.output, null, 2)}
|
|
@@ -29252,7 +29956,8 @@ async function resolveMediaSource(client, command, randomUUID4) {
|
|
|
29252
29956
|
"view-node-media-unavailable",
|
|
29253
29957
|
safeCanvasMessage(error51),
|
|
29254
29958
|
1,
|
|
29255
|
-
false
|
|
29959
|
+
false,
|
|
29960
|
+
{ cause: sanitizeDownloadCause(error51), phase: "source-resolution" }
|
|
29256
29961
|
);
|
|
29257
29962
|
}
|
|
29258
29963
|
} else {
|
|
@@ -29270,7 +29975,8 @@ async function resolveMediaSource(client, command, randomUUID4) {
|
|
|
29270
29975
|
"core-node-resolution-failed",
|
|
29271
29976
|
outcome.receipt.error?.message ?? `Core Node ${coreNodeId} could not be resolved.`,
|
|
29272
29977
|
indeterminate ? 2 : 1,
|
|
29273
|
-
indeterminate
|
|
29978
|
+
indeterminate,
|
|
29979
|
+
{ phase: "core-node-resolution" }
|
|
29274
29980
|
);
|
|
29275
29981
|
}
|
|
29276
29982
|
const coreNode = parseCoreNode(outcome.receipt.result?.coreNode, coreNodeId);
|
|
@@ -29289,7 +29995,8 @@ function parseCoreNode(value, expectedId) {
|
|
|
29289
29995
|
"core-node-response-invalid",
|
|
29290
29996
|
`Core Node ${expectedId} returned an invalid record.`,
|
|
29291
29997
|
2,
|
|
29292
|
-
true
|
|
29998
|
+
true,
|
|
29999
|
+
{ phase: "core-node-resolution" }
|
|
29293
30000
|
);
|
|
29294
30001
|
}
|
|
29295
30002
|
const record2 = value;
|
|
@@ -29298,7 +30005,8 @@ function parseCoreNode(value, expectedId) {
|
|
|
29298
30005
|
"core-node-response-invalid",
|
|
29299
30006
|
`Core Node ${expectedId} returned an invalid record.`,
|
|
29300
30007
|
2,
|
|
29301
|
-
true
|
|
30008
|
+
true,
|
|
30009
|
+
{ phase: "core-node-resolution" }
|
|
29302
30010
|
);
|
|
29303
30011
|
}
|
|
29304
30012
|
return value;
|
|
@@ -29309,7 +30017,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29309
30017
|
"invalid-output-path",
|
|
29310
30018
|
"Download output path must not be blank.",
|
|
29311
30019
|
1,
|
|
29312
|
-
false
|
|
30020
|
+
false,
|
|
30021
|
+
{ phase: "output-setup" }
|
|
29313
30022
|
);
|
|
29314
30023
|
}
|
|
29315
30024
|
const absolutePath = path6.resolve(cwd, requestedPath);
|
|
@@ -29322,7 +30031,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29322
30031
|
"output-parent-unavailable",
|
|
29323
30032
|
"Download output parent directory does not exist or cannot be accessed.",
|
|
29324
30033
|
1,
|
|
29325
|
-
false
|
|
30034
|
+
false,
|
|
30035
|
+
{ phase: "output-setup" }
|
|
29326
30036
|
);
|
|
29327
30037
|
}
|
|
29328
30038
|
if (!parent.isDirectory()) {
|
|
@@ -29330,7 +30040,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29330
30040
|
"output-parent-not-directory",
|
|
29331
30041
|
"Download output parent is not a directory.",
|
|
29332
30042
|
1,
|
|
29333
|
-
false
|
|
30043
|
+
false,
|
|
30044
|
+
{ phase: "output-setup" }
|
|
29334
30045
|
);
|
|
29335
30046
|
}
|
|
29336
30047
|
let existing;
|
|
@@ -29342,7 +30053,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29342
30053
|
"output-target-unavailable",
|
|
29343
30054
|
"Download output target cannot be inspected.",
|
|
29344
30055
|
1,
|
|
29345
|
-
false
|
|
30056
|
+
false,
|
|
30057
|
+
{ phase: "output-setup" }
|
|
29346
30058
|
);
|
|
29347
30059
|
}
|
|
29348
30060
|
}
|
|
@@ -29351,7 +30063,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29351
30063
|
"output-target-exists",
|
|
29352
30064
|
"Download output target already exists; pass --overwrite to replace a regular file.",
|
|
29353
30065
|
1,
|
|
29354
|
-
false
|
|
30066
|
+
false,
|
|
30067
|
+
{ phase: "output-setup" }
|
|
29355
30068
|
);
|
|
29356
30069
|
}
|
|
29357
30070
|
if (existing && (!existing.isFile() || existing.isSymbolicLink())) {
|
|
@@ -29359,7 +30072,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
|
|
|
29359
30072
|
"output-target-not-regular-file",
|
|
29360
30073
|
"Download output target must be a regular file and must not be a symbolic link.",
|
|
29361
30074
|
1,
|
|
29362
|
-
false
|
|
30075
|
+
false,
|
|
30076
|
+
{ phase: "output-setup" }
|
|
29363
30077
|
);
|
|
29364
30078
|
}
|
|
29365
30079
|
return { absolutePath, existed: Boolean(existing), parentPath };
|
|
@@ -29370,9 +30084,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29370
30084
|
let response;
|
|
29371
30085
|
try {
|
|
29372
30086
|
response = await config2.fetch(media.url, { redirect: "follow", signal: deadline.signal });
|
|
29373
|
-
} catch {
|
|
30087
|
+
} catch (error51) {
|
|
29374
30088
|
deadline.cleanup();
|
|
29375
|
-
throw transferFailure(deadline);
|
|
30089
|
+
throw transferFailure(deadline, error51);
|
|
29376
30090
|
}
|
|
29377
30091
|
if (!response.ok) {
|
|
29378
30092
|
deadline.cleanup();
|
|
@@ -29381,7 +30095,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29381
30095
|
"media-http-error",
|
|
29382
30096
|
`Media server returned HTTP ${response.status}.`,
|
|
29383
30097
|
retryable ? 2 : 1,
|
|
29384
|
-
retryable
|
|
30098
|
+
retryable,
|
|
30099
|
+
{ phase: "media-transfer" }
|
|
29385
30100
|
);
|
|
29386
30101
|
}
|
|
29387
30102
|
const contentType = normalizeContentType(response.headers.get("content-type"));
|
|
@@ -29391,8 +30106,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29391
30106
|
throw new MediaDownloadError(
|
|
29392
30107
|
"media-content-type-mismatch",
|
|
29393
30108
|
`Media response is ${headerFamily}, but Core Node ${media.coreNodeId} is ${media.mediaType}.`,
|
|
29394
|
-
|
|
29395
|
-
|
|
30109
|
+
1,
|
|
30110
|
+
false,
|
|
30111
|
+
{ phase: "media-verification" }
|
|
29396
30112
|
);
|
|
29397
30113
|
}
|
|
29398
30114
|
if (contentType === "text/html" || contentType === "application/json") {
|
|
@@ -29400,8 +30116,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29400
30116
|
throw new MediaDownloadError(
|
|
29401
30117
|
"media-content-type-mismatch",
|
|
29402
30118
|
"Media response returned a non-media document.",
|
|
29403
|
-
|
|
29404
|
-
|
|
30119
|
+
1,
|
|
30120
|
+
false,
|
|
30121
|
+
{ phase: "media-verification" }
|
|
29405
30122
|
);
|
|
29406
30123
|
}
|
|
29407
30124
|
const remoteContentLength = readContentLength(response);
|
|
@@ -29434,7 +30151,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29434
30151
|
"output-finalize-failed",
|
|
29435
30152
|
"Temporary download file could not be finalized.",
|
|
29436
30153
|
1,
|
|
29437
|
-
false
|
|
30154
|
+
false,
|
|
30155
|
+
{ phase: "output-finalize" }
|
|
29438
30156
|
);
|
|
29439
30157
|
}
|
|
29440
30158
|
let detected;
|
|
@@ -29445,7 +30163,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29445
30163
|
"media-verification-failed",
|
|
29446
30164
|
"Downloaded media could not be inspected safely.",
|
|
29447
30165
|
1,
|
|
29448
|
-
false
|
|
30166
|
+
false,
|
|
30167
|
+
{ phase: "media-verification" }
|
|
29449
30168
|
);
|
|
29450
30169
|
}
|
|
29451
30170
|
validateDownloadedMedia(media, contentType, detected);
|
|
@@ -29467,12 +30186,13 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
|
|
|
29467
30186
|
} catch (error51) {
|
|
29468
30187
|
await temporary.handle.close().catch(() => void 0);
|
|
29469
30188
|
if (error51 instanceof MediaDownloadError) throw error51;
|
|
29470
|
-
if (deadline.signal.aborted) throw transferFailure(deadline);
|
|
30189
|
+
if (deadline.signal.aborted) throw transferFailure(deadline, error51);
|
|
29471
30190
|
throw new MediaDownloadError(
|
|
29472
30191
|
"media-download-internal-failed",
|
|
29473
30192
|
"Media download could not complete its local processing.",
|
|
29474
30193
|
2,
|
|
29475
|
-
true
|
|
30194
|
+
true,
|
|
30195
|
+
{ cause: sanitizeDownloadCause(error51), phase: "media-transfer" }
|
|
29476
30196
|
);
|
|
29477
30197
|
} finally {
|
|
29478
30198
|
deadline.cleanup();
|
|
@@ -29523,7 +30243,8 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
|
|
|
29523
30243
|
"media-response-body-missing",
|
|
29524
30244
|
"Media response has no readable body.",
|
|
29525
30245
|
2,
|
|
29526
|
-
true
|
|
30246
|
+
true,
|
|
30247
|
+
{ phase: "media-transfer" }
|
|
29527
30248
|
);
|
|
29528
30249
|
}
|
|
29529
30250
|
const reader = response.body.getReader();
|
|
@@ -29541,16 +30262,17 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
|
|
|
29541
30262
|
}
|
|
29542
30263
|
} catch (error51) {
|
|
29543
30264
|
if (error51 instanceof MediaDownloadError) throw error51;
|
|
29544
|
-
if (deadline.signal.aborted) throw transferFailure(deadline);
|
|
30265
|
+
if (deadline.signal.aborted) throw transferFailure(deadline, error51);
|
|
29545
30266
|
if (readFsCode2(error51)) {
|
|
29546
30267
|
throw new MediaDownloadError(
|
|
29547
30268
|
"output-write-failed",
|
|
29548
30269
|
"Temporary download file could not be written completely.",
|
|
29549
30270
|
1,
|
|
29550
|
-
false
|
|
30271
|
+
false,
|
|
30272
|
+
{ cause: sanitizeDownloadCause(error51), phase: "output-write" }
|
|
29551
30273
|
);
|
|
29552
30274
|
}
|
|
29553
|
-
throw transferFailure(deadline);
|
|
30275
|
+
throw transferFailure(deadline, error51);
|
|
29554
30276
|
} finally {
|
|
29555
30277
|
reader.releaseLock();
|
|
29556
30278
|
}
|
|
@@ -29559,7 +30281,8 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
|
|
|
29559
30281
|
"media-content-length-mismatch",
|
|
29560
30282
|
`Media response declared ${expectedBytes} bytes but delivered ${bytes}.`,
|
|
29561
30283
|
2,
|
|
29562
|
-
true
|
|
30284
|
+
true,
|
|
30285
|
+
{ phase: "media-transfer" }
|
|
29563
30286
|
);
|
|
29564
30287
|
}
|
|
29565
30288
|
return { bytes, sha256: hash2.digest("hex") };
|
|
@@ -29573,7 +30296,8 @@ async function writeAll(handle, value) {
|
|
|
29573
30296
|
"output-write-stalled",
|
|
29574
30297
|
"Temporary download file stopped accepting data.",
|
|
29575
30298
|
1,
|
|
29576
|
-
false
|
|
30299
|
+
false,
|
|
30300
|
+
{ phase: "output-write" }
|
|
29577
30301
|
);
|
|
29578
30302
|
}
|
|
29579
30303
|
offset += bytesWritten;
|
|
@@ -29585,8 +30309,9 @@ function validateDownloadedMedia(media, contentType, detected) {
|
|
|
29585
30309
|
throw new MediaDownloadError(
|
|
29586
30310
|
"media-content-mismatch",
|
|
29587
30311
|
`Downloaded file is ${detectedFamily}, but Core Node ${media.coreNodeId} is ${media.mediaType}.`,
|
|
29588
|
-
|
|
29589
|
-
|
|
30312
|
+
1,
|
|
30313
|
+
false,
|
|
30314
|
+
{ phase: "media-verification" }
|
|
29590
30315
|
);
|
|
29591
30316
|
}
|
|
29592
30317
|
const headerMatches = readMediaFamily(contentType) === media.mediaType;
|
|
@@ -29595,8 +30320,9 @@ function validateDownloadedMedia(media, contentType, detected) {
|
|
|
29595
30320
|
throw new MediaDownloadError(
|
|
29596
30321
|
"media-content-unverified",
|
|
29597
30322
|
`Downloaded file could not be verified as ${media.mediaType} media.`,
|
|
29598
|
-
|
|
29599
|
-
|
|
30323
|
+
1,
|
|
30324
|
+
false,
|
|
30325
|
+
{ phase: "media-verification" }
|
|
29600
30326
|
);
|
|
29601
30327
|
}
|
|
29602
30328
|
}
|
|
@@ -29614,14 +30340,16 @@ async function commitTemporaryFile(temporaryPath, outputTarget, overwrite) {
|
|
|
29614
30340
|
"output-target-exists",
|
|
29615
30341
|
"Download output target was created before the download could be committed.",
|
|
29616
30342
|
1,
|
|
29617
|
-
false
|
|
30343
|
+
false,
|
|
30344
|
+
{ phase: "output-commit" }
|
|
29618
30345
|
);
|
|
29619
30346
|
}
|
|
29620
30347
|
throw new MediaDownloadError(
|
|
29621
30348
|
"output-commit-failed",
|
|
29622
30349
|
"Verified media could not be committed to the output path.",
|
|
29623
30350
|
1,
|
|
29624
|
-
false
|
|
30351
|
+
false,
|
|
30352
|
+
{ cause: sanitizeDownloadCause(error51), phase: "output-commit" }
|
|
29625
30353
|
);
|
|
29626
30354
|
}
|
|
29627
30355
|
}
|
|
@@ -29645,18 +30373,29 @@ function createTransferSignal(external, timeoutMs) {
|
|
|
29645
30373
|
signal: controller.signal
|
|
29646
30374
|
};
|
|
29647
30375
|
}
|
|
29648
|
-
function transferFailure(deadline) {
|
|
30376
|
+
function transferFailure(deadline, cause) {
|
|
29649
30377
|
if (deadline.external?.aborted) {
|
|
29650
|
-
return new MediaDownloadError(
|
|
30378
|
+
return new MediaDownloadError(
|
|
30379
|
+
"download-cancelled",
|
|
30380
|
+
"Media download was cancelled.",
|
|
30381
|
+
130,
|
|
30382
|
+
true,
|
|
30383
|
+
{
|
|
30384
|
+
phase: "media-transfer"
|
|
30385
|
+
}
|
|
30386
|
+
);
|
|
29651
30387
|
}
|
|
29652
30388
|
if (deadline.isTimedOut()) {
|
|
29653
|
-
return new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true
|
|
30389
|
+
return new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
|
|
30390
|
+
phase: "media-transfer"
|
|
30391
|
+
});
|
|
29654
30392
|
}
|
|
29655
30393
|
return new MediaDownloadError(
|
|
29656
30394
|
"media-network-failed",
|
|
29657
30395
|
"Media response could not be downloaded completely.",
|
|
29658
30396
|
2,
|
|
29659
|
-
true
|
|
30397
|
+
true,
|
|
30398
|
+
{ cause: sanitizeDownloadCause(cause), phase: "media-transfer" }
|
|
29660
30399
|
);
|
|
29661
30400
|
}
|
|
29662
30401
|
function throwIfCancelled(signal) {
|
|
@@ -29685,23 +30424,111 @@ function readMediaFamily(value) {
|
|
|
29685
30424
|
if (value?.startsWith("video/")) return "video";
|
|
29686
30425
|
return null;
|
|
29687
30426
|
}
|
|
29688
|
-
function failureResult2(command, error51, absolutePath) {
|
|
29689
|
-
const failure =
|
|
30427
|
+
function failureResult2(command, error51, absolutePath, attemptsCompleted = 1, maximumAttempts = 1) {
|
|
30428
|
+
const failure = normalizeDownloadError(error51);
|
|
29690
30429
|
return {
|
|
29691
30430
|
exitCode: failure.exitCode,
|
|
29692
30431
|
output: {
|
|
29693
30432
|
error: {
|
|
29694
30433
|
code: failure.code,
|
|
29695
30434
|
message: safeCanvasMessage(failure),
|
|
29696
|
-
|
|
30435
|
+
...failure.details.phase ? { phase: failure.details.phase } : {},
|
|
30436
|
+
retryable: failure.retryable,
|
|
30437
|
+
...failure.details.cause ? { cause: failure.details.cause } : {}
|
|
29697
30438
|
},
|
|
29698
30439
|
format: "az8.canvas.media-download.v1",
|
|
30440
|
+
localState: {
|
|
30441
|
+
destinationCommitted: false,
|
|
30442
|
+
partialFileRetained: false
|
|
30443
|
+
},
|
|
29699
30444
|
outputPath: absolutePath ?? path6.resolve(command.outputPath),
|
|
29700
30445
|
projectId: command.projectId,
|
|
30446
|
+
attempts: { completed: attemptsCompleted, maximum: maximumAttempts },
|
|
30447
|
+
remoteState: "unverified",
|
|
29701
30448
|
source: command.source
|
|
29702
30449
|
}
|
|
29703
30450
|
};
|
|
29704
30451
|
}
|
|
30452
|
+
async function resolveMediaForAttempt(config2, command, dependencies) {
|
|
30453
|
+
const client = dependencies.mediaDownloadClientFactory?.(config2, command.projectId) ?? new CanvasClient(config2, command.projectId);
|
|
30454
|
+
try {
|
|
30455
|
+
try {
|
|
30456
|
+
await client.connect(normalizeCanvasClientName(command.clientName));
|
|
30457
|
+
} catch (error51) {
|
|
30458
|
+
throw new MediaDownloadError(
|
|
30459
|
+
"canvas-connection-failed",
|
|
30460
|
+
"Unable to establish a ready Project Canvas context.",
|
|
30461
|
+
2,
|
|
30462
|
+
true,
|
|
30463
|
+
{ cause: sanitizeDownloadCause(error51), phase: "canvas-connect" }
|
|
30464
|
+
);
|
|
30465
|
+
}
|
|
30466
|
+
throwIfCancelled(dependencies.signal);
|
|
30467
|
+
return await resolveMediaSource(client, command, dependencies.randomUUID);
|
|
30468
|
+
} finally {
|
|
30469
|
+
client.close();
|
|
30470
|
+
}
|
|
30471
|
+
}
|
|
30472
|
+
function normalizeDownloadError(error51) {
|
|
30473
|
+
return error51 instanceof MediaDownloadError ? error51 : new MediaDownloadError("media-download-failed", safeCanvasMessage(error51), 2, true, {
|
|
30474
|
+
cause: sanitizeDownloadCause(error51),
|
|
30475
|
+
phase: "unknown"
|
|
30476
|
+
});
|
|
30477
|
+
}
|
|
30478
|
+
function remainingDownloadMs(deadlineAt) {
|
|
30479
|
+
if (deadlineAt === null) return void 0;
|
|
30480
|
+
const remaining = deadlineAt - Date.now();
|
|
30481
|
+
if (remaining < 1) {
|
|
30482
|
+
throw new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
|
|
30483
|
+
phase: "retry-wait"
|
|
30484
|
+
});
|
|
30485
|
+
}
|
|
30486
|
+
return remaining;
|
|
30487
|
+
}
|
|
30488
|
+
async function waitForDownloadRetry(delayMs, deadlineAt, dependencies) {
|
|
30489
|
+
const remaining = remainingDownloadMs(deadlineAt);
|
|
30490
|
+
if (remaining !== void 0 && remaining <= delayMs) {
|
|
30491
|
+
throw new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
|
|
30492
|
+
phase: "retry-wait"
|
|
30493
|
+
});
|
|
30494
|
+
}
|
|
30495
|
+
await (dependencies.sleep ?? abortableSleep)(delayMs, dependencies.signal);
|
|
30496
|
+
throwIfCancelled(dependencies.signal);
|
|
30497
|
+
}
|
|
30498
|
+
async function abortableSleep(delayMs, signal) {
|
|
30499
|
+
if (signal?.aborted) return;
|
|
30500
|
+
await new Promise((resolve) => {
|
|
30501
|
+
const timeout = setTimeout(done, delayMs);
|
|
30502
|
+
const onAbort = () => done();
|
|
30503
|
+
function done() {
|
|
30504
|
+
clearTimeout(timeout);
|
|
30505
|
+
signal?.removeEventListener("abort", onAbort);
|
|
30506
|
+
resolve();
|
|
30507
|
+
}
|
|
30508
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
30509
|
+
});
|
|
30510
|
+
}
|
|
30511
|
+
function sanitizeDownloadCause(error51) {
|
|
30512
|
+
if (!error51 || typeof error51 !== "object") return void 0;
|
|
30513
|
+
const record2 = error51;
|
|
30514
|
+
const cause = record2.cause && typeof record2.cause === "object" ? record2.cause : record2;
|
|
30515
|
+
const message = cause instanceof Error ? cause.message : typeof cause.message === "string" ? cause.message : error51 instanceof Error ? error51.message : void 0;
|
|
30516
|
+
const code = typeof cause.code === "string" || typeof cause.code === "number" ? String(cause.code) : void 0;
|
|
30517
|
+
const closeCode = typeof cause.closeCode === "number" ? cause.closeCode : typeof cause.websocketCloseCode === "number" ? cause.websocketCloseCode : void 0;
|
|
30518
|
+
const closeReason = typeof cause.closeReason === "string" ? cause.closeReason : typeof cause.websocketCloseReason === "string" ? cause.websocketCloseReason : void 0;
|
|
30519
|
+
const sanitizedMessage = message ? sanitizeTransportDetail(message) : void 0;
|
|
30520
|
+
const sanitizedReason = closeReason ? sanitizeTransportDetail(closeReason) : void 0;
|
|
30521
|
+
if (!code && !closeCode && !sanitizedMessage && !sanitizedReason) return void 0;
|
|
30522
|
+
return {
|
|
30523
|
+
...code ? { code } : {},
|
|
30524
|
+
...sanitizedMessage ? { message: sanitizedMessage } : {},
|
|
30525
|
+
...closeCode ? { websocketCloseCode: closeCode } : {},
|
|
30526
|
+
...sanitizedReason ? { websocketCloseReason: sanitizedReason } : {}
|
|
30527
|
+
};
|
|
30528
|
+
}
|
|
30529
|
+
function sanitizeTransportDetail(value) {
|
|
30530
|
+
return value.replace(/token=[^\s;&]+/gi, "token=[REDACTED]").replace(/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, "[REDACTED]").replace(/(?:https?|wss?):\/\/[^\s]+/gi, "[REDACTED_URL]");
|
|
30531
|
+
}
|
|
29705
30532
|
function readFsCode2(error51) {
|
|
29706
30533
|
return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : void 0;
|
|
29707
30534
|
}
|
|
@@ -44620,7 +45447,7 @@ function registerCanvasCommands(program2, dependencies, setResult) {
|
|
|
44620
45447
|
// package.json
|
|
44621
45448
|
var package_default = {
|
|
44622
45449
|
name: "@everfir/az8-cli",
|
|
44623
|
-
version: "0.3.0-preview.
|
|
45450
|
+
version: "0.3.0-preview.3",
|
|
44624
45451
|
description: "Semantic Project Canvas client for AZ8 Studio agents.",
|
|
44625
45452
|
license: "UNLICENSED",
|
|
44626
45453
|
type: "module",
|
|
@@ -44983,7 +45810,8 @@ var AgentSessionManager = class {
|
|
|
44983
45810
|
descriptor2.socketPath,
|
|
44984
45811
|
request,
|
|
44985
45812
|
options.timeoutMs ?? 5e3,
|
|
44986
|
-
options.signal
|
|
45813
|
+
options.signal,
|
|
45814
|
+
options.onProgress
|
|
44987
45815
|
).catch((error51) => {
|
|
44988
45816
|
if (isRemoteFailure(error51)) throw error51;
|
|
44989
45817
|
throw managerError(
|
|
@@ -45351,7 +46179,11 @@ function registerSessionCommands(program2, dependencies, setResult) {
|
|
|
45351
46179
|
requestId,
|
|
45352
46180
|
...options.timeoutMs ? { timeoutMs: options.timeoutMs } : {}
|
|
45353
46181
|
},
|
|
45354
|
-
{
|
|
46182
|
+
{
|
|
46183
|
+
onProgress: (progress) => dependencies.onProgress?.({ ...progress, requestId }),
|
|
46184
|
+
sessionId: options.session,
|
|
46185
|
+
timeoutMs: (options.timeoutMs ?? 15e3) + 5e3
|
|
46186
|
+
}
|
|
45355
46187
|
);
|
|
45356
46188
|
const receipt = readOperationReceipt(value);
|
|
45357
46189
|
const exitCode = receipt.status === "succeeded" ? 0 : receipt.error?.outcome === "indeterminate" ? 2 : 1;
|
|
@@ -45555,6 +46387,12 @@ process.once("SIGINT", abortOnSignal);
|
|
|
45555
46387
|
process.once("SIGTERM", abortOnSignal);
|
|
45556
46388
|
try {
|
|
45557
46389
|
const result3 = await runAz8Cli(process.argv.slice(2), {
|
|
46390
|
+
onProgress(progress) {
|
|
46391
|
+
process.stderr.write(
|
|
46392
|
+
`${JSON.stringify({ format: "az8.operation-progress.v1", ...progress })}
|
|
46393
|
+
`
|
|
46394
|
+
);
|
|
46395
|
+
},
|
|
45558
46396
|
persistentCanvas: runPersistentCanvas,
|
|
45559
46397
|
signal: abortController.signal
|
|
45560
46398
|
});
|