@loopstack/delegate-error-example-workflow 0.21.6 → 0.21.7

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.
@@ -1,17 +1,19 @@
1
- import { ClaudeGenerateText, ClaudeGenerateTextResult, DelegateToolCalls, DelegateToolCallsResult, UpdateToolResult } from '@loopstack/claude-module';
2
1
  import { BaseWorkflow } from '@loopstack/common';
2
+ import type { LlmDelegateResult, LlmGenerateTextResult, LlmResultMeta } from '@loopstack/llm-provider-module';
3
+ import { LlmDelegateToolCallsTool, LlmGenerateTextTool, LlmUpdateToolResultTool } from '@loopstack/llm-provider-module';
3
4
  import { FailingSubWorkflowTool } from './tools/failing-sub-workflow.tool';
4
5
  import { RuntimeErrorTool } from './tools/runtime-error.tool';
5
6
  import { StrictSchemaTool } from './tools/strict-schema.tool';
6
7
  export declare class DelegateErrorWorkflow extends BaseWorkflow {
7
- claudeGenerateText: ClaudeGenerateText;
8
- delegateToolCalls: DelegateToolCalls;
9
- updateToolResult: UpdateToolResult;
8
+ llmGenerateText: LlmGenerateTextTool;
9
+ llmDelegateToolCalls: LlmDelegateToolCallsTool;
10
+ llmUpdateToolResult: LlmUpdateToolResultTool;
10
11
  strictSchema: StrictSchemaTool;
11
12
  runtimeError: RuntimeErrorTool;
12
13
  failingSubWorkflow: FailingSubWorkflowTool;
13
- llmResult?: ClaudeGenerateTextResult;
14
- delegateResult?: DelegateToolCallsResult;
14
+ llmResult?: LlmGenerateTextResult;
15
+ llmMeta?: LlmResultMeta;
16
+ delegateResult?: LlmDelegateResult;
15
17
  turnCount: number;
16
18
  setup(): Promise<void>;
17
19
  llmTurn(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"delegate-error.workflow.d.ts","sourceRoot":"","sources":["../src/delegate-error.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EAExB,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,EASb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAU9D,qBAGa,qBAAsB,SAAQ,YAAY;IACvC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,kBAAkB,EAAE,sBAAsB,CAAC;IAEzD,SAAS,CAAC,EAAE,wBAAwB,CAAC;IACrC,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,SAAS,EAAG,MAAM,CAAC;IAGb,KAAK;IAoBL,OAAO;IAwBP,gBAAgB;IAUhB,kBAAkB,CAAC,OAAO,EAAE,OAAO;IAWnC,aAAa;IAGb,kBAAkB;IASlB,OAAO;IAIb,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,SAAS;CAGlB"}
1
+ {"version":3,"file":"delegate-error.workflow.d.ts","sourceRoot":"","sources":["../src/delegate-error.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EASb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC9G,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EAEnB,uBAAuB,EACxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAU9D,qBAGa,qBAAsB,SAAQ,YAAY;IAMrD,eAAe,EAAE,mBAAmB,CAAC;IACD,oBAAoB,EAAE,wBAAwB,CAAC;IAC/C,mBAAmB,EAAE,uBAAuB,CAAC;IACnE,YAAY,EAAE,gBAAgB,CAAC;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,kBAAkB,EAAE,sBAAsB,CAAC;IAEzD,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,cAAc,CAAC,EAAE,iBAAiB,CAAC;IACnC,SAAS,EAAG,MAAM,CAAC;IAGb,KAAK;IAoBL,OAAO;IAgBP,gBAAgB;IAahB,kBAAkB,CAAC,OAAO,EAAE,OAAO;IAUnC,aAAa;IAab,kBAAkB;IASlB,OAAO;IAMb,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,SAAS;CAGlB"}
@@ -10,19 +10,20 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DelegateErrorWorkflow = void 0;
13
- const claude_module_1 = require("@loopstack/claude-module");
14
13
  const common_1 = require("@loopstack/common");
14
+ const llm_provider_module_1 = require("@loopstack/llm-provider-module");
15
15
  const failing_sub_workflow_tool_1 = require("./tools/failing-sub-workflow.tool");
16
16
  const runtime_error_tool_1 = require("./tools/runtime-error.tool");
17
17
  const strict_schema_tool_1 = require("./tools/strict-schema.tool");
18
18
  let DelegateErrorWorkflow = class DelegateErrorWorkflow extends common_1.BaseWorkflow {
19
- claudeGenerateText;
20
- delegateToolCalls;
21
- updateToolResult;
19
+ llmGenerateText;
20
+ llmDelegateToolCalls;
21
+ llmUpdateToolResult;
22
22
  strictSchema;
23
23
  runtimeError;
24
24
  failingSubWorkflow;
25
25
  llmResult;
26
+ llmMeta;
26
27
  delegateResult;
27
28
  turnCount;
28
29
  async setup() {
@@ -33,7 +34,7 @@ let DelegateErrorWorkflow = class DelegateErrorWorkflow extends common_1.BaseWor
33
34
  'This workflow tests how tool errors are handled and fed back to the LLM.\n\n' +
34
35
  'The LLM will deliberately trigger errors, then self-correct.',
35
36
  });
36
- await this.repository.save(claude_module_1.ClaudeMessageDocument, {
37
+ await this.repository.save(llm_provider_module_1.LlmMessageDocument, {
37
38
  role: 'user',
38
39
  content: 'Follow the instructions in your system prompt exactly. ' +
39
40
  'Start with step 1: call strictSchema with no arguments.',
@@ -41,41 +42,42 @@ let DelegateErrorWorkflow = class DelegateErrorWorkflow extends common_1.BaseWor
41
42
  }
42
43
  async llmTurn() {
43
44
  this.turnCount++;
44
- const result = await this.claudeGenerateText.call({
45
- system: 'You are a test assistant that follows instructions precisely. ' +
46
- 'You are fully autonomous — do NOT ask the user for input or confirmation. ' +
47
- 'Just proceed through each step on your own.\n\n' +
48
- 'Complete these steps IN ORDER. Do exactly one step per turn:\n\n' +
49
- '1. Call the `strictSchema` tool with NO arguments (empty object {}). This will fail — that is expected.\n' +
50
- '2. After seeing the validation error, call `strictSchema` correctly with { "name": "World" }.\n' +
51
- '3. Call the `runtimeError` tool with { "shouldFail": true }. This will fail — that is expected.\n' +
52
- '4. After seeing the runtime error, call `runtimeError` with { "shouldFail": false }.\n' +
53
- '5. Call the `failingSubWorkflow` tool with {}. This launches a sub-workflow that will fail — that is expected.\n' +
54
- '6. After seeing the sub-workflow error, respond with a brief summary of what happened (do NOT call any tools).\n\n' +
55
- 'IMPORTANT: Only perform ONE step per turn. Do NOT skip steps. Do NOT wait for user input between steps.',
56
- claude: { model: 'claude-sonnet-4-6' },
57
- messagesSearchTag: 'message',
58
- tools: ['strictSchema', 'runtimeError', 'failingSubWorkflow'],
45
+ const result = await this.llmGenerateText.call({}, {
46
+ config: {
47
+ system: this.render(__dirname + '/templates/system.md'),
48
+ },
59
49
  });
60
50
  this.llmResult = result.data;
51
+ this.llmMeta = result.metadata;
61
52
  }
62
53
  async executeToolCalls() {
63
- const result = await this.delegateToolCalls.call({
64
- message: this.llmResult,
65
- document: claude_module_1.ClaudeMessageDocument,
54
+ await this.repository.save(llm_provider_module_1.LlmMessageDocument, this.llmResult.message, {
55
+ meta: { response: this.llmResult.response, provider: this.llmMeta.provider },
56
+ });
57
+ const result = await this.llmDelegateToolCalls.call({
58
+ message: this.llmResult.message,
66
59
  callback: { transition: 'toolResultReceived' },
67
60
  });
68
61
  this.delegateResult = result.data;
69
62
  }
70
63
  async toolResultReceived(payload) {
71
- const result = await this.updateToolResult.call({
64
+ const result = await this.llmUpdateToolResult.call({
72
65
  delegateResult: this.delegateResult,
73
66
  completedTool: payload,
74
- document: claude_module_1.ClaudeMessageDocument,
75
67
  });
76
68
  this.delegateResult = result.data;
77
69
  }
78
- async toolsComplete() { }
70
+ async toolsComplete() {
71
+ await this.repository.save(llm_provider_module_1.LlmMessageDocument, {
72
+ role: 'user',
73
+ content: this.delegateResult.toolResults.map((tr) => ({
74
+ type: 'tool_result',
75
+ toolCallId: tr.toolCallId,
76
+ content: tr.content ?? '',
77
+ isError: tr.isError ?? false,
78
+ })),
79
+ });
80
+ }
79
81
  async cancelPendingTools() {
80
82
  const workflowId = this.ctx.context.workflowId;
81
83
  if (workflowId) {
@@ -83,31 +85,37 @@ let DelegateErrorWorkflow = class DelegateErrorWorkflow extends common_1.BaseWor
83
85
  }
84
86
  }
85
87
  async respond() {
86
- await this.repository.save(claude_module_1.ClaudeMessageDocument, this.llmResult, { id: this.llmResult.id });
88
+ await this.repository.save(llm_provider_module_1.LlmMessageDocument, this.llmResult.message, {
89
+ meta: { response: this.llmResult.response, provider: this.llmMeta.provider },
90
+ });
87
91
  }
88
92
  hasToolCalls() {
89
- return this.llmResult?.stop_reason === 'tool_use';
93
+ return this.llmResult?.message.stopReason === 'tool_use';
90
94
  }
91
95
  allToolsComplete() {
92
96
  return !!this.delegateResult?.allCompleted;
93
97
  }
94
98
  isEndTurn() {
95
- return this.llmResult?.stop_reason === 'end_turn';
99
+ return this.llmResult?.message.stopReason === 'end_turn';
96
100
  }
97
101
  };
98
102
  exports.DelegateErrorWorkflow = DelegateErrorWorkflow;
99
103
  __decorate([
100
- (0, common_1.InjectTool)(),
101
- __metadata("design:type", claude_module_1.ClaudeGenerateText)
102
- ], DelegateErrorWorkflow.prototype, "claudeGenerateText", void 0);
104
+ (0, common_1.InjectTool)({
105
+ provider: 'claude',
106
+ model: 'claude-sonnet-4-6',
107
+ tools: ['strictSchema', 'runtimeError', 'failingSubWorkflow'],
108
+ }),
109
+ __metadata("design:type", llm_provider_module_1.LlmGenerateTextTool)
110
+ ], DelegateErrorWorkflow.prototype, "llmGenerateText", void 0);
103
111
  __decorate([
104
- (0, common_1.InjectTool)(),
105
- __metadata("design:type", claude_module_1.DelegateToolCalls)
106
- ], DelegateErrorWorkflow.prototype, "delegateToolCalls", void 0);
112
+ (0, common_1.InjectTool)({ provider: 'claude' }),
113
+ __metadata("design:type", llm_provider_module_1.LlmDelegateToolCallsTool)
114
+ ], DelegateErrorWorkflow.prototype, "llmDelegateToolCalls", void 0);
107
115
  __decorate([
108
- (0, common_1.InjectTool)(),
109
- __metadata("design:type", claude_module_1.UpdateToolResult)
110
- ], DelegateErrorWorkflow.prototype, "updateToolResult", void 0);
116
+ (0, common_1.InjectTool)({ provider: 'claude' }),
117
+ __metadata("design:type", llm_provider_module_1.LlmUpdateToolResultTool)
118
+ ], DelegateErrorWorkflow.prototype, "llmUpdateToolResult", void 0);
111
119
  __decorate([
112
120
  (0, common_1.InjectTool)(),
113
121
  __metadata("design:type", strict_schema_tool_1.StrictSchemaTool)
@@ -1 +1 @@
1
- {"version":3,"file":"delegate-error.workflow.js","sourceRoot":"","sources":["../src/delegate-error.workflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"delegate-error.workflow.js","sourceRoot":"","sources":["../src/delegate-error.workflow.ts"],"names":[],"mappings":"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"}
@@ -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.
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "example",
10
10
  "workflow"
11
11
  ],
12
- "version": "0.21.6",
12
+ "version": "0.21.7",
13
13
  "license": "MIT",
14
14
  "author": {
15
15
  "name": "Jakob Klippel",
@@ -30,9 +30,10 @@
30
30
  "watch": "nest build --watch"
31
31
  },
32
32
  "dependencies": {
33
- "@loopstack/claude-module": "^0.22.5",
34
- "@loopstack/common": "^0.28.0",
35
- "@loopstack/core": "^0.28.0",
33
+ "@loopstack/claude-module": "^0.23.0",
34
+ "@loopstack/llm-provider-module": "^0.2.0",
35
+ "@loopstack/common": "^0.29.0",
36
+ "@loopstack/core": "^0.29.0",
36
37
  "@nestjs/common": "^11.1.19",
37
38
  "zod": "^4.3.6"
38
39
  },
@@ -1,6 +1,7 @@
1
1
  import { TestingModule } from '@nestjs/testing';
2
- import { ClaudeGenerateText, ClaudeModule } from '@loopstack/claude-module';
2
+ import { ClaudeModule } from '@loopstack/claude-module';
3
3
  import { LoopCoreModule, WorkflowProcessorService } from '@loopstack/core';
4
+ import { LlmGenerateTextTool } from '@loopstack/llm-provider-module';
4
5
  import { ToolMock, createStatelessContext, createWorkflowTest } from '@loopstack/testing';
5
6
  import { DelegateErrorWorkflow } from '../delegate-error.workflow';
6
7
  import { FailingSubWorkflowTool } from '../tools/failing-sub-workflow.tool';
@@ -11,17 +12,20 @@ import { FailingWorkflow } from '../workflows/failing.workflow';
11
12
  /**
12
13
  * Helper to create a mock LLM response with a single tool_use block.
13
14
  */
14
- function mockToolUseResponse(toolName: string, input: Record<string, unknown>, toolUseId = 'toolu_test_1') {
15
+ function mockToolUseResponse(toolName: string, args: Record<string, unknown>, toolCallId = 'toolu_test_1') {
15
16
  return {
16
17
  data: {
17
- id: 'msg_test',
18
- role: 'assistant',
19
- stop_reason: 'tool_use',
20
- content: [
21
- { type: 'text', text: `Calling ${toolName}...` },
22
- { type: 'tool_use', id: toolUseId, name: toolName, input },
23
- ],
18
+ message: {
19
+ id: 'msg_test',
20
+ role: 'assistant',
21
+ content: [
22
+ { type: 'text', text: `Calling ${toolName}...` },
23
+ { type: 'tool_call', id: toolCallId, name: toolName, args },
24
+ ],
25
+ stopReason: 'tool_use',
26
+ },
24
27
  },
28
+ metadata: { provider: 'claude', model: 'claude-sonnet-4-6' },
25
29
  };
26
30
  }
27
31
 
@@ -31,11 +35,14 @@ function mockToolUseResponse(toolName: string, input: Record<string, unknown>, t
31
35
  function mockEndTurnResponse(text: string) {
32
36
  return {
33
37
  data: {
34
- id: 'msg_final',
35
- role: 'assistant',
36
- stop_reason: 'end_turn',
37
- content: [{ type: 'text', text }],
38
+ message: {
39
+ id: 'msg_final',
40
+ role: 'assistant',
41
+ content: [{ type: 'text', text }],
42
+ stopReason: 'end_turn',
43
+ },
38
44
  },
45
+ metadata: { provider: 'claude', model: 'claude-sonnet-4-6' },
39
46
  };
40
47
  }
41
48
 
@@ -43,20 +50,20 @@ describe('DelegateErrorWorkflow', () => {
43
50
  let module: TestingModule;
44
51
  let workflow: DelegateErrorWorkflow;
45
52
  let processor: WorkflowProcessorService;
46
- let mockClaude: ToolMock;
53
+ let mockLlm: ToolMock;
47
54
 
48
55
  beforeEach(async () => {
49
56
  module = await createWorkflowTest()
50
57
  .forWorkflow(DelegateErrorWorkflow)
51
58
  .withImports(LoopCoreModule, ClaudeModule)
52
- .withToolOverride(ClaudeGenerateText)
59
+ .withToolOverride(LlmGenerateTextTool)
53
60
  // Real tools — we want to test actual validation and runtime errors
54
61
  .withProviders(StrictSchemaTool, RuntimeErrorTool, FailingSubWorkflowTool, FailingWorkflow)
55
62
  .compile();
56
63
 
57
64
  workflow = module.get(DelegateErrorWorkflow);
58
65
  processor = module.get(WorkflowProcessorService);
59
- mockClaude = module.get(ClaudeGenerateText);
66
+ mockLlm = module.get(LlmGenerateTextTool);
60
67
  });
61
68
 
62
69
  afterEach(async () => {
@@ -68,9 +75,8 @@ describe('DelegateErrorWorkflow', () => {
68
75
  });
69
76
 
70
77
  describe('validation error (bad schema args)', () => {
71
- it('should capture validation error as is_error tool result and loop back to ready', async () => {
72
- // LLM calls strictSchema with empty args — Zod validation will fail
73
- mockClaude.call
78
+ it('should capture validation error as isError tool result and loop back to ready', async () => {
79
+ mockLlm.call
74
80
  .mockResolvedValueOnce(mockToolUseResponse('strictSchema', {}))
75
81
  .mockResolvedValueOnce(mockEndTurnResponse('Done.'));
76
82
 
@@ -79,24 +85,20 @@ describe('DelegateErrorWorkflow', () => {
79
85
 
80
86
  expect(result.hasError).toBe(false);
81
87
  expect(result.place).toBe('end');
88
+ expect(mockLlm.call).toHaveBeenCalledTimes(2);
82
89
 
83
- // ClaudeGenerateText should have been called twice:
84
- // 1st: LLM returns bad tool call → error fed back → loops to ready
85
- // 2nd: LLM sees the error and responds with end_turn
86
- expect(mockClaude.call).toHaveBeenCalledTimes(2);
87
-
88
- // Should have tool result documents with is_error
89
- const toolResultDocs = result.documents.filter((d) =>
90
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
90
+ const toolResultDocs = result.documents.filter(
91
+ (d) =>
92
+ Array.isArray(d.content?.content) &&
93
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
91
94
  );
92
95
  expect(toolResultDocs.length).toBeGreaterThan(0);
93
96
  });
94
97
  });
95
98
 
96
99
  describe('runtime error (tool throws)', () => {
97
- it('should capture runtime error as is_error tool result and loop back to ready', async () => {
98
- // LLM calls runtimeError with shouldFail: true — tool will throw
99
- mockClaude.call
100
+ it('should capture runtime error as isError tool result and loop back to ready', async () => {
101
+ mockLlm.call
100
102
  .mockResolvedValueOnce(mockToolUseResponse('runtimeError', { shouldFail: true }))
101
103
  .mockResolvedValueOnce(mockEndTurnResponse('Done.'));
102
104
 
@@ -105,10 +107,12 @@ describe('DelegateErrorWorkflow', () => {
105
107
 
106
108
  expect(result.hasError).toBe(false);
107
109
  expect(result.place).toBe('end');
108
- expect(mockClaude.call).toHaveBeenCalledTimes(2);
110
+ expect(mockLlm.call).toHaveBeenCalledTimes(2);
109
111
 
110
- const toolResultDocs = result.documents.filter((d) =>
111
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
112
+ const toolResultDocs = result.documents.filter(
113
+ (d) =>
114
+ Array.isArray(d.content?.content) &&
115
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
112
116
  );
113
117
  expect(toolResultDocs.length).toBeGreaterThan(0);
114
118
  });
@@ -116,8 +120,7 @@ describe('DelegateErrorWorkflow', () => {
116
120
 
117
121
  describe('successful tool call', () => {
118
122
  it('should process successful tool call without errors', async () => {
119
- // LLM calls strictSchema with valid args
120
- mockClaude.call
123
+ mockLlm.call
121
124
  .mockResolvedValueOnce(mockToolUseResponse('strictSchema', { name: 'World' }))
122
125
  .mockResolvedValueOnce(mockEndTurnResponse('Done.'));
123
126
 
@@ -126,19 +129,20 @@ describe('DelegateErrorWorkflow', () => {
126
129
 
127
130
  expect(result.hasError).toBe(false);
128
131
  expect(result.place).toBe('end');
129
- expect(mockClaude.call).toHaveBeenCalledTimes(2);
132
+ expect(mockLlm.call).toHaveBeenCalledTimes(2);
130
133
 
131
- // No error tool results
132
- const errorToolResults = result.documents.filter((d) =>
133
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
134
+ const errorToolResults = result.documents.filter(
135
+ (d) =>
136
+ Array.isArray(d.content?.content) &&
137
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
134
138
  );
135
139
  expect(errorToolResults).toHaveLength(0);
136
140
  });
137
141
  });
138
142
 
139
143
  describe('error metadata in tool result documents', () => {
140
- it('should include validation error message in is_error tool result', async () => {
141
- mockClaude.call
144
+ it('should include validation error message in isError tool result', async () => {
145
+ mockLlm.call
142
146
  .mockResolvedValueOnce(mockToolUseResponse('strictSchema', {}))
143
147
  .mockResolvedValueOnce(mockEndTurnResponse('Done.'));
144
148
 
@@ -147,19 +151,22 @@ describe('DelegateErrorWorkflow', () => {
147
151
 
148
152
  expect(result.hasError).toBe(false);
149
153
 
150
- const toolResultDocs = result.documents.filter((d) =>
151
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
154
+ const toolResultDocs = result.documents.filter(
155
+ (d) =>
156
+ Array.isArray(d.content?.content) &&
157
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
152
158
  );
153
159
  expect(toolResultDocs.length).toBeGreaterThan(0);
154
160
 
155
- const errorResult = toolResultDocs[0].content.toolResults.find((tr: { is_error?: boolean }) => tr.is_error);
161
+ const errorResult = toolResultDocs[0].content.content.find(
162
+ (b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError,
163
+ );
156
164
  expect(errorResult.content).toBeDefined();
157
- // Zod v4 error message for missing required string field
158
165
  expect(errorResult.content).toContain('invalid_type');
159
166
  });
160
167
 
161
- it('should include runtime error message in is_error tool result', async () => {
162
- mockClaude.call
168
+ it('should include runtime error message in isError tool result', async () => {
169
+ mockLlm.call
163
170
  .mockResolvedValueOnce(mockToolUseResponse('runtimeError', { shouldFail: true }))
164
171
  .mockResolvedValueOnce(mockEndTurnResponse('Done.'));
165
172
 
@@ -168,49 +175,60 @@ describe('DelegateErrorWorkflow', () => {
168
175
 
169
176
  expect(result.hasError).toBe(false);
170
177
 
171
- const toolResultDocs = result.documents.filter((d) =>
172
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
178
+ const toolResultDocs = result.documents.filter(
179
+ (d) =>
180
+ Array.isArray(d.content?.content) &&
181
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
173
182
  );
174
183
  expect(toolResultDocs.length).toBeGreaterThan(0);
175
184
 
176
- const errorResult = toolResultDocs[0].content.toolResults.find((tr: { is_error?: boolean }) => tr.is_error);
185
+ const errorResult = toolResultDocs[0].content.content.find(
186
+ (b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError,
187
+ );
177
188
  expect(errorResult.content).toContain('external service unavailable');
178
189
  });
179
190
  });
180
191
 
181
192
  describe('validation and runtime errors produce identical structure', () => {
182
- it('should produce the same is_error tool result shape for both error types', async () => {
183
- // Run validation error
184
- mockClaude.call.mockResolvedValueOnce(mockToolUseResponse('strictSchema', {}));
193
+ it('should produce the same isError tool result shape for both error types', async () => {
194
+ mockLlm.call
195
+ .mockResolvedValueOnce(mockToolUseResponse('strictSchema', {}))
196
+ .mockResolvedValueOnce(mockEndTurnResponse('Recovered.'));
185
197
  const context1 = createStatelessContext();
186
198
  const result1 = await processor.process(workflow, {}, context1);
187
199
 
188
- const validationErrorDoc = result1.documents.find((d) =>
189
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
200
+ const validationErrorDoc = result1.documents.find(
201
+ (d) =>
202
+ Array.isArray(d.content?.content) &&
203
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
204
+ );
205
+ const validationError = validationErrorDoc?.content.content.find(
206
+ (b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError,
190
207
  );
191
- const validationError = validationErrorDoc?.content.toolResults.find((tr: { is_error?: boolean }) => tr.is_error);
192
208
 
193
- // Run runtime error (fresh module needed for clean state)
194
- mockClaude.call.mockResolvedValueOnce(mockToolUseResponse('runtimeError', { shouldFail: true }));
209
+ mockLlm.call
210
+ .mockResolvedValueOnce(mockToolUseResponse('runtimeError', { shouldFail: true }))
211
+ .mockResolvedValueOnce(mockEndTurnResponse('Recovered.'));
195
212
  const context2 = createStatelessContext();
196
213
  const result2 = await processor.process(workflow, {}, context2);
197
214
 
198
- const runtimeErrorDoc = result2.documents.find((d) =>
199
- d.content?.toolResults?.some((tr: { is_error?: boolean }) => tr.is_error),
215
+ const runtimeErrorDoc = result2.documents.find(
216
+ (d) =>
217
+ Array.isArray(d.content?.content) &&
218
+ d.content.content.some((b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError),
219
+ );
220
+ const runtimeError = runtimeErrorDoc?.content.content.find(
221
+ (b: { type?: string; isError?: boolean }) => b.type === 'tool_result' && b.isError,
200
222
  );
201
- const runtimeError = runtimeErrorDoc?.content.toolResults.find((tr: { is_error?: boolean }) => tr.is_error);
202
223
 
203
- // Both should have the same structure
204
224
  expect(validationError).toBeDefined();
205
225
  expect(runtimeError).toBeDefined();
206
- expect(validationError.type).toBe('tool_result');
207
- expect(runtimeError.type).toBe('tool_result');
208
- expect(validationError.is_error).toBe(true);
209
- expect(runtimeError.is_error).toBe(true);
226
+ expect(validationError.isError).toBe(true);
227
+ expect(runtimeError.isError).toBe(true);
210
228
  expect(typeof validationError.content).toBe('string');
211
229
  expect(typeof runtimeError.content).toBe('string');
212
- expect(typeof validationError.tool_use_id).toBe('string');
213
- expect(typeof runtimeError.tool_use_id).toBe('string');
230
+ expect(typeof validationError.toolCallId).toBe('string');
231
+ expect(typeof runtimeError.toolCallId).toBe('string');
214
232
  });
215
233
  });
216
234
  });
@@ -1,11 +1,3 @@
1
- import {
2
- ClaudeGenerateText,
3
- ClaudeGenerateTextResult,
4
- ClaudeMessageDocument,
5
- DelegateToolCalls,
6
- DelegateToolCallsResult,
7
- UpdateToolResult,
8
- } from '@loopstack/claude-module';
9
1
  import {
10
2
  BaseWorkflow,
11
3
  Final,
@@ -17,6 +9,13 @@ import {
17
9
  Transition,
18
10
  Workflow,
19
11
  } from '@loopstack/common';
12
+ import type { LlmDelegateResult, LlmGenerateTextResult, LlmResultMeta } from '@loopstack/llm-provider-module';
13
+ import {
14
+ LlmDelegateToolCallsTool,
15
+ LlmGenerateTextTool,
16
+ LlmMessageDocument,
17
+ LlmUpdateToolResultTool,
18
+ } from '@loopstack/llm-provider-module';
20
19
  import { FailingSubWorkflowTool } from './tools/failing-sub-workflow.tool';
21
20
  import { RuntimeErrorTool } from './tools/runtime-error.tool';
22
21
  import { StrictSchemaTool } from './tools/strict-schema.tool';
@@ -33,15 +32,21 @@ import { StrictSchemaTool } from './tools/strict-schema.tool';
33
32
  uiConfig: __dirname + '/delegate-error.ui.yaml',
34
33
  })
35
34
  export class DelegateErrorWorkflow extends BaseWorkflow {
36
- @InjectTool() claudeGenerateText: ClaudeGenerateText;
37
- @InjectTool() delegateToolCalls: DelegateToolCalls;
38
- @InjectTool() updateToolResult: UpdateToolResult;
35
+ @InjectTool({
36
+ provider: 'claude',
37
+ model: 'claude-sonnet-4-6',
38
+ tools: ['strictSchema', 'runtimeError', 'failingSubWorkflow'],
39
+ })
40
+ llmGenerateText: LlmGenerateTextTool;
41
+ @InjectTool({ provider: 'claude' }) llmDelegateToolCalls: LlmDelegateToolCallsTool;
42
+ @InjectTool({ provider: 'claude' }) llmUpdateToolResult: LlmUpdateToolResultTool;
39
43
  @InjectTool() strictSchema: StrictSchemaTool;
40
44
  @InjectTool() runtimeError: RuntimeErrorTool;
41
45
  @InjectTool() failingSubWorkflow: FailingSubWorkflowTool;
42
46
 
43
- llmResult?: ClaudeGenerateTextResult;
44
- delegateResult?: DelegateToolCallsResult;
47
+ llmResult?: LlmGenerateTextResult;
48
+ llmMeta?: LlmResultMeta;
49
+ delegateResult?: LlmDelegateResult;
45
50
  turnCount!: number;
46
51
 
47
52
  @Initial({ to: 'ready' })
@@ -56,7 +61,7 @@ export class DelegateErrorWorkflow extends BaseWorkflow {
56
61
  'The LLM will deliberately trigger errors, then self-correct.',
57
62
  });
58
63
 
59
- await this.repository.save(ClaudeMessageDocument, {
64
+ await this.repository.save(LlmMessageDocument, {
60
65
  role: 'user',
61
66
  content:
62
67
  'Follow the instructions in your system prompt exactly. ' +
@@ -67,32 +72,27 @@ export class DelegateErrorWorkflow extends BaseWorkflow {
67
72
  @Transition({ from: 'ready', to: 'prompt_executed' })
68
73
  async llmTurn() {
69
74
  this.turnCount++;
70
- const result: ToolResult<ClaudeGenerateTextResult> = await this.claudeGenerateText.call({
71
- system:
72
- 'You are a test assistant that follows instructions precisely. ' +
73
- 'You are fully autonomous — do NOT ask the user for input or confirmation. ' +
74
- 'Just proceed through each step on your own.\n\n' +
75
- 'Complete these steps IN ORDER. Do exactly one step per turn:\n\n' +
76
- '1. Call the `strictSchema` tool with NO arguments (empty object {}). This will fail — that is expected.\n' +
77
- '2. After seeing the validation error, call `strictSchema` correctly with { "name": "World" }.\n' +
78
- '3. Call the `runtimeError` tool with { "shouldFail": true }. This will fail — that is expected.\n' +
79
- '4. After seeing the runtime error, call `runtimeError` with { "shouldFail": false }.\n' +
80
- '5. Call the `failingSubWorkflow` tool with {}. This launches a sub-workflow that will fail — that is expected.\n' +
81
- '6. After seeing the sub-workflow error, respond with a brief summary of what happened (do NOT call any tools).\n\n' +
82
- 'IMPORTANT: Only perform ONE step per turn. Do NOT skip steps. Do NOT wait for user input between steps.',
83
- claude: { model: 'claude-sonnet-4-6' },
84
- messagesSearchTag: 'message',
85
- tools: ['strictSchema', 'runtimeError', 'failingSubWorkflow'],
86
- });
75
+ const result: ToolResult<LlmGenerateTextResult, LlmResultMeta> = await this.llmGenerateText.call(
76
+ {},
77
+ {
78
+ config: {
79
+ system: this.render(__dirname + '/templates/system.md'),
80
+ },
81
+ },
82
+ );
87
83
  this.llmResult = result.data;
84
+ this.llmMeta = result.metadata;
88
85
  }
89
86
 
90
87
  @Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
91
88
  @Guard('hasToolCalls')
92
89
  async executeToolCalls() {
93
- const result: ToolResult<DelegateToolCallsResult> = await this.delegateToolCalls.call({
94
- message: this.llmResult!,
95
- document: ClaudeMessageDocument,
90
+ await this.repository.save(LlmMessageDocument, this.llmResult!.message, {
91
+ meta: { response: this.llmResult!.response, provider: this.llmMeta!.provider },
92
+ });
93
+
94
+ const result: ToolResult<LlmDelegateResult> = await this.llmDelegateToolCalls.call({
95
+ message: this.llmResult!.message,
96
96
  callback: { transition: 'toolResultReceived' },
97
97
  });
98
98
  this.delegateResult = result.data;
@@ -100,17 +100,26 @@ export class DelegateErrorWorkflow extends BaseWorkflow {
100
100
 
101
101
  @Transition({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true })
102
102
  async toolResultReceived(payload: unknown) {
103
- const result = await this.updateToolResult.call({
103
+ const result = await this.llmUpdateToolResult.call({
104
104
  delegateResult: this.delegateResult!,
105
105
  completedTool: payload,
106
- document: ClaudeMessageDocument,
107
106
  });
108
- this.delegateResult = result.data as DelegateToolCallsResult;
107
+ this.delegateResult = result.data as LlmDelegateResult;
109
108
  }
110
109
 
111
110
  @Transition({ from: 'awaiting_tools', to: 'ready' })
112
111
  @Guard('allToolsComplete')
113
- async toolsComplete() {}
112
+ async toolsComplete() {
113
+ await this.repository.save(LlmMessageDocument, {
114
+ role: 'user',
115
+ content: this.delegateResult!.toolResults.map((tr) => ({
116
+ type: 'tool_result' as const,
117
+ toolCallId: tr.toolCallId,
118
+ content: tr.content ?? '',
119
+ isError: tr.isError ?? false,
120
+ })),
121
+ });
122
+ }
114
123
 
115
124
  @Transition({ from: 'awaiting_tools', to: 'ready', wait: true })
116
125
  async cancelPendingTools() {
@@ -123,11 +132,13 @@ export class DelegateErrorWorkflow extends BaseWorkflow {
123
132
  @Final({ from: 'prompt_executed' })
124
133
  @Guard('isEndTurn')
125
134
  async respond() {
126
- await this.repository.save(ClaudeMessageDocument, this.llmResult!, { id: this.llmResult!.id });
135
+ await this.repository.save(LlmMessageDocument, this.llmResult!.message, {
136
+ meta: { response: this.llmResult!.response, provider: this.llmMeta!.provider },
137
+ });
127
138
  }
128
139
 
129
140
  private hasToolCalls(): boolean {
130
- return this.llmResult?.stop_reason === 'tool_use';
141
+ return this.llmResult?.message.stopReason === 'tool_use';
131
142
  }
132
143
 
133
144
  private allToolsComplete(): boolean {
@@ -135,6 +146,6 @@ export class DelegateErrorWorkflow extends BaseWorkflow {
135
146
  }
136
147
 
137
148
  private isEndTurn(): boolean {
138
- return this.llmResult?.stop_reason === 'end_turn';
149
+ return this.llmResult?.message.stopReason === 'end_turn';
139
150
  }
140
151
  }
@@ -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.