@gajae-code/coding-agent 0.16.3 → 0.16.6

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 (277) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/dist/types/capability/types.d.ts +4 -0
  3. package/dist/types/cli/args.d.ts +2 -2
  4. package/dist/types/cli/customize-doctor.d.ts +22 -0
  5. package/dist/types/cli/root-flags.d.ts +4 -2
  6. package/dist/types/cli.d.ts +1 -1
  7. package/dist/types/commands/launch.d.ts +1 -1
  8. package/dist/types/config/autorouting-tier-map.d.ts +3 -0
  9. package/dist/types/config/file-lock.d.ts +31 -3
  10. package/dist/types/config/model-registry.d.ts +4 -0
  11. package/dist/types/config/model-resolver.d.ts +9 -0
  12. package/dist/types/config/settings-schema.d.ts +29 -1
  13. package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
  14. package/dist/types/coordinator-mcp/server.d.ts +7 -0
  15. package/dist/types/discovery/claude-plugins.d.ts +3 -1
  16. package/dist/types/discovery/helpers.d.ts +35 -0
  17. package/dist/types/exec/bash-executor.d.ts +17 -1
  18. package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
  19. package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
  20. package/dist/types/exec/bash-shell-signals.d.ts +2 -0
  21. package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
  22. package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
  23. package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
  24. package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
  25. package/dist/types/exec/bash-shell-worker.d.ts +1 -0
  26. package/dist/types/exec/isolated-shell.d.ts +19 -0
  27. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
  28. package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
  29. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  30. package/dist/types/extensibility/extensions/runner.d.ts +46 -6
  31. package/dist/types/extensibility/extensions/types.d.ts +16 -0
  32. package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
  33. package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
  34. package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
  35. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
  36. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  37. package/dist/types/extensibility/hooks/types.d.ts +4 -0
  38. package/dist/types/extensibility/runtime-skill-discovery.d.ts +5 -2
  39. package/dist/types/extensibility/shared-events.d.ts +1 -1
  40. package/dist/types/extensibility/skill-management.d.ts +9 -4
  41. package/dist/types/extensibility/skills.d.ts +2 -0
  42. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
  43. package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
  44. package/dist/types/gjc-runtime/session-state-lock.d.ts +14 -0
  45. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
  46. package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
  47. package/dist/types/modes/components/assistant-message.d.ts +1 -2
  48. package/dist/types/modes/components/bash-execution.d.ts +1 -0
  49. package/dist/types/modes/components/execution-shared.d.ts +1 -0
  50. package/dist/types/modes/components/settings-selector.d.ts +6 -1
  51. package/dist/types/modes/components/status-line/command.d.ts +35 -0
  52. package/dist/types/modes/components/status-line/index.d.ts +1 -0
  53. package/dist/types/modes/components/status-line/types.d.ts +5 -0
  54. package/dist/types/modes/components/tool-status-header.d.ts +14 -0
  55. package/dist/types/modes/controllers/event-controller.d.ts +6 -0
  56. package/dist/types/modes/interactive-mode.d.ts +3 -0
  57. package/dist/types/modes/types.d.ts +7 -0
  58. package/dist/types/runtime-mcp/client.d.ts +9 -1
  59. package/dist/types/runtime-mcp/manager.d.ts +7 -1
  60. package/dist/types/runtime-mcp/pool.d.ts +22 -3
  61. package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
  62. package/dist/types/runtime-mcp/types.d.ts +22 -0
  63. package/dist/types/sdk/broker/ensure.d.ts +32 -0
  64. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
  65. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  66. package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
  67. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
  68. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
  69. package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
  70. package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
  71. package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
  72. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  73. package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
  74. package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
  75. package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
  76. package/dist/types/sdk/client/client.d.ts +6 -0
  77. package/dist/types/sdk/host/host.d.ts +3 -1
  78. package/dist/types/sdk/host/session-runtime.d.ts +26 -1
  79. package/dist/types/sdk/lifecycle/service.d.ts +1 -0
  80. package/dist/types/sdk/router/session-router.d.ts +10 -2
  81. package/dist/types/sdk/session-api-key.d.ts +26 -0
  82. package/dist/types/sdk/session.d.ts +5 -1
  83. package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
  84. package/dist/types/session/agent-session.d.ts +11 -0
  85. package/dist/types/session/fallback-chain-controller.d.ts +15 -0
  86. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  87. package/dist/types/session/messages.d.ts +1 -0
  88. package/dist/types/session/session-manager.d.ts +3 -1
  89. package/dist/types/setup/paseo/announce.d.ts +112 -0
  90. package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
  91. package/dist/types/skill-state/canonical-skills.d.ts +4 -0
  92. package/dist/types/system-prompt.d.ts +10 -0
  93. package/dist/types/task/executor.d.ts +5 -1
  94. package/dist/types/tools/bash.d.ts +1 -0
  95. package/dist/types/tools/fetch.d.ts +7 -0
  96. package/dist/types/tools/github-cache.d.ts +2 -1
  97. package/dist/types/tools/index.d.ts +10 -0
  98. package/dist/types/tools/path-utils.d.ts +2 -0
  99. package/dist/types/tools/search.d.ts +6 -0
  100. package/package.json +9 -7
  101. package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
  102. package/scripts/generate-sdk-operation-inventory.ts +11 -0
  103. package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
  104. package/src/capability/index.ts +14 -2
  105. package/src/capability/types.ts +4 -0
  106. package/src/cli/args.ts +20 -9
  107. package/src/cli/customize-doctor.ts +155 -12
  108. package/src/cli/root-flags.ts +5 -3
  109. package/src/cli.ts +22 -0
  110. package/src/commands/sdk.ts +74 -31
  111. package/src/config/autorouting-tier-map.ts +1 -0
  112. package/src/config/file-lock-gc.ts +40 -2
  113. package/src/config/file-lock.ts +379 -31
  114. package/src/config/model-preset-registry.ts +120 -19
  115. package/src/config/model-profiles.ts +40 -0
  116. package/src/config/model-registry.ts +73 -23
  117. package/src/config/model-resolver.ts +48 -0
  118. package/src/config/settings-schema.ts +33 -1
  119. package/src/config/settings.ts +1 -0
  120. package/src/coordinator-mcp/question-state.ts +33 -2
  121. package/src/coordinator-mcp/server.ts +477 -101
  122. package/src/crash/sanitize.ts +1 -1
  123. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
  124. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
  125. package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
  126. package/src/discovery/builtin.ts +52 -55
  127. package/src/discovery/claude-plugins.ts +12 -4
  128. package/src/discovery/helpers.ts +307 -34
  129. package/src/exec/bash-executor.ts +330 -50
  130. package/src/exec/bash-shell-guardian.ts +388 -0
  131. package/src/exec/bash-shell-runtime-entry.ts +5 -0
  132. package/src/exec/bash-shell-signals.ts +29 -0
  133. package/src/exec/bash-shell-supervisor-entry.ts +3 -0
  134. package/src/exec/bash-shell-supervisor.ts +359 -0
  135. package/src/exec/bash-shell-worker-entry.ts +5 -0
  136. package/src/exec/bash-shell-worker-protocol.ts +36 -0
  137. package/src/exec/bash-shell-worker.ts +116 -0
  138. package/src/exec/isolated-shell.ts +393 -0
  139. package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
  140. package/src/extensibility/extensions/function-hooks.ts +959 -0
  141. package/src/extensibility/extensions/index.ts +22 -0
  142. package/src/extensibility/extensions/loader.ts +53 -2
  143. package/src/extensibility/extensions/runner.ts +895 -264
  144. package/src/extensibility/extensions/types.ts +42 -0
  145. package/src/extensibility/extensions/wrapper.ts +56 -15
  146. package/src/extensibility/gjc-plugins/compiler.ts +228 -60
  147. package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
  148. package/src/extensibility/gjc-plugins/installer.ts +13 -12
  149. package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
  150. package/src/extensibility/gjc-plugins/paths.ts +13 -0
  151. package/src/extensibility/gjc-plugins/registry.ts +113 -1
  152. package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
  153. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
  154. package/src/extensibility/gjc-plugins/schema.ts +121 -4
  155. package/src/extensibility/gjc-plugins/types.ts +25 -0
  156. package/src/extensibility/hooks/loader.ts +133 -16
  157. package/src/extensibility/hooks/types.ts +28 -0
  158. package/src/extensibility/runtime-skill-discovery.ts +149 -190
  159. package/src/extensibility/shared-events.ts +1 -1
  160. package/src/extensibility/skill-management.ts +200 -52
  161. package/src/extensibility/skills.ts +73 -25
  162. package/src/gjc-runtime/empty-delete-gc.ts +94 -4
  163. package/src/gjc-runtime/gc-runtime.ts +29 -0
  164. package/src/gjc-runtime/linux-proc.ts +14 -0
  165. package/src/gjc-runtime/repository-binding.ts +1 -1
  166. package/src/gjc-runtime/session-state-lock.ts +143 -10
  167. package/src/gjc-runtime/session-state-sidecar.ts +27 -11
  168. package/src/gjc-runtime/state-writer.ts +30 -29
  169. package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
  170. package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
  171. package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
  172. package/src/gjc-runtime/workflow-manifest.ts +16 -0
  173. package/src/harness-control-plane/session-lease.ts +2 -2
  174. package/src/internal-urls/docs-index.generated.ts +12 -12
  175. package/src/lsp/config.ts +10 -3
  176. package/src/lsp/index.ts +1 -1
  177. package/src/lsp/path-trust.ts +69 -14
  178. package/src/main.ts +78 -35
  179. package/src/modes/DESIGN.md +54 -0
  180. package/src/modes/acp/acp-agent.ts +16 -3
  181. package/src/modes/components/assistant-message.ts +22 -9
  182. package/src/modes/components/bash-execution.ts +4 -1
  183. package/src/modes/components/execution-shared.ts +4 -1
  184. package/src/modes/components/model-selector.ts +166 -33
  185. package/src/modes/components/session-observer-overlay.ts +1 -1
  186. package/src/modes/components/settings-selector.ts +1075 -235
  187. package/src/modes/components/status-line/command.ts +190 -0
  188. package/src/modes/components/status-line/index.ts +1 -0
  189. package/src/modes/components/status-line/segments.ts +32 -5
  190. package/src/modes/components/status-line/types.ts +5 -0
  191. package/src/modes/components/tool-status-header.ts +396 -22
  192. package/src/modes/controllers/command-controller.ts +3 -0
  193. package/src/modes/controllers/event-controller.ts +151 -7
  194. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  195. package/src/modes/controllers/input-controller.ts +6 -3
  196. package/src/modes/controllers/selector-controller.ts +21 -0
  197. package/src/modes/interactive-mode.ts +53 -6
  198. package/src/modes/print-mode.ts +5 -1
  199. package/src/modes/prompt-action-autocomplete.ts +54 -27
  200. package/src/modes/runtime-init.ts +2 -0
  201. package/src/modes/types.ts +17 -3
  202. package/src/modes/utils/ui-helpers.ts +2 -0
  203. package/src/prompts/system/system-prompt.md +8 -0
  204. package/src/prompts/tools/search.md +2 -0
  205. package/src/prompts/tools/subagent.md +1 -0
  206. package/src/runtime-mcp/client.ts +60 -17
  207. package/src/runtime-mcp/manager.ts +189 -14
  208. package/src/runtime-mcp/pool.test.ts +218 -0
  209. package/src/runtime-mcp/pool.ts +224 -23
  210. package/src/runtime-mcp/transports/stdio.ts +158 -24
  211. package/src/runtime-mcp/types.ts +24 -0
  212. package/src/sdk/broker/broker.ts +124 -32
  213. package/src/sdk/broker/ensure.ts +248 -41
  214. package/src/sdk/broker/lifecycle-ledger.ts +7 -17
  215. package/src/sdk/broker/lifecycle.ts +56 -6
  216. package/src/sdk/broker/startup-failure.ts +15 -6
  217. package/src/sdk/bus/chat-daemon-control.ts +3 -1
  218. package/src/sdk/bus/chat-daemon-runtime.ts +72 -6
  219. package/src/sdk/bus/chat-effect-journal.ts +132 -7
  220. package/src/sdk/bus/conversation-store.ts +29 -0
  221. package/src/sdk/bus/lifecycle-commands.ts +17 -11
  222. package/src/sdk/bus/slack-conversation.ts +1 -0
  223. package/src/sdk/bus/slack-daemon.ts +478 -49
  224. package/src/sdk/bus/slack-live-provider.ts +12 -0
  225. package/src/sdk/bus/slack-provider.ts +5 -0
  226. package/src/sdk/bus/telegram-daemon.ts +16 -2
  227. package/src/sdk/bus/topic-registry.ts +19 -0
  228. package/src/sdk/cli/master-cli.ts +1 -1
  229. package/src/sdk/cli/session-cli.ts +11 -8
  230. package/src/sdk/client/client.ts +66 -17
  231. package/src/sdk/host/host.ts +29 -9
  232. package/src/sdk/host/query/revision-store.ts +19 -13
  233. package/src/sdk/host/reverse-leases.ts +7 -4
  234. package/src/sdk/host/session-runtime.test.ts +52 -10
  235. package/src/sdk/host/session-runtime.ts +383 -44
  236. package/src/sdk/lifecycle/service.ts +4 -0
  237. package/src/sdk/protocol/operation-inventory.generated.json +77 -0
  238. package/src/sdk/router/session-router.ts +89 -38
  239. package/src/sdk/session-api-key.ts +94 -0
  240. package/src/sdk/session.ts +228 -55
  241. package/src/sdk/transport/relay.ts +35 -13
  242. package/src/sdk/transport/serve-cli.ts +3 -3
  243. package/src/session/agent-session.ts +240 -90
  244. package/src/session/contribution-prep.ts +214 -21
  245. package/src/session/fallback-chain-controller.ts +38 -0
  246. package/src/session/internal/managed-session-storage.ts +19 -5
  247. package/src/session/messages.ts +5 -0
  248. package/src/session/session-manager.ts +29 -5
  249. package/src/session-import/redact.ts +5 -1
  250. package/src/setup/paseo/announce.ts +456 -0
  251. package/src/setup/paseo/setup-deps.ts +15 -3
  252. package/src/skill-state/canonical-skills.ts +12 -0
  253. package/src/skills/skills.test.ts +41 -21
  254. package/src/slash-commands/builtin-registry.ts +1 -0
  255. package/src/system-prompt.ts +29 -3
  256. package/src/task/executor.ts +7 -1
  257. package/src/task/index.ts +21 -4
  258. package/src/tools/bash.ts +63 -19
  259. package/src/tools/cron.ts +1 -1
  260. package/src/tools/fetch.ts +29 -1
  261. package/src/tools/gh.ts +26 -3
  262. package/src/tools/github-cache.ts +68 -16
  263. package/src/tools/index.ts +10 -0
  264. package/src/tools/job.ts +21 -4
  265. package/src/tools/monitor.ts +8 -2
  266. package/src/tools/path-utils.ts +7 -0
  267. package/src/tools/read.ts +14 -2
  268. package/src/tools/render-utils.ts +1 -3
  269. package/src/tools/resource-gc.ts +28 -8
  270. package/src/tools/search.ts +116 -58
  271. package/src/tools/skill-discovery.ts +5 -0
  272. package/src/tools/skill.ts +37 -2
  273. package/src/tools/subagent.ts +6 -3
  274. package/src/tools/tool-catalog.generated.ts +10 -3
  275. package/src/tools/ultragoal-ask-guard.ts +2 -2
  276. package/src/web/insane/bridge.ts +113 -23
  277. package/src/web/search/providers/perplexity.ts +102 -41
