@codemation/core-nodes 0.13.0 → 0.14.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.cjs +34 -433
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +95 -1312
  5. package/dist/index.d.ts +95 -1312
  6. package/dist/index.js +35 -435
  7. package/dist/index.js.map +1 -1
  8. package/dist/metadata.json +1 -1
  9. package/package.json +2 -2
  10. package/src/authoring/defineRestNode.types.ts +0 -84
  11. package/src/canvasIconName.ts +0 -7
  12. package/src/chatModels/CodemationChatModelConfig.ts +0 -10
  13. package/src/chatModels/CodemationChatModelFactory.ts +0 -7
  14. package/src/chatModels/ManagedHmacSignerFactory.types.ts +0 -35
  15. package/src/chatModels/OpenAIChatModelFactory.ts +0 -2
  16. package/src/chatModels/OpenAiChatModelPresetsFactory.ts +0 -5
  17. package/src/chatModels/OpenAiCredentialSession.ts +0 -1
  18. package/src/chatModels/OpenAiStrictJsonSchemaFactory.ts +0 -21
  19. package/src/credentials/ApiKeyCredentialType.ts +0 -3
  20. package/src/credentials/BasicAuthCredentialType.ts +0 -4
  21. package/src/credentials/BearerTokenCredentialType.ts +0 -4
  22. package/src/credentials/OAuth2ClientCredentialsTypeFactory.ts +0 -19
  23. package/src/credentials/OAuth2TokenExchangeFactory.ts +0 -7
  24. package/src/http/HttpBodyBuilder.ts +0 -16
  25. package/src/http/HttpRequestExecutor.ts +0 -35
  26. package/src/http/HttpUrlBuilder.ts +0 -4
  27. package/src/http/SSRFBlockedError.ts +0 -4
  28. package/src/http/SsrfGuard.ts +10 -50
  29. package/src/http/httpRequest.types.ts +0 -49
  30. package/src/index.ts +1 -0
  31. package/src/nodes/AIAgentConfig.ts +0 -44
  32. package/src/nodes/AIAgentExecutionHelpersFactory.ts +0 -37
  33. package/src/nodes/AIAgentNode.ts +0 -132
  34. package/src/nodes/AgentBinaryContentFactory.ts +0 -12
  35. package/src/nodes/AgentLoopCheckpoint.types.ts +0 -13
  36. package/src/nodes/AgentMessageFactory.ts +0 -18
  37. package/src/nodes/AgentStructuredOutputRunner.ts +0 -17
  38. package/src/nodes/AgentToolExecutionCoordinator.ts +0 -12
  39. package/src/nodes/AgentToolResultContentFactory.ts +0 -29
  40. package/src/nodes/AssertionNode.ts +0 -14
  41. package/src/nodes/BM25Index.ts +0 -14
  42. package/src/nodes/ConnectionCredentialExecutionContextFactory.ts +0 -5
  43. package/src/nodes/ConnectionCredentialNode.ts +0 -4
  44. package/src/nodes/CronTriggerFactory.ts +0 -9
  45. package/src/nodes/DeferredMetaToolStrategy.ts +0 -18
  46. package/src/nodes/DeferredMetaToolStrategyFactory.ts +0 -5
  47. package/src/nodes/HttpRequestNodeFactory.ts +0 -14
  48. package/src/nodes/InboxApprovalNode.types.ts +0 -16
  49. package/src/nodes/IsTestRunNode.ts +0 -8
  50. package/src/nodes/ManualTriggerFactory.ts +0 -3
  51. package/src/nodes/ManualTriggerNode.ts +0 -4
  52. package/src/nodes/MergeNode.ts +0 -1
  53. package/src/nodes/NodeBackedToolRuntime.ts +0 -14
  54. package/src/nodes/SubWorkflowNode.ts +0 -3
  55. package/src/nodes/SwitchNode.ts +0 -3
  56. package/src/nodes/TestTriggerNode.ts +0 -9
  57. package/src/nodes/aiAgentSupport.types.ts +0 -16
  58. package/src/nodes/assertion.ts +0 -10
  59. package/src/nodes/codemationDocumentScannerNode.ts +0 -18
  60. package/src/nodes/collections/collectionListNode.types.ts +0 -1
  61. package/src/nodes/httpRequest.ts +0 -68
  62. package/src/nodes/isTestRun.ts +0 -4
  63. package/src/nodes/mapData.ts +0 -1
  64. package/src/nodes/merge.ts +0 -4
  65. package/src/nodes/mergeExecutionUtils.types.ts +0 -3
  66. package/src/nodes/nodeOptions.types.ts +0 -8
  67. package/src/nodes/schedulePollingTrigger.ts +37 -0
  68. package/src/nodes/split.ts +0 -4
  69. package/src/nodes/testTrigger.ts +0 -21
  70. package/src/nodes/wait.ts +0 -1
  71. package/src/nodes/webhookTriggerNode.ts +0 -5
  72. package/src/register.types.ts +0 -10
  73. package/src/workflows/AIAgentConnectionWorkflowExpander.ts +0 -3
