@gajae-code/coding-agent 0.18.7 → 0.18.8

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 (279) hide show
  1. package/CHANGELOG.md +279 -1
  2. package/dist/types/async/job-manager.d.ts +1 -1
  3. package/dist/types/capability/mcp.d.ts +2 -0
  4. package/dist/types/capability/skill.d.ts +2 -0
  5. package/dist/types/capability/types.d.ts +2 -0
  6. package/dist/types/cli/public-command-errors.d.ts +3 -1
  7. package/dist/types/cli/setup-cli.d.ts +1 -1
  8. package/dist/types/commands/deep-interview.d.ts +3 -0
  9. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  10. package/dist/types/config/file-lock.d.ts +1 -0
  11. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  12. package/dist/types/config/model-profile-activation.d.ts +11 -3
  13. package/dist/types/config/model-registry.d.ts +4 -9
  14. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  15. package/dist/types/cursor-delete-path.d.ts +1 -0
  16. package/dist/types/cursor-mcp-name.d.ts +8 -0
  17. package/dist/types/customization/inventory.d.ts +2 -0
  18. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  19. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  20. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  21. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  22. package/dist/types/discovery/helpers.d.ts +6 -1
  23. package/dist/types/export/html/css-color.d.ts +2 -0
  24. package/dist/types/export/html/template.generated.d.ts +1 -1
  25. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  26. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  27. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  28. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  29. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  30. package/dist/types/extensibility/skill-management.d.ts +3 -1
  31. package/dist/types/extensibility/skills.d.ts +2 -0
  32. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  33. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  34. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  35. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  36. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  37. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  38. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  39. package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
  40. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  41. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  42. package/dist/types/lsp/workspace-path.d.ts +1 -0
  43. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  44. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  45. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  46. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  47. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  48. package/dist/types/modes/interactive-mode.d.ts +1 -0
  49. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  50. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  51. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  52. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  53. package/dist/types/progress/progress-contract.d.ts +154 -0
  54. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  55. package/dist/types/progress/project-progress.d.ts +141 -0
  56. package/dist/types/progress/render-progress.d.ts +28 -0
  57. package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
  58. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  59. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  60. package/dist/types/runtime-mcp/types.d.ts +6 -0
  61. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  62. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  63. package/dist/types/sdk/broker/broker.d.ts +18 -1
  64. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  65. package/dist/types/sdk/bus/index.d.ts +12 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  67. package/dist/types/sdk/client/client.d.ts +2 -0
  68. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  69. package/dist/types/sdk/index.d.ts +1 -0
  70. package/dist/types/sdk/router/session-router.d.ts +2 -0
  71. package/dist/types/sdk/session-list.d.ts +1 -1
  72. package/dist/types/sdk/session.d.ts +8 -0
  73. package/dist/types/session/agent-session.d.ts +38 -9
  74. package/dist/types/session/artifacts.d.ts +2 -2
  75. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  76. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  77. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  78. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  79. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  80. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  81. package/dist/types/session/session-manager.d.ts +31 -5
  82. package/dist/types/session/session-retirement.d.ts +31 -7
  83. package/dist/types/session/session-storage.d.ts +62 -2
  84. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  85. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  86. package/dist/types/setup/credential-import.d.ts +6 -4
  87. package/dist/types/skill-state/active-state.d.ts +38 -0
  88. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  89. package/dist/types/task/executor.d.ts +3 -0
  90. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  91. package/dist/types/tools/index.d.ts +3 -0
  92. package/dist/types/tools/python.d.ts +9 -1
  93. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  94. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  95. package/dist/types/tools/todo-write.d.ts +6 -0
  96. package/dist/types/utils/tools-checksum.d.ts +7 -0
  97. package/package.json +7 -7
  98. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  99. package/scripts/verify-managed-empty-stop.ts +28 -0
  100. package/src/async/job-manager.ts +228 -122
  101. package/src/capability/index.ts +17 -0
  102. package/src/capability/mcp.ts +2 -0
  103. package/src/capability/skill.ts +2 -0
  104. package/src/capability/types.ts +2 -0
  105. package/src/cli/auth-broker-cli.ts +1 -1
  106. package/src/cli/customize-doctor.ts +15 -2
  107. package/src/cli/public-command-errors.ts +21 -1
  108. package/src/cli/setup-cli.ts +4 -4
  109. package/src/cli/skills-cli.ts +5 -0
  110. package/src/commands/deep-interview.ts +47 -8
  111. package/src/commands/sdk.ts +28 -8
  112. package/src/config/autorouting-tier-map.ts +117 -7
  113. package/src/config/file-lock.ts +307 -68
  114. package/src/config/mcp-schema.json +4 -0
  115. package/src/config/model-bindings-applier.ts +20 -0
  116. package/src/config/model-profile-activation.ts +240 -46
  117. package/src/config/model-registry.ts +47 -153
  118. package/src/config/skill-settings-defaults.ts +7 -0
  119. package/src/coordinator-mcp/policy.ts +3 -2
  120. package/src/cursor-delete-path.ts +10 -0
  121. package/src/cursor-mcp-name.ts +15 -0
  122. package/src/cursor.ts +13 -2
  123. package/src/customization/inventory.ts +3 -0
  124. package/src/debug/report-bundle-parent.ts +22 -0
  125. package/src/debug/report-bundle.ts +3 -0
  126. package/src/debug/system-info.ts +13 -1
  127. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  128. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  129. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  130. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  131. package/src/defaults/gjc-defaults.test.ts +18 -1
  132. package/src/defaults/gjc-defaults.ts +3 -0
  133. package/src/defaults/gjc-skills.generated.ts +58 -36
  134. package/src/discovery/agents.ts +6 -1
  135. package/src/discovery/builtin.ts +10 -0
  136. package/src/discovery/helpers.ts +159 -20
  137. package/src/discovery/mcp-json.ts +6 -0
  138. package/src/discovery/ssh.ts +1 -1
  139. package/src/discovery/substitute-plugin-root.ts +3 -1
  140. package/src/edit/file-read-cache.ts +1 -1
  141. package/src/eval/py/executor.ts +380 -120
  142. package/src/export/html/css-color.ts +9 -0
  143. package/src/export/html/index.ts +5 -4
  144. package/src/export/html/template.generated.ts +1 -1
  145. package/src/extensibility/extensions/runner.ts +4 -0
  146. package/src/extensibility/extensions/types.ts +7 -0
  147. package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
  148. package/src/extensibility/plugins/manager.ts +3 -2
  149. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  150. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  151. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  152. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  153. package/src/extensibility/skill-management.ts +6 -2
  154. package/src/extensibility/skills.ts +6 -1
  155. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  156. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  157. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  158. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  159. package/src/gjc-runtime/gc-runtime.ts +206 -9
  160. package/src/gjc-runtime/launch-worktree.ts +14 -4
  161. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  162. package/src/gjc-runtime/state-argv.ts +2 -0
  163. package/src/gjc-runtime/state-migrations.ts +91 -29
  164. package/src/gjc-runtime/state-runtime.ts +3716 -135
  165. package/src/gjc-runtime/state-writer.ts +83 -8
  166. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  167. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  168. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  169. package/src/harness-control-plane/operate.ts +12 -4
  170. package/src/harness-control-plane/owner.ts +5 -1
  171. package/src/hashline/parser.ts +48 -9
  172. package/src/hashline/recovery.ts +13 -4
  173. package/src/internal-urls/docs-index.generated.ts +18 -17
  174. package/src/internal-urls/skill-path.ts +19 -0
  175. package/src/internal-urls/skill-protocol.ts +2 -0
  176. package/src/lsp/edits.ts +6 -0
  177. package/src/lsp/workspace-path.ts +51 -0
  178. package/src/main.ts +195 -31
  179. package/src/modes/acp/acp-agent.ts +88 -4
  180. package/src/modes/components/custom-editor.ts +14 -0
  181. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  182. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  183. package/src/modes/components/extensions/state-manager.ts +15 -5
  184. package/src/modes/components/model-selector.ts +1 -4
  185. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  186. package/src/modes/components/status-line/segments.ts +4 -0
  187. package/src/modes/controllers/event-controller.ts +11 -4
  188. package/src/modes/controllers/input-controller.ts +74 -29
  189. package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
  190. package/src/modes/controllers/selector-controller.ts +28 -15
  191. package/src/modes/interactive-mode.ts +4 -0
  192. package/src/modes/runtime-init.ts +4 -0
  193. package/src/modes/utils/abort-message.ts +3 -1
  194. package/src/modes/utils/interrupt-ownership.ts +12 -0
  195. package/src/modes/utils/keybinding-matchers.ts +4 -0
  196. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  197. package/src/progress/collect-project-progress.ts +153 -0
  198. package/src/progress/progress-contract.ts +172 -0
  199. package/src/progress/progress-snapshot.ts +140 -0
  200. package/src/progress/project-progress.ts +446 -0
  201. package/src/progress/render-progress.ts +209 -0
  202. package/src/prompts/system/master-mode.md +1 -0
  203. package/src/prompts/tools/skill-discovery.md +1 -1
  204. package/src/runtime-mcp/config-writer.ts +10 -6
  205. package/src/runtime-mcp/config.ts +1 -0
  206. package/src/runtime-mcp/oauth-flow.ts +5 -0
  207. package/src/runtime-mcp/oauth-public-url.ts +8 -0
  208. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  209. package/src/runtime-mcp/smithery-registry.ts +22 -3
  210. package/src/runtime-mcp/transports/http.ts +2 -2
  211. package/src/runtime-mcp/types.ts +6 -0
  212. package/src/sdk/acp/adapter.ts +93 -8
  213. package/src/sdk/broker/broker-exit.ts +52 -23
  214. package/src/sdk/broker/broker.ts +75 -6
  215. package/src/sdk/broker/ensure.ts +2 -0
  216. package/src/sdk/broker/lifecycle.ts +711 -66
  217. package/src/sdk/broker/session-index.ts +191 -81
  218. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  219. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  220. package/src/sdk/bus/index.ts +238 -43
  221. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  222. package/src/sdk/cli/session-cli.ts +17 -3
  223. package/src/sdk/client/client.ts +28 -8
  224. package/src/sdk/host/query/handlers.ts +9 -1
  225. package/src/sdk/host/session-runtime.test.ts +2647 -294
  226. package/src/sdk/host/session-runtime.ts +426 -109
  227. package/src/sdk/host/surface-policy.ts +1 -0
  228. package/src/sdk/index.ts +8 -0
  229. package/src/sdk/lifecycle-session.ts +3 -1
  230. package/src/sdk/prompt-deadline-manager.ts +15 -0
  231. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  232. package/src/sdk/protocol/operation-registry.ts +9 -2
  233. package/src/sdk/router/session-router.ts +75 -12
  234. package/src/sdk/session-list.ts +1 -0
  235. package/src/sdk/session.ts +67 -38
  236. package/src/session/agent-session.ts +623 -224
  237. package/src/session/artifacts.ts +174 -70
  238. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  239. package/src/session/internal/managed-session-scope.ts +3457 -462
  240. package/src/session/internal/managed-session-storage.ts +376 -61
  241. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  242. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  243. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  244. package/src/session/session-manager.ts +939 -133
  245. package/src/session/session-retirement.ts +573 -14
  246. package/src/session/session-storage.ts +417 -14
  247. package/src/session/task-artifact-owner-codec.ts +71 -8
  248. package/src/session/task-artifact-owner-retirement.ts +21 -19
  249. package/src/setup/credential-auto-import.ts +12 -3
  250. package/src/setup/credential-import.ts +176 -5
  251. package/src/setup/paseo/announce.ts +6 -1
  252. package/src/skill-state/active-state.ts +132 -2
  253. package/src/slash-commands/builtin-registry.ts +39 -9
  254. package/src/slash-commands/helpers/progress-report.ts +23 -0
  255. package/src/stt/setup.ts +3 -5
  256. package/src/task/commands.ts +3 -1
  257. package/src/task/executor.ts +4 -0
  258. package/src/task/index.ts +6 -0
  259. package/src/tools/ask.ts +205 -6
  260. package/src/tools/ast-edit.ts +4 -0
  261. package/src/tools/bash-allowed-prefixes.ts +15 -0
  262. package/src/tools/bash-skill-urls.ts +56 -3
  263. package/src/tools/bash.ts +48 -79
  264. package/src/tools/descriptors.ts +5 -1
  265. package/src/tools/eval.ts +56 -36
  266. package/src/tools/image-gen.ts +1 -1
  267. package/src/tools/index.ts +3 -0
  268. package/src/tools/python.ts +187 -66
  269. package/src/tools/recipe/index.ts +9 -0
  270. package/src/tools/recipe/recipe-guard.ts +19 -0
  271. package/src/tools/restricted-cwd.ts +10 -0
  272. package/src/tools/skill-discovery.ts +5 -0
  273. package/src/tools/skill.ts +28 -1
  274. package/src/tools/todo-write.ts +10 -1
  275. package/src/tools/tool-catalog.generated.ts +1 -1
  276. package/src/tools/write.ts +1 -0
  277. package/src/utils/tools-checksum.ts +34 -0
  278. package/src/utils/tools-manager.ts +24 -6
  279. package/src/web/search/providers/codex.ts +3 -2
