@codemation/core-nodes 0.8.0 → 0.9.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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentConfigInspector, AgentConnectionNodeCollector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, ChildExecutionScopeFactory, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, DefinedNodeRegistry, GenAiTelemetryAttributeNames, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RetryPolicy, RunnableOutputBehaviorResolver, WorkflowBuilder, chatModel, defineCredential, defineNode, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
1
+ import { AgentConfigInspector, AgentConnectionNodeCollector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, ChildExecutionScopeFactory, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, DefinedNodeRegistry, GenAiTelemetryAttributeNames, InboxChannelResolverToken, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RetryPolicy, RunnableOutputBehaviorResolver, SuspensionRequest, WorkflowBuilder, chatModel, defineCredential, defineHumanApprovalNode, defineNode, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
2
2
  import dns from "node:dns/promises";
3
3
  import { createOpenAI } from "@ai-sdk/openai";
4
4
  import { CredentialResolverFactory } from "@codemation/core/bootstrap";
@@ -1279,6 +1279,7 @@ const string$1 = (params) => {
1279
1279
  };
1280
1280
  const integer = /^-?\d+$/;
1281
1281
  const number$1 = /^-?\d+(?:\.\d+)?$/;
1282
+ const boolean$1 = /^(?:true|false)$/i;
1282
1283
  const lowercase = /^[^A-Z]*$/;
1283
1284
  const uppercase = /^[^a-z]*$/;
1284
1285
 
@@ -2057,6 +2058,24 @@ const $ZodNumberFormat = /* @__PURE__ */ $constructor("$ZodNumberFormat", (inst,
2057
2058
  $ZodCheckNumberFormat.init(inst, def);
2058
2059
  $ZodNumber.init(inst, def);
2059
2060
  });
2061
+ const $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
2062
+ $ZodType.init(inst, def);
2063
+ inst._zod.pattern = boolean$1;
2064
+ inst._zod.parse = (payload, _ctx) => {
2065
+ if (def.coerce) try {
2066
+ payload.value = Boolean(payload.value);
2067
+ } catch (_) {}
2068
+ const input = payload.value;
2069
+ if (typeof input === "boolean") return payload;
2070
+ payload.issues.push({
2071
+ expected: "boolean",
2072
+ code: "invalid_type",
2073
+ input,
2074
+ inst
2075
+ });
2076
+ return payload;
2077
+ };
2078
+ });
2060
2079
  const $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
2061
2080
  $ZodType.init(inst, def);
2062
2081
  inst._zod.parse = (payload) => payload;
@@ -3133,6 +3152,13 @@ function _int(Class, params) {
3133
3152
  });
3134
3153
  }
3135
3154
  /* @__NO_SIDE_EFFECTS__ */
3155
+ function _boolean(Class, params) {
3156
+ return new Class({
3157
+ type: "boolean",
3158
+ ...normalizeParams(params)
3159
+ });
3160
+ }
3161
+ /* @__NO_SIDE_EFFECTS__ */
3136
3162
  function _unknown(Class) {
3137
3163
  return new Class({ type: "unknown" });
3138
3164
  }
@@ -3299,6 +3325,17 @@ function _array(Class, element, params) {
3299
3325
  });
3300
3326
  }
3301
3327
  /* @__NO_SIDE_EFFECTS__ */
