@gajae-code/coding-agent 0.15.5 → 0.16.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 (272) hide show
  1. package/CHANGELOG.md +85 -1
  2. package/dist/types/async/job-manager.d.ts +83 -0
  3. package/dist/types/cli/args.d.ts +2 -0
  4. package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
  5. package/dist/types/cli/model-presets-cli.d.ts +9 -0
  6. package/dist/types/cli/root-flags.d.ts +14 -0
  7. package/dist/types/cli/update-cli.d.ts +15 -0
  8. package/dist/types/cli.d.ts +7 -0
  9. package/dist/types/commands/auth-gateway.d.ts +3 -0
  10. package/dist/types/commands/gc.d.ts +13 -0
  11. package/dist/types/commands/launch.d.ts +10 -0
  12. package/dist/types/commands/model-presets.d.ts +21 -0
  13. package/dist/types/config/autorouting-tier-map.d.ts +120 -0
  14. package/dist/types/config/file-lock.d.ts +54 -2
  15. package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
  16. package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
  17. package/dist/types/config/keybindings.d.ts +3 -2
  18. package/dist/types/config/model-preset-registry.d.ts +368 -0
  19. package/dist/types/config/model-profile-activation.d.ts +39 -0
  20. package/dist/types/config/model-profiles.d.ts +5 -3
  21. package/dist/types/config/model-registry.d.ts +17 -5
  22. package/dist/types/config/settings-schema.d.ts +19 -1
  23. package/dist/types/config/settings.d.ts +2 -0
  24. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
  25. package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
  26. package/dist/types/crash/record-loader.d.ts +15 -0
  27. package/dist/types/extensibility/extensions/types.d.ts +2 -5
  28. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
  29. package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
  30. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  31. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  32. package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
  33. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
  34. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  35. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/main.d.ts +3 -1
  38. package/dist/types/master-mode/context.d.ts +21 -0
  39. package/dist/types/master-mode/first-request.d.ts +34 -0
  40. package/dist/types/master-mode/snapshot.d.ts +32 -0
  41. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  42. package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
  43. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  44. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  45. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  46. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  47. package/dist/types/modes/jobs-observer.d.ts +36 -0
  48. package/dist/types/modes/print-mode.d.ts +17 -0
  49. package/dist/types/modes/ui-language.d.ts +7 -1
  50. package/dist/types/sdk/broker/broker.d.ts +80 -1
  51. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  52. package/dist/types/sdk/broker/index.d.ts +3 -0
  53. package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
  54. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  55. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  56. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  57. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  58. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  59. package/dist/types/sdk/broker/state-version.d.ts +4 -1
  60. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  61. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  62. package/dist/types/sdk/bus/index.d.ts +6 -0
  63. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
  64. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
  65. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  66. package/dist/types/sdk/cli/index.d.ts +1 -0
  67. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  68. package/dist/types/sdk/cli/rows.d.ts +4 -3
  69. package/dist/types/sdk/cli/session-cli.d.ts +18 -2
  70. package/dist/types/sdk/client/client.d.ts +3 -2
  71. package/dist/types/sdk/client/discovery.d.ts +1 -0
  72. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  73. package/dist/types/sdk/host/control/index.d.ts +1 -1
  74. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  75. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  76. package/dist/types/sdk/host/host.d.ts +10 -0
  77. package/dist/types/sdk/host/model-pin.d.ts +7 -2
  78. package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
  79. package/dist/types/sdk/host/session-runtime.d.ts +67 -1
  80. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  81. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  82. package/dist/types/sdk/lifecycle-session.d.ts +2 -0
  83. package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
  84. package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
  85. package/dist/types/sdk/router/session-router.d.ts +11 -0
  86. package/dist/types/sdk/session.d.ts +16 -0
  87. package/dist/types/sdk/startup-capability.d.ts +2 -1
  88. package/dist/types/session/account-inventory.d.ts +1 -0
  89. package/dist/types/session/agent-session.d.ts +24 -7
  90. package/dist/types/session/credential-store-errors.d.ts +1 -0
  91. package/dist/types/session/fold-coordinator.d.ts +201 -0
  92. package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
  93. package/dist/types/session/sdk-run-capability.d.ts +2 -0
  94. package/dist/types/session/session-manager.d.ts +65 -0
  95. package/dist/types/session/yield-queue.d.ts +5 -4
  96. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  97. package/dist/types/telemetry/control.d.ts +11 -0
  98. package/dist/types/telemetry/events.d.ts +20 -0
  99. package/dist/types/telemetry/index.d.ts +3 -0
  100. package/dist/types/telemetry/transport.d.ts +22 -0
  101. package/dist/types/tools/bash-interactive.d.ts +23 -0
  102. package/dist/types/tools/bash.d.ts +4 -0
  103. package/dist/types/tools/gh.d.ts +8 -0
  104. package/dist/types/tools/index.d.ts +10 -5
  105. package/dist/types/web/search/providers/base.d.ts +9 -4
  106. package/dist/types/web/search/providers/xai.d.ts +2 -1
  107. package/dist/types/web/search/types.d.ts +14 -0
  108. package/package.json +10 -7
  109. package/scripts/generate-hotkeys-docs.ts +4 -0
  110. package/scripts/generate-sdk-operation-inventory.ts +9 -3
  111. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  112. package/src/async/job-manager.ts +671 -39
  113. package/src/cli/accounts-cli.ts +9 -5
  114. package/src/cli/args.ts +17 -0
  115. package/src/cli/auth-broker-cli.ts +40 -12
  116. package/src/cli/auth-gateway-cli.ts +273 -97
  117. package/src/cli/config-cli.ts +2 -2
  118. package/src/cli/fast-help.ts +4 -0
  119. package/src/cli/model-presets-cli.ts +100 -0
  120. package/src/cli/root-flags.ts +5 -0
  121. package/src/cli/update-cli.ts +117 -16
  122. package/src/cli.ts +6 -2
  123. package/src/commands/auth-gateway.ts +12 -8
  124. package/src/commands/gc.ts +19 -0
  125. package/src/commands/launch.ts +86 -18
  126. package/src/commands/model-presets.ts +24 -0
  127. package/src/commands/sdk.ts +113 -21
  128. package/src/config/autorouting-tier-map.ts +44 -0
  129. package/src/config/file-lock-gc.ts +9 -4
  130. package/src/config/file-lock.ts +890 -99
  131. package/src/config/internal/model-preset-registry-test-state.ts +28 -0
  132. package/src/config/internal/model-preset-registry-test-support.ts +11 -0
  133. package/src/config/keybindings.ts +6 -2
  134. package/src/config/model-preset-registry.ts +2795 -0
  135. package/src/config/model-profile-activation.ts +115 -28
  136. package/src/config/model-profile-contract.ts +4 -1
  137. package/src/config/model-profiles.ts +56 -14
  138. package/src/config/model-registry.ts +1044 -122
  139. package/src/config/model-resolver.ts +5 -0
  140. package/src/config/settings-schema.ts +19 -1
  141. package/src/config/settings.ts +11 -1
  142. package/src/coordinator-mcp/model-preset.ts +32 -17
  143. package/src/coordinator-mcp/projection-scan.ts +592 -0
  144. package/src/coordinator-mcp/question-state.ts +9 -11
  145. package/src/coordinator-mcp/server.ts +111 -42
  146. package/src/coordinator-mcp/session-reaper.ts +9 -3
  147. package/src/crash/index-store.ts +2 -0
  148. package/src/crash/record-loader.ts +10 -0
  149. package/src/extensibility/extensions/loader.ts +0 -1
  150. package/src/extensibility/extensions/types.ts +2 -3
  151. package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
  152. package/src/gjc-runtime/empty-delete-gc.ts +301 -0
  153. package/src/gjc-runtime/gc-render.ts +29 -0
  154. package/src/gjc-runtime/gc-runtime.ts +174 -11
  155. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  156. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  157. package/src/gjc-runtime/session-state-lock.ts +1321 -147
  158. package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
  159. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  160. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
  161. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  162. package/src/hooks/skill-state.ts +6 -0
  163. package/src/index.ts +1 -0
  164. package/src/internal-urls/docs-index.generated.ts +14 -13
  165. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  166. package/src/lsp/config.ts +4 -3
  167. package/src/lsp/path-trust.ts +12 -6
  168. package/src/main.ts +66 -4
  169. package/src/master-mode/context.ts +50 -0
  170. package/src/master-mode/first-request.ts +65 -0
  171. package/src/master-mode/snapshot.ts +205 -0
  172. package/src/modes/acp/acp-agent.ts +616 -161
  173. package/src/modes/components/footer.ts +4 -2
  174. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  175. package/src/modes/components/jobs-overlay-model.ts +99 -16
  176. package/src/modes/components/model-selector.ts +158 -18
  177. package/src/modes/components/oauth-selector.ts +6 -1
  178. package/src/modes/components/settings-selector.ts +1 -0
  179. package/src/modes/components/status-line/model-name.ts +53 -0
  180. package/src/modes/components/status-line/priority-row.ts +107 -0
  181. package/src/modes/components/status-line/segments.ts +29 -10
  182. package/src/modes/components/tool-status-header.ts +151 -15
  183. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  184. package/src/modes/controllers/input-controller.ts +51 -6
  185. package/src/modes/controllers/selector-controller.ts +191 -96
  186. package/src/modes/interactive-mode.ts +14 -1
  187. package/src/modes/jobs-observer.ts +149 -16
  188. package/src/modes/print-mode.ts +64 -0
  189. package/src/modes/runtime-init.ts +1 -1
  190. package/src/modes/ui-language.ts +49 -2
  191. package/src/modes/utils/injected-user-submission.ts +15 -1
  192. package/src/prompts/system/master-mode.md +28 -0
  193. package/src/prompts/tools/bash.md +2 -2
  194. package/src/prompts/tools/fold-receipt.md +3 -0
  195. package/src/prompts/tools/job.md +1 -1
  196. package/src/sdk/acp/adapter.ts +17 -2
  197. package/src/sdk/broker/broker.ts +1671 -34
  198. package/src/sdk/broker/endpoint-authority.ts +86 -0
  199. package/src/sdk/broker/ensure.ts +5 -0
  200. package/src/sdk/broker/index.ts +3 -0
  201. package/src/sdk/broker/lifecycle.ts +217 -46
  202. package/src/sdk/broker/master-capability.ts +232 -0
  203. package/src/sdk/broker/session-index.ts +121 -17
  204. package/src/sdk/broker/session-scope.ts +272 -0
  205. package/src/sdk/broker/spawn-authority.ts +1031 -0
  206. package/src/sdk/broker/spawn-substrate.ts +548 -0
  207. package/src/sdk/broker/state-version.ts +26 -14
  208. package/src/sdk/broker/transport.ts +1 -0
  209. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  210. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  211. package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
  212. package/src/sdk/bus/index.ts +51 -4
  213. package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
  214. package/src/sdk/bus/prompt-reconciliation.ts +28 -4
  215. package/src/sdk/bus/slack-conversation.ts +5 -1
  216. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  217. package/src/sdk/cli/index.ts +1 -0
  218. package/src/sdk/cli/master-cli.ts +259 -0
  219. package/src/sdk/cli/rows.ts +11 -8
  220. package/src/sdk/cli/session-cli.ts +276 -6
  221. package/src/sdk/client/client.ts +14 -8
  222. package/src/sdk/client/discovery.ts +3 -1
  223. package/src/sdk/host/control/dispatch.ts +101 -37
  224. package/src/sdk/host/control/index.ts +2 -0
  225. package/src/sdk/host/control/operations.ts +2 -0
  226. package/src/sdk/host/control/runtime-gate.ts +203 -11
  227. package/src/sdk/host/host.ts +27 -2
  228. package/src/sdk/host/model-pin.ts +185 -25
  229. package/src/sdk/host/sdk-run-capability.ts +1 -0
  230. package/src/sdk/host/session-runtime.test.ts +581 -43
  231. package/src/sdk/host/session-runtime.ts +655 -101
  232. package/src/sdk/lifecycle/broker-client.ts +28 -0
  233. package/src/sdk/lifecycle/service.ts +279 -20
  234. package/src/sdk/lifecycle-session.ts +10 -2
  235. package/src/sdk/prompt-deadline-manager.ts +16 -1
  236. package/src/sdk/protocol/adapter-validation.ts +27 -0
  237. package/src/sdk/protocol/operation-inventory.generated.json +53 -2
  238. package/src/sdk/protocol/operation-registry.ts +17 -4
  239. package/src/sdk/router/session-router.ts +294 -154
  240. package/src/sdk/session.ts +440 -167
  241. package/src/sdk/startup-capability.ts +1 -0
  242. package/src/session/account-inventory.ts +27 -11
  243. package/src/session/agent-session.ts +1292 -180
  244. package/src/session/blob-store.ts +15 -5
  245. package/src/session/credential-store-errors.ts +2 -0
  246. package/src/session/fold-coordinator.ts +433 -0
  247. package/src/session/internal/managed-session-storage.ts +153 -78
  248. package/src/session/sdk-run-capability-internal.ts +15 -0
  249. package/src/session/sdk-run-capability.ts +2 -0
  250. package/src/session/session-manager.ts +342 -28
  251. package/src/session/yield-queue.ts +24 -10
  252. package/src/setup/frictionless-onboarding.ts +2 -0
  253. package/src/setup/provider-presets.json +1 -1
  254. package/src/slash-commands/helpers/credential-switch.ts +6 -1
  255. package/src/task/executor.ts +19 -3
  256. package/src/telemetry/control.ts +26 -0
  257. package/src/telemetry/events.ts +1268 -0
  258. package/src/telemetry/index.ts +3 -0
  259. package/src/telemetry/transport.ts +87 -0
  260. package/src/tools/bash-interactive.ts +182 -85
  261. package/src/tools/bash.ts +1028 -109
  262. package/src/tools/gh.ts +104 -48
  263. package/src/tools/github-cache.ts +2 -2
  264. package/src/tools/index.ts +11 -5
  265. package/src/tools/tool-catalog.generated.ts +1 -1
  266. package/src/web/search/provider.ts +120 -11
  267. package/src/web/search/providers/anthropic.ts +89 -17
  268. package/src/web/search/providers/base.ts +9 -4
  269. package/src/web/search/providers/gemini.ts +87 -15
  270. package/src/web/search/providers/openai-compatible.ts +23 -8
  271. package/src/web/search/providers/xai.ts +98 -12
  272. package/src/web/search/types.ts +16 -0
