@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.
- package/CHANGELOG.md +185 -14
- package/dist/types/cli/update-cli.d.ts +8 -1
- package/dist/types/commands/session.d.ts +7 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +20 -1
- package/dist/types/config/keybindings.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +1 -1
- package/dist/types/config/models-config-schema.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/config/telegram-autostart.d.ts +9 -1
- package/dist/types/dap/client.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +5 -0
- package/dist/types/modes/components/pet-capability.d.ts +8 -7
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +13 -1
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +7 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -0
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/runtime-mcp/transports/stdio.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +3 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +9 -0
- package/dist/types/session/session-storage.d.ts +17 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +22 -0
- package/dist/types/session-import/command.d.ts +20 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +28 -0
- package/dist/types/session-import/types.d.ts +125 -0
- package/dist/types/skc-runtime/boot-generation.d.ts +59 -0
- package/dist/types/skc-runtime/launch-tmux.d.ts +10 -2
- package/dist/types/skc-runtime/launch-worktree.d.ts +17 -1
- package/dist/types/skc-runtime/session-restore-runtime.d.ts +41 -0
- package/dist/types/skc-runtime/session-restore.d.ts +99 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +160 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +26 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +4 -0
- package/dist/types/slash-commands/types.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +169 -4
- package/package.json +16 -7
- package/scripts/generate-sdk-operation-inventory.ts +5 -6
- package/src/cli/config-cli.ts +23 -8
- package/src/cli/update-cli.ts +265 -21
- package/src/commands/session.ts +88 -2
- package/src/config/atomic-yaml-patch.ts +167 -30
- package/src/config/keybindings.ts +10 -0
- package/src/config/model-profiles.ts +69 -1
- package/src/config/model-registry.ts +72 -38
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +9 -0
- package/src/config/settings.ts +13 -2
- package/src/config/telegram-autostart.ts +11 -4
- package/src/dap/client.ts +78 -30
- package/src/defaults/skc/skills/deep-interview/SKILL.md +29 -3
- package/src/internal-urls/docs-index.generated.ts +5 -4
- package/src/lsp/client.ts +17 -29
- package/src/main.ts +1 -1
- package/src/modes/action-registry.ts +2 -0
- package/src/modes/components/pet-capability.ts +22 -13
- package/src/modes/components/pet-selector.ts +1 -1
- package/src/modes/components/sayknow-pet-widget.ts +41 -7
- package/src/modes/components/welcome.ts +5 -0
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/goal-mode-controller.ts +7 -2
- package/src/modes/controllers/input-controller.ts +71 -6
- package/src/modes/controllers/plan-mode-controller.ts +8 -1
- package/src/modes/controllers/runtime-mcp-command-controller.ts +14 -0
- package/src/modes/controllers/selector-controller.ts +47 -19
- package/src/modes/interactive-mode.ts +54 -4
- package/src/modes/shared/agent-wire/unattended-session.ts +40 -9
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -0
- package/src/modes/types.ts +5 -1
- package/src/notifications/lifecycle-control-runtime.ts +258 -179
- package/src/prompts/system/eager-todo.md +2 -0
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/prompts/system/system-prompt.md +4 -2
- package/src/runtime-mcp/transports/stdio.ts +96 -33
- package/src/sdk/broker/lifecycle.ts +6 -5
- package/src/sdk/bus/lifecycle-control-runtime.ts +189 -110
- package/src/sdk/protocol/operation-inventory.generated.json +33 -0
- package/src/sdk/session.ts +25 -18
- package/src/session/agent-session.ts +120 -25
- package/src/session/internal/managed-session-scope.ts +41 -6
- package/src/session/internal/managed-session-storage.ts +14 -0
- package/src/session/session-manager.ts +113 -11
- package/src/session/session-storage.ts +70 -0
- package/src/session-import/claude.ts +382 -0
- package/src/session-import/codex.ts +458 -0
- package/src/session-import/command.ts +61 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +27 -0
- package/src/session-import/redact.ts +125 -0
- package/src/session-import/service.ts +749 -0
- package/src/session-import/types.ts +163 -0
- package/src/skc-runtime/boot-generation.ts +172 -0
- package/src/skc-runtime/launch-tmux.ts +219 -41
- package/src/skc-runtime/launch-worktree.ts +277 -4
- package/src/skc-runtime/session-restore-runtime.ts +120 -0
- package/src/skc-runtime/session-restore.ts +296 -0
- package/src/skc-runtime/session-state-sidecar.ts +41 -0
- package/src/skc-runtime/tmux-owner-isolation.ts +665 -0
- package/src/skc-runtime/tmux-sessions.ts +284 -108
- package/src/slash-commands/acp-builtins.ts +5 -1
- package/src/slash-commands/builtin-registry.ts +109 -5
- package/src/slash-commands/types.ts +5 -0
- package/src/tools/ask.ts +188 -10
- package/src/tools/eval.ts +2 -2
- package/src/export/html/template.css +0 -1060
- package/src/export/html/template.html +0 -47
- package/src/export/html/template.js +0 -2348
- package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
- package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
- package/vendor/insane-search/engine/tests/test_u1.py +0 -200
- package/vendor/insane-search/engine/tests/test_u4.py +0 -131
- package/vendor/insane-search/engine/tests/test_u5.py +0 -163
- package/vendor/insane-search/engine/tests/test_u7.py +0 -124
- 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(
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
const
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
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<
|
|
2108
|
-
this.#
|
|
2109
|
-
|
|
2110
|
-
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|
package/src/modes/types.ts
CHANGED
|
@@ -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<
|
|
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>;
|