3328
+ function _custom(Class, fn, _params) {
3329
+ const norm = normalizeParams(_params);
3330
+ norm.abort ?? (norm.abort = true);
3331
+ return new Class({
3332
+ type: "custom",
3333
+ check: "custom",
3334
+ fn,
3335
+ ...norm
3336
+ });
3337
+ }
3338
+ /* @__NO_SIDE_EFFECTS__ */
3302
3339
  function _refine(Class, fn, _params) {
3303
3340
  return new Class({
3304
3341
  type: "custom",
@@ -4895,6 +4932,14 @@ const ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, d
4895
4932
  function int(params) {
4896
4933
  return _int(ZodNumberFormat, params);
4897
4934
  }
4935
+ const ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
4936
+ $ZodBoolean.init(inst, def);
4937
+ ZodType.init(inst, def);
4938
+ inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
4939
+ });
4940
+ function boolean(params) {
4941
+ return _boolean(ZodBoolean, params);
4942
+ }
4898
4943
  const ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
4899
4944
  $ZodUnknown.init(inst, def);
4900
4945
  ZodType.init(inst, def);
@@ -5206,6 +5251,9 @@ const ZodCustom = /* @__PURE__ */ $constructor("ZodCustom", (inst, def) => {
5206
5251
  ZodType.init(inst, def);
5207
5252
  inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
5208
5253
  });
5254
+ function custom(fn, _params) {
5255
+ return _custom(ZodCustom, fn ?? (() => true), _params);
5256
+ }
5209
5257
  function refine(fn, _params = {}) {
5210
5258
  return _refine(ZodCustom, fn, _params);
5211
5259
  }
@@ -5455,12 +5503,22 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
5455
5503
  this.repairPolicy = repairPolicy;
5456
5504
  }
5457
5505
  async execute(args) {
5506
+ if (args.plannedToolCalls.filter((c) => c.binding.humanApproval !== void 0).length > 0 && args.plannedToolCalls.length > 1) return args.plannedToolCalls.map((c) => ({
5507
+ toolName: c.binding.config.name,
5508
+ toolCallId: c.toolCall.id ?? c.binding.config.name,
5509
+ result: { error: c.binding.humanApproval !== void 0 ? `HITL tool '${c.binding.config.name}' cannot be called alongside other tools in the same turn; call it alone.` : `deferred: a HITL tool in the same turn blocked execution. Retry this tool alone in the next turn.` },
5510
+ serialized: JSON.stringify({ error: c.binding.humanApproval !== void 0 ? `HITL tool '${c.binding.config.name}' cannot be called alongside other tools in the same turn; call it alone.` : `deferred: a HITL tool in the same turn blocked execution. Retry this tool alone in the next turn.` })
5511
+ }));
5458
5512
  const results = await Promise.allSettled(args.plannedToolCalls.map(async (plannedToolCall) => await this.executePlannedToolCall({
5459
5513
  ...args,
5460
5514
  plannedToolCall
5461
5515
  })));
5462
5516
  const rejected = results.find((result) => result.status === "rejected");
5463
- if (rejected?.status === "rejected") throw rejected.reason instanceof Error ? rejected.reason : new Error(String(rejected.reason));
5517
+ if (rejected?.status === "rejected") {
5518
+ const reason = rejected.reason;
5519
+ if (reason instanceof SuspensionRequest) throw reason;
5520
+ throw reason instanceof Error ? reason : new Error(String(reason));
5521
+ }
5464
5522
  return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
5465
5523
  }
5466
5524
  async executePlannedToolCall(args) {
@@ -5545,6 +5603,34 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
5545
5603
  result
5546
5604
  };
