@199-bio/engram 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +5 -0
- package/boba-prompt.md +107 -0
- package/dist/consolidation/consolidator.d.ts.map +1 -1
- package/dist/consolidation/plan.d.ts.map +1 -0
- package/dist/index.js +170 -9
- package/dist/retrieval/hybrid.d.ts.map +1 -1
- package/dist/storage/database.d.ts.map +1 -1
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/web/chat-handler.d.ts.map +1 -1
- package/nixpacks.toml +11 -0
- package/package.json +2 -1
- package/railway.json +13 -0
- package/src/consolidation/consolidator.ts +381 -29
- package/src/consolidation/plan.ts +444 -0
- package/src/index.ts +181 -10
- package/src/retrieval/hybrid.ts +69 -5
- package/src/storage/database.ts +358 -38
- package/src/transport/http.ts +111 -0
- package/src/transport/index.ts +24 -0
- package/src/web/chat-handler.ts +116 -70
- package/src/web/static/app.js +612 -360
- package/src/web/static/index.html +377 -130
- package/src/web/static/style.css +1249 -672
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport layer for Engram MCP Server
|
|
3
|
+
* Supports both stdio (local) and HTTP (remote/Railway) transports
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type TransportMode = "stdio" | "http";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detect transport mode from environment variables
|
|
10
|
+
* Default: stdio (preserves existing behavior)
|
|
11
|
+
*/
|
|
12
|
+
export function getTransportMode(): TransportMode {
|
|
13
|
+
const mode = process.env.ENGRAM_TRANSPORT?.toLowerCase();
|
|
14
|
+
if (mode === "http" || mode === "sse") return "http";
|
|
15
|
+
return "stdio";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get HTTP port from environment
|
|
20
|
+
* Railway provides PORT, we also support ENGRAM_MCP_PORT
|
|
21
|
+
*/
|
|
22
|
+
export function getHttpPort(): number {
|
|
23
|
+
return parseInt(process.env.PORT || process.env.ENGRAM_MCP_PORT || "3000", 10);
|
|
24
|
+
}
|
package/src/web/chat-handler.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chat Handler for Engram Web Interface
|
|
3
|
-
* Uses Claude
|
|
3
|
+
* Uses Claude Opus 4.5 with tools for entity/memory management
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -9,22 +9,22 @@ import { KnowledgeGraph } from "../graph/knowledge-graph.js";
|
|
|
9
9
|
import { HybridSearch } from "../retrieval/hybrid.js";
|
|
10
10
|
import { getAnthropicApiKey } from "../settings.js";
|
|
11
11
|
|
|
12
|
-
// Tool definitions for Claude
|
|
12
|
+
// Tool definitions for Claude - optimized for LLM consumption
|
|
13
13
|
const TOOLS: Anthropic.Tool[] = [
|
|
14
14
|
{
|
|
15
15
|
name: "list_entities",
|
|
16
|
-
description: "
|
|
16
|
+
description: "Returns array of {name, type, id}. Filters: type (person|organization|place), limit. Default limit=50.",
|
|
17
17
|
input_schema: {
|
|
18
18
|
type: "object" as const,
|
|
19
19
|
properties: {
|
|
20
20
|
type: {
|
|
21
21
|
type: "string",
|
|
22
22
|
enum: ["person", "organization", "place"],
|
|
23
|
-
description: "Filter
|
|
23
|
+
description: "Filter: person, organization, or place",
|
|
24
24
|
},
|
|
25
25
|
limit: {
|
|
26
|
-
type: "
|
|
27
|
-
description: "
|
|
26
|
+
type: "integer",
|
|
27
|
+
description: "Max results (default: 50)",
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
required: [],
|
|
@@ -32,13 +32,13 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
name: "get_entity",
|
|
35
|
-
description: "
|
|
35
|
+
description: "Returns {name, type, observations[], relationships_from[], relationships_to[]} or {error}.",
|
|
36
36
|
input_schema: {
|
|
37
37
|
type: "object" as const,
|
|
38
38
|
properties: {
|
|
39
39
|
name: {
|
|
40
40
|
type: "string",
|
|
41
|
-
description: "
|
|
41
|
+
description: "Exact entity name (case-sensitive)",
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
44
|
required: ["name"],
|
|
@@ -46,13 +46,13 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
name: "delete_entity",
|
|
49
|
-
description: "
|
|
49
|
+
description: "Permanently removes entity + all observations + all relationships. Returns {success, deleted} or {error}.",
|
|
50
50
|
input_schema: {
|
|
51
51
|
type: "object" as const,
|
|
52
52
|
properties: {
|
|
53
53
|
name: {
|
|
54
54
|
type: "string",
|
|
55
|
-
description: "
|
|
55
|
+
description: "Exact entity name to delete",
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
58
|
required: ["name"],
|
|
@@ -60,17 +60,17 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
name: "merge_entities",
|
|
63
|
-
description: "
|
|
63
|
+
description: "Moves all data from 'merge' into 'keep', then deletes 'merge'. Use for deduplication. Returns {success, kept, merged, observations_moved, relations_moved} or {error}.",
|
|
64
64
|
input_schema: {
|
|
65
65
|
type: "object" as const,
|
|
66
66
|
properties: {
|
|
67
67
|
keep: {
|
|
68
68
|
type: "string",
|
|
69
|
-
description: "
|
|
69
|
+
description: "Entity name to preserve (target)",
|
|
70
70
|
},
|
|
71
71
|
merge: {
|
|
72
72
|
type: "string",
|
|
73
|
-
description: "
|
|
73
|
+
description: "Entity name to merge then delete (source)",
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
76
|
required: ["keep", "merge"],
|
|
@@ -78,17 +78,17 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
name: "rename_entity",
|
|
81
|
-
description: "
|
|
81
|
+
description: "Changes entity name. Returns {success, old_name, new_name} or {error}.",
|
|
82
82
|
input_schema: {
|
|
83
83
|
type: "object" as const,
|
|
84
84
|
properties: {
|
|
85
85
|
old_name: {
|
|
86
86
|
type: "string",
|
|
87
|
-
description: "Current
|
|
87
|
+
description: "Current exact name",
|
|
88
88
|
},
|
|
89
89
|
new_name: {
|
|
90
90
|
type: "string",
|
|
91
|
-
description: "New
|
|
91
|
+
description: "New name",
|
|
92
92
|
},
|
|
93
93
|
},
|
|
94
94
|
required: ["old_name", "new_name"],
|
|
@@ -96,7 +96,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
name: "delete_relationship",
|
|
99
|
-
description: "
|
|
99
|
+
description: "Removes specific relationship. All 3 params must match exactly. Returns {success, deleted} or {error}.",
|
|
100
100
|
input_schema: {
|
|
101
101
|
type: "object" as const,
|
|
102
102
|
properties: {
|
|
@@ -110,7 +110,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
110
110
|
},
|
|
111
111
|
type: {
|
|
112
112
|
type: "string",
|
|
113
|
-
description: "Relationship type (e.g.,
|
|
113
|
+
description: "Relationship type (e.g., works_at, knows, lives_in)",
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
required: ["from", "to", "type"],
|
|
@@ -118,17 +118,17 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
name: "search_memories",
|
|
121
|
-
description: "
|
|
121
|
+
description: "Hybrid BM25+semantic search. Returns {results[{id, content, timestamp, score}], count}.",
|
|
122
122
|
input_schema: {
|
|
123
123
|
type: "object" as const,
|
|
124
124
|
properties: {
|
|
125
125
|
query: {
|
|
126
126
|
type: "string",
|
|
127
|
-
description: "Search query",
|
|
127
|
+
description: "Search query (keywords or natural language)",
|
|
128
128
|
},
|
|
129
129
|
limit: {
|
|
130
|
-
type: "
|
|
131
|
-
description: "
|
|
130
|
+
type: "integer",
|
|
131
|
+
description: "Max results (default: 10)",
|
|
132
132
|
},
|
|
133
133
|
},
|
|
134
134
|
required: ["query"],
|
|
@@ -136,13 +136,13 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
name: "delete_memory",
|
|
139
|
-
description: "
|
|
139
|
+
description: "Soft-delete (recoverable). Returns {success, disabled_id} or {error}.",
|
|
140
140
|
input_schema: {
|
|
141
141
|
type: "object" as const,
|
|
142
142
|
properties: {
|
|
143
143
|
id: {
|
|
144
144
|
type: "string",
|
|
145
|
-
description: "
|
|
145
|
+
description: "Memory UUID",
|
|
146
146
|
},
|
|
147
147
|
},
|
|
148
148
|
required: ["id"],
|
|
@@ -150,13 +150,13 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
name: "edit_memory",
|
|
153
|
-
description: "
|
|
153
|
+
description: "Updates content and/or importance. Returns {success, memory_id, updated_fields[]} or {error}.",
|
|
154
154
|
input_schema: {
|
|
155
155
|
type: "object" as const,
|
|
156
156
|
properties: {
|
|
157
157
|
id: {
|
|
158
158
|
type: "string",
|
|
159
|
-
description: "
|
|
159
|
+
description: "Memory UUID",
|
|
160
160
|
},
|
|
161
161
|
content: {
|
|
162
162
|
type: "string",
|
|
@@ -164,7 +164,9 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
164
164
|
},
|
|
165
165
|
importance: {
|
|
166
166
|
type: "number",
|
|
167
|
-
|
|
167
|
+
minimum: 0,
|
|
168
|
+
maximum: 1,
|
|
169
|
+
description: "0-1: 0.9=identity, 0.8=major, 0.5=normal, 0.3=minor",
|
|
168
170
|
},
|
|
169
171
|
},
|
|
170
172
|
required: ["id"],
|
|
@@ -172,17 +174,19 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
172
174
|
},
|
|
173
175
|
{
|
|
174
176
|
name: "create_memory",
|
|
175
|
-
description: "
|
|
177
|
+
description: "Stores new memory. Returns {success, memory_id, content}.",
|
|
176
178
|
input_schema: {
|
|
177
179
|
type: "object" as const,
|
|
178
180
|
properties: {
|
|
179
181
|
content: {
|
|
180
182
|
type: "string",
|
|
181
|
-
description: "
|
|
183
|
+
description: "Information to store",
|
|
182
184
|
},
|
|
183
185
|
importance: {
|
|
184
186
|
type: "number",
|
|
185
|
-
|
|
187
|
+
minimum: 0,
|
|
188
|
+
maximum: 1,
|
|
189
|
+
description: "0-1: 0.9=identity, 0.8=major, 0.5=normal (default), 0.3=minor",
|
|
186
190
|
},
|
|
187
191
|
},
|
|
188
192
|
required: ["content"],
|
|
@@ -190,13 +194,13 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
190
194
|
},
|
|
191
195
|
{
|
|
192
196
|
name: "create_entity",
|
|
193
|
-
description: "
|
|
197
|
+
description: "Creates new entity. Returns {success, entity_id, name, type} or {error} if exists.",
|
|
194
198
|
input_schema: {
|
|
195
199
|
type: "object" as const,
|
|
196
200
|
properties: {
|
|
197
201
|
name: {
|
|
198
202
|
type: "string",
|
|
199
|
-
description: "
|
|
203
|
+
description: "Entity name",
|
|
200
204
|
},
|
|
201
205
|
type: {
|
|
202
206
|
type: "string",
|
|
@@ -209,7 +213,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
209
213
|
},
|
|
210
214
|
{
|
|
211
215
|
name: "create_relationship",
|
|
212
|
-
description: "
|
|
216
|
+
description: "Links two entities. Auto-creates entities as 'person' if missing. Returns {success, relationship}.",
|
|
213
217
|
input_schema: {
|
|
214
218
|
type: "object" as const,
|
|
215
219
|
properties: {
|
|
@@ -223,7 +227,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
223
227
|
},
|
|
224
228
|
type: {
|
|
225
229
|
type: "string",
|
|
226
|
-
description: "Relationship type (e.g.,
|
|
230
|
+
description: "Relationship type (e.g., works_at, lives_in, knows, sibling_of, parent_of)",
|
|
227
231
|
},
|
|
228
232
|
},
|
|
229
233
|
required: ["from", "to", "type"],
|
|
@@ -231,7 +235,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
231
235
|
},
|
|
232
236
|
{
|
|
233
237
|
name: "find_duplicates",
|
|
234
|
-
description: "
|
|
238
|
+
description: "Detects similar entity names. Returns {groups[{keep, duplicates[]}], total_duplicates}.",
|
|
235
239
|
input_schema: {
|
|
236
240
|
type: "object" as const,
|
|
237
241
|
properties: {},
|
|
@@ -240,7 +244,7 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
240
244
|
},
|
|
241
245
|
{
|
|
242
246
|
name: "auto_tidy",
|
|
243
|
-
description: "
|
|
247
|
+
description: "Auto-merges all detected duplicates. Returns {entities_merged, observations_moved, relations_moved}.",
|
|
244
248
|
input_schema: {
|
|
245
249
|
type: "object" as const,
|
|
246
250
|
properties: {},
|
|
@@ -249,16 +253,34 @@ const TOOLS: Anthropic.Tool[] = [
|
|
|
249
253
|
},
|
|
250
254
|
];
|
|
251
255
|
|
|
252
|
-
const SYSTEM_PROMPT = `You are a helpful assistant for managing Engram, a personal memory system. You
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
257
|
-
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
256
|
+
const SYSTEM_PROMPT = `You are a helpful assistant for managing Engram, a personal memory system. You have extended thinking capabilities - use them to reason carefully about complex requests.
|
|
257
|
+
|
|
258
|
+
## Your Capabilities
|
|
259
|
+
- Search and retrieve memories using semantic + keyword hybrid search
|
|
260
|
+
- Manage entities (people, organizations, places) - create, rename, merge, delete
|
|
261
|
+
- Manage relationships between entities
|
|
262
|
+
- Create, edit, and delete memories
|
|
263
|
+
- Find and auto-merge duplicate entities
|
|
264
|
+
|
|
265
|
+
## Critical Behaviors
|
|
266
|
+
1. **Always search first**: When asked about anything that might be in memory, use search_memories FIRST before answering. Don't assume you know the answer.
|
|
267
|
+
2. **Multi-step reasoning**: For complex requests, break them into steps. Search, analyze results, then act.
|
|
268
|
+
3. **Confirm destructive actions**: Unless the user is explicit, ask before deleting or merging data.
|
|
269
|
+
4. **Be precise**: Use exact entity names when making changes. Check spelling.
|
|
270
|
+
5. **Context awareness**: Remember what the user discussed earlier in this conversation.
|
|
271
|
+
|
|
272
|
+
## Response Style
|
|
273
|
+
- Be concise but thorough
|
|
274
|
+
- Format lists and results clearly using markdown
|
|
275
|
+
- When you find relevant memories, quote the key parts
|
|
276
|
+
- If you're uncertain, say so and explain your reasoning
|
|
277
|
+
|
|
278
|
+
## Tool Usage
|
|
279
|
+
- search_memories: Use liberally - hybrid search is fast and effective
|
|
280
|
+
- list_entities: Good for getting an overview before specific operations
|
|
281
|
+
- get_entity: Get full details including observations and relationships
|
|
282
|
+
- find_duplicates: Run this when asked about data quality or cleanup
|
|
283
|
+
- auto_tidy: Only use when user explicitly wants automatic cleanup`;
|
|
262
284
|
|
|
263
285
|
interface ChatMessage {
|
|
264
286
|
role: "user" | "assistant";
|
|
@@ -267,7 +289,7 @@ interface ChatMessage {
|
|
|
267
289
|
|
|
268
290
|
// Stream event types for SSE
|
|
269
291
|
export interface StreamEvent {
|
|
270
|
-
type: "text" | "tool_start" | "tool_end" | "error" | "done";
|
|
292
|
+
type: "text" | "thinking" | "tool_start" | "tool_end" | "error" | "done";
|
|
271
293
|
content?: string;
|
|
272
294
|
tool?: string;
|
|
273
295
|
result?: unknown;
|
|
@@ -301,13 +323,23 @@ export class ChatHandler {
|
|
|
301
323
|
refreshClient(): void {
|
|
302
324
|
const apiKey = getAnthropicApiKey();
|
|
303
325
|
if (apiKey) {
|
|
304
|
-
this.client
|
|
326
|
+
if (!this.client) {
|
|
327
|
+
console.error("[Engram] ChatHandler: API key configured");
|
|
328
|
+
}
|
|
329
|
+
this.client = new Anthropic({
|
|
330
|
+
apiKey,
|
|
331
|
+
defaultHeaders: {
|
|
332
|
+
"anthropic-beta": "interleaved-thinking-2025-05-14",
|
|
333
|
+
},
|
|
334
|
+
});
|
|
305
335
|
} else {
|
|
306
336
|
this.client = null;
|
|
307
337
|
}
|
|
308
338
|
}
|
|
309
339
|
|
|
310
340
|
isConfigured(): boolean {
|
|
341
|
+
// Re-check API key in case it was added after startup
|
|
342
|
+
this.refreshClient();
|
|
311
343
|
return this.client !== null;
|
|
312
344
|
}
|
|
313
345
|
|
|
@@ -341,6 +373,7 @@ export class ChatHandler {
|
|
|
341
373
|
|
|
342
374
|
// Queue-aware chat method
|
|
343
375
|
async chat(userMessage: string): Promise<string> {
|
|
376
|
+
this.refreshClient();
|
|
344
377
|
if (!this.client) {
|
|
345
378
|
return "Chat is not configured. Set ANTHROPIC_API_KEY environment variable.";
|
|
346
379
|
}
|
|
@@ -357,6 +390,7 @@ export class ChatHandler {
|
|
|
357
390
|
|
|
358
391
|
// Streaming chat with callbacks for real-time updates
|
|
359
392
|
async *chatStream(userMessage: string): AsyncGenerator<StreamEvent> {
|
|
393
|
+
this.refreshClient();
|
|
360
394
|
if (!this.client) {
|
|
361
395
|
yield { type: "error", content: "Chat is not configured. Set ANTHROPIC_API_KEY environment variable." };
|
|
362
396
|
return;
|
|
@@ -377,18 +411,22 @@ export class ChatHandler {
|
|
|
377
411
|
}
|
|
378
412
|
|
|
379
413
|
let continueLoop = true;
|
|
380
|
-
let fullResponse = "";
|
|
381
414
|
|
|
382
415
|
while (continueLoop) {
|
|
383
416
|
const stream = this.client.messages.stream({
|
|
384
|
-
model: "claude-
|
|
385
|
-
max_tokens:
|
|
417
|
+
model: "claude-opus-4-5-20251101",
|
|
418
|
+
max_tokens: 16000,
|
|
386
419
|
system: SYSTEM_PROMPT,
|
|
387
420
|
tools: TOOLS,
|
|
388
421
|
messages: this.conversationHistory,
|
|
422
|
+
thinking: {
|
|
423
|
+
type: "enabled",
|
|
424
|
+
budget_tokens: 8000,
|
|
425
|
+
},
|
|
389
426
|
});
|
|
390
427
|
|
|
391
428
|
let currentToolUse: { id: string; name: string; input: string } | null = null;
|
|
429
|
+
let isThinking = false;
|
|
392
430
|
|
|
393
431
|
for await (const event of stream) {
|
|
394
432
|
if (event.type === "content_block_start") {
|
|
@@ -399,28 +437,23 @@ export class ChatHandler {
|
|
|
399
437
|
input: "",
|
|
400
438
|
};
|
|
401
439
|
yield { type: "tool_start", tool: event.content_block.name };
|
|
440
|
+
} else if (event.content_block.type === "thinking") {
|
|
441
|
+
isThinking = true;
|
|
442
|
+
yield { type: "thinking", content: "" };
|
|
402
443
|
}
|
|
403
444
|
} else if (event.type === "content_block_delta") {
|
|
404
445
|
if (event.delta.type === "text_delta") {
|
|
405
|
-
fullResponse += event.delta.text;
|
|
406
446
|
yield { type: "text", content: event.delta.text };
|
|
447
|
+
} else if (event.delta.type === "thinking_delta") {
|
|
448
|
+
// Stream thinking content for transparency
|
|
449
|
+
yield { type: "thinking", content: event.delta.thinking };
|
|
407
450
|
} else if (event.delta.type === "input_json_delta" && currentToolUse) {
|
|
408
451
|
currentToolUse.input += event.delta.partial_json;
|
|
409
452
|
}
|
|
410
453
|
} else if (event.type === "content_block_stop") {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
try {
|
|
415
|
-
toolInput = JSON.parse(currentToolUse.input || "{}");
|
|
416
|
-
} catch {
|
|
417
|
-
toolInput = {};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const result = await this.executeTool(currentToolUse.name, toolInput);
|
|
421
|
-
yield { type: "tool_end", tool: currentToolUse.name, result };
|
|
422
|
-
currentToolUse = null;
|
|
423
|
-
}
|
|
454
|
+
// Don't execute tools here - wait for finalMessage to avoid double execution
|
|
455
|
+
currentToolUse = null;
|
|
456
|
+
isThinking = false;
|
|
424
457
|
}
|
|
425
458
|
}
|
|
426
459
|
|
|
@@ -428,7 +461,7 @@ export class ChatHandler {
|
|
|
428
461
|
const finalMessage = await stream.finalMessage();
|
|
429
462
|
|
|
430
463
|
if (finalMessage.stop_reason === "tool_use") {
|
|
431
|
-
// Process tool results
|
|
464
|
+
// Process tool results (execute only once, here)
|
|
432
465
|
const toolUseBlocks = finalMessage.content.filter(
|
|
433
466
|
(block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
|
|
434
467
|
);
|
|
@@ -436,10 +469,13 @@ export class ChatHandler {
|
|
|
436
469
|
const toolResults: Anthropic.ToolResultBlockParam[] = [];
|
|
437
470
|
for (const toolUse of toolUseBlocks) {
|
|
438
471
|
const result = await this.executeTool(toolUse.name, toolUse.input as Record<string, unknown>);
|
|
472
|
+
const isError = typeof result === "object" && result !== null && "error" in result;
|
|
473
|
+
yield { type: "tool_end", tool: toolUse.name, result };
|
|
439
474
|
toolResults.push({
|
|
440
475
|
type: "tool_result",
|
|
441
476
|
tool_use_id: toolUse.id,
|
|
442
477
|
content: JSON.stringify(result),
|
|
478
|
+
is_error: isError,
|
|
443
479
|
});
|
|
444
480
|
}
|
|
445
481
|
|
|
@@ -492,11 +528,15 @@ export class ChatHandler {
|
|
|
492
528
|
}
|
|
493
529
|
|
|
494
530
|
let response = await this.client.messages.create({
|
|
495
|
-
model: "claude-
|
|
496
|
-
max_tokens:
|
|
531
|
+
model: "claude-opus-4-5-20251101",
|
|
532
|
+
max_tokens: 16000,
|
|
497
533
|
system: SYSTEM_PROMPT,
|
|
498
534
|
tools: TOOLS,
|
|
499
535
|
messages: this.conversationHistory,
|
|
536
|
+
thinking: {
|
|
537
|
+
type: "enabled",
|
|
538
|
+
budget_tokens: 8000,
|
|
539
|
+
},
|
|
500
540
|
});
|
|
501
541
|
|
|
502
542
|
// Handle tool use loop
|
|
@@ -508,10 +548,12 @@ export class ChatHandler {
|
|
|
508
548
|
const toolResults: Anthropic.ToolResultBlockParam[] = [];
|
|
509
549
|
for (const toolUse of toolUseBlocks) {
|
|
510
550
|
const result = await this.executeTool(toolUse.name, toolUse.input as Record<string, unknown>);
|
|
551
|
+
const isError = typeof result === "object" && result !== null && "error" in result;
|
|
511
552
|
toolResults.push({
|
|
512
553
|
type: "tool_result",
|
|
513
554
|
tool_use_id: toolUse.id,
|
|
514
555
|
content: JSON.stringify(result),
|
|
556
|
+
is_error: isError,
|
|
515
557
|
});
|
|
516
558
|
}
|
|
517
559
|
|
|
@@ -527,11 +569,15 @@ export class ChatHandler {
|
|
|
527
569
|
|
|
528
570
|
// Continue the conversation
|
|
529
571
|
response = await this.client.messages.create({
|
|
530
|
-
model: "claude-
|
|
531
|
-
max_tokens:
|
|
572
|
+
model: "claude-opus-4-5-20251101",
|
|
573
|
+
max_tokens: 16000,
|
|
532
574
|
system: SYSTEM_PROMPT,
|
|
533
575
|
tools: TOOLS,
|
|
534
576
|
messages: this.conversationHistory,
|
|
577
|
+
thinking: {
|
|
578
|
+
type: "enabled",
|
|
579
|
+
budget_tokens: 8000,
|
|
580
|
+
},
|
|
535
581
|
});
|
|
536
582
|
}
|
|
537
583
|
|