@@ -40,7 +40,7 @@ const INVISIBLE_PATTERN = new RegExp(INVISIBLE_CLASS, "g");
40
40
  // residual scanner that silently alternates verdicts is worse than none.
41
41
  const CONTROL_PROBE = new RegExp(CONTROL_CLASS);
42
42
  const INVISIBLE_PROBE = new RegExp(INVISIBLE_CLASS);
43
- const URL_PATTERN = /\b[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s<>"'`)\]]+/g;
43
+ const URL_PATTERN = /(?<![A-Za-z0-9+.-])[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s<>"'`)\]]+/g;
44
44
  /** Anything still matching these after sanitization means the scanner is not certain. */
45
45
  const RESIDUAL_PATTERNS: readonly RegExp[] = [
46
46
  /\bsk-[A-Za-z0-9_-]{8,}/,
@@ -73,6 +73,7 @@ const FALLBACK_MODELS: GrokCliModelConfig[] = [
73
73
  cost: COST_43,
74
74
  contextWindow: 1_000_000,
75
75
  maxTokens: 30_000,
76
+ maxReasoningEffort: Effort.High,
76
77
  },
77
78
  {
78
79
  // Official metadata and pricing: https://docs.x.ai/developers/models/grok-4.6
@@ -158,23 +159,22 @@ function matchesModelFamily(name: string, modelId: string): boolean {
158
159
  return name.startsWith(`${modelId}-`) && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(suffix);
159
160
  }
160
161
 
162
+ const REASONING_EFFORT_CAPS = [
163
+ { prefix: 'grok-3-mini', max: Effort.High },
164
+ { prefix: 'grok-4.3', max: Effort.High },
165
+ { prefix: 'grok-4.5', max: Effort.High },
166
+ { prefix: 'grok-4.6', max: Effort.XHigh },
167
+ { prefix: 'grok-4.20-multi-agent', max: Effort.XHigh },
168
+ ] as const;
169
+
161
170
  export function getMaxReasoningEffort(modelId: string): Effort | undefined {
162
171
  const name = getCanonicalModelName(modelId);
163
- return FALLBACK_MODELS.find((model) => matchesModelFamily(name, model.id.toLowerCase()))
164
- ?.maxReasoningEffort;
172
+ return REASONING_EFFORT_CAPS.find(({ prefix }) => matchesModelFamily(name, prefix))?.max;
165
173
  }
166
174
 
167
- const EFFORT_CAPABLE_PREFIXES = [
168
- 'grok-3-mini',
169
- 'grok-4.20-multi-agent',
170
- 'grok-4.3',
171
- 'grok-4.5',
172
- 'grok-4.6',
173
- ];
174
-
175
175
  export function supportsReasoningEffort(modelId: string): boolean {
176
176
  const name = getCanonicalModelName(modelId);
177
- if (!EFFORT_CAPABLE_PREFIXES.some((prefix) => matchesModelFamily(name, prefix))) {
177
+ if (!REASONING_EFFORT_CAPS.some(({ prefix }) => matchesModelFamily(name, prefix))) {
178
178
  return false;
179
179
  }
180
180
  const model = resolveModels().find((entry) => getCanonicalModelName(entry.id) === name);
@@ -7,7 +7,12 @@ import { Effort } from '@gajae-code/ai/model-thinking';
7
7
  import type { OAuthCredentials, OAuthLoginCallbacks } from '@gajae-code/ai/utils/oauth/types';
8
8
  import { loginXai, refreshXaiToken, XAI_OAUTH_SCOPE } from '@gajae-code/ai/utils/oauth/xai';
9
9
  import type { ExtensionAPI, ProviderConfig } from '@gajae-code/coding-agent';
10
- import { type GrokCliModelConfig, resolveModels } from '../models/catalog.js';
10
+ import {
11
+ type GrokCliModelConfig,
12
+ getMaxReasoningEffort,
13
+ resolveModels,
14
+ supportsReasoningEffort,
15
+ } from '../models/catalog.js';
11
16
  import { sanitizePayload } from '../payload/sanitize.js';
12
17
  import { getBaseUrl, isGrokBuildBaseUrlOverrideIgnored } from '../shared/base-url.js';
13
18
  import { streamGrokCli } from './stream.js';
@@ -38,7 +43,7 @@ export default function registerGrokCli(api: ExtensionAPI) {
38
43
  thinking: m.reasoning
39
44
  ? {
40
45
  minLevel: Effort.Low,
41
- maxLevel: m.maxReasoningEffort ?? Effort.XHigh,
46
+ maxLevel: getMaxReasoningEffort(m.id) ?? Effort.XHigh,
42
47
  mode: 'effort',
43
48
  }
44
49
  : undefined,
@@ -46,6 +51,12 @@ export default function registerGrokCli(api: ExtensionAPI) {
46
51
  cost: m.cost,
47
52
  contextWindow: m.contextWindow,
48
53
  maxTokens: m.maxTokens,
54
+ // cli-chat-proxy is not a generic audited OpenAI origin. Carry only the
55
+ // catalog's model-scoped evidence into core reasoning request gating.
56
+ compat:
57
+ m.reasoning && supportsReasoningEffort(m.id)
58
+ ? { supportsReasoningEffort: true }
59
+ : undefined,
49
60
  })),
50
61
  oauth: {
51
62
  name: 'Grok Build',
@@ -442,6 +442,28 @@ Receipts are freshness-scoped:
442
442
  - Appending required goals or changing final required-goal state stales final aggregate receipts. Final aggregate completion requires a fresh final aggregate receipt proving no incomplete, blocked, or `review_blocked` required goals remain.
443
443
  - Deferred per-goal receipts (validation-batch members) are incomplete until a matching fresh batch-close receipt exists on the batch's `finalGoalId`; a story-scope query for a deferred member stays blocked until that close, and mutating a member after close stales the batch-close and final aggregate receipts.
444
444
 
445
+ ## Cross-repository succession
446
+
447
+ When an approved run was planned in one repository but the implementation belongs in another, the repository-binding guard correctly refuses to execute the plan from the second repository. Never edit `repositoryBinding` and never copy the runtime directory: both defeat the boundary and launder verification provenance. Use the explicit successor path instead.
448
+
449
+ ```
450
+ gjc ultragoal succession offer --target-repo <path> --goal-id G002 --goal-id G003 --authorize "<who approved what, for which source, target and goals>" --authorized-by <identity> --json
451
+ gjc ultragoal succession adopt --offer <offer path inside the source worktree> --json
452
+ gjc ultragoal succession status --json
453
+ ```
454
+
455
+ `offer` runs in the source repository. It records an offer document plus a durable outgoing ownership fence, and writes nothing to `brief.md`, `goals.json` or `ledger.jsonl`. From that moment the source run refuses to schedule or checkpoint the fenced goals; goals outside the selection stay fully schedulable. Ownership therefore leaves the source at the offer, not when the target happens to adopt, so no interval exists in which both runs may resume the same goal. There is deliberately no `revoke` verb: any read-then-delete of the fence races a concurrent adoption and could revive the source while the target is starting, so an unadopted offer keeps its goals fenced.
456
+
457
+ Because unselected goals stay schedulable, ordinary source progress moves whole-file digests. Adoption therefore compares a **snapshot of the offered work** — the brief plus each selected goal's record and unresolved obligations — instead of whole-file equality, so unrelated progress cannot permanently strand the handed-off work. Whole-file digests are still recorded as provenance and any drift is written into the adoption record.
458
+
459
+ `adopt` runs in the target repository and starts a genuinely fresh run: new pending goals, fresh timestamps, the target repository's own binding, and no inherited receipts, quality gates, validation batches, steering or activated objective. The source brief and every selected objective are carried verbatim, together with the unresolved obligations recorded against those goals — requirements are carried forward in full, never summarized down. Dependency groups travel as explicit obligations with source-to-target id mapping: the source's batch metadata and receipts are not inherited, but the requirement to validate those goals together is.
460
+
461
+ Selection and authorization must be explicit. Only unfinished goals (`pending`, `failed`, `blocked`, `review_blocked`) may be handed off; the source run must be quiescent with no `active` goal; there is no implicit or wildcard selection; and a selection that takes only part of a validation batch, or splits a review blocker from the goal it still blocks, is rejected rather than silently dropping that obligation. Adoption fails closed on changed offered work, an altered or unfenced offer, an offer path that escapes the source worktree, a target the offer does not name, a linked worktree that is not the exact named target, an occupied target session, and any duplicate or simultaneous adoption anywhere in the target repository. A retry reconciles the exact recorded operation or fails closed — a matching operation id is never authority to overwrite target work it did not write, and a published plan that has disappeared is never reconstructed.
462
+
463
+ Until publication completes, the target refuses to execute: a visible `goals.json` is not admission evidence, the adoption record is. `start`/`complete-goals` and `checkpoint` reject while an adoption remains pending.
464
+
465
+ Integrity digests are evidence, not authorization: a matching digest set with no fence, no bounded authorization, or the wrong target is still rejected. Historical source statuses, receipts, reviews and approvals travel as provenance only and never satisfy a target gate.
466
+
445
467
  ## Handoff back to planning
446
468
 
447
469
  When the aggregate ultragoal is complete OR the user requests return to planning/clarification, mark ultragoal ready for handoff so the skill tool's chain guard permits the backward transition:
@@ -28,8 +28,11 @@ import {
28
28
  createSourceMeta,
29
29
  discoverExtensionModulePaths,
30
30
  expandEnvVarsDeep,
31
+ getAncestorDirs,
31
32
  getExtensionNameFromPath,
33
+ getUserSkillScanDirs,
32
34
  loadFilesFromDir,
35
+ readContainedFile,
33
36
  SOURCE_PATHS,
34
37
  scanSkillsFromDir,
35
38
  } from "./helpers";
@@ -41,10 +44,6 @@ const PRIORITY = 100;
41
44
 
42
45
  const PATHS = SOURCE_PATHS.native;
43
46
 
44
- function getUserAgentDirs(): string[] {
45
- return [PATHS.userAgent];
46
- }
47
-
48
47
  /**
49
48
  * GJC's user-scope config directory.
50
49
  *
@@ -92,26 +91,12 @@ async function getConfigDirs(ctx: LoadContext): Promise<Array<{ dir: string; lev
92
91
  return result;
93
92
  }
94
93
 
95
- function getAncestorDirs(cwd: string, stopAt?: string | null): Array<{ dir: string; depth: number }> {
96
- const ancestors: Array<{ dir: string; depth: number }> = [];
97
- let current = cwd;
98
- let depth = 0;
99
- while (true) {
100
- ancestors.push({ dir: current, depth });
101
- if (stopAt && current === stopAt) break;
102
- const parent = path.dirname(current);
103
- if (parent === current) break;
104
- current = parent;
105
- depth++;
106
- }
107
- return ancestors;
108
- }
109
-
110
94
  async function findNearestProjectConfigDir(
111
95
  cwd: string,
112
96
  repoRoot?: string | null,
97
+ home?: string,
113
98
  ): Promise<{ dir: string; depth: number } | null> {
114
- for (const ancestor of getAncestorDirs(cwd, repoRoot)) {
99
+ for (const ancestor of getAncestorDirs(cwd, repoRoot ?? home, home)) {
115
100
  for (const projectConfigDir of getProjectConfigDirs()) {
116
101
  const configDir = await ifNonEmptyDir(ancestor.dir, projectConfigDir);
117
102
  if (configDir) return { dir: configDir, depth: ancestor.depth };
@@ -280,20 +265,21 @@ registerProvider<MCPServer>(mcpCapability.id, {
280
265
  async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemPrompt>> {
281
266
  const items: SystemPrompt[] = [];
282
267
 
283
- for (const userAgentDir of getUserAgentDirs()) {
284
- const userPath = path.join(ctx.home, userAgentDir, "SYSTEM.md");
285
- const userContent = await readFile(userPath);
286
- if (userContent) {
287
- items.push({
288
- path: userPath,
289
- content: userContent,
290
- level: "user",
291
- _source: createSourceMeta(PROVIDER_ID, userPath, "user"),
292
- });
293
- }
268
+ // User scope is the agent directory — the directory `gjc config path` prints
269
+ // and the only user-scope seam. A profile is a separate scope; the default
270
+ // profile's home-relative SYSTEM.md is not read under one.
271
+ const userPath = path.join(resolveUserAgentDir(ctx), "SYSTEM.md");
272
+ const userContent = await readContainedFile(resolveUserAgentDir(ctx), userPath);
273
+ if (userContent) {
274
+ items.push({
275
+ path: userPath,
276
+ content: userContent,
277
+ level: "user",
278
+ _source: createSourceMeta(PROVIDER_ID, userPath, "user"),
279
+ });
294
280
  }
295
281
 
296
- const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot);
282
+ const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot, ctx.home);
297
283
  if (nearestProjectConfigDir) {
298
284
  const projectPath = path.join(nearestProjectConfigDir.dir, "SYSTEM.md");
299
285
  const projectContent = await readFile(projectPath);
@@ -321,7 +307,7 @@ registerProvider<SystemPrompt>(systemPromptCapability.id, {
321
307
  // Skills
322
308
  async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
323
309
  // Walk up from cwd finding .gjc/skills/ in ancestors (closest first)
324
- const ancestors = getAncestorDirs(ctx.cwd, ctx.repoRoot ?? ctx.home);
310
+ const ancestors = getAncestorDirs(ctx.cwd, ctx.repoRoot ?? ctx.home, ctx.home);
325
311
  const projectScans = ancestors.flatMap(({ dir }) =>
326
312
  getProjectConfigDirs().map(projectConfigDir =>
327
313
  scanSkillsFromDir(ctx, {
@@ -333,15 +319,18 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
333
319
  ),
334
320
  );
335
321
 
336
- // User-level scan from the active agent-directory profile.
337
- const userScans = [
322
+ // User-level scan from the selected agent-directory profile plus documented
323
+ // default-profile legacy scan roots.
324
+ const userAgentDir = resolveUserAgentDir(ctx);
325
+ const userScans = getUserSkillScanDirs(ctx.home, userAgentDir, ctx.profileAuthority).map(dir =>
338
326
  scanSkillsFromDir(ctx, {
339
- dir: path.join(resolveUserAgentDir(ctx), "skills"),
327
+ dir,
328
+ authorityRoot: path.resolve(dir) === path.resolve(userAgentDir, "skills") ? userAgentDir : undefined,
340
329
  providerId: PROVIDER_ID,
341
330
  level: "user",
342
331
  requireDescription: true,
343
332
  }),
344
- ];
333
+ );
345
334
 
346
335
  const results = await Promise.all([...projectScans, ...userScans]);
347
336
 
@@ -395,28 +384,36 @@ registerProvider<SlashCommand>(slashCommandCapability.id, {
395
384
  async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
396
385
  const items: Rule[] = [];
397
386
  const warnings: string[] = [];
398
-
399
- for (const { dir, level } of await getConfigDirs(ctx)) {
400
- const rulesDir = path.join(dir, "rules");
401
- const result = await loadFilesFromDir<Rule>(ctx, rulesDir, PROVIDER_ID, level, {
402
- extensions: ["md", "mdc"],
403
- transform: (name, content, path, source) =>
404
- buildRuleFromMarkdown(name, content, path, source, { stripNamePattern: /\.(md|mdc)$/ }),
405
- });
406
- items.push(...result.items);
407
- if (result.warnings) warnings.push(...result.warnings);
408
- }
387
+ const configDirs = await getConfigDirs(ctx);
388
+ const projectConfigDirs = configDirs.filter(({ level }) => level === "project");
389
+ const userConfigDirs = configDirs.filter(({ level }) => level === "user");
390
+
391
+ const loadRulesFromDirs = async (dirs: Array<{ dir: string; level: "user" | "project" }>) => {
392
+ for (const { dir, level } of dirs) {
393
+ const rulesDir = path.join(dir, "rules");
394
+ const result = await loadFilesFromDir<Rule>(ctx, rulesDir, PROVIDER_ID, level, {
395
+ extensions: ["md", "mdc"],
396
+ transform: (name, content, path, source) =>
397
+ buildRuleFromMarkdown(name, content, path, source, { stripNamePattern: /\.(md|mdc)$/ }),
398
+ });
399
+ items.push(...result.items);
400
+ if (result.warnings) warnings.push(...result.warnings);
401
+ }
402
+ };
409
403
 
410
404
  // Top-level RULES.md is a sticky always-apply rule. The context-file
411
405
  // discovery contract treats it as the file "re-injected near the current
412
406
  // turn so they keep hold across long conversations".
413
- // User scope: ~/.gjc/agent/RULES.md
407
+ // User scope: <agentDir>/RULES.md (a profile is a separate user scope;
408
+ // the default profile's home-relative copy is not read)
414
409
  // Project scope: nearest .gjc/RULES.md walking up from cwd to repoRoot
410
+ await loadRulesFromDirs([...projectConfigDirs, ...userConfigDirs]);
411
+
415
412
  const userRulesFile = path.join(resolveUserAgentDir(ctx), "RULES.md");
416
413
  const userRule = await loadStickyRulesFile(userRulesFile, "user");
417
414
  if (userRule) items.push(userRule);
418
415
 
419
- const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot);
416
+ const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot, ctx.home);
420
417
  if (nearestProjectConfigDir) {
421
418
  const projectRulesFile = path.join(nearestProjectConfigDir.dir, "RULES.md");
422
419
  const projectRule = await loadStickyRulesFile(projectRulesFile, "project");
@@ -431,7 +428,7 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
431
428
  * Returns null when the file is absent or empty so callers can short-circuit.
432
429
  */
433
430
  async function loadStickyRulesFile(filePath: string, level: "user" | "project"): Promise<Rule | null> {
434
- const content = await readFile(filePath);
431
+ const content = await readContainedFile(path.dirname(filePath), filePath);
435
432
  if (!content) return null;
436
433
  const source = createSourceMeta(PROVIDER_ID, filePath, level);
437
434
  const rule = buildRuleFromMarkdown("RULES.md", content, filePath, source, { ruleName: "RULES" });
@@ -719,7 +716,7 @@ async function loadHooks(ctx: LoadContext): Promise<LoadResult<Hook>> {
719
716
  const { typeDir, hookType, level } = typeDirRequests[i];
720
717
  const typeEntries = typeEntriesResults[i];
721
718
 
722
- for (const entry of typeEntries) {
719
+ for (const entry of [...typeEntries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
723
720
  if (entry.name.startsWith(".")) continue;
724
721
  if (!entry.isFile()) continue;
725
722
 
@@ -929,8 +926,8 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
929
926
  const items: ContextFile[] = [];
930
927
  const warnings: string[] = [];
931
928
 
932
- const userPath = path.join(ctx.home, PATHS.userAgent, "AGENTS.md");
933
- const userContent = await readFile(userPath);
929
+ const userPath = path.join(resolveUserAgentDir(ctx), "AGENTS.md");
930
+ const userContent = await readContainedFile(resolveUserAgentDir(ctx), userPath);
934
931
  if (userContent) {
935
932
  items.push({
936
933
  path: userPath,
@@ -940,7 +937,7 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
940
937
  });
941
938
  }
942
939
 
943
- const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot);
940
+ const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot, ctx.home);
944
941
  if (nearestProjectConfigDir) {
945
942
  const projectPath = path.join(nearestProjectConfigDir.dir, "AGENTS.md");
946
943
  const projectContent = await readFile(projectPath);
@@ -17,6 +17,7 @@ import { rootContainsGjcManifest } from "../extensibility/gjc-plugins";
17
17
  import {
18
18
  type ClaudePluginRoot,
19
19
  createSourceMeta,
20
+ invalidateClaudePluginRoots,
20
21
  listClaudePluginRoots,
21
22
  loadFilesFromDir,
22
23
  scanSkillsFromDir,
@@ -95,12 +96,15 @@ async function resolvePluginDir(
95
96
  async function listNonGjcPluginRoots(
96
97
  home: string,
97
98
  cwd: string,
99
+ allowedLevels: ReadonlySet<"user" | "project"> = new Set(["user", "project"]),
98
100
  ): Promise<{ roots: ClaudePluginRoot[]; warnings: string[] }> {
101
+ await invalidateClaudePluginRoots(home, cwd);
99
102
  const { roots, warnings } = await listClaudePluginRoots(home, cwd);
100
103
  const filteredRoots: ClaudePluginRoot[] = [];
101
- const filteredWarnings = [...warnings];
104
+ const filteredWarnings = allowedLevels.size === 2 ? [...warnings] : [];
102
105
 
103
106
  for (const root of roots) {
107
+ if (!allowedLevels.has(root.scope)) continue;
104
108
  if (await rootContainsGjcManifest(root.path)) {
105
109
  filteredWarnings.push(`[claude-plugins] Skipping gajae-code plugin root (binding-only): ${root.path}`);
106
110
  continue;
@@ -115,11 +119,14 @@ async function listNonGjcPluginRoots(
115
119
  // Skills
116
120
  // =============================================================================
117
121
 
118
- async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
122
+ export async function loadMarketplaceSkills(
123
+ ctx: LoadContext,
124
+ allowedLevels: ReadonlySet<"user" | "project"> = new Set(["user", "project"]),
125
+ ): Promise<LoadResult<Skill>> {
119
126
  const items: Skill[] = [];
120
127
  const warnings: string[] = [];
121
128
 
122
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd);
129
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd, allowedLevels);
123
130
  warnings.push(...rootWarnings);
124
131
 
125
132
  const results = await Promise.all(
@@ -129,6 +136,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
129
136
  dir: skillsDir,
130
137
  providerId: PROVIDER_ID,
131
138
  level: root.scope,
139
+ requireDescription: true,
132
140
  });
133
141
  return { root, result, warning };
134
142
  }),
@@ -371,7 +379,7 @@ registerProvider<Skill>(skillCapability.id, {
371
379
  displayName: DISPLAY_NAME,
372
380
  description: "Load skills from GJC marketplace plugins",
373
381
  priority: PRIORITY,
374
- load: loadSkills,
382
+ load: loadMarketplaceSkills,
375
383
  });
376
384
 
377
385
  registerProvider<SlashCommand>(slashCommandCapability.id, {