thinkwell 0.5.4 → 0.5.6

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 (44) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +207 -279
  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.js +89 -245
  19. package/dist/cli/workspace.js +92 -226
  20. package/dist/connectors/index.js +1 -7
  21. package/dist/generated/features.d.ts +5 -0
  22. package/dist/generated/features.d.ts.map +1 -0
  23. package/dist/generated/features.js +4 -0
  24. package/dist/generated/features.js.map +1 -0
  25. package/dist/index.js +0 -5
  26. package/dist/schema.js +3 -36
  27. package/dist/session.js +50 -82
  28. package/dist/think-builder.d.ts.map +1 -1
  29. package/dist/think-builder.js +269 -370
  30. package/dist/think-builder.js.map +1 -1
  31. package/dist/thought-event.d.ts +1 -0
  32. package/dist/thought-event.d.ts.map +1 -1
  33. package/dist/thought-event.js +0 -1
  34. package/dist/thought-stream.js +60 -96
  35. package/dist-pkg/acp.cjs +13385 -1876
  36. package/dist-pkg/cli-build.cjs +171 -369
  37. package/dist-pkg/cli-bundle.cjs +289 -690
  38. package/dist-pkg/cli-check.cjs +202 -415
  39. package/dist-pkg/cli-dependency-check.cjs +39 -82
  40. package/dist-pkg/cli-dependency-errors.cjs +9 -41
  41. package/dist-pkg/cli-loader.cjs +90 -173
  42. package/dist-pkg/protocol.cjs +2 -8
  43. package/dist-pkg/thinkwell.cjs +876 -1842
  44. package/package.json +7 -6
@@ -1,386 +1,285 @@
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 { mcpServer, createSkillServer, parseSkillMd, validateSkillName, validateSkillDescription } from "@thinkwell/acp";
4
4
  import { ThoughtStream } from "./thought-stream.js";
5
- /**
6
- * Internal session handler for ThinkBuilder
7
- */
8
5
  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
- }
6
+ sessionId;
7
+ _conn;
8
+ _pendingUpdates = [];
9
+ _updateResolvers = [];
10
+ _closed = !1;
11
+ constructor(sessionId, conn) {
12
+ this.sessionId = sessionId, this._conn = conn;
13
+ }
14
+ async sendPrompt(content) {
15
+ const response = await this._conn.connection.prompt({
16
+ sessionId: this.sessionId,
17
+ prompt: [{ type: "text", text: content }]
18
+ });
19
+ response.stopReason && this._pushInternal({ type: "stop", reason: response.stopReason });
20
+ }
21
+ pushUpdate(update) {
22
+ this._pushInternal(update);
23
+ }
24
+ _pushInternal(update) {
25
+ this._updateResolvers.length > 0 ? this._updateResolvers.shift()(update) : this._pendingUpdates.push(update);
26
+ }
27
+ async readUpdate() {
28
+ return this._pendingUpdates.length > 0 ? this._pendingUpdates.shift() : this._closed ? { type: "stop", reason: "session_closed" } : new Promise((resolve) => {
29
+ this._updateResolvers.push(resolve);
30
+ });
31
+ }
32
+ close() {
33
+ this._closed = !0;
34
+ for (const resolver of this._updateResolvers)
35
+ resolver({ type: "stop", reason: "session_closed" });
36
+ this._updateResolvers = [];
37
+ }
57
38
  }
