@loopstack/advanced-workflows-examples 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +176 -0
  2. package/dist/advanced-workflows-examples.module.d.ts +3 -0
  3. package/dist/advanced-workflows-examples.module.d.ts.map +1 -0
  4. package/dist/advanced-workflows-examples.module.js +95 -0
  5. package/dist/advanced-workflows-examples.module.js.map +1 -0
  6. package/dist/index.d.ts +18 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +34 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.d.ts +5 -0
  11. package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.d.ts.map +1 -0
  12. package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.js +29 -0
  13. package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.js.map +1 -0
  14. package/dist/workflows/agent-error-handling/agent-error-handling.ui.yaml +6 -0
  15. package/dist/workflows/agent-error-handling/agent-error-handling.workflow.d.ts +34 -0
  16. package/dist/workflows/agent-error-handling/agent-error-handling.workflow.d.ts.map +1 -0
  17. package/dist/workflows/agent-error-handling/agent-error-handling.workflow.js +158 -0
  18. package/dist/workflows/agent-error-handling/agent-error-handling.workflow.js.map +1 -0
  19. package/dist/workflows/agent-error-handling/templates/system.md +12 -0
  20. package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.d.ts +12 -0
  21. package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.d.ts.map +1 -0
  22. package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.js +40 -0
  23. package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.js.map +1 -0
  24. package/dist/workflows/agent-error-handling/tools/runtime-error.tool.d.ts +11 -0
  25. package/dist/workflows/agent-error-handling/tools/runtime-error.tool.d.ts.map +1 -0
  26. package/dist/workflows/agent-error-handling/tools/runtime-error.tool.js +33 -0
  27. package/dist/workflows/agent-error-handling/tools/runtime-error.tool.js.map +1 -0
  28. package/dist/workflows/agent-error-handling/tools/strict-schema.tool.d.ts +10 -0
  29. package/dist/workflows/agent-error-handling/tools/strict-schema.tool.d.ts.map +1 -0
  30. package/dist/workflows/agent-error-handling/tools/strict-schema.tool.js +31 -0
  31. package/dist/workflows/agent-error-handling/tools/strict-schema.tool.js.map +1 -0
  32. package/dist/workflows/batch-processing/batch-processing-example.workflow.d.ts +27 -0
  33. package/dist/workflows/batch-processing/batch-processing-example.workflow.d.ts.map +1 -0
  34. package/dist/workflows/batch-processing/batch-processing-example.workflow.js +93 -0
  35. package/dist/workflows/batch-processing/batch-processing-example.workflow.js.map +1 -0
  36. package/dist/workflows/custom-tool/custom-tool-example.ui.yaml +6 -0
  37. package/dist/workflows/custom-tool/custom-tool-example.workflow.d.ts +23 -0
  38. package/dist/workflows/custom-tool/custom-tool-example.workflow.d.ts.map +1 -0
  39. package/dist/workflows/custom-tool/custom-tool-example.workflow.js +95 -0
  40. package/dist/workflows/custom-tool/custom-tool-example.workflow.js.map +1 -0
  41. package/dist/workflows/custom-tool/index.d.ts +2 -0
  42. package/dist/workflows/custom-tool/index.d.ts.map +1 -0
  43. package/dist/workflows/custom-tool/index.js +18 -0
  44. package/dist/workflows/custom-tool/index.js.map +1 -0
  45. package/dist/workflows/custom-tool/services/math.service.d.ts +4 -0
  46. package/dist/workflows/custom-tool/services/math.service.d.ts.map +1 -0
  47. package/dist/workflows/custom-tool/services/math.service.js +20 -0
  48. package/dist/workflows/custom-tool/services/math.service.js.map +1 -0
  49. package/dist/workflows/custom-tool/tools/counter.tool.d.ts +7 -0
  50. package/dist/workflows/custom-tool/tools/counter.tool.d.ts.map +1 -0
  51. package/dist/workflows/custom-tool/tools/counter.tool.js +25 -0
  52. package/dist/workflows/custom-tool/tools/counter.tool.js.map +1 -0
  53. package/dist/workflows/custom-tool/tools/index.d.ts +3 -0
  54. package/dist/workflows/custom-tool/tools/index.d.ts.map +1 -0
  55. package/dist/workflows/custom-tool/tools/index.js +19 -0
  56. package/dist/workflows/custom-tool/tools/index.js.map +1 -0
  57. package/dist/workflows/custom-tool/tools/math-sum.tool.d.ts +16 -0
  58. package/dist/workflows/custom-tool/tools/math-sum.tool.d.ts.map +1 -0
  59. package/dist/workflows/custom-tool/tools/math-sum.tool.js +42 -0
  60. package/dist/workflows/custom-tool/tools/math-sum.tool.js.map +1 -0
  61. package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.d.ts +24 -0
  62. package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.d.ts.map +1 -0
  63. package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.js +115 -0
  64. package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.js.map +1 -0
  65. package/dist/workflows/error-retry/error-retry-example.ui.yaml +25 -0
  66. package/dist/workflows/error-retry/error-retry-example.workflow.d.ts +43 -0
  67. package/dist/workflows/error-retry/error-retry-example.workflow.d.ts.map +1 -0
  68. package/dist/workflows/error-retry/error-retry-example.workflow.js +343 -0
  69. package/dist/workflows/error-retry/error-retry-example.workflow.js.map +1 -0
  70. package/dist/workflows/error-retry/failing-child.workflow.d.ts +5 -0
  71. package/dist/workflows/error-retry/failing-child.workflow.d.ts.map +1 -0
  72. package/dist/workflows/error-retry/failing-child.workflow.js +36 -0
  73. package/dist/workflows/error-retry/failing-child.workflow.js.map +1 -0
  74. package/dist/workflows/error-retry/tools/slow.tool.d.ts +10 -0
  75. package/dist/workflows/error-retry/tools/slow.tool.d.ts.map +1 -0
  76. package/dist/workflows/error-retry/tools/slow.tool.js +27 -0
  77. package/dist/workflows/error-retry/tools/slow.tool.js.map +1 -0
  78. package/dist/workflows/error-retry/tools/step1.tool.d.ts +6 -0
  79. package/dist/workflows/error-retry/tools/step1.tool.d.ts.map +1 -0
  80. package/dist/workflows/error-retry/tools/step1.tool.js +26 -0
  81. package/dist/workflows/error-retry/tools/step1.tool.js.map +1 -0
  82. package/dist/workflows/error-retry/tools/step2.tool.d.ts +10 -0
  83. package/dist/workflows/error-retry/tools/step2.tool.d.ts.map +1 -0
  84. package/dist/workflows/error-retry/tools/step2.tool.js +29 -0
  85. package/dist/workflows/error-retry/tools/step2.tool.js.map +1 -0
  86. package/dist/workflows/fan-out/fan-out-example.workflow.d.ts +12 -0
  87. package/dist/workflows/fan-out/fan-out-example.workflow.d.ts.map +1 -0
  88. package/dist/workflows/fan-out/fan-out-example.workflow.js +68 -0
  89. package/dist/workflows/fan-out/fan-out-example.workflow.js.map +1 -0
  90. package/dist/workflows/module-config/consumers/default-greeting.module.d.ts +3 -0
  91. package/dist/workflows/module-config/consumers/default-greeting.module.d.ts.map +1 -0
  92. package/dist/workflows/module-config/consumers/default-greeting.module.js +21 -0
  93. package/dist/workflows/module-config/consumers/default-greeting.module.js.map +1 -0
  94. package/dist/workflows/module-config/consumers/default-greeting.workflow.d.ts +9 -0
  95. package/dist/workflows/module-config/consumers/default-greeting.workflow.d.ts.map +1 -0
  96. package/dist/workflows/module-config/consumers/default-greeting.workflow.js +47 -0
  97. package/dist/workflows/module-config/consumers/default-greeting.workflow.js.map +1 -0
  98. package/dist/workflows/module-config/consumers/french-greeting.module.d.ts +3 -0
  99. package/dist/workflows/module-config/consumers/french-greeting.module.d.ts.map +1 -0
  100. package/dist/workflows/module-config/consumers/french-greeting.module.js +23 -0
  101. package/dist/workflows/module-config/consumers/french-greeting.module.js.map +1 -0
  102. package/dist/workflows/module-config/consumers/french-greeting.workflow.d.ts +9 -0
  103. package/dist/workflows/module-config/consumers/french-greeting.workflow.d.ts.map +1 -0
  104. package/dist/workflows/module-config/consumers/french-greeting.workflow.js +47 -0
  105. package/dist/workflows/module-config/consumers/french-greeting.workflow.js.map +1 -0
  106. package/dist/workflows/module-config/consumers/german-greeting.module.d.ts +3 -0
  107. package/dist/workflows/module-config/consumers/german-greeting.module.d.ts.map +1 -0
  108. package/dist/workflows/module-config/consumers/german-greeting.module.js +23 -0
  109. package/dist/workflows/module-config/consumers/german-greeting.module.js.map +1 -0
  110. package/dist/workflows/module-config/consumers/german-greeting.workflow.d.ts +9 -0
  111. package/dist/workflows/module-config/consumers/german-greeting.workflow.d.ts.map +1 -0
  112. package/dist/workflows/module-config/consumers/german-greeting.workflow.js +47 -0
  113. package/dist/workflows/module-config/consumers/german-greeting.workflow.js.map +1 -0
  114. package/dist/workflows/module-config/consumers/nested-greeting.module.d.ts +3 -0
  115. package/dist/workflows/module-config/consumers/nested-greeting.module.d.ts.map +1 -0
  116. package/dist/workflows/module-config/consumers/nested-greeting.module.js +23 -0
  117. package/dist/workflows/module-config/consumers/nested-greeting.module.js.map +1 -0
  118. package/dist/workflows/module-config/consumers/nested-greeting.workflow.d.ts +9 -0
  119. package/dist/workflows/module-config/consumers/nested-greeting.workflow.d.ts.map +1 -0
  120. package/dist/workflows/module-config/consumers/nested-greeting.workflow.js +47 -0
  121. package/dist/workflows/module-config/consumers/nested-greeting.workflow.js.map +1 -0
  122. package/dist/workflows/module-config/greeter/greeter-agent.module.d.ts +8 -0
  123. package/dist/workflows/module-config/greeter/greeter-agent.module.d.ts.map +1 -0
  124. package/dist/workflows/module-config/greeter/greeter-agent.module.js +36 -0
  125. package/dist/workflows/module-config/greeter/greeter-agent.module.js.map +1 -0
  126. package/dist/workflows/module-config/greeter/greeter.constants.d.ts +6 -0
  127. package/dist/workflows/module-config/greeter/greeter.constants.d.ts.map +1 -0
  128. package/dist/workflows/module-config/greeter/greeter.constants.js +5 -0
  129. package/dist/workflows/module-config/greeter/greeter.constants.js.map +1 -0
  130. package/dist/workflows/module-config/greeter/greeter.module.d.ts +7 -0
  131. package/dist/workflows/module-config/greeter/greeter.module.d.ts.map +1 -0
  132. package/dist/workflows/module-config/greeter/greeter.module.js +47 -0
  133. package/dist/workflows/module-config/greeter/greeter.module.js.map +1 -0
  134. package/dist/workflows/module-config/greeter/greeter.tool.d.ts +17 -0
  135. package/dist/workflows/module-config/greeter/greeter.tool.d.ts.map +1 -0
  136. package/dist/workflows/module-config/greeter/greeter.tool.js +45 -0
  137. package/dist/workflows/module-config/greeter/greeter.tool.js.map +1 -0
  138. package/dist/workflows/module-config/greeter/index.d.ts +4 -0
  139. package/dist/workflows/module-config/greeter/index.d.ts.map +1 -0
  140. package/dist/workflows/module-config/greeter/index.js +20 -0
  141. package/dist/workflows/module-config/greeter/index.js.map +1 -0
  142. package/dist/workflows/sequence/sequence-example.workflow.d.ts +12 -0
  143. package/dist/workflows/sequence/sequence-example.workflow.d.ts.map +1 -0
  144. package/dist/workflows/sequence/sequence-example.workflow.js +67 -0
  145. package/dist/workflows/sequence/sequence-example.workflow.js.map +1 -0
  146. package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.d.ts +10 -0
  147. package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.d.ts.map +1 -0
  148. package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.js +77 -0
  149. package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.js.map +1 -0
  150. package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.d.ts +5 -0
  151. package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.d.ts.map +1 -0
  152. package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.js +36 -0
  153. package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.js.map +1 -0
  154. package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.d.ts +15 -0
  155. package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.d.ts.map +1 -0
  156. package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.js +84 -0
  157. package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.js.map +1 -0
  158. package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.d.ts +17 -0
  159. package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.d.ts.map +1 -0
  160. package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.js +93 -0
  161. package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.js.map +1 -0
  162. package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.d.ts +5 -0
  163. package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.d.ts.map +1 -0
  164. package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.js +36 -0
  165. package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.js.map +1 -0
  166. package/dist/workflows/ui-documents/ui-documents-example.workflow.d.ts +6 -0
  167. package/dist/workflows/ui-documents/ui-documents-example.workflow.d.ts.map +1 -0
  168. package/dist/workflows/ui-documents/ui-documents-example.workflow.js +51 -0
  169. package/dist/workflows/ui-documents/ui-documents-example.workflow.js.map +1 -0
  170. package/dist/workflows/workflow-state/tool-results-example.workflow.d.ts +10 -0
  171. package/dist/workflows/workflow-state/tool-results-example.workflow.d.ts.map +1 -0
  172. package/dist/workflows/workflow-state/tool-results-example.workflow.js +48 -0
  173. package/dist/workflows/workflow-state/tool-results-example.workflow.js.map +1 -0
  174. package/dist/workflows/workflow-state/workflow-state-example.workflow.d.ts +11 -0
  175. package/dist/workflows/workflow-state/workflow-state-example.workflow.d.ts.map +1 -0
  176. package/dist/workflows/workflow-state/workflow-state-example.workflow.js +51 -0
  177. package/dist/workflows/workflow-state/workflow-state-example.workflow.js.map +1 -0
  178. package/package.json +56 -0
  179. package/src/advanced-workflows-examples.module.ts +83 -0
  180. package/src/index.ts +17 -0
  181. package/src/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.ts +13 -0
  182. package/src/workflows/agent-error-handling/agent-error-handling.ui.yaml +6 -0
  183. package/src/workflows/agent-error-handling/agent-error-handling.workflow.ts +130 -0
  184. package/src/workflows/agent-error-handling/templates/system.md +12 -0
  185. package/src/workflows/agent-error-handling/tools/failing-sub-workflow.tool.ts +32 -0
  186. package/src/workflows/agent-error-handling/tools/runtime-error.tool.ts +29 -0
  187. package/src/workflows/agent-error-handling/tools/strict-schema.tool.ts +21 -0
  188. package/src/workflows/batch-processing/batch-processing-example.workflow.ts +79 -0
  189. package/src/workflows/custom-tool/custom-tool-example.ui.yaml +6 -0
  190. package/src/workflows/custom-tool/custom-tool-example.workflow.ts +87 -0
  191. package/src/workflows/custom-tool/index.ts +1 -0
  192. package/src/workflows/custom-tool/services/math.service.ts +8 -0
  193. package/src/workflows/custom-tool/tools/counter.tool.ts +16 -0
  194. package/src/workflows/custom-tool/tools/index.ts +2 -0
  195. package/src/workflows/custom-tool/tools/math-sum.tool.ts +30 -0
  196. package/src/workflows/dynamic-routing/dynamic-routing-example.workflow.ts +86 -0
  197. package/src/workflows/error-retry/error-retry-example.ui.yaml +25 -0
  198. package/src/workflows/error-retry/error-retry-example.workflow.ts +286 -0
  199. package/src/workflows/error-retry/failing-child.workflow.ts +20 -0
  200. package/src/workflows/error-retry/tools/slow.tool.ts +17 -0
  201. package/src/workflows/error-retry/tools/step1.tool.ts +16 -0
  202. package/src/workflows/error-retry/tools/step2.tool.ts +20 -0
  203. package/src/workflows/fan-out/fan-out-example.workflow.ts +58 -0
  204. package/src/workflows/module-config/consumers/default-greeting.module.ts +12 -0
  205. package/src/workflows/module-config/consumers/default-greeting.workflow.ts +24 -0
  206. package/src/workflows/module-config/consumers/french-greeting.module.ts +14 -0
  207. package/src/workflows/module-config/consumers/french-greeting.workflow.ts +25 -0
  208. package/src/workflows/module-config/consumers/german-greeting.module.ts +14 -0
  209. package/src/workflows/module-config/consumers/german-greeting.workflow.ts +24 -0
  210. package/src/workflows/module-config/consumers/nested-greeting.module.ts +15 -0
  211. package/src/workflows/module-config/consumers/nested-greeting.workflow.ts +25 -0
  212. package/src/workflows/module-config/greeter/greeter-agent.module.ts +33 -0
  213. package/src/workflows/module-config/greeter/greeter.constants.ts +6 -0
  214. package/src/workflows/module-config/greeter/greeter.module.ts +48 -0
  215. package/src/workflows/module-config/greeter/greeter.tool.ts +38 -0
  216. package/src/workflows/module-config/greeter/index.ts +3 -0
  217. package/src/workflows/sequence/sequence-example.workflow.ts +57 -0
  218. package/src/workflows/sub-workflow/sub-workflow-error-handling.workflow.ts +63 -0
  219. package/src/workflows/sub-workflow/sub-workflow-failing-sub.workflow.ts +21 -0
  220. package/src/workflows/sub-workflow/sub-workflow-parent.workflow.ts +58 -0
  221. package/src/workflows/sub-workflow/sub-workflow-show-modes.workflow.ts +73 -0
  222. package/src/workflows/sub-workflow/sub-workflow-sub.workflow.ts +17 -0
  223. package/src/workflows/ui-documents/ui-documents-example.workflow.ts +43 -0
  224. package/src/workflows/workflow-state/tool-results-example.workflow.ts +28 -0
  225. package/src/workflows/workflow-state/workflow-state-example.workflow.ts +33 -0
