@gajae-code/coding-agent 0.7.10 → 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.
Files changed (88) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/dist/types/config/keybindings.d.ts +11 -5
  4. package/dist/types/defaults/gjc-defaults.d.ts +7 -0
  5. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
  6. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +3 -2
  7. package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +1 -0
  8. package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
  9. package/dist/types/modes/components/custom-editor.d.ts +2 -1
  10. package/dist/types/modes/components/skill-message.d.ts +1 -0
  11. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  12. package/dist/types/modes/controllers/input-controller.d.ts +4 -0
  13. package/dist/types/modes/interactive-mode.d.ts +1 -0
  14. package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -0
  15. package/dist/types/modes/slash-command-visibility.d.ts +1 -0
  16. package/dist/types/modes/types.d.ts +1 -0
  17. package/dist/types/sdk.d.ts +1 -1
  18. package/dist/types/session/agent-session.d.ts +8 -5
  19. package/dist/types/slash-commands/types.d.ts +2 -0
  20. package/dist/types/utils/cmux-workspace.d.ts +30 -1
  21. package/dist/types/web/search/index.d.ts +3 -1
  22. package/dist/types/web/search/provider.d.ts +9 -0
  23. package/dist/types/web/search/providers/utils.d.ts +42 -13
  24. package/package.json +7 -7
  25. package/src/cli/update-cli.ts +24 -0
  26. package/src/cli/web-search-cli.ts +2 -5
  27. package/src/cli.ts +1 -1
  28. package/src/commands/launch.ts +1 -1
  29. package/src/commands/ralplan.ts +1 -0
  30. package/src/config/keybindings.ts +14 -5
  31. package/src/config/model-profiles.ts +16 -0
  32. package/src/coordinator-mcp/server.ts +9 -3
  33. package/src/defaults/gjc/skills/ralplan/SKILL.md +16 -14
  34. package/src/defaults/gjc-defaults.ts +16 -0
  35. package/src/extensibility/extensions/runner.ts +2 -1
  36. package/src/gjc-runtime/launch-tmux.ts +164 -42
  37. package/src/gjc-runtime/ralplan-runtime.ts +22 -6
  38. package/src/gjc-runtime/restricted-role-agent-bash.ts +1 -0
  39. package/src/gjc-runtime/state-runtime.ts +8 -5
  40. package/src/gjc-runtime/state-writer.ts +5 -5
  41. package/src/internal-urls/docs-index.generated.ts +5 -4
  42. package/src/main.ts +2 -2
  43. package/src/migrate/executor.ts +23 -6
  44. package/src/modes/components/custom-editor.ts +8 -0
  45. package/src/modes/components/model-selector.ts +6 -2
  46. package/src/modes/components/settings-selector.ts +0 -2
  47. package/src/modes/components/skill-message.ts +18 -10
  48. package/src/modes/controllers/command-controller.ts +23 -0
  49. package/src/modes/controllers/input-controller.ts +41 -9
  50. package/src/modes/interactive-mode.ts +16 -7
  51. package/src/modes/prompt-action-autocomplete.ts +23 -1
  52. package/src/modes/slash-command-visibility.ts +9 -0
  53. package/src/modes/types.ts +1 -0
  54. package/src/modes/utils/hotkeys-markdown.ts +5 -2
  55. package/src/modes/utils/ui-helpers.ts +76 -3
  56. package/src/notifications/telegram-daemon.ts +4 -4
  57. package/src/prompts/agents/architect.md +7 -4
  58. package/src/prompts/agents/critic.md +15 -6
  59. package/src/prompts/agents/planner.md +8 -3
  60. package/src/prompts/tools/bash.md +1 -1
  61. package/src/sdk.ts +3 -6
  62. package/src/session/agent-session.ts +60 -17
  63. package/src/slash-commands/builtin-registry.ts +30 -10
  64. package/src/slash-commands/types.ts +2 -0
  65. package/src/tools/bash.ts +24 -8
  66. package/src/utils/cmux-workspace.ts +134 -7
  67. package/src/utils/title-generator.ts +1 -1
  68. package/src/web/kagi.ts +1 -1
  69. package/src/web/parallel.ts +2 -2
  70. package/src/web/search/index.ts +118 -45
  71. package/src/web/search/provider.ts +65 -0
  72. package/src/web/search/providers/anthropic.ts +1 -1
  73. package/src/web/search/providers/brave.ts +1 -1
  74. package/src/web/search/providers/codex.ts +1 -1
  75. package/src/web/search/providers/duckduckgo.ts +1 -1
  76. package/src/web/search/providers/exa.ts +1 -1
  77. package/src/web/search/providers/gemini.ts +7 -3
  78. package/src/web/search/providers/jina.ts +1 -1
  79. package/src/web/search/providers/kimi.ts +6 -1
  80. package/src/web/search/providers/openai-compatible.ts +1 -1
  81. package/src/web/search/providers/parallel.ts +1 -1
  82. package/src/web/search/providers/perplexity.ts +2 -2
  83. package/src/web/search/providers/searxng.ts +1 -1
  84. package/src/web/search/providers/synthetic.ts +1 -1
  85. package/src/web/search/providers/tavily.ts +1 -1
  86. package/src/web/search/providers/utils.ts +67 -17
  87. package/src/web/search/providers/xai.ts +1 -1
  88. package/src/web/search/providers/zai.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.11] - 2026-07-03
