@gajae-code/coding-agent 0.12.0 → 0.12.2

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 (247) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/dist/types/async/job-manager.d.ts +17 -3
  3. package/dist/types/config/settings-schema.d.ts +56 -2
  4. package/dist/types/exa/mcp-client.d.ts +10 -1
  5. package/dist/types/exec/bash-executor.d.ts +45 -12
  6. package/dist/types/extensibility/extensions/types.d.ts +16 -2
  7. package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
  8. package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
  9. package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
  10. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
  11. package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
  12. package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
  13. package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
  14. package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
  15. package/dist/types/gjc-runtime/ralplan-review-conflicts.d.ts +114 -0
  16. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +23 -3
  17. package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
  18. package/dist/types/gjc-runtime/team-runtime.d.ts +25 -0
  19. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
  20. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +10 -0
  21. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +37 -80
  22. package/dist/types/modes/acp/acp-agent.d.ts +2 -19
  23. package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
  24. package/dist/types/modes/components/irc-sidebar.d.ts +16 -0
  25. package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
  26. package/dist/types/modes/components/session-observer-overlay.d.ts +18 -0
  27. package/dist/types/modes/components/settings-selector.d.ts +9 -0
  28. package/dist/types/modes/components/status-line/gh.d.ts +5 -0
  29. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
  30. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  31. package/dist/types/modes/controllers/event-controller.d.ts +1 -1
  32. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
  33. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  34. package/dist/types/modes/interactive-mode.d.ts +9 -0
  35. package/dist/types/modes/theme/theme.d.ts +3 -1
  36. package/dist/types/modes/types.d.ts +32 -0
  37. package/dist/types/reminders/star-reminder.d.ts +3 -13
  38. package/dist/types/runtime-mcp/manager.d.ts +1 -0
  39. package/dist/types/sdk/acp/adapter.d.ts +3 -1
  40. package/dist/types/sdk/broker/transport.d.ts +2 -0
  41. package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
  42. package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -1
  43. package/dist/types/sdk/bus/config.d.ts +6 -2
  44. package/dist/types/sdk/bus/index.d.ts +29 -0
  45. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +8 -1
  46. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  47. package/dist/types/sdk/bus/reconciliation-store.d.ts +7 -3
  48. package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
  49. package/dist/types/sdk/bus/rich-render.d.ts +2 -0
  50. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
  51. package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
  52. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -4
  53. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
  54. package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
  55. package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
  56. package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
  57. package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
  58. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  59. package/dist/types/sdk/prompt-status.d.ts +23 -3
  60. package/dist/types/sdk/session.d.ts +6 -0
  61. package/dist/types/session/agent-session.d.ts +40 -2
  62. package/dist/types/session/artifacts.d.ts +8 -0
  63. package/dist/types/session/blob-store.d.ts +57 -4
  64. package/dist/types/session/internal/managed-session-storage.d.ts +30 -4
  65. package/dist/types/session/session-manager.d.ts +23 -6
  66. package/dist/types/session/session-storage.d.ts +4 -0
  67. package/dist/types/session/streaming-output.d.ts +36 -7
  68. package/dist/types/setup/credential-import.d.ts +12 -0
  69. package/dist/types/skill-state/active-state.d.ts +12 -0
  70. package/dist/types/skill-state/workflow-hud.d.ts +6 -0
  71. package/dist/types/task/executor.d.ts +3 -2
  72. package/dist/types/task/index.d.ts +7 -1
  73. package/dist/types/task/output-manager.d.ts +1 -0
  74. package/dist/types/task/provider-retry-status.d.ts +8 -0
  75. package/dist/types/task/receipt.d.ts +9 -1
  76. package/dist/types/task/types.d.ts +23 -0
  77. package/dist/types/tools/ask-answer-registry.d.ts +2 -2
  78. package/dist/types/tools/bash-interactive.d.ts +4 -1
  79. package/dist/types/tools/bash.d.ts +3 -1
  80. package/dist/types/tools/image-gen.d.ts +4 -0
  81. package/dist/types/tools/index.d.ts +6 -0
  82. package/dist/types/tools/output-meta.d.ts +23 -2
  83. package/dist/types/tools/path-utils.d.ts +1 -0
  84. package/dist/types/tools/read.d.ts +0 -6
  85. package/dist/types/tools/review.d.ts +5 -7
  86. package/dist/types/tools/subagent.d.ts +2 -0
  87. package/dist/types/utils/gh.d.ts +13 -0
  88. package/dist/types/utils/image-resize.d.ts +1 -0
  89. package/dist/types/web/search/providers/kimi.d.ts +2 -0
  90. package/dist/types/web/search/providers/searxng.d.ts +7 -0
  91. package/dist/types/web/search/providers/xai.d.ts +2 -0
  92. package/package.json +10 -8
  93. package/scripts/acp-conformance-agent.ts +218 -0
  94. package/scripts/capture-gjc-bundle-settings.ts +306 -0
  95. package/scripts/dogfood-ralplan-review-conflicts.ts +272 -0
  96. package/scripts/generate-sdk-operation-inventory.ts +5 -0
  97. package/scripts/resident-memory-bench.ts +1341 -0
  98. package/scripts/run-acp-conformance.ts +290 -0
  99. package/src/async/job-manager.ts +99 -39
  100. package/src/cli/fast-help.ts +1 -1
  101. package/src/cli/plugin-cli.ts +176 -18
  102. package/src/commands/team.ts +19 -1
  103. package/src/config/file-lock.ts +11 -2
  104. package/src/config/settings-schema.ts +53 -2
  105. package/src/coordinator-mcp/question-gate-codec.ts +1 -1
  106. package/src/coordinator-mcp/server.ts +5 -12
  107. package/src/debug/index.ts +42 -17
  108. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
  109. package/src/defaults/gjc/skills/ralplan/SKILL.md +25 -20
  110. package/src/defaults/gjc/skills/team/SKILL.md +2 -0
  111. package/src/defaults/gjc/skills/ultragoal/SKILL.md +39 -26
  112. package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
  113. package/src/defaults/gjc-defaults.ts +4 -4
  114. package/src/exa/mcp-client.ts +12 -3
  115. package/src/exec/bash-executor.ts +144 -27
  116. package/src/extensibility/extensions/runner.ts +15 -0
  117. package/src/extensibility/extensions/types.ts +18 -1
  118. package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
  119. package/src/extensibility/gjc-plugins/index.ts +19 -2
  120. package/src/extensibility/gjc-plugins/installer.ts +189 -50
  121. package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
  122. package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
  123. package/src/extensibility/gjc-plugins/observability.ts +3 -3
  124. package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
  125. package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
  126. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
  127. package/src/extensibility/gjc-plugins/schema.ts +84 -12
  128. package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
  129. package/src/extensibility/gjc-plugins/types.ts +141 -0
  130. package/src/extensibility/gjc-plugins/validation.ts +9 -0
  131. package/src/gjc-runtime/launch-tmux.ts +25 -6
  132. package/src/gjc-runtime/launch-worktree.ts +103 -1
  133. package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
  134. package/src/gjc-runtime/managed-owner-admission.ts +1 -1
  135. package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
  136. package/src/gjc-runtime/ralplan-runtime.ts +491 -27
  137. package/src/gjc-runtime/team-launch.ts +8 -0
  138. package/src/gjc-runtime/team-runtime.ts +140 -32
  139. package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
  140. package/src/gjc-runtime/tmux-owner-isolation.ts +10 -0
  141. package/src/gjc-runtime/tmux-sessions.ts +58 -13
  142. package/src/gjc-runtime/ultragoal-evidence.ts +6 -74
  143. package/src/gjc-runtime/ultragoal-runtime.ts +635 -612
  144. package/src/gjc-runtime/workflow-manifest.generated.json +37 -99
  145. package/src/gjc-runtime/workflow-manifest.ts +21 -70
  146. package/src/internal-urls/docs-index.generated.ts +16 -16
  147. package/src/modes/DESIGN.md +104 -0
  148. package/src/modes/acp/acp-agent.ts +298 -186
  149. package/src/modes/components/gjc-bundle-settings.ts +508 -0
  150. package/src/modes/components/irc-sidebar.ts +30 -1
  151. package/src/modes/components/notifications-settings-editor.ts +12 -1
  152. package/src/modes/components/oauth-selector.ts +68 -30
  153. package/src/modes/components/session-observer-overlay.ts +305 -86
  154. package/src/modes/components/settings-selector.ts +56 -5
  155. package/src/modes/components/status-line/gh.ts +16 -0
  156. package/src/modes/components/tool-status-header.ts +3 -15
  157. package/src/modes/components/transcript-viewer-overlay.ts +99 -24
  158. package/src/modes/controllers/command-controller.ts +42 -34
  159. package/src/modes/controllers/event-controller.ts +27 -16
  160. package/src/modes/controllers/extension-ui-controller.ts +193 -34
  161. package/src/modes/controllers/selector-controller.ts +160 -22
  162. package/src/modes/interactive-mode.ts +189 -62
  163. package/src/modes/jobs-observer.ts +1 -1
  164. package/src/modes/runtime-init.ts +3 -0
  165. package/src/modes/theme/theme.ts +11 -9
  166. package/src/modes/types.ts +88 -0
  167. package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
  168. package/src/prompts/system/eager-todo.md +8 -4
  169. package/src/prompts/system/system-prompt.md +11 -4
  170. package/src/prompts/tools/bash.md +5 -5
  171. package/src/prompts/tools/search-tool-bm25.md +5 -0
  172. package/src/prompts/tools/task.md +1 -1
  173. package/src/reminders/star-reminder.ts +4 -42
  174. package/src/runtime-mcp/manager.ts +19 -4
  175. package/src/sdk/acp/adapter.ts +22 -6
  176. package/src/sdk/broker/lifecycle.ts +18 -13
  177. package/src/sdk/broker/transport.ts +35 -3
  178. package/src/sdk/bus/attachment-registry.ts +5 -1
  179. package/src/sdk/bus/chat-daemon-cli.ts +4 -39
  180. package/src/sdk/bus/chat-daemon-control.ts +7 -3
  181. package/src/sdk/bus/config.ts +18 -2
  182. package/src/sdk/bus/index.ts +661 -126
  183. package/src/sdk/bus/kind-aware-reconciliation.ts +148 -73
  184. package/src/sdk/bus/lifecycle-commands.ts +4 -1
  185. package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
  186. package/src/sdk/bus/reconciliation-store.ts +95 -16
  187. package/src/sdk/bus/rich-draft.ts +1 -1
  188. package/src/sdk/bus/rich-render.ts +2 -2
  189. package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
  190. package/src/sdk/bus/telegram-cli.ts +33 -8
  191. package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
  192. package/src/sdk/bus/telegram-daemon-contract.ts +12 -4
  193. package/src/sdk/bus/telegram-daemon.ts +916 -23
  194. package/src/sdk/bus/telegram-reference.ts +40 -3
  195. package/src/sdk/bus/threaded-render.ts +48 -3
  196. package/src/sdk/bus/topic-registry.ts +20 -0
  197. package/src/sdk/host/query/revision-store.ts +36 -2
  198. package/src/sdk/host/reverse-leases.ts +44 -7
  199. package/src/sdk/prompt-status.ts +16 -3
  200. package/src/sdk/protocol/operation-inventory.generated.json +33 -0
  201. package/src/sdk/session.ts +89 -9
  202. package/src/session/agent-session.ts +844 -173
  203. package/src/session/artifacts.ts +22 -0
  204. package/src/session/auth-broker-config.ts +10 -3
  205. package/src/session/blob-store.ts +915 -24
  206. package/src/session/internal/managed-session-scope.ts +200 -111
  207. package/src/session/internal/managed-session-storage.ts +492 -62
  208. package/src/session/session-manager.ts +1614 -435
  209. package/src/session/session-storage.ts +38 -45
  210. package/src/session/streaming-output.ts +238 -68
  211. package/src/setup/credential-import.ts +75 -13
  212. package/src/skill-state/active-state.ts +32 -0
  213. package/src/skill-state/workflow-hud.ts +17 -0
  214. package/src/skill-state/workflow-mutation-guard.ts +496 -2
  215. package/src/task/executor.ts +122 -39
  216. package/src/task/index.ts +280 -45
  217. package/src/task/output-manager.ts +24 -14
  218. package/src/task/provider-retry-status.ts +16 -0
  219. package/src/task/receipt.ts +34 -8
  220. package/src/task/render.ts +6 -2
  221. package/src/task/types.ts +108 -0
  222. package/src/tools/ask-answer-registry.ts +12 -6
  223. package/src/tools/ask.ts +86 -55
  224. package/src/tools/ast-edit.ts +1 -0
  225. package/src/tools/ast-grep.ts +1 -0
  226. package/src/tools/bash-interactive.ts +8 -3
  227. package/src/tools/bash.ts +503 -60
  228. package/src/tools/find.ts +1 -0
  229. package/src/tools/image-gen.ts +29 -5
  230. package/src/tools/index.ts +6 -0
  231. package/src/tools/output-meta.ts +108 -14
  232. package/src/tools/path-utils.ts +3 -2
  233. package/src/tools/read.ts +18 -0
  234. package/src/tools/review.ts +5 -7
  235. package/src/tools/search.ts +1 -0
  236. package/src/tools/subagent-render.ts +6 -2
  237. package/src/tools/subagent.ts +6 -0
  238. package/src/tools/telegram-send.ts +6 -3
  239. package/src/utils/gh.ts +39 -0
  240. package/src/utils/image-resize.ts +3 -0
  241. package/src/web/search/providers/anthropic.ts +4 -4
  242. package/src/web/search/providers/kimi.ts +15 -5
  243. package/src/web/search/providers/searxng.ts +47 -4
  244. package/src/web/search/providers/xai.ts +7 -2
  245. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
  246. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
  247. package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
