@gajae-code/coding-agent 0.7.9 → 0.7.10

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 (53) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +2 -0
  3. package/dist/types/config/settings-schema.d.ts +10 -1
  4. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  5. package/dist/types/gjc-runtime/launch-tmux.d.ts +7 -0
  6. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -0
  7. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -4
  8. package/dist/types/modes/components/hook-selector.d.ts +5 -0
  9. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +20 -6
  10. package/dist/types/notifications/html-format.d.ts +2 -0
  11. package/dist/types/notifications/recent-activity.d.ts +5 -1
  12. package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
  13. package/dist/types/notifications/telegram-reference.d.ts +4 -0
  14. package/dist/types/notifications/threaded-render.d.ts +1 -0
  15. package/dist/types/tools/ask.d.ts +2 -0
  16. package/dist/types/utils/cmux-workspace.d.ts +23 -0
  17. package/dist/types/web/search/index.d.ts +1 -1
  18. package/dist/types/web/search/provider.d.ts +5 -0
  19. package/dist/types/web/search/render.d.ts +1 -0
  20. package/package.json +7 -7
  21. package/src/cli/web-search-cli.ts +3 -2
  22. package/src/config/settings-schema.ts +11 -1
  23. package/src/coordinator-mcp/server.ts +1 -1
  24. package/src/defaults/gjc/skills/deep-interview/SKILL.md +26 -2
  25. package/src/defaults/gjc/skills/ultragoal/SKILL.md +37 -112
  26. package/src/extensibility/extensions/types.ts +11 -0
  27. package/src/gjc-runtime/launch-tmux.ts +63 -2
  28. package/src/gjc-runtime/ultragoal-guard.ts +139 -19
  29. package/src/gjc-runtime/ultragoal-runtime.ts +16 -124
  30. package/src/gjc-runtime/workflow-manifest.generated.json +0 -8
  31. package/src/gjc-runtime/workflow-manifest.ts +0 -1
  32. package/src/hooks/skill-state.ts +33 -73
  33. package/src/internal-urls/docs-index.generated.ts +2 -1
  34. package/src/modes/components/hook-selector.ts +21 -3
  35. package/src/modes/components/settings-selector.ts +47 -3
  36. package/src/modes/components/status-line/presets.ts +11 -0
  37. package/src/modes/controllers/extension-ui-controller.ts +18 -2
  38. package/src/modes/shared/agent-wire/deep-interview-gate.ts +53 -9
  39. package/src/notifications/html-format.ts +75 -1
  40. package/src/notifications/index.ts +12 -1
  41. package/src/notifications/recent-activity.ts +32 -8
  42. package/src/notifications/telegram-daemon-control.ts +9 -0
  43. package/src/notifications/telegram-daemon.ts +67 -24
  44. package/src/notifications/telegram-reference.ts +36 -14
  45. package/src/notifications/threaded-render.ts +14 -6
  46. package/src/prompts/system/system-prompt.md +5 -1
  47. package/src/sdk.ts +3 -5
  48. package/src/tools/ask.ts +136 -16
  49. package/src/utils/cmux-workspace.ts +106 -0
  50. package/src/utils/title-generator.ts +2 -0
  51. package/src/web/search/index.ts +22 -10
  52. package/src/web/search/provider.ts +15 -0
  53. package/src/web/search/render.ts +3 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.10] - 2026-07-02
