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.
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +207 -279
- package/dist/agent.js.map +1 -1
- package/dist/build.js +44 -98
- package/dist/cli/build.js +92 -227
- package/dist/cli/bundle.js +570 -1136
- package/dist/cli/check.js +125 -214
- package/dist/cli/commands.js +63 -177
- package/dist/cli/compiler-host.js +81 -190
- package/dist/cli/dependency-check.js +125 -269
- package/dist/cli/dependency-errors.js +12 -84
- package/dist/cli/fmt.js +1 -13
- package/dist/cli/init-command.js +21 -68
- package/dist/cli/init.js +90 -220
- package/dist/cli/loader.js +95 -361
- package/dist/cli/new-command.js +25 -73
- package/dist/cli/package-manager.js +50 -117
- package/dist/cli/schema.js +89 -245
- package/dist/cli/workspace.js +92 -226
- package/dist/connectors/index.js +1 -7
- package/dist/generated/features.d.ts +5 -0
- package/dist/generated/features.d.ts.map +1 -0
- package/dist/generated/features.js +4 -0
- package/dist/generated/features.js.map +1 -0
- package/dist/index.js +0 -5
- package/dist/schema.js +3 -36
- package/dist/session.js +50 -82
- package/dist/think-builder.d.ts.map +1 -1
- package/dist/think-builder.js +269 -370
- package/dist/think-builder.js.map +1 -1
- package/dist/thought-event.d.ts +1 -0
- package/dist/thought-event.d.ts.map +1 -1
- package/dist/thought-event.js +0 -1
- package/dist/thought-stream.js +60 -96
- package/dist-pkg/acp.cjs +13385 -1876
- package/dist-pkg/cli-build.cjs +171 -369
- package/dist-pkg/cli-bundle.cjs +289 -690
- package/dist-pkg/cli-check.cjs +202 -415
- package/dist-pkg/cli-dependency-check.cjs +39 -82
- package/dist-pkg/cli-dependency-errors.cjs +9 -41
- package/dist-pkg/cli-loader.cjs +90 -173
- package/dist-pkg/protocol.cjs +2 -8
- package/dist-pkg/thinkwell.cjs +876 -1842
- package/package.json +7 -6
package/dist/think-builder.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|