@synergenius/flow-weaver 0.33.2 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -226
- package/NOTICE +13 -0
- package/README.md +152 -245
- package/dist/agent/agent-loop.d.ts +4 -2
- package/dist/agent/cli-session.d.ts +3 -3
- package/dist/agent/cli-session.js +511 -438
- package/dist/agent/cli-spawn-config.d.ts +4 -4
- package/dist/agent/gate.d.ts +164 -0
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +174 -28
- package/dist/agent/mcp-bridge.d.ts +1 -1
- package/dist/agent/mcp-bridge.js +114 -128
- package/dist/agent/mcp-tool-server.d.ts +2 -2
- package/dist/agent/mcp-tool-server.js +147 -189
- package/dist/agent/profiles.d.ts +69 -0
- package/dist/agent/providers/anthropic.d.ts +2 -4
- package/dist/agent/providers/claude-cli.d.ts +4 -5
- package/dist/agent/providers/openai-compat.d.ts +8 -1
- package/dist/agent/streaming.d.ts +2 -2
- package/dist/agent/types.d.ts +16 -10
- package/dist/api/builder.d.ts +3 -3
- package/dist/api/compile.d.ts +9 -4
- package/dist/api/durable-validation.d.ts +15 -0
- package/dist/api/generate-in-place.d.ts +21 -19
- package/dist/api/generate.d.ts +21 -16
- package/dist/api/generated-module/local-node-functions.d.ts +24 -0
- package/dist/api/generated-module/module-format.d.ts +23 -0
- package/dist/api/generated-module/module-writer.d.ts +34 -0
- package/dist/api/generated-module/node-type-imports.d.ts +39 -0
- package/dist/api/generated-module/preamble.d.ts +16 -0
- package/dist/api/generated-module/workflow-functions.d.ts +29 -0
- package/dist/api/graph-identity.d.ts +39 -0
- package/dist/api/helpers.d.ts +4 -60
- package/dist/api/in-place/built-in-nodes.d.ts +18 -0
- package/dist/api/in-place/function-body.d.ts +21 -0
- package/dist/api/in-place/fw-imports.d.ts +30 -0
- package/dist/api/in-place/macro-coverage.d.ts +14 -0
- package/dist/api/in-place/node-type-functions.d.ts +18 -0
- package/dist/api/in-place/node-type-jsdoc.d.ts +20 -0
- package/dist/api/in-place/orphaned-node-types.d.ts +16 -0
- package/dist/api/in-place/runtime-section.d.ts +16 -0
- package/dist/api/in-place/signature.d.ts +47 -0
- package/dist/api/in-place/source-file.d.ts +19 -0
- package/dist/api/in-place/workflow-jsdoc.d.ts +19 -0
- package/dist/api/index.d.ts +18 -21
- package/dist/api/index.js +259 -53
- package/dist/api/inline-engine.generated.d.ts +3 -0
- package/dist/api/inline-runtime.d.ts +10 -2
- package/dist/api/manipulation/connections.d.ts +1 -1
- package/dist/api/manipulation/index.d.ts +8 -8
- package/dist/api/manipulation/node-types.d.ts +1 -1
- package/dist/api/manipulation/nodes.d.ts +3 -13
- package/dist/api/manipulation/ports.d.ts +1 -1
- package/dist/api/manipulation/scopes.d.ts +1 -1
- package/dist/api/manipulation/workflow.d.ts +1 -1
- package/dist/api/modify-operation.d.ts +0 -2
- package/dist/api/parse.d.ts +22 -1
- package/dist/api/port-references.d.ts +66 -0
- package/dist/api/query.d.ts +18 -2
- package/dist/api/templates.d.ts +1 -1
- package/dist/api/transform.d.ts +1 -1
- package/dist/api/validate.d.ts +1 -1
- package/dist/api/validation-registry.d.ts +2 -2
- package/dist/artifacts/brief.d.ts +43 -0
- package/dist/artifacts/index.d.ts +31 -0
- package/dist/artifacts/pdf.d.ts +19 -0
- package/dist/ast/builder.d.ts +3 -3
- package/dist/ast/index.d.ts +4 -4
- package/dist/ast/index.js +60 -5
- package/dist/ast/serialization-node.d.ts +1 -1
- package/dist/ast/serialization.d.ts +1 -1
- package/dist/ast/types.d.ts +82 -291
- package/dist/ast/workflow-utils.d.ts +4 -4
- package/dist/built-in-nodes/coercion-types.d.ts +1 -1
- package/dist/built-in-nodes/delay.d.ts +3 -1
- package/dist/built-in-nodes/generated-registry.d.ts +1 -1
- package/dist/built-in-nodes/index.d.ts +6 -5
- package/dist/built-in-nodes/index.js +79 -5
- package/dist/built-in-nodes/invoke-workflow.d.ts +2 -1
- package/dist/built-in-nodes/mock-types.d.ts +45 -14
- package/dist/built-in-nodes/sleep.d.ts +12 -0
- package/dist/built-in-nodes/wait-for-agent.d.ts +2 -1
- package/dist/built-in-nodes/wait-for-event.d.ts +2 -1
- package/dist/chevrotain-parser/coerce-parser.d.ts +1 -1
- package/dist/chevrotain-parser/connect-parser.d.ts +1 -1
- package/dist/chevrotain-parser/grammar-diagrams.d.ts +1 -1
- package/dist/chevrotain-parser/index.d.ts +20 -23
- package/dist/chevrotain-parser/node-parser.d.ts +7 -8
- package/dist/chevrotain-parser/parse-line.d.ts +54 -0
- package/dist/chevrotain-parser/tokens.d.ts +0 -17
- package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +6 -1
- package/dist/chunks/chunk-4EUSHAY6.js +298 -0
- package/dist/chunks/chunk-4KDUEOFD.js +197 -0
- package/dist/chunks/chunk-5Y3AIUWY.js +911 -0
- package/dist/chunks/chunk-6KMVP6AU.js +20 -0
- package/dist/chunks/chunk-6PNJORFL.js +128 -0
- package/dist/chunks/chunk-7AAQNGRH.js +2001 -0
- package/dist/chunks/chunk-7YYKTPJ5.js +22705 -0
- package/dist/chunks/chunk-A5MBOFVY.js +33 -0
- package/dist/chunks/chunk-AXNQRVJB.js +18 -0
- package/dist/chunks/chunk-B6HQ6YS5.js +572 -0
- package/dist/chunks/chunk-CY3RNVML.js +296 -0
- package/dist/chunks/chunk-CYHYDLV4.js +653 -0
- package/dist/chunks/chunk-CZBCCK4Y.js +82 -0
- package/dist/chunks/chunk-DCMGQP5V.js +505 -0
- package/dist/chunks/chunk-DIAA66GL.js +72 -0
- package/dist/chunks/chunk-DUO6H5PR.js +20 -0
- package/dist/chunks/chunk-E5WN7IGO.js +125 -0
- package/dist/chunks/chunk-EFGFXXQF.js +184 -0
- package/dist/chunks/chunk-F3RTZV2A.js +2811 -0
- package/dist/chunks/chunk-FMTLP5ZX.js +1397 -0
- package/dist/chunks/chunk-HKZLQJDV.js +79 -0
- package/dist/chunks/chunk-INDEVYEC.js +146 -0
- package/dist/chunks/chunk-JQTF4R5L.js +390 -0
- package/dist/chunks/chunk-KMCAYNUM.js +331 -0
- package/dist/chunks/chunk-KMWCCOUJ.js +1444 -0
- package/dist/chunks/chunk-KU3CLJI2.js +338 -0
- package/dist/chunks/chunk-NDSTGMKR.js +3300 -0
- package/dist/chunks/chunk-NQNLUAPO.js +505 -0
- package/dist/chunks/chunk-O2U2LO57.js +466 -0
- package/dist/chunks/chunk-OOQG7UPP.js +457 -0
- package/dist/chunks/chunk-OZQAW5ZG.js +112 -0
- package/dist/chunks/chunk-P2CVDGHR.js +487 -0
- package/dist/chunks/chunk-P44RIZ7C.js +592 -0
- package/dist/chunks/chunk-PEZCT37T.js +1475 -0
- package/dist/chunks/chunk-PKWSAW6O.js +152 -0
- package/dist/chunks/chunk-Q6MDDZMT.js +4373 -0
- package/dist/chunks/chunk-QDE4Z7KW.js +6 -0
- package/dist/chunks/chunk-QJDAVSNY.js +8 -0
- package/dist/chunks/chunk-QYPORSLO.js +7552 -0
- package/dist/chunks/chunk-R4QGNK34.js +183 -0
- package/dist/chunks/chunk-REEYC73I.js +3002 -0
- package/dist/chunks/chunk-SEWDMQ6Q.js +46 -0
- package/dist/chunks/chunk-TAIUWKZB.js +3057 -0
- package/dist/chunks/chunk-TN4YZ3O7.js +196 -0
- package/dist/chunks/chunk-TRMFB43C.js +143 -0
- package/dist/chunks/chunk-VO4QXG2X.js +1016 -0
- package/dist/chunks/chunk-VP2P3BPA.js +1002 -0
- package/dist/chunks/chunk-VQ7H33J4.js +2176 -0
- package/dist/chunks/chunk-VWA7MCV2.js +105 -0
- package/dist/chunks/chunk-XAV22WD7.js +253 -0
- package/dist/chunks/chunk-YSPBPZDV.js +148 -0
- package/dist/chunks/chunk-ZKEGXO73.js +282 -0
- package/dist/chunks/chunk-ZMGN6F3S.js +4256 -0
- package/dist/chunks/grammar-diagrams-AI7VDXOM.js +14 -0
- package/dist/chunks/index.min-ECJD6GZC.js +37 -0
- package/dist/chunks/manifest-EPTR5MYH.js +21 -0
- package/dist/chunks/openapi-document-6TCGVDUW.js +69 -0
- package/dist/chunks/registry-K2U5KUTY.js +23 -0
- package/dist/chunks/serialization-node-UGY6CFH6.js +38 -0
- package/dist/chunks/validate-GEKSZPIN.js +24 -0
- package/dist/chunks/validation-registry-IQZX3UE3.js +7 -0
- package/dist/chunks/validator-VXFHLPDW.js +26 -0
- package/dist/chunks/workflow-executor-KBGFKHNR.js +36 -0
- package/dist/cli/commands/agents.d.ts +16 -0
- package/dist/cli/commands/artifact.d.ts +10 -0
- package/dist/cli/commands/compile.d.ts +0 -30
- package/dist/cli/commands/console.d.ts +32 -0
- package/dist/cli/commands/context.d.ts +0 -3
- package/dist/cli/commands/describe.js +386 -352
- package/dist/cli/commands/dev.d.ts +9 -4
- package/dist/cli/commands/diagram.d.ts +3 -5
- package/dist/cli/commands/doctor.d.ts +27 -4
- package/dist/cli/commands/doctor.js +3307 -633
- package/dist/cli/commands/export.d.ts +6 -4
- package/dist/cli/commands/grammar.d.ts +0 -3
- package/dist/cli/commands/implement.d.ts +1 -1
- package/dist/cli/commands/init/agent-handoff.d.ts +28 -0
- package/dist/cli/commands/init/config.d.ts +3 -0
- package/dist/cli/commands/init/files.d.ts +12 -0
- package/dist/cli/commands/init/naming.d.ts +13 -0
- package/dist/cli/commands/init/output.d.ts +12 -0
- package/dist/cli/commands/init/setup.d.ts +21 -0
- package/dist/cli/commands/init/types.d.ts +53 -0
- package/dist/cli/commands/init-personas.d.ts +9 -1
- package/dist/cli/commands/init.d.ts +24 -88
- package/dist/cli/commands/market.d.ts +1 -1
- package/dist/cli/commands/mcp-setup.d.ts +1 -4
- package/dist/cli/commands/modify.d.ts +0 -5
- package/dist/cli/commands/openapi.d.ts +9 -11
- package/dist/cli/commands/run.d.ts +0 -4
- package/dist/cli/commands/serve.d.ts +24 -26
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/exports.d.ts +3 -4
- package/dist/cli/exports.js +124 -23
- package/dist/cli/flow-weaver-agents-H653446S.mjs +81 -0
- package/dist/cli/flow-weaver-artifact-WBFYTW76.mjs +58 -0
- package/dist/cli/flow-weaver-chunk-265CEK74.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-2C7PZCV5.mjs +186 -0
- package/dist/cli/flow-weaver-chunk-3BSYCWPL.mjs +3109 -0
- package/dist/cli/flow-weaver-chunk-3MKUQP3D.mjs +547 -0
- package/dist/cli/flow-weaver-chunk-3ZLMPQTH.mjs +27 -0
- package/dist/cli/flow-weaver-chunk-4DVY73ML.mjs +280 -0
- package/dist/cli/flow-weaver-chunk-54M4KNEL.mjs +200 -0
- package/dist/cli/flow-weaver-chunk-55QMUZIA.mjs +256 -0
- package/dist/cli/flow-weaver-chunk-6ZUJ7LMX.mjs +2003 -0
- package/dist/cli/flow-weaver-chunk-7ET6N32V.mjs +419 -0
- package/dist/cli/flow-weaver-chunk-7G63RR4K.mjs +8880 -0
- package/dist/cli/flow-weaver-chunk-A6M5K3W7.mjs +4418 -0
- package/dist/cli/flow-weaver-chunk-AA3NATMX.mjs +154 -0
- package/dist/cli/flow-weaver-chunk-APX6MQ5A.mjs +5652 -0
- package/dist/cli/flow-weaver-chunk-BHCC5FE5.mjs +17 -0
- package/dist/cli/flow-weaver-chunk-DM4BBRRU.mjs +35 -0
- package/dist/cli/flow-weaver-chunk-DRN7KPWY.mjs +168 -0
- package/dist/cli/flow-weaver-chunk-EKWF27G6.mjs +23 -0
- package/dist/cli/flow-weaver-chunk-EM5HWQ4R.mjs +338 -0
- package/dist/cli/flow-weaver-chunk-FPPPU2DG.mjs +226 -0
- package/dist/cli/flow-weaver-chunk-GAQXZHNO.mjs +3005 -0
- package/dist/cli/flow-weaver-chunk-GBUNNP33.mjs +1329 -0
- package/dist/cli/flow-weaver-chunk-HMAKDE55.mjs +636 -0
- package/dist/cli/flow-weaver-chunk-HQ3ZAN44.mjs +367 -0
- package/dist/cli/flow-weaver-chunk-I4APCXYN.mjs +21442 -0
- package/dist/cli/flow-weaver-chunk-IJJ4JXTL.mjs +2297 -0
- package/dist/cli/flow-weaver-chunk-J7EPZW2I.mjs +33 -0
- package/dist/cli/flow-weaver-chunk-KOEMJHLG.mjs +83 -0
- package/dist/cli/flow-weaver-chunk-M6CBWQKJ.mjs +271 -0
- package/dist/cli/flow-weaver-chunk-MHJ4N652.mjs +1580 -0
- package/dist/cli/flow-weaver-chunk-MKAFQWLP.mjs +202 -0
- package/dist/cli/flow-weaver-chunk-MMV2XGD3.mjs +3291 -0
- package/dist/cli/flow-weaver-chunk-MSI2UJES.mjs +125 -0
- package/dist/cli/flow-weaver-chunk-N767YRAI.mjs +48 -0
- package/dist/cli/flow-weaver-chunk-NQKKWDGF.mjs +56 -0
- package/dist/cli/flow-weaver-chunk-NYZDQYFU.mjs +45 -0
- package/dist/cli/flow-weaver-chunk-OMVWSNHJ.mjs +14 -0
- package/dist/cli/flow-weaver-chunk-PUWZ47H5.mjs +2188 -0
- package/dist/cli/flow-weaver-chunk-QOPRVDES.mjs +46 -0
- package/dist/cli/flow-weaver-chunk-RIWE7YTR.mjs +216 -0
- package/dist/cli/flow-weaver-chunk-S63Q3JIU.mjs +789 -0
- package/dist/cli/flow-weaver-chunk-T7X5A23H.mjs +558 -0
- package/dist/cli/flow-weaver-chunk-TLV3VZUN.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-TS5P3PIN.mjs +194 -0
- package/dist/cli/flow-weaver-chunk-U3EO3X4B.mjs +649 -0
- package/dist/cli/flow-weaver-chunk-UNFS3NQP.mjs +34 -0
- package/dist/cli/flow-weaver-chunk-V6DHWFQG.mjs +2811 -0
- package/dist/cli/flow-weaver-chunk-VXWNLYQU.mjs +9 -0
- package/dist/cli/flow-weaver-chunk-W3YNK26P.mjs +149 -0
- package/dist/cli/flow-weaver-chunk-XG2WKXYZ.mjs +324 -0
- package/dist/cli/flow-weaver-chunk-YLSRZWFK.mjs +2219 -0
- package/dist/cli/flow-weaver-chunk-YVPURWS6.mjs +40 -0
- package/dist/cli/flow-weaver-chunk-Z44JK332.mjs +582 -0
- package/dist/cli/flow-weaver-chunk-Z5ZJPNSY.mjs +517 -0
- package/dist/cli/flow-weaver-chunk-ZQALO25T.mjs +594 -0
- package/dist/cli/flow-weaver-compile-A2CABDSB.mjs +37 -0
- package/dist/cli/flow-weaver-console-6EBHDBTN.mjs +71 -0
- package/dist/cli/flow-weaver-context-ECLYANFF.mjs +17 -0
- package/dist/cli/flow-weaver-context-ZRUMW2KE.mjs +69 -0
- package/dist/cli/flow-weaver-create-WIP4EFNP.mjs +124 -0
- package/dist/cli/flow-weaver-deployment-WJJRK7NO.mjs +14 -0
- package/dist/cli/flow-weaver-describe-XDIFF5LI.mjs +41 -0
- package/dist/cli/flow-weaver-dev-LG2NSQAU.mjs +196 -0
- package/dist/cli/flow-weaver-diagram-3EVQARQE.mjs +52 -0
- package/dist/cli/flow-weaver-diagram-7LR7O4M2.mjs +44 -0
- package/dist/cli/flow-weaver-diff-USF7DJLU.mjs +85 -0
- package/dist/cli/flow-weaver-docs-QU2ZOXKH.mjs +96 -0
- package/dist/cli/flow-weaver-doctor-K447YWHR.mjs +43 -0
- package/dist/cli/flow-weaver-export-LAS6N56Y.mjs +140 -0
- package/dist/cli/flow-weaver-grammar-RIRUS3Q6.mjs +49 -0
- package/dist/cli/flow-weaver-grammar-diagrams-WXRTW5SK.mjs +15 -0
- package/dist/cli/flow-weaver-implement-5JG6BCFZ.mjs +114 -0
- package/dist/cli/flow-weaver-index.min-OR5SIHXD.mjs +39 -0
- package/dist/cli/flow-weaver-init-34YZNPQX.mjs +972 -0
- package/dist/cli/flow-weaver-init-personas-NOGZTQQ2.mjs +48 -0
- package/dist/cli/flow-weaver-manifest-4JNUSEB3.mjs +18 -0
- package/dist/cli/flow-weaver-market-WA4CKHKV.mjs +444 -0
- package/dist/cli/flow-weaver-mcp-setup-O26MGXSD.mjs +22 -0
- package/dist/cli/flow-weaver-migrate-VFVEUTPH.mjs +119 -0
- package/dist/cli/flow-weaver-modify-VM3JP5K6.mjs +97 -0
- package/dist/cli/flow-weaver-openapi-IRBWI3HM.mjs +71 -0
- package/dist/cli/flow-weaver-openapi-document-7N6YMPG3.mjs +30 -0
- package/dist/cli/flow-weaver-pack-commands-OS2QRXAK.mjs +146 -0
- package/dist/cli/flow-weaver-registry-5MLFRPPT.mjs +25 -0
- package/dist/cli/flow-weaver-run-DSTLSTHR.mjs +555 -0
- package/dist/cli/flow-weaver-serialization-node-LE6PCG4M.mjs +50 -0
- package/dist/cli/flow-weaver-serve-NVLCLSOW.mjs +192 -0
- package/dist/cli/flow-weaver-server-7IERLVXE.mjs +2925 -0
- package/dist/cli/flow-weaver-server-KWF4PXYC.mjs +19112 -0
- package/dist/cli/flow-weaver-status-75W2S3YB.mjs +138 -0
- package/dist/cli/flow-weaver-strip-AZPXUYVA.mjs +79 -0
- package/dist/cli/flow-weaver-templates-DSNMB6K7.mjs +72 -0
- package/dist/cli/flow-weaver-validate-A4UWDQN6.mjs +19 -0
- package/dist/cli/flow-weaver-validate-GRBZ5W4C.mjs +296 -0
- package/dist/cli/flow-weaver-validation-registry-BINGUNXX.mjs +9 -0
- package/dist/cli/flow-weaver-validator-TY56CI42.mjs +21 -0
- package/dist/cli/flow-weaver-watch-OPPERXL6.mjs +114 -0
- package/dist/cli/flow-weaver-workflow-executor-6AHYPXAN.mjs +26 -0
- package/dist/cli/flow-weaver.mjs +3557 -89384
- package/dist/cli/index.d.ts +5 -2
- package/dist/cli/pack-commands-needed.d.ts +12 -0
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/templates/ai/mock-provider.d.ts +1 -1
- package/dist/cli/templates/index.d.ts +1 -8
- package/dist/cli/templates/nodes/agent-router.d.ts +1 -1
- package/dist/cli/templates/nodes/aggregator.d.ts +1 -1
- package/dist/cli/templates/nodes/conversation-memory.d.ts +1 -1
- package/dist/cli/templates/nodes/http.d.ts +1 -1
- package/dist/cli/templates/nodes/human-approval.d.ts +1 -1
- package/dist/cli/templates/nodes/json-extractor.d.ts +1 -1
- package/dist/cli/templates/nodes/llm-call.d.ts +1 -1
- package/dist/cli/templates/nodes/prompt-template.d.ts +1 -1
- package/dist/cli/templates/nodes/rag-retriever.d.ts +1 -1
- package/dist/cli/templates/nodes/tool-executor.d.ts +1 -1
- package/dist/cli/templates/nodes/transformer.d.ts +1 -1
- package/dist/cli/templates/nodes/validator.d.ts +1 -1
- package/dist/cli/templates/shared/approval-types.d.ts +1 -1
- package/dist/cli/templates/shared/case.d.ts +14 -0
- package/dist/cli/templates/shared/llm-types.d.ts +4 -4
- package/dist/cli/templates/workflows/aggregator.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-agent.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-chat.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-rag.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-react.d.ts +1 -1
- package/dist/cli/templates/workflows/approval.d.ts +10 -0
- package/dist/cli/templates/workflows/conditional.d.ts +2 -2
- package/dist/cli/templates/workflows/error-handler.d.ts +2 -2
- package/dist/cli/templates/workflows/foreach.d.ts +1 -1
- package/dist/cli/templates/workflows/sequential.d.ts +1 -1
- package/dist/cli/templates/workflows/webhook.d.ts +1 -1
- package/dist/cli/utils/interactive.d.ts +6 -0
- package/dist/cli/utils/json-option.d.ts +8 -0
- package/dist/cli/utils/logger.d.ts +2 -0
- package/dist/compiler/durable-source-proof.d.ts +23 -0
- package/dist/compiler/executable-artifact.d.ts +29 -0
- package/dist/compiler/index.d.ts +4 -0
- package/dist/compiler/index.js +36 -0
- package/dist/console/app.js +106 -0
- package/dist/console/assets/synergenius.svg +18 -0
- package/dist/console/author.d.ts +32 -0
- package/dist/console/cli-catalog.d.ts +21 -0
- package/dist/console/cli-run.d.ts +35 -0
- package/dist/console/cli-run.js +46 -0
- package/dist/console/debug.d.ts +67 -0
- package/dist/console/diff-view.d.ts +96 -0
- package/dist/console/export.d.ts +36 -0
- package/dist/console/git.d.ts +27 -0
- package/dist/console/index.d.ts +4 -0
- package/dist/console/index.html +16 -0
- package/dist/console/index.js +203 -0
- package/dist/console/packs.d.ts +100 -0
- package/dist/console/request-guard.d.ts +10 -0
- package/dist/console/respond.d.ts +16 -0
- package/dist/console/router.d.ts +64 -0
- package/dist/console/routes-agents.d.ts +14 -0
- package/dist/console/routes-project.d.ts +3 -0
- package/dist/console/routes-runs.d.ts +3 -0
- package/dist/console/routes-services.d.ts +7 -0
- package/dist/console/routes-tools.d.ts +3 -0
- package/dist/console/runs.d.ts +69 -0
- package/dist/console/scan.d.ts +49 -0
- package/dist/console/schema.d.ts +74 -0
- package/dist/console/server.d.ts +34 -0
- package/dist/console/server.js +43264 -0
- package/dist/console/services.d.ts +119 -0
- package/dist/console/source.d.ts +23 -0
- package/dist/console/status.d.ts +68 -0
- package/dist/console/styles.css +1023 -0
- package/dist/console/terminals.d.ts +22 -0
- package/dist/console/workflow-view.d.ts +153 -0
- package/dist/constants.d.ts +5 -1
- package/dist/constants.js +56 -167
- package/dist/context/index.d.ts +10 -0
- package/dist/context/index.js +14 -191
- package/dist/coordinator/agent-gate.d.ts +107 -0
- package/dist/coordinator/bundle-digest.d.ts +20 -0
- package/dist/coordinator/clock-tick.d.ts +4 -0
- package/dist/coordinator/effect-receipts.d.ts +10 -0
- package/dist/coordinator/errors.d.ts +31 -0
- package/dist/coordinator/file-store.d.ts +3 -0
- package/dist/coordinator/gate-labeling.d.ts +33 -0
- package/dist/coordinator/gate-resolution.d.ts +28 -0
- package/dist/coordinator/index.d.ts +12 -0
- package/dist/coordinator/index.js +107 -0
- package/dist/coordinator/memory-store.d.ts +3 -0
- package/dist/coordinator/params.d.ts +18 -0
- package/dist/coordinator/run-annotations.d.ts +6 -0
- package/dist/coordinator/run-context.d.ts +11 -0
- package/dist/coordinator/run-ending.d.ts +5 -0
- package/dist/coordinator/run-outcome.d.ts +31 -0
- package/dist/coordinator/run-resume.d.ts +6 -0
- package/dist/coordinator/run-start.d.ts +4 -0
- package/dist/coordinator/run-store.d.ts +275 -0
- package/dist/coordinator/run-trace.d.ts +41 -0
- package/dist/coordinator/run-views.d.ts +12 -0
- package/dist/coordinator/runs-dir.d.ts +26 -0
- package/dist/coordinator/store.d.ts +67 -0
- package/dist/coordinator/time.d.ts +39 -0
- package/dist/coordinator/workflow-selection.d.ts +6 -0
- package/dist/deployment/index.d.ts +11 -16
- package/dist/deployment/index.js +541 -50
- package/dist/deployment/targets/base.d.ts +11 -13
- package/dist/deployment/targets/openapi-parts.d.ts +92 -0
- package/dist/diagram/arms.d.ts +70 -0
- package/dist/diagram/ascii-renderer.d.ts +1 -1
- package/dist/diagram/geometry.d.ts +20 -32
- package/dist/diagram/graph/bounds.d.ts +30 -0
- package/dist/diagram/graph/metrics.d.ts +21 -0
- package/dist/diagram/graph/nodes.d.ts +33 -0
- package/dist/diagram/graph/placement.d.ts +22 -0
- package/dist/diagram/graph/ports.d.ts +23 -0
- package/dist/diagram/graph/routing.d.ts +40 -0
- package/dist/diagram/graph/scopes.d.ts +30 -0
- package/dist/diagram/index.d.ts +15 -23
- package/dist/diagram/index.js +632 -111
- package/dist/diagram/labels.d.ts +12 -0
- package/dist/diagram/lanes.d.ts +93 -0
- package/dist/diagram/layout.d.ts +1 -1
- package/dist/diagram/material-symbols.d.ts +7 -0
- package/dist/diagram/orthogonal-router.d.ts +2 -3
- package/dist/diagram/process-view.d.ts +79 -0
- package/dist/diagram/spine.d.ts +25 -0
- package/dist/diagram/theme.d.ts +1 -5
- package/dist/diagram/types.d.ts +6 -20
- package/dist/diff/index.js +53 -9
- package/dist/diff/types.d.ts +1 -5
- package/dist/doc-metadata/extractors/annotations.d.ts +0 -4
- package/dist/doc-metadata/extractors/cli-commands.d.ts +2 -1
- package/dist/doc-metadata/extractors/core-metadata.d.ts +19 -4
- package/dist/doc-metadata/extractors/error-codes.d.ts +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.d.ts +7 -3
- package/dist/doc-metadata/index.d.ts +1 -2
- package/dist/doc-metadata/index.js +2368 -9
- package/dist/doc-metadata/types.d.ts +7 -24
- package/dist/docs/guide.d.ts +33 -0
- package/dist/docs/index.d.ts +7 -4
- package/dist/docs/index.js +298 -298
- package/dist/docs/pack-topics.d.ts +18 -0
- package/dist/editor-completions/annotationValues.d.ts +1 -1
- package/dist/editor-completions/contextParser.d.ts +3 -2
- package/dist/editor-completions/dataTypes.d.ts +1 -1
- package/dist/editor-completions/goToDefinition.d.ts +1 -1
- package/dist/editor-completions/index.d.ts +8 -8
- package/dist/editor-completions/index.js +1244 -175
- package/dist/editor-completions/jsDocAnnotations.d.ts +1 -1
- package/dist/editor-completions/modifierCompletions.d.ts +1 -1
- package/dist/export/index.d.ts +7 -1
- package/dist/generated-branding.js +8 -21
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +7 -3
- package/dist/generator/annotation-generator.d.ts +84 -0
- package/dist/generator/async-detection.d.ts +1 -1
- package/dist/{body-generator.d.ts → generator/body-generator.d.ts} +7 -3
- package/dist/generator/code-utils.d.ts +15 -60
- package/dist/generator/connection-reads.d.ts +94 -0
- package/dist/generator/control-flow-plan.d.ts +74 -0
- package/dist/generator/control-flow.d.ts +1 -1
- package/dist/generator/exit-assembly.d.ts +11 -0
- package/dist/generator/index.d.ts +4 -5
- package/dist/generator/index.js +34 -11
- package/dist/generator/input-port-values.d.ts +22 -0
- package/dist/generator/{scope-function-generator.d.ts → node-arguments.d.ts} +76 -1
- package/dist/generator/node-invocation.d.ts +74 -0
- package/dist/{node-types-generator.d.ts → generator/node-types-generator.d.ts} +3 -2
- package/dist/generator/scope-closure.d.ts +23 -0
- package/dist/generator/scoped-child.d.ts +49 -0
- package/dist/{sugar-optimizer.d.ts → generator/sugar-optimizer.d.ts} +1 -1
- package/dist/generator/unified.d.ts +14 -2
- package/dist/generator/workflow-generator.d.ts +37 -0
- package/dist/index.d.ts +29 -26
- package/dist/index.js +1322 -92
- package/dist/jsdoc-port-sync/apply-diff.d.ts +15 -0
- package/dist/jsdoc-port-sync/callback-signature.d.ts +17 -0
- package/dist/jsdoc-port-sync/diff.d.ts +5 -7
- package/dist/jsdoc-port-sync/incomplete-lines.d.ts +41 -0
- package/dist/jsdoc-port-sync/index.d.ts +11 -11
- package/dist/jsdoc-port-sync/index.js +88 -45
- package/dist/jsdoc-port-sync/jsdoc-to-signature.d.ts +66 -0
- package/dist/jsdoc-port-sync/port-parser.d.ts +6 -24
- package/dist/jsdoc-port-sync/port-tag-placement.d.ts +55 -0
- package/dist/jsdoc-port-sync/scoped-ports.d.ts +21 -0
- package/dist/jsdoc-port-sync/signature-parser.d.ts +8 -1
- package/dist/jsdoc-port-sync/signature-to-jsdoc.d.ts +77 -0
- package/dist/jsdoc-port-sync/sync.d.ts +4 -1
- package/dist/marketplace/index.d.ts +5 -3
- package/dist/marketplace/index.js +62 -10
- package/dist/marketplace/install.d.ts +24 -0
- package/dist/marketplace/manifest.d.ts +1 -1
- package/dist/marketplace/npmrc.d.ts +24 -0
- package/dist/marketplace/registry.d.ts +46 -11
- package/dist/marketplace/types.d.ts +41 -24
- package/dist/marketplace/validator.d.ts +1 -1
- package/dist/mcp/debug-session.d.ts +2 -4
- package/dist/mcp/dist-imports.d.ts +12 -0
- package/dist/mcp/response-utils.d.ts +11 -9
- package/dist/mcp/server.d.ts +0 -1
- package/dist/mcp/tools-context.d.ts +11 -0
- package/dist/mcp/tools-diagram.d.ts +1 -2
- package/dist/mcp/tools-export.d.ts +2 -2
- package/dist/mcp/tools-marketplace.d.ts +1 -1
- package/dist/mcp/tools-run.d.ts +17 -0
- package/dist/mcp/tools-workflow.d.ts +3 -0
- package/dist/mcp/types.d.ts +4 -14
- package/dist/mcp/workflow-executor.d.ts +57 -19
- package/dist/npm-packages.d.ts +1 -1
- package/dist/npm-packages.js +10 -473
- package/dist/parser/annotation-parser.d.ts +88 -0
- package/dist/parser/annotation-suggestion.d.ts +23 -0
- package/dist/parser/durable-effect-contract.d.ts +10 -0
- package/dist/parser/expression-references.d.ts +101 -0
- package/dist/parser/external-node-types.d.ts +69 -0
- package/dist/{function-like.d.ts → parser/function-like.d.ts} +10 -3
- package/dist/parser/generated-sections.d.ts +25 -0
- package/dist/parser/import-resolution.d.ts +67 -0
- package/dist/parser/import-stub.d.ts +29 -0
- package/dist/parser/jsdoc/config-types.d.ts +209 -0
- package/dist/parser/jsdoc/graph-tags.d.ts +54 -0
- package/dist/parser/jsdoc/node-type-block.d.ts +23 -0
- package/dist/parser/jsdoc/parse-line-once.d.ts +11 -0
- package/dist/parser/jsdoc/port-tags.d.ts +42 -0
- package/dist/parser/jsdoc/runtime-tags.d.ts +65 -0
- package/dist/parser/jsdoc/signature-types.d.ts +52 -0
- package/dist/parser/jsdoc/workflow-block.d.ts +22 -0
- package/dist/parser/jsdoc-parser.d.ts +23 -0
- package/dist/parser/macro-expansion.d.ts +80 -0
- package/dist/parser/module-resolution.d.ts +38 -0
- package/dist/parser/node-inference.d.ts +39 -0
- package/dist/parser/npm-node-types.d.ts +24 -0
- package/dist/parser/pack-handlers.d.ts +12 -0
- package/dist/parser/path-data-resolution.d.ts +75 -0
- package/dist/parser/port-inference.d.ts +15 -0
- package/dist/{resolve-package-types.d.ts → parser/resolve-package-types.d.ts} +4 -0
- package/dist/parser/serialize-deploy-annotations.d.ts +19 -0
- package/dist/parser/tag-registry.d.ts +22 -0
- package/dist/parser/workflow-extraction.d.ts +39 -0
- package/dist/runtime/ExecutionContext.d.ts +87 -99
- package/dist/runtime/continuation-core.d.ts +147 -0
- package/dist/runtime/continuation.d.ts +48 -0
- package/dist/runtime/debug-controller.d.ts +7 -22
- package/dist/runtime/durable-execution.d.ts +176 -0
- package/dist/runtime/events.d.ts +16 -1
- package/dist/runtime/executable-module-contract.d.ts +16 -0
- package/dist/runtime/index.d.ts +13 -10
- package/dist/runtime/index.js +947 -9
- package/dist/runtime/strict-json.d.ts +15 -0
- package/dist/server/access.d.ts +12 -0
- package/dist/server/api.d.ts +126 -0
- package/dist/server/callback-delivery.d.ts +62 -0
- package/dist/server/callback-url.d.ts +44 -0
- package/dist/server/context.d.ts +35 -0
- package/dist/server/http-error.d.ts +10 -0
- package/dist/server/index.d.ts +7 -1
- package/dist/server/index.js +125 -6
- package/dist/server/openapi-document.d.ts +33 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/respond.d.ts +35 -0
- package/dist/server/route-params.d.ts +17 -0
- package/dist/server/route-plan.d.ts +41 -0
- package/dist/server/routes-catalog.d.ts +5 -0
- package/dist/server/routes-declared.d.ts +12 -0
- package/dist/server/routes-runs.d.ts +8 -0
- package/dist/server/run-driver.d.ts +58 -0
- package/dist/server/run-view.d.ts +26 -0
- package/dist/server/transport.d.ts +35 -0
- package/dist/server/types.d.ts +143 -52
- package/dist/server/webhook-server.d.ts +12 -38
- package/dist/server/workflow-registry.d.ts +7 -4
- package/dist/service-registry.d.ts +49 -0
- package/dist/service-registry.js +123 -0
- package/dist/testing/assertions.d.ts +2 -2
- package/dist/testing/index.d.ts +14 -13
- package/dist/testing/index.js +582 -24
- package/dist/testing/mock-approval.d.ts +3 -5
- package/dist/testing/mock-llm.d.ts +2 -3
- package/dist/testing/recorder.d.ts +5 -6
- package/dist/testing/replayer.d.ts +6 -7
- package/dist/testing/run-store-check.d.ts +3 -0
- package/dist/testing/token-tracker.d.ts +2 -2
- package/dist/{type-mappings.d.ts → types/type-mappings.d.ts} +13 -1
- package/dist/utils/error-utils.d.ts +4 -2
- package/dist/utils/port-ordering.d.ts +2 -2
- package/dist/utils/port-tag-utils.d.ts +1 -1
- package/dist/validation/agent-detection.d.ts +17 -5
- package/dist/validation/agent-rules.d.ts +6 -7
- package/dist/validation/core-rules.d.ts +49 -0
- package/dist/validation/design-rules.d.ts +9 -3
- package/dist/validation/rule-registry.d.ts +3 -3
- package/dist/validation/rules/connections.d.ts +27 -0
- package/dist/validation/rules/context.d.ts +11 -0
- package/dist/validation/rules/cycles.d.ts +17 -0
- package/dist/validation/rules/data-flow.d.ts +19 -0
- package/dist/validation/rules/scope-topology.d.ts +35 -0
- package/dist/validation/rules/type-compatibility.d.ts +30 -0
- package/dist/validation/type-checker.d.ts +39 -0
- package/dist/validation/validator-helpers.d.ts +31 -0
- package/dist/validation/validator.d.ts +30 -0
- package/docs/reference/advanced-annotations.md +277 -60
- package/docs/reference/built-in-nodes.md +164 -94
- package/docs/reference/cancellation.md +44 -0
- package/docs/reference/cli-reference.md +252 -234
- package/docs/reference/compilation.md +36 -109
- package/docs/reference/concepts.md +80 -63
- package/docs/reference/console.md +169 -0
- package/docs/reference/debugging.md +26 -57
- package/docs/reference/deployment.md +217 -143
- package/docs/reference/durable-gates.md +445 -0
- package/docs/reference/error-codes.md +90 -43
- package/docs/reference/export-interface.md +91 -26
- package/docs/reference/iterative-development.md +50 -28
- package/docs/reference/jsdoc-grammar.md +66 -311
- package/docs/reference/library.md +250 -0
- package/docs/reference/marketplace.md +301 -152
- package/docs/reference/mcp-tools.md +102 -0
- package/docs/reference/node-conversion.md +84 -59
- package/docs/reference/orientation.md +68 -0
- package/docs/reference/scaffold.md +5 -4
- package/docs/reference/tutorial.md +106 -166
- package/docs/reference/visual-reference.md +1 -1
- package/package.json +85 -50
- package/dist/agent/agent-loop.js +0 -156
- package/dist/agent/cli-spawn-config.js +0 -65
- package/dist/agent/device-connection.d.ts +0 -79
- package/dist/agent/device-connection.js +0 -184
- package/dist/agent/env-allowlist.js +0 -60
- package/dist/agent/providers/anthropic.js +0 -198
- package/dist/agent/providers/claude-cli.js +0 -161
- package/dist/agent/providers/openai-compat.js +0 -205
- package/dist/agent/providers/platform.d.ts +0 -23
- package/dist/agent/providers/platform.js +0 -132
- package/dist/agent/streaming.js +0 -186
- package/dist/agent/types.js +0 -20
- package/dist/annotation-generator.d.ts +0 -50
- package/dist/annotation-generator.js +0 -743
- package/dist/api/builder.js +0 -345
- package/dist/api/command-runner.js +0 -787
- package/dist/api/compile.js +0 -148
- package/dist/api/extract-types.js +0 -57
- package/dist/api/generate-in-place.js +0 -1477
- package/dist/api/generate.js +0 -514
- package/dist/api/helpers.js +0 -285
- package/dist/api/inline-runtime.js +0 -526
- package/dist/api/manipulation/connections.js +0 -151
- package/dist/api/manipulation/index.js +0 -41
- package/dist/api/manipulation/node-types.js +0 -200
- package/dist/api/manipulation/nodes.js +0 -355
- package/dist/api/manipulation/ports.js +0 -228
- package/dist/api/manipulation/scopes.js +0 -156
- package/dist/api/manipulation/workflow.js +0 -116
- package/dist/api/modify-operation.js +0 -177
- package/dist/api/parse.js +0 -115
- package/dist/api/patterns.d.ts +0 -112
- package/dist/api/patterns.js +0 -303
- package/dist/api/query.js +0 -840
- package/dist/api/templates.js +0 -117
- package/dist/api/transform.js +0 -40
- package/dist/api/validate.js +0 -64
- package/dist/api/validation-registry.js +0 -10
- package/dist/api/workflow-file-operations.js +0 -180
- package/dist/ast/builder.js +0 -395
- package/dist/ast/serialization-node.js +0 -30
- package/dist/ast/serialization.js +0 -134
- package/dist/ast/types.js +0 -2
- package/dist/ast/workflow-utils.js +0 -114
- package/dist/body-generator.js +0 -35
- package/dist/built-in-nodes/coercion-types.js +0 -61
- package/dist/built-in-nodes/delay.js +0 -29
- package/dist/built-in-nodes/generated-registry.js +0 -299
- package/dist/built-in-nodes/invoke-workflow.js +0 -36
- package/dist/built-in-nodes/mock-types.js +0 -42
- package/dist/built-in-nodes/wait-for-agent.js +0 -31
- package/dist/built-in-nodes/wait-for-event.js +0 -25
- package/dist/chevrotain-parser/coerce-parser.js +0 -103
- package/dist/chevrotain-parser/connect-parser.js +0 -136
- package/dist/chevrotain-parser/fan-parser.js +0 -149
- package/dist/chevrotain-parser/grammar-diagrams.js +0 -273
- package/dist/chevrotain-parser/index.js +0 -31
- package/dist/chevrotain-parser/map-parser.js +0 -130
- package/dist/chevrotain-parser/node-parser.js +0 -558
- package/dist/chevrotain-parser/path-parser.js +0 -140
- package/dist/chevrotain-parser/port-parser.js +0 -519
- package/dist/chevrotain-parser/position-parser.d.ts +0 -20
- package/dist/chevrotain-parser/position-parser.js +0 -83
- package/dist/chevrotain-parser/scope-parser.js +0 -104
- package/dist/chevrotain-parser/tokens.js +0 -429
- package/dist/chevrotain-parser/trigger-cancel-parser.js +0 -291
- package/dist/cli/commands/account.d.ts +0 -4
- package/dist/cli/commands/account.js +0 -50
- package/dist/cli/commands/ai-credentials.d.ts +0 -12
- package/dist/cli/commands/ai-credentials.js +0 -95
- package/dist/cli/commands/apikey.d.ts +0 -4
- package/dist/cli/commands/apikey.js +0 -64
- package/dist/cli/commands/auth.d.ts +0 -9
- package/dist/cli/commands/auth.js +0 -255
- package/dist/cli/commands/compile.js +0 -331
- package/dist/cli/commands/connect.d.ts +0 -4
- package/dist/cli/commands/connect.js +0 -163
- package/dist/cli/commands/context.js +0 -51
- package/dist/cli/commands/create.js +0 -139
- package/dist/cli/commands/deploy.d.ts +0 -6
- package/dist/cli/commands/deploy.js +0 -74
- package/dist/cli/commands/dev.js +0 -227
- package/dist/cli/commands/diagram.js +0 -39
- package/dist/cli/commands/diff.js +0 -57
- package/dist/cli/commands/docs.js +0 -74
- package/dist/cli/commands/export.js +0 -153
- package/dist/cli/commands/grammar.js +0 -40
- package/dist/cli/commands/implement.js +0 -91
- package/dist/cli/commands/init-personas.js +0 -541
- package/dist/cli/commands/init.js +0 -797
- package/dist/cli/commands/market.js +0 -440
- package/dist/cli/commands/mcp-setup.js +0 -433
- package/dist/cli/commands/migrate.js +0 -91
- package/dist/cli/commands/modify.js +0 -58
- package/dist/cli/commands/openapi.js +0 -71
- package/dist/cli/commands/org.d.ts +0 -8
- package/dist/cli/commands/org.js +0 -136
- package/dist/cli/commands/pattern.d.ts +0 -34
- package/dist/cli/commands/pattern.js +0 -186
- package/dist/cli/commands/plugin.d.ts +0 -16
- package/dist/cli/commands/plugin.js +0 -176
- package/dist/cli/commands/run.js +0 -742
- package/dist/cli/commands/serve.js +0 -93
- package/dist/cli/commands/status.js +0 -123
- package/dist/cli/commands/strip.js +0 -61
- package/dist/cli/commands/templates.js +0 -58
- package/dist/cli/commands/validate.js +0 -267
- package/dist/cli/commands/watch.js +0 -71
- package/dist/cli/config/credentials.d.ts +0 -15
- package/dist/cli/config/credentials.js +0 -43
- package/dist/cli/config/platform-client.d.ts +0 -123
- package/dist/cli/config/platform-client.js +0 -248
- package/dist/cli/env-setup.js +0 -12
- package/dist/cli/index.js +0 -850
- package/dist/cli/pack-commands.js +0 -122
- package/dist/cli/postinstall.d.ts +0 -16
- package/dist/cli/postinstall.js +0 -119
- package/dist/cli/templates/ai/mock-provider.js +0 -64
- package/dist/cli/templates/ai/types.js +0 -5
- package/dist/cli/templates/approvals/index.d.ts +0 -15
- package/dist/cli/templates/approvals/index.js +0 -241
- package/dist/cli/templates/index.js +0 -123
- package/dist/cli/templates/nodes/agent-router.js +0 -114
- package/dist/cli/templates/nodes/aggregator.js +0 -63
- package/dist/cli/templates/nodes/conversation-memory.js +0 -85
- package/dist/cli/templates/nodes/http.js +0 -80
- package/dist/cli/templates/nodes/human-approval.js +0 -109
- package/dist/cli/templates/nodes/json-extractor.js +0 -119
- package/dist/cli/templates/nodes/llm-call.js +0 -106
- package/dist/cli/templates/nodes/prompt-template.js +0 -52
- package/dist/cli/templates/nodes/rag-retriever.js +0 -128
- package/dist/cli/templates/nodes/tool-executor.js +0 -108
- package/dist/cli/templates/nodes/transformer.js +0 -68
- package/dist/cli/templates/nodes/validator.js +0 -62
- package/dist/cli/templates/pack-loader.js +0 -58
- package/dist/cli/templates/providers/index.js +0 -239
- package/dist/cli/templates/shared/approval-types.js +0 -31
- package/dist/cli/templates/shared/llm-types.js +0 -109
- package/dist/cli/templates/workflows/aggregator.js +0 -101
- package/dist/cli/templates/workflows/ai-agent.js +0 -326
- package/dist/cli/templates/workflows/ai-chat.js +0 -167
- package/dist/cli/templates/workflows/ai-rag.js +0 -184
- package/dist/cli/templates/workflows/ai-react.js +0 -293
- package/dist/cli/templates/workflows/conditional.js +0 -139
- package/dist/cli/templates/workflows/error-handler.js +0 -145
- package/dist/cli/templates/workflows/foreach.js +0 -140
- package/dist/cli/templates/workflows/sequential.js +0 -200
- package/dist/cli/templates/workflows/webhook.js +0 -158
- package/dist/cli/utils/cli-helpers.d.ts +0 -44
- package/dist/cli/utils/cli-helpers.js +0 -99
- package/dist/cli/utils/logger.js +0 -126
- package/dist/cli/utils/parse-int-strict.js +0 -17
- package/dist/cli/utils/safe-write.js +0 -54
- package/dist/deployment/config/defaults.d.ts +0 -29
- package/dist/deployment/config/defaults.js +0 -98
- package/dist/deployment/config/loader.d.ts +0 -24
- package/dist/deployment/config/loader.js +0 -237
- package/dist/deployment/config/types.d.ts +0 -117
- package/dist/deployment/config/types.js +0 -5
- package/dist/deployment/core/adapters.d.ts +0 -66
- package/dist/deployment/core/adapters.js +0 -156
- package/dist/deployment/core/executor.d.ts +0 -62
- package/dist/deployment/core/executor.js +0 -197
- package/dist/deployment/core/formatters.d.ts +0 -42
- package/dist/deployment/core/formatters.js +0 -146
- package/dist/deployment/openapi/generator.d.ts +0 -146
- package/dist/deployment/openapi/generator.js +0 -347
- package/dist/deployment/openapi/schema-converter.d.ts +0 -49
- package/dist/deployment/openapi/schema-converter.js +0 -192
- package/dist/deployment/targets/base.js +0 -855
- package/dist/deployment/types.d.ts +0 -140
- package/dist/deployment/types.js +0 -8
- package/dist/diagram/ascii-renderer.js +0 -528
- package/dist/diagram/geometry.js +0 -1226
- package/dist/diagram/html-viewer.d.ts +0 -21
- package/dist/diagram/html-viewer.js +0 -1745
- package/dist/diagram/layout.js +0 -143
- package/dist/diagram/orthogonal-router.js +0 -458
- package/dist/diagram/renderer.d.ts +0 -3
- package/dist/diagram/renderer.js +0 -280
- package/dist/diagram/theme.js +0 -282
- package/dist/diagram/types.js +0 -2
- package/dist/diff/WorkflowDiffer.js +0 -429
- package/dist/diff/formatDiff.js +0 -239
- package/dist/diff/impact.js +0 -119
- package/dist/diff/types.js +0 -35
- package/dist/doc-metadata/extractors/annotations.js +0 -638
- package/dist/doc-metadata/extractors/cli-commands.js +0 -326
- package/dist/doc-metadata/extractors/core-metadata.js +0 -61
- package/dist/doc-metadata/extractors/error-codes.js +0 -372
- package/dist/doc-metadata/extractors/grammar-rules.js +0 -51
- package/dist/doc-metadata/extractors/mcp-tools.js +0 -705
- package/dist/doc-metadata/extractors/plugin-api.d.ts +0 -19
- package/dist/doc-metadata/extractors/plugin-api.js +0 -279
- package/dist/doc-metadata/types.js +0 -5
- package/dist/editor-completions/annotationValues.js +0 -146
- package/dist/editor-completions/contextParser.js +0 -410
- package/dist/editor-completions/dataTypes.js +0 -95
- package/dist/editor-completions/goToDefinition.js +0 -112
- package/dist/editor-completions/jsDocAnnotations.js +0 -359
- package/dist/editor-completions/modifierCompletions.js +0 -197
- package/dist/editor-completions/types.js +0 -8
- package/dist/export/index.js +0 -212
- package/dist/extensions/index.d.ts +0 -16
- package/dist/extensions/index.js +0 -17
- package/dist/friendly-errors.js +0 -761
- package/dist/function-like.js +0 -84
- package/dist/generator/async-detection.js +0 -56
- package/dist/generator/code-utils.js +0 -551
- package/dist/generator/compile-target-registry.d.ts +0 -20
- package/dist/generator/compile-target-registry.js +0 -20
- package/dist/generator/control-flow.js +0 -358
- package/dist/generator/dev-mode-registry.d.ts +0 -27
- package/dist/generator/dev-mode-registry.js +0 -20
- package/dist/generator/scope-function-generator.js +0 -397
- package/dist/generator/unified.js +0 -1831
- package/dist/generator.d.ts +0 -54
- package/dist/generator.js +0 -101
- package/dist/jsdoc-parser.d.ts +0 -377
- package/dist/jsdoc-parser.js +0 -1189
- package/dist/jsdoc-port-sync/constants.js +0 -103
- package/dist/jsdoc-port-sync/diff.js +0 -319
- package/dist/jsdoc-port-sync/port-parser.js +0 -579
- package/dist/jsdoc-port-sync/rename.js +0 -256
- package/dist/jsdoc-port-sync/signature-parser.js +0 -559
- package/dist/jsdoc-port-sync/sync.js +0 -653
- package/dist/marketplace/manifest.js +0 -194
- package/dist/marketplace/registry.js +0 -198
- package/dist/marketplace/types.js +0 -9
- package/dist/marketplace/validator.js +0 -150
- package/dist/mcp/agent-channel.d.ts +0 -35
- package/dist/mcp/agent-channel.js +0 -61
- package/dist/mcp/auto-registration.d.ts +0 -3
- package/dist/mcp/auto-registration.js +0 -62
- package/dist/mcp/debug-session.js +0 -25
- package/dist/mcp/index.d.ts +0 -11
- package/dist/mcp/index.js +0 -10
- package/dist/mcp/pack-tools.js +0 -59
- package/dist/mcp/prompts.js +0 -68
- package/dist/mcp/response-utils.js +0 -97
- package/dist/mcp/run-registry.d.ts +0 -29
- package/dist/mcp/run-registry.js +0 -24
- package/dist/mcp/server.js +0 -63
- package/dist/mcp/tools-context.js +0 -51
- package/dist/mcp/tools-debug.js +0 -451
- package/dist/mcp/tools-diagram.js +0 -97
- package/dist/mcp/tools-docs.js +0 -62
- package/dist/mcp/tools-export.js +0 -222
- package/dist/mcp/tools-marketplace.js +0 -132
- package/dist/mcp/tools-model.d.ts +0 -3
- package/dist/mcp/tools-model.js +0 -279
- package/dist/mcp/tools-pattern.d.ts +0 -3
- package/dist/mcp/tools-pattern.js +0 -604
- package/dist/mcp/tools-query.js +0 -377
- package/dist/mcp/tools-resources.js +0 -41
- package/dist/mcp/tools-template.js +0 -119
- package/dist/mcp/tools-workflow-run.d.ts +0 -40
- package/dist/mcp/tools-workflow-run.js +0 -150
- package/dist/mcp/types.js +0 -2
- package/dist/mcp/workflow-executor.js +0 -242
- package/dist/migration/registry.js +0 -29
- package/dist/node-types-generator.js +0 -139
- package/dist/parser/tag-registry.js +0 -57
- package/dist/parser.d.ts +0 -232
- package/dist/parser.js +0 -2500
- package/dist/plugin/PluginPanel.d.ts +0 -12
- package/dist/plugin/PluginPanel.js +0 -5
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +0 -14
- package/dist/plugin/types.d.ts +0 -75
- package/dist/plugin/types.js +0 -8
- package/dist/resolve-package-types.js +0 -123
- package/dist/runtime/CancellationError.js +0 -20
- package/dist/runtime/ExecutionContext.js +0 -280
- package/dist/runtime/builtin-functions.js +0 -549
- package/dist/runtime/checkpoint.d.ts +0 -84
- package/dist/runtime/checkpoint.js +0 -225
- package/dist/runtime/debug-controller.js +0 -247
- package/dist/runtime/events.js +0 -2
- package/dist/runtime/function-registry.js +0 -66
- package/dist/runtime/parameter-resolver.js +0 -113
- package/dist/server/types.js +0 -5
- package/dist/server/webhook-server.js +0 -269
- package/dist/server/workflow-registry.js +0 -202
- package/dist/shared-project.js +0 -28
- package/dist/sugar-optimizer.js +0 -498
- package/dist/testing/assertions.js +0 -127
- package/dist/testing/mock-approval.js +0 -98
- package/dist/testing/mock-llm.js +0 -119
- package/dist/testing/recorder.js +0 -70
- package/dist/testing/replayer.js +0 -143
- package/dist/testing/token-tracker.js +0 -94
- package/dist/type-checker.d.ts +0 -42
- package/dist/type-checker.js +0 -190
- package/dist/type-mappings.js +0 -131
- package/dist/types/branded-ports.d.ts +0 -151
- package/dist/types/branded-ports.js +0 -121
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -5
- package/dist/types.d.ts +0 -139
- package/dist/types.js +0 -15
- package/dist/utils/error-utils.js +0 -27
- package/dist/utils/lru-cache.js +0 -40
- package/dist/utils/port-ordering.js +0 -81
- package/dist/utils/port-tag-utils.js +0 -41
- package/dist/utils/string-distance.js +0 -56
- package/dist/validation/agent-detection.js +0 -115
- package/dist/validation/agent-rules.js +0 -262
- package/dist/validation/design-rules.js +0 -377
- package/dist/validation/rule-registry.js +0 -37
- package/dist/validator.d.ts +0 -113
- package/dist/validator.js +0 -1631
- package/docs/reference/patterns.md +0 -159
- package/scripts/postinstall.cjs +0 -86
- /package/dist/{shared-project.d.ts → parser/shared-project.d.ts} +0 -0
- /package/dist/{friendly-errors.d.ts → validation/friendly-errors.d.ts} +0 -0
|
@@ -0,0 +1,3291 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/cli/templates/shared/case.ts
|
|
5
|
+
function toCamelCase(str) {
|
|
6
|
+
const leadingMatch = str.match(/^[_$]+/);
|
|
7
|
+
const leading = leadingMatch ? leadingMatch[0] : "";
|
|
8
|
+
const rest = leading ? str.slice(leading.length) : str;
|
|
9
|
+
const result = rest.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^[^a-zA-Z_$]+/, "").replace(/^./, (c) => c.toLowerCase());
|
|
10
|
+
const final = leading + result;
|
|
11
|
+
return final || "_" + str.replace(/[^a-zA-Z0-9_$]/g, "");
|
|
12
|
+
}
|
|
13
|
+
function toPascalCase(str) {
|
|
14
|
+
const camel = toCamelCase(str);
|
|
15
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/cli/templates/workflows/sequential.ts
|
|
19
|
+
var configSchema = {
|
|
20
|
+
nodes: {
|
|
21
|
+
type: "string",
|
|
22
|
+
label: "Node Names",
|
|
23
|
+
description: 'Comma-separated list of node function names (e.g. "fetch,parse,store")',
|
|
24
|
+
placeholder: "validateData,transformData,outputResult"
|
|
25
|
+
},
|
|
26
|
+
input: {
|
|
27
|
+
type: "string",
|
|
28
|
+
label: "Input Port Name",
|
|
29
|
+
description: "Name of the input data port",
|
|
30
|
+
default: "data",
|
|
31
|
+
placeholder: "data"
|
|
32
|
+
},
|
|
33
|
+
output: {
|
|
34
|
+
type: "string",
|
|
35
|
+
label: "Output Port Name",
|
|
36
|
+
description: "Name of the output data port",
|
|
37
|
+
default: "result",
|
|
38
|
+
placeholder: "result"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function generateDefaultTemplate(workflowName, asyncKeyword, returnType) {
|
|
42
|
+
return `
|
|
43
|
+
// Use @expression for most functions, normal mode only for error-with-data or void side-effects
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validates input data
|
|
47
|
+
* Normal mode: explicit error routing with data on failure
|
|
48
|
+
*
|
|
49
|
+
* @flowWeaver nodeType
|
|
50
|
+
* @label Validate
|
|
51
|
+
* @input data [order:1] - Data to validate
|
|
52
|
+
* @input execute [order:0] - Execute
|
|
53
|
+
* @output data [order:2] - Validated data
|
|
54
|
+
* @output isValid [order:3] - Validation passed
|
|
55
|
+
* @output error [order:4] - Validation error message
|
|
56
|
+
* @output onSuccess [order:0] - On Success
|
|
57
|
+
* @output onFailure [order:1] - On Failure
|
|
58
|
+
*/
|
|
59
|
+
function validateData(
|
|
60
|
+
execute: boolean,
|
|
61
|
+
data: any
|
|
62
|
+
): { onSuccess: boolean; onFailure: boolean; data: any; isValid: boolean; error: string | null } {
|
|
63
|
+
if (!execute) {
|
|
64
|
+
return { onSuccess: false, onFailure: false, data: null, isValid: false, error: null };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// TODO: Add validation logic
|
|
68
|
+
if (!data) {
|
|
69
|
+
return { onSuccess: false, onFailure: true, data: null, isValid: false, error: "Data is required" };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { onSuccess: true, onFailure: false, data, isValid: true, error: null };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Transforms validated data
|
|
77
|
+
*
|
|
78
|
+
* @flowWeaver nodeType
|
|
79
|
+
* @expression
|
|
80
|
+
* @label Transform
|
|
81
|
+
* @input data [order:0] - Data to transform
|
|
82
|
+
* @output data [order:0] - Transformed data
|
|
83
|
+
*/
|
|
84
|
+
function transformData(data: any): { data: any } {
|
|
85
|
+
// TODO: Add transformation logic
|
|
86
|
+
return { data: { ...data, processedAt: new Date().toISOString() } };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Outputs the final result
|
|
91
|
+
*
|
|
92
|
+
* @flowWeaver nodeType
|
|
93
|
+
* @expression
|
|
94
|
+
* @label Output
|
|
95
|
+
* @input data [order:0] - Data to output
|
|
96
|
+
* @output result [order:0] - Final result
|
|
97
|
+
*/
|
|
98
|
+
function outputResult(data: any): { result: any } {
|
|
99
|
+
return { result: data };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @flowWeaver workflow
|
|
104
|
+
* @node validator validateData [color: "green"] [icon: "verified"] [suppress: "UNUSED_OUTPUT_PORT"]
|
|
105
|
+
* @node transformer transformData [color: "blue"] [icon: "sync"]
|
|
106
|
+
* @node outputter outputResult [color: "cyan"] [icon: "inventory"]
|
|
107
|
+
* @path Start -> validator -> transformer -> outputter -> Exit
|
|
108
|
+
* @path Start -> validator:fail -> Exit
|
|
109
|
+
* @param execute [order:0] - Execute
|
|
110
|
+
* @param data [order:1] - Input data
|
|
111
|
+
* @returns onSuccess [order:0] - On Success
|
|
112
|
+
* @returns onFailure [order:1] - On Failure
|
|
113
|
+
* @returns result [order:2] - Processed result
|
|
114
|
+
* @returns error [order:3] - Error message if failed
|
|
115
|
+
*/
|
|
116
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
117
|
+
execute: boolean,
|
|
118
|
+
params: { data: any }
|
|
119
|
+
): ${returnType} {
|
|
120
|
+
throw new Error("Compile with: fw compile <file>");
|
|
121
|
+
}
|
|
122
|
+
`.trim();
|
|
123
|
+
}
|
|
124
|
+
function generateCustomTemplate(workflowName, asyncKeyword, returnType, nodeNames, inputPort, outputPort) {
|
|
125
|
+
const nodeTypeDefs = nodeNames.map((name, i) => {
|
|
126
|
+
const isFirst = i === 0;
|
|
127
|
+
const isLast = i === nodeNames.length - 1;
|
|
128
|
+
const inputPortName = isFirst ? inputPort : "data";
|
|
129
|
+
const outputPortName = isLast ? outputPort : "data";
|
|
130
|
+
return `/**
|
|
131
|
+
* ${name} step
|
|
132
|
+
*
|
|
133
|
+
* @flowWeaver nodeType
|
|
134
|
+
* @expression
|
|
135
|
+
* @label ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
136
|
+
* @input ${inputPortName} [order:0] - Input data
|
|
137
|
+
* @output ${outputPortName} [order:0] - Output data
|
|
138
|
+
*/
|
|
139
|
+
function ${name}(${inputPortName}: any): { ${outputPortName}: any } {
|
|
140
|
+
// TODO: Implement ${name} logic
|
|
141
|
+
return { ${outputPortName}: ${inputPortName} };
|
|
142
|
+
}`;
|
|
143
|
+
});
|
|
144
|
+
const stepColors = ["green", "blue", "cyan", "orange", "purple", "teal", "pink", "yellow"];
|
|
145
|
+
const nodeAnnotations = nodeNames.map((name, i) => {
|
|
146
|
+
const color = stepColors[i % stepColors.length];
|
|
147
|
+
return ` * @node step${i} ${name} [color: "${color}"] [icon: "settings"]`;
|
|
148
|
+
}).join("\n");
|
|
149
|
+
const pathNodes = ["Start", ...nodeNames.map((_, i) => `step${i}`), "Exit"];
|
|
150
|
+
const connections = [];
|
|
151
|
+
connections.push(` * @path ${pathNodes.join(" -> ")}`);
|
|
152
|
+
connections.push(` * @path Start -> step0:fail -> Exit`);
|
|
153
|
+
return `${nodeTypeDefs.join("\n\n")}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @flowWeaver workflow
|
|
157
|
+
${nodeAnnotations}
|
|
158
|
+
${connections.join("\n")}
|
|
159
|
+
* @param execute [order:0] - Execute
|
|
160
|
+
* @param ${inputPort} [order:1] - Input data
|
|
161
|
+
* @returns onSuccess [order:0] - On Success
|
|
162
|
+
* @returns onFailure [order:1] - On Failure
|
|
163
|
+
* @returns ${outputPort} [order:2] - Processed result
|
|
164
|
+
*/
|
|
165
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
166
|
+
execute: boolean,
|
|
167
|
+
params: { ${inputPort}: any }
|
|
168
|
+
): ${returnType} {
|
|
169
|
+
throw new Error("Compile with: fw compile <file>");
|
|
170
|
+
}`.trim();
|
|
171
|
+
}
|
|
172
|
+
var sequentialTemplate = {
|
|
173
|
+
id: "sequential",
|
|
174
|
+
name: "Sequential Pipeline",
|
|
175
|
+
description: "Linear pipeline: validate -> transform -> output",
|
|
176
|
+
category: "data-processing",
|
|
177
|
+
configSchema,
|
|
178
|
+
generate: (opts) => {
|
|
179
|
+
const { workflowName, async: isAsync, config } = opts;
|
|
180
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
181
|
+
const returnTypeFor = (outputPort2, withError) => {
|
|
182
|
+
const body = `onSuccess: boolean, onFailure: boolean, ${outputPort2}: any${withError ? ", error: string | null" : ""}`;
|
|
183
|
+
return isAsync ? `Promise<{ ${body} }>` : `{ ${body} }`;
|
|
184
|
+
};
|
|
185
|
+
const customNodes = config?.nodes;
|
|
186
|
+
const inputPort = config?.input || "data";
|
|
187
|
+
const outputPort = config?.output || "result";
|
|
188
|
+
if (customNodes && Array.isArray(customNodes) && customNodes.length > 0) {
|
|
189
|
+
return generateCustomTemplate(
|
|
190
|
+
workflowName,
|
|
191
|
+
asyncKeyword,
|
|
192
|
+
returnTypeFor(outputPort, false),
|
|
193
|
+
customNodes,
|
|
194
|
+
inputPort,
|
|
195
|
+
outputPort
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (inputPort !== "data" || outputPort !== "result") {
|
|
199
|
+
return generateCustomTemplate(
|
|
200
|
+
workflowName,
|
|
201
|
+
asyncKeyword,
|
|
202
|
+
returnTypeFor(outputPort, false),
|
|
203
|
+
["validateData", "transformData", "outputResult"],
|
|
204
|
+
inputPort,
|
|
205
|
+
outputPort
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return generateDefaultTemplate(workflowName, asyncKeyword, returnTypeFor("result", true));
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/cli/templates/workflows/foreach.ts
|
|
213
|
+
var foreachTemplate = {
|
|
214
|
+
id: "foreach",
|
|
215
|
+
name: "ForEach Batch",
|
|
216
|
+
description: "Batch processing with iteration over arrays",
|
|
217
|
+
category: "data-processing",
|
|
218
|
+
generate: (opts) => {
|
|
219
|
+
const { workflowName, async: isAsync } = opts;
|
|
220
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
221
|
+
const returnType = isAsync ? "Promise<{ onSuccess: boolean; onFailure: boolean; results: any[]; successCount: number; failedCount: number }>" : "{ onSuccess: boolean; onFailure: boolean; results: any[]; successCount: number; failedCount: number }";
|
|
222
|
+
return `
|
|
223
|
+
/**
|
|
224
|
+
* Iterates over items and processes each one.
|
|
225
|
+
* Normal mode on purpose: a scope owner drives its child through scoped STEP ports.
|
|
226
|
+
*
|
|
227
|
+
* @flowWeaver nodeType
|
|
228
|
+
* @label For Each Item
|
|
229
|
+
* @input items [order:1] - Array of items to iterate
|
|
230
|
+
* @input success scope:processItem [order:0] - From child onSuccess
|
|
231
|
+
* @input failure scope:processItem [order:1] - From child onFailure
|
|
232
|
+
* @input result scope:processItem [order:2] - Result from child
|
|
233
|
+
* @input execute [order:0] - Execute
|
|
234
|
+
* @output start scope:processItem [order:0] - Triggers child execute
|
|
235
|
+
* @output item scope:processItem [order:1] - Current item to process
|
|
236
|
+
* @output results [order:2] - Collected results after iteration
|
|
237
|
+
* @output onSuccess [order:0] - On Success
|
|
238
|
+
* @output onFailure [order:1] - On Failure
|
|
239
|
+
*/
|
|
240
|
+
function forEachItem(
|
|
241
|
+
execute: boolean,
|
|
242
|
+
items: any[],
|
|
243
|
+
processItem: (start: boolean, item: any) => { success: boolean; failure: boolean; result: any }
|
|
244
|
+
): { onSuccess: boolean; onFailure: boolean; results: any[] } {
|
|
245
|
+
if (!execute) {
|
|
246
|
+
return { onSuccess: false, onFailure: false, results: [] };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const results: any[] = [];
|
|
250
|
+
for (const item of items) {
|
|
251
|
+
const r = processItem(true, item);
|
|
252
|
+
results.push(r.result);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { onSuccess: true, onFailure: false, results };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Processes a single item. A throw here aborts the whole batch with that
|
|
260
|
+
* error; to record a per-item failure instead, return it as part of the result.
|
|
261
|
+
*
|
|
262
|
+
* @flowWeaver nodeType
|
|
263
|
+
* @expression
|
|
264
|
+
* @label Process Item
|
|
265
|
+
* @input item - Item to process
|
|
266
|
+
* @output result - Processed result
|
|
267
|
+
*/
|
|
268
|
+
function processItem(item: any): { result: any } {
|
|
269
|
+
// TODO: Add your processing logic here
|
|
270
|
+
return { result: { ...item, processed: true } };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Aggregates results from iteration
|
|
275
|
+
*
|
|
276
|
+
* @flowWeaver nodeType
|
|
277
|
+
* @expression
|
|
278
|
+
* @label Aggregate Results
|
|
279
|
+
* @input results - Array of processed results
|
|
280
|
+
* @output successCount - Number of successes
|
|
281
|
+
* @output failedCount - Number of failures
|
|
282
|
+
*/
|
|
283
|
+
function aggregateResults(results: any[]): { successCount: number; failedCount: number } {
|
|
284
|
+
const successCount = results.filter(r => r?.processed).length;
|
|
285
|
+
return { successCount, failedCount: results.length - successCount };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @flowWeaver workflow
|
|
290
|
+
* @node iterator forEachItem [color: "purple"] [icon: "repeat"] [suppress: "DESIGN_SCOPE_NO_FAILURE_EXIT"]
|
|
291
|
+
* @node processor processItem iterator.processItem [color: "blue"] [icon: "settings"]
|
|
292
|
+
* @node aggregator aggregateResults [color: "teal"] [icon: "inventory"]
|
|
293
|
+
* @path Start -> iterator -> aggregator -> Exit
|
|
294
|
+
* @connect iterator.start:processItem -> processor.execute
|
|
295
|
+
* @connect iterator.item:processItem -> processor.item
|
|
296
|
+
* @connect processor.result -> iterator.result:processItem
|
|
297
|
+
* @connect processor.onSuccess -> iterator.success:processItem
|
|
298
|
+
* @connect processor.onFailure -> iterator.failure:processItem
|
|
299
|
+
* @param execute [order:0] - Execute
|
|
300
|
+
* @param items [order:1] - Array of items to process
|
|
301
|
+
* @returns onSuccess [order:0] - On Success
|
|
302
|
+
* @returns onFailure [order:1] - On Failure
|
|
303
|
+
* @returns results [order:2] - Processed results
|
|
304
|
+
* @returns successCount [order:3] - Number of successful items
|
|
305
|
+
* @returns failedCount [order:4] - Number of failed items
|
|
306
|
+
*/
|
|
307
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
308
|
+
execute: boolean,
|
|
309
|
+
params: { items: any[] }
|
|
310
|
+
): ${returnType} {
|
|
311
|
+
throw new Error("Compile with: fw compile <file>");
|
|
312
|
+
}
|
|
313
|
+
`.trim();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/cli/templates/workflows/conditional.ts
|
|
318
|
+
var conditionalTemplate = {
|
|
319
|
+
id: "conditional",
|
|
320
|
+
name: "Conditional Router",
|
|
321
|
+
description: "Route data based on conditions with branching paths",
|
|
322
|
+
category: "automation",
|
|
323
|
+
generate: (opts) => {
|
|
324
|
+
const { workflowName, async: isAsync } = opts;
|
|
325
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
326
|
+
const returnType = isAsync ? "Promise<{ onSuccess: boolean; onFailure: boolean; successResult?: any; failureResult?: any }>" : "{ onSuccess: boolean; onFailure: boolean; successResult?: any; failureResult?: any }";
|
|
327
|
+
return `
|
|
328
|
+
/**
|
|
329
|
+
* Evaluates condition and routes data.
|
|
330
|
+
* onSuccess = condition met (true path), onFailure = condition not met (false path).
|
|
331
|
+
* Errors are caught internally and returned via the onFailure path.
|
|
332
|
+
*
|
|
333
|
+
* @flowWeaver nodeType
|
|
334
|
+
* @label Evaluate Condition
|
|
335
|
+
* @input data [order:1] - Data to evaluate
|
|
336
|
+
* @input execute [order:0] - Execute
|
|
337
|
+
* @output conditionTrue [order:2] - Data if condition passed
|
|
338
|
+
* @output conditionFalse [order:3] - Data if condition failed
|
|
339
|
+
* @output conditionMet [order:4] - Whether condition was met
|
|
340
|
+
* @output onSuccess [order:0] - Condition passed (routes to success handler)
|
|
341
|
+
* @output onFailure [order:1] - Condition not met (routes to failure handler)
|
|
342
|
+
*/
|
|
343
|
+
function evaluateCondition(
|
|
344
|
+
execute: boolean,
|
|
345
|
+
data: any
|
|
346
|
+
): { onSuccess: boolean; onFailure: boolean; conditionTrue: any; conditionFalse: any; conditionMet: boolean } {
|
|
347
|
+
if (!execute) {
|
|
348
|
+
return { onSuccess: false, onFailure: false, conditionTrue: null, conditionFalse: null, conditionMet: false };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
// TODO: Define your condition logic
|
|
353
|
+
const conditionMet = Boolean(data && data.isValid);
|
|
354
|
+
|
|
355
|
+
if (conditionMet) {
|
|
356
|
+
return { onSuccess: true, onFailure: false, conditionTrue: data, conditionFalse: null, conditionMet: true };
|
|
357
|
+
} else {
|
|
358
|
+
return { onSuccess: false, onFailure: true, conditionTrue: null, conditionFalse: data, conditionMet: false };
|
|
359
|
+
}
|
|
360
|
+
} catch (err) {
|
|
361
|
+
// On error, route to failure path with the original data
|
|
362
|
+
return { onSuccess: false, onFailure: true, conditionTrue: null, conditionFalse: data, conditionMet: false };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Handles success path
|
|
368
|
+
*
|
|
369
|
+
* @flowWeaver nodeType
|
|
370
|
+
* @label Handle Success
|
|
371
|
+
* @input data [order:1] - Data from success path
|
|
372
|
+
* @input execute [order:0] - Execute
|
|
373
|
+
* @output result [order:2] - Success result
|
|
374
|
+
* @output onSuccess [order:0] - On Success
|
|
375
|
+
* @output onFailure [order:1] - On Failure
|
|
376
|
+
*/
|
|
377
|
+
function handleSuccess(
|
|
378
|
+
execute: boolean,
|
|
379
|
+
data: any
|
|
380
|
+
): { onSuccess: boolean; onFailure: boolean; result: any } {
|
|
381
|
+
if (!execute) {
|
|
382
|
+
return { onSuccess: false, onFailure: false, result: null };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// TODO: Handle success case
|
|
386
|
+
return { onSuccess: true, onFailure: false, result: { status: "success", data } };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Handles failure path
|
|
391
|
+
*
|
|
392
|
+
* @flowWeaver nodeType
|
|
393
|
+
* @label Handle Failure
|
|
394
|
+
* @input data [order:1] - Data from failure path
|
|
395
|
+
* @input execute [order:0] - Execute
|
|
396
|
+
* @output result [order:2] - Failure result
|
|
397
|
+
* @output onSuccess [order:0] - On Success
|
|
398
|
+
* @output onFailure [order:1] - On Failure
|
|
399
|
+
*/
|
|
400
|
+
function handleFailure(
|
|
401
|
+
execute: boolean,
|
|
402
|
+
data: any
|
|
403
|
+
): { onSuccess: boolean; onFailure: boolean; result: any } {
|
|
404
|
+
if (!execute) {
|
|
405
|
+
return { onSuccess: false, onFailure: false, result: null };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// TODO: Handle failure case
|
|
409
|
+
return { onSuccess: true, onFailure: false, result: { status: "failure", data } };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* @flowWeaver workflow
|
|
414
|
+
* @node router evaluateCondition [color: "orange"] [icon: "altRoute"] [suppress: "UNUSED_OUTPUT_PORT"]
|
|
415
|
+
* @node successHandler handleSuccess [color: "green"] [icon: "checkCircle"]
|
|
416
|
+
* @node failureHandler handleFailure [color: "red"] [icon: "error"]
|
|
417
|
+
* @connect Start.execute -> router.execute
|
|
418
|
+
* @connect Start.data -> router.data
|
|
419
|
+
* @connect router.onSuccess -> successHandler.execute
|
|
420
|
+
* @connect router.conditionTrue -> successHandler.data
|
|
421
|
+
* @connect router.onFailure -> failureHandler.execute
|
|
422
|
+
* @connect router.conditionFalse -> failureHandler.data
|
|
423
|
+
* @connect successHandler.result -> Exit.successResult
|
|
424
|
+
* @connect successHandler.onSuccess -> Exit.onSuccess
|
|
425
|
+
* @connect failureHandler.result -> Exit.failureResult
|
|
426
|
+
* @connect failureHandler.onSuccess -> Exit.onFailure
|
|
427
|
+
* @param execute [order:0] - Execute
|
|
428
|
+
* @param data [order:1] - Input data to route
|
|
429
|
+
* @returns onSuccess [order:0] - Success path completed
|
|
430
|
+
* @returns onFailure [order:1] - Failure path completed
|
|
431
|
+
* @returns successResult [order:2] - Result from success path
|
|
432
|
+
* @returns failureResult [order:3] - Result from failure path
|
|
433
|
+
*/
|
|
434
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
435
|
+
execute: boolean,
|
|
436
|
+
params: { data: any }
|
|
437
|
+
): ${returnType} {
|
|
438
|
+
throw new Error("Compile with: fw compile <file>");
|
|
439
|
+
}
|
|
440
|
+
`.trim();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/cli/templates/providers/index.ts
|
|
445
|
+
function generateOpenAIProvider(opts) {
|
|
446
|
+
const model = opts.model || "gpt-4o";
|
|
447
|
+
const apiKey = opts.apiKeyEnvVar || "OPENAI_API_KEY";
|
|
448
|
+
return `
|
|
449
|
+
/* ============================================================
|
|
450
|
+
* OPENAI PROVIDER
|
|
451
|
+
* Set ${apiKey} environment variable before running
|
|
452
|
+
* ============================================================ */
|
|
453
|
+
|
|
454
|
+
const ${apiKey} = process.env.${apiKey};
|
|
455
|
+
if (!${apiKey}) {
|
|
456
|
+
throw new Error('Missing ${apiKey} environment variable');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const _defaultLlmProvider: LLMProvider = {
|
|
460
|
+
async chat(messages, options) {
|
|
461
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
462
|
+
method: 'POST',
|
|
463
|
+
headers: {
|
|
464
|
+
'Authorization': \`Bearer \${${apiKey}}\`,
|
|
465
|
+
'Content-Type': 'application/json',
|
|
466
|
+
},
|
|
467
|
+
body: JSON.stringify({
|
|
468
|
+
model: options?.model ?? '${model}',
|
|
469
|
+
messages: messages.map(m => ({
|
|
470
|
+
role: m.role,
|
|
471
|
+
content: m.content,
|
|
472
|
+
...(m.toolCallId && { tool_call_id: m.toolCallId }),
|
|
473
|
+
})),
|
|
474
|
+
tools: options?.tools?.map(t => ({
|
|
475
|
+
type: 'function',
|
|
476
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
477
|
+
})),
|
|
478
|
+
temperature: options?.temperature,
|
|
479
|
+
max_tokens: options?.maxTokens,
|
|
480
|
+
}),
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
if (!response.ok) {
|
|
484
|
+
const error = await response.text();
|
|
485
|
+
throw new Error(\`OpenAI API error: \${response.status} \${error}\`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const data = await response.json();
|
|
489
|
+
const choice = data.choices[0];
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
content: choice.message.content,
|
|
493
|
+
toolCalls: choice.message.tool_calls?.map((tc: any) => ({
|
|
494
|
+
id: tc.id,
|
|
495
|
+
name: tc.function.name,
|
|
496
|
+
arguments: JSON.parse(tc.function.arguments),
|
|
497
|
+
})) ?? [],
|
|
498
|
+
finishReason: choice.finish_reason === 'tool_calls' ? 'tool_calls' : 'stop',
|
|
499
|
+
};
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const llmProvider: LLMProvider = _defaultLlmProvider;
|
|
504
|
+
`;
|
|
505
|
+
}
|
|
506
|
+
function generateAnthropicProvider(opts) {
|
|
507
|
+
const model = opts.model || "claude-3-5-sonnet-20241022";
|
|
508
|
+
const apiKey = opts.apiKeyEnvVar || "ANTHROPIC_API_KEY";
|
|
509
|
+
return `
|
|
510
|
+
/* ============================================================
|
|
511
|
+
* ANTHROPIC PROVIDER
|
|
512
|
+
* Set ${apiKey} environment variable before running
|
|
513
|
+
* ============================================================ */
|
|
514
|
+
|
|
515
|
+
const ${apiKey} = process.env.${apiKey};
|
|
516
|
+
if (!${apiKey}) {
|
|
517
|
+
throw new Error('Missing ${apiKey} environment variable');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const _defaultLlmProvider: LLMProvider = {
|
|
521
|
+
async chat(messages, options) {
|
|
522
|
+
// Separate system message (Anthropic handles it differently)
|
|
523
|
+
const systemMessage = messages.find(m => m.role === 'system');
|
|
524
|
+
const chatMessages = messages.filter(m => m.role !== 'system');
|
|
525
|
+
|
|
526
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
527
|
+
method: 'POST',
|
|
528
|
+
headers: {
|
|
529
|
+
'x-api-key': ${apiKey},
|
|
530
|
+
'anthropic-version': '2023-06-01',
|
|
531
|
+
'Content-Type': 'application/json',
|
|
532
|
+
},
|
|
533
|
+
body: JSON.stringify({
|
|
534
|
+
model: options?.model ?? '${model}',
|
|
535
|
+
max_tokens: options?.maxTokens ?? 4096,
|
|
536
|
+
system: systemMessage?.content ?? options?.systemPrompt,
|
|
537
|
+
messages: chatMessages.map(m => {
|
|
538
|
+
if (m.role === 'tool') {
|
|
539
|
+
return {
|
|
540
|
+
role: 'user',
|
|
541
|
+
content: [{ type: 'tool_result', tool_use_id: m.toolCallId, content: m.content }],
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
return { role: m.role, content: m.content };
|
|
545
|
+
}),
|
|
546
|
+
tools: options?.tools?.map(t => ({
|
|
547
|
+
name: t.name,
|
|
548
|
+
description: t.description,
|
|
549
|
+
input_schema: { type: 'object', properties: t.parameters.properties, required: t.parameters.required },
|
|
550
|
+
})),
|
|
551
|
+
}),
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
if (!response.ok) {
|
|
555
|
+
const error = await response.text();
|
|
556
|
+
throw new Error(\`Anthropic API error: \${response.status} \${error}\`);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const data = await response.json();
|
|
560
|
+
|
|
561
|
+
let content: string | null = null;
|
|
562
|
+
const toolCalls: LLMToolCall[] = [];
|
|
563
|
+
|
|
564
|
+
for (const block of data.content) {
|
|
565
|
+
if (block.type === 'text') content = block.text;
|
|
566
|
+
if (block.type === 'tool_use') {
|
|
567
|
+
toolCalls.push({ id: block.id, name: block.name, arguments: block.input });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
content,
|
|
573
|
+
toolCalls,
|
|
574
|
+
finishReason: data.stop_reason === 'tool_use' ? 'tool_calls' : 'stop',
|
|
575
|
+
};
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
const llmProvider: LLMProvider = _defaultLlmProvider;
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
function generateOllamaProvider(opts) {
|
|
583
|
+
const model = opts.model || "llama3.2";
|
|
584
|
+
return `
|
|
585
|
+
/* ============================================================
|
|
586
|
+
* OLLAMA PROVIDER (Local)
|
|
587
|
+
* Ensure Ollama is running: ollama serve
|
|
588
|
+
* Pull model first: ollama pull ${model}
|
|
589
|
+
* ============================================================ */
|
|
590
|
+
|
|
591
|
+
const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434';
|
|
592
|
+
|
|
593
|
+
const _defaultLlmProvider: LLMProvider = {
|
|
594
|
+
async chat(messages, options) {
|
|
595
|
+
const response = await fetch(\`\${OLLAMA_BASE_URL}/api/chat\`, {
|
|
596
|
+
method: 'POST',
|
|
597
|
+
headers: { 'Content-Type': 'application/json' },
|
|
598
|
+
body: JSON.stringify({
|
|
599
|
+
model: options?.model ?? '${model}',
|
|
600
|
+
messages: messages.map(m => ({ role: m.role, content: m.content })),
|
|
601
|
+
stream: false,
|
|
602
|
+
options: {
|
|
603
|
+
temperature: options?.temperature,
|
|
604
|
+
num_predict: options?.maxTokens,
|
|
605
|
+
},
|
|
606
|
+
}),
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
if (!response.ok) {
|
|
610
|
+
const error = await response.text();
|
|
611
|
+
throw new Error(\`Ollama error: \${response.status} \${error}\`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const data = await response.json();
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
content: data.message?.content ?? null,
|
|
618
|
+
toolCalls: [], // Ollama tool support varies by model
|
|
619
|
+
finishReason: 'stop',
|
|
620
|
+
};
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const llmProvider: LLMProvider = _defaultLlmProvider;
|
|
625
|
+
`;
|
|
626
|
+
}
|
|
627
|
+
function generateMockProvider() {
|
|
628
|
+
return `
|
|
629
|
+
/* ============================================================
|
|
630
|
+
* MOCK PROVIDER (For Testing)
|
|
631
|
+
* Replace with real provider for production use
|
|
632
|
+
* ============================================================ */
|
|
633
|
+
|
|
634
|
+
const _defaultLlmProvider: LLMProvider = {
|
|
635
|
+
async chat(messages, options) {
|
|
636
|
+
const last = messages[messages.length - 1];
|
|
637
|
+
|
|
638
|
+
// Simulate tool call if message contains "search"
|
|
639
|
+
if (options?.tools && last.content.toLowerCase().includes('search')) {
|
|
640
|
+
return {
|
|
641
|
+
content: null,
|
|
642
|
+
toolCalls: [{
|
|
643
|
+
id: 'call_' + Date.now(),
|
|
644
|
+
name: 'search',
|
|
645
|
+
arguments: { query: last.content },
|
|
646
|
+
}],
|
|
647
|
+
finishReason: 'tool_calls',
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return {
|
|
652
|
+
content: '[Mock response] ' + last.content,
|
|
653
|
+
toolCalls: [],
|
|
654
|
+
finishReason: 'stop',
|
|
655
|
+
};
|
|
656
|
+
},
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const llmProvider: LLMProvider = _defaultLlmProvider;
|
|
660
|
+
`;
|
|
661
|
+
}
|
|
662
|
+
function getProviderCode(provider, model) {
|
|
663
|
+
const opts = { model: model || "" };
|
|
664
|
+
switch (provider) {
|
|
665
|
+
case "openai":
|
|
666
|
+
return generateOpenAIProvider({ ...opts, model: model || "gpt-4o" });
|
|
667
|
+
case "anthropic":
|
|
668
|
+
return generateAnthropicProvider({
|
|
669
|
+
...opts,
|
|
670
|
+
model: model || "claude-3-5-sonnet-20241022"
|
|
671
|
+
});
|
|
672
|
+
case "ollama":
|
|
673
|
+
return generateOllamaProvider({ ...opts, model: model || "llama3.2" });
|
|
674
|
+
case "mock":
|
|
675
|
+
default:
|
|
676
|
+
return generateMockProvider();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/cli/templates/shared/llm-types.ts
|
|
681
|
+
var LLM_CORE_TYPES = `interface LLMMessage {
|
|
682
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
683
|
+
content: string;
|
|
684
|
+
toolCallId?: string;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
interface LLMResponse {
|
|
688
|
+
content: string | null;
|
|
689
|
+
toolCalls: LLMToolCall[];
|
|
690
|
+
finishReason: 'stop' | 'tool_calls' | 'length' | 'error';
|
|
691
|
+
usage?: { promptTokens: number; completionTokens: number };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
interface LLMToolCall {
|
|
695
|
+
id: string;
|
|
696
|
+
name: string;
|
|
697
|
+
arguments: Record<string, unknown>;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
interface LLMTool {
|
|
701
|
+
name: string;
|
|
702
|
+
description: string;
|
|
703
|
+
parameters: {
|
|
704
|
+
type: 'object';
|
|
705
|
+
properties: Record<string, { type: string }>;
|
|
706
|
+
required?: string[];
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
interface LLMProvider {
|
|
711
|
+
chat(
|
|
712
|
+
messages: LLMMessage[],
|
|
713
|
+
options?: { tools?: LLMTool[]; systemPrompt?: string; model?: string; temperature?: number; maxTokens?: number }
|
|
714
|
+
): Promise<LLMResponse>;
|
|
715
|
+
}`;
|
|
716
|
+
var LLM_SIMPLE_TYPES = `interface LLMMessage {
|
|
717
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
718
|
+
content: string;
|
|
719
|
+
toolCallId?: string;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
interface LLMToolCall {
|
|
723
|
+
id: string;
|
|
724
|
+
name: string;
|
|
725
|
+
arguments: Record<string, unknown>;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
interface LLMResponse {
|
|
729
|
+
content: string | null;
|
|
730
|
+
toolCalls: LLMToolCall[];
|
|
731
|
+
finishReason: 'stop' | 'tool_calls' | 'length' | 'error';
|
|
732
|
+
usage?: { promptTokens: number; completionTokens: number };
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
interface LLMProvider {
|
|
736
|
+
chat(messages: LLMMessage[], options?: { systemPrompt?: string; model?: string; temperature?: number; maxTokens?: number }): Promise<LLMResponse>;
|
|
737
|
+
}`;
|
|
738
|
+
var LLM_MOCK_PROVIDER = `const createMockProvider = (): LLMProvider => ({
|
|
739
|
+
async chat(messages) {
|
|
740
|
+
const lastMessage = messages[messages.length - 1];
|
|
741
|
+
return {
|
|
742
|
+
content: \`[Mock response to: \${(lastMessage?.content ?? '').slice(0, 50)}...]\`,
|
|
743
|
+
toolCalls: [],
|
|
744
|
+
finishReason: 'stop',
|
|
745
|
+
usage: { promptTokens: 10, completionTokens: 20 },
|
|
746
|
+
};
|
|
747
|
+
},
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
const llmProvider: LLMProvider = createMockProvider();`;
|
|
751
|
+
var LLM_MOCK_PROVIDER_WITH_TOOLS = `const createMockProvider = (): LLMProvider => ({
|
|
752
|
+
async chat(messages, options) {
|
|
753
|
+
const last = messages[messages.length - 1];
|
|
754
|
+
if (options?.tools && last.content.toLowerCase().includes('search')) {
|
|
755
|
+
return {
|
|
756
|
+
content: null,
|
|
757
|
+
toolCalls: [
|
|
758
|
+
{
|
|
759
|
+
id: 'call_' + Date.now(),
|
|
760
|
+
name: 'search',
|
|
761
|
+
arguments: { query: last.content },
|
|
762
|
+
},
|
|
763
|
+
],
|
|
764
|
+
finishReason: 'tool_calls',
|
|
765
|
+
usage: { promptTokens: 15, completionTokens: 30 },
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return {
|
|
770
|
+
content: '[Mock answer] ' + last.content,
|
|
771
|
+
toolCalls: [],
|
|
772
|
+
finishReason: 'stop',
|
|
773
|
+
usage: { promptTokens: 10, completionTokens: 20 },
|
|
774
|
+
};
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
const llmProvider: LLMProvider = createMockProvider();`;
|
|
779
|
+
|
|
780
|
+
// src/cli/templates/workflows/ai-agent.ts
|
|
781
|
+
var aiConfigSchema = {
|
|
782
|
+
provider: {
|
|
783
|
+
type: "select",
|
|
784
|
+
label: "LLM Provider",
|
|
785
|
+
description: "Choose your LLM provider",
|
|
786
|
+
default: "mock",
|
|
787
|
+
options: [
|
|
788
|
+
{ value: "openai", label: "OpenAI" },
|
|
789
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
790
|
+
{ value: "ollama", label: "Ollama (Local)" },
|
|
791
|
+
{ value: "mock", label: "Mock (Testing)" }
|
|
792
|
+
]
|
|
793
|
+
},
|
|
794
|
+
model: {
|
|
795
|
+
type: "string",
|
|
796
|
+
label: "Model",
|
|
797
|
+
description: "Model identifier",
|
|
798
|
+
default: "gpt-4o",
|
|
799
|
+
placeholder: "e.g., gpt-4o, claude-3-5-sonnet-20241022, llama3.2",
|
|
800
|
+
dependsOn: {
|
|
801
|
+
field: "provider",
|
|
802
|
+
values: ["openai", "anthropic", "ollama"]
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var aiAgentTemplate = {
|
|
807
|
+
id: "ai-agent",
|
|
808
|
+
name: "AI Agent (Tool Calling)",
|
|
809
|
+
description: "Stateful LLM agent with explicit reasoning loop and tool execution",
|
|
810
|
+
category: "ai",
|
|
811
|
+
configSchema: aiConfigSchema,
|
|
812
|
+
generate: ({ workflowName, config }) => {
|
|
813
|
+
const provider = config?.provider || "mock";
|
|
814
|
+
const model = config?.model || "";
|
|
815
|
+
const providerCode = provider === "mock" ? `
|
|
816
|
+
/* ============================================================
|
|
817
|
+
* MOCK PROVIDER (REPLACE IN REAL USE)
|
|
818
|
+
* ============================================================
|
|
819
|
+
*/
|
|
820
|
+
|
|
821
|
+
${LLM_MOCK_PROVIDER_WITH_TOOLS}
|
|
822
|
+
` : getProviderCode(provider, model);
|
|
823
|
+
return `
|
|
824
|
+
/* ============================================================
|
|
825
|
+
* CORE TYPES
|
|
826
|
+
* ============================================================
|
|
827
|
+
*/
|
|
828
|
+
|
|
829
|
+
${LLM_CORE_TYPES}
|
|
830
|
+
|
|
831
|
+
type TerminationReason =
|
|
832
|
+
| 'completed'
|
|
833
|
+
| 'max_iterations'
|
|
834
|
+
| 'tool_error'
|
|
835
|
+
| 'llm_error'
|
|
836
|
+
| 'aborted';
|
|
837
|
+
|
|
838
|
+
/* ============================================================
|
|
839
|
+
* AGENT STATE
|
|
840
|
+
* ============================================================
|
|
841
|
+
*/
|
|
842
|
+
|
|
843
|
+
interface AgentState {
|
|
844
|
+
messages: LLMMessage[];
|
|
845
|
+
iteration: number;
|
|
846
|
+
toolsUsed: string[];
|
|
847
|
+
terminated: boolean;
|
|
848
|
+
terminationReason?: TerminationReason;
|
|
849
|
+
finalResponse?: string;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
${providerCode}
|
|
853
|
+
|
|
854
|
+
const SYSTEM_PROMPT = \`You are a helpful AI assistant with access to tools.
|
|
855
|
+
Use tools when necessary. Respond directly when the task is complete.\`;
|
|
856
|
+
|
|
857
|
+
const MAX_ITERATIONS = 10;
|
|
858
|
+
|
|
859
|
+
/* ============================================================
|
|
860
|
+
* TOOLS
|
|
861
|
+
* ============================================================
|
|
862
|
+
*/
|
|
863
|
+
|
|
864
|
+
const AVAILABLE_TOOLS: LLMTool[] = [
|
|
865
|
+
{
|
|
866
|
+
name: 'search',
|
|
867
|
+
description: 'Search for information',
|
|
868
|
+
parameters: {
|
|
869
|
+
type: 'object',
|
|
870
|
+
properties: {
|
|
871
|
+
query: { type: 'string' },
|
|
872
|
+
},
|
|
873
|
+
required: ['query'],
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
type ToolResult =
|
|
879
|
+
| { ok: true; value: string }
|
|
880
|
+
| { ok: false; error: string };
|
|
881
|
+
|
|
882
|
+
type ToolFn = (args: Record<string, unknown>) => Promise<ToolResult>;
|
|
883
|
+
|
|
884
|
+
const TOOL_IMPLEMENTATIONS: Record<string, ToolFn> = {
|
|
885
|
+
async search(args) {
|
|
886
|
+
if (typeof args.query !== 'string') {
|
|
887
|
+
return { ok: false, error: 'Invalid query' };
|
|
888
|
+
}
|
|
889
|
+
return { ok: true, value: '[Search results for: ' + args.query + ']' };
|
|
890
|
+
},
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
/* ============================================================
|
|
894
|
+
* NODES
|
|
895
|
+
* ============================================================
|
|
896
|
+
*/
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Agent loop that orchestrates LLM calls and tool execution
|
|
900
|
+
* Uses scoped ports to handle iteration internally
|
|
901
|
+
*
|
|
902
|
+
* @flowWeaver nodeType
|
|
903
|
+
* @label Agent Loop
|
|
904
|
+
* @input userMessage [order:1] - User's input message
|
|
905
|
+
* @input [maxIterations] [order:2] - Maximum loop iterations (default: 10)
|
|
906
|
+
* @input success scope:iteration [order:0] - From LLM onSuccess
|
|
907
|
+
* @input failure scope:iteration [order:1] - From LLM onFailure
|
|
908
|
+
* @input llmResponse scope:iteration [order:2] - LLM response
|
|
909
|
+
* @input toolMessages scope:iteration [order:3] - Tool results
|
|
910
|
+
* @input execute [order:0] - Execute
|
|
911
|
+
* @output start scope:iteration [order:0] - Triggers iteration
|
|
912
|
+
* @output state scope:iteration [order:1] - Current agent state
|
|
913
|
+
* @output response [order:2] - Final response when done
|
|
914
|
+
* @output onSuccess [order:0] - On Success
|
|
915
|
+
* @output onFailure [order:1] - On Failure
|
|
916
|
+
*/
|
|
917
|
+
async function agentLoop(
|
|
918
|
+
execute: boolean,
|
|
919
|
+
userMessage: string,
|
|
920
|
+
maxIterations: number = MAX_ITERATIONS,
|
|
921
|
+
iteration: (start: boolean, state: AgentState) => Promise<{
|
|
922
|
+
success: boolean;
|
|
923
|
+
failure: boolean;
|
|
924
|
+
llmResponse: LLMResponse | null;
|
|
925
|
+
toolMessages: LLMMessage[];
|
|
926
|
+
}>
|
|
927
|
+
): Promise<{ onSuccess: boolean; onFailure: boolean; response: string }> {
|
|
928
|
+
if (!execute) {
|
|
929
|
+
return { onSuccess: false, onFailure: false, response: '' };
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
let state: AgentState = {
|
|
933
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
934
|
+
iteration: 0,
|
|
935
|
+
toolsUsed: [],
|
|
936
|
+
terminated: false,
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
while (state.iteration < maxIterations) {
|
|
940
|
+
const result = await iteration(true, state);
|
|
941
|
+
|
|
942
|
+
state.iteration++;
|
|
943
|
+
|
|
944
|
+
// Handle LLM failure
|
|
945
|
+
if (result.failure) {
|
|
946
|
+
return { onSuccess: false, onFailure: true, response: 'LLM error occurred' };
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Add assistant message if present
|
|
950
|
+
if (result.llmResponse?.content) {
|
|
951
|
+
state.messages.push({ role: 'assistant', content: result.llmResponse.content });
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Add tool messages
|
|
955
|
+
if (result.toolMessages && result.toolMessages.length > 0) {
|
|
956
|
+
state.messages.push(...result.toolMessages);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Check termination: no tool calls means we're done
|
|
960
|
+
if (result.llmResponse && result.llmResponse.toolCalls.length === 0 && result.llmResponse.content) {
|
|
961
|
+
return { onSuccess: true, onFailure: false, response: result.llmResponse.content };
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
return { onSuccess: true, onFailure: false, response: 'Max iterations reached' };
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Calls the LLM with current state
|
|
970
|
+
*
|
|
971
|
+
* @flowWeaver nodeType
|
|
972
|
+
* @label Call LLM
|
|
973
|
+
* @input state [order:1] - Current agent state
|
|
974
|
+
* @input execute [order:0] - Execute
|
|
975
|
+
* @output response [order:2] - LLM response
|
|
976
|
+
* @output toolCalls [order:3] - Tool calls from LLM
|
|
977
|
+
* @output onSuccess [order:0] - On Success
|
|
978
|
+
* @output onFailure [order:1] - On Failure
|
|
979
|
+
*/
|
|
980
|
+
async function callLLM(
|
|
981
|
+
execute: boolean,
|
|
982
|
+
state: AgentState
|
|
983
|
+
): Promise<{
|
|
984
|
+
onSuccess: boolean;
|
|
985
|
+
onFailure: boolean;
|
|
986
|
+
response: LLMResponse | null;
|
|
987
|
+
toolCalls: LLMToolCall[];
|
|
988
|
+
}> {
|
|
989
|
+
if (!execute) {
|
|
990
|
+
return { onSuccess: false, onFailure: false, response: null, toolCalls: [] };
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
try {
|
|
994
|
+
const response = await llmProvider.chat(state.messages, {
|
|
995
|
+
tools: AVAILABLE_TOOLS,
|
|
996
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
return { onSuccess: true, onFailure: false, response, toolCalls: response.toolCalls };
|
|
1000
|
+
} catch {
|
|
1001
|
+
return { onSuccess: false, onFailure: true, response: null, toolCalls: [] };
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Executes tool calls
|
|
1007
|
+
*
|
|
1008
|
+
* @flowWeaver nodeType
|
|
1009
|
+
* @label Execute Tools
|
|
1010
|
+
* @input calls [order:1] - Tool calls to execute
|
|
1011
|
+
* @input execute [order:0] - Execute
|
|
1012
|
+
* @output messages [order:2] - Tool response messages
|
|
1013
|
+
* @output onSuccess [order:0] - On Success
|
|
1014
|
+
* @output onFailure [order:1] - On Failure
|
|
1015
|
+
*/
|
|
1016
|
+
async function executeTools(
|
|
1017
|
+
execute: boolean,
|
|
1018
|
+
calls: LLMToolCall[]
|
|
1019
|
+
): Promise<{
|
|
1020
|
+
onSuccess: boolean;
|
|
1021
|
+
onFailure: boolean;
|
|
1022
|
+
messages: LLMMessage[];
|
|
1023
|
+
}> {
|
|
1024
|
+
if (!execute || !calls || calls.length === 0) {
|
|
1025
|
+
return { onSuccess: true, onFailure: false, messages: [] };
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
const messages: LLMMessage[] = [];
|
|
1029
|
+
|
|
1030
|
+
for (const call of calls) {
|
|
1031
|
+
const impl = TOOL_IMPLEMENTATIONS[call.name];
|
|
1032
|
+
if (!impl) {
|
|
1033
|
+
messages.push({
|
|
1034
|
+
role: 'tool',
|
|
1035
|
+
content: 'Unknown tool: ' + call.name,
|
|
1036
|
+
toolCallId: call.id,
|
|
1037
|
+
});
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const result = await impl(call.arguments);
|
|
1042
|
+
|
|
1043
|
+
messages.push({
|
|
1044
|
+
role: 'tool',
|
|
1045
|
+
content: result.ok ? result.value : 'Error: ' + result.error,
|
|
1046
|
+
toolCallId: call.id,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return { onSuccess: true, onFailure: false, messages };
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/* ============================================================
|
|
1054
|
+
* WORKFLOW
|
|
1055
|
+
* ============================================================
|
|
1056
|
+
*/
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* AI Agent that uses tools to accomplish tasks
|
|
1060
|
+
*
|
|
1061
|
+
* @flowWeaver workflow
|
|
1062
|
+
* @node loop agentLoop [color: "purple"] [icon: "smartToy"]
|
|
1063
|
+
* @node llm callLLM loop.iteration [color: "blue"] [icon: "psychology"]
|
|
1064
|
+
* @node tools executeTools loop.iteration [color: "orange"] [icon: "build"] [suppress: "AGENT_UNGUARDED_TOOL_EXECUTOR"]
|
|
1065
|
+
* @connect Start.execute -> loop.execute
|
|
1066
|
+
* @connect Start.userMessage -> loop.userMessage
|
|
1067
|
+
* @connect loop.start:iteration -> llm.execute
|
|
1068
|
+
* @connect loop.state:iteration -> llm.state
|
|
1069
|
+
* @connect llm.toolCalls -> tools.calls
|
|
1070
|
+
* @connect llm.onSuccess -> tools.execute
|
|
1071
|
+
* @connect llm.response -> loop.llmResponse:iteration
|
|
1072
|
+
* @connect llm.onSuccess -> loop.success:iteration
|
|
1073
|
+
* @connect llm.onFailure -> loop.failure:iteration
|
|
1074
|
+
* @connect tools.messages -> loop.toolMessages:iteration
|
|
1075
|
+
* @connect tools.onFailure -> loop.failure:iteration
|
|
1076
|
+
* @connect loop.response -> Exit.response
|
|
1077
|
+
* @connect loop.onSuccess -> Exit.onSuccess
|
|
1078
|
+
* @connect loop.onFailure -> Exit.onFailure
|
|
1079
|
+
* @param execute [order:0] - Execute
|
|
1080
|
+
* @param userMessage [order:1] - User's message to the agent
|
|
1081
|
+
* @returns onSuccess [order:0] - Agent completed successfully
|
|
1082
|
+
* @returns onFailure [order:1] - Agent encountered an error
|
|
1083
|
+
* @returns response [order:2] - Agent's final response
|
|
1084
|
+
*/
|
|
1085
|
+
export async function ${workflowName}(
|
|
1086
|
+
execute: boolean,
|
|
1087
|
+
params: { userMessage: string }
|
|
1088
|
+
): Promise<{
|
|
1089
|
+
onSuccess: boolean;
|
|
1090
|
+
onFailure: boolean;
|
|
1091
|
+
response: string;
|
|
1092
|
+
}> {
|
|
1093
|
+
throw new Error('Compile with: fw compile <file>');
|
|
1094
|
+
}
|
|
1095
|
+
`.trim();
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
// src/cli/templates/workflows/ai-react.ts
|
|
1100
|
+
var aiReactTemplate = {
|
|
1101
|
+
id: "ai-react",
|
|
1102
|
+
name: "ReAct Agent",
|
|
1103
|
+
description: "Reasoning + Acting agent with explicit thought process",
|
|
1104
|
+
category: "ai",
|
|
1105
|
+
configSchema: aiConfigSchema,
|
|
1106
|
+
generate: (opts) => {
|
|
1107
|
+
const { workflowName, config } = opts;
|
|
1108
|
+
const provider = config?.provider || "mock";
|
|
1109
|
+
const model = config?.model || "";
|
|
1110
|
+
const providerCode = provider === "mock" ? `
|
|
1111
|
+
// Mock provider. Replace with a real provider for production.
|
|
1112
|
+
const createMockReactProvider = (): LLMProvider => ({
|
|
1113
|
+
async chat(messages) {
|
|
1114
|
+
const lastMessage = messages[messages.length - 1];
|
|
1115
|
+
return {
|
|
1116
|
+
content: \`Thought: Processing the request\\nAction: FINISH\\nAction Input: [Mock answer to: \${lastMessage.content.slice(0, 30)}...]\`,
|
|
1117
|
+
toolCalls: [],
|
|
1118
|
+
finishReason: 'stop',
|
|
1119
|
+
};
|
|
1120
|
+
},
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
const llmProvider: LLMProvider = createMockReactProvider();
|
|
1124
|
+
` : getProviderCode(provider, model);
|
|
1125
|
+
return `
|
|
1126
|
+
// ============================================================
|
|
1127
|
+
// ReAct Agent: Thought \u2192 Action \u2192 Observation loop
|
|
1128
|
+
// ============================================================
|
|
1129
|
+
//
|
|
1130
|
+
// The agent follows the ReAct pattern (Yao et al., 2022):
|
|
1131
|
+
// 1. Think: reason about the current state
|
|
1132
|
+
// 2. Act: call a tool or FINISH
|
|
1133
|
+
// 3. Observe: receive tool output, feed back into next thought
|
|
1134
|
+
//
|
|
1135
|
+
// The reactLoop node manages iteration and message accumulation.
|
|
1136
|
+
// think + act run inside the loop's scoped iteration.
|
|
1137
|
+
|
|
1138
|
+
// ============================================================
|
|
1139
|
+
// LLM TYPES
|
|
1140
|
+
// ============================================================
|
|
1141
|
+
|
|
1142
|
+
${LLM_CORE_TYPES}
|
|
1143
|
+
|
|
1144
|
+
${providerCode}
|
|
1145
|
+
const MAX_STEPS = 10;
|
|
1146
|
+
|
|
1147
|
+
const REACT_PROMPT = \`You are a ReAct (Reasoning + Acting) agent.
|
|
1148
|
+
|
|
1149
|
+
For each step, you MUST respond in this exact format:
|
|
1150
|
+
Thought: [Your reasoning about what to do next]
|
|
1151
|
+
Action: [tool_name] or [FINISH]
|
|
1152
|
+
Action Input: [input for the tool, or final answer if FINISH]
|
|
1153
|
+
|
|
1154
|
+
Available tools: search, calculate
|
|
1155
|
+
|
|
1156
|
+
When you have the final answer:
|
|
1157
|
+
Thought: I now have enough information to answer
|
|
1158
|
+
Action: FINISH
|
|
1159
|
+
Action Input: [Your final answer here]\`;
|
|
1160
|
+
|
|
1161
|
+
// Parse ReAct response format
|
|
1162
|
+
function parseReactResponse(content: string): {
|
|
1163
|
+
thought: string;
|
|
1164
|
+
action: string;
|
|
1165
|
+
actionInput: string;
|
|
1166
|
+
} {
|
|
1167
|
+
const thoughtMatch = content.match(/Thought:\\s*(.+?)(?=\\nAction:|$)/s);
|
|
1168
|
+
const actionMatch = content.match(/Action:\\s*(.+?)(?=\\nAction Input:|$)/s);
|
|
1169
|
+
const inputMatch = content.match(/Action Input:\\s*(.+?)$/s);
|
|
1170
|
+
|
|
1171
|
+
return {
|
|
1172
|
+
thought: thoughtMatch?.[1]?.trim() ?? '',
|
|
1173
|
+
action: actionMatch?.[1]?.trim() ?? 'FINISH',
|
|
1174
|
+
actionInput: inputMatch?.[1]?.trim() ?? '',
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// ============================================================
|
|
1179
|
+
// TOOL IMPLEMENTATIONS
|
|
1180
|
+
// ============================================================
|
|
1181
|
+
|
|
1182
|
+
// Replace with real tools
|
|
1183
|
+
const TOOL_IMPLEMENTATIONS: Record<string, (input: string) => Promise<string>> = {
|
|
1184
|
+
async search(input: string): Promise<string> {
|
|
1185
|
+
return '[Search results for: ' + input + ']';
|
|
1186
|
+
},
|
|
1187
|
+
async calculate(input: string): Promise<string> {
|
|
1188
|
+
return '[Calculation result for: ' + input + ']';
|
|
1189
|
+
},
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
// ============================================================
|
|
1193
|
+
// NODES
|
|
1194
|
+
// ============================================================
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* ReAct loop: manages Think\u2192Act\u2192Observe iterations.
|
|
1198
|
+
* Accumulates messages and terminates when action is FINISH or max steps.
|
|
1199
|
+
*
|
|
1200
|
+
* @flowWeaver nodeType
|
|
1201
|
+
* @label ReAct Loop
|
|
1202
|
+
* @input execute [order:0] - Execute
|
|
1203
|
+
* @input task [order:1] - Task for the agent
|
|
1204
|
+
* @input [maxSteps] [order:2] - Maximum reasoning steps (default: 10)
|
|
1205
|
+
* @input success scope:step [order:0] - Iteration succeeded
|
|
1206
|
+
* @input failure scope:step [order:1] - Iteration failed
|
|
1207
|
+
* @input thought scope:step [order:2] - Agent's reasoning
|
|
1208
|
+
* @input action scope:step [order:3] - Action name
|
|
1209
|
+
* @input actionInput scope:step [order:4] - Action input / final answer
|
|
1210
|
+
* @input observation scope:step [order:5] - Tool observation
|
|
1211
|
+
* @output onSuccess [order:0] - On Success (finished)
|
|
1212
|
+
* @output onFailure [order:1] - On Failure
|
|
1213
|
+
* @output start scope:step [order:0] - Triggers iteration
|
|
1214
|
+
* @output messages scope:step [order:1] - Current messages for LLM
|
|
1215
|
+
* @output answer [order:2] - Final answer
|
|
1216
|
+
*/
|
|
1217
|
+
async function reactLoop(
|
|
1218
|
+
execute: boolean,
|
|
1219
|
+
task: string,
|
|
1220
|
+
maxSteps: number = MAX_STEPS,
|
|
1221
|
+
step: (start: boolean, messages: LLMMessage[]) => Promise<{
|
|
1222
|
+
success: boolean;
|
|
1223
|
+
failure: boolean;
|
|
1224
|
+
thought: string;
|
|
1225
|
+
action: string;
|
|
1226
|
+
actionInput: string;
|
|
1227
|
+
observation: string;
|
|
1228
|
+
}>
|
|
1229
|
+
): Promise<{ onSuccess: boolean; onFailure: boolean; answer: string }> {
|
|
1230
|
+
if (!execute) {
|
|
1231
|
+
return { onSuccess: false, onFailure: false, answer: '' };
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
const messages: LLMMessage[] = [{ role: 'user', content: task }];
|
|
1235
|
+
|
|
1236
|
+
for (let i = 0; i < maxSteps; i++) {
|
|
1237
|
+
const result = await step(true, messages);
|
|
1238
|
+
|
|
1239
|
+
if (result.failure) {
|
|
1240
|
+
return { onSuccess: false, onFailure: true, answer: '' };
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Append thought + action as assistant message
|
|
1244
|
+
messages.push({
|
|
1245
|
+
role: 'assistant',
|
|
1246
|
+
content: \`Thought: \${result.thought}\\nAction: \${result.action}\\nAction Input: \${result.actionInput}\`,
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
// Check termination
|
|
1250
|
+
if (result.action.toUpperCase() === 'FINISH') {
|
|
1251
|
+
return { onSuccess: true, onFailure: false, answer: result.actionInput };
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Append observation
|
|
1255
|
+
if (result.observation) {
|
|
1256
|
+
messages.push({ role: 'user', content: \`Observation: \${result.observation}\` });
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
return { onSuccess: true, onFailure: false, answer: 'Max steps reached' };
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* ReAct reasoning step: calls LLM and parses Thought/Action/ActionInput
|
|
1265
|
+
*
|
|
1266
|
+
* @flowWeaver nodeType
|
|
1267
|
+
* @label Think
|
|
1268
|
+
* @input execute [order:0] - Execute
|
|
1269
|
+
* @input messages [order:1] - Conversation history
|
|
1270
|
+
* @output onSuccess [order:0] - On Success
|
|
1271
|
+
* @output onFailure [order:1] - On Failure
|
|
1272
|
+
* @output thought [order:2] - Agent's reasoning
|
|
1273
|
+
* @output action [order:3] - Action to take (tool name or FINISH)
|
|
1274
|
+
* @output actionInput [order:4] - Input for action or final answer
|
|
1275
|
+
*/
|
|
1276
|
+
async function think(
|
|
1277
|
+
execute: boolean,
|
|
1278
|
+
messages: LLMMessage[]
|
|
1279
|
+
): Promise<{
|
|
1280
|
+
onSuccess: boolean;
|
|
1281
|
+
onFailure: boolean;
|
|
1282
|
+
thought: string;
|
|
1283
|
+
action: string;
|
|
1284
|
+
actionInput: string;
|
|
1285
|
+
}> {
|
|
1286
|
+
if (!execute) {
|
|
1287
|
+
return { onSuccess: false, onFailure: false, thought: '', action: '', actionInput: '' };
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
try {
|
|
1291
|
+
const response = await llmProvider.chat(messages, { systemPrompt: REACT_PROMPT });
|
|
1292
|
+
const parsed = parseReactResponse(response.content ?? '');
|
|
1293
|
+
|
|
1294
|
+
return {
|
|
1295
|
+
onSuccess: true,
|
|
1296
|
+
onFailure: false,
|
|
1297
|
+
thought: parsed.thought,
|
|
1298
|
+
action: parsed.action,
|
|
1299
|
+
actionInput: parsed.actionInput,
|
|
1300
|
+
};
|
|
1301
|
+
} catch {
|
|
1302
|
+
return { onSuccess: false, onFailure: true, thought: '', action: '', actionInput: '' };
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Execute action and return observation
|
|
1308
|
+
*
|
|
1309
|
+
* @flowWeaver nodeType
|
|
1310
|
+
* @label Act
|
|
1311
|
+
* @input execute [order:0] - Execute
|
|
1312
|
+
* @input action [order:1] - Tool name to execute
|
|
1313
|
+
* @input actionInput [order:2] - Input for tool
|
|
1314
|
+
* @output onSuccess [order:0] - On Success
|
|
1315
|
+
* @output onFailure [order:1] - On Failure
|
|
1316
|
+
* @output observation [order:2] - Tool observation result
|
|
1317
|
+
*/
|
|
1318
|
+
async function act(
|
|
1319
|
+
execute: boolean,
|
|
1320
|
+
action: string,
|
|
1321
|
+
actionInput: string
|
|
1322
|
+
): Promise<{
|
|
1323
|
+
onSuccess: boolean;
|
|
1324
|
+
onFailure: boolean;
|
|
1325
|
+
observation: string;
|
|
1326
|
+
}> {
|
|
1327
|
+
if (!execute) {
|
|
1328
|
+
return { onSuccess: false, onFailure: false, observation: '' };
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
const tool = TOOL_IMPLEMENTATIONS[action.toLowerCase()];
|
|
1332
|
+
if (!tool) {
|
|
1333
|
+
return {
|
|
1334
|
+
onSuccess: true,
|
|
1335
|
+
onFailure: false,
|
|
1336
|
+
observation: 'Unknown tool: ' + action,
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
try {
|
|
1341
|
+
const observation = await tool(actionInput);
|
|
1342
|
+
return { onSuccess: true, onFailure: false, observation };
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
return { onSuccess: false, onFailure: true, observation: '' };
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// ============================================================
|
|
1349
|
+
// WORKFLOW
|
|
1350
|
+
// ============================================================
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* ReAct Agent: iterative Thought\u2192Action\u2192Observation loop
|
|
1354
|
+
*
|
|
1355
|
+
* @flowWeaver workflow
|
|
1356
|
+
* @node loop reactLoop [color: "purple"] [icon: "psychology"]
|
|
1357
|
+
* @node thinking think loop.step [color: "blue"] [icon: "autoAwesome"]
|
|
1358
|
+
* @node acting act loop.step [color: "orange"] [icon: "bolt"]
|
|
1359
|
+
* @connect Start.execute -> loop.execute
|
|
1360
|
+
* @connect Start.task -> loop.task
|
|
1361
|
+
* @connect loop.start:step -> thinking.execute
|
|
1362
|
+
* @connect loop.messages:step -> thinking.messages
|
|
1363
|
+
* @connect thinking.onSuccess -> acting.execute
|
|
1364
|
+
* @connect thinking.action -> acting.action
|
|
1365
|
+
* @connect thinking.actionInput -> acting.actionInput
|
|
1366
|
+
* @connect thinking.thought -> loop.thought:step
|
|
1367
|
+
* @connect thinking.action -> loop.action:step
|
|
1368
|
+
* @connect thinking.actionInput -> loop.actionInput:step
|
|
1369
|
+
* @connect thinking.onFailure -> loop.failure:step
|
|
1370
|
+
* @connect acting.observation -> loop.observation:step
|
|
1371
|
+
* @connect acting.onSuccess -> loop.success:step
|
|
1372
|
+
* @connect acting.onFailure -> loop.failure:step
|
|
1373
|
+
* @connect loop.onSuccess -> Exit.onSuccess
|
|
1374
|
+
* @connect loop.onFailure -> Exit.onFailure
|
|
1375
|
+
* @connect loop.answer -> Exit.answer
|
|
1376
|
+
* @param execute [order:0] - Execute
|
|
1377
|
+
* @param task [order:1] - Task for the agent
|
|
1378
|
+
* @returns onSuccess [order:0] - On Success
|
|
1379
|
+
* @returns onFailure [order:1] - On Failure
|
|
1380
|
+
* @returns answer [order:2] - Final answer
|
|
1381
|
+
*/
|
|
1382
|
+
export async function ${workflowName}(
|
|
1383
|
+
execute: boolean,
|
|
1384
|
+
params: { task: string }
|
|
1385
|
+
): Promise<{
|
|
1386
|
+
onSuccess: boolean;
|
|
1387
|
+
onFailure: boolean;
|
|
1388
|
+
answer: string;
|
|
1389
|
+
}> {
|
|
1390
|
+
throw new Error('Compile with: fw compile <file>');
|
|
1391
|
+
}
|
|
1392
|
+
`.trim();
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
// src/cli/templates/workflows/ai-rag.ts
|
|
1397
|
+
var aiRagTemplate = {
|
|
1398
|
+
id: "ai-rag",
|
|
1399
|
+
name: "RAG Pipeline",
|
|
1400
|
+
description: "Retrieval-Augmented Generation for knowledge-based Q&A",
|
|
1401
|
+
category: "ai",
|
|
1402
|
+
configSchema: aiConfigSchema,
|
|
1403
|
+
generate: (opts) => {
|
|
1404
|
+
const { workflowName, config } = opts;
|
|
1405
|
+
const provider = config?.provider || "mock";
|
|
1406
|
+
const model = config?.model || "";
|
|
1407
|
+
const providerCode = provider === "mock" ? `
|
|
1408
|
+
// Mock provider for testing - replace with real provider
|
|
1409
|
+
${LLM_MOCK_PROVIDER}
|
|
1410
|
+
` : getProviderCode(provider, model);
|
|
1411
|
+
return `
|
|
1412
|
+
// ============================================================
|
|
1413
|
+
// LLM TYPES - Provider-agnostic interface
|
|
1414
|
+
// ============================================================
|
|
1415
|
+
|
|
1416
|
+
${LLM_SIMPLE_TYPES}
|
|
1417
|
+
|
|
1418
|
+
${providerCode}
|
|
1419
|
+
|
|
1420
|
+
// Document store (replace with vector DB like Pinecone, Weaviate, etc.)
|
|
1421
|
+
interface Document {
|
|
1422
|
+
id: string;
|
|
1423
|
+
content: string;
|
|
1424
|
+
metadata?: Record<string, unknown>;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const documentStore: Document[] = [
|
|
1428
|
+
// TODO: Add your documents or connect to vector database
|
|
1429
|
+
{ id: '1', content: 'Flow Weaver is a visual programming tool.' },
|
|
1430
|
+
{ id: '2', content: 'It uses typed ports for data flow.' },
|
|
1431
|
+
];
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* Retrieves relevant documents for a query
|
|
1435
|
+
*
|
|
1436
|
+
* @flowWeaver nodeType
|
|
1437
|
+
* @label Retrieve
|
|
1438
|
+
* @input query [order:1] - Search query
|
|
1439
|
+
* @input [topK] [order:2] - Number of results (default 3)
|
|
1440
|
+
* @input execute [order:0] - Execute
|
|
1441
|
+
* @output documents [order:2] [hidden] - Retrieved documents
|
|
1442
|
+
* @output context [order:3] - Combined document text
|
|
1443
|
+
* @output onSuccess [order:0] - On Success
|
|
1444
|
+
* @output onFailure [order:1] - On Failure
|
|
1445
|
+
*/
|
|
1446
|
+
async function retrieve(
|
|
1447
|
+
execute: boolean,
|
|
1448
|
+
query: string,
|
|
1449
|
+
topK?: number
|
|
1450
|
+
): Promise<{
|
|
1451
|
+
onSuccess: boolean;
|
|
1452
|
+
onFailure: boolean;
|
|
1453
|
+
documents: Document[];
|
|
1454
|
+
context: string;
|
|
1455
|
+
}> {
|
|
1456
|
+
if (!execute) {
|
|
1457
|
+
return { onSuccess: false, onFailure: false, documents: [], context: '' };
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
const k = topK ?? 3;
|
|
1461
|
+
|
|
1462
|
+
// TODO: Replace with actual vector similarity search
|
|
1463
|
+
// For now, simple keyword matching
|
|
1464
|
+
const queryLower = query.toLowerCase();
|
|
1465
|
+
const scored = documentStore
|
|
1466
|
+
.map((doc) => ({
|
|
1467
|
+
doc,
|
|
1468
|
+
score: doc.content.toLowerCase().includes(queryLower) ? 1 : 0,
|
|
1469
|
+
}))
|
|
1470
|
+
.filter((s) => s.score > 0)
|
|
1471
|
+
.slice(0, k)
|
|
1472
|
+
.map((s) => s.doc);
|
|
1473
|
+
|
|
1474
|
+
const context = scored.map((d) => d.content).join('\\n\\n');
|
|
1475
|
+
|
|
1476
|
+
return {
|
|
1477
|
+
onSuccess: true,
|
|
1478
|
+
onFailure: false,
|
|
1479
|
+
documents: scored,
|
|
1480
|
+
context,
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Generates answer using retrieved context
|
|
1486
|
+
*
|
|
1487
|
+
* @flowWeaver nodeType
|
|
1488
|
+
* @label Generate
|
|
1489
|
+
* @input question [order:1] - User question
|
|
1490
|
+
* @input context [order:2] - Retrieved context
|
|
1491
|
+
* @input execute [order:0] - Execute
|
|
1492
|
+
* @output answer [order:2] - Generated answer
|
|
1493
|
+
* @output sourcesUsed [order:3] - Number of sources used
|
|
1494
|
+
* @output onSuccess [order:0] - On Success
|
|
1495
|
+
* @output onFailure [order:1] - On Failure
|
|
1496
|
+
*/
|
|
1497
|
+
async function generate(
|
|
1498
|
+
execute: boolean,
|
|
1499
|
+
question: string,
|
|
1500
|
+
context: string
|
|
1501
|
+
): Promise<{
|
|
1502
|
+
onSuccess: boolean;
|
|
1503
|
+
onFailure: boolean;
|
|
1504
|
+
answer: string;
|
|
1505
|
+
sourcesUsed: number;
|
|
1506
|
+
}> {
|
|
1507
|
+
if (!execute) {
|
|
1508
|
+
return { onSuccess: false, onFailure: false, answer: '', sourcesUsed: 0 };
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
const prompt = \`Answer the question based on the following context.
|
|
1512
|
+
If the context doesn't contain relevant information, say so.
|
|
1513
|
+
|
|
1514
|
+
Context:
|
|
1515
|
+
\${context}
|
|
1516
|
+
|
|
1517
|
+
Question: \${question}
|
|
1518
|
+
|
|
1519
|
+
Answer:\`;
|
|
1520
|
+
|
|
1521
|
+
const response = await llmProvider.chat([{ role: 'user', content: prompt }]);
|
|
1522
|
+
const sourcesUsed = context
|
|
1523
|
+
.split('\\n\\n')
|
|
1524
|
+
.filter((s) => s.trim()).length;
|
|
1525
|
+
|
|
1526
|
+
return {
|
|
1527
|
+
onSuccess: true,
|
|
1528
|
+
onFailure: false,
|
|
1529
|
+
answer: response.content ?? '',
|
|
1530
|
+
sourcesUsed,
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* RAG Pipeline for knowledge-based Q&A
|
|
1536
|
+
*
|
|
1537
|
+
* @flowWeaver workflow
|
|
1538
|
+
* @node retriever retrieve [color: "teal"] [icon: "search"] [suppress: "UNUSED_OUTPUT_PORT", "DESIGN_ASYNC_NO_ERROR_PATH"]
|
|
1539
|
+
* @node generator generate [color: "purple"] [icon: "autoAwesome"]
|
|
1540
|
+
* @connect Start.execute -> retriever.execute
|
|
1541
|
+
* @connect Start.question -> retriever.query
|
|
1542
|
+
* @connect Start.topK -> retriever.topK
|
|
1543
|
+
* @connect Start.question -> generator.question
|
|
1544
|
+
* @connect retriever.context -> generator.context
|
|
1545
|
+
* @connect retriever.onSuccess -> generator.execute
|
|
1546
|
+
* @connect generator.answer -> Exit.answer
|
|
1547
|
+
* @connect generator.sourcesUsed -> Exit.sourcesUsed
|
|
1548
|
+
* @connect generator.onSuccess -> Exit.onSuccess
|
|
1549
|
+
* @connect generator.onFailure -> Exit.onFailure
|
|
1550
|
+
* @param execute [order:0] - Execute
|
|
1551
|
+
* @param question [order:1] - User's question
|
|
1552
|
+
* @param topK [order:2] - Number of documents to retrieve
|
|
1553
|
+
* @returns onSuccess [order:0] - On Success
|
|
1554
|
+
* @returns onFailure [order:1] - On Failure
|
|
1555
|
+
* @returns answer [order:2] - Generated answer
|
|
1556
|
+
* @returns sourcesUsed [order:3] - Number of sources used
|
|
1557
|
+
*/
|
|
1558
|
+
export async function ${workflowName}(
|
|
1559
|
+
execute: boolean,
|
|
1560
|
+
params: { question: string; topK?: number }
|
|
1561
|
+
): Promise<{
|
|
1562
|
+
onSuccess: boolean;
|
|
1563
|
+
onFailure: boolean;
|
|
1564
|
+
answer: string;
|
|
1565
|
+
sourcesUsed: number;
|
|
1566
|
+
}> {
|
|
1567
|
+
throw new Error('Compile with: fw compile <file>');
|
|
1568
|
+
}
|
|
1569
|
+
`.trim();
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
// src/cli/templates/workflows/ai-chat.ts
|
|
1574
|
+
var aiChatTemplate = {
|
|
1575
|
+
id: "ai-chat",
|
|
1576
|
+
name: "Stateful Chat",
|
|
1577
|
+
description: "Conversational AI with memory management",
|
|
1578
|
+
category: "ai",
|
|
1579
|
+
configSchema: aiConfigSchema,
|
|
1580
|
+
generate: (opts) => {
|
|
1581
|
+
const { workflowName, config } = opts;
|
|
1582
|
+
const provider = config?.provider || "mock";
|
|
1583
|
+
const model = config?.model || "";
|
|
1584
|
+
const providerCode = provider === "mock" ? `
|
|
1585
|
+
// Mock provider for testing - replace with real provider
|
|
1586
|
+
${LLM_MOCK_PROVIDER}
|
|
1587
|
+
` : getProviderCode(provider, model);
|
|
1588
|
+
return `
|
|
1589
|
+
// ============================================================
|
|
1590
|
+
// LLM TYPES - Provider-agnostic interface
|
|
1591
|
+
// ============================================================
|
|
1592
|
+
|
|
1593
|
+
${LLM_SIMPLE_TYPES}
|
|
1594
|
+
|
|
1595
|
+
${providerCode}
|
|
1596
|
+
|
|
1597
|
+
// Conversation memory store
|
|
1598
|
+
const conversations: Map<string, LLMMessage[]> = new Map();
|
|
1599
|
+
|
|
1600
|
+
const DEFAULT_SYSTEM_PROMPT = \`You are a helpful AI assistant.
|
|
1601
|
+
Be concise but friendly. Remember context from earlier in the conversation.\`;
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* Manages conversation memory
|
|
1605
|
+
*
|
|
1606
|
+
* @flowWeaver nodeType
|
|
1607
|
+
* @label Memory
|
|
1608
|
+
* @input conversationId [order:1] - Conversation identifier
|
|
1609
|
+
* @input [newMessage] [order:2] - Message to add
|
|
1610
|
+
* @input [maxHistory=50] [order:3] - Max messages to retain
|
|
1611
|
+
* @input execute [order:0] - Execute
|
|
1612
|
+
* @output history [order:2] - Conversation history
|
|
1613
|
+
* @output onSuccess [order:0] - On Success
|
|
1614
|
+
* @output onFailure [order:1] - On Failure
|
|
1615
|
+
*/
|
|
1616
|
+
function memory(
|
|
1617
|
+
execute: boolean,
|
|
1618
|
+
conversationId: string,
|
|
1619
|
+
newMessage?: LLMMessage,
|
|
1620
|
+
maxHistory?: number
|
|
1621
|
+
): {
|
|
1622
|
+
onSuccess: boolean;
|
|
1623
|
+
onFailure: boolean;
|
|
1624
|
+
history: LLMMessage[];
|
|
1625
|
+
} {
|
|
1626
|
+
if (!execute) {
|
|
1627
|
+
return { onSuccess: false, onFailure: false, history: [] };
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (!conversations.has(conversationId)) {
|
|
1631
|
+
conversations.set(conversationId, []);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
const history = conversations.get(conversationId)!;
|
|
1635
|
+
|
|
1636
|
+
if (newMessage) {
|
|
1637
|
+
history.push(newMessage);
|
|
1638
|
+
const max = maxHistory ?? 50;
|
|
1639
|
+
while (history.length > max) {
|
|
1640
|
+
history.shift();
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
return { onSuccess: true, onFailure: false, history: [...history] };
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Generates chat response
|
|
1649
|
+
*
|
|
1650
|
+
* @flowWeaver nodeType
|
|
1651
|
+
* @label Chat
|
|
1652
|
+
* @input history [order:1] - Conversation history
|
|
1653
|
+
* @input [systemPrompt] [order:2] - System prompt
|
|
1654
|
+
* @input execute [order:0] - Execute
|
|
1655
|
+
* @output response [order:2] - Assistant response
|
|
1656
|
+
* @output responseMessage [order:3] - Response as LLMMessage
|
|
1657
|
+
* @output onSuccess [order:0] - On Success
|
|
1658
|
+
* @output onFailure [order:1] - On Failure
|
|
1659
|
+
*/
|
|
1660
|
+
async function chat(
|
|
1661
|
+
execute: boolean,
|
|
1662
|
+
history: LLMMessage[],
|
|
1663
|
+
systemPrompt?: string
|
|
1664
|
+
): Promise<{
|
|
1665
|
+
onSuccess: boolean;
|
|
1666
|
+
onFailure: boolean;
|
|
1667
|
+
response: string;
|
|
1668
|
+
responseMessage: LLMMessage;
|
|
1669
|
+
}> {
|
|
1670
|
+
if (!execute) {
|
|
1671
|
+
return {
|
|
1672
|
+
onSuccess: false,
|
|
1673
|
+
onFailure: false,
|
|
1674
|
+
response: '',
|
|
1675
|
+
responseMessage: { role: 'assistant', content: '' },
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
const response = await llmProvider.chat(history, {
|
|
1680
|
+
systemPrompt: systemPrompt ?? DEFAULT_SYSTEM_PROMPT,
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
const content = response.content ?? '';
|
|
1684
|
+
|
|
1685
|
+
return {
|
|
1686
|
+
onSuccess: true,
|
|
1687
|
+
onFailure: false,
|
|
1688
|
+
response: content,
|
|
1689
|
+
responseMessage: { role: 'assistant', content },
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Stateful chat with conversation memory
|
|
1695
|
+
*
|
|
1696
|
+
* @flowWeaver workflow
|
|
1697
|
+
* @node mem memory [color: "teal"] [icon: "database"]
|
|
1698
|
+
* @node respond chat [color: "purple"] [icon: "campaign"] [suppress: "AGENT_LLM_NO_FALLBACK"]
|
|
1699
|
+
* @node saveMem memory [color: "teal"] [icon: "database"] [suppress: "UNUSED_OUTPUT_PORT"]
|
|
1700
|
+
* @connect Start.execute -> mem.execute
|
|
1701
|
+
* @connect Start.conversationId -> mem.conversationId
|
|
1702
|
+
* @connect Start.userMessage -> mem.newMessage
|
|
1703
|
+
* @connect mem.history -> respond.history
|
|
1704
|
+
* @connect mem.onSuccess -> respond.execute
|
|
1705
|
+
* @connect Start.conversationId -> saveMem.conversationId
|
|
1706
|
+
* @connect respond.responseMessage -> saveMem.newMessage
|
|
1707
|
+
* @connect respond.onSuccess -> saveMem.execute
|
|
1708
|
+
* @connect respond.onFailure -> Exit.onFailure
|
|
1709
|
+
* @connect respond.response -> Exit.response
|
|
1710
|
+
* @connect saveMem.onSuccess -> Exit.onSuccess
|
|
1711
|
+
* @param execute [order:0] - Execute
|
|
1712
|
+
* @param conversationId [order:1] - Unique conversation ID
|
|
1713
|
+
* @param userMessage [order:2] - User's message
|
|
1714
|
+
* @param systemPrompt [order:3] - System prompt (optional)
|
|
1715
|
+
* @returns onSuccess [order:0] - On Success
|
|
1716
|
+
* @returns onFailure [order:1] - On Failure
|
|
1717
|
+
* @returns response [order:2] - Assistant's response
|
|
1718
|
+
*/
|
|
1719
|
+
export async function ${workflowName}(
|
|
1720
|
+
execute: boolean,
|
|
1721
|
+
params: { conversationId: string; userMessage: LLMMessage; systemPrompt?: string }
|
|
1722
|
+
): Promise<{
|
|
1723
|
+
onSuccess: boolean;
|
|
1724
|
+
onFailure: boolean;
|
|
1725
|
+
response: string;
|
|
1726
|
+
}> {
|
|
1727
|
+
throw new Error('Compile with: fw compile <file>');
|
|
1728
|
+
}
|
|
1729
|
+
`.trim();
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1733
|
+
// src/cli/templates/workflows/aggregator.ts
|
|
1734
|
+
var aggregatorTemplate = {
|
|
1735
|
+
id: "aggregator",
|
|
1736
|
+
name: "Aggregator",
|
|
1737
|
+
description: "Combine multiple data sources into one output",
|
|
1738
|
+
category: "data-processing",
|
|
1739
|
+
generate: (opts) => {
|
|
1740
|
+
const { workflowName, async: isAsync } = opts;
|
|
1741
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
1742
|
+
const returnType = isAsync ? "Promise<{ onSuccess: boolean; onFailure: boolean; aggregated: any }>" : "{ onSuccess: boolean; onFailure: boolean; aggregated: any }";
|
|
1743
|
+
return `
|
|
1744
|
+
// Use @expression for most functions, normal mode only for error-with-data or void side-effects
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
* Fetches data from source A
|
|
1748
|
+
*
|
|
1749
|
+
* @flowWeaver nodeType
|
|
1750
|
+
* @expression
|
|
1751
|
+
* @label Fetch Source A
|
|
1752
|
+
* @input query [order:0] - Query parameters
|
|
1753
|
+
* @output data [order:0] - Data from source A
|
|
1754
|
+
*/
|
|
1755
|
+
function fetchSourceA(query: any): { data: any } {
|
|
1756
|
+
// TODO: Fetch from source A
|
|
1757
|
+
return { data: { source: "A", items: [] } };
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Fetches data from source B
|
|
1762
|
+
*
|
|
1763
|
+
* @flowWeaver nodeType
|
|
1764
|
+
* @expression
|
|
1765
|
+
* @label Fetch Source B
|
|
1766
|
+
* @input query [order:0] - Query parameters
|
|
1767
|
+
* @output data [order:0] - Data from source B
|
|
1768
|
+
*/
|
|
1769
|
+
function fetchSourceB(query: any): { data: any } {
|
|
1770
|
+
// TODO: Fetch from source B
|
|
1771
|
+
return { data: { source: "B", items: [] } };
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* Combines data from multiple sources
|
|
1776
|
+
*
|
|
1777
|
+
* @flowWeaver nodeType
|
|
1778
|
+
* @expression
|
|
1779
|
+
* @label Combine Data
|
|
1780
|
+
* @input dataA [order:0] - Data from source A
|
|
1781
|
+
* @input dataB [order:1] - Data from source B
|
|
1782
|
+
* @output aggregated [order:0] - Combined data
|
|
1783
|
+
*/
|
|
1784
|
+
function combineData(dataA: any, dataB: any): { aggregated: any } {
|
|
1785
|
+
// TODO: Implement aggregation logic
|
|
1786
|
+
return {
|
|
1787
|
+
aggregated: {
|
|
1788
|
+
sources: [dataA?.source, dataB?.source],
|
|
1789
|
+
totalItems: (dataA?.items?.length || 0) + (dataB?.items?.length || 0),
|
|
1790
|
+
items: [...(dataA?.items || []), ...(dataB?.items || [])],
|
|
1791
|
+
aggregatedAt: new Date().toISOString()
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
/**
|
|
1797
|
+
* @flowWeaver workflow
|
|
1798
|
+
* @node sourceA fetchSourceA [color: "blue"] [icon: "download"]
|
|
1799
|
+
* @node sourceB fetchSourceB [color: "blue"] [icon: "download"]
|
|
1800
|
+
* @node combiner combineData [color: "purple"] [icon: "callMerge"]
|
|
1801
|
+
* @connect Start.execute -> sourceA.execute
|
|
1802
|
+
* @connect Start.execute -> sourceB.execute
|
|
1803
|
+
* @connect Start.query -> sourceA.query
|
|
1804
|
+
* @connect Start.query -> sourceB.query
|
|
1805
|
+
* @connect sourceA.onSuccess -> combiner.execute
|
|
1806
|
+
* @connect sourceA.data -> combiner.dataA
|
|
1807
|
+
* @connect sourceB.data -> combiner.dataB
|
|
1808
|
+
* @connect combiner.aggregated -> Exit.aggregated
|
|
1809
|
+
* @connect combiner.onSuccess -> Exit.onSuccess
|
|
1810
|
+
* @connect combiner.onFailure -> Exit.onFailure
|
|
1811
|
+
* @param execute [order:0] - Execute
|
|
1812
|
+
* @param query [order:1] - Query parameters for fetching
|
|
1813
|
+
* @returns onSuccess [order:0] - On Success
|
|
1814
|
+
* @returns onFailure [order:1] - On Failure
|
|
1815
|
+
* @returns aggregated [order:2] - Combined data from all sources
|
|
1816
|
+
*/
|
|
1817
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
1818
|
+
execute: boolean,
|
|
1819
|
+
params: { query: any }
|
|
1820
|
+
): ${returnType} {
|
|
1821
|
+
throw new Error("Compile with: fw compile <file>");
|
|
1822
|
+
}
|
|
1823
|
+
`.trim();
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
|
|
1827
|
+
// src/cli/templates/workflows/webhook.ts
|
|
1828
|
+
var webhookTemplate = {
|
|
1829
|
+
id: "webhook",
|
|
1830
|
+
name: "Webhook Handler",
|
|
1831
|
+
description: "HTTP-triggered workflow with request/response handling",
|
|
1832
|
+
category: "integration",
|
|
1833
|
+
generate: (opts) => {
|
|
1834
|
+
const { workflowName, async: isAsync } = opts;
|
|
1835
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
1836
|
+
const returnType = isAsync ? "Promise<{ onSuccess: boolean; onFailure: boolean; statusCode: number; body: any }>" : "{ onSuccess: boolean; onFailure: boolean; statusCode: number; body: any }";
|
|
1837
|
+
return `
|
|
1838
|
+
/**
|
|
1839
|
+
* Validates incoming webhook request
|
|
1840
|
+
*
|
|
1841
|
+
* @flowWeaver nodeType
|
|
1842
|
+
* @label Validate Request
|
|
1843
|
+
* @input headers [order:1] - Request headers
|
|
1844
|
+
* @input body [order:2] - Request body
|
|
1845
|
+
* @input execute [order:0] - Execute
|
|
1846
|
+
* @output isValid [order:2] - Whether request is valid
|
|
1847
|
+
* @output validatedData [order:3] - Validated request data
|
|
1848
|
+
* @output error [order:4] - Validation error message
|
|
1849
|
+
* @output onSuccess [order:0] - On Success
|
|
1850
|
+
* @output onFailure [order:1] - On Failure
|
|
1851
|
+
*/
|
|
1852
|
+
function validateRequest(
|
|
1853
|
+
execute: boolean,
|
|
1854
|
+
headers: Record<string, string>,
|
|
1855
|
+
body: any
|
|
1856
|
+
): { onSuccess: boolean; onFailure: boolean; isValid: boolean; validatedData: any; error: string } {
|
|
1857
|
+
if (!execute) {
|
|
1858
|
+
return { onSuccess: false, onFailure: false, isValid: false, validatedData: null, error: "" };
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
// TODO: Add your validation logic
|
|
1862
|
+
// Example: Check API key, validate body schema, etc.
|
|
1863
|
+
const apiKey = headers["x-api-key"] || headers["authorization"];
|
|
1864
|
+
|
|
1865
|
+
if (!apiKey) {
|
|
1866
|
+
return { onSuccess: false, onFailure: true, isValid: false, validatedData: null, error: "Missing API key" };
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
return { onSuccess: true, onFailure: false, isValid: true, validatedData: body, error: "" };
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
/**
|
|
1873
|
+
* Processes the webhook payload
|
|
1874
|
+
*
|
|
1875
|
+
* @flowWeaver nodeType
|
|
1876
|
+
* @label Process Payload
|
|
1877
|
+
* @input data [order:1] - Validated payload data
|
|
1878
|
+
* @input execute [order:0] - Execute
|
|
1879
|
+
* @output result [order:2] - Processing result
|
|
1880
|
+
* @output onSuccess [order:0] - On Success
|
|
1881
|
+
* @output onFailure [order:1] - On Failure
|
|
1882
|
+
*/
|
|
1883
|
+
function processPayload(
|
|
1884
|
+
execute: boolean,
|
|
1885
|
+
data: any
|
|
1886
|
+
): { onSuccess: boolean; onFailure: boolean; result: any } {
|
|
1887
|
+
if (!execute) {
|
|
1888
|
+
return { onSuccess: false, onFailure: false, result: null };
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// TODO: Add your business logic
|
|
1892
|
+
const result = {
|
|
1893
|
+
processed: true,
|
|
1894
|
+
timestamp: new Date().toISOString(),
|
|
1895
|
+
data
|
|
1896
|
+
};
|
|
1897
|
+
|
|
1898
|
+
return { onSuccess: true, onFailure: false, result };
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/**
|
|
1902
|
+
* Formats the HTTP response
|
|
1903
|
+
*
|
|
1904
|
+
* @flowWeaver nodeType
|
|
1905
|
+
* @label Format Response
|
|
1906
|
+
* @input result [order:1] - Processing result
|
|
1907
|
+
* @input error [order:2] - Error message if any
|
|
1908
|
+
* @input isSuccess [order:3] - Whether processing succeeded
|
|
1909
|
+
* @input execute [order:0] - Execute
|
|
1910
|
+
* @output statusCode [order:2] - HTTP status code
|
|
1911
|
+
* @output body [order:3] - Response body
|
|
1912
|
+
* @output onSuccess [order:0] - On Success
|
|
1913
|
+
* @output onFailure [order:1] - On Failure
|
|
1914
|
+
*/
|
|
1915
|
+
function formatResponse(
|
|
1916
|
+
execute: boolean,
|
|
1917
|
+
result: any,
|
|
1918
|
+
error: string,
|
|
1919
|
+
isSuccess: boolean
|
|
1920
|
+
): { onSuccess: boolean; onFailure: boolean; statusCode: number; body: any } {
|
|
1921
|
+
if (!execute) {
|
|
1922
|
+
return { onSuccess: false, onFailure: false, statusCode: 0, body: null };
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
if (isSuccess) {
|
|
1926
|
+
return {
|
|
1927
|
+
onSuccess: true,
|
|
1928
|
+
onFailure: false,
|
|
1929
|
+
statusCode: 200,
|
|
1930
|
+
body: { success: true, data: result }
|
|
1931
|
+
};
|
|
1932
|
+
} else {
|
|
1933
|
+
return {
|
|
1934
|
+
onSuccess: true,
|
|
1935
|
+
onFailure: false,
|
|
1936
|
+
statusCode: 400,
|
|
1937
|
+
body: { success: false, error: error || "Processing failed" }
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
/**
|
|
1943
|
+
* @flowWeaver workflow
|
|
1944
|
+
* @node validator validateRequest [color: "green"] [icon: "verified"]
|
|
1945
|
+
* @node processor processPayload [color: "blue"] [icon: "settings"]
|
|
1946
|
+
* @node responder formatResponse [color: "cyan"] [icon: "send"]
|
|
1947
|
+
* @connect Start.execute -> validator.execute
|
|
1948
|
+
* @connect Start.headers -> validator.headers
|
|
1949
|
+
* @connect Start.body -> validator.body
|
|
1950
|
+
* @connect validator.onSuccess -> processor.execute
|
|
1951
|
+
* @connect validator.validatedData -> processor.data
|
|
1952
|
+
* @connect validator.error -> responder.error
|
|
1953
|
+
* @connect processor.onSuccess -> responder.execute
|
|
1954
|
+
* @connect processor.result -> responder.result
|
|
1955
|
+
* @connect validator.isValid -> responder.isSuccess
|
|
1956
|
+
* @connect responder.statusCode -> Exit.statusCode
|
|
1957
|
+
* @connect responder.body -> Exit.body
|
|
1958
|
+
* @connect responder.onSuccess -> Exit.onSuccess
|
|
1959
|
+
* @connect responder.onFailure -> Exit.onFailure
|
|
1960
|
+
* @param execute [order:0] - Execute
|
|
1961
|
+
* @param headers [order:1] - HTTP request headers
|
|
1962
|
+
* @param body [order:2] - HTTP request body
|
|
1963
|
+
* @returns onSuccess [order:0] - On Success
|
|
1964
|
+
* @returns onFailure [order:1] - On Failure
|
|
1965
|
+
* @returns statusCode [order:2] - HTTP response status code
|
|
1966
|
+
* @returns body [order:3] - HTTP response body
|
|
1967
|
+
*/
|
|
1968
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
1969
|
+
execute: boolean,
|
|
1970
|
+
params: { headers: Record<string, string>; body: any }
|
|
1971
|
+
): ${returnType} {
|
|
1972
|
+
throw new Error("Compile with: fw compile <file>");
|
|
1973
|
+
}
|
|
1974
|
+
`.trim();
|
|
1975
|
+
}
|
|
1976
|
+
};
|
|
1977
|
+
|
|
1978
|
+
// src/cli/templates/workflows/error-handler.ts
|
|
1979
|
+
var errorHandlerTemplate = {
|
|
1980
|
+
id: "error-handler",
|
|
1981
|
+
name: "Error Handler",
|
|
1982
|
+
description: "Try/catch/retry pattern with error recovery",
|
|
1983
|
+
category: "utility",
|
|
1984
|
+
generate: (opts) => {
|
|
1985
|
+
const { workflowName, async: isAsync } = opts;
|
|
1986
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
1987
|
+
const returnType = isAsync ? "Promise<{ onSuccess: boolean; onFailure: boolean; result: any; attempts: number; lastError?: string }>" : "{ onSuccess: boolean; onFailure: boolean; result: any; attempts: number; lastError?: string }";
|
|
1988
|
+
return `
|
|
1989
|
+
/**
|
|
1990
|
+
* Retry loop that orchestrates operation attempts
|
|
1991
|
+
* Uses scoped ports to handle retry iteration internally
|
|
1992
|
+
*
|
|
1993
|
+
* @flowWeaver nodeType
|
|
1994
|
+
* @label Retry Loop
|
|
1995
|
+
* @input data [order:1] - Input data to process
|
|
1996
|
+
* @input maxRetries [order:2] - Maximum retry attempts
|
|
1997
|
+
* @input success scope:attempt [order:0] - From operation onSuccess
|
|
1998
|
+
* @input failure scope:attempt [order:1] - From operation onFailure
|
|
1999
|
+
* @input result scope:attempt [order:2] - Operation result
|
|
2000
|
+
* @input error scope:attempt [order:3] - Error from operation
|
|
2001
|
+
* @input execute [order:0] - Execute
|
|
2002
|
+
* @output start scope:attempt [order:0] - Triggers attempt
|
|
2003
|
+
* @output attemptData scope:attempt [order:1] - Data for this attempt
|
|
2004
|
+
* @output attemptNum scope:attempt [order:2] - Current attempt number
|
|
2005
|
+
* @output finalResult [order:2] - Final result
|
|
2006
|
+
* @output attempts [order:3] - Total attempts made
|
|
2007
|
+
* @output lastError [order:4] - Last error if failed
|
|
2008
|
+
* @output onSuccess [order:0] - On Success
|
|
2009
|
+
* @output onFailure [order:1] - On Failure
|
|
2010
|
+
*/
|
|
2011
|
+
function retryLoop(
|
|
2012
|
+
execute: boolean,
|
|
2013
|
+
data: any,
|
|
2014
|
+
maxRetries: number,
|
|
2015
|
+
attempt: (start: boolean, attemptData: any, attemptNum: number) => {
|
|
2016
|
+
success: boolean;
|
|
2017
|
+
failure: boolean;
|
|
2018
|
+
result: any;
|
|
2019
|
+
error: string;
|
|
2020
|
+
}
|
|
2021
|
+
): { onSuccess: boolean; onFailure: boolean; finalResult: any; attempts: number; lastError: string } {
|
|
2022
|
+
if (!execute) {
|
|
2023
|
+
return { onSuccess: false, onFailure: false, finalResult: null, attempts: 0, lastError: '' };
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
let lastError = '';
|
|
2027
|
+
for (let attemptNum = 1; attemptNum <= maxRetries; attemptNum++) {
|
|
2028
|
+
const result = attempt(true, data, attemptNum);
|
|
2029
|
+
|
|
2030
|
+
if (result.success) {
|
|
2031
|
+
return { onSuccess: true, onFailure: false, finalResult: result.result, attempts: attemptNum, lastError: '' };
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
lastError = result.error;
|
|
2035
|
+
console.log(\`[Retry] Attempt \${attemptNum} failed: \${lastError}. \${attemptNum < maxRetries ? 'Retrying...' : 'Max retries reached.'}\`);
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
return { onSuccess: false, onFailure: true, finalResult: null, attempts: maxRetries, lastError };
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* Attempts an operation that might fail
|
|
2043
|
+
*
|
|
2044
|
+
* @flowWeaver nodeType
|
|
2045
|
+
* @label Try Operation
|
|
2046
|
+
* @input data [order:1] - Input data
|
|
2047
|
+
* @input attempt [order:2] - Current attempt number
|
|
2048
|
+
* @input execute [order:0] - Execute
|
|
2049
|
+
* @output result [order:2] - Operation result
|
|
2050
|
+
* @output error [order:3] - Error message if failed
|
|
2051
|
+
* @output onSuccess [order:0] - On Success
|
|
2052
|
+
* @output onFailure [order:1] - On Failure
|
|
2053
|
+
*/
|
|
2054
|
+
function tryOperation(
|
|
2055
|
+
execute: boolean,
|
|
2056
|
+
data: any,
|
|
2057
|
+
attempt: number
|
|
2058
|
+
): { onSuccess: boolean; onFailure: boolean; result: any; error: string } {
|
|
2059
|
+
if (!execute) {
|
|
2060
|
+
return { onSuccess: false, onFailure: false, result: null, error: '' };
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
try {
|
|
2064
|
+
// TODO: Replace with your operation that might fail
|
|
2065
|
+
// This is a placeholder that randomly fails for demonstration
|
|
2066
|
+
if (Math.random() < 0.3) {
|
|
2067
|
+
throw new Error('Simulated failure');
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
const result = { processed: data, attempt };
|
|
2071
|
+
return { onSuccess: true, onFailure: false, result, error: '' };
|
|
2072
|
+
} catch (err) {
|
|
2073
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
2074
|
+
return { onSuccess: false, onFailure: true, result: null, error };
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/**
|
|
2079
|
+
* @flowWeaver workflow
|
|
2080
|
+
* @node loop retryLoop [color: "orange"] [icon: "refresh"]
|
|
2081
|
+
* @node tryOp tryOperation loop.attempt [color: "blue"] [icon: "playArrow"]
|
|
2082
|
+
* @connect Start.execute -> loop.execute
|
|
2083
|
+
* @connect Start.data -> loop.data
|
|
2084
|
+
* @connect Start.maxRetries -> loop.maxRetries
|
|
2085
|
+
* @connect loop.start:attempt -> tryOp.execute
|
|
2086
|
+
* @connect loop.attemptData:attempt -> tryOp.data
|
|
2087
|
+
* @connect loop.attemptNum:attempt -> tryOp.attempt
|
|
2088
|
+
* @connect tryOp.result -> loop.result:attempt
|
|
2089
|
+
* @connect tryOp.error -> loop.error:attempt
|
|
2090
|
+
* @connect tryOp.onSuccess -> loop.success:attempt
|
|
2091
|
+
* @connect tryOp.onFailure -> loop.failure:attempt
|
|
2092
|
+
* @connect loop.finalResult -> Exit.result
|
|
2093
|
+
* @connect loop.attempts -> Exit.attempts
|
|
2094
|
+
* @connect loop.lastError -> Exit.lastError
|
|
2095
|
+
* @connect loop.onSuccess -> Exit.onSuccess
|
|
2096
|
+
* @connect loop.onFailure -> Exit.onFailure
|
|
2097
|
+
* @param execute [order:0] - Execute
|
|
2098
|
+
* @param data [order:1] - Input data to process
|
|
2099
|
+
* @param [maxRetries=3] [order:2] - Maximum retry attempts
|
|
2100
|
+
* @returns onSuccess [order:0] - Operation succeeded
|
|
2101
|
+
* @returns onFailure [order:1] - All retries exhausted
|
|
2102
|
+
* @returns result [order:2] - Operation result
|
|
2103
|
+
* @returns attempts [order:3] - Number of attempts made
|
|
2104
|
+
* @returns lastError [order:4] - Last error message if failed
|
|
2105
|
+
*/
|
|
2106
|
+
export ${asyncKeyword}function ${workflowName}(
|
|
2107
|
+
execute: boolean,
|
|
2108
|
+
params: { data: any; maxRetries?: number }
|
|
2109
|
+
): ${returnType} {
|
|
2110
|
+
throw new Error("Compile with: fw compile <file>");
|
|
2111
|
+
}
|
|
2112
|
+
`.trim();
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
|
|
2116
|
+
// src/cli/templates/workflows/approval.ts
|
|
2117
|
+
var configSchema2 = {
|
|
2118
|
+
input: {
|
|
2119
|
+
type: "string",
|
|
2120
|
+
label: "Input Port Name",
|
|
2121
|
+
description: "Name of the parameter that carries what needs approval",
|
|
2122
|
+
default: "request",
|
|
2123
|
+
placeholder: "request"
|
|
2124
|
+
},
|
|
2125
|
+
output: {
|
|
2126
|
+
type: "string",
|
|
2127
|
+
label: "Output Port Name",
|
|
2128
|
+
description: "Name of the return value",
|
|
2129
|
+
default: "outcome",
|
|
2130
|
+
placeholder: "outcome"
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
var approvalTemplate = {
|
|
2134
|
+
id: "approval",
|
|
2135
|
+
name: "Approval",
|
|
2136
|
+
description: "Prepare a request, pause for a person to approve it, then carry it out",
|
|
2137
|
+
category: "automation",
|
|
2138
|
+
configSchema: configSchema2,
|
|
2139
|
+
generate: (opts) => {
|
|
2140
|
+
const { workflowName, config } = opts;
|
|
2141
|
+
const input = config?.input || "request";
|
|
2142
|
+
const output = config?.output || "outcome";
|
|
2143
|
+
return `
|
|
2144
|
+
/**
|
|
2145
|
+
* Summarises the ${input} for the person who will decide.
|
|
2146
|
+
*
|
|
2147
|
+
* @flowWeaver nodeType
|
|
2148
|
+
* @expression
|
|
2149
|
+
* @label Prepare
|
|
2150
|
+
* @input ${input} - What needs approval
|
|
2151
|
+
* @output summary - One line for the approver
|
|
2152
|
+
* @output ${input} - The same, passed on
|
|
2153
|
+
*/
|
|
2154
|
+
function prepare${cap(input)}(${input}: any): { summary: string; ${input}: any } {
|
|
2155
|
+
// TODO: say what is being asked, in one line
|
|
2156
|
+
return { summary: typeof ${input} === 'string' ? ${input} : JSON.stringify(${input}), ${input} };
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
/**
|
|
2160
|
+
* A person approves or refuses. The run pauses here and resumes with the
|
|
2161
|
+
* answer: the approver's note on onSuccess, or a refusal along onFailure.
|
|
2162
|
+
* The body never runs. The coordinator substitutes the answer.
|
|
2163
|
+
*
|
|
2164
|
+
* @flowWeaver nodeType
|
|
2165
|
+
* @durableGate approval
|
|
2166
|
+
* @label Approve
|
|
2167
|
+
* @input summary - What the approver sees
|
|
2168
|
+
* @output note - What the approver said
|
|
2169
|
+
*/
|
|
2170
|
+
async function approve(
|
|
2171
|
+
execute: boolean,
|
|
2172
|
+
summary: string
|
|
2173
|
+
): Promise<{ onSuccess: boolean; onFailure: boolean; note: string }> {
|
|
2174
|
+
throw new Error(\`durable gate: never executes (\${execute}:\${summary})\`);
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
/**
|
|
2178
|
+
* Carries out the approved ${input}.
|
|
2179
|
+
*
|
|
2180
|
+
* @flowWeaver nodeType
|
|
2181
|
+
* @expression
|
|
2182
|
+
* @label Apply
|
|
2183
|
+
* @input ${input} - The approved ${input}, from Prepare
|
|
2184
|
+
* @input note - The approver's note
|
|
2185
|
+
* @output ${output} - What happened
|
|
2186
|
+
*/
|
|
2187
|
+
function apply${cap(input)}(${input}: any, note: string): { ${output}: string } {
|
|
2188
|
+
// TODO: do the thing
|
|
2189
|
+
return { ${output}: \`applied \${JSON.stringify(${input})} -- \${note}\` };
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
/**
|
|
2193
|
+
* @flowWeaver workflow
|
|
2194
|
+
* @node prepare prepare${cap(input)} [color: "blue"] [icon: "description"]
|
|
2195
|
+
* @node approval approve [color: "purple"] [icon: "how_to_reg"]
|
|
2196
|
+
* @node apply apply${cap(input)} [color: "green"] [icon: "task_alt"]
|
|
2197
|
+
* @path Start -> prepare -> approval -> apply -> Exit
|
|
2198
|
+
* @path approval:fail -> Exit
|
|
2199
|
+
* @param ${input} - What needs approval
|
|
2200
|
+
* @returns ${output} - What happened
|
|
2201
|
+
*/
|
|
2202
|
+
export async function ${workflowName}(
|
|
2203
|
+
execute: boolean,
|
|
2204
|
+
params: { ${input}: any }
|
|
2205
|
+
): Promise<{ onSuccess: boolean, onFailure: boolean, ${output}: string }> {
|
|
2206
|
+
throw new Error("Compile with: fw compile <file>");
|
|
2207
|
+
}
|
|
2208
|
+
`.trim();
|
|
2209
|
+
}
|
|
2210
|
+
};
|
|
2211
|
+
var cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
2212
|
+
|
|
2213
|
+
// src/cli/templates/nodes/validator.ts
|
|
2214
|
+
var validatorNodeTemplate = {
|
|
2215
|
+
id: "validator",
|
|
2216
|
+
name: "Validator",
|
|
2217
|
+
description: "Input validation with success/failure routing",
|
|
2218
|
+
category: "validation",
|
|
2219
|
+
generate: (name) => {
|
|
2220
|
+
const pascalName = toPascalCase(name);
|
|
2221
|
+
return `
|
|
2222
|
+
/**
|
|
2223
|
+
* Validates input data against rules
|
|
2224
|
+
*
|
|
2225
|
+
* @flowWeaver nodeType
|
|
2226
|
+
* @label ${pascalName}
|
|
2227
|
+
* @color green
|
|
2228
|
+
* @icon rule
|
|
2229
|
+
* @input data [order:1] - Data to validate
|
|
2230
|
+
* @input execute [order:0] - Execute
|
|
2231
|
+
* @output isValid [order:2] - Whether data is valid
|
|
2232
|
+
* @output validData [order:3] - Validated data (if valid)
|
|
2233
|
+
* @output errors [order:4] - Validation errors (if invalid)
|
|
2234
|
+
* @output onSuccess [order:0] - Validation passed
|
|
2235
|
+
* @output onFailure [order:1] - Validation failed
|
|
2236
|
+
*/
|
|
2237
|
+
function ${name}(
|
|
2238
|
+
execute: boolean,
|
|
2239
|
+
data: any
|
|
2240
|
+
): { onSuccess: boolean; onFailure: boolean; isValid: boolean; validData: any; errors: string[] } {
|
|
2241
|
+
if (!execute) {
|
|
2242
|
+
return { onSuccess: false, onFailure: false, isValid: false, validData: null, errors: [] };
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
const errors: string[] = [];
|
|
2246
|
+
|
|
2247
|
+
// TODO: Add your validation rules
|
|
2248
|
+
// Example validations:
|
|
2249
|
+
if (data === null || data === undefined) {
|
|
2250
|
+
errors.push("Data is required");
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
// if (typeof data.email === 'string' && !data.email.includes('@')) {
|
|
2254
|
+
// errors.push("Invalid email format");
|
|
2255
|
+
// }
|
|
2256
|
+
|
|
2257
|
+
// if (typeof data.age === 'number' && (data.age < 0 || data.age > 150)) {
|
|
2258
|
+
// errors.push("Age must be between 0 and 150");
|
|
2259
|
+
// }
|
|
2260
|
+
|
|
2261
|
+
if (errors.length > 0) {
|
|
2262
|
+
return { onSuccess: false, onFailure: true, isValid: false, validData: null, errors };
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
return { onSuccess: true, onFailure: false, isValid: true, validData: data, errors: [] };
|
|
2266
|
+
}
|
|
2267
|
+
`.trim();
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
|
|
2271
|
+
// src/cli/templates/nodes/transformer.ts
|
|
2272
|
+
var transformerNodeTemplate = {
|
|
2273
|
+
id: "transformer",
|
|
2274
|
+
name: "Transformer",
|
|
2275
|
+
description: "Data transformation and mapping",
|
|
2276
|
+
category: "data",
|
|
2277
|
+
generate: (name) => {
|
|
2278
|
+
const pascalName = toPascalCase(name);
|
|
2279
|
+
return `
|
|
2280
|
+
/**
|
|
2281
|
+
* Transforms input data into a new shape
|
|
2282
|
+
*
|
|
2283
|
+
* @flowWeaver nodeType
|
|
2284
|
+
* @label ${pascalName}
|
|
2285
|
+
* @color pink
|
|
2286
|
+
* @icon changeCircle
|
|
2287
|
+
* @input data [order:1] - Input data to transform
|
|
2288
|
+
* @input execute [order:0] - Execute
|
|
2289
|
+
* @output result [order:2] - Transformed data
|
|
2290
|
+
* @output onSuccess [order:0] - On Success
|
|
2291
|
+
* @output onFailure [order:1] - On Failure
|
|
2292
|
+
*/
|
|
2293
|
+
function ${name}(
|
|
2294
|
+
execute: boolean,
|
|
2295
|
+
data: any
|
|
2296
|
+
): { onSuccess: boolean; onFailure: boolean; result: any } {
|
|
2297
|
+
if (!execute) {
|
|
2298
|
+
return { onSuccess: false, onFailure: false, result: null };
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
try {
|
|
2302
|
+
// TODO: Implement your transformation logic
|
|
2303
|
+
// Examples:
|
|
2304
|
+
|
|
2305
|
+
// Map array items
|
|
2306
|
+
// const result = data.map(item => ({ ...item, processed: true }));
|
|
2307
|
+
|
|
2308
|
+
// Reshape object
|
|
2309
|
+
// const result = {
|
|
2310
|
+
// id: data.userId,
|
|
2311
|
+
// fullName: \`\${data.firstName} \${data.lastName}\`,
|
|
2312
|
+
// email: data.contactEmail
|
|
2313
|
+
// };
|
|
2314
|
+
|
|
2315
|
+
// Filter and transform
|
|
2316
|
+
// const result = data
|
|
2317
|
+
// .filter(item => item.active)
|
|
2318
|
+
// .map(item => item.name);
|
|
2319
|
+
|
|
2320
|
+
const result = {
|
|
2321
|
+
...data,
|
|
2322
|
+
transformedAt: new Date().toISOString()
|
|
2323
|
+
};
|
|
2324
|
+
|
|
2325
|
+
return { onSuccess: true, onFailure: false, result };
|
|
2326
|
+
} catch (err) {
|
|
2327
|
+
console.error("[${pascalName}] Transform error:", err);
|
|
2328
|
+
return { onSuccess: false, onFailure: true, result: null };
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
`.trim();
|
|
2332
|
+
}
|
|
2333
|
+
};
|
|
2334
|
+
|
|
2335
|
+
// src/cli/templates/nodes/http.ts
|
|
2336
|
+
var httpNodeTemplate = {
|
|
2337
|
+
id: "http",
|
|
2338
|
+
name: "HTTP Request",
|
|
2339
|
+
description: "Make HTTP requests to external APIs",
|
|
2340
|
+
category: "integration",
|
|
2341
|
+
generate: (name) => {
|
|
2342
|
+
const pascalName = toPascalCase(name);
|
|
2343
|
+
return `
|
|
2344
|
+
/**
|
|
2345
|
+
* Makes an HTTP request
|
|
2346
|
+
*
|
|
2347
|
+
* @flowWeaver nodeType
|
|
2348
|
+
* @label ${pascalName}
|
|
2349
|
+
* @color blue
|
|
2350
|
+
* @icon api
|
|
2351
|
+
* @input url [order:1] - Request URL
|
|
2352
|
+
* @input method [order:2] - HTTP method (GET, POST, PUT, DELETE)
|
|
2353
|
+
* @input headers [order:3] - Request headers
|
|
2354
|
+
* @input body [order:4] - Request body
|
|
2355
|
+
* @input execute [order:0] - Execute
|
|
2356
|
+
* @output response [order:2] - Response data
|
|
2357
|
+
* @output statusCode [order:3] - HTTP status code
|
|
2358
|
+
* @output onSuccess [order:0] - On Success
|
|
2359
|
+
* @output onFailure [order:1] - On Failure
|
|
2360
|
+
*/
|
|
2361
|
+
async function ${name}(
|
|
2362
|
+
execute: boolean,
|
|
2363
|
+
url: string,
|
|
2364
|
+
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
2365
|
+
headers: Record<string, string>,
|
|
2366
|
+
body: any
|
|
2367
|
+
): Promise<{ onSuccess: boolean; onFailure: boolean; response: any; statusCode: number }> {
|
|
2368
|
+
if (!execute) {
|
|
2369
|
+
return { onSuccess: false, onFailure: false, response: null, statusCode: 0 };
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
try {
|
|
2373
|
+
const options: RequestInit = {
|
|
2374
|
+
method,
|
|
2375
|
+
headers: {
|
|
2376
|
+
"Content-Type": "application/json",
|
|
2377
|
+
...headers
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
|
|
2381
|
+
if (method !== "GET" && body) {
|
|
2382
|
+
options.body = JSON.stringify(body);
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
const res = await fetch(url, options);
|
|
2386
|
+
const statusCode = res.status;
|
|
2387
|
+
|
|
2388
|
+
let response;
|
|
2389
|
+
const contentType = res.headers.get("content-type");
|
|
2390
|
+
if (contentType?.includes("application/json")) {
|
|
2391
|
+
response = await res.json();
|
|
2392
|
+
} else {
|
|
2393
|
+
response = await res.text();
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
if (!res.ok) {
|
|
2397
|
+
console.error("[${pascalName}] HTTP error:", statusCode, response);
|
|
2398
|
+
return { onSuccess: false, onFailure: true, response, statusCode };
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
return { onSuccess: true, onFailure: false, response, statusCode };
|
|
2402
|
+
} catch (err) {
|
|
2403
|
+
console.error("[${pascalName}] Request failed:", err);
|
|
2404
|
+
return { onSuccess: false, onFailure: true, response: null, statusCode: 0 };
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
`.trim();
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
|
|
2411
|
+
// src/cli/templates/nodes/aggregator.ts
|
|
2412
|
+
var aggregatorNodeTemplate = {
|
|
2413
|
+
id: "aggregator",
|
|
2414
|
+
name: "Aggregator",
|
|
2415
|
+
description: "Combine multiple inputs into a single output",
|
|
2416
|
+
category: "data",
|
|
2417
|
+
generate: (name) => {
|
|
2418
|
+
const pascalName = toPascalCase(name);
|
|
2419
|
+
return `
|
|
2420
|
+
/**
|
|
2421
|
+
* Combines multiple inputs into a single output
|
|
2422
|
+
*
|
|
2423
|
+
* @flowWeaver nodeType
|
|
2424
|
+
* @label ${pascalName}
|
|
2425
|
+
* @color orange
|
|
2426
|
+
* @icon callMerge
|
|
2427
|
+
* @input inputA [order:1] - First input
|
|
2428
|
+
* @input inputB [order:2] - Second input
|
|
2429
|
+
* @input inputC [order:3] - Third input (optional)
|
|
2430
|
+
* @input execute [order:0] - Execute
|
|
2431
|
+
* @output combined [order:2] - Combined result
|
|
2432
|
+
* @output count [order:3] - Number of non-null inputs combined
|
|
2433
|
+
* @output onSuccess [order:0] - On Success
|
|
2434
|
+
* @output onFailure [order:1] - On Failure
|
|
2435
|
+
*/
|
|
2436
|
+
function ${name}(
|
|
2437
|
+
execute: boolean,
|
|
2438
|
+
inputA: any,
|
|
2439
|
+
inputB: any,
|
|
2440
|
+
inputC: any
|
|
2441
|
+
): { onSuccess: boolean; onFailure: boolean; combined: any; count: number } {
|
|
2442
|
+
if (!execute) {
|
|
2443
|
+
return { onSuccess: false, onFailure: false, combined: null, count: 0 };
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// TODO: Customize aggregation logic based on your data types
|
|
2447
|
+
const inputs = [inputA, inputB, inputC].filter(x => x !== null && x !== undefined);
|
|
2448
|
+
const count = inputs.length;
|
|
2449
|
+
|
|
2450
|
+
// Default: merge arrays or objects, or collect into array
|
|
2451
|
+
let combined: any;
|
|
2452
|
+
|
|
2453
|
+
if (inputs.every(Array.isArray)) {
|
|
2454
|
+
// Merge arrays
|
|
2455
|
+
combined = inputs.flat();
|
|
2456
|
+
} else if (inputs.every(x => typeof x === "object" && x !== null && !Array.isArray(x))) {
|
|
2457
|
+
// Merge objects
|
|
2458
|
+
combined = Object.assign({}, ...inputs);
|
|
2459
|
+
} else {
|
|
2460
|
+
// Collect as array
|
|
2461
|
+
combined = inputs;
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
return { onSuccess: true, onFailure: false, combined, count };
|
|
2465
|
+
}
|
|
2466
|
+
`.trim();
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
|
|
2470
|
+
// src/cli/templates/nodes/llm-call.ts
|
|
2471
|
+
var llmCallNodeTemplate = {
|
|
2472
|
+
id: "llm-call",
|
|
2473
|
+
name: "LLM Call",
|
|
2474
|
+
description: "Provider-agnostic LLM API call with tool support",
|
|
2475
|
+
category: "ai",
|
|
2476
|
+
generate: (name) => {
|
|
2477
|
+
const funcName = name || "llmCall";
|
|
2478
|
+
return `
|
|
2479
|
+
// LLM Types
|
|
2480
|
+
interface LLMMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string; toolCallId?: string; }
|
|
2481
|
+
interface LLMTool { name: string; description: string; parameters: { type: 'object'; properties: Record<string, { type: string; description?: string }>; required?: string[] }; }
|
|
2482
|
+
interface LLMToolCall { id: string; name: string; arguments: Record<string, unknown>; }
|
|
2483
|
+
interface LLMResponse { content: string | null; toolCalls: LLMToolCall[]; finishReason: string; }
|
|
2484
|
+
interface LLMProvider { chat(messages: LLMMessage[], options?: { tools?: LLMTool[]; temperature?: number }): Promise<LLMResponse>; }
|
|
2485
|
+
|
|
2486
|
+
// Mock provider - replace with real provider (OpenAI, Anthropic, etc.)
|
|
2487
|
+
const createMockProvider = (): LLMProvider => ({
|
|
2488
|
+
async chat(messages, options) {
|
|
2489
|
+
const lastMsg = messages[messages.length - 1];
|
|
2490
|
+
if (options?.tools?.length && lastMsg.content.toLowerCase().includes('search')) {
|
|
2491
|
+
return { content: null, toolCalls: [{ id: \`call_\${Date.now()}\`, name: 'search', arguments: { query: lastMsg.content } }], finishReason: 'tool_calls' };
|
|
2492
|
+
}
|
|
2493
|
+
return { content: \`[Mock: \${lastMsg.content.slice(0, 40)}...]\`, toolCalls: [], finishReason: 'stop' };
|
|
2494
|
+
},
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2497
|
+
const llmProvider: LLMProvider = createMockProvider();
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* Calls an LLM with messages and optional tools
|
|
2501
|
+
*
|
|
2502
|
+
* @flowWeaver nodeType
|
|
2503
|
+
* @label ${funcName}
|
|
2504
|
+
* @color purple
|
|
2505
|
+
* @icon psychology
|
|
2506
|
+
* @input messages [order:1] - Conversation messages
|
|
2507
|
+
* @input tools [order:2] - Available tools/functions (optional)
|
|
2508
|
+
* @input systemPrompt [order:3] - System prompt (optional)
|
|
2509
|
+
* @input temperature [order:4] - Temperature 0-1 (optional)
|
|
2510
|
+
* @input execute [order:0] - Execute
|
|
2511
|
+
* @output content [order:2] - Text response (null if tool call)
|
|
2512
|
+
* @output toolCalls [order:3] - Tool calls requested by LLM
|
|
2513
|
+
* @output hasToolCalls [order:4] - Whether LLM wants to call tools
|
|
2514
|
+
* @output finishReason [order:5] - Why generation stopped
|
|
2515
|
+
* @output onSuccess [order:0] - On Success
|
|
2516
|
+
* @output onFailure [order:1] - On Failure
|
|
2517
|
+
*/
|
|
2518
|
+
async function ${funcName}(
|
|
2519
|
+
execute: boolean,
|
|
2520
|
+
messages: LLMMessage[],
|
|
2521
|
+
tools?: LLMTool[],
|
|
2522
|
+
systemPrompt?: string,
|
|
2523
|
+
temperature?: number
|
|
2524
|
+
): Promise<{
|
|
2525
|
+
onSuccess: boolean;
|
|
2526
|
+
onFailure: boolean;
|
|
2527
|
+
content: string | null;
|
|
2528
|
+
toolCalls: LLMToolCall[];
|
|
2529
|
+
hasToolCalls: boolean;
|
|
2530
|
+
finishReason: string;
|
|
2531
|
+
}> {
|
|
2532
|
+
if (!execute) {
|
|
2533
|
+
return {
|
|
2534
|
+
onSuccess: false,
|
|
2535
|
+
onFailure: false,
|
|
2536
|
+
content: null,
|
|
2537
|
+
toolCalls: [],
|
|
2538
|
+
hasToolCalls: false,
|
|
2539
|
+
finishReason: 'skipped'
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
try {
|
|
2544
|
+
const allMessages: LLMMessage[] = systemPrompt
|
|
2545
|
+
? [{ role: 'system', content: systemPrompt }, ...messages]
|
|
2546
|
+
: messages;
|
|
2547
|
+
|
|
2548
|
+
const response = await llmProvider.chat(allMessages, {
|
|
2549
|
+
tools,
|
|
2550
|
+
temperature,
|
|
2551
|
+
});
|
|
2552
|
+
|
|
2553
|
+
return {
|
|
2554
|
+
onSuccess: true,
|
|
2555
|
+
onFailure: false,
|
|
2556
|
+
content: response.content,
|
|
2557
|
+
toolCalls: response.toolCalls,
|
|
2558
|
+
hasToolCalls: response.toolCalls.length > 0,
|
|
2559
|
+
finishReason: response.finishReason
|
|
2560
|
+
};
|
|
2561
|
+
} catch (error) {
|
|
2562
|
+
console.error('LLM call failed:', error);
|
|
2563
|
+
return {
|
|
2564
|
+
onSuccess: false,
|
|
2565
|
+
onFailure: true,
|
|
2566
|
+
content: null,
|
|
2567
|
+
toolCalls: [],
|
|
2568
|
+
hasToolCalls: false,
|
|
2569
|
+
finishReason: 'error'
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
`.trim();
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
|
|
2577
|
+
// src/cli/templates/nodes/tool-executor.ts
|
|
2578
|
+
var toolExecutorNodeTemplate = {
|
|
2579
|
+
id: "tool-executor",
|
|
2580
|
+
name: "Tool Executor",
|
|
2581
|
+
description: "Executes tool calls from LLM response",
|
|
2582
|
+
category: "ai",
|
|
2583
|
+
generate: (name) => {
|
|
2584
|
+
const funcName = name || "executeTool";
|
|
2585
|
+
return `
|
|
2586
|
+
// LLM Types
|
|
2587
|
+
interface LLMToolCall { id: string; name: string; arguments: Record<string, unknown>; }
|
|
2588
|
+
interface LLMMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string; toolCallId?: string; }
|
|
2589
|
+
|
|
2590
|
+
// Register your tools here
|
|
2591
|
+
type ToolFunction = (args: Record<string, unknown>) => Promise<unknown>;
|
|
2592
|
+
const toolRegistry: Record<string, ToolFunction> = {
|
|
2593
|
+
// Example tools:
|
|
2594
|
+
// 'search': async (args) => {
|
|
2595
|
+
// const query = String(args.query || '');
|
|
2596
|
+
// return await searchDatabase(query);
|
|
2597
|
+
// },
|
|
2598
|
+
// 'get_weather': async (args) => {
|
|
2599
|
+
// const city = String(args.city || '');
|
|
2600
|
+
// const response = await fetch(\`https://api.weather.example/v1/\${encodeURIComponent(city)}\`);
|
|
2601
|
+
// return response.json();
|
|
2602
|
+
// },
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
/**
|
|
2606
|
+
* Executes a single tool call and returns result as message
|
|
2607
|
+
*
|
|
2608
|
+
* @flowWeaver nodeType
|
|
2609
|
+
* @label ${funcName}
|
|
2610
|
+
* @color cyan
|
|
2611
|
+
* @icon build
|
|
2612
|
+
* @input toolCall [order:1] - Tool call to execute
|
|
2613
|
+
* @input execute [order:0] - Execute
|
|
2614
|
+
* @output result [order:2] - Tool execution result
|
|
2615
|
+
* @output resultMessage [order:3] - Result formatted as LLM message
|
|
2616
|
+
* @output toolName [order:4] - Name of executed tool
|
|
2617
|
+
* @output onSuccess [order:0] - On Success
|
|
2618
|
+
* @output onFailure [order:1] - On Failure
|
|
2619
|
+
*/
|
|
2620
|
+
async function ${funcName}(
|
|
2621
|
+
execute: boolean,
|
|
2622
|
+
toolCall: LLMToolCall
|
|
2623
|
+
): Promise<{
|
|
2624
|
+
onSuccess: boolean;
|
|
2625
|
+
onFailure: boolean;
|
|
2626
|
+
result: unknown;
|
|
2627
|
+
resultMessage: LLMMessage;
|
|
2628
|
+
toolName: string;
|
|
2629
|
+
}> {
|
|
2630
|
+
if (!execute) {
|
|
2631
|
+
return {
|
|
2632
|
+
onSuccess: false,
|
|
2633
|
+
onFailure: false,
|
|
2634
|
+
result: null,
|
|
2635
|
+
resultMessage: { role: 'tool', content: '', toolCallId: '' },
|
|
2636
|
+
toolName: ''
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
const tool = toolRegistry[toolCall.name];
|
|
2641
|
+
if (!tool) {
|
|
2642
|
+
return {
|
|
2643
|
+
onSuccess: false,
|
|
2644
|
+
onFailure: true,
|
|
2645
|
+
result: null,
|
|
2646
|
+
resultMessage: {
|
|
2647
|
+
role: 'tool',
|
|
2648
|
+
content: JSON.stringify({ error: \`Unknown tool: \${toolCall.name}\` }),
|
|
2649
|
+
toolCallId: toolCall.id
|
|
2650
|
+
},
|
|
2651
|
+
toolName: toolCall.name
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
try {
|
|
2656
|
+
const result = await tool(toolCall.arguments);
|
|
2657
|
+
return {
|
|
2658
|
+
onSuccess: true,
|
|
2659
|
+
onFailure: false,
|
|
2660
|
+
result,
|
|
2661
|
+
resultMessage: {
|
|
2662
|
+
role: 'tool',
|
|
2663
|
+
content: JSON.stringify(result),
|
|
2664
|
+
toolCallId: toolCall.id
|
|
2665
|
+
},
|
|
2666
|
+
toolName: toolCall.name
|
|
2667
|
+
};
|
|
2668
|
+
} catch (error) {
|
|
2669
|
+
return {
|
|
2670
|
+
onSuccess: false,
|
|
2671
|
+
onFailure: true,
|
|
2672
|
+
result: null,
|
|
2673
|
+
resultMessage: {
|
|
2674
|
+
role: 'tool',
|
|
2675
|
+
content: JSON.stringify({ error: String(error) }),
|
|
2676
|
+
toolCallId: toolCall.id
|
|
2677
|
+
},
|
|
2678
|
+
toolName: toolCall.name
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
`.trim();
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
|
|
2686
|
+
// src/cli/templates/nodes/conversation-memory.ts
|
|
2687
|
+
var conversationMemoryNodeTemplate = {
|
|
2688
|
+
id: "conversation-memory",
|
|
2689
|
+
name: "Conversation Memory",
|
|
2690
|
+
description: "Stores and retrieves conversation history",
|
|
2691
|
+
category: "ai",
|
|
2692
|
+
generate: (name) => {
|
|
2693
|
+
const funcName = name || "conversationMemory";
|
|
2694
|
+
return `
|
|
2695
|
+
// LLM Types
|
|
2696
|
+
interface LLMMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string; toolCallId?: string; }
|
|
2697
|
+
|
|
2698
|
+
// In-memory store (replace with Redis, database, etc. for production)
|
|
2699
|
+
const conversations: Map<string, LLMMessage[]> = new Map();
|
|
2700
|
+
|
|
2701
|
+
/**
|
|
2702
|
+
* Manages conversation history for stateful chat
|
|
2703
|
+
*
|
|
2704
|
+
* @flowWeaver nodeType
|
|
2705
|
+
* @label ${funcName}
|
|
2706
|
+
* @color blue
|
|
2707
|
+
* @icon database
|
|
2708
|
+
* @input conversationId [order:1] - Unique conversation identifier
|
|
2709
|
+
* @input newMessage [order:2] - New message to add (optional)
|
|
2710
|
+
* @input maxMessages [order:3] - Max messages to retain (optional, default 50)
|
|
2711
|
+
* @input execute [order:0] - Execute
|
|
2712
|
+
* @output messages [order:2] - Full conversation history
|
|
2713
|
+
* @output messageCount [order:3] - Number of messages
|
|
2714
|
+
* @output onSuccess [order:0] - On Success
|
|
2715
|
+
* @output onFailure [order:1] - On Failure
|
|
2716
|
+
*/
|
|
2717
|
+
function ${funcName}(
|
|
2718
|
+
execute: boolean,
|
|
2719
|
+
conversationId: string,
|
|
2720
|
+
newMessage?: LLMMessage,
|
|
2721
|
+
maxMessages?: number
|
|
2722
|
+
): {
|
|
2723
|
+
onSuccess: boolean;
|
|
2724
|
+
onFailure: boolean;
|
|
2725
|
+
messages: LLMMessage[];
|
|
2726
|
+
messageCount: number;
|
|
2727
|
+
} {
|
|
2728
|
+
if (!execute) {
|
|
2729
|
+
return {
|
|
2730
|
+
onSuccess: false,
|
|
2731
|
+
onFailure: false,
|
|
2732
|
+
messages: [],
|
|
2733
|
+
messageCount: 0
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
const limit = maxMessages ?? 50;
|
|
2738
|
+
|
|
2739
|
+
// Get or create conversation
|
|
2740
|
+
if (!conversations.has(conversationId)) {
|
|
2741
|
+
conversations.set(conversationId, []);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
const history = conversations.get(conversationId)!;
|
|
2745
|
+
|
|
2746
|
+
// Add new message if provided
|
|
2747
|
+
if (newMessage) {
|
|
2748
|
+
history.push(newMessage);
|
|
2749
|
+
|
|
2750
|
+
// Trim to max length (keep system message if present)
|
|
2751
|
+
while (history.length > limit) {
|
|
2752
|
+
const firstNonSystem = history.findIndex(m => m.role !== 'system');
|
|
2753
|
+
if (firstNonSystem > 0) {
|
|
2754
|
+
history.splice(firstNonSystem, 1);
|
|
2755
|
+
} else {
|
|
2756
|
+
history.shift();
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
return {
|
|
2762
|
+
onSuccess: true,
|
|
2763
|
+
onFailure: false,
|
|
2764
|
+
messages: [...history],
|
|
2765
|
+
messageCount: history.length
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
`.trim();
|
|
2769
|
+
}
|
|
2770
|
+
};
|
|
2771
|
+
|
|
2772
|
+
// src/cli/templates/nodes/prompt-template.ts
|
|
2773
|
+
var promptTemplateNodeTemplate = {
|
|
2774
|
+
id: "prompt-template",
|
|
2775
|
+
name: "Prompt Template",
|
|
2776
|
+
description: "Interpolate variables into a prompt template string",
|
|
2777
|
+
category: "ai",
|
|
2778
|
+
generate: (name) => {
|
|
2779
|
+
const funcName = name || "promptTemplate";
|
|
2780
|
+
const label = toPascalCase(funcName);
|
|
2781
|
+
return `
|
|
2782
|
+
/**
|
|
2783
|
+
* Interpolate variables into a prompt template string.
|
|
2784
|
+
* Uses {{variableName}} syntax for placeholders.
|
|
2785
|
+
*
|
|
2786
|
+
* Example:
|
|
2787
|
+
* template: "Hello {{name}}, your order {{orderId}} is ready."
|
|
2788
|
+
* variables: { name: "Alice", orderId: "12345" }
|
|
2789
|
+
* \u2192 prompt: "Hello Alice, your order 12345 is ready."
|
|
2790
|
+
*
|
|
2791
|
+
* @flowWeaver nodeType
|
|
2792
|
+
* @expression
|
|
2793
|
+
* @label ${label}
|
|
2794
|
+
* @color pink
|
|
2795
|
+
* @icon textSnippet
|
|
2796
|
+
* @input template [order:0] - Template string with {{variable}} placeholders
|
|
2797
|
+
* @input variables [order:1] - Key-value pairs to interpolate
|
|
2798
|
+
* @output prompt [order:0] - Rendered prompt string
|
|
2799
|
+
* @output unresolvedCount [order:1] - Number of unresolved placeholders
|
|
2800
|
+
*/
|
|
2801
|
+
function ${funcName}(
|
|
2802
|
+
template: string,
|
|
2803
|
+
variables: Record<string, string>
|
|
2804
|
+
): {
|
|
2805
|
+
prompt: string;
|
|
2806
|
+
unresolvedCount: number;
|
|
2807
|
+
} {
|
|
2808
|
+
let unresolved = 0;
|
|
2809
|
+
|
|
2810
|
+
const prompt = template.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (match, key) => {
|
|
2811
|
+
if (key in variables) {
|
|
2812
|
+
return variables[key];
|
|
2813
|
+
}
|
|
2814
|
+
unresolved++;
|
|
2815
|
+
return match; // Leave unresolved placeholders as-is
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
return { prompt, unresolvedCount: unresolved };
|
|
2819
|
+
}
|
|
2820
|
+
`.trim();
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
|
|
2824
|
+
// src/cli/templates/nodes/json-extractor.ts
|
|
2825
|
+
var jsonExtractorNodeTemplate = {
|
|
2826
|
+
id: "json-extractor",
|
|
2827
|
+
name: "JSON Extractor",
|
|
2828
|
+
description: "Extract structured JSON from LLM text output",
|
|
2829
|
+
category: "ai",
|
|
2830
|
+
generate: (name) => {
|
|
2831
|
+
const funcName = name || "jsonExtractor";
|
|
2832
|
+
const label = toPascalCase(funcName);
|
|
2833
|
+
return `
|
|
2834
|
+
/**
|
|
2835
|
+
* Extract structured JSON from LLM text output.
|
|
2836
|
+
* Handles markdown code fences, leading/trailing text, and optional schema validation.
|
|
2837
|
+
*
|
|
2838
|
+
* Strips \\\`\\\`\\\`json ... \\\`\\\`\\\` fences, finds the first valid JSON object or array,
|
|
2839
|
+
* and optionally validates that expected field names are present.
|
|
2840
|
+
*
|
|
2841
|
+
* @flowWeaver nodeType
|
|
2842
|
+
* @expression
|
|
2843
|
+
* @label ${label}
|
|
2844
|
+
* @color cyan
|
|
2845
|
+
* @icon dataObject
|
|
2846
|
+
* @input text [order:0] - Raw text potentially containing JSON
|
|
2847
|
+
* @input schema [order:1] - Expected field names for validation (optional)
|
|
2848
|
+
* @output extracted [order:0] - Parsed JSON object (null if extraction failed)
|
|
2849
|
+
* @output valid [order:1] - Whether extraction and validation succeeded
|
|
2850
|
+
* @output raw [order:2] - The raw JSON string that was found
|
|
2851
|
+
*/
|
|
2852
|
+
function ${funcName}(
|
|
2853
|
+
text: string,
|
|
2854
|
+
schema?: string[]
|
|
2855
|
+
): {
|
|
2856
|
+
extracted: Record<string, unknown> | null;
|
|
2857
|
+
valid: boolean;
|
|
2858
|
+
raw: string;
|
|
2859
|
+
} {
|
|
2860
|
+
if (!text || typeof text !== 'string') {
|
|
2861
|
+
return { extracted: null, valid: false, raw: '' };
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
// Strip markdown code fences
|
|
2865
|
+
let cleaned = text;
|
|
2866
|
+
const fenceMatch = cleaned.match(/\\\`\\\`\\\`(?:json)?\\s*([\\s\\S]*?)\\\`\\\`\\\`/);
|
|
2867
|
+
if (fenceMatch) {
|
|
2868
|
+
cleaned = fenceMatch[1].trim();
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// Find first JSON object or array via brace/bracket matching
|
|
2872
|
+
const startChar = cleaned.indexOf('{') !== -1 && (cleaned.indexOf('[') === -1 || cleaned.indexOf('{') < cleaned.indexOf('['))
|
|
2873
|
+
? '{'
|
|
2874
|
+
: '[';
|
|
2875
|
+
const endChar = startChar === '{' ? '}' : ']';
|
|
2876
|
+
const startIdx = cleaned.indexOf(startChar);
|
|
2877
|
+
|
|
2878
|
+
if (startIdx === -1) {
|
|
2879
|
+
return { extracted: null, valid: false, raw: '' };
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
// Walk forward to find the matching closing brace/bracket
|
|
2883
|
+
let depth = 0;
|
|
2884
|
+
let inString = false;
|
|
2885
|
+
let escape = false;
|
|
2886
|
+
let endIdx = -1;
|
|
2887
|
+
|
|
2888
|
+
for (let i = startIdx; i < cleaned.length; i++) {
|
|
2889
|
+
const ch = cleaned[i];
|
|
2890
|
+
|
|
2891
|
+
if (escape) {
|
|
2892
|
+
escape = false;
|
|
2893
|
+
continue;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
if (ch === '\\\\') {
|
|
2897
|
+
escape = true;
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
if (ch === '"') {
|
|
2902
|
+
inString = !inString;
|
|
2903
|
+
continue;
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
if (inString) continue;
|
|
2907
|
+
|
|
2908
|
+
if (ch === startChar) depth++;
|
|
2909
|
+
if (ch === endChar) depth--;
|
|
2910
|
+
|
|
2911
|
+
if (depth === 0) {
|
|
2912
|
+
endIdx = i;
|
|
2913
|
+
break;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
if (endIdx === -1) {
|
|
2918
|
+
return { extracted: null, valid: false, raw: '' };
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
const raw = cleaned.slice(startIdx, endIdx + 1);
|
|
2922
|
+
|
|
2923
|
+
try {
|
|
2924
|
+
const parsed = JSON.parse(raw);
|
|
2925
|
+
|
|
2926
|
+
// Validate schema if provided
|
|
2927
|
+
if (schema && schema.length > 0 && typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
2928
|
+
const missingFields = schema.filter(field => !(field in parsed));
|
|
2929
|
+
if (missingFields.length > 0) {
|
|
2930
|
+
return { extracted: parsed, valid: false, raw };
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
return { extracted: parsed, valid: true, raw };
|
|
2935
|
+
} catch {
|
|
2936
|
+
return { extracted: null, valid: false, raw };
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
`.trim();
|
|
2940
|
+
}
|
|
2941
|
+
};
|
|
2942
|
+
|
|
2943
|
+
// src/cli/templates/nodes/human-approval.ts
|
|
2944
|
+
var humanApprovalNodeTemplate = {
|
|
2945
|
+
id: "human-approval",
|
|
2946
|
+
name: "Human Approval",
|
|
2947
|
+
description: "Pause workflow and wait for human approval",
|
|
2948
|
+
category: "workflow",
|
|
2949
|
+
generate: (name) => {
|
|
2950
|
+
const funcName = name || "humanApproval";
|
|
2951
|
+
const label = toPascalCase(funcName);
|
|
2952
|
+
return `
|
|
2953
|
+
/**
|
|
2954
|
+
* Yield a durable human-approval gate.
|
|
2955
|
+
*
|
|
2956
|
+
* The generated engine never calls this body. A coordinator persists the
|
|
2957
|
+
* yielded continuation and gate atomically, then supplies the exact resolution
|
|
2958
|
+
* to a later compatible Node.js executor invocation.
|
|
2959
|
+
*
|
|
2960
|
+
* @flowWeaver nodeType
|
|
2961
|
+
* @durableGate approval
|
|
2962
|
+
* @label ${label}
|
|
2963
|
+
* @color orange
|
|
2964
|
+
* @icon verified
|
|
2965
|
+
* @input execute [order:0] - Execute
|
|
2966
|
+
* @input prompt [order:1] - Approval prompt shown to reviewer
|
|
2967
|
+
* @input context [order:2] - Additional context for the reviewer (optional)
|
|
2968
|
+
* @input timeout [order:3] - Timeout duration e.g. "7d", "1h" (optional)
|
|
2969
|
+
* @output onSuccess [order:0] - On Success
|
|
2970
|
+
* @output onFailure [order:1] - On Failure (timeout or error)
|
|
2971
|
+
* @output approved [order:2] - Whether the request was approved
|
|
2972
|
+
* @output response [order:3] - Reviewer's response text
|
|
2973
|
+
* @output reviewer [order:4] - Reviewer identifier
|
|
2974
|
+
*/
|
|
2975
|
+
async function ${funcName}(
|
|
2976
|
+
execute: boolean,
|
|
2977
|
+
prompt: string,
|
|
2978
|
+
context?: Record<string, unknown>,
|
|
2979
|
+
timeout?: string
|
|
2980
|
+
): Promise<{
|
|
2981
|
+
onSuccess: boolean;
|
|
2982
|
+
onFailure: boolean;
|
|
2983
|
+
approved: boolean;
|
|
2984
|
+
response?: string;
|
|
2985
|
+
reviewer?: string;
|
|
2986
|
+
}> {
|
|
2987
|
+
throw new Error(
|
|
2988
|
+
\`durable approval gate must not execute: \${execute}:\${prompt}:\${String(context)}:\${String(timeout)}\`,
|
|
2989
|
+
);
|
|
2990
|
+
}
|
|
2991
|
+
`.trim();
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
|
|
2995
|
+
// src/cli/templates/nodes/agent-router.ts
|
|
2996
|
+
var agentRouterNodeTemplate = {
|
|
2997
|
+
id: "agent-router",
|
|
2998
|
+
name: "Agent Router",
|
|
2999
|
+
description: "Route to different handlers based on a classification result",
|
|
3000
|
+
category: "ai",
|
|
3001
|
+
generate: (name) => {
|
|
3002
|
+
const funcName = name || "agentRouter";
|
|
3003
|
+
const label = toPascalCase(funcName);
|
|
3004
|
+
return `
|
|
3005
|
+
// ============================================================
|
|
3006
|
+
// ROUTE DEFINITIONS
|
|
3007
|
+
// ============================================================
|
|
3008
|
+
//
|
|
3009
|
+
// Define your routes here. The router matches the 'category' input
|
|
3010
|
+
// (from an upstream LLM call) against route names using case-insensitive
|
|
3011
|
+
// comparison. The last route is used as a default/fallback.
|
|
3012
|
+
//
|
|
3013
|
+
// Example wiring in a workflow:
|
|
3014
|
+
// llmCall.content \u2192 agentRouter.category
|
|
3015
|
+
// agentRouter.selectedRoute \u2192 (use for downstream branching)
|
|
3016
|
+
|
|
3017
|
+
interface Route {
|
|
3018
|
+
name: string;
|
|
3019
|
+
description: string;
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
const DEFAULT_ROUTES: Route[] = [
|
|
3023
|
+
{ name: 'billing', description: 'Billing inquiries, invoices, payments' },
|
|
3024
|
+
{ name: 'technical', description: 'Technical support, bugs, troubleshooting' },
|
|
3025
|
+
{ name: 'general', description: 'General questions (default fallback)' },
|
|
3026
|
+
];
|
|
3027
|
+
|
|
3028
|
+
/**
|
|
3029
|
+
* Route to different handlers based on a classification result.
|
|
3030
|
+
* Matches the category string against route names (case-insensitive).
|
|
3031
|
+
* Falls back to the last route if no match is found.
|
|
3032
|
+
*
|
|
3033
|
+
* @flowWeaver nodeType
|
|
3034
|
+
* @label ${label}
|
|
3035
|
+
* @color blue
|
|
3036
|
+
* @icon altRoute
|
|
3037
|
+
* @input execute [order:0] - Execute
|
|
3038
|
+
* @input category [order:1] - Classification result (from upstream LLM call)
|
|
3039
|
+
* @input routes [order:2] - Available routes with name and description (optional)
|
|
3040
|
+
* @output onSuccess [order:0] - On Success
|
|
3041
|
+
* @output onFailure [order:1] - On Failure (no routes provided)
|
|
3042
|
+
* @output selectedRoute [order:2] - Name of the matched route
|
|
3043
|
+
* @output routeDescription [order:3] - Description of the matched route
|
|
3044
|
+
* @output isDefault [order:4] - Whether the default/fallback route was used
|
|
3045
|
+
*/
|
|
3046
|
+
function ${funcName}(
|
|
3047
|
+
execute: boolean,
|
|
3048
|
+
category: string,
|
|
3049
|
+
routes?: Route[]
|
|
3050
|
+
): {
|
|
3051
|
+
onSuccess: boolean;
|
|
3052
|
+
onFailure: boolean;
|
|
3053
|
+
selectedRoute: string;
|
|
3054
|
+
routeDescription: string;
|
|
3055
|
+
isDefault: boolean;
|
|
3056
|
+
} {
|
|
3057
|
+
if (!execute) {
|
|
3058
|
+
return {
|
|
3059
|
+
onSuccess: false,
|
|
3060
|
+
onFailure: false,
|
|
3061
|
+
selectedRoute: '',
|
|
3062
|
+
routeDescription: '',
|
|
3063
|
+
isDefault: false,
|
|
3064
|
+
};
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
const activeRoutes = routes && routes.length > 0 ? routes : DEFAULT_ROUTES;
|
|
3068
|
+
|
|
3069
|
+
if (activeRoutes.length === 0) {
|
|
3070
|
+
return {
|
|
3071
|
+
onSuccess: false,
|
|
3072
|
+
onFailure: true,
|
|
3073
|
+
selectedRoute: '',
|
|
3074
|
+
routeDescription: '',
|
|
3075
|
+
isDefault: false,
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
// Case-insensitive match against route names
|
|
3080
|
+
const normalized = (category || '').trim().toLowerCase();
|
|
3081
|
+
const matched = activeRoutes.find(
|
|
3082
|
+
(r) => r.name.toLowerCase() === normalized
|
|
3083
|
+
);
|
|
3084
|
+
|
|
3085
|
+
if (matched) {
|
|
3086
|
+
return {
|
|
3087
|
+
onSuccess: true,
|
|
3088
|
+
onFailure: false,
|
|
3089
|
+
selectedRoute: matched.name,
|
|
3090
|
+
routeDescription: matched.description,
|
|
3091
|
+
isDefault: false,
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
// Fallback to last route as default
|
|
3096
|
+
const fallback = activeRoutes[activeRoutes.length - 1];
|
|
3097
|
+
return {
|
|
3098
|
+
onSuccess: true,
|
|
3099
|
+
onFailure: false,
|
|
3100
|
+
selectedRoute: fallback.name,
|
|
3101
|
+
routeDescription: fallback.description,
|
|
3102
|
+
isDefault: true,
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
`.trim();
|
|
3106
|
+
}
|
|
3107
|
+
};
|
|
3108
|
+
|
|
3109
|
+
// src/cli/templates/nodes/rag-retriever.ts
|
|
3110
|
+
var ragRetrieverNodeTemplate = {
|
|
3111
|
+
id: "rag-retriever",
|
|
3112
|
+
name: "RAG Retriever",
|
|
3113
|
+
description: "Retrieve relevant documents via vector similarity search",
|
|
3114
|
+
category: "ai",
|
|
3115
|
+
generate: (name) => {
|
|
3116
|
+
const funcName = name || "ragRetriever";
|
|
3117
|
+
const label = toPascalCase(funcName);
|
|
3118
|
+
return `
|
|
3119
|
+
// ============================================================
|
|
3120
|
+
// VECTOR STORE
|
|
3121
|
+
// ============================================================
|
|
3122
|
+
//
|
|
3123
|
+
// Replace mockRetrieve() with your vector store:
|
|
3124
|
+
//
|
|
3125
|
+
// Pinecone: const results = await index.query({ vector: embedding, topK });
|
|
3126
|
+
// pgvector: const results = await db.query(
|
|
3127
|
+
// 'SELECT text, metadata, 1 - (embedding <=> $1) AS score FROM docs ORDER BY score DESC LIMIT $2',
|
|
3128
|
+
// [embedding, topK]
|
|
3129
|
+
// );
|
|
3130
|
+
// Weaviate: const results = await client.graphql.get()
|
|
3131
|
+
// .withClassName('Document')
|
|
3132
|
+
// .withNearText({ concepts: [query] })
|
|
3133
|
+
// .withLimit(topK)
|
|
3134
|
+
// .do();
|
|
3135
|
+
// ChromaDB: const results = await collection.query({ queryTexts: [query], nResults: topK });
|
|
3136
|
+
|
|
3137
|
+
interface RetrievedDocument {
|
|
3138
|
+
text: string;
|
|
3139
|
+
score: number;
|
|
3140
|
+
metadata?: Record<string, unknown>;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
// Mock document store. Replace with a real vector database
|
|
3144
|
+
const MOCK_DOCUMENTS: Record<string, RetrievedDocument[]> = {
|
|
3145
|
+
default: [
|
|
3146
|
+
{ text: 'Flow Weaver compiles workflows to standalone TypeScript functions.', score: 0.95, metadata: { source: 'docs/overview.md' } },
|
|
3147
|
+
{ text: 'The compiler validates connections at compile time, catching errors before runtime.', score: 0.88, metadata: { source: 'docs/compiler.md' } },
|
|
3148
|
+
{ text: 'Workflows are standard TypeScript files with JSDoc annotations.', score: 0.82, metadata: { source: 'docs/authoring.md' } },
|
|
3149
|
+
{ text: 'Deploy compiled workflows to any supported target via installed packs.', score: 0.75, metadata: { source: 'docs/deployment.md' } },
|
|
3150
|
+
{ text: 'The console shows each run step by step and answers paused gates from a form.', score: 0.71, metadata: { source: 'docs/console.md' } },
|
|
3151
|
+
],
|
|
3152
|
+
};
|
|
3153
|
+
|
|
3154
|
+
async function mockRetrieve(query: string, collection: string, topK: number): Promise<RetrievedDocument[]> {
|
|
3155
|
+
const docs = MOCK_DOCUMENTS[collection] || MOCK_DOCUMENTS['default'] || [];
|
|
3156
|
+
|
|
3157
|
+
// Simple keyword overlap scoring (mock, since real stores use vector similarity)
|
|
3158
|
+
const queryWords = new Set(query.toLowerCase().split(/\\s+/));
|
|
3159
|
+
const scored = docs.map((doc) => {
|
|
3160
|
+
const docWords = doc.text.toLowerCase().split(/\\s+/);
|
|
3161
|
+
const overlap = docWords.filter((w) => queryWords.has(w)).length;
|
|
3162
|
+
const score = Math.min(0.99, overlap / Math.max(queryWords.size, 1) * 0.5 + doc.score * 0.5);
|
|
3163
|
+
return { ...doc, score };
|
|
3164
|
+
});
|
|
3165
|
+
|
|
3166
|
+
return scored
|
|
3167
|
+
.sort((a, b) => b.score - a.score)
|
|
3168
|
+
.slice(0, topK);
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
/**
|
|
3172
|
+
* Retrieve relevant documents via vector similarity search.
|
|
3173
|
+
* Returns scored documents and a concatenated context string ready for LLM input.
|
|
3174
|
+
*
|
|
3175
|
+
* @flowWeaver nodeType
|
|
3176
|
+
* @label ${label}
|
|
3177
|
+
* @color green
|
|
3178
|
+
* @icon search
|
|
3179
|
+
* @input execute [order:0] - Execute
|
|
3180
|
+
* @input query [order:1] - Search query text
|
|
3181
|
+
* @input collection [order:2] - Collection/index name
|
|
3182
|
+
* @input topK [order:3] - Number of results (optional, default 5)
|
|
3183
|
+
* @output onSuccess [order:0] - On Success
|
|
3184
|
+
* @output onFailure [order:1] - On Failure
|
|
3185
|
+
* @output documents [order:2] - Retrieved documents with relevance scores
|
|
3186
|
+
* @output context [order:3] - Concatenated document text for LLM context
|
|
3187
|
+
*/
|
|
3188
|
+
async function ${funcName}(
|
|
3189
|
+
execute: boolean,
|
|
3190
|
+
query: string,
|
|
3191
|
+
collection: string,
|
|
3192
|
+
topK?: number
|
|
3193
|
+
): Promise<{
|
|
3194
|
+
onSuccess: boolean;
|
|
3195
|
+
onFailure: boolean;
|
|
3196
|
+
documents: RetrievedDocument[];
|
|
3197
|
+
context: string;
|
|
3198
|
+
}> {
|
|
3199
|
+
if (!execute) {
|
|
3200
|
+
return {
|
|
3201
|
+
onSuccess: false,
|
|
3202
|
+
onFailure: false,
|
|
3203
|
+
documents: [],
|
|
3204
|
+
context: '',
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
try {
|
|
3209
|
+
const limit = topK ?? 5;
|
|
3210
|
+
const documents = await mockRetrieve(query, collection, limit);
|
|
3211
|
+
|
|
3212
|
+
// Concatenate document texts for easy LLM context injection
|
|
3213
|
+
const context = documents
|
|
3214
|
+
.map((doc, i) => \`[Document \${i + 1} (score: \${doc.score.toFixed(2)})]
|
|
3215
|
+
\${doc.text}\`)
|
|
3216
|
+
.join('\\n\\n');
|
|
3217
|
+
|
|
3218
|
+
return {
|
|
3219
|
+
onSuccess: true,
|
|
3220
|
+
onFailure: false,
|
|
3221
|
+
documents,
|
|
3222
|
+
context,
|
|
3223
|
+
};
|
|
3224
|
+
} catch (error) {
|
|
3225
|
+
console.error('RAG retrieval failed:', error);
|
|
3226
|
+
return {
|
|
3227
|
+
onSuccess: false,
|
|
3228
|
+
onFailure: true,
|
|
3229
|
+
documents: [],
|
|
3230
|
+
context: '',
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
`.trim();
|
|
3235
|
+
}
|
|
3236
|
+
};
|
|
3237
|
+
|
|
3238
|
+
// src/cli/templates/index.ts
|
|
3239
|
+
var workflowTemplates = [
|
|
3240
|
+
sequentialTemplate,
|
|
3241
|
+
foreachTemplate,
|
|
3242
|
+
conditionalTemplate,
|
|
3243
|
+
approvalTemplate,
|
|
3244
|
+
aiAgentTemplate,
|
|
3245
|
+
aiReactTemplate,
|
|
3246
|
+
aiRagTemplate,
|
|
3247
|
+
aiChatTemplate,
|
|
3248
|
+
aggregatorTemplate,
|
|
3249
|
+
webhookTemplate,
|
|
3250
|
+
errorHandlerTemplate
|
|
3251
|
+
];
|
|
3252
|
+
var nodeTemplates = [
|
|
3253
|
+
validatorNodeTemplate,
|
|
3254
|
+
transformerNodeTemplate,
|
|
3255
|
+
httpNodeTemplate,
|
|
3256
|
+
aggregatorNodeTemplate,
|
|
3257
|
+
llmCallNodeTemplate,
|
|
3258
|
+
toolExecutorNodeTemplate,
|
|
3259
|
+
conversationMemoryNodeTemplate,
|
|
3260
|
+
promptTemplateNodeTemplate,
|
|
3261
|
+
jsonExtractorNodeTemplate,
|
|
3262
|
+
humanApprovalNodeTemplate,
|
|
3263
|
+
agentRouterNodeTemplate,
|
|
3264
|
+
ragRetrieverNodeTemplate
|
|
3265
|
+
];
|
|
3266
|
+
function getWorkflowTemplate(id) {
|
|
3267
|
+
return workflowTemplates.find((t) => t.id === id) ?? packWorkflowTemplates.find((t) => t.id === id);
|
|
3268
|
+
}
|
|
3269
|
+
function getNodeTemplate(id) {
|
|
3270
|
+
return nodeTemplates.find((t) => t.id === id);
|
|
3271
|
+
}
|
|
3272
|
+
var packWorkflowTemplates = [];
|
|
3273
|
+
function getAllWorkflowTemplates() {
|
|
3274
|
+
return [...workflowTemplates, ...packWorkflowTemplates];
|
|
3275
|
+
}
|
|
3276
|
+
function registerWorkflowTemplates(templates) {
|
|
3277
|
+
for (const tmpl of templates) {
|
|
3278
|
+
if (!packWorkflowTemplates.some((t) => t.id === tmpl.id)) {
|
|
3279
|
+
packWorkflowTemplates.push(tmpl);
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
export {
|
|
3285
|
+
toCamelCase,
|
|
3286
|
+
nodeTemplates,
|
|
3287
|
+
getWorkflowTemplate,
|
|
3288
|
+
getNodeTemplate,
|
|
3289
|
+
getAllWorkflowTemplates,
|
|
3290
|
+
registerWorkflowTemplates
|
|
3291
|
+
};
|