@everfir/az8-cli 0.3.0-preview.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -2387,7 +2387,7 @@ var require_websocket = __commonJS({
2387
2387
  var http = __require("http");
2388
2388
  var net2 = __require("net");
2389
2389
  var tls = __require("tls");
2390
- var { randomBytes, createHash: createHash4 } = __require("crypto");
2390
+ var { randomBytes, createHash: createHash5 } = __require("crypto");
2391
2391
  var { Duplex, Readable } = __require("stream");
2392
2392
  var { URL: URL2 } = __require("url");
2393
2393
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -3047,7 +3047,7 @@ var require_websocket = __commonJS({
3047
3047
  abortHandshake(websocket, socket, "Invalid Upgrade header");
3048
3048
  return;
3049
3049
  }
3050
- const digest = createHash4("sha1").update(key + GUID).digest("base64");
3050
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
3051
3051
  if (res.headers["sec-websocket-accept"] !== digest) {
3052
3052
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
3053
3053
  return;
@@ -3414,7 +3414,7 @@ var require_websocket_server = __commonJS({
3414
3414
  var EventEmitter3 = __require("events");
3415
3415
  var http = __require("http");
3416
3416
  var { Duplex } = __require("stream");
3417
- var { createHash: createHash4 } = __require("crypto");
3417
+ var { createHash: createHash5 } = __require("crypto");
3418
3418
  var extension2 = require_extension();
3419
3419
  var PerMessageDeflate2 = require_permessage_deflate();
3420
3420
  var subprotocol2 = require_subprotocol();
@@ -3715,7 +3715,7 @@ var require_websocket_server = __commonJS({
3715
3715
  );
3716
3716
  }
3717
3717
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3718
- const digest = createHash4("sha1").update(key + GUID).digest("base64");
3718
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
3719
3719
  const headers = [
3720
3720
  "HTTP/1.1 101 Switching Protocols",
3721
3721
  "Upgrade: websocket",
@@ -4822,6 +4822,58 @@ function normalize(value) {
4822
4822
  return (value ?? "").trim().toLowerCase().replaceAll(/[\s_]+/g, "-");
4823
4823
  }
4824
4824
 
4825
+ // ../../packages/project-canvas-runtime/dist/generation/workflow-access.js
4826
+ function resolveWorkflowDefinitionAccess(definition, accessTier) {
4827
+ if (accessTier === "paid") {
4828
+ return { accessReason: "paid-account", availableForAccount: true };
4829
+ }
4830
+ if (normalize2(getInternalTag(definition, "available")) === "free") {
4831
+ return { accessReason: "free-tier-eligible", availableForAccount: true };
4832
+ }
4833
+ return { accessReason: "requires-paid-account", availableForAccount: false };
4834
+ }
4835
+ function isWorkflowDefinitionAvailable(definition, accessTier) {
4836
+ return resolveWorkflowDefinitionAccess(definition, accessTier).availableForAccount;
4837
+ }
4838
+ function normalize2(value) {
4839
+ return (value ?? "").trim().toLowerCase().replaceAll(/[\s_]+/g, "-");
4840
+ }
4841
+
4842
+ // ../../packages/project-canvas-runtime/dist/generation/workflow-default.js
4843
+ function selectDefaultWorkflowDefinition(input) {
4844
+ const compatible = input.definitions.filter((definition) => isDefaultCompatible(definition, input.contentType));
4845
+ const preferred = input.preference && compatible.find((definition) => definition.id === input.preference?.definitionId);
4846
+ if (preferred) {
4847
+ return { definition: preferred, preference: input.preference, source: "preference" };
4848
+ }
4849
+ const fallback = compatible[0] ?? null;
4850
+ return {
4851
+ definition: fallback,
4852
+ preference: null,
4853
+ source: fallback ? "fallback" : "none"
4854
+ };
4855
+ }
4856
+ function isDefaultCompatible(definition, contentType) {
4857
+ const metadata = resolveWorkflowDefinitionMetadata(definition);
4858
+ if (metadata.target !== contentType)
4859
+ return false;
4860
+ if (!definition.outputSchema.some((output) => output.dataType === contentType))
4861
+ return false;
4862
+ if (getInternalTag(definition, "function") && !getInternalTag(definition, "capability")) {
4863
+ return false;
4864
+ }
4865
+ return definition.inputSchema.some((slot) => !isConfigSlot(slot));
4866
+ }
4867
+
4868
+ // ../../packages/project-canvas-runtime/dist/generation/workflow-target.js
4869
+ function resolveWorkflowTargetFromSnapshot(snapshot, workflowRunId) {
4870
+ const targetIds = new Set(snapshot.viewNodes.flatMap((viewNode) => readGenerationHistory(viewNode.history).some((entry) => entry.runtime.instanceIds.includes(workflowRunId)) ? [viewNode.id] : []));
4871
+ if (targetIds.size === 1) {
4872
+ return { status: "resolved", viewNodeId: [...targetIds][0] };
4873
+ }
4874
+ return { status: targetIds.size > 1 ? "ambiguous" : "unresolved" };
4875
+ }
4876
+
4825
4877
  // ../../node_modules/.pnpm/lib0@0.2.117/node_modules/lib0/math.js
4826
4878
  var floor = Math.floor;
4827
4879
  var abs = Math.abs;
@@ -8238,10 +8290,10 @@ var YEvent = class {
8238
8290
  }
8239
8291
  };
8240
8292
  var getPathTo = (parent, child) => {
8241
- const path9 = [];
8293
+ const path10 = [];
8242
8294
  while (child._item !== null && child !== parent) {
8243
8295
  if (child._item.parentSub !== null) {
8244
- path9.unshift(child._item.parentSub);
8296
+ path10.unshift(child._item.parentSub);
8245
8297
  } else {
8246
8298
  let i = 0;
8247
8299
  let c = (
@@ -8254,12 +8306,12 @@ var getPathTo = (parent, child) => {
8254
8306
  }
8255
8307
  c = c.right;
8256
8308
  }
8257
- path9.unshift(i);
8309
+ path10.unshift(i);
8258
8310
  }
8259
8311
  child = /** @type {AbstractType<any>} */
8260
8312
  child._item.parent;
8261
8313
  }
8262
- return path9;
8314
+ return path10;
8263
8315
  };
8264
8316
  var warnPrematureAccess = () => {
8265
8317
  warn("Invalid access: Add Yjs type to a document before reading data.");
@@ -14095,7 +14147,7 @@ var CANVAS_OPERATION_REGISTRY = [
14095
14147
  },
14096
14148
  required: ["contentType"]
14097
14149
  }),
14098
- write2("createGenerationTarget", "Create a blank audio, image, text, or video target", "undoable", {
14150
+ write2("createGenerationTarget", "Create an audio, image, text, or video target with an eligible default Draft when available", "undoable", {
14099
14151
  properties: {
14100
14152
  contentType: { enum: ["audio", "image", "text", "video"], type: "string" },
14101
14153
  name: stringSchema(),
@@ -14159,7 +14211,7 @@ var CANVAS_OPERATION_REGISTRY = [
14159
14211
  },
14160
14212
  required: ["idempotencyKey", "source"]
14161
14213
  }),
14162
- write2("editGenerationDraft", "Edit semantic prompt, config, or Workflow Definition", "silent", {
14214
+ write2("editGenerationDraft", "Edit prompt, config, or Workflow Definition, resolving an eligible default for a null Draft", "silent", {
14163
14215
  properties: {
14164
14216
  config: { additionalProperties: { type: ["boolean", "number", "string"] }, type: "object" },
14165
14217
  definitionId: stringSchema(),
@@ -14177,7 +14229,28 @@ var CANVAS_OPERATION_REGISTRY = [
14177
14229
  }),
14178
14230
  write2("insertPromptReference", "Bind one visible image, text, or video View Node and place its platform At token in the Prompt", "silent", {
14179
14231
  properties: {
14180
- placement: { enum: ["start", "end"], type: "string" },
14232
+ placement: {
14233
+ anyOf: [
14234
+ { enum: ["start", "end"], type: "string" },
14235
+ {
14236
+ additionalProperties: false,
14237
+ properties: {
14238
+ anchor: {
14239
+ additionalProperties: false,
14240
+ properties: {
14241
+ occurrence: { minimum: 1, type: "integer" },
14242
+ text: stringSchema()
14243
+ },
14244
+ required: ["text"],
14245
+ type: "object"
14246
+ },
14247
+ kind: { enum: ["before", "after"], type: "string" }
14248
+ },
14249
+ required: ["anchor", "kind"],
14250
+ type: "object"
14251
+ }
14252
+ ]
14253
+ },
14181
14254
  referenceViewNodeId: stringSchema(),
14182
14255
  viewNodeId: stringSchema()
14183
14256
  },
@@ -14997,7 +15070,156 @@ function isConfigValueValid(value, property, normalizedEnum = normalizeConfigEnu
14997
15070
  return true;
14998
15071
  }
14999
15072
 
15073
+ // ../../packages/project-canvas-runtime/dist/generation/planning-contract.js
15074
+ function generationPlanningIssue(code, message, slotName, field) {
15075
+ return {
15076
+ code,
15077
+ message,
15078
+ severity: "error",
15079
+ ...slotName ? { slotName } : {},
15080
+ ...field ? { field } : {}
15081
+ };
15082
+ }
15083
+
15084
+ // ../../packages/project-canvas-runtime/dist/generation/planning-config.js
15085
+ function createGenerationConfigDefaults(definition, preferred) {
15086
+ const slots = generationConfigSlots(definition);
15087
+ if (slots.length === 0)
15088
+ return null;
15089
+ if (slots.length !== 1)
15090
+ return null;
15091
+ const slot = slots[0];
15092
+ const properties = slot.jsonSchema?.properties ?? {};
15093
+ const slotDefaults = readConfigDefaults(slot.defaultValue);
15094
+ return Object.fromEntries(Object.entries(properties).map(([name, property]) => {
15095
+ const preferredValue = preferred?.[name];
15096
+ if (preferredValue !== void 0 && isConfigValueValid(preferredValue, property)) {
15097
+ return [name, preferredValue];
15098
+ }
15099
+ const slotDefault = normalizeSchemaConfigValue(slotDefaults[name], property.type);
15100
+ return [
15101
+ name,
15102
+ slotDefault !== void 0 && isConfigValueValid(slotDefault, property) ? slotDefault : configFallback(property, normalizeConfigEnum(property))
15103
+ ];
15104
+ }));
15105
+ }
15106
+ async function createGenerationConfigPlan(definition, requested, runtime) {
15107
+ const slots = generationConfigSlots(definition);
15108
+ if (slots.length === 0) {
15109
+ return {
15110
+ fields: [],
15111
+ issues: requested ? [generationPlanningIssue("config-not-supported", "This Workflow Definition does not accept configuration.")] : [],
15112
+ values: null
15113
+ };
15114
+ }
15115
+ if (slots.length !== 1) {
15116
+ return {
15117
+ fields: [],
15118
+ issues: [
15119
+ generationPlanningIssue("multiple-config-slots-unsupported", "Generation Planning currently requires exactly one configuration slot.")
15120
+ ],
15121
+ values: null
15122
+ };
15123
+ }
15124
+ const slot = slots[0];
15125
+ const properties = slot.jsonSchema?.properties ?? {};
15126
+ const slotDefaults = readConfigDefaults(slot.defaultValue);
15127
+ const values = {};
15128
+ const issues = [];
15129
+ for (const name of Object.keys(requested ?? {})) {
15130
+ if (!properties[name]) {
15131
+ issues.push(generationPlanningIssue("config-field-unknown", `Unknown configuration field: ${name}.`, slot.name, name));
15132
+ }
15133
+ }
15134
+ const fields = await Promise.all(Object.entries(properties).map(async ([name, property]) => {
15135
+ const normalizedEnum = normalizeConfigEnum(property);
15136
+ if (normalizedEnum?.invalidValues.length) {
15137
+ issues.push(generationPlanningIssue("config-enum-invalid", `Configuration ${name} contains enum values that do not match ${property.type}.`, slot.name, name));
15138
+ }
15139
+ const requestedValue = Object.hasOwn(requested ?? {}, name) ? requested?.[name] : normalizeSchemaConfigValue(slotDefaults[name], property.type);
15140
+ const fallback = configFallback(property, normalizedEnum);
15141
+ if (requestedValue !== void 0 && !isConfigValueValid(requestedValue, property)) {
15142
+ issues.push(generationPlanningIssue("config-value-invalid", `Configuration ${name} is outside its allowed values.`, slot.name, name));
15143
+ }
15144
+ const value = requestedValue !== void 0 && isConfigValueValid(requestedValue, property) ? requestedValue : fallback;
15145
+ values[name] = value;
15146
+ const dynamicOptions = await readDynamicOptions(runtime, name, property, value, issues, slot.name);
15147
+ return {
15148
+ description: property.description ?? "",
15149
+ name,
15150
+ required: slot.jsonSchema?.required?.includes(name) ?? false,
15151
+ type: property.type,
15152
+ value,
15153
+ ...property.maximum === void 0 ? {} : { maximum: property.maximum },
15154
+ ...property.minimum === void 0 ? {} : { minimum: property.minimum },
15155
+ ...dynamicOptions ?? normalizedEnum?.options ? { options: dynamicOptions ?? normalizedEnum?.options } : {}
15156
+ };
15157
+ }));
15158
+ for (const name of slot.jsonSchema?.required ?? []) {
15159
+ if (values[name] === "") {
15160
+ issues.push(generationPlanningIssue("config-field-required", `Configuration ${name} requires a value.`, slot.name, name));
15161
+ }
15162
+ }
15163
+ return { fields, issues, values };
15164
+ }
15165
+ function readConfigDefaults(value) {
15166
+ if (!value || typeof value !== "object" || Array.isArray(value))
15167
+ return {};
15168
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "boolean" || typeof entry[1] === "string" || typeof entry[1] === "number" && Number.isFinite(entry[1])));
15169
+ }
15170
+ async function readDynamicOptions(runtime, name, property, current, issues, slotName) {
15171
+ const source = property.dynamicEnum;
15172
+ if (property.type !== "string" || !source?.modelKey || !runtime.getDynamicEnumValues)
15173
+ return null;
15174
+ try {
15175
+ const remote = await runtime.getDynamicEnumValues({
15176
+ modelKey: source.modelKey,
15177
+ paramName: name,
15178
+ provider: source.provider
15179
+ });
15180
+ const values = typeof current === "string" && current.trim() ? [current] : [];
15181
+ for (const item of remote)
15182
+ if (!values.includes(item))
15183
+ values.push(item);
15184
+ return values;
15185
+ } catch {
15186
+ issues.push({
15187
+ code: "dynamic-options-unavailable",
15188
+ field: name,
15189
+ message: `Dynamic options for configuration ${name} are temporarily unavailable.`,
15190
+ severity: "warning",
15191
+ slotName
15192
+ });
15193
+ return null;
15194
+ }
15195
+ }
15196
+ function configFallback(property, normalizedEnum) {
15197
+ const normalizedDefault = normalizeSchemaConfigValue(property.default, property.type);
15198
+ if (normalizedDefault !== void 0 && isConfigValueValid(normalizedDefault, property)) {
15199
+ return normalizedDefault;
15200
+ }
15201
+ const first = normalizedEnum?.options[0];
15202
+ if (first !== void 0)
15203
+ return first;
15204
+ if (property.type === "boolean")
15205
+ return false;
15206
+ if (property.type === "number") {
15207
+ return property.minimum ?? (property.maximum && property.maximum < 0 ? property.maximum : 0);
15208
+ }
15209
+ return "";
15210
+ }
15211
+ function generationConfigSlots(definition) {
15212
+ return definition.inputSchema.filter((slot) => slot.dataType === "json" && (slot.required || isConfigSlot(slot)));
15213
+ }
15214
+
15000
15215
  // ../../packages/project-canvas-runtime/dist/generation/draft-edit.js
15216
+ function createInitialGenerationDraft(definition, preferredConfig, now) {
15217
+ const config2 = createGenerationConfigDefaults(definition, preferredConfig);
15218
+ return editGenerationDraft(null, definition, {
15219
+ definitionId: definition.id,
15220
+ ...config2 ? { config: config2 } : {}
15221
+ }, now);
15222
+ }
15001
15223
  function editGenerationDraft(current, definition, input, now) {
15002
15224
  const changedDefinition = current?.definitionId !== definition.id;
15003
15225
  const draft = !current || changedDefinition ? {
@@ -15068,7 +15290,7 @@ function insertPromptReferenceIntoDraft(draft, definition, reference, sourceData
15068
15290
  const prompt = promptInput.content;
15069
15291
  const token = `{{@${binding.value.sourceDataType}:${binding.slotName}:${binding.index}:${reference.id}|view-node|${encodeURIComponent(reference.viewNodeId)}}}`;
15070
15292
  const content = placement === "start" ? prompt.trim() ? `${token}
15071
- ${prompt}` : token : prompt.trim() ? `${prompt}${prompt.endsWith("\n") ? "" : "\n"}${token}` : token;
15293
+ ${prompt}` : token : placement === "end" ? prompt.trim() ? `${prompt}${prompt.endsWith("\n") ? "" : "\n"}${token}` : token : insertAtPromptAnchor(prompt, token, placement);
15072
15294
  return {
15073
15295
  ...structuredClone(withoutExistingToken),
15074
15296
  inputs: {
@@ -15129,23 +15351,72 @@ function appendPromptReferenceToken(draft, slotName, referenceId, viewNodeId, no
15129
15351
  };
15130
15352
  }
15131
15353
  function removePromptReferenceToken(draft, referenceId) {
15132
- const pattern = new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN, "g");
15133
15354
  const slots = Object.fromEntries(Object.entries(draft.inputs.slots).map(([slotName, values]) => [
15134
15355
  slotName,
15135
15356
  values.map((value) => {
15136
15357
  if (value.kind !== "literal" || value.assetType !== "text")
15137
15358
  return value;
15138
- const lines = value.content.split("\n").flatMap((line) => {
15139
- const content = line.replace(pattern, (token, _type, _slot, _index, bindingId) => bindingId === referenceId ? "" : token);
15140
- return content.trim() ? [content] : [];
15141
- });
15142
- return { ...value, content: lines.join("\n") };
15359
+ return { ...value, content: removeReferenceTokenFromContent(value.content, referenceId) };
15143
15360
  })
15144
15361
  ]));
15145
15362
  return { ...draft, inputs: { slots } };
15146
15363
  }
15364
+ function insertAtPromptAnchor(prompt, token, placement) {
15365
+ const anchor = placement.anchor.text;
15366
+ if (!anchor.trim())
15367
+ throw new Error("placement.anchor.text must be non-empty.");
15368
+ const indexes = [];
15369
+ let from2 = 0;
15370
+ while (from2 <= prompt.length - anchor.length) {
15371
+ const index2 = prompt.indexOf(anchor, from2);
15372
+ if (index2 < 0)
15373
+ break;
15374
+ indexes.push(index2);
15375
+ from2 = index2 + Math.max(anchor.length, 1);
15376
+ }
15377
+ if (indexes.length === 0) {
15378
+ throw new Error(`Prompt anchor ${JSON.stringify(anchor)} does not exist.`);
15379
+ }
15380
+ const occurrence = placement.anchor.occurrence;
15381
+ if (occurrence === void 0 && indexes.length !== 1) {
15382
+ throw new Error(`Prompt anchor ${JSON.stringify(anchor)} is ambiguous; placement.anchor.occurrence is required.`);
15383
+ }
15384
+ const index = indexes[(occurrence ?? 1) - 1];
15385
+ if (index === void 0) {
15386
+ throw new Error(`Prompt anchor ${JSON.stringify(anchor)} does not have occurrence ${String(occurrence)}.`);
15387
+ }
15388
+ const insertionIndex = placement.kind === "before" ? index : index + anchor.length;
15389
+ return `${prompt.slice(0, insertionIndex)}${token}${prompt.slice(insertionIndex)}`;
15390
+ }
15391
+ function removeReferenceTokenFromContent(content, referenceId) {
15392
+ const matches2 = [...content.matchAll(new RegExp(PROMPT_REFERENCE_TOKEN_PATTERN, "g"))].filter((match) => match[4] === referenceId);
15393
+ let next = content;
15394
+ for (const match of matches2.reverse()) {
15395
+ let start = match.index;
15396
+ let end = start + match[0].length;
15397
+ if (start === 0 && next[end] === "\n")
15398
+ end += 1;
15399
+ else if (end === next.length && next[start - 1] === "\n")
15400
+ start -= 1;
15401
+ next = `${next.slice(0, start)}${next.slice(end)}`;
15402
+ }
15403
+ return next;
15404
+ }
15147
15405
  function createDefaultSlots(definition) {
15406
+ const promptSlot = findPromptSlot(definition);
15407
+ const config2 = createGenerationConfigDefaults(definition);
15408
+ const configSlots = definition.inputSchema.filter((slot) => slot.dataType === "json");
15409
+ const configSlot = configSlots.length === 1 ? configSlots[0] : void 0;
15148
15410
  return Object.fromEntries(definition.inputSchema.map((slot) => {
15411
+ if (slot === promptSlot) {
15412
+ return [slot.name, [literal({ id: definition.id }, slot.name, "text", "")]];
15413
+ }
15414
+ if (slot === configSlot && config2) {
15415
+ return [
15416
+ slot.name,
15417
+ [literal({ id: definition.id }, slot.name, "json", JSON.stringify(config2))]
15418
+ ];
15419
+ }
15149
15420
  if (slot.defaultValue === void 0)
15150
15421
  return [slot.name, []];
15151
15422
  const content = slot.dataType === "json" ? JSON.stringify(slot.defaultValue) : String(slot.defaultValue);
@@ -15172,11 +15443,14 @@ function requireSingleSlot(definition, dataType, purpose) {
15172
15443
  return slots[0];
15173
15444
  }
15174
15445
  function requirePromptSlot(definition) {
15175
- const slot = definition.inputSchema.find((candidate) => candidate.name === "prompt" && candidate.dataType === "text") ?? definition.inputSchema.find((candidate) => candidate.required && candidate.dataType === "text") ?? definition.inputSchema.find((candidate) => candidate.dataType === "text");
15446
+ const slot = findPromptSlot(definition);
15176
15447
  if (!slot)
15177
15448
  throw new Error("Workflow Definition must expose a prompt slot.");
15178
15449
  return slot;
15179
15450
  }
15451
+ function findPromptSlot(definition) {
15452
+ return definition.inputSchema.find((candidate) => candidate.name === "prompt" && candidate.dataType === "text") ?? definition.inputSchema.find((candidate) => candidate.required && candidate.dataType === "text") ?? definition.inputSchema.find((candidate) => candidate.dataType === "text");
15453
+ }
15180
15454
  function validateConfig(slot, config2) {
15181
15455
  const schema = slot.jsonSchema;
15182
15456
  if (!schema)
@@ -15248,124 +15522,6 @@ function requirePrimaryOutput(definition, contentType) {
15248
15522
  return output;
15249
15523
  }
15250
15524
 
15251
- // ../../packages/project-canvas-runtime/dist/generation/planning-contract.js
15252
- function generationPlanningIssue(code, message, slotName, field) {
15253
- return {
15254
- code,
15255
- message,
15256
- severity: "error",
15257
- ...slotName ? { slotName } : {},
15258
- ...field ? { field } : {}
15259
- };
15260
- }
15261
-
15262
- // ../../packages/project-canvas-runtime/dist/generation/planning-config.js
15263
- async function createGenerationConfigPlan(definition, requested, runtime) {
15264
- const slots = definition.inputSchema.filter((slot2) => slot2.dataType === "json" && (slot2.required || isConfigSlot(slot2)));
15265
- if (slots.length === 0) {
15266
- return {
15267
- fields: [],
15268
- issues: requested ? [generationPlanningIssue("config-not-supported", "This Workflow Definition does not accept configuration.")] : [],
15269
- values: null
15270
- };
15271
- }
15272
- if (slots.length !== 1) {
15273
- return {
15274
- fields: [],
15275
- issues: [
15276
- generationPlanningIssue("multiple-config-slots-unsupported", "Generation Planning currently requires exactly one configuration slot.")
15277
- ],
15278
- values: null
15279
- };
15280
- }
15281
- const slot = slots[0];
15282
- const properties = slot.jsonSchema?.properties ?? {};
15283
- const slotDefaults = readConfigDefaults(slot.defaultValue);
15284
- const values = {};
15285
- const issues = [];
15286
- for (const name of Object.keys(requested ?? {})) {
15287
- if (!properties[name]) {
15288
- issues.push(generationPlanningIssue("config-field-unknown", `Unknown configuration field: ${name}.`, slot.name, name));
15289
- }
15290
- }
15291
- const fields = await Promise.all(Object.entries(properties).map(async ([name, property]) => {
15292
- const normalizedEnum = normalizeConfigEnum(property);
15293
- if (normalizedEnum?.invalidValues.length) {
15294
- issues.push(generationPlanningIssue("config-enum-invalid", `Configuration ${name} contains enum values that do not match ${property.type}.`, slot.name, name));
15295
- }
15296
- const requestedValue = Object.hasOwn(requested ?? {}, name) ? requested?.[name] : normalizeSchemaConfigValue(slotDefaults[name], property.type);
15297
- const fallback = configFallback(property, normalizedEnum);
15298
- if (requestedValue !== void 0 && !isConfigValueValid(requestedValue, property)) {
15299
- issues.push(generationPlanningIssue("config-value-invalid", `Configuration ${name} is outside its allowed values.`, slot.name, name));
15300
- }
15301
- const value = requestedValue !== void 0 && isConfigValueValid(requestedValue, property) ? requestedValue : fallback;
15302
- values[name] = value;
15303
- const dynamicOptions = await readDynamicOptions(runtime, name, property, value, issues, slot.name);
15304
- return {
15305
- description: property.description ?? "",
15306
- name,
15307
- required: slot.jsonSchema?.required?.includes(name) ?? false,
15308
- type: property.type,
15309
- value,
15310
- ...property.maximum === void 0 ? {} : { maximum: property.maximum },
15311
- ...property.minimum === void 0 ? {} : { minimum: property.minimum },
15312
- ...dynamicOptions ?? normalizedEnum?.options ? { options: dynamicOptions ?? normalizedEnum?.options } : {}
15313
- };
15314
- }));
15315
- for (const name of slot.jsonSchema?.required ?? []) {
15316
- if (values[name] === "") {
15317
- issues.push(generationPlanningIssue("config-field-required", `Configuration ${name} requires a value.`, slot.name, name));
15318
- }
15319
- }
15320
- return { fields, issues, values };
15321
- }
15322
- function readConfigDefaults(value) {
15323
- if (!value || typeof value !== "object" || Array.isArray(value))
15324
- return {};
15325
- return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "boolean" || typeof entry[1] === "string" || typeof entry[1] === "number" && Number.isFinite(entry[1])));
15326
- }
15327
- async function readDynamicOptions(runtime, name, property, current, issues, slotName) {
15328
- const source = property.dynamicEnum;
15329
- if (property.type !== "string" || !source?.modelKey || !runtime.getDynamicEnumValues)
15330
- return null;
15331
- try {
15332
- const remote = await runtime.getDynamicEnumValues({
15333
- modelKey: source.modelKey,
15334
- paramName: name,
15335
- provider: source.provider
15336
- });
15337
- const values = typeof current === "string" && current.trim() ? [current] : [];
15338
- for (const item of remote)
15339
- if (!values.includes(item))
15340
- values.push(item);
15341
- return values;
15342
- } catch {
15343
- issues.push({
15344
- code: "dynamic-options-unavailable",
15345
- field: name,
15346
- message: `Dynamic options for configuration ${name} are temporarily unavailable.`,
15347
- severity: "warning",
15348
- slotName
15349
- });
15350
- return null;
15351
- }
15352
- }
15353
- function configFallback(property, normalizedEnum) {
15354
- const normalizedDefault = normalizeSchemaConfigValue(property.default, property.type);
15355
- if (normalizedDefault !== void 0 && isConfigValueValid(normalizedDefault, property)) {
15356
- return normalizedDefault;
15357
- }
15358
- const first = normalizedEnum?.options[0];
15359
- if (first !== void 0)
15360
- return first;
15361
- if (property.type === "boolean")
15362
- return false;
15363
- if (property.type === "number") {
15364
- return property.minimum ?? (property.maximum && property.maximum < 0 ? property.maximum : 0);
15365
- }
15366
- return "";
15367
- }
15368
-
15369
15525
  // ../../packages/project-canvas-runtime/dist/generation/planning-execution.js
15370
15526
  function createGenerationExecutionOperations(input) {
15371
15527
  const operations = [];
@@ -15374,10 +15530,10 @@ function createGenerationExecutionOperations(input) {
15374
15530
  definitionId: input.definition.id,
15375
15531
  viewNodeId: input.targetViewNodeId
15376
15532
  };
15377
- if (resolvePromptSlot(input.definition) && input.prompt !== readGenerationDraftPrompt(input.currentDraft)) {
15533
+ if (resolvePromptSlot(input.definition) && (definitionChanges || input.prompt !== readGenerationDraftPrompt(input.currentDraft))) {
15378
15534
  editInput.prompt = input.prompt;
15379
15535
  }
15380
- if (!sameConfig(input.config, readGenerationDraftConfig(input.currentDraft)) && input.config) {
15536
+ if (input.config && (definitionChanges || !sameConfig(input.config, readGenerationDraftConfig(input.currentDraft)))) {
15381
15537
  editInput.config = input.config;
15382
15538
  }
15383
15539
  if (definitionChanges || Object.keys(editInput).length > 2) {
@@ -16056,9 +16212,12 @@ var CoreNodeCreativeLoop = class {
16056
16212
  if (name === "createGenerationTarget")
16057
16213
  return this.createGenerationTarget(input, timeoutMs);
16058
16214
  if (name === "planGeneration") {
16215
+ if (input.definitionId !== void 0) {
16216
+ await this.requireSelectableDefinition(requireString3(input.definitionId, "definitionId"));
16217
+ }
16059
16218
  return {
16060
16219
  result: await planGeneration(input, {
16061
- definitions: [...this.definitions.values()],
16220
+ definitions: this.selectableDefinitions(),
16062
16221
  foundationData: this.systems.foundationData,
16063
16222
  getSnapshot: () => this.context.getSnapshot(),
16064
16223
  projectContentProjection: this.projection,
@@ -16086,15 +16245,23 @@ var CoreNodeCreativeLoop = class {
16086
16245
  throw new Error(`Operation ${name} is outside the Core Node creative loop.`);
16087
16246
  }
16088
16247
  getDefinitions(detail, definitionId) {
16089
- const definitions = [...this.definitions.values()].filter((definition) => !definitionId || definition.id === definitionId);
16248
+ const definitions = [...this.definitions.values()].filter((definition) => {
16249
+ if (definitionId)
16250
+ return definition.id === definitionId;
16251
+ return isWorkflowDefinitionAvailable(definition, this.systems.workflowAccessTier);
16252
+ });
16090
16253
  return {
16091
- definitions: definitions.map((definition) => detail ? structuredClone(definition) : {
16092
- description: definition.description,
16093
- id: definition.id,
16094
- name: definition.name,
16095
- outputs: definition.outputSchema.map((output) => output.dataType),
16096
- tags: definition.tags,
16097
- version: definition.version
16254
+ definitions: definitions.map((definition) => {
16255
+ const access = resolveWorkflowDefinitionAccess(definition, this.systems.workflowAccessTier);
16256
+ return detail ? { ...structuredClone(definition), ...access } : {
16257
+ ...access,
16258
+ description: definition.description,
16259
+ id: definition.id,
16260
+ name: definition.name,
16261
+ outputs: definition.outputSchema.map((output) => output.dataType),
16262
+ tags: definition.tags,
16263
+ version: definition.version
16264
+ };
16098
16265
  })
16099
16266
  };
16100
16267
  }
@@ -16105,6 +16272,18 @@ var CoreNodeCreativeLoop = class {
16105
16272
  for (const id2 of ids)
16106
16273
  await this.recoverWorkflowRun(id2, timeoutMs);
16107
16274
  }
16275
+ observePendingWorkflowRuns(ids, timeoutMs) {
16276
+ for (const id2 of new Set(ids)) {
16277
+ if (!this.isPending(id2))
16278
+ continue;
16279
+ void this.recoverWorkflowRun(id2, timeoutMs).catch((error51) => {
16280
+ if (isSettlementWriteAttempt(error51))
16281
+ this.context.reportBackgroundError(error51);
16282
+ else
16283
+ this.scheduleWorkflowRunRecovery(id2, timeoutMs, error51);
16284
+ });
16285
+ }
16286
+ }
16108
16287
  close() {
16109
16288
  this.closed = true;
16110
16289
  for (const subscription of this.subscriptions.values())
@@ -16203,10 +16382,14 @@ var CoreNodeCreativeLoop = class {
16203
16382
  assertOnlyKeys3(input, ["contentType", "name", "position"]);
16204
16383
  const contentType = requireString3(input.contentType, "contentType");
16205
16384
  requireSupportedContentType(contentType);
16385
+ const targetContentType = contentType;
16386
+ const selection = await this.resolveDefaultDefinition(targetContentType);
16387
+ const draft = selection.definition ? createInitialGenerationDraft(selection.definition, selection.preference?.config, this.context.now()) : null;
16206
16388
  const viewNode = this.createResourceViewNode(contentType, input.position, optionalString2(input.name), {
16207
16389
  creationIntent: "blank",
16208
16390
  ...contentType === "text" ? { mode: "content" } : {}
16209
16391
  }, "generated");
16392
+ viewNode.draft = draft ? toDocumentJson(draft) : null;
16210
16393
  await this.context.commit({
16211
16394
  after: [viewNode],
16212
16395
  before: [],
@@ -16214,24 +16397,39 @@ var CoreNodeCreativeLoop = class {
16214
16397
  label: "Create generation target",
16215
16398
  timeoutMs
16216
16399
  });
16217
- return attention(viewNode.id, { viewNodeId: viewNode.id });
16400
+ if (draft)
16401
+ await this.rememberWorkflowPreference(targetContentType, draft);
16402
+ return attention(viewNode.id, {
16403
+ ...draft ? { definitionId: draft.definitionId, draftId: draft.id } : {},
16404
+ draftInitialized: Boolean(draft),
16405
+ selectionSource: selection.source,
16406
+ viewNodeId: viewNode.id
16407
+ });
16218
16408
  }
16219
16409
  async editDraft(input, timeoutMs) {
16220
16410
  assertOnlyKeys3(input, ["config", "definitionId", "prompt", "viewNodeId"]);
16221
16411
  const current = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
16222
16412
  const existing = readGenerationDraft(current.draft);
16223
- const definitionId = optionalString2(input.definitionId) ?? existing?.definitionId;
16224
- if (!definitionId)
16225
- throw new Error("definitionId is required when creating a Draft.");
16226
- const definition = await this.requireDefinition(definitionId);
16413
+ const contentType = current.contentType;
16414
+ const explicitDefinitionId = optionalString2(input.definitionId);
16415
+ const resolved = existing ? {
16416
+ definition: await this.requireSelectableDefinition(explicitDefinitionId ?? existing.definitionId),
16417
+ preference: null,
16418
+ source: explicitDefinitionId ? "explicit" : "existing"
16419
+ } : explicitDefinitionId ? await this.resolveExplicitDefinition(contentType, explicitDefinitionId) : await this.resolveDefaultDefinition(contentType);
16420
+ const definition = resolved.definition;
16421
+ if (!definition) {
16422
+ throw Object.assign(new Error(`No compatible Workflow Definition is available for ${contentType}.`), { code: "no-compatible-workflow" });
16423
+ }
16227
16424
  requirePrimaryOutput(definition, current.contentType);
16228
16425
  if (input.prompt === void 0 && input.config === void 0 && input.definitionId === void 0) {
16229
16426
  throw new Error("Draft edit requires definitionId, prompt, or config.");
16230
16427
  }
16231
16428
  const config2 = input.config === void 0 ? void 0 : parseConfig2(input.config);
16232
- const nextDraft = editGenerationDraft(existing, definition, {
16429
+ const baseDraft = existing ?? createInitialGenerationDraft(definition, resolved.preference?.config, this.context.now());
16430
+ const nextDraft = editGenerationDraft(baseDraft, definition, {
16233
16431
  ...config2 ? { config: config2 } : {},
16234
- ...input.definitionId === void 0 ? {} : { definitionId },
16432
+ ...input.definitionId === void 0 ? {} : { definitionId: definition.id },
16235
16433
  ...input.prompt === void 0 ? {} : { prompt: requirePlainString2(input.prompt, "prompt") }
16236
16434
  }, this.context.now());
16237
16435
  const after = {
@@ -16246,13 +16444,19 @@ var CoreNodeCreativeLoop = class {
16246
16444
  label: "Edit generation Draft",
16247
16445
  timeoutMs
16248
16446
  });
16249
- return attention(current.id, { draftId: nextDraft.id, viewNodeId: current.id });
16447
+ await this.rememberWorkflowPreference(contentType, nextDraft);
16448
+ return attention(current.id, {
16449
+ definitionId: nextDraft.definitionId,
16450
+ draftId: nextDraft.id,
16451
+ selectionSource: resolved.source,
16452
+ viewNodeId: current.id
16453
+ });
16250
16454
  }
16251
16455
  async addVisualReference(input, timeoutMs) {
16252
16456
  assertOnlyKeys3(input, ["referenceViewNodeId", "viewNodeId"]);
16253
16457
  const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
16254
16458
  const draft = requireDraft(target);
16255
- const definition = await this.requireDefinition(draft.definitionId);
16459
+ const definition = await this.requireSelectableDefinition(draft.definitionId);
16256
16460
  const referenceViewNodeId = requireString3(input.referenceViewNodeId, "referenceViewNodeId");
16257
16461
  const sourceDataType = await this.resolveReferenceDataType(referenceViewNodeId);
16258
16462
  const referenceId = `input_ref_${crypto.randomUUID().replaceAll("-", "")}`;
@@ -16264,14 +16468,12 @@ var CoreNodeCreativeLoop = class {
16264
16468
  assertOnlyKeys3(input, ["placement", "referenceViewNodeId", "viewNodeId"]);
16265
16469
  const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
16266
16470
  const draft = requireDraft(target);
16267
- const definition = await this.requireDefinition(draft.definitionId);
16471
+ const definition = await this.requireSelectableDefinition(draft.definitionId);
16268
16472
  const referenceViewNodeId = requireString3(input.referenceViewNodeId, "referenceViewNodeId");
16269
16473
  const sourceDataType = await this.resolveReferenceDataType(referenceViewNodeId);
16270
16474
  const existing = Object.values(draft.inputs.slots).flat().find((reference) => reference.kind === "view-node" && reference.viewNodeId === referenceViewNodeId);
16271
16475
  const referenceId = existing?.id ?? `input_ref_${crypto.randomUUID().replaceAll("-", "")}`;
16272
- const placement = input.placement === void 0 ? "end" : input.placement === "start" || input.placement === "end" ? input.placement : null;
16273
- if (!placement)
16274
- throw new Error("placement must be start or end.");
16476
+ const placement = parsePromptReferencePlacement(input.placement);
16275
16477
  const nextDraft = insertPromptReferenceIntoDraft(draft, definition, { id: referenceId, kind: "view-node", viewNodeId: referenceViewNodeId }, sourceDataType, placement, this.context.now());
16276
16478
  await this.replaceDraft(target, nextDraft, "Insert Prompt Reference", timeoutMs);
16277
16479
  return attention(target.id, { placement, referenceId, viewNodeId: target.id });
@@ -16292,7 +16494,7 @@ var CoreNodeCreativeLoop = class {
16292
16494
  assertOnlyKeys3(input, ["viewNodeId"]);
16293
16495
  const target = this.requireTarget(requireString3(input.viewNodeId, "viewNodeId"));
16294
16496
  const draft = requireDraft(target);
16295
- const definition = await this.requireDefinition(draft.definitionId);
16497
+ const definition = await this.requireSelectableDefinition(draft.definitionId);
16296
16498
  const output = requirePrimaryOutput(definition, target.contentType);
16297
16499
  const prepared = await prepareGenerationInput({
16298
16500
  definition,
@@ -16708,6 +16910,40 @@ var CoreNodeCreativeLoop = class {
16708
16910
  this.coreNodes.set(id2, node);
16709
16911
  return structuredClone(node);
16710
16912
  }
16913
+ async resolveDefaultDefinition(contentType) {
16914
+ const preference = await this.readWorkflowPreference(contentType);
16915
+ return selectDefaultWorkflowDefinition({
16916
+ contentType,
16917
+ definitions: this.selectableDefinitions(),
16918
+ preference
16919
+ });
16920
+ }
16921
+ async resolveExplicitDefinition(contentType, definitionId) {
16922
+ const definition = await this.requireSelectableDefinition(definitionId);
16923
+ requirePrimaryOutput(definition, contentType);
16924
+ const preference = await this.readWorkflowPreference(contentType);
16925
+ return {
16926
+ definition,
16927
+ preference: preference?.definitionId === definition.id ? preference : null,
16928
+ source: "explicit"
16929
+ };
16930
+ }
16931
+ async readWorkflowPreference(contentType) {
16932
+ try {
16933
+ return await this.systems.workflowPreferences?.read(contentType) ?? null;
16934
+ } catch {
16935
+ return null;
16936
+ }
16937
+ }
16938
+ async rememberWorkflowPreference(contentType, draft) {
16939
+ try {
16940
+ await this.systems.workflowPreferences?.write(contentType, {
16941
+ config: readGenerationDraftConfig(draft) ?? {},
16942
+ definitionId: draft.definitionId
16943
+ });
16944
+ } catch {
16945
+ }
16946
+ }
16711
16947
  async requireDefinition(id2) {
16712
16948
  const cached2 = this.definitions.get(id2);
16713
16949
  if (cached2)
@@ -16718,6 +16954,16 @@ var CoreNodeCreativeLoop = class {
16718
16954
  this.definitions.set(id2, definition);
16719
16955
  return definition;
16720
16956
  }
16957
+ async requireSelectableDefinition(id2) {
16958
+ const definition = await this.requireDefinition(id2);
16959
+ if (!isWorkflowDefinitionAvailable(definition, this.systems.workflowAccessTier)) {
16960
+ throw Object.assign(new Error(`Workflow Definition ${id2} is not available to the authenticated account.`), { code: "workflow-not-available-for-account" });
16961
+ }
16962
+ return definition;
16963
+ }
16964
+ selectableDefinitions() {
16965
+ return [...this.definitions.values()].filter((definition) => isWorkflowDefinitionAvailable(definition, this.systems.workflowAccessTier));
16966
+ }
16721
16967
  async resolveReferenceDataType(referenceViewNodeId) {
16722
16968
  const referenceViewNode = this.requireViewNode(referenceViewNodeId);
16723
16969
  if (referenceViewNode.type !== "resource") {
@@ -16921,6 +17167,38 @@ function requireString3(value, field) {
16921
17167
  throw new Error(`${field} must be non-empty.`);
16922
17168
  return value.trim();
16923
17169
  }
17170
+ function parsePromptReferencePlacement(value) {
17171
+ if (value === void 0 || value === "end")
17172
+ return "end";
17173
+ if (value === "start")
17174
+ return "start";
17175
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
17176
+ throw new Error("placement must be start, end, or an anchor placement object.");
17177
+ }
17178
+ const placement = value;
17179
+ assertOnlyKeys3(placement, ["anchor", "kind"]);
17180
+ if (placement.kind !== "before" && placement.kind !== "after") {
17181
+ throw new Error("placement.kind must be before or after.");
17182
+ }
17183
+ if (typeof placement.anchor !== "object" || placement.anchor === null || Array.isArray(placement.anchor)) {
17184
+ throw new Error("placement.anchor must be an object.");
17185
+ }
17186
+ const anchor = placement.anchor;
17187
+ assertOnlyKeys3(anchor, ["occurrence", "text"]);
17188
+ const text = requirePlainString2(anchor.text, "placement.anchor.text");
17189
+ if (!text.trim())
17190
+ throw new Error("placement.anchor.text must be non-empty.");
17191
+ if (anchor.occurrence !== void 0 && (!Number.isInteger(anchor.occurrence) || anchor.occurrence < 1)) {
17192
+ throw new Error("placement.anchor.occurrence must be a positive integer.");
17193
+ }
17194
+ return {
17195
+ anchor: {
17196
+ ...anchor.occurrence === void 0 ? {} : { occurrence: anchor.occurrence },
17197
+ text
17198
+ },
17199
+ kind: placement.kind
17200
+ };
17201
+ }
16924
17202
  function requirePlainString2(value, field) {
16925
17203
  if (typeof value !== "string")
16926
17204
  throw new Error(`${field} must be a string.`);
@@ -17278,8 +17556,8 @@ function parsePosition3(value) {
17278
17556
  return { x: position.x, y: position.y };
17279
17557
  }
17280
17558
  function deriveName(url2, contentType) {
17281
- const path9 = new URL(url2).pathname;
17282
- const filename = decodeURIComponent(path9.split("/").filter(Boolean).at(-1) ?? "");
17559
+ const path10 = new URL(url2).pathname;
17560
+ const filename = decodeURIComponent(path10.split("/").filter(Boolean).at(-1) ?? "");
17283
17561
  const withoutExtension = filename.replace(/\.[a-z0-9]+$/i, "").trim();
17284
17562
  return withoutExtension || `Imported ${contentType}`;
17285
17563
  }
@@ -17384,9 +17662,12 @@ var MediaUploader = class {
17384
17662
  this.systems = systems;
17385
17663
  this.projection = systems.projectContentProjection;
17386
17664
  }
17387
- async execute(input, timeoutMs) {
17665
+ async execute(input, timeoutMs, onProgress) {
17388
17666
  const request = normalizeUploadRequest(input);
17389
- const prepared = await this.systems.mediaUpload.prepare(request.source);
17667
+ const safeProgress = safeProgressListener(onProgress);
17668
+ const prepared = await this.systems.mediaUpload.prepare(request.source, {
17669
+ onProgress: safeProgress
17670
+ });
17390
17671
  try {
17391
17672
  const descriptor2 = requireValidDescriptor(prepared.descriptor);
17392
17673
  const intent = await createIntent(request, descriptor2);
@@ -17420,9 +17701,10 @@ var MediaUploader = class {
17420
17701
  let uploaded = false;
17421
17702
  let coreNodeId;
17422
17703
  try {
17423
- const object2 = await prepared.upload({ timeoutMs });
17704
+ const object2 = await prepared.upload({ onProgress: safeProgress, timeoutMs });
17424
17705
  uploaded = true;
17425
17706
  const fileUrl = requireUploadedMediaUrl(object2.url);
17707
+ await safeProgress({ operation: "uploadMedia", stage: "creating-content" });
17426
17708
  const completion = await this.systems.projectContent.completeMediaUploads(this.context.projectId, [
17427
17709
  {
17428
17710
  asset: {
@@ -17456,6 +17738,7 @@ var MediaUploader = class {
17456
17738
  await this.updateBindingStatus(viewNode, "failed", errorMessage2(error51), timeoutMs);
17457
17739
  throw error51;
17458
17740
  }
17741
+ await safeProgress({ operation: "uploadMedia", stage: "updating-canvas" });
17459
17742
  await this.updateBindingStatus(viewNode, "ready", null, timeoutMs);
17460
17743
  return result2(viewNode.id, coreNodeId, intent, true, uploaded);
17461
17744
  } finally {
@@ -17559,6 +17842,14 @@ var MediaUploader = class {
17559
17842
  return after;
17560
17843
  }
17561
17844
  };
17845
+ function safeProgressListener(listener) {
17846
+ return async (progress) => {
17847
+ try {
17848
+ await listener?.(progress);
17849
+ } catch {
17850
+ }
17851
+ };
17852
+ }
17562
17853
  var MediaUploadIndeterminateError = class extends Error {
17563
17854
  indeterminate = true;
17564
17855
  timedOut = false;
@@ -18328,6 +18619,8 @@ var SemanticCanvasRuntime = class {
18328
18619
  this.unsubscribeRemote = options.document.subscribeRemoteChange(() => {
18329
18620
  const previous = this.snapshot;
18330
18621
  this.snapshot = options.document.getSnapshot();
18622
+ const previousPending = new Set(previous.pendingWorkflowInstanceIds);
18623
+ const addedPendingWorkflowInstanceIds = this.snapshot.pendingWorkflowInstanceIds.filter((id2) => !previousPending.has(id2));
18331
18624
  const viewNodeChanges = diffSemanticViewNodes(previous.viewNodes, this.snapshot.viewNodes);
18332
18625
  this.projectionRevision += 1;
18333
18626
  this.emit({
@@ -18339,6 +18632,9 @@ var SemanticCanvasRuntime = class {
18339
18632
  revision: this.projectionRevision
18340
18633
  });
18341
18634
  this.scheduleProjectContentRefresh();
18635
+ if (addedPendingWorkflowInstanceIds.length > 0) {
18636
+ this.creativeLoop?.observePendingWorkflowRuns(addedPendingWorkflowInstanceIds, 15e3);
18637
+ }
18342
18638
  });
18343
18639
  }
18344
18640
  get revision() {
@@ -18421,7 +18717,7 @@ var SemanticCanvasRuntime = class {
18421
18717
  this.listeners.add(listener);
18422
18718
  return () => this.listeners.delete(listener);
18423
18719
  }
18424
- async execute(name, input, timeoutMs) {
18720
+ async execute(name, input, timeoutMs, options = {}) {
18425
18721
  this.requireCapability(name);
18426
18722
  if (name === "importExternalMedia") {
18427
18723
  if (!this.externalMediaImporter)
@@ -18432,7 +18728,7 @@ var SemanticCanvasRuntime = class {
18432
18728
  if (name === "uploadMedia") {
18433
18729
  if (!this.mediaUploader)
18434
18730
  throw new Error("Media upload system is unavailable.");
18435
- const result3 = await this.mediaUploader.execute(input, timeoutMs);
18731
+ const result3 = await this.mediaUploader.execute(input, timeoutMs, options.onProgress);
18436
18732
  return { ...result3, revision: this.projectionRevision };
18437
18733
  }
18438
18734
  if (isCreativeLoopOperation(name)) {
@@ -18766,6 +19062,8 @@ function createCreativeLoop(options, projectContentProjection, context) {
18766
19062
  foundationData: options.foundationData,
18767
19063
  projectContent: options.projectContent,
18768
19064
  projectContentProjection,
19065
+ workflowAccessTier: options.workflowAccessTier ?? "paid",
19066
+ workflowPreferences: options.workflowPreferences,
18769
19067
  workflowRuntime: options.workflowRuntime
18770
19068
  };
18771
19069
  return new CoreNodeCreativeLoop(context, systems);
@@ -18845,6 +19143,114 @@ function normalizeCreationProvenanceSuffix(value) {
18845
19143
  return normalized;
18846
19144
  }
18847
19145
 
19146
+ // src/cli/workflow-preference-store.ts
19147
+ import { createHash, randomUUID } from "node:crypto";
19148
+ import { chmod, lstat, mkdir, readFile, rename, writeFile } from "node:fs/promises";
19149
+ import { homedir } from "node:os";
19150
+ import path from "node:path";
19151
+ var FORMAT = "az8.workflow-preference.v1";
19152
+ function createNodeWorkflowPreferenceStore(options) {
19153
+ const accountId = options.accountId.trim();
19154
+ if (!accountId) throw new Error("Workflow preference scope requires an account id.");
19155
+ const env = options.env ?? process.env;
19156
+ const stateRoot = path.resolve(
19157
+ options.stateRoot ?? env.AZ8_CLI_STATE_DIR ?? path.join(env.XDG_STATE_HOME ?? path.join(homedir(), ".local", "state"), "az8-cli")
19158
+ );
19159
+ const directory = path.join(stateRoot, "workflow-preferences");
19160
+ return {
19161
+ async read(contentType) {
19162
+ await ensurePrivateDirectory(directory);
19163
+ const filePath = preferencePath(directory, options.environment, accountId, contentType);
19164
+ try {
19165
+ await assertPrivateRegularFile(filePath);
19166
+ return parseStoredPreference(
19167
+ JSON.parse(await readFile(filePath, "utf8")),
19168
+ options.environment,
19169
+ accountId,
19170
+ contentType
19171
+ );
19172
+ } catch (error51) {
19173
+ if (error51.code === "ENOENT") return null;
19174
+ return null;
19175
+ }
19176
+ },
19177
+ async write(contentType, preference) {
19178
+ const normalized = normalizePreference(preference);
19179
+ await ensurePrivateDirectory(directory);
19180
+ const value = {
19181
+ accountId,
19182
+ contentType,
19183
+ environment: options.environment,
19184
+ format: FORMAT,
19185
+ preference: normalized
19186
+ };
19187
+ await writeJsonAtomic(
19188
+ preferencePath(directory, options.environment, accountId, contentType),
19189
+ value
19190
+ );
19191
+ }
19192
+ };
19193
+ }
19194
+ function parseStoredPreference(value, environment, accountId, contentType) {
19195
+ if (!isRecord2(value) || value.format !== FORMAT || value.environment !== environment || value.accountId !== accountId || value.contentType !== contentType) {
19196
+ return null;
19197
+ }
19198
+ try {
19199
+ return normalizePreference(value.preference);
19200
+ } catch {
19201
+ return null;
19202
+ }
19203
+ }
19204
+ function normalizePreference(value) {
19205
+ if (!isRecord2(value) || typeof value.definitionId !== "string" || !value.definitionId.trim()) {
19206
+ throw new Error("Workflow preference requires a Definition id.");
19207
+ }
19208
+ if (!isRecord2(value.config)) throw new Error("Workflow preference config must be an object.");
19209
+ const config2 = Object.fromEntries(
19210
+ Object.entries(value.config).filter(
19211
+ (entry) => typeof entry[1] === "boolean" || typeof entry[1] === "string" || typeof entry[1] === "number" && Number.isFinite(entry[1])
19212
+ )
19213
+ );
19214
+ return { config: config2, definitionId: value.definitionId.trim() };
19215
+ }
19216
+ function preferencePath(directory, environment, accountId, contentType) {
19217
+ const digest = createHash("sha256").update(`${environment}\0${accountId}\0${contentType}`).digest("hex");
19218
+ return path.join(directory, `${digest}.json`);
19219
+ }
19220
+ async function ensurePrivateDirectory(directory) {
19221
+ await mkdir(directory, { mode: 448, recursive: true });
19222
+ const metadata = await lstat(directory);
19223
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
19224
+ throw new Error("Workflow preference state path is not a private directory.");
19225
+ }
19226
+ if (typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
19227
+ throw new Error("Workflow preference state directory has a different owner.");
19228
+ }
19229
+ await chmod(directory, 448);
19230
+ }
19231
+ async function assertPrivateRegularFile(filePath) {
19232
+ const metadata = await lstat(filePath);
19233
+ if (!metadata.isFile() || metadata.isSymbolicLink()) {
19234
+ throw new Error("Workflow preference state is not a regular file.");
19235
+ }
19236
+ if (typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
19237
+ throw new Error("Workflow preference state has a different owner.");
19238
+ }
19239
+ if ((metadata.mode & 63) !== 0) {
19240
+ throw new Error("Workflow preference state is not owner-private.");
19241
+ }
19242
+ }
19243
+ async function writeJsonAtomic(filePath, value) {
19244
+ const temporary = `${filePath}.${randomUUID()}.tmp`;
19245
+ await writeFile(temporary, `${JSON.stringify(value, null, 2)}
19246
+ `, { mode: 384 });
19247
+ await rename(temporary, filePath);
19248
+ await chmod(filePath, 384);
19249
+ }
19250
+ function isRecord2(value) {
19251
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19252
+ }
19253
+
18848
19254
  // src/integrations/backend.ts
18849
19255
  var BackendRequestError = class extends Error {
18850
19256
  code;
@@ -18873,7 +19279,7 @@ var Az8BackendClient = class {
18873
19279
  this.config = config2;
18874
19280
  }
18875
19281
  config;
18876
- async request(path9, options = {}) {
19282
+ async request(path10, options = {}) {
18877
19283
  const method = options.method ?? "GET";
18878
19284
  const token = await this.config.credentials.getToken();
18879
19285
  const attempts = options.write ? 1 : 2;
@@ -18881,7 +19287,7 @@ var Az8BackendClient = class {
18881
19287
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
18882
19288
  let response;
18883
19289
  try {
18884
- response = await this.fetchWithDeadline(path9, token, method, options);
19290
+ response = await this.fetchWithDeadline(path10, token, method, options);
18885
19291
  } catch (error51) {
18886
19292
  if (options.write) throw new IndeterminateBackendWriteError(error51);
18887
19293
  lastError = error51;
@@ -18893,11 +19299,11 @@ var Az8BackendClient = class {
18893
19299
  }
18894
19300
  throw lastError;
18895
19301
  }
18896
- async fetchWithDeadline(path9, token, method, options) {
19302
+ async fetchWithDeadline(path10, token, method, options) {
18897
19303
  const abortController2 = new AbortController();
18898
19304
  const timeout = setTimeout(() => abortController2.abort(), options.timeoutMs ?? 15e3);
18899
19305
  try {
18900
- return await this.config.fetch(`${this.config.backendUrl}/${path9.replace(/^\/+/, "")}`, {
19306
+ return await this.config.fetch(`${this.config.backendUrl}/${path10.replace(/^\/+/, "")}`, {
18901
19307
  method,
18902
19308
  headers: {
18903
19309
  Cookie: `token=${token}`,
@@ -18942,15 +19348,22 @@ function toAuthenticatedAccount(profile) {
18942
19348
  const accountId = profile.accountId.trim();
18943
19349
  if (!accountId)
18944
19350
  throw new Error("Authenticated account profile is missing account id.");
19351
+ const accessTier = isFreeAccount(profile) ? "free" : "paid";
18945
19352
  const nickname = profile.nickname?.trim();
18946
19353
  if (nickname)
18947
- return { accountId, displayName: nickname };
19354
+ return { accessTier, accountId, displayName: nickname };
18948
19355
  const emailName = profile.email.split("@")[0]?.trim();
18949
19356
  return {
19357
+ accessTier,
18950
19358
  accountId,
18951
19359
  displayName: emailName || `User ${accountId}`
18952
19360
  };
18953
19361
  }
19362
+ function isFreeAccount(profile) {
19363
+ if (profile.identities)
19364
+ return !profile.identities.includes("valid_user");
19365
+ return (profile.subscriptionType?.trim().toLowerCase() || "free") === "free";
19366
+ }
18954
19367
 
18955
19368
  // src/integrations/account-profile.ts
18956
19369
  function createAz8AccountProfileClient(config2) {
@@ -18985,7 +19398,13 @@ function createAz8AccountProfileRepository(config2) {
18985
19398
  return {
18986
19399
  accountId: String(payload.data.account_id),
18987
19400
  email: payload.data.email,
18988
- ...payload.data.nickname === void 0 ? {} : { nickname: payload.data.nickname }
19401
+ ...Array.isArray(payload.data.identities) ? {
19402
+ identities: payload.data.identities.filter(
19403
+ (value) => typeof value === "string"
19404
+ )
19405
+ } : {},
19406
+ ...payload.data.nickname === void 0 ? {} : { nickname: payload.data.nickname },
19407
+ ...payload.data.subscription_type === void 0 ? {} : { subscriptionType: payload.data.subscription_type }
18989
19408
  };
18990
19409
  }
18991
19410
  };
@@ -19635,10 +20054,10 @@ async function parseStatusEvent(event, onStatus) {
19635
20054
  }
19636
20055
 
19637
20056
  // src/media/upload.ts
19638
- import { createHash } from "node:crypto";
20057
+ import { createHash as createHash2 } from "node:crypto";
19639
20058
  import { constants } from "node:fs";
19640
- import { lstat, open } from "node:fs/promises";
19641
- import path from "node:path";
20059
+ import { lstat as lstat2, open } from "node:fs/promises";
20060
+ import path2 from "node:path";
19642
20061
  import { Transform } from "node:stream";
19643
20062
 
19644
20063
  // ../../node_modules/.pnpm/strtok3@10.3.5/node_modules/strtok3/lib/stream/Errors.js
@@ -23510,10 +23929,12 @@ var HASH_BUFFER_BYTES = 256 * 1024;
23510
23929
  function createNodeCanvasMediaUploadSystem(config2, cwd = process.cwd()) {
23511
23930
  const backend = new Az8BackendClient(config2);
23512
23931
  return {
23513
- async prepare(sourceReference) {
23932
+ async prepare(sourceReference, options) {
23933
+ const report = createProgressReporter(options?.onProgress);
23934
+ await report({ bytesCompleted: 0, operation: "uploadMedia", stage: "inspecting" });
23514
23935
  const source = await openUploadSource(sourceReference, cwd);
23515
23936
  try {
23516
- const descriptor2 = await inspectUploadSource(source.handle, source.absolutePath);
23937
+ const descriptor2 = await inspectUploadSource(source.handle, source.absolutePath, report);
23517
23938
  return createPreparedUpload(config2, backend, source.handle, source.absolutePath, descriptor2);
23518
23939
  } catch (error51) {
23519
23940
  await source.handle.close().catch(() => void 0);
@@ -23524,10 +23945,10 @@ function createNodeCanvasMediaUploadSystem(config2, cwd = process.cwd()) {
23524
23945
  }
23525
23946
  async function openUploadSource(sourceReference, cwd) {
23526
23947
  if (!sourceReference.trim()) throw new Error("Media upload source must not be blank.");
23527
- const absolutePath = path.resolve(cwd, sourceReference);
23948
+ const absolutePath = path2.resolve(cwd, sourceReference);
23528
23949
  let sourceStat;
23529
23950
  try {
23530
- sourceStat = await lstat(absolutePath);
23951
+ sourceStat = await lstat2(absolutePath);
23531
23952
  } catch {
23532
23953
  throw new Error("Media upload source does not exist or cannot be inspected.");
23533
23954
  }
@@ -23547,12 +23968,12 @@ async function openUploadSource(sourceReference, cwd) {
23547
23968
  }
23548
23969
  return { absolutePath, handle };
23549
23970
  }
23550
- async function inspectUploadSource(handle, absolutePath) {
23971
+ async function inspectUploadSource(handle, absolutePath, report) {
23551
23972
  const before = await handle.stat();
23552
23973
  if (!Number.isSafeInteger(before.size) || before.size < 1) {
23553
23974
  throw new Error("Media upload source must contain at least one byte.");
23554
23975
  }
23555
- const hash2 = createHash("sha256");
23976
+ const hash2 = createHash2("sha256");
23556
23977
  const header = Buffer.allocUnsafe(Math.min(FILE_TYPE_HEADER_BYTES, before.size));
23557
23978
  const buffer = Buffer.allocUnsafe(Math.min(HASH_BUFFER_BYTES, before.size));
23558
23979
  let bytes = 0;
@@ -23566,6 +23987,7 @@ async function inspectUploadSource(handle, absolutePath) {
23566
23987
  chunk2.copy(header, bytes, 0, Math.min(chunk2.byteLength, header.byteLength - bytes));
23567
23988
  }
23568
23989
  bytes += bytesRead;
23990
+ await report(uploadProgress("inspecting", bytes, before.size));
23569
23991
  }
23570
23992
  const after = await handle.stat();
23571
23993
  if (after.size !== before.size || after.mtimeMs !== before.mtimeMs) {
@@ -23579,7 +24001,7 @@ async function inspectUploadSource(handle, absolutePath) {
23579
24001
  return {
23580
24002
  bytes,
23581
24003
  extension: detected.ext,
23582
- fileName: path.basename(absolutePath),
24004
+ fileName: path2.basename(absolutePath),
23583
24005
  lastModified: Math.max(0, Math.round(after.mtimeMs)),
23584
24006
  mediaType,
23585
24007
  mimeType: detected.mime,
@@ -23602,6 +24024,10 @@ function createPreparedUpload(config2, backend, handle, absolutePath, descriptor
23602
24024
  if (closed) throw new Error("Prepared media upload is already closed.");
23603
24025
  if (attempted) throw new Error("Prepared media upload can only be attempted once.");
23604
24026
  attempted = true;
24027
+ await reportProgress(options?.onProgress, {
24028
+ operation: "uploadMedia",
24029
+ stage: "requesting-upload"
24030
+ });
23605
24031
  const presign = await backend.request("core_nodes/upload/presign", {
23606
24032
  json: { format: descriptor2.extension, type: descriptor2.mediaType },
23607
24033
  method: "POST",
@@ -23610,12 +24036,20 @@ function createPreparedUpload(config2, backend, handle, absolutePath, descriptor
23610
24036
  });
23611
24037
  const uploadUrl = requireHttpUrl(presign.upload_url, "upload");
23612
24038
  const fileUrl = requireHttpUrl(presign.file_url, "file");
23613
- await putPreparedFile(config2, handle, absolutePath, descriptor2, uploadUrl, options?.timeoutMs);
24039
+ await putPreparedFile(
24040
+ config2,
24041
+ handle,
24042
+ absolutePath,
24043
+ descriptor2,
24044
+ uploadUrl,
24045
+ options?.timeoutMs,
24046
+ createProgressReporter(options?.onProgress)
24047
+ );
23614
24048
  return { url: fileUrl };
23615
24049
  }
23616
24050
  };
23617
24051
  }
23618
- async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploadUrl, timeoutMs) {
24052
+ async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploadUrl, timeoutMs, report) {
23619
24053
  const originalStat = await handle.stat();
23620
24054
  const uploadHandle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);
23621
24055
  let uploadStat;
@@ -23634,13 +24068,17 @@ async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploa
23634
24068
  () => controller.abort(new Error("Media upload timed out.")),
23635
24069
  timeoutMs ?? 3e5
23636
24070
  );
23637
- const hash2 = createHash("sha256");
24071
+ const hash2 = createHash2("sha256");
23638
24072
  let bytes = 0;
24073
+ await report(uploadProgress("uploading", 0, descriptor2.bytes));
23639
24074
  const meter = new Transform({
23640
24075
  transform(chunk2, _encoding, callback) {
23641
24076
  hash2.update(chunk2);
23642
24077
  bytes += chunk2.byteLength;
23643
- callback(null, chunk2);
24078
+ Promise.resolve(report(uploadProgress("uploading", bytes, descriptor2.bytes))).then(
24079
+ () => callback(null, chunk2),
24080
+ callback
24081
+ );
23644
24082
  }
23645
24083
  });
23646
24084
  const source = uploadHandle.createReadStream({
@@ -23676,6 +24114,35 @@ async function putPreparedFile(config2, handle, absolutePath, descriptor2, uploa
23676
24114
  throw new Error("Media upload source changed before the upload completed.");
23677
24115
  }
23678
24116
  }
24117
+ function createProgressReporter(listener) {
24118
+ let lastStage = "";
24119
+ let lastReportedAt = 0;
24120
+ return async (progress) => {
24121
+ if (!listener) return;
24122
+ const now = Date.now();
24123
+ const complete = progress.bytesTotal !== void 0 && progress.bytesCompleted === progress.bytesTotal;
24124
+ const stageChanged = progress.stage !== lastStage;
24125
+ if (!stageChanged && !complete && now - lastReportedAt < 500) return;
24126
+ lastStage = progress.stage;
24127
+ lastReportedAt = now;
24128
+ await reportProgress(listener, progress);
24129
+ };
24130
+ }
24131
+ async function reportProgress(listener, progress) {
24132
+ try {
24133
+ await listener?.(progress);
24134
+ } catch {
24135
+ }
24136
+ }
24137
+ function uploadProgress(stage, bytesCompleted, bytesTotal) {
24138
+ return {
24139
+ bytesCompleted,
24140
+ bytesTotal,
24141
+ operation: "uploadMedia",
24142
+ percent: Math.min(100, Math.round(bytesCompleted / bytesTotal * 1e3) / 10),
24143
+ stage
24144
+ };
24145
+ }
23679
24146
  function requireHttpUrl(value, kind) {
23680
24147
  if (typeof value !== "string" || !value.trim()) {
23681
24148
  throw new Error(`Media upload presign returned no ${kind} URL.`);
@@ -23744,8 +24211,19 @@ var COLLABORATION_SESSION_STATES = [
23744
24211
  ];
23745
24212
 
23746
24213
  // src/canvas/watch/interest-registry.ts
24214
+ var SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY = "system.workflow-terminal";
23747
24215
  var CollaborationInterestRegistry = class {
23748
24216
  interests = /* @__PURE__ */ new Map();
24217
+ constructor(includeSystemWorkflowTerminal = false) {
24218
+ if (includeSystemWorkflowTerminal) {
24219
+ this.interests.set(SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY, {
24220
+ filter: { terminalOnly: true },
24221
+ key: SYSTEM_WORKFLOW_TERMINAL_INTEREST_KEY,
24222
+ kind: "workflow",
24223
+ system: true
24224
+ });
24225
+ }
24226
+ }
23749
24227
  patch(input) {
23750
24228
  const upserts = input.upsert ?? [];
23751
24229
  const removals = input.remove ?? [];
@@ -23755,6 +24233,9 @@ var CollaborationInterestRegistry = class {
23755
24233
  for (const entry of upserts) {
23756
24234
  const key = requireKey(entry.key);
23757
24235
  const current = next.get(key);
24236
+ if (current?.system) {
24237
+ throw interestError("system-interest-readonly", `System Interest ${key} cannot be changed.`);
24238
+ }
23758
24239
  if (entry.patch.kind !== void 0 && !isInterestKind(entry.patch.kind)) {
23759
24240
  throw interestError(
23760
24241
  "interest-kind-invalid",
@@ -23775,6 +24256,9 @@ var CollaborationInterestRegistry = class {
23775
24256
  }
23776
24257
  for (const rawKey of removals) {
23777
24258
  const key = requireKey(rawKey);
24259
+ if (next.get(key)?.system) {
24260
+ throw interestError("system-interest-readonly", `System Interest ${key} cannot be removed.`);
24261
+ }
23778
24262
  if (next.delete(key)) changedKeys.push(key);
23779
24263
  }
23780
24264
  this.interests.clear();
@@ -23813,7 +24297,7 @@ function matches(interest, fact) {
23813
24297
  return !filter.states || filter.states.includes(fact.state);
23814
24298
  }
23815
24299
  function patchFilter(current, patch) {
23816
- if (!isRecord2(patch)) {
24300
+ if (!isRecord3(patch)) {
23817
24301
  throw interestError("interest-filter-invalid", "Interest filter patch must be an object.");
23818
24302
  }
23819
24303
  const next = structuredClone(current);
@@ -23828,7 +24312,7 @@ function validateFilter(kind, filter) {
23828
24312
  validateFilterEntries(kind, Object.entries(filter));
23829
24313
  }
23830
24314
  function validateFilterPatch(kind, filter) {
23831
- if (!isRecord2(filter)) {
24315
+ if (!isRecord3(filter)) {
23832
24316
  throw interestError("interest-filter-invalid", "Interest filter patch must be an object.");
23833
24317
  }
23834
24318
  validateFilterEntries(kind, Object.entries(filter));
@@ -23922,7 +24406,7 @@ function assertAllowed(values, allowed, label) {
23922
24406
  function isInterestKind(value) {
23923
24407
  return value === "canvas" || value === "session" || value === "workflow";
23924
24408
  }
23925
- function isRecord2(value) {
24409
+ function isRecord3(value) {
23926
24410
  return typeof value === "object" && value !== null && !Array.isArray(value);
23927
24411
  }
23928
24412
  function interestError(code, message) {
@@ -23930,11 +24414,11 @@ function interestError(code, message) {
23930
24414
  }
23931
24415
 
23932
24416
  // src/canvas/watch/project-observation.ts
23933
- function projectCanvasObservation(observation, observedAt) {
24417
+ function projectCanvasObservation(observation, observedAt, workflowTarget) {
23934
24418
  const viewNodeFacts = (observation.viewNodeChanges ?? []).map(
23935
24419
  (change) => projectViewNodeChange(change, observation.kind, observedAt)
23936
24420
  );
23937
- return observation.workflowRun ? [...viewNodeFacts, projectWorkflowRun(observation.workflowRun, observedAt)] : viewNodeFacts;
24421
+ return observation.workflowRun ? [...viewNodeFacts, projectWorkflowRun(observation.workflowRun, observedAt, workflowTarget)] : viewNodeFacts;
23938
24422
  }
23939
24423
  function projectSessionState(input) {
23940
24424
  return {
@@ -23969,7 +24453,7 @@ function projectViewNodeChange(change, source, observedAt) {
23969
24453
  ...typeof viewNode.type === "string" ? { viewNodeType: viewNode.type } : {}
23970
24454
  };
23971
24455
  }
23972
- function projectWorkflowRun(run, observedAt) {
24456
+ function projectWorkflowRun(run, observedAt, target) {
23973
24457
  const terminal = isTerminalWorkflowRunStatus(run.status);
23974
24458
  return {
23975
24459
  changeKind: terminal ? `workflow.${run.status}` : "workflow.progress",
@@ -23980,6 +24464,7 @@ function projectWorkflowRun(run, observedAt) {
23980
24464
  status: run.status,
23981
24465
  summary: {
23982
24466
  definitionId: run.definitionId,
24467
+ workflowRunId: run.id,
23983
24468
  ...run.error ? {
23984
24469
  error: {
23985
24470
  ...run.error.code ? { code: run.error.code } : {},
@@ -23987,8 +24472,10 @@ function projectWorkflowRun(run, observedAt) {
23987
24472
  }
23988
24473
  } : {},
23989
24474
  ...run.progress === void 0 ? {} : { progress: run.progress },
24475
+ ...target?.status === "resolved" ? { viewNodeId: target.viewNodeId } : terminal ? { targetResolution: target?.status ?? "unresolved" } : {},
23990
24476
  ...run.outputs.length > 0 ? {
23991
24477
  outputs: run.outputs.map((output) => ({
24478
+ coreNodeId: output.coreNodeId,
23992
24479
  role: output.role,
23993
24480
  slotName: output.slotName,
23994
24481
  ...output.viewNodeId ? { viewNodeId: output.viewNodeId } : {}
@@ -24002,7 +24489,7 @@ function projectWorkflowRun(run, observedAt) {
24002
24489
  function summarizeViewNode(change) {
24003
24490
  const viewNode = change.after ?? change.before;
24004
24491
  if (!viewNode) return {};
24005
- const data = isRecord3(viewNode.data) ? viewNode.data : {};
24492
+ const data = isRecord4(viewNode.data) ? viewNode.data : {};
24006
24493
  const summary = {
24007
24494
  ...typeof viewNode.contentType === "string" ? { contentType: viewNode.contentType } : {},
24008
24495
  ...typeof data.label === "string" ? { name: data.label } : {},
@@ -24013,10 +24500,10 @@ function summarizeViewNode(change) {
24013
24500
  if (typeof data.color === "string") summary.color = data.color;
24014
24501
  if (typeof data.creator_name === "string") summary.creatorName = data.creator_name;
24015
24502
  }
24016
- if (change.changedFields.includes("position") && isRecord3(viewNode.position)) {
24503
+ if (change.changedFields.includes("position") && isRecord4(viewNode.position)) {
24017
24504
  summary.position = structuredClone(viewNode.position);
24018
24505
  }
24019
- if (change.changedFields.includes("size") && isRecord3(viewNode.size)) {
24506
+ if (change.changedFields.includes("size") && isRecord4(viewNode.size)) {
24020
24507
  summary.size = structuredClone(viewNode.size);
24021
24508
  }
24022
24509
  if (change.changedFields.includes("parent")) summary.parentId = viewNode.parentId ?? null;
@@ -24034,7 +24521,7 @@ function fieldGroup(field) {
24034
24521
  if (field === "parent" || field === "stacking") return "hierarchy";
24035
24522
  return field;
24036
24523
  }
24037
- function isRecord3(value) {
24524
+ function isRecord4(value) {
24038
24525
  return typeof value === "object" && value !== null && !Array.isArray(value);
24039
24526
  }
24040
24527
  function safeWorkflowMessage(value) {
@@ -24043,7 +24530,7 @@ function safeWorkflowMessage(value) {
24043
24530
 
24044
24531
  // src/canvas/watch/watch.ts
24045
24532
  var AgentCollaborationWatch = class {
24046
- interests = new CollaborationInterestRegistry();
24533
+ interests;
24047
24534
  maxChangesPerDelivery;
24048
24535
  maxPendingChanges;
24049
24536
  maxRecentResults;
@@ -24054,6 +24541,7 @@ var AgentCollaborationWatch = class {
24054
24541
  activeWait = null;
24055
24542
  continuityLost = false;
24056
24543
  constructor(options = {}) {
24544
+ this.interests = new CollaborationInterestRegistry(options.includeSystemWorkflowTerminal);
24057
24545
  this.maxChangesPerDelivery = positive(
24058
24546
  options.maxChangesPerDelivery ?? 64,
24059
24547
  "maxChangesPerDelivery"
@@ -24289,7 +24777,7 @@ var CanvasSession = class {
24289
24777
  this.projectId = projectId;
24290
24778
  this.dependencies = dependencies;
24291
24779
  this.clientInstanceId = clientInstanceId;
24292
- this.watch = dependencies.watch ?? new AgentCollaborationWatch();
24780
+ this.watch = dependencies.watch ?? new AgentCollaborationWatch({ includeSystemWorkflowTerminal: true });
24293
24781
  this.health = {
24294
24782
  connectionAttempts: 0,
24295
24783
  connectionId: null,
@@ -24308,6 +24796,7 @@ var CanvasSession = class {
24308
24796
  unsubscribeRuntime = null;
24309
24797
  observationListeners = /* @__PURE__ */ new Set();
24310
24798
  knownWorkflowRunIds = /* @__PURE__ */ new Set();
24799
+ knownWorkflowTargets = /* @__PURE__ */ new Map();
24311
24800
  projectionRevision = 0;
24312
24801
  actorId = null;
24313
24802
  collaborationContext = null;
@@ -24372,17 +24861,29 @@ var CanvasSession = class {
24372
24861
  projectContents: contentSync.contents,
24373
24862
  projectId: authenticated.id,
24374
24863
  role: authenticated.role,
24864
+ workflowAccessTier: actor.accessTier,
24375
24865
  workflowDefinitions,
24866
+ workflowPreferences: this.dependencies.workflowPreferences?.(actor),
24376
24867
  workflowRuntime
24377
24868
  });
24378
24869
  this.runtime = runtime;
24379
24870
  for (const workflowRunId of runtime.getSnapshot().pendingWorkflowInstanceIds) {
24380
24871
  this.knownWorkflowRunIds.add(workflowRunId);
24872
+ this.retainWorkflowTarget(workflowRunId);
24381
24873
  }
24382
24874
  this.projectionRevision = runtime.revision;
24383
24875
  this.unsubscribeRuntime = runtime.subscribe((observation) => {
24384
24876
  this.projectionRevision = observation.revision;
24385
- this.watch.recordMany(projectCanvasObservation(observation, Date.now()));
24877
+ if (observation.workflowRun) {
24878
+ this.knownWorkflowRunIds.add(observation.workflowRun.id);
24879
+ }
24880
+ this.watch.recordMany(
24881
+ projectCanvasObservation(
24882
+ observation,
24883
+ Date.now(),
24884
+ observation.workflowRun ? this.resolveWorkflowTarget(observation.workflowRun.id) : void 0
24885
+ )
24886
+ );
24386
24887
  const legacyObservation = {
24387
24888
  ...observation.changedViewNodeIds ? { changedViewNodeIds: observation.changedViewNodeIds } : {},
24388
24889
  kind: observation.kind,
@@ -24400,14 +24901,29 @@ var CanvasSession = class {
24400
24901
  throw error51;
24401
24902
  }
24402
24903
  }
24403
- async execute(name, input, timeoutMs) {
24904
+ async execute(name, input, timeoutMs, options = {}) {
24404
24905
  const runtime = this.requireRuntime();
24405
24906
  if (typeof input.workflowRunId === "string" && input.workflowRunId) {
24406
24907
  this.knownWorkflowRunIds.add(input.workflowRunId);
24407
24908
  }
24408
- const result3 = await runtime.execute(name, input, timeoutMs);
24909
+ let result3;
24910
+ try {
24911
+ result3 = await runtime.execute(name, input, timeoutMs, options);
24912
+ } catch (error51) {
24913
+ const indeterminate = readCanvasOperationIndeterminateResult(error51);
24914
+ if (typeof indeterminate?.workflowRunId === "string" && indeterminate.workflowRunId && typeof indeterminate.viewNodeId === "string" && indeterminate.viewNodeId) {
24915
+ this.knownWorkflowRunIds.add(indeterminate.workflowRunId);
24916
+ this.knownWorkflowTargets.set(indeterminate.workflowRunId, indeterminate.viewNodeId);
24917
+ }
24918
+ throw error51;
24919
+ }
24409
24920
  if (typeof result3.result.workflowRunId === "string" && result3.result.workflowRunId) {
24410
24921
  this.knownWorkflowRunIds.add(result3.result.workflowRunId);
24922
+ if (typeof result3.result.viewNodeId === "string" && result3.result.viewNodeId) {
24923
+ this.knownWorkflowTargets.set(result3.result.workflowRunId, result3.result.viewNodeId);
24924
+ } else {
24925
+ this.retainWorkflowTarget(result3.result.workflowRunId);
24926
+ }
24411
24927
  }
24412
24928
  this.projectionRevision = result3.revision;
24413
24929
  if (result3.attention) this.publishAttention(result3.attention);
@@ -24431,7 +24947,8 @@ var CanvasSession = class {
24431
24947
  if (!isWorkflowRun(workflowRun)) continue;
24432
24948
  for (const fact of projectCanvasObservation(
24433
24949
  { kind: "workflow", revision: result3.revision, workflowRun },
24434
- Date.now()
24950
+ Date.now(),
24951
+ this.resolveWorkflowTarget(workflowRun.id)
24435
24952
  )) {
24436
24953
  this.watch.recordAuthoritativeWorkflow(fact);
24437
24954
  }
@@ -24530,6 +25047,28 @@ var CanvasSession = class {
24530
25047
  this.document = null;
24531
25048
  this.health.connectionId = null;
24532
25049
  }
25050
+ resolveWorkflowTarget(workflowRunId) {
25051
+ const retained = this.knownWorkflowTargets.get(workflowRunId);
25052
+ if (!this.runtime) {
25053
+ return retained ? { status: "resolved", viewNodeId: retained } : { status: "unresolved" };
25054
+ }
25055
+ const current = resolveWorkflowTargetFromSnapshot(this.runtime.getSnapshot(), workflowRunId);
25056
+ if (current.status === "ambiguous") return current;
25057
+ if (current.status === "resolved") {
25058
+ if (retained && retained !== current.viewNodeId) return { status: "ambiguous" };
25059
+ this.knownWorkflowTargets.set(workflowRunId, current.viewNodeId);
25060
+ return current;
25061
+ }
25062
+ return retained ? { status: "resolved", viewNodeId: retained } : current;
25063
+ }
25064
+ retainWorkflowTarget(workflowRunId) {
25065
+ if (!this.runtime) return { status: "unresolved" };
25066
+ const resolution = resolveWorkflowTargetFromSnapshot(this.runtime.getSnapshot(), workflowRunId);
25067
+ if (resolution.status === "resolved") {
25068
+ this.knownWorkflowTargets.set(workflowRunId, resolution.viewNodeId);
25069
+ }
25070
+ return resolution;
25071
+ }
24533
25072
  transition(state, onState) {
24534
25073
  const previousState = this.health.state;
24535
25074
  this.health.state = state;
@@ -24588,7 +25127,12 @@ var CanvasClient = class {
24588
25127
  session;
24589
25128
  receipts = [];
24590
25129
  constructor(config2, projectId, session) {
24591
- this.session = session ?? new CanvasSession(config2, projectId);
25130
+ this.session = session ?? new CanvasSession(config2, projectId, void 0, {
25131
+ workflowPreferences: (actor) => createNodeWorkflowPreferenceStore({
25132
+ accountId: actor.accountId,
25133
+ environment: config2.environment
25134
+ })
25135
+ });
24592
25136
  }
24593
25137
  async connect(displayName, onState) {
24594
25138
  await this.session.connect(displayName, onState);
@@ -24654,7 +25198,9 @@ var CanvasClient = class {
24654
25198
  };
24655
25199
  this.receipts.push(receipt);
24656
25200
  try {
24657
- const operation = await this.session.execute(name, input.input, input.timeoutMs ?? 15e3);
25201
+ const operation = await this.session.execute(name, input.input, input.timeoutMs ?? 15e3, {
25202
+ onProgress: input.onProgress
25203
+ });
24658
25204
  Object.assign(receipt, {
24659
25205
  completedAt: (/* @__PURE__ */ new Date()).toISOString(),
24660
25206
  projectionRevisionAfter: operation.revision,
@@ -24747,8 +25293,8 @@ function readTimedOut(value) {
24747
25293
  }
24748
25294
 
24749
25295
  // src/cli/config.ts
24750
- import { readFile } from "node:fs/promises";
24751
- import path2 from "node:path";
25296
+ import { readFile as readFile2 } from "node:fs/promises";
25297
+ import path3 from "node:path";
24752
25298
  var DEFAULT_ENV_FILE = ".env.local";
24753
25299
  var DEFAULT_ENVIRONMENT = "production";
24754
25300
  var ENVIRONMENT_ORIGINS = {
@@ -24758,7 +25304,7 @@ var ENVIRONMENT_ORIGINS = {
24758
25304
  async function resolveCliConfig(options, dependencies) {
24759
25305
  const cwd = dependencies.cwd ?? process.cwd();
24760
25306
  const envFile = options.envFile;
24761
- const fileEnvironment = envFile ? await readEnvironmentFile(path2.resolve(cwd, envFile)) : await readDefaultEnvironmentFile(cwd);
25307
+ const fileEnvironment = envFile ? await readEnvironmentFile(path3.resolve(cwd, envFile)) : await readDefaultEnvironmentFile(cwd);
24762
25308
  const environmentVariables = {
24763
25309
  ...fileEnvironment,
24764
25310
  ...dependencies.env ?? process.env
@@ -24780,6 +25326,7 @@ async function resolveCliConfig(options, dependencies) {
24780
25326
  return token;
24781
25327
  }
24782
25328
  },
25329
+ environment,
24783
25330
  fetch: dependencies.fetch ?? globalThis.fetch,
24784
25331
  serviceUrl: normalizeUrl(
24785
25332
  options.serviceUrl ?? environmentVariables.AZ8_SERVICE_URL ?? `${origin}/api`
@@ -24793,7 +25340,7 @@ function readEnvironment(value) {
24793
25340
  }
24794
25341
  async function readEnvironmentFile(filePath) {
24795
25342
  try {
24796
- const contents = await readFile(filePath, "utf8");
25343
+ const contents = await readFile2(filePath, "utf8");
24797
25344
  return Object.fromEntries(
24798
25345
  contents.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap((line) => {
24799
25346
  const separator = line.indexOf("=");
@@ -24809,11 +25356,11 @@ async function readEnvironmentFile(filePath) {
24809
25356
  }
24810
25357
  }
24811
25358
  async function readDefaultEnvironmentFile(cwd) {
24812
- let directory = path2.resolve(cwd);
25359
+ let directory = path3.resolve(cwd);
24813
25360
  while (true) {
24814
- const environment = await readEnvironmentFile(path2.join(directory, DEFAULT_ENV_FILE));
25361
+ const environment = await readEnvironmentFile(path3.join(directory, DEFAULT_ENV_FILE));
24815
25362
  if (Object.keys(environment).length > 0) return environment;
24816
- const parent = path2.dirname(directory);
25363
+ const parent = path3.dirname(directory);
24817
25364
  if (parent === directory) return {};
24818
25365
  directory = parent;
24819
25366
  }
@@ -24823,11 +25370,11 @@ function normalizeUrl(value) {
24823
25370
  }
24824
25371
 
24825
25372
  // src/agent-session/rpc.ts
24826
- import { chmod, unlink } from "node:fs/promises";
25373
+ import { chmod as chmod2, unlink } from "node:fs/promises";
24827
25374
  import net from "node:net";
24828
25375
  var MAX_RPC_REQUEST_BYTES = 1024 * 1024;
24829
25376
  var MAX_RPC_RESPONSE_BYTES = 16 * 1024 * 1024;
24830
- async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal) {
25377
+ async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal, onProgress) {
24831
25378
  return new Promise((resolve, reject) => {
24832
25379
  const socket = net.createConnection(socketPath);
24833
25380
  let source = "";
@@ -24857,14 +25404,23 @@ async function requestAgentSession(socketPath, request, timeoutMs = 5e3, signal)
24857
25404
  finish(rpcError("session-rpc-too-large", "Agent Session RPC response is too large."));
24858
25405
  return;
24859
25406
  }
24860
- const newline = source.indexOf("\n");
24861
- if (newline < 0) return;
24862
- try {
24863
- const response = JSON.parse(source.slice(0, newline));
24864
- if (!response.ok) finish(rpcError(response.error.code, response.error.message));
24865
- else finish(void 0, response.value);
24866
- } catch {
24867
- finish(rpcError("invalid-session-rpc", "Agent Session RPC response is invalid."));
25407
+ while (!settled) {
25408
+ const newline = source.indexOf("\n");
25409
+ if (newline < 0) return;
25410
+ const line = source.slice(0, newline);
25411
+ source = source.slice(newline + 1);
25412
+ try {
25413
+ const message = JSON.parse(line);
25414
+ if ("type" in message && message.type === "progress") {
25415
+ void Promise.resolve(onProgress?.(message.progress)).catch(() => void 0);
25416
+ continue;
25417
+ }
25418
+ const response = message;
25419
+ if (!response.ok) finish(rpcError(response.error.code, response.error.message));
25420
+ else finish(void 0, response.value);
25421
+ } catch {
25422
+ finish(rpcError("invalid-session-rpc", "Agent Session RPC response is invalid."));
25423
+ }
24868
25424
  }
24869
25425
  });
24870
25426
  socket.once("error", (error51) => finish(error51));
@@ -24903,7 +25459,10 @@ async function listenAgentSessionRpc(socketPath, handler) {
24903
25459
  handled = true;
24904
25460
  try {
24905
25461
  const request = JSON.parse(source.slice(0, newline));
24906
- const value = await handler(request, { signal: abortController2.signal });
25462
+ const value = await handler(request, {
25463
+ reportProgress: (progress) => writeProgress(socket, progress),
25464
+ signal: abortController2.signal
25465
+ });
24907
25466
  writeResponse(socket, { ok: true, value });
24908
25467
  } catch (error51) {
24909
25468
  writeResponse(
@@ -24923,7 +25482,7 @@ async function listenAgentSessionRpc(socketPath, handler) {
24923
25482
  resolve();
24924
25483
  });
24925
25484
  });
24926
- await chmod(socketPath, 384);
25485
+ await chmod2(socketPath, 384);
24927
25486
  return {
24928
25487
  async close() {
24929
25488
  const closed = new Promise(
@@ -24935,6 +25494,13 @@ async function listenAgentSessionRpc(socketPath, handler) {
24935
25494
  }
24936
25495
  };
24937
25496
  }
25497
+ function writeProgress(socket, progress) {
25498
+ if (socket.destroyed || !socket.writable) return Promise.resolve();
25499
+ return new Promise((resolve) => {
25500
+ socket.write(`${JSON.stringify({ progress, type: "progress" })}
25501
+ `, () => resolve());
25502
+ });
25503
+ }
24938
25504
  function writeResponse(socket, response) {
24939
25505
  if (socket.destroyed) return;
24940
25506
  socket.end(`${JSON.stringify(response)}
@@ -24961,20 +25527,20 @@ async function unlinkIfPresent(filePath) {
24961
25527
  }
24962
25528
 
24963
25529
  // src/agent-session/state-store.ts
24964
- import { createHash as createHash2, randomUUID } from "node:crypto";
25530
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
24965
25531
  import {
24966
- chmod as chmod2,
24967
- lstat as lstat2,
24968
- mkdir,
25532
+ chmod as chmod3,
25533
+ lstat as lstat3,
25534
+ mkdir as mkdir2,
24969
25535
  readdir,
24970
- readFile as readFile2,
25536
+ readFile as readFile3,
24971
25537
  realpath,
24972
- rename,
25538
+ rename as rename2,
24973
25539
  unlink as unlink2,
24974
- writeFile
25540
+ writeFile as writeFile2
24975
25541
  } from "node:fs/promises";
24976
- import { homedir } from "node:os";
24977
- import path3 from "node:path";
25542
+ import { homedir as homedir2 } from "node:os";
25543
+ import path4 from "node:path";
24978
25544
 
24979
25545
  // src/agent-session/types.ts
24980
25546
  var AGENT_SESSION_FORMAT = "az8.agent-session.v1";
@@ -24987,44 +25553,44 @@ var AgentSessionStateStore = class {
24987
25553
  stateRoot;
24988
25554
  env;
24989
25555
  constructor(options = {}) {
24990
- this.cwd = path3.resolve(options.cwd ?? process.cwd());
25556
+ this.cwd = path4.resolve(options.cwd ?? process.cwd());
24991
25557
  this.env = options.env ?? process.env;
24992
- this.stateRoot = path3.resolve(
24993
- options.stateRoot ?? this.env.AZ8_CLI_STATE_DIR ?? path3.join(this.env.XDG_STATE_HOME ?? path3.join(homedir(), ".local", "state"), "az8-cli")
25558
+ this.stateRoot = path4.resolve(
25559
+ options.stateRoot ?? this.env.AZ8_CLI_STATE_DIR ?? path4.join(this.env.XDG_STATE_HOME ?? path4.join(homedir2(), ".local", "state"), "az8-cli")
24994
25560
  );
24995
25561
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
24996
- const defaultRuntimeRoot = process.platform === "linux" && this.env.XDG_RUNTIME_DIR ? path3.join(this.env.XDG_RUNTIME_DIR, "az8-cli") : path3.join("/tmp", `az8-cli-${uid}`);
24997
- this.runtimeRoot = path3.resolve(options.runtimeRoot ?? defaultRuntimeRoot);
25562
+ const defaultRuntimeRoot = process.platform === "linux" && this.env.XDG_RUNTIME_DIR ? path4.join(this.env.XDG_RUNTIME_DIR, "az8-cli") : path4.join("/tmp", `az8-cli-${uid}`);
25563
+ this.runtimeRoot = path4.resolve(options.runtimeRoot ?? defaultRuntimeRoot);
24998
25564
  }
24999
25565
  createSessionId() {
25000
- return `session_${randomUUID()}`;
25566
+ return `session_${randomUUID2()}`;
25001
25567
  }
25002
25568
  async initialize() {
25003
25569
  await Promise.all([
25004
- ensurePrivateDirectory(this.stateRoot),
25005
- ensurePrivateDirectory(this.runtimeRoot),
25006
- ensurePrivateDirectory(this.descriptorsDirectory()),
25007
- ensurePrivateDirectory(this.contextsDirectory()),
25008
- ensurePrivateDirectory(this.launchesDirectory())
25570
+ ensurePrivateDirectory2(this.stateRoot),
25571
+ ensurePrivateDirectory2(this.runtimeRoot),
25572
+ ensurePrivateDirectory2(this.descriptorsDirectory()),
25573
+ ensurePrivateDirectory2(this.contextsDirectory()),
25574
+ ensurePrivateDirectory2(this.launchesDirectory())
25009
25575
  ]);
25010
25576
  }
25011
25577
  descriptorPath(sessionId) {
25012
- return path3.join(this.descriptorsDirectory(), `${requireSessionId(sessionId)}.json`);
25578
+ return path4.join(this.descriptorsDirectory(), `${requireSessionId(sessionId)}.json`);
25013
25579
  }
25014
25580
  socketPath(sessionId) {
25015
- const digest = createHash2("sha256").update(requireSessionId(sessionId)).digest("hex").slice(0, 24);
25016
- return path3.join(this.runtimeRoot, `${digest}.sock`);
25581
+ const digest = createHash3("sha256").update(requireSessionId(sessionId)).digest("hex").slice(0, 24);
25582
+ return path4.join(this.runtimeRoot, `${digest}.sock`);
25017
25583
  }
25018
25584
  async writeDescriptor(descriptor2) {
25019
25585
  assertDescriptor(descriptor2);
25020
25586
  await this.initialize();
25021
- await writeJsonAtomic(this.descriptorPath(descriptor2.sessionId), descriptor2);
25587
+ await writeJsonAtomic2(this.descriptorPath(descriptor2.sessionId), descriptor2);
25022
25588
  }
25023
25589
  async readDescriptor(sessionId) {
25024
25590
  await this.initialize();
25025
25591
  const filePath = this.descriptorPath(sessionId);
25026
- await assertPrivateRegularFile(filePath);
25027
- const value = JSON.parse(await readFile2(filePath, "utf8"));
25592
+ await assertPrivateRegularFile2(filePath);
25593
+ const value = JSON.parse(await readFile3(filePath, "utf8"));
25028
25594
  assertDescriptor(value);
25029
25595
  return value;
25030
25596
  }
@@ -25046,25 +25612,25 @@ var AgentSessionStateStore = class {
25046
25612
  }
25047
25613
  async writeLaunchSpec(spec) {
25048
25614
  await this.initialize();
25049
- const filePath = path3.join(this.launchesDirectory(), `${requireSessionId(spec.sessionId)}.json`);
25050
- await writeJsonAtomic(filePath, spec);
25615
+ const filePath = path4.join(this.launchesDirectory(), `${requireSessionId(spec.sessionId)}.json`);
25616
+ await writeJsonAtomic2(filePath, spec);
25051
25617
  return filePath;
25052
25618
  }
25053
25619
  async deleteLaunchSpec(sessionId) {
25054
25620
  await unlinkIfPresent2(
25055
- path3.join(this.launchesDirectory(), `${requireSessionId(sessionId)}.json`)
25621
+ path4.join(this.launchesDirectory(), `${requireSessionId(sessionId)}.json`)
25056
25622
  );
25057
25623
  }
25058
25624
  async selectCurrent(sessionId) {
25059
25625
  await this.readDescriptor(sessionId);
25060
25626
  const context = await this.contextIdentity();
25061
- await writeJsonAtomic(this.contextPath(context), { context, sessionId });
25627
+ await writeJsonAtomic2(this.contextPath(context), { context, sessionId });
25062
25628
  }
25063
25629
  async readCurrent() {
25064
25630
  await this.initialize();
25065
25631
  const context = await this.contextIdentity();
25066
25632
  try {
25067
- const value = JSON.parse(await readFile2(this.contextPath(context), "utf8"));
25633
+ const value = JSON.parse(await readFile3(this.contextPath(context), "utf8"));
25068
25634
  if (value.context !== context || typeof value.sessionId !== "string") return void 0;
25069
25635
  return requireSessionId(value.sessionId);
25070
25636
  } catch (error51) {
@@ -25088,13 +25654,13 @@ var AgentSessionStateStore = class {
25088
25654
  return requireSessionId(selected);
25089
25655
  }
25090
25656
  descriptorsDirectory() {
25091
- return path3.join(this.stateRoot, "sessions");
25657
+ return path4.join(this.stateRoot, "sessions");
25092
25658
  }
25093
25659
  contextsDirectory() {
25094
- return path3.join(this.stateRoot, "contexts");
25660
+ return path4.join(this.stateRoot, "contexts");
25095
25661
  }
25096
25662
  launchesDirectory() {
25097
- return path3.join(this.stateRoot, "launches");
25663
+ return path4.join(this.stateRoot, "launches");
25098
25664
  }
25099
25665
  async contextIdentity() {
25100
25666
  const override = this.env.AZ8_CONTEXT?.trim();
@@ -25106,16 +25672,16 @@ var AgentSessionStateStore = class {
25106
25672
  }
25107
25673
  }
25108
25674
  contextPath(context) {
25109
- const digest = createHash2("sha256").update(context).digest("hex");
25110
- return path3.join(this.contextsDirectory(), `${digest}.json`);
25675
+ const digest = createHash3("sha256").update(context).digest("hex");
25676
+ return path4.join(this.contextsDirectory(), `${digest}.json`);
25111
25677
  }
25112
25678
  };
25113
25679
  async function readAndDeleteLaunchSpec(filePath) {
25114
- if (path3.basename(path3.dirname(filePath)) !== "launches") {
25680
+ if (path4.basename(path4.dirname(filePath)) !== "launches") {
25115
25681
  throw sessionStateError("invalid-session-launch", "Session launch path is invalid.");
25116
25682
  }
25117
- await assertPrivateRegularFile(filePath);
25118
- const value = JSON.parse(await readFile2(filePath, "utf8"));
25683
+ await assertPrivateRegularFile2(filePath);
25684
+ const value = JSON.parse(await readFile3(filePath, "utf8"));
25119
25685
  assertLaunchSpec(value, filePath);
25120
25686
  await unlinkIfPresent2(filePath);
25121
25687
  return value;
@@ -25137,30 +25703,30 @@ function assertLaunchSpec(value, launchPath) {
25137
25703
  throw sessionStateError("invalid-session-launch", "Session launch descriptor is invalid.");
25138
25704
  }
25139
25705
  const sessionId = requireSessionId(value.sessionId);
25140
- const stateRoot = path3.resolve(value.stateRoot);
25141
- const expectedLaunch = path3.join(stateRoot, "launches", `${sessionId}.json`);
25142
- if (path3.resolve(launchPath) !== expectedLaunch) {
25706
+ const stateRoot = path4.resolve(value.stateRoot);
25707
+ const expectedLaunch = path4.join(stateRoot, "launches", `${sessionId}.json`);
25708
+ if (path4.resolve(launchPath) !== expectedLaunch) {
25143
25709
  throw sessionStateError(
25144
25710
  "invalid-session-launch",
25145
25711
  "Session launch identity does not match its path."
25146
25712
  );
25147
25713
  }
25148
- const expectedDescriptor = path3.join(stateRoot, "sessions", `${sessionId}.json`);
25149
- if (path3.resolve(value.descriptorPath) !== expectedDescriptor) {
25714
+ const expectedDescriptor = path4.join(stateRoot, "sessions", `${sessionId}.json`);
25715
+ if (path4.resolve(value.descriptorPath) !== expectedDescriptor) {
25150
25716
  throw sessionStateError("invalid-session-launch", "Session descriptor path is invalid.");
25151
25717
  }
25152
- const socketDigest = createHash2("sha256").update(sessionId).digest("hex").slice(0, 24);
25153
- const expectedSocket = path3.join(
25154
- path3.resolve(value.runtimeRoot),
25718
+ const socketDigest = createHash3("sha256").update(sessionId).digest("hex").slice(0, 24);
25719
+ const expectedSocket = path4.join(
25720
+ path4.resolve(value.runtimeRoot),
25155
25721
  `${socketDigest}.sock`
25156
25722
  );
25157
- if (path3.resolve(value.socketPath) !== expectedSocket) {
25723
+ if (path4.resolve(value.socketPath) !== expectedSocket) {
25158
25724
  throw sessionStateError("invalid-session-launch", "Session socket path is invalid.");
25159
25725
  }
25160
25726
  }
25161
- async function ensurePrivateDirectory(directory) {
25162
- await mkdir(directory, { mode: 448, recursive: true });
25163
- const metadata = await lstat2(directory);
25727
+ async function ensurePrivateDirectory2(directory) {
25728
+ await mkdir2(directory, { mode: 448, recursive: true });
25729
+ const metadata = await lstat3(directory);
25164
25730
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
25165
25731
  throw sessionStateError(
25166
25732
  "unsafe-session-state",
@@ -25173,10 +25739,10 @@ async function ensurePrivateDirectory(directory) {
25173
25739
  "Session state directory has a different owner."
25174
25740
  );
25175
25741
  }
25176
- await chmod2(directory, 448);
25742
+ await chmod3(directory, 448);
25177
25743
  }
25178
- async function assertPrivateRegularFile(filePath) {
25179
- const metadata = await lstat2(filePath);
25744
+ async function assertPrivateRegularFile2(filePath) {
25745
+ const metadata = await lstat3(filePath);
25180
25746
  if (!metadata.isFile() || metadata.isSymbolicLink()) {
25181
25747
  throw sessionStateError("unsafe-session-state", "Session state file is not a regular file.");
25182
25748
  }
@@ -25187,12 +25753,12 @@ async function assertPrivateRegularFile(filePath) {
25187
25753
  throw sessionStateError("unsafe-session-state", "Session state file is not owner-private.");
25188
25754
  }
25189
25755
  }
25190
- async function writeJsonAtomic(filePath, value) {
25191
- const temporary = `${filePath}.${randomUUID()}.tmp`;
25192
- await writeFile(temporary, `${JSON.stringify(value, null, 2)}
25756
+ async function writeJsonAtomic2(filePath, value) {
25757
+ const temporary = `${filePath}.${randomUUID2()}.tmp`;
25758
+ await writeFile2(temporary, `${JSON.stringify(value, null, 2)}
25193
25759
  `, { mode: 384 });
25194
- await rename(temporary, filePath);
25195
- await chmod2(filePath, 384);
25760
+ await rename2(temporary, filePath);
25761
+ await chmod3(filePath, 384);
25196
25762
  }
25197
25763
  async function unlinkIfPresent2(filePath) {
25198
25764
  try {
@@ -25339,6 +25905,7 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
25339
25905
  return await client.executeOperation({
25340
25906
  input: request.input,
25341
25907
  name: request.name,
25908
+ onProgress: context.reportProgress,
25342
25909
  requestId: request.requestId,
25343
25910
  ...request.timeoutMs ? { timeoutMs: request.timeoutMs } : {}
25344
25911
  });
@@ -25347,6 +25914,7 @@ async function startAgentSessionDaemon(spec, dependencies = {}) {
25347
25914
  () => client.executeOperation({
25348
25915
  input: request.input,
25349
25916
  name: request.name,
25917
+ onProgress: context.reportProgress,
25350
25918
  requestId: request.requestId,
25351
25919
  ...request.timeoutMs ? { timeoutMs: request.timeoutMs } : {}
25352
25920
  })
@@ -25383,7 +25951,13 @@ async function createCanvasClient(spec, dependencies) {
25383
25951
  ...dependencies,
25384
25952
  cwd: spec.cwd
25385
25953
  });
25386
- const session = new CanvasSession(config2, spec.projectId);
25954
+ const session = new CanvasSession(config2, spec.projectId, void 0, {
25955
+ workflowPreferences: (actor) => createNodeWorkflowPreferenceStore({
25956
+ accountId: actor.accountId,
25957
+ environment: config2.environment,
25958
+ stateRoot: spec.stateRoot
25959
+ })
25960
+ });
25387
25961
  const client = new CanvasClient(config2, spec.projectId, session);
25388
25962
  await client.connect(spec.clientName);
25389
25963
  return {
@@ -25437,13 +26011,13 @@ function assertRequest(request) {
25437
26011
  code: "invalid-watch-timeout"
25438
26012
  });
25439
26013
  }
25440
- if (request.kind === "interests.patch" && !isRecord4(request.input)) invalidPayload();
26014
+ if (request.kind === "interests.patch" && !isRecord5(request.input)) invalidPayload();
25441
26015
  if (request.kind === "query") {
25442
- if (!isRecord4(request.input) || typeof request.input.target !== "string") invalidPayload();
26016
+ if (!isRecord5(request.input) || typeof request.input.target !== "string") invalidPayload();
25443
26017
  parseCanvasQueryTarget(request.input.target);
25444
26018
  }
25445
26019
  if (request.kind === "operation") {
25446
- if (!isRecord4(request.input) || typeof request.name !== "string" || typeof request.requestId !== "string" || request.requestId.length === 0 || request.timeoutMs !== void 0 && (!Number.isInteger(request.timeoutMs) || request.timeoutMs < 1)) {
26020
+ if (!isRecord5(request.input) || typeof request.name !== "string" || typeof request.requestId !== "string" || request.requestId.length === 0 || request.timeoutMs !== void 0 && (!Number.isInteger(request.timeoutMs) || request.timeoutMs < 1)) {
25447
26021
  invalidPayload();
25448
26022
  }
25449
26023
  }
@@ -25459,7 +26033,7 @@ function invalidPayload() {
25459
26033
  code: "invalid-session-request"
25460
26034
  });
25461
26035
  }
25462
- function isRecord4(value) {
26036
+ function isRecord5(value) {
25463
26037
  return typeof value === "object" && value !== null && !Array.isArray(value);
25464
26038
  }
25465
26039
  function readErrorCode2(error51) {
@@ -25795,6 +26369,11 @@ var CanvasProtocolServer = class {
25795
26369
  const outcome = await this.client.executeOperation({
25796
26370
  input: request.payload.input,
25797
26371
  name: request.payload.name,
26372
+ onProgress: (progress) => this.output.emitMandatory({
26373
+ payload: progress,
26374
+ requestId: request.requestId,
26375
+ type: "operationProgress"
26376
+ }),
25798
26377
  requestId: request.requestId,
25799
26378
  ...request.payload.timeoutMs ? { timeoutMs: request.payload.timeoutMs } : {}
25800
26379
  });
@@ -26191,7 +26770,7 @@ function humanReadableArgName(arg) {
26191
26770
  // ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/command.js
26192
26771
  import { EventEmitter as EventEmitter2 } from "node:events";
26193
26772
  import childProcess from "node:child_process";
26194
- import path4 from "node:path";
26773
+ import path5 from "node:path";
26195
26774
  import fs from "node:fs";
26196
26775
  import process2 from "node:process";
26197
26776
  import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
@@ -28177,9 +28756,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
28177
28756
  args2 = args2.slice();
28178
28757
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
28179
28758
  function findFile(baseDir, baseName) {
28180
- const localBin = path4.resolve(baseDir, baseName);
28759
+ const localBin = path5.resolve(baseDir, baseName);
28181
28760
  if (fs.existsSync(localBin)) return localBin;
28182
- if (sourceExt.includes(path4.extname(baseName))) return void 0;
28761
+ if (sourceExt.includes(path5.extname(baseName))) return void 0;
28183
28762
  const foundExt = sourceExt.find(
28184
28763
  (ext) => fs.existsSync(`${localBin}${ext}`)
28185
28764
  );
@@ -28197,17 +28776,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
28197
28776
  } catch {
28198
28777
  resolvedScriptPath = this._scriptPath;
28199
28778
  }
28200
- executableDir = path4.resolve(
28201
- path4.dirname(resolvedScriptPath),
28779
+ executableDir = path5.resolve(
28780
+ path5.dirname(resolvedScriptPath),
28202
28781
  executableDir
28203
28782
  );
28204
28783
  }
28205
28784
  if (executableDir) {
28206
28785
  let localFile = findFile(executableDir, executableFile);
28207
28786
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
28208
- const legacyName = path4.basename(
28787
+ const legacyName = path5.basename(
28209
28788
  this._scriptPath,
28210
- path4.extname(this._scriptPath)
28789
+ path5.extname(this._scriptPath)
28211
28790
  );
28212
28791
  if (legacyName !== this._name) {
28213
28792
  localFile = findFile(
@@ -28218,7 +28797,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
28218
28797
  }
28219
28798
  executableFile = localFile || executableFile;
28220
28799
  }
28221
- const launchWithNode = sourceExt.includes(path4.extname(executableFile));
28800
+ const launchWithNode = sourceExt.includes(path5.extname(executableFile));
28222
28801
  let proc;
28223
28802
  if (process2.platform !== "win32") {
28224
28803
  if (launchWithNode) {
@@ -29135,7 +29714,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
29135
29714
  * @return {Command}
29136
29715
  */
29137
29716
  nameFromFilename(filename) {
29138
- this._name = path4.basename(filename, path4.extname(filename));
29717
+ this._name = path5.basename(filename, path5.extname(filename));
29139
29718
  return this;
29140
29719
  }
29141
29720
  /**
@@ -29149,9 +29728,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
29149
29728
  * @param {string} [path]
29150
29729
  * @return {(string|null|Command)}
29151
29730
  */
29152
- executableDir(path9) {
29153
- if (path9 === void 0) return this._executableDir;
29154
- this._executableDir = path9;
29731
+ executableDir(path10) {
29732
+ if (path10 === void 0) return this._executableDir;
29733
+ this._executableDir = path10;
29155
29734
  return this;
29156
29735
  }
29157
29736
  /**
@@ -29407,8 +29986,8 @@ function useColor() {
29407
29986
  var program = new Command();
29408
29987
 
29409
29988
  // src/canvas/one-shot.ts
29410
- import { readFile as readFile3 } from "node:fs/promises";
29411
- import path5 from "node:path";
29989
+ import { readFile as readFile4 } from "node:fs/promises";
29990
+ import path6 from "node:path";
29412
29991
  var MAX_INPUT_BYTES = 1024 * 1024;
29413
29992
  var PROCESS_SCOPED_OPERATIONS = /* @__PURE__ */ new Set(["redo", "undo"]);
29414
29993
  var PROCESS_SCOPED_QUERIES = /* @__PURE__ */ new Set(["receipt.detail", "receipts.summary"]);
@@ -29434,6 +30013,9 @@ async function runCanvasOneShot(config2, command, dependencies = {}) {
29434
30013
  input: command.input,
29435
30014
  name: command.name,
29436
30015
  requestId: command.requestId,
30016
+ ...dependencies.onProgress ? {
30017
+ onProgress: (progress) => dependencies.onProgress?.({ ...progress, requestId: command.requestId })
30018
+ } : {},
29437
30019
  ...command.timeoutMs ? { timeoutMs: command.timeoutMs } : {}
29438
30020
  });
29439
30021
  return operationResult(command, outcome);
@@ -29457,9 +30039,9 @@ async function readCanvasOneShotInputOptions(options, dependencies = {}) {
29457
30039
  let source = "{}";
29458
30040
  if (inline !== void 0) source = inline;
29459
30041
  if (file2 !== void 0) {
29460
- const filePath = path5.resolve(dependencies.cwd ?? process.cwd(), file2);
30042
+ const filePath = path6.resolve(dependencies.cwd ?? process.cwd(), file2);
29461
30043
  try {
29462
- source = dependencies.readFile ? await dependencies.readFile(filePath) : await readFile3(filePath, "utf8");
30044
+ source = dependencies.readFile ? await dependencies.readFile(filePath) : await readFile4(filePath, "utf8");
29463
30045
  } catch {
29464
30046
  throw canvasClientFailure(
29465
30047
  "invalid-operation-input",
@@ -29580,9 +30162,9 @@ function formatZodError(error51) {
29580
30162
  }
29581
30163
 
29582
30164
  // src/media/download.ts
29583
- import { createHash as createHash3 } from "node:crypto";
29584
- import { link, lstat as lstat3, open as open2, rename as rename2, stat, unlink as unlink3 } from "node:fs/promises";
29585
- import path6 from "node:path";
30165
+ import { createHash as createHash4 } from "node:crypto";
30166
+ import { link, lstat as lstat4, open as open2, rename as rename3, stat, unlink as unlink3 } from "node:fs/promises";
30167
+ import path7 from "node:path";
29586
30168
 
29587
30169
  // ../../packages/foundation-data/dist/media.js
29588
30170
  var CoreNodeMediaResolutionError = class extends Error {
@@ -29619,21 +30201,24 @@ function resolveDownloadableCoreNodeMedia(coreNode) {
29619
30201
 
29620
30202
  // src/media/download.ts
29621
30203
  var DEFAULT_RESOLUTION_TIMEOUT_MS = 15e3;
30204
+ var DEFAULT_DOWNLOAD_ATTEMPTS = 3;
29622
30205
  var FILE_TYPE_HEADER_BYTES2 = 64 * 1024;
29623
30206
  var MediaDownloadError = class extends Error {
29624
- constructor(code, message, exitCode, retryable) {
30207
+ constructor(code, message, exitCode, retryable, details = {}) {
29625
30208
  super(message);
29626
30209
  this.code = code;
29627
30210
  this.exitCode = exitCode;
29628
30211
  this.retryable = retryable;
30212
+ this.details = details;
29629
30213
  this.name = "MediaDownloadError";
29630
30214
  }
29631
30215
  code;
29632
30216
  exitCode;
29633
30217
  retryable;
30218
+ details;
29634
30219
  };
29635
30220
  async function runCanvasMediaDownload(config2, command, dependencies = {}) {
29636
- const requestedAbsolutePath = path6.resolve(dependencies.cwd ?? process.cwd(), command.outputPath);
30221
+ const requestedAbsolutePath = path7.resolve(dependencies.cwd ?? process.cwd(), command.outputPath);
29637
30222
  let outputTarget;
29638
30223
  try {
29639
30224
  outputTarget = await prepareOutputTarget(
@@ -29645,48 +30230,86 @@ async function runCanvasMediaDownload(config2, command, dependencies = {}) {
29645
30230
  } catch (error51) {
29646
30231
  return failureResult2(command, error51, requestedAbsolutePath);
29647
30232
  }
29648
- let client;
29649
- let media;
29650
- try {
29651
- client = dependencies.mediaDownloadClientFactory?.(config2, command.projectId) ?? new CanvasClient(config2, command.projectId);
29652
- try {
29653
- await client.connect(normalizeCanvasClientName(command.clientName));
29654
- } catch {
29655
- throw new MediaDownloadError(
29656
- "canvas-connection-failed",
29657
- "Unable to establish a ready Project Canvas context.",
29658
- 2,
29659
- true
29660
- );
29661
- }
29662
- throwIfCancelled(dependencies.signal);
29663
- media = await resolveMediaSource(client, command, dependencies.randomUUID);
29664
- } catch (error51) {
29665
- return failureResult2(command, error51, outputTarget.absolutePath);
29666
- } finally {
29667
- client?.close();
30233
+ const maximumAttempts = dependencies.maxAttempts ?? DEFAULT_DOWNLOAD_ATTEMPTS;
30234
+ if (!Number.isInteger(maximumAttempts) || maximumAttempts < 1 || maximumAttempts > 3) {
30235
+ return failureResult2(
30236
+ command,
30237
+ new MediaDownloadError(
30238
+ "invalid-download-attempts",
30239
+ "Download attempt limit must be between 1 and 3.",
30240
+ 1,
30241
+ false,
30242
+ { phase: "setup" }
30243
+ ),
30244
+ outputTarget.absolutePath,
30245
+ 0,
30246
+ 0
30247
+ );
29668
30248
  }
29669
- try {
29670
- const file2 = await transferMedia(config2, media, outputTarget, command, dependencies);
29671
- return {
29672
- exitCode: 0,
29673
- output: {
29674
- file: file2,
29675
- format: "az8.canvas.media-download.v1",
29676
- media: { coreNodeId: media.coreNodeId, mediaType: media.mediaType },
29677
- projectId: command.projectId,
29678
- source: command.source
30249
+ const deadlineAt = command.timeoutMs ? Date.now() + command.timeoutMs : null;
30250
+ let lastError;
30251
+ for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
30252
+ try {
30253
+ throwIfCancelled(dependencies.signal);
30254
+ const remainingMs = remainingDownloadMs(deadlineAt);
30255
+ const attemptCommand = {
30256
+ ...command,
30257
+ ...remainingMs === void 0 ? {} : { timeoutMs: remainingMs }
30258
+ };
30259
+ const media = await resolveMediaForAttempt(config2, attemptCommand, dependencies);
30260
+ const file2 = await transferMedia(config2, media, outputTarget, attemptCommand, dependencies);
30261
+ return {
30262
+ exitCode: 0,
30263
+ output: {
30264
+ attempts: { completed: attempt, maximum: maximumAttempts },
30265
+ file: file2,
30266
+ format: "az8.canvas.media-download.v1",
30267
+ media: { coreNodeId: media.coreNodeId, mediaType: media.mediaType },
30268
+ projectId: command.projectId,
30269
+ source: command.source
30270
+ }
30271
+ };
30272
+ } catch (error51) {
30273
+ lastError = error51;
30274
+ const failure = normalizeDownloadError(error51);
30275
+ if (!failure.retryable || failure.code === "download-cancelled" || failure.code === "download-timed-out" || attempt === maximumAttempts) {
30276
+ return failureResult2(command, failure, outputTarget.absolutePath, attempt, maximumAttempts);
29679
30277
  }
29680
- };
29681
- } catch (error51) {
29682
- return failureResult2(command, error51, outputTarget.absolutePath);
30278
+ try {
30279
+ await waitForDownloadRetry(
30280
+ downloadRetryDelay(attempt, dependencies.random?.() ?? Math.random()),
30281
+ deadlineAt,
30282
+ dependencies
30283
+ );
30284
+ } catch (retryError) {
30285
+ return failureResult2(
30286
+ command,
30287
+ retryError,
30288
+ outputTarget.absolutePath,
30289
+ attempt,
30290
+ maximumAttempts
30291
+ );
30292
+ }
30293
+ }
29683
30294
  }
30295
+ return failureResult2(
30296
+ command,
30297
+ lastError,
30298
+ outputTarget.absolutePath,
30299
+ maximumAttempts,
30300
+ maximumAttempts
30301
+ );
30302
+ }
30303
+ function downloadRetryDelay(attempt, random) {
30304
+ const boundedRandom = Number.isFinite(random) ? Math.max(0, Math.min(1, random)) : 0.5;
30305
+ const base = Math.min(250 * 2 ** (attempt - 1), 1e3);
30306
+ return Math.min(1e3, Math.round(base * (0.75 + boundedRandom * 0.5)));
29684
30307
  }
29685
30308
  function formatCanvasMediaDownloadResult(result3) {
29686
30309
  return `${JSON.stringify(result3.output, null, 2)}
29687
30310
  `;
29688
30311
  }
29689
- async function resolveMediaSource(client, command, randomUUID4) {
30312
+ async function resolveMediaSource(client, command, randomUUID5) {
29690
30313
  let coreNodeId;
29691
30314
  if (command.source.kind === "view-node") {
29692
30315
  try {
@@ -29696,7 +30319,8 @@ async function resolveMediaSource(client, command, randomUUID4) {
29696
30319
  "view-node-media-unavailable",
29697
30320
  safeCanvasMessage(error51),
29698
30321
  1,
29699
- false
30322
+ false,
30323
+ { cause: sanitizeDownloadCause(error51), phase: "source-resolution" }
29700
30324
  );
29701
30325
  }
29702
30326
  } else {
@@ -29705,7 +30329,7 @@ async function resolveMediaSource(client, command, randomUUID4) {
29705
30329
  const outcome = await client.executeOperation({
29706
30330
  input: { coreNodeId },
29707
30331
  name: "getCoreNode",
29708
- requestId: `media-download-resolve-${randomUUID4?.() ?? crypto.randomUUID()}`,
30332
+ requestId: `media-download-resolve-${randomUUID5?.() ?? crypto.randomUUID()}`,
29709
30333
  timeoutMs: command.timeoutMs ?? DEFAULT_RESOLUTION_TIMEOUT_MS
29710
30334
  });
29711
30335
  if (outcome.receipt.status !== "succeeded") {
@@ -29714,7 +30338,8 @@ async function resolveMediaSource(client, command, randomUUID4) {
29714
30338
  "core-node-resolution-failed",
29715
30339
  outcome.receipt.error?.message ?? `Core Node ${coreNodeId} could not be resolved.`,
29716
30340
  indeterminate ? 2 : 1,
29717
- indeterminate
30341
+ indeterminate,
30342
+ { phase: "core-node-resolution" }
29718
30343
  );
29719
30344
  }
29720
30345
  const coreNode = parseCoreNode(outcome.receipt.result?.coreNode, coreNodeId);
@@ -29733,7 +30358,8 @@ function parseCoreNode(value, expectedId) {
29733
30358
  "core-node-response-invalid",
29734
30359
  `Core Node ${expectedId} returned an invalid record.`,
29735
30360
  2,
29736
- true
30361
+ true,
30362
+ { phase: "core-node-resolution" }
29737
30363
  );
29738
30364
  }
29739
30365
  const record2 = value;
@@ -29742,7 +30368,8 @@ function parseCoreNode(value, expectedId) {
29742
30368
  "core-node-response-invalid",
29743
30369
  `Core Node ${expectedId} returned an invalid record.`,
29744
30370
  2,
29745
- true
30371
+ true,
30372
+ { phase: "core-node-resolution" }
29746
30373
  );
29747
30374
  }
29748
30375
  return value;
@@ -29753,11 +30380,12 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
29753
30380
  "invalid-output-path",
29754
30381
  "Download output path must not be blank.",
29755
30382
  1,
29756
- false
30383
+ false,
30384
+ { phase: "output-setup" }
29757
30385
  );
29758
30386
  }
29759
- const absolutePath = path6.resolve(cwd, requestedPath);
29760
- const parentPath = path6.dirname(absolutePath);
30387
+ const absolutePath = path7.resolve(cwd, requestedPath);
30388
+ const parentPath = path7.dirname(absolutePath);
29761
30389
  let parent;
29762
30390
  try {
29763
30391
  parent = await stat(parentPath);
@@ -29766,7 +30394,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
29766
30394
  "output-parent-unavailable",
29767
30395
  "Download output parent directory does not exist or cannot be accessed.",
29768
30396
  1,
29769
- false
30397
+ false,
30398
+ { phase: "output-setup" }
29770
30399
  );
29771
30400
  }
29772
30401
  if (!parent.isDirectory()) {
@@ -29774,19 +30403,21 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
29774
30403
  "output-parent-not-directory",
29775
30404
  "Download output parent is not a directory.",
29776
30405
  1,
29777
- false
30406
+ false,
30407
+ { phase: "output-setup" }
29778
30408
  );
29779
30409
  }
29780
30410
  let existing;
29781
30411
  try {
29782
- existing = await lstat3(absolutePath);
30412
+ existing = await lstat4(absolutePath);
29783
30413
  } catch (error51) {
29784
30414
  if (readFsCode2(error51) !== "ENOENT") {
29785
30415
  throw new MediaDownloadError(
29786
30416
  "output-target-unavailable",
29787
30417
  "Download output target cannot be inspected.",
29788
30418
  1,
29789
- false
30419
+ false,
30420
+ { phase: "output-setup" }
29790
30421
  );
29791
30422
  }
29792
30423
  }
@@ -29795,7 +30426,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
29795
30426
  "output-target-exists",
29796
30427
  "Download output target already exists; pass --overwrite to replace a regular file.",
29797
30428
  1,
29798
- false
30429
+ false,
30430
+ { phase: "output-setup" }
29799
30431
  );
29800
30432
  }
29801
30433
  if (existing && (!existing.isFile() || existing.isSymbolicLink())) {
@@ -29803,7 +30435,8 @@ async function prepareOutputTarget(requestedPath, overwrite, cwd) {
29803
30435
  "output-target-not-regular-file",
29804
30436
  "Download output target must be a regular file and must not be a symbolic link.",
29805
30437
  1,
29806
- false
30438
+ false,
30439
+ { phase: "output-setup" }
29807
30440
  );
29808
30441
  }
29809
30442
  return { absolutePath, existed: Boolean(existing), parentPath };
@@ -29814,9 +30447,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29814
30447
  let response;
29815
30448
  try {
29816
30449
  response = await config2.fetch(media.url, { redirect: "follow", signal: deadline.signal });
29817
- } catch {
30450
+ } catch (error51) {
29818
30451
  deadline.cleanup();
29819
- throw transferFailure(deadline);
30452
+ throw transferFailure(deadline, error51);
29820
30453
  }
29821
30454
  if (!response.ok) {
29822
30455
  deadline.cleanup();
@@ -29825,7 +30458,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29825
30458
  "media-http-error",
29826
30459
  `Media server returned HTTP ${response.status}.`,
29827
30460
  retryable ? 2 : 1,
29828
- retryable
30461
+ retryable,
30462
+ { phase: "media-transfer" }
29829
30463
  );
29830
30464
  }
29831
30465
  const contentType = normalizeContentType(response.headers.get("content-type"));
@@ -29835,8 +30469,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29835
30469
  throw new MediaDownloadError(
29836
30470
  "media-content-type-mismatch",
29837
30471
  `Media response is ${headerFamily}, but Core Node ${media.coreNodeId} is ${media.mediaType}.`,
29838
- 2,
29839
- true
30472
+ 1,
30473
+ false,
30474
+ { phase: "media-verification" }
29840
30475
  );
29841
30476
  }
29842
30477
  if (contentType === "text/html" || contentType === "application/json") {
@@ -29844,8 +30479,9 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29844
30479
  throw new MediaDownloadError(
29845
30480
  "media-content-type-mismatch",
29846
30481
  "Media response returned a non-media document.",
29847
- 2,
29848
- true
30482
+ 1,
30483
+ false,
30484
+ { phase: "media-verification" }
29849
30485
  );
29850
30486
  }
29851
30487
  const remoteContentLength = readContentLength(response);
@@ -29878,7 +30514,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29878
30514
  "output-finalize-failed",
29879
30515
  "Temporary download file could not be finalized.",
29880
30516
  1,
29881
- false
30517
+ false,
30518
+ { phase: "output-finalize" }
29882
30519
  );
29883
30520
  }
29884
30521
  let detected;
@@ -29889,7 +30526,8 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29889
30526
  "media-verification-failed",
29890
30527
  "Downloaded media could not be inspected safely.",
29891
30528
  1,
29892
- false
30529
+ false,
30530
+ { phase: "media-verification" }
29893
30531
  );
29894
30532
  }
29895
30533
  validateDownloadedMedia(media, contentType, detected);
@@ -29911,12 +30549,13 @@ async function transferMedia(config2, media, outputTarget, command, dependencies
29911
30549
  } catch (error51) {
29912
30550
  await temporary.handle.close().catch(() => void 0);
29913
30551
  if (error51 instanceof MediaDownloadError) throw error51;
29914
- if (deadline.signal.aborted) throw transferFailure(deadline);
30552
+ if (deadline.signal.aborted) throw transferFailure(deadline, error51);
29915
30553
  throw new MediaDownloadError(
29916
30554
  "media-download-internal-failed",
29917
30555
  "Media download could not complete its local processing.",
29918
30556
  2,
29919
- true
30557
+ true,
30558
+ { cause: sanitizeDownloadCause(error51), phase: "media-transfer" }
29920
30559
  );
29921
30560
  } finally {
29922
30561
  deadline.cleanup();
@@ -29935,9 +30574,9 @@ async function detectFileTypeFromFileHeader(filePath) {
29935
30574
  }
29936
30575
  async function openTemporaryFile(outputTarget, identity) {
29937
30576
  const safeIdentity = identity.replace(/[^a-z0-9-]/gi, "").slice(0, 64) || "download";
29938
- const basename = path6.basename(outputTarget.absolutePath);
30577
+ const basename = path7.basename(outputTarget.absolutePath);
29939
30578
  for (let attempt = 0; attempt < 3; attempt += 1) {
29940
- const temporaryPath = path6.join(
30579
+ const temporaryPath = path7.join(
29941
30580
  outputTarget.parentPath,
29942
30581
  `.${basename}.az8-${safeIdentity}-${attempt}.part`
29943
30582
  );
@@ -29967,11 +30606,12 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
29967
30606
  "media-response-body-missing",
29968
30607
  "Media response has no readable body.",
29969
30608
  2,
29970
- true
30609
+ true,
30610
+ { phase: "media-transfer" }
29971
30611
  );
29972
30612
  }
29973
30613
  const reader = response.body.getReader();
29974
- const hash2 = createHash3("sha256");
30614
+ const hash2 = createHash4("sha256");
29975
30615
  let bytes = 0;
29976
30616
  try {
29977
30617
  while (true) {
@@ -29985,16 +30625,17 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
29985
30625
  }
29986
30626
  } catch (error51) {
29987
30627
  if (error51 instanceof MediaDownloadError) throw error51;
29988
- if (deadline.signal.aborted) throw transferFailure(deadline);
30628
+ if (deadline.signal.aborted) throw transferFailure(deadline, error51);
29989
30629
  if (readFsCode2(error51)) {
29990
30630
  throw new MediaDownloadError(
29991
30631
  "output-write-failed",
29992
30632
  "Temporary download file could not be written completely.",
29993
30633
  1,
29994
- false
30634
+ false,
30635
+ { cause: sanitizeDownloadCause(error51), phase: "output-write" }
29995
30636
  );
29996
30637
  }
29997
- throw transferFailure(deadline);
30638
+ throw transferFailure(deadline, error51);
29998
30639
  } finally {
29999
30640
  reader.releaseLock();
30000
30641
  }
@@ -30003,7 +30644,8 @@ async function streamResponseToFile(response, handle, deadline, expectedBytes) {
30003
30644
  "media-content-length-mismatch",
30004
30645
  `Media response declared ${expectedBytes} bytes but delivered ${bytes}.`,
30005
30646
  2,
30006
- true
30647
+ true,
30648
+ { phase: "media-transfer" }
30007
30649
  );
30008
30650
  }
30009
30651
  return { bytes, sha256: hash2.digest("hex") };
@@ -30017,7 +30659,8 @@ async function writeAll(handle, value) {
30017
30659
  "output-write-stalled",
30018
30660
  "Temporary download file stopped accepting data.",
30019
30661
  1,
30020
- false
30662
+ false,
30663
+ { phase: "output-write" }
30021
30664
  );
30022
30665
  }
30023
30666
  offset += bytesWritten;
@@ -30029,8 +30672,9 @@ function validateDownloadedMedia(media, contentType, detected) {
30029
30672
  throw new MediaDownloadError(
30030
30673
  "media-content-mismatch",
30031
30674
  `Downloaded file is ${detectedFamily}, but Core Node ${media.coreNodeId} is ${media.mediaType}.`,
30032
- 2,
30033
- true
30675
+ 1,
30676
+ false,
30677
+ { phase: "media-verification" }
30034
30678
  );
30035
30679
  }
30036
30680
  const headerMatches = readMediaFamily(contentType) === media.mediaType;
@@ -30039,15 +30683,16 @@ function validateDownloadedMedia(media, contentType, detected) {
30039
30683
  throw new MediaDownloadError(
30040
30684
  "media-content-unverified",
30041
30685
  `Downloaded file could not be verified as ${media.mediaType} media.`,
30042
- 2,
30043
- true
30686
+ 1,
30687
+ false,
30688
+ { phase: "media-verification" }
30044
30689
  );
30045
30690
  }
30046
30691
  }
30047
30692
  async function commitTemporaryFile(temporaryPath, outputTarget, overwrite) {
30048
30693
  try {
30049
30694
  if (overwrite) {
30050
- await rename2(temporaryPath, outputTarget.absolutePath);
30695
+ await rename3(temporaryPath, outputTarget.absolutePath);
30051
30696
  return;
30052
30697
  }
30053
30698
  await link(temporaryPath, outputTarget.absolutePath);
@@ -30058,14 +30703,16 @@ async function commitTemporaryFile(temporaryPath, outputTarget, overwrite) {
30058
30703
  "output-target-exists",
30059
30704
  "Download output target was created before the download could be committed.",
30060
30705
  1,
30061
- false
30706
+ false,
30707
+ { phase: "output-commit" }
30062
30708
  );
30063
30709
  }
30064
30710
  throw new MediaDownloadError(
30065
30711
  "output-commit-failed",
30066
30712
  "Verified media could not be committed to the output path.",
30067
30713
  1,
30068
- false
30714
+ false,
30715
+ { cause: sanitizeDownloadCause(error51), phase: "output-commit" }
30069
30716
  );
30070
30717
  }
30071
30718
  }
@@ -30089,18 +30736,29 @@ function createTransferSignal(external, timeoutMs) {
30089
30736
  signal: controller.signal
30090
30737
  };
30091
30738
  }
30092
- function transferFailure(deadline) {
30739
+ function transferFailure(deadline, cause) {
30093
30740
  if (deadline.external?.aborted) {
30094
- return new MediaDownloadError("download-cancelled", "Media download was cancelled.", 130, true);
30741
+ return new MediaDownloadError(
30742
+ "download-cancelled",
30743
+ "Media download was cancelled.",
30744
+ 130,
30745
+ true,
30746
+ {
30747
+ phase: "media-transfer"
30748
+ }
30749
+ );
30095
30750
  }
30096
30751
  if (deadline.isTimedOut()) {
30097
- return new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true);
30752
+ return new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
30753
+ phase: "media-transfer"
30754
+ });
30098
30755
  }
30099
30756
  return new MediaDownloadError(
30100
30757
  "media-network-failed",
30101
30758
  "Media response could not be downloaded completely.",
30102
30759
  2,
30103
- true
30760
+ true,
30761
+ { cause: sanitizeDownloadCause(cause), phase: "media-transfer" }
30104
30762
  );
30105
30763
  }
30106
30764
  function throwIfCancelled(signal) {
@@ -30129,23 +30787,111 @@ function readMediaFamily(value) {
30129
30787
  if (value?.startsWith("video/")) return "video";
30130
30788
  return null;
30131
30789
  }
30132
- function failureResult2(command, error51, absolutePath) {
30133
- const failure = error51 instanceof MediaDownloadError ? error51 : new MediaDownloadError("media-download-failed", safeCanvasMessage(error51), 2, true);
30790
+ function failureResult2(command, error51, absolutePath, attemptsCompleted = 1, maximumAttempts = 1) {
30791
+ const failure = normalizeDownloadError(error51);
30134
30792
  return {
30135
30793
  exitCode: failure.exitCode,
30136
30794
  output: {
30137
30795
  error: {
30138
30796
  code: failure.code,
30139
30797
  message: safeCanvasMessage(failure),
30140
- retryable: failure.retryable
30798
+ ...failure.details.phase ? { phase: failure.details.phase } : {},
30799
+ retryable: failure.retryable,
30800
+ ...failure.details.cause ? { cause: failure.details.cause } : {}
30141
30801
  },
30142
30802
  format: "az8.canvas.media-download.v1",
30143
- outputPath: absolutePath ?? path6.resolve(command.outputPath),
30803
+ localState: {
30804
+ destinationCommitted: false,
30805
+ partialFileRetained: false
30806
+ },
30807
+ outputPath: absolutePath ?? path7.resolve(command.outputPath),
30144
30808
  projectId: command.projectId,
30809
+ attempts: { completed: attemptsCompleted, maximum: maximumAttempts },
30810
+ remoteState: "unverified",
30145
30811
  source: command.source
30146
30812
  }
30147
30813
  };
30148
30814
  }
30815
+ async function resolveMediaForAttempt(config2, command, dependencies) {
30816
+ const client = dependencies.mediaDownloadClientFactory?.(config2, command.projectId) ?? new CanvasClient(config2, command.projectId);
30817
+ try {
30818
+ try {
30819
+ await client.connect(normalizeCanvasClientName(command.clientName));
30820
+ } catch (error51) {
30821
+ throw new MediaDownloadError(
30822
+ "canvas-connection-failed",
30823
+ "Unable to establish a ready Project Canvas context.",
30824
+ 2,
30825
+ true,
30826
+ { cause: sanitizeDownloadCause(error51), phase: "canvas-connect" }
30827
+ );
30828
+ }
30829
+ throwIfCancelled(dependencies.signal);
30830
+ return await resolveMediaSource(client, command, dependencies.randomUUID);
30831
+ } finally {
30832
+ client.close();
30833
+ }
30834
+ }
30835
+ function normalizeDownloadError(error51) {
30836
+ return error51 instanceof MediaDownloadError ? error51 : new MediaDownloadError("media-download-failed", safeCanvasMessage(error51), 2, true, {
30837
+ cause: sanitizeDownloadCause(error51),
30838
+ phase: "unknown"
30839
+ });
30840
+ }
30841
+ function remainingDownloadMs(deadlineAt) {
30842
+ if (deadlineAt === null) return void 0;
30843
+ const remaining = deadlineAt - Date.now();
30844
+ if (remaining < 1) {
30845
+ throw new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
30846
+ phase: "retry-wait"
30847
+ });
30848
+ }
30849
+ return remaining;
30850
+ }
30851
+ async function waitForDownloadRetry(delayMs, deadlineAt, dependencies) {
30852
+ const remaining = remainingDownloadMs(deadlineAt);
30853
+ if (remaining !== void 0 && remaining <= delayMs) {
30854
+ throw new MediaDownloadError("download-timed-out", "Media download timed out.", 2, true, {
30855
+ phase: "retry-wait"
30856
+ });
30857
+ }
30858
+ await (dependencies.sleep ?? abortableSleep)(delayMs, dependencies.signal);
30859
+ throwIfCancelled(dependencies.signal);
30860
+ }
30861
+ async function abortableSleep(delayMs, signal) {
30862
+ if (signal?.aborted) return;
30863
+ await new Promise((resolve) => {
30864
+ const timeout = setTimeout(done, delayMs);
30865
+ const onAbort = () => done();
30866
+ function done() {
30867
+ clearTimeout(timeout);
30868
+ signal?.removeEventListener("abort", onAbort);
30869
+ resolve();
30870
+ }
30871
+ signal?.addEventListener("abort", onAbort, { once: true });
30872
+ });
30873
+ }
30874
+ function sanitizeDownloadCause(error51) {
30875
+ if (!error51 || typeof error51 !== "object") return void 0;
30876
+ const record2 = error51;
30877
+ const cause = record2.cause && typeof record2.cause === "object" ? record2.cause : record2;
30878
+ const message = cause instanceof Error ? cause.message : typeof cause.message === "string" ? cause.message : error51 instanceof Error ? error51.message : void 0;
30879
+ const code = typeof cause.code === "string" || typeof cause.code === "number" ? String(cause.code) : void 0;
30880
+ const closeCode = typeof cause.closeCode === "number" ? cause.closeCode : typeof cause.websocketCloseCode === "number" ? cause.websocketCloseCode : void 0;
30881
+ const closeReason = typeof cause.closeReason === "string" ? cause.closeReason : typeof cause.websocketCloseReason === "string" ? cause.websocketCloseReason : void 0;
30882
+ const sanitizedMessage = message ? sanitizeTransportDetail(message) : void 0;
30883
+ const sanitizedReason = closeReason ? sanitizeTransportDetail(closeReason) : void 0;
30884
+ if (!code && !closeCode && !sanitizedMessage && !sanitizedReason) return void 0;
30885
+ return {
30886
+ ...code ? { code } : {},
30887
+ ...sanitizedMessage ? { message: sanitizedMessage } : {},
30888
+ ...closeCode ? { websocketCloseCode: closeCode } : {},
30889
+ ...sanitizedReason ? { websocketCloseReason: sanitizedReason } : {}
30890
+ };
30891
+ }
30892
+ function sanitizeTransportDetail(value) {
30893
+ 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]");
30894
+ }
30149
30895
  function readFsCode2(error51) {
30150
30896
  return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : void 0;
30151
30897
  }
@@ -30222,8 +30968,8 @@ function canvasOneShotSetupError(kind, projectId, capability, error51) {
30222
30968
  }
30223
30969
 
30224
30970
  // src/commands/canvas.ts
30225
- import { randomUUID as randomUUID2 } from "node:crypto";
30226
- import path7 from "node:path";
30971
+ import { randomUUID as randomUUID3 } from "node:crypto";
30972
+ import path8 from "node:path";
30227
30973
 
30228
30974
  // src/integrations/snapshot-reader.ts
30229
30975
  function createAz8StudioSnapshotReader(config2) {
@@ -31044,10 +31790,10 @@ function mergeDefs(...defs) {
31044
31790
  function cloneDef(schema) {
31045
31791
  return mergeDefs(schema._zod.def);
31046
31792
  }
31047
- function getElementAtPath(obj, path9) {
31048
- if (!path9)
31793
+ function getElementAtPath(obj, path10) {
31794
+ if (!path10)
31049
31795
  return obj;
31050
- return path9.reduce((acc, key) => acc?.[key], obj);
31796
+ return path10.reduce((acc, key) => acc?.[key], obj);
31051
31797
  }
31052
31798
  function promiseAllObject(promisesObj) {
31053
31799
  const keys2 = Object.keys(promisesObj);
@@ -31456,11 +32202,11 @@ function explicitlyAborted(x, startIndex = 0) {
31456
32202
  }
31457
32203
  return false;
31458
32204
  }
31459
- function prefixIssues(path9, issues) {
32205
+ function prefixIssues(path10, issues) {
31460
32206
  return issues.map((iss) => {
31461
32207
  var _a3;
31462
32208
  (_a3 = iss).path ?? (_a3.path = []);
31463
- iss.path.unshift(path9);
32209
+ iss.path.unshift(path10);
31464
32210
  return iss;
31465
32211
  });
31466
32212
  }
@@ -31607,16 +32353,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
31607
32353
  }
31608
32354
  function formatError(error51, mapper = (issue2) => issue2.message) {
31609
32355
  const fieldErrors = { _errors: [] };
31610
- const processError = (error52, path9 = []) => {
32356
+ const processError = (error52, path10 = []) => {
31611
32357
  for (const issue2 of error52.issues) {
31612
32358
  if (issue2.code === "invalid_union" && issue2.errors.length) {
31613
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
32359
+ issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
31614
32360
  } else if (issue2.code === "invalid_key") {
31615
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
32361
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
31616
32362
  } else if (issue2.code === "invalid_element") {
31617
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
32363
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
31618
32364
  } else {
31619
- const fullpath = [...path9, ...issue2.path];
32365
+ const fullpath = [...path10, ...issue2.path];
31620
32366
  if (fullpath.length === 0) {
31621
32367
  fieldErrors._errors.push(mapper(issue2));
31622
32368
  } else {
@@ -31643,17 +32389,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
31643
32389
  }
31644
32390
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
31645
32391
  const result3 = { errors: [] };
31646
- const processError = (error52, path9 = []) => {
32392
+ const processError = (error52, path10 = []) => {
31647
32393
  var _a3, _b;
31648
32394
  for (const issue2 of error52.issues) {
31649
32395
  if (issue2.code === "invalid_union" && issue2.errors.length) {
31650
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
32396
+ issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
31651
32397
  } else if (issue2.code === "invalid_key") {
31652
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
32398
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
31653
32399
  } else if (issue2.code === "invalid_element") {
31654
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
32400
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
31655
32401
  } else {
31656
- const fullpath = [...path9, ...issue2.path];
32402
+ const fullpath = [...path10, ...issue2.path];
31657
32403
  if (fullpath.length === 0) {
31658
32404
  result3.errors.push(mapper(issue2));
31659
32405
  continue;
@@ -31685,8 +32431,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
31685
32431
  }
31686
32432
  function toDotPath(_path) {
31687
32433
  const segs = [];
31688
- const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
31689
- for (const seg of path9) {
32434
+ const path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
32435
+ for (const seg of path10) {
31690
32436
  if (typeof seg === "number")
31691
32437
  segs.push(`[${seg}]`);
31692
32438
  else if (typeof seg === "symbol")
@@ -44378,13 +45124,13 @@ function resolveRef(ref, ctx) {
44378
45124
  if (!ref.startsWith("#")) {
44379
45125
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
44380
45126
  }
44381
- const path9 = ref.slice(1).split("/").filter(Boolean);
44382
- if (path9.length === 0) {
45127
+ const path10 = ref.slice(1).split("/").filter(Boolean);
45128
+ if (path10.length === 0) {
44383
45129
  return ctx.rootSchema;
44384
45130
  }
44385
45131
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
44386
- if (path9[0] === defsKey) {
44387
- const key = path9[1];
45132
+ if (path10[0] === defsKey) {
45133
+ const key = path10[1];
44388
45134
  if (!key || !ctx.defs[key]) {
44389
45135
  throw new Error(`Reference not found: ${ref}`);
44390
45136
  }
@@ -44954,7 +45700,7 @@ function registerCanvasCommands(program2, dependencies, setResult) {
44954
45700
  return "invalid-option";
44955
45701
  });
44956
45702
  const config2 = await resolveCliConfig(readConfigOptions(command), dependencies);
44957
- const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID2();
45703
+ const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID3();
44958
45704
  const result3 = await runCanvasOneShot(
44959
45705
  config2,
44960
45706
  {
@@ -44963,7 +45709,7 @@ function registerCanvasCommands(program2, dependencies, setResult) {
44963
45709
  idempotencyKey: options.idempotencyKey,
44964
45710
  ...options.name ? { name: options.name } : {},
44965
45711
  ...options.positionX !== void 0 && options.positionY !== void 0 ? { position: { x: options.positionX, y: options.positionY } } : {},
44966
- source: path7.resolve(dependencies.cwd ?? process.cwd(), options.file)
45712
+ source: path8.resolve(dependencies.cwd ?? process.cwd(), options.file)
44967
45713
  },
44968
45714
  kind: "operation",
44969
45715
  name: "uploadMedia",
@@ -45039,7 +45785,7 @@ function registerCanvasCommands(program2, dependencies, setResult) {
45039
45785
  (issue2) => issue2.path[0] === "input" ? "invalid-input-source" : "invalid-option"
45040
45786
  );
45041
45787
  const config2 = await resolveCliConfig(readConfigOptions(command), dependencies);
45042
- const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID2();
45788
+ const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID3();
45043
45789
  const result3 = await runCanvasOneShot(
45044
45790
  config2,
45045
45791
  {
@@ -45064,7 +45810,7 @@ function registerCanvasCommands(program2, dependencies, setResult) {
45064
45810
  // package.json
45065
45811
  var package_default = {
45066
45812
  name: "@everfir/az8-cli",
45067
- version: "0.3.0-preview.2",
45813
+ version: "0.3.0",
45068
45814
  description: "Semantic Project Canvas client for AZ8 Studio agents.",
45069
45815
  license: "UNLICENSED",
45070
45816
  type: "module",
@@ -45080,7 +45826,7 @@ var package_default = {
45080
45826
  main: "./dist/main.js",
45081
45827
  publishConfig: {
45082
45828
  access: "public",
45083
- tag: "preview"
45829
+ tag: "latest"
45084
45830
  },
45085
45831
  scripts: {
45086
45832
  build: `pnpm build:dependencies && rm -rf dist && esbuild src/main.ts --bundle --platform=node --format=esm --target=node22.12 --banner:js='import { createRequire } from "node:module"; const require = createRequire(import.meta.url);' --outfile=dist/main.js`,
@@ -45131,7 +45877,7 @@ function readAz8CliVersion() {
45131
45877
  }
45132
45878
 
45133
45879
  // src/guide/agent-guide.ts
45134
- import { readFile as readFile4 } from "node:fs/promises";
45880
+ import { readFile as readFile5 } from "node:fs/promises";
45135
45881
  var AGENT_GUIDE_URLS = [
45136
45882
  new URL("../skills/az8-cli/SKILL.md", import.meta.url),
45137
45883
  new URL("../../skills/az8-cli/SKILL.md", import.meta.url)
@@ -45164,7 +45910,7 @@ async function renderAgentGuide(options) {
45164
45910
  async function readPackagedGuide() {
45165
45911
  for (const candidate of AGENT_GUIDE_URLS) {
45166
45912
  try {
45167
- return await readFile4(candidate, "utf8");
45913
+ return await readFile5(candidate, "utf8");
45168
45914
  } catch (error51) {
45169
45915
  if (error51.code !== "ENOENT") throw error51;
45170
45916
  }
@@ -45276,11 +46022,11 @@ async function withProjectClient(command, dependencies, setResult, action) {
45276
46022
  }
45277
46023
 
45278
46024
  // src/commands/session.ts
45279
- import { randomUUID as randomUUID3 } from "node:crypto";
46025
+ import { randomUUID as randomUUID4 } from "node:crypto";
45280
46026
 
45281
46027
  // src/agent-session/manager.ts
45282
46028
  import { spawn } from "node:child_process";
45283
- import path8 from "node:path";
46029
+ import path9 from "node:path";
45284
46030
  var AgentSessionManager = class {
45285
46031
  constructor(options = {}) {
45286
46032
  this.options = options;
@@ -45308,7 +46054,7 @@ var AgentSessionManager = class {
45308
46054
  const descriptorPath = this.store.descriptorPath(sessionId);
45309
46055
  const config2 = {
45310
46056
  ...input.config,
45311
- ...input.config.envFile ? { envFile: path8.resolve(this.store.cwd, input.config.envFile) } : {}
46057
+ ...input.config.envFile ? { envFile: path9.resolve(this.store.cwd, input.config.envFile) } : {}
45312
46058
  };
45313
46059
  const descriptor2 = {
45314
46060
  clientName,
@@ -45427,7 +46173,8 @@ var AgentSessionManager = class {
45427
46173
  descriptor2.socketPath,
45428
46174
  request,
45429
46175
  options.timeoutMs ?? 5e3,
45430
- options.signal
46176
+ options.signal,
46177
+ options.onProgress
45431
46178
  ).catch((error51) => {
45432
46179
  if (isRemoteFailure(error51)) throw error51;
45433
46180
  throw managerError(
@@ -45786,7 +46533,7 @@ function registerSessionCommands(program2, dependencies, setResult) {
45786
46533
  ...recordOptions(rawOptions),
45787
46534
  session: readSessionOption(command)
45788
46535
  });
45789
- const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID3();
46536
+ const requestId = options.requestId ?? dependencies.randomUUID?.() ?? randomUUID4();
45790
46537
  const { descriptor: descriptor2, value } = await manager.call(
45791
46538
  {
45792
46539
  input: await readCanvasOneShotInputOptions(options, dependencies),
@@ -45795,7 +46542,11 @@ function registerSessionCommands(program2, dependencies, setResult) {
45795
46542
  requestId,
45796
46543
  ...options.timeoutMs ? { timeoutMs: options.timeoutMs } : {}
45797
46544
  },
45798
- { sessionId: options.session, timeoutMs: (options.timeoutMs ?? 15e3) + 5e3 }
46545
+ {
46546
+ onProgress: (progress) => dependencies.onProgress?.({ ...progress, requestId }),
46547
+ sessionId: options.session,
46548
+ timeoutMs: (options.timeoutMs ?? 15e3) + 5e3
46549
+ }
45799
46550
  );
45800
46551
  const receipt = readOperationReceipt(value);
45801
46552
  const exitCode = receipt.status === "succeeded" ? 0 : receipt.error?.outcome === "indeterminate" ? 2 : 1;
@@ -45862,7 +46613,7 @@ function publicDescriptor(descriptor2) {
45862
46613
  };
45863
46614
  }
45864
46615
  function requireWatchDelivery(value) {
45865
- if (!("result" in value) || !isRecord5(value.result)) {
46616
+ if (!("result" in value) || !isRecord6(value.result)) {
45866
46617
  throw new Error("Agent Session returned an invalid Watch delivery.");
45867
46618
  }
45868
46619
  return value;
@@ -45877,7 +46628,7 @@ function readCanvasSummary(value) {
45877
46628
  return "canvas" in value ? value.canvas : value;
45878
46629
  }
45879
46630
  function readOperationReceipt(value) {
45880
- if (!("receipt" in value) || !isRecord5(value.receipt)) {
46631
+ if (!("receipt" in value) || !isRecord6(value.receipt)) {
45881
46632
  throw new Error("Agent Session returned an invalid Operation Receipt.");
45882
46633
  }
45883
46634
  return value.receipt;
@@ -45900,11 +46651,11 @@ function sessionError(error51) {
45900
46651
  `
45901
46652
  };
45902
46653
  }
45903
- function isRecord5(value) {
46654
+ function isRecord6(value) {
45904
46655
  return typeof value === "object" && value !== null && !Array.isArray(value);
45905
46656
  }
45906
46657
  function recordOptions(value) {
45907
- return isRecord5(value) ? value : {};
46658
+ return isRecord6(value) ? value : {};
45908
46659
  }
45909
46660
 
45910
46661
  // src/cli/program.ts
@@ -45999,6 +46750,12 @@ process.once("SIGINT", abortOnSignal);
45999
46750
  process.once("SIGTERM", abortOnSignal);
46000
46751
  try {
46001
46752
  const result3 = await runAz8Cli(process.argv.slice(2), {
46753
+ onProgress(progress) {
46754
+ process.stderr.write(
46755
+ `${JSON.stringify({ format: "az8.operation-progress.v1", ...progress })}
46756
+ `
46757
+ );
46758
+ },
46002
46759
  persistentCanvas: runPersistentCanvas,
46003
46760
  signal: abortController.signal
46004
46761
  });