5547
5605
  } catch (error) {
5606
+ if (error instanceof SuspensionRequest) {
5607
+ const pendingToolCallId = plannedToolCall.toolCall.id ?? plannedToolCall.binding.config.name;
5608
+ const checkpoint = {
5609
+ conversation: args.conversationSnapshot ? [...args.conversationSnapshot] : [],
5610
+ turnCount: args.turnCount ?? 0,
5611
+ toolCallCount: args.toolCallCount ?? 0,
5612
+ pendingToolCallId,
5613
+ agentName: args.agentName,
5614
+ modelId: args.modelId ?? ""
5615
+ };
5616
+ const agentReasoning = this.extractLastAssistantText(args.conversationSnapshot ?? []);
5617
+ const augmented = new SuspensionRequest({
5618
+ ...error.request,
5619
+ metadata: {
5620
+ ...error.request.metadata,
5621
+ agentCheckpoint: checkpoint,
5622
+ pendingToolCallId,
5623
+ agentReasoning,
5624
+ onRejected: plannedToolCall.binding.humanApproval?.onRejected ?? "return"
5625
+ }
5626
+ });
5627
+ await span.end({
5628
+ status: "error",
5629
+ statusMessage: "suspended",
5630
+ endedAt: /* @__PURE__ */ new Date()
5631
+ });
5632
+ throw augmented;
5633
+ }
5548
5634
  const classification = this.errorClassifier.classify({
5549
5635
  error,
5550
5636
  toolName: plannedToolCall.binding.config.name,
@@ -5720,6 +5806,24 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
5720
5806
  extractErrorDetails(error) {
5721
5807
  return error.details;
5722
5808
  }
5809
+ /**
5810
+ * Extracts the text content from the last assistant message in the conversation snapshot.
5811
+ * Used to populate `agentReasoning` in the HITL suspension metadata.
5812
+ */
5813
+ extractLastAssistantText(conversation) {
5814
+ for (let i = conversation.length - 1; i >= 0; i--) {
5815
+ const msg = conversation[i];
5816
+ if (msg?.role !== "assistant") continue;
5817
+ const content = msg.content;
5818
+ if (typeof content === "string") return content;
5819
+ if (Array.isArray(content)) {
5820
+ const textParts = content.filter((part) => typeof part === "object" && part.type === "text").map((part) => part.text);
5821
+ if (textParts.length > 0) return textParts.join("");
5822
+ }
5823
+ break;
5824
+ }
5825
+ return "";
5826
+ }
5723
5827
  serializeIssue(issue$1) {
5724
5828
  const result = {
5725
5829
  path: [...issue$1.path],
@@ -6073,6 +6177,7 @@ var AgentItemPortMap = class {
6073
6177
  //#endregion
6074
6178
  //#region src/nodes/AIAgentNode.ts
6075
6179
  var _ref, _ref2, _ref3, _ref4, _ref5;
6180
+ const HITL_SOLO_CONSTRAINT_SENTENCE = "This tool requires human approval and may take time. Call it alone — do not invoke other tools in the same turn. Your turn will be paused until a decision is made.";
6076
6181
  let AIAgentNode = class AIAgentNode$1 {
6077
6182
  kind = "node";
6078
6183
  outputPorts = ["main"];
@@ -6090,13 +6195,90 @@ let AIAgentNode = class AIAgentNode$1 {
6090
6195
  this.connectionCredentialExecutionContextFactory = this.executionHelpers.createConnectionCredentialExecutionContextFactory(credentialSessions);
6091
6196
  }
6092
6197
  async execute(args) {
6093
- const prepared = await this.getOrPrepareExecution(args.ctx);
6198
+ const { ctx } = args;
6199
+ if (ctx.resumeContext) return this.executeResumed(args, ctx.resumeContext);
6200
+ const prepared = await this.getOrPrepareExecution(ctx);
6094
6201
  const itemWithMappedJson = {
6095
6202
  ...args.item,
6096
6203
  json: args.input
6097
6204
  };
6098
6205
  return (await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items)).json;
6099
6206
  }
6207
+ /**
6208
+ * Resume path: re-enters the agent loop after a HITL suspension.
6209
+ * Reconstructs the conversation from the checkpoint, injects the human decision
6210
+ * as a tool_result, and continues the loop from where it suspended.
6211
+ */
6212
+ async executeResumed(args, resumeContext) {
6213
+ const { ctx } = args;
6214
+ const taskMetadata = resumeContext.task.metadata ?? {};
6215
+ const checkpoint = taskMetadata["agentCheckpoint"];
6216
+ const onRejected = taskMetadata["onRejected"] ?? "return";
6217
+ if (!checkpoint) {
6218
+ const prepared$1 = await this.getOrPrepareExecution(ctx);
6219
+ const itemWithMappedJson = {
6220
+ ...args.item,
6221
+ json: args.input
6222
+ };
6223
+ return (await this.runAgentForItem(prepared$1, itemWithMappedJson, args.itemIndex, args.items)).json;
6224
+ }
6225
+ if (resumeContext.decision.kind === "decided" && resumeContext.decision.value === null && onRejected === "halt") return;
6226
+ const decision = this.normalizeDecision(resumeContext);
6227
+ if (decision.status === "rejected" && onRejected === "halt") return;
6228
+ const prepared = await this.getOrPrepareExecution(ctx);
6229
+ const item = args.item;
6230
+ const itemInputsByPort = AgentItemPortMap.fromItem(item);
6231
+ const itemScopedTools = this.createItemScopedTools(prepared.resolvedTools, ctx, item, args.itemIndex, args.items);
6232
+ const toolResultEntry = {
6233
+ toolName: checkpoint.pendingToolCallId,
6234
+ toolCallId: checkpoint.pendingToolCallId,
6235
+ result: decision,
6236
+ serialized: JSON.stringify(decision)
6237
+ };
6238
+ const conversation = [...checkpoint.conversation, AgentMessageFactory.createToolResultsMessage([toolResultEntry])];
6239
+ const loopResult = await this.runTurnLoopUntilFinalAnswer({
6240
+ prepared,
6241
+ itemInputsByPort,
6242
+ itemScopedTools,
6243
+ conversation,
6244
+ resumedTurnCount: checkpoint.turnCount,
6245
+ resumedToolCallCount: checkpoint.toolCallCount
6246
+ });
6247
+ await ctx.telemetry.recordMetric({
6248
+ name: CodemationTelemetryMetricNames.agentTurns,
6249
+ value: loopResult.turnCount
6250
+ });
6251
+ await ctx.telemetry.recordMetric({
6252
+ name: CodemationTelemetryMetricNames.agentToolCalls,
6253
+ value: loopResult.toolCallCount
6254
+ });
6255
+ const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
6256
+ return this.buildOutputItem(item, outputJson).json;
6257
+ }
6258
+ /**
6259
+ * Normalizes a {@link ResumeContext} decision into a flat JSON-serializable shape
6260
+ * suitable for injection as a tool_result content.
6261
+ */
6262
+ normalizeDecision(resumeContext) {
6263
+ const { decision } = resumeContext;
6264
+ if (decision.kind === "decided") {
6265
+ const value = decision.value;
6266
+ return {
6267
+ status: (typeof value === "object" && value !== null && "approved" in value ? Boolean(value["approved"]) : true) ? "approved" : "rejected",
6268
+ value: decision.value,
6269
+ actor: decision.actor,
6270
+ decidedAt: decision.decidedAt.toISOString()
6271
+ };
6272
+ }
6273
+ if (decision.kind === "timed_out") return {
6274
+ status: "timed_out",
6275
+ at: decision.at.toISOString()
6276
+ };
6277
+ return {
6278
+ status: "auto_accepted",
6279
+ at: decision.at.toISOString()
6280
+ };
6281
+ }
6100
6282
  async getOrPrepareExecution(ctx) {
6101
6283
  let pending = this.preparedByExecutionContext.get(ctx);
6102
6284
  if (!pending) {
@@ -6217,7 +6399,7 @@ let AIAgentNode = class AIAgentNode$1 {
6217
6399
  const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
6218
6400
  const { ctx, guardrails, toolLoadingStrategy } = prepared;
6219
6401
  let finalText = "";
6220
- let toolCallCount = 0;
6402
+ let toolCallCount = args.resumedToolCallCount ?? 0;
6221
6403
  let turnCount = 0;
6222
6404
  const repairAttemptsByToolName = /* @__PURE__ */ new Map();
6223
6405
  /** Tool IDs surfaced by find_tools across all prior turns in this item run. */
@@ -6258,11 +6440,17 @@ let AIAgentNode = class AIAgentNode$1 {
6258
6440
  const plannedToolCalls = this.planToolCalls(itemScopedTools, coordinatorCalls, ctx.nodeId);
6259
6441
  toolCallCount += plannedToolCalls.length;
6260
6442
  await this.markQueuedTools(plannedToolCalls, ctx);
6443
+ const assistantMsg = result.assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(result.text, result.toolCalls);
6444
+ const conversationWithAssistant = [...conversation, assistantMsg];
6261
6445
  const executed = await this.toolExecutionCoordinator.execute({
6262
6446
  plannedToolCalls,
6263
6447
  ctx,
6264
6448
  agentName: this.getAgentDisplayName(ctx),
6265
- repairAttemptsByToolName
6449
+ repairAttemptsByToolName,
6450
+ conversationSnapshot: conversationWithAssistant,
6451
+ turnCount,
6452
+ toolCallCount,
6453
+ modelId: this.resolveChatModelName(ctx.config.chatModel)
6266
6454
  });
6267
6455
  coordinatorExecutedCalls.push(...executed);
6268
6456
  }
@@ -6324,6 +6512,7 @@ let AIAgentNode = class AIAgentNode$1 {
6324
6512
  connectionNodeId: ConnectionNodeIdFactory.toolConnectionNodeId(ctx.nodeId, entry.config.name),
6325
6513
  getCredentialRequirements: () => entry.config.getCredentialRequirements?.() ?? []
6326
6514
  });
6515
+ const hitlBehavior = this.resolveHumanApprovalBehavior(entry.config);
6327
6516
  return {
6328
6517
  config: entry.config,
6329
6518
  inputSchema: entry.runtime.inputSchema,
@@ -6338,11 +6527,22 @@ let AIAgentNode = class AIAgentNode$1 {
6338
6527
  items,
6339
6528
  hooks
6340
6529
  });
6341
- }
6530
+ },
6531
+ ...hitlBehavior !== void 0 ? { humanApproval: hitlBehavior } : {}
6342
6532
  };
6343
6533
  });
6344
6534
  }
6345
6535
  /**
6536
+ * Detects whether a tool config is backed by a `defineHumanApprovalNode` marker
6537
+ * and returns the HITL behavior config, or `undefined` when not a HITL tool.
6538
+ */
6539
+ resolveHumanApprovalBehavior(config$1) {
6540
+ if (!this.isNodeBackedToolConfig(config$1)) return void 0;
6541
+ const marker = config$1.node.humanApprovalToolBehavior;
6542
+ if (marker === void 0) return void 0;
6543
+ return { onRejected: marker.onRejected ?? "return" };
6544
+ }
6545
+ /**
6346
6546
  * Invoke a text turn using the merged tool set from item-scoped tools (coordinator-managed)
6347
6547
  * and strategy tools (find_tools + discovered MCP tools).
6348
6548
  * Strategy tools take precedence for names that overlap.
@@ -6372,6 +6572,8 @@ let AIAgentNode = class AIAgentNode$1 {
6372
6572
  /**
6373
6573
  * Builds a ToolSet from resolved tools for strategy initialization.
6374
6574
  * The strategy uses this for its "always-included" node-backed tool descriptions.
6575
+ * HITL tools (detected via the `humanApprovalToolBehavior` field set by `defineHumanApprovalNode`) get the solo-constraint sentence
6576
+ * appended to their description.
6375
6577
  */
6376
6578
  buildToolSetFromResolved(resolvedTools) {
6377
6579
  if (resolvedTools.length === 0) return {};
@@ -6381,8 +6583,10 @@ let AIAgentNode = class AIAgentNode$1 {
6381
6583
  schemaName: entry.config.name,
6382
6584
  requireObjectRoot: true
6383
6585
  });
6586
+ const baseDescription = entry.config.description ?? entry.runtime.defaultDescription;
6587
+ const description = this.resolveHumanApprovalBehavior(entry.config) !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : baseDescription;
6384
6588
  toolSet[entry.config.name] = {
6385
- description: entry.config.description ?? entry.runtime.defaultDescription,
6589
+ description,
6386
6590
  inputSchema: jsonSchema(schemaRecord)
6387
6591
  };
6388
6592
  }
@@ -6410,8 +6614,10 @@ let AIAgentNode = class AIAgentNode$1 {
6410
6614
  schemaName: entry.config.name,
6411
6615
  requireObjectRoot: true
6412
6616
  });
