@gajae-code/coding-agent 0.15.6 → 0.16.1

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 (270) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/args.d.ts +2 -0
  5. package/dist/types/cli/root-flags.d.ts +14 -0
  6. package/dist/types/cli/update-cli.d.ts +14 -0
  7. package/dist/types/cli.d.ts +7 -0
  8. package/dist/types/commands/launch.d.ts +10 -0
  9. package/dist/types/commands/sdk.d.ts +2 -0
  10. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  11. package/dist/types/config/billing-path.d.ts +25 -0
  12. package/dist/types/config/config-schema-version.d.ts +13 -1
  13. package/dist/types/config/file-lock.d.ts +5 -0
  14. package/dist/types/config/model-registry.d.ts +22 -0
  15. package/dist/types/config/settings-schema.d.ts +27 -8
  16. package/dist/types/config/settings.d.ts +2 -0
  17. package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
  18. package/dist/types/crash/record-loader.d.ts +15 -0
  19. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  20. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  21. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  22. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  23. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  24. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
  25. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  26. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  27. package/dist/types/main.d.ts +1 -0
  28. package/dist/types/master-mode/context.d.ts +21 -0
  29. package/dist/types/master-mode/first-request.d.ts +34 -0
  30. package/dist/types/master-mode/snapshot.d.ts +32 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  32. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  33. package/dist/types/modes/components/model-selector.d.ts +4 -0
  34. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  35. package/dist/types/modes/components/session-selector.d.ts +1 -1
  36. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  37. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  38. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  39. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  40. package/dist/types/modes/interactive-mode.d.ts +16 -2
  41. package/dist/types/modes/print-mode.d.ts +17 -0
  42. package/dist/types/modes/theme/theme.d.ts +2 -1
  43. package/dist/types/modes/types.d.ts +2 -0
  44. package/dist/types/modes/ui-language.d.ts +7 -1
  45. package/dist/types/sdk/broker/broker.d.ts +79 -2
  46. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  47. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  48. package/dist/types/sdk/broker/index.d.ts +3 -0
  49. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  50. package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
  51. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  52. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  53. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  54. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  55. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  56. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  57. package/dist/types/sdk/broker/state-version.d.ts +21 -1
  58. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  59. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  60. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  61. package/dist/types/sdk/bus/index.d.ts +9 -1
  62. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  63. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  64. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  65. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  67. package/dist/types/sdk/cli/index.d.ts +1 -0
  68. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  69. package/dist/types/sdk/cli/rows.d.ts +12 -5
  70. package/dist/types/sdk/cli/session-cli.d.ts +20 -3
  71. package/dist/types/sdk/client/client.d.ts +3 -2
  72. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  73. package/dist/types/sdk/host/control/index.d.ts +1 -1
  74. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  75. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  76. package/dist/types/sdk/host/events.d.ts +1 -0
  77. package/dist/types/sdk/host/host.d.ts +10 -0
  78. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  79. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  80. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  81. package/dist/types/sdk/host/session-runtime.d.ts +70 -17
  82. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  83. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  84. package/dist/types/sdk/lifecycle-session.d.ts +3 -1
  85. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  86. package/dist/types/sdk/receipt-state.d.ts +2 -0
  87. package/dist/types/sdk/router/session-router.d.ts +6 -0
  88. package/dist/types/sdk/session.d.ts +16 -0
  89. package/dist/types/sdk/startup-capability.d.ts +2 -1
  90. package/dist/types/sdk/turn-result.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +52 -7
  92. package/dist/types/session/credential-store-errors.d.ts +1 -0
  93. package/dist/types/session/fold-coordinator.d.ts +29 -2
  94. package/dist/types/session/session-manager.d.ts +79 -2
  95. package/dist/types/session/yield-queue.d.ts +5 -4
  96. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  97. package/dist/types/tools/ask.d.ts +1 -0
  98. package/dist/types/tools/bash.d.ts +16 -0
  99. package/dist/types/tools/browser/launch.d.ts +10 -0
  100. package/dist/types/tools/browser/registry.d.ts +8 -0
  101. package/dist/types/tools/gh.d.ts +8 -0
  102. package/dist/types/tools/index.d.ts +10 -10
  103. package/dist/types/tools/job.d.ts +3 -1
  104. package/dist/types/utils/external-editor.d.ts +7 -0
  105. package/examples/extensions/README.md +1 -1
  106. package/examples/extensions/pirate.ts +7 -4
  107. package/package.json +7 -7
  108. package/scripts/generate-sdk-operation-inventory.ts +13 -1
  109. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  110. package/src/async/job-manager.ts +107 -9
  111. package/src/browser-backend.ts +29 -0
  112. package/src/cli/accounts-cli.ts +9 -5
  113. package/src/cli/args.ts +17 -0
  114. package/src/cli/fast-help.ts +1 -0
  115. package/src/cli/plugin-cli.ts +29 -0
  116. package/src/cli/root-flags.ts +5 -0
  117. package/src/cli/update-cli.ts +140 -23
  118. package/src/commands/launch.ts +86 -18
  119. package/src/commands/sdk.ts +135 -28
  120. package/src/config/autorouting-tier-map.ts +12 -0
  121. package/src/config/billing-path.ts +42 -0
  122. package/src/config/config-schema-version.ts +18 -1
  123. package/src/config/file-lock.ts +158 -83
  124. package/src/config/model-preset-registry.ts +178 -23
  125. package/src/config/model-profiles.ts +5 -5
  126. package/src/config/model-registry.ts +291 -42
  127. package/src/config/model-resolver.ts +5 -0
  128. package/src/config/settings-schema.ts +27 -8
  129. package/src/config/settings.ts +51 -15
  130. package/src/coordinator-mcp/projection-scan.ts +27 -6
  131. package/src/coordinator-mcp/question-state.ts +9 -11
  132. package/src/coordinator-mcp/server.ts +115 -21
  133. package/src/crash/index-store.ts +2 -0
  134. package/src/crash/record-loader.ts +10 -0
  135. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  136. package/src/extensibility/extensions/runner.ts +27 -0
  137. package/src/extensibility/extensions/types.ts +8 -3
  138. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  139. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  140. package/src/extensibility/skills.ts +22 -12
  141. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  142. package/src/gjc-runtime/launch-worktree.ts +285 -4
  143. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  144. package/src/gjc-runtime/session-state-lock.ts +213 -14
  145. package/src/gjc-runtime/session-state-sidecar.ts +938 -39
  146. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  147. package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
  148. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  149. package/src/hooks/skill-state.ts +6 -0
  150. package/src/internal-urls/docs-index.generated.ts +15 -15
  151. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  152. package/src/main.ts +61 -3
  153. package/src/master-mode/context.ts +50 -0
  154. package/src/master-mode/first-request.ts +65 -0
  155. package/src/master-mode/snapshot.ts +205 -0
  156. package/src/modes/acp/acp-agent.ts +610 -161
  157. package/src/modes/components/footer.ts +4 -2
  158. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  159. package/src/modes/components/model-selector.ts +76 -8
  160. package/src/modes/components/oauth-selector.ts +6 -1
  161. package/src/modes/components/session-selector.ts +23 -15
  162. package/src/modes/components/sessions-dashboard.ts +8 -5
  163. package/src/modes/components/settings-selector.ts +1 -0
  164. package/src/modes/components/status-line/model-name.ts +53 -0
  165. package/src/modes/components/status-line/priority-row.ts +107 -0
  166. package/src/modes/components/status-line/segments.ts +23 -9
  167. package/src/modes/components/tool-status-header.ts +151 -15
  168. package/src/modes/controllers/command-controller.ts +1 -1
  169. package/src/modes/controllers/event-controller.ts +30 -4
  170. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  171. package/src/modes/controllers/input-controller.ts +30 -2
  172. package/src/modes/controllers/selector-controller.ts +200 -98
  173. package/src/modes/interactive-mode.ts +36 -12
  174. package/src/modes/print-mode.ts +64 -0
  175. package/src/modes/prompt-action-autocomplete.ts +30 -4
  176. package/src/modes/runtime-init.ts +12 -9
  177. package/src/modes/theme/theme.ts +5 -0
  178. package/src/modes/types.ts +2 -0
  179. package/src/modes/ui-language.ts +49 -2
  180. package/src/modes/utils/ui-helpers.ts +22 -3
  181. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  182. package/src/prompts/system/master-mode.md +28 -0
  183. package/src/prompts/tools/ask.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -0
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/sdk/acp/adapter.ts +5 -0
  187. package/src/sdk/broker/broker.ts +1646 -27
  188. package/src/sdk/broker/endpoint-authority.ts +86 -0
  189. package/src/sdk/broker/ensure.ts +81 -3
  190. package/src/sdk/broker/index.ts +3 -0
  191. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  192. package/src/sdk/broker/lifecycle.ts +440 -75
  193. package/src/sdk/broker/master-capability.ts +232 -0
  194. package/src/sdk/broker/session-index.ts +121 -17
  195. package/src/sdk/broker/session-scope.ts +272 -0
  196. package/src/sdk/broker/spawn-authority.ts +1031 -0
  197. package/src/sdk/broker/spawn-substrate.ts +591 -0
  198. package/src/sdk/broker/startup-budget.ts +144 -2
  199. package/src/sdk/broker/state-version.ts +43 -14
  200. package/src/sdk/broker/transport.ts +1 -0
  201. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  202. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  203. package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
  204. package/src/sdk/bus/discord-daemon.ts +50 -10
  205. package/src/sdk/bus/index.ts +84 -7
  206. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  207. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  208. package/src/sdk/bus/notification-service.ts +22 -5
  209. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  210. package/src/sdk/bus/reconciliation-store.ts +55 -31
  211. package/src/sdk/bus/slack-conversation.ts +1 -1
  212. package/src/sdk/bus/slack-daemon.ts +72 -38
  213. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  214. package/src/sdk/cli/index.ts +1 -0
  215. package/src/sdk/cli/master-cli.ts +259 -0
  216. package/src/sdk/cli/rows.ts +41 -15
  217. package/src/sdk/cli/session-cli.ts +297 -26
  218. package/src/sdk/client/client.ts +14 -8
  219. package/src/sdk/host/control/dispatch.ts +101 -40
  220. package/src/sdk/host/control/index.ts +2 -0
  221. package/src/sdk/host/control/operations.ts +2 -0
  222. package/src/sdk/host/control/runtime-gate.ts +203 -11
  223. package/src/sdk/host/events.ts +1 -0
  224. package/src/sdk/host/host.ts +29 -2
  225. package/src/sdk/host/query/cursor.ts +52 -7
  226. package/src/sdk/host/query/handlers.ts +33 -12
  227. package/src/sdk/host/query/revision-store.ts +12 -0
  228. package/src/sdk/host/session-runtime.test.ts +673 -14
  229. package/src/sdk/host/session-runtime.ts +674 -103
  230. package/src/sdk/host/surface-policy.ts +3 -3
  231. package/src/sdk/lifecycle/broker-client.ts +28 -0
  232. package/src/sdk/lifecycle/service.ts +279 -20
  233. package/src/sdk/lifecycle-session.ts +17 -4
  234. package/src/sdk/prompt-deadline-manager.ts +2 -1
  235. package/src/sdk/protocol/operation-inventory.generated.json +105 -3
  236. package/src/sdk/protocol/operation-registry.ts +14 -4
  237. package/src/sdk/receipt-state.ts +10 -0
  238. package/src/sdk/router/session-router.ts +219 -118
  239. package/src/sdk/session.ts +416 -195
  240. package/src/sdk/startup-capability.ts +1 -0
  241. package/src/sdk/turn-result.ts +22 -0
  242. package/src/session/agent-session.ts +1628 -318
  243. package/src/session/blob-store.ts +15 -5
  244. package/src/session/credential-store-errors.ts +2 -0
  245. package/src/session/fold-coordinator.ts +40 -3
  246. package/src/session/internal/managed-session-storage.ts +153 -78
  247. package/src/session/session-manager.ts +850 -97
  248. package/src/session/yield-queue.ts +24 -10
  249. package/src/setup/frictionless-onboarding.ts +2 -0
  250. package/src/slash-commands/builtin-registry.ts +41 -0
  251. package/src/task/executor.ts +9 -4
  252. package/src/telemetry/events.ts +1138 -12
  253. package/src/tools/ask-contract.test.ts +64 -0
  254. package/src/tools/ask-contract.ts +36 -1
  255. package/src/tools/ask.ts +10 -8
  256. package/src/tools/bash-interactive.ts +11 -2
  257. package/src/tools/bash.ts +412 -75
  258. package/src/tools/browser/launch.ts +37 -0
  259. package/src/tools/browser/registry.ts +99 -9
  260. package/src/tools/cron.ts +6 -4
  261. package/src/tools/descriptors.test.ts +15 -0
  262. package/src/tools/descriptors.ts +5 -1
  263. package/src/tools/gh.ts +104 -48
  264. package/src/tools/github-cache.ts +2 -2
  265. package/src/tools/index.ts +10 -6
  266. package/src/tools/job.ts +4 -1
  267. package/src/tools/monitor.ts +12 -9
  268. package/src/tools/resolve.ts +30 -10
  269. package/src/tools/tool-catalog.generated.ts +2 -2
  270. package/src/utils/external-editor.ts +72 -1