@@ -11,7 +11,9 @@ import { resolveGjcSessionForRead, SessionResolutionError } from "../gjc-runtime
11
11
  import {
12
12
  type ActiveSessionScope,
13
13
  type GuardedWriteResult,
14
+ persistedStateRevision,
14
15
  readActiveEntries,
16
+ readActiveEntriesStrict,
15
17
  rebuildActiveSnapshot,
16
18
  removeActiveEntry,
17
19
  setActiveStateCacheInvalidator,
@@ -667,13 +669,17 @@ async function mergeVisibleEntries(
667
669
  cwd: string,
668
670
  sessionState: SkillActiveState | null,
669
671
  sessionId: string,
672
+ perSkillEntries?: readonly SkillActiveEntry[],
670
673
  ): Promise<SkillActiveEntry[]> {
671
674
  // Use the raw (active + inactive) rows so a handoff demotion stays visible
672
675
  // long enough to supersede a stale same-skill row before the active filter.
673
676
  // Per-skill files in active/<skill>.json are authoritative and are merged
674
677
  // after the derived snapshot cache, so a stale skill-active-state.json row
675
678
  // cannot override the latest entry file.
676
- const entries = [...rawActiveEntries(sessionState), ...(await readActiveEntries(cwd, { sessionId }))];
679
+ const entries = [
680
+ ...rawActiveEntries(sessionState),
681
+ ...(perSkillEntries ?? (await readActiveEntries(cwd, { sessionId }))),
682
+ ];
677
683
  const merged = new Map(entries.map(entry => [entryKey(entry), entry]));
678
684
  const canonicalRalplanPhase = await readModeStatePhase(cwd, sessionId, "ralplan");
679
685
  const visibleEntries = dedupeVisibleBySkill([...merged.values()], sessionId)
@@ -788,7 +794,16 @@ async function readVisibleSkillActiveStateUncached(
788
794
  ): Promise<SkillActiveState | null> {
789
795
  const { sessionPath } = getSkillActiveStatePaths(cwd, resolvedSessionId);
790
796
  const sessionState = await readRawActiveStateForHandoff(sessionPath, false);
791
- const activeSkills = await mergeVisibleEntries(cwd, sessionState, resolvedSessionId);
797
+ return await buildVisibleSkillActiveState(cwd, sessionState, resolvedSessionId);
798
+ }
799
+
800
+ async function buildVisibleSkillActiveState(
801
+ cwd: string,
802
+ sessionState: SkillActiveState | null,
803
+ resolvedSessionId: string,
804
+ perSkillEntries?: readonly SkillActiveEntry[],
805
+ ): Promise<SkillActiveState | null> {
806
+ const activeSkills = await mergeVisibleEntries(cwd, sessionState, resolvedSessionId, perSkillEntries);
792
807
  if (activeSkills.length === 0) return null;
793
808
  const primary = activeSkills[0];
794
809
  return {
@@ -805,6 +820,121 @@ async function readVisibleSkillActiveStateUncached(
805
820
 
806
821
  setActiveStateCacheInvalidator(invalidateVisibleSkillActiveStateCache);
807
822
 
823
+ /**
824
+ * Outcome of a failure-observable workflow-state read:
825
+ * - `absent`: neither a session snapshot nor a per-skill entry is recorded.
826
+ * - `read`: every recorded source was read and no snapshot row lacks authority.
827
+ * - `recovered`: the session snapshot was unreadable, but authoritative per-skill
828
+ * entries (which the snapshot is rebuilt from) supplied the visible state.
829
+ * - `discarded`: the snapshot is a derived cache that still lists active workflows
830
+ * whose per-skill entry is gone or inactive (e.g. a deactivation whose snapshot
831
+ * rebuild never ran); those stale rows were dropped and the visible state comes
832
+ * from the per-skill entries alone.
833
+ * - `unresolved`: the snapshot lists active workflows without an active per-skill
834
+ * entry, but it cannot be shown to be a derived cache, so those rows may be legacy
835
+ * authority or stale cache; they are excluded without confirming their absence.
836
+ * - `failed`: the session snapshot was unreadable and no per-skill entry could
837
+ * replace it (e.g. a corrupt legacy root-only record), so the state is unknown.
838
+ */
839
+ export type VisibleSkillActiveStateReadStatus = "absent" | "read" | "recovered" | "discarded" | "unresolved" | "failed";
840
+
841
+ export interface VisibleSkillActiveStateRead {
842
+ status: VisibleSkillActiveStateReadStatus;
843
+ /** Visible active state; `null` when nothing is active or the read failed. */
844
+ state: SkillActiveState | null;
845
+ }
846
+
847
+ /** Strict snapshot read: `undefined` when absent; throws on I/O errors, invalid JSON, or a non-object record. */
848
+ async function readSessionSnapshotStrict(filePath: string): Promise<SkillActiveState | undefined> {
849
+ let raw: string;
850
+ try {
851
+ raw = await Bun.file(filePath).text();
852
+ } catch (error) {
853
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
854
+ throw error;
855
+ }
856
+ const parsed: unknown = JSON.parse(raw);
857
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
858
+ throw new Error(`skill-active-state at ${filePath} is not a JSON object`);
859
+ return parsed as SkillActiveState;
860
+ }
861
+
862
+ /**
863
+ * Whether a readable session snapshot can carry workflow authority of its own.
864
+ *
865
+ * - `legacy`: a root-only record (no `active_skills` array). The derived-cache
866
+ * writers (`rebuildActiveSnapshot` and the activation seed) always persist an
867
+ * `active_skills` array, so this shape predates per-skill entries and its row
868
+ * stays visible.
869
+ * - `derived`: an `active_skills` snapshot stamped by the guarded state writer.
870
+ * Every stamped producer persists the per-skill entry before the snapshot, so
871
+ * the per-skill entries are the only authority for its rows.
872
+ * - `indeterminate`: an unstamped `active_skills` snapshot. Unstamped derived
873
+ * rebuilds and unstamped authoritative activation seeds were both written before
874
+ * the guarded writer, so the record cannot establish which one it is.
875
+ */
876
+ function classifySnapshotAuthority(snapshot: SkillActiveState): "legacy" | "derived" | "indeterminate" {
877
+ if (!Array.isArray(snapshot.active_skills)) return "legacy";
878
+ return persistedStateRevision(snapshot) > 0 ? "derived" : "indeterminate";
879
+ }
880
+
881
+ /**
882
+ * Uncached, failure-observable counterpart of `readVisibleSkillActiveState` for
883
+ * read-only projections that must not present unreadable state as absence. The
884
+ * tolerant reader keeps mapping an unreadable session snapshot to `null` for
885
+ * HUD and guard callers; this variant reports `failed` or `recovered` instead.
886
+ *
887
+ * Unlike the tolerant reader, a snapshot row never survives without authority: an
888
+ * active row with no active per-skill entry is dropped as stale (`discarded`) when
889
+ * the snapshot is provably derived, or excluded as `unresolved` when it is not.
890
+ * Only a legacy root-only record keeps its own row. Per-skill entry read failures
891
+ * propagate, as they do from the tolerant reader, and so does an entry file that
892
+ * exists but holds no usable skill entry (e.g. `{}`): the tolerant reader skips
893
+ * it, which would make it indistinguishable from a deleted entry and let it pass
894
+ * as proof that a snapshot row is stale. The read never repairs state.
895
+ */
896
+ export async function readVisibleSkillActiveStateWithStatus(
897
+ cwd: string,
898
+ sessionId: string,
899
+ ): Promise<VisibleSkillActiveStateRead> {
900
+ const resolvedCwd = path.resolve(cwd);
901
+ const { sessionPath } = getSkillActiveStatePaths(resolvedCwd, sessionId);
902
+ let sessionState: SkillActiveState | undefined;
903
+ let snapshotUnreadable = false;
904
+ try {
905
+ sessionState = await readSessionSnapshotStrict(sessionPath);
906
+ } catch {
907
+ snapshotUnreadable = true;
908
+ }
909
+ const perSkillEntries = await readActiveEntriesStrict(resolvedCwd, { sessionId });
910
+ const fromEntries = () => buildVisibleSkillActiveState(resolvedCwd, null, sessionId, perSkillEntries);
911
+ if (snapshotUnreadable) {
912
+ if (perSkillEntries.length === 0) return { status: "failed", state: null };
913
+ return { status: "recovered", state: await fromEntries() };
914
+ }
915
+ if (sessionState === undefined) {
916
+ return perSkillEntries.length === 0
917
+ ? { status: "absent", state: null }
918
+ : { status: "read", state: await fromEntries() };
919
+ }
920
+ const authority = classifySnapshotAuthority(sessionState);
921
+ if (authority === "legacy") {
922
+ return {
923
+ status: "read",
924
+ state: await buildVisibleSkillActiveState(resolvedCwd, sessionState, sessionId, perSkillEntries),
925
+ };
926
+ }
927
+ const activeEntrySkills = new Set(
928
+ perSkillEntries.filter(entry => entry.active !== false).map(entry => safeString(entry.skill).trim()),
929
+ );
930
+ const hasUnbackedRow = rawActiveEntries(sessionState).some(
931
+ entry => entry.active !== false && !activeEntrySkills.has(entry.skill),
932
+ );
933
+ const state = await fromEntries();
934
+ if (!hasUnbackedRow) return { status: "read", state };
935
+ return { status: authority === "derived" ? "discarded" : "unresolved", state };
936
+ }
937
+
808
938
  export async function readVisibleSkillActiveState(
809
939
  cwd: string,
810
940
  sessionId?: string,
@@ -31,6 +31,8 @@ import {
31
31
  type InteractiveModeContext,
32
32
  } from "../modes/types";
33
33
  import { parseUiLanguage, resolveUiLanguage, UI_LANGUAGE_LABELS, UI_LANGUAGES, uiString } from "../modes/ui-language";
34
+ import { buildSessionProjectProgress } from "../progress/collect-project-progress";
35
+ import { PROGRESS_COMMAND_ACP_DESCRIPTION } from "../progress/render-progress";
34
36
  // W1b/W5b: notification-service and daemon controllers stay off the static
35
37
  // import graph; the /notify handlers import them lazily at first use.
36
38
  import type { NotificationProvider } from "../sdk/bus/config";
@@ -63,6 +65,7 @@ import { switchSessionCredentialCommand } from "./helpers/credential-switch";
63
65
  import { buildFastStatusReport } from "./helpers/fast-status-report";
64
66
  import { formatDuration } from "./helpers/format";
65
67
  import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
68
+ import { renderProgressReportLines, renderProgressReportText } from "./helpers/progress-report";
66
69
  import { handleSshAcp } from "./helpers/ssh";
67
70
  import { buildUsageReportText, collectCachedUsageReports } from "./helpers/usage-report";
68
71
  import type {
@@ -1047,6 +1050,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1047
1050
  runtime,
1048
1051
  );
1049
1052
  }
1053
+ if (targetIds.some(targetId => targetId !== "default")) runtime.session.markUserModelSelection();
1050
1054
  // Preset shortcut: when the selector names a known model profile
1051
1055
  // (optionally `gajae-code/`-prefixed) and the target is implicit,
1052
1056
  // activate the profile immediately instead of treating the preset name
@@ -1059,14 +1063,17 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1059
1063
  const profileLabel = formatModelProfileDisplayLabel(
1060
1064
  runtime.session.modelRegistry.getModelProfile(presetName) ?? { name: presetName },
1061
1065
  );
1062
- await activateModelProfile(
1063
- {
1064
- session: runtime.session,
1065
- modelRegistry: runtime.session.modelRegistry,
1066
- settings: runtime.settings,
1067
- profileName: presetName,
1068
- },
1069
- { persistDefault: false },
1066
+ runtime.session.markUserModelSelection();
1067
+ await runtime.session.withSdkControlMutation(() =>
1068
+ activateModelProfile(
1069
+ {
1070
+ session: runtime.session,
1071
+ modelRegistry: runtime.session.modelRegistry,
1072
+ settings: runtime.settings,
1073
+ profileName: presetName,
1074
+ },
1075
+ { persistDefault: false },
1076
+ ),
1070
1077
  );
1071
1078
  await runtime.output(`Model profile: ${profileLabel}`);
1072
1079
  await runtime.notifyTitleChanged?.();
@@ -1136,7 +1143,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1136
1143
  runtime.session.setThinkingLevel(existingDefaultThinkingLevel);
1137
1144
  }
1138
1145
  }
1139
-
1140
1146
  const materializedProfile = materializeActiveModelProfileAssignments({
1141
1147
  session: runtime.session,
1142
1148
  settings: runtime.settings,
@@ -1584,6 +1590,30 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1584
1590
  runtime.ctx.editor.setText("");
1585
1591
  },
1586
1592
  },
1593
+ {
1594
+ name: "progress",
1595
+ description:
1596
+ "Show a read-only project progress overview from durable goal, todo, workflow, agent, and verification state",
1597
+ // Production ACP advertises and dispatches `/progress` itself over the
1598
+ // `session.progress` SDK query (modes/acp/acp-agent.ts) with the same copy.
1599
+ acpDescription: PROGRESS_COMMAND_ACP_DESCRIPTION,
1600
+ allowArgs: false,
1601
+ handle: async (_command, runtime) => {
1602
+ const report = await buildSessionProjectProgress(runtime.session, runtime.sessionManager);
1603
+ await runtime.output(renderProgressReportText(report));
1604
+ return commandConsumed();
1605
+ },
1606
+ handleTui: async (_command, runtime) => {
1607
+ const ctx = runtime.ctx;
1608
+ const report = await buildSessionProjectProgress(ctx.session, ctx.sessionManager);
1609
+ ctx.chatContainer.addChild(new Spacer(1));
1610
+ ctx.chatContainer.addChild(new DynamicBorder());
1611
+ ctx.chatContainer.addChild(new Text(renderProgressReportLines(report, theme).join("\n"), 1, 0));
1612
+ ctx.chatContainer.addChild(new DynamicBorder());
1613
+ ctx.ui.requestRender();
1614
+ ctx.editor.setText("");
1615
+ },
1616
+ },
1587
1617
  {
1588
1618
  name: "usage",
1589
1619
  description: "Show provider usage and limits",
@@ -0,0 +1,23 @@
1
+ import { replaceTabs, truncateToWidth } from "@gajae-code/tui";
2
+ import { sanitizeDisplayLine } from "@gajae-code/utils";
3
+ import { toProjectProgressSnapshot } from "../../progress/progress-snapshot";
4
+ import type { ProjectProgressReport } from "../../progress/project-progress";
5
+ import { type ProgressRenderStyle, renderProgressSnapshot } from "../../progress/render-progress";
6
+
7
+ /** Bound untrusted durable text (objectives, titles, HUD values) to one sanitized display line of `max` columns. */
8
+ export function clipProgressText(text: string, max: number): string {
9
+ return truncateToWidth(sanitizeDisplayLine(replaceTabs(text)).trim(), max);
10
+ }
11
+
12
+ /** TUI view: the `session.progress` snapshot rendered with column-aware clipping and theme colours. */
13
+ export function renderProgressReportLines(report: ProjectProgressReport, style?: ProgressRenderStyle): string[] {
14
+ return renderProgressSnapshot(toProjectProgressSnapshot(report), {
15
+ clip: clipProgressText,
16
+ ...(style ? { style } : {}),
17
+ });
18
+ }
19
+
20
+ /** Plain text view: identical to what production ACP renders from the `session.progress` query. */
21
+ export function renderProgressReportText(report: ProjectProgressReport): string {
22
+ return renderProgressSnapshot(toProjectProgressSnapshot(report)).join("\n");
23
+ }
package/src/stt/setup.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { macosOptionForwardingInstruction } from "../modes/utils/macos-option-forwarding";
1
2
  import { detectRecordingTools } from "./recorder";
2
3
  import { resolvePython } from "./transcriber";
3
4
 
@@ -20,11 +21,8 @@ export function formatSTTUsage(
20
21
  ];
21
22
  if (platform === "darwin") {
22
23
  lines.push("On macOS, Alt+H is Option+H. Your terminal must forward Option as Meta/Esc.");
23
- if (terminalProgram?.toLowerCase().includes("ghostty")) {
24
- lines.push(
25
- "Ghostty: set macos-option-as-alt = true in its config, then reload the config or restart Ghostty.",
26
- );
27
- }
24
+ const instruction = macosOptionForwardingInstruction(terminalProgram);
25
+ if (instruction) lines.push(instruction);
28
26
  }
29
27
  lines.push("Run /hotkeys inside GJC to confirm the active shortcut.");
30
28
  return lines.join("\n");
@@ -120,7 +120,9 @@ export function getCommand(commands: WorkflowCommand[], name: string): WorkflowC
120
120
  * Replaces $@ with the provided input.
121
121
  */
122
122
  export function expandCommand(command: WorkflowCommand, input: string): string {
123
- return command.instructions.replace(/\$@/g, input);
123
+ // Function replacer: `input` is user text and must be inserted verbatim, not
124
+ // parsed for `$&`, `$$`, `` $` `` or `$'` replacement patterns.
125
+ return command.instructions.replace(/\$@/g, () => input);
124
126
  }
125
127
 
126
128
  /**
@@ -33,6 +33,7 @@ import * as canonicalSdk from "@gajae-code/coding-agent/sdk";
33
33
  import { logger, prompt, untilAborted } from "@gajae-code/utils";
34
34
  import { AsyncJobManager } from "../async";
35
35
  import { AUTOROUTING_SELECTOR_MAX_LENGTH, type AutoroutingReasonCode } from "../config/autorouting-contract";
36
+ import type { ModelProfileOwnershipMarker } from "../config/model-profile-ownership";
36
37
  import { ModelRegistry } from "../config/model-registry";
37
38
  import {
38
39
  formatModelString,
@@ -262,6 +263,8 @@ export interface ExecutorOptions {
262
263
  * Manual/direct parents (no active profile) keep exact resolution.
263
264
  */
264
265
  parentActiveModelProfile?: string;
266
+ /** Parent session's model profile ownership marker for propagating to subagent sessions. */
267
+ parentModelProfileOwnershipMarker?: ModelProfileOwnershipMarker;
265
268
  parentSessionId?: string;
266
269
  parentCredentialSessionId?: string;
267
270
  thinkingLevel?: ThinkingLevel;
@@ -2026,6 +2029,7 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
2026
2029
  model,
2027
2030
  thinkingLevel: effectiveThinkingLevel,
2028
2031
  activeModelProfile: options.parentActiveModelProfile,
2032
+ modelProfileOwnershipMarker: options.parentModelProfileOwnershipMarker,
2029
2033
  credentialSessionId: options.parentCredentialSessionId ?? options.parentSessionId,
2030
2034
  modelSubstitution:
2031
2035
  modelSubstitutionWarning?.reason === "auth_unavailable" && requestedModel
package/src/task/index.ts CHANGED
@@ -1738,6 +1738,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1738
1738
  const parentActiveModelProfile = (
1739
1739
  this.session as { getActiveModelProfile?: () => string | undefined }
1740
1740
  ).getActiveModelProfile?.();
1741
+ // Propagate the parent session's ownership marker when a session-local profile is active.
1742
+ const parentModelProfileOwnershipMarker = (
1743
+ this.session as { sessionManager?: { getModelProfileOwnershipMarker?: () => unknown } }
1744
+ ).sessionManager?.getModelProfileOwnershipMarker?.();
1741
1745
  const parentProfileDefinition = parentActiveModelProfile
1742
1746
  ? this.session.modelRegistry?.getModelProfile?.(parentActiveModelProfile)
1743
1747
  : undefined;
@@ -2273,6 +2277,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
2273
2277
  modelOverride: effectivePatterns(index),
2274
2278
  parentActiveModelPattern,
2275
2279
  parentActiveModelProfile: parentOwnedModelProfile,
2280
+ parentModelProfileOwnershipMarker: parentModelProfileOwnershipMarker as any,
2276
2281
  parentSessionId: this.session.getSessionId?.() ?? undefined,
2277
2282
  parentCredentialSessionId:
2278
2283
  this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,
@@ -2360,6 +2365,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
2360
2365
  modelOverride: effectivePatterns(index),
2361
2366
  parentActiveModelPattern,
2362
2367
  parentActiveModelProfile: parentOwnedModelProfile,
2368
+ parentModelProfileOwnershipMarker: parentModelProfileOwnershipMarker as any,
2363
2369
  parentSessionId: this.session.getSessionId?.() ?? undefined,
2364
2370
  parentCredentialSessionId:
2365
2371
  this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,