@khalilgharbaoui/opencode-claude-code-plugin 0.4.22 → 0.5.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/README.md +37 -0
- package/dist/index.js +223 -47
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -313,6 +313,32 @@ Set `permissionMode: "plan"` to forward `--permission-mode plan` to Claude. The
|
|
|
313
313
|
|
|
314
314
|
---
|
|
315
315
|
|
|
316
|
+
## AskUserQuestion
|
|
317
|
+
|
|
318
|
+
opencode has no native structured ask-question executor to proxy through (unlike `Bash`/`Task`), so the plugin handles `AskUserQuestion` specially:
|
|
319
|
+
|
|
320
|
+
1. **It renders the full question.** The tool's payload — every question, header, option label, and option description — is emitted as readable markdown into the assistant stream so the user actually sees the choices (same approach as `ExitPlanMode`).
|
|
321
|
+
2. **It is never auto-allowed at the CLI gate.** Allowing it would let the headless Claude CLI resolve its own question (no TTY → fabricated/empty answer) and proceed on a guess. `controlRequestBehaviorForTool` hard-denies `AskUserQuestion` and returns a message telling the model to wait for the operator's answer — or, if the run is non-interactive, to proceed with the single most reasonable option and state its assumption rather than stall.
|
|
322
|
+
|
|
323
|
+
This hard-deny sits **below** `controlRequestToolBehaviors` in precedence but **above** the global `controlRequestBehavior`. So:
|
|
324
|
+
|
|
325
|
+
- The global `controlRequestBehavior: "allow"` does **not** override it (interactive setups stay correct by default).
|
|
326
|
+
- An explicit per-tool entry **does**. For a fully unattended/automated deployment that prefers "guess and continue" over "stop and wait", restore the old auto-allow:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
"provider": {
|
|
330
|
+
"claude-code": {
|
|
331
|
+
"options": {
|
|
332
|
+
"controlRequestToolBehaviors": { "AskUserQuestion": "allow" }
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
With `"allow"`, the Claude CLI answers its own `AskUserQuestion` internally and the run never blocks — appropriate only when no operator is watching and forward progress matters more than a correct decision.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
316
342
|
## Compaction
|
|
317
343
|
|
|
318
344
|
When you run `/compact` in opencode, the plugin handles it on a short-lived dedicated Claude CLI spawn instead of routing it through your main conversation process. Three reasons:
|
|
@@ -435,6 +461,7 @@ plugin internals.
|
|
|
435
461
|
- No streaming of tool inputs as they're being constructed (Anthropic's `input_json_delta`); the plugin emits them once complete.
|
|
436
462
|
- Raw chain-of-thought is not available. Claude 4 family models ship summarized thinking only. See [Extended thinking](#extended-thinking) for the full picture.
|
|
437
463
|
- Recommended Claude Code CLI: **2.1.142+**. Older CLIs work for everything else but skip the `--thinking-display` flag, so Claude Opus 4.7 turns may render empty Thinking rows. If something breaks after a Claude Code update, the CLI version is the first thing to check.
|
|
464
|
+
- **Subagent todos require explicit permission.** opencode's task tool gates `todowrite` per subagent: without a `permission: { todowrite: "allow" }` rule on the subagent definition, opencode injects `todowrite: false` into the tools dict and the plugin's synthetic `todowrite` emissions surface as `⚙ invalid todowrite` rows. The built-in `general` subagent denies `todowrite` by default; use a custom subagent for parallel work that needs todo visibility. Subagent todos render inline in the **subagent's** session view (navigate with the TUI's `session.child.next` / `session.parent` commands), not in the parent session's panel.
|
|
438
465
|
|
|
439
466
|
---
|
|
440
467
|
|
|
@@ -481,6 +508,16 @@ git push origin master --follow-tags
|
|
|
481
508
|
|
|
482
509
|
The GitHub Actions workflow at `.github/workflows/publish.yml` runs `npm publish --access public` on tag push (requires `NPM_TOKEN` secret in the repo settings — use a classic automation token so 2FA isn't required at workflow time).
|
|
483
510
|
|
|
511
|
+
## Star History
|
|
512
|
+
|
|
513
|
+
<a href="https://www.star-history.com/?repos=khalilgharbaoui%2Fopencode-claude-code-plugin&type=date&legend=top-left">
|
|
514
|
+
<picture>
|
|
515
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&theme=dark&legend=top-left" />
|
|
516
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left" />
|
|
517
|
+
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left" />
|
|
518
|
+
</picture>
|
|
519
|
+
</a>
|
|
520
|
+
|
|
484
521
|
## License
|
|
485
522
|
|
|
486
523
|
MIT. See [LICENSE](./LICENSE).
|
package/dist/index.js
CHANGED
|
@@ -124,6 +124,91 @@ var log = {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
// src/todo-ledger.ts
|
|
128
|
+
var ledgers = /* @__PURE__ */ new Map();
|
|
129
|
+
var PENDING_CREATE_TTL_MS = 6e4;
|
|
130
|
+
var TASK_CREATED_PATTERN = /Task\s*#?\s*(\d+)\s+created/i;
|
|
131
|
+
var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
132
|
+
function getOrCreate(sessionId) {
|
|
133
|
+
let ledger = ledgers.get(sessionId);
|
|
134
|
+
if (!ledger) {
|
|
135
|
+
ledger = { todos: /* @__PURE__ */ new Map(), pendingCreates: /* @__PURE__ */ new Map() };
|
|
136
|
+
ledgers.set(sessionId, ledger);
|
|
137
|
+
}
|
|
138
|
+
return ledger;
|
|
139
|
+
}
|
|
140
|
+
function prunePending(ledger) {
|
|
141
|
+
const cutoff = Date.now() - PENDING_CREATE_TTL_MS;
|
|
142
|
+
for (const [id, pending] of ledger.pendingCreates) {
|
|
143
|
+
if (pending.createdAt < cutoff) ledger.pendingCreates.delete(id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function materialize(ledger) {
|
|
147
|
+
return Array.from(ledger.todos.values());
|
|
148
|
+
}
|
|
149
|
+
function resolveSubject(input) {
|
|
150
|
+
const subject = typeof input?.subject === "string" ? input.subject.trim() : "";
|
|
151
|
+
if (subject) return subject;
|
|
152
|
+
const description = typeof input?.description === "string" ? input.description.trim() : "";
|
|
153
|
+
if (description) return description;
|
|
154
|
+
return "(no subject)";
|
|
155
|
+
}
|
|
156
|
+
function applyTaskCreateToolUse(sessionId, toolUseId, input) {
|
|
157
|
+
if (!sessionId || !toolUseId) return;
|
|
158
|
+
const ledger = getOrCreate(sessionId);
|
|
159
|
+
prunePending(ledger);
|
|
160
|
+
ledger.pendingCreates.set(toolUseId, {
|
|
161
|
+
subject: resolveSubject(input),
|
|
162
|
+
createdAt: Date.now()
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function applyTaskCreateToolResult(sessionId, toolUseId, resultText) {
|
|
166
|
+
if (!sessionId || !toolUseId) return null;
|
|
167
|
+
const ledger = ledgers.get(sessionId);
|
|
168
|
+
if (!ledger) return null;
|
|
169
|
+
const pending = ledger.pendingCreates.get(toolUseId);
|
|
170
|
+
if (!pending) return null;
|
|
171
|
+
ledger.pendingCreates.delete(toolUseId);
|
|
172
|
+
const match = typeof resultText === "string" ? resultText.match(TASK_CREATED_PATTERN) : null;
|
|
173
|
+
if (!match) {
|
|
174
|
+
log.debug("TaskCreate result did not match expected format", { sessionId, toolUseId, resultText });
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const claudeId = match[1];
|
|
178
|
+
if (ledger.todos.has(claudeId)) {
|
|
179
|
+
log.debug("TaskCreate result for already-known claude id; overwriting", { sessionId, claudeId });
|
|
180
|
+
}
|
|
181
|
+
ledger.todos.set(claudeId, { id: claudeId, content: pending.subject, status: "pending" });
|
|
182
|
+
return materialize(ledger);
|
|
183
|
+
}
|
|
184
|
+
function applyTaskUpdate(sessionId, input) {
|
|
185
|
+
if (!sessionId) return null;
|
|
186
|
+
const taskId = typeof input?.taskId === "string" ? input.taskId : null;
|
|
187
|
+
if (!taskId) return null;
|
|
188
|
+
const ledger = ledgers.get(sessionId);
|
|
189
|
+
if (!ledger) return null;
|
|
190
|
+
const entry = ledger.todos.get(taskId);
|
|
191
|
+
if (!entry) {
|
|
192
|
+
log.debug("TaskUpdate for unknown task id", { sessionId, taskId });
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
if (input?.status === "deleted") {
|
|
196
|
+
ledger.todos.delete(taskId);
|
|
197
|
+
return materialize(ledger);
|
|
198
|
+
}
|
|
199
|
+
if (typeof input?.status === "string" && VALID_STATUSES.has(input.status)) {
|
|
200
|
+
entry.status = input.status;
|
|
201
|
+
}
|
|
202
|
+
if (typeof input?.subject === "string" && input.subject.trim().length > 0) {
|
|
203
|
+
entry.content = input.subject.trim();
|
|
204
|
+
}
|
|
205
|
+
return materialize(ledger);
|
|
206
|
+
}
|
|
207
|
+
function clearLedger(sessionId) {
|
|
208
|
+
if (!sessionId) return;
|
|
209
|
+
ledgers.delete(sessionId);
|
|
210
|
+
}
|
|
211
|
+
|
|
127
212
|
// src/tool-mapping.ts
|
|
128
213
|
function mapToolInput(name, input) {
|
|
129
214
|
if (!input) return input;
|
|
@@ -199,17 +284,42 @@ var CLAUDE_INTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
199
284
|
"ToolSearch",
|
|
200
285
|
"Agent",
|
|
201
286
|
"AskFollowupQuestion",
|
|
202
|
-
"TaskCreate",
|
|
203
|
-
"TaskUpdate",
|
|
204
287
|
"TaskList",
|
|
205
288
|
"TaskGet",
|
|
206
289
|
"TaskStop"
|
|
207
290
|
]);
|
|
291
|
+
function emitTodoWrite(todos) {
|
|
292
|
+
return {
|
|
293
|
+
name: "todowrite",
|
|
294
|
+
input: {
|
|
295
|
+
todos: todos.map((todo) => ({
|
|
296
|
+
id: todo.id,
|
|
297
|
+
content: todo.content,
|
|
298
|
+
status: todo.status,
|
|
299
|
+
priority: "medium"
|
|
300
|
+
}))
|
|
301
|
+
},
|
|
302
|
+
executed: false
|
|
303
|
+
};
|
|
304
|
+
}
|
|
208
305
|
function mapTool(name, input, opts) {
|
|
209
306
|
if (CLAUDE_INTERNAL_TOOLS.has(name)) {
|
|
210
307
|
log.debug("skipping Claude CLI internal tool", { name });
|
|
211
308
|
return { name, input, executed: true, skip: true };
|
|
212
309
|
}
|
|
310
|
+
if (name === "TaskCreate") {
|
|
311
|
+
if (opts?.sessionId && opts?.toolUseId) {
|
|
312
|
+
applyTaskCreateToolUse(opts.sessionId, opts.toolUseId, input);
|
|
313
|
+
}
|
|
314
|
+
return { name, input, executed: true, skip: true };
|
|
315
|
+
}
|
|
316
|
+
if (name === "TaskUpdate") {
|
|
317
|
+
if (opts?.sessionId) {
|
|
318
|
+
const list = applyTaskUpdate(opts.sessionId, input);
|
|
319
|
+
if (list !== null) return emitTodoWrite(list);
|
|
320
|
+
}
|
|
321
|
+
return { name, input, executed: true, skip: true };
|
|
322
|
+
}
|
|
213
323
|
if (name === "EnterPlanMode") return { name: "plan_enter", input: {}, executed: false };
|
|
214
324
|
if (name === "ExitPlanMode") return { name: "plan_exit", input, executed: false };
|
|
215
325
|
if (name === "TodoWrite") {
|
|
@@ -1181,6 +1291,8 @@ function setClaudeSessionId(key, sessionId) {
|
|
|
1181
1291
|
claudeSessions.set(key, sessionId);
|
|
1182
1292
|
}
|
|
1183
1293
|
function deleteClaudeSessionId(key) {
|
|
1294
|
+
const claudeSessionId = claudeSessions.get(key);
|
|
1295
|
+
if (claudeSessionId) clearLedger(claudeSessionId);
|
|
1184
1296
|
claudeSessions.delete(key);
|
|
1185
1297
|
}
|
|
1186
1298
|
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile) {
|
|
@@ -1885,6 +1997,46 @@ var AUTO_CONTINUE_PROMPT = "Continue the task from where you stopped. Do not sum
|
|
|
1885
1997
|
function normalizeVisibleText(text) {
|
|
1886
1998
|
return text.replace(/\s+/g, " ").trim();
|
|
1887
1999
|
}
|
|
2000
|
+
function isAskUserQuestionTool(name) {
|
|
2001
|
+
if (!name) return false;
|
|
2002
|
+
const n = name.toLowerCase();
|
|
2003
|
+
return n === "askuserquestion" || n === "ask_user_question";
|
|
2004
|
+
}
|
|
2005
|
+
function formatAskUserQuestion(input) {
|
|
2006
|
+
const anyInput = input;
|
|
2007
|
+
const questions = Array.isArray(anyInput?.questions) ? anyInput.questions : [];
|
|
2008
|
+
if (questions.length === 0) {
|
|
2009
|
+
const single = anyInput?.question ?? anyInput?.text;
|
|
2010
|
+
const q = typeof single === "string" && single.trim() ? single.trim() : "Question?";
|
|
2011
|
+
return `
|
|
2012
|
+
|
|
2013
|
+
**${q}**
|
|
2014
|
+
|
|
2015
|
+
_Reply with your answer to continue._
|
|
2016
|
+
|
|
2017
|
+
`;
|
|
2018
|
+
}
|
|
2019
|
+
const out = ["\n\n"];
|
|
2020
|
+
const multiQ = questions.length > 1;
|
|
2021
|
+
questions.forEach((q, i) => {
|
|
2022
|
+
const text = typeof q?.question === "string" && q.question.trim() || typeof q?.text === "string" && q.text.trim() || "Question?";
|
|
2023
|
+
const header = typeof q?.header === "string" && q.header.trim() ? q.header.trim() : "";
|
|
2024
|
+
out.push(`**${multiQ ? `${i + 1}. ` : ""}${text}**`);
|
|
2025
|
+
if (header) out.push(` _(${header})_`);
|
|
2026
|
+
out.push("\n\n");
|
|
2027
|
+
const options = Array.isArray(q?.options) ? q.options : [];
|
|
2028
|
+
options.forEach((opt, j) => {
|
|
2029
|
+
const label = typeof opt?.label === "string" && opt.label.trim() || typeof opt === "string" && opt.trim() || `Option ${j + 1}`;
|
|
2030
|
+
const desc = typeof opt?.description === "string" && opt.description.trim() ? ` \u2014 ${opt.description.trim()}` : "";
|
|
2031
|
+
out.push(`${j + 1}. **${label}**${desc}
|
|
2032
|
+
`);
|
|
2033
|
+
});
|
|
2034
|
+
out.push(
|
|
2035
|
+
q?.multiSelect === true ? "\n_Select one or more \u2014 reply with the numbers or labels._\n\n" : "\n_Reply with your choice (the number or label)._\n\n"
|
|
2036
|
+
);
|
|
2037
|
+
});
|
|
2038
|
+
return out.join("");
|
|
2039
|
+
}
|
|
1888
2040
|
function looksLikeQuestion(text) {
|
|
1889
2041
|
const normalized = normalizeVisibleText(text).toLowerCase();
|
|
1890
2042
|
if (!normalized) return false;
|
|
@@ -2213,6 +2365,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2213
2365
|
}
|
|
2214
2366
|
}
|
|
2215
2367
|
}
|
|
2368
|
+
if (isAskUserQuestionTool(toolName)) return "deny";
|
|
2216
2369
|
return this.config.controlRequestBehavior ?? "allow";
|
|
2217
2370
|
}
|
|
2218
2371
|
writeControlResponse(proc, requestId, response) {
|
|
@@ -2256,9 +2409,10 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2256
2409
|
toolName
|
|
2257
2410
|
});
|
|
2258
2411
|
} else {
|
|
2412
|
+
const denyMessage = isAskUserQuestionTool(toolName) ? "Your question and its options have already been presented to the operator in full. Prefer to stop here and wait for their answer in the next message \u2014 do not silently guess. But if this is an automated or otherwise non-interactive run where no operator will reply, do not stall: proceed with the single most reasonable option and state, in one line, the assumption you made so it can be corrected later." : this.config.controlRequestDenyMessage ?? `Denied by opencode-claude-code policy for tool ${toolName}`;
|
|
2259
2413
|
this.writeControlResponse(proc, requestId, {
|
|
2260
2414
|
behavior: "deny",
|
|
2261
|
-
message:
|
|
2415
|
+
message: denyMessage,
|
|
2262
2416
|
toolUseID: request.tool_use_id
|
|
2263
2417
|
});
|
|
2264
2418
|
log.info("control request auto-denied", {
|
|
@@ -2576,14 +2730,9 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2576
2730
|
thinkingText += block.thinking;
|
|
2577
2731
|
}
|
|
2578
2732
|
if (block.type === "tool_use" && block.id && block.name) {
|
|
2579
|
-
if (block.name
|
|
2733
|
+
if (isAskUserQuestionTool(block.name)) {
|
|
2580
2734
|
const parsedInput = block.input ?? {};
|
|
2581
|
-
|
|
2582
|
-
responseText += `
|
|
2583
|
-
|
|
2584
|
-
_Asking: ${question}_
|
|
2585
|
-
|
|
2586
|
-
`;
|
|
2735
|
+
responseText += formatAskUserQuestion(parsedInput);
|
|
2587
2736
|
continue;
|
|
2588
2737
|
}
|
|
2589
2738
|
if (block.name === "ExitPlanMode") {
|
|
@@ -2703,7 +2852,11 @@ ${plan}
|
|
|
2703
2852
|
input: mappedInput,
|
|
2704
2853
|
executed,
|
|
2705
2854
|
skip
|
|
2706
|
-
} = mapTool(tc.name, tc.args, {
|
|
2855
|
+
} = mapTool(tc.name, tc.args, {
|
|
2856
|
+
webSearch: this.config.webSearch,
|
|
2857
|
+
sessionId: getClaudeSessionId(sk),
|
|
2858
|
+
toolUseId: tc.id
|
|
2859
|
+
});
|
|
2707
2860
|
if (skip) continue;
|
|
2708
2861
|
content.push({
|
|
2709
2862
|
type: "tool-call",
|
|
@@ -3146,7 +3299,11 @@ ${plan}
|
|
|
3146
3299
|
const { name: mappedName, skip, executed } = mapTool(
|
|
3147
3300
|
block.name,
|
|
3148
3301
|
void 0,
|
|
3149
|
-
{
|
|
3302
|
+
{
|
|
3303
|
+
webSearch: self.config.webSearch,
|
|
3304
|
+
sessionId: getClaudeSessionId(sk),
|
|
3305
|
+
toolUseId: block.id
|
|
3306
|
+
}
|
|
3150
3307
|
);
|
|
3151
3308
|
if (!skip) {
|
|
3152
3309
|
controller.enqueue({
|
|
@@ -3236,22 +3393,12 @@ ${plan}
|
|
|
3236
3393
|
parsedInput = JSON.parse(tc.inputJson || "{}");
|
|
3237
3394
|
} catch {
|
|
3238
3395
|
}
|
|
3239
|
-
if (tc.name
|
|
3240
|
-
let question = "Question?";
|
|
3241
|
-
if (parsedInput?.questions && Array.isArray(parsedInput.questions) && parsedInput.questions.length > 0) {
|
|
3242
|
-
question = parsedInput.questions[0].question || parsedInput.questions[0].text || "Question?";
|
|
3243
|
-
} else {
|
|
3244
|
-
question = parsedInput?.question || parsedInput?.text || "Question?";
|
|
3245
|
-
}
|
|
3396
|
+
if (isAskUserQuestionTool(tc.name)) {
|
|
3246
3397
|
const askId = startTextBlock();
|
|
3247
3398
|
controller.enqueue({
|
|
3248
3399
|
type: "text-delta",
|
|
3249
3400
|
id: askId,
|
|
3250
|
-
delta:
|
|
3251
|
-
|
|
3252
|
-
_Asking: ${question}_
|
|
3253
|
-
|
|
3254
|
-
`
|
|
3401
|
+
delta: formatAskUserQuestion(parsedInput)
|
|
3255
3402
|
});
|
|
3256
3403
|
endTextBlock();
|
|
3257
3404
|
} else if (tc.name === "ExitPlanMode") {
|
|
@@ -3280,7 +3427,11 @@ ${plan}
|
|
|
3280
3427
|
input: mappedInput,
|
|
3281
3428
|
executed,
|
|
3282
3429
|
skip
|
|
3283
|
-
} = mapTool(tc.name, parsedInput, {
|
|
3430
|
+
} = mapTool(tc.name, parsedInput, {
|
|
3431
|
+
webSearch: self.config.webSearch,
|
|
3432
|
+
sessionId: getClaudeSessionId(sk),
|
|
3433
|
+
toolUseId: tc.id
|
|
3434
|
+
});
|
|
3284
3435
|
if (!skip) {
|
|
3285
3436
|
toolCallsById.set(tc.id, {
|
|
3286
3437
|
id: tc.id,
|
|
@@ -3401,23 +3552,12 @@ ${plan}
|
|
|
3401
3552
|
name: block.name,
|
|
3402
3553
|
input: parsedInput
|
|
3403
3554
|
});
|
|
3404
|
-
if (block.name
|
|
3405
|
-
let question = "Question?";
|
|
3406
|
-
if (parsedInput?.questions && Array.isArray(parsedInput.questions) && parsedInput.questions.length > 0) {
|
|
3407
|
-
const q = parsedInput.questions[0];
|
|
3408
|
-
question = q.question || q.text || "Question?";
|
|
3409
|
-
} else {
|
|
3410
|
-
question = parsedInput?.question || parsedInput?.text || "Question?";
|
|
3411
|
-
}
|
|
3555
|
+
if (isAskUserQuestionTool(block.name)) {
|
|
3412
3556
|
const askId = startTextBlock();
|
|
3413
3557
|
controller.enqueue({
|
|
3414
3558
|
type: "text-delta",
|
|
3415
3559
|
id: askId,
|
|
3416
|
-
delta:
|
|
3417
|
-
|
|
3418
|
-
_Asking: ${question}_
|
|
3419
|
-
|
|
3420
|
-
`
|
|
3560
|
+
delta: formatAskUserQuestion(parsedInput)
|
|
3421
3561
|
});
|
|
3422
3562
|
endTextBlock();
|
|
3423
3563
|
} else if (block.name === "ExitPlanMode") {
|
|
@@ -3446,7 +3586,11 @@ ${plan}
|
|
|
3446
3586
|
input: mappedInput,
|
|
3447
3587
|
executed,
|
|
3448
3588
|
skip
|
|
3449
|
-
} = mapTool(block.name, parsedInput, {
|
|
3589
|
+
} = mapTool(block.name, parsedInput, {
|
|
3590
|
+
webSearch: self.config.webSearch,
|
|
3591
|
+
sessionId: getClaudeSessionId(sk),
|
|
3592
|
+
toolUseId: block.id
|
|
3593
|
+
});
|
|
3450
3594
|
if (!skip) {
|
|
3451
3595
|
if (!executed) skipResultForIds.add(block.id);
|
|
3452
3596
|
controller.enqueue({
|
|
@@ -3487,16 +3631,48 @@ ${plan}
|
|
|
3487
3631
|
});
|
|
3488
3632
|
continue;
|
|
3489
3633
|
}
|
|
3634
|
+
let resultText = "";
|
|
3635
|
+
if (typeof block.content === "string") {
|
|
3636
|
+
resultText = block.content;
|
|
3637
|
+
} else if (Array.isArray(block.content)) {
|
|
3638
|
+
resultText = block.content.filter(
|
|
3639
|
+
(c) => c.type === "text" && typeof c.text === "string"
|
|
3640
|
+
).map((c) => c.text).join("\n");
|
|
3641
|
+
}
|
|
3642
|
+
const claudeSessionId = getClaudeSessionId(sk);
|
|
3643
|
+
if (claudeSessionId) {
|
|
3644
|
+
const list = applyTaskCreateToolResult(
|
|
3645
|
+
claudeSessionId,
|
|
3646
|
+
block.tool_use_id,
|
|
3647
|
+
resultText
|
|
3648
|
+
);
|
|
3649
|
+
if (list) {
|
|
3650
|
+
const synthId = `todowrite_${block.tool_use_id}`;
|
|
3651
|
+
controller.enqueue({
|
|
3652
|
+
type: "tool-input-start",
|
|
3653
|
+
id: synthId,
|
|
3654
|
+
toolName: "todowrite",
|
|
3655
|
+
providerExecuted: false
|
|
3656
|
+
});
|
|
3657
|
+
controller.enqueue({
|
|
3658
|
+
type: "tool-call",
|
|
3659
|
+
toolCallId: synthId,
|
|
3660
|
+
toolName: "todowrite",
|
|
3661
|
+
input: JSON.stringify({
|
|
3662
|
+
todos: list.map((t) => ({
|
|
3663
|
+
id: t.id,
|
|
3664
|
+
content: t.content,
|
|
3665
|
+
status: t.status,
|
|
3666
|
+
priority: "medium"
|
|
3667
|
+
}))
|
|
3668
|
+
}),
|
|
3669
|
+
providerExecuted: false
|
|
3670
|
+
});
|
|
3671
|
+
noteToolActivity();
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3490
3674
|
const toolCall = toolCallsById.get(block.tool_use_id);
|
|
3491
3675
|
if (toolCall) {
|
|
3492
|
-
let resultText = "";
|
|
3493
|
-
if (typeof block.content === "string") {
|
|
3494
|
-
resultText = block.content;
|
|
3495
|
-
} else if (Array.isArray(block.content)) {
|
|
3496
|
-
resultText = block.content.filter(
|
|
3497
|
-
(c) => c.type === "text" && typeof c.text === "string"
|
|
3498
|
-
).map((c) => c.text).join("\n");
|
|
3499
|
-
}
|
|
3500
3676
|
controller.enqueue({
|
|
3501
3677
|
type: "tool-result",
|
|
3502
3678
|
toolCallId: block.tool_use_id,
|