@@ -0,0 +1,306 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import type { GjcLifecycleContext } from "../src/extensibility/gjc-plugins/lifecycle";
4
+ import type { GjcRuntimeSnapshotProvider } from "../src/extensibility/gjc-plugins/runtime-quarantine";
5
+ import type {
6
+ GjcBundleIdentity,
7
+ GjcBundleSummary,
8
+ GjcLifecycleResult,
9
+ GjcToggleResult,
10
+ GjcUpdateApplyResult,
11
+ GjcUpdatePreview,
12
+ } from "../src/extensibility/gjc-plugins/types";
13
+ import { type GjcBundleLifecyclePort, GjcBundleSettingsComponent } from "../src/modes/components/gjc-bundle-settings";
14
+ import { setTheme } from "../src/modes/theme/theme";
15
+ import {
16
+ GJC_BUNDLE_SETTINGS_ENTRIES,
17
+ GJC_BUNDLE_SETTINGS_STATES,
18
+ GJC_BUNDLE_SETTINGS_VIEWPORTS,
19
+ type GjcBundleSettingsFixture,
20
+ } from "../test/fixtures/gjc-bundles-settings-cases";
21
+
22
+ export const GJC_BUNDLE_SETTINGS_CAPTURE_FILES = [
23
+ "terminal.txt",
24
+ "terminal-ansi.txt",
25
+ "terminal.html",
26
+ "metadata.json",
27
+ ] as const;
28
+
29
+ type CaptureFileName = (typeof GJC_BUNDLE_SETTINGS_CAPTURE_FILES)[number];
30
+
31
+ type FixtureEntry = (typeof GJC_BUNDLE_SETTINGS_ENTRIES)[number];
32
+
33
+ type CapturePlanItem = {
34
+ entryId: string;
35
+ fileName: CaptureFileName;
36
+ };
37
+
38
+ type CaptureMetadata = {
39
+ entryId: string;
40
+ stateId: string;
41
+ viewport: { id: string; cols: number; rows: number };
42
+ variant: { renderMode: string };
43
+ sha256: Record<Exclude<CaptureFileName, "metadata.json">, string>;
44
+ };
45
+
46
+ /**
47
+ * Locator-shaped content that must never reach a capture. Applied to the
48
+ * rendered terminal text, which is the only surface that can carry a leaked
49
+ * locator; generated HTML/CSS legitimately contains `#rrggbb` colors and
50
+ * escaped entities, so it is checked against the same rules via its source
51
+ * text rather than its markup.
52
+ */
53
+ const FORBIDDEN_CAPTURE_CONTENT = /:\/\/user:|@[^\s/]+|\?[^\s]*=|token|\/Users\/|\/home\//i;
54
+
55
+ function sha256(content: string): string {
56
+ return new Bun.CryptoHasher("sha256").update(content).digest("hex");
57
+ }
58
+
59
+ function json(value: unknown): string {
60
+ return `${JSON.stringify(value, null, 2)}\n`;
61
+ }
62
+
63
+ function escapeHtml(text: string): string {
64
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
65
+ }
66
+
67
+ function ansiToHtml(ansi: string): string {
68
+ const sgr = /\x1b\[([0-9;]*)m/g;
69
+ let html = "";
70
+ let offset = 0;
71
+ let style = "";
72
+ for (const match of ansi.matchAll(sgr)) {
73
+ html += `<span style="${style}">${escapeHtml(ansi.slice(offset, match.index))}</span>`;
74
+ offset = (match.index ?? 0) + match[0].length;
75
+ const codes = (match[1] || "0").split(";").map(Number);
76
+ for (let index = 0; index < codes.length; index += 1) {
77
+ const code = codes[index];
78
+ if (code === 0) style = "";
79
+ else if (code === 1) style = `${style}font-weight:700;`;
80
+ else if (code === 2) style = `${style}opacity:.65;`;
81
+ else if (code === 22) style = style.replace("font-weight:700;", "").replace("opacity:.65;", "");
82
+ else if (code === 38 && codes[index + 1] === 2) {
83
+ const [red, green, blue] = codes.slice(index + 2, index + 5);
84
+ if ([red, green, blue].every(Number.isInteger)) style = `${style}color:rgb(${red},${green},${blue});`;
85
+ index += 4;
86
+ } else if (code === 39) style = style.replace(/color:[^;]+;/g, "");
87
+ }
88
+ }
89
+ html += `<span style="${style}">${escapeHtml(ansi.slice(offset))}</span>`;
90
+ return `<!doctype html>
91
+ <html lang="en">
92
+ <head><meta charset="utf-8"><meta name="color-scheme" content="dark"><title>GJC Bundles settings</title><style>body{margin:0;background:#111;color:#eee}pre{margin:0;padding:1em;white-space:pre-wrap;font-family:ui-monospace,monospace;line-height:1.2}</style></head>
93
+ <body><pre>${html}</pre></body>
94
+ </html>
95
+ `;
96
+ }
97
+
98
+ function asciiText(text: string): string {
99
+ return text
100
+ .replaceAll("─", "-")
101
+ .replaceAll("…", "...")
102
+ .replaceAll("·", ".")
103
+ .replaceAll("→", "->")
104
+ .replaceAll("−", "-");
105
+ }
106
+
107
+ function cloneSummary(summary: GjcBundleSummary): GjcBundleSummary {
108
+ return {
109
+ ...summary,
110
+ identity: { ...summary.identity },
111
+ source: { ...summary.source },
112
+ surfaces: summary.surfaces.map(surface => ({ ...surface })),
113
+ };
114
+ }
115
+
116
+ class FixtureLifecyclePort implements GjcBundleLifecyclePort {
117
+ constructor(private readonly fixture: GjcBundleSettingsFixture) {}
118
+
119
+ async listGjcBundles(_ctx: GjcLifecycleContext): Promise<GjcBundleSummary[]> {
120
+ return this.fixture.bundles.map(cloneSummary);
121
+ }
122
+
123
+ async getGjcBundle(
124
+ _ctx: GjcLifecycleContext,
125
+ identity: GjcBundleIdentity,
126
+ ): Promise<GjcLifecycleResult<GjcBundleSummary>> {
127
+ const summary = this.fixture.bundles.find(
128
+ bundle =>
129
+ bundle.identity.kind === identity.kind &&
130
+ bundle.identity.scope === identity.scope &&
131
+ bundle.identity.name === identity.name,
132
+ );
133
+ return summary
134
+ ? { ok: true, value: cloneSummary(summary) }
135
+ : { ok: false, error: { code: "not_installed", message: "Bundle is not installed." } };
136
+ }
137
+
138
+ async previewGjcBundleUpdate(
139
+ _ctx: GjcLifecycleContext,
140
+ _identity: GjcBundleIdentity,
141
+ ): Promise<GjcLifecycleResult<GjcUpdatePreview>> {
142
+ return this.fixture.updatePreview
143
+ ? { ok: true, value: this.fixture.updatePreview }
144
+ : { ok: false, error: { code: "source_unsupported", message: "Update is unavailable." } };
145
+ }
146
+
147
+ async applyGjcBundleUpdate(
148
+ _ctx: GjcLifecycleContext,
149
+ _token: GjcUpdatePreview["token"],
150
+ ): Promise<GjcLifecycleResult<GjcUpdateApplyResult>> {
151
+ return { ok: false, error: { code: "stale_candidate", message: "Capture fixtures do not apply updates." } };
152
+ }
153
+
154
+ async setGjcBundleEnabled(
155
+ _ctx: GjcLifecycleContext,
156
+ _identity: GjcBundleIdentity,
157
+ _enabled: boolean,
158
+ ): Promise<GjcLifecycleResult<GjcToggleResult>> {
159
+ return { ok: false, error: { code: "invalid_target", message: "Capture fixtures do not mutate bundles." } };
160
+ }
161
+
162
+ async setGjcBundleSurfaceEnabled(
163
+ _ctx: GjcLifecycleContext,
164
+ _identity: GjcBundleIdentity,
165
+ _surfaceId: string,
166
+ _enabled: boolean,
167
+ ): Promise<GjcLifecycleResult<GjcToggleResult>> {
168
+ return { ok: false, error: { code: "invalid_target", message: "Capture fixtures do not mutate surfaces." } };
169
+ }
170
+ }
171
+
172
+ function fixtureFor(stateId: string): GjcBundleSettingsFixture {
173
+ const state = GJC_BUNDLE_SETTINGS_STATES.find(candidate => candidate.id === stateId);
174
+ if (!state) throw new Error(`Unknown GJC Bundle settings state: ${stateId}`);
175
+ return state.fixture;
176
+ }
177
+
178
+ function viewportFor(viewportId: string): { id: string; cols: number; rows: number } {
179
+ const viewport = GJC_BUNDLE_SETTINGS_VIEWPORTS.find(candidate => candidate.id === viewportId);
180
+ if (viewport) return viewport;
181
+ if (viewportId === "48x36") return { id: "48x36", cols: 48, rows: 36 };
182
+ throw new Error(`Unknown GJC Bundle settings viewport: ${viewportId}`);
183
+ }
184
+
185
+ async function settle(): Promise<void> {
186
+ for (let index = 0; index < 8; index += 1) await Promise.resolve();
187
+ }
188
+
189
+ export async function renderGjcBundleSettingsEntry(
190
+ entry: FixtureEntry,
191
+ ): Promise<{ terminalText: string; terminalAnsiText: string; viewport: { id: string; cols: number; rows: number } }> {
192
+ const fixture = fixtureFor(entry.stateId);
193
+ const viewport = viewportFor(entry.viewportId);
194
+ const runtime: GjcRuntimeSnapshotProvider = { current: () => fixture.runtime };
195
+ await setTheme("red-claw");
196
+ const component = new GjcBundleSettingsComponent(
197
+ "/fixture/project",
198
+ { onClose: () => {} },
199
+ {
200
+ lifecycle: new FixtureLifecyclePort(fixture),
201
+ runtimeSnapshotProvider: runtime,
202
+ activationGeneration: 7,
203
+ },
204
+ );
205
+ await settle();
206
+ const rendered = component.render(viewport.cols).join("\n");
207
+ component.dispose();
208
+ const terminalAnsiText = entry.renderMode === "ascii-no-color" ? asciiText(Bun.stripANSI(rendered)) : rendered;
209
+ const terminalText = Bun.stripANSI(terminalAnsiText);
210
+ return { terminalText, terminalAnsiText, viewport };
211
+ }
212
+
213
+ export function gjcBundleSettingsCapturePlan(
214
+ entries: readonly FixtureEntry[] = GJC_BUNDLE_SETTINGS_ENTRIES,
215
+ ): CapturePlanItem[] {
216
+ return entries.flatMap(entry =>
217
+ GJC_BUNDLE_SETTINGS_CAPTURE_FILES.map(fileName => ({ entryId: entry.entryId, fileName })),
218
+ );
219
+ }
220
+
221
+ function assertSafeContent(label: string, content: string): void {
222
+ if (FORBIDDEN_CAPTURE_CONTENT.test(content)) throw new Error(`Unsafe locator content in ${label}`);
223
+ }
224
+
225
+ async function artifactContents(entry: FixtureEntry): Promise<Record<CaptureFileName, string>> {
226
+ const rendered = await renderGjcBundleSettingsEntry(entry);
227
+ const terminalHtml = ansiToHtml(rendered.terminalAnsiText);
228
+ const metadata: CaptureMetadata = {
229
+ entryId: entry.entryId,
230
+ stateId: entry.stateId,
231
+ viewport: rendered.viewport,
232
+ variant: { renderMode: entry.renderMode },
233
+ sha256: {
234
+ "terminal.txt": sha256(rendered.terminalText),
235
+ "terminal-ansi.txt": sha256(rendered.terminalAnsiText),
236
+ "terminal.html": sha256(terminalHtml),
237
+ },
238
+ };
239
+ return {
240
+ "terminal.txt": rendered.terminalText,
241
+ "terminal-ansi.txt": rendered.terminalAnsiText,
242
+ "terminal.html": terminalHtml,
243
+ "metadata.json": json(metadata),
244
+ };
245
+ }
246
+
247
+ async function writeEntry(entry: FixtureEntry, outputRoot: string): Promise<void> {
248
+ const artifacts = await artifactContents(entry);
249
+ // The HTML artifact is a pure rendering of the ANSI text, so proving the two
250
+ // text surfaces and the metadata are clean proves the whole entry is clean.
251
+ for (const name of ["terminal.txt", "terminal-ansi.txt", "metadata.json"] as const) {
252
+ assertSafeContent(`${entry.entryId}/${name}`, artifacts[name]);
253
+ }
254
+ const directory = path.join(outputRoot, entry.entryId);
255
+ await fs.mkdir(directory, { recursive: true });
256
+ await Promise.all(
257
+ GJC_BUNDLE_SETTINGS_CAPTURE_FILES.map(fileName => Bun.write(path.join(directory, fileName), artifacts[fileName])),
258
+ );
259
+ }
260
+
261
+ export async function verifyGjcBundleSettingsCapture(outputRoot: string): Promise<void> {
262
+ for (const entry of GJC_BUNDLE_SETTINGS_ENTRIES) {
263
+ const directory = path.join(outputRoot, entry.entryId);
264
+ const names = (await fs.readdir(directory)).sort();
265
+ if (
266
+ names.length !== GJC_BUNDLE_SETTINGS_CAPTURE_FILES.length ||
267
+ names.some((name, index) => name !== GJC_BUNDLE_SETTINGS_CAPTURE_FILES.slice().sort()[index])
268
+ ) {
269
+ throw new Error(`Expected exactly four capture files for ${entry.entryId}`);
270
+ }
271
+ const artifacts = await artifactContents(entry);
272
+ for (const fileName of GJC_BUNDLE_SETTINGS_CAPTURE_FILES) {
273
+ const content = await Bun.file(path.join(directory, fileName)).text();
274
+ assertSafeContent(`${entry.entryId}/${fileName}`, content);
275
+ if (content !== artifacts[fileName])
276
+ throw new Error(`Capture does not match deterministic fixture render for ${entry.entryId}/${fileName}`);
277
+ }
278
+ const metadata = JSON.parse(artifacts["metadata.json"]) as CaptureMetadata;
279
+ for (const fileName of ["terminal.txt", "terminal-ansi.txt", "terminal.html"] as const) {
280
+ if (metadata.sha256[fileName] !== sha256(artifacts[fileName]))
281
+ throw new Error(`SHA-256 mismatch for ${entry.entryId}/${fileName}`);
282
+ }
283
+ }
284
+ }
285
+
286
+ function parseArgs(args: string[]): { mode: "capture" | "verify"; outputRoot: string } {
287
+ if (args.length === 2 && args[0] === "--output" && args[1]) return { mode: "capture", outputRoot: args[1] };
288
+ if (args.length === 2 && args[0] === "--verify" && args[1]) return { mode: "verify", outputRoot: args[1] };
289
+ throw new Error("Usage: bun scripts/capture-gjc-bundle-settings.ts --output <directory> | --verify <directory>");
290
+ }
291
+
292
+ async function main(): Promise<void> {
293
+ const { mode, outputRoot } = parseArgs(process.argv.slice(2));
294
+ const resolvedOutputRoot = path.resolve(outputRoot);
295
+ if (mode === "verify") {
296
+ await verifyGjcBundleSettingsCapture(resolvedOutputRoot);
297
+ process.stdout.write(
298
+ `Verified ${GJC_BUNDLE_SETTINGS_ENTRIES.length} deterministic GJC Bundle settings entries.\n`,
299
+ );
300
+ return;
301
+ }
302
+ for (const entry of GJC_BUNDLE_SETTINGS_ENTRIES) await writeEntry(entry, resolvedOutputRoot);
303
+ process.stdout.write(`Captured ${GJC_BUNDLE_SETTINGS_ENTRIES.length} deterministic GJC Bundle settings entries.\n`);
304
+ }
305
+
306
+ if (import.meta.main) await main();
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Product-surface dogfood for #2902 ralplan typed review conflicts.
3
+ *
4
+ * Uses the **compiled** `packages/coding-agent/dist/gjc` binary (not source
5
+ * `cli.ts`) so evidence matches the owner exact-head compiled-binary gate:
6
+ * 1) open conflicts fail closed (exit 2, Join blocked)
7
+ * 2) complete disposition document is accepted and persisted
8
+ * 3) stored artifact is dispositioned under ralplan.review_conflicts.v1
9
+ * 4) spoofed receipts fail closed against the run index
10
+ *
11
+ * Prerequisites (from monorepo root, same HEAD):
12
+ * bun run build
13
+ * bun packages/coding-agent/scripts/dogfood-ralplan-review-conflicts.ts
14
+ *
15
+ * Optional: GJC_BINARY=/path/to/gjc overrides the default dist path.
16
+ */
17
+ import * as fsp from "node:fs/promises";
18
+ import * as os from "node:os";
19
+ import * as path from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
23
+ const defaultBinary = path.join(repoRoot, "packages/coding-agent/dist/gjc");
24
+
25
+ async function resolveCompiledBinary(): Promise<string> {
26
+ const binary = process.env.GJC_BINARY?.trim() || defaultBinary;
27
+ try {
28
+ const st = await fsp.stat(binary);
29
+ if (!st.isFile()) throw new Error(`not a file: ${binary}`);
30
+ // Ensure executable bit is present for direct spawn.
31
+ await fsp.access(binary, fsp.constants.X_OK).catch(async () => {
32
+ await fsp.chmod(binary, 0o755);
33
+ });
34
+ } catch (error) {
35
+ throw new Error(
36
+ `Compiled binary missing or not executable: ${binary}. Run \`bun run build\` on this exact HEAD first. (${error instanceof Error ? error.message : String(error)})`,
37
+ );
38
+ }
39
+ return binary;
40
+ }
41
+
42
+ async function runGjc(
43
+ binary: string,
44
+ cwd: string,
45
+ args: string[],
46
+ env: NodeJS.ProcessEnv,
47
+ ): Promise<{ code: number; stdout: string; stderr: string }> {
48
+ const proc = Bun.spawn([binary, ...args], { cwd, env, stdout: "pipe", stderr: "pipe" });
49
+ const [code, stdout, stderr] = await Promise.all([
50
+ proc.exited,
51
+ new Response(proc.stdout).text(),
52
+ new Response(proc.stderr).text(),
53
+ ]);
54
+ return { code, stdout, stderr };
55
+ }
56
+
57
+ async function main(): Promise<void> {
58
+ const binary = await resolveCompiledBinary();
59
+ const dogfoodRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "gjc-dogfood-2902-"));
60
+ const sessionId = `dogfood-2902-${process.pid}`;
61
+ const env = { ...process.env, GJC_SESSION_ID: sessionId };
62
+ const full = Bun.spawnSync(["git", "-C", repoRoot, "rev-parse", "HEAD"]).stdout.toString().trim();
63
+ const short = Bun.spawnSync(["git", "-C", repoRoot, "rev-parse", "--short=8", "HEAD"]).stdout.toString().trim();
64
+ const binaryStat = await fsp.stat(binary);
65
+
66
+ console.log("# Dogfood: ralplan review conflicts (#2902) — compiled binary");
67
+ console.log(`root=${dogfoodRoot}`);
68
+ console.log(`session=${sessionId}`);
69
+ console.log(`binary=${binary}`);
70
+ console.log(`binary_size=${binaryStat.size}`);
71
+ console.log(`binary_mtime=${binaryStat.mtime.toISOString()}`);
72
+ console.log(`commit=${short}`);
73
+ console.log(`commit_full=${full}`);
74
+ console.log(`bun=${Bun.version}`);
75
+ console.log();
76
+
77
+ // Seed ralplan run state so --write has an active run.
78
+ console.log("## 1) compiled gjc ralplan seed");
79
+ const seed = await runGjc(binary, dogfoodRoot, ["ralplan", "--deliberate", "--json", "dogfood #2902"], env);
80
+ console.log(`exit=${seed.code}`);
81
+ console.log((seed.stdout || seed.stderr).trim());
82
+ if (seed.code !== 0) process.exit(1);
83
+
84
+ // Persist same-pass Architect/Critic artifacts so disposition receipts resolve.
85
+ console.log();
86
+ console.log("## 2) seed architect + critic stage artifacts (same-pass stage_n=1)");
87
+ const architect = await runGjc(
88
+ binary,
89
+ dogfoodRoot,
90
+ ["ralplan", "--write", "--stage", "architect", "--stage_n", "1", "--artifact", "# architect", "--json"],
91
+ env,
92
+ );
93
+ console.log(`architect exit=${architect.code}`);
94
+ if (architect.code !== 0) {
95
+ console.error(architect.stderr || architect.stdout);
96
+ process.exit(1);
97
+ }
98
+ const critic = await runGjc(
99
+ binary,
100
+ dogfoodRoot,
101
+ ["ralplan", "--write", "--stage", "critic", "--stage_n", "1", "--artifact", "# critic", "--json"],
102
+ env,
103
+ );
104
+ console.log(`critic exit=${critic.code}`);
105
+ if (critic.code !== 0) {
106
+ console.error(critic.stderr || critic.stdout);
107
+ process.exit(1);
108
+ }
109
+ const archReceipt = JSON.parse(architect.stdout) as { path: string; sha256: string };
110
+ const critReceipt = JSON.parse(critic.stdout) as { path: string; sha256: string };
111
+ console.log(`architect path=${archReceipt.path}`);
112
+ console.log(`critic path=${critReceipt.path}`);
113
+
114
+ const findings = [
115
+ {
116
+ findingId: "arch-1",
117
+ targetId: "contract.field",
118
+ action: "remove",
119
+ severity: "block",
120
+ evidence: "redundant with session identity",
121
+ sourceRole: "architect",
122
+ sourceReceipt: {
123
+ stage: "architect",
124
+ stageN: 1,
125
+ path: archReceipt.path,
126
+ sha256: archReceipt.sha256,
127
+ },
128
+ },
129
+ {
130
+ findingId: "crit-1",
131
+ targetId: "contract.field",
132
+ action: "add",
133
+ severity: "watch",
134
+ evidence: "needed for multi-repo binding",
135
+ sourceRole: "critic",
136
+ sourceReceipt: {
137
+ stage: "critic",
138
+ stageN: 1,
139
+ path: critReceipt.path,
140
+ sha256: critReceipt.sha256,
141
+ },
142
+ },
143
+ ];
144
+
145
+ // 3) Open disposition must fail closed.
146
+ const openPath = path.join(dogfoodRoot, "open-disposition.json");
147
+ await fsp.writeFile(
148
+ openPath,
149
+ JSON.stringify({
150
+ schema: "ralplan.review_conflicts.v1",
151
+ plannerStageN: 1,
152
+ findings,
153
+ dispositions: [],
154
+ }),
155
+ );
156
+ console.log();
157
+ console.log("## 3) disposition stage with open conflicts (expect fail-closed)");
158
+ const open = await runGjc(
159
+ binary,
160
+ dogfoodRoot,
161
+ ["ralplan", "--write", "--stage", "disposition", "--stage_n", "1", "--artifact", openPath],
162
+ env,
163
+ );
164
+ console.log(`exit=${open.code}`);
165
+ console.log((open.stderr || open.stdout).trim());
166
+ if (open.code !== 2 || !`${open.stderr}${open.stdout}`.includes("Join blocked")) {
167
+ console.error("FAIL: expected exit=2 and Join blocked for open conflicts");
168
+ process.exit(1);
169
+ }
170
+ console.log("fail_closed: ok");
171
+
172
+ // 4) Spoofed receipt must fail closed.
173
+ const spoofPath = path.join(dogfoodRoot, "spoof-disposition.json");
174
+ await fsp.writeFile(
175
+ spoofPath,
176
+ JSON.stringify({
177
+ schema: "ralplan.review_conflicts.v1",
178
+ plannerStageN: 1,
179
+ findings: [
180
+ {
181
+ ...findings[0],
182
+ sourceReceipt: {
183
+ stage: "architect",
184
+ stageN: 1,
185
+ path: "/tmp/spoofed.md",
186
+ sha256: "deadbeef",
187
+ },
188
+ },
189
+ findings[1],
190
+ ],
191
+ dispositions: [
192
+ {
193
+ conflictId: "conflict:contract.field:arch-1:crit-1",
194
+ choice: "accept_architect",
195
+ rationale: "spoof should not land",
196
+ decisionOwner: "ralplan-leader",
197
+ affectedSections: ["## Contracts"],
198
+ },
199
+ ],
200
+ }),
201
+ );
202
+ console.log();
203
+ console.log("## 4) disposition with spoofed receipt (expect fail-closed)");
204
+ const spoof = await runGjc(
205
+ binary,
206
+ dogfoodRoot,
207
+ ["ralplan", "--write", "--stage", "disposition", "--stage_n", "1", "--artifact", spoofPath],
208
+ env,
209
+ );
210
+ console.log(`exit=${spoof.code}`);
211
+ console.log((spoof.stderr || spoof.stdout).trim());
212
+ if (spoof.code !== 2 || !`${spoof.stderr}${spoof.stdout}`.includes("does not match indexed")) {
213
+ console.error("FAIL: expected exit=2 for spoofed receipt");
214
+ process.exit(1);
215
+ }
216
+ console.log("spoof_rejected: ok");
217
+
218
+ // 5) Complete disposition must accept and persist.
219
+ const closedPath = path.join(dogfoodRoot, "closed-disposition.json");
220
+ await fsp.writeFile(
221
+ closedPath,
222
+ JSON.stringify({
223
+ schema: "ralplan.review_conflicts.v1",
224
+ plannerStageN: 1,
225
+ findings,
226
+ dispositions: [
227
+ {
228
+ conflictId: "conflict:contract.field:arch-1:crit-1",
229
+ choice: "accept_architect",
230
+ rationale: "Field duplicates existing session identity.",
231
+ decisionOwner: "ralplan-leader",
232
+ affectedSections: ["## Contracts"],
233
+ },
234
+ ],
235
+ }),
236
+ );
237
+ console.log();
238
+ console.log("## 5) disposition stage with complete dispositions (expect accept)");
239
+ const closed = await runGjc(
240
+ binary,
241
+ dogfoodRoot,
242
+ ["ralplan", "--write", "--stage", "disposition", "--stage_n", "1", "--artifact", closedPath, "--json"],
243
+ env,
244
+ );
245
+ console.log(`exit=${closed.code}`);
246
+ console.log(closed.stdout.trim() || closed.stderr.trim());
247
+ if (closed.code !== 0) {
248
+ console.error("FAIL: expected disposition write success");
249
+ process.exit(1);
250
+ }
251
+ const payload = JSON.parse(closed.stdout) as { path?: string; stage?: string };
252
+ if (payload.stage !== "disposition" || !payload.path) {
253
+ console.error("FAIL: unexpected write payload", payload);
254
+ process.exit(1);
255
+ }
256
+ const body = await fsp.readFile(payload.path, "utf-8");
257
+ console.log();
258
+ console.log("## 6) persisted disposition artifact");
259
+ console.log(`path=${payload.path}`);
260
+ console.log(body.slice(0, 1200));
261
+ if (!body.includes("ralplan.review_conflicts.v1") || !body.includes("dispositioned")) {
262
+ console.error("FAIL: artifact missing schema or dispositioned status");
263
+ process.exit(1);
264
+ }
265
+
266
+ console.log();
267
+ console.log("DOGFOOD_OK");
268
+ console.log(`DOGFOOD_BINARY=${binary}`);
269
+ console.log(`DOGFOOD_HEAD=${full}`);
270
+ }
271
+
272
+ await main();
@@ -35,6 +35,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
35
35
  "slash_command:transcript": "visual/local-only transcript viewer, not a user-facing SDK control seam",
