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.
- package/package.json +1 -1
- package/ui-dist/{chunk-Y6VMBLSJ.js → chunk-7UMVYN3A.js} +1 -1
- package/ui-dist/{chunk-6DQ2XSRW.js → chunk-MDGXX73Z.js} +3 -3
- package/ui-dist/index.html +1 -1
- package/ui-dist/main-2JJGCNTD.js +313 -0
- package/vendor/common-nodes/dist/ai/chat-completion-stream.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/critique/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/critique/node.js +2 -2
- package/vendor/common-nodes/dist/ai/fake-llm/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/features/chat-completion-stream.d.ts +53 -0
- package/vendor/common-nodes/dist/ai/features/chat-completion-stream.js +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.js +17 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.js +113 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.js +194 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.d.ts +9 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.js +21 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.js +216 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.d.ts +125 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.js +39 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.js +59 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.js +7 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.d.ts +43 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.js +96 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.d.ts +32 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.js +58 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.d.ts +80 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.js +681 -0
- package/vendor/common-nodes/dist/ai/features/llm-role-preset.d.ts +69 -0
- package/vendor/common-nodes/dist/ai/features/llm-role-preset.js +265 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +148 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +198 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.d.ts +21 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.js +118 -0
- package/vendor/common-nodes/dist/ai/features/openai/context-length-error.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/openai/context-length-error.js +63 -0
- package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.d.ts +19 -0
- package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.js +182 -0
- package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.js +52 -0
- package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.d.ts +31 -0
- package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.js +326 -0
- package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.js +21 -0
- package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.d.ts +30 -0
- package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.js +132 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.d.ts +20 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.js +70 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.d.ts +35 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.js +106 -0
- package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.d.ts +9 -0
- package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.js +21 -0
- package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.js +23 -0
- package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.d.ts +10 -0
- package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.js +8 -0
- package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +54 -0
- package/vendor/common-nodes/dist/ai/features/run-host-services.js +29 -0
- package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.d.ts +17 -0
- package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.js +61 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.d.ts +39 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.js +43 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.js +18 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.d.ts +80 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.js +76 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.d.ts +113 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.js +131 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.d.ts +6 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.js +30 -0
- package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.js +17 -0
- package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.js +194 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-reducer.js +36 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.d.ts +35 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.js +19 -0
- package/vendor/common-nodes/dist/ai/llm-loop/normalize-llm-recovery-policy.js +52 -9
- package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.d.ts +8 -2
- package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.js +43 -13
- package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.d.ts +3 -5
- package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.js +116 -1
- package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.d.ts +82 -0
- package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.js +95 -0
- package/vendor/common-nodes/dist/ai/llm-session-shell.d.ts +4 -9
- package/vendor/common-nodes/dist/ai/llm-session-shell.js +2 -4
- package/vendor/common-nodes/dist/ai/nodes/critique/node.d.ts +588 -0
- package/vendor/common-nodes/dist/ai/nodes/critique/node.js +240 -0
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.d.ts +571 -0
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +208 -0
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.d.ts +587 -0
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +91 -0
- package/vendor/common-nodes/dist/ai/nodes/review/node.d.ts +593 -0
- package/vendor/common-nodes/dist/ai/nodes/review/node.js +240 -0
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.d.ts +630 -0
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +302 -0
- package/vendor/common-nodes/dist/ai/openai/create-chat-completion-stream.js +6 -0
- package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.js +18 -0
- package/vendor/common-nodes/dist/ai/openai-llm/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/review/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/review/node.js +2 -2
- package/vendor/common-nodes/dist/ai/sub-agent/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/sub-agent/node.js +2 -2
- package/vendor/common-nodes/dist/catalog.js +7 -5
- package/vendor/common-nodes/dist/compile-custom-nodes/node.d.ts +21 -0
- package/vendor/common-nodes/dist/compile-custom-nodes/node.js +45 -0
- package/vendor/common-nodes/dist/crawl/crawl/node.js +1 -1
- package/vendor/common-nodes/dist/crawl/fetch-url/node.js +1 -1
- package/vendor/common-nodes/dist/flow/repeat/node.js +6 -1
- package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.d.ts +18 -0
- package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.js +29 -0
- package/vendor/common-nodes/dist/langflower-tools/node.d.ts +20 -0
- package/vendor/common-nodes/dist/langflower-tools/node.js +77 -0
- package/vendor/common-nodes/dist/text/append-file/node.js +1 -1
- package/vendor/common-nodes/dist/text/read-file/node.js +1 -1
- package/vendor/common-nodes/dist/text/write-file/node.js +1 -1
- package/vendor/common-nodes/dist/tools/inventory-tool-round.d.ts +2 -2
- package/vendor/common-nodes/package.json +10 -10
- package/vendor/compiler/dist/compile-project-nodes.d.ts +2 -1
- package/vendor/compiler/dist/compile-project-nodes.js +35 -6
- package/vendor/compiler/dist/resolve-host-types.d.ts +0 -5
- package/vendor/compiler/dist/resolve-host-types.js +0 -8
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.d.ts +1 -1
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.js +1 -1
- package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.d.ts +8 -0
- package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.js +14 -0
- package/vendor/runtime/dist/runtime-editor.d.ts +5 -1
- package/vendor/runtime/dist/runtime-editor.js +19 -0
- package/vendor/runtime/dist/runtime-runner.js +17 -17
- package/vendor/runtime/dist/runtime.d.ts +2 -1
- package/vendor/runtime/dist/runtime.js +1 -0
- package/vendor/runtime/dist/testing/workflows/workflow-events.d.ts +8 -6
- package/vendor/runtime/dist/testing/workflows/workflow-events.js +19 -31
- package/vendor/runtime/dist/types.d.ts +33 -38
- package/vendor/runtime/dist/types.js +2 -1
- package/vendor/server/dist/bridge/attach-langflower-bridge.js +3 -2
- package/vendor/server/dist/bridge/bridge-event-log.js +17 -102
- package/vendor/server/dist/bridge/build-execution-context.d.ts +5 -2
- package/vendor/server/dist/bridge/build-execution-context.js +45 -5
- package/vendor/server/dist/bridge/forward-runner-event.d.ts +1 -1
- package/vendor/server/dist/bridge/forward-runner-event.js +7 -12
- package/vendor/server/dist/bridge/get-live-wired-tools.d.ts +12 -0
- package/vendor/server/dist/bridge/get-live-wired-tools.js +80 -0
- package/vendor/server/dist/bridge/langflower-tools-rpc.d.ts +7 -0
- package/vendor/server/dist/bridge/langflower-tools-rpc.js +29 -0
- package/vendor/server/dist/bridge/settings-draft-controller.js +26 -3
- package/vendor/server/dist/bridge/wire-custom-palette-handlers.d.ts +2 -1
- package/vendor/server/dist/bridge/wire-custom-palette-handlers.js +3 -5
- package/vendor/server/dist/bridge/wire-runner-handlers.js +15 -8
- package/vendor/server/dist/checkpoint/run-checkpoint-session.d.ts +1 -1
- package/vendor/server/dist/checkpoint/run-checkpoint-session.js +10 -9
- package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.d.ts +10 -0
- package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.js +23 -0
- package/vendor/server/dist/workflow/apply-editor-mutation.d.ts +5 -0
- package/vendor/server/dist/workflow/apply-editor-mutation.js +32 -0
- package/vendor/server/skeleton/instructions.md +9 -5
- package/vendor/server/skeleton/nodes/my-nodes/README.md +4 -3
- package/vendor/server/skeleton/schemas/langflower-config.schema.json +11 -1
- package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +49 -19
- package/vendor/server/skeleton/skills/langflower-helper/architecture.md +22 -9
- package/vendor/server/skeleton/skills/langflower-helper/layout.md +15 -15
- package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +19 -9
- package/vendor/server/skeleton/skills/langflower-workflow-writer/SKILL.md +14 -10
- package/vendor/server/skeleton/workflows/starter.json +30 -3
- package/vendor/shared/dist/execution/derive-run-settle-outcome.d.ts +1 -1
- package/vendor/shared/dist/execution/derive-run-settle-outcome.js +4 -3
- package/vendor/shared/dist/execution/derive-run-settle-outcome.test.js +9 -9
- package/vendor/shared/dist/langflower-bus-config.d.ts +6 -14
- package/vendor/shared/dist/langflower-bus-config.js +4 -12
- package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.js +6 -0
- package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.test.js +30 -0
- package/vendor/shared/dist/langflower-ws-waits.d.ts +7 -14
- package/vendor/shared/dist/langflower-ws-waits.js +14 -14
- package/vendor/websocket-bridge/dist/bridge-codec.d.ts +1 -0
- package/vendor/websocket-bridge/dist/bridge-codec.js +11 -11
- package/vendor/websocket-bridge/dist/bridge-frame.js +1 -1
- package/vendor/websocket-bridge/dist/bridge-subjects.d.ts +1 -1
- package/vendor/websocket-bridge/dist/bridge-subjects.js +2 -2
- package/vendor/websocket-bridge/dist/bridge-types.d.ts +13 -1
- package/vendor/websocket-bridge/dist/create-client.js +3 -3
- package/vendor/websocket-bridge/dist/create-server.js +12 -6
- package/vendor/websocket-bridge/package.json +8 -0
- package/ui-dist/main-SMO3L7U5.js +0 -311
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Instance role preset — one LLM node type, many configured roles. */
|
|
2
|
+
export type LlmRolePreset = 'custom' | 'plan' | 'coder' | 'explorer';
|
|
3
|
+
export type ToolPermissionDecision = 'allow' | 'ask' | 'deny';
|
|
4
|
+
/** Per-tool coarse decision on the LLM node (`params.toolPermissions`). */
|
|
5
|
+
export type ToolPermissionsMap = Readonly<Record<string, ToolPermissionDecision>>;
|
|
6
|
+
export type LlmRolePresetDefaults = {
|
|
7
|
+
readonly systemPrompt: string;
|
|
8
|
+
readonly skillId: string;
|
|
9
|
+
/** Visible Inspector table materialization for this preset. */
|
|
10
|
+
readonly toolPermissions: ToolPermissionsMap;
|
|
11
|
+
};
|
|
12
|
+
export declare const LLM_ROLE_PRESET_OPTIONS: readonly [{
|
|
13
|
+
readonly value: "custom";
|
|
14
|
+
readonly title: "Custom";
|
|
15
|
+
}, {
|
|
16
|
+
readonly value: "plan";
|
|
17
|
+
readonly title: "Plan";
|
|
18
|
+
}, {
|
|
19
|
+
readonly value: "coder";
|
|
20
|
+
readonly title: "Coder";
|
|
21
|
+
}, {
|
|
22
|
+
readonly value: "explorer";
|
|
23
|
+
readonly title: "Explorer";
|
|
24
|
+
}];
|
|
25
|
+
/**
|
|
26
|
+
* Harness builtin ids — twin of `@langflower/tools` `BUILTIN_TOOL_IDS` and
|
|
27
|
+
* shared `HARNESS_BUILTIN_TOOL_OPTIONS`. Kept local so UI can import this
|
|
28
|
+
* module without bundling Node builtins from tools.
|
|
29
|
+
*/
|
|
30
|
+
export declare const HARNESS_BUILTIN_TOOL_IDS: readonly ["read", "glob", "grep", "edit", "write", "create", "delete", "bash"];
|
|
31
|
+
export declare const PLAN_AGENT_SYSTEM_PROMPT: string;
|
|
32
|
+
export declare const CODER_AGENT_SYSTEM_PROMPT: string;
|
|
33
|
+
export declare const LLM_ROLE_PRESET_DEFAULTS: Readonly<Record<LlmRolePreset, LlmRolePresetDefaults>>;
|
|
34
|
+
export declare const parseLlmRolePreset: (value: unknown) => LlmRolePreset;
|
|
35
|
+
export declare const resolveEffectiveSkillId: (rolePreset: LlmRolePreset, skillIdParam: unknown) => string;
|
|
36
|
+
/** Parse `params.toolPermissions` into a validated map (empty if unset). */
|
|
37
|
+
export declare const parseToolPermissions: (value: unknown) => ToolPermissionsMap;
|
|
38
|
+
/**
|
|
39
|
+
* Migrate legacy `enabledToolIds` allowlist → coarse toolPermissions.
|
|
40
|
+
* Listed → allow (bash/delete → ask); unlisted builtins → deny.
|
|
41
|
+
*/
|
|
42
|
+
export declare const migrateEnabledToolIdsToPermissions: (enabledToolIds: readonly string[]) => ToolPermissionsMap;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve node toolPermissions: explicit map, else migrate enabledToolIds,
|
|
45
|
+
* else preset defaults for the current role.
|
|
46
|
+
*/
|
|
47
|
+
export declare const resolveEffectiveToolPermissions: (rolePreset: LlmRolePreset, toolPermissionsParam: unknown, enabledToolIdsParam?: unknown) => ToolPermissionsMap;
|
|
48
|
+
/** Inventory ids whose decision is not deny. */
|
|
49
|
+
export declare const toolPermissionsToEnabledIds: (toolPermissions: ToolPermissionsMap) => readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* Params patch when the author selects a role preset in the Inspector.
|
|
52
|
+
*
|
|
53
|
+
* - Writes `rolePreset` + materializes `toolPermissions`.
|
|
54
|
+
* - Removes legacy `enabledToolIds`.
|
|
55
|
+
* - Does **not** touch `skillId` / `systemPrompt`.
|
|
56
|
+
*/
|
|
57
|
+
export declare const paramsAfterRolePresetApply: (currentParams: Readonly<Record<string, unknown>>, rolePreset: LlmRolePreset) => Readonly<Record<string, unknown>>;
|
|
58
|
+
/**
|
|
59
|
+
* Project permission layer shape — structural twin of tools PermissionConfig
|
|
60
|
+
* (UI must not import `@langflower/tools`).
|
|
61
|
+
*/
|
|
62
|
+
export type ProjectPermissionConfig = Readonly<Record<string, ToolPermissionDecision | Readonly<Record<string, ToolPermissionDecision>>>>;
|
|
63
|
+
/** Coarse floor for Inspector (parity with tools `toolFloorDecision`). */
|
|
64
|
+
export declare const toolFloorDecisionForUi: (config: ProjectPermissionConfig | undefined, toolId: string) => ToolPermissionDecision;
|
|
65
|
+
export declare const validNodePermissionOptionsForUi: (floor: ToolPermissionDecision) => readonly ToolPermissionDecision[];
|
|
66
|
+
export declare const clampToolPermissionForUi: (floor: ToolPermissionDecision, node: ToolPermissionDecision) => ToolPermissionDecision;
|
|
67
|
+
export declare const isHarnessToolAlwaysDenied: (config: ProjectPermissionConfig | undefined, toolId: string) => boolean;
|
|
68
|
+
/** Opt-in: newly wired tool ids default to allow when missing. */
|
|
69
|
+
export declare const mergeToolPermissionsOnNewWires: (toolPermissions: ToolPermissionsMap, wiredToolIds: readonly string[]) => ToolPermissionsMap;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/** Instance role preset — one LLM node type, many configured roles. */
|
|
2
|
+
export const LLM_ROLE_PRESET_OPTIONS = [
|
|
3
|
+
{ value: 'custom', title: 'Custom' },
|
|
4
|
+
{ value: 'plan', title: 'Plan' },
|
|
5
|
+
{ value: 'coder', title: 'Coder' },
|
|
6
|
+
{ value: 'explorer', title: 'Explorer' },
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* Harness builtin ids — twin of `@langflower/tools` `BUILTIN_TOOL_IDS` and
|
|
10
|
+
* shared `HARNESS_BUILTIN_TOOL_OPTIONS`. Kept local so UI can import this
|
|
11
|
+
* module without bundling Node builtins from tools.
|
|
12
|
+
*/
|
|
13
|
+
export const HARNESS_BUILTIN_TOOL_IDS = [
|
|
14
|
+
'read',
|
|
15
|
+
'glob',
|
|
16
|
+
'grep',
|
|
17
|
+
'edit',
|
|
18
|
+
'write',
|
|
19
|
+
'create',
|
|
20
|
+
'delete',
|
|
21
|
+
'bash',
|
|
22
|
+
];
|
|
23
|
+
export const PLAN_AGENT_SYSTEM_PROMPT = [
|
|
24
|
+
'You are the Plan agent in a Langflower workflow.',
|
|
25
|
+
'',
|
|
26
|
+
"Understand the user's goal, explore the codebase read-only, and produce a clear",
|
|
27
|
+
'implementation plan. Do not modify source code or non-documentation files.',
|
|
28
|
+
'',
|
|
29
|
+
'Write plans in Markdown with sections: Goal, Context, Steps, Risks, Open questions.',
|
|
30
|
+
'',
|
|
31
|
+
'When requirements are ambiguous, use ask_user before finalizing the plan.',
|
|
32
|
+
].join('\n');
|
|
33
|
+
export const CODER_AGENT_SYSTEM_PROMPT = [
|
|
34
|
+
'You are the Coder agent in a Langflower workflow.',
|
|
35
|
+
'',
|
|
36
|
+
'Implement the requested changes in the project repository. Make minimal, correct',
|
|
37
|
+
'edits. Prefer precise file edits over large rewrites.',
|
|
38
|
+
'',
|
|
39
|
+
'When tests are available, run them to verify your work. Summarize what you changed',
|
|
40
|
+
'in your final response.',
|
|
41
|
+
].join('\n');
|
|
42
|
+
const EXPLORER_AGENT_SYSTEM_PROMPT = [
|
|
43
|
+
'You are the Explorer agent in a Langflower workflow.',
|
|
44
|
+
'',
|
|
45
|
+
'Research the topic using web_fetch. Synthesize findings into clear Markdown notes.',
|
|
46
|
+
'Do not modify application source code—only *.md research notes.',
|
|
47
|
+
'',
|
|
48
|
+
'Cite URLs. Separate facts from inference.',
|
|
49
|
+
].join('\n');
|
|
50
|
+
const allAllow = () => Object.fromEntries(HARNESS_BUILTIN_TOOL_IDS.map((id) => [id, 'allow']));
|
|
51
|
+
const CUSTOM_TOOL_PERMISSIONS = allAllow();
|
|
52
|
+
const CODER_TOOL_PERMISSIONS = {
|
|
53
|
+
...allAllow(),
|
|
54
|
+
bash: 'ask',
|
|
55
|
+
delete: 'ask',
|
|
56
|
+
};
|
|
57
|
+
const PLAN_TOOL_PERMISSIONS = {
|
|
58
|
+
...allAllow(),
|
|
59
|
+
write: 'ask',
|
|
60
|
+
create: 'ask',
|
|
61
|
+
edit: 'deny',
|
|
62
|
+
delete: 'deny',
|
|
63
|
+
bash: 'deny',
|
|
64
|
+
};
|
|
65
|
+
const EXPLORER_TOOL_PERMISSIONS = {
|
|
66
|
+
...allAllow(),
|
|
67
|
+
read: 'allow',
|
|
68
|
+
write: 'ask',
|
|
69
|
+
create: 'ask',
|
|
70
|
+
glob: 'deny',
|
|
71
|
+
grep: 'deny',
|
|
72
|
+
edit: 'deny',
|
|
73
|
+
delete: 'deny',
|
|
74
|
+
bash: 'deny',
|
|
75
|
+
};
|
|
76
|
+
export const LLM_ROLE_PRESET_DEFAULTS = {
|
|
77
|
+
custom: {
|
|
78
|
+
systemPrompt: '',
|
|
79
|
+
skillId: '',
|
|
80
|
+
toolPermissions: CUSTOM_TOOL_PERMISSIONS,
|
|
81
|
+
},
|
|
82
|
+
plan: {
|
|
83
|
+
systemPrompt: PLAN_AGENT_SYSTEM_PROMPT,
|
|
84
|
+
skillId: 'plan',
|
|
85
|
+
toolPermissions: PLAN_TOOL_PERMISSIONS,
|
|
86
|
+
},
|
|
87
|
+
coder: {
|
|
88
|
+
systemPrompt: CODER_AGENT_SYSTEM_PROMPT,
|
|
89
|
+
skillId: 'coder',
|
|
90
|
+
toolPermissions: CODER_TOOL_PERMISSIONS,
|
|
91
|
+
},
|
|
92
|
+
explorer: {
|
|
93
|
+
systemPrompt: EXPLORER_AGENT_SYSTEM_PROMPT,
|
|
94
|
+
skillId: 'explorer',
|
|
95
|
+
toolPermissions: EXPLORER_TOOL_PERMISSIONS,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export const parseLlmRolePreset = (value) => {
|
|
99
|
+
if (value === 'plan' || value === 'coder' || value === 'explorer') {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
return 'custom';
|
|
103
|
+
};
|
|
104
|
+
export const resolveEffectiveSkillId = (rolePreset, skillIdParam) => {
|
|
105
|
+
const explicit = typeof skillIdParam === 'string' ? skillIdParam.trim() : '';
|
|
106
|
+
if (explicit !== '') {
|
|
107
|
+
return explicit;
|
|
108
|
+
}
|
|
109
|
+
return LLM_ROLE_PRESET_DEFAULTS[rolePreset].skillId;
|
|
110
|
+
};
|
|
111
|
+
const isToolPermissionDecision = (value) => value === 'allow' || value === 'ask' || value === 'deny';
|
|
112
|
+
/** Parse `params.toolPermissions` into a validated map (empty if unset). */
|
|
113
|
+
export const parseToolPermissions = (value) => {
|
|
114
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
const entries = Object.entries(value).filter((entry) => isToolPermissionDecision(entry[1]));
|
|
118
|
+
return Object.fromEntries(entries);
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Migrate legacy `enabledToolIds` allowlist → coarse toolPermissions.
|
|
122
|
+
* Listed → allow (bash/delete → ask); unlisted builtins → deny.
|
|
123
|
+
*/
|
|
124
|
+
export const migrateEnabledToolIdsToPermissions = (enabledToolIds) => {
|
|
125
|
+
const allowed = new Set(enabledToolIds);
|
|
126
|
+
const next = {};
|
|
127
|
+
for (const id of HARNESS_BUILTIN_TOOL_IDS) {
|
|
128
|
+
if (!allowed.has(id)) {
|
|
129
|
+
next[id] = 'deny';
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
next[id] = id === 'bash' || id === 'delete' ? 'ask' : 'allow';
|
|
133
|
+
}
|
|
134
|
+
for (const id of enabledToolIds) {
|
|
135
|
+
if (!(id in next)) {
|
|
136
|
+
next[id] = 'allow';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return next;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Resolve node toolPermissions: explicit map, else migrate enabledToolIds,
|
|
143
|
+
* else preset defaults for the current role.
|
|
144
|
+
*/
|
|
145
|
+
export const resolveEffectiveToolPermissions = (rolePreset, toolPermissionsParam, enabledToolIdsParam) => {
|
|
146
|
+
const parsed = parseToolPermissions(toolPermissionsParam);
|
|
147
|
+
if (Object.keys(parsed).length > 0) {
|
|
148
|
+
return parsed;
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(enabledToolIdsParam)) {
|
|
151
|
+
return migrateEnabledToolIdsToPermissions(enabledToolIdsParam.map(String));
|
|
152
|
+
}
|
|
153
|
+
return LLM_ROLE_PRESET_DEFAULTS[rolePreset].toolPermissions;
|
|
154
|
+
};
|
|
155
|
+
/** Inventory ids whose decision is not deny. */
|
|
156
|
+
export const toolPermissionsToEnabledIds = (toolPermissions) => Object.entries(toolPermissions)
|
|
157
|
+
.filter(([, decision]) => decision !== 'deny')
|
|
158
|
+
.map(([toolId]) => toolId);
|
|
159
|
+
/**
|
|
160
|
+
* Params patch when the author selects a role preset in the Inspector.
|
|
161
|
+
*
|
|
162
|
+
* - Writes `rolePreset` + materializes `toolPermissions`.
|
|
163
|
+
* - Removes legacy `enabledToolIds`.
|
|
164
|
+
* - Does **not** touch `skillId` / `systemPrompt`.
|
|
165
|
+
*/
|
|
166
|
+
export const paramsAfterRolePresetApply = (currentParams, rolePreset) => {
|
|
167
|
+
const { enabledToolIds: _legacy, toolPermissions: _previous, ...rest } = currentParams;
|
|
168
|
+
const defaults = LLM_ROLE_PRESET_DEFAULTS[rolePreset];
|
|
169
|
+
return {
|
|
170
|
+
...rest,
|
|
171
|
+
rolePreset,
|
|
172
|
+
toolPermissions: { ...defaults.toolPermissions },
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
/** Defaults twin of `@langflower/tools` `DEFAULT_PERMISSION_CONFIG` (allow-all). */
|
|
176
|
+
const DEFAULT_HARNESS_PERMISSION = {
|
|
177
|
+
read: { '*': 'allow' },
|
|
178
|
+
glob: { '*': 'allow' },
|
|
179
|
+
grep: { '*': 'allow' },
|
|
180
|
+
edit: { '*': 'allow' },
|
|
181
|
+
write: { '*': 'allow' },
|
|
182
|
+
create: { '*': 'allow' },
|
|
183
|
+
delete: { '*': 'allow' },
|
|
184
|
+
bash: { '*': 'allow' },
|
|
185
|
+
};
|
|
186
|
+
const DECISION_RANK = {
|
|
187
|
+
deny: 3,
|
|
188
|
+
ask: 2,
|
|
189
|
+
allow: 1,
|
|
190
|
+
};
|
|
191
|
+
const rulesForTool = (config, toolId) => {
|
|
192
|
+
const raw = config[toolId] ?? DEFAULT_HARNESS_PERMISSION[toolId];
|
|
193
|
+
if (raw === undefined) {
|
|
194
|
+
return { '*': 'allow' };
|
|
195
|
+
}
|
|
196
|
+
if (isToolPermissionDecision(raw)) {
|
|
197
|
+
return { '*': raw };
|
|
198
|
+
}
|
|
199
|
+
const entries = Object.entries(raw).filter((entry) => isToolPermissionDecision(entry[1]));
|
|
200
|
+
if (entries.length === 0) {
|
|
201
|
+
return { '*': 'deny' };
|
|
202
|
+
}
|
|
203
|
+
return Object.fromEntries(entries);
|
|
204
|
+
};
|
|
205
|
+
/** Coarse floor for Inspector (parity with tools `toolFloorDecision`). */
|
|
206
|
+
export const toolFloorDecisionForUi = (config, toolId) => {
|
|
207
|
+
const merged = {
|
|
208
|
+
...DEFAULT_HARNESS_PERMISSION,
|
|
209
|
+
...(config ?? {}),
|
|
210
|
+
};
|
|
211
|
+
if (merged[toolId] === undefined &&
|
|
212
|
+
DEFAULT_HARNESS_PERMISSION[toolId] === undefined) {
|
|
213
|
+
return 'allow';
|
|
214
|
+
}
|
|
215
|
+
const rules = rulesForTool(merged, toolId);
|
|
216
|
+
const star = rules['*'];
|
|
217
|
+
if (star !== undefined && Object.keys(rules).length === 1) {
|
|
218
|
+
return star;
|
|
219
|
+
}
|
|
220
|
+
const decisions = Object.values(rules);
|
|
221
|
+
if (decisions.some((decision) => decision === 'allow')) {
|
|
222
|
+
return 'allow';
|
|
223
|
+
}
|
|
224
|
+
if (decisions.some((decision) => decision === 'ask')) {
|
|
225
|
+
return 'ask';
|
|
226
|
+
}
|
|
227
|
+
return 'deny';
|
|
228
|
+
};
|
|
229
|
+
export const validNodePermissionOptionsForUi = (floor) => {
|
|
230
|
+
if (floor === 'deny') {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (floor === 'ask') {
|
|
234
|
+
return ['deny', 'ask'];
|
|
235
|
+
}
|
|
236
|
+
return ['deny', 'ask', 'allow'];
|
|
237
|
+
};
|
|
238
|
+
export const clampToolPermissionForUi = (floor, node) => DECISION_RANK[floor] >= DECISION_RANK[node] ? floor : node;
|
|
239
|
+
export const isHarnessToolAlwaysDenied = (config, toolId) => {
|
|
240
|
+
const merged = {
|
|
241
|
+
...DEFAULT_HARNESS_PERMISSION,
|
|
242
|
+
...(config ?? {}),
|
|
243
|
+
};
|
|
244
|
+
if (merged[toolId] === undefined &&
|
|
245
|
+
DEFAULT_HARNESS_PERMISSION[toolId] === undefined) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const decisions = Object.values(rulesForTool(merged, toolId));
|
|
249
|
+
return (decisions.length > 0 &&
|
|
250
|
+
decisions.every((decision) => decision === 'deny'));
|
|
251
|
+
};
|
|
252
|
+
/** Opt-in: newly wired tool ids default to allow when missing. */
|
|
253
|
+
export const mergeToolPermissionsOnNewWires = (toolPermissions, wiredToolIds) => {
|
|
254
|
+
const next = {
|
|
255
|
+
...toolPermissions,
|
|
256
|
+
};
|
|
257
|
+
let changed = false;
|
|
258
|
+
for (const id of wiredToolIds) {
|
|
259
|
+
if (next[id] === undefined) {
|
|
260
|
+
next[id] = 'allow';
|
|
261
|
+
changed = true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return changed ? next : toolPermissions;
|
|
265
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ToolHandle, configureOutput, makeInput } from '@langflower/node-sdk';
|
|
2
|
+
import type { ToolHandlerContext } from '@langflower/tools/domain-tool-configs';
|
|
3
|
+
import type { PermissionAskRequest } from '@langflower/tools/permission';
|
|
4
|
+
import type { ChatCompletionMessage } from '../chat-completion-stream.js';
|
|
5
|
+
import { type LlmInventoryInputs, type LlmRecoveryNotice, type SteerControlPayload } from '@langflower/node-sdk/llm';
|
|
6
|
+
import type { PortMeta } from '@langflower/node-sdk';
|
|
7
|
+
import type { combineStatefulObservables, StatefulObservable } from '@rx-evo/stateful-observable';
|
|
8
|
+
import { type Observable, type OperatorFunction } from 'rxjs';
|
|
9
|
+
import { type LlmRolePreset } from '../llm-role-preset.js';
|
|
10
|
+
import { type SubAgentRegistration, type SubAgentSpawnPayload } from '../sub-agent-protocol.js';
|
|
11
|
+
import type { LlmCompactionConfig } from '../openai/normalize-compaction-params.js';
|
|
12
|
+
import type { LlmRecoveryPolicy } from '../llm-loop/llm-loop-types.js';
|
|
13
|
+
/** Shared inventory + panel fields assembled for OpenAI / Fake agent binds. */
|
|
14
|
+
export type LlmAgentInventoryContext = {
|
|
15
|
+
readonly prompt: string;
|
|
16
|
+
readonly tools: readonly ToolHandle[];
|
|
17
|
+
readonly getTools?: () => readonly ToolHandle[];
|
|
18
|
+
readonly subagentRegistrations: readonly SubAgentRegistration[];
|
|
19
|
+
readonly providerId: string;
|
|
20
|
+
readonly model: string;
|
|
21
|
+
readonly skillId: string;
|
|
22
|
+
readonly rolePreset: LlmRolePreset;
|
|
23
|
+
readonly effectiveSystemPrompt: string;
|
|
24
|
+
readonly skillMarkdown: string;
|
|
25
|
+
readonly agentsMarkdown: string;
|
|
26
|
+
readonly toolCtx: ToolHandlerContext;
|
|
27
|
+
readonly maxIterations: number;
|
|
28
|
+
/** Cap on feedback turns after turn 0; 0 = unlimited (Soft↔Hard guardrail). */
|
|
29
|
+
readonly maxFeedbackTurns: number;
|
|
30
|
+
/**
|
|
31
|
+
* HITL continue asks for storm caps (`agent.maxIterations` /
|
|
32
|
+
* `agent.maxFeedbackTurns`). Missing → Deny (prior stop behavior).
|
|
33
|
+
*/
|
|
34
|
+
readonly requestPermission?: (request: PermissionAskRequest) => Promise<'allow' | 'deny'>;
|
|
35
|
+
readonly compaction: LlmCompactionConfig;
|
|
36
|
+
readonly recovery: LlmRecoveryPolicy;
|
|
37
|
+
/** ADR-032 soft Pause / Steer — set in {@link bindLlmAgentSession}. */
|
|
38
|
+
readonly steerControl$?: Observable<SteerControlPayload>;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Prep for the shared LLM session cycle (ADR-016). All LLM nodes MUST use
|
|
42
|
+
* {@link createLlmSessionCycle$} — per-node cold-start history is forbidden.
|
|
43
|
+
*/
|
|
44
|
+
export type LlmFeedbackSessionPrep<Session = undefined> = {
|
|
45
|
+
readonly history: readonly ChatCompletionMessage[];
|
|
46
|
+
/** Append `{ role: assistant, content }` when a `response` chunk arrives. */
|
|
47
|
+
readonly trackAssistantHistory: boolean;
|
|
48
|
+
/** Append turn payload as `{ role: user }` before feedback turns (>0). */
|
|
49
|
+
readonly appendUserFeedbackToHistory: boolean;
|
|
50
|
+
readonly session: Session;
|
|
51
|
+
};
|
|
52
|
+
export type LlmSessionCycleOptions = {
|
|
53
|
+
/**
|
|
54
|
+
* When true (default, Soft↔Hard / openai-llm), prime turn 0 with
|
|
55
|
+
* `startWith('')` and call `runTurn` with `turnPayload === undefined`.
|
|
56
|
+
* When false (Critique `packet` / Review `result`), the first non-empty
|
|
57
|
+
* turn$ value is turn 0's payload — no empty prime.
|
|
58
|
+
*/
|
|
59
|
+
readonly primeTurn0?: boolean;
|
|
60
|
+
};
|
|
61
|
+
/** Standard agent cycle chunk kinds demuxed to OpenAI / Fake ports. */
|
|
62
|
+
type StandardLlmAgentChunk = {
|
|
63
|
+
readonly kind: 'reasoning';
|
|
64
|
+
readonly text: string;
|
|
65
|
+
} | {
|
|
66
|
+
readonly kind: 'draftResponse';
|
|
67
|
+
readonly text: string;
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: 'toolLog';
|
|
70
|
+
readonly text: string;
|
|
71
|
+
} | ({
|
|
72
|
+
readonly kind: 'recoveryNotice';
|
|
73
|
+
} & LlmRecoveryNotice) | {
|
|
74
|
+
readonly kind: 'response';
|
|
75
|
+
readonly text: string;
|
|
76
|
+
} | {
|
|
77
|
+
readonly kind: 'historySync';
|
|
78
|
+
readonly messages: readonly ChatCompletionMessage[];
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: 'subagentSpawn';
|
|
81
|
+
readonly payload: SubAgentSpawnPayload;
|
|
82
|
+
};
|
|
83
|
+
type LlmAgentEcSlice = {
|
|
84
|
+
readonly projectDir: string;
|
|
85
|
+
readonly runId: string;
|
|
86
|
+
readonly nodeId: string;
|
|
87
|
+
readonly params: Readonly<Record<string, unknown>>;
|
|
88
|
+
readonly toolHandles?: readonly ToolHandle[];
|
|
89
|
+
readonly mcpHandles?: readonly import('@langflower/node-sdk/mcp').McpHandle[];
|
|
90
|
+
};
|
|
91
|
+
type ReactiveBindHelpers = {
|
|
92
|
+
readonly makeInput: typeof makeInput;
|
|
93
|
+
readonly configureOutput: typeof configureOutput;
|
|
94
|
+
readonly combineInputs: typeof combineStatefulObservables;
|
|
95
|
+
};
|
|
96
|
+
/** 0 = unlimited; positive = max feedback turns after turn 0. */
|
|
97
|
+
export declare const normalizeMaxFeedbackTurns: (value: unknown) => number;
|
|
98
|
+
export declare const appendToolInventory: (systemPrompt: string, tools: readonly ToolHandle[]) => string;
|
|
99
|
+
/**
|
|
100
|
+
* RxJS operator: filter session chunks by `kind` and project the payload.
|
|
101
|
+
* Use inside `pipeValue` — do not wrap as `(cycle$) => cycle$.pipeValue(...)`.
|
|
102
|
+
*
|
|
103
|
+
* `@rx-evo` wraps values in `NoInfer`, which breaks `Extract` type guards —
|
|
104
|
+
* callers narrow with a cast inside `project`.
|
|
105
|
+
*/
|
|
106
|
+
export declare const demuxByKind: <Out>(kind: string, project: (chunk: {
|
|
107
|
+
readonly kind: string;
|
|
108
|
+
}) => Out) => OperatorFunction<{
|
|
109
|
+
readonly kind: string;
|
|
110
|
+
}, Out>;
|
|
111
|
+
/**
|
|
112
|
+
* Shared LLM session cycle (ADR-016): init context recreates the session;
|
|
113
|
+
* turn$ drives turn 0 + later turns with accumulated `messages[]`.
|
|
114
|
+
*
|
|
115
|
+
* All LLM nodes (openai / fake / critique / review / future) MUST use this —
|
|
116
|
+
* do not invent per-node cold-start `switchMap` history.
|
|
117
|
+
*/
|
|
118
|
+
export declare const createLlmSessionCycle$: <Context extends {
|
|
119
|
+
readonly maxFeedbackTurns: number;
|
|
120
|
+
readonly requestPermission?: (request: PermissionAskRequest) => Promise<"allow" | "deny">;
|
|
121
|
+
}, Chunk extends {
|
|
122
|
+
readonly kind: string;
|
|
123
|
+
}, Session, Deps, Meta>(context$: StatefulObservable<Context, Deps, Meta>, turn$: Observable<unknown>, prepareSession: (context: Context) => LlmFeedbackSessionPrep<Session>, runTurn: (context: Context, turnPayload: unknown, history: readonly ChatCompletionMessage[], session: Session) => Observable<Chunk>, options?: LlmSessionCycleOptions) => StatefulObservable<Chunk, Deps, Meta>;
|
|
124
|
+
type BindLlmAgentSessionOptions<Context extends LlmAgentInventoryContext, Chunk extends StandardLlmAgentChunk, Session> = {
|
|
125
|
+
readonly extendContext: (base: LlmAgentInventoryContext, ec: LlmAgentEcSlice) => Context;
|
|
126
|
+
readonly prepareSession: (context: Context) => LlmFeedbackSessionPrep<Session>;
|
|
127
|
+
readonly runTurn: (context: Context, feedback: string | undefined, history: readonly ChatCompletionMessage[], subagentResult$: Observable<unknown>, session: Session) => Observable<Chunk>;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Composer entry for OpenAI / Fake agent binds.
|
|
131
|
+
*
|
|
132
|
+
* Order:
|
|
133
|
+
* 1. make role inputs (userPrompt / systemPrompt / feedback)
|
|
134
|
+
* 2. assemble inventory context$ (+ provider extend)
|
|
135
|
+
* 3. ADR-016 feedback cycle$
|
|
136
|
+
* 4. demux chunk ports
|
|
137
|
+
* 5. configure standard outputs
|
|
138
|
+
*/
|
|
139
|
+
export declare const bindLlmAgentSession: <Context extends LlmAgentInventoryContext, Chunk extends StandardLlmAgentChunk, Session = undefined>(ctx: StatefulObservable<LlmAgentEcSlice, unknown, PortMeta>, helpers: ReactiveBindHelpers, inventory: LlmInventoryInputs, options: BindLlmAgentSessionOptions<Context, Chunk, Session>) => {
|
|
140
|
+
inputs: import("@rx-evo/stateful-observable").StatefulConnection<string, unknown, import("@langflower/node-sdk").InputPortMeta<string>>[];
|
|
141
|
+
outputs: StatefulObservable<string, unknown, import("@langflower/node-sdk").OutputParams & PortMeta>[];
|
|
142
|
+
inventoryOutputs: {
|
|
143
|
+
toolLog$: StatefulObservable<string, [unknown, unknown, unknown, unknown, unknown, unknown], undefined>;
|
|
144
|
+
recovery$: StatefulObservable<LlmRecoveryNotice, [unknown, unknown, unknown, unknown, unknown, unknown], undefined>;
|
|
145
|
+
subagent$: StatefulObservable<SubAgentSpawnPayload, [unknown, unknown, unknown, unknown, unknown, unknown], undefined>;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
export {};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { toLlmRecoveryPortValue, } from '@langflower/node-sdk/llm';
|
|
2
|
+
import { filter, map, pipe, shareReplay, switchMap, } from 'rxjs';
|
|
3
|
+
import { buildEffectiveSystemPrompt } from '../prompt/build-effective-system-prompt.js';
|
|
4
|
+
import { collectAgentToolHandles } from '../../../tools/collect-agent-tool-handles.js';
|
|
5
|
+
import { parseLlmRolePreset, resolveEffectiveSkillId, } from '../llm-role-preset.js';
|
|
6
|
+
import { flattenSubAgentRegistrations, } from '../sub-agent-protocol.js';
|
|
7
|
+
import { normalizeCompactionConfig } from '../openai/normalize-compaction-params.js';
|
|
8
|
+
import { resolveChatProviderModel } from '../prompt/resolve-chat-provider-model.js';
|
|
9
|
+
import { buildAgentToolCtx, getRunHostServices } from '../run-host-services.js';
|
|
10
|
+
import { AGENT_MAX_ITERATIONS_CAP, DEFAULT_AGENT_MAX_ITERATIONS, normalizeMaxIterations as normalizeMaxIterationsParam, } from '../prompt/normalize-max-iterations.js';
|
|
11
|
+
import { normalizeLlmRecoveryPolicy } from '../llm-loop/normalize-llm-recovery-policy.js';
|
|
12
|
+
import { runLlmSessionMachine } from './run-session-machine.js';
|
|
13
|
+
const asPortList = (value) => {
|
|
14
|
+
if (value === undefined || value === null) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return Array.isArray(value) ? value : [value];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Close over live editor wired tools when the server hook is present;
|
|
21
|
+
* otherwise freeze the combineInputs snapshot (unit tests).
|
|
22
|
+
*/
|
|
23
|
+
const createAgentGetTools = (ec, toolList, mcpList) => {
|
|
24
|
+
const hostServices = getRunHostServices(ec);
|
|
25
|
+
return () => {
|
|
26
|
+
const live = hostServices?.getLiveWiredTools?.(ec.nodeId);
|
|
27
|
+
return collectAgentToolHandles({
|
|
28
|
+
toolHandles: ec.toolHandles,
|
|
29
|
+
toolsPort: live === undefined
|
|
30
|
+
? toolList
|
|
31
|
+
: [...asPortList(toolList), ...live],
|
|
32
|
+
mcpHandles: ec.mcpHandles,
|
|
33
|
+
mcpPort: mcpList,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const toolLabel = (tool) => tool.name.length > 0 ? tool.name : tool.toolId || 'unknown';
|
|
38
|
+
const formatList = (items, labelOf) => (items.length === 0 ? 'none' : items.map(labelOf).join(', '));
|
|
39
|
+
/** 0 = unlimited; positive = max feedback turns after turn 0. */
|
|
40
|
+
export const normalizeMaxFeedbackTurns = (value) => {
|
|
41
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
42
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
return Math.min(AGENT_MAX_ITERATIONS_CAP, Math.floor(n));
|
|
46
|
+
};
|
|
47
|
+
export const appendToolInventory = (systemPrompt, tools) => {
|
|
48
|
+
if (tools.length === 0) {
|
|
49
|
+
return systemPrompt;
|
|
50
|
+
}
|
|
51
|
+
return [
|
|
52
|
+
systemPrompt,
|
|
53
|
+
`Available tools (callable via internal tool loop): ${formatList(tools, toolLabel)}.`,
|
|
54
|
+
]
|
|
55
|
+
.filter((part) => part.trim().length > 0)
|
|
56
|
+
.join('\n\n');
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Step: merge prompt / inventory / role panel into the shared agent context.
|
|
60
|
+
*/
|
|
61
|
+
const assembleLlmAgentInventoryContext = (prompt, toolList, subagentList, mcpList, systemPromptValue, ec) => {
|
|
62
|
+
const rolePreset = parseLlmRolePreset(ec.params.rolePreset);
|
|
63
|
+
const skillId = resolveEffectiveSkillId(rolePreset, ec.params.skillId);
|
|
64
|
+
const hostServices = getRunHostServices(ec);
|
|
65
|
+
const skillMarkdown = hostServices?.skillMarkdown ?? '';
|
|
66
|
+
const agentsMarkdown = hostServices?.agentsMarkdown ?? '';
|
|
67
|
+
const getTools = createAgentGetTools(ec, toolList, mcpList);
|
|
68
|
+
return {
|
|
69
|
+
prompt: String(prompt ?? ''),
|
|
70
|
+
tools: getTools(),
|
|
71
|
+
getTools,
|
|
72
|
+
subagentRegistrations: flattenSubAgentRegistrations(subagentList),
|
|
73
|
+
...resolveChatProviderModel(ec.params, hostServices),
|
|
74
|
+
skillId,
|
|
75
|
+
rolePreset,
|
|
76
|
+
skillMarkdown,
|
|
77
|
+
agentsMarkdown,
|
|
78
|
+
effectiveSystemPrompt: buildEffectiveSystemPrompt({
|
|
79
|
+
rolePreset,
|
|
80
|
+
systemPromptInput: String(systemPromptValue ?? ''),
|
|
81
|
+
agentsMarkdown,
|
|
82
|
+
skillMarkdown,
|
|
83
|
+
}),
|
|
84
|
+
toolCtx: buildAgentToolCtx({ projectDir: ec.projectDir, runId: ec.runId }, hostServices),
|
|
85
|
+
maxIterations: normalizeMaxIterationsParam(ec.params.maxIterations, {
|
|
86
|
+
fallback: DEFAULT_AGENT_MAX_ITERATIONS,
|
|
87
|
+
maxCap: AGENT_MAX_ITERATIONS_CAP,
|
|
88
|
+
}),
|
|
89
|
+
maxFeedbackTurns: normalizeMaxFeedbackTurns(ec.params.maxFeedbackTurns),
|
|
90
|
+
...(hostServices?.requestPermission !== undefined
|
|
91
|
+
? { requestPermission: hostServices.requestPermission }
|
|
92
|
+
: {}),
|
|
93
|
+
compaction: normalizeCompactionConfig(ec.params),
|
|
94
|
+
recovery: normalizeLlmRecoveryPolicy(ec.params),
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* RxJS operator: filter session chunks by `kind` and project the payload.
|
|
99
|
+
* Use inside `pipeValue` — do not wrap as `(cycle$) => cycle$.pipeValue(...)`.
|
|
100
|
+
*
|
|
101
|
+
* `@rx-evo` wraps values in `NoInfer`, which breaks `Extract` type guards —
|
|
102
|
+
* callers narrow with a cast inside `project`.
|
|
103
|
+
*/
|
|
104
|
+
export const demuxByKind = (kind, project) => pipe(filter((chunk) => chunk.kind === kind), map(project));
|
|
105
|
+
const asKind = (chunk) => chunk;
|
|
106
|
+
const demuxLlmAgentPorts = (cycle$) => ({
|
|
107
|
+
reasoning$: cycle$.pipeValue(demuxByKind('reasoning', (chunk) => asKind(chunk).text)),
|
|
108
|
+
draftResponse$: cycle$.pipeValue(demuxByKind('draftResponse', (chunk) => asKind(chunk).text)),
|
|
109
|
+
toolLog$: cycle$.pipeValue(demuxByKind('toolLog', (chunk) => asKind(chunk).text)),
|
|
110
|
+
recovery$: cycle$.pipeValue(demuxByKind('recoveryNotice', (chunk) => {
|
|
111
|
+
const notice = asKind(chunk);
|
|
112
|
+
return toLlmRecoveryPortValue(notice);
|
|
113
|
+
})),
|
|
114
|
+
response$: cycle$.pipeValue(demuxByKind('response', (chunk) => asKind(chunk).text)),
|
|
115
|
+
subagent$: cycle$.pipeValue(demuxByKind('subagentSpawn', (chunk) => asKind(chunk).payload)),
|
|
116
|
+
});
|
|
117
|
+
/**
|
|
118
|
+
* Shared LLM session cycle (ADR-016): init context recreates the session;
|
|
119
|
+
* turn$ drives turn 0 + later turns with accumulated `messages[]`.
|
|
120
|
+
*
|
|
121
|
+
* All LLM nodes (openai / fake / critique / review / future) MUST use this —
|
|
122
|
+
* do not invent per-node cold-start `switchMap` history.
|
|
123
|
+
*/
|
|
124
|
+
export const createLlmSessionCycle$ = (context$, turn$, prepareSession, runTurn, options) => {
|
|
125
|
+
const primeTurn0 = options?.primeTurn0 ?? true;
|
|
126
|
+
return context$
|
|
127
|
+
.pipeValue(switchMap((context) => {
|
|
128
|
+
const prep = prepareSession(context);
|
|
129
|
+
return runLlmSessionMachine(context, turn$, prep, runTurn, primeTurn0);
|
|
130
|
+
}))
|
|
131
|
+
.pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Composer entry for OpenAI / Fake agent binds.
|
|
135
|
+
*
|
|
136
|
+
* Order:
|
|
137
|
+
* 1. make role inputs (userPrompt / systemPrompt / feedback)
|
|
138
|
+
* 2. assemble inventory context$ (+ provider extend)
|
|
139
|
+
* 3. ADR-016 feedback cycle$
|
|
140
|
+
* 4. demux chunk ports
|
|
141
|
+
* 5. configure standard outputs
|
|
142
|
+
*/
|
|
143
|
+
export const bindLlmAgentSession = (ctx, helpers, inventory, options) => {
|
|
144
|
+
const { makeInput, configureOutput, combineInputs } = helpers;
|
|
145
|
+
const { tools, mcp, subagentRegistration, subagentResult, steerControl } = inventory;
|
|
146
|
+
// 1. role inputs
|
|
147
|
+
const userPrompt = makeInput('userPrompt', {
|
|
148
|
+
name: 'userPrompt',
|
|
149
|
+
wireType: 'string',
|
|
150
|
+
inline: 'text-multiline',
|
|
151
|
+
required: true,
|
|
152
|
+
});
|
|
153
|
+
const systemPrompt = makeInput('systemPrompt', {
|
|
154
|
+
name: 'systemPrompt',
|
|
155
|
+
wireType: 'string',
|
|
156
|
+
inline: 'text-multiline',
|
|
157
|
+
defaultValue: '',
|
|
158
|
+
});
|
|
159
|
+
const feedback = makeInput('feedback', {
|
|
160
|
+
name: 'feedback',
|
|
161
|
+
wireType: 'string',
|
|
162
|
+
multi: 'merge',
|
|
163
|
+
defaultValue: '',
|
|
164
|
+
});
|
|
165
|
+
// 2. inventory context$
|
|
166
|
+
const context$ = combineInputs([userPrompt, tools, subagentRegistration, mcp, systemPrompt, ctx], ([prompt, toolList, subagentList, mcpList, systemPromptValue, ec]) => {
|
|
167
|
+
const base = assembleLlmAgentInventoryContext(prompt, toolList, subagentList, mcpList, systemPromptValue, ec);
|
|
168
|
+
return {
|
|
169
|
+
...options.extendContext(base, ec),
|
|
170
|
+
steerControl$: steerControl.value$,
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
// 3. feedback cycle$ (ADR-016 Soft↔Hard: primeTurn0)
|
|
174
|
+
const cycle$ = createLlmSessionCycle$(context$, feedback.value$, options.prepareSession, (context, turnFeedback, history, session) => options.runTurn(context, turnFeedback === undefined
|
|
175
|
+
? undefined
|
|
176
|
+
: String(turnFeedback ?? ''), history, subagentResult.value$, session), { primeTurn0: true });
|
|
177
|
+
// 4. demux
|
|
178
|
+
const { reasoning$, draftResponse$, toolLog$, recovery$, response$, subagent$, } = demuxLlmAgentPorts(cycle$);
|
|
179
|
+
// 5. outputs
|
|
180
|
+
return {
|
|
181
|
+
inputs: [userPrompt, systemPrompt, feedback],
|
|
182
|
+
outputs: [
|
|
183
|
+
configureOutput('reasoning', reasoning$, {
|
|
184
|
+
wireType: 'string',
|
|
185
|
+
feed: { role: 'reasoning', streaming: true },
|
|
186
|
+
}),
|
|
187
|
+
configureOutput('draftResponse', draftResponse$, {
|
|
188
|
+
wireType: 'string',
|
|
189
|
+
feed: { role: 'draft', streaming: true },
|
|
190
|
+
}),
|
|
191
|
+
configureOutput('response', response$, {
|
|
192
|
+
wireType: 'string',
|
|
193
|
+
feed: { role: 'result' },
|
|
194
|
+
}),
|
|
195
|
+
],
|
|
196
|
+
inventoryOutputs: { toolLog$, recovery$, subagent$ },
|
|
197
|
+
};
|
|
198
|
+
};
|