@@ -4,7 +4,10 @@ import {
4
4
  type Api,
5
5
  type AuthCredentialSelector,
6
6
  type CredentialRemovalTarget,
7
+ isSqliteCorruptionError,
8
+ isSqliteError,
7
9
  type Model,
10
+ OAuthCredentialSelectorError,
8
11
  resolveOAuthStorageProvider,
9
12
  } from "@gajae-code/ai/core";
10
13
  import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
@@ -106,6 +109,7 @@ import { TelegramDaemonController } from "../../sdk/bus/telegram-daemon-control"
106
109
  import { runTelegramSetup, type TelegramSetupPreflight } from "../../sdk/bus/telegram-setup";
107
110
  import { clearPersistentPinForRemovedRows } from "../../session/account-inventory";
108
111
  import type { DefaultFallbackRuntimeState } from "../../session/agent-session";
112
+ import { CREDENTIAL_STORE_UNREADABLE_MESSAGE } from "../../session/credential-store-errors";
109
113
  import { type SessionInfo, SessionManager } from "../../session/session-manager";
110
114
  import { getTreeForInternalRead } from "../../session/session-manager-internal";
111
115
  import { FileSessionStorage } from "../../session/session-storage";
@@ -125,6 +129,12 @@ import {
125
129
  logCredentialAutoImportFailures,
126
130
  runExternalCredentialAutoImport,
127
131
  } from "../../setup/credential-auto-import";
