@khalilgharbaoui/opencode-claude-code-plugin 0.4.23 → 0.5.1

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 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.d.ts CHANGED
@@ -89,6 +89,13 @@ type OpenCodeEvent = {
89
89
  * for the current call ("default", "compaction", "title", etc.), the
90
90
  * resolved model, and the user message. Output is the mutable params bag
91
91
  * the hook can adjust before opencode forwards them to the LM.
92
+ *
93
+ * The plugin injects `input.agent` as `opencodeAgent` and `input.sessionID`
94
+ * as `opencodeSessionID` into `output.options` so the language model can
95
+ * read them from `providerOptions[providerID]` on every LLM request.
96
+ * `opencodeSessionID` serves as a fallback affinity token when the
97
+ * `x-session-affinity` request header is absent (provider switch
98
+ * mid-session, title synthesis paths, older opencode versions).
92
99
  */
93
100
  type OpenCodeChatParamsInput = {
94
101
  sessionID?: string;
@@ -450,11 +457,14 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
450
457
  private ensureProxyServer;
451
458
  private extractPendingProxyResult;
452
459
  /**
453
- * Opencode sets `x-session-affinity: <sessionID>` on LLM calls for
454
- * third-party providers (packages/opencode/src/session/llm.ts). Use it so
455
- * two chats in the same cwd+model get separate CLI processes instead of
456
- * stomping on each other. Falls back to "default" when absent (older
457
- * opencode, direct AI-SDK use, title synthesis paths, etc).
460
+ * Resolve the session affinity token for this LLM call. Delegates to the
461
+ * exported `resolveSessionAffinity` helper so the logic is unit-testable.
462
+ * Priority:
463
+ * 1. `x-session-affinity` request header (primary).
464
+ * 2. `opencodeSessionID` in providerOptions (chat.params hook fallback
465
+ * covers provider switches mid-session and title synthesis paths
466
+ * where the header is absent).
467
+ * 3. `"default"`.
458
468
  */
459
469
  private sessionAffinity;
460
470
  private controlRequestBehaviorForTool;
package/dist/index.js CHANGED
@@ -1955,6 +1955,22 @@ function resolveCompactionModel(configured) {
1955
1955
  if (trimmed) return trimmed;
1956
1956
  return DEFAULT_COMPACTION_MODEL;
1957
1957
  }
1958
+ function resolveSessionAffinity(headers, providerOptions, providerKey) {
1959
+ if (headers) {
1960
+ for (const key of Object.keys(headers)) {
1961
+ if (key.toLowerCase() === "x-session-affinity") {
1962
+ const v = headers[key];
1963
+ if (typeof v === "string" && v.length > 0) return v;
1964
+ }
1965
+ }
1966
+ }
1967
+ if (providerOptions) {
1968
+ const bag = providerOptions[providerKey] ?? providerOptions["claude-code"];
1969
+ const sid = bag?.opencodeSessionID;
1970
+ if (typeof sid === "string" && sid.length > 0) return sid;
1971
+ }
1972
+ return "default";
1973
+ }
1958
1974
  var KNOWN_DELTA_TYPES = /* @__PURE__ */ new Set([
1959
1975
  "thinking_delta",
1960
1976
  "text_delta",
@@ -1997,6 +2013,46 @@ var AUTO_CONTINUE_PROMPT = "Continue the task from where you stopped. Do not sum
1997
2013
  function normalizeVisibleText(text) {
1998
2014
  return text.replace(/\s+/g, " ").trim();
1999
2015
  }
2016
+ function isAskUserQuestionTool(name) {
2017
+ if (!name) return false;
2018
+ const n = name.toLowerCase();
2019
+ return n === "askuserquestion" || n === "ask_user_question";
2020
+ }
2021
+ function formatAskUserQuestion(input) {
2022
+ const anyInput = input;
2023
+ const questions = Array.isArray(anyInput?.questions) ? anyInput.questions : [];
2024
+ if (questions.length === 0) {
2025
+ const single = anyInput?.question ?? anyInput?.text;
2026
+ const q = typeof single === "string" && single.trim() ? single.trim() : "Question?";
2027
+ return `
2028
+
2029
+ **${q}**
2030
+
2031
+ _Reply with your answer to continue._
2032
+
2033
+ `;
2034
+ }
2035
+ const out = ["\n\n"];
2036
+ const multiQ = questions.length > 1;
2037
+ questions.forEach((q, i) => {
2038
+ const text = typeof q?.question === "string" && q.question.trim() || typeof q?.text === "string" && q.text.trim() || "Question?";
2039
+ const header = typeof q?.header === "string" && q.header.trim() ? q.header.trim() : "";
2040
+ out.push(`**${multiQ ? `${i + 1}. ` : ""}${text}**`);
2041
+ if (header) out.push(` _(${header})_`);
2042
+ out.push("\n\n");
2043
+ const options = Array.isArray(q?.options) ? q.options : [];
2044
+ options.forEach((opt, j) => {
2045
+ const label = typeof opt?.label === "string" && opt.label.trim() || typeof opt === "string" && opt.trim() || `Option ${j + 1}`;
2046
+ const desc = typeof opt?.description === "string" && opt.description.trim() ? ` \u2014 ${opt.description.trim()}` : "";
2047
+ out.push(`${j + 1}. **${label}**${desc}
2048
+ `);
2049
+ });
2050
+ out.push(
2051
+ 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"
2052
+ );
2053
+ });
2054
+ return out.join("");
2055
+ }
2000
2056
  function looksLikeQuestion(text) {
2001
2057
  const normalized = normalizeVisibleText(text).toLowerCase();
2002
2058
  if (!normalized) return false;
@@ -2092,6 +2148,11 @@ function nearestWorkspaceAgentsPrompt(cwd) {
2092
2148
  dir = parent;
2093
2149
  }
2094
2150
  }
2151
+ var AGENTS_MAINTENANCE_HINT = `## Keeping AGENTS.md up to date
2152
+
2153
+ When you complete a task, phase, or to-do item that is listed in AGENTS.md, update the file
2154
+ immediately after the work is done \u2014 mark it \u2705, check it off, or remove it. Do this inside
2155
+ the same turn so the next session does not repeat work that is already finished.`;
2095
2156
  var MULTI_STEP_TASK_HINT = `## Continuing through multi-step tasks
2096
2157
 
2097
2158
  opencode requires the user to press "continue" after each turn ends. When a
@@ -2100,13 +2161,43 @@ than pausing for user confirmation between subtasks. End the turn only
2100
2161
  when the task is done, you need clarification on intent, or you hit a real
2101
2162
  blocker. The user can interrupt or abort at any time; turn endings should
2102
2163
  mark meaningful checkpoints, not every completed substep.`;
2103
- function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true) {
2164
+ var CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI
2165
+
2166
+ You are running via the Claude Code CLI (not a direct API call). This affects context management:
2167
+
2168
+ - The \`compress\` tool is NOT available. Do not attempt to call it.
2169
+ - The \`distill\`, \`prune\`, and \`extract\` tools are NOT available.
2170
+ - Context window management is handled automatically by Claude CLI's own session history.
2171
+ - Ignore any system instructions that tell you to call \`compress\` \u2014 they are intended for direct API providers, not this environment.
2172
+ - DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`;
2173
+ function extractSystemMessages(prompt) {
2174
+ const out = [];
2175
+ for (const msg of prompt) {
2176
+ if (msg.role !== "system") continue;
2177
+ if (typeof msg.content === "string") {
2178
+ if (msg.content.trim()) out.push(msg.content.trim());
2179
+ } else if (Array.isArray(msg.content)) {
2180
+ for (const part of msg.content) {
2181
+ if (part?.type === "text" && typeof part.text === "string" && part.text.trim()) {
2182
+ out.push(part.text.trim());
2183
+ }
2184
+ }
2185
+ }
2186
+ }
2187
+ return out;
2188
+ }
2189
+ function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true, extraSystemContent = []) {
2104
2190
  const parts = [];
2191
+ parts.push(CLAUDE_CLI_CONTEXT_NOTE);
2192
+ for (const s of extraSystemContent) {
2193
+ if (s.trim()) parts.push(s.trim());
2194
+ }
2105
2195
  const configRoot = process.env.XDG_CONFIG_HOME ?? join5(homedir3(), ".config");
2106
2196
  const globalAgents = readPromptFileIfPresent(join5(configRoot, "opencode", "AGENTS.md"));
2107
2197
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
2108
2198
  if (globalAgents) parts.push(globalAgents);
2109
2199
  if (workspaceAgents && workspaceAgents !== globalAgents) parts.push(workspaceAgents);
2200
+ if (globalAgents || workspaceAgents) parts.push(AGENTS_MAINTENANCE_HINT);
2110
2201
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
2111
2202
  const content = parts.join("\n\n");
2112
2203
  if (!content) return void 0;
@@ -2296,22 +2387,22 @@ var ClaudeCodeLanguageModel = class {
2296
2387
  return null;
2297
2388
  }
2298
2389
  /**
2299
- * Opencode sets `x-session-affinity: <sessionID>` on LLM calls for
2300
- * third-party providers (packages/opencode/src/session/llm.ts). Use it so
2301
- * two chats in the same cwd+model get separate CLI processes instead of
2302
- * stomping on each other. Falls back to "default" when absent (older
2303
- * opencode, direct AI-SDK use, title synthesis paths, etc).
2390
+ * Resolve the session affinity token for this LLM call. Delegates to the
2391
+ * exported `resolveSessionAffinity` helper so the logic is unit-testable.
2392
+ * Priority:
2393
+ * 1. `x-session-affinity` request header (primary).
2394
+ * 2. `opencodeSessionID` in providerOptions (chat.params hook fallback
2395
+ * covers provider switches mid-session and title synthesis paths
2396
+ * where the header is absent).
2397
+ * 3. `"default"`.
2304
2398
  */
2305
2399
  sessionAffinity(options) {
2306
2400
  const headers = options?.headers;
2307
- if (!headers) return "default";
2308
- for (const key of Object.keys(headers)) {
2309
- if (key.toLowerCase() === "x-session-affinity") {
2310
- const v = headers[key];
2311
- if (typeof v === "string" && v.length > 0) return v;
2312
- }
2313
- }
2314
- return "default";
2401
+ return resolveSessionAffinity(
2402
+ headers,
2403
+ options.providerOptions,
2404
+ this.config.provider
2405
+ );
2315
2406
  }
2316
2407
  controlRequestBehaviorForTool(toolName) {
2317
2408
  const configured = this.config.controlRequestToolBehaviors;
@@ -2325,6 +2416,7 @@ var ClaudeCodeLanguageModel = class {
2325
2416
  }
2326
2417
  }
2327
2418
  }
2419
+ if (isAskUserQuestionTool(toolName)) return "deny";
2328
2420
  return this.config.controlRequestBehavior ?? "allow";
2329
2421
  }
2330
2422
  writeControlResponse(proc, requestId, response) {
@@ -2368,9 +2460,10 @@ var ClaudeCodeLanguageModel = class {
2368
2460
  toolName
2369
2461
  });
2370
2462
  } else {
2463
+ 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}`;
2371
2464
  this.writeControlResponse(proc, requestId, {
2372
2465
  behavior: "deny",
2373
- message: this.config.controlRequestDenyMessage ?? `Denied by opencode-claude-code policy for tool ${toolName}`,
2466
+ message: denyMessage,
2374
2467
  toolUseID: request.tool_use_id
2375
2468
  });
2376
2469
  log.info("control request auto-denied", {
@@ -2621,7 +2714,8 @@ var ClaudeCodeLanguageModel = class {
2621
2714
  ]);
2622
2715
  const systemPromptFile = buildAppendedSystemPrompt(
2623
2716
  cwd,
2624
- this.config.multiStepContinuation !== false
2717
+ this.config.multiStepContinuation !== false,
2718
+ extractSystemMessages(options.prompt)
2625
2719
  );
2626
2720
  const cliArgs = buildCliArgs({
2627
2721
  sessionKey: sk,
@@ -2661,8 +2755,15 @@ var ClaudeCodeLanguageModel = class {
2661
2755
  let thinkingText = "";
2662
2756
  let resultMeta = {};
2663
2757
  const toolCalls = [];
2758
+ const toolCallStreams = /* @__PURE__ */ new Map();
2664
2759
  let gotPartialEvents = false;
2665
2760
  const result = await new Promise((resolve3, reject) => {
2761
+ const cleanup = () => {
2762
+ try {
2763
+ if (!proc.killed && proc.exitCode === null) proc.kill();
2764
+ } catch {
2765
+ }
2766
+ };
2666
2767
  rl.on("line", (line) => {
2667
2768
  if (!line.trim()) return;
2668
2769
  try {
@@ -2688,14 +2789,9 @@ var ClaudeCodeLanguageModel = class {
2688
2789
  thinkingText += block.thinking;
2689
2790
  }
2690
2791
  if (block.type === "tool_use" && block.id && block.name) {
2691
- if (block.name === "AskUserQuestion" || block.name === "ask_user_question") {
2792
+ if (isAskUserQuestionTool(block.name)) {
2692
2793
  const parsedInput = block.input ?? {};
2693
- const question = parsedInput?.question || "Question?";
2694
- responseText += `
2695
-
2696
- _Asking: ${question}_
2697
-
2698
- `;
2794
+ responseText += formatAskUserQuestion(parsedInput);
2699
2795
  continue;
2700
2796
  }
2701
2797
  if (block.name === "ExitPlanMode") {
@@ -2718,30 +2814,41 @@ ${plan}
2718
2814
  }
2719
2815
  }
2720
2816
  }
2721
- if (msg.type === "content_block_start" && msg.content_block) {
2817
+ if (msg.type === "content_block_start" && msg.content_block && msg.index !== void 0) {
2722
2818
  if (msg.content_block.type === "tool_use" && msg.content_block.id && msg.content_block.name) {
2723
- toolCalls.push({
2819
+ toolCallStreams.set(msg.index, {
2724
2820
  id: msg.content_block.id,
2725
2821
  name: msg.content_block.name,
2726
- args: {}
2822
+ inputJson: ""
2727
2823
  });
2728
2824
  }
2729
2825
  }
2730
- if (msg.type === "content_block_delta" && msg.delta) {
2826
+ if (msg.type === "content_block_delta" && msg.delta && msg.index !== void 0) {
2731
2827
  if (msg.delta.type === "text_delta" && msg.delta.text) {
2732
2828
  responseText += msg.delta.text;
2733
2829
  }
2734
2830
  if (msg.delta.type === "thinking_delta" && msg.delta.thinking) {
2735
2831
  thinkingText += msg.delta.thinking;
2736
2832
  }
2737
- if (msg.delta.type === "input_json_delta" && msg.delta.partial_json && msg.index !== void 0) {
2738
- const tc = toolCalls[msg.index];
2739
- if (tc) {
2740
- try {
2741
- tc.args = JSON.parse(msg.delta.partial_json);
2742
- } catch {
2743
- }
2833
+ if (msg.delta.type === "input_json_delta" && msg.delta.partial_json) {
2834
+ const tc = toolCallStreams.get(msg.index);
2835
+ if (tc) tc.inputJson += msg.delta.partial_json;
2836
+ }
2837
+ }
2838
+ if (msg.type === "content_block_stop" && msg.index !== void 0) {
2839
+ const tc = toolCallStreams.get(msg.index);
2840
+ if (tc) {
2841
+ let args = {};
2842
+ try {
2843
+ args = tc.inputJson ? JSON.parse(tc.inputJson) : {};
2844
+ } catch (err) {
2845
+ log.warn("tool input JSON parse failed", {
2846
+ name: tc.name,
2847
+ error: String(err)
2848
+ });
2744
2849
  }
2850
+ toolCalls.push({ id: tc.id, name: tc.name, args });
2851
+ toolCallStreams.delete(msg.index);
2745
2852
  }
2746
2853
  }
2747
2854
  if (msg.type === "result") {
@@ -2757,6 +2864,7 @@ ${plan}
2757
2864
  durationMs: msg.duration_ms,
2758
2865
  usage: msg.usage
2759
2866
  };
2867
+ cleanup();
2760
2868
  resolve3({
2761
2869
  ...resultMeta,
2762
2870
  text: responseText,
@@ -2768,6 +2876,7 @@ ${plan}
2768
2876
  }
2769
2877
  });
2770
2878
  rl.on("close", () => {
2879
+ cleanup();
2771
2880
  resolve3({
2772
2881
  ...resultMeta,
2773
2882
  text: responseText,
@@ -2777,6 +2886,7 @@ ${plan}
2777
2886
  });
2778
2887
  proc.on("error", (err) => {
2779
2888
  log.error("process error", { error: err.message });
2889
+ cleanup();
2780
2890
  reject(err);
2781
2891
  });
2782
2892
  proc.stderr?.on("data", (data) => {
@@ -3033,7 +3143,8 @@ ${plan}
3033
3143
  );
3034
3144
  const systemPromptFile = activeProcess ? void 0 : buildAppendedSystemPrompt(
3035
3145
  cwd,
3036
- self.config.multiStepContinuation !== false
3146
+ self.config.multiStepContinuation !== false,
3147
+ extractSystemMessages(options.prompt)
3037
3148
  );
3038
3149
  cliArgs = buildCliArgs({
3039
3150
  sessionKey: sk,
@@ -3356,22 +3467,12 @@ ${plan}
3356
3467
  parsedInput = JSON.parse(tc.inputJson || "{}");
3357
3468
  } catch {
3358
3469
  }
3359
- if (tc.name === "AskUserQuestion" || tc.name === "ask_user_question") {
3360
- let question = "Question?";
3361
- if (parsedInput?.questions && Array.isArray(parsedInput.questions) && parsedInput.questions.length > 0) {
3362
- question = parsedInput.questions[0].question || parsedInput.questions[0].text || "Question?";
3363
- } else {
3364
- question = parsedInput?.question || parsedInput?.text || "Question?";
3365
- }
3470
+ if (isAskUserQuestionTool(tc.name)) {
3366
3471
  const askId = startTextBlock();
3367
3472
  controller.enqueue({
3368
3473
  type: "text-delta",
3369
3474
  id: askId,
3370
- delta: `
3371
-
3372
- _Asking: ${question}_
3373
-
3374
- `
3475
+ delta: formatAskUserQuestion(parsedInput)
3375
3476
  });
3376
3477
  endTextBlock();
3377
3478
  } else if (tc.name === "ExitPlanMode") {
@@ -3525,23 +3626,12 @@ ${plan}
3525
3626
  name: block.name,
3526
3627
  input: parsedInput
3527
3628
  });
3528
- if (block.name === "AskUserQuestion" || block.name === "ask_user_question") {
3529
- let question = "Question?";
3530
- if (parsedInput?.questions && Array.isArray(parsedInput.questions) && parsedInput.questions.length > 0) {
3531
- const q = parsedInput.questions[0];
3532
- question = q.question || q.text || "Question?";
3533
- } else {
3534
- question = parsedInput?.question || parsedInput?.text || "Question?";
3535
- }
3629
+ if (isAskUserQuestionTool(block.name)) {
3536
3630
  const askId = startTextBlock();
3537
3631
  controller.enqueue({
3538
3632
  type: "text-delta",
3539
3633
  id: askId,
3540
- delta: `
3541
-
3542
- _Asking: ${question}_
3543
-
3544
- `
3634
+ delta: formatAskUserQuestion(parsedInput)
3545
3635
  });
3546
3636
  endTextBlock();
3547
3637
  } else if (block.name === "ExitPlanMode") {
@@ -4646,11 +4736,16 @@ var server = async (input) => {
4646
4736
  });
4647
4737
  if (typeof providerID !== "string") return;
4648
4738
  if (providerID !== PROVIDER_ID2 && !providerID.startsWith(`${PROVIDER_ID2}-`)) return;
4739
+ if (typeof input2.sessionID === "string" && input2.sessionID.length > 0) {
4740
+ output.options ??= {};
4741
+ output.options.opencodeSessionID = input2.sessionID;
4742
+ }
4649
4743
  if (!input2.agent) return;
4650
4744
  output.options ??= {};
4651
4745
  output.options.opencodeAgent = input2.agent;
4652
4746
  log.debug("chat.params tagged providerOptions", {
4653
4747
  agent: input2.agent,
4748
+ sessionID: input2.sessionID,
4654
4749
  providerID
4655
4750
  });
4656
4751
  }