@@ -0,0 +1,13 @@
1
+ import { BaseWorkflow, Transition, Workflow } from '@loopstack/common';
2
+
3
+ /**
4
+ * A minimal sub-workflow that always fails on its initial transition.
5
+ * Used to verify that failed sub-workflow callbacks propagate errors to the parent.
6
+ */
7
+ @Workflow({ title: 'Agent Error Handling - Failing Sub-Workflow' })
8
+ export class AgentErrorHandlingFailingSubWorkflow extends BaseWorkflow {
9
+ @Transition({ to: 'done' })
10
+ start(_state: Record<string, unknown>) {
11
+ return Promise.reject(new Error('Simulated sub-workflow failure.'));
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ widget: button
2
+ showWhen:
3
+ - awaiting_tools
4
+ options:
5
+ transition: cancelPendingTools
6
+ label: Cancel pending tools
@@ -0,0 +1,130 @@
1
+ import { Inject } from '@nestjs/common';
2
+ import { join } from 'node:path';
3
+ import { BaseWorkflow, Guard, MessageDocument, Transition, WORKFLOW_ORCHESTRATOR, Workflow } from '@loopstack/common';
4
+ import type { RunContext, TransitionInput, WorkflowOrchestrator } from '@loopstack/common';
5
+ import type { LlmDelegateResult, LlmGenerateTextResult } from '@loopstack/llm-provider-module';
6
+ import {
7
+ LlmDelegateToolCallsTool,
8
+ LlmGenerateTextTool,
9
+ LlmMessageDocument,
10
+ LlmUpdateToolResultTool,
11
+ } from '@loopstack/llm-provider-module';
12
+ import { FailingSubWorkflowTool } from './tools/failing-sub-workflow.tool';
13
+ import { RuntimeErrorTool } from './tools/runtime-error.tool';
14
+ import { StrictSchemaTool } from './tools/strict-schema.tool';
15
+
16
+ interface AgentErrorHandlingState {
17
+ llmResult?: LlmGenerateTextResult;
18
+ delegateResult?: LlmDelegateResult;
19
+ turnCount: number;
20
+ }
21
+
22
+ /**
23
+ * Demonstrates how tool errors (validation, runtime, and failed sub-workflows)
24
+ * are handled by DelegateToolCalls and fed back to the LLM for self-correction.
25
+ *
26
+ * The LLM is instructed to deliberately trigger errors first, then correct them.
27
+ * This verifies that all error types produce identical is_error tool results
28
+ * and that the LLM agent loop handles them correctly.
29
+ */
30
+ @Workflow({
31
+ title: 'Advanced Workflows - Agent Error Handling Example',
32
+ description:
33
+ 'Exercises every tool-error path in an agent loop — schema validation failures, runtime exceptions, failed sub-workflows — and verifies the LLM receives identical is_error tool_results and can self-correct.',
34
+ widget: './agent-error-handling.ui.yaml',
35
+ })
36
+ export class AgentErrorHandlingWorkflow extends BaseWorkflow {
37
+ constructor(
38
+ private readonly llmGenerateText: LlmGenerateTextTool,
39
+ private readonly llmDelegateToolCalls: LlmDelegateToolCallsTool,
40
+ private readonly llmUpdateToolResult: LlmUpdateToolResultTool,
41
+ private readonly strictSchema: StrictSchemaTool,
42
+ private readonly runtimeError: RuntimeErrorTool,
43
+ private readonly failingSubWorkflow: FailingSubWorkflowTool,
44
+ @Inject(WORKFLOW_ORCHESTRATOR) private readonly orchestrator: WorkflowOrchestrator,
45
+ ) {
46
+ super();
47
+ }
48
+
49
+ @Transition({ to: 'ready' })
50
+ async setup(_state: AgentErrorHandlingState) {
51
+ await this.documentStore.save(MessageDocument, {
52
+ role: 'assistant',
53
+ text:
54
+ '# Agent Error Handling Example\n\n' +
55
+ 'This workflow tests how tool errors are handled and fed back to the LLM.\n\n' +
56
+ 'The LLM will deliberately trigger errors, then self-correct.',
57
+ });
58
+
59
+ await this.documentStore.save(LlmMessageDocument, {
60
+ role: 'user',
61
+ text:
62
+ 'Follow the instructions in your system prompt exactly. ' +
63
+ 'Start with step 1: call strictSchema with no arguments.',
64
+ });
65
+ this.assignState({ turnCount: 0 });
66
+ }
67
+
68
+ @Transition({ from: 'ready', to: 'prompt_executed' })
69
+ async llmTurn(state: AgentErrorHandlingState) {
70
+ const result = await this.llmGenerateText.call(
71
+ {},
72
+ {
73
+ config: {
74
+ provider: 'claude',
75
+ model: 'claude-sonnet-4-6',
76
+ system: this.render(join(__dirname, 'templates', 'system.md')),
77
+ tools: ['strict_schema', 'runtime_error', 'failing_sub_workflow'],
78
+ },
79
+ },
80
+ );
81
+ this.assignState({
82
+ turnCount: state.turnCount + 1,
83
+ llmResult: result.data,
84
+ });
85
+ }
86
+
87
+ @Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
88
+ @Guard('hasToolCalls')
89
+ async executeToolCalls(state: AgentErrorHandlingState) {
90
+ const result = await this.llmDelegateToolCalls.call({
91
+ message: state.llmResult!.message,
92
+ callback: { transition: 'toolResultReceived' },
93
+ });
94
+ this.assignState({ delegateResult: result.data });
95
+ }
96
+
97
+ @Transition({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true })
98
+ async toolResultReceived(state: AgentErrorHandlingState, input: TransitionInput) {
99
+ const result = await this.llmUpdateToolResult.call({
100
+ delegateResult: state.delegateResult!,
101
+ completedTool: input,
102
+ });
103
+ this.assignState({ delegateResult: result.data });
104
+ }
105
+
106
+ @Transition({ from: 'awaiting_tools', to: 'ready' })
107
+ @Guard('allToolsComplete')
108
+ toolsComplete(_state: AgentErrorHandlingState) {}
109
+
110
+ @Transition({ from: 'awaiting_tools', to: 'ready', wait: true })
111
+ async cancelPendingTools(state: AgentErrorHandlingState, ctx: RunContext) {
112
+ await this.orchestrator.cancelChildren(ctx.workflowId);
113
+ }
114
+
115
+ @Transition({ from: 'prompt_executed', to: 'end' })
116
+ @Guard('isEndTurn')
117
+ respond(_state: AgentErrorHandlingState) {}
118
+
119
+ private hasToolCalls(state: AgentErrorHandlingState): boolean {
120
+ return state.llmResult?.message.stopReason === 'tool_use';
121
+ }
122
+
123
+ private allToolsComplete(state: AgentErrorHandlingState): boolean {
124
+ return !!state.delegateResult?.allCompleted;
125
+ }
126
+
127
+ private isEndTurn(state: AgentErrorHandlingState): boolean {
128
+ return state.llmResult?.message.stopReason === 'end_turn';
129
+ }
130
+ }
@@ -0,0 +1,12 @@
1
+ You are a test assistant that follows instructions precisely. You are fully autonomous — do NOT ask the user for input or confirmation. Just proceed through each step on your own.
2
+
3
+ Complete these steps IN ORDER. Do exactly one step per turn:
4
+
5
+ 1. Call the `strictSchema` tool with NO arguments (empty object {}). This will fail — that is expected.
6
+ 2. After seeing the validation error, call `strictSchema` correctly with { "name": "World" }.
7
+ 3. Call the `runtimeError` tool with { "shouldFail": true }. This will fail — that is expected.
8
+ 4. After seeing the runtime error, call `runtimeError` with { "shouldFail": false }.
9
+ 5. Call the `failingSubWorkflow` tool with {}. This launches a sub-workflow that will fail — that is expected.
10
+ 6. After seeing the sub-workflow error, respond with a brief summary of what happened (do NOT call any tools).
11
+
12
+ IMPORTANT: Only perform ONE step per turn. Do NOT skip steps. Do NOT wait for user input between steps.
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import { BaseTool, Tool, ToolCallOptions, ToolEnvelope } from '@loopstack/common';
3
+ import type { RunContext } from '@loopstack/common';
4
+ import { AgentErrorHandlingFailingSubWorkflow } from '../agent-error-handling-failing-sub.workflow';
5
+
6
+ export type FailingSubWorkflowToolResult = { workflowId: string };
7
+
8
+ @Tool({
9
+ name: 'failing_sub_workflow',
10
+ description:
11
+ 'Launch an async sub-workflow that always fails. ' +
12
+ 'Used to test that failed sub-workflow errors propagate back to the parent.',
13
+ schema: z.object({}),
14
+ })
15
+ export class FailingSubWorkflowTool extends BaseTool<object, object, FailingSubWorkflowToolResult> {
16
+ constructor(private readonly failingWorkflow: AgentErrorHandlingFailingSubWorkflow) {
17
+ super();
18
+ }
19
+
20
+ protected async handle(
21
+ _args: object,
22
+ _ctx: RunContext,
23
+ options?: ToolCallOptions,
24
+ ): Promise<ToolEnvelope<FailingSubWorkflowToolResult>> {
25
+ const result = await this.failingWorkflow.run({}, { callback: options?.callback });
26
+
27
+ return {
28
+ data: { workflowId: result.workflowId },
29
+ pending: { workflowId: result.workflowId },
30
+ };
31
+ }
32
+ }
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
3
+ import type { RunContext } from '@loopstack/common';
4
+
5
+ export type RuntimeErrorToolResult = string;
6
+
7
+ @Tool({
8
+ name: 'runtime_error',
9
+ description:
10
+ 'Perform an action that may fail at runtime. ' +
11
+ 'Pass shouldFail: true to simulate a runtime error, or false to succeed.',
12
+ schema: z.object({
13
+ shouldFail: z.boolean().describe('Whether the tool should simulate a runtime failure.'),
14
+ }),
15
+ })
16
+ export class RuntimeErrorTool extends BaseTool<{ shouldFail: boolean }, object, RuntimeErrorToolResult> {
17
+ protected async handle(
18
+ args: { shouldFail: boolean },
19
+ _ctx: RunContext,
20
+ ): Promise<ToolEnvelope<RuntimeErrorToolResult>> {
21
+ if (args.shouldFail) {
22
+ throw new Error('Simulated runtime error: external service unavailable.');
23
+ }
24
+
25
+ return Promise.resolve({
26
+ data: 'Action completed successfully.',
27
+ });
28
+ }
29
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
3
+
4
+ export type StrictSchemaToolResult = string;
5
+
6
+ @Tool({
7
+ name: 'strict_schema',
8
+ description: 'Greet a person by name. Requires a name argument. ' + 'Returns a greeting message.',
9
+ schema: z
10
+ .object({
11
+ name: z.string().describe('The name of the person to greet.'),
12
+ })
13
+ .strict(),
14
+ })
15
+ export class StrictSchemaTool extends BaseTool<{ name: string }, object, StrictSchemaToolResult> {
16
+ protected async handle(args: { name: string }): Promise<ToolEnvelope<StrictSchemaToolResult>> {
17
+ return Promise.resolve({
18
+ data: `Hello, ${args.name}! Nice to meet you.`,
19
+ });
20
+ }
21
+ }
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+ import { BaseWorkflow, Guard, MarkdownDocument, Transition, Workflow } from '@loopstack/common';
3
+ import type { RunContext } from '@loopstack/common';
4
+
5
+ interface BatchProcessingState {
6
+ items: string[];
7
+ processed: Array<{ item: string; result: string }>;
8
+ batchIndex: number;
9
+ }
10
+
11
+ const BatchProcessingArgsSchema = z.object({
12
+ totalItems: z.number().int().min(1).default(25),
13
+ batchSize: z.number().int().min(1).default(5),
14
+ });
15
+
16
+ type BatchProcessingArgs = z.infer<typeof BatchProcessingArgsSchema>;
17
+
18
+ @Workflow({
19
+ title: 'Advanced - Batch Processing Example',
20
+ description:
21
+ 'Processes a list of items in fixed-size batches with a state-machine loop. Different from FanOutWorkflow — batches run sequentially, items within a batch can run concurrently. Useful for rate-limited APIs, memory-bounded processing, or quota-constrained operations.',
22
+ schema: BatchProcessingArgsSchema,
23
+ })
24
+ export class BatchProcessingExampleWorkflow extends BaseWorkflow<BatchProcessingArgs> {
25
+ @Transition({ to: 'batch_ready' })
26
+ setup(_state: BatchProcessingState, ctx: RunContext<BatchProcessingArgs>) {
27
+ const items = Array.from({ length: ctx.args.totalItems }, (_, i) => `item-${i + 1}`);
28
+ this.assignState({ items, processed: [], batchIndex: 0 });
29
+ }
30
+
31
+ @Transition({ from: 'batch_ready', to: 'batch_done' })
32
+ async processBatch(state: BatchProcessingState, ctx: RunContext<BatchProcessingArgs>) {
33
+ const start = state.batchIndex * ctx.args.batchSize;
34
+ const batch = state.items.slice(start, start + ctx.args.batchSize);
35
+
36
+ const results = await Promise.all(
37
+ batch.map(async (item) => ({
38
+ item,
39
+ result: await this.processItem(item),
40
+ })),
41
+ );
42
+
43
+ this.assignState({ processed: [...state.processed, ...results] });
44
+ }
45
+
46
+ @Transition({ from: 'batch_done', to: 'batch_ready' })
47
+ @Guard('hasMoreItems')
48
+ nextBatch(state: BatchProcessingState) {
49
+ this.assignState({ batchIndex: state.batchIndex + 1 });
50
+ }
51
+
52
+ hasMoreItems(state: BatchProcessingState): boolean {
53
+ return state.processed.length < state.items.length;
54
+ }
55
+
56
+ @Transition({ from: 'batch_done', to: 'end', priority: 10 })
57
+ @Guard('allItemsProcessed')
58
+ async finish(state: BatchProcessingState, ctx: RunContext<BatchProcessingArgs>) {
59
+ await this.documentStore.save(MarkdownDocument, {
60
+ markdown: [
61
+ `# Batch Processing Complete`,
62
+ ``,
63
+ `Processed ${state.processed.length} items in batches of ${ctx.args.batchSize}.`,
64
+ ``,
65
+ ...state.processed.slice(0, 10).map((r) => `- ${r.item} → ${r.result}`),
66
+ state.processed.length > 10 ? `\n_(${state.processed.length - 10} more omitted)_` : '',
67
+ ].join('\n'),
68
+ });
69
+ }
70
+
71
+ allItemsProcessed(state: BatchProcessingState): boolean {
72
+ return state.processed.length >= state.items.length;
73
+ }
74
+
75
+ private async processItem(item: string): Promise<string> {
76
+ await new Promise((resolve) => setTimeout(resolve, 10));
77
+ return `processed:${item}`;
78
+ }
79
+ }
@@ -0,0 +1,6 @@
1
+ widget: button
2
+ enabledWhen:
3
+ - waiting_for_user
4
+ options:
5
+ transition: userContinue
6
+ label: Continue
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
3
+ import type { RunContext } from '@loopstack/common';
4
+ import { CounterTool, MathSumTool } from './tools';
5
+
6
+ interface CustomToolExampleState {
7
+ total?: number;
8
+ }
9
+
10
+ const CustomToolExampleArgsSchema = z
11
+ .object({
12
+ a: z.number().default(1),
13
+ b: z.number().default(2),
14
+ })
15
+ .strict();
16
+
17
+ type CustomToolExampleArgs = z.infer<typeof CustomToolExampleArgsSchema>;
18
+
19
+ @Workflow({
20
+ title: 'Advanced - Custom Tool Example',
21
+ description:
22
+ 'Authoring custom tools — stateless and stateful, with @Tool() decorator, Zod schemas, and NestJS injection.',
23
+ widget: './custom-tool-example.ui.yaml',
24
+ schema: CustomToolExampleArgsSchema,
25
+ })
26
+ export class CustomToolExampleWorkflow extends BaseWorkflow<CustomToolExampleArgs> {
27
+ constructor(
28
+ private readonly counterTool: CounterTool,
29
+ private readonly mathTool: MathSumTool,
30
+ ) {
31
+ super();
32
+ }
33
+
34
+ @Transition({ to: 'waiting_for_user' })
35
+ async calculate(state: CustomToolExampleState, ctx: RunContext<CustomToolExampleArgs>) {
36
+ // Use a custom tool
37
+ const calcResult = await this.mathTool.call({ a: ctx.args.a, b: ctx.args.b });
38
+ const total = calcResult.data as number;
39
+
40
+ // Display the result
41
+ await this.documentStore.save(MessageDocument, {
42
+ role: 'assistant',
43
+ text: `Tool calculation result:\n${ctx.args.a} + ${ctx.args.b} = ${total}`,
44
+ });
45
+
46
+ // Alternatively, use a custom workflow method
47
+ await this.documentStore.save(MessageDocument, {
48
+ role: 'assistant',
49
+ text: `Alternatively, using workflow method:\n${ctx.args.a} + ${ctx.args.b} = ${this.sum(ctx.args.a, ctx.args.b)}`,
50
+ });
51
+
52
+ // Count before pause — should be 1, 2, 3
53
+ const c1 = await this.counterTool.call();
54
+ const c2 = await this.counterTool.call();
55
+ const c3 = await this.counterTool.call();
56
+
57
+ await this.documentStore.save(MessageDocument, {
58
+ role: 'assistant',
59
+ text: `Counter before pause: ${c1.data}, ${c2.data}, ${c3.data}\n\nPress Next to continue...`,
60
+ });
61
+ this.assignState({ total });
62
+ }
63
+
64
+ @Transition({ from: 'waiting_for_user', to: 'resumed', wait: true })
65
+ userContinue(_state: CustomToolExampleState) {
66
+ // User pressed Next — counter state should persist from checkpoint
67
+ }
68
+
69
+ @Transition({ from: 'resumed', to: 'end' })
70
+ async continueCount(state: CustomToolExampleState) {
71
+ // Count after resume — should continue: 4, 5, 6
72
+ const c4 = await this.counterTool.call();
73
+ const c5 = await this.counterTool.call();
74
+ const c6 = await this.counterTool.call();
75
+
76
+ await this.documentStore.save(MessageDocument, {
77
+ role: 'assistant',
78
+ text: `Counter after resume: ${c4.data}, ${c5.data}, ${c6.data}\n\nIf state persisted, this should be 4, 5, 6.`,
79
+ });
80
+
81
+ this.setResult({ total: state.total } as unknown as Record<string, unknown>);
82
+ }
83
+
84
+ private sum(a: number, b: number) {
85
+ return a + b;
86
+ }
87
+ }
@@ -0,0 +1 @@
1
+ export * from './custom-tool-example.workflow';
@@ -0,0 +1,8 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ @Injectable()
4
+ export class MathService {
5
+ public sum(a: number, b: number) {
6
+ return a + b;
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
2
+
3
+ export type CounterToolResult = number;
4
+
5
+ @Tool({
6
+ name: 'counter',
7
+ description: 'Counter tool.',
8
+ })
9
+ export class CounterTool extends BaseTool<object, object, CounterToolResult> {
10
+ count: number = 0;
11
+
12
+ protected async handle(): Promise<ToolEnvelope<CounterToolResult>> {
13
+ this.count++;
14
+ return Promise.resolve({ data: this.count });
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ export * from './counter.tool';
2
+ export * from './math-sum.tool';
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
3
+ import { MathService } from '../services/math.service';
4
+
5
+ const MathSumSchema = z
6
+ .object({
7
+ a: z.number(),
8
+ b: z.number(),
9
+ })
10
+ .strict();
11
+
12
+ type MathSumArgs = z.infer<typeof MathSumSchema>;
13
+
14
+ export type MathSumToolResult = number;
15
+
16
+ @Tool({
17
+ name: 'math_sum',
18
+ description: 'Math tool calculating the sum of two arguments by using an injected service.',
19
+ schema: MathSumSchema,
20
+ })
21
+ export class MathSumTool extends BaseTool<MathSumArgs, object, MathSumToolResult> {
22
+ constructor(private readonly mathService: MathService) {
23
+ super();
24
+ }
25
+
26
+ protected async handle(args: MathSumArgs): Promise<ToolEnvelope<MathSumToolResult>> {
27
+ const sum = this.mathService.sum(args.a, args.b);
28
+ return Promise.resolve({ data: sum });
29
+ }
30
+ }
@@ -0,0 +1,86 @@
1
+ import { z } from 'zod';
2
+ import { BaseWorkflow, Guard, MessageDocument, Transition, Workflow } from '@loopstack/common';
3
+ import type { RunContext } from '@loopstack/common';
4
+
5
+ interface DynamicRoutingState {
6
+ value: number;
7
+ }
8
+
9
+ const DynamicRoutingArgsSchema = z
10
+ .object({
11
+ value: z.number().default(150),
12
+ })
13
+ .strict();
14
+
15
+ type DynamicRoutingArgs = z.infer<typeof DynamicRoutingArgsSchema>;
16
+
17
+ @Workflow({
18
+ title: 'Advanced - Dynamic Routing Example',
19
+ description:
20
+ 'Conditional routing using @Guard decorators and transition priorities — branching on runtime values without explicit state machines.',
21
+ schema: DynamicRoutingArgsSchema,
22
+ })
23
+ export class DynamicRoutingExampleWorkflow extends BaseWorkflow<DynamicRoutingArgs> {
24
+ // --- Initial transition ---
25
+
26
+ @Transition({ to: 'prepared' })
27
+ async createMockData(state: DynamicRoutingState, ctx: RunContext<DynamicRoutingArgs>) {
28
+ await this.documentStore.save(MessageDocument, {
29
+ role: 'assistant',
30
+ text: `Analysing value = ${ctx.args.value}`,
31
+ });
32
+ this.assignState({ value: ctx.args.value });
33
+ }
34
+
35
+ // --- First routing fork (from 'prepared') ---
36
+
37
+ @Transition({ from: 'prepared', to: 'placeA', priority: 10 })
38
+ @Guard('isAbove100')
39
+ routeToPlaceA(_state: DynamicRoutingState) {}
40
+
41
+ isAbove100(state: DynamicRoutingState): boolean {
42
+ return state.value > 100;
43
+ }
44
+
45
+ @Transition({ from: 'prepared', to: 'placeB' })
46
+ routeToPlaceB(_state: DynamicRoutingState) {} // no priority -> evaluated last, acts as fallback
47
+
48
+ // --- Second routing fork (from 'placeA') ---
49
+
50
+ @Transition({ from: 'placeA', to: 'placeC', priority: 10 })
51
+ @Guard('isAbove200')
52
+ routeToPlaceC(_state: DynamicRoutingState) {}
53
+
54
+ isAbove200(state: DynamicRoutingState): boolean {
55
+ return state.value > 200;
56
+ }
57
+
58
+ @Transition({ from: 'placeA', to: 'placeD' })
59
+ routeToPlaceD(_state: DynamicRoutingState) {} // no priority -> evaluated last, acts as fallback
60
+
61
+ // --- Terminal transitions ---
62
+
63
+ @Transition({ from: 'placeB', to: 'end' })
64
+ async showMessagePlaceB(_state: DynamicRoutingState) {
65
+ await this.documentStore.save(MessageDocument, {
66
+ role: 'assistant',
67
+ text: 'Value is less or equal 100',
68
+ });
69
+ }
70
+
71
+ @Transition({ from: 'placeC', to: 'end' })
72
+ async showMessagePlaceC(_state: DynamicRoutingState) {
73
+ await this.documentStore.save(MessageDocument, {
74
+ role: 'assistant',
75
+ text: 'Value is greater than 200',
76
+ });
77
+ }
78
+
79
+ @Transition({ from: 'placeD', to: 'end' })
80
+ async showMessagePlaceD(_state: DynamicRoutingState) {
81
+ await this.documentStore.save(MessageDocument, {
82
+ role: 'assistant',
83
+ text: 'Value is less or equal 200, but greater than 100',
84
+ });
85
+ }
86
+ }
@@ -0,0 +1,25 @@
1
+ widgets:
2
+ - widget: button
3
+ showWhen:
4
+ - error_custom
5
+ options:
6
+ transition: handleCustomError
7
+ label: Recover
8
+ - widget: button
9
+ showWhen:
10
+ - error_hybrid
11
+ options:
12
+ transition: handleHybridError
13
+ label: Recover
14
+ - widget: button
15
+ showWhen:
16
+ - error_retry_target
17
+ options:
18
+ transition: handleRetryTargetError
19
+ label: Recover
20
+ - widget: button
21
+ showWhen:
22
+ - sub_failed
23
+ options:
24
+ transition: handleSubFailed
25
+ label: Recover