@oomol-lab/open-flow 0.1.0-beta.74 → 0.1.0-beta.76

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 (139) hide show
  1. package/README.md +3 -1
  2. package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-Cm1hbVzW.js} +4 -3
  3. package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-NzKtla_c.js} +62 -62
  4. package/dist/browser/{createResourceDialog-DI9pL-Yr.js → createResourceDialog-BgdedQqu.js} +3 -3
  5. package/dist/browser/{dialog-BYJGvItc.js → dialog-aSElFFw2.js} +322 -26
  6. package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-Dcz4HTo_.js} +2 -2
  7. package/dist/browser/{flowChanges-RL-H4CjD.js → flowChanges-NZyS7HSp.js} +490 -438
  8. package/dist/browser/{flowWorkspace-BkDsETj0.js → flowWorkspace-Bl0SDehy.js} +499 -61
  9. package/dist/browser/preview.css +696 -825
  10. package/dist/browser/preview.js +1399 -717
  11. package/dist/browser/theme.css +114 -262
  12. package/dist/browser/{tooltip-wZJRB9oP.js → tooltip-3XMK9jkr.js} +1 -1
  13. package/dist/browser/{typeScriptSession-CSRwWmZm.js → typeScriptSession-C9sBGOKZ.js} +1 -1
  14. package/dist/browser/ui.css +354 -460
  15. package/dist/browser/ui.js +1 -1
  16. package/dist/browser/workbench.css +696 -825
  17. package/dist/browser/workbench.js +13 -7
  18. package/dist/browser/{workbenchSelect-BcWPWGzw.js → workbenchSelect-DBGP7Y4Z.js} +2 -2
  19. package/dist/command/index.js +41 -43
  20. package/dist/common/authoring.d.ts +5 -5
  21. package/dist/common/authoringExamples.d.ts +3 -2
  22. package/dist/common/authoringSchema.d.ts +70 -4
  23. package/dist/common/connector-proxy.d.ts +1 -1
  24. package/dist/common/control-api-conformance.js +250 -138
  25. package/dist/common/control-api.d.ts +3 -2
  26. package/dist/common/control-api.js +246 -134
  27. package/dist/common/control-requests.d.ts +3 -3
  28. package/dist/common/control-requests.js +353 -79
  29. package/dist/common/cron-trigger.d.ts +2 -1
  30. package/dist/common/decision.d.ts +4 -4
  31. package/dist/common/decision.js +5 -2
  32. package/dist/common/engine-contract.d.ts +6 -1
  33. package/dist/common/event-source-definition.d.ts +1 -1
  34. package/dist/common/event-source-view.d.ts +22 -0
  35. package/dist/common/event-source.d.ts +1 -1
  36. package/dist/common/event-source.js +24 -65
  37. package/dist/common/event-sources.d.ts +0 -21
  38. package/dist/common/flow-authoring.js +90 -19
  39. package/dist/common/flow-change-schema.d.ts +9 -1
  40. package/dist/common/flow-change.d.ts +7 -201
  41. package/dist/common/flow-change.js +96 -23
  42. package/dist/common/flow-encoding.js +89 -21
  43. package/dist/common/flow-graph.d.ts +2 -1
  44. package/dist/common/flow-input-value.d.ts +1 -1
  45. package/dist/common/flow-model.d.ts +121 -0
  46. package/dist/common/flow-notifications.d.ts +1 -1
  47. package/dist/common/flow-retry.d.ts +3 -5
  48. package/dist/common/flow-semantics.js +816 -586
  49. package/dist/common/flow-workflowReferences.d.ts +8 -0
  50. package/dist/common/flowInspection.d.ts +1 -1
  51. package/dist/common/http-hosts.d.ts +6 -0
  52. package/dist/common/http-request.d.ts +164 -0
  53. package/dist/common/http-request.js +5272 -0
  54. package/dist/common/integration-trigger.d.ts +2 -1
  55. package/dist/common/mcp.d.ts +3 -3
  56. package/dist/common/mcp.js +248 -136
  57. package/dist/common/openapi.d.ts +4 -4
  58. package/dist/common/openapi.js +4 -1
  59. package/dist/common/poll-trigger.d.ts +2 -1
  60. package/dist/common/run-events.js +11 -9
  61. package/dist/common/runtime-contract.d.ts +23 -1
  62. package/dist/common/runtime-contract.js +607 -72
  63. package/dist/common/runtime-nodejs-compat.d.ts +44 -0
  64. package/dist/common/runtime-nodejs-compat.js +5870 -0
  65. package/dist/common/scheduler-checkpoint.d.ts +1 -1
  66. package/dist/common/scheduler-node-execution.d.ts +1 -1
  67. package/dist/common/scheduler.d.ts +2 -2
  68. package/dist/common/scheduler.js +95 -25
  69. package/dist/common/trigger-catalog.d.ts +1 -7
  70. package/dist/common/trigger-config-options.d.ts +1 -1
  71. package/dist/common/trigger-config.d.ts +1 -1
  72. package/dist/common/trigger-contract.d.ts +2 -1
  73. package/dist/common/trigger-localization.d.ts +8 -2
  74. package/dist/common/trigger-model.d.ts +101 -0
  75. package/dist/common/webhook-response.d.ts +1 -3
  76. package/dist/common/webhook-trigger.d.ts +3 -1
  77. package/dist/common/webhook-trigger.js +12 -11
  78. package/dist/common/workflow.d.ts +3 -12
  79. package/dist/common/workflow.js +25 -23
  80. package/dist/index.d.ts +41 -14
  81. package/package.json +9 -1
  82. package/skills/open-flow/references/flow-authoring.md +1 -0
  83. package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
  84. package/src/base/common/json.ts +1 -1
  85. package/src/connector/common/proxy.ts +1 -1
  86. package/src/control/common/api.ts +7 -5
  87. package/src/control/common/authoring.ts +14 -3
  88. package/src/control/common/authoringExamples.ts +8 -0
  89. package/src/control/common/authoringSchema.ts +38 -25
  90. package/src/control/common/authoringTools.ts +1 -1
  91. package/src/control/common/conformance.ts +1 -1
  92. package/src/control/common/connectorDecoders.ts +1 -1
  93. package/src/control/common/eventSources.ts +2 -29
  94. package/src/control/common/triggerCatalog.ts +1 -8
  95. package/src/decision/common/decision.ts +15 -7
  96. package/src/event-source/common/definition.ts +1 -1
  97. package/src/event-source/common/index.ts +1 -1
  98. package/src/event-source/common/view.ts +20 -0
  99. package/src/execution/common/checkpoint.ts +1 -1
  100. package/src/execution/common/events.ts +3 -0
  101. package/src/execution/common/node-execution.ts +5 -3
  102. package/src/execution/common/scheduler.ts +2 -1
  103. package/src/flow/common/change.ts +59 -192
  104. package/src/flow/common/changeSchema.ts +39 -5
  105. package/src/flow/common/connectionUsage.ts +1 -1
  106. package/src/flow/common/graph.ts +10 -4
  107. package/src/flow/common/inputValue.ts +1 -1
  108. package/src/flow/common/model.ts +130 -0
  109. package/src/flow/common/modules.ts +1 -1
  110. package/src/flow/common/portChanges.ts +1 -1
  111. package/src/flow/common/retry.ts +3 -5
  112. package/src/flow/common/semantics.ts +71 -7
  113. package/src/flow/common/webhookResponse.ts +4 -8
  114. package/src/flow/common/workflowReferences.ts +31 -0
  115. package/src/http/common/authBindings.ts +9 -0
  116. package/src/http/common/headers.ts +5 -0
  117. package/src/http/common/hosts.ts +13 -0
  118. package/src/http/common/httpRequest.ts +266 -0
  119. package/src/openapi/common/openapi.ts +9 -5
  120. package/src/{execution → runtime}/common/engineContract.ts +24 -15
  121. package/src/runtime/common/nodejsCompat.ts +275 -0
  122. package/src/runtime/common/nodejsGuest.generated.json +47 -0
  123. package/src/runtime/common/runtime.ts +1138 -0
  124. package/src/trigger/common/config.ts +1 -1
  125. package/src/trigger/common/configOptions.ts +1 -1
  126. package/src/trigger/common/contract.ts +2 -1
  127. package/src/trigger/common/cron.ts +2 -1
  128. package/src/trigger/common/integration.ts +2 -1
  129. package/src/trigger/common/model.ts +87 -0
  130. package/src/trigger/common/poll.ts +2 -1
  131. package/src/trigger/common/webhook.ts +7 -4
  132. package/src/trigger/providers/fieldDescriptions.ts +1 -1
  133. package/src/trigger/providers/localization.ts +9 -2
  134. package/src/trigger/providers/remote.ts +1 -1
  135. package/src/types/index.ts +44 -15
  136. package/src/workflow/common/workflow.ts +3 -44
  137. package/src/execution/common/runtime.ts +0 -563
  138. package/src/openapi/common/authBindings.ts +0 -12
  139. /package/src/{control → connector}/common/providerIconSprite.ts +0 -0
