thinkwell 0.5.5 → 0.5.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.
Files changed (46) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +232 -278
  3. package/dist/agent.js.map +1 -1
  4. package/dist/build.js +44 -98
  5. package/dist/cli/build.js +92 -227
  6. package/dist/cli/bundle.js +570 -1136
  7. package/dist/cli/check.js +125 -214
  8. package/dist/cli/commands.js +63 -177
  9. package/dist/cli/compiler-host.js +81 -190
  10. package/dist/cli/dependency-check.js +125 -269
  11. package/dist/cli/dependency-errors.js +12 -84
  12. package/dist/cli/fmt.js +1 -13
  13. package/dist/cli/init-command.js +21 -68
  14. package/dist/cli/init.js +90 -220
  15. package/dist/cli/loader.js +95 -361
  16. package/dist/cli/new-command.js +25 -73
  17. package/dist/cli/package-manager.js +50 -117
  18. package/dist/cli/schema.d.ts.map +1 -1
  19. package/dist/cli/schema.js +91 -245
  20. package/dist/cli/schema.js.map +1 -1
  21. package/dist/cli/workspace.js +92 -226
  22. package/dist/connectors/index.js +1 -7
  23. package/dist/generated/features.d.ts +6 -0
  24. package/dist/generated/features.d.ts.map +1 -0
  25. package/dist/generated/features.js +5 -0
  26. package/dist/generated/features.js.map +1 -0
  27. package/dist/index.js +0 -5
  28. package/dist/schema.js +3 -36
  29. package/dist/session.js +50 -82
  30. package/dist/think-builder.d.ts.map +1 -1
  31. package/dist/think-builder.js +287 -368
  32. package/dist/think-builder.js.map +1 -1
  33. package/dist/thought-event.d.ts +1 -0
  34. package/dist/thought-event.d.ts.map +1 -1
  35. package/dist/thought-event.js +0 -1
  36. package/dist/thought-stream.js +60 -96
  37. package/dist-pkg/acp.cjs +13386 -1876
  38. package/dist-pkg/cli-build.cjs +264 -446
  39. package/dist-pkg/cli-bundle.cjs +433 -818
  40. package/dist-pkg/cli-check.cjs +302 -499
  41. package/dist-pkg/cli-dependency-check.cjs +39 -82
  42. package/dist-pkg/cli-dependency-errors.cjs +9 -41
  43. package/dist-pkg/cli-loader.cjs +91 -173
  44. package/dist-pkg/protocol.cjs +2 -8
  45. package/dist-pkg/thinkwell.cjs +927 -1846
  46. package/package.json +9 -7
@@ -1,386 +1,305 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
- import { mcpServer, createSkillServer, parseSkillMd, validateSkillName, validateSkillDescription, } from "@thinkwell/acp";
3
+ import { features } from "./generated/features.js";
4
+ import { mcpServer, createSkillServer, parseSkillMd, validateSkillName, validateSkillDescription } from "@thinkwell/acp";
4
5
  import { ThoughtStream } from "./thought-stream.js";
