@gajae-code/coding-agent 0.7.9 → 0.7.11
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 +47 -0
- package/README.md +2 -0
- package/dist/types/config/keybindings.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +10 -1
- package/dist/types/defaults/gjc-defaults.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +10 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +4 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +20 -6
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/notifications/html-format.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +5 -1
- package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
- package/dist/types/notifications/telegram-reference.d.ts +4 -0
- package/dist/types/notifications/threaded-render.d.ts +1 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +8 -5
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +2 -0
- package/dist/types/utils/cmux-workspace.d.ts +52 -0
- package/dist/types/web/search/index.d.ts +4 -2
- package/dist/types/web/search/provider.d.ts +14 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/web/search/render.d.ts +1 -0
- package/package.json +7 -7
- package/src/cli/update-cli.ts +24 -0
- package/src/cli/web-search-cli.ts +5 -7
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/config/keybindings.ts +14 -5
- package/src/config/model-profiles.ts +16 -0
- package/src/config/settings-schema.ts +11 -1
- package/src/coordinator-mcp/server.ts +9 -3
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +26 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +16 -14
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +37 -112
- package/src/defaults/gjc-defaults.ts +16 -0
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/gjc-runtime/launch-tmux.ts +209 -26
- package/src/gjc-runtime/ralplan-runtime.ts +22 -6
- package/src/gjc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +8 -5
- package/src/gjc-runtime/state-writer.ts +5 -5
- package/src/gjc-runtime/ultragoal-guard.ts +139 -19
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -124
- package/src/gjc-runtime/workflow-manifest.generated.json +0 -8
- package/src/gjc-runtime/workflow-manifest.ts +0 -1
- package/src/hooks/skill-state.ts +33 -73
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/main.ts +2 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/components/custom-editor.ts +8 -0
- package/src/modes/components/hook-selector.ts +21 -3
- package/src/modes/components/model-selector.ts +6 -2
- package/src/modes/components/settings-selector.ts +47 -5
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line/presets.ts +11 -0
- package/src/modes/controllers/command-controller.ts +23 -0
- package/src/modes/controllers/extension-ui-controller.ts +18 -2
- package/src/modes/controllers/input-controller.ts +41 -9
- package/src/modes/interactive-mode.ts +16 -7
- package/src/modes/prompt-action-autocomplete.ts +23 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +53 -9
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/html-format.ts +75 -1
- package/src/notifications/index.ts +12 -1
- package/src/notifications/recent-activity.ts +32 -8
- package/src/notifications/telegram-daemon-control.ts +9 -0
- package/src/notifications/telegram-daemon.ts +68 -25
- package/src/notifications/telegram-reference.ts +36 -14
- package/src/notifications/threaded-render.ts +14 -6
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/sdk.ts +6 -11
- package/src/session/agent-session.ts +60 -17
- package/src/slash-commands/builtin-registry.ts +30 -10
- package/src/slash-commands/types.ts +2 -0
- package/src/tools/ask.ts +136 -16
- package/src/tools/bash.ts +24 -8
- package/src/utils/cmux-workspace.ts +233 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +137 -52
- package/src/web/search/provider.ts +80 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/web/search/render.ts +3 -0
|
@@ -322,7 +322,8 @@ const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashComma
|
|
|
322
322
|
const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
323
323
|
{
|
|
324
324
|
name: "settings",
|
|
325
|
-
|
|
325
|
+
priority: 40,
|
|
326
|
+
description: "Open settings and preferences",
|
|
326
327
|
handleTui: (_command, runtime) => {
|
|
327
328
|
runtime.ctx.showSettingsSelector();
|
|
328
329
|
runtime.ctx.editor.setText("");
|
|
@@ -338,7 +339,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
338
339
|
},
|
|
339
340
|
{
|
|
340
341
|
name: "goal",
|
|
341
|
-
|
|
342
|
+
priority: 84,
|
|
343
|
+
description: "Plan and track an autonomous goal",
|
|
342
344
|
subcommands: [
|
|
343
345
|
{ name: "set", description: "Set or replace the goal", usage: "<objective>" },
|
|
344
346
|
{ name: "show", description: "Show current goal details" },
|
|
@@ -541,7 +543,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
541
543
|
},
|
|
542
544
|
{
|
|
543
545
|
name: "export",
|
|
544
|
-
|
|
546
|
+
priority: 50,
|
|
547
|
+
description: "Export this session to an HTML file",
|
|
545
548
|
inlineHint: "[path]",
|
|
546
549
|
allowArgs: true,
|
|
547
550
|
handle: async (command, runtime) => {
|
|
@@ -568,7 +571,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
568
571
|
},
|
|
569
572
|
{
|
|
570
573
|
name: "copy",
|
|
571
|
-
|
|
574
|
+
priority: 55,
|
|
575
|
+
description: "Copy the last response for review or sharing",
|
|
572
576
|
// Public `/copy` is strict zero-argument, but `allowArgs` lets the
|
|
573
577
|
// TUI dispatcher route `/copy <arg>` here so it can be rejected locally
|
|
574
578
|
// instead of falling through as a model prompt.
|
|
@@ -585,7 +589,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
585
589
|
},
|
|
586
590
|
{
|
|
587
591
|
name: "dump",
|
|
588
|
-
|
|
592
|
+
priority: 54,
|
|
593
|
+
description: "Dump the full transcript for review or sharing",
|
|
589
594
|
acpDescription: "Return full transcript as plain text",
|
|
590
595
|
handle: async (_command, runtime) => {
|
|
591
596
|
const text = runtime.session.formatSessionAsText();
|
|
@@ -599,11 +604,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
599
604
|
},
|
|
600
605
|
{
|
|
601
606
|
name: "session",
|
|
602
|
-
|
|
607
|
+
priority: 88,
|
|
608
|
+
description: "Show current session info or delete current session",
|
|
603
609
|
acpDescription: "Show session information",
|
|
604
610
|
acpInputHint: "info|delete",
|
|
605
611
|
subcommands: [
|
|
606
|
-
{ name: "info", description: "Show session
|
|
612
|
+
{ name: "info", description: "Show current session id, title, and workspace" },
|
|
607
613
|
{ name: "delete", description: "Delete current session and return to selector" },
|
|
608
614
|
],
|
|
609
615
|
allowArgs: true,
|
|
@@ -713,6 +719,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
713
719
|
runtime.ctx.editor.setText("");
|
|
714
720
|
},
|
|
715
721
|
},
|
|
722
|
+
{
|
|
723
|
+
name: "help",
|
|
724
|
+
priority: 100,
|
|
725
|
+
description: "Learn commands and beginner workflows",
|
|
726
|
+
handleTui: (_command, runtime) => {
|
|
727
|
+
runtime.ctx.handleHelpCommand();
|
|
728
|
+
runtime.ctx.editor.setText("");
|
|
729
|
+
},
|
|
730
|
+
},
|
|
716
731
|
{
|
|
717
732
|
name: "hotkeys",
|
|
718
733
|
description: "Show all keyboard shortcuts",
|
|
@@ -953,6 +968,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
953
968
|
},
|
|
954
969
|
{
|
|
955
970
|
name: "new",
|
|
971
|
+
priority: 96,
|
|
956
972
|
description: "Start a new session",
|
|
957
973
|
handleTui: async (_command, runtime) => {
|
|
958
974
|
runtime.ctx.editor.setText("");
|
|
@@ -969,7 +985,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
969
985
|
},
|
|
970
986
|
{
|
|
971
987
|
name: "compact",
|
|
972
|
-
|
|
988
|
+
priority: 72,
|
|
989
|
+
description: "Compact context and continue this session",
|
|
973
990
|
acpDescription: "Compact the conversation",
|
|
974
991
|
inlineHint: "[focus instructions]",
|
|
975
992
|
allowArgs: true,
|
|
@@ -1026,7 +1043,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1026
1043
|
},
|
|
1027
1044
|
{
|
|
1028
1045
|
name: "resume",
|
|
1029
|
-
|
|
1046
|
+
priority: 92,
|
|
1047
|
+
description: "Resume a previous session",
|
|
1030
1048
|
handleTui: (_command, runtime) => {
|
|
1031
1049
|
runtime.ctx.showSessionSelector();
|
|
1032
1050
|
runtime.ctx.editor.setText("");
|
|
@@ -1045,7 +1063,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1045
1063
|
},
|
|
1046
1064
|
{
|
|
1047
1065
|
name: "retry",
|
|
1048
|
-
|
|
1066
|
+
priority: 70,
|
|
1067
|
+
description: "Retry or continue the last interrupted turn",
|
|
1049
1068
|
handleTui: async (_command, runtime) => {
|
|
1050
1069
|
const didRetry = await runtime.ctx.session.retry();
|
|
1051
1070
|
if (!didRetry) {
|
|
@@ -1242,6 +1261,7 @@ export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = AC
|
|
|
1242
1261
|
description: command.description,
|
|
1243
1262
|
subcommands: command.subcommands,
|
|
1244
1263
|
inlineHint: command.inlineHint,
|
|
1264
|
+
priority: command.priority,
|
|
1245
1265
|
}),
|
|
1246
1266
|
);
|
|
1247
1267
|
|
|
@@ -19,6 +19,8 @@ export interface BuiltinSlashCommand {
|
|
|
19
19
|
subcommands?: SubcommandDef[];
|
|
20
20
|
/** Static inline hint when command takes a simple argument (no subcommands). */
|
|
21
21
|
inlineHint?: string;
|
|
22
|
+
/** Higher values sort earlier in TUI slash-command autocomplete. */
|
|
23
|
+
priority?: number;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/** Parsed slash-command text after stripping the leading "/". */
|
package/src/tools/ask.ts
CHANGED
|
@@ -91,6 +91,7 @@ export interface QuestionResult {
|
|
|
91
91
|
multi: boolean;
|
|
92
92
|
selectedOptions: string[];
|
|
93
93
|
customInput?: string;
|
|
94
|
+
clarificationQuestion?: string;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
export interface AskToolDetails {
|
|
@@ -99,6 +100,7 @@ export interface AskToolDetails {
|
|
|
99
100
|
multi?: boolean;
|
|
100
101
|
selectedOptions?: string[];
|
|
101
102
|
customInput?: string;
|
|
103
|
+
clarificationQuestion?: string;
|
|
102
104
|
/** Multi-part question mode */
|
|
103
105
|
results?: QuestionResult[];
|
|
104
106
|
}
|
|
@@ -108,6 +110,7 @@ export interface AskToolDetails {
|
|
|
108
110
|
// =============================================================================
|
|
109
111
|
|
|
110
112
|
const OTHER_OPTION = "Other (type your own)";
|
|
113
|
+
const ASK_CLARIFICATION_OPTION = "Ask about these choices";
|
|
111
114
|
const RECOMMENDED_SUFFIX = " (Recommended)";
|
|
112
115
|
const DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS = Number.MAX_SAFE_INTEGER;
|
|
113
116
|
const DEEP_INTERVIEW_RECORDER_AWAIT_TIMEOUT_MS = 250;
|
|
@@ -183,6 +186,7 @@ function numberOptionLabels(labels: string[]): string[] {
|
|
|
183
186
|
interface SelectionResult {
|
|
184
187
|
selectedOptions: string[];
|
|
185
188
|
customInput?: string;
|
|
189
|
+
clarificationQuestion?: string;
|
|
186
190
|
timedOut: boolean;
|
|
187
191
|
navigation?: "back" | "forward";
|
|
188
192
|
cancelled?: boolean;
|
|
@@ -201,6 +205,7 @@ interface AskSingleQuestionOptions {
|
|
|
201
205
|
navigation?: NavigationControls;
|
|
202
206
|
scrollTitleRows?: number;
|
|
203
207
|
otherOptionLabel?: string;
|
|
208
|
+
clarificationOptionLabel?: string;
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
interface UIContext {
|
|
@@ -219,6 +224,7 @@ interface UIContext {
|
|
|
219
224
|
onRight?: () => void;
|
|
220
225
|
helpText?: string;
|
|
221
226
|
customInput?: { optionLabel: string; onSubmit: (text: string) => void };
|
|
227
|
+
clarificationInput?: { optionLabel: string; onSubmit: (text: string) => void; allowEmpty?: boolean };
|
|
222
228
|
},
|
|
223
229
|
): Promise<string | undefined>;
|
|
224
230
|
editor(
|
|
@@ -239,6 +245,7 @@ async function askSingleQuestion(
|
|
|
239
245
|
const { recommended, timeout, signal, initialSelection, navigation, scrollTitleRows } = options;
|
|
240
246
|
const doneLabel = getDoneOptionLabel();
|
|
241
247
|
const otherOptionLabel = options.otherOptionLabel ?? OTHER_OPTION;
|
|
248
|
+
const clarificationOptionLabel = options.clarificationOptionLabel;
|
|
242
249
|
let selectedOptions = [...(initialSelection?.selectedOptions ?? [])];
|
|
243
250
|
let customInput = initialSelection?.customInput;
|
|
244
251
|
let timedOut = false;
|
|
@@ -252,15 +259,17 @@ async function askSingleQuestion(
|
|
|
252
259
|
timedOut: boolean;
|
|
253
260
|
navigation?: "back" | "forward";
|
|
254
261
|
inlineInput?: string;
|
|
262
|
+
inlineClarification?: string;
|
|
255
263
|
}> => {
|
|
256
264
|
let timeoutTriggered = false;
|
|
257
265
|
const onTimeout = () => {
|
|
258
266
|
timeoutTriggered = true;
|
|
259
267
|
};
|
|
260
|
-
// Inline custom input: the TUI selector keeps the question
|
|
261
|
-
// list on screen and collects
|
|
262
|
-
//
|
|
268
|
+
// Inline custom/clarification input: the TUI selector keeps the question
|
|
269
|
+
// and option list on screen and collects text below the list, instead of
|
|
270
|
+
// swapping to a separate editor screen that hides the question.
|
|
263
271
|
let inlineInput: string | undefined;
|
|
272
|
+
let inlineClarification: string | undefined;
|
|
264
273
|
let navigationAction: "back" | "forward" | undefined;
|
|
265
274
|
const baseHelpText = navigation
|
|
266
275
|
? "up/down navigate enter select ←/→ question esc cancel"
|
|
@@ -286,6 +295,15 @@ async function askSingleQuestion(
|
|
|
286
295
|
inlineInput = text;
|
|
287
296
|
},
|
|
288
297
|
},
|
|
298
|
+
clarificationInput: clarificationOptionLabel
|
|
299
|
+
? {
|
|
300
|
+
optionLabel: clarificationOptionLabel,
|
|
301
|
+
allowEmpty: false,
|
|
302
|
+
onSubmit: (text: string) => {
|
|
303
|
+
inlineClarification = text;
|
|
304
|
+
},
|
|
305
|
+
}
|
|
306
|
+
: undefined,
|
|
289
307
|
onLeft: navigation?.allowBack
|
|
290
308
|
? () => {
|
|
291
309
|
navigationAction = "back";
|
|
@@ -304,7 +322,7 @@ async function askSingleQuestion(
|
|
|
304
322
|
if (!timeoutTriggered && choice === undefined && typeof timeout === "number") {
|
|
305
323
|
timeoutTriggered = Date.now() - startMs >= timeout;
|
|
306
324
|
}
|
|
307
|
-
return { choice, timedOut: timeoutTriggered, navigation: navigationAction, inlineInput };
|
|
325
|
+
return { choice, timedOut: timeoutTriggered, navigation: navigationAction, inlineInput, inlineClarification };
|
|
308
326
|
};
|
|
309
327
|
|
|
310
328
|
// Fallback for UI contexts that don't support inline custom input (they
|
|
@@ -315,6 +333,13 @@ async function askSingleQuestion(
|
|
|
315
333
|
const input = signal ? await untilAborted(signal, showCustomInput) : await showCustomInput();
|
|
316
334
|
return { input };
|
|
317
335
|
};
|
|
336
|
+
const promptForClarificationInput = async (): Promise<{ input: string | undefined }> => {
|
|
337
|
+
const dialogOptions = signal ? { signal } : undefined;
|
|
338
|
+
const showClarificationInput = () =>
|
|
339
|
+
ui.editor("Ask a clarification question:", undefined, dialogOptions, { promptStyle: true });
|
|
340
|
+
const input = signal ? await untilAborted(signal, showClarificationInput) : await showClarificationInput();
|
|
341
|
+
return { input: input !== undefined && input.trim() === "" ? undefined : input };
|
|
342
|
+
};
|
|
318
343
|
|
|
319
344
|
const promptWithProgress = navigation?.progressText ? `${question} (${navigation.progressText})` : question;
|
|
320
345
|
if (multi) {
|
|
@@ -337,6 +362,9 @@ async function askSingleQuestion(
|
|
|
337
362
|
opts.push(doneLabel);
|
|
338
363
|
}
|
|
339
364
|
opts.push(otherOptionLabel);
|
|
365
|
+
if (clarificationOptionLabel) {
|
|
366
|
+
opts.push(clarificationOptionLabel);
|
|
367
|
+
}
|
|
340
368
|
|
|
341
369
|
const prefix = selected.size > 0 ? `(${selected.size} selected) ` : "";
|
|
342
370
|
const {
|
|
@@ -344,6 +372,7 @@ async function askSingleQuestion(
|
|
|
344
372
|
timedOut: selectTimedOut,
|
|
345
373
|
navigation: arrowNavigation,
|
|
346
374
|
inlineInput,
|
|
375
|
+
inlineClarification,
|
|
347
376
|
} = await selectOption(`${prefix}${promptWithProgress}`, opts, cursorIndex);
|
|
348
377
|
|
|
349
378
|
if (arrowNavigation) {
|
|
@@ -370,6 +399,18 @@ async function askSingleQuestion(
|
|
|
370
399
|
customInput = input;
|
|
371
400
|
break;
|
|
372
401
|
}
|
|
402
|
+
if (clarificationOptionLabel && choice === clarificationOptionLabel) {
|
|
403
|
+
if (selectTimedOut) {
|
|
404
|
+
timedOut = true;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
const input =
|
|
408
|
+
inlineClarification !== undefined ? inlineClarification : (await promptForClarificationInput()).input;
|
|
409
|
+
if (input === undefined) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
return { selectedOptions: [], clarificationQuestion: input, timedOut };
|
|
413
|
+
}
|
|
373
414
|
|
|
374
415
|
const selectedIdx = opts.indexOf(choice);
|
|
375
416
|
if (selectedIdx >= 0) {
|
|
@@ -400,7 +441,9 @@ async function askSingleQuestion(
|
|
|
400
441
|
selectedOptions = Array.from(selected);
|
|
401
442
|
} else {
|
|
402
443
|
const displayLabels = addRecommendedSuffix(optionLabels, recommended);
|
|
403
|
-
const optionsWithNavigation =
|
|
444
|
+
const optionsWithNavigation = clarificationOptionLabel
|
|
445
|
+
? [...displayLabels, otherOptionLabel, clarificationOptionLabel]
|
|
446
|
+
: [...displayLabels, otherOptionLabel];
|
|
404
447
|
|
|
405
448
|
let initialIndex = recommended;
|
|
406
449
|
const previouslySelected = selectedOptions[0];
|
|
@@ -420,6 +463,7 @@ async function askSingleQuestion(
|
|
|
420
463
|
timedOut: selectTimedOut,
|
|
421
464
|
navigation: arrowNavigation,
|
|
422
465
|
inlineInput,
|
|
466
|
+
inlineClarification,
|
|
423
467
|
} = await selectOption(promptWithProgress, optionsWithNavigation, initialIndex);
|
|
424
468
|
timedOut = selectTimedOut;
|
|
425
469
|
|
|
@@ -439,6 +483,15 @@ async function askSingleQuestion(
|
|
|
439
483
|
}
|
|
440
484
|
// If input was dismissed (undefined), keep prior selectedOptions/customInput intact
|
|
441
485
|
}
|
|
486
|
+
} else if (clarificationOptionLabel && choice === clarificationOptionLabel) {
|
|
487
|
+
if (!selectTimedOut) {
|
|
488
|
+
const input =
|
|
489
|
+
inlineClarification !== undefined ? inlineClarification : (await promptForClarificationInput()).input;
|
|
490
|
+
if (input !== undefined) {
|
|
491
|
+
return { selectedOptions: [], clarificationQuestion: input, timedOut };
|
|
492
|
+
}
|
|
493
|
+
// If input was dismissed (undefined), keep prior selectedOptions/customInput intact
|
|
494
|
+
}
|
|
442
495
|
} else {
|
|
443
496
|
const stripped = stripRecommendedSuffix(choice);
|
|
444
497
|
if (optionLabels.includes(stripped)) {
|
|
@@ -466,6 +519,9 @@ async function askSingleQuestion(
|
|
|
466
519
|
}
|
|
467
520
|
|
|
468
521
|
function formatQuestionResult(result: QuestionResult): string {
|
|
522
|
+
if (result.clarificationQuestion !== undefined) {
|
|
523
|
+
return `${result.id}: clarification requested: ${result.clarificationQuestion}`;
|
|
524
|
+
}
|
|
469
525
|
if (result.customInput !== undefined) {
|
|
470
526
|
return `${result.id}: "${result.customInput}"`;
|
|
471
527
|
}
|
|
@@ -682,6 +738,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
682
738
|
optionLabels: rawOptionLabels,
|
|
683
739
|
selectedOptions: decoded.selectedOptions,
|
|
684
740
|
customInput: decoded.customInput,
|
|
741
|
+
clarificationQuestion: decoded.clarificationQuestion,
|
|
685
742
|
navigation: undefined as NavigationControls | undefined,
|
|
686
743
|
cancelled: false,
|
|
687
744
|
timedOut: false,
|
|
@@ -693,6 +750,9 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
693
750
|
const displayQuestion = deepInterviewPrompt ?? q.question;
|
|
694
751
|
const shouldNumberOptions = isDeepInterviewQuestion || isDeepInterviewAskQuestion(q.question);
|
|
695
752
|
const optionLabels = shouldNumberOptions ? numberOptionLabels(rawOptionLabels) : rawOptionLabels;
|
|
753
|
+
const clarificationOptionLabel = shouldNumberOptions
|
|
754
|
+
? formatNumberedOptionLabel(ASK_CLARIFICATION_OPTION, optionLabels.length + 1)
|
|
755
|
+
: undefined;
|
|
696
756
|
const initialSelection =
|
|
697
757
|
shouldNumberOptions && options?.previous
|
|
698
758
|
? {
|
|
@@ -706,6 +766,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
706
766
|
const {
|
|
707
767
|
selectedOptions: displaySelectedOptions,
|
|
708
768
|
customInput,
|
|
769
|
+
clarificationQuestion,
|
|
709
770
|
navigation,
|
|
710
771
|
cancelled,
|
|
711
772
|
timedOut,
|
|
@@ -719,6 +780,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
719
780
|
otherOptionLabel: shouldNumberOptions
|
|
720
781
|
? formatNumberedOptionLabel(OTHER_OPTION, optionLabels.length)
|
|
721
782
|
: undefined,
|
|
783
|
+
clarificationOptionLabel,
|
|
722
784
|
});
|
|
723
785
|
const selectedOptions = shouldNumberOptions
|
|
724
786
|
? displaySelectedOptions.map(selected => {
|
|
@@ -726,7 +788,15 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
726
788
|
return displayIndex >= 0 ? (rawOptionLabels[displayIndex] ?? selected) : selected;
|
|
727
789
|
})
|
|
728
790
|
: displaySelectedOptions;
|
|
729
|
-
return {
|
|
791
|
+
return {
|
|
792
|
+
optionLabels: rawOptionLabels,
|
|
793
|
+
selectedOptions,
|
|
794
|
+
customInput,
|
|
795
|
+
clarificationQuestion,
|
|
796
|
+
navigation,
|
|
797
|
+
cancelled,
|
|
798
|
+
timedOut,
|
|
799
|
+
};
|
|
730
800
|
} catch (error) {
|
|
731
801
|
if (error instanceof Error && error.name === "AbortError") {
|
|
732
802
|
throw new ToolAbortError("Ask input was cancelled");
|
|
@@ -737,22 +807,40 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
737
807
|
|
|
738
808
|
if (params.questions.length === 1) {
|
|
739
809
|
const [q] = params.questions;
|
|
740
|
-
const { optionLabels, selectedOptions, customInput, cancelled, timedOut } =
|
|
741
|
-
|
|
742
|
-
|
|
810
|
+
const { optionLabels, selectedOptions, customInput, clarificationQuestion, cancelled, timedOut } =
|
|
811
|
+
await askQuestion(q);
|
|
812
|
+
|
|
813
|
+
if (
|
|
814
|
+
!timedOut &&
|
|
815
|
+
(cancelled ||
|
|
816
|
+
(selectedOptions.length === 0 && customInput === undefined && clarificationQuestion === undefined))
|
|
817
|
+
) {
|
|
743
818
|
context?.abort();
|
|
744
819
|
throw new ToolAbortError("Ask tool was cancelled by the user");
|
|
745
820
|
}
|
|
746
|
-
|
|
821
|
+
if (clarificationQuestion === undefined) {
|
|
822
|
+
await this.#recordDeepInterviewRound(q, selectedOptions, customInput);
|
|
823
|
+
}
|
|
747
824
|
const details: AskToolDetails = {
|
|
748
825
|
question: q.question,
|
|
749
826
|
options: optionLabels,
|
|
750
827
|
multi: q.multi ?? false,
|
|
751
828
|
selectedOptions,
|
|
752
829
|
customInput,
|
|
830
|
+
clarificationQuestion,
|
|
753
831
|
};
|
|
754
832
|
|
|
755
833
|
const responseParts: string[] = [];
|
|
834
|
+
if (clarificationQuestion !== undefined) {
|
|
835
|
+
responseParts.push(
|
|
836
|
+
clarificationQuestion.includes("\n")
|
|
837
|
+
? `User asked a clarification question about the choices:\n${clarificationQuestion
|
|
838
|
+
.split("\n")
|
|
839
|
+
.map(line => ` ${line}`)
|
|
840
|
+
.join("\n")}`
|
|
841
|
+
: `User asked a clarification question about the choices: ${clarificationQuestion}`,
|
|
842
|
+
);
|
|
843
|
+
}
|
|
756
844
|
if (selectedOptions.length > 0) {
|
|
757
845
|
responseParts.push(
|
|
758
846
|
q.multi ? `User selected: ${selectedOptions.join(", ")}` : `User selected: ${selectedOptions[0]}`,
|
|
@@ -787,6 +875,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
787
875
|
optionLabels,
|
|
788
876
|
selectedOptions,
|
|
789
877
|
customInput,
|
|
878
|
+
clarificationQuestion,
|
|
790
879
|
navigation: navAction,
|
|
791
880
|
cancelled,
|
|
792
881
|
timedOut,
|
|
@@ -804,9 +893,12 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
804
893
|
multi: q.multi ?? false,
|
|
805
894
|
selectedOptions,
|
|
806
895
|
customInput,
|
|
896
|
+
clarificationQuestion,
|
|
807
897
|
};
|
|
808
898
|
|
|
809
|
-
|
|
899
|
+
if (clarificationQuestion === undefined) {
|
|
900
|
+
await this.#recordDeepInterviewRound(q, selectedOptions, customInput);
|
|
901
|
+
}
|
|
810
902
|
|
|
811
903
|
if (navAction === "back") {
|
|
812
904
|
questionIndex = Math.max(0, questionIndex - 1);
|
|
@@ -995,7 +1087,10 @@ export const askToolRenderer = {
|
|
|
995
1087
|
// Multi-part results
|
|
996
1088
|
if (details.results && details.results.length > 0) {
|
|
997
1089
|
const hasAnySelection = details.results.some(
|
|
998
|
-
r =>
|
|
1090
|
+
r =>
|
|
1091
|
+
r.clarificationQuestion !== undefined ||
|
|
1092
|
+
r.customInput !== undefined ||
|
|
1093
|
+
(r.selectedOptions && r.selectedOptions.length > 0),
|
|
999
1094
|
);
|
|
1000
1095
|
const header = renderStatusLine(
|
|
1001
1096
|
{
|
|
@@ -1013,7 +1108,8 @@ export const askToolRenderer = {
|
|
|
1013
1108
|
const isLastQuestion = i === details.results.length - 1;
|
|
1014
1109
|
const branch = isLastQuestion ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
1015
1110
|
const continuation = isLastQuestion ? " " : `${uiTheme.fg("dim", uiTheme.tree.vertical)} `;
|
|
1016
|
-
const hasSelection =
|
|
1111
|
+
const hasSelection =
|
|
1112
|
+
r.clarificationQuestion !== undefined || r.customInput !== undefined || r.selectedOptions.length > 0;
|
|
1017
1113
|
const statusIcon = hasSelection
|
|
1018
1114
|
? uiTheme.styledSymbol("status.success", "success")
|
|
1019
1115
|
: uiTheme.styledSymbol("status.warning", "warning");
|
|
@@ -1028,7 +1124,10 @@ export const askToolRenderer = {
|
|
|
1028
1124
|
|
|
1029
1125
|
const answerLines: string[] = [];
|
|
1030
1126
|
for (let j = 0; j < r.selectedOptions.length; j++) {
|
|
1031
|
-
const isLast =
|
|
1127
|
+
const isLast =
|
|
1128
|
+
j === r.selectedOptions.length - 1 &&
|
|
1129
|
+
r.customInput === undefined &&
|
|
1130
|
+
r.clarificationQuestion === undefined;
|
|
1032
1131
|
const optBranch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
1033
1132
|
const selectedLabel = renderInlineMarkdown(r.selectedOptions[j], mdTheme, t =>
|
|
1034
1133
|
uiTheme.fg("toolOutput", t),
|
|
@@ -1042,6 +1141,14 @@ export const askToolRenderer = {
|
|
|
1042
1141
|
}
|
|
1043
1142
|
if (r.customInput !== undefined) {
|
|
1044
1143
|
container.addChild(new Text(renderCustomInput(uiTheme, continuation, r.customInput, true, false), 0, 0));
|
|
1144
|
+
} else if (r.clarificationQuestion !== undefined) {
|
|
1145
|
+
container.addChild(
|
|
1146
|
+
new Text(
|
|
1147
|
+
renderCustomInput(uiTheme, continuation, `Clarification: ${r.clarificationQuestion}`, true, false),
|
|
1148
|
+
0,
|
|
1149
|
+
0,
|
|
1150
|
+
),
|
|
1151
|
+
);
|
|
1045
1152
|
} else if (r.selectedOptions.length === 0) {
|
|
1046
1153
|
container.addChild(
|
|
1047
1154
|
new Text(
|
|
@@ -1063,7 +1170,9 @@ export const askToolRenderer = {
|
|
|
1063
1170
|
}
|
|
1064
1171
|
|
|
1065
1172
|
const hasSelection =
|
|
1066
|
-
details.
|
|
1173
|
+
details.clarificationQuestion !== undefined ||
|
|
1174
|
+
details.customInput !== undefined ||
|
|
1175
|
+
(details.selectedOptions && details.selectedOptions.length > 0);
|
|
1067
1176
|
const header = renderStatusLine({ icon: hasSelection ? "success" : "warning", title: "Ask" }, uiTheme);
|
|
1068
1177
|
const container = new Container();
|
|
1069
1178
|
container.addChild(new Text(header, 0, 0));
|
|
@@ -1075,7 +1184,10 @@ export const askToolRenderer = {
|
|
|
1075
1184
|
const answerLines: string[] = [];
|
|
1076
1185
|
if (details.selectedOptions && details.selectedOptions.length > 0) {
|
|
1077
1186
|
for (let i = 0; i < details.selectedOptions.length; i++) {
|
|
1078
|
-
const isLast =
|
|
1187
|
+
const isLast =
|
|
1188
|
+
i === details.selectedOptions.length - 1 &&
|
|
1189
|
+
details.customInput === undefined &&
|
|
1190
|
+
details.clarificationQuestion === undefined;
|
|
1079
1191
|
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
1080
1192
|
const selectedLabel = renderInlineMarkdown(details.selectedOptions[i], mdTheme, t =>
|
|
1081
1193
|
uiTheme.fg("toolOutput", t),
|
|
@@ -1090,6 +1202,14 @@ export const askToolRenderer = {
|
|
|
1090
1202
|
}
|
|
1091
1203
|
if (details.customInput !== undefined) {
|
|
1092
1204
|
container.addChild(new Text(renderCustomInput(uiTheme, " ", details.customInput, true, false), 0, 0));
|
|
1205
|
+
} else if (details.clarificationQuestion !== undefined) {
|
|
1206
|
+
container.addChild(
|
|
1207
|
+
new Text(
|
|
1208
|
+
renderCustomInput(uiTheme, " ", `Clarification: ${details.clarificationQuestion}`, true, false),
|
|
1209
|
+
0,
|
|
1210
|
+
0,
|
|
1211
|
+
),
|
|
1212
|
+
);
|
|
1093
1213
|
} else if (!details.selectedOptions || details.selectedOptions.length === 0) {
|
|
1094
1214
|
container.addChild(
|
|
1095
1215
|
new Text(
|
package/src/tools/bash.ts
CHANGED
|
@@ -8,7 +8,10 @@ import { AsyncJobManager } from "../async";
|
|
|
8
8
|
import { type BashResult, executeBash } from "../exec/bash-executor";
|
|
9
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
10
|
import { buildGjcRuntimeSessionEnv } from "../gjc-runtime/goal-mode-request";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
GJC_RALPLAN_ARTIFACT_ENV,
|
|
13
|
+
GJC_RESTRICTED_ROLE_AGENT_BASH_ENV,
|
|
14
|
+
} from "../gjc-runtime/restricted-role-agent-bash";
|
|
12
15
|
import { InternalUrlRouter } from "../internal-urls";
|
|
13
16
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
14
17
|
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
@@ -530,13 +533,24 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
530
533
|
|
|
531
534
|
const rawCommand = input.command;
|
|
532
535
|
const allowedPrefixes = this.session.bashAllowedPrefixes;
|
|
536
|
+
const isRestrictedRalplanArtifactEnv =
|
|
537
|
+
allowedPrefixes &&
|
|
538
|
+
allowedPrefixes.length > 0 &&
|
|
539
|
+
this.session.bashRestrictionProfile !== "read-only" &&
|
|
540
|
+
env &&
|
|
541
|
+
Object.keys(env).length === 1 &&
|
|
542
|
+
Object.hasOwn(env, GJC_RALPLAN_ARTIFACT_ENV) &&
|
|
543
|
+
rawCommand.includes(`--artifact-env ${GJC_RALPLAN_ARTIFACT_ENV}`);
|
|
533
544
|
if (
|
|
534
545
|
(this.session.bashRestrictionProfile === "read-only" || (allowedPrefixes && allowedPrefixes.length > 0)) &&
|
|
535
546
|
env &&
|
|
536
|
-
Object.keys(env).length > 0
|
|
547
|
+
Object.keys(env).length > 0 &&
|
|
548
|
+
!isRestrictedRalplanArtifactEnv
|
|
537
549
|
) {
|
|
538
550
|
const mode = this.session.bashRestrictionProfile === "read-only" ? "Read-only" : "Restricted role-agent";
|
|
539
|
-
throw new ToolError(
|
|
551
|
+
throw new ToolError(
|
|
552
|
+
`${mode} bash only allows the ${GJC_RALPLAN_ARTIFACT_ENV} env override for --artifact-env.`,
|
|
553
|
+
);
|
|
540
554
|
}
|
|
541
555
|
if (allowedPrefixes && allowedPrefixes.length > 0) {
|
|
542
556
|
const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
|
|
@@ -597,11 +611,13 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
597
611
|
await Promise.all(
|
|
598
612
|
Object.entries(env).map(async ([key, value]) => [
|
|
599
613
|
key,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
614
|
+
key === GJC_RALPLAN_ARTIFACT_ENV
|
|
615
|
+
? value
|
|
616
|
+
: await expandInternalUrls(value, {
|
|
617
|
+
...internalUrlOptions,
|
|
618
|
+
ensureLocalParentDirs: true,
|
|
619
|
+
noEscape: true,
|
|
620
|
+
}),
|
|
605
621
|
]),
|
|
606
622
|
),
|
|
607
623
|
)
|