36
36
  "slash_command:sessions": "visual/local-only sessions dashboard, not a user-facing SDK control seam",
37
37
  "agent_session:constructor": "internal accessor/plumbing, not a user-facing control seam",
38
+ "agent_session:registerToolSessionCleanup":
39
+ "internal tool lifecycle cleanup registration, not a user-facing SDK control seam",
38
40
  "agent_session:nextToolChoice": "internal accessor/plumbing, not a user-facing control seam",
39
41
  "agent_session:setForcedToolChoice": "internal accessor/plumbing, not a user-facing control seam",
40
42
  "agent_session:getActiveSkillState": "internal accessor/plumbing, not a user-facing control seam",
@@ -125,6 +127,7 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
125
127
  "agent_session:getActiveModelProfile": "internal accessor/plumbing, not a user-facing control seam",
126
128
  "agent_session:getSessionDefaultModelSelector": "internal accessor/plumbing, not a user-facing control seam",
127
129
  "agent_session:recordResumeDefaultModel": "internal accessor/plumbing, not a user-facing control seam",
130
+ "agent_session:resolveConfiguredDefaultModel": "internal accessor/plumbing, not a user-facing control seam",
128
131
  "agent_session:setModelTemporary": "internal accessor/plumbing, not a user-facing control seam",
129
132
  "agent_session:setModelTemporaryForControl":
130
133
  "internal Telegram control wrapper over the reviewed model.set seam, not an independent public SDK operation",
@@ -150,6 +153,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
150
153
  "agent_session:runIdleCompaction": "internal accessor/plumbing, not a user-facing control seam",
151
154
  "agent_session:abortBranchSummary": "internal accessor/plumbing, not a user-facing control seam",
152
155
  "agent_session:abortHandoff": "internal accessor/plumbing, not a user-facing control seam",
156
+ "agent_session:abortPromptAndWait":
157
+ "internal SDK prompt-terminalization resource fence over a host-captured run handle, not an independent public SDK control seam",
153
158
  "agent_session:prepareContributionPrep": "internal accessor/plumbing, not a user-facing control seam",
154
159
  "agent_session:setResourceSampler": "internal accessor/plumbing, not a user-facing control seam",
155
160
  "agent_session:setRetainedMemorySampler": "internal accessor/plumbing, not a user-facing control seam",