package/dist/index.d.ts CHANGED
@@ -20,18 +20,50 @@ export interface ArtifactRef {
20
20
  readonly kind: 'artifact';
21
21
  readonly id: string;
22
22
  readonly name: string;
23
- readonly mediaType: string | undefined;
23
+ readonly mediaType?: string;
24
24
  readonly size: number;
25
25
  readonly digest: string;
26
26
  }
27
27
  export interface ArtifactPutOptions {
28
28
  readonly name: string;
29
- readonly mediaType?: string | undefined;
29
+ readonly mediaType?: string;
30
30
  }
31
- export type ArtifactData = Uint8Array | Blob | ReadableStream<Uint8Array>;
31
+ /** Strings are stored as UTF-8. */
32
+ export type ArtifactData = string | Uint8Array;
33
+ /** Artifacts belong to the current Run; an ArtifactRef is JSON and can be passed to downstream nodes. */
32
34
  export interface ArtifactCapability {
33
35
  put(data: ArtifactData, options: ArtifactPutOptions): Promise<ArtifactRef>;
34
- open(ref: ArtifactRef): Promise<ReadableStream<Uint8Array>>;
36
+ open(ref: ArtifactRef): Promise<Uint8Array>;
37
+ }
38
+ export interface TaskFetchInit {
39
+ /** GET, HEAD, POST, PUT, PATCH, DELETE or OPTIONS. */
40
+ readonly method?: string;
41
+ readonly headers?: Readonly<Record<string, string>> | Iterable<readonly [string, string]>;
42
+ /** At most 1 MiB; strings are sent as UTF-8 text/plain unless a content-type header is supplied. */
43
+ readonly body?: string | Uint8Array | ArrayBuffer;
44
+ readonly signal?: AbortSignal;
45
+ }
46
+ export interface TaskFetchHeaders extends Iterable<[string, string]> {
47
+ get(name: string): string | null;
48
+ has(name: string): boolean;
49
+ entries(): IterableIterator<[string, string]>;
50
+ keys(): IterableIterator<string>;
51
+ values(): IterableIterator<string>;
52
+ forEach(callback: (value: string, name: string) => void): void;
53
+ }
54
+ /** A read-only subset of the Fetch Response; the body is buffered, at most 1 MiB, and readable once. */
55
+ export interface TaskFetchResponse {
56
+ readonly status: number;
57
+ readonly statusText: string;
58
+ readonly ok: boolean;
59
+ readonly redirected: false;
60
+ readonly url: string;
61
+ readonly headers: TaskFetchHeaders;
62
+ readonly bodyUsed: boolean;
63
+ bytes(): Promise<Uint8Array>;
64
+ arrayBuffer(): Promise<ArrayBuffer>;
65
+ text(): Promise<string>;
66
+ json(): Promise<unknown>;
35
67
  }