5
- /**
6
- * Internal session handler for ThinkBuilder
7
- */
8
6
  class ThinkSession {
9
- sessionId;
10
- _conn;
11
- _pendingUpdates = [];
12
- _updateResolvers = [];
13
- _closed = false;
14
- constructor(sessionId, conn) {
15
- this.sessionId = sessionId;
16
- this._conn = conn;
17
- }
18
- async sendPrompt(content) {
19
- const response = await this._conn.connection.prompt({
20
- sessionId: this.sessionId,
21
- prompt: [{ type: "text", text: content }],
22
- });
23
- if (response.stopReason) {
24
- this._pushInternal({ type: "stop", reason: response.stopReason });
25
- }
26
- }
27
- pushUpdate(update) {
28
- this._pushInternal(update);
29
- }
30
- _pushInternal(update) {
31
- if (this._updateResolvers.length > 0) {
32
- const resolver = this._updateResolvers.shift();
33
- resolver(update);
34
- }
35
- else {
36
- this._pendingUpdates.push(update);
37
- }
38
- }
39
- async readUpdate() {
40
- if (this._pendingUpdates.length > 0) {
41
- return this._pendingUpdates.shift();
42
- }
43
- if (this._closed) {
44
- return { type: "stop", reason: "session_closed" };
45
- }
46
- return new Promise((resolve) => {
47
- this._updateResolvers.push(resolve);
48
- });
49
- }
50
- close() {
51
- this._closed = true;
52
- for (const resolver of this._updateResolvers) {
53
- resolver({ type: "stop", reason: "session_closed" });
54
- }
55
- this._updateResolvers = [];
56
- }
7
+ sessionId;
8
+ _conn;
9
+ _pendingUpdates = [];
10
+ _updateResolvers = [];
11
+ _closed = !1;
12
+ constructor(sessionId, conn) {
13
+ this.sessionId = sessionId, this._conn = conn;
14
+ }
15
+ async sendPrompt(content) {
16
+ const response = await this._conn.connection.prompt({
17
+ sessionId: this.sessionId,
18
+ prompt: [{ type: "text", text: content }]
19
+ });
20
+ response.stopReason && this._pushInternal({ type: "stop", reason: response.stopReason });
21
+ }
22
+ pushUpdate(update) {
23
+ this._pushInternal(update);
24
+ }
25
+ _pushInternal(update) {
26
+ this._updateResolvers.length > 0 ? this._updateResolvers.shift()(update) : this._pendingUpdates.push(update);
27
+ }
28
+ async readUpdate() {
29
+ return this._pendingUpdates.length > 0 ? this._pendingUpdates.shift() : this._closed ? { type: "stop", reason: "session_closed" } : new Promise((resolve) => {
30
+ this._updateResolvers.push(resolve);
31
+ });
32
+ }
33
+ close() {
34
+ this._closed = !0;
35
+ for (const resolver of this._updateResolvers)
36
+ resolver({ type: "stop", reason: "session_closed" });
37
+ this._updateResolvers = [];
38
+ }
57
39
  }
