@loopstack/claude-module 0.21.0 → 0.21.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.
- package/dist/claude.module.d.ts.map +1 -1
- package/dist/claude.module.js +6 -4
- package/dist/claude.module.js.map +1 -1
- package/dist/services/claude-tools-helper.service.d.ts.map +1 -1
- package/dist/services/claude-tools-helper.service.js +6 -8
- package/dist/services/claude-tools-helper.service.js.map +1 -1
- package/dist/tools/claude-generate-document.tool.js +2 -2
- package/dist/tools/claude-generate-document.tool.js.map +1 -1
- package/dist/tools/claude-generate-text.tool.d.ts +3 -0
- package/dist/tools/claude-generate-text.tool.d.ts.map +1 -1
- package/dist/tools/claude-generate-text.tool.js +43 -1
- package/dist/tools/claude-generate-text.tool.js.map +1 -1
- package/dist/tools/delegate-tool-calls.tool.d.ts +28 -0
- package/dist/tools/delegate-tool-calls.tool.d.ts.map +1 -0
- package/dist/tools/delegate-tool-calls.tool.js +146 -0
- package/dist/tools/delegate-tool-calls.tool.js.map +1 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/update-tool-result.tool.d.ts +24 -0
- package/dist/tools/update-tool-result.tool.d.ts.map +1 -0
- package/dist/tools/update-tool-result.tool.js +124 -0
- package/dist/tools/update-tool-result.tool.js.map +1 -0
- package/package.json +3 -3
- package/src/claude.module.ts +13 -5
- package/src/services/claude-tools-helper.service.ts +6 -8
- package/src/tools/claude-generate-document.tool.ts +1 -1
- package/src/tools/claude-generate-text.tool.ts +66 -1
- package/src/tools/delegate-tool-calls.tool.ts +198 -0
- package/src/tools/index.ts +2 -1
- package/src/tools/update-tool-result.tool.ts +139 -0
- package/dist/tools/claude-delegate-tool-call.tool.d.ts +0 -19
- package/dist/tools/claude-delegate-tool-call.tool.d.ts.map +0 -1
- package/dist/tools/claude-delegate-tool-call.tool.js +0 -102
- package/dist/tools/claude-delegate-tool-call.tool.js.map +0 -1
- package/src/tools/claude-delegate-tool-call.tool.ts +0 -125
package/src/claude.module.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { LoopCoreModule } from '@loopstack/core';
|
|
3
3
|
import { ClaudeMessageDocument } from './documents';
|
|
4
4
|
import { ClaudeClientService, ClaudeMessagesHelperService, ClaudeToolsHelperService } from './services';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ClaudeGenerateDocument,
|
|
7
|
+
ClaudeGenerateObject,
|
|
8
|
+
ClaudeGenerateText,
|
|
9
|
+
DelegateToolCalls,
|
|
10
|
+
UpdateToolResult,
|
|
11
|
+
} from './tools';
|
|
6
12
|
|
|
7
13
|
@Module({
|
|
8
|
-
imports: [
|
|
14
|
+
imports: [LoopCoreModule],
|
|
9
15
|
providers: [
|
|
10
16
|
// services
|
|
11
17
|
ClaudeClientService,
|
|
@@ -16,7 +22,8 @@ import { ClaudeDelegateToolCall, ClaudeGenerateDocument, ClaudeGenerateObject, C
|
|
|
16
22
|
ClaudeGenerateText,
|
|
17
23
|
ClaudeGenerateObject,
|
|
18
24
|
ClaudeGenerateDocument,
|
|
19
|
-
|
|
25
|
+
DelegateToolCalls,
|
|
26
|
+
UpdateToolResult,
|
|
20
27
|
|
|
21
28
|
// documents
|
|
22
29
|
ClaudeMessageDocument,
|
|
@@ -25,7 +32,8 @@ import { ClaudeDelegateToolCall, ClaudeGenerateDocument, ClaudeGenerateObject, C
|
|
|
25
32
|
ClaudeGenerateText,
|
|
26
33
|
ClaudeGenerateObject,
|
|
27
34
|
ClaudeGenerateDocument,
|
|
28
|
-
|
|
35
|
+
DelegateToolCalls,
|
|
36
|
+
UpdateToolResult,
|
|
29
37
|
ClaudeMessageDocument,
|
|
30
38
|
],
|
|
31
39
|
})
|
|
@@ -22,15 +22,13 @@ export class ClaudeToolsHelperService {
|
|
|
22
22
|
throw new Error(`Block ${tool.constructor.name} is missing @BlockConfig decorator`);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
const jsonSchema = toJSONSchema(inputSchema);
|
|
25
|
+
const jsonSchema = inputSchema ? toJSONSchema(inputSchema) : { type: 'object' };
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
27
|
+
toolDefinitions.push({
|
|
28
|
+
name: toolName,
|
|
29
|
+
description: config.description ?? '',
|
|
30
|
+
input_schema: jsonSchema as Anthropic.Tool['input_schema'],
|
|
31
|
+
});
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
return toolDefinitions.length ? toolDefinitions : undefined;
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
WorkflowMetadataInterface,
|
|
10
10
|
getBlockTool,
|
|
11
11
|
} from '@loopstack/common';
|
|
12
|
-
import { CreateDocument } from '@loopstack/core
|
|
12
|
+
import { CreateDocument } from '@loopstack/core';
|
|
13
13
|
import {
|
|
14
14
|
ClaudeGenerateObject,
|
|
15
15
|
ClaudeGenerateObjectArgsType,
|
|
@@ -5,10 +5,14 @@ import {
|
|
|
5
5
|
Input,
|
|
6
6
|
RunContext,
|
|
7
7
|
Tool,
|
|
8
|
+
ToolCallEntry,
|
|
9
|
+
ToolCallsMap,
|
|
8
10
|
ToolInterface,
|
|
9
11
|
ToolResult,
|
|
12
|
+
ToolSideEffects,
|
|
10
13
|
WorkflowInterface,
|
|
11
14
|
WorkflowMetadataInterface,
|
|
15
|
+
getBlockTool,
|
|
12
16
|
} from '@loopstack/common';
|
|
13
17
|
import { ClaudeGenerateToolBaseSchema } from '../schemas/claude-generate-tool-base.schema';
|
|
14
18
|
import { ClaudeClientService } from '../services';
|
|
@@ -18,6 +22,7 @@ import { applyCacheBreakpoints } from '../utils/cache.utils';
|
|
|
18
22
|
|
|
19
23
|
export const ClaudeGenerateTextSchema = ClaudeGenerateToolBaseSchema.extend({
|
|
20
24
|
tools: z.array(z.string()).optional(),
|
|
25
|
+
document: z.string().optional(),
|
|
21
26
|
}).strict();
|
|
22
27
|
|
|
23
28
|
type ClaudeGenerateTextArgsType = z.infer<typeof ClaudeGenerateTextSchema>;
|
|
@@ -72,8 +77,23 @@ export class ClaudeGenerateText implements ToolInterface<ClaudeGenerateTextArgsT
|
|
|
72
77
|
cache: args.claude?.cache,
|
|
73
78
|
});
|
|
74
79
|
|
|
80
|
+
const toolCalls = this.extractToolCalls(response);
|
|
81
|
+
|
|
82
|
+
let effects: ToolSideEffects[] = [];
|
|
83
|
+
|
|
84
|
+
if (args.document) {
|
|
85
|
+
const docResult = await this.createResponseMessage(args.document, response, ctx, parent, runtime);
|
|
86
|
+
if (docResult.effects) {
|
|
87
|
+
effects = [...docResult.effects];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
return {
|
|
76
|
-
data:
|
|
92
|
+
data: {
|
|
93
|
+
...response,
|
|
94
|
+
...(toolCalls ? { toolCalls } : {}),
|
|
95
|
+
},
|
|
96
|
+
...(effects.length > 0 ? { effects } : {}),
|
|
77
97
|
metadata: {
|
|
78
98
|
usage: {
|
|
79
99
|
inputTokens: response.usage.input_tokens,
|
|
@@ -85,6 +105,51 @@ export class ClaudeGenerateText implements ToolInterface<ClaudeGenerateTextArgsT
|
|
|
85
105
|
};
|
|
86
106
|
}
|
|
87
107
|
|
|
108
|
+
private async createResponseMessage(
|
|
109
|
+
document: string,
|
|
110
|
+
response: Anthropic.Message,
|
|
111
|
+
ctx: RunContext,
|
|
112
|
+
parent: WorkflowInterface,
|
|
113
|
+
runtime: WorkflowMetadataInterface,
|
|
114
|
+
): Promise<ToolResult> {
|
|
115
|
+
const createDocumentTool = getBlockTool<ToolInterface>(parent, 'createDocument');
|
|
116
|
+
if (!createDocumentTool) {
|
|
117
|
+
throw new Error('createDocument tool not found in parent context.');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return createDocumentTool.execute(
|
|
121
|
+
{
|
|
122
|
+
id: response.id,
|
|
123
|
+
document,
|
|
124
|
+
update: {
|
|
125
|
+
content: response,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
ctx,
|
|
129
|
+
parent,
|
|
130
|
+
runtime,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private extractToolCalls(response: Anthropic.Message): ToolCallsMap | null {
|
|
135
|
+
if (response.stop_reason !== 'tool_use') {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const toolCalls: ToolCallsMap = {};
|
|
140
|
+
for (const block of response.content) {
|
|
141
|
+
if (block.type === 'tool_use') {
|
|
142
|
+
toolCalls[block.name] = {
|
|
143
|
+
id: block.id,
|
|
144
|
+
name: block.name,
|
|
145
|
+
input: block.input,
|
|
146
|
+
} satisfies ToolCallEntry;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return Object.keys(toolCalls).length > 0 ? toolCalls : null;
|
|
151
|
+
}
|
|
152
|
+
|
|
88
153
|
private async handleGenerateText(
|
|
89
154
|
client: Anthropic,
|
|
90
155
|
options: {
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import {
|
|
5
|
+
InjectTool,
|
|
6
|
+
Input,
|
|
7
|
+
RunContext,
|
|
8
|
+
Tool,
|
|
9
|
+
ToolInterface,
|
|
10
|
+
ToolResult,
|
|
11
|
+
ToolSideEffects,
|
|
12
|
+
WorkflowInterface,
|
|
13
|
+
WorkflowMetadataInterface,
|
|
14
|
+
} from '@loopstack/common';
|
|
15
|
+
import { CreateDocument, EventSubscriberService, StateMachineToolCallProcessorService } from '@loopstack/core';
|
|
16
|
+
|
|
17
|
+
const DelegateToolCallsSchema = z.object({
|
|
18
|
+
message: z.object({
|
|
19
|
+
id: z.string().optional(),
|
|
20
|
+
content: z.array(z.any()),
|
|
21
|
+
}),
|
|
22
|
+
document: z.string().optional(),
|
|
23
|
+
skipResponseMessage: z.boolean().optional(),
|
|
24
|
+
callback: z
|
|
25
|
+
.object({
|
|
26
|
+
transition: z.string(),
|
|
27
|
+
})
|
|
28
|
+
.optional(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type DelegateToolCallsArgs = z.infer<typeof DelegateToolCallsSchema>;
|
|
32
|
+
|
|
33
|
+
interface ToolResultEntry {
|
|
34
|
+
type: 'tool_result';
|
|
35
|
+
tool_use_id: string;
|
|
36
|
+
content?: string;
|
|
37
|
+
is_error?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Injectable()
|
|
41
|
+
@Tool({
|
|
42
|
+
config: {
|
|
43
|
+
description: 'Delegate tool calls from an LLM response. Handles both sync tools and async task tools.',
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
export class DelegateToolCalls implements ToolInterface<DelegateToolCallsArgs> {
|
|
47
|
+
private readonly logger = new Logger(DelegateToolCalls.name);
|
|
48
|
+
|
|
49
|
+
@InjectTool() private createDocument: CreateDocument;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
private readonly toolCallProcessor: StateMachineToolCallProcessorService,
|
|
53
|
+
private readonly eventSubscriberService: EventSubscriberService,
|
|
54
|
+
) {}
|
|
55
|
+
|
|
56
|
+
@Input({
|
|
57
|
+
schema: DelegateToolCallsSchema,
|
|
58
|
+
})
|
|
59
|
+
args: DelegateToolCallsArgs;
|
|
60
|
+
|
|
61
|
+
async execute(
|
|
62
|
+
args: DelegateToolCallsArgs,
|
|
63
|
+
ctx: RunContext,
|
|
64
|
+
parent: WorkflowInterface,
|
|
65
|
+
metadata: WorkflowMetadataInterface,
|
|
66
|
+
): Promise<ToolResult> {
|
|
67
|
+
const contentBlocks = args.message.content as Anthropic.ContentBlock[];
|
|
68
|
+
const toolUseBlocks = contentBlocks.filter((b): b is Anthropic.ToolUseBlock => b.type === 'tool_use');
|
|
69
|
+
|
|
70
|
+
if (toolUseBlocks.length === 0) {
|
|
71
|
+
return {
|
|
72
|
+
data: { allCompleted: true, toolResults: [], message: args.message, pendingCount: 0 },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 1. Execute ALL tools in parallel
|
|
77
|
+
const results = await Promise.all(toolUseBlocks.map((block) => this.executeTool(block, ctx, parent, metadata)));
|
|
78
|
+
|
|
79
|
+
// 2. Register subscribers for async results + build toolResults array
|
|
80
|
+
let pendingCount = 0;
|
|
81
|
+
const toolResults: ToolResultEntry[] = [];
|
|
82
|
+
const toolEffects: ToolSideEffects[] = [];
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < toolUseBlocks.length; i++) {
|
|
85
|
+
const block = toolUseBlocks[i];
|
|
86
|
+
const result = results[i];
|
|
87
|
+
|
|
88
|
+
// Collect effects from tool execution (e.g. link documents)
|
|
89
|
+
if (result.effects) {
|
|
90
|
+
toolEffects.push(...result.effects);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const resultData = result.data as Record<string, unknown> | undefined;
|
|
94
|
+
if (resultData?.mode === 'async') {
|
|
95
|
+
// Validate callback is provided
|
|
96
|
+
if (!args.callback?.transition) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Tool "${block.name}" returned an async result but no callback.transition was provided. ` +
|
|
99
|
+
`Add a callback configuration to the delegateToolCalls args.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Register event subscriber
|
|
104
|
+
await this.eventSubscriberService.registerSubscriber(
|
|
105
|
+
ctx.pipelineId,
|
|
106
|
+
ctx.workflowId!,
|
|
107
|
+
args.callback.transition,
|
|
108
|
+
resultData.correlationId as string,
|
|
109
|
+
resultData.eventName as string,
|
|
110
|
+
ctx.userId,
|
|
111
|
+
ctx.workspaceId,
|
|
112
|
+
{ toolUseId: block.id, toolName: block.name },
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
pendingCount++;
|
|
116
|
+
} else {
|
|
117
|
+
toolResults.push({
|
|
118
|
+
type: 'tool_result',
|
|
119
|
+
tool_use_id: block.id,
|
|
120
|
+
content: result.data ? JSON.stringify(result.data, null, 2) : '',
|
|
121
|
+
is_error: !!result.error,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 3. Create response document first (tool call message), then append
|
|
127
|
+
// tool execution effects (e.g. link documents) so they appear after.
|
|
128
|
+
const effects: ToolSideEffects[] = [];
|
|
129
|
+
if (args.document && !args.skipResponseMessage) {
|
|
130
|
+
const docResult = await this.createResponseDocument(args, toolResults, ctx, parent, metadata);
|
|
131
|
+
if (docResult.effects) {
|
|
132
|
+
effects.push(...docResult.effects);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
effects.push(...toolEffects);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
data: {
|
|
139
|
+
allCompleted: pendingCount === 0,
|
|
140
|
+
toolResults,
|
|
141
|
+
message: args.message,
|
|
142
|
+
pendingCount,
|
|
143
|
+
},
|
|
144
|
+
effects,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private async executeTool(
|
|
149
|
+
block: Anthropic.ToolUseBlock,
|
|
150
|
+
ctx: RunContext,
|
|
151
|
+
parent: WorkflowInterface,
|
|
152
|
+
metadata: WorkflowMetadataInterface,
|
|
153
|
+
): Promise<ToolResult> {
|
|
154
|
+
const tool = this.toolCallProcessor.getTool(parent, block.name);
|
|
155
|
+
const parsedArgs = this.toolCallProcessor.parseArgs(
|
|
156
|
+
tool,
|
|
157
|
+
block.input as Record<string, unknown> | undefined,
|
|
158
|
+
metadata.transition!,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
return await this.toolCallProcessor.executeToolCall(tool, parsedArgs, ctx, parent, metadata);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
165
|
+
this.logger.error(`Tool "${block.name}" failed: ${errorMessage}`);
|
|
166
|
+
return {
|
|
167
|
+
data: errorMessage,
|
|
168
|
+
error: errorMessage,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async createResponseDocument(
|
|
174
|
+
args: DelegateToolCallsArgs,
|
|
175
|
+
toolResults: ToolResultEntry[],
|
|
176
|
+
ctx: RunContext,
|
|
177
|
+
parent: WorkflowInterface,
|
|
178
|
+
metadata: WorkflowMetadataInterface,
|
|
179
|
+
): Promise<ToolResult> {
|
|
180
|
+
return this.createDocument.execute(
|
|
181
|
+
{
|
|
182
|
+
id: args.message.id,
|
|
183
|
+
document: args.document!,
|
|
184
|
+
validate: 'skip' as const,
|
|
185
|
+
update: {
|
|
186
|
+
content: {
|
|
187
|
+
role: 'assistant',
|
|
188
|
+
content: args.message.content,
|
|
189
|
+
toolResults,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
ctx,
|
|
194
|
+
parent,
|
|
195
|
+
metadata,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
package/src/tools/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './claude-generate-text.tool';
|
|
2
2
|
export * from './claude-generate-object.tool';
|
|
3
3
|
export * from './claude-generate-document.tool';
|
|
4
|
-
export * from './
|
|
4
|
+
export * from './delegate-tool-calls.tool';
|
|
5
|
+
export * from './update-tool-result.tool';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
InjectTool,
|
|
5
|
+
Input,
|
|
6
|
+
RunContext,
|
|
7
|
+
Tool,
|
|
8
|
+
ToolInterface,
|
|
9
|
+
ToolResult,
|
|
10
|
+
ToolSideEffects,
|
|
11
|
+
WorkflowInterface,
|
|
12
|
+
WorkflowMetadataInterface,
|
|
13
|
+
} from '@loopstack/common';
|
|
14
|
+
import { CreateDocument, StateMachineToolCallProcessorService } from '@loopstack/core';
|
|
15
|
+
|
|
16
|
+
const UpdateToolResultSchema = z.object({
|
|
17
|
+
delegateResult: z.object({
|
|
18
|
+
allCompleted: z.boolean(),
|
|
19
|
+
toolResults: z.array(z.any()),
|
|
20
|
+
message: z.any(),
|
|
21
|
+
pendingCount: z.number(),
|
|
22
|
+
}),
|
|
23
|
+
completedTool: z.any(),
|
|
24
|
+
document: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
type UpdateToolResultArgs = z.infer<typeof UpdateToolResultSchema>;
|
|
28
|
+
|
|
29
|
+
@Injectable()
|
|
30
|
+
@Tool({
|
|
31
|
+
config: {
|
|
32
|
+
description: 'Handle async tool completion callback. Updates the response document and tracks completion state.',
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
export class UpdateToolResult implements ToolInterface<UpdateToolResultArgs> {
|
|
36
|
+
private readonly logger = new Logger(UpdateToolResult.name);
|
|
37
|
+
|
|
38
|
+
@InjectTool() private createDocument: CreateDocument;
|
|
39
|
+
|
|
40
|
+
constructor(private readonly toolCallProcessor: StateMachineToolCallProcessorService) {}
|
|
41
|
+
|
|
42
|
+
@Input({
|
|
43
|
+
schema: UpdateToolResultSchema,
|
|
44
|
+
})
|
|
45
|
+
args: UpdateToolResultArgs;
|
|
46
|
+
|
|
47
|
+
async execute(
|
|
48
|
+
args: UpdateToolResultArgs,
|
|
49
|
+
ctx: RunContext,
|
|
50
|
+
parent: WorkflowInterface,
|
|
51
|
+
metadata: WorkflowMetadataInterface,
|
|
52
|
+
): Promise<ToolResult> {
|
|
53
|
+
const { delegateResult } = args;
|
|
54
|
+
const completedToolRecord = args.completedTool as Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
// 1. Extract tool identity from subscriber metadata
|
|
57
|
+
const subscriberMetadata = completedToolRecord._subscriberMetadata as
|
|
58
|
+
| { toolUseId: string; toolName: string }
|
|
59
|
+
| undefined;
|
|
60
|
+
if (!subscriberMetadata?.toolUseId || !subscriberMetadata?.toolName) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'Callback payload is missing _subscriberMetadata with toolUseId and toolName. ' +
|
|
63
|
+
'Ensure the event subscriber was registered with metadata by delegateToolCalls.',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { toolUseId, toolName } = subscriberMetadata;
|
|
68
|
+
|
|
69
|
+
// 2. Resolve tool and call complete() if it exists
|
|
70
|
+
const tool = this.toolCallProcessor.getTool(parent, toolName);
|
|
71
|
+
let toolResult: ToolResult;
|
|
72
|
+
try {
|
|
73
|
+
if (tool.complete) {
|
|
74
|
+
toolResult = await tool.complete(completedToolRecord, ctx, parent, metadata);
|
|
75
|
+
} else {
|
|
76
|
+
toolResult = { data: completedToolRecord.result ?? completedToolRecord };
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
80
|
+
this.logger.error(`Tool "${toolName}" complete() failed: ${errorMessage}`);
|
|
81
|
+
toolResult = {
|
|
82
|
+
data: errorMessage,
|
|
83
|
+
error: errorMessage,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 3. Merge into toolResults (append completed result)
|
|
88
|
+
const completedEntry = {
|
|
89
|
+
type: 'tool_result' as const,
|
|
90
|
+
tool_use_id: toolUseId,
|
|
91
|
+
content: toolResult.data ? JSON.stringify(toolResult.data, null, 2) : '',
|
|
92
|
+
is_error: !!toolResult.error,
|
|
93
|
+
};
|
|
94
|
+
const updatedResults = [...(delegateResult.toolResults as Record<string, unknown>[]), completedEntry];
|
|
95
|
+
|
|
96
|
+
// 4. Update completion state
|
|
97
|
+
const pendingCount = delegateResult.pendingCount - 1;
|
|
98
|
+
const allCompleted = pendingCount === 0;
|
|
99
|
+
|
|
100
|
+
// 5. Create updated response document (invalidates previous)
|
|
101
|
+
const allEffects: ToolSideEffects[] = [];
|
|
102
|
+
if (toolResult.effects) {
|
|
103
|
+
allEffects.push(...toolResult.effects);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (args.document) {
|
|
107
|
+
const docResult = await this.createDocument.execute(
|
|
108
|
+
{
|
|
109
|
+
id: (delegateResult.message as Record<string, unknown>).id as string,
|
|
110
|
+
document: args.document,
|
|
111
|
+
validate: 'skip' as const,
|
|
112
|
+
update: {
|
|
113
|
+
content: {
|
|
114
|
+
role: 'assistant',
|
|
115
|
+
content: (delegateResult.message as Record<string, unknown>).content,
|
|
116
|
+
toolResults: updatedResults,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
ctx,
|
|
121
|
+
parent,
|
|
122
|
+
metadata,
|
|
123
|
+
);
|
|
124
|
+
if (docResult.effects) {
|
|
125
|
+
allEffects.push(...docResult.effects);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
data: {
|
|
131
|
+
...delegateResult,
|
|
132
|
+
toolResults: updatedResults,
|
|
133
|
+
allCompleted,
|
|
134
|
+
pendingCount,
|
|
135
|
+
},
|
|
136
|
+
effects: allEffects.length > 0 ? allEffects : undefined,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { RunContext, ToolInterface, ToolResult, WorkflowInterface, WorkflowMetadataInterface } from '@loopstack/common';
|
|
3
|
-
declare const ClaudeDelegateToolCallSchema: z.ZodObject<{
|
|
4
|
-
message: z.ZodObject<{
|
|
5
|
-
id: z.ZodOptional<z.ZodString>;
|
|
6
|
-
content: z.ZodArray<z.ZodAny>;
|
|
7
|
-
}, z.core.$strip>;
|
|
8
|
-
document: z.ZodOptional<z.ZodString>;
|
|
9
|
-
skipResponseMessage: z.ZodOptional<z.ZodBoolean>;
|
|
10
|
-
}, z.core.$strip>;
|
|
11
|
-
type ClaudeDelegateToolCallArgs = z.infer<typeof ClaudeDelegateToolCallSchema>;
|
|
12
|
-
export declare class ClaudeDelegateToolCall implements ToolInterface<ClaudeDelegateToolCallArgs> {
|
|
13
|
-
private readonly logger;
|
|
14
|
-
args: ClaudeDelegateToolCallArgs;
|
|
15
|
-
execute(args: ClaudeDelegateToolCallArgs, ctx: RunContext, parent: WorkflowInterface, runtime: WorkflowMetadataInterface): Promise<ToolResult>;
|
|
16
|
-
private createResponseMessage;
|
|
17
|
-
}
|
|
18
|
-
export {};
|
|
19
|
-
//# sourceMappingURL=claude-delegate-tool-call.tool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claude-delegate-tool-call.tool.d.ts","sourceRoot":"","sources":["../../src/tools/claude-delegate-tool-call.tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,UAAU,EAEV,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,yBAAyB,EAE1B,MAAM,mBAAmB,CAAC;AAE3B,QAAA,MAAM,4BAA4B;;;;;;;iBAOhC,CAAC;AAEH,KAAK,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAE/E,qBAKa,sBAAuB,YAAW,aAAa,CAAC,0BAA0B,CAAC;IACtF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;IAKlE,IAAI,EAAE,0BAA0B,CAAC;IAE3B,OAAO,CACX,IAAI,EAAE,0BAA0B,EAChC,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,UAAU,CAAC;YAoDR,qBAAqB;CA6BpC"}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var ClaudeDelegateToolCall_1;
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.ClaudeDelegateToolCall = void 0;
|
|
14
|
-
const common_1 = require("@nestjs/common");
|
|
15
|
-
const zod_1 = require("zod");
|
|
16
|
-
const common_2 = require("@loopstack/common");
|
|
17
|
-
const ClaudeDelegateToolCallSchema = zod_1.z.object({
|
|
18
|
-
message: zod_1.z.object({
|
|
19
|
-
id: zod_1.z.string().optional(),
|
|
20
|
-
content: zod_1.z.array(zod_1.z.any()),
|
|
21
|
-
}),
|
|
22
|
-
document: zod_1.z.string().optional(),
|
|
23
|
-
skipResponseMessage: zod_1.z.boolean().optional(),
|
|
24
|
-
});
|
|
25
|
-
let ClaudeDelegateToolCall = ClaudeDelegateToolCall_1 = class ClaudeDelegateToolCall {
|
|
26
|
-
logger = new common_1.Logger(ClaudeDelegateToolCall_1.name);
|
|
27
|
-
args;
|
|
28
|
-
async execute(args, ctx, parent, runtime) {
|
|
29
|
-
const contentBlocks = args.message.content;
|
|
30
|
-
const toolResults = [];
|
|
31
|
-
for (const block of contentBlocks) {
|
|
32
|
-
if (block.type !== 'tool_use') {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const tool = (0, common_2.getBlockTool)(parent, block.name);
|
|
36
|
-
if (!tool) {
|
|
37
|
-
throw new Error(`Tool ${block.name} not found.`);
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
40
|
-
const result = await tool.execute(block.input, ctx, parent, runtime);
|
|
41
|
-
toolResults.push({
|
|
42
|
-
type: 'tool_result',
|
|
43
|
-
tool_use_id: block.id,
|
|
44
|
-
content: JSON.stringify(result.data, null, 2),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
49
|
-
this.logger.error(`Tool "${block.name}" failed: ${errorMessage}`);
|
|
50
|
-
toolResults.push({
|
|
51
|
-
type: 'tool_result',
|
|
52
|
-
tool_use_id: block.id,
|
|
53
|
-
content: errorMessage,
|
|
54
|
-
is_error: true,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const resultData = { toolResults };
|
|
59
|
-
if (args.document && !args.skipResponseMessage) {
|
|
60
|
-
const docResult = await this.createResponseMessage(args, toolResults, ctx, parent, runtime);
|
|
61
|
-
return {
|
|
62
|
-
data: resultData,
|
|
63
|
-
effects: docResult.effects,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
data: resultData,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
async createResponseMessage(args, toolResults, ctx, parent, runtime) {
|
|
71
|
-
const createDocumentTool = (0, common_2.getBlockTool)(parent, 'createDocument');
|
|
72
|
-
if (!createDocumentTool) {
|
|
73
|
-
throw new Error('createDocument tool not found in parent context.');
|
|
74
|
-
}
|
|
75
|
-
return createDocumentTool.execute({
|
|
76
|
-
id: args.message.id,
|
|
77
|
-
document: args.document,
|
|
78
|
-
update: {
|
|
79
|
-
content: {
|
|
80
|
-
role: 'assistant',
|
|
81
|
-
content: args.message.content,
|
|
82
|
-
toolResults,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
}, ctx, parent, runtime);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
exports.ClaudeDelegateToolCall = ClaudeDelegateToolCall;
|
|
89
|
-
__decorate([
|
|
90
|
-
(0, common_2.Input)({
|
|
91
|
-
schema: ClaudeDelegateToolCallSchema,
|
|
92
|
-
}),
|
|
93
|
-
__metadata("design:type", Object)
|
|
94
|
-
], ClaudeDelegateToolCall.prototype, "args", void 0);
|
|
95
|
-
exports.ClaudeDelegateToolCall = ClaudeDelegateToolCall = ClaudeDelegateToolCall_1 = __decorate([
|
|
96
|
-
(0, common_2.Tool)({
|
|
97
|
-
config: {
|
|
98
|
-
description: 'Delegate tool calls from a Claude response.',
|
|
99
|
-
},
|
|
100
|
-
})
|
|
101
|
-
], ClaudeDelegateToolCall);
|
|
102
|
-
//# sourceMappingURL=claude-delegate-tool-call.tool.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claude-delegate-tool-call.tool.js","sourceRoot":"","sources":["../../src/tools/claude-delegate-tool-call.tool.ts"],"names":[],"mappings":"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"}
|