langflower 0.0.7 → 0.0.9

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 (189) hide show
  1. package/package.json +1 -1
  2. package/ui-dist/{chunk-Y6VMBLSJ.js → chunk-7UMVYN3A.js} +1 -1
  3. package/ui-dist/{chunk-6DQ2XSRW.js → chunk-MDGXX73Z.js} +3 -3
  4. package/ui-dist/index.html +1 -1
  5. package/ui-dist/main-2JJGCNTD.js +313 -0
  6. package/vendor/common-nodes/dist/ai/chat-completion-stream.d.ts +2 -0
  7. package/vendor/common-nodes/dist/ai/critique/node.d.ts +246 -0
  8. package/vendor/common-nodes/dist/ai/critique/node.js +2 -2
  9. package/vendor/common-nodes/dist/ai/fake-llm/node.d.ts +246 -0
  10. package/vendor/common-nodes/dist/ai/features/chat-completion-stream.d.ts +53 -0
  11. package/vendor/common-nodes/dist/ai/features/chat-completion-stream.js +1 -0
  12. package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.d.ts +13 -0
  13. package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.js +17 -0
  14. package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.d.ts +2 -0
  15. package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.js +113 -0
  16. package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.d.ts +28 -0
  17. package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.js +194 -0
  18. package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.d.ts +9 -0
  19. package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.js +21 -0
  20. package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.d.ts +2 -0
  21. package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.js +216 -0
  22. package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.d.ts +125 -0
  23. package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.js +39 -0
  24. package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.d.ts +2 -0
  25. package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.js +59 -0
  26. package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.d.ts +1 -0
  27. package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.js +7 -0
  28. package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.d.ts +43 -0
  29. package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.js +96 -0
  30. package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.d.ts +32 -0
  31. package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.js +58 -0
  32. package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.d.ts +80 -0
  33. package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.js +681 -0
  34. package/vendor/common-nodes/dist/ai/features/llm-role-preset.d.ts +69 -0
  35. package/vendor/common-nodes/dist/ai/features/llm-role-preset.js +265 -0
  36. package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +148 -0
  37. package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +198 -0
  38. package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.d.ts +21 -0
  39. package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.js +118 -0
  40. package/vendor/common-nodes/dist/ai/features/openai/context-length-error.d.ts +14 -0
  41. package/vendor/common-nodes/dist/ai/features/openai/context-length-error.js +63 -0
  42. package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.d.ts +19 -0
  43. package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.js +182 -0
  44. package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.d.ts +28 -0
  45. package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.js +52 -0
  46. package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.d.ts +31 -0
  47. package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.js +326 -0
  48. package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.d.ts +14 -0
  49. package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.js +21 -0
  50. package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.d.ts +30 -0
  51. package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.js +132 -0
  52. package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.d.ts +20 -0
  53. package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.js +70 -0
  54. package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.d.ts +35 -0
  55. package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.js +106 -0
  56. package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.d.ts +9 -0
  57. package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.js +21 -0
  58. package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.d.ts +13 -0
  59. package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.js +23 -0
  60. package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.d.ts +10 -0
  61. package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.js +8 -0
  62. package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +54 -0
  63. package/vendor/common-nodes/dist/ai/features/run-host-services.js +29 -0
  64. package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.d.ts +17 -0
  65. package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.js +61 -0
  66. package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.d.ts +39 -0
  67. package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.js +43 -0
  68. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.d.ts +14 -0
  69. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.js +18 -0
  70. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.d.ts +80 -0
  71. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.js +76 -0
  72. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.d.ts +113 -0
  73. package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.js +131 -0
  74. package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.d.ts +6 -0
  75. package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.js +30 -0
  76. package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.d.ts +13 -0
  77. package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.js +17 -0
  78. package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.d.ts +28 -0
  79. package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.js +194 -0
  80. package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-reducer.js +36 -0
  81. package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.d.ts +35 -0
  82. package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.js +19 -0
  83. package/vendor/common-nodes/dist/ai/llm-loop/normalize-llm-recovery-policy.js +52 -9
  84. package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.d.ts +8 -2
  85. package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.js +43 -13
  86. package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.d.ts +3 -5
  87. package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.js +116 -1
  88. package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.d.ts +82 -0
  89. package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.js +95 -0
  90. package/vendor/common-nodes/dist/ai/llm-session-shell.d.ts +4 -9
  91. package/vendor/common-nodes/dist/ai/llm-session-shell.js +2 -4
  92. package/vendor/common-nodes/dist/ai/nodes/critique/node.d.ts +588 -0
  93. package/vendor/common-nodes/dist/ai/nodes/critique/node.js +240 -0
  94. package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.d.ts +571 -0
  95. package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +208 -0
  96. package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.d.ts +587 -0
  97. package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +91 -0
  98. package/vendor/common-nodes/dist/ai/nodes/review/node.d.ts +593 -0
  99. package/vendor/common-nodes/dist/ai/nodes/review/node.js +240 -0
  100. package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.d.ts +630 -0
  101. package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +302 -0
  102. package/vendor/common-nodes/dist/ai/openai/create-chat-completion-stream.js +6 -0
  103. package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.d.ts +2 -0
  104. package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.js +18 -0
  105. package/vendor/common-nodes/dist/ai/openai-llm/node.d.ts +246 -0
  106. package/vendor/common-nodes/dist/ai/review/node.d.ts +246 -0
  107. package/vendor/common-nodes/dist/ai/review/node.js +2 -2
  108. package/vendor/common-nodes/dist/ai/sub-agent/node.d.ts +246 -0
  109. package/vendor/common-nodes/dist/ai/sub-agent/node.js +2 -2
  110. package/vendor/common-nodes/dist/catalog.js +7 -5
  111. package/vendor/common-nodes/dist/compile-custom-nodes/node.d.ts +21 -0
  112. package/vendor/common-nodes/dist/compile-custom-nodes/node.js +45 -0
  113. package/vendor/common-nodes/dist/crawl/crawl/node.js +1 -1
  114. package/vendor/common-nodes/dist/crawl/fetch-url/node.js +1 -1
  115. package/vendor/common-nodes/dist/flow/repeat/node.js +6 -1
  116. package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.d.ts +18 -0
  117. package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.js +29 -0
  118. package/vendor/common-nodes/dist/langflower-tools/node.d.ts +20 -0
  119. package/vendor/common-nodes/dist/langflower-tools/node.js +77 -0
  120. package/vendor/common-nodes/dist/text/append-file/node.js +1 -1
  121. package/vendor/common-nodes/dist/text/read-file/node.js +1 -1
  122. package/vendor/common-nodes/dist/text/write-file/node.js +1 -1
  123. package/vendor/common-nodes/dist/tools/inventory-tool-round.d.ts +2 -2
  124. package/vendor/common-nodes/package.json +10 -10
  125. package/vendor/compiler/dist/compile-project-nodes.d.ts +2 -1
  126. package/vendor/compiler/dist/compile-project-nodes.js +35 -6
  127. package/vendor/compiler/dist/resolve-host-types.d.ts +0 -5
  128. package/vendor/compiler/dist/resolve-host-types.js +0 -8
  129. package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.d.ts +1 -1
  130. package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.js +1 -1
  131. package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.d.ts +8 -0
  132. package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.js +14 -0
  133. package/vendor/runtime/dist/runtime-editor.d.ts +5 -1
  134. package/vendor/runtime/dist/runtime-editor.js +19 -0
  135. package/vendor/runtime/dist/runtime-runner.js +17 -17
  136. package/vendor/runtime/dist/runtime.d.ts +2 -1
  137. package/vendor/runtime/dist/runtime.js +1 -0
  138. package/vendor/runtime/dist/testing/workflows/workflow-events.d.ts +8 -6
  139. package/vendor/runtime/dist/testing/workflows/workflow-events.js +19 -31
  140. package/vendor/runtime/dist/types.d.ts +33 -38
  141. package/vendor/runtime/dist/types.js +2 -1
  142. package/vendor/server/dist/bridge/attach-langflower-bridge.js +3 -2
  143. package/vendor/server/dist/bridge/bridge-event-log.js +17 -102
  144. package/vendor/server/dist/bridge/build-execution-context.d.ts +5 -2
  145. package/vendor/server/dist/bridge/build-execution-context.js +45 -5
  146. package/vendor/server/dist/bridge/forward-runner-event.d.ts +1 -1
  147. package/vendor/server/dist/bridge/forward-runner-event.js +7 -12
  148. package/vendor/server/dist/bridge/get-live-wired-tools.d.ts +12 -0
  149. package/vendor/server/dist/bridge/get-live-wired-tools.js +80 -0
  150. package/vendor/server/dist/bridge/langflower-tools-rpc.d.ts +7 -0
  151. package/vendor/server/dist/bridge/langflower-tools-rpc.js +29 -0
  152. package/vendor/server/dist/bridge/settings-draft-controller.js +26 -3
  153. package/vendor/server/dist/bridge/wire-custom-palette-handlers.d.ts +2 -1
  154. package/vendor/server/dist/bridge/wire-custom-palette-handlers.js +3 -5
  155. package/vendor/server/dist/bridge/wire-runner-handlers.js +15 -8
  156. package/vendor/server/dist/checkpoint/run-checkpoint-session.d.ts +1 -1
  157. package/vendor/server/dist/checkpoint/run-checkpoint-session.js +10 -9
  158. package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.d.ts +10 -0
  159. package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.js +23 -0
  160. package/vendor/server/dist/workflow/apply-editor-mutation.d.ts +5 -0
  161. package/vendor/server/dist/workflow/apply-editor-mutation.js +32 -0
  162. package/vendor/server/skeleton/instructions.md +9 -5
  163. package/vendor/server/skeleton/nodes/my-nodes/README.md +4 -3
  164. package/vendor/server/skeleton/schemas/langflower-config.schema.json +11 -1
  165. package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +49 -19
  166. package/vendor/server/skeleton/skills/langflower-helper/architecture.md +22 -9
  167. package/vendor/server/skeleton/skills/langflower-helper/layout.md +15 -15
  168. package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +19 -9
  169. package/vendor/server/skeleton/skills/langflower-workflow-writer/SKILL.md +14 -10
  170. package/vendor/server/skeleton/workflows/starter.json +30 -3
  171. package/vendor/shared/dist/execution/derive-run-settle-outcome.d.ts +1 -1
  172. package/vendor/shared/dist/execution/derive-run-settle-outcome.js +4 -3
  173. package/vendor/shared/dist/execution/derive-run-settle-outcome.test.js +9 -9
  174. package/vendor/shared/dist/langflower-bus-config.d.ts +6 -14
  175. package/vendor/shared/dist/langflower-bus-config.js +4 -12
  176. package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.js +6 -0
  177. package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.test.js +30 -0
  178. package/vendor/shared/dist/langflower-ws-waits.d.ts +7 -14
  179. package/vendor/shared/dist/langflower-ws-waits.js +14 -14
  180. package/vendor/websocket-bridge/dist/bridge-codec.d.ts +1 -0
  181. package/vendor/websocket-bridge/dist/bridge-codec.js +11 -11
  182. package/vendor/websocket-bridge/dist/bridge-frame.js +1 -1
  183. package/vendor/websocket-bridge/dist/bridge-subjects.d.ts +1 -1
  184. package/vendor/websocket-bridge/dist/bridge-subjects.js +2 -2
  185. package/vendor/websocket-bridge/dist/bridge-types.d.ts +13 -1
  186. package/vendor/websocket-bridge/dist/create-client.js +3 -3
  187. package/vendor/websocket-bridge/dist/create-server.js +12 -6
  188. package/vendor/websocket-bridge/package.json +8 -0
  189. package/ui-dist/main-SMO3L7U5.js +0 -311