132
+
133
+ function credentialStoreOperationError(action: "Login" | "Logout", error: unknown): string {
134
+ const code = error && typeof error === "object" ? (error as { code?: unknown }).code : undefined;
135
+ return `${action} failed: credential store operation failed${typeof code === "string" ? ` (${code})` : ""}.`;
136
+ }
137
+
128
138
  import {
129
139
  filterAutoImportOAuthCredentials,
130
140
  formatDiscoverySummary,
@@ -172,6 +182,7 @@ import {
172
182
  FrictionlessOnboardingSelectorComponent,
173
183
  type FrictionlessOnboardingStage,
174
184
  getFrictionlessOnboardingCopy,
185
+ InterfaceLanguageSelectorComponent,
175
186
  } from "../components/frictionless-onboarding-selector";
176
187
  import type { PetMode } from "../components/gajae-pet-widget";
177
188
  import { HistorySearchComponent } from "../components/history-search";
@@ -216,6 +227,7 @@ import type { SessionObserverRegistry } from "../session-observer-registry";
216
227
  import { buildOAuthLoginAnchor, createOAuthUrlCopyLease } from "../shared/oauth-url-copy";
217
228
  import type { TasksAggregator } from "../tasks-aggregator";
218
229
  import type { TranscriptItemRegistry } from "../transcript-item-registry";
230
+ import { resolveExplicitUiLanguage, type UiLanguage } from "../ui-language";
219
231
  import { acquireResumeProgressLease, type ResumeProgressLease } from "../utils/ui-helpers";
220
232
 
221
233
  const CALLBACK_SERVER_PROVIDERS = new Set<string>([
@@ -1366,11 +1378,16 @@ export class SelectorController {
1366
1378
  async #refreshOAuthProviderAuthState(): Promise<void> {
1367
1379
  const oauthProviders = getOAuthProviders();
1368
1380
  await Promise.all(
1369
- oauthProviders.map(provider =>
1370
- this.ctx.session.modelRegistry
1371
- .getApiKeyForProvider(provider.id, this.ctx.session.credentialSessionId)
1372
- .catch(() => undefined),
1373
- ),
1381
+ oauthProviders.map(async provider => {
1382
+ try {
1383
+ await this.ctx.session.modelRegistry.getApiKeyForProvider(
1384
+ provider.id,
1385
+ this.ctx.session.credentialSessionId,
1386
+ );
1387
+ } catch (error) {
1388
+ if (isSqliteError(error)) throw error;
1389
+ }
1390
+ }),
1374
1391
  );
1375
1392
  }
1376
1393
  /**
@@ -1469,11 +1486,57 @@ export class SelectorController {
1469
1486
 
1470
1487
  async showFrictionlessOnboarding(): Promise<void> {
1471
1488
  const agentDir = this.ctx.session.getSessionAgentDir();
1489
+ const languagePreference = resolveExplicitUiLanguage(
1490
+ this.ctx.settings.has("ui.language") ? this.ctx.settings.get("ui.language") : undefined,
1491
+ process.env.GJC_UI_LANGUAGE,
1492
+ );
1493
+ let selectedLanguage: UiLanguage = languagePreference.language;
1494
+ if (!languagePreference.hasPreference && this.ctx.settings.canPersistDurableConfig()) {
1495
+ const decision = Promise.withResolvers<UiLanguage | undefined>();
1496
+ let settled = false;
1497
+ let unregisterStop: (() => void) | undefined;
1498
+ const finish = (language: UiLanguage | undefined): void => {
1499
+ if (settled) return;
1500
+ settled = true;
1501
+ unregisterStop?.();
1502
+ decision.resolve(language);
1503
+ };
1504
+ this.showSelector(done => {
1505
+ unregisterStop = this.ctx.onStop(() => {
1506
+ done();
1507
+ finish(undefined);
1508
+ });
1509
+ const selector = new InterfaceLanguageSelectorComponent(
1510
+ language => {
1511
+ done();
1512
+ finish(language);
1513
+ },
1514
+ () => {
1515
+ done();
1516
+ finish(undefined);
1517
+ },
1518
+ );
1519
+ return { component: selector, focus: selector };
1520
+ });
1521
+ selectedLanguage = (await decision.promise) ?? "en";
1522
+ try {
1523
+ await this.ctx.settings.commitAtomicBatch([{ path: "ui.language", op: "set", value: selectedLanguage }]);
1524
+ } catch (error) {
1525
+ this.ctx.showError(
1526
+ error instanceof Error
1527
+ ? `${error.message}; continuing in English.`
1528
+ : "Cannot save the interface language; continuing in English.",
1529
+ );
1530
+ selectedLanguage = "en";
1531
+ }
1532
+ } else if (!languagePreference.hasPreference) {
1533
+ this.ctx.showError("Cannot save the interface language; continuing in English.");
1534
+ }
1472
1535
  const presence = await discoverOnboardingRootPresence();
1473
1536
  // An explicit `/language` (or settings) selection outranks locale and transcript evidence.
1474
1537
  const profileOptions = {
1475
1538
  osLocale: Intl.DateTimeFormat().resolvedOptions().locale,
1476
- ...(this.ctx.settings.has("ui.language") ? { preferredLanguage: this.ctx.settings.get("ui.language") } : {}),
1539
+ preferredLanguage: selectedLanguage,
1477
1540
  };
1478
1541
  const initialProfile = deriveOnboardingProfile([], profileOptions);
1479
1542
 
@@ -2181,8 +2244,8 @@ export class SelectorController {
2181
2244
  case "followUpMode":
2182
2245
  this.ctx.session.setFollowUpMode(value as "all" | "one-at-a-time");
2183
2246
  break;
2184
- case "interruptMode":
2185
- this.ctx.session.setInterruptMode(value as "immediate" | "wait");
2247
+ case "toolInterruptPolicy":
2248
+ this.ctx.session.setToolInterruptPolicy(value as "abort_tools" | "finish_tools");
2186
2249
  break;
2187
2250
  case "thinkingLevel":
2188
2251
  case "defaultThinkingLevel":
@@ -3557,7 +3620,8 @@ export class SelectorController {
3557
3620
  }
3558
3621
  this.ctx.ui.requestRender();
3559
3622
  } catch (error: unknown) {
3560
- this.ctx.showError(`Login failed: ${error instanceof Error ? error.message : String(error)}`);
3623
+ if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
3624
+ else this.ctx.showError(`Login failed: ${error instanceof Error ? error.message : String(error)}`);
3561
3625
  } finally {
3562
3626
  oauthUrlCopyLease.release();
3563
3627
  if (useManualInput) {
@@ -3599,7 +3663,8 @@ export class SelectorController {
3599
3663
  `Successfully removed ${result.ids.length} stored credential${result.ids.length === 1 ? "" : "s"} from ${providerId}.`,
3600
3664
  );
3601
3665
  } catch (error: unknown) {
3602
- this.ctx.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
3666
+ if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
3667
+ else this.ctx.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
3603
3668
  }
3604
3669
  }
3605
3670
 
@@ -3616,75 +3681,96 @@ export class SelectorController {
3616
3681
  return;
3617
3682
  }
3618
3683
  const authStorage = this.ctx.session.modelRegistry.authStorage;
3619
- const hasOAuthInventory = authStorage
3620
- .listCredentialInventory(selectedProviderId)
3621
- .some(row => row.provider === selectedProviderId && row.credentialKind === "oauth");
3622
- const hasRemovableCredential = authStorage
3623
- .listCredentialRemovalTargets(selectedProviderId)
3624
- .some(target => target.provider === selectedProviderId);
3625
- if (mode === "logout" && hasOAuthInventory && !hasRemovableCredential) {
3626
- await this.#handleLogout(selectedProviderId);
3627
- return;
3628
- }
3629
- if (hasOAuthInventory && !options?.manualCode) {
3630
- this.showSelector(done => {
3631
- let selector: OAuthSelectorComponent;
3632
- selector = new OAuthSelectorComponent(
3633
- mode,
3634
- authStorage,
3635
- () => undefined,
3636
- () => {
3637
- selector.stopValidation();
3638
- done();
3639
- this.ctx.ui.requestRender();
3640
- },
3641
- {
3642
- accountProviderId: selectedProviderId,
3643
- onAccountSelect: async (selectorValue: AuthCredentialSelector) => {
3644
- await this.ctx.session.setCredentialPin(selectedProviderId, selectorValue);
3645
- selector.stopValidation();
3646
- done();
3647
- this.ctx.showStatus(`Pinned OAuth account for ${selectedProviderId} to this session.`);
3648
- },
3649
- onAutoSelect: async () => {
3650
- await this.ctx.session.setCredentialAuto(selectedProviderId);
3651
- selector.stopValidation();
3652
- done();
3653
- this.ctx.showStatus(`Using AUTO (ranked) OAuth accounts for ${selectedProviderId}.`);
3654
- },
3655
- onAddAccount: async () => {
3684
+ try {
3685
+ const hasOAuthInventory = authStorage
3686
+ .listCredentialInventory(selectedProviderId)
3687
+ .some(row => row.provider === selectedProviderId && row.credentialKind === "oauth");
3688
+ const hasRemovableCredential = authStorage
3689
+ .listCredentialRemovalTargets(selectedProviderId)
3690
+ .some(target => target.provider === selectedProviderId);
3691
+ if (mode === "logout" && hasOAuthInventory && !hasRemovableCredential) {
3692
+ await this.#handleLogout(selectedProviderId);
3693
+ return;
3694
+ }
3695
+ if (hasOAuthInventory && !options?.manualCode) {
3696
+ this.showSelector(done => {
3697
+ let selector: OAuthSelectorComponent;
3698
+ selector = new OAuthSelectorComponent(
3699
+ mode,
3700
+ authStorage,
3701
+ () => undefined,
3702
+ () => {
3656
3703
  selector.stopValidation();
3657
3704
  done();
3658
- await this.#handleOAuthLogin(selectedProviderId, options);
3705
+ this.ctx.ui.requestRender();
3659
3706
  },
3660
- onAccountRemove: async targets => {
3661
- await this.#handleLogout(selectedProviderId, targets);
3662
- selector.stopValidation();
3663
- done();
3707
+ {
3708
+ accountProviderId: selectedProviderId,
3709
+ onAccountSelect: async (selectorValue: AuthCredentialSelector) => {
3710
+ try {
3711
+ await this.ctx.session.setCredentialPin(selectedProviderId, selectorValue);
3712
+ } catch (error: unknown) {
3713
+ if (!(error instanceof OAuthCredentialSelectorError)) throw error;
3714
+ this.ctx.showError(error.message);
3715
+ return;
3716
+ }
3717
+ selector.stopValidation();
3718
+ done();
3719
+ this.ctx.showStatus(`Pinned OAuth account for ${selectedProviderId} to this session.`);
3720
+ },
3721
+ onAutoSelect: async () => {
3722
+ await this.ctx.session.setCredentialAuto(selectedProviderId);
3723
+ selector.stopValidation();
3724
+ done();
3725
+ this.ctx.showStatus(`Using AUTO (ranked) OAuth accounts for ${selectedProviderId}.`);
3726
+ },
3727
+ onAddAccount: async () => {
3728
+ selector.stopValidation();
3729
+ done();
3730
+ await this.#handleOAuthLogin(selectedProviderId, options);
3731
+ },
3732
+ onAccountRemove: async targets => {
3733
+ await this.#handleLogout(selectedProviderId, targets);
3734
+ selector.stopValidation();
3735
+ done();
3736
+ },
3664
3737
  },
3665
- },
3666
- );
3667
- return { component: selector, focus: selector };
3668
- });
3738
+ );
3739
+ return { component: selector, focus: selector };
3740
+ });
3741
+ return;
3742
+ }
3743
+ if (mode === "login") {
3744
+ await this.#handleOAuthLogin(selectedProviderId, options);
3745
+ return;
3746
+ }
3747
+ // mode === "logout" with no OAuth accounts: remove stored api-key credentials directly.
3748
+ await this.#handleLogout(selectedProviderId);
3669
3749
  return;
3670
- }
3671
- if (mode === "login") {
3672
- await this.#handleOAuthLogin(selectedProviderId, options);
3750
+ } catch (error: unknown) {
3751
+ if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
3752
+ else if (isSqliteError(error))
3753
+ this.ctx.showError(credentialStoreOperationError(mode === "login" ? "Login" : "Logout", error));
3754
+ else throw error;
3673
3755
  return;
3674
3756
  }
3675
- // mode === "logout" with no OAuth accounts: remove stored api-key credentials directly.
3676
- await this.#handleLogout(selectedProviderId);
3677
- return;
3678
3757
  }
3679
3758
 
3680
3759
  if (mode === "logout") {
3681
- await this.#refreshOAuthProviderAuthState();
3682
- const oauthProviders = getOAuthProviders();
3683
- const loggedInProviders = oauthProviders.filter(provider =>
3684
- this.ctx.session.modelRegistry.authStorage.hasAuth(provider.id),
3685
- );
3686
- if (loggedInProviders.length === 0) {
3687
- this.ctx.showStatus("No OAuth providers logged in. Use /login first.");
3760
+ try {
3761
+ await this.#refreshOAuthProviderAuthState();
3762
+ const oauthProviders = getOAuthProviders();
3763
+ const loggedInProviders = oauthProviders.filter(provider =>
3764
+ this.ctx.session.modelRegistry.authStorage.hasAuth(provider.id),
3765
+ );
3766
+ if (loggedInProviders.length === 0) {
3767
+ this.ctx.showStatus("No OAuth providers logged in. Use /login first.");
3768
+ return;
3769
+ }
3770
+ } catch (error: unknown) {
3771
+ if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
3772
+ else if (isSqliteError(error)) this.ctx.showError(credentialStoreOperationError("Logout", error));
3773
+ else throw error;
3688
3774
  return;
3689
3775
  }
3690
3776
  }
@@ -3807,37 +3893,53 @@ export class SelectorController {
3807
3893
  }
3808
3894
  }
3809
3895
  }
3810
- this.showSelector(done => {
3811
- let selector: OAuthSelectorComponent;
3812
- selector = new OAuthSelectorComponent(
3813
- mode,
3814
- this.ctx.session.modelRegistry.authStorage,
3815
- async (selectedProviderId: string) => {
3816
- selector.stopValidation();
3817
- done();
3818
- await this.showOAuthSelector(mode, selectedProviderId);
3819
- },
3820
- () => {
3821
- selector.stopValidation();
3822
- done();
3823
- this.ctx.ui.requestLayoutRender("oauth-selector-close");
3824
- },
3825
- {
3826
- validateAuth: async (selectedProviderId: string) => {
3827
- const apiKey = await this.ctx.session.modelRegistry.getApiKeyForProvider(
3828
- selectedProviderId,
3829
- this.ctx.session.credentialSessionId,
3830
- );
3831
- return !!apiKey;
3896
+ try {
3897
+ this.showSelector(done => {
3898
+ let selector: OAuthSelectorComponent;
3899
+ selector = new OAuthSelectorComponent(
3900
+ mode,
3901
+ this.ctx.session.modelRegistry.authStorage,
3902
+ async (selectedProviderId: string) => {
3903
+ selector.stopValidation();
3904
+ done();
3905
+ await this.showOAuthSelector(mode, selectedProviderId);
3832
3906
  },
3833
- requestRender: () => {
3834
- this.ctx.ui.requestLayoutRender("oauth-selector-spinner");
3907
+ () => {
3908
+ selector.stopValidation();
3909
+ done();
3910
+ this.ctx.ui.requestLayoutRender("oauth-selector-close");
3835
3911
  },
3836
- externalCredentialCandidates,
3837
- },
3838
- );
3839
- return { component: selector, focus: selector };
3840
- });
3912
+ {
3913
+ validateAuth: async (selectedProviderId: string) => {
3914
+ const apiKey = await this.ctx.session.modelRegistry.getApiKeyForProvider(
3915
+ selectedProviderId,
3916
+ this.ctx.session.credentialSessionId,
3917
+ );
3918
+ return !!apiKey;
3919
+ },
3920
+ onValidationError: error => {
3921
+ if (!isSqliteError(error)) return false;
3922
+ selector.stopValidation();
3923
+ done();
3924
+ this.ctx.showError(
3925
+ isSqliteCorruptionError(error)
3926
+ ? CREDENTIAL_STORE_UNREADABLE_MESSAGE
3927
+ : credentialStoreOperationError("Login", error),
3928
+ );
3929
+ return true;
3930
+ },
3931
+ requestRender: () => {
3932
+ this.ctx.ui.requestLayoutRender("oauth-selector-spinner");
3933
+ },
3934
+ externalCredentialCandidates,
3935
+ },
3936
+ );
3937
+ return { component: selector, focus: selector };
3938
+ });
3939
+ } catch (error: unknown) {
3940
+ if (!isSqliteCorruptionError(error)) throw error;
3941
+ this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
3942
+ }
3841
3943
  }
3842
3944
 
3843
3945
  showDebugSelector(): void {
@@ -45,7 +45,7 @@ import {
45
45
  starReminderLaunchGate,
46
46
  } from "../reminders/star-reminder";
47
47
  import type { NotificationSessionReconcileResult, NotificationSessionStatus } from "../sdk/bus/session-control";
48
- import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
48
+ import type { AgentSession, AgentSessionEvent, AsyncJobSnapshotItem } from "../session/agent-session";
49
49
  import type { HistoryStorage } from "../session/history-storage";
50
50
  import type { SessionContext, SessionManager } from "../session/session-manager";
51
51
  import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
@@ -186,18 +186,42 @@ export function getComposerPlaceholder(
186
186
  return buildComposerPlaceholder(keybindings, context, options);
187
187
  }
188
188
 
189
+ /** Running background work split into the three kinds the activity indicator names. */
190
+ export interface BackgroundActivityTally {
191
+ subagents: number;
192
+ backgroundBash: number;
193
+ monitors: number;
194
+ }
195
+
196
+ /**
197
+ * Classify running jobs for the activity indicator using the three locked
198
+ * predicates: subagents are `task` jobs carrying subagent metadata, monitors
199
+ * are flagged by `metadata.monitor`, and background bash is a `bash` job that
200
+ * is not a monitor. A running job matching none of them (a task job without
201
+ * subagent metadata) is outside this indicator and is not counted.
202
+ */
203
+ export function tallyBackgroundActivity(running: readonly AsyncJobSnapshotItem[]): BackgroundActivityTally {
204
+ const tally: BackgroundActivityTally = { subagents: 0, backgroundBash: 0, monitors: 0 };
205
+ for (const item of running) {
206
+ if (item.type === "task" && item.metadata?.subagent !== undefined) tally.subagents += 1;
207
+ else if (item.metadata?.monitor === true) tally.monitors += 1;
208
+ else if (item.type === "bash" && item.metadata?.backgrounded === true) tally.backgroundBash += 1;
209
+ }
210
+ return tally;
211
+ }
212
+
189
213
  export function resolveActivityIndicatorMessage(
190
214
  foregroundActive: boolean,
191
- activeBackgroundTasks: number,
215
+ activity: BackgroundActivityTally,
192
216
  foregroundMessage: string,
193
217
  ): string | undefined {
194
- const backgroundCount = Math.max(0, Math.trunc(activeBackgroundTasks));
195
- if (foregroundActive) {
196
- if (backgroundCount === 0) return foregroundMessage;
197
- return `${foregroundMessage} · ${backgroundCount} background task${backgroundCount === 1 ? "" : "s"}`;
198
- }
199
- if (backgroundCount === 0) return undefined;
200
- return `Background: ${backgroundCount} task${backgroundCount === 1 ? "" : "s"}…`;
218
+ const parts: string[] = [];
219
+ if (activity.subagents > 0) parts.push(`${activity.subagents} subagent${activity.subagents === 1 ? "" : "s"}`);
220
+ if (activity.backgroundBash > 0) parts.push(`${activity.backgroundBash} background bash`);
221
+ if (activity.monitors > 0) parts.push(`${activity.monitors} monitor${activity.monitors === 1 ? "" : "s"}`);
222
+
223
+ if (foregroundActive) return parts.length === 0 ? foregroundMessage : `${foregroundMessage} · ${parts.join(", ")}`;
224
+ return parts.length === 0 ? undefined : `Background: ${parts.join(", ")}…`;
201
225
  }
202
226
  const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
203
227
  const COMPOSER_RIGHT_GUTTER_WIDTH = 1;
@@ -1926,8 +1950,8 @@ export class InteractiveMode implements InteractiveModeContext {
1926
1950
  return main && dim ? { main, dim } : undefined;
1927
1951
  }
1928
1952
 
1929
- #activeBackgroundTaskCount(): number {
1930
- return this.session.getAsyncJobSnapshot()?.running.length ?? 0;
1953
+ #activeBackgroundActivity(): BackgroundActivityTally {
1954
+ return tallyBackgroundActivity(this.session.getAsyncJobSnapshot()?.running ?? []);
1931
1955
  }
1932
1956
 
1933
1957
  #stopLoadingAnimation(): void {
@@ -1946,7 +1970,7 @@ export class InteractiveMode implements InteractiveModeContext {
1946
1970
  }
1947
1971
  const message = resolveActivityIndicatorMessage(
1948
1972
  foregroundActive,
1949
- this.#activeBackgroundTaskCount(),
1973
+ this.#activeBackgroundActivity(),
1950
1974
  this.#pendingWorkingMessage ?? this.#defaultWorkingMessage,
1951
1975
  );
1952
1976
  if (!message) {
@@ -5,6 +5,7 @@
5
5
  * - `gjc -p "prompt"` - text output
6
6
  * - `gjc --mode json "prompt"` - JSON event stream
7
7
  */
8
+ import type { AgentMessage } from "@gajae-code/agent-core";
8
9
  import { type AssistantMessage, type ImageContent, isContextOverflow } from "@gajae-code/ai/core";
9
10
  import { isKnownSinkPeerClosedError, logger, sanitizeText } from "@gajae-code/utils";
10
11
  import { loadSlashCommands } from "../extensibility/slash-commands";
@@ -46,6 +47,48 @@ export interface PrintModeOptions {
46
47
  */
47
48
  export const CONTEXT_OVERFLOW_EXIT_CODE = 78;
48
49
 
50
+ const EMPTY_TURN_ERROR = {
51
+ code: "empty_response",
52
+ message: "Turn completed without assistant text.",
53
+ } as const;
54
+
55
+ export type PrintTerminalDisposition =
56
+ | { status: "success"; assistantText: true }
57
+ | { status: "failure"; error: typeof EMPTY_TURN_ERROR | { code: "assistant_error"; message: string } };
58
+
59
+ function assistantText(message: AssistantMessage): string {
60
+ return message.content
61
+ .filter((content): content is { type: "text"; text: string } => content.type === "text")
62
+ .map(content => content.text)
63
+ .join("");
64
+ }
65
+
66
+ export function resolvePrintTerminalDisposition(
67
+ messages: readonly AgentMessage[],
68
+ submittedPrompt: boolean,
69
+ turnStartIndex?: number,
70
+ ): PrintTerminalDisposition | undefined {
71
+ if (!submittedPrompt) return undefined;
72
+ const turnMessages = turnStartIndex === undefined ? messages : messages.slice(turnStartIndex);
73
+ const lastAssistant = turnMessages.findLast((message): message is AssistantMessage => message.role === "assistant");
74
+ if (!lastAssistant) return { status: "failure", error: EMPTY_TURN_ERROR };
75
+ if (
76
+ (lastAssistant.stopReason === "error" || lastAssistant.stopReason === "aborted") &&
77
+ !isSilentAbort(lastAssistant.errorMessage)
78
+ ) {
79
+ return {
80
+ status: "failure",
81
+ error: {
82
+ code: "assistant_error",
83
+ message: sanitizeText(lastAssistant.errorMessage || `Request ${lastAssistant.stopReason}`),
84
+ },
85
+ };
86
+ }
87
+ if (isSilentAbort(lastAssistant.errorMessage)) return undefined;
88
+ if (assistantText(lastAssistant).trim().length === 0) return { status: "failure", error: EMPTY_TURN_ERROR };
89
+ return { status: "success", assistantText: true };
90
+ }
91
+
49
92
  /**
50
93
  * Build an actionable stderr diagnostic for a terminal context-overflow error in
51
94
  * text mode. The raw provider message is preserved (appended) for debugging, but
@@ -201,6 +244,8 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
201
244
  let unsubscribe: (() => void) | undefined;
202
245
  let submittedPrompt = false;
203
246
  let consumedCommand = false;
247
+ let jsonFailureObserved = false;
248
+ let lastPromptMessageStart: number | undefined;
204
249
 
205
250
  try {
206
251
  for (const warning of new Set(session.configWarnings)) {
@@ -237,6 +282,14 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
237
282
  // when it must render the JSON event stream.
238
283
  if (mode === "json") {
239
284
  unsubscribe = session.subscribe(event => {
285
+ if (
286
+ event.type === "agent_failed" ||
287
+ (event.type === "message_end" &&
288
+ event.message.role === "assistant" &&
289
+ (event.message.stopReason === "error" || event.message.stopReason === "aborted"))
290
+ ) {
291
+ jsonFailureObserved = true;
292
+ }
240
293
  stdout.write(`${JSON.stringify(event)}\n`);
241
294
  });
242
295
  }
@@ -265,6 +318,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
265
318
  if (command !== false) text = command.prompt;
266
319
  }
267
320
  submittedPrompt = true;
321
+ lastPromptMessageStart = session.state.messages.length;
268
322
  await session.prompt(text, images ? { images } : undefined);
269
323
  };
270
324
 
@@ -277,6 +331,16 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
277
331
  await logger.time("print:prompt:next", () => submitInput(message));
278
332
  }
279
333
 
334
+ const terminal = resolvePrintTerminalDisposition(session.state.messages, submittedPrompt, lastPromptMessageStart);
335
+ if (terminal?.status === "failure") {
336
+ if (!options.suppressProcessExit) process.exitCode = 1;
337
+ if (mode === "json" && !jsonFailureObserved) {
338
+ stdout.write(`${JSON.stringify({ type: "agent_failed", error: terminal.error })}\n`);
339
+ } else if (mode === "text" && terminal.error.code === EMPTY_TURN_ERROR.code) {
340
+ await writeStderrAndQuiesce(`${terminal.error.message}\n`);
341
+ }
342
+ }
343
+
280
344
  // In text mode, output final response.
281
345
  if (mode === "text" && (!consumedCommand || submittedPrompt)) {
282
346
  const lastMessage = session.state.messages.findLast(message => message.role === "assistant");
@@ -6,6 +6,8 @@ import {
6
6
  CombinedAutocompleteProvider,
7
7
  extractSlashCommandTokenPrefix,
8
8
  getSlashCommandMatchRank,
9
+ hasInlineCodeDelimiter,
10
+ isInsideInlineCodeSpan,
9
11
  normalizeFuzzyText,
10
12
  type SlashCommand,
11
13
  } from "@gajae-code/tui";
@@ -140,7 +142,7 @@ function withoutSkillCommandSuggestions(
140
142
  suggestions: { items: AutocompleteItem[]; prefix: string } | null,
141
143
  ): { items: AutocompleteItem[]; prefix: string } | null {
142
144
  if (!suggestions) return null;
143
- const items = suggestions.items.filter(item => !item.value.startsWith("skill:"));
145
+ const items = suggestions.items.filter(item => !item.value.toLowerCase().startsWith("skill:"));
144
146
  return items.length > 0 ? { ...suggestions, items } : null;
145
147
  }
146
148
 
@@ -160,6 +162,21 @@ function getSlashTokenPrefix(textBeforeCursor: string): string | null {
160
162
  return extractSlashCommandTokenPrefix(textBeforeCursor);
161
163
  }
162
164
 
165
+ function getRawSlashTokenPrefix(textBeforeCursor: string): string | null {
166
+ return textBeforeCursor.match(/(?:^|\s)(\/[^\s]*)$/)?.[1] ?? null;
167
+ }
168
+
169
+ function isSlashTokenInsideInlineCodeSpan(
170
+ lines: string[],
171
+ cursorLine: number,
172
+ textBeforeCursor: string,
173
+ prefix: string,
174
+ ): boolean {
175
+ const tokenStart = textBeforeCursor.length - prefix.length;
176
+ const textBeforeToken = [...lines.slice(0, cursorLine), textBeforeCursor.slice(0, tokenStart)].join("\n");
177
+ return isInsideInlineCodeSpan(textBeforeToken);
178
+ }
179
+
163
180
  export class PromptActionAutocompleteProvider implements AutocompleteProvider {
164
181
  #baseProvider: CombinedAutocompleteProvider;
165
182
  #actions: PromptActionDefinition[];
@@ -211,8 +228,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
211
228
  }
212
229
  }
213
230
 
231
+ const rawSlashPrefix = getRawSlashTokenPrefix(textBeforeCursor);
214
232
  const slashPrefix = getSlashTokenPrefix(textBeforeCursor);
215
- if (slashPrefix) {
233
+ const slashTokenInsideCode =
234
+ rawSlashPrefix !== null &&
235
+ (hasInlineCodeDelimiter(rawSlashPrefix) ||
236
+ isSlashTokenInsideInlineCodeSpan(lines, cursorLine, textBeforeCursor, rawSlashPrefix));
237
+ if (slashPrefix && !slashTokenInsideCode) {
216
238
  const baseSuggestions = withoutSkillCommandSuggestions(
217
239
  await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol),
218
240
  );
@@ -225,6 +247,9 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
225
247
  this.#commands,
226
248
  );
227
249
  }
250
+ if (rawSlashPrefix && slashTokenInsideCode) {
251
+ return withoutSkillCommandSuggestions(await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol));
252
+ }
228
253
 
229
254
  if (!isSettingsInitialized() || settings.get("emojiAutocomplete")) {
230
255
  const emojiSuggestions = getEmojiSuggestions(textBeforeCursor);
@@ -282,12 +307,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
282
307
  const currentLine = lines[cursorLine] || "";
283
308
  const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
284
309
  const afterCursor = currentLine.slice(cursorCol);
310
+ const separator = afterCursor.length === 0 ? " " : "";
285
311
  const newLines = [...lines];
286
- newLines[cursorLine] = `${beforePrefix}/${item.value} ${afterCursor}`;
312
+ newLines[cursorLine] = `${beforePrefix}/${item.value}${separator}${afterCursor}`;
287
313
  return {
288
314
  lines: newLines,
289
315
  cursorLine,
290
- cursorCol: beforePrefix.length + item.value.length + 2,
316
+ cursorCol: beforePrefix.length + item.value.length + 1 + separator.length,
291
317
  };
292
318
  }
293
319
  return this.#baseProvider.applyCompletion(lines, cursorLine, cursorCol, item, prefix);