58
40
  class PlanImpl {
59
- _conn;
60
- _promptParts;
61
- _tools;
62
- _skills;
63
- _schemaProvider;
64
- _cwd;
65
- _existingSessionId;
66
- constructor(state) {
67
- this._conn = state.conn;
68
- this._promptParts = state.promptParts;
69
- this._tools = state.tools;
70
- this._skills = state.skills;
71
- this._schemaProvider = state.schemaProvider;
72
- this._cwd = state.cwd;
73
- this._existingSessionId = state.existingSessionId;
74
- }
75
- _clone(overrides) {
76
- return new PlanImpl({
77
- conn: this._conn,
78
- promptParts: this._promptParts,
79
- tools: this._tools,
80
- skills: this._skills,
81
- schemaProvider: this._schemaProvider,
82
- cwd: this._cwd,
83
- existingSessionId: this._existingSessionId,
84
- ...overrides,
85
- });
86
- }
87
- text(content) {
88
- return this._clone({ promptParts: [...this._promptParts, content] });
89
- }
90
- textln(content) {
91
- return this._clone({ promptParts: [...this._promptParts, content + "\n"] });
92
- }
93
- quote(content, tag = "quote") {
94
- const part = !content.includes("\n")
95
- ? `<${tag}>${content}</${tag}>\n`
96
- : `<${tag}>\n${content}\n</${tag}>\n`;
97
- return this._clone({ promptParts: [...this._promptParts, part] });
98
- }
99
- code(content, language = "") {
100
- return this._clone({
101
- promptParts: [...this._promptParts, `\`\`\`${language}\n${content}\n\`\`\`\n`],
102
- });
103
- }
104
- tool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
105
- let inputSchema;
106
- let outputSchema;
107
- let actualHandler;
108
- if (typeof inputSchemaOrHandler === "function") {
109
- inputSchema = { toJsonSchema: () => ({ type: "object" }) };
110
- outputSchema = { toJsonSchema: () => ({ type: "object" }) };
111
- actualHandler = inputSchemaOrHandler;
112
- }
113
- else if (typeof outputSchemaOrHandler === "function") {
114
- inputSchema = inputSchemaOrHandler;
115
- outputSchema = { toJsonSchema: () => ({ type: "object" }) };
116
- actualHandler = outputSchemaOrHandler;
117
- }
118
- else {
119
- inputSchema = inputSchemaOrHandler;
120
- outputSchema = outputSchemaOrHandler;
121
- actualHandler = handler;
122
- }
123
- const newTools = new Map(this._tools);
124
- newTools.set(name, {
125
- name,
126
- description,
127
- handler: actualHandler,
128
- inputSchema,
129
- outputSchema,
130
- includeInPrompt: true,
131
- });
132
- return this._clone({ tools: newTools });
133
- }
134
- defineTool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
135
- let inputSchema;
136
- let outputSchema;
137
- let actualHandler;
138
- if (typeof inputSchemaOrHandler === "function") {
139
- inputSchema = { toJsonSchema: () => ({ type: "object" }) };
140
- outputSchema = { toJsonSchema: () => ({ type: "object" }) };
141
- actualHandler = inputSchemaOrHandler;
142
- }
143
- else if (typeof outputSchemaOrHandler === "function") {
144
- inputSchema = inputSchemaOrHandler;
145
- outputSchema = { toJsonSchema: () => ({ type: "object" }) };
146
- actualHandler = outputSchemaOrHandler;
147
- }
148
- else {
149
- inputSchema = inputSchemaOrHandler;
150
- outputSchema = outputSchemaOrHandler;
151
- actualHandler = handler;
152
- }
153
- const newTools = new Map(this._tools);
154
- newTools.set(name, {
155
- name,
156
- description,
157
- handler: actualHandler,
158
- inputSchema,
159
- outputSchema,
160
- includeInPrompt: false,
161
- });
162
- return this._clone({ tools: newTools });
163
- }
164
- skill(pathOrDef) {
165
- if (typeof pathOrDef === "string") {
166
- return this._clone({ skills: [...this._skills, { type: "stored", path: pathOrDef }] });
167
- }
168
- else {
169
- validateSkillName(pathOrDef.name);
170
- validateSkillDescription(pathOrDef.description);
171
- return this._clone({
172
- skills: [...this._skills, {
173
- type: "virtual",
174
- skill: {
175
- name: pathOrDef.name,
176
- description: pathOrDef.description,
177
- body: pathOrDef.body,
178
- tools: pathOrDef.tools,
179
- },
180
- }],
181
- });
182
- }
183
- }
184
- cwd(path) {
185
- return this._clone({ cwd: path });
186
- }
187
- async _resolveSkills() {
188
- const resolved = [];
189
- for (const deferred of this._skills) {
190
- if (deferred.type === "virtual") {
191
- resolved.push(deferred.skill);
192
- }
193
- else {
194
- const content = await readFile(deferred.path, "utf-8");
195
- const parsed = parseSkillMd(content);
196
- const stored = {
197
- name: parsed.name,
198
- description: parsed.description,
199
- body: parsed.body,
200
- basePath: dirname(deferred.path),
201
- };
202
- resolved.push(stored);
203
- }
204
- }
205
- return resolved;
206
- }
207
- _buildSkillsPrompt(skills) {
208
- if (skills.length === 0)
209
- return "";
210
- let xml = "<available_skills>\n";
211
- for (const skill of skills) {
212
- xml += ` <skill>\n`;
213
- xml += ` <name>${skill.name}</name>\n`;
214
- xml += ` <description>${skill.description}</description>\n`;
215
- xml += ` </skill>\n`;
41
+ _conn;
42
+ _promptParts;
43
+ _tools;
44
+ _skills;
45
+ _schemaProvider;
46
+ _cwd;
47
+ _existingSessionId;
48
+ constructor(state) {
49
+ this._conn = state.conn, this._promptParts = state.promptParts, this._tools = state.tools, this._skills = state.skills, this._schemaProvider = state.schemaProvider, this._cwd = state.cwd, this._existingSessionId = state.existingSessionId;
50
+ }
51
+ _clone(overrides) {
52
+ return new PlanImpl({
53
+ conn: this._conn,
54
+ promptParts: this._promptParts,
55
+ tools: this._tools,
56
+ skills: this._skills,
57
+ schemaProvider: this._schemaProvider,
58
+ cwd: this._cwd,
59
+ existingSessionId: this._existingSessionId,
60
+ ...overrides
61
+ });
62
+ }
63
+ text(content) {
64
+ return this._clone({ promptParts: [...this._promptParts, content] });
65
+ }
66
+ textln(content) {
67
+ return this._clone({ promptParts: [...this._promptParts, content + `
68
+ `] });
69
+ }
70
+ quote(content, tag = "quote") {
71
+ const part = content.includes(`
72
+ `) ? `<${tag}>
73
+ ${content}
74
+ </${tag}>
75
+ ` : `<${tag}>${content}</${tag}>
76
+ `;
77
+ return this._clone({ promptParts: [...this._promptParts, part] });
78
+ }
79
+ code(content, language = "") {
80
+ return this._clone({
81
+ promptParts: [...this._promptParts, `\`\`\`${language}
82
+ ${content}
83
+ \`\`\`
84
+ `]
85
+ });
86
+ }
87
+ tool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
88
+ let inputSchema, outputSchema, actualHandler;
89
+ typeof inputSchemaOrHandler == "function" ? (inputSchema = { toJsonSchema: () => ({ type: "object" }) }, outputSchema = { toJsonSchema: () => ({ type: "object" }) }, actualHandler = inputSchemaOrHandler) : typeof outputSchemaOrHandler == "function" ? (inputSchema = inputSchemaOrHandler, outputSchema = { toJsonSchema: () => ({ type: "object" }) }, actualHandler = outputSchemaOrHandler) : (inputSchema = inputSchemaOrHandler, outputSchema = outputSchemaOrHandler, actualHandler = handler);
90
+ const newTools = new Map(this._tools);
91
+ return newTools.set(name, {
92
+ name,
93
+ description,
94
+ handler: actualHandler,
95
+ inputSchema,
96
+ outputSchema,
97
+ includeInPrompt: !0
98
+ }), this._clone({ tools: newTools });
99
+ }
100
+ defineTool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
101
+ let inputSchema, outputSchema, actualHandler;
102
+ typeof inputSchemaOrHandler == "function" ? (inputSchema = { toJsonSchema: () => ({ type: "object" }) }, outputSchema = { toJsonSchema: () => ({ type: "object" }) }, actualHandler = inputSchemaOrHandler) : typeof outputSchemaOrHandler == "function" ? (inputSchema = inputSchemaOrHandler, outputSchema = { toJsonSchema: () => ({ type: "object" }) }, actualHandler = outputSchemaOrHandler) : (inputSchema = inputSchemaOrHandler, outputSchema = outputSchemaOrHandler, actualHandler = handler);
103
+ const newTools = new Map(this._tools);
104
+ return newTools.set(name, {
105
+ name,
106
+ description,
107
+ handler: actualHandler,
108
+ inputSchema,
109
+ outputSchema,
110
+ includeInPrompt: !1
111
+ }), this._clone({ tools: newTools });
112
+ }
113
+ skill(pathOrDef) {
114
+ return typeof pathOrDef == "string" ? this._clone({ skills: [...this._skills, { type: "stored", path: pathOrDef }] }) : (validateSkillName(pathOrDef.name), validateSkillDescription(pathOrDef.description), this._clone({
115
+ skills: [...this._skills, {
116
+ type: "virtual",
117
+ skill: {
118
+ name: pathOrDef.name,
119
+ description: pathOrDef.description,
120
+ body: pathOrDef.body,
121
+ tools: pathOrDef.tools
216
122
  }
217
- xml += "</available_skills>\n";
218
- xml += "\n";
219
- xml += "The above skills are available to you. When a task matches a skill's description,\n";
220
- xml += "call the `activate_skill` tool with the skill name to load its full instructions.\n";
221
- xml += "If the skill provides tools, use `call_skill_tool` to invoke them.\n";
222
- xml += "If the skill references files, use `read_skill_file` to access them.\n";
223
- return xml + "\n";
123
+ }]
124
+ }));
125
+ }
126
+ cwd(path) {
127
+ return this._clone({ cwd: path });
128
+ }
129
+ async _resolveSkills() {
130
+ const resolved = [];
131
+ for (const deferred of this._skills)
132
+ if (deferred.type === "virtual")
133
+ resolved.push(deferred.skill);
134
+ else {
135
+ const content = await readFile(deferred.path, "utf-8"), parsed = parseSkillMd(content), stored = {
136
+ name: parsed.name,
137
+ description: parsed.description,
138
+ body: parsed.body,
139
+ basePath: dirname(deferred.path)
140
+ };
141
+ resolved.push(stored);
142
+ }
143
+ return resolved;
144
+ }
145
+ _buildSkillsPrompt(skills) {
146
+ if (skills.length === 0)
147
+ return "";
148
+ let xml = `<available_skills>
149
+ `;
150
+ for (const skill of skills)
151
+ xml += ` <skill>
152
+ `, xml += ` <name>${skill.name}</name>
153
+ `, xml += ` <description>${skill.description}</description>
154
+ `, xml += ` </skill>
155
+ `;
156
+ return xml += `</available_skills>
157
+ `, xml += `
158
+ `, xml += `The above skills are available to you. When a task matches a skill's description,
159
+ `, xml += "call the `activate_skill` tool with the skill name to load its full instructions.\n", xml += "If the skill provides tools, use `call_skill_tool` to invoke them.\n", xml += "If the skill references files, use `read_skill_file` to access them.\n", xml + `
160
+ `;
161
+ }
162
+ async run() {
163
+ return this.stream().result;
164
+ }
165
+ stream() {
166
+ const stream = new ThoughtStream();
167
+ return this._executeStream(stream).catch((err) => {
168
+ stream.rejectResult(err instanceof Error ? err : new Error(String(err?.message ?? err))), stream.close();
169
+ }), stream;
170
+ }
171
+ async _executeStream(stream) {
172
+ this._conn.initialized || (await this._conn.connection.initialize({
173
+ protocolVersion: 1,
174
+ clientCapabilities: {}
175
+ }), this._conn.initialized = !0);
176
+ const resolvedSkills = await this._resolveSkills();
177
+ let prompt = this._buildSkillsPrompt(resolvedSkills) + this._promptParts.join("");
178
+ const toolsWithPrompt = Array.from(this._tools.values()).filter((t) => t.includeInPrompt);
179
+ if (toolsWithPrompt.length > 0) {
180
+ prompt += `
181
+
182
+ Available tools:
183
+ `;
184
+ for (const tool of toolsWithPrompt)
185
+ prompt += `- ${tool.name}: ${tool.description}
186
+ `;
224
187
  }
225
- async run() {
226
- return this.stream().result;
227
- }
228
- stream() {
229
- const stream = new ThoughtStream();
230
- this._executeStream(stream).catch((err) => {
231
- stream.rejectResult(err instanceof Error ? err : new Error(String(err?.message ?? err)));
232
- stream.close();
233
- });
234
- return stream;
235
- }
236
- async _executeStream(stream) {
237
- // Ensure initialized
238
- if (!this._conn.initialized) {
239
- await this._conn.connection.initialize({
240
- protocolVersion: 1,
241
- clientCapabilities: {},
242
- });
243
- this._conn.initialized = true;
188
+ const serverBuilder = mcpServer("thinkwell");
189
+ let resultReceived = !1, result, resultError, resolveResultReady;
190
+ const resultReady = new Promise((r) => {
191
+ resolveResultReady = r;
192
+ }), rawSchema = this._schemaProvider?.toJsonSchema() ?? { type: "object" }, needsWrap = rawSchema.type !== "object", outputSchema = needsWrap ? { type: "object", properties: { result: rawSchema }, required: ["result"] } : rawSchema, acceptResult = (input) => {
193
+ if (resultReceived)
194
+ return;
195
+ const value = needsWrap ? input.result : input, schemaRequired = rawSchema.required;
196
+ let required = Array.isArray(schemaRequired) ? schemaRequired : void 0;
197
+ if (features.FAULT_INJECTION) {
198
+ const injectedField = process.env.THINKWELL_INJECT_REQUIRED_FIELD;
199
+ injectedField && (required = required ? [...required, injectedField] : [injectedField]);
200
+ }
201
+ if (Array.isArray(required)) {
202
+ if (value == null || typeof value != "object") {
203
+ resultError = new TypeError(`Agent result must be an object when schema has required fields, got ${value === null ? "null" : typeof value}`), resultReceived = !0, resolveResultReady();
204
+ return;
244
205
  }
245
- // Resolve deferred skills
246
- const resolvedSkills = await this._resolveSkills();
247
- // Build the prompt: skills block first, then user prompt parts
248
- let prompt = this._buildSkillsPrompt(resolvedSkills) + this._promptParts.join("");
249
- // Add tool references to the prompt
250
- const toolsWithPrompt = Array.from(this._tools.values()).filter((t) => t.includeInPrompt);
251
- if (toolsWithPrompt.length > 0) {
252
- prompt += "\n\nAvailable tools:\n";
253
- for (const tool of toolsWithPrompt) {
254
- prompt += `- ${tool.name}: ${tool.description}\n`;
255
- }
206
+ const obj = value, missing = required.filter((key) => !Object.hasOwn(obj, key));
207
+ if (missing.length > 0) {
208
+ resultError = new TypeError(`Agent result is missing required field(s): ${missing.join(", ")}`), resultReceived = !0, resolveResultReady();
209
+ return;
256
210
  }
257
- // Create the MCP server builder
258
- const serverBuilder = mcpServer("thinkwell");
259
- // Track if we've received a result
260
- let resultReceived = false;
261
- let result;
262
- // Get the output schema for the return_result tool.
263
- // The Anthropic API requires tool input schemas to have type: "object" at
264
- // the root. Union types (anyOf/oneOf) don't satisfy this, so we wrap them
265
- // in a single-property object and unwrap in the handler.
266
- const rawSchema = this._schemaProvider?.toJsonSchema() ?? { type: "object" };
267
- const needsWrap = rawSchema.type !== "object";
268
- const outputSchema = needsWrap
269
- ? { type: "object", properties: { result: rawSchema }, required: ["result"] }
270
- : rawSchema;
271
- // Add return instruction
272
- prompt += "\n\nWhen you have your answer, call the `return_result` MCP tool with the result.";
273
- // Add the return_result tool
274
- serverBuilder.tool("return_result", "Return the final result", outputSchema, { type: "object", properties: { success: { type: "boolean" } } }, async (input) => {
275
- result = (needsWrap ? input.result : input);
276
- resultReceived = true;
277
- return { success: true };
211
+ }
212
+ result = value, resultReceived = !0, resolveResultReady();
213
+ };
214
+ prompt += "\n\nWhen you have your answer, call the `return_result` MCP tool with the result.", serverBuilder.tool("return_result", "Return the final result", outputSchema, { type: "object", properties: { success: { type: "boolean" } } }, async (input) => (acceptResult(input), { success: !0 }));
215
+ for (const tool of this._tools.values())
216
+ serverBuilder.tool(tool.name, tool.description, tool.inputSchema.toJsonSchema(), tool.outputSchema.toJsonSchema(), async (input, _context) => tool.handler(input));
217
+ const server = serverBuilder.build(), skillServer = resolvedSkills.length > 0 ? createSkillServer(resolvedSkills) : void 0;
218
+ this._conn.mcpHandler.register(server), skillServer && this._conn.mcpHandler.register(skillServer), this._conn.mcpHandler.setSessionId(this._existingSessionId ?? "pending");
219
+ try {
220
+ let sessionId;
221
+ if (this._existingSessionId)
222
+ sessionId = this._existingSessionId;
223
+ else {
224
+ const mcpServers = [{
225
+ type: "http",
226
+ name: server.name,
227
+ url: server.acpUrl,
228
+ headers: []
229
+ }];
230
+ skillServer && mcpServers.push({
231
+ type: "http",
232
+ name: skillServer.name,
233
+ url: skillServer.acpUrl,
234
+ headers: []
278
235
  });
279
- // Add all registered tools
280
- for (const tool of this._tools.values()) {
281
- serverBuilder.tool(tool.name, tool.description, tool.inputSchema.toJsonSchema(), tool.outputSchema.toJsonSchema(), async (input, _context) => {
282
- return tool.handler(input);
283
- });
284
- }
285
- const server = serverBuilder.build();
286
- // Build skill MCP server if skills are present
287
- const skillServer = resolvedSkills.length > 0
288
- ? createSkillServer(resolvedSkills)
289
- : undefined;
290
- // Register the MCP server(s)
291
- this._conn.mcpHandler.register(server);
292
- if (skillServer) {
293
- this._conn.mcpHandler.register(skillServer);
294
- }
295
- this._conn.mcpHandler.setSessionId(this._existingSessionId ?? "pending");
296
- try {
297
- // Create or reuse session
298
- let sessionId;
299
- if (this._existingSessionId) {
300
- // Reuse existing session
301
- sessionId = this._existingSessionId;
302
- }
303
- else {
304
- // Create new ephemeral session
305
- const mcpServers = [{
306
- type: "http",
307
- name: server.name,
308
- url: server.acpUrl,
309
- headers: [],
310
- }];
311
- if (skillServer) {
312
- mcpServers.push({
313
- type: "http",
314
- name: skillServer.name,
315
- url: skillServer.acpUrl,
316
- headers: [],
317
- });
318
- }
319
- const request = {
320
- cwd: this._cwd ?? process.cwd(),
321
- mcpServers,
322
- };
323
- const response = await this._conn.connection.newSession(request);
324
- sessionId = response.sessionId;
236
+ const request = {
237
+ cwd: this._cwd ?? process.cwd(),
238
+ mcpServers
239
+ };
240
+ sessionId = (await this._conn.connection.newSession(request)).sessionId;
241
+ }
242
+ this._conn.mcpHandler.setSessionId(sessionId);
243
+ const session = new ThinkSession(sessionId, this._conn);
244
+ this._conn.sessionHandlers.set(sessionId, session), await this._conn.mcpHandler.waitForToolsDiscovery(sessionId, 2e3);
245
+ try {
246
+ const pendingToolCalls = [], sendTurn = async (text) => {
247
+ const promptPromise = session.sendPrompt(text);
248
+ for (; !resultReceived; ) {
249
+ const update = await session.readUpdate();
250
+ if (update.type === "stop")
251
+ break;
252
+ if (update.type === "tool_start") {
253
+ const toolName = update.title;
254
+ (this._tools.has(toolName) || toolName === "return_result") && pendingToolCalls.push({ id: update.id, name: toolName, input: update.input });
325
255
  }
326
- // Update MCP handler with actual session ID
327
- this._conn.mcpHandler.setSessionId(sessionId);
328
- // Create internal session handler
329
- const session = new ThinkSession(sessionId, this._conn);
330
- this._conn.sessionHandlers.set(sessionId, session);
331
- // Wait for MCP tools discovery
332
- await this._conn.mcpHandler.waitForToolsDiscovery(sessionId, 2000);
333
- try {
334
- // Start the prompt without awaiting - we need to read updates concurrently
335
- const promptPromise = session.sendPrompt(prompt);
336
- // Read updates, forwarding events to the stream and watching for result
337
- while (!resultReceived) {
338
- const update = await session.readUpdate();
339
- if (update.type === "stop") {
340
- if (!resultReceived) {
341
- stream.rejectResult(new Error("Session ended without calling return_result"));
342
- }
343
- break;
344
- }
345
- // Forward the event to stream consumers
346
- stream.pushEvent(update);
347
- }
348
- // Wait for the prompt to complete (it should already be done since we got a stop)
349
- await promptPromise;
350
- if (resultReceived && result !== undefined) {
351
- stream.resolveResult(result);
352
- }
353
- }
354
- finally {
355
- stream.close();
356
- session.close();
357
- this._conn.sessionHandlers.delete(sessionId);
358
- }
359
- }
360
- finally {
361
- // Unregister MCP server(s)
362
- this._conn.mcpHandler.unregister(server);
363
- if (skillServer) {
364
- this._conn.mcpHandler.unregister(skillServer);
256
+ stream.pushEvent(update);
257
+ }
258
+ await promptPromise, !resultReceived && pendingToolCalls.some((c) => c.name === "return_result") && await resultReady;
259
+ };
260
+ for (await sendTurn(prompt); !resultReceived && pendingToolCalls.length > 0; ) {
261
+ const calls = pendingToolCalls.splice(0), results = [];
262
+ for (const call of calls) {
263
+ if (call.name === "return_result") {
264
+ acceptResult(call.input);
265
+ break;
365
266
  }
267
+ const tool = this._tools.get(call.name);
268
+ if (tool)
269
+ try {
270
+ const output = await tool.handler(call.input), text = typeof output == "string" ? output : JSON.stringify(output);
271
+ results.push(`Tool "${call.name}" (id: ${call.id}) result:
272
+ ${text}`);
273
+ } catch (err) {
274
+ const msg = err instanceof Error ? err.message : String(err);
275
+ results.push(`Tool "${call.name}" (id: ${call.id}) error:
276
+ ${msg}`);
277
+ }
278
+ }
279
+ if (resultReceived || results.length === 0)
280
+ break;
281
+ const followUp = results.join(`
282
+
283
+ `) + "\n\nWhen you have your answer, call the `return_result` MCP tool with the result.";
284
+ await sendTurn(followUp);
366
285
  }
286
+ resultError ? stream.rejectResult(resultError) : resultReceived && result !== void 0 ? stream.resolveResult(result) : resultReceived ? stream.rejectResult(new TypeError("Agent returned an undefined value")) : stream.rejectResult(new Error("Agent session ended without returning a result"));
287
+ } finally {
288
+ stream.close(), session.close(), this._conn.sessionHandlers.delete(sessionId);
289
+ }
290
+ } finally {
291
+ this._conn.mcpHandler.unregister(server), skillServer && this._conn.mcpHandler.unregister(skillServer);
367
292
  }
293
+ }
368
294
  }
369
- /**
370
- * Create a new Plan for composing a prompt with tools.
371
- *
372
- * This is the factory function for creating Plan instances. It is used
373
- * internally by `Agent.think()` and `Session.think()`.
374
- */
375
295
  export function createPlan(conn, schema, existingSessionId) {
376
- return new PlanImpl({
377
- conn,
378
- promptParts: [],
379
- tools: new Map(),
380
- skills: [],
381
- schemaProvider: schema,
382
- cwd: undefined,
383
- existingSessionId,
384
- });
296
+ return new PlanImpl({
297
+ conn,
298
+ promptParts: [],
299
+ tools: /* @__PURE__ */ new Map(),
300
+ skills: [],
301
+ schemaProvider: schema,
302
+ cwd: void 0,
303
+ existingSessionId
304
+ });
385
305
  }
386
- //# sourceMappingURL=think-builder.js.map