@gajae-code/coding-agent 0.16.0 → 0.16.3
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 +52 -1
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/update-cli.d.ts +1 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/model-registry.d.ts +16 -0
- package/dist/types/config/settings-schema.d.ts +17 -7
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +16 -2
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -1
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +14 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +17 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +3 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/rows.d.ts +15 -2
- package/dist/types/sdk/cli/session-cli.d.ts +2 -1
- package/dist/types/sdk/host/events.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +18 -17
- package/dist/types/sdk/lifecycle-session.d.ts +1 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +4 -0
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +47 -7
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +14 -2
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +12 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/index.d.ts +4 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +12 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +7 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/update-cli.ts +63 -12
- package/src/commands/sdk.ts +22 -7
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/model-preset-registry.ts +177 -18
- package/src/config/model-registry.ts +268 -42
- package/src/config/settings-schema.ts +19 -7
- package/src/config/settings.ts +46 -15
- package/src/coordinator-mcp/projection-scan.ts +6 -6
- package/src/coordinator-mcp/server.ts +21 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/session-state-lock.ts +156 -32
- package/src/gjc-runtime/session-state-sidecar.ts +10 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/main.ts +4 -1
- package/src/modes/acp/acp-agent.ts +34 -19
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/controllers/selector-controller.ts +10 -3
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +6 -1
- package/src/sdk/broker/broker.ts +10 -1
- package/src/sdk/broker/ensure.ts +76 -3
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +254 -34
- package/src/sdk/broker/spawn-substrate.ts +45 -2
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +17 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +118 -24
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +33 -3
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/rows.ts +75 -7
- package/src/sdk/cli/session-cli.ts +68 -35
- package/src/sdk/host/control/dispatch.ts +2 -2
- package/src/sdk/host/events.ts +26 -2
- package/src/sdk/host/host.ts +6 -1
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +497 -31
- package/src/sdk/host/session-runtime.ts +387 -54
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle-session.ts +7 -2
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +54 -3
- package/src/sdk/protocol/operation-registry.ts +1 -1
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +210 -94
- package/src/sdk/session.ts +59 -41
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1033 -260
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/session-manager.ts +517 -77
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +158 -33
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/index.ts +4 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +86 -1
|
@@ -139,6 +139,29 @@ function toSlashCommandRuntime(runtime: TuiSlashCommandRuntime): SlashCommandRun
|
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
async function updateSessionStar(
|
|
143
|
+
starred: boolean,
|
|
144
|
+
command: ParsedSlashCommand,
|
|
145
|
+
runtime: SlashCommandRuntime,
|
|
146
|
+
): Promise<SlashCommandResult> {
|
|
147
|
+
if (command.args) return usage(`Usage: /${starred ? "star" : "unstar"}`, runtime);
|
|
148
|
+
if (!runtime.sessionManager.isPersisted()) {
|
|
149
|
+
await runtime.output("Session stars require a persisted session.");
|
|
150
|
+
return commandConsumed();
|
|
151
|
+
}
|
|
152
|
+
const changed = await runtime.sessionManager.setSessionStarred(starred);
|
|
153
|
+
await runtime.output(
|
|
154
|
+
changed
|
|
155
|
+
? starred
|
|
156
|
+
? "Session starred."
|
|
157
|
+
: "Session unstarred."
|
|
158
|
+
: starred
|
|
159
|
+
? "Session is already starred."
|
|
160
|
+
: "Session is not starred.",
|
|
161
|
+
);
|
|
162
|
+
return commandConsumed();
|
|
163
|
+
}
|
|
164
|
+
|
|
142
165
|
function parseProviderSetupSlashArgs(args: string): {
|
|
143
166
|
preset?: string;
|
|
144
167
|
compat?: string;
|
|
@@ -1961,6 +1984,24 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1961
1984
|
await runtime.ctx.handleContributionPrepCommand(command.args || undefined);
|
|
1962
1985
|
},
|
|
1963
1986
|
},
|
|
1987
|
+
{
|
|
1988
|
+
name: "star",
|
|
1989
|
+
priority: 91,
|
|
1990
|
+
description: "Star the current session for easier discovery",
|
|
1991
|
+
// Accept args at dispatch so the handler can consume them as a usage error
|
|
1992
|
+
// instead of silently forwarding `/star ...` to the model.
|
|
1993
|
+
allowArgs: true,
|
|
1994
|
+
handle: (command, runtime) => updateSessionStar(true, command, runtime),
|
|
1995
|
+
},
|
|
1996
|
+
{
|
|
1997
|
+
name: "unstar",
|
|
1998
|
+
priority: 91,
|
|
1999
|
+
description: "Remove the star from the current session",
|
|
2000
|
+
// Accept args at dispatch so the handler can consume them as a usage error
|
|
2001
|
+
// instead of silently forwarding `/unstar ...` to the model.
|
|
2002
|
+
allowArgs: true,
|
|
2003
|
+
handle: (command, runtime) => updateSessionStar(false, command, runtime),
|
|
2004
|
+
},
|
|
1964
2005
|
{
|
|
1965
2006
|
name: "resume",
|
|
1966
2007
|
priority: 92,
|
package/src/task/executor.ts
CHANGED
|
@@ -2114,12 +2114,17 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
|
|
|
2114
2114
|
if (extensionRunner && !options.preflightProbe) {
|
|
2115
2115
|
extensionRunner.initialize(
|
|
2116
2116
|
{
|
|
2117
|
+
// Extension sends are INDEPENDENT producers (see runtime-init):
|
|
2118
|
+
// classify them at the trusted adapter so a terminal abort keeps
|
|
2119
|
+
// them rather than dropping them as turn-owned continuations.
|
|
2117
2120
|
sendMessage: (message, options) => {
|
|
2118
|
-
const sendPromise = session
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
+
const sendPromise = session
|
|
2122
|
+
.sendCustomMessage(message, { ...options, origin: "external" })
|
|
2123
|
+
.catch(e => {
|
|
2124
|
+
logger.error("Extension sendMessage failed", {
|
|
2125
|
+
error: e instanceof Error ? e.message : String(e),
|
|
2126
|
+
});
|
|
2121
2127
|
});
|
|
2122
|
-
});
|
|
2123
2128
|
pendingExtensionMessages.push(sendPromise);
|
|
2124
2129
|
},
|
|
2125
2130
|
sendUserMessage: (content, options) => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { validRecommendedIndex } from "./ask";
|
|
3
|
+
import { askSchema, ordinaryAskSchema } from "./ask-contract";
|
|
4
|
+
|
|
5
|
+
const baseQuestion = {
|
|
6
|
+
id: "q",
|
|
7
|
+
question: "Choose one.",
|
|
8
|
+
options: [{ label: "Keep current behavior" }, { label: "Use repository default" }],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe("ask option contract", () => {
|
|
12
|
+
test("rejects custom-input pseudo-options", () => {
|
|
13
|
+
for (const label of [
|
|
14
|
+
"Other (type your own)",
|
|
15
|
+
"Type your own",
|
|
16
|
+
"custom input",
|
|
17
|
+
"직접 입력",
|
|
18
|
+
"1. 직접 입력",
|
|
19
|
+
"직접 입력으로 추가",
|
|
20
|
+
" OTHER(SPECIFY) ",
|
|
21
|
+
"other…",
|
|
22
|
+
"write your own",
|
|
23
|
+
"enter manually",
|
|
24
|
+
"기타",
|
|
25
|
+
"기타 입력",
|
|
26
|
+
]) {
|
|
27
|
+
const result = ordinaryAskSchema.safeParse({
|
|
28
|
+
questions: [{ ...baseQuestion, options: [{ label }, { label: "Cancel" }], recommended: 0 }],
|
|
29
|
+
});
|
|
30
|
+
expect(result.success, label).toBe(false);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("accepts a genuine Other domain value", () => {
|
|
35
|
+
const result = ordinaryAskSchema.safeParse({
|
|
36
|
+
questions: [{ ...baseQuestion, options: [{ label: "Other" }, { label: "Cancel" }] }],
|
|
37
|
+
});
|
|
38
|
+
expect(result.success).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("accepts descriptive labels that merely contain custom-input words", () => {
|
|
42
|
+
const result = ordinaryAskSchema.safeParse({
|
|
43
|
+
questions: [{ ...baseQuestion, options: [{ label: "Deploy Other (type your own)" }, { label: "Cancel" }] }],
|
|
44
|
+
});
|
|
45
|
+
expect(result.success).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("accepts ordinary recommended options", () => {
|
|
49
|
+
const result = ordinaryAskSchema.safeParse({ questions: [{ ...baseQuestion, recommended: 0 }] });
|
|
50
|
+
expect(result.success).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("applies pseudo-option rejection to deep-interview ask schema", () => {
|
|
54
|
+
const result = askSchema.safeParse({
|
|
55
|
+
questions: [{ ...baseQuestion, options: [{ label: "직접 입력으로 추가" }, { label: "취소" }] }],
|
|
56
|
+
});
|
|
57
|
+
expect(result.success).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("does not recommend single-option questions", () => {
|
|
61
|
+
expect(validRecommendedIndex(0, 1)).toBeUndefined();
|
|
62
|
+
expect(validRecommendedIndex(0, 2)).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -26,8 +26,43 @@ function deepInterviewBoundedString(maximum: number) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function isReservedCustomInputOptionLabel(label: string): boolean {
|
|
30
|
+
const withoutNumbering = label
|
|
31
|
+
.normalize("NFKC")
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.trim()
|
|
34
|
+
.replace(/^\d+[.)]\s+/u, "");
|
|
35
|
+
if (/^other[\p{P}\p{S}\s]*$/u.test(withoutNumbering) && withoutNumbering !== "other") return true;
|
|
36
|
+
const wordsOnly = withoutNumbering
|
|
37
|
+
.replace(/[\p{P}\p{S}]+/gu, " ")
|
|
38
|
+
.replace(/\s+/g, " ")
|
|
39
|
+
.trim();
|
|
40
|
+
const englishPseudoOptions = new Set([
|
|
41
|
+
"other type your own",
|
|
42
|
+
"other specify",
|
|
43
|
+
"other please specify",
|
|
44
|
+
"type your own",
|
|
45
|
+
"write your own",
|
|
46
|
+
"enter manually",
|
|
47
|
+
"custom input",
|
|
48
|
+
]);
|
|
49
|
+
if (englishPseudoOptions.has(wordsOnly)) return true;
|
|
50
|
+
const compact = wordsOnly.replace(/\s+/g, "");
|
|
51
|
+
return ["직접입력", "직접입력으로추가", "기타", "기타입력"].includes(compact);
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
const OptionItem = z.object({
|
|
30
|
-
label: z
|
|
55
|
+
label: z
|
|
56
|
+
.string()
|
|
57
|
+
.describe("display label")
|
|
58
|
+
.superRefine((value, context) => {
|
|
59
|
+
if (isReservedCustomInputOptionLabel(value))
|
|
60
|
+
context.addIssue({
|
|
61
|
+
code: "custom",
|
|
62
|
+
message:
|
|
63
|
+
"ask option labels must not imitate the reserved custom-input control; omit the pseudo-option and let the Ask UI add Other (type your own)",
|
|
64
|
+
});
|
|
65
|
+
}),
|
|
31
66
|
});
|
|
32
67
|
|
|
33
68
|
const DEEP_INTERVIEW_INTENT_ID_PATTERN = /^(artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}$/;
|
package/src/tools/ask.ts
CHANGED
|
@@ -158,7 +158,8 @@ function getDoneOptionLabel(): string {
|
|
|
158
158
|
return success ? `${success} Done selecting` : "Done selecting";
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
function validRecommendedIndex(recommended: number | undefined, optionCount: number): number | undefined {
|
|
161
|
+
export function validRecommendedIndex(recommended: number | undefined, optionCount: number): number | undefined {
|
|
162
|
+
if (optionCount <= 1) return undefined;
|
|
162
163
|
return typeof recommended === "number" &&
|
|
163
164
|
Number.isFinite(recommended) &&
|
|
164
165
|
Number.isInteger(recommended) &&
|
|
@@ -350,7 +351,7 @@ async function askSingleQuestion(
|
|
|
350
351
|
options: AskSingleQuestionOptions = {},
|
|
351
352
|
): Promise<SelectionResult> {
|
|
352
353
|
const {
|
|
353
|
-
recommended,
|
|
354
|
+
recommended: requestedRecommended,
|
|
354
355
|
timeout,
|
|
355
356
|
signal,
|
|
356
357
|
initialSelection,
|
|
@@ -358,6 +359,7 @@ async function askSingleQuestion(
|
|
|
358
359
|
scrollTitleRows,
|
|
359
360
|
autoSelectOnTimeout = true,
|
|
360
361
|
} = options;
|
|
362
|
+
const recommended = validRecommendedIndex(requestedRecommended, optionLabels.length);
|
|
361
363
|
const doneLabel = getDoneOptionLabel();
|
|
362
364
|
const otherOptionLabel = options.otherOptionLabel ?? OTHER_OPTION;
|
|
363
365
|
const clarificationOptionLabel = options.clarificationOptionLabel;
|
|
@@ -1118,6 +1120,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1118
1120
|
},
|
|
1119
1121
|
) => {
|
|
1120
1122
|
const rawOptionLabels = q.options.map(o => o.label);
|
|
1123
|
+
const recommended = validRecommendedIndex(q.recommended, rawOptionLabels.length);
|
|
1121
1124
|
const questionIndex = params.questions.indexOf(q);
|
|
1122
1125
|
const emptyCustomAttempt = options?.emptyCustomAttempt ?? 0;
|
|
1123
1126
|
// Route headless asks through the SDK workflow-gate emitter; a connected
|
|
@@ -1128,7 +1131,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1128
1131
|
question: q.question,
|
|
1129
1132
|
options: q.options,
|
|
1130
1133
|
multi: q.multi,
|
|
1131
|
-
recommended
|
|
1134
|
+
recommended,
|
|
1132
1135
|
deepInterview: q.deepInterview,
|
|
1133
1136
|
workflowGate: q.workflowGate,
|
|
1134
1137
|
allowEmpty: q.multi === true && params.questions.length > 1,
|
|
@@ -1177,12 +1180,11 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1177
1180
|
}),
|
|
1178
1181
|
}
|
|
1179
1182
|
: options?.previous;
|
|
1180
|
-
const recommendedIndex = validRecommendedIndex(q.recommended, rawOptionLabels.length);
|
|
1181
1183
|
activeRemoteRequest = {
|
|
1182
1184
|
question: displayQuestion,
|
|
1183
1185
|
options: remoteSelectorOptions,
|
|
1184
1186
|
interaction: "selector",
|
|
1185
|
-
...(
|
|
1187
|
+
...(recommended === undefined ? {} : { recommendedIndex: recommended }),
|
|
1186
1188
|
...(timeout === undefined || timeout === null ? {} : { timeoutMs: timeout }),
|
|
1187
1189
|
...(clarificationOptionLabel ? { transitionCount: 2 } : { transitionCount: 1 }),
|
|
1188
1190
|
multi: q.multi === true,
|
|
@@ -1204,7 +1206,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1204
1206
|
cancelled,
|
|
1205
1207
|
timedOut,
|
|
1206
1208
|
} = await askSingleQuestion(ui, displayQuestion, optionLabels, q.multi ?? false, {
|
|
1207
|
-
recommended
|
|
1209
|
+
recommended,
|
|
1208
1210
|
timeout: timeout ?? undefined,
|
|
1209
1211
|
signal,
|
|
1210
1212
|
initialSelection,
|
|
@@ -1221,8 +1223,8 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1221
1223
|
question: displayQuestion,
|
|
1222
1224
|
options: state.interaction === "selector" ? remoteSelectorOptions : [],
|
|
1223
1225
|
interaction: state.interaction,
|
|
1224
|
-
...(state.interaction === "selector" &&
|
|
1225
|
-
? { recommendedIndex }
|
|
1226
|
+
...(state.interaction === "selector" && recommended !== undefined
|
|
1227
|
+
? { recommendedIndex: recommended }
|
|
1226
1228
|
: {}),
|
|
1227
1229
|
...(state.interaction === "selector" && timeout !== undefined && timeout !== null
|
|
1228
1230
|
? { timeoutMs: timeout }
|
|
@@ -377,6 +377,7 @@ export async function runInteractiveBashPty(
|
|
|
377
377
|
let observer: BashInteractiveOverlayComponent | undefined;
|
|
378
378
|
let settleForeground: ((result: BashInteractiveResult) => void) | undefined;
|
|
379
379
|
let settled = false;
|
|
380
|
+
let inputAttached = true;
|
|
380
381
|
const foreground = Promise.withResolvers<BashInteractiveResult>();
|
|
381
382
|
const terminal = Promise.withResolvers<BashInteractiveResult>();
|
|
382
383
|
|
|
@@ -449,6 +450,10 @@ export async function runInteractiveBashPty(
|
|
|
449
450
|
options.signal?.removeEventListener("abort", forwardForegroundAbort);
|
|
450
451
|
},
|
|
451
452
|
detachObserver: (foldResult: BashInteractiveResult) => {
|
|
453
|
+
// Stop accepting bytes synchronously at the ownership-transfer boundary.
|
|
454
|
+
// Overlay teardown may be deferred by the host, so disposal alone is not
|
|
455
|
+
// a sufficient input fence for a folded, output-only PTY.
|
|
456
|
+
inputAttached = false;
|
|
452
457
|
const outcome = settle(foldResult);
|
|
453
458
|
if (outcome === "resolved") observer = undefined;
|
|
454
459
|
return outcome;
|
|
@@ -476,15 +481,19 @@ export async function runInteractiveBashPty(
|
|
|
476
481
|
// Escape explicitly force-kills, while disposal never kills the work.
|
|
477
482
|
component.setHandlers(
|
|
478
483
|
data => {
|
|
484
|
+
if (!inputAttached) return;
|
|
479
485
|
try {
|
|
480
486
|
session.write(data);
|
|
481
487
|
} catch {
|
|
482
488
|
// ignore writes after the command exits
|
|
483
489
|
}
|
|
484
490
|
},
|
|
485
|
-
|
|
491
|
+
() => {
|
|
492
|
+
if (!inputAttached) return;
|
|
493
|
+
(options.onDismiss ?? (() => session.kill()))();
|
|
494
|
+
},
|
|
486
495
|
() => {},
|
|
487
|
-
options.onFoldKey
|
|
496
|
+
() => inputAttached && (options.onFoldKey?.() ?? false),
|
|
488
497
|
);
|
|
489
498
|
observer = component;
|
|
490
499
|
return component;
|