58
39
  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`;
40
+ _conn;
41
+ _promptParts;
42
+ _tools;
43
+ _skills;
44
+ _schemaProvider;
45
+ _cwd;
46
+ _existingSessionId;
47
+ constructor(state) {
48
+ 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;
49
+ }
50
+ _clone(overrides) {
51
+ return new PlanImpl({
52
+ conn: this._conn,
53
+ promptParts: this._promptParts,
54
+ tools: this._tools,
55
+ skills: this._skills,
56
+ schemaProvider: this._schemaProvider,
57
+ cwd: this._cwd,
58
+ existingSessionId: this._existingSessionId,
59
+ ...overrides
60
+ });
61
+ }
62
+ text(content) {
63
+ return this._clone({ promptParts: [...this._promptParts, content] });
64
+ }
65
+ textln(content) {
66
+ return this._clone({ promptParts: [...this._promptParts, content + `
67
+ `] });
68
+ }
69
+ quote(content, tag = "quote") {
70
+ const part = content.includes(`
71
+ `) ? `<${tag}>
72
+ ${content}
73
+ </${tag}>
74
+ ` : `<${tag}>${content}</${tag}>
75
+ `;
76
+ return this._clone({ promptParts: [...this._promptParts, part] });
77
+ }
78
+ code(content, language = "") {
79
+ return this._clone({
80
+ promptParts: [...this._promptParts, `\`\`\`${language}
81
+ ${content}
82
+ \`\`\`
83
+ `]
84
+ });
85
+ }
86
+ tool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
87
+ let inputSchema, outputSchema, actualHandler;
88
+ 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);
89
+ const newTools = new Map(this._tools);
90
+ return newTools.set(name, {
91
+ name,
92
+ description,
93
+ handler: actualHandler,
94
+ inputSchema,
95
+ outputSchema,
96
+ includeInPrompt: !0
97
+ }), this._clone({ tools: newTools });
98
+ }
99
+ defineTool(name, description, inputSchemaOrHandler, outputSchemaOrHandler, handler) {
100
+ let inputSchema, outputSchema, actualHandler;
101
+ 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);
102
+ const newTools = new Map(this._tools);
103
+ return newTools.set(name, {
104
+ name,
105
+ description,
106
+ handler: actualHandler,
107
+ inputSchema,
108
+ outputSchema,
109
+ includeInPrompt: !1
110
+ }), this._clone({ tools: newTools });
111
+ }
112
+ skill(pathOrDef) {
113
+ return typeof pathOrDef == "string" ? this._clone({ skills: [...this._skills, { type: "stored", path: pathOrDef }] }) : (validateSkillName(pathOrDef.name), validateSkillDescription(pathOrDef.description), this._clone({
114
+ skills: [...this._skills, {
115
+ type: "virtual",
116
+ skill: {
117
+ name: pathOrDef.name,
118
+ description: pathOrDef.description,
119
+ body: pathOrDef.body,
120
+ tools: pathOrDef.tools
216
121
  }
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";
122
+ }]
123
+ }));
124
+ }
125
+ cwd(path) {
126
+ return this._clone({ cwd: path });
127
+ }
128
+ async _resolveSkills() {
129
+ const resolved = [];
130
+ for (const deferred of this._skills)
131
+ if (deferred.type === "virtual")
132
+ resolved.push(deferred.skill);
133
+ else {
134
+ const content = await readFile(deferred.path, "utf-8"), parsed = parseSkillMd(content), stored = {
135
+ name: parsed.name,
136
+ description: parsed.description,
137
+ body: parsed.body,
138
+ basePath: dirname(deferred.path)
139
+ };
140
+ resolved.push(stored);
141
+ }
142
+ return resolved;
143
+ }
144
+ _buildSkillsPrompt(skills) {
145
+ if (skills.length === 0)
146
+ return "";
147
+ let xml = `<available_skills>
148
+ `;
149
+ for (const skill of skills)
150
+ xml += ` <skill>
151
+ `, xml += ` <name>${skill.name}</name>
152
+ `, xml += ` <description>${skill.description}</description>
153
+ `, xml += ` </skill>
154
+ `;
155
+ return xml += `</available_skills>
156
+ `, xml += `
157
+ `, xml += `The above skills are available to you. When a task matches a skill's description,
158
+ `, 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 + `
159
+ `;
160
+ }
161
+ async run() {
162
+ return this.stream().result;
163
+ }
164
+ stream() {
165
+ const stream = new ThoughtStream();
166
+ return this._executeStream(stream).catch((err) => {
167
+ stream.rejectResult(err instanceof Error ? err : new Error(String(err?.message ?? err))), stream.close();
168
+ }), stream;
169
+ }
170
+ async _executeStream(stream) {
171
+ this._conn.initialized || (await this._conn.connection.initialize({
172
+ protocolVersion: 1,
173
+ clientCapabilities: {}
174
+ }), this._conn.initialized = !0);
175
+ const resolvedSkills = await this._resolveSkills();
176
+ let prompt = this._buildSkillsPrompt(resolvedSkills) + this._promptParts.join("");
177
+ const toolsWithPrompt = Array.from(this._tools.values()).filter((t) => t.includeInPrompt);
178
+ if (toolsWithPrompt.length > 0) {
179
+ prompt += `
180
+
181
+ Available tools:
182
+ `;
183
+ for (const tool of toolsWithPrompt)
184
+ prompt += `- ${tool.name}: ${tool.description}
185
+ `;
224
186
  }
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();
187
+ const serverBuilder = mcpServer("thinkwell");
188
+ let resultReceived = !1, result, resolveResultReady;
189
+ const resultReady = new Promise((r) => {
190
+ resolveResultReady = r;
191
+ }), acceptResult = (input) => {
192
+ result = needsWrap ? input.result : input, resultReceived = !0, resolveResultReady();
193
+ }, rawSchema = this._schemaProvider?.toJsonSchema() ?? { type: "object" }, needsWrap = rawSchema.type !== "object", outputSchema = needsWrap ? { type: "object", properties: { result: rawSchema }, required: ["result"] } : rawSchema;
194
+ 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 }));
195
+ for (const tool of this._tools.values())
196
+ serverBuilder.tool(tool.name, tool.description, tool.inputSchema.toJsonSchema(), tool.outputSchema.toJsonSchema(), async (input, _context) => tool.handler(input));
197
+ const server = serverBuilder.build(), skillServer = resolvedSkills.length > 0 ? createSkillServer(resolvedSkills) : void 0;
198
+ this._conn.mcpHandler.register(server), skillServer && this._conn.mcpHandler.register(skillServer), this._conn.mcpHandler.setSessionId(this._existingSessionId ?? "pending");
199
+ try {
200
+ let sessionId;
201
+ if (this._existingSessionId)
202
+ sessionId = this._existingSessionId;
203
+ else {
204
+ const mcpServers = [{
205
+ type: "http",
206
+ name: server.name,
207
+ url: server.acpUrl,
208
+ headers: []
209
+ }];
210
+ skillServer && mcpServers.push({
211
+ type: "http",
212
+ name: skillServer.name,
213
+ url: skillServer.acpUrl,
214
+ headers: []
233
215
  });
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;
244
- }
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
- }
256
- }
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 };
278
- });
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;
216
+ const request = {
217
+ cwd: this._cwd ?? process.cwd(),
218
+ mcpServers
219
+ };
220
+ sessionId = (await this._conn.connection.newSession(request)).sessionId;
221
+ }
222
+ this._conn.mcpHandler.setSessionId(sessionId);
223
+ const session = new ThinkSession(sessionId, this._conn);
224
+ this._conn.sessionHandlers.set(sessionId, session), await this._conn.mcpHandler.waitForToolsDiscovery(sessionId, 2e3);
225
+ try {
226
+ const pendingToolCalls = [], sendTurn = async (text) => {
227
+ const promptPromise = session.sendPrompt(text);
228
+ for (; !resultReceived; ) {
229
+ const update = await session.readUpdate();
230
+ if (update.type === "stop")
231
+ break;
232
+ if (update.type === "tool_start") {
233
+ const toolName = update.title;
234
+ (this._tools.has(toolName) || toolName === "return_result") && pendingToolCalls.push({ id: update.id, name: toolName, input: update.input });
325
235
  }
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);
236
+ stream.pushEvent(update);
237
+ }
238
+ await promptPromise, !resultReceived && pendingToolCalls.some((c) => c.name === "return_result") && await resultReady;
239
+ };
240
+ for (await sendTurn(prompt); !resultReceived && pendingToolCalls.length > 0; ) {
241
+ const calls = pendingToolCalls.splice(0), results = [];
242
+ for (const call of calls) {
243
+ if (call.name === "return_result") {
244
+ acceptResult(call.input);
245
+ break;
365
246
  }
247
+ const tool = this._tools.get(call.name);
248
+ if (tool)
249
+ try {
250
+ const output = await tool.handler(call.input), text = typeof output == "string" ? output : JSON.stringify(output);
251
+ results.push(`Tool "${call.name}" (id: ${call.id}) result:
252
+ ${text}`);
253
+ } catch (err) {
254
+ const msg = err instanceof Error ? err.message : String(err);
255
+ results.push(`Tool "${call.name}" (id: ${call.id}) error:
256
+ ${msg}`);
257
+ }
258
+ }
259
+ if (resultReceived || results.length === 0)
260
+ break;
261
+ const followUp = results.join(`
262
+
263
+ `) + "\n\nWhen you have your answer, call the `return_result` MCP tool with the result.";
264
+ await sendTurn(followUp);
366
265
  }
266
+ resultReceived && result !== void 0 ? stream.resolveResult(result) : stream.rejectResult(new Error("Session ended without calling return_result"));
267
+ } finally {
268
+ stream.close(), session.close(), this._conn.sessionHandlers.delete(sessionId);
269
+ }
270
+ } finally {
271
+ this._conn.mcpHandler.unregister(server), skillServer && this._conn.mcpHandler.unregister(skillServer);
367
272
  }
273
+ }
368
274
  }
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
275
  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
- });
276
+ return new PlanImpl({
277
+ conn,
278
+ promptParts: [],
279
+ tools: /* @__PURE__ */ new Map(),
280
+ skills: [],
281
+ schemaProvider: schema,
282
+ cwd: void 0,
283
+ existingSessionId
284
+ });
385
285
  }
386
- //# sourceMappingURL=think-builder.js.map