opencode-copilot-account-switcher 0.2.7 → 0.2.8

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.
@@ -13,7 +13,7 @@ const RETRYABLE_MESSAGES = [
13
13
  "unable to verify the first certificate",
14
14
  "self-signed certificate in certificate chain",
15
15
  ];
16
- const MAX_INPUT_ID_REPAIR_ATTEMPTS = 3;
16
+ const INPUT_ID_REPAIR_HARD_LIMIT = 64;
17
17
  const INTERNAL_SESSION_HEADER = "x-opencode-session-id";
18
18
  const defaultDebugLogFile = (() => {
19
19
  const tmp = process.env.TEMP || process.env.TMP || "/tmp";
@@ -70,6 +70,9 @@ function parseInputIdTooLongDetails(text) {
70
70
  function buildIdPreview(id) {
71
71
  return `${id.slice(0, 12)}...`;
72
72
  }
73
+ function buildMessagePreview(message) {
74
+ return message.slice(0, 160);
75
+ }
73
76
  function getPayloadCandidates(payload) {
74
77
  const input = payload.input;
75
78
  if (!Array.isArray(input))
@@ -89,33 +92,52 @@ function hasLongInputIds(payload) {
89
92
  return false;
90
93
  return input.some((item) => typeof item?.id === "string" && (item.id?.length ?? 0) > 64);
91
94
  }
92
- function getTargetedLongInputId(payload, reportedLength) {
95
+ function countLongInputIdCandidates(payload) {
96
+ const input = payload?.input;
97
+ if (!Array.isArray(input))
98
+ return 0;
99
+ return input.filter((item) => typeof item?.id === "string" && (item.id?.length ?? 0) > 64)
100
+ .length;
101
+ }
102
+ function collectLongInputIdCandidates(payload) {
93
103
  const input = payload.input;
94
104
  if (!Array.isArray(input))
95
- return undefined;
96
- const matches = input.filter((item) => typeof item?.id === "string" && (item.id?.length ?? 0) > 64);
105
+ return [];
106
+ return input.flatMap((item, payloadIndex) => {
107
+ const id = item?.id;
108
+ if (typeof id !== "string" || id.length <= 64)
109
+ return [];
110
+ return [{ item: item, payloadIndex, idLength: id.length }];
111
+ });
112
+ }
113
+ function pickCandidateByServerIndexHint(candidates, serverReportedIndex) {
114
+ const hintedPayloadIndex = serverReportedIndex - 1;
115
+ return candidates
116
+ .filter((candidate) => candidate.payloadIndex >= hintedPayloadIndex)
117
+ .sort((left, right) => left.payloadIndex - right.payloadIndex)[0];
118
+ }
119
+ function getTargetedLongInputId(payload, serverReportedIndex, reportedLength) {
120
+ const matches = collectLongInputIdCandidates(payload);
97
121
  if (matches.length === 0)
98
122
  return undefined;
99
123
  const lengthMatches = reportedLength
100
- ? matches.filter((item) => String(item.id ?? "").length === reportedLength)
124
+ ? matches.filter((item) => item.idLength === reportedLength)
101
125
  : matches;
102
126
  if (lengthMatches.length === 1)
103
- return lengthMatches[0];
104
- if (lengthMatches.length > 1)
105
- return lengthMatches[0];
127
+ return lengthMatches[0].item;
106
128
  if (matches.length === 1)
107
- return matches[0];
108
- return matches.reduce((best, item) => {
109
- const bestLength = String(best.id ?? "").length;
110
- const itemLength = String(item.id ?? "").length;
111
- return itemLength > bestLength ? item : best;
112
- }, matches[0]);
129
+ return matches[0].item;
130
+ const narrowedCandidates = lengthMatches.length > 0 ? lengthMatches : matches;
131
+ if (typeof serverReportedIndex === "number") {
132
+ return pickCandidateByServerIndexHint(narrowedCandidates, serverReportedIndex)?.item;
133
+ }
134
+ return undefined;
113
135
  }
114
- function stripTargetedLongInputId(payload, reportedLength) {
136
+ function stripTargetedLongInputId(payload, serverReportedIndex, reportedLength) {
115
137
  const input = payload.input;
116
138
  if (!Array.isArray(input))
117
139
  return payload;
118
- const target = getTargetedLongInputId(payload, reportedLength);
140
+ const target = getTargetedLongInputId(payload, serverReportedIndex, reportedLength);
119
141
  if (!target)
120
142
  return payload;
121
143
  let changed = false;
@@ -177,8 +199,8 @@ function getHeader(request, init, name) {
177
199
  return request.headers.get(name) ?? undefined;
178
200
  return undefined;
179
201
  }
180
- function getTargetedInputId(payload, reportedLength) {
181
- const target = getTargetedLongInputId(payload, reportedLength);
202
+ function getTargetedInputId(payload, serverReportedIndex, reportedLength) {
203
+ const target = getTargetedLongInputId(payload, serverReportedIndex, reportedLength);
182
204
  const id = target?.id;
183
205
  if (typeof id !== "string")
184
206
  return undefined;
@@ -247,30 +269,61 @@ async function repairSessionPart(sessionID, failingId, ctx) {
247
269
  body: JSON.stringify(body),
248
270
  };
249
271
  if (ctx?.patchPart) {
250
- await ctx.patchPart({ url: url.href, init });
272
+ try {
273
+ await ctx.patchPart({ url: url.href, init });
274
+ debugLog("input-id retry session repair", {
275
+ partID: match.partID,
276
+ messageID: match.messageID,
277
+ sessionID,
278
+ });
279
+ return true;
280
+ }
281
+ catch (error) {
282
+ debugLog("input-id retry session repair failed", {
283
+ partID: match.partID,
284
+ messageID: match.messageID,
285
+ sessionID,
286
+ error: String(error instanceof Error ? error.message : error),
287
+ });
288
+ return false;
289
+ }
290
+ }
291
+ try {
292
+ const response = await fetch(url, init);
251
293
  debugLog("input-id retry session repair", {
252
294
  partID: match.partID,
253
295
  messageID: match.messageID,
254
296
  sessionID,
297
+ ok: response.ok,
255
298
  });
256
- return true;
299
+ if (!response.ok) {
300
+ debugLog("input-id retry session repair failed", {
301
+ partID: match.partID,
302
+ messageID: match.messageID,
303
+ sessionID,
304
+ status: response.status,
305
+ });
306
+ }
307
+ return response.ok;
308
+ }
309
+ catch (error) {
310
+ debugLog("input-id retry session repair failed", {
311
+ partID: match.partID,
312
+ messageID: match.messageID,
313
+ sessionID,
314
+ error: String(error instanceof Error ? error.message : error),
315
+ });
316
+ return false;
257
317
  }
258
- const response = await fetch(url, init);
259
- debugLog("input-id retry session repair", {
260
- partID: match.partID,
261
- messageID: match.messageID,
262
- sessionID,
263
- ok: response.ok,
264
- });
265
- return response.ok;
266
318
  }
267
319
  async function maybeRetryInputIdTooLong(request, init, response, baseFetch, ctx, sessionID) {
268
- if (response.status !== 400)
269
- return { response, retried: false, nextInit: init };
320
+ if (response.status !== 400) {
321
+ return { response, retried: false, nextInit: init, retryState: undefined };
322
+ }
270
323
  const requestPayload = parseJsonBody(init);
271
324
  if (!requestPayload || !hasLongInputIds(requestPayload)) {
272
325
  debugLog("skip input-id retry: request has no long ids");
273
- return { response, retried: false, nextInit: init };
326
+ return { response, retried: false, nextInit: init, retryState: undefined };
274
327
  }
275
328
  debugLog("input-id retry candidate", {
276
329
  status: response.status,
@@ -282,7 +335,7 @@ async function maybeRetryInputIdTooLong(request, init, response, baseFetch, ctx,
282
335
  .catch(() => "");
283
336
  if (!responseText) {
284
337
  debugLog("skip input-id retry: empty response body");
285
- return { response, retried: false, nextInit: init };
338
+ return { response, retried: false, nextInit: init, retryState: undefined };
286
339
  }
287
340
  let parsed = parseInputIdTooLongDetails(responseText);
288
341
  let matched = parsed.matched;
@@ -307,20 +360,21 @@ async function maybeRetryInputIdTooLong(request, init, response, baseFetch, ctx,
307
360
  serverReportedIndex: parsed.serverReportedIndex,
308
361
  reportedLength: parsed.reportedLength,
309
362
  });
310
- if (!matched)
311
- return { response, retried: false, nextInit: init };
363
+ if (!matched) {
364
+ return { response, retried: false, nextInit: init, retryState: undefined };
365
+ }
312
366
  if (parsed.serverReportedIndex === undefined) {
313
367
  debugLog("skip input-id retry: missing server input index", {
314
368
  reportedLength: parsed.reportedLength,
315
369
  });
316
- return { response, retried: false, nextInit: init };
370
+ return { response, retried: false, nextInit: init, retryState: undefined };
317
371
  }
318
372
  const payloadCandidates = getPayloadCandidates(requestPayload);
319
373
  debugLog("input-id retry payload candidates", {
320
374
  serverReportedIndex: parsed.serverReportedIndex,
321
375
  candidates: payloadCandidates,
322
376
  });
323
- const failingId = getTargetedInputId(requestPayload, parsed.reportedLength);
377
+ const failingId = getTargetedInputId(requestPayload, parsed.serverReportedIndex, parsed.reportedLength);
324
378
  const targetedPayload = payloadCandidates.find((item) => item.idLength === parsed.reportedLength) ?? payloadCandidates[0];
325
379
  if (targetedPayload) {
326
380
  debugLog("input-id retry payload target", {
@@ -333,10 +387,20 @@ async function maybeRetryInputIdTooLong(request, init, response, baseFetch, ctx,
333
387
  if (sessionID && failingId) {
334
388
  await repairSessionPart(sessionID, failingId, ctx).catch(() => false);
335
389
  }
336
- const sanitized = stripTargetedLongInputId(requestPayload, parsed.reportedLength);
390
+ const sanitized = stripTargetedLongInputId(requestPayload, parsed.serverReportedIndex, parsed.reportedLength);
337
391
  if (sanitized === requestPayload) {
338
392
  debugLog("skip input-id retry: sanitize made no changes");
339
- return { response, retried: false, nextInit: init };
393
+ return {
394
+ response,
395
+ retried: false,
396
+ nextInit: init,
397
+ retryState: {
398
+ previousServerReportedIndex: parsed.serverReportedIndex,
399
+ previousErrorMessagePreview: buildMessagePreview(responseText),
400
+ remainingLongIdCandidatesBefore: countLongInputIdCandidates(requestPayload),
401
+ remainingLongIdCandidatesAfter: countLongInputIdCandidates(requestPayload),
402
+ },
403
+ };
340
404
  }
341
405
  debugLog("input-id retry triggered", {
342
406
  removedLongIds: true,
@@ -344,11 +408,17 @@ async function maybeRetryInputIdTooLong(request, init, response, baseFetch, ctx,
344
408
  });
345
409
  const nextInit = buildRetryInit(init, sanitized);
346
410
  const retried = await baseFetch(request, nextInit);
411
+ const retryState = {
412
+ previousServerReportedIndex: parsed.serverReportedIndex,
413
+ previousErrorMessagePreview: buildMessagePreview(responseText),
414
+ remainingLongIdCandidatesBefore: countLongInputIdCandidates(requestPayload),
415
+ remainingLongIdCandidatesAfter: countLongInputIdCandidates(parseJsonBody(nextInit)),
416
+ };
347
417
  debugLog("input-id retry response", {
348
418
  status: retried.status,
349
419
  contentType: retried.headers.get("content-type") ?? undefined,
350
420
  });
351
- return { response: retried, retried: true, nextInit };
421
+ return { response: retried, retried: true, nextInit, retryState };
352
422
  }
353
423
  function toRetryableSystemError(error) {
354
424
  const base = error instanceof Error ? error : new Error(String(error));
@@ -370,6 +440,37 @@ function isCopilotUrl(request) {
370
440
  return false;
371
441
  }
372
442
  }
443
+ async function getInputIdRetryErrorDetails(response) {
444
+ if (response.status !== 400)
445
+ return undefined;
446
+ const responseText = await response
447
+ .clone()
448
+ .text()
449
+ .catch(() => "");
450
+ if (!responseText)
451
+ return undefined;
452
+ let parsed = parseInputIdTooLongDetails(responseText);
453
+ let matched = parsed.matched;
454
+ let message = responseText;
455
+ if (!matched) {
456
+ try {
457
+ const bodyPayload = JSON.parse(responseText);
458
+ const error = bodyPayload.error;
459
+ message = String(error?.message ?? "");
460
+ parsed = parseInputIdTooLongDetails(message);
461
+ matched = parsed.matched || isInputIdTooLongErrorBody(bodyPayload);
462
+ }
463
+ catch {
464
+ matched = false;
465
+ }
466
+ }
467
+ if (!matched)
468
+ return undefined;
469
+ return {
470
+ serverReportedIndex: parsed.serverReportedIndex,
471
+ errorMessagePreview: buildMessagePreview(message),
472
+ };
473
+ }
373
474
  function withStreamDebugLogs(response, request) {
374
475
  if (!isDebugEnabled())
375
476
  return response;
@@ -443,12 +544,46 @@ export function createCopilotRetryingFetch(baseFetch, options) {
443
544
  if (isCopilotUrl(safeRequest)) {
444
545
  let currentResponse = response;
445
546
  let currentInit = effectiveInit;
446
- for (let attempt = 0; attempt < MAX_INPUT_ID_REPAIR_ATTEMPTS; attempt += 1) {
547
+ let attempts = 0;
548
+ while (attempts < INPUT_ID_REPAIR_HARD_LIMIT) {
549
+ const remainingCandidates = countLongInputIdCandidates(parseJsonBody(currentInit));
550
+ if (remainingCandidates === 0)
551
+ break;
447
552
  const result = await maybeRetryInputIdTooLong(safeRequest, currentInit, currentResponse, baseFetch, options, sessionID);
448
553
  currentResponse = result.response;
449
554
  currentInit = result.nextInit;
555
+ if (result.retryState) {
556
+ const currentError = await getInputIdRetryErrorDetails(currentResponse);
557
+ let stopReason;
558
+ if (result.retryState.remainingLongIdCandidatesAfter >= result.retryState.remainingLongIdCandidatesBefore) {
559
+ stopReason = "remaining-candidates-not-reduced";
560
+ }
561
+ else if (currentError) {
562
+ const serverIndexChanged = result.retryState.previousServerReportedIndex !== currentError.serverReportedIndex;
563
+ const errorMessageChanged = result.retryState.previousErrorMessagePreview !== currentError.errorMessagePreview;
564
+ if (!serverIndexChanged && !errorMessageChanged) {
565
+ stopReason = "server-error-unchanged";
566
+ }
567
+ }
568
+ if (currentError || stopReason) {
569
+ debugLog("input-id retry progress", {
570
+ attempt: attempts + 1,
571
+ previousServerReportedIndex: result.retryState.previousServerReportedIndex,
572
+ currentServerReportedIndex: currentError?.serverReportedIndex,
573
+ serverIndexChanged: result.retryState.previousServerReportedIndex !== currentError?.serverReportedIndex,
574
+ previousErrorMessagePreview: result.retryState.previousErrorMessagePreview,
575
+ currentErrorMessagePreview: currentError?.errorMessagePreview,
576
+ remainingLongIdCandidatesBefore: result.retryState.remainingLongIdCandidatesBefore,
577
+ remainingLongIdCandidatesAfter: result.retryState.remainingLongIdCandidatesAfter,
578
+ stopReason,
579
+ });
580
+ }
581
+ if (stopReason)
582
+ break;
583
+ }
450
584
  if (!result.retried)
451
585
  break;
586
+ attempts += 1;
452
587
  }
453
588
  return withStreamDebugLogs(currentResponse, safeRequest);
454
589
  }
@@ -1,6 +1,6 @@
1
1
  import type { Hooks } from "@opencode-ai/plugin";
2
2
  import { type StoreFile } from "./store.js";
3
- export declare const LOOP_SAFETY_POLICY = "Guided Loop Safety Policy\n- Continue working on any remaining non-blocked task before stopping to report or wait for more instructions.\n- If you are not fully blocked, do not stop just because you feel ready to pause; finish the work that can still be done safely.\n- MANDATORY: When the question tool is available and permitted in the current session, ALL user-facing reports MUST be delivered through the question tool.\n- MANDATORY: Treat progress updates, status reports, summaries, explanations, completion notes, and next-step choices as user-facing reports.\n- MANDATORY: In this workflow, the question tool is both the question channel and the report channel for user-visible communication.\n- MANDATORY: Do not reinterpret \"keep working unless blocked\" as permission to send direct assistant text. If you need to report, report through the question tool.\n- MANDATORY: Do not reinterpret \"don't ask unnecessary questions\" as permission to skip the question tool. Use fewer question batches, not a different reporting channel.\n- MANDATORY: If the user says a report was sent the wrong way, first check whether you used the wrong channel before analyzing verbosity, timing, or wording.\n- MANDATORY: When reflecting on mistakes, check report-channel violations before any secondary critique.\n- MANDATORY: Do not turn a pure report into a preference question, permission question, or route-selection question just to satisfy the question tool requirement.\n- MANDATORY: When no user decision is needed, use the question tool as a report container with the smallest sensible option set.\n- NO EXCEPTIONS: Do not switch to direct assistant text just because the report is long, detailed, complex, or contains multiple related items.\n- The question tool is considered available and permitted when it appears in the active tool list and the current session has not denied its use.\n- Direct assistant text is allowed only when the question tool is unavailable, denied, or absent from the current session.\n- If a report would otherwise be sent as direct text, convert it into one question batch or a short sequence of question batches instead.\n- IMPORTANT: A report-mode violation usually means the wrong tool was used, not that the report was too long.\n- IMPORTANT: Do not reinterpret a tool-usage violation as a verbosity problem.\n- Long reports are allowed. Use pagination or sequential question batches when needed; do not fall back to direct assistant text.\n- A single question tool call may contain multiple well-grouped questions. Prefer that over multiple small interruptions when it keeps the report clear.\n- Group related items into explicit batches such as current progress, key findings, decisions, and next-step choices.\n- Present the highest-priority information first and defer secondary details to later question batches when needed.\n- Even when no explicit decision is required, use brief question-tool status updates instead of direct assistant text whenever the tool is available.\n- Avoid unnecessary question frequency; combine small related updates when a single question call can cover them clearly.\n- MANDATORY: After any successful question-tool report, immediately choose between (a) continue unfinished non-blocked work, or (b) issue a question-tool wait-for-instruction message when no such work remains.\n- MANDATORY: Never send assistant text as a post-report fallback in either branch.\n- MANDATORY: If a post-report branch has no content, suppress assistant output and re-enter question-tool flow.\n- When no further action can be taken safely and no non-blocked work remains, use the question tool to ask for the next task or clarification instead of ending with direct assistant text.\n- When the user says \"stop\", do not send assistant text to acknowledge the stop. Use the question tool only if more user-visible communication is still required by policy.\n- Dispatching task or subagent work is expensive and should be avoided unless it materially improves the result.\n- Materially improves the result means clearly beneficial cases such as parallel analysis of independent areas; it does not include routine local searches, small file reads, or straightforward edits.\n- If task or subagent delegation is used, keep the number minimal and explain the reason briefly through the question tool when available.";
3
+ export declare const LOOP_SAFETY_POLICY = "Guided Loop Safety Policy\n- Continue working on any remaining non-blocked task before stopping to report or wait for more instructions.\n- If you are not fully blocked, do not stop just because you feel ready to pause; finish the work that can still be done safely.\n- MANDATORY: When the question tool is available and permitted in the current session, ALL user-facing reports MUST be delivered through the question tool.\n- MANDATORY: Treat progress updates, status reports, summaries, explanations, completion notes, and next-step choices as user-facing reports.\n- MANDATORY: In this workflow, the question tool is both the question channel and the report channel for user-visible communication.\n- MANDATORY: Do not reinterpret \"keep working unless blocked\" as permission to send direct assistant text. If you need to report, report through the question tool.\n- MANDATORY: Do not reinterpret \"don't ask unnecessary questions\" as permission to skip the question tool. Use fewer question batches, not a different reporting channel.\n- MANDATORY: If the user says a report was sent the wrong way, first check whether you used the wrong channel before analyzing verbosity, timing, or wording.\n- MANDATORY: When reflecting on report-delivery or tool-usage mistakes, first check report-channel violations before any secondary critique.\n- MANDATORY: Do not reinterpret those report-channel violations as a problem with the report policy itself.\n- MANDATORY: Do not turn a pure report into a preference question, permission question, or route-selection question just to satisfy the question tool requirement.\n- MANDATORY: When no user decision is needed, use the question tool as a report container with the smallest sensible option set.\n- NO EXCEPTIONS: Do not switch to direct assistant text just because the report is long, detailed, complex, or contains multiple related items.\n- The question tool is considered available and permitted when it appears in the active tool list and the current session has not denied its use.\n- Direct assistant text is allowed only when the question tool is unavailable, denied, or absent from the current session.\n- If a report would otherwise be sent as direct text, convert it into one question batch or a short sequence of question batches instead.\n- IMPORTANT: A report-mode violation usually means the wrong tool was used, not that the report was too long.\n- IMPORTANT: Do not reinterpret a tool-usage violation as a verbosity problem.\n- Long reports are allowed. Use pagination or sequential question batches when needed; do not fall back to direct assistant text.\n- A single question tool call may contain multiple well-grouped questions. Prefer that over multiple small interruptions when it keeps the report clear.\n- Group related items into explicit batches such as current progress, key findings, decisions, and next-step choices.\n- Present the highest-priority information first and defer secondary details to later question batches when needed.\n- Even when no explicit decision is required, use brief question-tool status updates instead of direct assistant text whenever the tool is available.\n- Avoid unnecessary question frequency; combine small related updates when a single question call can cover them clearly.\n- MANDATORY: After any successful question-tool report, immediately choose between (a) continue unfinished non-blocked work, or (b) issue a question-tool wait-for-instruction message when no such work remains.\n- MANDATORY: Never send assistant text as a post-report fallback in either branch.\n- MANDATORY: If a post-report branch has no content, suppress assistant output and re-enter question-tool flow.\n- MANDATORY: When idle or waiting for the next task, continue using the question tool to maintain user control of the session.\n- MANDATORY: Repeated waiting for the next task is not a reason to stop using the question tool.\n- When no further action can be taken safely and no non-blocked work remains, use the question tool to ask for the next task or clarification instead of ending with direct assistant text.\n- When the user says \"stop\", do not send assistant text to acknowledge the stop. Use the question tool only if more user-visible communication is still required by policy.\n- Dispatching task or subagent work is expensive and should be avoided unless it materially improves the result.\n- Materially improves the result means clearly beneficial cases such as parallel analysis of independent areas; it does not include routine local searches, small file reads, or straightforward edits.\n- If task or subagent delegation is used, keep the number minimal and explain the reason briefly through the question tool when available.";
4
4
  export type ExperimentalChatSystemTransformHook = (input: {
5
5
  sessionID: string;
6
6
  model: {
@@ -8,7 +8,8 @@ export const LOOP_SAFETY_POLICY = `Guided Loop Safety Policy
8
8
  - MANDATORY: Do not reinterpret "keep working unless blocked" as permission to send direct assistant text. If you need to report, report through the question tool.
9
9
  - MANDATORY: Do not reinterpret "don't ask unnecessary questions" as permission to skip the question tool. Use fewer question batches, not a different reporting channel.
10
10
  - MANDATORY: If the user says a report was sent the wrong way, first check whether you used the wrong channel before analyzing verbosity, timing, or wording.
11
- - MANDATORY: When reflecting on mistakes, check report-channel violations before any secondary critique.
11
+ - MANDATORY: When reflecting on report-delivery or tool-usage mistakes, first check report-channel violations before any secondary critique.
12
+ - MANDATORY: Do not reinterpret those report-channel violations as a problem with the report policy itself.
12
13
  - MANDATORY: Do not turn a pure report into a preference question, permission question, or route-selection question just to satisfy the question tool requirement.
13
14
  - MANDATORY: When no user decision is needed, use the question tool as a report container with the smallest sensible option set.
14
15
  - NO EXCEPTIONS: Do not switch to direct assistant text just because the report is long, detailed, complex, or contains multiple related items.
@@ -26,6 +27,8 @@ export const LOOP_SAFETY_POLICY = `Guided Loop Safety Policy
26
27
  - MANDATORY: After any successful question-tool report, immediately choose between (a) continue unfinished non-blocked work, or (b) issue a question-tool wait-for-instruction message when no such work remains.
27
28
  - MANDATORY: Never send assistant text as a post-report fallback in either branch.
28
29
  - MANDATORY: If a post-report branch has no content, suppress assistant output and re-enter question-tool flow.
30
+ - MANDATORY: When idle or waiting for the next task, continue using the question tool to maintain user control of the session.
31
+ - MANDATORY: Repeated waiting for the next task is not a reason to stop using the question tool.
29
32
  - When no further action can be taken safely and no non-blocked work remains, use the question tool to ask for the next task or clarification instead of ending with direct assistant text.
30
33
  - When the user says "stop", do not send assistant text to acknowledge the stop. Use the question tool only if more user-visible communication is still required by policy.
31
34
  - Dispatching task or subagent work is expensive and should be avoided unless it materially improves the result.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-copilot-account-switcher",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "GitHub Copilot account switcher plugin for OpenCode",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",