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
@@ -8,6 +8,30 @@ import { applyDraftPatch, buildDraftSnapshot, providerIndexesWithBaseUrl, seedSc
8
8
  import { bridgeEmit, clientEmit } from './bridge-outbound.js';
9
9
  import { buildLangflowerConfigSnapshot } from './build-langflower-config-snapshot.js';
10
10
  const isScope = (value) => value === 'project' || value === 'global';
11
+ /** Fill missing/empty Save keys from the session draft (write-only apiKey). */
12
+ const mergeProviderApiKeysFromSession = (payload, sessionDraft) => {
13
+ if (payload.provider === undefined || sessionDraft === undefined) {
14
+ return payload.providerApiKeys;
15
+ }
16
+ const merged = {
17
+ ...(payload.providerApiKeys ?? {}),
18
+ };
19
+ for (const row of sessionDraft.providers) {
20
+ const id = row.id.trim();
21
+ if (id.length === 0) {
22
+ continue;
23
+ }
24
+ const fromPayload = merged[id]?.trim() ?? '';
25
+ if (fromPayload.length > 0) {
26
+ continue;
27
+ }
28
+ const pending = row.apiKey.trim();
29
+ if (pending.length > 0) {
30
+ merged[id] = pending;
31
+ }
32
+ }
33
+ return Object.keys(merged).length > 0 ? merged : payload.providerApiKeys;
34
+ };
11
35
  const layerForScope = (layers, scope) => (scope === 'project' ? layers.project : layers.global);
12
36
  export const createSettingsDraftController = (bridge, context, session) => {
13
37
  const probeGeneration = new Map();
@@ -144,15 +168,14 @@ export const createSettingsDraftController = (bridge, context, session) => {
144
168
  if (payload === undefined || !isScope(payload.scope)) {
145
169
  return null;
146
170
  }
171
+ const providerApiKeys = mergeProviderApiKeysFromSession(payload, state?.draft);
147
172
  const layers = await context.langflowerConfigService.writeSettings({
148
173
  scope: payload.scope,
149
174
  ...(payload.model !== undefined ? { model: payload.model } : {}),
150
175
  ...(payload.provider !== undefined
151
176
  ? { provider: payload.provider }
152
177
  : {}),
153
- ...(payload.providerApiKeys !== undefined
154
- ? { providerApiKeys: payload.providerApiKeys }
155
- : {}),
178
+ ...(providerApiKeys !== undefined ? { providerApiKeys } : {}),
156
179
  ...('serverLogs' in payload
157
180
  ? { serverLogs: payload.serverLogs }
158
181
  : {}),
@@ -1,4 +1,5 @@
1
1
  import type { Subscription } from 'rxjs';
2
2
  import type { ServerContext } from '../server-context.js';
3
+ import type { LangflowerSession } from '../session/langflower-session.js';
3
4
  import type { LangflowerBridge } from './langflower-bridge.types.js';
4
- export declare const wireCustomPaletteHandlers: (bridge: LangflowerBridge, context: ServerContext) => Subscription;
5
+ export declare const wireCustomPaletteHandlers: (bridge: LangflowerBridge, context: ServerContext, session: LangflowerSession) => Subscription;
@@ -1,10 +1,8 @@
1
- import { bridgeEmit } from './bridge-outbound.js';
1
+ import { compileAndHotSwapCustomNodes } from '../palette/compile-and-hot-swap-custom-nodes.js';
2
2
  import { isInboundEvent } from './inbound-guards.js';
3
- export const wireCustomPaletteHandlers = (bridge, context) => bridge['customPalette.update.requested'].subscribe(async (raw) => {
3
+ export const wireCustomPaletteHandlers = (bridge, context, session) => bridge['customPalette.update.requested'].subscribe(async (raw) => {
4
4
  if (!isInboundEvent(raw)) {
5
5
  return;
6
6
  }
7
- bridgeEmit(bridge, 'customPalette.snapshot', context.customPaletteService.compilingSnapshot());
8
- const snapshot = await context.customPaletteService.update(context.projectDir);
9
- bridgeEmit(bridge, 'customPalette.snapshot', snapshot);
7
+ await compileAndHotSwapCustomNodes(session, context, bridge);
10
8
  });
@@ -1,8 +1,11 @@
1
+ import { isPortTelemetry, isRuntimeDone, } from '@langflower/runtime';
1
2
  import { buildWorkflowFingerprint, } from '@langflower/shared/langflower.js';
2
3
  import { Subscription } from 'rxjs';
3
4
  import { listResumableCheckpoints } from '../checkpoint/list-resumable-checkpoints.js';
4
5
  import { resolveCheckpointBoundary } from '../checkpoint/resolve-checkpoint-boundary.js';
5
6
  import { buildContextSeeds, applyObservableContextSeeds, } from './build-execution-context.js';
7
+ import { getLiveWiredTools } from './get-live-wired-tools.js';
8
+ import { createLangflowerToolsRpc } from './langflower-tools-rpc.js';
6
9
  import { bridgeEmit, clientEmit } from './bridge-outbound.js';
7
10
  import { findClientById } from './client-index.js';
8
11
  import { isInboundEvent } from './inbound-guards.js';
@@ -35,11 +38,15 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
35
38
  const emitPermissionAsk = (payload) => {
36
39
  bridgeEmit(bridge, 'runner.permission.ask', payload);
37
40
  };
41
+ const requestLangflowerBus = createLangflowerToolsRpc(bridge);
42
+ const liveWiredTools = (agentNodeId) => getLiveWiredTools(session, agentNodeId);
38
43
  subscription.add(session.runtime.runner.events$.subscribe((event) => {
39
- const boundary = event.kind === 'output-emitted' &&
40
- typeof event.portId === 'string' &&
44
+ const [, nodeId, portId] = isPortTelemetry(event) ? event : [];
45
+ const boundary = isPortTelemetry(event) &&
46
+ event[0] === 'out' &&
47
+ typeof portId === 'string' &&
41
48
  session.activeWorkflow !== null
42
- ? resolveCheckpointBoundary(session.activeWorkflow, String(event.nodeId), event.portId)
49
+ ? resolveCheckpointBoundary(session.activeWorkflow, String(nodeId), portId)
43
50
  : undefined;
44
51
  const shouldPersist = checkpoints.observe(event, boundary === undefined
45
52
  ? undefined
@@ -57,7 +64,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
57
64
  })
58
65
  .catch(() => undefined);
59
66
  }
60
- if (event.kind === 'done') {
67
+ if (isRuntimeDone(event)) {
61
68
  void checkpoints
62
69
  .markCompleted()
63
70
  .then(async (summary) => {
@@ -83,7 +90,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
83
90
  crypto.randomUUID());
84
91
  // Lock before async seed build so workflow load cannot race in.
85
92
  session.runnerStatus = 'running';
86
- const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk));
93
+ const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk, requestLangflowerBus, liveWiredTools));
87
94
  const initialPayload = mergeSeeds(contextSeeds, clientInitialPayload);
88
95
  if (session.activeWorkflow !== null) {
89
96
  checkpoints.beginRun(resolvedRunId, session.activeWorkflow);
@@ -111,7 +118,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
111
118
  const resolvedRunId = (clientRunId ??
112
119
  crypto.randomUUID());
113
120
  session.runnerStatus = 'running';
114
- const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk));
121
+ const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk, requestLangflowerBus, liveWiredTools));
115
122
  const initialPayload = mergeSeeds(contextSeeds, clientInitialPayload);
116
123
  if (session.activeWorkflow !== null) {
117
124
  checkpoints.beginRun(resolvedRunId, session.activeWorkflow);
@@ -196,7 +203,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
196
203
  return;
197
204
  }
198
205
  session.runnerStatus = 'running';
199
- const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, checkpoint.runId, emitPermissionAsk));
206
+ const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, checkpoint.runId, emitPermissionAsk, requestLangflowerBus, liveWiredTools));
200
207
  checkpoints.hydrateFromCheckpoint(checkpoint, workflow);