@@ -201,12 +201,9 @@ export async function runTmuxOwnerIsolationCliFromStdin(): Promise<void> {
201
201
  const stdin = await readOneBoundedJsonLine();
202
202
  const output = stdin === null ? cliFailure("invalid_json_line") : await runTmuxOwnerIsolationCli(stdin);
203
203
  process.stdout.write(`${output}\n`);
204
- const request = stdin === null ? null : parseOwnerIsolationRequest(stdin);
205
- if (request?.op === "bootstrap") {
206
- try {
207
- if (JSON.parse(output).ok !== true) process.exitCode = 1;
208
- } catch {
209
- process.exitCode = 1;
210
- }
204
+ try {
205
+ if (JSON.parse(output).ok === false) process.exitCode = 1;
206
+ } catch {
207
+ process.exitCode = 1;
211
208
  }
212
209
  }
@@ -249,7 +249,7 @@ export type TmuxOwnerIsolationExecutionResult = TmuxOwnerIsolationExecutionSucce
249
249
  /** Synchronous spawn boundary; callers pass argv and scoped stdin unchanged. */
250
250
  export interface TmuxOwnerIsolationExecutionDependencies {
251
251
  socketKey: string;
252
- spawn(argv: string[], stdinLine?: string): { exitCode: number | null; stdout?: string };
252
+ spawn(argv: string[], stdinLine?: string): { exitCode: number | null; stdout?: string; stderr?: string };
253
253
  probeServer(socketKey: string): TmuxServerProof;
254
254
  /** Reads the published owner generation immediately around direct execution. */
255
255
  isCurrentGeneration?(): boolean;
@@ -493,7 +493,7 @@ export function executeTmuxOwnerIsolationPlanSync(
493
493
  return {
494
494
  ok: false,
495
495
  code: "scope_bootstrap_failed",
496
- diagnostic: "planned_spawn_failed",
496
+ diagnostic: safeDiagnostic(`planned_spawn_failed${result.stderr?.trim() ? `: ${result.stderr}` : ""}`),
497
497
  };
498
498
  if (execution.mode === "scoped" && !isExactScopedBootstrapSuccessReceipt(result.stdout ?? ""))
499
499
  return {
@@ -7,6 +7,13 @@ import type { Process } from "@gajae-code/natives";
7
7
  import { nativeProcessBindings } from "@gajae-code/utils/native-process";
8
8
  import { managedSecurityFailureClassification } from "../session/internal/managed-session-storage";
9
9
  import { readLinuxProcStartTime, readLinuxProcStartTimeSync } from "./linux-proc";
10
+ import {
11
+ MANAGED_OWNER_COMMAND_ENV,
12
+ MANAGED_OWNER_INCARNATION_ENV,
13
+ MANAGED_OWNER_REDACT_COMMAND_ENV,
14
+ MANAGED_OWNER_RUN_ID_ENV,
15
+ MANAGED_OWNER_SUPERVISOR_ARG,
16
+ } from "./managed-owner-supervisor";
10
17
  import { resolveGjcTmuxBinary } from "./psmux-detect";
11
18
  import { GJC_DIR, GJC_SESSION_PREFIX, tmuxRuntimeSessionPath } from "./session-layout";
12
19
  import {
@@ -70,6 +77,60 @@ import {
70
77
  } from "./tmux-provider-context";
71
78
  import { buildWindowsPowerShellInnerCommand } from "./windows-powershell-command";
72
79
 
80
+ export interface ManagedTmuxLaunchSpec {
81
+ childSessionId: string;
82
+ cwd: string;
83
+ argv: readonly string[];
84
+ env?: Readonly<Record<string, string>>;
85
+ }
86
+
87
+ export interface ManagedTmuxLaunchProof {
88
+ name: string;
89
+ nativeSessionId: string;
90
+ serverPid: number;
91
+ serverStartTime: string;
92
+ ownerGeneration: string;
93
+ sessionId: string;
94
+ sessionStateFile: string;
95
+ pid: number;
96
+ providerIdentity: string;
97
+ psmuxIncarnation?: string;
98
+ }
99
+
100
+ function providerIdentity(
101
+ provider: Pick<ProviderAuthority, "kind" | "command" | "namespace" | "executableIdentity">,
102
+ ): string {
103
+ return JSON.stringify([provider.kind, provider.command, provider.namespace, provider.executableIdentity]);
104
+ }
105
+
106
+ function managedOwnerSupervisorArgv(): string[] {
107
+ const entry = process.argv[1];
108
+ if (!entry || !/\.(?:[cm]?[jt]s)$/.test(entry) || /\.test\.[^.]+$/.test(entry))
109
+ return [process.execPath, MANAGED_OWNER_SUPERVISOR_ARG];
110
+ return [process.execPath, entry, MANAGED_OWNER_SUPERVISOR_ARG];
111
+ }
112
+
113
+ function validManagedLaunchSpec(spec: ManagedTmuxLaunchSpec, platform: NodeJS.Platform): boolean {
114
+ const pathModule = platform === "win32" ? path.win32 : path;
115
+ return (
116
+ typeof spec.childSessionId === "string" &&
117
+ spec.childSessionId.length > 0 &&
118
+ !spec.childSessionId.includes("\0") &&
119
+ typeof spec.cwd === "string" &&
120
+ spec.cwd.length > 0 &&
121
+ !spec.cwd.includes("\0") &&
122
+ pathModule.isAbsolute(spec.cwd) &&
123
+ Array.isArray(spec.argv) &&
124
+ spec.argv.length > 0 &&
125
+ spec.argv.every(value => typeof value === "string" && value.length > 0 && !value.includes("\0")) &&
126
+ (spec.env === undefined ||
127
+ Object.entries(spec.env).every(
128
+ ([name, value]) =>
129
+ /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && typeof value === "string" && !value.includes("\0"),
130
+ ))
131
+ );
132
+ }
133
+
73
134
  export interface GjcTmuxSessionStatus {
74
135
  name: string;
75
136
  attached: boolean;
@@ -151,6 +212,8 @@ export interface ForceCloseOwnerDependencies {
151
212
  listPanePids(sessionName: string, env: NodeJS.ProcessEnv): number[];
152
213
  /** Test/runtime seam for an exact owner-exit observer; durable validation remains authoritative. */
153
214
  waitForOwnerExitVerdict?(): Promise<OwnerVerdict>;
215
+ /** Broker close pin; production callers use this to reject reused session identity. */
216
+ expectedManagedProof?: ManagedTmuxLaunchProof;
154
217
  }
155
218
  const GJC_TMUX_PSMUX_INCARNATION_OPTION = "@gjc-psmux-incarnation";
156
219
  const effectiveSessionEnvironments = new WeakMap<GjcTmuxSessionStatus, NodeJS.ProcessEnv>();
@@ -161,6 +224,7 @@ const FORCE_CLOSE_VERDICT_POLL_MS = 50;
161
224
 
162
225
  export interface CreateGjcTmuxSessionOptions {
163
226
  platform?: NodeJS.Platform;
227
+ launch?: ManagedTmuxLaunchSpec;
164
228
  }
165
229
 
166
230
  export type CreateOwnerIsolationTestDependencies = {
@@ -600,40 +664,66 @@ export function createGjcTmuxSession(
600
664
  env: NodeJS.ProcessEnv = process.env,
601
665
  options: CreateGjcTmuxSessionOptions = {},
602
666
  ): GjcTmuxSessionStatus {
667
+ const launch = options.launch;
603
668
  const platform = options.platform ?? process.platform;
669
+ if (launch && !validManagedLaunchSpec(launch, platform)) throw new Error("gjc_tmux_managed_launch_spec_invalid");
604
670
  const provider = resolveGjcTmuxProviderContext({ env, platform });
605
671
  const tmuxCommand = provider.command;
606
- const sessionName = buildGjcTmuxSessionName(env);
607
- const cwd = process.cwd();
608
- const sessionId = env[GJC_COORDINATOR_SESSION_ID_ENV]?.trim() || sessionName;
672
+ const launchEnvironment = launch ? { ...env, GJC_TMUX_SESSION: launch.childSessionId } : env;
673
+ const sessionName = buildGjcTmuxSessionName(launchEnvironment);
674
+ const cwd = launch?.cwd ?? process.cwd();
675
+ const sessionId = launch?.childSessionId ?? (env[GJC_COORDINATOR_SESSION_ID_ENV]?.trim() || sessionName);
609
676
  const stateFile =
610
- env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV]?.trim() ||
611
- tmuxRuntimeSessionPath(cwd, env.GJC_SESSION_ID?.trim() || sessionId, buildGjcTmuxSessionSlug(sessionName));
677
+ (!launch ? env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV]?.trim() : undefined) ||
678
+ tmuxRuntimeSessionPath(
679
+ cwd,
680
+ launch?.childSessionId ?? (env.GJC_SESSION_ID?.trim() || sessionId),
681
+ buildGjcTmuxSessionSlug(sessionName),
682
+ );
612
683
  const stateDir = (platform === "win32" ? path.win32 : path).dirname(stateFile);
613
684
  const generation = crypto.randomUUID();
614
685
  const authority = bindGjcTmuxProviderAuthority(provider, { stateDir, sessionId, generation });
615
- const childEnvironment: Record<string, string> = {
686
+ const managedEnvironment: Record<string, string> = {
616
687
  GJC_TMUX_LAUNCHED: "1",
617
688
  [GJC_TMUX_OWNER_GENERATION_ENV]: generation,
618
689
  [GJC_TMUX_OWNER_STATE_DIR_ENV]: stateDir,
619
690
  [GJC_TMUX_OWNER_SERVER_KEY_ENV]: tmuxCommand,
620
691
  [GJC_COORDINATOR_SESSION_ID_ENV]: sessionId,
621
692
  [GJC_COORDINATOR_SESSION_STATE_FILE_ENV]: stateFile,
693
+ ...(launch
694
+ ? {
695
+ [MANAGED_OWNER_RUN_ID_ENV]: crypto.randomUUID(),
696
+ [MANAGED_OWNER_INCARNATION_ENV]: crypto.randomUUID(),
697
+ [MANAGED_OWNER_REDACT_COMMAND_ENV]: "1",
698
+ }
699
+ : {}),
622
700
  };
623
- const executionEnv = { ...env, ...childEnvironment };
701
+ const childEnvironment: Record<string, string> = { ...(launch?.env ?? {}), ...managedEnvironment };
702
+ const executionEnv = { ...env, ...managedEnvironment };
624
703
  const shellQuote = (value: string): string => `'${value.replace(/'/g, `'\\''`)}'`;
625
704
  const command =
626
705
  platform === "win32"
627
- ? buildWindowsPowerShellInnerCommand({ command: ["gjc"], environment: childEnvironment })
628
- : `exec env ${Object.entries(childEnvironment)
629
- .map(([name, value]) => `${name}=${shellQuote(value)}`)
630
- .join(" ")} gjc`;
706
+ ? buildWindowsPowerShellInnerCommand({
707
+ command: launch ? managedOwnerSupervisorArgv() : ["gjc"],
708
+ environment: {
709
+ ...childEnvironment,
710
+ ...(launch ? { [MANAGED_OWNER_COMMAND_ENV]: JSON.stringify(launch.argv) } : {}),
711
+ },
712
+ })
713
+ : (() => {
714
+ const supervisorEnvironment = launch ? { [MANAGED_OWNER_COMMAND_ENV]: JSON.stringify(launch.argv) } : {};
715
+ const invocation = launch ? managedOwnerSupervisorArgv() : ["gjc"];
716
+ return `exec env ${Object.entries({ ...childEnvironment, ...supervisorEnvironment })
717
+ .map(([name, value]) => `${name}=${shellQuote(value)}`)
718
+ .join(" ")} ${invocation.map(shellQuote).join(" ")}`;
719
+ })();
631
720
  const tmuxArgv = [
632
721
  tmuxCommand,
633
722
  ...buildTmuxProviderCommand(provider, "new-session", [
634
723
  "-d",
635
724
  "-s",
636
725
  sessionName,
726
+ ...(launch ? ["-c", cwd] : []),
637
727
  ...(platform === "win32" ? [] : ["-P", "-F", "#{session_id}"]),
638
728
  command,
639
729
  ]),
@@ -761,7 +851,7 @@ export function createGjcTmuxSession(
761
851
  env: executionEnv,
762
852
  })
763
853
  : Bun.spawnSync(argv, { stdout: "pipe", stderr: "pipe", env: executionEnv });
764
- return { exitCode: result.exitCode, stdout: result.stdout.toString() };
854
+ return { exitCode: result.exitCode, stdout: result.stdout.toString(), stderr: result.stderr.toString() };
765
855
  } finally {
766
856
  assertGjcTmuxStagedMutationAuthoritySync(authority);
767
857
  }
@@ -862,6 +952,150 @@ export function createGjcTmuxSession(
862
952
  : statusGjcTmuxSessionByNativeId(nativeSessionId, executionEnv);
863
953
  }
864
954
 
955
+ function cleanupCreatedManagedGjcTmuxSession(
956
+ session: GjcTmuxSessionStatus,
957
+ sessionEnv: NodeJS.ProcessEnv,
958
+ proof: ProvenTmuxSessionIdentity | undefined,
959
+ ): void {
960
+ const nativeSessionId = proof?.nativeSessionId ?? session.nativeSessionId;
961
+ if (!nativeSessionId || !session.ownerGeneration)
962
+ throw new Error(`gjc_tmux_managed_launch_cleanup_identity_unavailable:${session.name}`);
963
+ const tmuxCommand = session.providerAuthority?.command ?? resolveGjcTmuxCommand(sessionEnv);
964
+ const server = requireSafeTmuxServerForMutation(tmuxCommand, sessionEnv);
965
+ if (proof && (server.pid !== proof.serverPid || server.startTime !== proof.serverStartTime))
966
+ throw new Error("gjc_tmux_cleanup_target_changed");
967
+ cleanupExactCreatedTmuxSession(
968
+ nativeSessionId,
969
+ session.name,
970
+ tmuxCommand,
971
+ sessionEnv,
972
+ proof?.serverPid ?? server.pid,
973
+ proof?.serverStartTime ?? server.startTime,
974
+ server.pidProven,
975
+ session.providerAuthority,
976
+ session.ownerGeneration,
977
+ proof?.psmuxIncarnation ?? session.psmuxIncarnation,
978
+ );
979
+ }
980
+
981
+ /** Launches one Broker-owned child through the managed owner path and returns only re-proof inputs. */
982
+ export function createManagedGjcTmuxSession(
983
+ spec: ManagedTmuxLaunchSpec,
984
+ env: NodeJS.ProcessEnv = process.env,
985
+ options: Omit<CreateGjcTmuxSessionOptions, "launch"> = {},
986
+ ): ManagedTmuxLaunchProof {
987
+ const session = createGjcTmuxSession(env, { ...options, launch: spec });
988
+ const sessionEnv = session.providerAuthority ? environmentForProviderAuthority(env, session.providerAuthority) : env;
989
+ let proof: ProvenTmuxSessionIdentity | undefined;
990
+ try {
991
+ proof = proveGjcTmuxSessionMutationTarget(session.name, sessionEnv);
992
+ const status = statusGjcTmuxSession(session.name, sessionEnv);
993
+ if (
994
+ !status.sessionId ||
995
+ !status.sessionStateFile ||
996
+ !status.ownerGeneration ||
997
+ status.panePids.length !== 1 ||
998
+ proof.nativeSessionId !== status.nativeSessionId
999
+ )
1000
+ throw new Error("gjc_tmux_managed_launch_proof_unavailable");
1001
+ return {
1002
+ name: status.name,
1003
+ nativeSessionId: proof.nativeSessionId,
1004
+ serverPid: proof.serverPid,
1005
+ serverStartTime: proof.serverStartTime,
1006
+ ownerGeneration: status.ownerGeneration,
1007
+ sessionId: status.sessionId,
1008
+ sessionStateFile: status.sessionStateFile,
1009
+ pid: status.panePids[0]!,
1010
+ providerIdentity: providerIdentity(
1011
+ session.providerAuthority ?? resolveGjcTmuxProviderContext({ env, platform: options.platform }),
1012
+ ),
1013
+ psmuxIncarnation: proof.psmuxIncarnation,
1014
+ };
1015
+ } catch (proofError) {
1016
+ try {
1017
+ cleanupCreatedManagedGjcTmuxSession(session, sessionEnv, proof);
1018
+ } catch (cleanupError) {
1019
+ throw new AggregateError([proofError, cleanupError], "gjc_tmux_managed_launch_proof_failed_cleanup_failed");
1020
+ }
1021
+ throw proofError;
1022
+ }
1023
+ }
1024
+
1025
+ function isManagedSessionGone(error: unknown): boolean {
1026
+ const message = error instanceof Error ? error.message : "";
1027
+ return (
1028
+ message.startsWith("gjc_tmux_session_not_found:") ||
1029
+ message.includes("no server running") ||
1030
+ message.includes("failed to connect to server") ||
1031
+ message.includes("error connecting to")
1032
+ );
1033
+ }
1034
+
1035
+ function managedProofAuthority(proof: ManagedTmuxLaunchProof): ProviderAuthority | null | undefined {
1036
+ if (!proof.psmuxIncarnation) return undefined;
1037
+ try {
1038
+ const authority = readGjcTmuxProviderAuthoritySync({
1039
+ stateDir: path.dirname(proof.sessionStateFile),
1040
+ sessionId: proof.sessionId,
1041
+ generation: proof.ownerGeneration,
1042
+ });
1043
+ return authority.kind === "windows-psmux" ? authority : null;
1044
+ } catch {
1045
+ return null;
1046
+ }
1047
+ }
1048
+
1049
+ /** Re-proves every mutable multiplexer identity field before a Broker effect. */
1050
+ export function verifyManagedGjcTmuxSession(
1051
+ expected: ManagedTmuxLaunchProof,
1052
+ env: NodeJS.ProcessEnv = process.env,
1053
+ ): "verified" | "mismatch" | "gone" {
1054
+ const authority = managedProofAuthority(expected);
1055
+ if (authority === null) return "mismatch";
1056
+ const sessionEnv = authority ? environmentForProviderAuthority(env, authority) : env;
1057
+ try {
1058
+ const session = statusGjcTmuxSession(expected.name, sessionEnv);
1059
+ const identity = proveGjcTmuxSessionMutationTarget(expected.name, sessionEnv);
1060
+ const currentProvider = authority ?? resolveGjcTmuxProviderContext({ env: sessionEnv });
1061
+ if (
1062
+ session.name !== expected.name ||
1063
+ session.nativeSessionId !== expected.nativeSessionId ||
1064
+ session.sessionId !== expected.sessionId ||
1065
+ session.sessionStateFile !== expected.sessionStateFile ||
1066
+ session.ownerGeneration !== expected.ownerGeneration ||
1067
+ session.panePids.length !== 1 ||
1068
+ session.panePids[0] !== expected.pid ||
1069
+ identity.nativeSessionId !== expected.nativeSessionId ||
1070
+ identity.serverPid !== expected.serverPid ||
1071
+ identity.serverStartTime !== expected.serverStartTime ||
1072
+ identity.psmuxIncarnation !== expected.psmuxIncarnation ||
1073
+ providerIdentity(currentProvider) !== expected.providerIdentity
1074
+ )
1075
+ return "mismatch";
1076
+ return "verified";
1077
+ } catch (error) {
1078
+ return isManagedSessionGone(error) ? "gone" : "mismatch";
1079
+ }
1080
+ }
1081
+
1082
+ /** Closes only an identity that passes the same exhaustive verification used for replay and reaping. */
1083
+ export async function forceCloseManagedGjcTmuxSession(
1084
+ expected: ManagedTmuxLaunchProof,
1085
+ env: NodeJS.ProcessEnv = process.env,
1086
+ ): Promise<GjcTmuxSessionStatus> {
1087
+ const authority = managedProofAuthority(expected);
1088
+ if (authority === null || verifyManagedGjcTmuxSession(expected, env) !== "verified")
1089
+ throw new Error(`gjc_tmux_owner_changed:${expected.name}`);
1090
+ return await forceCloseGjcTmuxSession(
1091
+ expected.name,
1092
+ authority ? environmentForProviderAuthority(env, authority) : env,
1093
+ expected.sessionId,
1094
+ expected.sessionStateFile,
1095
+ { expectedManagedProof: expected },
1096
+ );
1097
+ }
1098
+
865
1099
  function statusGjcTmuxSessionByNativeId(nativeSessionId: string, env: NodeJS.ProcessEnv): GjcTmuxSessionStatus {
866
1100
  const name = runTmux(
867
1101
  ["display-message", "-p", "-t", normalizeExactTmuxTarget(nativeSessionId, env, "option"), "#{session_name}"],
@@ -977,6 +1211,8 @@ function cleanupExactCreatedTmuxSession(
977
1211
  expectedStartTime: string,
978
1212
  expectedPidProven?: boolean,
979
1213
  provisionalAuthority?: ProviderAuthority,
1214
+ expectedOwnerGeneration?: string,
1215
+ expectedPsmuxIncarnation?: string,
980
1216
  ): void {
981
1217
  const server = requireSafeTmuxServerForMutation(tmuxCommand, env);
982
1218
  if (server.pid !== expectedPid || server.startTime !== expectedStartTime)
@@ -987,8 +1223,9 @@ function cleanupExactCreatedTmuxSession(
987
1223
  { pid: expectedPid, pidProven: expectedPidProven ?? server.pidProven },
988
1224
  env,
989
1225
  `kill-session -t ${tmuxCommandArgument(normalizeExactTmuxTarget(nativeSessionId, env, "session"))}`,
990
- undefined,
1226
+ expectedOwnerGeneration,
991
1227
  provisionalAuthority,
1228
+ expectedPsmuxIncarnation,
992
1229
  );
993
1230
  }
994
1231
 
@@ -1518,6 +1755,8 @@ export async function forceCloseGjcTmuxSession(
1518
1755
  expectedStateFile?: string,
1519
1756
  deps: Partial<ForceCloseOwnerDependencies> = {},
1520
1757
  ): Promise<GjcTmuxSessionStatus> {
1758
+ if (deps.expectedManagedProof && verifyManagedGjcTmuxSession(deps.expectedManagedProof, env) !== "verified")
1759
+ throw new Error(`gjc_tmux_owner_changed:${sessionName}`);
1521
1760
  const session = statusGjcTmuxSession(sessionName, env);
1522
1761
  assertMutationAuthority(session);
1523
1762
  const sessionEnv =
@@ -3,6 +3,7 @@ import * as path from "node:path";
3
3
  import { logger } from "@gajae-code/utils";
4
4
  import { resolveSkillScopeTrust, type SkillDiscoverySettings } from "../config/skill-settings-defaults";
5
5
  import { detectDeepInterviewPlaintextAskLeak } from "../deep-interview/plaintext-gate-guard";
6
+ import { captureRepositoryBinding, publicRepositoryBinding } from "../gjc-runtime/repository-binding";
6
7
  import { activeSnapshotPath, modeStatePath as sessionModeStatePath } from "../gjc-runtime/session-layout";
7
8
  import { resolveGjcSessionForRead } from "../gjc-runtime/session-resolution";
8
9
  import { ModeStateSchema, SkillActiveStateSchema } from "../gjc-runtime/state-schema";
@@ -421,6 +422,10 @@ async function seedSkillActivationState(
421
422
  active_skills: [entry],
422
423
  ...(input.activeSubskills ? { active_subskills: input.activeSubskills } : {}),
423
424
  };
425
+ const repositoryBinding =
426
+ skill === "ralplan"
427
+ ? publicRepositoryBinding(await captureRepositoryBinding(input.cwd, { displayPath: input.cwd }))
428
+ : undefined;
424
429
  const modeState: ModeState = {
425
430
  active: true,
426
431
  version: WORKFLOW_STATE_VERSION,
@@ -431,6 +436,7 @@ async function seedSkillActivationState(
431
436
  session_id: resolvedSessionId,
432
437
  ...(input.threadId ? { thread_id: input.threadId } : {}),
433
438
  ...(input.turnId ? { turn_id: input.turnId } : {}),
439
+ ...(repositoryBinding ? { repository_binding: repositoryBinding } : {}),
434
440
  };
435
441
  if (skill === "deep-interview") {
436
442
  modeState.threshold = DEFAULT_DEEP_INTERVIEW_AMBIGUITY_THRESHOLD;
package/src/index.ts CHANGED
@@ -45,6 +45,7 @@ export * from "./session/session-dump-format";
45
45
  export * from "./session/session-manager";
46
46
  export * from "./task/executor";
47
47
  export type * from "./task/types";
48
+ export * from "./telemetry";
48
49
  // Tools (detail types and utilities)
49
50
  export * from "./tools";
50
51
  export * from "./utils/git";