@gajae-code/coding-agent 0.8.1 → 0.9.0

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 (253) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/types/append-only-mode.d.ts +31 -0
  3. package/dist/types/capability/rule.d.ts +8 -0
  4. package/dist/types/cli/completion-cli.d.ts +41 -0
  5. package/dist/types/cli/config-cli.d.ts +1 -0
  6. package/dist/types/cli/fixture-report.d.ts +20 -0
  7. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  8. package/dist/types/cli/nofile-limit.d.ts +11 -0
  9. package/dist/types/cli/notify-cli.d.ts +7 -0
  10. package/dist/types/cli/update-cli.d.ts +22 -0
  11. package/dist/types/cli.d.ts +124 -1
  12. package/dist/types/commands/completion.d.ts +29 -0
  13. package/dist/types/commands/config.d.ts +3 -0
  14. package/dist/types/commands/deep-interview.d.ts +3 -0
  15. package/dist/types/config/file-lock.d.ts +7 -6
  16. package/dist/types/config/keybindings.d.ts +2 -2
  17. package/dist/types/config/model-profile-activation.d.ts +6 -0
  18. package/dist/types/config/settings-schema.d.ts +105 -1
  19. package/dist/types/config/settings.d.ts +8 -0
  20. package/dist/types/discovery/index.d.ts +1 -0
  21. package/dist/types/export/html/template.generated.d.ts +1 -1
  22. package/dist/types/export/ttsr.d.ts +14 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  24. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  25. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
  26. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  27. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
  28. package/dist/types/gjc-runtime/deep-interview-state.d.ts +17 -2
  29. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
  30. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  31. package/dist/types/gjc-runtime/team-runtime.d.ts +28 -1
  32. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  33. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +120 -13
  34. package/dist/types/goals/runtime.d.ts +1 -0
  35. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  36. package/dist/types/main.d.ts +4 -1
  37. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  38. package/dist/types/modes/components/model-selector.d.ts +4 -0
  39. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  40. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  41. package/dist/types/modes/components/status-line.d.ts +9 -0
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  43. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  45. package/dist/types/modes/interactive-mode.d.ts +2 -0
  46. package/dist/types/modes/print-mode.d.ts +13 -1
  47. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  48. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  49. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  50. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  51. package/dist/types/modes/types.d.ts +1 -0
  52. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  53. package/dist/types/notifications/config.d.ts +14 -1
  54. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  55. package/dist/types/notifications/index.d.ts +6 -3
  56. package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
  57. package/dist/types/notifications/telegram-daemon.d.ts +23 -3
  58. package/dist/types/notifications/telegram-reference.d.ts +0 -5
  59. package/dist/types/notifications/threaded-render.d.ts +6 -0
  60. package/dist/types/runtime-api-key.d.ts +7 -0
  61. package/dist/types/sdk.d.ts +3 -0
  62. package/dist/types/session/agent-session.d.ts +53 -1
  63. package/dist/types/session/agent-storage.d.ts +1 -0
  64. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  65. package/dist/types/session/messages.d.ts +4 -0
  66. package/dist/types/session/session-manager.d.ts +17 -1
  67. package/dist/types/skill-state/active-state.d.ts +18 -1
  68. package/dist/types/system-prompt.d.ts +18 -2
  69. package/dist/types/task/executor.d.ts +3 -0
  70. package/dist/types/task/fork-context-budget.d.ts +7 -0
  71. package/dist/types/task/token-log.d.ts +30 -0
  72. package/dist/types/task/token-log.test.d.ts +1 -0
  73. package/dist/types/tools/bisect.d.ts +81 -0
  74. package/dist/types/tools/computer.d.ts +2 -2
  75. package/dist/types/tools/index.d.ts +1 -0
  76. package/dist/types/tools/search.d.ts +2 -2
  77. package/dist/types/utils/file-mentions.d.ts +14 -0
  78. package/dist/types/utils/git.d.ts +32 -0
  79. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  80. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  81. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  82. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  83. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  84. package/package.json +7 -7
  85. package/src/append-only-mode.ts +58 -0
  86. package/src/autoresearch/prompt.md +1 -1
  87. package/src/autoresearch/tools/log-experiment.ts +1 -1
  88. package/src/capability/rule.ts +9 -0
  89. package/src/cli/completion-cli.ts +234 -0
  90. package/src/cli/config-cli.ts +61 -9
  91. package/src/cli/fast-help.ts +1 -0
  92. package/src/cli/fixture-report.test.ts +230 -0
  93. package/src/cli/fixture-report.ts +171 -0
  94. package/src/cli/nofile-limit.ts +48 -0
  95. package/src/cli/notify-cli.ts +91 -7
  96. package/src/cli/update-cli.ts +85 -9
  97. package/src/cli.ts +29 -1
  98. package/src/commands/completion.ts +84 -0
  99. package/src/commands/config.ts +2 -0
  100. package/src/commands/contribution-prep.ts +1 -1
  101. package/src/commands/deep-interview.ts +2 -1
  102. package/src/config/file-lock.ts +73 -34
  103. package/src/config/keybindings.ts +2 -2
  104. package/src/config/model-profile-activation.ts +65 -0
  105. package/src/config/model-registry.ts +14 -2
  106. package/src/config/settings-schema.ts +68 -1
  107. package/src/config/settings.ts +22 -0
  108. package/src/coordinator-mcp/server.ts +34 -8
  109. package/src/defaults/gjc/skills/deep-interview/SKILL.md +50 -18
  110. package/src/defaults/gjc/skills/ralplan/SKILL.md +40 -99
  111. package/src/defaults/gjc/skills/team/SKILL.md +17 -14
  112. package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
  113. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  114. package/src/defaults/gjc-defaults.ts +9 -0
  115. package/src/discovery/helpers.ts +10 -0
  116. package/src/discovery/index.ts +1 -0
  117. package/src/export/html/template.generated.ts +1 -1
  118. package/src/export/html/template.js +60 -2
  119. package/src/export/ttsr.ts +96 -18
  120. package/src/extensibility/extensions/runner.ts +183 -219
  121. package/src/extensibility/extensions/types.ts +9 -0
  122. package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
  123. package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
  124. package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
  125. package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
  126. package/src/gjc-runtime/deep-interview-state.ts +44 -2
  127. package/src/gjc-runtime/session-state-sidecar.ts +263 -20
  128. package/src/gjc-runtime/state-runtime.ts +12 -2
  129. package/src/gjc-runtime/state-writer.ts +15 -0
  130. package/src/gjc-runtime/team-runtime.ts +187 -15
  131. package/src/gjc-runtime/ultragoal-guard.ts +55 -66
  132. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
  133. package/src/gjc-runtime/ultragoal-runtime.ts +1663 -159
  134. package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
  135. package/src/gjc-runtime/workflow-manifest.ts +74 -1
  136. package/src/goals/runtime.ts +10 -2
  137. package/src/hindsight/backend.ts +1 -3
  138. package/src/hooks/native-skill-hook.ts +124 -4
  139. package/src/internal-urls/docs-index.generated.ts +16 -7
  140. package/src/main.ts +95 -43
  141. package/src/memories/index.ts +12 -1
  142. package/src/modes/components/assistant-message.ts +241 -82
  143. package/src/modes/components/custom-editor.ts +1 -0
  144. package/src/modes/components/model-selector.ts +54 -11
  145. package/src/modes/components/queued-message-selector.ts +92 -0
  146. package/src/modes/components/settings-selector.ts +4 -0
  147. package/src/modes/components/status-line.ts +167 -54
  148. package/src/modes/components/tool-execution.ts +44 -19
  149. package/src/modes/components/welcome.ts +160 -44
  150. package/src/modes/controllers/command-controller.ts +40 -4
  151. package/src/modes/controllers/event-controller.ts +66 -47
  152. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  153. package/src/modes/controllers/input-controller.ts +180 -18
  154. package/src/modes/controllers/selector-controller.ts +122 -27
  155. package/src/modes/interactive-mode.ts +51 -16
  156. package/src/modes/print-mode.ts +43 -4
  157. package/src/modes/prompt-action-autocomplete.ts +21 -20
  158. package/src/modes/rpc/rpc-mode.ts +29 -3
  159. package/src/modes/rpc/rpc-types.ts +9 -0
  160. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  161. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  162. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  163. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  164. package/src/modes/types.ts +1 -0
  165. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  166. package/src/modes/utils/ui-helpers.ts +19 -6
  167. package/src/notifications/config.ts +25 -5
  168. package/src/notifications/daemon-paths.ts +2 -0
  169. package/src/notifications/index.ts +117 -14
  170. package/src/notifications/lifecycle-commands.ts +37 -7
  171. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  172. package/src/notifications/telegram-daemon-cli.ts +2 -2
  173. package/src/notifications/telegram-daemon-control.ts +5 -3
  174. package/src/notifications/telegram-daemon.ts +302 -51
  175. package/src/notifications/telegram-reference.ts +0 -16
  176. package/src/notifications/threaded-render.ts +17 -1
  177. package/src/prompts/agents/architect.md +19 -10
  178. package/src/prompts/agents/critic.md +16 -18
  179. package/src/prompts/agents/executor.md +14 -11
  180. package/src/prompts/agents/planner.md +18 -13
  181. package/src/prompts/ci-green-request.md +3 -8
  182. package/src/prompts/goals/goal-continuation.md +0 -5
  183. package/src/prompts/goals/goal-mode-active.md +0 -5
  184. package/src/prompts/system/agent-creation-architect.md +2 -2
  185. package/src/prompts/system/plan-mode-active.md +1 -1
  186. package/src/prompts/system/plan-mode-subagent.md +3 -3
  187. package/src/prompts/system/project-prompt.md +0 -13
  188. package/src/prompts/system/subagent-system-prompt.md +3 -2
  189. package/src/prompts/system/system-prompt.md +29 -19
  190. package/src/prompts/system/volatile-project-context.md +10 -0
  191. package/src/prompts/system/web-search.md +4 -7
  192. package/src/prompts/tools/apply-patch.md +6 -0
  193. package/src/prompts/tools/ast-edit.md +1 -0
  194. package/src/prompts/tools/bash.md +1 -1
  195. package/src/prompts/tools/bisect.md +22 -0
  196. package/src/prompts/tools/browser.md +1 -1
  197. package/src/prompts/tools/computer.md +2 -2
  198. package/src/prompts/tools/cron.md +0 -2
  199. package/src/prompts/tools/eval.md +2 -2
  200. package/src/prompts/tools/find.md +1 -1
  201. package/src/prompts/tools/github.md +6 -6
  202. package/src/prompts/tools/goal.md +1 -3
  203. package/src/prompts/tools/image-gen.md +12 -5
  204. package/src/prompts/tools/irc.md +5 -4
  205. package/src/prompts/tools/job.md +2 -2
  206. package/src/prompts/tools/lsp.md +1 -1
  207. package/src/prompts/tools/monitor.md +2 -2
  208. package/src/prompts/tools/patch.md +2 -2
  209. package/src/prompts/tools/replace.md +2 -21
  210. package/src/prompts/tools/rewind.md +1 -1
  211. package/src/prompts/tools/search-tool-bm25.md +1 -11
  212. package/src/prompts/tools/search.md +1 -1
  213. package/src/prompts/tools/skill.md +3 -2
  214. package/src/prompts/tools/ssh.md +2 -1
  215. package/src/prompts/tools/subagent.md +2 -2
  216. package/src/prompts/tools/task.md +4 -5
  217. package/src/prompts/tools/todo-write.md +1 -1
  218. package/src/prompts/tools/vim.md +1 -0
  219. package/src/prompts/tools/web-search.md +2 -0
  220. package/src/prompts/tools/write.md +8 -2
  221. package/src/runtime-api-key.ts +16 -0
  222. package/src/sdk.ts +52 -28
  223. package/src/session/agent-session.ts +951 -132
  224. package/src/session/agent-storage.ts +4 -0
  225. package/src/session/file-mention-pruning.ts +64 -0
  226. package/src/session/messages.ts +4 -0
  227. package/src/session/session-manager.ts +103 -4
  228. package/src/skill-state/active-state.ts +141 -5
  229. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  230. package/src/slash-commands/builtin-registry.ts +218 -71
  231. package/src/system-prompt.ts +57 -16
  232. package/src/task/executor.ts +132 -24
  233. package/src/task/fork-context-advisory.ts +2 -7
  234. package/src/task/fork-context-budget.ts +9 -0
  235. package/src/task/index.ts +13 -6
  236. package/src/task/output-manager.ts +20 -8
  237. package/src/task/receipt.ts +26 -7
  238. package/src/task/token-log.test.ts +188 -0
  239. package/src/task/token-log.ts +179 -0
  240. package/src/tools/bisect.ts +367 -0
  241. package/src/tools/computer.ts +246 -43
  242. package/src/tools/index.ts +3 -0
  243. package/src/tools/output-meta.ts +116 -4
  244. package/src/tools/search.ts +8 -2
  245. package/src/tools/skill.ts +15 -0
  246. package/src/tools/telegram-send.ts +6 -0
  247. package/src/utils/file-mentions.ts +39 -7
  248. package/src/utils/git.ts +73 -0
  249. package/src/web/scrapers/youtube.ts +24 -12
  250. package/src/workflow/workflow-intent-diff.ts +29 -6
  251. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  252. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  253. package/src/workflow/workflow-intent-report.ts +135 -0