6
+ ### Fixed
7
+
8
+ - Native Windows terminals now default `app.message.queue` to `Alt+Q` instead of `Alt+Enter`, avoiding the Windows Terminal fullscreen shortcut conflict (#1422).
9
+ - Coordinator MCP tmux prompt delivery now submits with tmux `Enter` instead of `C-m`, while preserving runtime prompt-ack/`turn_start` as the delivery success gate (#1409).
10
+ - The session-close resume hint now prints the `gjc --resume <id>` command on its own line so it can be selected and copied without the surrounding prose.
11
+ - Coordinator MCP tmux prompt delivery now uses a paste buffer for prompt text before submitting with `Enter`, preserving multiline delegated `/skill:*` prompt separators that `send-keys -l` could flatten into an unstarted visible prompt (#1416).
12
+
13
+ - `/retry` now resumes sessions left with an interrupted user/custom/tool-result tail after a crash or power loss, and recovers unresolved assistant tool-use tails instead of reporting "Nothing to retry".
14
+ - Queued prompt shortcuts now keep working during auto context-full compaction: Tab/Alt+Enter queue text immediately, `/skill:*` entries replay through the skill invocation path after compaction, and Alt+Up restores only the newest queued prompt for editing instead of merging the full queue.
15
+ - Skill prompt cards now size their collapsed arguments preview to the current terminal width instead of wrapping at a fixed narrow column.
16
+ - `gjc update` now refreshes opted-in on-disk default workflow skill copies (written by `gjc setup defaults` under the agent dir) after a successful update, so they no longer stay stale relative to the embedded defaults; copies that were never installed are left absent.
17
+ - `gjc migrate` skill imports now tolerate project or agent directories reached through macOS system symlinks such as `/var` while still rejecting symlinked destination skill directories, so local dry-run/live parity tests no longer fail on Darwin temp paths.
18
+ - cmux workspace auto-renames now include a `GJC: ` prefix so renamed workspaces remain identifiable as GJC sessions.
19
+ - The cmux workspace auto-rename is now ownership-guarded: GJC reads the current workspace title via `cmux workspace list` and only renames a workspace that still has its default title, so it no longer overwrites a user-pinned workspace name or thrash a shared workspace title across multiple sessions running under the same `CMUX_WORKSPACE_ID`. Opt out with `GJC_NO_CMUX_RENAME`.
20
+ - `gjc --tmux --resume` now reaches the session picker/resume target instead of auto-attaching a same-branch managed tmux session before the inner resume resolver runs.
21
+ - `gjc --tmux` now preserves a newly created managed tmux session when `attach-session` exits after the parent SSH/PTY closes but the tmux server still reports the session live, so closing a Windows Terminal tab no longer kills the Mac host session before reattach.
22
+ - Managed `gjc --tmux` launches now size the inner tmux window to the caller terminal minus inherited tmux status lines, preventing the bottom of the GJC input from being clipped when the user's tmux status bar is visible.
23
+ - Managed `gjc --tmux` launches no longer pin the initial window to `manual` sizing on native tmux. The pre-attach reassert used `resize-window`, which flips the window's `window-size` option to `manual` and stops `attach-session` from resizing the window to the real terminal; when the attaching terminal was larger than the capture-time size, tmux left a smaller-than-client window and painted the uncovered area with `·` fill. The window now stays on `window-size latest` so it tracks the attaching client (psmux keeps the explicit `resize-window` reassert).
24
+
25
+ ### Changed
26
+
27
+ - `web_search` latency overhaul: provider hard timeouts are now class-based (pure search APIs 15s, LLM-mediated providers 120s, Kimi 35s aligned to its upstream 30s budget, replacing the uniform 300s ceiling; an explicitly configured `web_search.timeout` still overrides — the schema default no longer reinstalls 300s), DuckDuckGo is fired as a background hedge 3s into a slower primary so a failing primary falls back to an already-settled result, the Gemini 429/5xx retry-delay budget dropped from 5 minutes to 30 seconds, resolved provider chains are cached per AuthStorage for 60s keyed on the credential generation (availability probes skipped on repeat searches; login/logout invalidates immediately), and `WebSearchTool` prewarms the chain at construction. Measured: hung-primary fallback 301s → 15s, slow-failing-primary fallback ~7s → 6s, repeat chain resolution ~26ms → ~0.01ms.
28
+
5
29
  ## [0.7.10] - 2026-07-02
6
30
  ### Added
7
31
 
@@ -13,6 +37,7 @@
13
37
 
14
38
  - 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
39
  - `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.
40
+ - Ralplan role agents can now persist Planner/Architect/Critic artifacts through a sanctioned `GJC_RALPLAN_ARTIFACT` env handoff (`--artifact-env GJC_RALPLAN_ARTIFACT`), avoiding restricted-bash failures on markdown containing quotes, backslashes, shell-expansion characters, or command-substitution syntax.
16
41
  - Managed `gjc --tmux` launches now size the initial window correctly (#1376).
17
42
  - Coordinator tmux-delivered turns now submit correctly on Enter (#1372).
18
43
  - Generic long `retry-after` rate limits are now kept retryable instead of being treated as fatal (#1370).
package/README.md CHANGED
@@ -24,7 +24,7 @@ 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.
27
+ When GJC runs inside a cmux terminal (`CMUX_WORKSPACE_ID` is set), GJC best-effort renames that cmux workspace to the current GJC session name (with a `GJC: ` prefix) — but only when the workspace still has its default title, so a name you pinned (or one set by a peer session sharing the workspace) is never overwritten. Opt out with `GJC_NO_CMUX_RENAME=1`.
28
28
 
29
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:
30
30
 
@@ -10,6 +10,7 @@ interface AppKeybindings {
10
10
  "app.suspend": true;
11
11
  "app.thinking.cycle": true;
12
12
  "app.thinking.toggle": true;
13
+ "app.commandPalette.open": true;
13
14
  "app.model.cycleForward": true;
14
15
  "app.model.cycleBackward": true;
15
16
  "app.model.select": true;
@@ -45,6 +46,7 @@ declare module "@gajae-code/tui" {
45
46
  interface Keybindings extends AppKeybindings {
46
47
  }
47
48
  }
49
+ export declare function defaultMessageQueueKeysForPlatform(platform?: NodeJS.Platform): KeyId;
48
50
  /**
49
51
  * All keybindings definitions: TUI + app-specific.
50
52
  */
@@ -201,12 +203,16 @@ export declare const KEYBINDINGS: {
201
203
  readonly defaultKeys: "ctrl+t";
202
204
  readonly description: "Toggle thinking mode";
203
205
  };
204
- readonly "app.model.cycleForward": {
206
+ readonly "app.commandPalette.open": {
205
207
  readonly defaultKeys: "ctrl+p";
208
+ readonly description: "Open command palette";
209
+ };
210
+ readonly "app.model.cycleForward": {
211
+ readonly defaultKeys: "alt+n";
206
212
  readonly description: "Cycle to next model";
207
213
  };
208
214
  readonly "app.model.cycleBackward": {
209
- readonly defaultKeys: "shift+ctrl+p";
215
+ readonly defaultKeys: "alt+shift+n";
210
216
  readonly description: "Cycle to previous model";
211
217
  };
212
218
  readonly "app.model.select": {
@@ -234,7 +240,7 @@ export declare const KEYBINDINGS: {
234
240
  readonly description: "Send follow-up message (no default; Ctrl+Enter submits)";
235
241
  };
236
242
  readonly "app.message.queue": {
237
- readonly defaultKeys: "alt+enter";
243
+ readonly defaultKeys: KeyId;
238
244
  readonly description: "Queue message for next turn";
239
245
  };
240
246
  readonly "app.message.dequeue": {
@@ -254,8 +260,8 @@ export declare const KEYBINDINGS: {
254
260
  readonly description: "Copy prompt";
255
261
  };
256
262
  readonly "app.session.new": {
257
- readonly defaultKeys: [];
258
- readonly description: "Create new session";
263
+ readonly defaultKeys: "ctrl+n";
264
+ readonly description: "Start a new session";
259
265
  };
260
266
  readonly "app.session.tree": {
261
267
  readonly defaultKeys: [];
@@ -27,6 +27,13 @@ export type DefaultGjcDefinition = DefaultGjcSkillDefinition | DefaultGjcSkillFr
27
27
  export interface InstallDefaultGjcDefinitionsOptions {
28
28
  check?: boolean;
29
29
  force?: boolean;
30
+ /**
31
+ * Only rewrite default definition files that already exist on disk but whose
32
+ * content differs from the embedded defaults. Files that are absent are left
33
+ * absent (status "missing"). Used by `gjc update` to refresh opted-in copies
34
+ * without materializing new on-disk copies for users who never installed them.
35
+ */
36
+ refreshOnly?: boolean;
30
37
  targetRoot?: string;
31
38
  }
32
39
  export type DefaultGjcDefinitionInstallFile = {
@@ -22,6 +22,7 @@ export interface TmuxLaunchContext {
22
22
  tty?: TtyState;
23
23
  spawnSync?: TmuxSpawnSync;
24
24
  tmuxAvailable?: boolean;
25
+ tmuxStatusLines?: number;
25
26
  worktreeBranch?: string | null;
26
27
  currentBranch?: string | null;
27
28
  existingBranchSessionName?: string | null;
@@ -66,6 +67,8 @@ export interface TmuxLaunchPlan {
66
67
  project?: string | null;
67
68
  sessionId?: string | null;
68
69
  sessionStateFile?: string | null;
70
+ isPsmux: boolean;
71
+ platform: NodeJS.Platform;
69
72
  }
70
73
  export interface GjcTmuxProfileResult {
71
74
  skipped: boolean;
@@ -10,8 +10,9 @@
10
10
  * that lives in the bundled `/skill:ralplan` skill — but it accepts every documented flag so
11
11
  * scripted users see a useful response and the active run is visible to the TUI.
12
12
  *
13
- * 2. **Artifact write**: `gjc ralplan --write --stage <type> --stage_n <N> --artifact
14
- * <path-or-string> [--run-id <id>] [--session-id <id>] [--json]` persists Planner / Architect
13
+ * 2. **Artifact write**: `gjc ralplan --write --stage <type> --stage_n <N>
14
+ * (--artifact <path-or-string> | --artifact-env GJC_RALPLAN_ARTIFACT)
15
+ * [--run-id <id>] [--session-id <id>] [--json]` persists Planner / Architect
15
16
  * / Critic / revision / post-interview / ADR / final markdown under `.gjc/plans/ralplan/<run-id>/`, maintains
16
17
  * an `index.jsonl` audit log, copies `final` stages to `pending-approval.md`, and advances
17
18
  * the HUD chip to reflect the latest persisted stage.
@@ -1,2 +1,3 @@
1
1
  export declare const GJC_RESTRICTED_ROLE_AGENT_BASH_ENV = "GJC_RESTRICTED_ROLE_AGENT_BASH";
2
+ export declare const GJC_RALPLAN_ARTIFACT_ENV = "GJC_RALPLAN_ARTIFACT";
2
3
  export declare function isRestrictedRoleAgentBash(): boolean;
@@ -67,6 +67,7 @@ export type GuardedWriteResult = {
67
67
  path: string;
68
68
  written: true;
69
69
  revision: number;
70
+ stamped: unknown;
70
71
  } | {
71
72
  path: string;
72
73
  written: false;
@@ -1,6 +1,6 @@
1
1
  import { Editor, type KeyId } from "@gajae-code/tui";
2
2
  import { type AppKeybinding } from "../../config/keybindings";
3
- type ConfigurableEditorAction = Extract<AppKeybinding, "app.interrupt" | "app.clear" | "app.exit" | "app.suspend" | "app.thinking.cycle" | "app.model.cycleForward" | "app.model.cycleBackward" | "app.model.select" | "app.model.selectTemporary" | "app.tools.expand" | "app.thinking.toggle" | "app.editor.external" | "app.history.search" | "app.message.dequeue" | "app.message.followUp" | "app.message.queue" | "app.clipboard.pasteImage" | "app.clipboard.copyPrompt">;
3
+ type ConfigurableEditorAction = Extract<AppKeybinding, "app.interrupt" | "app.clear" | "app.exit" | "app.suspend" | "app.thinking.cycle" | "app.commandPalette.open" | "app.model.cycleForward" | "app.model.cycleBackward" | "app.model.select" | "app.model.selectTemporary" | "app.tools.expand" | "app.thinking.toggle" | "app.editor.external" | "app.history.search" | "app.message.dequeue" | "app.message.followUp" | "app.message.queue" | "app.clipboard.pasteImage" | "app.clipboard.copyPrompt">;
4
4
  type PastePendingClearReason = "timeout" | "queue-limit";
5
5
  /**
6
6
  * Custom editor that handles configurable app-level shortcuts for coding-agent.
@@ -19,6 +19,7 @@ export declare class CustomEditor extends Editor {
19
19
  onClear?: () => void;
20
20
  onExit?: () => void;
21
21
  onCycleThinkingLevel?: () => void;
22
+ onOpenCommandPalette?: () => void;
22
23
  onCycleModelForward?: () => void;
23
24
  onCycleModelBackward?: () => void;
24
25
  onSelectModel?: () => void;
@@ -6,4 +6,5 @@ export declare class SkillMessageComponent extends Container {
6
6
  constructor(message: CustomMessage<SkillPromptDetails>);
7
7
  setExpanded(expanded: boolean): void;
8
8
  invalidate(): void;
9
+ render(width: number): string[];
9
10
  }
@@ -18,6 +18,7 @@ export declare class CommandController {
18
18
  handleUsageCommand(reports?: UsageReport[] | null): Promise<void>;
19
19
  handleChangelogCommand(showFull?: boolean): Promise<void>;
20
20
  handleHotkeysCommand(): void;
21
+ handleHelpCommand(): void;
21
22
  handleToolsCommand(): void;
22
23
  handleContextCommand(): void;
23
24
  handleMemoryCommand(text: string): Promise<void>;
@@ -14,6 +14,9 @@ export declare class InputController {
14
14
  handleFollowUp(): Promise<void>;
15
15
  /** Send editor text explicitly as a queued next-turn message. */
16
16
  handleQueueSubmit(): Promise<void>;
17
+ restoreLatestQueuedMessageToEditor(options?: {
18
+ currentText?: string;
19
+ }): number;
17
20
  restoreQueuedMessagesToEditor(options?: {
18
21
  abort?: boolean;
19
22
  currentText?: string;
@@ -27,6 +30,7 @@ export declare class InputController {
27
30
  handleCopyCurrentLine(): void;
28
31
  /** Copy current prompt text to system clipboard. */
29
32
  handleCopyPrompt(): void;
33
+ openCommandPalette(): void;
30
34
  cycleThinkingLevel(): void;
31
35
  cycleRoleModel(options?: {
32
36
  temporary?: boolean;
@@ -184,6 +184,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
184
184
  handleUsageCommand(reports?: UsageReport[] | null): Promise<void>;
185
185
  handleChangelogCommand(showFull?: boolean): Promise<void>;
186
186
  handleHotkeysCommand(): void;
187
+ handleHelpCommand(): void;
187
188
  handleToolsCommand(): void;
188
189
  handleContextCommand(): void;
189
190
  handleClearCommand(): Promise<void>;
@@ -14,6 +14,8 @@ interface PromptActionAutocompleteOptions {
14
14
  copyCurrentLine: () => void;
15
15
  copyPrompt: () => void;
16
16
  pasteImage: () => void;
17
+ newSession: () => void;
18
+ showHelp: () => void;
17
19
  scrollTmuxToPreviousUserInput: () => void;
18
20
  undo: (prefix: string) => void;
19
21
  moveCursorToMessageEnd: () => void;
@@ -0,0 +1 @@
1
+ export declare function shouldShowExtensionCommand(name: string, activeProvider: string | undefined): boolean;
@@ -191,6 +191,7 @@ export interface InteractiveModeContext {
191
191
  handleUsageCommand(reports?: UsageReport[] | null): Promise<void>;
192
192
  handleChangelogCommand(showFull?: boolean): Promise<void>;
193
193
  handleHotkeysCommand(): void;
194
+ handleHelpCommand(): void;
194
195
  handleToolsCommand(): void;
195
196
  handleContextCommand(): void;
196
197
  handleDumpCommand(): void;
@@ -47,7 +47,7 @@ export interface CreateAgentSessionOptions {
47
47
  requestedModel: Model;
48
48
  reason: string;
49
49
  };
50
- /** Models available for cycling (Ctrl+P in interactive mode) */
50
+ /** Models available for cycling (Alt+N in interactive mode) */
51
51
  scopedModels?: ScopedModelSelection[];
52
52
  /** System prompt blocks. Array replaces default, function receives default blocks and returns final blocks. */
53
53
  systemPrompt?: string[] | ((defaultPrompt: string[]) => string[]);
@@ -159,7 +159,7 @@ export interface AgentSessionConfig {
159
159
  agent: Agent;
160
160
  sessionManager: SessionManager;
161
161
  settings: Settings;
162
- /** Models to cycle through with Ctrl+P (from --models flag) */
162
+ /** Models to cycle through with Alt+N (from --models flag) */
163
163
  scopedModels?: ScopedModelSelection[];
164
164
  /** Initial session thinking selector. */
165
165
  thinkingLevel?: ThinkingLevel;
@@ -671,7 +671,7 @@ export declare class AgentSession {
671
671
  followUp: readonly string[];
672
672
  };
673
673
  /**
674
- * Pop the last queued message (steering first, then follow-up).
674
+ * Pop the newest queued message across steering and follow-up queues.
675
675
  * Used by dequeue keybinding to restore messages to editor one at a time.
676
676
  * Returns the popped entry's `.text`; the tag (if any) dies with the
677
677
  * record — no orphan state can outlive the queue entry.
@@ -905,9 +905,12 @@ export declare class AgentSession {
905
905
  */
906
906
  setAutoRetryEnabled(enabled: boolean): void;
907
907
  /**
908
- * Manually retry the last failed assistant turn.
909
- * Removes the error message from agent state and re-attempts with a fresh retry budget.
910
- * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
908
+ * Manually retry the last failed assistant turn, or resume an interrupted tail
909
+ * left by a non-graceful process exit after the user/custom/tool-result message
910
+ * was persisted but before the agent emitted a terminal assistant response.
911
+ * Removes failed/aborted/unresolved tool-use assistant tails before
912
+ * re-attempting with a fresh retry budget.
913
+ * @returns true if retry/resume was initiated, false if no retryable tail exists or agent is busy
911
914
  */
912
915
  retry(): Promise<boolean>;
913
916
  /**
@@ -17,6 +17,8 @@ export interface BuiltinSlashCommand {
17
17
  subcommands?: SubcommandDef[];
18
18
  /** Static inline hint when command takes a simple argument (no subcommands). */
19
19
  inlineHint?: string;
20
+ /** Higher values sort earlier in TUI slash-command autocomplete. */
21
+ priority?: number;
20
22
  }
21
23
  /** Parsed slash-command text after stripping the leading "/". */
22
24
  export interface ParsedSlashCommand {
@@ -2,6 +2,13 @@ export interface CmuxWorkspaceRenameCommand {
2
2
  command: string;
3
3
  args: string[];
4
4
  }
5
+ /** Current ownership state of a cmux workspace, read back from `cmux workspace list`. */
6
+ export interface CmuxWorkspaceOwnership {
7
+ /** cmux marks a workspace `has_custom_title` once an explicit title is set (by the user or by GJC). */
8
+ hasCustomTitle: boolean;
9
+ /** The workspace's current display title. */
10
+ title: string;
11
+ }
5
12
  export interface CmuxWorkspaceRenameProcess {
6
13
  exited: Promise<number>;
7
14
  kill(): void;
@@ -17,7 +24,29 @@ export interface CmuxWorkspaceTitleSyncOptions {
17
24
  stdout: "ignore";
18
25
  stderr: "ignore";
19
26
  }) => CmuxWorkspaceRenameProcess;
27
+ /** Reads the current ownership state of `workspaceId`. Injectable for tests. */
28
+ readOwnership?: (cmuxCommand: string, workspaceId: string, env: NodeJS.ProcessEnv) => Promise<CmuxWorkspaceOwnership | null>;
20
29
  }
21
30
  export declare function sanitizeCmuxWorkspaceTitle(title: string | undefined): string | undefined;
31
+ export declare function formatCmuxWorkspaceTitle(title: string | undefined): string | undefined;
22
32
  export declare function buildCmuxWorkspaceRenameCommand(sessionName: string | undefined, env?: NodeJS.ProcessEnv): CmuxWorkspaceRenameCommand | null;
23
- export declare function syncCmuxWorkspaceTitle(sessionName: string | undefined, options?: CmuxWorkspaceTitleSyncOptions): void;
33
+ /** Parse `cmux workspace list --json --id-format both` output and return the
34
+ * ownership state of the workspace matching `workspaceId` (by UUID `id` or `ref`). */
35
+ export declare function parseCmuxWorkspaceOwnership(jsonText: string, workspaceId: string): CmuxWorkspaceOwnership | null;
36
+ /** Only rename when GJC owns the name:
37
+ * - unknown ownership (read failed) → skip (fail safe, never clobber)
38
+ * - already the desired title → skip (no-op)
39
+ * - workspace still on its default title → rename
40
+ * - any custom title (user- or peer-set) → skip
41
+ * This makes GJC name a fresh workspace once and then leave it alone, so it
42
+ * never overwrites a user-pinned name and multiple sessions sharing one
43
+ * CMUX_WORKSPACE_ID do not thrash the workspace title. */
44
+ export declare function shouldRenameCmuxWorkspace(ownership: CmuxWorkspaceOwnership | null, desiredTitle: string): boolean;
45
+ /**
46
+ * Best-effort sync of the containing cmux workspace title to the current GJC
47
+ * session name. Ownership-guarded: GJC reads the current workspace title and
48
+ * only renames a workspace that still has its default title, so it never
49
+ * overwrites a name the user pinned or a name a peer session (sharing the same
50
+ * CMUX_WORKSPACE_ID) set. Opt out with GJC_NO_CMUX_RENAME.
51
+ */
52
+ export declare function syncCmuxWorkspaceTitle(sessionName: string | undefined, options?: CmuxWorkspaceTitleSyncOptions): Promise<void>;
@@ -44,6 +44,8 @@ export type SearchToolParams = z.infer<typeof webSearchSchema>;
44
44
  export interface SearchQueryParams extends SearchToolParams {
45
45
  provider?: SearchProviderId | "auto";
46
46
  }
47
+ /** Override the hedge delay (tests). Non-finite/non-positive resets to default. */
48
+ export declare function setDdgHedgeDelayMs(ms: number | undefined): void;
47
49
  /**
48
50
  * Execute a web search query for CLI/testing workflows.
49
51
  *
@@ -111,6 +113,6 @@ export declare class WebSearchTool implements AgentTool<typeof webSearchSchema,
111
113
  export declare const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRenderDetails>;
112
114
  export declare function getSearchTools(): CustomTool<any, any>[];
113
115
  export { getConfiguredSearchProviderPreference, getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders, } from "./provider";
114
- export { setSearchHardTimeoutMs } from "./providers/utils";
116
+ export { applyConfiguredSearchTimeout, setSearchHardTimeoutMs } from "./providers/utils";
115
117
  export type { SearchProviderId as SearchProvider, SearchResponse } from "./types";
116
118
  export { isConfigurableSearchProviderId, isSearchProviderPreference } from "./types";
@@ -6,8 +6,17 @@ export { SearchProvider } from "./providers/base";
6
6
  export declare function getSearchProviderLabel(id: SearchProviderId): string;
7
7
  export declare function getSearchProvider(id: SearchProviderId): Promise<SearchProvider>;
8
8
  export declare const SEARCH_PROVIDER_ORDER: SearchProviderId[];
9
+ /** Drop every cached resolved chain (settings changed, tests). */
10
+ export declare function clearResolvedChainCache(): void;
9
11
  export declare function setPreferredSearchProvider(provider: SearchProviderId | "auto"): void;
10
12
  export declare function setSearchFallbackProviders(ids: readonly string[]): void;
13
+ /**
14
+ * Fire-and-forget warm-up: resolve the provider chain once so the provider
15
+ * modules are imported and the chain cache is primed before the first
16
+ * user-visible search. Errors are intentionally swallowed — prewarming must
17
+ * never surface a failure the real search path would handle itself.
18
+ */
19
+ export declare function prewarmSearchProviders(options: ResolveProviderChainOptions): void;
11
20
  export interface SearchProviderPreferenceSource {
12
21
  get(path: "providers.webSearch" | "web_search.provider"): SearchProviderId | "auto";
13
22
  has(path: "providers.webSearch" | "web_search.provider"): boolean;
@@ -15,27 +15,53 @@ import { SearchProviderError, type SearchProviderId, type SearchSource } from ".
15
15
  */
16
16
  export declare function findCredential(storage: AgentStorage | null | undefined, envKey: string | null | undefined, ...storageProviders: string[]): string | null;
17
17
  /**
18
- * Default hard ceiling for a single web-search round-trip. 300s tolerates
19
- * legitimate slow LLM-mediated responses (anthropic web_search_20250305,
20
- * perplexity, gemini, OpenAI code backend) while still guaranteeing the session unfreezes
21
- * if Bun's `AbortSignal` fails to propagate on Windows.
22
- *
23
- * Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
24
- * settle far faster in practice; reusing the same ceiling keeps the wiring
25
- * uniform without compromising correctness.
18
+ * Legacy hard ceiling for a single web-search round-trip, retained as the
19
+ * fallback for call sites that do not declare a timeout class. 300s tolerates
20
+ * pathological cases while still guaranteeing the session unfreezes if Bun's
21
+ * `AbortSignal` fails to propagate on Windows.
26
22
  */
27
23
  export declare const SEARCH_HARD_TIMEOUT_MS = 300000;
24
+ /**
25
+ * Hard ceiling for pure search APIs (brave, exa, jina, tavily, kagi,
26
+ * parallel, searxng, synthetic, zai, duckduckgo). These settle in ~1-3s in
27
+ * practice; a hung TCP/TLS connection should fall through to the next
28
+ * provider in seconds, not minutes. Kimi is the exception: it declares an
29
+ * explicit ceiling aligned with its upstream 30s request budget.
30
+ */
31
+ export declare const SEARCH_API_TIMEOUT_MS = 15000;
32
+ /**
33
+ * Hard ceiling for LLM-mediated search providers (anthropic, codex, gemini,
34
+ * perplexity, xai, openai-compatible). These legitimately take tens of
35
+ * seconds because a model performs the search and synthesizes an answer.
36
+ */
37
+ export declare const SEARCH_LLM_TIMEOUT_MS = 120000;
38
+ /** Timeout class a provider declares for its outbound round-trips. */
39
+ export type SearchTimeoutClass = "api" | "llm";
28
40
  /**
29
41
  * Override the hard timeout applied to every web-search round-trip.
30
42
  *
31
43
  * @param ms - Hard timeout in milliseconds. Non-finite or non-positive
32
- * values reset the timeout to {@link SEARCH_HARD_TIMEOUT_MS}.
44
+ * values clear the override so per-class defaults apply.
33
45
  */
34
46
  export declare function setSearchHardTimeoutMs(ms: number | undefined): void;
47
+ /** Structural settings source for {@link applyConfiguredSearchTimeout}. */
48
+ export interface SearchTimeoutSettingSource {
49
+ get(path: "web_search.timeout"): unknown;
50
+ has(path: "web_search.timeout"): boolean;
51
+ }
52
+ /**
53
+ * Install the `web_search.timeout` override from settings — but only when the
54
+ * user explicitly configured it. The settings schema ships a default value
55
+ * (300), and consuming that default via `get()` alone would reinstall a
56
+ * uniform 300s ceiling on every session, silently disabling the per-class
57
+ * timeout defaults. `has()` distinguishes loaded/overridden settings from
58
+ * schema defaults; unset clears any previous override so class defaults apply.
59
+ */
60
+ export declare function applyConfiguredSearchTimeout(settings: SearchTimeoutSettingSource): void;
35
61
  /**
36
62
  * Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
37
- * outbound `fetch()` is guaranteed to settle within `ms` even when the
38
- * runtime fails to propagate cancellation to the underlying transport.
63
+ * outbound `fetch()` is guaranteed to settle even when the runtime fails to
64
+ * propagate cancellation to the underlying transport.
39
65
  *
40
66
  * Bun's WinHTTP backend on Windows is known to ignore `AbortSignal` once a
41
67
  * TCP/TLS connection stalls (oven-sh/bun#15275, oven-sh/bun#18536); without
@@ -43,9 +69,12 @@ export declare function setSearchHardTimeoutMs(ms: number | undefined): void;
43
69
  * because the user's Esc is never delivered to the native layer.
44
70
  *
45
71
  * @param signal - Caller cancellation signal, if any.
46
- * @param ms - Hard timeout in milliseconds. Defaults to the configured value.
72
+ * @param msOrClass - Explicit timeout in milliseconds, or a
73
+ * {@link SearchTimeoutClass} resolved to its class default. Omitted falls
74
+ * back to the legacy {@link SEARCH_HARD_TIMEOUT_MS} ceiling. A user-set
75
+ * `web_search.timeout` overrides class defaults (but not explicit ms).
47
76
  */
48
- export declare function withHardTimeout(signal: AbortSignal | undefined, ms?: number): AbortSignal;
77
+ export declare function withHardTimeout(signal: AbortSignal | undefined, msOrClass?: number | SearchTimeoutClass): AbortSignal;
49
78
  /**
50
79
  * Map a provider's raw source list to the unified SearchSource shape,
51
80
  * clamped to the requested result count and annotated with ageSeconds.
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.10",
4
+ "version": "0.7.11",
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.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",
55
+ "@gajae-code/stats": "0.7.11",
56
+ "@gajae-code/agent-core": "0.7.11",
57
+ "@gajae-code/ai": "0.7.11",
58
+ "@gajae-code/natives": "0.7.11",
59
+ "@gajae-code/tui": "0.7.11",
60
+ "@gajae-code/utils": "0.7.11",
61
61
  "@puppeteer/browsers": "^2.13.0",
62
62
  "@types/turndown": "5.0.6",
63
63
  "@xterm/headless": "^6.0.0",
@@ -10,6 +10,7 @@ import { pipeline } from "node:stream/promises";
10
10
  import { $which, APP_NAME, isEnoent, VERSION } from "@gajae-code/utils";
11
11
  import { $ } from "bun";
12
12
  import chalk from "chalk";
13
+ import { installDefaultGjcDefinitions } from "../defaults/gjc-defaults";
13
14
  import { theme } from "../modes/theme/theme";
14
15
 
15
16
  const RELEASE_REPO = "Yeachan-Heo/gajae-code";
@@ -628,6 +629,29 @@ export async function runUpdateCommand(opts: { force: boolean; check: boolean })
628
629
  console.error(chalk.red(`Update failed: ${err}`));
629
630
  process.exit(1);
630
631
  }
632
+
633
+ await refreshInstalledDefaultSkills();
634
+ }
635
+
636
+ /**
637
+ * Refresh opted-in on-disk default workflow skill copies after a successful
638
+ * update. The four default skills ship embedded in the binary, so most users
639
+ * need nothing here. But users who ran `gjc setup defaults` have on-disk copies
640
+ * under the agent dir that shadow the embedded defaults; those would otherwise
641
+ * go stale after an update. Only rewrite files that already exist and differ —
642
+ * never materialize new copies for users who never opted in.
643
+ */
644
+ async function refreshInstalledDefaultSkills(): Promise<void> {
645
+ try {
646
+ const result = await installDefaultGjcDefinitions({ refreshOnly: true });
647
+ if (result.written > 0) {
648
+ console.log(
649
+ chalk.dim(`Refreshed ${result.written} local default workflow skill file(s) at ${result.targetRoot}`),
650
+ );
651
+ }
652
+ } catch (err) {
653
+ console.error(chalk.yellow(`Warning: failed to refresh local default workflow skills: ${err}`));
654
+ }
631
655
  }
632
656
 
633
657
  /**
@@ -16,7 +16,7 @@ import {
16
16
  type SearchQueryParams,
17
17
  } from "../web/search/index";
18
18
  import { SEARCH_PROVIDER_ORDER, setPreferredSearchProvider, setSearchFallbackProviders } from "../web/search/provider";
19
- import { setSearchHardTimeoutMs } from "../web/search/providers/utils";
19
+ import { applyConfiguredSearchTimeout } from "../web/search/providers/utils";
20
20
  import { renderSearchResult } from "../web/search/render";
21
21
  import type { SearchProviderId } from "../web/search/types";
22
22
 
@@ -181,10 +181,7 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
181
181
  configuredFallback.filter(value => typeof value === "string" && isConfigurableSearchProviderId(value)),
182
182
  );
183
183
  }
184
- const configuredTimeout = settings.get("web_search.timeout");
185
- if (typeof configuredTimeout === "number" && Number.isFinite(configuredTimeout) && configuredTimeout > 0) {
186
- setSearchHardTimeoutMs(configuredTimeout * 1000);
187
- }
184
+ applyConfiguredSearchTimeout(settings);
188
185
 
189
186
  const params: SearchQueryParams = {
190
187
  query: cmd.query,
package/src/cli.ts CHANGED
@@ -141,7 +141,7 @@ class RootHelpCommand extends Command {
141
141
  resume: Flags.string({ char: "r", description: "Resume a session (by ID prefix, path, or picker if omitted)" }),
142
142
  "session-dir": Flags.string({ description: "Directory for session storage and lookup" }),
143
143
  "no-session": Flags.boolean({ description: "Don't save session (ephemeral)" }),
144
- models: Flags.string({ description: "Comma-separated model patterns for Ctrl+P cycling" }),
144
+ models: Flags.string({ description: "Comma-separated model patterns for Alt+N cycling" }),
145
145
  "no-tools": Flags.boolean({ description: "Disable all built-in tools" }),
146
146
  "no-lsp": Flags.boolean({ description: "Disable LSP tools, formatting, and diagnostics" }),
147
147
  "no-pty": Flags.boolean({ description: "Disable PTY-based interactive bash execution" }),
@@ -119,7 +119,7 @@ export default class Index extends Command {
119
119
  description: "Don't save session (ephemeral)",
120
120
  }),
121
121
  models: Flags.string({
122
- description: "Comma-separated model patterns for Ctrl+P cycling",
122
+ description: "Comma-separated model patterns for Alt+N cycling",
123
123
  }),
124
124
  "no-tools": Flags.boolean({
125
125
  description: "Disable all built-in tools",
@@ -8,6 +8,7 @@ export default class Ralplan extends Command {
8
8
  '$ gjc ralplan "<task description>"',
9
9
  '$ gjc ralplan --interactive --deliberate "<task description>"',
10
10
  '$ gjc ralplan --write --stage planner --stage_n 1 --artifact "<markdown or path>"',
11
+ "$ gjc ralplan --write --stage critic --stage_n 1 --artifact-env GJC_RALPLAN_ARTIFACT",
11
12
  ];
12
13
 
13
14
  async run(): Promise<void> {