@@ -3,10 +3,6 @@ import { node } from "@codemation/core";
3
3
 
4
4
  import type { ConnectionCredentialNodeConfig } from "./ConnectionCredentialNodeConfig";
5
5
 
6
- /**
7
- * Placeholder runnable node for connection-owned workflow nodes (LLM/tool slots).
8
- * The engine does not schedule these; they exist for credentials, tokens, and UI identity.
9
- */
10
6
  @node({ packageName: "@codemation/core-nodes" })
11
7
  export class ConnectionCredentialNode implements RunnableNode<ConnectionCredentialNodeConfig> {
12
8
  kind = "node" as const;
@@ -8,15 +8,6 @@ import type { NodeBaseOptions } from "./nodeOptions.types";
8
8
 
9
9
  export type CronTickJson = { firedAt: string; scheduledFor: string };
10
10
 
11
- /**
12
- * Schedules a workflow on a standard cron expression.
13
- *
14
- * Each tick emits one item: `{ firedAt: string, scheduledFor: string }` — both ISO-8601 timestamps.
15
- * `firedAt` is the wall-clock moment the callback ran; `scheduledFor` is the cron-computed
16
- * firing instant (these differ when the job was delayed).
17
- *
18
- * Timezone defaults to UTC when omitted — cron without an explicit TZ is a DST footgun.
19
- */
20
11
  export class CronTrigger implements TriggerNodeConfig<CronTickJson> {
21
12
  readonly kind = "trigger" as const;
22
13
  readonly type: TypeToken<unknown> = CronTriggerNode;
@@ -21,26 +21,12 @@ interface McpToolEntry {
21
21
  readonly toolDef: ToolSet[string];
22
22
  }
23
23
 
24
- /**
25
- * Default tool-loading strategy: BM25-indexed MCP tool deferral via a `find_tools` meta-tool.
26
- *
27
- * - Node-backed tools and pinned MCP tools are always included in every turn.
28
- * - `find_tools(query, limit?)` is added to the tool set when MCP tools are indexed.
29
- * - Tools surfaced by `find_tools` are included in subsequent turns.
30
- *
31
- * Not DI-managed; instantiated per agent execution by DeferredMetaToolStrategyFactory.
32
- */
33
24
  export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
34
25
  private nodeBackedTools: ToolSet = {};
35
26
  private pinnedTools: ToolSet = {};
36
27
  private mcpEntries: McpToolEntry[] = [];
37
28
  private toolsByServerId = new Map<string, Map<string, ToolSet[string]>>();
38
29
  private foundToolIds = new Set<string>();
39
- /**
40
- * `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
41
- * (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
42
- * `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
43
- */
44
30
  private jsonSchema!: typeof import("ai").jsonSchema;
45
31
 
46
32
  constructor(
@@ -124,8 +110,6 @@ export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
124
110
 
125
111
  ownsToolName(toolName: string): boolean {
126
112
  if (toolName === FIND_TOOLS_NAME) return true;
127
- // Any tool that came from an MCP server is strategy-owned so the coordinator
128
- // does not attempt to dispatch it as a node-backed tool.
129
113
  for (const serverMap of this.toolsByServerId.values()) {
130
114
  if (serverMap.has(toolName)) return true;
131
115
  }
@@ -149,8 +133,6 @@ export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
149
133
  return results;
150
134
  }
151
135
 
152
- // Route to the MCP tool's execute callback (injected by AgentMcpIntegrationImpl with
153
- // telemetry + 403 detection wrapping).
154
136
  for (const serverMap of this.toolsByServerId.values()) {
155
137
  const toolDef = serverMap.get(toolName);
156
138
  if (toolDef) {
@@ -3,11 +3,6 @@ import { BM25Index } from "./BM25Index";
3
3
  import { DeferredMetaToolStrategy } from "./DeferredMetaToolStrategy";
4
4
  import type { ToolLoadingStrategy, ToolLoadingStrategyInitInput } from "./ToolLoadingStrategy";
5
5
 
6
- /**
7
- * Factory for creating and initializing a DeferredMetaToolStrategy per agent execution.
8
- * Injected into AIAgentNode; each agent call creates its own initialized strategy instance.
9
- * BM25Index is constructed here (this file is a composition root via the Factory suffix).
10
- */
11
6
  @injectable()
12
7
  export class DeferredMetaToolStrategyFactory {
13
8
  async create(input: ToolLoadingStrategyInitInput): Promise<ToolLoadingStrategy> {
@@ -39,9 +39,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
39
39
  ctx: ctx as unknown as HttpRequestSpec["ctx"],
40
40
  };
41
41
 
42
- // Build the request (headers, body encoding, URL query merge) once,
43
- // then make a SINGLE fetch call and decide what to do with the response.
44
- // This avoids a double-fetch regression for auto-mode binary responses.
45
42
  const executor = new HttpRequestExecutor(
46
43
  globalThis.fetch,
47
44
  new HttpBodyBuilder(),
@@ -55,7 +52,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
55
52
  const headers = this.readHeaders(response.headers);
56
53
  const mimeType = this.resolveMimeType(headers);
57
54
 
58
- // New explicit responseFormat="binary" path — takes precedence over downloadMode.
59
55
  if (ctx.config.responseFormat === "binary") {
60
56
  return await this.handleBinaryResponse(response, resolvedUrl, headers, mimeType, ctx);
61
57
  }
@@ -90,7 +86,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
90
86
  return outputItem;
91
87
  }
92
88
 
93
- // Non-binary path: parse JSON or read text.
94
89
  const isJson = this.isJsonMimeType(mimeType);
95
90
  let json: unknown | undefined;
96
91
  let text: string | undefined;
@@ -130,7 +125,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
130
125
  const slotName = ctx.config.responseBinarySlot;
131
126
  const sizeCap = ctx.config.responseSizeCapBytes;
132
127
 
133
- // Check Content-Length against size cap before allocating.
134
128
  const contentLengthHeader = headers["content-length"];
135
129
  if (contentLengthHeader) {
136
130
  const declaredSize = parseInt(contentLengthHeader, 10);
@@ -144,11 +138,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
144
138
 
145
139
  const filename = this.resolveFilename(resolvedUrl, headers);
146
140
 
147
- // Stream response.body straight into binary storage — never load the
148
- // whole payload into memory. ctx.binary.attach accepts ReadableStream
149
- // natively. Falls back to arrayBuffer only when response.body is null
150
- // (rare; 204/304-style responses where the cap-check above already
151
- // covers the meaningful size case).
152
141
  const attachment = await ctx.binary.attach({
153
142
  name: slotName,
154
143
  body: response.body
@@ -168,7 +157,6 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
168
157
  headers,
169
158
  binarySlot: slotName,
170
159
  contentType: mimeType,
171
- // Reported by the binary storage adapter after streaming completes.
172
160
  size: attachment.size,
173
161
  ...(filename !== undefined ? { filename } : {}),
174
162
  };
@@ -189,13 +177,11 @@ export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
189
177
  try {
190
178
  return await ctx.getCredential<CredentialSession>(slotKey);
191
179
  } catch {
192
- // Credential slot configured but not bound — treat as no credential.
193
180
  return undefined;
194
181
  }
195
182
  }
196
183
 
197
184
  private resolveUrl(item: Item, ctx: NodeExecutionContext<HttpRequest<any, any>>): string {
198
- // Literal URL in args takes precedence over the legacy urlField approach.
199
185
  const literalUrl = ctx.config.args.url;
200
186
  if (literalUrl && literalUrl.trim().length > 0) {
201
187
  return literalUrl.trim();
@@ -3,28 +3,12 @@ import { defineHumanApprovalNode } from "@codemation/core";
3
3
  import type { Item, JsonValue } from "@codemation/core";
4
4
  import { InboxChannelResolverToken } from "@codemation/core";
5
5
 
6
- /**
7
- * A subject field (title / body) for an inbox approval. Either a static string
8
- * or a contextual callback that builds the string from the item using ordinary
9
- * JavaScript template literals — e.g. `({ item }) => `Approve ${item.json.vendor}``.
10
- * Code-first: no template DSL, just functions.
11
- */
12
6
  type InboxSubjectField = string | ((args: { item: Item }) => string);
13
7
 
14
8
  function resolveSubjectField(field: InboxSubjectField, item: Item): string {
15
9
  return typeof field === "function" ? field({ item }) : field;
16
10
  }
17
11
 
18
- /**
19
- * Auto-detecting inbox approval node.
20
- *
21
- * Uses `ctx.resolve(InboxChannelResolverToken)` to pick the right inbox channel
22
- * at runtime:
23
- * - In managed mode (PairingConfig present): routes to the control-plane inbox.
24
- * - Otherwise: routes to the local inbox.
25
- *
26
- * Authors use this node directly; no extra wiring needed per deployment mode.
27
- */
28
12
  export const inboxApproval = defineHumanApprovalNode({
29
13
  key: "inbox.approval",
30
14
  title: "Inbox Approval",
@@ -3,14 +3,6 @@ import { emitPorts, node } from "@codemation/core";
3
3
 
4
4
  import { IsTestRun } from "./isTestRun";
5
5
 
6
- /**
7
- * Routes each item to the `true` port if `ctx.testContext` is set (the run was started by the
8
- * TestSuiteOrchestrator), else to `false`. Lets workflow authors guard real side-effects:
9
- *
10
- * GmailTrigger / TestTrigger → ClassifyAgent → IsTestRun
11
- * ├── true → AssertionNode
12
- * └── false → SendReply
13
- */
14
6
  @node({ packageName: "@codemation/core-nodes" })
15
7
  export class IsTestRunNode implements RunnableNode<IsTestRun<unknown>> {
16
8
  kind = "node" as const;
@@ -15,7 +15,6 @@ export class ManualTrigger<TOutputJson = unknown> implements TriggerNodeConfig<T
15
15
  readonly defaultItems?: Items<TOutputJson>;
16
16
  readonly id?: string;
17
17
  readonly description?: string;
18
- /** Manual runs often emit an empty batch; still schedule downstream by default. */
19
18
  readonly continueWhenEmptyOutput = true as const;
20
19
 
21
20
  constructor(name?: string, idOrOptions?: string | NodeBaseOptions);
@@ -29,8 +28,6 @@ export class ManualTrigger<TOutputJson = unknown> implements TriggerNodeConfig<T
29
28
  defaultItemsOrId?: ManualTriggerDefaultValue<TOutputJson> | string,
30
29
  idOrOptions?: string | NodeBaseOptions,
31
30
  ) {
32
- // Position 2 keeps its existing string-vs-object meaning (string→id, object/array→default items).
33
- // Options (id/description) live in the trailing slot, or position 2 when it's a bare string id.
34
31
  this.defaultItems = ManualTrigger.resolveDefaultItems(defaultItemsOrId);
35
32
  const trailing = idOrOptions ?? (typeof defaultItemsOrId === "string" ? defaultItemsOrId : undefined);
36
33
  const options = typeof trailing === "string" ? { id: trailing } : (trailing ?? {});
@@ -11,10 +11,6 @@ import { node } from "@codemation/core";
11
11
 
12
12
  import { ManualTrigger } from "./ManualTriggerFactory";
13
13
 
14
- /**
15
- * Setup is intentionally a no-op: the engine host can run workflows manually
16
- * by calling `engine.runWorkflow(workflow, triggerNodeId, items)`.
17
- */
18
14
  @node({ packageName: "@codemation/core-nodes" })
19
15
  export class ManualTriggerNode implements TestableTriggerNode<ManualTrigger<any>> {
20
16
  kind = "trigger" as const;
@@ -48,7 +48,6 @@ export class MergeNode implements MultiInputNode<Merge<any, any>> {
48
48
  return { main: out };
49
49
  }
50
50
 
51
- // passThrough (default): for each origin index, take first available input (deterministic input precedence).
52
51
  const chosenByOrigin = new Map<number, Item>();
53
52
  const fallback: Item[] = [];
54
53
 
@@ -63,20 +63,6 @@ export class NodeBackedToolRuntime {
63
63
  });
64
64
  }
65
65
 
66
- /**
67
- * Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
68
- * from the tool connection node, telemetry parents under the tool-call span, and connection
69
- * invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
70
- * ctx with only `config` swapped — no nesting concern.
71
- *
72
- * The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
73
- * `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
74
- * tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
75
- * through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
76
- * config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
77
- * deepen ids on each invocation, which also breaks credential resolution because user-provided
78
- * bindings sit on the single-level connection node id.
79
- */
80
66
  private resolveNodeCtx(
81
67
  config: NodeBackedToolConfig<any, ZodSchemaAny, ZodSchemaAny>,
82
68
  args: ToolExecuteArgs,
@@ -37,9 +37,6 @@ export class SubWorkflowNode implements RunnableNode<SubWorkflow<any, any>> {
37
37
  engineMaxSubworkflowDepth: args.ctx.engineMaxSubworkflowDepth,
38
38
  },
39
39
  });
40
- // Annotate the parent node's snapshot with the child run id so the UI can deep-link to
41
- // the specific child execution. The engine's subsequent markCompleted preserves this via
42
- // NodeExecutionSnapshotFactory.completed which carries forward previous.childRunId.
43
40
  await args.ctx.nodeState?.setChildRunId?.({ nodeId: args.ctx.nodeId, childRunId: result.runId });
44
41
  if (result.status !== "completed") {
45
42
  throw new Error(`Subworkflow ${args.ctx.config.workflowId} did not complete (status=${result.status})`);
@@ -4,9 +4,6 @@ import { emitPorts, node } from "@codemation/core";
4
4
  import type { Switch } from "./switch";
5
5
  import { tagItemForRouterFanIn } from "./mergeExecutionUtils.types";
6
6
 
7
- /**
8
- * Routes each item to exactly one output port. Port names must match workflow edges (see {@link Switch} config).
9
- */
10
7
  @node({ packageName: "@codemation/core-nodes" })
11
8
  export class SwitchNode implements RunnableNode<Switch<any>> {
12
9
  kind = "node" as const;
@@ -9,15 +9,6 @@ import type {
9
9
 
10
10
  import { node } from "@codemation/core";
11
11
 
12
- /**
13
- * Author-defined test-fixture trigger. Live activation skips this trigger (filtered by
14
- * `triggerKind === "test"` in `TriggerRuntimeService`); the `TestSuiteOrchestrator` drives its
15
- * `generateItems` callback during a TestSuiteRun and dispatches one workflow run per yielded item.
16
- *
17
- * `setup` is intentionally a no-op for symmetry with other trigger nodes — the real work happens
18
- * in the orchestrator. `execute` is a passthrough so items provided to `engine.runWorkflow(...)`
19
- * (one per case) flow downstream unchanged on `main`.
20
- */
21
12
  @node({ packageName: "@codemation/core-nodes" })
22
13
  export class TestTriggerNode implements TriggerNode<TestTriggerNodeConfig<any>> {
23
14
  kind = "trigger" as const;
@@ -24,30 +24,15 @@ export type ResolvedTool = Readonly<{
24
24
  }>;
25
25
  }>;
26
26
 
27
- /**
28
- * Per-item binding of a tool: the user config plus the resolved runtime and a snapshot of the
29
- * original Zod `inputSchema`.
30
- *
31
- * `execute` accepts optional `hooks` so the agent coordinator can pass the live `agent.tool.call`
32
- * span and the planned tool-call's `invocationId`. Node-backed sub-agent tools use these hooks
33
- * via {@link ChildExecutionScopeFactory} to re-root their runtime ctx under the tool-call boundary
34
- * (fresh activationId, telemetry parented at the tool-call span, `parentInvocationId` set).
35
- *
36
- * `humanApproval` is present only when the tool was created via `defineHumanApprovalNode`
37
- * (via its marker) — detected during `resolveTools` in `AIAgentNode`.
38
- */
39
27
  export type ItemScopedToolBinding = Readonly<{
40
28
  config: ToolConfig;
41
29
  inputSchema: ZodSchemaAny;
42
30
  execute(input: unknown, hooks?: ItemScopedToolCallHooks): Promise<unknown>;
43
- /** Present when this binding is backed by a HITL-approval node (story 10). */
44
31
  humanApproval?: Readonly<{ onRejected: "halt" | "return" }>;
45
32
  }>;
46
33
 
47
34
  export type ItemScopedToolCallHooks = Readonly<{
48
- /** Live agent.tool.call span (used to parent sub-agent telemetry). */
49
35
  parentSpan?: TelemetrySpanScope;
50
- /** invocationId of the parent tool call (used to thread `parentInvocationId` through ctx). */
51
36
  parentInvocationId?: ConnectionInvocationId;
52
37
  }>;
53
38
 
@@ -56,7 +41,6 @@ export type PlannedToolCall = Readonly<{
56
41
  toolCall: AgentToolCall;
57
42
  invocationIndex: number;
58
43
  nodeId: string;
59
- /** Stable id reused across queued / running / completed connection invocation rows for this tool call. */
60
44
  invocationId: string;
61
45
  }>;
62
46
 
@@ -14,22 +14,12 @@ export interface AssertionOptions<TInputJson> {
14
14
  readonly id?: string;
15
15
  readonly icon?: string;
16
16
  readonly description?: string;
17
- /**
18
- * Author callback. Returns one or more {@link AssertionResult}s per input item. Each becomes
19
- * one emitted output item — useful for per-row reporting in the Tests tab. Return `[]` to
20
- * emit nothing for this case (rare; usually you want at least a "no-op" pass).
21
- */
22
17
  assertions(
23
18
  item: Item<TInputJson>,
24
19
  ctx: NodeExecutionContext<Assertion<TInputJson>>,
25
20
  ): Promise<ReadonlyArray<AssertionResult>> | ReadonlyArray<AssertionResult>;
26
21
  }
27
22
 
28
- /**
29
- * Generic assertion node — the "callback" form. For declarative shorthands (StringEquals,
30
- * JudgeByAgent) compose this with helpers added in later phases. Sets `emitsAssertions: true`
31
- * so host-side persisters know to record its outputs as `TestAssertion` rows.
32
- */
33
23
  export class Assertion<TInputJson = unknown> implements RunnableNodeConfig<TInputJson, AssertionResult> {
34
24
  readonly kind = "node" as const;
35
25
  readonly type: TypeToken<unknown> = AssertionNode;
@@ -5,30 +5,21 @@ import { managedHmacFetchFactory } from "../chatModels/ManagedHmacSignerFactory.
5
5
  const ANALYZER_TYPES = ["document", "invoice", "image", "auto"] as const;
6
6
  export type DocScannerAnalyzerType = (typeof ANALYZER_TYPES)[number];
7
7
 
8
- /** Per-field value/confidence shape as returned by apps/doc-scanner. */
9
8
  export type DocScannerField = Readonly<{
10
9
  value: unknown;
11
10
  confidence: number | null;
12
11
  }>;
13
12
 
14
- /** Output shape of CodemationDocumentScanner — identical to the service wire response. */
15
13
  export type DocScannerOutput = Readonly<{
16
14
  markdown: string;
17
15
  fields: Readonly<Record<string, DocScannerField>>;
18
16
  }>;
19
17
 
20
18
  export type CodemationDocumentScannerConfig = Readonly<{
21
- /** Key on `item.binary` that holds the document. Default: "data". */
22
19
  binaryField?: string;
23
- /** Analyzer type. Default: "auto" (routes on mime type on the service side). */
24
20
  analyzerType?: DocScannerAnalyzerType;
25
- /** MIME type override. Falls back to attachment.mimeType. */
26
21
  contentType?: string;
27
- /** Include per-field confidence scores (0–1). Default: false.
28
- * Enabling this roughly doubles contextualization tokens for document analyzers.
29
- * Image and auto-to-image requests silently ignore this flag (confidence stays null). */
30
22
  includeConfidence?: boolean;
31
- /** Max bytes checked before any read. Default: 50 MiB (same cap as OCR nodes, LD10). */
32
23
  maxBytes?: number;
33
24
  }>;
34
25
 
@@ -54,9 +45,6 @@ export const codemationDocumentScannerNode = defineNode({
54
45
  includeConfidence: z.boolean().optional(),
55
46
  maxBytes: z.number().int().positive().optional(),
56
47
  }),
57
- // keepBinaries is omitted (false) — the output replaces the item payload.
58
- // To carry the source binary forward, set keepBinaries: true at the call site
59
- // or use a downstream step that reads item.binary.
60
48
  inspectorSummary({ config }) {
61
49
  const cfg = config as unknown as CodemationDocumentScannerConfig;
62
50
  const rows: Array<{ label: string; value: string }> = [
@@ -79,9 +67,6 @@ export const codemationDocumentScannerNode = defineNode({
79
67
  );
80
68
  }
81
69
 
82
- // Workspace pairing identity — injected by the CP provisioner (mirrors
83
- // CodemationChatModelFactory). Passed to the signer; the HMAC binds THIS
84
- // workspace (LD4) and does not sign the file body (LD11).
85
70
  // eslint-disable-next-line no-restricted-properties -- WORKSPACE_ID is injected by the provisioner; read at execute time.
86
71
  const workspaceId = process.env["WORKSPACE_ID"];
87
72
  // eslint-disable-next-line no-restricted-properties -- WORKSPACE_PAIRING_SECRET is injected by the provisioner; read at execute time.
@@ -104,9 +89,6 @@ export const codemationDocumentScannerNode = defineNode({
104
89
  const confidenceSuffix = includeConfidence ? "&confidence=true" : "";
105
90
  const url = `${gatewayUrl}/analyze?type=${encodeURIComponent(analyzerType)}${confidenceSuffix}`;
106
91
 
107
- // signBody: false (LD11): the HMAC binds the workspace, not the file body.
108
- // The signer forwards the bytes untouched and signs an empty-body hash, so
109
- // we never re-read or normalise the binary payload.
110
92
  const hmacFetch = managedHmacFetchFactory(workspaceId, pairingSecret, { signBody: false });
111
93
 
112
94
  const response = await hmacFetch(url, {
@@ -30,7 +30,6 @@ export const collectionListNode = defineNode({
30
30
  offset: config.offset,
31
31
  where: config.where,
32
32
  });
33
- // Emit one item per row per AGENTS.md engine/node contract.
34
33
  return [...rows];
35
34
  },
36
35
  });
@@ -18,7 +18,6 @@ import { HttpRequestNode } from "./HttpRequestNodeFactory";
18
18
 
19
19
  export type HttpRequestDownloadMode = "auto" | "always" | "never";
20
20
 
21
- /** JSON emitted by {@link HttpRequest} — response metadata only (input item fields are not passed through). */
22
21
  export type HttpRequestOutputJson = Readonly<{
23
22
  url: string;
24
23
  method: string;
@@ -30,20 +29,12 @@ export type HttpRequestOutputJson = Readonly<{
30
29
  json?: unknown;
31
30
  text?: string;
32
31
  bodyBinaryName?: string;
33
- /** Set when `responseFormat === "binary"`. Name of the binary slot the response was stored in. */
34
32
  binarySlot?: string;
35
- /** Set when `responseFormat === "binary"`. MIME type of the stored response. */
36
33
  contentType?: string;
37
- /** Set when `responseFormat === "binary"`. Size in bytes of the stored response. */
38
34
  size?: number;
39
- /** Set when `responseFormat === "binary"`. Filename inferred from URL or Content-Disposition. */
40
35
  filename?: string;
41
36
  }>;
42
37
 
43
- /**
44
- * The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
45
- * These match the four generic credential types shipped with `@codemation/core-nodes`.
46
- */
47
38
  export const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES: ReadonlyArray<string> = [
48
39
  bearerTokenCredentialType.definition.typeId,
49
40
  apiKeyCredentialType.definition.typeId,
@@ -51,7 +42,6 @@ export const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES: ReadonlyArray<string> = [
51
42
  oauth2ClientCredentialsType.definition.typeId,
52
43
  ] as const;
53
44
 
54
- /** Default maximum response size for binary mode: 100 MiB. */
55
45
  const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
56
46
 
57
47
  export class HttpRequest<
@@ -67,77 +57,20 @@ export class HttpRequest<
67
57
  constructor(
68
58
  public readonly name: string,
69
59
  public readonly args: Readonly<{
70
- /** HTTP method (default: GET). */
71
60
  method?: string;
72
- /**
73
- * Legacy: field name on item.json to read the URL from.
74
- * Use `url` for a literal/templated URL instead.
75
- */
76
61
  urlField?: string;
77
- /** Literal or templated URL. When present, takes precedence over `urlField`. */
78
62
  url?: string;
79
- /** Extra headers to add to every request. */
80
63
  headers?: Readonly<Record<string, string>>;
81
- /** Query parameters to append to the URL. */
82
64
  query?: Readonly<Record<string, string>>;
83
- /** Request body specification. For `kind:"json"`, pass the object directly in `body.data` — it is JSON-encoded exactly once, so never a pre-stringified string. */
84
65
  body?: HttpBodySpec;
85
- /**
86
- * Credential slot.
87
- *
88
- * **String shorthand** (existing): `credentialSlot: "auth"` — the slot accepts all four
89
- * default HTTP credential types (bearer, API-key, basic, OAuth2).
90
- *
91
- * **Object form** (new): narrows the accepted types to the caller-supplied list, useful
92
- * when only a subset of credential types makes sense for a specific endpoint.
93
- * ```ts
94
- * credentialSlot: { name: "auth", acceptedTypes: [bearerTokenCredentialType] }
95
- * ```
96
- * The slot must be declared in `getCredentialRequirements()`, which is wired automatically.
97
- */
98
66
  credentialSlot?: string | Readonly<{ name: string; acceptedTypes?: ReadonlyArray<AnyCredentialType> }>;
99
67
  binaryName?: string;
100
68
  downloadMode?: HttpRequestDownloadMode;
101
- /**
102
- * Controls how the response body is handled.
103
- * - `"json"` / `"text"`: existing behaviour (parse + emit on `item.json`).
104
- * - `"binary"`: read the response as raw bytes and store via `ctx.binary.attach`.
105
- * The output JSON contains `{ status, headers, binarySlot, contentType, size, filename }`
106
- * but NOT the raw bytes. Use `responseBinarySlot` to name the slot (default `"response"`).
107
- *
108
- * When omitted, the existing `downloadMode` logic applies (backward-compatible).
109
- */
110
69
  responseFormat?: "json" | "text" | "binary";
111
- /**
112
- * Name of the binary slot to write the response body into when `responseFormat === "binary"`.
113
- * Defaults to `"response"`.
114
- */
115
70
  responseBinarySlot?: string;
116
- /**
117
- * Maximum response size in bytes for binary mode. Checked against the `Content-Length`
118
- * response header before allocating memory. Defaults to 100 MiB (104857600).
119
- * Requests whose `Content-Length` exceeds this cap are rejected before the body is read.
120
- */
121
71
  responseSizeCapBytes?: number;
122
- /**
123
- * Operator-configurable outbound host allowlist.
124
- *
125
- * When set, every HTTP request target must match an entry in this list before the
126
- * request is made — requests to any other host are rejected with {@link SSRFBlockedError}.
127
- * Supports exact hostnames (`api.example.com`) and wildcard subdomain patterns
128
- * (`*.example.com` matches `sub.example.com` but not `example.com` itself).
129
- *
130
- * When unset (default), the existing SSRF private-network guard applies:
131
- * public hosts are allowed and private/loopback ranges are blocked.
132
- *
133
- * **Production warning**: when `NODE_ENV === "production"` and this is unset, a one-time
134
- * warning is logged at workflow startup.
135
- *
136
- * Setting this to an empty array `[]` is equivalent to "block everything".
137
- */
138
72
  allowedOutboundHosts?: ReadonlyArray<string>;
139
73
  id?: string;
140
- /** Plain-language explanation surfaced in the node sidebar. */
141
74
  description?: string;
142
75
  }> = {},
143
76
  public readonly retryPolicy: RetryPolicySpec = RetryPolicy.defaultForHttp,
@@ -197,7 +130,6 @@ export class HttpRequest<
197
130
  },
198
131
  ];
199
132
  }
200
- // Object form: use caller-supplied acceptedTypes (mapped to typeIds), falling back to all defaults.
201
133
  const acceptedTypes =
202
134
  slot.acceptedTypes && slot.acceptedTypes.length > 0
203
135
  ? slot.acceptedTypes.map((ct) => ct.definition.typeId)
@@ -3,10 +3,6 @@ import type { RunnableNodeConfig, TypeToken } from "@codemation/core";
3
3
  import { IsTestRunNode } from "./IsTestRunNode";
4
4
  import type { NodeBaseOptions } from "./nodeOptions.types";
5
5
 
6
- /**
7
- * Branches per-item on whether the current run is a test run. Output ports: `true`, `false`.
8
- * The wire payload is unchanged — this is a router, not a transform.
9
- */
10
6
  export class IsTestRun<TInputJson = unknown> implements RunnableNodeConfig<TInputJson, TInputJson> {
11
7
  readonly kind = "node" as const;
12
8
  readonly type: TypeToken<unknown> = IsTestRunNode;
@@ -19,7 +19,6 @@ export class MapData<TInputJson = unknown, TOutputJson = unknown> implements Run
19
19
  readonly kind = "node" as const;
20
20
  readonly type: TypeToken<unknown> = MapDataNode;
21
21
  readonly execution = { hint: "local" } as const;
22
- /** Zero mapped items should still allow downstream nodes to run. */
23
22
  readonly continueWhenEmptyOutput = true as const;
24
23
  readonly icon = "lucide:square-pen" as const;
25
24
  readonly keepBinaries: boolean;
@@ -18,10 +18,6 @@ export class Merge<TInputJson = unknown, TOutputJson = TInputJson> implements Ru
18
18
  public readonly name: string,
19
19
  public readonly cfg: Readonly<{
20
20
  mode: MergeMode;
21
- /**
22
- * Deterministic input precedence order (only used for passThrough/append).
23
- * Any inputs not listed are appended in lexicographic order.
24
- */
25
21
  prefer?: ReadonlyArray<InputPortKey>;
26
22
  }> = { mode: "passThrough" },
27
23
  idOrOptions?: string | NodeBaseOptions,
@@ -5,9 +5,6 @@ export function getOriginIndex(item: Item): number | undefined {
5
5
  return getOriginIndexFromItem(item);
6
6
  }
7
7
 
8
- /**
9
- * Tags items routed to fan-in merge-by-origin (same contract as {@link IfNode} / {@link SwitchNode}).
10
- */
11
8
  export function tagItemForRouterFanIn<TJson>(
12
9
  args: Readonly<{
13
10
  item: Item<TJson>;
@@ -1,11 +1,3 @@
1
- /**
2
- * Options shared by every authorable built-in node: a stable `id` and a plain-language
3
- * `description` (the non-technical "what does this node do" line surfaced in the node sidebar).
4
- *
5
- * `description` is a first-class config option — passed inline in the node's options, exactly like
6
- * `id` — and is threaded onto the config instance so it flows into the persisted workflow snapshot
7
- * the host / canvas mappers read. Node-specific option types extend this.
8
- */
9
1
  export interface NodeBaseOptions {
10
2
  readonly id?: string;
11
3
  readonly description?: string;
@@ -0,0 +1,37 @@
1
+ import { definePollingTrigger } from "@codemation/core";
2
+
3
+ type ScheduleItem = {
4
+ firedAt: string;
5
+ tick: number;
6
+ };
7
+
8
+ type ScheduleState = {
9
+ tick: number;
10
+ };
11
+
12
+ export const schedulePollingTrigger = definePollingTrigger({
13
+ key: "schedule.interval",
14
+ packageName: "@codemation/core-nodes",
15
+ title: "Run on schedule",
16
+ description: "Emit one tick item on every poll cycle.",
17
+ icon: "lucide:clock",
18
+ pollIntervalMs: 60_000,
19
+ initialState(): ScheduleState {
20
+ return { tick: 0 };
21
+ },
22
+ poll({ state }) {
23
+ const currentState = (state ?? { tick: 0 }) as ScheduleState;
24
+ const tick = currentState.tick + 1;
25
+ const item: { json: ScheduleItem } = { json: { firedAt: new Date().toISOString(), tick } };
26
+ return {
27
+ items: [item],
28
+ nextState: { tick },
29
+ };
30
+ },
31
+ execute(items) {
32
+ return { main: items };
33
+ },
34
+ testItems() {
35
+ return [{ json: { firedAt: new Date().toISOString(), tick: 0 } }];
36
+ },
37
+ });