@@ -1,4 +1,5 @@
1
1
  import { filter, firstValueFrom } from 'rxjs';
2
+ import { isPortTelemetry, isRuntimeDone, } from '../../types.js';
2
3
  import { RuntimeEditor } from '../../runtime-editor.js';
3
4
  import { RuntimeRunner } from '../../runtime-runner.js';
4
5
  export function createRuntimeHarness(options) {
@@ -17,27 +18,23 @@ export function wireEdge(editor, edge) {
17
18
  throw new Error(`Failed to wire ${edge.fromNodeId}.${edge.fromPort[0]} → ${edge.toNodeId}.${edge.toPort[0]}`);
18
19
  }
19
20
  }
20
- function isOutputEmitted(event) {
21
- return event.kind === 'output-emitted' && event.state === 'value';
22
- }
23
- export function outputValues(events, nodeId, portId, runId) {
21
+ const isOutputValue = (event) => isPortTelemetry(event) && event[0] === 'out' && event[3] === 'value';
22
+ export function outputValues(events, nodeId, portId, _runId) {
24
23
  return events
25
- .filter((event) => isOutputEmitted(event) &&
26
- event.nodeId === nodeId &&
27
- event.portId === portId &&
28
- (runId === undefined || event.runId === runId))
29
- .map((event) => event.value);
24
+ .filter((event) => isOutputValue(event) &&
25
+ event[1] === nodeId &&
26
+ event[2] === portId)
27
+ .map((event) => event[4]);
30
28
  }
31
- export async function waitForOutput(runtime, nodeId, portId, runId) {
32
- return waitForOutputMatching(runtime, nodeId, portId, () => true, runId);
29
+ export async function waitForOutput(runtime, nodeId, portId, _runId) {
30
+ return waitForOutputMatching(runtime, nodeId, portId, () => true, _runId);
33
31
  }
34
- async function waitForOutputMatching(runtime, nodeId, portId, predicate, runId) {
32
+ async function waitForOutputMatching(runtime, nodeId, portId, predicate, _runId) {
35
33
  const runner = resolveRunner(runtime);
36
- return firstValueFrom(runner.events$.pipe(filter((event) => isOutputEmitted(event) &&
37
- event.nodeId === nodeId &&
38
- event.portId === portId &&
39
- (runId === undefined || event.runId === runId) &&
40
- predicate(event.value))));
34
+ return firstValueFrom(runner.events$.pipe(filter((event) => isOutputValue(event) &&
35
+ event[1] === nodeId &&
36
+ event[2] === portId &&
37
+ predicate(event[4]))));
41
38
  }
42
39
  export async function runAndCollectEvents(runtime, startRun, settleMs = 50) {
43
40
  const events = [];
@@ -52,26 +49,17 @@ export async function runAndCollectEvents(runtime, startRun, settleMs = 50) {
52
49
  subscription.unsubscribe();
53
50
  return { runId, events };
54
51
  }
55
- async function collectEventsForMs(runtime, ms) {
56
- const events = [];
57
- const runner = resolveRunner(runtime);
58
- const subscription = runner.events$.subscribe((event) => {
59
- events.push(event);
60
- });
61
- await new Promise((resolve) => {
62
- setTimeout(resolve, ms);
63
- });
64
- subscription.unsubscribe();
65
- return events;
66
- }
67
52
  export async function noDoneWithin(runtime, ms, runId) {
68
53
  const runner = resolveRunner(runtime);
69
54
  const result = await Promise.race([
70
- firstValueFrom(runner.events$.pipe(filter((event) => event.kind === 'done' &&
71
- (runId === undefined || event.runId === runId)))).then(() => 'done'),
55
+ firstValueFrom(runner.events$.pipe(filter((event) => isRuntimeDone(event) &&
56
+ event.length === 2 &&
57
+ (runId === undefined || event[1] === runId)))).then(() => 'done'),
72
58
  new Promise((resolve) => {
73
59
  setTimeout(() => resolve('timeout'), ms);
74
60
  }),
75
61
  ]);
76
62
  return result === 'timeout';
77
63
  }
64
+ export const edgeIdsFromPortEvent = (event) => event[6];
65
+ export const portDirLabel = (event) => event[0] === 'out' ? 'output-emitted' : 'input-received';
@@ -119,44 +119,26 @@ export type MetaFromStatefulObservable<T extends StatefulObservable<unknown, unk
119
119
  */
120
120
  export type RuntimeRunnerStatus = 'idle' | 'running' | 'stopped';
121
121
  export type RuntimePortSignalState = 'pending' | 'value' | 'error';
122
- export type RuntimeOutputEmittedEvent = {
123
- readonly kind: 'output-emitted';
124
- readonly runId: RunId;
125
- readonly nodeId: NodeId;
126
- readonly portId: string | symbol;
127
- readonly portIdx: number;
128
- readonly edgeIds: EdgeId[];
129
- readonly state: RuntimePortSignalState;
130
- readonly value: unknown;
131
- /** Resolved author feed contract carried with the runtime fact. */
132
- readonly feed?: RuntimeFeedPortMeta;
133
- };
134
- export type RuntimeInputReceivedEvent = {
135
- readonly kind: 'input-received';
136
- readonly runId: RunId;
137
- readonly nodeId: NodeId;
138
- readonly portId: string | symbol;
139
- readonly portIdx: number;
140
- readonly edgeIds: EdgeId[];
141
- readonly state: RuntimePortSignalState;
142
- readonly value: unknown;
143
- /** Resolved author feed contract carried with the runtime fact. */
144
- readonly feed?: RuntimeFeedPortMeta;
145
- };
146
- export type RuntimeRunnerEvent = RuntimeOutputEmittedEvent | RuntimeInputReceivedEvent | {
147
- /**
148
- * Run ended naturally — sets {@link RuntimeRunnerStatus} to `'idle'`.
149
- *
150
- * Emitted when:
151
- * - the graph is **empty** on {@link RuntimeRunner.start}, or
152
- * - a node with {@link RuntimeNode.stopsRun} emits on a watched output.
153
- *
154
- * Otherwise the run stays `'running'` until
155
- * {@link RuntimeRunner.interrupt}.
156
- */
157
- readonly kind: 'done';
158
- readonly runId: RunId;
159
- };
122
+ /**
123
+ * Port signal tuple — `portDir` `'out'` = output emitted, `'in'` = input received.
124
+ * No per-frame `runId`; session snapshots own run correlation.
125
+ */
126
+ export type PortTelemetry = readonly [
127
+ portDir: 'in' | 'out',
128
+ nodeId: NodeId,
129
+ portId: string,
130
+ state: RuntimePortSignalState,
131
+ value: unknown,
132
+ portIdx: number,
133
+ edgeIds: readonly EdgeId[],
134
+ /** Absent feed meta — use `null` (never `undefined`; JSON arrays drop undefined). */
135
+ feed: RuntimeFeedPortMeta | null
136
+ ];
137
+ /** Run ended — `runId` at index 1 when replay needs explicit correlation. */
138
+ export type RuntimeDoneTelemetry = readonly ['done'] | readonly ['done', RunId];
139
+ export type RuntimeRunnerEvent = PortTelemetry | RuntimeDoneTelemetry;
140
+ export declare const isPortTelemetry: (event: RuntimeRunnerEvent) => event is PortTelemetry;
141
+ export declare const isRuntimeDone: (event: RuntimeRunnerEvent) => event is RuntimeDoneTelemetry;
160
142
  /** Seed value pushed into an input port slot on {@link RuntimeRunner.start}. */
161
143
  export type RuntimeSeedPortValue = {
162
144
  readonly portId: string | symbol;
@@ -255,6 +237,10 @@ export type RuntimeOptions = {
255
237
  */
256
238
  readonly log?: boolean;
257
239
  };
240
+ export type SwapNodeResult = {
241
+ readonly node: RuntimeNode;
242
+ readonly droppedEdges: readonly RuntimeEdge[];
243
+ };
258
244
  export type RuntimeEditorApi = {
259
245
  /**
260
246
  * Register a node and its port observables (handler already bound).
@@ -288,6 +274,15 @@ export type RuntimeEditorApi = {
288
274
  * graph is locked. Does not cascade-remove other edges.
289
275
  */
290
276
  replaceEdge(edge: Omit<RuntimeEdge, 'edgeId'>): RuntimeEdge | false;
277
+ /**
278
+ * Replace the live instance at `nodeId` with `next`. Keeps `nodeId`.
279
+ * Drops incident edges whose ports vanished or whose wire types are no
280
+ * longer compatible (same rules as {@link addEdge}).
281
+ *
282
+ * Allowed while the graph is locked. Does not call {@link addNode} /
283
+ * {@link removeNode}. Returns `false` when disposed or `nodeId` is missing.
284
+ */
285
+ swapNode(nodeId: NodeId, next: Omit<RuntimeNode, 'nodeId'>): SwapNodeResult | false;
291
286
  /** Must fail while runner status is `'running'`.
292
287
  *
293
288
  * return false while running or node not found
@@ -1 +1,2 @@
1
- export {};
1
+ export const isPortTelemetry = (event) => Array.isArray(event) && (event[0] === 'in' || event[0] === 'out');
2
+ export const isRuntimeDone = (event) => Array.isArray(event) && event[0] === 'done';
@@ -1,3 +1,4 @@
1
+ import { isRuntimeDone } from '@langflower/runtime';
1
2
  import { deriveExecutionProgressStatus, terminalExecutionProgressStatus, } from '@langflower/shared/langflower.js';
2
3
  import { Subscription } from 'rxjs';
3
4
  import { RunCheckpointSession } from '../checkpoint/run-checkpoint-session.js';
@@ -36,7 +37,7 @@ export const attachLangflowerBridge = (bridge, context, options = {}) => {
36
37
  // miss them (`events$` is a non-replaying Subject). Late clients get the
37
38
  // backlog via `executionFeed.snapshot`. See FOUND_BUGS pending race.
38
39
  rootSubscription.add(session.runtime.runner.events$.subscribe((event) => {
39
- if (event.kind === 'done' && onRunSettled !== undefined) {
40
+ if (isRuntimeDone(event) && onRunSettled !== undefined) {
40
41
  const progress = deriveExecutionProgressStatus('idle', session.runtime.runner.eventLog);
41
42
  const status = terminalExecutionProgressStatus(progress);
42
43
  if (status !== null) {
@@ -59,7 +60,7 @@ export const attachLangflowerBridge = (bridge, context, options = {}) => {
59
60
  // 3. Intent handlers (bus namespaces)
60
61
  rootSubscription.add(wireWorkflowHandlers(bridge, context, session));
61
62
  rootSubscription.add(wirePaletteHandlers(bridge, context));
62
- rootSubscription.add(wireCustomPaletteHandlers(bridge, context));
63
+ rootSubscription.add(wireCustomPaletteHandlers(bridge, context, session));
63
64
  rootSubscription.add(wireConfigHandlers(bridge, context, session, draftController, {
64
65
  onEffectiveConfig: (config) => {
65
66
  applyServerLogsGate(diagnosticLog.setEnabled, config);
@@ -2,67 +2,22 @@ import { randomUUID } from 'node:crypto';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { langflowerWsConfig } from '@langflower/shared/langflower.js';
5
+ import { encodeBridgeFrame } from '@langflower/websocket-bridge/bridge-codec';
5
6
  import { Subscription } from 'rxjs';
6
- const REDACTED = '[REDACTED]';
7
- const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
8
- const isSecretKey = (key) => {
9
- const normalized = key.toLowerCase();
10
- return (normalized.includes('apikey') ||
11
- normalized.includes('authorization') ||
12
- normalized.includes('cookie') ||
13
- normalized.includes('token') ||
14
- normalized.includes('password') ||
15
- normalized.includes('secret') ||
16
- normalized.includes('credential'));
17
- };
18
- const sanitize = (value, seen = new WeakSet()) => {
19
- if (value === null ||
20
- typeof value === 'string' ||
21
- typeof value === 'number' ||
22
- typeof value === 'boolean') {
23
- return value;
24
- }
25
- if (typeof value === 'bigint') {
26
- return `${value}n`;
27
- }
28
- if (typeof value === 'undefined') {
29
- return '[undefined]';
30
- }
31
- if (typeof value === 'symbol' || typeof value === 'function') {
32
- return `[${typeof value}]`;
33
- }
34
- if (value instanceof Error) {
35
- return {
36
- name: value.name,
37
- message: value.message,
38
- };
39
- }
40
- if (value instanceof Date) {
41
- return value.toISOString();
42
- }
43
- if (typeof value === 'object') {
44
- if (seen.has(value)) {
45
- return '[circular]';
46
- }
47
- seen.add(value);
48
- }
49
- if (Array.isArray(value)) {
50
- return value.map((entry) => sanitize(entry, seen));
51
- }
52
- if (isRecord(value)) {
53
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
54
- key,
55
- isSecretKey(key) ? REDACTED : sanitize(entry, seen),
56
- ]));
57
- }
58
- return String(value);
59
- };
60
7
  const toSafeTimestamp = (date) => date.toISOString().replaceAll(':', '-').replaceAll('.', '-');
61
- const asObservable = (value) => isRecord(value) && typeof value.subscribe === 'function'
8
+ const asObservable = (value) => typeof value === 'object' &&
9
+ value !== null &&
10
+ typeof value.subscribe === 'function'
62
11
  ? value
63
12
  : undefined;
64
- const asInboundEvent = (value) => isRecord(value) && typeof value.clientId === 'string' && 'payload' in value
65
- ? { clientId: value.clientId, payload: value.payload }
13
+ const asInboundEvent = (value) => typeof value === 'object' &&
14
+ value !== null &&
15
+ typeof value.clientId === 'string' &&
16
+ 'payload' in value
17
+ ? {
18
+ clientId: value.clientId,
19
+ payload: value.payload,
20
+ }
66
21
  : undefined;
67
22
  const logPath = (projectDir) => path.join(projectDir, '.langflower', 'logs', `${toSafeTimestamp(new Date())}-${process.pid}-${randomUUID()}.log`);
68
23
  export const attachBridgeEventLog = (bridge, projectDir, subscriptions, options = {}) => {
@@ -81,15 +36,11 @@ export const attachBridgeEventLog = (bridge, projectDir, subscriptions, options
81
36
  const message = error instanceof Error ? error.message : String(error);
82
37
  process.stderr.write(`Langflower diagnostic logging disabled: ${message}\n`);
83
38
  };
84
- const write = (record) => {
39
+ const writeFrame = (transportDir, busType, payload) => {
85
40
  if (!enabled || !acceptingWrites || closed) {
86
41
  return;
87
42
  }
88
- const line = `${JSON.stringify(sanitize({
89
- schemaVersion: 1,
90
- timestamp: new Date().toISOString(),
91
- ...record,
92
- }))}\n`;
43
+ const line = `${encodeBridgeFrame(transportDir, busType, payload)}\n`;
93
44
  writeQueue = writeQueue.then(async () => {
94
45
  if (!enabled || !acceptingWrites) {
95
46
  return;
@@ -118,13 +69,7 @@ export const attachBridgeEventLog = (bridge, projectDir, subscriptions, options
118
69
  subscriptions.add(channel.subscribe((value) => {
119
70
  const event = asInboundEvent(value);
120
71
  if (event !== undefined) {
121
- write({
122
- kind: 'frame',
123
- direction: 'inbound',
124
- clientId: event.clientId,
125
- type,
126
- payload: event.payload,
127
- });
72
+ writeFrame('in', type, event.payload);
128
73
  }
129
74
  }));
130
75
  }
@@ -134,30 +79,10 @@ export const attachBridgeEventLog = (bridge, projectDir, subscriptions, options
134
79
  continue;
135
80
  }
136
81
  subscriptions.add(channel.subscribe((payload) => {
137
- write({
138
- kind: 'frame',
139
- direction: 'outbound',
140
- scope: 'broadcast',
141
- type,
142
- payload,
143
- });
82
+ writeFrame('out', type, payload);
144
83
  }));
145
84
  }
146
- subscriptions.add(bridge.status$.subscribe((status) => {
147
- write({ kind: 'status', status });
148
- }));
149
- subscriptions.add(bridge.errors$.subscribe((error) => {
150
- write({
151
- kind: 'error',
152
- error: {
153
- code: error.code,
154
- message: error.message,
155
- ...('cause' in error ? { cause: error.cause } : {}),
156
- },
157
- });
158
- }));
159
85
  subscriptions.add(bridge.connections$.subscribe((client) => {
160
- write({ kind: 'connection', clientId: client.id });
161
86
  const clientSubscriptions = new Subscription();
162
87
  subscriptions.add(clientSubscriptions);
163
88
  const clientChannels = client;
@@ -167,29 +92,19 @@ export const attachBridgeEventLog = (bridge, projectDir, subscriptions, options
167
92
  continue;
168
93
  }
169
94
  clientSubscriptions.add(channel.subscribe((payload) => {
170
- write({
171
- kind: 'frame',
172
- direction: 'outbound',
173
- scope: 'client',
174
- clientId: client.id,
175
- type,
176
- payload,
177
- });
95
+ writeFrame('out', type, payload);
178
96
  }));
179
97
  }
180
98
  clientSubscriptions.add(client.disconnected$.subscribe(() => {
181
- write({ kind: 'disconnection', clientId: client.id });
182
99
  clientSubscriptions.unsubscribe();
183
100
  }));
184
101
  }));
185
- write({ kind: 'server-started' });
186
102
  return {
187
103
  filePath,
188
104
  setEnabled: (nextEnabled) => {
189
105
  enabled = nextEnabled;
190
106
  },
191
107
  writeServerClosing: () => {
192
- write({ kind: 'server-closing' });
193
108
  closed = true;
194
109
  },
195
110
  flush: async () => {
@@ -1,4 +1,5 @@
1
- import { type ExecutionContext } from '@langflower/node-sdk';
1
+ import type { LangflowerBusRequest } from '@langflower/common-nodes/ai/run-host-services';
2
+ import { type ExecutionContext, type ToolHandle } from '@langflower/node-sdk';
2
3
  import type { LlmExecutionCaps } from '@langflower/node-sdk/llm';
3
4
  import type { McpHandle } from '@langflower/node-sdk/mcp';
4
5
  import type { RuntimeSeedPortValue } from '@langflower/runtime';
@@ -16,6 +17,8 @@ type BuildHarnessHooks = {
16
17
  readonly nodeId: string;
17
18
  readonly requestPermission: LangflowerSession['permissionAsks']['requestPermission'];
18
19
  readonly emitPermissionAsk: (payload: RunnerPermissionAskPayload) => void;
20
+ readonly requestLangflowerBus?: LangflowerBusRequest;
21
+ readonly getLiveWiredTools?: (agentNodeId: string) => readonly ToolHandle[];
19
22
  };
20
23
  export declare const buildExecutionContext: (context: ExecutionContextDeps, runId: string, node: WorkflowGraphNode, hooks?: BuildHarnessHooks, preloadedConfig?: LangflowerConfig, runMcpHandles?: readonly McpHandle[]) => Promise<ExecutionContext<never, LlmExecutionCaps>>;
21
24
  /**
@@ -24,5 +27,5 @@ export declare const buildExecutionContext: (context: ExecutionContextDeps, runI
24
27
  * which would put the Observable in value$ instead of error$.
25
28
  */
26
29
  export declare const applyObservableContextSeeds: (session: LangflowerSession, seeds: Readonly<Record<string, ReadonlyArray<RuntimeSeedPortValue>>>) => Record<string, ReadonlyArray<RuntimeSeedPortValue>>;
27
- export declare const buildContextSeeds: (session: LangflowerSession, context: ExecutionContextDeps, runId: string, emitPermissionAsk: (payload: RunnerPermissionAskPayload) => void) => Promise<Record<string, ReadonlyArray<RuntimeSeedPortValue>>>;
30
+ export declare const buildContextSeeds: (session: LangflowerSession, context: ExecutionContextDeps, runId: string, emitPermissionAsk: (payload: RunnerPermissionAskPayload) => void, requestLangflowerBus?: LangflowerBusRequest, getLiveWiredTools?: (agentNodeId: string) => readonly ToolHandle[]) => Promise<Record<string, ReadonlyArray<RuntimeSeedPortValue>>>;
28
31
  export {};
@@ -28,6 +28,13 @@ const createToolHarness = (options) => {
28
28
  : {}),
29
29
  });
30
30
  };
31
+ const LIVE_WIRED_TOOLS_NODE_TYPES = new Set([
32
+ 'common-openai-llm',
33
+ 'common-fake-llm',
34
+ 'common-review',
35
+ 'common-critique',
36
+ 'common-sub-agent',
37
+ ]);
31
38
  export const buildExecutionContext = async (context, runId, node, hooks, preloadedConfig, runMcpHandles) => {
32
39
  const rolePreset = parseLlmRolePreset(node.params.rolePreset);
33
40
  const skillId = resolveEffectiveSkillId(rolePreset, node.params.skillId);
@@ -104,8 +111,28 @@ export const buildExecutionContext = async (context, runId, node, hooks, preload
104
111
  ...(config.harness?.allowedHosts !== undefined
105
112
  ? { allowedHosts: config.harness.allowedHosts }
106
113
  : {}),
114
+ ...(hooks?.requestLangflowerBus !== undefined &&
115
+ node.type === 'common-langflower-tools'
116
+ ? { requestLangflowerBus: hooks.requestLangflowerBus }
117
+ : {}),
118
+ ...(hooks?.getLiveWiredTools !== undefined &&
119
+ LIVE_WIRED_TOOLS_NODE_TYPES.has(node.type)
120
+ ? { getLiveWiredTools: hooks.getLiveWiredTools }
121
+ : {}),
107
122
  });
108
123
  };
124
+ /** Host `contextSymbol` vs compiled-pack duplicate (`file://` / Vitest alias). */
125
+ const contextPortLabel = String(contextSymbol);
126
+ const resolveLiveContextPortId = (session, nodeId) => {
127
+ const node = session.runtime.editor.getNode(nodeId);
128
+ if (node === false || node.inputs[contextSymbol] !== undefined) {
129
+ return contextSymbol;
130
+ }
131
+ const match = Object.getOwnPropertySymbols(node.inputs).find((portId) => String(portId) === contextPortLabel);
132
+ return match ?? contextSymbol;
133
+ };
134
+ const isContextSeedPort = (portId) => portId === contextSymbol ||
135
+ (typeof portId === 'symbol' && String(portId) === contextPortLabel);
109
136
  const ctxErrorFromFailures = (failures) => ({
110
137
  message: failures.map((failure) => failure.message).join('\n'),
111
138
  });
@@ -119,9 +146,10 @@ export const applyObservableContextSeeds = (session, seeds) => {
119
146
  for (const [nodeId, list] of Object.entries(seeds)) {
120
147
  const kept = [];
121
148
  for (const seed of list) {
122
- if (seed.portId === contextSymbol && isObservable(seed.value)) {
149
+ if (isContextSeedPort(seed.portId) && isObservable(seed.value)) {
123
150
  const node = session.runtime.editor.getNode(nodeId);
124
- const connection = node === false ? undefined : node.inputs[contextSymbol];
151
+ const portId = resolveLiveContextPortId(session, nodeId);
152
+ const connection = node === false ? undefined : node.inputs[portId];
125
153
  if (connection !== undefined) {
126
154
  connection.connect(seed.value);
127
155
  }
@@ -135,7 +163,7 @@ export const applyObservableContextSeeds = (session, seeds) => {
135
163
  }
136
164
  return valueSeeds;
137
165
  };
138
- export const buildContextSeeds = async (session, context, runId, emitPermissionAsk) => {
166
+ export const buildContextSeeds = async (session, context, runId, emitPermissionAsk, requestLangflowerBus, getLiveWiredTools) => {
139
167
  const workflow = session.activeWorkflow;
140
168
  if (workflow === null) {
141
169
  return {};
@@ -160,7 +188,7 @@ export const buildContextSeeds = async (session, context, runId, emitPermissionA
160
188
  const error = ctxErrorFromFailures(nodeFailures);
161
189
  seeds[node.id] = [
162
190
  {
163
- portId: contextSymbol,
191
+ portId: resolveLiveContextPortId(session, node.id),
164
192
  slotIndex: 0,
165
193
  value: throwError(() => error),
166
194
  },
@@ -172,8 +200,20 @@ export const buildContextSeeds = async (session, context, runId, emitPermissionA
172
200
  nodeId: node.id,
173
201
  requestPermission: session.permissionAsks.requestPermission,
174
202
  emitPermissionAsk,
203
+ ...(requestLangflowerBus !== undefined
204
+ ? { requestLangflowerBus }
205
+ : {}),
206
+ ...(getLiveWiredTools !== undefined
207
+ ? { getLiveWiredTools }
208
+ : {}),
175
209
  }, config, runMcp?.handles);
176
- seeds[node.id] = [{ portId: contextSymbol, slotIndex: 0, value: ctx }];
210
+ seeds[node.id] = [
211
+ {
212
+ portId: resolveLiveContextPortId(session, node.id),
213
+ slotIndex: 0,
214
+ value: ctx,
215
+ },
216
+ ];
177
217
  }
178
218
  return seeds;
179
219
  };
@@ -1,4 +1,4 @@
1
- import type { RuntimeRunnerEvent } from '@langflower/runtime';
1
+ import { type RuntimeRunnerEvent } from '@langflower/runtime';
2
2
  import type { LangflowerBridge } from './langflower-bridge.types.js';
3
3
  /** Fan-out runner telemetry to every connected tab (session-shared). */
4
4
  export declare const forwardRunnerEvent: (bridge: LangflowerBridge, event: RuntimeRunnerEvent) => void;
@@ -1,17 +1,12 @@
1
+ import { isPortTelemetry, isRuntimeDone, } from '@langflower/runtime';
1
2
  import { bridgeEmit } from './bridge-outbound.js';
2
3
  /** Fan-out runner telemetry to every connected tab (session-shared). */
3
4
  export const forwardRunnerEvent = (bridge, event) => {
4
- switch (event.kind) {
5
- case 'output-emitted':
6
- bridgeEmit(bridge, 'runner.output-emitted', event);
7
- break;
8
- case 'input-received':
9
- bridgeEmit(bridge, 'runner.input-received', event);
10
- break;
11
- case 'done':
12
- bridgeEmit(bridge, 'runner.done', event);
13
- break;
14
- default:
15
- break;
5
+ if (isPortTelemetry(event)) {
6
+ bridgeEmit(bridge, 'runner.port', event);
7
+ return;
8
+ }
9
+ if (isRuntimeDone(event)) {
10
+ bridgeEmit(bridge, 'runner.done', event);
16
11
  }
17
12
  };
@@ -0,0 +1,12 @@
1
+ import type { ToolHandle } from '@langflower/node-sdk';
2
+ import type { LangflowerSession } from '../session/langflower-session.js';
3
+ /**
4
+ * Activate current editor `tools` outputs (including post-swap instances)
5
+ * so a later inbound peek in {@link getLiveWiredTools} sees live handles.
6
+ */
7
+ export declare const refreshLiveWiredToolPacks: (session: LangflowerSession) => void;
8
+ /**
9
+ * Walk inbound `tools` edges on **current** editor instances (post-swap).
10
+ * Does not emit on the LLM tools port (ADR-016 session must not reset).
11
+ */
12
+ export declare const getLiveWiredTools: (session: LangflowerSession, agentNodeId: string) => readonly ToolHandle[];
@@ -0,0 +1,80 @@
1
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
2
+ const isToolHandle = (value) => {
3
+ if (!isRecord(value)) {
4
+ return false;
5
+ }
6
+ return (typeof value.toolId === 'string' &&
7
+ typeof value.name === 'string' &&
8
+ typeof value.description === 'string' &&
9
+ typeof value.inputSchema === 'object' &&
10
+ value.inputSchema !== null &&
11
+ typeof value.invoke === 'function');
12
+ };
13
+ /** Flatten multi-wire values that may be single handles or arrays (packs). */
14
+ const flattenToolHandles = (wired) => wired.flatMap((item) => {
15
+ if (Array.isArray(item)) {
16
+ return item.filter(isToolHandle);
17
+ }
18
+ if (isToolHandle(item)) {
19
+ return [item];
20
+ }
21
+ return [];
22
+ });
23
+ const resolveToolsOutput = (outputs, portId) => {
24
+ const direct = outputs[portId];
25
+ if (direct !== undefined) {
26
+ return direct;
27
+ }
28
+ return Object.values(outputs).find((port) => port.meta.portId === portId);
29
+ };
30
+ /**
31
+ * One-shot subscribe to activate a freshly swapped instance without
32
+ * connecting into the LLM `tools` input.
33
+ */
34
+ const peekPortValue = (port) => {
35
+ let peeked;
36
+ const sub = port.value$.subscribe((value) => {
37
+ peeked = value;
38
+ });
39
+ sub.unsubscribe();
40
+ return peeked;
41
+ };
42
+ /**
43
+ * Activate current editor `tools` outputs (including post-swap instances)
44
+ * so a later inbound peek in {@link getLiveWiredTools} sees live handles.
45
+ */
46
+ export const refreshLiveWiredToolPacks = (session) => {
47
+ for (const node of session.runtime.editor.getNodes()) {
48
+ const port = resolveToolsOutput(node.outputs, 'tools');
49
+ if (port !== undefined) {
50
+ peekPortValue(port);
51
+ }
52
+ }
53
+ };
54
+ /**
55
+ * Walk inbound `tools` edges on **current** editor instances (post-swap).
56
+ * Does not emit on the LLM tools port (ADR-016 session must not reset).
57
+ */
58
+ export const getLiveWiredTools = (session, agentNodeId) => {
59
+ const editor = session.runtime.editor;
60
+ const inbound = editor
61
+ .getEdges()
62
+ .filter((edge) => String(edge.toNodeId) === agentNodeId &&
63
+ edge.toPort[0] === 'tools');
64
+ const packs = [];
65
+ for (const edge of inbound) {
66
+ const source = editor.getNode(edge.fromNodeId);
67
+ if (source === false) {
68
+ continue;
69
+ }
70
+ const port = resolveToolsOutput(source.outputs, edge.fromPort[0]);
71
+ if (port === undefined) {
72
+ continue;
73
+ }
74
+ const peeked = peekPortValue(port);
75
+ if (peeked !== undefined) {
76
+ packs.push(peeked);
77
+ }
78
+ }
79
+ return flattenToolHandles(packs);
80
+ };
@@ -0,0 +1,7 @@
1
+ import type { LangflowerBusRequest } from '@langflower/common-nodes/ai/run-host-services';
2
+ import type { LangflowerBridge } from './langflower-bridge.types.js';
3
+ /**
4
+ * Wait-then-emit RPC (MCP `emitAction` shape) over the **server** bridge.
5
+ * Does not import `@langflower/mcp`.
6
+ */
7
+ export declare const createLangflowerToolsRpc: (bridge: LangflowerBridge) => LangflowerBusRequest;
@@ -0,0 +1,29 @@
1
+ import { waitBusEvent } from '@langflower/shared/langflower-ws-waits';
2
+ /**
3
+ * In-process bus intents this pack may emit. Compile only for now.
4
+ * Later: `editor.addNode.requested`, `editor.removeNode.requested`,
5
+ * `editor.addEdge.requested`, `editor.removeEdge.requested`.
6
+ */
7
+ const LANGFLOWER_TOOLS_INTENTS = ['customPalette.update.requested'];
8
+ const isAllowedIntent = (intent) => LANGFLOWER_TOOLS_INTENTS.includes(intent);
9
+ /**
10
+ * Wait-then-emit RPC (MCP `emitAction` shape) over the **server** bridge.
11
+ * Does not import `@langflower/mcp`.
12
+ */
13
+ export const createLangflowerToolsRpc = (bridge) => {
14
+ return async (intent, payload) => {
15
+ if (!isAllowedIntent(intent)) {
16
+ throw new Error(`Langflower Tools does not allow intent: ${intent}`);
17
+ }
18
+ const resultPromise = waitBusEvent(bridge['customPalette.snapshot'], {
19
+ predicate: (snapshot) => snapshot.status !== 'compiling',
20
+ });
21
+ const body = payload !== null &&
22
+ typeof payload === 'object' &&
23
+ !Array.isArray(payload)
24
+ ? payload
25
+ : {};
26
+ bridge.injectInbound(intent, body, 'langflower-tools');
27
+ return resultPromise;
28
+ };
29
+ };