201
208
  const resumeOptions = checkpoints.resumeOptionsFromCheckpoint(checkpoint);
202
209
  const resumed = session.runtime.runner.resume({
@@ -249,7 +256,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
249
256
  if (wasIdle) {
250
257
  const resolvedRunId = crypto.randomUUID();
251
258
  session.runnerStatus = 'running';
252
- const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk));
259
+ const contextSeeds = applyObservableContextSeeds(session, await buildContextSeeds(session, context, resolvedRunId, emitPermissionAsk, requestLangflowerBus, liveWiredTools));
253
260
  if (session.activeWorkflow !== null) {
254
261
  checkpoints.beginRun(resolvedRunId, session.activeWorkflow);
255
262
  }
@@ -1,4 +1,4 @@
1
- import type { NodeId, RunId, RuntimeRunnerEvent } from '@langflower/runtime';
1
+ import { type NodeId, type RunId, type RuntimeRunnerEvent } from '@langflower/runtime';
2
2
  import { type WorkflowCheckpoint, type WorkflowCheckpointSummary, type WorkflowLoadedPayload } from '@langflower/shared/langflower.js';
3
3
  import { WorkflowCheckpointStore } from './workflow-checkpoint-store.js';
4
4
  /**
@@ -1,3 +1,4 @@
1
+ import { isPortTelemetry, } from '@langflower/runtime';
1
2
  import { buildWorkflowFingerprint, toCheckpointJsonValue, } from '@langflower/shared/langflower.js';
2
3
  import { summarizeCheckpoint, WorkflowCheckpointStore, } from './workflow-checkpoint-store.js';
3
4
  /**
@@ -56,27 +57,27 @@ export class RunCheckpointSession {
56
57
  * that should trigger an immediate persist.
57
58
  */
58
59
  observe(event, boundary) {
59
- if (this.active === undefined || event.kind !== 'output-emitted') {
60
+ if (this.active === undefined ||
61
+ !isPortTelemetry(event) ||
62
+ event[0] !== 'out') {
60
63
  return false;
61
64
  }
62
- if (String(event.runId) !== this.active.runId) {
65
+ const [, nodeIdRaw, portId, state, value] = event;
66
+ if (state !== 'value' || typeof portId !== 'string') {
63
67
  return false;
64
68
  }
65
- if (event.state !== 'value' || typeof event.portId !== 'string') {
66
- return false;
67
- }
68
- const jsonValue = toCheckpointJsonValue(event.value);
69
+ const jsonValue = toCheckpointJsonValue(value);
69
70
  if (jsonValue === undefined) {
70
- this.unsupportedValue = `${event.nodeId}.${event.portId}`;
71
+ this.unsupportedValue = `${String(nodeIdRaw)}.${portId}`;
71
72
  return false;
72
73
  }
73
- const nodeId = String(event.nodeId);
74
+ const nodeId = String(nodeIdRaw);
74
75
  let ports = this.active.outputSnapshots.get(nodeId);
75
76
  if (ports === undefined) {
76
77
  ports = new Map();
77
78
  this.active.outputSnapshots.set(nodeId, ports);
78
79
  }
79
- ports.set(event.portId, {
80
+ ports.set(portId, {
80
81
  state: 'value',
81
82
  value: jsonValue,
82
83
  });
@@ -0,0 +1,10 @@
1
+ import type { CustomPaletteSnapshotPayload } from '@langflower/shared/langflower.js';
2
+ import type { LangflowerBridge } from '../bridge/langflower-bridge.types.js';
3
+ import type { ServerContext } from '../server-context.js';
4
+ import type { LangflowerSession } from '../session/langflower-session.js';
5
+ /**
6
+ * One composer for Custom → Update and `compile_custom_nodes` (via bus
7
+ * intent `customPalette.update.requested`): compile, registry, live swap,
8
+ * then palette snapshot (and deleteEdges if ports vanished).
9
+ */
10
+ export declare const compileAndHotSwapCustomNodes: (session: LangflowerSession, context: ServerContext, bridge: LangflowerBridge) => Promise<CustomPaletteSnapshotPayload>;
@@ -0,0 +1,23 @@
1
+ import { swapCustomNodesInEditor } from '../workflow/apply-editor-mutation.js';
2
+ import { refreshLiveWiredToolPacks } from '../bridge/get-live-wired-tools.js';
3
+ import { bridgeEmit } from '../bridge/bridge-outbound.js';
4
+ /**
5
+ * One composer for Custom → Update and `compile_custom_nodes` (via bus
6
+ * intent `customPalette.update.requested`): compile, registry, live swap,
7
+ * then palette snapshot (and deleteEdges if ports vanished).
8
+ */
9
+ export const compileAndHotSwapCustomNodes = async (session, context, bridge) => {
10
+ bridgeEmit(bridge, 'customPalette.snapshot', context.customPaletteService.compilingSnapshot());
11
+ const snapshot = await context.customPaletteService.update(context.projectDir);
12
+ const customTypes = new Set(snapshot.nodes.map((node) => node.type));
13
+ const droppedEdges = swapCustomNodesInEditor(session, context.projectDir, context.resolveDefinition, customTypes);
14
+ refreshLiveWiredToolPacks(session);
15
+ if (droppedEdges.length > 0) {
16
+ bridgeEmit(bridge, 'editor.deleteEdges', droppedEdges);
17
+ bridgeEmit(bridge, 'workflow.currentStatus.snapshot', {
18
+ status: session.currentStatus,
19
+ });
20
+ }
21
+ bridgeEmit(bridge, 'customPalette.snapshot', snapshot);
22
+ return snapshot;
23
+ };
@@ -44,6 +44,11 @@ export declare const applyEditorRemoveEdge: (session: LangflowerSession, edgeId:
44
44
  * Composer: snapshot → runtime remove → sync session topology from editor.
45
45
  */
46
46
  export declare const applyEditorRemoveNode: (session: LangflowerSession, nodeId: NodeId) => WorkflowNodePersisted[];
47
+ /**
48
+ * Replace live editor instances whose persisted `type` is in `customTypes`.
49
+ * Types missing from the set keep the old instance. Allowed while locked.
50
+ */
51
+ export declare const swapCustomNodesInEditor: (session: LangflowerSession, projectDir: string, resolveDefinition: ResolveNodeDefinition, customTypes: ReadonlySet<string>) => readonly RuntimeEdge[];
47
52
  export type BindWorkflowResult = {
48
53
  readonly ok: true;
49
54
  readonly droppedNodeIds: readonly string[];
@@ -447,6 +447,38 @@ export const applyEditorRemoveNode = (session, nodeId) => {
447
447
  syncActiveWorkflowTopologyFromEditor(session);
448
448
  return nodeSnapshot !== undefined ? [nodeSnapshot] : [];
449
449
  };
450
+ /**
451
+ * Replace live editor instances whose persisted `type` is in `customTypes`.
452
+ * Types missing from the set keep the old instance. Allowed while locked.
453
+ */
454
+ export const swapCustomNodesInEditor = (session, projectDir, resolveDefinition, customTypes) => {
455
+ const active = session.activeWorkflow;
456
+ if (active === null) {
457
+ return [];
458
+ }
459
+ const persistedById = new Map(active.graph.nodes.map((node) => [node.id, node]));
460
+ const droppedEdges = [];
461
+ for (const runtimeNode of session.runtime.editor.getNodes()) {
462
+ const persisted = persistedById.get(runtimeNode.nodeId);
463
+ if (persisted === undefined || !customTypes.has(persisted.type)) {
464
+ continue;
465
+ }
466
+ const next = materializeRuntimeNode(projectDir, persisted, resolveDefinition);
467
+ if (next === undefined) {
468
+ continue;
469
+ }
470
+ const { nodeId: _nodeId, ...rest } = next;
471
+ const swapped = session.runtime.editor.swapNode(runtimeNode.nodeId, rest);
472
+ if (swapped === false) {
473
+ continue;
474
+ }
475
+ droppedEdges.push(...swapped.droppedEdges);
476
+ }
477
+ if (droppedEdges.length > 0) {
478
+ syncActiveWorkflowTopologyFromEditor(session);
479
+ }
480
+ return droppedEdges;
481
+ };
450
482
  /**
451
483
  * Load path: document → editor (activate/rename/rebind).
452
484
  * Unsupported nodes and invalid edges are skipped (graceful degradation);
@@ -27,11 +27,15 @@ contract.
27
27
 
28
28
  ## Load custom nodes
29
29
 
30
- In the editor Custom section, click **Update** (or reconnect). Langflower
31
- typechecks each pack (`tsc --noEmit` when `tsconfig.json` is present), then
32
- esbuild-bundles entries that passed. On success nodes appear under **Custom**.
33
- Failures write `COMPILATION_ERRORS.md` in the pack without dropping other
34
- successful custom nodes.
30
+ Call **`compile_custom_nodes`** (no args) — on starter, Helper and Writer
31
+ already have it because **Langflower Tools** is wired — or, in the editor
32
+ Custom section, click **Update** (or reconnect). Same composer: typecheck
33
+ each pack (`tsc --noEmit` when `tsconfig.json` is present), esbuild-bundle
34
+ entries that passed, hot-swap live custom instances, refresh **Custom**.
35
+ Stop is not required for already-placed custom types. Already-wired custom
36
+ tools can be invoked later in the **same run**. New types are not auto-placed
37
+ or auto-wired mid-run. Failures write `COMPILATION_ERRORS.md` in the pack
38
+ without dropping other successful custom nodes.
35
39
 
36
40
  ## Gaps
37
41
 
@@ -199,9 +199,10 @@ even though Langflower bundles with esbuild for load.
199
199
  2. `export default defineNode({ … })`, `defineToolRegistrations({ … })` for an
200
200
  LLM `tools` pack, **or** `defineReactiveNode({ … })` when exclusive `ok` /
201
201
  `fail` (or other silent-branch) ports are required.
202
- 3. Run `npm install` in the pack if peers or deps changed.
203
- 4. In the editor Custom section, click **Update** (or reconnect) — the node
204
- appears under **Custom**.
202
+ 3. Run `npm install` in the pack if author deps changed.
203
+ 4. Call **`compile_custom_nodes`** (starter Helper / Writer) or Custom →
204
+ **Update**. The type appears under **Custom**. Already-placed instances
205
+ hot-swap (no Stop). Place **new** types on the canvas yourself.
205
206
 
206
207
  ## Second shareable pack
207
208
 
@@ -55,7 +55,17 @@
55
55
  },
56
56
  "options": {
57
57
  "type": "object",
58
- "additionalProperties": true
58
+ "additionalProperties": true,
59
+ "properties": {
60
+ "baseURL": {
61
+ "type": "string",
62
+ "description": "OpenAI-compatible API base URL (e.g. https://api.openai.com/v1 or http://127.0.0.1:1234/v1)."
63
+ },
64
+ "apiKey": {
65
+ "type": "string",
66
+ "description": "API key or `{env:VAR_NAME}` placeholder. Resolved on the server only; omitted from WebSocket snapshots. Prefer `{env:…}` over literals on disk."
67
+ }
68
+ }
59
69
  }
60
70
  }
61
71
  }
@@ -13,6 +13,10 @@ provider. They are past bootstrap; do not lead with CLI start or provider setup.
13
13
  ## Role and style
14
14
 
15
15
  - Short, actionable steps. No long essays.
16
+ - On **starter**, **Langflower Tools** is wired to you and Writer — you
17
+ already have **`compile_custom_nodes`**. After pack file changes, **call
18
+ it** (no arguments). Do not send the user to Custom → **Update** as the
19
+ only path.
16
20
  - Answer **only** from allowed fact sources: the **Companion docs** (via
17
21
  `read` when relevant) and the **Knowledge base** below. If a fact is
18
22
  missing, say **not in my facts** (or point at project use-case docs) —
@@ -35,8 +39,8 @@ Bootstrap and provider facts stay in the Knowledge base for when they ask.
35
39
  Example opening:
36
40
 
37
41
  > Glad you got Langflower running and a provider configured. I can explain what
38
- > Langflower can do and help you set up workflows and nodes for your needs.
39
- > What do you want to build?
42
+ > Langflower can do, help you set up workflows and custom nodes, and compile
43
+ > packs from this chat (`compile_custom_nodes`). What do you want to build?
40
44
 
41
45
  ## Companion docs
42
46
 
@@ -141,23 +145,28 @@ do not say coding pipelines “don’t exist” or are unavailable.
141
145
 
142
146
  ### 7. Run lifecycle
143
147
 
144
- | Mechanism | Meaning |
145
- | --------------------- | ----------------------------------------------------------------------------------------------------- |
146
- | Hard Stop | Cancel; does **not** by itself create checkpoint Continue |
147
- | Soft Pause | Per-node Steer pause of the last feed agent; siblings keep working; Send / Resume continues that node |
148
- | Browser close | If `langflower start` stays up, run continues; reopen live or settled |
149
- | Process kill / reboot | **Not** detachable resume |
150
- | Checkpoint Continue | Needs explicit boundary (`common-checkpoint` / `createCheckpoint`); **no** every-node auto Continue |
148
+ | Mechanism | Meaning |
149
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
150
+ | Hard Stop | Cancel; does **not** by itself create checkpoint Continue |
151
+ | Soft Pause | Per-node Steer pause of the last feed agent; siblings keep working; Send / Resume continues that node |
152
+ | Provider recovery | Idle: default **autokick** (abort, exponential backoff, full-store replay + kick). Dead-loop: abort + short `retryBaseDelayMs` wait (not idle backoff) + kick. 429/5xx/network: short transient budget, then autokick **wait** (no kick, no penalty). `'retry'` banner stays in the feed after reasoning/draft; does not open Steer. Off / cap exhausted → **Steer**. Auth/config stay fatal |
153
+ | Browser close | If `langflower start` stays up, run continues; reopen live or settled |
154
+ | Process kill / reboot | **Not** detachable resume |
155
+ | Checkpoint Continue | Needs explicit boundary (`common-checkpoint` / `createCheckpoint`); **no** every-node auto Continue |
151
156
 
152
157
  These four are **not** the same thing.
153
158
 
154
159
  ### 8. Tools, permissions, MCP
155
160
 
156
161
  - **Can:** Harness builtins + `permission.ask` Allow/Deny for those builtins.
157
- Wired pack / MCP tools (including memory writes) do **not** ask — authoring
158
- the edge is consent. Write Allow does **not** grant bash. Agents receive
159
- **McpHandle**s; system MCP starts only for ids enabled on nodes in the
160
- active workflow.
162
+ Wired pack / MCP tools (including memory writes and **Langflower Tools**)
163
+ do **not** ask — authoring the edge is consent. Write Allow does **not**
164
+ grant bash. Agents receive **McpHandle**s; system MCP starts only for ids
165
+ enabled on nodes in the active workflow.
166
+ - **Can:** `compile_custom_nodes` exists only when **Langflower Tools**
167
+ (`common-langflower-tools`) is wired into that agent’s `tools` port
168
+ (starter Helper / Writer). Same intent as Custom → **Update**. Recompile
169
+ is unsafe — not ambient on every agent.
161
170
  - **Can:** First-run `langflower.jsonc` seeds `permission` allow-all (project
162
171
  **floor**). Inspector **Tool permissions** table (`tool` / `deny` / `ask` /
163
172
  `allow`) sets per-agent `toolPermissions` (cannot loosen past floor). Presets
@@ -217,17 +226,38 @@ When the user asks to “create a project wiki”, “build a knowledge base”,
217
226
  ### 11. Custom nodes
218
227
 
219
228
  - **Can:** **TypeScript** via `@langflower/node-sdk` — first-class language;
220
- `tsc` / IDE types act as compile-time validators before Custom → **Update**.
221
- Packs under `.langflower/nodes/<pack>/`; user runs `npm install` in the pack.
222
- See `instructions.md` and `nodes/my-nodes/README.md`.
229
+ `tsc` / IDE types act as compile-time validators. After file changes call
230
+ **`compile_custom_nodes`** (no args) or Custom → **Update** — same
231
+ composer. On starter you already have the tool (Langflower Tools wired).
232
+ Stop is not required for already-placed custom types (hot-swap).
233
+ An already-wired custom tools pack can be invoked later in the **same run**
234
+ after compile. New types appear under **Custom**; Langflower does **not**
235
+ auto-place or auto-wire them mid-run.
236
+ Packs under `.langflower/nodes/<pack>/`; user runs `npm install` in the pack
237
+ when adding author deps. See `instructions.md` and `nodes/my-nodes/README.md`.
223
238
  Nodes may **intentionally keep in-memory internal state across runs**
224
239
  (Stop / done / Start) until the user loads another workflow or shuts down
225
240
  Langflower — not the same as Checkpoint resume after process kill.
226
241
  - **Cannot:** Plain JS as the authoring path, Go, Python, or other languages.
227
242
  Server auto-install. Sandboxed arbitrary user-node execution as shipped.
228
- Custom-pack compiler as product-Implementable.
229
- Claim that every node always resets on Stop, or that in-memory node state
230
- survives process restart without Checkpoints.
243
+ Ambient compile without Langflower Tools wired. Canvas add/remove node or
244
+ edge tools (not shipped). Claim that every node always resets on Stop, or
245
+ that in-memory node state survives process restart without Checkpoints.
246
+
247
+ ### 11a. Recipe — write / reload a custom node
248
+
249
+ When the user asks to add or change a custom node from this starter chat:
250
+
251
+ 1. Spawn Writer (or edit `.langflower/nodes/<pack>/*.ts` yourself).
252
+ 2. Call **`compile_custom_nodes`** (no args). Report `status` / `nodeTypes` /
253
+ `errors` from the tool text. Pack failures also write
254
+ `COMPILATION_ERRORS.md`.
255
+ 3. **Already on the canvas** → live now, no Stop. **New type** → user places
256
+ it from Custom and wires it; this tool does not drop nodes on the canvas.
257
+ 4. If a custom tools pack is already wired into this agent, you may call those
258
+ `toolId`s later in **this run** after compile.
259
+
260
+ Do **not** treat Custom → Update as the only reload path on starter.
231
261
 
232
262
  ### 12. Status vocabulary
233
263
 
@@ -57,11 +57,18 @@ Project product data for Langflower lives under **`.langflower/`**.
57
57
  - **HITL loops** (Review Gate, feedback ports, composer Start/Send) are normal
58
58
  **internal waiting state of nodes**, not a separate product “interrupt the
59
59
  engine” mechanism.
60
- - LLM provider idle/HTTP 5xx/network failures retry from the last committed
61
- round, then keep the node alive for **Steer / Resume** (recovery notices on
62
- the `recovery` port; suspend opens the Steer composer). Partial draft text is
63
- telemetry, not committed history. Authentication/configuration failures are
64
- terminal. Generic reload of any failed non-LLM node is not yet shipped.
60
+ - LLM provider idle **aborts** the in-flight request, then default
61
+ **autokick** (exponential backoff, full stored messages + kick user
62
+ turn). Dead-loop aborts the same way but waits **`retryBaseDelayMs`**
63
+ (not idle backoff) before that replay. HTTP 429 / 5xx / network use the
64
+ short transient budget, then join the idle autokick wait **without** a
65
+ kick turn or penalty bump. `'retry'` notices on the `recovery` port stay
66
+ in the feed after reasoning/draft, tick locally on the visit tail, and
67
+ do **not** open Steer. Autokick off or a finite cap exhausted keep the
68
+ node alive for **Steer / Resume** (`'suspended'`).
69
+ Partial draft text is telemetry, not committed history.
70
+ Authentication/configuration failures are terminal. Generic reload of any
71
+ failed non-LLM node is not yet shipped.
65
72
  - Also on the bus: runner start / resume / interrupt intents, `permission.ask`,
66
73
  and related facts.
67
74
 
@@ -80,11 +87,17 @@ Project product data for Langflower lives under **`.langflower/`**.
80
87
  ## Extensibility
81
88
 
82
89
  - Custom nodes use the same **`@langflower/node-sdk`** path as built-ins.
83
- - **TypeScript** is first-class; `tsc` / IDE types act as compile-time gates
84
- before Custom → **Update**.
90
+ - **TypeScript** is first-class; `tsc` / IDE types act as compile-time gates.
91
+ After file changes call `compile_custom_nodes` (only if **Langflower
92
+ Tools** is wired — starter Helper / Writer already are) or Custom →
93
+ **Update**. Same composer: typecheck + bundle + hot-swap live custom
94
+ instances + refresh the Custom palette.
95
+ Stop is not required for already-placed custom types.
96
+ An already-wired custom tools pack can be invoked later in the same run
97
+ after compile. Do not auto-place or auto-wire a new type mid-run.
85
98
  - Plain JS / Go / Python are not the authoring path. Sandboxed arbitrary
86
- user-node execution and a custom-pack compiler are **not** claimed as
87
- shipped Implementable here.
99
+ user-node execution is **not** shipped. Canvas add/remove node or edge
100
+ tools are **not** shipped (later rows on Langflower Tools).
88
101
 
89
102
  ## What stays local
90
103
 
@@ -17,21 +17,21 @@ Facts for UI chrome and on-disk paths. Prefer this file over guessing.
17
17
 
18
18
  ## Where can I…?
19
19
 
20
- | Goal | Where |
21
- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
22
- | Change provider / API key | Gear → **Settings** (right aside; Global auto-opens when none configured), or edit `.langflower/langflower.jsonc` (prefer `{env:VAR}`) |
23
- | Start a Chat Input graph | Composer **Start** — not plain **Run** (Run stays disabled for those graphs) |
24
- | See run output / stream | Right aside **feed** |
25
- | Answer HITL / send into a waiting node | Composer + feed context |
26
- | Allow or Deny `permission.ask` | Composer permission controls |
27
- | Soft-pause last feed agent / continue | Composer **Pause** (per-node) / Send or Resume (soft pause ≠ Hard Stop) |
28
- | Hard-stop a run | Composer **Stop** |
29
- | Edit selected node params / ports | Select node → right aside **inspector** (not Settings) |
30
- | Add or reload custom nodes | Put pack under `.langflower/nodes/<pack>/`, then Custom → **Update** |
31
- | Switch workflow | Topbar workflow list |
32
- | Open Settings without leaving canvas | Gear — Settings in right aside |
33
- | Find sample coding graphs | Skeleton / demo-project workflow files — **manual copy** into project, then load → Start. No Sample workflows catalog UI. |
34
- | Project instructions for agents | `.langflower/instructions.md` |
20
+ | Goal | Where |
21
+ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22
+ | Change provider / API key | Gear → **Settings** (right aside; Global auto-opens when none configured), or edit `.langflower/langflower.jsonc` (prefer `{env:VAR}`) |
23
+ | Start a Chat Input graph | Composer **Start** — not plain **Run** (Run stays disabled for those graphs) |
24
+ | See run output / stream | Right aside **feed** |
25
+ | Answer HITL / send into a waiting node | Composer + feed context |
26
+ | Allow or Deny `permission.ask` | Composer permission controls |
27
+ | Soft-pause last feed agent / continue | Composer **Pause** (per-node) / Send or Resume (soft pause ≠ Hard Stop) |
28
+ | Hard-stop a run | Composer **Stop** |
29
+ | Edit selected node params / ports | Select node → right aside **inspector** (not Settings) |
30
+ | Add or reload custom nodes | Pack under `.langflower/nodes/<pack>/`. On starter, Helper / Writer call `compile_custom_nodes` (Langflower Tools is wired). Else Custom → **Update**. Hot-swap already-placed types; already-wired custom tools work later in the same run. New types: user places from Custom (no auto-place / auto-wire mid-run). |
31
+ | Switch workflow | Topbar workflow list |
32
+ | Open Settings without leaving canvas | Gear — Settings in right aside |
33
+ | Find sample coding graphs | Skeleton / demo-project workflow files — **manual copy** into project, then load → Start. No Sample workflows catalog UI. |
34
+ | Project instructions for agents | `.langflower/instructions.md` |
35
35
 
36
36
  ## On-disk `.langflower/` map
37
37
 
@@ -3,7 +3,7 @@ name: Langflower node writer
3
3
  description: >-
4
4
  Guides authors writing project custom nodes under .langflower/nodes packs
5
5
  (defineNode / defineToolRegistrations / defineReactiveNode, ADR-030 layout,
6
- reload via Custom Update).
6
+ reload via compile_custom_nodes or Custom Update).
7
7
  ---
8
8
 
9
9
  # Langflower node writer
@@ -16,10 +16,14 @@ You help the user write **custom nodes** for Langflower.
16
16
  language; `tsc` / IDE types are the compile-time validators. **Not** plain JS
17
17
  as the authoring path, Go, Python, Rust, or other runtimes.
18
18
  - Do **not** claim sandboxed execution of arbitrary user-node code is shipped.
19
- - Do **not** claim a custom-pack compiler epic is product-Implementable unless
20
- the user’s docs say so — packs reload via Custom → **Update** /
21
- `COMPILATION_ERRORS.md` today.
19
+ - After file changes **call `compile_custom_nodes` yourself** (no args) — on
20
+ starter, Writer has it because **Langflower Tools** is wired. Custom →
21
+ **Update** is the same composer. Failures land in `COMPILATION_ERRORS.md`.
22
+ Do **not** only remind the user to click Update.
22
23
  - Langflower does **not** auto-`npm install` pack dependencies.
24
+ - Do **not** claim you can place a newly compiled type on the canvas or wire
25
+ it mid-run. Already-placed types hot-swap; already-wired custom tools can
26
+ be invoked later in the **same run**.
23
27
 
24
28
  ## Contract (do not invent forks)
25
29
 
@@ -34,8 +38,12 @@ You help the user write **custom nodes** for Langflower.
34
38
  Skip `*.test.ts`, `*.d.ts`, `dist/`, `node_modules/`.
35
39
  - Peer deps on the host SDK / RxJS are supplied by Langflower; author libs go
36
40
  in pack `dependencies`. The user runs `npm install` in the pack.
37
- - After file changes: Custom section → **Update**. Failures land in
38
- `COMPILATION_ERRORS.md` in that pack.
41
+ - After file changes: call **`compile_custom_nodes`** (wired on starter
42
+ Helper / Writer via **Langflower Tools** — not ambient on every agent) or
43
+ Custom section → **Update**. Stop is not required for already-placed
44
+ custom types. An already-wired custom tools pack can be invoked later in
45
+ the **same run** after compile. Failures land in `COMPILATION_ERRORS.md`
46
+ in that pack.
39
47
 
40
48
  ## When drafting a node
41
49
 
@@ -44,9 +52,11 @@ You help the user write **custom nodes** for Langflower.
44
52
  (`defineToolRegistrations` owns a `tools` output — no custom inputs).
45
53
  3. Implement `execute` (`defineNode`), `tools` handlers
46
54
  (`defineToolRegistrations`), or `bind` (`defineReactiveNode`).
47
- 4. Show the full file the user can paste into `my-nodes/`.
48
- 5. Remind them to Update Custom and fix compile errors from
49
- `COMPILATION_ERRORS.md` if present.
55
+ 4. Show the full file the user can paste into `my-nodes/` **or write it**
56
+ under `.langflower/nodes/<pack>/`.
57
+ 5. Call **`compile_custom_nodes`**. If the snapshot lists errors, `read`
58
+ that pack’s `COMPILATION_ERRORS.md`, fix, and compile again. Tell the
59
+ user to place **new** types from Custom; already-placed types are live.
50
60
 
51
61
  ## References in the project
52
62