oc-browser-relay 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22641,6 +22641,7 @@ var ERROR_CODES = {
22641
22641
  // 页面类
22642
22642
  ERR_PAGE_NOT_SUPPORTED: "ERR_PAGE_NOT_SUPPORTED",
22643
22643
  ERR_PAGE_NOT_READY: "ERR_PAGE_NOT_READY",
22644
+ ERR_PAGE_BLOCKED: "ERR_PAGE_BLOCKED",
22644
22645
  ERR_PAGE_CHANGED: "ERR_PAGE_CHANGED",
22645
22646
  ERR_FIELD_NOT_FOUND: "ERR_FIELD_NOT_FOUND",
22646
22647
  ERR_ACTION_NOT_FOUND: "ERR_ACTION_NOT_FOUND",
@@ -23060,20 +23061,6 @@ var TemplateExecutor = class _TemplateExecutor {
23060
23061
  }
23061
23062
  return obj;
23062
23063
  }
23063
- evaluateCondition(condition, data) {
23064
- try {
23065
- if (condition.includes(".length")) {
23066
- const fieldName = condition.replace(".length", "").trim();
23067
- const value2 = data[fieldName];
23068
- return Array.isArray(value2) ? value2.length > 0 : false;
23069
- }
23070
- const value = data[condition];
23071
- return value !== void 0 && value !== null && value !== "";
23072
- } catch (err) {
23073
- console.error("[TemplateExecutor] \u6761\u4EF6\u5224\u65AD\u5931\u8D25:", condition, err);
23074
- return false;
23075
- }
23076
- }
23077
23064
  coerceArrayFields(template, data) {
23078
23065
  const requiredFields = template.requiredFields ?? [];
23079
23066
  const optionalFields = template.optionalFields ?? [];
@@ -23178,30 +23165,16 @@ ${topicLine}`;
23178
23165
  resolvedData = this.mergeXiaohongshuTopicsIntoContent(template, resolvedData);
23179
23166
  this.validateRequiredFields(template, resolvedData);
23180
23167
  this.validateRules(template, resolvedData);
23181
- const steps = [];
23182
- for (const workflowStep of template.workflow) {
23183
- if (workflowStep.condition) {
23184
- const shouldExecute = this.evaluateCondition(workflowStep.condition, resolvedData);
23185
- if (!shouldExecute) continue;
23186
- }
23187
- steps.push({
23188
- stepId: workflowStep.stepId,
23189
- name: workflowStep.name || workflowStep.stepId,
23190
- goal: workflowStep.goal,
23191
- tool: workflowStep.tool,
23192
- args: this.replaceObjectVariables(workflowStep.args, resolvedData),
23193
- onFailure: workflowStep.onFailure,
23194
- preconditions: workflowStep.preconditions,
23195
- expectedObservation: workflowStep.expectedObservation,
23196
- status: "pending"
23197
- });
23168
+ const nodes = Array.isArray(template.nodes) ? this.replaceObjectVariables(template.nodes, resolvedData) : [];
23169
+ if (nodes.length === 0) {
23170
+ throw new Error("\u4EFB\u52A1\u6A21\u677F\u7F3A\u5C11 nodes \u5B9A\u4E49");
23198
23171
  }
23199
23172
  return {
23200
23173
  taskType: template.taskType,
23201
23174
  platform: template.platform,
23202
23175
  payload: resolvedData,
23203
23176
  context: template.siteId ? { siteId: template.siteId } : void 0,
23204
- steps
23177
+ nodes
23205
23178
  };
23206
23179
  }
23207
23180
  async executePublishItem(template, item) {
@@ -23281,7 +23254,7 @@ function isActionRequiredWaitReason(waitReason) {
23281
23254
  return false;
23282
23255
  }
23283
23256
  }
23284
- function mapTaskStatusToCurrentStepStatus(status) {
23257
+ function mapTaskStatusToCurrentNodeStatus(status) {
23285
23258
  switch (status) {
23286
23259
  case "queued":
23287
23260
  return "pending";
@@ -23312,8 +23285,8 @@ function mapToolNameToTaskOperation(tool) {
23312
23285
  case "switch_tab":
23313
23286
  case "apply_search_filters":
23314
23287
  return "click";
23315
- case "collect_search_notes":
23316
- case "collect_note_details":
23288
+ case "collect_search_list":
23289
+ case "collect_search_item_details":
23317
23290
  case "collect_structured_data":
23318
23291
  return "capture_response";
23319
23292
  case "validate_page":
@@ -23324,12 +23297,12 @@ function mapToolNameToTaskOperation(tool) {
23324
23297
  return "unknown";
23325
23298
  }
23326
23299
  }
23327
- function buildTaskProgressView(steps, currentStepId, status, hint) {
23328
- if (!Array.isArray(steps) || steps.length === 0) {
23300
+ function buildTaskProgressView(entries, currentEntryId, status, hint) {
23301
+ if (!Array.isArray(entries) || entries.length === 0) {
23329
23302
  return void 0;
23330
23303
  }
23331
- const total = steps.length;
23332
- const currentIndex = typeof currentStepId === "string" ? steps.findIndex((step) => step.stepId === currentStepId) : -1;
23304
+ const total = entries.length;
23305
+ const currentIndex = typeof currentEntryId === "string" ? entries.findIndex((entry) => entry.nodeId === currentEntryId) : -1;
23333
23306
  let current = 0;
23334
23307
  if (currentIndex >= 0) {
23335
23308
  current = currentIndex + 1;
@@ -23346,13 +23319,19 @@ function buildTaskProgressView(steps, currentStepId, status, hint) {
23346
23319
  }
23347
23320
  function buildTaskStatusView(input) {
23348
23321
  const waitReason = input.waitReason ?? mapTaskStatusToWaitReason(input.status);
23349
- const progress = buildTaskProgressView(input.steps, input.currentStepId, input.status, input.progressHint);
23350
- const resolvedCurrentStepStatus = input.currentStepStatus ?? mapTaskStatusToCurrentStepStatus(input.status);
23351
- const currentStep = input.currentStepId && resolvedCurrentStepStatus ? {
23352
- stepId: input.currentStepId,
23353
- tool: input.currentStepTool,
23354
- name: input.currentStepName,
23355
- status: resolvedCurrentStepStatus
23322
+ const progressEntries = Array.isArray(input.nodes) && input.nodes.length > 0 ? input.nodes.map((node) => ({ nodeId: node.nodeId })) : input.progressEntries;
23323
+ const progress = buildTaskProgressView(
23324
+ progressEntries,
23325
+ input.currentNodeId,
23326
+ input.status,
23327
+ input.progressHint
23328
+ );
23329
+ const resolvedCurrentNodeStatus = input.currentNodeStatus ?? mapTaskStatusToCurrentNodeStatus(input.status);
23330
+ const currentNode = input.currentNodeId && resolvedCurrentNodeStatus ? {
23331
+ nodeId: input.currentNodeId,
23332
+ tool: input.currentNodeTool,
23333
+ name: input.currentNodeName,
23334
+ status: resolvedCurrentNodeStatus
23356
23335
  } : void 0;
23357
23336
  return {
23358
23337
  status: mapTaskStatusToViewStatus(input.status),
@@ -23360,13 +23339,120 @@ function buildTaskStatusView(input) {
23360
23339
  capability: input.capability,
23361
23340
  currentOperation: input.currentOperation,
23362
23341
  progress,
23363
- currentStep,
23342
+ currentNode,
23343
+ browsingContext: input.browsingContext,
23364
23344
  captureWindow: input.captureWindow,
23365
23345
  lastArtifact: input.lastArtifact ?? null
23366
23346
  };
23367
23347
  }
23368
23348
 
23349
+ // relay/src/runtime-node-execution.ts
23350
+ function deriveNodeIdempotency(node) {
23351
+ if (node.idempotency) {
23352
+ return node.idempotency;
23353
+ }
23354
+ switch (node.tool) {
23355
+ case "get_page_state":
23356
+ case "validate_page":
23357
+ case "list_tabs":
23358
+ case "collect_search_list":
23359
+ case "collect_search_item_details":
23360
+ case "collect_structured_data":
23361
+ return "safe_retry";
23362
+ case "trigger_action":
23363
+ case "fill_field":
23364
+ case "switch_tab":
23365
+ case "open_tab":
23366
+ return "conditional_retry";
23367
+ case "upload_assets":
23368
+ case "ensure_publish_ready":
23369
+ return "non_idempotent";
23370
+ default:
23371
+ return "conditional_retry";
23372
+ }
23373
+ }
23374
+ function buildRecoveryAnchor(task, nodeId, status) {
23375
+ const activeContext = Array.isArray(task.browsingContexts) && task.currentContextId ? task.browsingContexts.find((context) => context.contextId === task.currentContextId) ?? null : null;
23376
+ return {
23377
+ nodeId,
23378
+ status,
23379
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
23380
+ contextId: task.currentContextId,
23381
+ pageId: activeContext?.pageId ?? task.currentPageId,
23382
+ pageSignature: activeContext?.pageSignature ?? null,
23383
+ tabId: activeContext?.tabId ?? task.currentTabId,
23384
+ windowId: activeContext?.windowId,
23385
+ url: activeContext?.url
23386
+ };
23387
+ }
23388
+ function ensureTaskNodeResults(task) {
23389
+ task.payload.nodeResults = task.payload.nodeResults || {};
23390
+ const nodes = Array.isArray(task.payload.nodes) ? task.payload.nodes : [];
23391
+ for (const node of nodes) {
23392
+ const previous = task.payload.nodeResults[node.nodeId];
23393
+ task.payload.nodeResults[node.nodeId] = {
23394
+ nodeId: node.nodeId,
23395
+ status: previous?.status ?? "pending",
23396
+ attempts: previous?.attempts ?? 0,
23397
+ lastUpdatedAt: previous?.lastUpdatedAt,
23398
+ idempotency: previous?.idempotency ?? deriveNodeIdempotency(node),
23399
+ recoveryAnchor: previous?.recoveryAnchor ?? null,
23400
+ error: previous?.error ?? null,
23401
+ result: previous?.result ?? null
23402
+ };
23403
+ }
23404
+ return task.payload.nodeResults;
23405
+ }
23406
+ function markNodeDispatched(task, node) {
23407
+ const nodeResults = ensureTaskNodeResults(task);
23408
+ const previous = nodeResults[node.nodeId];
23409
+ nodeResults[node.nodeId] = {
23410
+ nodeId: node.nodeId,
23411
+ status: "running",
23412
+ attempts: (previous?.attempts ?? 0) + 1,
23413
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
23414
+ idempotency: previous?.idempotency ?? deriveNodeIdempotency(node),
23415
+ recoveryAnchor: buildRecoveryAnchor(task, node.nodeId, "running"),
23416
+ error: null,
23417
+ result: previous?.result ?? null
23418
+ };
23419
+ }
23420
+ function updateNodeExecutionStatus(task, nodeId, status, options) {
23421
+ const nodeResults = ensureTaskNodeResults(task);
23422
+ const previous = nodeResults[nodeId];
23423
+ if (!previous) {
23424
+ return;
23425
+ }
23426
+ nodeResults[nodeId] = {
23427
+ ...previous,
23428
+ status,
23429
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
23430
+ recoveryAnchor: buildRecoveryAnchor(task, nodeId, status),
23431
+ error: options?.error ?? previous.error ?? null,
23432
+ result: options?.result ?? previous.result ?? null
23433
+ };
23434
+ }
23435
+
23369
23436
  // relay/src/task-status-view.ts
23437
+ function buildMirroredSteps(task) {
23438
+ const nodes = Array.isArray(task.payload?.nodes) ? task.payload.nodes : [];
23439
+ const nodeResults = task.payload?.nodeResults || {};
23440
+ return nodes.map((node) => {
23441
+ const nodeResult = nodeResults[node.nodeId];
23442
+ return {
23443
+ nodeId: node.nodeId,
23444
+ name: node.name || node.nodeId,
23445
+ goal: node.goal,
23446
+ tool: node.tool,
23447
+ args: node.args,
23448
+ onFailure: node.onFailure,
23449
+ preconditions: node.preconditions,
23450
+ expectedObservation: node.expectedObservation,
23451
+ status: nodeResult?.status === "pending" ? "pending" : nodeResult?.status === "running" ? "running" : nodeResult?.status === "skipped" ? "skipped" : nodeResult?.status === "failed" ? "failed" : "success",
23452
+ result: nodeResult?.result ?? null
23453
+ };
23454
+ });
23455
+ }
23370
23456
  function resolveTaskCapability(task, captureCapability) {
23371
23457
  if (captureCapability) {
23372
23458
  return captureCapability;
@@ -23374,7 +23460,6 @@ function resolveTaskCapability(task, captureCapability) {
23374
23460
  switch (task.type) {
23375
23461
  case "publish_note":
23376
23462
  case "save_note_draft":
23377
- case "open_website":
23378
23463
  case "browser_action":
23379
23464
  case "search_notes":
23380
23465
  return "browser";
@@ -23395,22 +23480,44 @@ function isCaptureOperation(operation) {
23395
23480
  return false;
23396
23481
  }
23397
23482
  }
23483
+ function mapProcessStatusToRuntimeNodeStatus(status) {
23484
+ switch (status) {
23485
+ case "running":
23486
+ case "waiting":
23487
+ return "running";
23488
+ case "succeeded":
23489
+ return "success";
23490
+ case "failed":
23491
+ return "failed";
23492
+ case "skipped":
23493
+ return "skipped";
23494
+ default:
23495
+ return "pending";
23496
+ }
23497
+ }
23398
23498
  function applyTaskStatusView(task, overrides) {
23399
- const steps = Array.isArray(task.payload?.steps) ? task.payload.steps : [];
23400
- const currentStepId = overrides?.currentStepId ?? task.currentStepId;
23401
- const matchedStep = currentStepId ? steps.find((step) => step.stepId === currentStepId) : void 0;
23402
- const nextOperation = overrides?.currentOperation ?? mapToolNameToTaskOperation(overrides?.currentStepTool ?? matchedStep?.tool);
23499
+ const nodes = Array.isArray(task.payload?.nodes) ? task.payload.nodes : [];
23500
+ const steps = buildMirroredSteps(task);
23501
+ const currentNodeId = overrides?.currentNodeId ?? task.currentNodeId;
23502
+ const matchedStep = currentNodeId ? steps.find((step) => step.nodeId === currentNodeId) : void 0;
23503
+ const matchedNode = currentNodeId ? nodes.find((node) => node.nodeId === currentNodeId) : void 0;
23504
+ const nextOperation = overrides?.currentOperation ?? mapToolNameToTaskOperation(overrides?.currentNodeTool ?? matchedStep?.tool);
23403
23505
  const captureWindow = overrides?.captureWindow ?? task.statusView?.captureWindow;
23404
23506
  task.statusView = buildTaskStatusView({
23405
23507
  status: task.status,
23406
- steps,
23407
- currentStepId,
23408
- currentStepTool: overrides?.currentStepTool ?? matchedStep?.tool,
23409
- currentStepName: overrides?.currentStepName ?? matchedStep?.name,
23410
- currentStepStatus: overrides?.currentStepStatus,
23508
+ progressEntries: steps,
23509
+ nodes,
23510
+ currentNodeId: matchedNode?.nodeId ?? currentNodeId,
23511
+ currentNodeTool: overrides?.currentNodeTool ?? matchedStep?.tool,
23512
+ currentNodeName: overrides?.currentNodeName ?? matchedNode?.name ?? matchedStep?.name,
23513
+ currentNodeStatus: overrides?.currentNodeStatus,
23411
23514
  capability: resolveTaskCapability(task, captureWindow?.capability),
23412
23515
  currentOperation: nextOperation,
23413
23516
  waitReason: overrides?.waitReason,
23517
+ browsingContext: task.browsingContexts?.length ? {
23518
+ activeContextId: task.currentContextId,
23519
+ contexts: task.browsingContexts
23520
+ } : void 0,
23414
23521
  captureWindow,
23415
23522
  lastArtifact: overrides?.lastArtifact ?? task.statusView?.lastArtifact,
23416
23523
  progressHint: overrides?.progressHint
@@ -23428,25 +23535,22 @@ function resolveCaptureWindowForStepProcess(task, currentOperation) {
23428
23535
  }
23429
23536
  return captureWindow;
23430
23537
  }
23431
- function applyTaskStepProcessView(task, payload) {
23432
- const steps = Array.isArray(task.payload?.steps) ? task.payload.steps : [];
23433
- const matchedStep = steps.find((step) => step.stepId === payload.stepId);
23434
- if (matchedStep) {
23435
- if (payload.status === "running" || payload.status === "waiting") {
23436
- matchedStep.status = "running";
23437
- } else if (payload.status === "succeeded") {
23438
- matchedStep.status = "success";
23439
- } else if (payload.status === "failed") {
23440
- matchedStep.status = "failed";
23441
- } else if (payload.status === "skipped") {
23442
- matchedStep.status = "skipped";
23443
- }
23538
+ function applyTaskNodeProcessView(task, payload) {
23539
+ const nodes = Array.isArray(task.payload?.nodes) ? task.payload.nodes : [];
23540
+ const steps = buildMirroredSteps(task);
23541
+ const matchedStep = steps.find((step) => step.nodeId === payload.nodeId);
23542
+ const matchedNode = nodes.find((node) => node.nodeId === payload.nodeId);
23543
+ if (matchedNode) {
23544
+ task.currentNodeId = matchedNode.nodeId;
23545
+ }
23546
+ if (matchedNode) {
23547
+ updateNodeExecutionStatus(task, payload.nodeId, mapProcessStatusToRuntimeNodeStatus(payload.status));
23444
23548
  }
23445
23549
  applyTaskStatusView(task, {
23446
- currentStepId: payload.stepId,
23447
- currentStepTool: payload.tool ?? matchedStep?.tool,
23448
- currentStepName: payload.name ?? matchedStep?.name,
23449
- currentStepStatus: payload.status,
23550
+ currentNodeId: payload.nodeId,
23551
+ currentNodeTool: payload.tool ?? matchedStep?.tool,
23552
+ currentNodeName: payload.name ?? matchedNode?.name ?? matchedStep?.name,
23553
+ currentNodeStatus: payload.status,
23450
23554
  currentOperation: payload.currentOperation,
23451
23555
  waitReason: "waitReason" in payload ? payload.waitReason : void 0,
23452
23556
  captureWindow: resolveCaptureWindowForStepProcess(task, payload.currentOperation)
@@ -23454,10 +23558,10 @@ function applyTaskStepProcessView(task, payload) {
23454
23558
  if (!task.statusView) {
23455
23559
  return;
23456
23560
  }
23457
- task.statusView.currentStep = {
23458
- stepId: payload.stepId,
23561
+ task.statusView.currentNode = {
23562
+ nodeId: payload.nodeId,
23459
23563
  tool: payload.tool ?? matchedStep?.tool,
23460
- name: payload.name ?? matchedStep?.name,
23564
+ name: payload.name ?? matchedNode?.name ?? matchedStep?.name,
23461
23565
  status: payload.status
23462
23566
  };
23463
23567
  task.statusView.currentOperation = payload.currentOperation ?? task.statusView.currentOperation;
@@ -23555,28 +23659,28 @@ var TaskReadyRegistry = class {
23555
23659
  }
23556
23660
  };
23557
23661
 
23558
- // relay/src/step-result-guard.ts
23559
- function guardStepResult(task, orchestrator, result) {
23662
+ // relay/src/node-result-guard.ts
23663
+ function guardNodeResult(task, orchestrator, result) {
23560
23664
  if (!task) {
23561
23665
  return { accept: false, reason: "task_missing" };
23562
23666
  }
23563
23667
  if (!orchestrator) {
23564
23668
  return { accept: false, reason: "orchestrator_missing" };
23565
23669
  }
23566
- const expectedStep = task.payload?.steps?.[orchestrator.currentStepIndex];
23567
- if (!expectedStep) {
23568
- return { accept: false, reason: "step_missing" };
23670
+ const expectedNode = task.payload?.nodes?.[orchestrator.currentStepIndex];
23671
+ if (!expectedNode) {
23672
+ return { accept: false, reason: "node_missing" };
23569
23673
  }
23570
- if (expectedStep.stepId !== result.stepId) {
23674
+ if (expectedNode.nodeId !== result.nodeId) {
23571
23675
  return {
23572
23676
  accept: false,
23573
- reason: "step_mismatch",
23574
- expectedStepId: expectedStep.stepId
23677
+ reason: "node_mismatch",
23678
+ expectedNodeId: expectedNode.nodeId
23575
23679
  };
23576
23680
  }
23577
23681
  return {
23578
23682
  accept: true,
23579
- expectedStepId: expectedStep.stepId
23683
+ expectedNodeId: expectedNode.nodeId
23580
23684
  };
23581
23685
  }
23582
23686
 
@@ -23585,6 +23689,8 @@ function isTerminalTaskStatus(status) {
23585
23689
  return status === "completed" || status === "failed" || status === "cancelled";
23586
23690
  }
23587
23691
  function buildTaskFinalResult(task) {
23692
+ const nodes = task.payload?.nodes || [];
23693
+ const nodeResults = task.payload?.nodeResults || {};
23588
23694
  return {
23589
23695
  taskId: task.taskId,
23590
23696
  type: task.type,
@@ -23592,40 +23698,327 @@ function buildTaskFinalResult(task) {
23592
23698
  status: task.status,
23593
23699
  statusView: task.statusView,
23594
23700
  lastError: task.lastError ?? null,
23595
- currentStepId: task.currentStepId,
23701
+ currentNodeId: task.currentNodeId,
23702
+ currentContextId: task.currentContextId,
23703
+ resolvedContextId: task.resolvedContextId,
23704
+ candidateChildContextIds: task.candidateChildContextIds ?? [],
23705
+ lastTriggeredNavigation: task.lastTriggeredNavigation ?? null,
23706
+ browsingContexts: task.browsingContexts ?? [],
23596
23707
  updatedAt: task.updatedAt,
23597
- steps: (task.payload?.steps || []).map((step) => ({
23598
- stepId: step.stepId,
23599
- name: step.name,
23600
- tool: step.tool,
23601
- status: step.status,
23602
- result: step.result ?? null
23603
- })),
23708
+ steps: nodes.map((node) => {
23709
+ const nodeResult = nodeResults[node.nodeId];
23710
+ return {
23711
+ nodeId: node.nodeId,
23712
+ name: node.name,
23713
+ tool: node.tool,
23714
+ status: nodeResult?.status === "pending" ? "pending" : nodeResult?.status === "running" ? "running" : nodeResult?.status === "skipped" ? "skipped" : nodeResult?.status === "failed" ? "failed" : "success",
23715
+ result: nodeResult?.result ?? null
23716
+ };
23717
+ }),
23718
+ nodes: nodes.map((node) => {
23719
+ const mirroredNodeResult = nodeResults[node.nodeId];
23720
+ return {
23721
+ nodeId: node.nodeId,
23722
+ name: node.name,
23723
+ tool: node.tool,
23724
+ status: mirroredNodeResult?.status ?? "pending",
23725
+ attempts: mirroredNodeResult?.attempts ?? 0,
23726
+ lastUpdatedAt: mirroredNodeResult?.lastUpdatedAt,
23727
+ idempotency: mirroredNodeResult?.idempotency,
23728
+ recoveryAnchor: mirroredNodeResult?.recoveryAnchor ?? null,
23729
+ error: mirroredNodeResult?.error ?? null,
23730
+ result: mirroredNodeResult?.result ?? null
23731
+ };
23732
+ }),
23604
23733
  lastArtifact: task.statusView?.lastArtifact ?? null
23605
23734
  };
23606
23735
  }
23607
23736
 
23608
- // relay/src/task-step-result.ts
23609
- function mapStepResultToStepStatus(result) {
23737
+ // shared/node-template-compiler.ts
23738
+ function deriveOnFailurePolicyFromRetry(node) {
23739
+ if (node.onFailure) {
23740
+ return node.onFailure;
23741
+ }
23742
+ if (!node.retry || node.retry.attempts <= 1) {
23743
+ return void 0;
23744
+ }
23745
+ if (node.retry.attempts === 2) {
23746
+ return { policy: "retry_once" };
23747
+ }
23748
+ return { policy: "retry_twice" };
23749
+ }
23750
+ function buildRuntimeNodeMeta(node) {
23751
+ const runtimeNodeMeta = {};
23752
+ if (node.inputs?.length) {
23753
+ runtimeNodeMeta.inputs = node.inputs;
23754
+ }
23755
+ if (node.outputs?.length) {
23756
+ runtimeNodeMeta.outputs = node.outputs;
23757
+ }
23758
+ if (node.fromContext && Object.keys(node.fromContext).length > 0) {
23759
+ runtimeNodeMeta.fromContext = node.fromContext;
23760
+ }
23761
+ if (typeof node.storeAs === "string" && node.storeAs.length > 0) {
23762
+ runtimeNodeMeta.storeAs = node.storeAs;
23763
+ }
23764
+ if (typeof node.outputScope === "string" && node.outputScope.length > 0) {
23765
+ runtimeNodeMeta.outputScope = node.outputScope;
23766
+ }
23767
+ if (typeof node.timeoutMs === "number" && Number.isFinite(node.timeoutMs) && node.timeoutMs > 0) {
23768
+ runtimeNodeMeta.timeoutMs = node.timeoutMs;
23769
+ }
23770
+ if (typeof node.optional === "boolean") {
23771
+ runtimeNodeMeta.optional = node.optional;
23772
+ }
23773
+ if (node.assert?.length) {
23774
+ runtimeNodeMeta.assert = node.assert;
23775
+ }
23776
+ if (node.fallback) {
23777
+ runtimeNodeMeta.fallback = node.fallback;
23778
+ }
23779
+ if (node.retry) {
23780
+ runtimeNodeMeta.retry = node.retry;
23781
+ }
23782
+ if (typeof node.when === "string" && node.when.trim().length > 0) {
23783
+ runtimeNodeMeta.when = node.when;
23784
+ }
23785
+ return Object.keys(runtimeNodeMeta).length > 0 ? runtimeNodeMeta : void 0;
23786
+ }
23787
+ function compileNodeToExecution(node) {
23788
+ const runtimeNodeMeta = buildRuntimeNodeMeta(node);
23789
+ return {
23790
+ nodeId: node.nodeId,
23791
+ name: node.name || node.nodeId,
23792
+ goal: node.goal,
23793
+ tool: node.tool,
23794
+ args: runtimeNodeMeta ? {
23795
+ ...node.args,
23796
+ __runtimeNode: runtimeNodeMeta
23797
+ } : node.args,
23798
+ onFailure: deriveOnFailurePolicyFromRetry(node),
23799
+ preconditions: node.preconditions,
23800
+ expectedObservation: node.expectedObservation,
23801
+ status: "pending"
23802
+ };
23803
+ }
23804
+
23805
+ // relay/src/runtime-node-fallback.ts
23806
+ function readRuntimeNodeFallback(task, stepIndex) {
23807
+ const node = task.payload?.nodes?.[stepIndex];
23808
+ return node?.fallback ?? null;
23809
+ }
23810
+ function resolveFallbackStepIndex(task, currentStepIndex, result) {
23811
+ if (result.ok) {
23812
+ return null;
23813
+ }
23814
+ const fallback = readRuntimeNodeFallback(task, currentStepIndex);
23815
+ if (!fallback || fallback.policy === "abort") {
23816
+ return null;
23817
+ }
23818
+ if (fallback.nodeId) {
23819
+ const nextIndex = Array.isArray(task.payload?.nodes) ? task.payload.nodes.findIndex((node) => node.nodeId === fallback.nodeId) : -1;
23820
+ return nextIndex >= 0 ? nextIndex : null;
23821
+ }
23822
+ if (fallback.policy === "continue") {
23823
+ return currentStepIndex + 1;
23824
+ }
23825
+ return null;
23826
+ }
23827
+
23828
+ // relay/src/task-node-result.ts
23829
+ function mapNodeResultToExecutionStatus(result) {
23610
23830
  return result.ok ? "success" : "failed";
23611
23831
  }
23612
- function normalizeStepResult(result, normalizedError) {
23832
+ function normalizeNodeResult(result, normalizedError) {
23613
23833
  return {
23614
23834
  ...result,
23615
23835
  error: normalizedError
23616
23836
  };
23617
23837
  }
23618
- function persistStepResultOnTask(task, result) {
23619
- const steps = Array.isArray(task.payload?.steps) ? task.payload.steps : [];
23620
- const matchedStep = steps.find((step) => step.stepId === result.stepId);
23621
- if (!matchedStep) {
23838
+ function persistNodeResultOnTask(task, result) {
23839
+ const nodes = Array.isArray(task.payload?.nodes) ? task.payload.nodes : [];
23840
+ const matchedNode = nodes.find((node) => node.nodeId === result.nodeId);
23841
+ if (!matchedNode) {
23622
23842
  return false;
23623
23843
  }
23624
- matchedStep.status = mapStepResultToStepStatus(result);
23625
- matchedStep.result = result;
23844
+ updateNodeExecutionStatus(task, result.nodeId, mapNodeResultToExecutionStatus(result), {
23845
+ result,
23846
+ error: result.error
23847
+ });
23626
23848
  return true;
23627
23849
  }
23628
23850
 
23851
+ // relay/src/runtime-task-orchestrator.ts
23852
+ function createTaskOrchestrator(task) {
23853
+ const executionLength = getTaskExecutionLength(task);
23854
+ if (executionLength === 0) {
23855
+ return null;
23856
+ }
23857
+ const recovery = resolveNodeExecutionRecovery(task);
23858
+ return {
23859
+ currentStepIndex: recovery?.index ?? 0,
23860
+ recovery: recovery?.state ?? null
23861
+ };
23862
+ }
23863
+ function hydrateTaskFromRecoveryAnchor(task, anchor) {
23864
+ if (!anchor) {
23865
+ return;
23866
+ }
23867
+ if (anchor.contextId) {
23868
+ task.currentContextId = anchor.contextId;
23869
+ }
23870
+ if (typeof anchor.tabId === "number") {
23871
+ task.currentTabId = anchor.tabId;
23872
+ }
23873
+ if (anchor.pageId) {
23874
+ task.currentPageId = anchor.pageId;
23875
+ }
23876
+ if (!anchor.contextId || typeof anchor.tabId !== "number") {
23877
+ return;
23878
+ }
23879
+ const browsingContexts = Array.isArray(task.browsingContexts) ? [...task.browsingContexts] : [];
23880
+ const existingIndex = browsingContexts.findIndex((context) => context.contextId === anchor.contextId);
23881
+ const nextContext = {
23882
+ contextId: anchor.contextId,
23883
+ tabId: anchor.tabId,
23884
+ windowId: anchor.windowId,
23885
+ url: anchor.url,
23886
+ pageId: anchor.pageId,
23887
+ pageSignature: anchor.pageSignature ?? null,
23888
+ role: "active",
23889
+ origin: "detected",
23890
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23891
+ };
23892
+ if (existingIndex >= 0) {
23893
+ browsingContexts[existingIndex] = {
23894
+ ...browsingContexts[existingIndex],
23895
+ ...nextContext
23896
+ };
23897
+ } else {
23898
+ browsingContexts.push(nextContext);
23899
+ }
23900
+ task.browsingContexts = browsingContexts;
23901
+ }
23902
+ function resolveNodeExecutionRecovery(task) {
23903
+ const nodes = Array.isArray(task.payload?.nodes) ? task.payload.nodes : [];
23904
+ if (nodes.length === 0) {
23905
+ return {
23906
+ index: 0,
23907
+ state: null
23908
+ };
23909
+ }
23910
+ const nodeResults = ensureTaskNodeResults(task);
23911
+ for (let index = 0; index < nodes.length; index += 1) {
23912
+ const node = nodes[index];
23913
+ const nodeResult = nodeResults[node.nodeId];
23914
+ const status = nodeResult?.status ?? "pending";
23915
+ if (status === "success" || status === "skipped") {
23916
+ continue;
23917
+ }
23918
+ const anchor = nodeResult?.recoveryAnchor ?? null;
23919
+ hydrateTaskFromRecoveryAnchor(task, anchor);
23920
+ if (status === "running") {
23921
+ return {
23922
+ index,
23923
+ state: {
23924
+ nodeId: node.nodeId,
23925
+ anchor,
23926
+ reason: "resume_running"
23927
+ }
23928
+ };
23929
+ }
23930
+ if (status === "failed") {
23931
+ return {
23932
+ index,
23933
+ state: {
23934
+ nodeId: node.nodeId,
23935
+ anchor,
23936
+ reason: "retry_failed"
23937
+ }
23938
+ };
23939
+ }
23940
+ return {
23941
+ index,
23942
+ state: index > 0 ? {
23943
+ nodeId: node.nodeId,
23944
+ anchor,
23945
+ reason: "resume_after_completed_prefix"
23946
+ } : null
23947
+ };
23948
+ }
23949
+ return {
23950
+ index: nodes.length,
23951
+ state: null
23952
+ };
23953
+ }
23954
+ function getTaskExecutionLength(task) {
23955
+ return Array.isArray(task.payload?.nodes) ? task.payload.nodes.length : 0;
23956
+ }
23957
+ function resolveTaskExecutionStep(task, index) {
23958
+ const node = task.payload?.nodes?.[index];
23959
+ if (!node) {
23960
+ return null;
23961
+ }
23962
+ const compiledNode = compileNodeToExecution(node);
23963
+ const nodeResults = ensureTaskNodeResults(task);
23964
+ const nodeResult = nodeResults[node.nodeId];
23965
+ return {
23966
+ ...compiledNode,
23967
+ status: nodeResult?.status === "pending" ? "pending" : nodeResult?.status === "running" ? "running" : nodeResult?.status === "skipped" ? "skipped" : nodeResult?.status === "failed" ? "failed" : "success",
23968
+ result: nodeResult?.result ?? null
23969
+ };
23970
+ }
23971
+ function resolveCurrentTaskExecutionStep(task, orchestrator) {
23972
+ return resolveTaskExecutionStep(task, orchestrator.currentStepIndex);
23973
+ }
23974
+ function markTaskExecutionDispatch(task, orchestrator) {
23975
+ const step = resolveCurrentTaskExecutionStep(task, orchestrator);
23976
+ if (!step) {
23977
+ return null;
23978
+ }
23979
+ task.status = "running";
23980
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23981
+ const activeNode = task.payload?.nodes?.[orchestrator.currentStepIndex];
23982
+ if (activeNode) {
23983
+ task.currentNodeId = activeNode.nodeId;
23984
+ ensureTaskNodeResults(task);
23985
+ markNodeDispatched(task, activeNode);
23986
+ }
23987
+ return step;
23988
+ }
23989
+ function advanceTaskOrchestrator(orchestrator, nextStepIndex) {
23990
+ if (typeof nextStepIndex === "number" && Number.isFinite(nextStepIndex) && nextStepIndex >= 0) {
23991
+ orchestrator.currentStepIndex = nextStepIndex;
23992
+ } else {
23993
+ orchestrator.currentStepIndex += 1;
23994
+ }
23995
+ return orchestrator.currentStepIndex;
23996
+ }
23997
+ function applyTaskExecutionResult(task, orchestrator, result) {
23998
+ const observationError = typeof result.observation?.error === "string" ? result.observation.error : void 0;
23999
+ const normalizedNodeError = result.error || (!result.ok ? {
24000
+ code: "ERR_STEP_EXECUTION_FAILED",
24001
+ message: observationError || "Step execution failed"
24002
+ } : null);
24003
+ const persistedNodeResult = normalizeNodeResult(result, normalizedNodeError);
24004
+ persistNodeResultOnTask(task, persistedNodeResult);
24005
+ const fallbackStepIndex = resolveFallbackStepIndex(
24006
+ task,
24007
+ orchestrator.currentStepIndex,
24008
+ persistedNodeResult
24009
+ );
24010
+ task.status = result.ok || fallbackStepIndex != null ? "running" : "failed";
24011
+ task.currentNodeId = result.nodeId;
24012
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
24013
+ task.lastError = result.ok || fallbackStepIndex != null ? null : normalizedNodeError;
24014
+ return {
24015
+ persistedNodeResult,
24016
+ fallbackStepIndex,
24017
+ shouldContinue: result.ok || fallbackStepIndex != null,
24018
+ nextStepIndex: result.ok ? orchestrator.currentStepIndex + 1 : fallbackStepIndex
24019
+ };
24020
+ }
24021
+
23629
24022
  // relay/src/index.ts
23630
24023
  var app = (0, import_express.default)();
23631
24024
  var server = createServer(app);
@@ -23755,8 +24148,8 @@ function requireTaskRuntimeId(task) {
23755
24148
  function updateTaskStatusView(task, overrides) {
23756
24149
  applyTaskStatusView(task, overrides);
23757
24150
  }
23758
- function updateTaskStepProcessView(task, payload) {
23759
- applyTaskStepProcessView(task, payload);
24151
+ function updateTaskNodeProcessView(task, payload) {
24152
+ applyTaskNodeProcessView(task, payload);
23760
24153
  }
23761
24154
  function removeBatchJobFromQueues(batchJobId, targetRuntimeId) {
23762
24155
  if (targetRuntimeId) {
@@ -23843,13 +24236,32 @@ async function loadTaskTemplate(platform, taskType, siteId) {
23843
24236
  });
23844
24237
  throw new Error(`TaskTemplate not found: ${missingTemplateKey}`);
23845
24238
  }
23846
- function reindexSubmissionSteps(steps) {
23847
- return steps.map((step, index) => ({
23848
- ...step,
23849
- stepId: `s${index + 1}`,
23850
- name: step.name || `s${index + 1}`,
23851
- status: "pending"
23852
- }));
24239
+ async function hydrateTaskSubmissionWithTemplate(taskConfig) {
24240
+ if (Array.isArray(taskConfig.nodes) && taskConfig.nodes.length > 0) {
24241
+ return taskConfig;
24242
+ }
24243
+ const siteId = taskConfig.context?.siteId;
24244
+ const template = await loadTaskTemplate(taskConfig.platform, taskConfig.taskType, siteId);
24245
+ const baseSubmission = TemplateExecutor.isPublishItemTemplate(template) ? await templateExecutor.executePublishItem(template, TemplateExecutor.buildPublishItemInput(taskConfig.payload || {})) : await templateExecutor.execute(template, taskConfig.payload || {});
24246
+ return {
24247
+ ...baseSubmission,
24248
+ taskType: taskConfig.taskType,
24249
+ platform: taskConfig.platform,
24250
+ targetRuntimeId: taskConfig.targetRuntimeId,
24251
+ sourceClientId: taskConfig.sourceClientId,
24252
+ context: {
24253
+ ...baseSubmission.context,
24254
+ ...taskConfig.context
24255
+ },
24256
+ payload: {
24257
+ ...baseSubmission.payload || {},
24258
+ ...taskConfig.payload || {}
24259
+ },
24260
+ options: {
24261
+ ...baseSubmission.options,
24262
+ ...taskConfig.options
24263
+ }
24264
+ };
23853
24265
  }
23854
24266
  function resolveImageRefs(noteSource, payload) {
23855
24267
  const imageRefs = Array.isArray(payload.imageRefs) ? payload.imageRefs.map(String) : null;
@@ -23884,11 +24296,19 @@ async function prepareResolvedAssets(filePaths) {
23884
24296
  }
23885
24297
  return assets;
23886
24298
  }
23887
- function extractLegacyUploadPaths(taskConfig) {
24299
+ function extractDirectAssetPaths(taskConfig) {
24300
+ const payloadImageRefs = Array.isArray(taskConfig.payload?.imageRefs) ? taskConfig.payload.imageRefs.filter((value) => typeof value === "string") : [];
24301
+ if (payloadImageRefs.length > 0) {
24302
+ return payloadImageRefs;
24303
+ }
24304
+ const payloadImages = Array.isArray(taskConfig.payload?.images) ? taskConfig.payload.images.filter((value) => typeof value === "string") : [];
24305
+ if (payloadImages.length > 0) {
24306
+ return payloadImages;
24307
+ }
23888
24308
  const paths = [];
23889
- for (const step of taskConfig.steps || []) {
23890
- if (step.tool === "upload_assets" && Array.isArray(step.args?.assets)) {
23891
- for (const asset of step.args.assets) {
24309
+ for (const node of taskConfig.nodes || []) {
24310
+ if (node.tool === "upload_assets" && Array.isArray(node.args?.assets)) {
24311
+ for (const asset of node.args.assets) {
23892
24312
  if (typeof asset === "string") {
23893
24313
  paths.push(asset);
23894
24314
  }
@@ -23897,14 +24317,14 @@ function extractLegacyUploadPaths(taskConfig) {
23897
24317
  }
23898
24318
  return paths;
23899
24319
  }
23900
- function rewriteUploadStepsWithAssetIds(steps, assets) {
24320
+ function rewriteUploadNodesWithAssetIds(nodes, assets) {
23901
24321
  const assetIds = assets.map((asset) => asset.fileId || asset.id);
23902
- return steps.map((step) => {
23903
- if (step.tool !== "upload_assets") return step;
24322
+ return (nodes || []).map((node) => {
24323
+ if (node.tool !== "upload_assets") return node;
23904
24324
  return {
23905
- ...step,
24325
+ ...node,
23906
24326
  args: {
23907
- ...step.args,
24327
+ ...node.args,
23908
24328
  assets: assetIds
23909
24329
  }
23910
24330
  };
@@ -23943,11 +24363,11 @@ async function assembleTaskFromSubmission(taskConfig, source) {
23943
24363
  const relativeImages = resolveImageRefs(noteSource, normalizedPayload);
23944
24364
  resolvedFilePaths = relativeImages.map((relativePath) => path3.resolve(workspace.rootDir, relativePath));
23945
24365
  } else {
23946
- resolvedFilePaths = extractLegacyUploadPaths(taskConfig);
24366
+ resolvedFilePaths = extractDirectAssetPaths(taskConfig);
23947
24367
  }
23948
24368
  console.log(`[Relay] \u89E3\u6790\u7684\u6587\u4EF6\u8DEF\u5F84:`, resolvedFilePaths);
23949
24369
  const assets = await prepareResolvedAssets(resolvedFilePaths);
23950
- const rewrittenSteps = rewriteUploadStepsWithAssetIds(taskConfig.steps || [], assets);
24370
+ const rewrittenNodes = rewriteUploadNodesWithAssetIds(taskConfig.nodes, assets);
23951
24371
  const taskId = `task_${Date.now()}`;
23952
24372
  const task = {
23953
24373
  taskId,
@@ -23956,7 +24376,7 @@ async function assembleTaskFromSubmission(taskConfig, source) {
23956
24376
  platform: taskConfig.platform,
23957
24377
  payload: {
23958
24378
  ...normalizedPayload,
23959
- steps: rewrittenSteps
24379
+ nodes: Array.isArray(rewrittenNodes) ? rewrittenNodes : void 0
23960
24380
  },
23961
24381
  assets,
23962
24382
  options: {
@@ -23972,6 +24392,7 @@ async function assembleTaskFromSubmission(taskConfig, source) {
23972
24392
  context: taskConfig.context,
23973
24393
  lastError: null
23974
24394
  };
24395
+ ensureTaskNodeResults(task);
23975
24396
  updateTaskStatusView(task);
23976
24397
  return task;
23977
24398
  }
@@ -23987,14 +24408,13 @@ async function buildBatchItemSubmission(batchJob, item, itemIndex) {
23987
24408
  images: item.images,
23988
24409
  metadata: item.metadata
23989
24410
  }));
23990
- const steps = reindexSubmissionSteps(baseSubmission.steps || []);
23991
24411
  console.log("[Relay] \u6279\u91CF\u6761\u76EE\u6D3E\u751F\u7ED3\u679C:", {
23992
24412
  batchJobId: batchJob.batchJobId,
23993
24413
  itemIndex,
23994
24414
  noteSourceId: item.noteSourceId,
23995
24415
  topicCount: Array.isArray(item.topics) ? item.topics.length : 0,
23996
24416
  topics: item.topics || [],
23997
- stepTools: steps.map((step) => `${step.stepId}:${step.tool}`),
24417
+ nodeTools: (baseSubmission.nodes || []).map((node) => `${node.nodeId}:${node.tool}`),
23998
24418
  contentTailPreview: typeof baseSubmission.payload?.content === "string" ? baseSubmission.payload.content.slice(-160) : ""
23999
24419
  });
24000
24420
  return {
@@ -24024,8 +24444,7 @@ async function buildBatchItemSubmission(batchJob, item, itemIndex) {
24024
24444
  humanLike: true,
24025
24445
  allowFallback: true,
24026
24446
  requireConfirmation: false
24027
- },
24028
- steps
24447
+ }
24029
24448
  };
24030
24449
  }
24031
24450
  async function dispatchTask(task) {
@@ -24045,7 +24464,7 @@ async function dispatchTask(task) {
24045
24464
  siteId: task.context?.siteId,
24046
24465
  batchJobId: task.context?.batchJobId,
24047
24466
  batchItemIndex: task.context?.batchItemIndex,
24048
- stepTools: task.payload?.steps?.map((step) => `${step.stepId}:${step.tool}`) || []
24467
+ nodeTools: task.payload?.nodes?.map((node) => `${node.nodeId}:${node.tool}`) || []
24049
24468
  });
24050
24469
  const taskMessage = {
24051
24470
  type: "task.submit",
@@ -24245,7 +24664,8 @@ async function registerAndDispatchTask(task) {
24245
24664
  return task;
24246
24665
  }
24247
24666
  async function submitTaskSubmission(taskConfig, source) {
24248
- const task = await assembleTaskFromSubmission(taskConfig, source);
24667
+ const hydratedTaskConfig = await hydrateTaskSubmissionWithTemplate(taskConfig);
24668
+ const task = await assembleTaskFromSubmission(hydratedTaskConfig, source);
24249
24669
  await registerAndDispatchTask(task);
24250
24670
  return task;
24251
24671
  }
@@ -24261,11 +24681,11 @@ async function handleRuntimeMessage(msg) {
24261
24681
  case "task.status":
24262
24682
  handleTaskStatus(msg);
24263
24683
  break;
24264
- case "step.started":
24265
- handleStepStarted(msg);
24684
+ case "node.started":
24685
+ handleNodeStarted(msg);
24266
24686
  break;
24267
- case "step.status":
24268
- handleStepStatus(msg);
24687
+ case "node.status":
24688
+ handleNodeStatus(msg);
24269
24689
  break;
24270
24690
  case "capture.started":
24271
24691
  handleCaptureStarted(msg);
@@ -24276,8 +24696,8 @@ async function handleRuntimeMessage(msg) {
24276
24696
  case "artifact.created":
24277
24697
  handleArtifactCreated(msg);
24278
24698
  break;
24279
- case "step.result":
24280
- handleStepResult(msg);
24699
+ case "node.result":
24700
+ handleNodeResult(msg);
24281
24701
  break;
24282
24702
  case "runtime.query.result":
24283
24703
  handleRuntimeQueryResult(msg);
@@ -24313,75 +24733,84 @@ function handleArtifactCreated(msg) {
24313
24733
  }
24314
24734
  applyArtifactCreatedToTask(task, payload);
24315
24735
  }
24316
- function handleStepStarted(msg) {
24736
+ function handleNodeStarted(msg) {
24317
24737
  const payload = msg.payload;
24318
24738
  const task = tasks.get(payload.taskId);
24319
24739
  if (!task) {
24320
24740
  return;
24321
24741
  }
24322
- task.currentStepId = payload.stepId;
24742
+ task.currentNodeId = payload.nodeId;
24323
24743
  task.updatedAt = payload.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
24324
- updateTaskStepProcessView(task, payload);
24744
+ updateTaskNodeProcessView(task, {
24745
+ taskId: payload.taskId,
24746
+ nodeId: payload.nodeId,
24747
+ tool: payload.tool,
24748
+ name: payload.name,
24749
+ status: payload.status,
24750
+ updatedAt: payload.updatedAt,
24751
+ currentOperation: payload.currentOperation
24752
+ });
24325
24753
  }
24326
- function handleStepStatus(msg) {
24754
+ function handleNodeStatus(msg) {
24327
24755
  const payload = msg.payload;
24328
24756
  const task = tasks.get(payload.taskId);
24329
24757
  if (!task) {
24330
24758
  return;
24331
24759
  }
24332
- task.currentStepId = payload.stepId;
24760
+ task.currentNodeId = payload.nodeId;
24333
24761
  task.updatedAt = payload.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
24334
- updateTaskStepProcessView(task, payload);
24762
+ updateTaskNodeProcessView(task, {
24763
+ taskId: payload.taskId,
24764
+ nodeId: payload.nodeId,
24765
+ tool: payload.tool,
24766
+ name: payload.name,
24767
+ status: payload.status,
24768
+ updatedAt: payload.updatedAt,
24769
+ currentOperation: payload.currentOperation,
24770
+ waitReason: payload.waitReason,
24771
+ message: payload.message
24772
+ });
24335
24773
  }
24336
- function handleStepResult(msg) {
24337
- const result = msg.payload;
24338
- logRelayFlow("Received step result", {
24339
- taskId: result.taskId,
24340
- stepId: result.stepId,
24341
- ok: result.ok,
24342
- status: result.status,
24343
- error: result.error?.message,
24344
- observation: result.observation
24774
+ function handleNodeResult(msg) {
24775
+ const rawResult = msg.payload;
24776
+ logRelayFlow("Received node result", {
24777
+ taskId: rawResult.taskId,
24778
+ nodeId: rawResult.nodeId,
24779
+ ok: rawResult.ok,
24780
+ status: rawResult.status,
24781
+ error: rawResult.error?.message,
24782
+ observation: rawResult.observation
24345
24783
  });
24346
- const task = tasks.get(result.taskId);
24347
- const orchestrator = orchestrators.get(result.taskId);
24348
- const decision = guardStepResult(task, orchestrator, result);
24784
+ const task = tasks.get(rawResult.taskId);
24785
+ const orchestrator = orchestrators.get(rawResult.taskId);
24786
+ const decision = guardNodeResult(task, orchestrator, rawResult);
24349
24787
  if (!decision.accept) {
24350
24788
  if (decision.reason === "task_missing") {
24351
- console.warn(`[Relay] \u5FFD\u7565 step.result\uFF1A\u4EFB\u52A1\u4E0D\u5B58\u5728 (${result.taskId})`);
24789
+ console.warn(`[Relay] \u5FFD\u7565 node.result\uFF1A\u4EFB\u52A1\u4E0D\u5B58\u5728 (${rawResult.taskId})`);
24352
24790
  } else if (decision.reason === "orchestrator_missing") {
24353
- console.warn(`[Relay] \u5FFD\u7565 step.result\uFF1A\u7F16\u6392\u5668\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u8FDF\u5230/\u91CD\u590D\u7ED3\u679C (${result.taskId}/${result.stepId})`);
24354
- } else if (decision.reason === "step_missing") {
24355
- console.warn(`[Relay] \u5FFD\u7565 step.result\uFF1A\u5F53\u524D\u65E0\u53EF\u6267\u884C\u6B65\u9AA4\uFF0C\u53EF\u80FD\u662F\u8FDF\u5230\u7ED3\u679C (${result.taskId}/${result.stepId})`);
24791
+ console.warn(`[Relay] \u5FFD\u7565 node.result\uFF1A\u7F16\u6392\u5668\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u8FDF\u5230/\u91CD\u590D\u7ED3\u679C (${rawResult.taskId}/${rawResult.nodeId})`);
24792
+ } else if (decision.reason === "node_missing") {
24793
+ console.warn(`[Relay] \u5FFD\u7565 node.result\uFF1A\u5F53\u524D\u65E0\u53EF\u6267\u884C\u8282\u70B9\uFF0C\u53EF\u80FD\u662F\u8FDF\u5230\u7ED3\u679C (${rawResult.taskId}/${rawResult.nodeId})`);
24356
24794
  } else {
24357
24795
  console.warn(
24358
- `[Relay] \u5FFD\u7565 step.result\uFF1Astep \u4E0D\u5339\u914D\uFF0Cexpected=${decision.expectedStepId}, got=${result.stepId}, task=${result.taskId}`
24796
+ `[Relay] \u5FFD\u7565 node.result\uFF1Anode \u4E0D\u5339\u914D\uFF0Cexpected=${decision.expectedNodeId}, got=${rawResult.nodeId}, task=${rawResult.taskId}`
24359
24797
  );
24360
24798
  }
24361
24799
  return;
24362
24800
  }
24363
24801
  const resolvedTask = task;
24364
- const observationError = typeof result?.observation?.error === "string" ? result.observation.error : void 0;
24365
- const normalizedStepError = result.error || (!result.ok ? {
24366
- code: ERROR_CODES.ERR_STEP_EXECUTION_FAILED,
24367
- message: observationError || "Step execution failed"
24368
- } : null);
24369
- const persistedStepResult = normalizeStepResult(result, normalizedStepError);
24370
- persistStepResultOnTask(resolvedTask, persistedStepResult);
24371
- resolvedTask.status = result.ok ? "running" : "failed";
24372
- resolvedTask.currentStepId = result.stepId;
24373
- resolvedTask.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
24374
- resolvedTask.lastError = result.ok ? null : normalizedStepError;
24802
+ const resolvedOrchestrator = orchestrator;
24803
+ const appliedResult = applyTaskExecutionResult(resolvedTask, resolvedOrchestrator, rawResult);
24375
24804
  updateTaskStatusView(resolvedTask, {
24376
- currentStepId: result.stepId,
24377
- currentStepStatus: result.ok ? "succeeded" : "failed"
24805
+ currentNodeId: rawResult.nodeId,
24806
+ currentNodeStatus: appliedResult.persistedNodeResult.ok ? "succeeded" : "failed"
24378
24807
  });
24379
- if (result.ok) {
24380
- continueOrchestrator(result.taskId, result.stepId);
24808
+ if (appliedResult.shouldContinue) {
24809
+ continueOrchestrator(rawResult.taskId, rawResult.nodeId, appliedResult.nextStepIndex ?? void 0);
24381
24810
  } else {
24382
- orchestrators.delete(result.taskId);
24811
+ orchestrators.delete(rawResult.taskId);
24383
24812
  notifyRuntimeTaskFinalized(resolvedTask, "failed");
24384
- void handleBatchTaskTerminal(result.taskId, "failed", resolvedTask.lastError);
24813
+ void handleBatchTaskTerminal(rawResult.taskId, "failed", resolvedTask.lastError);
24385
24814
  }
24386
24815
  }
24387
24816
  function handleTaskStatus(msg) {
@@ -24391,21 +24820,26 @@ function handleTaskStatus(msg) {
24391
24820
  logRelayFlow("Received task status", {
24392
24821
  taskId: status.taskId,
24393
24822
  status: status.status,
24394
- currentStepId: status.currentStepId,
24823
+ currentNodeId: status.currentNodeId,
24395
24824
  currentPageId: status.currentPageId,
24396
24825
  currentTabId: status.currentTabId,
24397
24826
  error: status.lastError?.message
24398
24827
  });
24399
24828
  task.status = status.status;
24400
24829
  task.lastError = status.lastError ?? task.lastError ?? null;
24401
- task.currentStepId = status.currentStepId ?? task.currentStepId;
24830
+ task.currentNodeId = status.currentNodeId ?? task.currentNodeId;
24402
24831
  task.currentPageId = status.currentPageId ?? task.currentPageId;
24403
24832
  task.currentTabId = status.currentTabId ?? task.currentTabId;
24833
+ task.currentContextId = status.currentContextId ?? task.currentContextId;
24834
+ task.resolvedContextId = status.resolvedContextId ?? task.resolvedContextId;
24835
+ task.candidateChildContextIds = Array.isArray(status.candidateChildContextIds) ? status.candidateChildContextIds : task.candidateChildContextIds;
24836
+ task.lastTriggeredNavigation = status.lastTriggeredNavigation ?? task.lastTriggeredNavigation;
24837
+ task.browsingContexts = Array.isArray(status.browsingContexts) ? status.browsingContexts : task.browsingContexts;
24404
24838
  task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
24405
24839
  task.statusView = status.statusView ?? task.statusView;
24406
24840
  if (!task.statusView) {
24407
24841
  updateTaskStatusView(task, {
24408
- currentStepId: status.currentStepId
24842
+ currentNodeId: status.currentNodeId
24409
24843
  });
24410
24844
  }
24411
24845
  if (status.status === "queued") {
@@ -24936,12 +25370,11 @@ app.post("/api/tools/:toolName/execute", async (req, res) => {
24936
25370
  taskType: "browser_action",
24937
25371
  platform: "browser",
24938
25372
  payload: input,
24939
- steps: [{
24940
- stepId: "s1",
25373
+ nodes: [{
25374
+ nodeId: toolName,
24941
25375
  name: toolName,
24942
25376
  tool: toolName,
24943
- args: input,
24944
- status: "pending"
25377
+ args: input
24945
25378
  }],
24946
25379
  targetRuntimeId: effectiveRuntimeId
24947
25380
  };
@@ -24964,7 +25397,7 @@ app.post("/api/tasks", async (req, res) => {
24964
25397
  try {
24965
25398
  const taskConfig = req.body;
24966
25399
  console.log("[Relay] \u6536\u5230HTTP\u4EFB\u52A1\u63D0\u4EA4:", taskConfig);
24967
- const task = await submitTaskSubmission(taskConfig, "sidepanel");
25400
+ const task = await submitTaskSubmission(taskConfig, "api");
24968
25401
  const taskId = task.taskId;
24969
25402
  console.log(`[Relay] \u4EFB\u52A1\u5DF2\u521B\u5EFA: ${taskId}\uFF0C\u5305\u542B ${task.assets.length} \u4E2A\u6587\u4EF6`);
24970
25403
  res.json({ success: true, taskId, channel: "http" });
@@ -25068,12 +25501,14 @@ function startOrchestrator(taskId) {
25068
25501
  console.error(`[Relay] \u4EFB\u52A1\u4E0D\u5B58\u5728: ${taskId}`);
25069
25502
  return;
25070
25503
  }
25071
- if (!task.payload?.steps) {
25072
- console.error(`[Relay] \u4EFB\u52A1\u6CA1\u6709 steps: ${taskId}`);
25504
+ const orchestrator = createTaskOrchestrator(task);
25505
+ if (!orchestrator) {
25506
+ console.error(`[Relay] \u4EFB\u52A1\u6CA1\u6709\u53EF\u6267\u884C\u8282\u70B9/\u6B65\u9AA4: ${taskId}`);
25073
25507
  return;
25074
25508
  }
25075
- console.log(`[Relay] \u4EFB\u52A1\u6709 ${task.payload.steps.length} \u4E2A\u6B65\u9AA4`);
25076
- orchestrators.set(taskId, { currentStepIndex: 0 });
25509
+ const executionLength = getTaskExecutionLength(task);
25510
+ console.log(`[Relay] \u4EFB\u52A1\u6309 nodes \u6267\u884C\uFF0C\u5171 ${executionLength} \u4E2A\u8282\u70B9`);
25511
+ orchestrators.set(taskId, orchestrator);
25077
25512
  void sendNextStep(taskId);
25078
25513
  }
25079
25514
  async function sendNextStep(taskId) {
@@ -25088,17 +25523,17 @@ async function sendNextStep(taskId) {
25088
25523
  console.error(`[Relay] \u7F16\u6392\u5668\u4E0D\u5B58\u5728: ${taskId}`);
25089
25524
  return;
25090
25525
  }
25091
- if (!task.payload?.steps) {
25092
- console.error(`[Relay] \u4EFB\u52A1\u6CA1\u6709 steps`);
25526
+ const executionLength = getTaskExecutionLength(task);
25527
+ if (executionLength === 0) {
25528
+ console.error(`[Relay] \u4EFB\u52A1\u6CA1\u6709\u53EF\u6267\u884C\u8282\u70B9`);
25093
25529
  return;
25094
25530
  }
25095
- const steps = task.payload.steps;
25096
- console.log(`[Relay] \u5F53\u524D\u6B65\u9AA4\u7D22\u5F15: ${orch.currentStepIndex}, \u603B\u6B65\u9AA4\u6570: ${steps.length}`);
25097
- if (orch.currentStepIndex >= steps.length) {
25531
+ console.log(`[Relay] \u5F53\u524D\u8282\u70B9\u7D22\u5F15: ${orch.currentStepIndex}, \u603B\u8282\u70B9\u6570: ${executionLength}`);
25532
+ if (orch.currentStepIndex >= executionLength) {
25098
25533
  task.status = "completed";
25099
25534
  task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25100
25535
  updateTaskStatusView(task, {
25101
- currentStepId: task.currentStepId
25536
+ currentNodeId: task.currentNodeId
25102
25537
  });
25103
25538
  orchestrators.delete(taskId);
25104
25539
  console.log(`[Relay] Task ${taskId} completed`);
@@ -25106,20 +25541,30 @@ async function sendNextStep(taskId) {
25106
25541
  void handleBatchTaskTerminal(taskId, "completed", null);
25107
25542
  return;
25108
25543
  }
25109
- const step = steps[orch.currentStepIndex];
25110
- task.status = "running";
25111
- task.currentStepId = step.stepId;
25112
- task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25544
+ const step = markTaskExecutionDispatch(task, orch);
25545
+ if (!step) {
25546
+ task.status = "failed";
25547
+ task.lastError = {
25548
+ code: ERROR_CODES.ERR_INVALID_TASK,
25549
+ message: `Unable to resolve executable node at index ${orch.currentStepIndex}`
25550
+ };
25551
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25552
+ updateTaskStatusView(task);
25553
+ orchestrators.delete(taskId);
25554
+ notifyRuntimeTaskFinalized(task, "failed");
25555
+ void handleBatchTaskTerminal(taskId, "failed", task.lastError);
25556
+ return;
25557
+ }
25113
25558
  updateTaskStatusView(task, {
25114
- currentStepId: step.stepId,
25115
- currentStepTool: step.tool,
25116
- currentStepName: step.name
25559
+ currentNodeId: task.currentNodeId ?? step.nodeId,
25560
+ currentNodeTool: step.tool,
25561
+ currentNodeName: step.name
25117
25562
  });
25118
25563
  const stepMessage = {
25119
- type: "step.submit",
25120
- requestId: `step_${taskId}_${orch.currentStepIndex}`,
25564
+ type: "node.submit",
25565
+ requestId: `node_${taskId}_${orch.currentStepIndex}`,
25121
25566
  timestamp: Date.now(),
25122
- payload: { taskId, step }
25567
+ payload: { taskId, node: step }
25123
25568
  };
25124
25569
  let targetRuntimeId;
25125
25570
  try {
@@ -25153,17 +25598,17 @@ async function sendNextStep(taskId) {
25153
25598
  void handleBatchTaskTerminal(taskId, "failed", task.lastError);
25154
25599
  return;
25155
25600
  }
25156
- console.log(`[Relay] Sent step ${orch.currentStepIndex + 1}/${steps.length} for task ${taskId}`);
25601
+ console.log(`[Relay] Sent node ${orch.currentStepIndex + 1}/${executionLength} for task ${taskId}`);
25157
25602
  }
25158
- function continueOrchestrator(taskId, completedStepId) {
25159
- console.log(`[Relay] continueOrchestrator \u88AB\u8C03\u7528: ${taskId}, stepId: ${completedStepId}`);
25603
+ function continueOrchestrator(taskId, completedNodeId, nextStepIndex) {
25604
+ console.log(`[Relay] continueOrchestrator \u88AB\u8C03\u7528: ${taskId}, nodeId: ${completedNodeId}`);
25160
25605
  const orch = orchestrators.get(taskId);
25161
25606
  if (!orch) {
25162
25607
  console.error(`[Relay] \u7F16\u6392\u5668\u4E0D\u5B58\u5728: ${taskId}`);
25163
25608
  return;
25164
25609
  }
25165
- orch.currentStepIndex++;
25166
- console.log(`[Relay] \u6B65\u9AA4\u7D22\u5F15\u9012\u589E\u81F3: ${orch.currentStepIndex}`);
25610
+ advanceTaskOrchestrator(orch, nextStepIndex);
25611
+ console.log(`[Relay] \u8282\u70B9\u7D22\u5F15\u9012\u589E\u81F3: ${orch.currentStepIndex}`);
25167
25612
  setTimeout(() => {
25168
25613
  void sendNextStep(taskId);
25169
25614
  }, STEP_DISPATCH_INTERVAL_MS);