36
68
  export interface MediaPreviewPayload {
37
69
  readonly type: 'video' | 'audio' | 'markdown' | 'iframe' | 'html';
@@ -60,12 +92,6 @@ export interface CsvPreviewPayload {
60
92
  }
61
93
  export type PreviewPayload = MediaPreviewPayload | TextPreviewPayload | ImagePreviewPayload | TablePreviewPayload | CsvPreviewPayload;
62
94
  export type PreviewType = PreviewPayload['type'];
63
- export interface TaskLogger {
64
- debug(message: string, data?: unknown): void;
65
- info(message: string, data?: unknown): void;
66
- warn(message: string, data?: unknown): void;
67
- error(message: string, data?: unknown): void;
68
- }
69
95
  export interface TaskContext<Actions extends object = Record<string, never>, Workflows extends object = {
70
96
  call: (name: never, input?: never) => Promise<never>;
71
97
  }> {
@@ -77,12 +103,13 @@ export interface TaskContext<Actions extends object = Record<string, never>, Wor
77
103
  readonly runId: string;
78
104
  readonly blockId: string;
79
105
  readonly artifact: ArtifactCapability;
80
- readonly fetch: typeof fetch;
81
- readonly logger: TaskLogger;
82
106
  /** Reads the selected direct predecessors, sorted by node ID; empty for a graph root. */
83
107
  getPrevious(): Promise<readonly PreviousNode[]>;
84
- preview(payload: PreviewPayload, id?: string): Promise<void>;
85
- reportProgress(progress: number): Promise<void>;
108
+ /**
109
+ * Calls an HTTP(S) URL whose host the node declares in its `http` Capability. Redirects are returned, not followed,
110
+ * and no platform credentials are added. Network failures reject with a TypeError.
111
+ */
112
+ fetch(input: string | URL, init?: TaskFetchInit): Promise<TaskFetchResponse>;
86
113
  }
87
114
  export type TaskResult<Outputs extends object> = keyof Outputs extends never ? Outputs | void : Outputs;
88
115
  export interface Task<Inputs extends object = Record<string, unknown>, Outputs extends object = Record<string, unknown>, Actions extends object = Record<string, never>, Workflows extends object = TaskContext['workflows']> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.74",
3
+ "version": "0.1.0-beta.76",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -25,6 +25,10 @@
25
25
  "types": "./dist/common/decision.d.ts",
26
26
  "import": "./dist/common/decision.js"
27
27
  },
28
+ "./http-request": {
29
+ "types": "./dist/common/http-request.d.ts",
30
+ "import": "./dist/common/http-request.js"
31
+ },
28
32
  "./openapi": {
29
33
  "types": "./dist/common/openapi.d.ts",
30
34
  "import": "./dist/common/openapi.js"
@@ -109,6 +113,10 @@
109
113
  "types": "./dist/common/runtime-contract.d.ts",
110
114
  "import": "./dist/common/runtime-contract.js"
111
115
  },
