@sayknow-cli/coding-agent 0.5.1 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +185 -14
  2. package/dist/types/cli/update-cli.d.ts +8 -1
  3. package/dist/types/commands/session.d.ts +7 -0
  4. package/dist/types/config/atomic-yaml-patch.d.ts +20 -1
  5. package/dist/types/config/keybindings.d.ts +10 -0
  6. package/dist/types/config/model-registry.d.ts +1 -1
  7. package/dist/types/config/models-config-schema.d.ts +4 -0
  8. package/dist/types/config/settings-schema.d.ts +9 -0
  9. package/dist/types/config/telegram-autostart.d.ts +9 -1
  10. package/dist/types/dap/client.d.ts +1 -0
  11. package/dist/types/lsp/client.d.ts +5 -0
  12. package/dist/types/modes/components/pet-capability.d.ts +8 -7
  13. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  14. package/dist/types/modes/components/sayknow-pet-widget.d.ts +1 -1
  15. package/dist/types/modes/components/welcome.d.ts +1 -0
  16. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  17. package/dist/types/modes/interactive-mode.d.ts +13 -1
  18. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +7 -0
  19. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -0
  20. package/dist/types/modes/types.d.ts +7 -1
  21. package/dist/types/runtime-mcp/transports/stdio.d.ts +2 -0
  22. package/dist/types/session/agent-session.d.ts +9 -0
  23. package/dist/types/session/internal/managed-session-scope.d.ts +3 -1
  24. package/dist/types/session/internal/managed-session-storage.d.ts +2 -0
  25. package/dist/types/session/session-manager.d.ts +9 -0
  26. package/dist/types/session/session-storage.d.ts +17 -0
  27. package/dist/types/session-import/claude.d.ts +26 -0
  28. package/dist/types/session-import/codex.d.ts +22 -0
  29. package/dist/types/session-import/command.d.ts +20 -0
  30. package/dist/types/session-import/detect.d.ts +22 -0
  31. package/dist/types/session-import/index.d.ts +7 -0
  32. package/dist/types/session-import/redact.d.ts +23 -0
  33. package/dist/types/session-import/service.d.ts +28 -0
  34. package/dist/types/session-import/types.d.ts +125 -0
  35. package/dist/types/skc-runtime/boot-generation.d.ts +59 -0
  36. package/dist/types/skc-runtime/launch-tmux.d.ts +10 -2
  37. package/dist/types/skc-runtime/launch-worktree.d.ts +17 -1
  38. package/dist/types/skc-runtime/session-restore-runtime.d.ts +41 -0
  39. package/dist/types/skc-runtime/session-restore.d.ts +99 -0
  40. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +160 -0
  41. package/dist/types/skc-runtime/tmux-sessions.d.ts +26 -1
  42. package/dist/types/slash-commands/builtin-registry.d.ts +4 -0
  43. package/dist/types/slash-commands/types.d.ts +5 -0
  44. package/dist/types/tools/ask.d.ts +169 -4
  45. package/package.json +16 -7
  46. package/scripts/generate-sdk-operation-inventory.ts +5 -6
  47. package/src/cli/config-cli.ts +23 -8
  48. package/src/cli/update-cli.ts +265 -21
  49. package/src/commands/session.ts +88 -2
  50. package/src/config/atomic-yaml-patch.ts +167 -30
  51. package/src/config/keybindings.ts +10 -0
  52. package/src/config/model-profiles.ts +69 -1
  53. package/src/config/model-registry.ts +72 -38
  54. package/src/config/models-config-schema.ts +1 -1
  55. package/src/config/settings-schema.ts +9 -0
  56. package/src/config/settings.ts +13 -2
  57. package/src/config/telegram-autostart.ts +11 -4
  58. package/src/dap/client.ts +78 -30
  59. package/src/defaults/skc/skills/deep-interview/SKILL.md +29 -3
  60. package/src/internal-urls/docs-index.generated.ts +5 -4
  61. package/src/lsp/client.ts +17 -29
  62. package/src/main.ts +1 -1
  63. package/src/modes/action-registry.ts +2 -0
  64. package/src/modes/components/pet-capability.ts +22 -13
  65. package/src/modes/components/pet-selector.ts +1 -1
  66. package/src/modes/components/sayknow-pet-widget.ts +41 -7
  67. package/src/modes/components/welcome.ts +5 -0
  68. package/src/modes/controllers/event-controller.ts +1 -1
  69. package/src/modes/controllers/goal-mode-controller.ts +7 -2
  70. package/src/modes/controllers/input-controller.ts +71 -6
  71. package/src/modes/controllers/plan-mode-controller.ts +8 -1
  72. package/src/modes/controllers/runtime-mcp-command-controller.ts +14 -0
  73. package/src/modes/controllers/selector-controller.ts +47 -19
  74. package/src/modes/interactive-mode.ts +54 -4
  75. package/src/modes/shared/agent-wire/unattended-session.ts +40 -9
  76. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -0
  77. package/src/modes/types.ts +5 -1
  78. package/src/notifications/lifecycle-control-runtime.ts +258 -179
  79. package/src/prompts/system/eager-todo.md +2 -0
  80. package/src/prompts/system/plan-mode-approved.md +1 -1
  81. package/src/prompts/system/system-prompt.md +4 -2
  82. package/src/runtime-mcp/transports/stdio.ts +96 -33
  83. package/src/sdk/broker/lifecycle.ts +6 -5
  84. package/src/sdk/bus/lifecycle-control-runtime.ts +189 -110
  85. package/src/sdk/protocol/operation-inventory.generated.json +33 -0
  86. package/src/sdk/session.ts +25 -18
  87. package/src/session/agent-session.ts +120 -25
  88. package/src/session/internal/managed-session-scope.ts +41 -6
  89. package/src/session/internal/managed-session-storage.ts +14 -0
  90. package/src/session/session-manager.ts +113 -11
  91. package/src/session/session-storage.ts +70 -0
  92. package/src/session-import/claude.ts +382 -0
  93. package/src/session-import/codex.ts +458 -0
  94. package/src/session-import/command.ts +61 -0
  95. package/src/session-import/detect.ts +133 -0
  96. package/src/session-import/index.ts +27 -0
  97. package/src/session-import/redact.ts +125 -0
  98. package/src/session-import/service.ts +749 -0
  99. package/src/session-import/types.ts +163 -0
  100. package/src/skc-runtime/boot-generation.ts +172 -0
  101. package/src/skc-runtime/launch-tmux.ts +219 -41
  102. package/src/skc-runtime/launch-worktree.ts +277 -4
  103. package/src/skc-runtime/session-restore-runtime.ts +120 -0
  104. package/src/skc-runtime/session-restore.ts +296 -0
  105. package/src/skc-runtime/session-state-sidecar.ts +41 -0
  106. package/src/skc-runtime/tmux-owner-isolation.ts +665 -0
  107. package/src/skc-runtime/tmux-sessions.ts +284 -108
  108. package/src/slash-commands/acp-builtins.ts +5 -1
  109. package/src/slash-commands/builtin-registry.ts +109 -5
  110. package/src/slash-commands/types.ts +5 -0
  111. package/src/tools/ask.ts +188 -10
  112. package/src/tools/eval.ts +2 -2
  113. package/src/export/html/template.css +0 -1060
  114. package/src/export/html/template.html +0 -47
  115. package/src/export/html/template.js +0 -2348
  116. package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
  117. package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
  118. package/vendor/insane-search/engine/tests/test_u1.py +0 -200
  119. package/vendor/insane-search/engine/tests/test_u4.py +0 -131
  120. package/vendor/insane-search/engine/tests/test_u5.py +0 -163
  121. package/vendor/insane-search/engine/tests/test_u7.py +0 -124
  122. package/vendor/insane-search/engine/tests/test_u8.py +0 -216