@@ -204,6 +204,7 @@ export type GjcTeamNotificationDeliveryState =
204
204
  | "acknowledged";
205
205
 
206
206
  export type GjcTeamPaneAttemptResult = "sent" | "queued" | "deferred" | "failed";
207
+ export type GjcTeamMailboxDeliveryTransportKind = "notifications_sdk" | "pane";
207
208
 
208
209
  export interface GjcTeamNotification {
209
210
  id: string;
@@ -220,6 +221,36 @@ export interface GjcTeamNotification {
220
221
  updated_at: string;
221
222
  replay_count: number;
222
223
  }
224
+ export interface GjcTeamMailboxDeliveryInput {
225
+ team_name: string;
226
+ state_dir: string;
227
+ config: GjcTeamConfig;
228
+ notification: GjcTeamNotification;
229
+ message: GjcTeamMailboxMessage;
230
+ cwd: string;
231
+ env: NodeJS.ProcessEnv;
232
+ }
233
+ export type GjcTeamMailboxDeliveryResult =
234
+ | { transport: "notifications_sdk"; state: GjcTeamNotificationDeliveryState; reason?: string }
235
+ | { transport: "pane"; state: GjcTeamPaneAttemptResult; reason?: string };
236
+ export interface GjcTeamMailboxDeliveryTransport {
237
+ deliverMailboxMessage(input: GjcTeamMailboxDeliveryInput): Promise<GjcTeamMailboxDeliveryResult | null>;
238
+ }
239
+
240
+ let gjcTeamMailboxDeliveryTransport: GjcTeamMailboxDeliveryTransport | undefined;
241
+
242
+ export function setGjcTeamMailboxDeliveryTransport(transport: GjcTeamMailboxDeliveryTransport | undefined): () => void {
243
+ const previous = gjcTeamMailboxDeliveryTransport;
244
+ gjcTeamMailboxDeliveryTransport = transport;
245
+ return () => {
246
+ gjcTeamMailboxDeliveryTransport = previous;
247
+ };
248
+ }
249
+ export function setGjcTeamMailboxDeliveryTransportForTest(
250
+ transport: GjcTeamMailboxDeliveryTransport | undefined,
251
+ ): () => void {
252
+ return setGjcTeamMailboxDeliveryTransport(transport);
253
+ }
223
254
 
224
255
  export interface GjcTeamNotificationSummary {
225
256
  total: number;
@@ -256,6 +287,7 @@ export interface GjcTeamStartOptions {
256
287
  cwd?: string;
257
288
  env?: NodeJS.ProcessEnv;
258
289
  dryRun?: boolean;
290
+ mailboxDeliveryTransport?: GjcTeamMailboxDeliveryTransport;
259
291
  }
260
292
 
261
293
  export interface GjcTeamApiClaimResult {
@@ -1056,6 +1088,22 @@ async function readConfig(dir: string): Promise<GjcTeamConfig> {
1056
1088
  worker_cli_plan: config.worker_cli_plan ?? Array.from({ length: config.worker_count }, () => "gjc"),
1057
1089
  };
1058
1090
  }
1091
+ const WORKER_INTEGRATION_CONFIG_CACHE_TTL_MS = 100;
1092
+ type WorkerIntegrationConfigCacheEntry = { checkedAt: number; mtimeMs: number; config: GjcTeamConfig };
1093
+ const workerIntegrationConfigCache = new Map<string, WorkerIntegrationConfigCacheEntry>();
1094
+
1095
+ async function readConfigForWorkerIntegration(dir: string): Promise<GjcTeamConfig> {
1096
+ const configPath = path.join(dir, "config.json");
1097
+ const nowMs = Date.now();
1098
+ const stat = await fs.stat(configPath);
1099
+ const cached = workerIntegrationConfigCache.get(configPath);
1100
+ if (cached && cached.mtimeMs === stat.mtimeMs && nowMs - cached.checkedAt <= WORKER_INTEGRATION_CONFIG_CACHE_TTL_MS)
1101
+ return cached.config;
1102
+ const config = await readConfig(dir);
1103
+ workerIntegrationConfigCache.set(configPath, { checkedAt: nowMs, mtimeMs: stat.mtimeMs, config });
1104
+
1105
+ return config;
1106
+ }
1059
1107
  async function readPhase(dir: string): Promise<GjcTeamPhase> {
1060
1108
  const phase = await readJsonFile<{ current_phase?: GjcTeamPhase }>(path.join(dir, "phase.json"));
1061
1109
  return phase?.current_phase ?? "running";
@@ -1704,6 +1752,23 @@ function runGitResult(cwd: string, args: string[]): GitResult {
1704
1752
  stderr: result.stderr.toString().trim(),
1705
1753
  };
1706
1754
  }
1755
+ async function runGitResultAsync(cwd: string, args: string[]): Promise<GitResult> {
1756
+ const result = Bun.spawn(["git", ...args], { cwd, stdout: "pipe", stderr: "pipe" });
1757
+ const [exitCode, stdout, stderr] = await Promise.all([
1758
+ result.exited,
1759
+ new Response(result.stdout).text(),
1760
+ new Response(result.stderr).text(),
1761
+ ]);
1762
+ return {
1763
+ ok: exitCode === 0,
1764
+ stdout: stdout.trim(),
1765
+ stderr: stderr.trim(),
1766
+ };
1767
+ }
1768
+ async function tryRunGitAsync(cwd: string, args: string[]): Promise<string | null> {
1769
+ const result = await runGitResultAsync(cwd, args);
1770
+ return result.ok ? result.stdout : null;
1771
+ }
1707
1772
  function runGit(cwd: string, args: string[]): string {
1708
1773
  const result = runGitResult(cwd, args);
1709
1774
  if (result.ok) return result.stdout;
@@ -1896,16 +1961,39 @@ function readCurrentTmuxLeaderContext(tmuxCommand: string, env: NodeJS.ProcessEn
1896
1961
  }
1897
1962
  return { sessionName, windowIndex, leaderPaneId, target: `${sessionName}:${windowIndex}` };
1898
1963
  }
1964
+ type CommandPathResolver = (command: string) => string | null;
1965
+
1966
+ function isBunVirtualPath(candidate: string | undefined): boolean {
1967
+ const normalized = candidate?.trim().replace(/\\/g, "/");
1968
+ return normalized === "/$bunfs" || normalized?.startsWith("/$bunfs/") === true;
1969
+ }
1970
+
1971
+ function resolveFallbackGjcWorkerCommand(
1972
+ platform: NodeJS.Platform,
1973
+ execPath: string,
1974
+ which: CommandPathResolver,
1975
+ ): string {
1976
+ const pathModule = platform === "win32" ? path.win32 : path;
1977
+ const quote = platform === "win32" ? powershellQuote : shellQuote;
1978
+ const executable = execPath.trim();
1979
+ if (executable && !isBunVirtualPath(executable) && pathModule.isAbsolute(executable)) return quote(executable);
1980
+ const gjcPath = which("gjc")?.trim();
1981
+ if (gjcPath && !isBunVirtualPath(gjcPath)) return quote(gjcPath);
1982
+ return "gjc";
1983
+ }
1984
+
1899
1985
  export function resolveGjcWorkerCommand(
1900
1986
  cwd = process.cwd(),
1901
1987
  env: NodeJS.ProcessEnv = process.env,
1902
1988
  platform: NodeJS.Platform = process.platform,
1903
1989
  argv: string[] = process.argv,
1904
1990
  execPath = process.execPath,
1991
+ which: CommandPathResolver = Bun.which,
1905
1992
  ): string {
1906
1993
  const explicit = env.GJC_TEAM_WORKER_COMMAND?.trim();
1907
1994
  if (explicit) return explicit;
1908
1995
  const entrypoint = argv[1];
1996
+ if (isBunVirtualPath(entrypoint)) return resolveFallbackGjcWorkerCommand(platform, execPath, which);
1909
1997
  if (!entrypoint) return "gjc";
1910
1998
  const pathModule = platform === "win32" ? path.win32 : path;
1911
1999
  const resolvedEntrypoint = pathModule.isAbsolute(entrypoint) ? entrypoint : pathModule.resolve(cwd, entrypoint);
@@ -2295,6 +2383,9 @@ async function appendCommitHygieneEntries(config: GjcTeamConfig, entries: GjcTea
2295
2383
  function resolveHead(cwd: string): string | null {
2296
2384
  return tryRunGit(cwd, ["rev-parse", "HEAD"]);
2297
2385
  }
2386
+ async function resolveHeadAsync(cwd: string): Promise<string | null> {
2387
+ return tryRunGitAsync(cwd, ["rev-parse", "HEAD"]);
2388
+ }
2298
2389
  function isAncestor(cwd: string, ancestor: string, descendant: string): boolean {
2299
2390
  return runGitResult(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]).ok;
2300
2391
  }
@@ -2314,6 +2405,14 @@ function listConflictFiles(cwd: string): string[] {
2314
2405
  .map(line => line.trim())
2315
2406
  .filter(Boolean);
2316
2407
  }
2408
+ async function listConflictFilesAsync(cwd: string): Promise<string[]> {
2409
+ const result = await runGitResultAsync(cwd, ["diff", "--name-only", "--diff-filter=U"]);
2410
+ if (!result.ok || !result.stdout) return [];
2411
+ return result.stdout
2412
+ .split(/\r?\n/)
2413
+ .map(line => line.trim())
2414
+ .filter(Boolean);
2415
+ }
2317
2416
 
2318
2417
  export type GjcWorkerCheckpointClassification =
2319
2418
  | { kind: "clean"; files: string[] }
@@ -2323,17 +2422,14 @@ export type GjcWorkerCheckpointClassification =
2323
2422
  | { kind: "git_error"; files: string[]; detail: string };
2324
2423
 
2325
2424
  const UNMERGED_GIT_STATUS_CODES = new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
2326
- const PROTECTED_WORKER_CHECKPOINT_PREFIXES = [
2327
- ".gjc/_session-*/state/",
2328
- ".gjc/_session-*/logs/",
2329
- ".gjc/_session-*/reports/",
2330
- ".gjc/_session-*/runtime/",
2331
- ".gjc/_session-*/ultragoal/",
2332
- ".gjc/_session-*/plans/",
2333
- ".gjc/_session-*/specs/",
2334
- ".gjc/_session-*/rlm/",
2335
- ".gjc/_session-*/audit/",
2336
- ];
2425
+ // Every generated/runtime artifact for a GJC session lives under
2426
+ // `.gjc/_session-{id}/...` (see session-layout.ts), so worker auto-checkpoints
2427
+ // exclude the whole session subtree instead of enumerating its subdirectories.
2428
+ // The enumerated form drifted: subtrees outside the list (for example the
2429
+ // extragoal gate receipts from docs/extragoal-skill-template.md, or the
2430
+ // session-root `.session-activity.json` marker) were auto-committed and merged
2431
+ // into the leader branch on projects that do not gitignore `.gjc/_session-*/`.
2432
+ const PROTECTED_WORKER_CHECKPOINT_PREFIXES = [".gjc/_session-*/"];
2337
2433
 
2338
2434
  function parsePorcelainStatusFiles(stdout: string): string[] {
2339
2435
  return stdout
@@ -2384,6 +2480,26 @@ export function classifyWorkerCheckpointStatus(cwd: string): GjcWorkerCheckpoint
2384
2480
  return { kind: "protected_only", files: classified.protected };
2385
2481
  return { kind: "eligible", files: classified.eligible };
2386
2482
  }
2483
+ export async function classifyWorkerCheckpointStatusAsync(cwd: string): Promise<GjcWorkerCheckpointClassification> {
2484
+ const status = await runGitResultAsync(cwd, ["status", "--porcelain", "-uall"]);
2485
+ if (!status.ok) {
2486
+ return { kind: "git_error", files: [], detail: status.stderr || status.stdout || "git status failed" };
2487
+ }
2488
+ if (!status.stdout.trim()) return { kind: "clean", files: [] };
2489
+ const files = parsePorcelainStatusFiles(status.stdout);
2490
+ const hasUnmergedStatus = status.stdout
2491
+ .split(/\r?\n/)
2492
+ .filter(Boolean)
2493
+ .some(line => UNMERGED_GIT_STATUS_CODES.has(line.slice(0, 2)));
2494
+ const conflictFiles = await listConflictFilesAsync(cwd);
2495
+ if (hasUnmergedStatus || conflictFiles.length > 0) {
2496
+ return { kind: "conflicted", files: conflictFiles.length > 0 ? conflictFiles : files };
2497
+ }
2498
+ const classified = classifyGjcTeamCheckpointFiles(files);
2499
+ if (classified.eligible.length === 0 && classified.protected.length > 0)
2500
+ return { kind: "protected_only", files: classified.protected };
2501
+ return { kind: "eligible", files: classified.eligible };
2502
+ }
2387
2503
  async function appendIntegrationEvent(
2388
2504
  dir: string,
2389
2505
  type: string,
@@ -2861,6 +2977,7 @@ async function initializeStateDirs(dir: string, workers: GjcTeamWorker[]): Promi
2861
2977
  export async function startGjcTeam(options: GjcTeamStartOptions): Promise<GjcTeamSnapshot> {
2862
2978
  const cwd = options.cwd ?? process.cwd();
2863
2979
  const env = options.env ?? process.env;
2980
+ if (options.mailboxDeliveryTransport) setGjcTeamMailboxDeliveryTransport(options.mailboxDeliveryTransport);
2864
2981
  if (!Number.isInteger(options.workerCount) || options.workerCount < 1 || options.workerCount > GJC_TEAM_MAX_WORKERS)
2865
2982
  throw new Error(`invalid_team_worker_count:${options.workerCount}:expected_1_${GJC_TEAM_MAX_WORKERS}`);
2866
2983
  const workerCliPlan = resolveGjcTeamWorkerCliPlan(options.workerCount, env);
@@ -3042,13 +3159,15 @@ export async function requestGjcWorkerIntegrationAttempt(
3042
3159
  const worker = env.GJC_TEAM_WORKER_ID?.trim() || env.GJC_TEAM_INTERNAL_WORKER?.split("/").pop()?.trim();
3043
3160
  if (!teamName || !worker) return { requested: false, reason: "not_worker" };
3044
3161
  const dir = await findTeamDir(teamName, cwd, env);
3045
- const config = await readConfig(dir);
3162
+ const config = await readConfigForWorkerIntegration(dir);
3046
3163
  const configuredWorker = config.workers.find(candidate => candidate.id === worker);
3047
3164
  const worktreePath = env.GJC_TEAM_WORKTREE_PATH?.trim() || configuredWorker?.worktree_path;
3048
3165
  if (!worktreePath || !(await pathExists(worktreePath)))
3049
3166
  return { requested: false, reason: "missing_worktree", worker, team_name: teamName };
3050
- const classification = classifyWorkerCheckpointStatus(worktreePath);
3051
- const head = resolveHead(worktreePath);
3167
+ const [classification, head] = await Promise.all([
3168
+ classifyWorkerCheckpointStatusAsync(worktreePath),
3169
+ resolveHeadAsync(worktreePath),
3170
+ ]);
3052
3171
  if (classification.kind === "git_error") {
3053
3172
  return { requested: false, reason: "git_error", worker, team_name: teamName, head, status: classification.kind };
3054
3173
  }
@@ -3816,12 +3935,59 @@ async function reconcileTeamNotifications(dir: string, config: GjcTeamConfig): P
3816
3935
  }
3817
3936
  return summarizeNotifications(await listNotificationRecords(dir));
3818
3937
  }
3938
+ async function messageForNotification(
3939
+ dir: string,
3940
+ notification: GjcTeamNotification,
3941
+ ): Promise<GjcTeamMailboxMessage | null> {
3942
+ if (notification.kind !== "mailbox_message" || notification.source.type !== "message") return null;
3943
+ const mailbox = await readMailbox(dir, notification.recipient);
3944
+ return mailbox.messages.find(message => message.message_id === notification.source.id) ?? null;
3945
+ }
3946
+
3947
+ async function attemptConfiguredMailboxTransport(
3948
+ dir: string,
3949
+ config: GjcTeamConfig,
3950
+ notification: GjcTeamNotification,
3951
+ cwd: string,
3952
+ env: NodeJS.ProcessEnv,
3953
+ ): Promise<GjcTeamNotification | null> {
3954
+ if (!gjcTeamMailboxDeliveryTransport) return null;
3955
+ const message = await messageForNotification(dir, notification);
3956
+ if (!message) return null;
3957
+ try {
3958
+ const result = await gjcTeamMailboxDeliveryTransport.deliverMailboxMessage({
3959
+ team_name: config.team_name,
3960
+ state_dir: dir,
3961
+ config,
3962
+ notification,
3963
+ message,
3964
+ cwd,
3965
+ env,
3966
+ });
3967
+ if (!result) return null;
3968
+ if (result.transport === "notifications_sdk" && result.state === "failed") return null;
3969
+ return writeNotificationRecord(dir, {
3970
+ ...notification,
3971
+ delivery_state: result.state,
3972
+ pane_attempt_result: result.transport === "pane" ? result.state : undefined,
3973
+ pane_attempt_reason: result.reason ?? result.transport,
3974
+ pane_attempt_at: now(),
3975
+ updated_at: now(),
3976
+ });
3977
+ } catch {
3978
+ return null;
3979
+ }
3980
+ }
3981
+
3819
3982
  async function attemptPaneNotification(
3820
3983
  dir: string,
3821
3984
  config: GjcTeamConfig,
3822
3985
  notification: GjcTeamNotification,
3823
3986
  env: NodeJS.ProcessEnv,
3987
+ cwd = process.cwd(),
3824
3988
  ): Promise<GjcTeamNotification> {
3989
+ const transported = await attemptConfiguredMailboxTransport(dir, config, notification, cwd, env);
3990
+ if (transported) return transported;
3825
3991
  const paneId =
3826
3992
  notification.recipient === "leader-fixed"
3827
3993
  ? config.leader.pane_id
@@ -3868,6 +4034,7 @@ export async function replayGjcTeamNotifications(
3868
4034
  replay_count: (notification.replay_count ?? 0) + 1,
3869
4035
  },
3870
4036
  env,
4037
+ cwd,
3871
4038
  );
3872
4039
  next.push(attempted);
3873
4040
  }
@@ -3896,8 +4063,13 @@ export async function sendGjcTeamMessage(
3896
4063
  ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),
3897
4064
  };
3898
4065
  const written = await writeMailboxMessage(dir, toWorker, message);
4066
+ const existingNotification = await readJsonFile<GjcTeamNotification>(
4067
+ notificationPath(dir, messageNotificationId(config.team_name, toWorker, written.message_id)),
4068
+ );
3899
4069
  const notification = await createMessageNotification(dir, config.team_name, written);
3900
- await attemptPaneNotification(dir, config, notification, env);
4070
+ if (!existingNotification) {
4071
+ await attemptPaneNotification(dir, config, notification, env, cwd);
4072
+ }
3901
4073
  await appendEvent(dir, {
3902
4074
  type: "message_sent",
3903
4075
  worker: fromWorker,
@@ -1,17 +1,15 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
3
3
  import { resolveGjcSessionForRead, SessionResolutionError } from "./session-resolution";
4
+ import { validateDeferredMemberReceiptFresh, validateReceiptFreshBase } from "./ultragoal-receipt-freshness";
4
5
  import {
5
- computeUltragoalPlanGeneration,
6
6
  getUltragoalPaths,
7
7
  getUltragoalRunCompletionState,
8
- hashStructuredValue,
9
8
  readUltragoalLedger,
10
9
  readUltragoalPlan,
11
10
  recordUltragoalNudgeIfBudgetRemaining,
12
11
  resolveUltragoalNudgeBudget,
13
12
  selectUltragoalNudgeTarget,
14
- type UltragoalCompletionVerification,
15
13
  type UltragoalGoal,
16
14
  type UltragoalLedgerEvent,
17
15
  type UltragoalNudgeSurface,
@@ -176,26 +174,6 @@ function findReceiptGoal(
176
174
  return storyGoal ? { goal: storyGoal, receiptKind: "per-goal" } : null;
177
175
  }
178
176
 
179
- function findLedgerReceiptEvent(
180
- ledger: readonly UltragoalLedgerEvent[],
181
- receipt: UltragoalCompletionVerification,
182
- ): UltragoalLedgerEvent | null {
183
- return (
184
- ledger.find(event => {
185
- if (event.eventId !== receipt.checkpointLedgerEventId) return false;
186
- if (event.event !== "goal_checkpointed") return false;
187
- if (event.goalId !== receipt.goalId) return false;
188
- const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
189
- return (
190
- event.status === "complete" &&
191
- eventReceipt?.receiptId === receipt.receiptId &&
192
- eventReceipt.receiptKind === receipt.receiptKind &&
193
- eventReceipt.planGeneration === receipt.planGeneration
194
- );
195
- }) ?? null
196
- );
197
- }
198
-
199
177
  export function validateCompletionReceipt(input: {
200
178
  plan: UltragoalPlan;
201
179
  ledger: readonly UltragoalLedgerEvent[];
@@ -210,55 +188,66 @@ export function validateCompletionReceipt(input: {
210
188
  goalId: input.goal.id,
211
189
  };
212
190
  }
213
- if (
214
- receipt.schemaVersion !== 1 ||
215
- receipt.goalId !== input.goal.id ||
216
- receipt.receiptKind !== input.receiptKind ||
217
- !receipt.planGeneration ||
218
- !receipt.checkpointLedgerEventId
219
- ) {
220
- return {
221
- state: "active_stale_receipt",
222
- message: `Ultragoal ${input.goal.id} receipt is malformed or stale.`,
223
- goalId: input.goal.id,
224
- };
225
- }
226
- const event = findLedgerReceiptEvent(input.ledger, receipt);
227
- if (!event) {
228
- return {
229
- state: "active_stale_receipt",
230
- message: `Ultragoal ${input.goal.id} receipt ledger event is missing.`,
231
- goalId: input.goal.id,
232
- };
191
+ if (receipt.validationBatch?.role === "deferred-member") {
192
+ return validateDeferredMemberReceiptFresh({
193
+ plan: input.plan,
194
+ ledger: input.ledger,
195
+ goal: input.goal,
196
+ receipt,
197
+ receiptKind: input.receiptKind,
198
+ requireClose: true,
199
+ });
233
200
  }
234
- const generation = computeUltragoalPlanGeneration({
201
+ const baseDiagnostic = validateReceiptFreshBase({
235
202
  plan: input.plan,
236
203
  ledger: input.ledger,
237
204
  goal: input.goal,
205
+ receipt,
238
206
  receiptKind: input.receiptKind,
239
- beforeStatus: receipt.goalStatusBeforeCheckpoint,
240
- excludeEventId: receipt.checkpointLedgerEventId,
241
207
  });
242
- if (generation.planGeneration !== receipt.planGeneration) {
243
- return {
244
- state: "active_stale_receipt",
245
- message: `Ultragoal ${input.goal.id} receipt generation is stale.`,
246
- goalId: input.goal.id,
247
- };
248
- }
249
- if (hashStructuredValue(event.qualityGateJson) !== receipt.qualityGateHash) {
250
- return {
251
- state: "active_dirty_quality_gate",
252
- message: `Ultragoal ${input.goal.id} receipt quality-gate hash does not match ledger.`,
253
- goalId: input.goal.id,
254
- };
255
- }
256
- if (input.goal.updatedAt !== receipt.verifiedAt) {
257
- return {
258
- state: "active_stale_receipt",
259
- message: `Ultragoal ${input.goal.id} receipt target changed after verification.`,
260
- goalId: input.goal.id,
261
- };
208
+ if (baseDiagnostic) return baseDiagnostic;
209
+ if (receipt.validationBatch?.role === "batch-close") {
210
+ for (const memberId of receipt.validationBatch.memberIds) {
211
+ const member = input.plan.goals.find(goal => goal.id === memberId);
212
+ if (
213
+ !member?.validationBatch ||
214
+ member.validationBatch.metadataHash !== receipt.validationBatch.memberMetadataHashes[memberId]
215
+ ) {
216
+ return {
217
+ state: "active_stale_receipt",
218
+ message: `Ultragoal ${input.goal.id} batch-close receipt has stale member metadata for ${memberId}.`,
219
+ goalId: input.goal.id,
220
+ };
221
+ }
222
+ if (memberId === receipt.validationBatch.finalGoalId) continue;
223
+ const memberReceipt = member.completionVerification;
224
+ if (memberReceipt?.validationBatch?.role !== "deferred-member") {
225
+ return {
226
+ state: "active_missing_final_receipt",
227
+ message: `Ultragoal ${input.goal.id} batch-close receipt requires deferred member receipt for ${memberId}.`,
228
+ goalId: input.goal.id,
229
+ };
230
+ }
231
+ const memberDiagnostic = validateDeferredMemberReceiptFresh({
232
+ plan: input.plan,
233
+ ledger: input.ledger,
234
+ goal: member,
235
+ receipt: memberReceipt,
236
+ receiptKind: "per-goal",
237
+ requireClose: false,
238
+ });
239
+ if (memberDiagnostic.state !== "active_verified_complete") return memberDiagnostic;
240
+ if (
241
+ receipt.validationBatch.memberReceiptIds[memberId] !== memberReceipt.receiptId ||
242
+ receipt.validationBatch.memberChangeSetHashes[memberId] !== memberReceipt.validationBatch.changeSetHash
243
+ ) {
244
+ return {
245
+ state: "active_stale_receipt",
246
+ message: `Ultragoal ${input.goal.id} batch-close receipt is stale for deferred member ${memberId}.`,
247
+ goalId: input.goal.id,
248
+ };
249
+ }
250
+ }
262
251
  }
263
252
  if (input.receiptKind === "final-aggregate") {
264
253
  const incomplete = requiredGoals(input.plan).filter(goal => goal.status !== "complete");