6617
+ const baseDescription = entry.config.description;
6618
+ const description = entry.humanApproval !== void 0 && baseDescription !== void 0 ? `${baseDescription} ${HITL_SOLO_CONSTRAINT_SENTENCE}` : entry.humanApproval !== void 0 ? HITL_SOLO_CONSTRAINT_SENTENCE : baseDescription;
6413
6619
  toolSet[entry.config.name] = {
6414
- description: entry.config.description,
6620
+ description,
6415
6621
  inputSchema: jsonSchema(schemaRecord)
6416
6622
  };
6417
6623
  }
@@ -8729,5 +8935,92 @@ const collectionDeleteNode = defineNode({
8729
8935
  });
8730
8936
 
8731
8937
  //#endregion
8732
- export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Assertion, AssertionNode, BM25Index, Callback, CallbackNode, CallbackResultNormalizer, CodemationChatModelConfig, CodemationChatModelFactory, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, CronTrigger, CronTriggerNode, DeferredMetaToolStrategy, DeferredMetaToolStrategyFactory, Filter, FilterNode, HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES, HttpRequest, HttpRequestNode, If, IfNode, IsTestRun, IsTestRunNode, ManagedModelFetcher, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory, OpenAiChatModelPresets, OpenAiStrictJsonSchemaFactory, SSRFBlockedError, Split, SplitNode, SsrfGuard, SubWorkflow, SubWorkflowNode, Switch, SwitchNode, TestTrigger, TestTriggerNode, Wait, WaitDuration, WaitNode, WebhookRespondNowAndContinueError, WebhookRespondNowError, WebhookTrigger, WebhookTriggerNode, WorkflowAuthoringBuilder, WorkflowBranchBuilder, WorkflowChain, apiKeyCredentialType, basicAuthCredentialType, bearerTokenCredentialType, collectionDeleteNode, collectionFindOneNode, collectionGetNode, collectionInsertNode, collectionListNode, collectionUpdateNode, createWorkflowBuilder, defineRestNode, oauth2ClientCredentialsType, openAiChatModelPresets, registerCoreNodes, workflow };
8938
+ //#region src/nodes/InboxApprovalNode.types.ts
8939
+ function resolveSubjectField(field, item) {
8940
+ return typeof field === "function" ? field({ item }) : field;
8941
+ }
8942
+ /**
8943
+ * Auto-detecting inbox approval node.
8944
+ *
8945
+ * Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
8946
+ * at runtime:
8947
+ * - In managed mode (PairingConfig present): routes to the control-plane inbox.
8948
+ * - Otherwise: routes to the local inbox.
8949
+ *
8950
+ * Authors use this node directly; no extra wiring needed per deployment mode.
8951
+ */
8952
+ const inboxApproval = defineHumanApprovalNode({
8953
+ key: "inbox.approval",
8954
+ title: "Inbox Approval",
8955
+ description: "Suspend and wait for a human reviewer to approve or reject.",
8956
+ icon: "lucide:inbox",
8957
+ channel: "inbox",
8958
+ configSchema: object({
8959
+ title: custom((v) => typeof v === "string" || typeof v === "function"),
8960
+ body: custom((v) => typeof v === "string" || typeof v === "function"),
8961
+ priority: _enum([
8962
+ "low",
8963
+ "normal",
8964
+ "high"
8965
+ ]).default("normal"),
8966
+ timeout: string().default("24h"),
8967
+ onTimeout: _enum(["halt", "auto-accept"]).default("halt")
8968
+ }),
8969
+ decisionSchema: object({
8970
+ approved: boolean(),
8971
+ note: string().optional()
8972
+ }),
8973
+ defaultTimeout: "24h",
8974
+ defaultOnTimeout: "halt",
8975
+ async deliver({ task, config: config$1, item }, ctx) {
8976
+ const resolver = ctx.resolve(InboxChannelResolverToken);
8977
+ if (!resolver) throw new Error("inboxApproval: no InboxChannelResolver registered. Ensure the host DI container is wired.");
8978
+ const { channel, workspaceId } = resolver.resolve();
8979
+ const subject = {
8980
+ title: resolveSubjectField(config$1.title, item),
8981
+ summary: resolveSubjectField(config$1.body, item),
8982
+ attributes: {
8983
+ workflowId: ctx.workflowId,
8984
+ item: item.json
8985
+ }
8986
+ };
8987
+ const delivery = await channel.deliver({
8988
+ task,
8989
+ subject,
8990
+ priority: config$1.priority,
8991
+ item,
8992
+ workspaceId
8993
+ });
8994
+ ctx.telemetry.addSpanEvent({
8995
+ name: "hitl.task.delivered",
8996
+ attributes: {
8997
+ taskId: task.taskId,
8998
+ channel: channel.kind
8999
+ }
9000
+ });
9001
+ return delivery;
9002
+ },
9003
+ async onDecision({ decision, actor, delivery }, ctx) {
9004
+ const resolver = ctx.resolve(InboxChannelResolverToken);
9005
+ if (!resolver) return;
9006
+ const { channel } = resolver.resolve();
9007
+ await channel.updateOnDecision?.({
9008
+ delivery,
9009
+ decision,
9010
+ actor
9011
+ });
9012
+ },
9013
+ async onTimeout({ delivery, policy }, ctx) {
9014
+ const resolver = ctx.resolve(InboxChannelResolverToken);
9015
+ if (!resolver) return;
9016
+ const { channel } = resolver.resolve();
9017
+ await channel.updateOnTimeout?.({
9018
+ delivery,
9019
+ policy
9020
+ });
9021
+ }
9022
+ });
9023
+
9024
+ //#endregion
9025
+ export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Assertion, AssertionNode, BM25Index, Callback, CallbackNode, CallbackResultNormalizer, CodemationChatModelConfig, CodemationChatModelFactory, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, CronTrigger, CronTriggerNode, DeferredMetaToolStrategy, DeferredMetaToolStrategyFactory, Filter, FilterNode, HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES, HttpRequest, HttpRequestNode, If, IfNode, IsTestRun, IsTestRunNode, ManagedModelFetcher, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory, OpenAiChatModelPresets, OpenAiStrictJsonSchemaFactory, SSRFBlockedError, Split, SplitNode, SsrfGuard, SubWorkflow, SubWorkflowNode, Switch, SwitchNode, TestTrigger, TestTriggerNode, Wait, WaitDuration, WaitNode, WebhookRespondNowAndContinueError, WebhookRespondNowError, WebhookTrigger, WebhookTriggerNode, WorkflowAuthoringBuilder, WorkflowBranchBuilder, WorkflowChain, apiKeyCredentialType, basicAuthCredentialType, bearerTokenCredentialType, collectionDeleteNode, collectionFindOneNode, collectionGetNode, collectionInsertNode, collectionListNode, collectionUpdateNode, createWorkflowBuilder, defineRestNode, inboxApproval, oauth2ClientCredentialsType, openAiChatModelPresets, registerCoreNodes, workflow };
8733
9026
  //# sourceMappingURL=index.js.map