@@ -40,6 +40,7 @@ import {
40
40
  theme,
41
41
  } from "../../modes/theme/theme";
42
42
  import type { InteractiveModeContext, OAuthSelectorOptions } from "../../modes/types";
43
+ import { acquireResumeProgressLease } from "../../modes/utils/ui-helpers";
43
44
  import { getNotificationConfig, isTelegramConfigured, maskToken } from "../../sdk/bus/config";
44
45
  import {
45
46
  clearTelegramActivationMarker,
@@ -2052,26 +2053,40 @@ export class SelectorController {
2052
2053
  return true;
2053
2054
  }
2054
2055
 
2055
- async handleResumeSession(sessionPath: string): Promise<void> {
2056
- const previousSessionId = this.ctx.sessionManager.getSessionId();
2057
- this.#clearTransientSessionUi();
2058
- const migrationPolicy =
2059
- this.ctx.settings?.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
2060
- const writableSessionPath = await this.ctx.sessionManager.prepareManagedCandidateForWrite(
2061
- sessionPath,
2062
- migrationPolicy,
2063
- );
2064
-
2065
- // Switch session via AgentSession (emits hook and tool session events)
2066
- if (!(await this.ctx.session.switchSession(writableSessionPath))) return;
2067
- const switchingToDifferentSession = previousSessionId !== this.ctx.sessionManager.getSessionId();
2068
- if (switchingToDifferentSession) this.ctx.resetIrcSidebarSession();
2069
- this.#refreshSessionTerminalTitle();
2070
- this.ctx.updateEditorBorderColor();
2056
+ async handleResumeSession(
2057
+ sessionPath: string,
2058
+ options?: { requireIdle?: boolean },
2059
+ onSwitched?: () => void,
2060
+ ): Promise<boolean> {
2061
+ const progress = acquireResumeProgressLease(this.ctx);
2062
+ await progress.committed;
2063
+ try {
2064
+ const previousSessionId = this.ctx.sessionManager.getSessionId();
2065
+ const migrationPolicy =
2066
+ this.ctx.settings?.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
2067
+ const writableSessionPath = await this.ctx.sessionManager.prepareManagedCandidateForWrite(
2068
+ sessionPath,
2069
+ migrationPolicy,
2070
+ );
2071
2071
 
2072
- this.ctx.rebuildInitialMessages(switchingToDifferentSession ? "replace-identity" : "reconcile-same-transcript");
2073
- await this.ctx.reloadTodos();
2074
- this.ctx.showStatus("Resumed session");
2072
+ // Switch session via AgentSession (emits hook and tool session events)
2073
+ if (!(await this.ctx.session.switchSession(writableSessionPath, options))) return false;
2074
+ onSwitched?.();
2075
+ this.#clearTransientSessionUi();
2076
+ const switchingToDifferentSession = previousSessionId !== this.ctx.sessionManager.getSessionId();
2077
+ if (switchingToDifferentSession) this.ctx.resetIrcSidebarSession();
2078
+ this.#refreshSessionTerminalTitle();
2079
+ this.ctx.updateEditorBorderColor();
2080
+
2081
+ this.ctx.rebuildInitialMessages(
2082
+ switchingToDifferentSession ? "replace-identity" : "reconcile-same-transcript",
2083
+ );
2084
+ await this.ctx.reloadTodos();
2085
+ this.ctx.showStatus("Resumed session");
2086
+ return true;
2087
+ } finally {
2088
+ progress.clear();
2089
+ }
2075
2090
  }
2076
2091
 
2077
2092
  async handleSessionDeleteCommand(): Promise<void> {
@@ -2158,6 +2173,7 @@ export class SelectorController {
2158
2173
  }
2159
2174
 
2160
2175
  this.ctx.showStatus(`Logging in to ${providerId}… (Esc to cancel)`);
2176
+ let authorizationUrlLease: number | undefined;
2161
2177
  const manualInput = this.ctx.oauthManualInput;
2162
2178
  const useManualInput = CALLBACK_SERVER_PROVIDERS.has(providerId as OAuthProvider);
2163
2179
  const abort = new AbortController();
@@ -2172,6 +2188,7 @@ export class SelectorController {
2172
2188
  await this.ctx.session.modelRegistry.authStorage.login(providerId as OAuthProvider, {
2173
2189
  signal: abort.signal,
2174
2190
  onAuth: (info: { url: string; instructions?: string }) => {
2191
+ authorizationUrlLease = this.ctx.setOAuthAuthorizationUrl(info.url);
2175
2192
  this.ctx.chatContainer.addChild(new Spacer(1));
2176
2193
  this.ctx.chatContainer.addChild(new Text(theme.fg("dim", info.url), 1, 0));
2177
2194
  const hyperlink = `\x1b]8;;${info.url}\x07Click here to login\x1b]8;;\x07`;
@@ -2184,6 +2201,16 @@ export class SelectorController {
2184
2201
  this.ctx.chatContainer.addChild(new Spacer(1));
2185
2202
  this.ctx.chatContainer.addChild(new Text(theme.fg("dim", MANUAL_LOGIN_TIP), 1, 0));
2186
2203
  }
2204
+ this.ctx.chatContainer.addChild(
2205
+ new Text(
2206
+ theme.fg(
2207
+ "dim",
2208
+ `Press ${this.ctx.keybindings.getDisplayString("app.oauth.copyUrl")} to copy the authorization URL.`,
2209
+ ),
2210
+ 1,
2211
+ 0,
2212
+ ),
2213
+ );
2187
2214
  this.ctx.ui.requestRender();
2188
2215
  this.ctx.openInBrowser(info.url);
2189
2216
  },
@@ -2235,6 +2262,7 @@ export class SelectorController {
2235
2262
  if (useManualInput) {
2236
2263
  manualInput.clear(`Manual OAuth input cleared for ${providerId}`);
2237
2264
  }
2265
+ if (authorizationUrlLease !== undefined) this.ctx.clearOAuthAuthorizationUrl(authorizationUrlLease);
2238
2266
  }
2239
2267
  }
2240
2268
 
@@ -312,6 +312,25 @@ export function selectShutdownDraft(editorText: string, hasActiveBtw: boolean):
312
312
  return hasActiveBtw ? "" : editorText;
313
313
  }
314
314
 
315
+ export class OAuthAuthorizationUrlState {
316
+ #active: { url: string; lease: number } | undefined;
317
+ #nextLease = 0;
318
+
319
+ set(url: string): number {
320
+ const lease = ++this.#nextLease;
321
+ this.#active = { url, lease };
322
+ return lease;
323
+ }
324
+
325
+ clear(lease: number): void {
326
+ if (this.#active?.lease === lease) this.#active = undefined;
327
+ }
328
+
329
+ get current(): string | undefined {
330
+ return this.#active?.url;
331
+ }
332
+ }
333
+
315
334
  export class InteractiveMode implements InteractiveModeContext {
316
335
  session: AgentSession;
317
336
  sessionManager: SessionManager;
@@ -384,6 +403,7 @@ export class InteractiveMode implements InteractiveModeContext {
384
403
  fileSlashCommands: Set<string> = new Set();
385
404
  skillCommands: Map<string, Skill> = new Map();
386
405
  oauthManualInput: OAuthManualInputManager = new OAuthManualInputManager();
406
+ #oauthAuthorizationUrl = new OAuthAuthorizationUrlState();
387
407
  promptSuggestion: PromptSuggestionController;
388
408
 
389
409
  #baseSlashCommands: SlashCommand[] = [];
@@ -697,6 +717,7 @@ export class InteractiveMode implements InteractiveModeContext {
697
717
 
698
718
  const startupQuiet = settings.get("startup.quiet");
699
719
  const welcomeLogoMode = resolveWelcomeLogoMode(settings.get("startup.welcomeBannerMode"));
720
+ const skipLogoAnimation = settings.get("startup.skipLogoAnimation");
700
721
  this.#welcomeComponent = undefined;
701
722
 
702
723
  for (const warning of this.session.configWarnings) {
@@ -722,6 +743,7 @@ export class InteractiveMode implements InteractiveModeContext {
722
743
  rightGutterWidth: COMPOSER_RIGHT_GUTTER_WIDTH,
723
744
  collapseChangelog: settings.get("collapseChangelog"),
724
745
  keyDisplayContext: this.#keyDisplayContext,
746
+ skipLogoAnimation,
725
747
  },
726
748
  );
727
749
 
@@ -2042,6 +2064,33 @@ export class InteractiveMode implements InteractiveModeContext {
2042
2064
  openInBrowser(urlOrPath: string): void {
2043
2065
  this.#commandController.openInBrowser(urlOrPath);
2044
2066
  }
2067
+ setOAuthAuthorizationUrl(url: string): number {
2068
+ return this.#oauthAuthorizationUrl.set(url);
2069
+ }
2070
+
2071
+ clearOAuthAuthorizationUrl(lease: number): void {
2072
+ this.#oauthAuthorizationUrl.clear(lease);
2073
+ }
2074
+
2075
+ hasOAuthAuthorizationUrl(): boolean {
2076
+ return this.#oauthAuthorizationUrl.current !== undefined;
2077
+ }
2078
+
2079
+ async copyOAuthAuthorizationUrl(): Promise<void> {
2080
+ const url = this.#oauthAuthorizationUrl.current;
2081
+ if (!url) {
2082
+ this.showWarning("No OAuth authorization URL is available to copy.");
2083
+ return;
2084
+ }
2085
+ try {
2086
+ await copyToClipboard(url);
2087
+ this.showStatus("OAuth authorization URL copied to clipboard");
2088
+ } catch (error) {
2089
+ this.showError(
2090
+ `Failed to copy OAuth authorization URL: ${error instanceof Error ? error.message : String(error)}`,
2091
+ );
2092
+ }
2093
+ }
2045
2094
 
2046
2095
  // Selector handling
2047
2096
  showCommandPalette(
@@ -2104,10 +2153,11 @@ export class InteractiveMode implements InteractiveModeContext {
2104
2153
  this.#selectorController.showSessionSelector();
2105
2154
  }
2106
2155
 
2107
- handleResumeSession(sessionPath: string): Promise<void> {
2108
- this.#btwController.dispose();
2109
- this.resetObserverRegistry();
2110
- return this.#selectorController.handleResumeSession(sessionPath);
2156
+ handleResumeSession(sessionPath: string, options?: { requireIdle?: boolean }): Promise<boolean> {
2157
+ return this.#selectorController.handleResumeSession(sessionPath, options, () => {
2158
+ this.#btwController.dispose();
2159
+ this.resetObserverRegistry();
2160
+ });
2111
2161
  }
2112
2162
 
2113
2163
  handleSessionDeleteCommand(): Promise<void> {
@@ -104,6 +104,12 @@ export function modelSupportsTokenCostMetrics(model: Model | undefined): boolean
104
104
  export interface WorkflowGateEmitter {
105
105
  /** True only when unattended mode has been negotiated. */
106
106
  isUnattended(): boolean;
107
+ /**
108
+ * True when gates emitted through this emitter can be answered remotely
109
+ * (workflow_gate_response over RPC/bridge). The ask tool refuses headless
110
+ * execution unless this reports true.
111
+ */
112
+ supportsRemoteGateAnswers(): boolean;
107
113
  /** Open + emit a gate; resolves with the agent's answer (from workflow_gate_response). */
108
114
  emitGate(input: OpenGateInput): Promise<unknown>;
109
115
  /**
@@ -157,6 +163,10 @@ export class UnattendedSessionControlPlane implements RpcUnattendedControlPlane,
157
163
  return this.#controller !== undefined;
158
164
  }
159
165
 
166
+ supportsRemoteGateAnswers(): boolean {
167
+ return true;
168
+ }
169
+
160
170
  /** Observe every emitted gate (e.g. so an extension can map an ask to its gate_id). */
161
171
  onGateEmitted(listener: (gate: RpcWorkflowGate) => void): () => void {
162
172
  this.#gateListeners.add(listener);
@@ -219,19 +229,27 @@ export class UnattendedSessionControlPlane implements RpcUnattendedControlPlane,
219
229
  this.#broker = new WorkflowGateBroker(this.opts.runId, this.opts.store ?? new MemoryGateStore(), {
220
230
  emit: gate => this.opts.emitFrame(gate),
221
231
  audit: e => this.opts.audit?.(e),
222
- advance: (gate, answer) => {
223
- const pending = this.#pending.get(gate.gate_id);
232
+ // The broker verifies the continuation is still live *after* `advance`
233
+ // returns, so the pending waiter must survive advance and only settle in
234
+ // `completeAccepted` (mirroring the session-side gate emitter).
235
+ advance: () => {},
236
+ completeAccepted: record => {
237
+ const pending = this.#pending.get(record.gate.gate_id);
224
238
  if (pending) {
225
- this.#pending.delete(gate.gate_id);
226
- pending.resolve(answer);
239
+ this.#pending.delete(record.gate.gate_id);
240
+ pending.resolve(record.answer);
227
241
  return;
228
242
  }
229
243
  // A controller may answer synchronously while emitFrame is still on the
230
244
  // stack. Keep the accepted answer so emitGate can still resolve after
231
245
  // openGate returns and the caller starts awaiting.
232
- this.#earlyAnswers.set(gate.gate_id, answer);
246
+ this.#earlyAnswers.set(record.gate.gate_id, record.answer);
233
247
  },
234
248
  finalizeAccepted: record => this.#finalizeAcceptedGate(record),
249
+ // The control plane's durable record is the gate store itself; it never
250
+ // publishes a separate terminal transcript, so accepted gates (live and
251
+ // recovered) terminalize with the designed "not_published" proof.
252
+ terminalizeAccepted: () => "not_published",
235
253
  });
236
254
  this.#brokerReady = true;
237
255
  void this.startRecoveryOnce();
@@ -496,7 +514,21 @@ export class UnattendedSessionControlPlane implements RpcUnattendedControlPlane,
496
514
  if (!this.#broker) {
497
515
  return Promise.reject(new Error("cannot emit a workflow gate before unattended mode is negotiated"));
498
516
  }
499
- const gate = this.#broker.openGate(input);
517
+ // Opening without a continuation quarantines the gate as
518
+ // "opened_without_continuation", which makes it permanently unanswerable.
519
+ // Register the pending waiter through the continuation contract so the
520
+ // broker records a live, resolvable gate and can fence it on release.
521
+ const waiter = Promise.withResolvers<unknown>();
522
+ const gate = this.#broker.openGate(input, {
523
+ activate: opened => this.#pending.set(opened.gate_id, { resolve: waiter.resolve, reject: waiter.reject }),
524
+ isLive: gateId => this.#pending.has(gateId),
525
+ release: gateId => {
526
+ const pending = this.#pending.get(gateId);
527
+ if (!pending) return;
528
+ this.#pending.delete(gateId);
529
+ pending.reject(new Error(`workflow gate ${gateId} continuation was fenced`));
530
+ },
531
+ });
500
532
  for (const listener of this.#gateListeners) {
501
533
  try {
502
534
  listener(gate);
@@ -507,11 +539,10 @@ export class UnattendedSessionControlPlane implements RpcUnattendedControlPlane,
507
539
  if (this.#earlyAnswers.has(gate.gate_id)) {
508
540
  const answer = this.#earlyAnswers.get(gate.gate_id);
509
541
  this.#earlyAnswers.delete(gate.gate_id);
542
+ this.#pending.delete(gate.gate_id);
510
543
  return Promise.resolve(answer);
511
544
  }
512
- const { promise, resolve, reject } = Promise.withResolvers<unknown>();
513
- this.#pending.set(gate.gate_id, { resolve, reject });
514
- return promise;
545
+ return waiter.promise;
515
546
  }
516
547
 
517
548
  async recover(): Promise<void> {
@@ -96,6 +96,8 @@ export interface AskSelectedAckRecoveryParticipant {
96
96
  /** SDK-native surface for emitting a workflow gate and awaiting its answer. */
97
97
  export interface WorkflowGateEmitter {
98
98
  supportsRemoteGateAnswers(): boolean;
99
+ /** True when an unattended run has been negotiated for this emitter (control-plane emitters). */
100
+ isUnattended?(): boolean;
99
101
  emitGate(input: OpenGateInput): Promise<unknown>;
100
102
  onGateEmitted?(listener: (gate: WorkflowGate) => void): () => void;
101
103
  resolveGate?(response: WorkflowGateResponse): Promise<WorkflowGateResolution>;
@@ -298,6 +298,10 @@ export interface InteractiveModeContext {
298
298
  ): Promise<CompactionOutcome>;
299
299
 
300
300
  openInBrowser(urlOrPath: string): void;
301
+ setOAuthAuthorizationUrl(url: string): number;
302
+ hasOAuthAuthorizationUrl(): boolean;
303
+ clearOAuthAuthorizationUrl(lease: number): void;
304
+ copyOAuthAuthorizationUrl(): Promise<void>;
301
305
  /** Resolved source of truth for slash autocomplete and command palette entries. */
302
306
  getSlashCommands?(): readonly SlashCommand[];
303
307
  refreshSlashCommandState(cwd?: string): Promise<void>;
@@ -322,7 +326,7 @@ export interface InteractiveModeContext {
322
326
  showTreeSelector(): void;
323
327
  showSessionSelector(): void;
324
328
  showSessionsDashboard(): void;
325
- handleResumeSession(sessionPath: string): Promise<void>;
329
+ handleResumeSession(sessionPath: string, options?: { requireIdle?: boolean }): Promise<boolean>;
326
330
  handleSessionDeleteCommand(): Promise<void>;
327
331
  showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: OAuthSelectorOptions): Promise<void>;
328
332
  showHookConfirm(title: string, message: string): Promise<boolean>;