6
+ ### Added
7
+
8
+ - GJC now best-effort renames the containing cmux workspace to the current GJC session name when running inside a cmux terminal.
9
+ - Added a status line default usage preset (#1305).
10
+ - Telegram threaded updates now include verbose session identity (session id and cwd) metadata.
11
+
12
+ ### Fixed
13
+
14
+ - Deep Interview option-clarification prompts now stay out of the interview transcript and ambiguity recorder, so asking about displayed choices no longer persists as the round answer before the user selects an actual option.
15
+ - `gjc daemon reload telegram` now spawns the replacement daemon with a stable owner pid so the new daemon does not exit immediately after the short-lived reload CLI process ends.
16
+ - Managed `gjc --tmux` launches now size the initial window correctly (#1376).
17
+ - Coordinator tmux-delivered turns now submit correctly on Enter (#1372).
18
+ - Generic long `retry-after` rate limits are now kept retryable instead of being treated as fatal (#1370).
19
+ - Anthropic hard 429 responses are now surfaced before the stream watchdog fires (#1369).
20
+ - Internal sessions are now hidden from the Telegram recent-session list (#1367).
21
+ - Telegram messages exceeding the length limit are now chunked correctly.
22
+ - Web search now falls back correctly when the preferred provider is unavailable (#1348).
23
+ - Restored the Ultragoal team checkpoint snapshot wording (#1349).
24
+ - Deep Interview now exits early for simple tasks instead of running unnecessary rounds (#1311).
25
+ - Ultragoal goal identity and state now resolve solely from the canonical session-scoped `goals.json`, eliminating goal-identity drift from transcript-replayed snapshots (#1344).
26
+
5
27
  ## [0.7.9] - 2026-07-01
6
28
  ### Added
7
29
 
package/README.md CHANGED
@@ -24,6 +24,8 @@ For simple local side effects that do not need a full extension, set the user-le
24
24
  gjc config set completion.notifyCommand 'cmux notify --title "$GJC_NOTIFICATION_TITLE" --body "$GJC_NOTIFICATION_BODY"'
25
25
  ```
26
26
 
27
+ When GJC is running inside a cmux terminal (`CMUX_WORKSPACE_ID` is set), GJC also best-effort renames that cmux workspace to the current GJC session name whenever the session title is set or restored.
28
+
27
29
  Windows Terminal may keep BEL (`[Console]::Write([char]7)`) silent depending on profile and system sound settings even when `notifications.terminalBell` is enabled. For an audible Windows completion beep, configure a user-level PowerShell command hook instead:
28
30
 
29
31
  ```powershell
@@ -351,7 +351,7 @@ export declare const SETTINGS_SCHEMA: {
351
351
  };
352
352
  readonly "statusLine.preset": {
353
353
  readonly type: "enum";
354
- readonly values: readonly ["default", "minimal", "compact", "full", "nerd", "ascii", "custom"];
354
+ readonly values: readonly ["default", "default-usage", "minimal", "compact", "full", "nerd", "ascii", "custom"];
355
355
  readonly default: "default";
356
356
  readonly ui: {
357
357
  readonly tab: "appearance";
@@ -361,6 +361,10 @@ export declare const SETTINGS_SCHEMA: {
361
361
  readonly value: "default";
362
362
  readonly label: "Default";
363
363
  readonly description: "Model, path, git, context, tokens, cost";
364
+ }, {
365
+ readonly value: "default-usage";
366
+ readonly label: "Default + Usage";
367
+ readonly description: "Default layout with provider usage quota";
364
368
  }, {
365
369
  readonly value: "minimal";
366
370
  readonly label: "Minimal";
@@ -2575,6 +2579,11 @@ export declare const SETTINGS_SCHEMA: {
2575
2579
  readonly description: "Enable the web_search tool for web searching";
2576
2580
  };
2577
2581
  };
2582
+ readonly "web_search.provider": {
2583
+ readonly type: "enum";
2584
+ readonly values: readonly ["auto", "duckduckgo", "insane", "exa", "brave", "jina", "kimi", "zai", "anthropic", "perplexity", "gemini", "codex", "xai", "tavily", "parallel", "kagi", "synthetic", "searxng"];
2585
+ readonly default: "auto";
2586
+ };
2578
2587
  readonly "web_search.fallback": {
2579
2588
  readonly type: "array";
2580
2589
  readonly default: string[];
@@ -76,6 +76,17 @@ export interface ExtensionUIDialogOptions {
76
76
  optionLabel: string;
77
77
  onSubmit: (text: string) => void;
78
78
  };
79
+ /**
80
+ * Inline free-text input for a non-answer clarification action. It is
81
+ * separate from `customInput` so callers can distinguish "ask about these
82
+ * choices" from "provide my own answer". Empty/whitespace-only submissions
83
+ * may be rejected by the selector when `allowEmpty` is false.
84
+ */
85
+ clarificationInput?: {
86
+ optionLabel: string;
87
+ onSubmit: (text: string) => void;
88
+ allowEmpty?: boolean;
89
+ };
79
90
  }
80
91
  /** Raw terminal input listener for extensions. */
81
92
  export type TerminalInputHandler = (data: string) => {
@@ -8,6 +8,8 @@ export declare const GJC_PSMUX_PROFILE_FORCE_ENV = "GJC_PSMUX_PROFILE_FORCE";
8
8
  interface TtyState {
9
9
  stdin: boolean;
10
10
  stdout: boolean;
11
+ columns?: number;
12
+ rows?: number;
11
13
  }
12
14
  export interface TmuxLaunchContext {
13
15
  parsed: Args;
@@ -31,6 +33,10 @@ export interface TmuxSpawnResult {
31
33
  signalCode?: string | null;
32
34
  stderr?: string;
33
35
  }
36
+ export interface TmuxTerminalSize {
37
+ columns: number;
38
+ rows: number;
39
+ }
34
40
  export type TmuxSpawnSync = (command: string, args: string[], options: TmuxSpawnOptions) => TmuxSpawnResult;
35
41
  export interface TmuxSpawnOptions {
36
42
  cwd: string;
@@ -54,6 +60,7 @@ export interface TmuxLaunchPlan {
54
60
  cwd: string;
55
61
  innerCommand: string;
56
62
  newSessionArgs: string[];
63
+ initialSize?: TmuxTerminalSize;
57
64
  branch?: string | null;
58
65
  attachSessionName?: string;
59
66
  project?: string | null;
@@ -29,6 +29,10 @@ export declare function readUltragoalVerificationState(input: {
29
29
  currentGoal?: CurrentGoalLike | null;
30
30
  sessionId?: string | null;
31
31
  }): Promise<UltragoalGuardDiagnostic>;
32
+ export declare function verifyUltragoalDurableCompletionState(input: {
33
+ cwd: string;
34
+ sessionId?: string | null;
35
+ }): Promise<UltragoalGuardDiagnostic>;
32
36
  export declare function isUltragoalAskBlocked(cwd: string, options?: {
33
37
  sessionId?: string | null;
34
38
  }): Promise<UltragoalAskBlockDiagnostic>;
@@ -36,7 +36,6 @@ export interface UltragoalCompletionVerification {
36
36
  gjcGoalMode: UltragoalGjcGoalMode;
37
37
  gjcObjective: string;
38
38
  qualityGateHash: string;
39
- gjcGoalSnapshotHash: string;
40
39
  planGeneration: string;
41
40
  basis: {
42
41
  planHashBeforeCheckpoint: string;
@@ -237,7 +236,6 @@ export declare function checkpointUltragoalGoal(input: {
237
236
  goalId: string;
238
237
  status: UltragoalGoalStatus;
239
238
  evidence: string;
240
- gjcGoalJson?: string;
241
239
  qualityGateJson?: string;
242
240
  }): Promise<UltragoalPlan>;
243
241
  export interface UltragoalCheckpointContinuation {
@@ -253,7 +251,6 @@ export declare function checkpointAndContinueUltragoalGoal(input: {
253
251
  goalId: string;
254
252
  status: UltragoalGoalStatus;
255
253
  evidence: string;
256
- gjcGoalJson?: string;
257
254
  qualityGateJson?: string;
258
255
  advanceNext?: boolean;
259
256
  retryFailed?: boolean;
@@ -271,7 +268,6 @@ export declare function recordUltragoalReviewBlockers(input: {
271
268
  title: string;
272
269
  objective: string;
273
270
  evidence: string;
274
- gjcGoalJson?: string;
275
271
  }): Promise<UltragoalPlan>;
276
272
  export type UltragoalBlockerClassification = "human_blocked" | "resolvable";
277
273
  /**
@@ -33,6 +33,11 @@ export interface HookSelectorOptions {
33
33
  optionLabel: string;
34
34
  onSubmit: (text: string) => void;
35
35
  };
36
+ clarificationInput?: {
37
+ optionLabel: string;
38
+ onSubmit: (text: string) => void;
39
+ allowEmpty?: boolean;
40
+ };
36
41
  /**
37
42
  * Autocomplete provider for the inline custom-input editor. When present,
38
43
  * the "Other (type your own)" editor gains the same `@` file-link and `/`
@@ -50,6 +50,7 @@ export interface AskGateResult {
50
50
  multi: boolean;
51
51
  selectedOptions: string[];
52
52
  customInput?: string;
53
+ clarificationQuestion?: string;
53
54
  }
54
55
  /**
55
56
  * The answer shape an agent returns for a deep-interview question gate.
@@ -57,15 +58,28 @@ export interface AskGateResult {
57
58
  * `selected` are picked option labels; free text is conveyed by `other: true`
58
59
  * plus `custom`, encoded separately from `selected` so a real option whose label
59
60
  * happens to equal the display sentinel can never collide with the free-text path.
61
+ *
62
+ * `action: "clarify"` is a non-answer. It lets a Deep Interview user ask about
63
+ * the presented choices without advancing the round, scoring ambiguity, or
64
+ * recording selection state.
60
65
  */
61
- export interface DeepInterviewGateAnswer {
66
+ export type DeepInterviewGateAnswer = {
62
67
  selected: string[];
63
- other?: boolean;
64
- custom?: string;
65
- }
68
+ other?: false;
69
+ custom?: undefined;
70
+ action?: "answer";
71
+ } | {
72
+ selected: string[];
73
+ other: true;
74
+ custom: string;
75
+ action?: "answer";
76
+ } | {
77
+ action: "clarify";
78
+ question: string;
79
+ };
66
80
  export declare class DeepInterviewGateError extends Error {
67
- readonly code: "invalid_answer_shape" | "unknown_option" | "multi_not_allowed" | "missing_custom" | "empty_selection" | "duplicate_selection";
68
- constructor(code: "invalid_answer_shape" | "unknown_option" | "multi_not_allowed" | "missing_custom" | "empty_selection" | "duplicate_selection", message: string);
81
+ readonly code: "invalid_answer_shape" | "unknown_option" | "multi_not_allowed" | "missing_custom" | "missing_clarification" | "empty_selection" | "duplicate_selection";
82
+ constructor(code: "invalid_answer_shape" | "unknown_option" | "multi_not_allowed" | "missing_custom" | "missing_clarification" | "empty_selection" | "duplicate_selection", message: string);
69
83
  }
70
84
  /** Build the `workflow_gate` open-input for one ask question. */
71
85
  export declare function questionToGate(question: AskGateQuestion): OpenGateInput;
@@ -38,6 +38,8 @@ export declare function markdownToTelegramHtml(markdown: string): string;
38
38
  * `marker`. The final string is guaranteed to be <= `max`.
39
39
  */
40
40
  export declare function truncateTelegramHtml(message: string, max?: number, marker?: string): string;
41
+ /** Split a finished Telegram HTML message into ordered chunks without splitting tags or entities. */
42
+ export declare function splitTelegramHtml(message: string, max?: number): string[];
41
43
  /** Finalize an optional message: undefined passthrough, else safe-truncate. */
42
44
  export declare function finalizeTelegramHtml(message?: string): string | undefined;
43
45
  /**
@@ -14,6 +14,8 @@ export interface RecentSessionEntry {
14
14
  mtimeMs: number;
15
15
  /** True when a terminal breadcrumb points at this session file. */
16
16
  currentTerminal?: boolean;
17
+ /** True when this history is an internal helper/sub-agent session. */
18
+ internal?: boolean;
17
19
  }
18
20
  export interface RecentActivityDeps {
19
21
  /** Root holding `<encoded-cwd>/<sessionId>.jsonl` history files. */
@@ -22,8 +24,10 @@ export interface RecentActivityDeps {
22
24
  breadcrumbPaths?: string[];
23
25
  /** Max entries to return (default 20). */
24
26
  limit?: number;
27
+ /** Include internal helper/sub-agent sessions (default true). */
28
+ includeInternal?: boolean;
25
29
  /** Injection seam for tests. */
26
- readFirstLine?: (file: string) => string | undefined;
30
+ readInitialLines?: (file: string, maxLines: number) => string[];
27
31
  }
28
32
  /**
29
33
  * List recent sessions ranked by history-file mtime (newest first).
@@ -29,6 +29,14 @@ export interface TelegramDaemonControlDeps {
29
29
  sendSignal?: (pid: number, signal: NodeJS.Signals) => void;
30
30
  spawn?: TelegramDaemonDeps["spawn"];
31
31
  execPath?: string;
32
+ /**
33
+ * Stable process id encoded into freshly-spawned daemon owner ids.
34
+ *
35
+ * The daemon-internal entrypoint rejects numeric owner ids whose process is
36
+ * already gone. `gjc daemon reload` is a short-lived CLI process, so using its
37
+ * own pid can race the child startup and make the replacement exit immediately.
38
+ */
39
+ ownerPid?: number;
32
40
  randomId?: () => string;
33
41
  sleep?: (ms: number) => Promise<void>;
34
42
  waitStepMs?: number;
@@ -23,6 +23,7 @@ export interface RenderedMessage {
23
23
  text: string;
24
24
  inline_keyboard?: InlineButton[][];
25
25
  }
26
+ type TelegramSend = (method: string, body: unknown) => Promise<Response>;
26
27
  /** Encode `actionId` + option `index` into Telegram callback_data (<=64 bytes). */
27
28
  export declare function encodeCallbackData(actionId: string, index: number): string;
28
29
  /** Decode callback_data produced by {@link encodeCallbackData}. */
@@ -58,6 +59,8 @@ export declare function buildActionMessage(action: {
58
59
  options?: string[];
59
60
  summary?: string;
60
61
  }): RenderedMessage;
62
+ /** Send Telegram HTML text chunks sequentially so long messages preserve order. */
63
+ export declare function sendTelegramHtmlChunks(send: TelegramSend, chatId: string, text: string, inlineKeyboard?: InlineButton[][]): Promise<void>;
61
64
  /** A protocol `reply` frame the client should send to the server. */
62
65
  export interface ReplyFrame {
63
66
  type: "reply";
@@ -111,3 +114,4 @@ export interface TelegramReferenceOptions {
111
114
  * production clients add reconnection, multi-chat routing, and persistence.
112
115
  */
113
116
  export declare function runTelegramReferenceClient(opts: TelegramReferenceOptions): Promise<void>;
117
+ export {};
@@ -42,6 +42,7 @@ interface ThreadedFrame {
42
42
  tokenUsage?: unknown;
43
43
  model?: unknown;
44
44
  diff?: unknown;
45
+ cwd?: unknown;
45
46
  phase?: unknown;
46
47
  text?: unknown;
47
48
  messageRef?: unknown;
@@ -59,6 +59,7 @@ export interface QuestionResult {
59
59
  multi: boolean;
60
60
  selectedOptions: string[];
61
61
  customInput?: string;
62
+ clarificationQuestion?: string;
62
63
  }
63
64
  export interface AskToolDetails {
64
65
  question?: string;
@@ -66,6 +67,7 @@ export interface AskToolDetails {
66
67
  multi?: boolean;
67
68
  selectedOptions?: string[];
68
69
  customInput?: string;
70
+ clarificationQuestion?: string;
69
71
  /** Multi-part question mode */
70
72
  results?: QuestionResult[];
71
73
  }
@@ -0,0 +1,23 @@
1
+ export interface CmuxWorkspaceRenameCommand {
2
+ command: string;
3
+ args: string[];
4
+ }
5
+ export interface CmuxWorkspaceRenameProcess {
6
+ exited: Promise<number>;
7
+ kill(): void;
8
+ unref(): void;
9
+ }
10
+ export interface CmuxWorkspaceTitleSyncOptions {
11
+ env?: NodeJS.ProcessEnv;
12
+ isTty?: boolean;
13
+ which?: (command: string) => string | null;
14
+ spawn?: (command: string[], options: {
15
+ env: NodeJS.ProcessEnv;
16
+ stdin: "ignore";
17
+ stdout: "ignore";
18
+ stderr: "ignore";
19
+ }) => CmuxWorkspaceRenameProcess;
20
+ }
21
+ export declare function sanitizeCmuxWorkspaceTitle(title: string | undefined): string | undefined;
22
+ export declare function buildCmuxWorkspaceRenameCommand(sessionName: string | undefined, env?: NodeJS.ProcessEnv): CmuxWorkspaceRenameCommand | null;
23
+ export declare function syncCmuxWorkspaceTitle(sessionName: string | undefined, options?: CmuxWorkspaceTitleSyncOptions): void;
@@ -110,7 +110,7 @@ export declare class WebSearchTool implements AgentTool<typeof webSearchSchema,
110
110
  /** Web search tool as CustomTool (for TUI rendering support) */
111
111
  export declare const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRenderDetails>;
112
112
  export declare function getSearchTools(): CustomTool<any, any>[];
113
- export { getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders } from "./provider";
113
+ export { getConfiguredSearchProviderPreference, getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders, } from "./provider";
114
114
  export { setSearchHardTimeoutMs } from "./providers/utils";
115
115
  export type { SearchProviderId as SearchProvider, SearchResponse } from "./types";
116
116
  export { isConfigurableSearchProviderId, isSearchProviderPreference } from "./types";
@@ -8,6 +8,11 @@ export declare function getSearchProvider(id: SearchProviderId): Promise<SearchP
8
8
  export declare const SEARCH_PROVIDER_ORDER: SearchProviderId[];
9
9
  export declare function setPreferredSearchProvider(provider: SearchProviderId | "auto"): void;
10
10
  export declare function setSearchFallbackProviders(ids: readonly string[]): void;
11
+ export interface SearchProviderPreferenceSource {
12
+ get(path: "providers.webSearch" | "web_search.provider"): SearchProviderId | "auto";
13
+ has(path: "providers.webSearch" | "web_search.provider"): boolean;
14
+ }
15
+ export declare function getConfiguredSearchProviderPreference(settings: SearchProviderPreferenceSource): SearchProviderId | "auto";
11
16
  export interface ResolveProviderChainOptions {
12
17
  authStorage: AuthStorage;
13
18
  sessionId?: string;
@@ -10,6 +10,7 @@ import type { SearchResponse } from "./types";
10
10
  export interface SearchRenderDetails {
11
11
  response: SearchResponse;
12
12
  error?: string;
13
+ warning?: string;
13
14
  }
14
15
  /** Render web search result with tree-based layout */
15
16
  export declare function renderSearchResult(result: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.7.9",
4
+ "version": "0.7.10",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -52,12 +52,12 @@
52
52
  "@agentclientprotocol/sdk": "0.21.0",
53
53
  "@babel/parser": "^7.29.3",
54
54
  "@mozilla/readability": "^0.6.0",
55
- "@gajae-code/stats": "0.7.9",
56
- "@gajae-code/agent-core": "0.7.9",
57
- "@gajae-code/ai": "0.7.9",
58
- "@gajae-code/natives": "0.7.9",
59
- "@gajae-code/tui": "0.7.9",
60
- "@gajae-code/utils": "0.7.9",
55
+ "@gajae-code/stats": "0.7.10",
56
+ "@gajae-code/agent-core": "0.7.10",
57
+ "@gajae-code/ai": "0.7.10",
58
+ "@gajae-code/natives": "0.7.10",
59
+ "@gajae-code/tui": "0.7.10",
60
+ "@gajae-code/utils": "0.7.10",
61
61
  "@puppeteer/browsers": "^2.13.0",
62
62
  "@types/turndown": "5.0.6",
63
63
  "@xterm/headless": "^6.0.0",
@@ -9,6 +9,7 @@ import chalk from "chalk";
9
9
  import { Settings } from "../config/settings";
10
10
  import { initTheme, theme } from "../modes/theme/theme";
11
11
  import {
12
+ getConfiguredSearchProviderPreference,
12
13
  isConfigurableSearchProviderId,
13
14
  isSearchProviderPreference,
14
15
  runSearchQuery,
@@ -170,8 +171,8 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
170
171
 
171
172
  await initTheme();
172
173
  const settings = await Settings.init();
173
- const configuredProvider = settings.get("providers.webSearch");
174
- if (typeof configuredProvider === "string" && isSearchProviderPreference(configuredProvider)) {
174
+ const configuredProvider = getConfiguredSearchProviderPreference(settings);
175
+ if (isSearchProviderPreference(configuredProvider)) {
175
176
  setPreferredSearchProvider(configuredProvider);
176
177
  }
177
178
  const configuredFallback = settings.get("web_search.fallback");
@@ -455,7 +455,7 @@ export const SETTINGS_SCHEMA = {
455
455
  // Status line
456
456
  "statusLine.preset": {
457
457
  type: "enum",
458
- values: ["default", "minimal", "compact", "full", "nerd", "ascii", "custom"] as const,
458
+ values: ["default", "default-usage", "minimal", "compact", "full", "nerd", "ascii", "custom"] as const,
459
459
  default: "default",
460
460
  ui: {
461
461
  tab: "appearance",
@@ -463,6 +463,11 @@ export const SETTINGS_SCHEMA = {
463
463
  description: "Pre-built status line configurations",
464
464
  options: [
465
465
  { value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
466
+ {
467
+ value: "default-usage",
468
+ label: "Default + Usage",
469
+ description: "Default layout with provider usage quota",
470
+ },
466
471
  { value: "minimal", label: "Minimal", description: "Path and git only" },
467
472
  { value: "compact", label: "Compact", description: "Model, git, cost, context" },
468
473
  { value: "full", label: "Full", description: "All segments including time" },
@@ -2205,6 +2210,11 @@ export const SETTINGS_SCHEMA = {
2205
2210
  ui: { tab: "tools", label: "Web Search", description: "Enable the web_search tool for web searching" },
2206
2211
  },
2207
2212
 
2213
+ "web_search.provider": {
2214
+ type: "enum",
2215
+ values: ["auto", ...CONFIGURABLE_SEARCH_PROVIDER_IDS] as const,
2216
+ default: "auto",
2217
+ },
2208
2218
  "web_search.fallback": {
2209
2219
  type: "array",
2210
2220
  default: EMPTY_STRING_ARRAY,
@@ -1275,7 +1275,7 @@ async function sendTmuxPromptKeys(
1275
1275
  ): Promise<boolean> {
1276
1276
  const typed = await runner(["tmux", "send-keys", "-t", target, "-l", prompt]);
1277
1277
  if (typed.exitCode !== 0) return false;
1278
- const submitted = await runner(["tmux", "send-keys", "-t", target, "-l", "\x1b[13;5u"]);
1278
+ const submitted = await runner(["tmux", "send-keys", "-t", target, "C-m"]);
1279
1279
  return submitted.exitCode === 0;
1280
1280
  }
1281
1281
 
@@ -21,13 +21,14 @@ Deep Interview implements Ouroboros-inspired Socratic questioning with mathemati
21
21
  - User wants to avoid "that's not what I meant" outcomes from autonomous execution
22
22
  - Task is complex enough that jumping to code would waste cycles on scope discovery
23
23
  - User wants mathematically-validated clarity before committing to execution
24
+ - User explicitly requests deep-interview even after being told the request is already clear, bounded, and low-risk
24
25
  </Use_When>
25
26
 
26
27
  <Do_Not_Use_When>
27
28
  - User has a detailed, specific request with file paths, function names, or acceptance criteria -- execute directly
28
29
  - User wants to explore options or brainstorm -- use `ralplan` skill instead
29
- - User wants a quick fix or single change -- delegate to executor or execution
30
- - User says "just do it" or "skip the questions" without an explicit execution path -- respect their intent by ending interview and writing a `pending approval` spec, not by mutating files or delegating execution
30
+ - User wants a quick fix or single change -- use direct execution, not deep-interview or role-agent delegation
31
+ - User says "just do it" or "skip the questions" without an explicit execution path -- respect their intent by exiting deep-interview, not by writing a `pending approval` spec
31
32
  - User already has a PRD or plan file and explicitly asks to execute it -- use the requested execution skill with that plan
32
33
  </Do_Not_Use_When>
33
34
 
@@ -111,6 +112,27 @@ Deep Interview threshold: <resolvedThresholdPercent> (source: <resolvedThreshold
111
112
  - Include both threshold and source in the final spec metadata.
112
113
  - Read any `language` object from active deep-interview state and carry `language.instruction` forward mechanically. If absent, default to English unless `{{ARGUMENTS}}` makes another user/session language obvious or the user explicitly requests another language. Do not add language-specific special cases.
113
114
 
115
+ ## Phase 0.5: Suitability Gate
116
+
117
+ Run this gate after the Phase 0 threshold marker and before Phase 1, brownfield exploration, `gjc state write`, Round 0, ambiguity scoring, or spec writing.
118
+
119
+ If `{{ARGUMENTS}}` is already clear, bounded, low-risk, and asks for a quick fix, single change, known file/symbol edit, explicit command, or direct answer:
120
+
121
+ 1. **Stop deep-interview immediately**:
122
+ - First inspect current-session state with `gjc state read --mode deep-interview --json` (include `--session-id <current-session-id>` when available).
123
+ - Clear through `gjc state clear --force --mode deep-interview --json` only when the state is a newly seeded empty interview: no recorded `rounds`, no `spec_path`, no `handoff_from`, no final/pending spec, and no user-confirmed topology.
124
+ - If state already contains rounds, a spec path, handoff metadata, pending approval, or confirmed topology, do not clear it. Preserve the active interview and ask the user whether to continue, cancel, or explicitly clear the workflow.
125
+ - Do not initialize deep-interview state.
126
+ - Do not run Round 0.
127
+ - Do not write a pending-approval spec.
128
+ - Do not hand off to `ralplan`, `ultragoal`, `team`, or a role agent.
129
+ 2. **Return the request to direct implementation**:
130
+ - Say briefly that deep-interview is unnecessary because the request is already clear and small.
131
+ - State the direct implementation path the normal coding agent should take.
132
+ - If the user explicitly insists on deep-interview anyway, continue to Phase 1.
133
+
134
+ This gate exists to prevent deep-interview from making easy problems harder. A small verification need does not make a request interview-worthy.
135
+
114
136
  ## Phase 1: Initialize
115
137
 
116
138
  1. **Parse the user's idea** from `{{ARGUMENTS}}`
@@ -314,6 +336,8 @@ After applying `language.instruction` to the visible question, options, and gene
314
336
 
315
337
  When calling `ask`, SHOULD include optional structured metadata so the runtime can record the round without manual state writes: `deepInterview.round_id?`, `deepInterview.round`, `deepInterview.component`, `deepInterview.dimension`, and `deepInterview.ambiguity`. Keep this metadata aligned with the visible Round/Component/Targeting/Ambiguity line; if metadata cannot be supplied, the legacy formatted question text remains the fallback.
316
338
 
339
+ If the `ask` tool returns `clarificationQuestion`, treat it as a non-answer about the displayed choices. Answer the clarification briefly from the current interview context, then call `ask` again with the exact original question, options, and `deepInterview.*` metadata. A clarification bypasses Step 2b′ auto-answer, Step 2b″ free-text refine, Step 2c ambiguity scoring, Step 2d progress reporting, and Step 2e state updates; it must not be recorded as a round answer. This does not violate the one-question-per-round rule because the round remains unresolved until the user submits a real listed option or `Other` answer.
340
+
317
341
  ### Step 2b′: Auto-Answer Opted-Out Questions
318
342
 
319
343
  After the `ask` tool resolves and before ambiguity scoring, if the user opts out of answering the current question or explicitly asks the agent to decide, load `auto-answer-uncertain.md` as an internal `kind: "skill-fragment"` prompt for a fork-context architect. Pass the opted-out question, prompt-safe transcript summary, locked topology, current scores/gaps, and any auto-research candidates used for the round. The architect must return exactly one decisive answer with rationale, confidence, and explicit uncertainty. Validate the response shape before using it; if valid, record it as the tentative answer for scoring, append the round number to `auto_answered_rounds`, and mark the transcript answer as architect-assisted.