116
+ "./runtime-nodejs-compat": {
117
+ "types": "./dist/common/runtime-nodejs-compat.d.ts",
118
+ "import": "./dist/common/runtime-nodejs-compat.js"
119
+ },
112
120
  "./scheduler": {
113
121
  "types": "./dist/common/scheduler.d.ts",
114
122
  "import": "./dist/common/scheduler.js"
@@ -298,6 +298,7 @@ For Connector Tasks, independent Code Actions, Agent tools and notifications, an
298
298
  | Manual, HTTP or scheduled entry | `start`, `webhook`, `cron`: workflow `inputDefs`, HTTP body fields, or schedule. |
299
299
  | Workflow failure handler | `error`: published upstream Flows' automatic Run failures; not a node-level catch or a handler for manual Run failures. |
300
300
  | Call another workflow | `workflow`: select an owned published Flow ID; inputs and outputs derive from its saved contract. Use `node.refresh` to update it. |
301
+ | Single HTTP request | `http-request`: method, parameter modes, authentication, body and response configuration; URL and request values use public inputs. |
301
302
  | Fixed HTTP operation | `openapi`: an OpenAPI source, path and method; resolved authentication choices and ports appear in node details. |
302
303
  | Reusable constants | `value`: named JSON values and optional output constraints. |
303
304
  | Durable interaction | `wait`, `approval`: prompt and named inputs; resume through the Run's Wait ID. Wait additionally supports optional `config.autoContinue` with `{kind:"after",durationMs:86400000}` or `{kind:"at",at:"2026-10-25T10:00:00+08:00"}`. |
@@ -13,17 +13,17 @@ publishing and enabling remain separate actions within the user's authorization.
13
13
 
14
14
  ## Choose a representation
15
15
 
16
- | Source behavior | Open Flow building blocks | What determines a good fit |
17
- | --------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------ |
18
- | Scheduled, webhook or provider event | Cron, Webhook, Poll or Integration Trigger | Schedule/timezone, event payload, delivery and response behavior. |
19
- | Provider read or write | Connector | Exact operation, input/output shape, account and number of calls. |
20
- | Field shaping, filtering, aggregation or join | Code with named input/output fields | Explicit collection representation, ordering, keys and empty-input behavior. |
21
- | IF or Switch | Condition and named execution branches | Predicate, first/all matches, fallback route and data sent onward. |
22
- | Prompt plus model and output parser | LLM with template, inputs and `resultSchema` | Supported model, result shape and any parser repair behavior. |
23
- | Agent with tools or memory | Agent where its current contract fits | Tool permissions, state lifetime and how context is supplied. |
24
- | HTTP request | OpenAPI where an actual operation contract fits | Method, parameters, auth, response format and error behavior. |
25
- | Human response or durable pause | Wait or Approval | Who resumes it, payload, timing, notification and external response lifecycle. |
26
- | Workflow failure handling | Error Trigger where its scope fits | Which failures trigger it and what failure data it receives. |
16
+ | Source behavior | Open Flow building blocks | What determines a good fit |
17
+ | --------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ |
18
+ | Scheduled, webhook or provider event | Cron, Webhook, Poll or Integration Trigger | Schedule/timezone, event payload, delivery and response behavior. |
19
+ | Provider read or write | Connector | Exact operation, input/output shape, account and number of calls. |
20
+ | Field shaping, filtering, aggregation or join | Code with named input/output fields | Explicit collection representation, ordering, keys and empty-input behavior. |
21
+ | IF or Switch | Condition and named execution branches | Predicate, first/all matches, fallback route and data sent onward. |
22
+ | Prompt plus model and output parser | LLM with template, inputs and `resultSchema` | Supported model, result shape and any parser repair behavior. |
23
+ | Agent with tools or memory | Agent where its current contract fits | Tool permissions, state lifetime and how context is supplied. |
24
+ | HTTP request | `http-request`, or `openapi` for a documented operation | Method, parameters, auth, response format and error behavior. |
25
+ | Human response or durable pause | Wait or Approval | Who resumes it, payload, timing, notification and external response lifecycle. |
26
+ | Workflow failure handling | Error Trigger where its scope fits | Which failures trigger it and what failure data it receives. |
27
27
 
28
28
  Use `schema TYPE` and provider discovery for the actual deployment. A node's
29
29
  name alone does not establish equivalence. Provider operations can differ even
@@ -120,10 +120,14 @@ inside URLs, headers, expressions or Code; keep those out of Drafts and reports.
120
120
  Use supported credential bindings for the selected operation. A missing account
121
121
  can leave a Draft incomplete without implying that the action itself is absent.
122
122
 
123
- For HTTP Request conversion, `schema openapi` describes source URL, operation
124
- selection and supported authentication. Check the actual operation's response,
125
- non-success status, redirect, binary and retry requirements against that contract.
126
- An invented API specification does not establish support for an unknown operation.
123
+ For HTTP Request conversion, `schema http-request` describes a single request's
124
+ method, parameter modes, body, authentication and response handling. Bind `url`,
125
+ request parameters and body through public inputs. Authentication inputs require
126
+ variables or upstream outputs. Query arrays become repeated parameters; response
127
+ arrays remain one value. The first version does not follow redirects or support
128
+ pagination, files, OAuth or direct Agent tools. Check these requirements before
129
+ calling the conversion equivalent. Use `schema openapi` when an actual operation
130
+ contract is available; do not invent an API specification to make a request.
127
131
 
128
132
  A provider event may expose an ID where the source expected a full record. A
129
133
  Connector read can supply the missing data if it preserves the desired behavior.
@@ -1,4 +1,4 @@
1
- export type JsonValue = null | boolean | number | string | readonly JsonValue[] | JsonObject
1
+ import type { JsonValue } from '../../types/index.ts'
2
2
 
3
3
  export type JsonObject = { readonly [key: string]: JsonValue }
4
4
 
@@ -1,5 +1,5 @@
1
1
  import type { SourceOperation, SourceReferenceOperation } from '../../event-source/common/definition.ts'
2
- import type { JsonValue } from '../../flow/common/change.ts'
2
+ import type { JsonValue } from '../../types/index.ts'
3
3
  export type TriggerOperationRequest =
4
4
  | { readonly operation: 'sourceReference'; readonly request: SourceReferenceOperation }
5
5
  | { readonly operation: 'source'; readonly request: SourceOperation }
@@ -1,6 +1,6 @@
1
1
  import type { CanvasCommand } from '../../canvas/common/commands.ts'
2
2
 
3
- import { currentEngineContract } from '../../execution/common/engineContract.ts'
3
+ import { currentEngineContract } from '../../runtime/common/engineContract.ts'
4
4
  import { workflowContractSchema, workflowReferencesSchema } from '../../workflow/common/workflow.ts'
5
5
  export type { CanvasCommand, CanvasCommandSubscriber } from '../../canvas/common/commands.ts'
6
6
  import type { LiveDetails } from './liveDetails.ts'
@@ -9,12 +9,12 @@ import { decodeLiveDetails } from './liveDetails.ts'
9
9
  export { liveEntries } from './liveDetails.ts'
10
10
  export { flowInputDefinitions } from './authoringFields.ts'
11
11
  export type { LiveDetails, LiveEntry } from './liveDetails.ts'
12
+ import type { ProviderIconAppearance } from '../../connector/common/providerIconSprite.ts'
12
13
  import type { AuthoringRead, AuthoringSearch, AuthoringRequest, AuthoringType } from './authoringSchema.ts'
13
- import type { ProviderIconAppearance } from './providerIconSprite.ts'
14
14
 
15
15
  import { authoringReadResponse, authoringSearchResponse, authoringEditResponse, authoringCheckResponse } from './authoringSchema.ts'
16
- export { providerIconAppearance } from './providerIconSprite.ts'
17
- export type { ProviderIconAppearance, ProviderIconSprite, ProviderIconSpriteCatalog } from './providerIconSprite.ts'
16
+ export { providerIconAppearance } from '../../connector/common/providerIconSprite.ts'
17
+ export type { ProviderIconAppearance, ProviderIconSprite, ProviderIconSpriteCatalog } from '../../connector/common/providerIconSprite.ts'
18
18
  export { connectorAccess as decodeConnectorAccess, connectorAccessSnapshot as decodeConnectorAccessSnapshot } from './connectorDecoders.ts'
19
19
  import type { ProviderAccessIdentity, ProviderAccessReference } from './providerAccess.ts'
20
20
  export {
@@ -35,7 +35,8 @@ export { decodeEventSource, decodeEventSources, type CreateEventSource, type Upd
35
35
  import type { TriggerCatalogCache } from './triggerCatalog.ts'
36
36
 
37
37
  import { decodeTriggerCatalog } from './triggerCatalog.ts'
38
- export { decodeTriggerCatalog, searchTriggerKeys, type TriggerCatalog, type TriggerCatalogCache, type TriggerDisplay } from './triggerCatalog.ts'
38
+ export { decodeTriggerCatalog, searchTriggerKeys, type TriggerCatalog, type TriggerCatalogCache } from './triggerCatalog.ts'
39
+ export type { TriggerDisplay } from '../../trigger/providers/localization.ts'
39
40
  import type { TriggerConfigOption } from '../../trigger/common/configOptions.ts'
40
41
  export type { TriggerConfigOption } from '../../trigger/common/configOptions.ts'
41
42
  import type { ResultQuery } from './results.ts'
@@ -675,6 +676,7 @@ export function decodeRunEvent(value: unknown) {
675
676
  nodeKind !== 'javascript' &&
676
677
  nodeKind !== 'decision' &&
677
678
  nodeKind !== 'openapi' &&
679
+ nodeKind !== 'http-request' &&
678
680
  nodeKind !== 'llm' &&
679
681
  nodeKind !== 'agent' &&
680
682
  nodeKind !== 'value' &&
@@ -42,6 +42,7 @@ import {
42
42
  renameFlowOutputSources,
43
43
  } from '../../flow/common/nodeChanges.ts'
44
44
  import { retryConfigurationSchema } from '../../flow/common/retry.ts'
45
+ import { httpRequestTask, httpRequestConfigSchema, httpRequestInputs } from '../../http/common/httpRequest.ts'
45
46
  import { withWorkflowTask, workflowTask, workflowSelectionTask, workflowCapability, workflowAgentTool } from '../../workflow/common/workflow.ts'
46
47
  import { conditionConfigurationView, prepareConditionConfiguration } from './authoringCondition.ts'
47
48
  import { authoringDiagnosticField } from './authoringDiagnosticFields.ts'
@@ -145,7 +146,10 @@ export function authoringNode(content: RevisionContent, ref: string) {
145
146
  ),
146
147
  }
147
148
  else if (task.executor.kind == 'workflow') config = { flowId: task.executor.flowId }
148
- else if (task.executor.kind == 'openapi') {
149
+ else if (task.executor.kind == 'http-request') {
150
+ const { kind: _kind, ...settings } = task.executor
151
+ config = settings
152
+ } else if (task.executor.kind == 'openapi') {
149
153
  config = { ...openApiConfigView(task.executor) }
150
154
  }
151
155
  } else if (node.kind == 'task' && 'moduleId' in node.task) {
@@ -442,7 +446,8 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
442
446
  const timing = record(record(patch.config).autoContinue)
443
447
  if (Object.hasOwn(timing, 'kind') && timing.kind != record(record(view.config).autoContinue).kind) record(update.config).autoContinue = timing
444
448
  }
445
- if (type == 'openapi' && Object.hasOwn(record(patch.config), 'authentication')) record(update.config).authentication = record(patch.config).authentication
449
+ if ((type == 'openapi' || type == 'http-request') && Object.hasOwn(record(patch.config), 'authentication'))
450
+ record(update.config).authentication = record(patch.config).authentication
446
451
  for (const path of clear) clearPath(update, path)
447
452
  if (typeof update.name != 'string') fail('node.name-required', 'Node name cannot be cleared.')
448
453
  const config = authoringConfigurations[type].parse(update.config) as Record<string, unknown>
@@ -516,7 +521,10 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
516
521
  task = { ...before, name: String(update.name), executor: { ...executor, ...config } } as ManagedTaskDefinition
517
522
  } else task = await connectorTask(config, String(update.name))
518
523
  } else if (type == 'decision') task = decisionTask(config.questions as Parameters<typeof decisionTask>[0], String(update.name))
519
- else if (type == 'openapi') {
524
+ else if (type == 'http-request') {
525
+ task = httpRequestTask(httpRequestConfigSchema.parse(config), String(update.name), before)
526
+ next = { ...next, inputs: httpRequestInputs(node.inputs, before, task) } as GraphNode
527
+ } else if (type == 'openapi') {
520
528
  task = await prepareOpenApiTask(config as AuthoringOpenApiConfig, before, (url) => host.openapi(url), String(update.name), {
521
529
  authentication: explicit('authentication'),
522
530
  serverUrl: explicit('serverUrl'),
@@ -766,6 +774,9 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
766
774
  case 'connector':
767
775
  changes = createManagedTask(identity, await connectorTask(config, name))
768
776
  break
777
+ case 'http-request':
778
+ changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name))
779
+ break
769
780
  case 'openapi':
770
781
  changes = createManagedTask(
771
782
  identity,
@@ -11,6 +11,7 @@ import {
11
11
  createValue,
12
12
  createWait,
13
13
  } from '../../flow/common/nodeChanges.ts'
14
+ import { httpRequestTask } from '../../http/common/httpRequest.ts'
14
15
  import { openApiTask, selectOperation } from '../../openapi/common/openapi.ts'
15
16
 
16
17
  const descriptions = {
@@ -22,6 +23,7 @@ const descriptions = {
22
23
  'poll': 'Gmail polling by key; replace CONNECTION_ID with an active Gmail connection.',
23
24
  'integration': 'Telegram webhook integration by key; replace CONNECTION_ID with an active Telegram connection.',
24
25
  'decision': 'AI Decision evaluates named questions against target and returns a complete answer object per output. Configure target before running.',
26
+ 'http-request': 'A single HTTP request. Bind url and request inputs; outputs are body, statusCode and headers.',
25
27
  'openapi': 'A fixed JSON API operation. Replace the example document and URL before running.',
26
28
  'connector':
27
29
  'A Connector Task and its node. Replace ACTION_ID, CONNECTION_ID and port definitions using connector_get / connector show. Set connectionId to an empty string to defer account configuration; execution will require an account.',
@@ -55,6 +57,12 @@ export function authoringExample(name: string): { version: 1; operations: readon
55
57
  { name: 'needs_support', type: 'noul', instructions: 'Does the customer need support?' },
56
58
  ])
57
59
  break
60
+ case 'http-request':
61
+ operations = [
62
+ ...createManagedTask({ nodeId: 'http' }, httpRequestTask()),
63
+ { kind: 'graph.node.input.set', nodeId: 'http', handle: 'url', value: { kind: 'value', value: 'https://example.com' } },
64
+ ]
65
+ break
58
66
  case 'openapi': {
59
67
  const document = {
60
68
  openapi: '3.1.0',
@@ -6,9 +6,12 @@ import {
6
6
  connectorAgentToolSchema,
7
7
  workflowAgentToolSchema,
8
8
  connectorCapabilitySchema,
9
+ httpCapabilitySchema,
9
10
  authoringDefinitionSchemas as definitions,
10
11
  } from '../../flow/common/changeSchema.ts'
11
12
  import { retryConfigurationSchema, retryNodeTypes } from '../../flow/common/retry.ts'
13
+ import { httpRequestConfigSchema } from '../../http/common/httpRequest.ts'
14
+ import { currentEngineContract, findEngineContract } from '../../runtime/common/engineContract.ts'
12
15
  import { authoringLlmConfiguration } from './authoringLlm.ts'
13
16
  import { authoringOpenApiSchema } from './authoringOpenApi.ts'
14
17
 
@@ -31,6 +34,7 @@ export const authoringTypes = [
31
34
  'llm',
32
35
  'decision',
33
36
  'openapi',
37
+ 'http-request',
34
38
  'condition',
35
39
  'value',
36
40
  'wait',
@@ -69,7 +73,9 @@ const agentTool = z.union([
69
73
  workflowAgentToolSchema.omit({ id: true, inputs: true, outputs: true, revisionId: true }).extend({ inputs: toolSources }).strict(),
70
74
  connectorAgentToolSchema.omit({ id: true, inputs: true }).extend({ inputs: toolSources }).strict(),
71
75
  ])
72
- const authoringCapabilities = z.array(z.union([connectorCapabilitySchema, z.strictObject({ kind: z.literal('workflow'), name: id, flowId: id })]))
76
+ const authoringCapabilities = z.array(
77
+ z.union([connectorCapabilitySchema, z.strictObject({ kind: z.literal('workflow'), name: id, flowId: id }), httpCapabilitySchema]),
78
+ )
73
79
  const notification = z.strictObject({
74
80
  ...definitions.connector.shape,
75
81
  messageHandle: id,
@@ -111,6 +117,7 @@ export const authoringConfigurations = {
111
117
  'code': z.strictObject({ inputs: inputFields, outputs: outputFields, capabilities: authoringCapabilities.optional() }),
112
118
  'connector': definitions.connector.omit({ outputMode: true }).strict(),
113
119
  'openapi': authoringOpenApiSchema,
120
+ 'http-request': httpRequestConfigSchema,
114
121
  'agent': definitions.agent
115
122
  .omit({ prompt: true, tools: true })
116
123
  .extend({
@@ -256,6 +263,9 @@ const requestContracts = {
256
263
  },
257
264
  }
258
265
 
266
+ const codeBuiltins = [...findEngineContract(currentEngineContract)!.builtinModules!].filter((name) => !name.startsWith('node:'))
267
+ const codeGuidance = `Code is an ES module whose default export receives (inputs, context) and returns the business outputs. It may import the Node builtins ${codeBuiltins.join(', ')}, with or without the node: prefix; fs is a fresh in-memory filesystem containing /tmp per execution. require, dynamic import, packages and the http/net builtins are unavailable. To call HTTP APIs, declare their hosts in config.capabilities as {kind: "http", hosts: ["api.example.com", "*.example.org"]} and use context.fetch(url, {method, headers, body}); it returns status, ok, headers.get(), text(), json() and bytes(), does not follow redirects, adds no credentials, and limits bodies to 1 MiB. Prefer an HTTP Request node for a single call without transformation and Connector Actions for services with accounts. context provides inputs, signal, getPrevious(), fetch, actions, workflows, artifact, flowId, runId and blockId. Select workflows through config.capabilities: [{kind: "workflow", flowId, name}]. Call context.workflows.call(name, input) and await its business output. The platform derives input/output types. Refresh a selected workflow tool with node.refresh and tool: its name. Calls obey node and Run time limits.`
268
+
259
269
  /** Transport-independent discovery of the same requests the service validates. */
260
270
  export function authoringRequestContract(name: string) {
261
271
  if (!Object.hasOwn(requestContracts, name)) return undefined
@@ -296,15 +306,17 @@ export function authoringSchema(kind?: AuthoringType) {
296
306
  ? 'Configure static parameters with node.add.inputs or input.set. Definitions come from the selected Trigger key; execution output and variable sources are unavailable.'
297
307
  : kind == 'llm'
298
308
  ? 'Configure model settings, message templates and history as defaults. Input bindings with the same names override these defaults; named business inputs can be bound directly. Template strings interpolate {{name}}.'
299
- : kind == 'openapi'
300
- ? 'Select an operation; the tool resolves its ports and authentication. Read authenticationChoices from node details. authentication is replaced as a complete choice. Bind advertised credentials through inputs.'
301
- : kind == 'workflow'
302
- ? 'Select another workflow with config.flowId. A workflow cannot call itself, including in a disconnected node. Owned unpublished or unavailable targets may be saved as incomplete drafts. Use node.refresh to load the current published interface.'
303
- : kind == 'agent'
304
- ? 'Tools accept Connector Actions or {kind: "workflow", flowId, name, description, approval, inputs}. Workflow parameter sources use model, input or value. The platform derives the interface. Refresh a workflow tool with node.refresh and tool: its name.'
305
- : kind == 'code'
306
- ? 'Select workflows through config.capabilities: [{kind: "workflow", flowId, name}]. Call context.workflows.call(name, input) and await its business output. The platform derives input/output types. Refresh a selected workflow tool with node.refresh and tool: its name. Calls obey node and Run time limits.'
307
- : 'Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.',
309
+ : kind == 'http-request'
310
+ ? 'Configure method, query/headers modes and names, bodyFormat, authentication, responseFormat and statusPolicy. Bind url, query.<name>/header.<name> or whole query/headers objects, body and auth.token (Basic: auth.username/auth.password). Credentials accept variables or upstream outputs only. Outputs: body, statusCode, headers. Authentication is replaced as a complete choice.'
311
+ : kind == 'openapi'
312
+ ? 'Select an operation; the tool resolves its ports and authentication. Read authenticationChoices from node details. authentication is replaced as a complete choice. Bind advertised credentials through inputs.'
313
+ : kind == 'workflow'
314
+ ? 'Select another workflow with config.flowId. A workflow cannot call itself, including in a disconnected node. Owned unpublished or unavailable targets may be saved as incomplete drafts. Use node.refresh to load the current published interface.'
315
+ : kind == 'agent'
316
+ ? 'Tools accept Connector Actions or {kind: "workflow", flowId, name, description, approval, inputs}. Workflow parameter sources use model, input or value. The platform derives the interface. Refresh a workflow tool with node.refresh and tool: its name.'
317
+ : kind == 'code'
318
+ ? codeGuidance
319
+ : 'Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.',
308
320
  example: {
309
321
  op: 'node.add',
310
322
  as: 'step',
@@ -312,15 +324,16 @@ export function authoringSchema(kind?: AuthoringType) {
312
324
  name: 'Step',
313
325
  ...((
314
326
  {
315
- start: { config: { inputDefs: {} } },
316
- outputs: { config: { outputDefs: {} } },
317
- workflow: { config: { flowId: '<Flow ID>' } },
318
- connector: { config: { action: '<Action ID>', connectionId: '<Connection ID>' } },
319
- poll: { config: { key: '<Trigger key>', schedule: [{ type: 'every', unit: 'minute', value: 5 }] } },
320
- integration: { config: { key: '<Trigger key>' } },
321
- openapi: { config: { sourceUrl: 'https://example.com/openapi.json', path: '/items', method: 'get' } },
322
- code: { code: 'export default ({value}) => ({result:value})', inputs: { value: { kind: 'value', value: 'Hello' } } },
323
- condition: {
327
+ 'start': { config: { inputDefs: {} } },
328
+ 'outputs': { config: { outputDefs: {} } },
329
+ 'workflow': { config: { flowId: '<Flow ID>' } },
330
+ 'connector': { config: { action: '<Action ID>', connectionId: '<Connection ID>' } },
331
+ 'poll': { config: { key: '<Trigger key>', schedule: [{ type: 'every', unit: 'minute', value: 5 }] } },
332
+ 'integration': { config: { key: '<Trigger key>' } },
333
+ 'http-request': { config: {}, inputs: { url: { kind: 'value', value: 'https://example.com' } } },
334
+ 'openapi': { config: { sourceUrl: 'https://example.com/openapi.json', path: '/items', method: 'get' } },
335
+ 'code': { code: 'export default ({value}) => ({result:value})', inputs: { value: { kind: 'value', value: 'Hello' } } },
336
+ 'condition': {
324
337
  config: {
325
338
  match: 'first',
326
339
  branches: [
@@ -335,15 +348,15 @@ export function authoringSchema(kind?: AuthoringType) {
335
348
  ],
336
349
  },
337
350
  },
338
- value: { config: { values: { threshold: 100, region: 'EU' } } },
339
- webhook: { config: { method: 'POST', body: { orderId: { schema: { type: 'string' }, nullable: false } } } },
340
- wait: { prompt: 'Continue processing this order?', inputs: { order: { kind: 'value', value: { orderId: 'A-1' } } } },
341
- approval: { prompt: 'Approve this order?', inputs: { order: { kind: 'value', value: { orderId: 'A-1' } } } },
342
- llm: {
351
+ 'value': { config: { values: { threshold: 100, region: 'EU' } } },
352
+ 'webhook': { config: { method: 'POST', body: { orderId: { schema: { type: 'string' }, nullable: false } } } },
353
+ 'wait': { prompt: 'Continue processing this order?', inputs: { order: { kind: 'value', value: { orderId: 'A-1' } } } },
354
+ 'approval': { prompt: 'Approve this order?', inputs: { order: { kind: 'value', value: { orderId: 'A-1' } } } },
355
+ 'llm': {
343
356
  config: { model: { model: 'deepseek-v4-flash' }, template: [{ role: 'user', content: 'Summarize {{order}}.' }] },
344
357
  inputs: { order: { kind: 'value', value: { orderId: 'A-1', total: 42 } } },
345
358
  },
346
- agent: {
359
+ 'agent': {
347
360
  config: {
348
361
  model: 'deepseek-v4-flash',
349
362
  },
@@ -17,7 +17,7 @@ export type AuthoringCapability = Exclude<TaskCapability, WorkflowCapability> |
17
17
  export type WorkflowLookup = (flowId: string) => Promise<WorkflowContract | WorkflowCandidate>
18
18
 
19
19
  interface ToolIdentity {
20
- readonly kind?: 'connector' | 'workflow'
20
+ readonly kind?: string
21
21
  readonly name?: string
22
22
  readonly flowId?: string
23
23
  }
@@ -4,8 +4,8 @@ import type { ConnectorConnection, CreateEventSource, ResultInfo, PollTriggerTes
4
4
  import type { ProviderAccessIdentity } from './providerAccess.ts'
5
5
 
6
6
  import { dequal } from 'dequal/lite'
7
- import { currentEngineContract as engineContract } from '../../execution/common/engineContract.ts'
8
7
  import { upgradeRevisionContent } from '../../flow/common/change.ts'
8
+ import { currentEngineContract as engineContract } from '../../runtime/common/engineContract.ts'
9
9
  import { ControlClient, decodeRunEvent } from './api.ts'
10
10
  import { allConnectorConnectionsQuery } from './connectorQueries.ts'
11
11
 
@@ -14,10 +14,10 @@ import type {
14
14
  ProviderAccessBindingCandidate,
15
15
  } from './api.ts'
16
16
 
17
+ import { providerIconAppearance } from '../../connector/common/providerIconSprite.ts'
17
18
  import { exact, requiredFields, integer, invalidResponse, jsonValue, record, string } from './decoding.ts'
18
19
  import { ApiError } from './errors.ts'
19
20
  import { parseProviderAccessSource } from './providerAccess.ts'
20
- import { providerIconAppearance } from './providerIconSprite.ts'
21
21
 
22
22
  export function leinaBots(value: unknown): readonly LeinaBot[] {
23
23
  const source = record(value)
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod'
2
+ import { eventSourceViewSchema } from '../../event-source/common/view.ts'
2
3
 
3
4
  const id = z.string().min(1).max(256)
4
5
  const eventType = z.string().regex(/^[a-z][a-z0-9_.:]{0,127}$/)
@@ -10,45 +11,17 @@ const eventSourceSchema = z.object({
10
11
  revision: z.int().positive(),
11
12
  name,
12
13
  provider: z.enum(['feishu', 'feishu_app_bot', 'slackbot', 'intercom', 'sentry', 'zoom', 'fireflies', 'jira']),
13
- appId: id.nullable(),
14
- workspaceId: id.optional(),
15
- installationId: id.optional(),
16
- organizationId: id.optional(),
17
- projectId: id.optional(),
18
- projectKey: id.optional(),
19
- subscriptionStatus: z.enum(['idle', 'pending', 'ready', 'error']).optional(),
20
- lastError: z.string().optional(),
21
- signingSecretConfigured: z.boolean().optional(),
14
+ ...eventSourceViewSchema.shape,
22
15
  connectionId: id,
23
16
  teamId: id.nullable(),
24
17
  enabled: z.boolean(),
25
18
  eventTypes: z.array(eventType),
26
- manageSubscriptions: z.boolean().nullable(),
27
- verificationTokenConfigured: z.boolean().nullable(),
28
- encryptKeyConfigured: z.boolean().nullable(),
29
19
  endpointUrl: z.string().nullable(),
30
- verifiedAt: z.string().nullable(),
31
20
  lastReceivedAt: z.string().nullable(),
32
21
  updatedAt: z.string(),
33
22
  consumers: z.array(z.object({ flowId: id, flowName: z.string(), triggerNodeId: id })),
34
23
  })
35
24
 
36
- export const eventSourceViewSchema = eventSourceSchema.pick({
37
- appId: true,
38
- workspaceId: true,
39
- installationId: true,
40
- organizationId: true,
41
- projectId: true,
42
- projectKey: true,
43
- subscriptionStatus: true,
44
- lastError: true,
45
- signingSecretConfigured: true,
46
- manageSubscriptions: true,
47
- verificationTokenConfigured: true,
48
- encryptKeyConfigured: true,
49
- verifiedAt: true,
50
- })
51
-
52
25
  export interface CreateFeishuEventSource {
53
26
  readonly kind?: 'feishu'
54
27
  readonly version: 1
@@ -1,19 +1,12 @@
1
1
  import type { TriggerKeySnapshot } from '../../flow/common/change.ts'
2
2
  import type { UiLanguage } from '../../localization/common/languages.ts'
3
+ import type { TriggerDisplay } from '../../trigger/providers/localization.ts'
3
4
  import type { TriggerKeySummary } from './api.ts'
4
5
 
5
6
  import { isUiLanguage } from '../../localization/common/languages.ts'
6
7
  import { exact, requiredFields, invalidResponse, record, string } from './decoding.ts'
7
8
  import { triggerKey } from './triggerDecoders.ts'
8
9
 
9
- export interface TriggerDisplay {
10
- readonly configInputs: Readonly<Record<string, string>>
11
- readonly configInputLabels: Readonly<Record<string, string>>
12
- readonly displayName: string
13
- readonly description: string
14
- readonly outputs: Readonly<Record<string, string>>
15
- }
16
-
17
10
  export interface TriggerCatalog {
18
11
  readonly version: 3
19
12
  readonly locale: UiLanguage
@@ -1,4 +1,4 @@
1
- import type { InputPort, JsonValue, ManagedTaskDefinition, Port } from '../../flow/common/change.ts'
1
+ import type { InputPort, JsonValue, ManagedTaskDefinitionOf, Port, TaskExecutor } from '../../flow/common/model.ts'
2
2
 
3
3
  import { dequal } from 'dequal/lite'
4
4
  import { matchesSchema } from '../../flow/common/schema.ts'
@@ -18,6 +18,10 @@ export interface DecisionIssue {
18
18
  readonly message: string
19
19
  }
20
20
 
21
+ function isDecisionExecutor(executor: TaskExecutor): executor is DecisionExecutor {
22
+ return executor.kind === 'decision'
23
+ }
24
+
21
25
  export const decisionModel = 'typesafe/jev'
22
26
  export const decisionLimits = { choice: 255, score: 10 } as const
23
27
 
@@ -34,8 +38,8 @@ export function limitDecisionQuestions(questions: readonly DecisionQuestion[]):
34
38
  }
35
39
 
36
40
  /** Loading an oversized task also updates its previously generated output schemas. */
37
- export function limitDecisionTask(task: ManagedTaskDefinition): ManagedTaskDefinition {
38
- if (task.executor.kind !== 'decision') return task
41
+ export function limitDecisionTask<Task extends ManagedTaskDefinitionOf<TaskExecutor>>(task: Task): Task {
42
+ if (!isDecisionExecutor(task.executor)) return task
39
43
  const original = task.executor.questions
40
44
  const questions = limitDecisionQuestions(original)
41
45
  if (questions === original) return task
@@ -45,7 +49,8 @@ export function limitDecisionTask(task: ManagedTaskDefinition): ManagedTaskDefin
45
49
  if (index < 0 || original[index] === questions[index] || !dequal(port.jsonSchema, answerSchema(original[index]!))) return port
46
50
  return { ...port, jsonSchema: decisionAnswerSchema(questions[index]!) }
47
51
  })
48
- return { ...task, executor: { ...task.executor, questions }, outputs }
52
+ // Only the decision executor and its derived outputs change, so the task keeps its own type.
53
+ return { ...task, executor: { ...task.executor, questions }, outputs } as Task
49
54
  }
50
55
  export const decisionStateSchema: JsonValue = { type: ['string', 'object', 'array'] }
51
56
  const probability: JsonValue = { type: 'number', minimum: 0, maximum: 1 }
@@ -102,7 +107,10 @@ function answerSchema(question: DecisionQuestion): JsonValue {
102
107
  return { type: 'object', properties, required: Object.keys(properties) }
103
108
  }
104
109
 
105
- export function decisionTask(questions: readonly DecisionQuestion[] = [defaultDecisionQuestion()], name = 'AI Decision'): ManagedTaskDefinition {
110
+ export function decisionTask(
111
+ questions: readonly DecisionQuestion[] = [defaultDecisionQuestion()],
112
+ name = 'AI Decision',
113
+ ): ManagedTaskDefinitionOf<DecisionExecutor> {
106
114
  questions = limitDecisionQuestions(questions)
107
115
  const inputs: InputPort[] = [
108
116
  { handle: 'target', description: 'Text, object, or array to evaluate across all questions.', jsonSchema: decisionStateSchema, nullable: false },
@@ -111,9 +119,9 @@ export function decisionTask(questions: readonly DecisionQuestion[] = [defaultDe
111
119
  return { name, executor: { kind: 'decision', questions }, inputs, outputs }
112
120
  }
113
121
 
114
- export function decisionTaskIssues(task: ManagedTaskDefinition): readonly DecisionIssue[] {
122
+ export function decisionTaskIssues(task: ManagedTaskDefinitionOf<TaskExecutor>): readonly DecisionIssue[] {
115
123
  task = limitDecisionTask(task)
116
- if (task.executor.kind !== 'decision') return []
124
+ if (!isDecisionExecutor(task.executor)) return []
117
125
  const expected = decisionTask(task.executor.questions, task.name)
118
126
  return [
119
127
  ...decisionQuestionIssues(task.executor.questions),