@gajae-code/coding-agent 0.15.6 → 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 (167) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/types/cli/args.d.ts +2 -0
  3. package/dist/types/cli/root-flags.d.ts +14 -0
  4. package/dist/types/cli/update-cli.d.ts +13 -0
  5. package/dist/types/cli.d.ts +7 -0
  6. package/dist/types/commands/launch.d.ts +10 -0
  7. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  8. package/dist/types/config/file-lock.d.ts +5 -0
  9. package/dist/types/config/model-registry.d.ts +6 -0
  10. package/dist/types/config/settings-schema.d.ts +10 -1
  11. package/dist/types/config/settings.d.ts +2 -0
  12. package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
  13. package/dist/types/crash/record-loader.d.ts +15 -0
  14. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  15. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  16. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
  17. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  18. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  19. package/dist/types/master-mode/context.d.ts +21 -0
  20. package/dist/types/master-mode/first-request.d.ts +34 -0
  21. package/dist/types/master-mode/snapshot.d.ts +32 -0
  22. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  23. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  24. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  25. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  26. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  27. package/dist/types/modes/print-mode.d.ts +17 -0
  28. package/dist/types/modes/ui-language.d.ts +7 -1
  29. package/dist/types/sdk/broker/broker.d.ts +77 -1
  30. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  31. package/dist/types/sdk/broker/index.d.ts +3 -0
  32. package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
  33. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  34. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  35. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  36. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  37. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  38. package/dist/types/sdk/broker/state-version.d.ts +4 -1
  39. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  40. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  41. package/dist/types/sdk/bus/index.d.ts +6 -0
  42. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  43. package/dist/types/sdk/cli/index.d.ts +1 -0
  44. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  45. package/dist/types/sdk/cli/rows.d.ts +4 -3
  46. package/dist/types/sdk/cli/session-cli.d.ts +18 -2
  47. package/dist/types/sdk/client/client.d.ts +3 -2
  48. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  49. package/dist/types/sdk/host/control/index.d.ts +1 -1
  50. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  51. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  52. package/dist/types/sdk/host/host.d.ts +10 -0
  53. package/dist/types/sdk/host/session-runtime.d.ts +52 -0
  54. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  55. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  56. package/dist/types/sdk/lifecycle-session.d.ts +2 -0
  57. package/dist/types/sdk/router/session-router.d.ts +2 -0
  58. package/dist/types/sdk/session.d.ts +16 -0
  59. package/dist/types/sdk/startup-capability.d.ts +2 -1
  60. package/dist/types/session/agent-session.d.ts +5 -0
  61. package/dist/types/session/credential-store-errors.d.ts +1 -0
  62. package/dist/types/session/session-manager.d.ts +65 -0
  63. package/dist/types/session/yield-queue.d.ts +5 -4
  64. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  65. package/dist/types/tools/bash.d.ts +4 -0
  66. package/dist/types/tools/gh.d.ts +8 -0
  67. package/dist/types/tools/index.d.ts +4 -0
  68. package/package.json +7 -7
  69. package/scripts/generate-sdk-operation-inventory.ts +6 -0
  70. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  71. package/src/cli/accounts-cli.ts +9 -5
  72. package/src/cli/args.ts +17 -0
  73. package/src/cli/fast-help.ts +1 -0
  74. package/src/cli/root-flags.ts +5 -0
  75. package/src/cli/update-cli.ts +77 -11
  76. package/src/commands/launch.ts +86 -18
  77. package/src/commands/sdk.ts +113 -21
  78. package/src/config/autorouting-tier-map.ts +12 -0
  79. package/src/config/file-lock.ts +158 -83
  80. package/src/config/model-preset-registry.ts +1 -5
  81. package/src/config/model-profiles.ts +5 -5
  82. package/src/config/model-registry.ts +23 -0
  83. package/src/config/model-resolver.ts +5 -0
  84. package/src/config/settings-schema.ts +8 -1
  85. package/src/config/settings.ts +5 -0
  86. package/src/coordinator-mcp/projection-scan.ts +21 -0
  87. package/src/coordinator-mcp/question-state.ts +9 -11
  88. package/src/coordinator-mcp/server.ts +94 -20
  89. package/src/crash/index-store.ts +2 -0
  90. package/src/crash/record-loader.ts +10 -0
  91. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  92. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  93. package/src/gjc-runtime/session-state-lock.ts +79 -6
  94. package/src/gjc-runtime/session-state-sidecar.ts +929 -23
  95. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  96. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
  97. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  98. package/src/hooks/skill-state.ts +6 -0
  99. package/src/internal-urls/docs-index.generated.ts +7 -7
  100. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  101. package/src/main.ts +57 -2
  102. package/src/master-mode/context.ts +50 -0
  103. package/src/master-mode/first-request.ts +65 -0
  104. package/src/master-mode/snapshot.ts +205 -0
  105. package/src/modes/acp/acp-agent.ts +605 -156
  106. package/src/modes/components/footer.ts +4 -2
  107. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  108. package/src/modes/components/oauth-selector.ts +6 -1
  109. package/src/modes/components/settings-selector.ts +1 -0
  110. package/src/modes/components/status-line/model-name.ts +53 -0
  111. package/src/modes/components/status-line/priority-row.ts +107 -0
  112. package/src/modes/components/status-line/segments.ts +23 -9
  113. package/src/modes/components/tool-status-header.ts +151 -15
  114. package/src/modes/controllers/input-controller.ts +28 -0
  115. package/src/modes/controllers/selector-controller.ts +191 -96
  116. package/src/modes/print-mode.ts +64 -0
  117. package/src/modes/ui-language.ts +49 -2
  118. package/src/prompts/system/master-mode.md +28 -0
  119. package/src/sdk/broker/broker.ts +1636 -26
  120. package/src/sdk/broker/endpoint-authority.ts +86 -0
  121. package/src/sdk/broker/ensure.ts +5 -0
  122. package/src/sdk/broker/index.ts +3 -0
  123. package/src/sdk/broker/lifecycle.ts +186 -41
  124. package/src/sdk/broker/master-capability.ts +232 -0
  125. package/src/sdk/broker/session-index.ts +121 -17
  126. package/src/sdk/broker/session-scope.ts +272 -0
  127. package/src/sdk/broker/spawn-authority.ts +1031 -0
  128. package/src/sdk/broker/spawn-substrate.ts +548 -0
  129. package/src/sdk/broker/state-version.ts +26 -14
  130. package/src/sdk/broker/transport.ts +1 -0
  131. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  132. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  133. package/src/sdk/bus/chat-daemon-runtime.ts +35 -4
  134. package/src/sdk/bus/index.ts +51 -4
  135. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  136. package/src/sdk/cli/index.ts +1 -0
  137. package/src/sdk/cli/master-cli.ts +259 -0
  138. package/src/sdk/cli/rows.ts +11 -8
  139. package/src/sdk/cli/session-cli.ts +264 -5
  140. package/src/sdk/client/client.ts +14 -8
  141. package/src/sdk/host/control/dispatch.ts +99 -38
  142. package/src/sdk/host/control/index.ts +2 -0
  143. package/src/sdk/host/control/operations.ts +2 -0
  144. package/src/sdk/host/control/runtime-gate.ts +203 -11
  145. package/src/sdk/host/host.ts +27 -2
  146. package/src/sdk/host/session-runtime.test.ts +210 -6
  147. package/src/sdk/host/session-runtime.ts +291 -49
  148. package/src/sdk/lifecycle/broker-client.ts +28 -0
  149. package/src/sdk/lifecycle/service.ts +279 -20
  150. package/src/sdk/lifecycle-session.ts +10 -2
  151. package/src/sdk/protocol/operation-inventory.generated.json +51 -0
  152. package/src/sdk/protocol/operation-registry.ts +13 -3
  153. package/src/sdk/router/session-router.ts +67 -59
  154. package/src/sdk/session.ts +357 -155
  155. package/src/sdk/startup-capability.ts +1 -0
  156. package/src/session/agent-session.ts +640 -103
  157. package/src/session/blob-store.ts +15 -5
  158. package/src/session/credential-store-errors.ts +2 -0
  159. package/src/session/internal/managed-session-storage.ts +153 -78
  160. package/src/session/session-manager.ts +337 -24
  161. package/src/session/yield-queue.ts +24 -10
  162. package/src/setup/frictionless-onboarding.ts +2 -0
  163. package/src/telemetry/events.ts +1138 -12
  164. package/src/tools/bash.ts +256 -42
  165. package/src/tools/gh.ts +104 -48
  166. package/src/tools/github-cache.ts +2 -2
  167. package/src/tools/index.ts +4 -0
@@ -2,20 +2,30 @@
2
2
  * Root command for the coding agent CLI.
3
3
  */
4
4
 
5
- import { APP_NAME, setProjectDir } from "@gajae-code/utils";
5
+ import { APP_NAME, getAgentDir, setProjectDir } from "@gajae-code/utils";
6
6
  import { Args, Command } from "@gajae-code/utils/cli";
7
7
  import { assertLocalLaunchArgs, parseArgs } from "../cli/args";
8
8
  import { ROOT_LAUNCH_FLAGS } from "../cli/root-flags";
9
9
  import { writeCoordinatorAtomic } from "../coordinator-mcp/durability";
10
10
  import { launchDefaultTmuxIfNeeded } from "../gjc-runtime/launch-tmux";
11
- import { type PreparedLaunchWorktree, prepareLaunchWorktree } from "../gjc-runtime/launch-worktree";
11
+ import {
12
+ type GjcLaunchWorktreePlan,
13
+ type PreparedLaunchWorktree,
14
+ parseLaunchWorktreeMode,
15
+ planLaunchWorktree,
16
+ prepareLaunchWorktree,
17
+ } from "../gjc-runtime/launch-worktree";
18
+ import { type LaunchWorktreeReservation, reserveLaunchWorktree } from "../gjc-runtime/launch-worktree-reservation";
12
19
  import {
13
20
  GJC_COORDINATOR_SESSION_ID_ENV,
14
21
  GJC_COORDINATOR_SESSION_STATE_FILE_ENV,
15
22
  GJC_TMUX_OWNER_GENERATION_ENV,
16
23
  } from "../gjc-runtime/session-state-sidecar";
17
24
  import { runRootCommand } from "../main";
25
+ import { assertMasterLaunchArgs } from "../master-mode/context";
18
26
  import { prepareAcpTerminalAuthArgs } from "../modes/acp/terminal-auth";
27
+ import { type IndexedSession, SessionIndex } from "../sdk/broker/session-index";
28
+ import { worktreeOccupant } from "../sdk/broker/worktree-occupancy";
19
29
 
20
30
  export async function persistCoordinatorLaunchFailure(
21
31
  error: unknown,
@@ -56,6 +66,54 @@ export async function persistCoordinatorLaunchFailure(
56
66
  await writeCoordinatorAtomic(stateFile, `${JSON.stringify(payload, null, 2)}\n`);
57
67
  }
58
68
 
69
+ function worktreeInUseError(worktreePath: string, occupant?: string): Error {
70
+ const holder = occupant ? `session ${occupant}` : "another launch";
71
+ return new Error(
72
+ `worktree_in_use:${worktreePath} is already held by ${holder}. ` +
73
+ "Use gjc --worktree <name> for a separate checkout, or stop that session.",
74
+ );
75
+ }
76
+
77
+ async function reserveLaunchWorktreeOrFail(agentDir: string, worktreePath: string): Promise<LaunchWorktreeReservation> {
78
+ const reservation = await reserveLaunchWorktree(agentDir, worktreePath);
79
+ if (!reservation) throw worktreeInUseError(worktreePath);
80
+ return reservation;
81
+ }
82
+
83
+ /** Test seam for the path-keyed atomic reservation. */
84
+ export async function reserveLaunchWorktreeForTest(
85
+ agentDir: string,
86
+ worktreePath: string,
87
+ ): Promise<LaunchWorktreeReservation> {
88
+ return await reserveLaunchWorktreeOrFail(agentDir, worktreePath);
89
+ }
90
+
91
+ async function assertLaunchWorktreeUnoccupied(worktree: GjcLaunchWorktreePlan | { enabled: false }): Promise<void> {
92
+ if (!worktree.enabled) return;
93
+ let sessions: readonly IndexedSession[];
94
+ try {
95
+ sessions = (await new SessionIndex(getAgentDir()).open()).listSessionIdentities();
96
+ } catch {
97
+ throw new Error(`worktree_occupancy_unavailable:${worktree.worktreePath}`);
98
+ }
99
+ const occupant = worktreeOccupant(sessions, worktree.worktreePath);
100
+ if (occupant) throw worktreeInUseError(worktree.worktreePath, occupant);
101
+ }
102
+
103
+ async function reserveLaunchWorktreePreflight(
104
+ worktree: GjcLaunchWorktreePlan | { enabled: false },
105
+ ): Promise<LaunchWorktreeReservation | undefined> {
106
+ if (!worktree.enabled) return undefined;
107
+ const reservation = await reserveLaunchWorktreeOrFail(getAgentDir(), worktree.worktreePath);
108
+ try {
109
+ await assertLaunchWorktreeUnoccupied(worktree);
110
+ return reservation;
111
+ } catch (error) {
112
+ await reservation.release();
113
+ throw error;
114
+ }
115
+ }
116
+
59
117
  export default class Index extends Command {
60
118
  static description = "Red-claw AI coding assistant";
61
119
  static hidden = true;
@@ -93,34 +151,44 @@ export default class Index extends Command {
93
151
  const { args } = prepareAcpTerminalAuthArgs(this.argv);
94
152
  const parsed = parseArgs([...args], "deferred");
95
153
  if (parsed.mode !== "acp") assertLocalLaunchArgs(parsed);
154
+ assertMasterLaunchArgs(parsed);
96
155
  if (parsed.help || parsed.version) {
97
156
  await runRootCommand(parsed, args);
98
157
  return;
99
158
  }
100
159
 
101
160
  let launch: PreparedLaunchWorktree;
161
+ let reservation: LaunchWorktreeReservation | undefined;
102
162
  try {
163
+ const plannedWorktree = planLaunchWorktree(process.cwd(), parseLaunchWorktreeMode(args).mode);
164
+ reservation = await reserveLaunchWorktreePreflight(plannedWorktree);
103
165
  launch = prepareLaunchWorktree(process.cwd(), args);
104
166
  } catch (error) {
167
+ await reservation?.release();
105
168
  await persistCoordinatorLaunchFailure(error, process.cwd());
106
169
  throw error;
107
170
  }
108
- if (launch.worktree.enabled) {
109
- process.chdir(launch.cwd);
110
- setProjectDir(launch.cwd);
171
+ try {
172
+ if (launch.worktree.enabled) {
173
+ process.chdir(launch.cwd);
174
+ setProjectDir(launch.cwd);
175
+ }
176
+ const launchParsed = parseArgs(launch.args, "deferred");
177
+ if (launchParsed.mode !== "acp") assertLocalLaunchArgs(launchParsed);
178
+ assertMasterLaunchArgs(launchParsed);
179
+ if (
180
+ launchDefaultTmuxIfNeeded({
181
+ parsed: launchParsed,
182
+ rawArgs: launch.args,
183
+ cwd: launch.cwd,
184
+ worktreeBranch: launch.worktree.enabled && !launch.worktree.detached ? launch.worktree.branchName : null,
185
+ project: launch.cwd,
186
+ })
187
+ )
188
+ return;
189
+ await runRootCommand(launchParsed, launch.args);
190
+ } finally {
191
+ await reservation?.release();
111
192
  }
112
- const launchParsed = parseArgs(launch.args, "deferred");
113
- if (launchParsed.mode !== "acp") assertLocalLaunchArgs(launchParsed);
114
- if (
115
- launchDefaultTmuxIfNeeded({
116
- parsed: launchParsed,
117
- rawArgs: launch.args,
118
- cwd: launch.cwd,
119
- worktreeBranch: launch.worktree.enabled && !launch.worktree.detached ? launch.worktree.branchName : null,
120
- project: launch.cwd,
121
- })
122
- )
123
- return;
124
- await runRootCommand(launchParsed, launch.args);
125
193
  }
126
194
  }
@@ -27,7 +27,8 @@ import {
27
27
  } from "../sdk/broker/lifecycle";
28
28
  import { processIncarnation } from "../sdk/broker/process-incarnation";
29
29
  import { writeBrokerStartupFailureMarker } from "../sdk/broker/startup-failure";
30
- import { runSdkSessionCli } from "../sdk/cli";
30
+ import { renderSdkSearchTable, runSdkSearch, runSdkSessionCli } from "../sdk/cli";
31
+ import { renderSpawnTable, runSdkSpawn, SdkMasterCliError } from "../sdk/cli/master-cli";
31
32
  import { runSdkGuidesCli } from "../sdk/guides/cli";
32
33
  import { type CreateLifecycleAgentSessionResult, createLifecycleAgentSession } from "../sdk/lifecycle-session";
33
34
  import { listManagedSessionCandidates, resolveManagedSessionScope } from "../sdk/session-directory";
@@ -582,6 +583,7 @@ export async function runSessionHost(
582
583
  ...(mcpStartupTimeoutMs !== undefined ? { mcpStartupTimeoutMs } : {}),
583
584
  ...(request.readiness ? { readiness: request.readiness } : {}),
584
585
  ...(request.modelId ? { modelId: request.modelId } : {}),
586
+ lifecycleRequestId: effectMarker,
585
587
  });
586
588
  } catch (error) {
587
589
  throw await registrationFailure(error);
@@ -838,19 +840,24 @@ class SdkSessionHelp extends Command {
838
840
  description: "Workspace directory for saved-session resolution (default: current directory)",
839
841
  }),
840
842
  op: Flags.string({ description: "Raw control or global operation" }),
841
- scope: Flags.string({
842
- description: "list scope: repo (default), cwd, worktree, or all",
843
- options: ["repo", "cwd", "worktree", "all"],
844
- }),
843
+
845
844
  query: Flags.string({ description: "Raw query name" }),
846
845
  "json-input": Flags.string({ description: "SDK request JSON object" }),
847
846
  "json-input-file": Flags.string({ description: "Read SDK request JSON from a 0600 file" }),
848
847
  "json-input-stdin": Flags.boolean({ description: "Read SDK request JSON from standard input" }),
849
- "idempotency-key": Flags.string({ description: "Caller idempotency key required for SDK lifecycle globals" }),
850
- confirm: Flags.boolean({ description: "Confirm a destructive SDK control operation" }),
848
+ "idempotency-key": Flags.string({
849
+ description: "Caller idempotency key required for lifecycle globals and terminal abort controls",
850
+ }),
851
+ confirm: Flags.boolean({ description: "Confirm a destructive local CLI control operation" }),
851
852
  cursor: Flags.string({
852
- description: "Raw query continuation cursor, or a saved checkpoint token to resume tail from",
853
+ description: "Raw query continuation cursor, saved checkpoint token, or search continuation cursor",
853
854
  }),
855
+ scope: Flags.string({
856
+ description:
857
+ "session list scope: repo (default), cwd, worktree, or all; search scope: repo (default), pwd, or global",
858
+ }),
859
+ limit: Flags.integer({ description: "Search page size from 1 to 100" }),
860
+ json: Flags.boolean({ description: "Render search as the SdkSearchResultV1 JSON envelope" }),
854
861
  text: Flags.string({ description: "Prompt text for send (alternative to --json-input)" }),
855
862
  "op-ref": Flags.string({ description: "Operation reference for send (defaults to a generated ULID)" }),
856
863
  wait: Flags.boolean({
@@ -864,6 +871,70 @@ class SdkSessionHelp extends Command {
864
871
  async run(): Promise<void> {}
865
872
  }
866
873
 
874
+ class SdkSpawnCommand extends Command {
875
+ static description = "Spawn a task-seeded background child session (local interactive master only).";
876
+ static flags = {
877
+ cwd: Flags.string({ description: "Working directory for the spawned child" }),
878
+ prompt: Flags.string({ description: "Seed task delivered once to the child" }),
879
+ model: Flags.string({ description: "Model selector for the child" }),
880
+ profile: Flags.string({ description: "Model profile name for the child" }),
881
+ "agent-dir": Flags.string({ description: "SDK broker state directory" }),
882
+ "idempotency-key": Flags.string({
883
+ description: "Idempotency key for replaying an uncertain session.spawn result",
884
+ }),
885
+ json: Flags.boolean({ description: "Render the safe spawn result as JSON" }),
886
+ };
887
+ async run(): Promise<void> {
888
+ const { flags } = await this.parse(SdkSpawnCommand);
889
+ try {
890
+ const spawn = await runSdkSpawn({
891
+ cwd: flags.cwd,
892
+ prompt: flags.prompt,
893
+ model: flags.model,
894
+ profile: flags.profile,
895
+ agentDir: flags["agent-dir"],
896
+ idempotencyKey: flags["idempotency-key"],
897
+ });
898
+ process.stdout.write(`${flags.json ? JSON.stringify(spawn.rendered) : renderSpawnTable(spawn.rendered)}\n`);
899
+ if (spawn.exitCode !== 0) process.exitCode = spawn.exitCode;
900
+ } catch (error) {
901
+ if (error instanceof SdkMasterCliError) {
902
+ process.stderr.write(`Error: ${error.code}: ${error.message}\n`);
903
+ process.exitCode = error.exitCode;
904
+ return;
905
+ }
906
+ throw error;
907
+ }
908
+ }
909
+ }
910
+
911
+ class SdkSearchCommand extends Command {
912
+ static description = "Search broker-visible SDK sessions within an exact repo, pwd, or global scope.";
913
+ static flags = {
914
+ "agent-dir": Flags.string({ description: "SDK broker state directory" }),
915
+ repo: Flags.string({ description: "Workspace directory for scope resolution (default: current directory)" }),
916
+ scope: Flags.string({ description: "Search scope: repo, pwd, or global (default: repo)" }),
917
+ limit: Flags.integer({ description: "Search page size from 1 to 100" }),
918
+ cursor: Flags.string({ description: "Frozen scoped search continuation cursor" }),
919
+ json: Flags.boolean({ description: "Render exactly the SdkSearchResultV1 JSON envelope" }),
920
+ };
921
+ async run(): Promise<void> {
922
+ const { flags } = await this.parse(SdkSearchCommand);
923
+ const scope = flags.scope;
924
+ if (scope !== undefined && scope !== "repo" && scope !== "pwd" && scope !== "global")
925
+ throw new CliParseError("--scope must be repo, pwd, or global.");
926
+ const search = await runSdkSearch({
927
+ agentDir: flags["agent-dir"],
928
+ repo: flags.repo,
929
+ scope,
930
+ limit: flags.limit,
931
+ cursor: flags.cursor,
932
+ });
933
+ process.stdout.write(`${flags.json ? JSON.stringify(search.result) : renderSdkSearchTable(search.result)}\n`);
934
+ if (search.exitCode !== 0) process.exitCode = search.exitCode;
935
+ }
936
+ }
937
+
867
938
  class SdkSessionCommand extends Command {
868
939
  static description = SdkSessionHelp.description;
869
940
  static args = SdkSessionHelp.args;
@@ -936,10 +1007,12 @@ class SdkGuidesCommand extends Command {
936
1007
 
937
1008
  export default class Sdk extends Command {
938
1009
  static description =
939
- "gjc sdk serve --stdio | --socket <path> [--session <id>]; gjc sdk session list|inspect|send|status|tail; gjc sdk guides refresh|list|show|status|trust";
1010
+ "gjc sdk serve --stdio | --socket <path> [--session <id>]; gjc sdk search [--scope repo|pwd|global] [--json] [--limit N] [--cursor ...]; gjc sdk spawn --cwd <dir> --prompt <task> (master only); gjc sdk session list|inspect|send|status|tail; gjc sdk guides refresh|list|show|status|trust";
940
1011
  static hidden = false;
941
1012
  static delegateHelp = true;
942
- static args = { action: Args.string({ required: false, options: ["serve", "session", "guides"] }) };
1013
+ static args = {
1014
+ action: Args.string({ required: false, options: ["serve", "search", "spawn", "session", "guides"] }),
1015
+ };
943
1016
  static flags = SdkServeHelp.flags;
944
1017
  async run(): Promise<void> {
945
1018
  const action = this.argv[0];
@@ -947,22 +1020,38 @@ export default class Sdk extends Command {
947
1020
  const helpAction =
948
1021
  action === "serve"
949
1022
  ? "sdk serve"
950
- : action === "session"
951
- ? "sdk session"
952
- : action === "guides"
953
- ? "sdk guides"
954
- : "sdk";
1023
+ : action === "search"
1024
+ ? "sdk search"
1025
+ : action === "spawn"
1026
+ ? "sdk spawn"
1027
+ : action === "session"
1028
+ ? "sdk session"
1029
+ : action === "guides"
1030
+ ? "sdk guides"
1031
+ : "sdk";
955
1032
  const helpCommand =
956
1033
  action === "serve"
957
1034
  ? SdkServeHelp
958
- : action === "session"
959
- ? SdkSessionCommand
960
- : action === "guides"
961
- ? SdkGuidesCommand
962
- : Sdk;
1035
+ : action === "search"
1036
+ ? SdkSearchCommand
1037
+ : action === "spawn"
1038
+ ? SdkSpawnCommand
1039
+ : action === "session"
1040
+ ? SdkSessionCommand
1041
+ : action === "guides"
1042
+ ? SdkGuidesCommand
1043
+ : Sdk;
963
1044
  renderCommandHelp("gjc", helpAction, helpCommand);
964
1045
  return;
965
1046
  }
1047
+ if (action === "search") {
1048
+ await new SdkSearchCommand(this.argv.slice(1), this.config).run();
1049
+ return;
1050
+ }
1051
+ if (action === "spawn") {
1052
+ await new SdkSpawnCommand(this.argv.slice(1), this.config).run();
1053
+ return;
1054
+ }
966
1055
  if (action === "session") {
967
1056
  await new SdkSessionCommand(this.argv.slice(1), this.config).run();
968
1057
  return;
@@ -976,7 +1065,7 @@ export default class Sdk extends Command {
976
1065
  return;
977
1066
  }
978
1067
  if (action !== "broker-internal" && action !== "session-host-internal")
979
- throw new CliParseError("Expected action to be serve, session, or guides.");
1068
+ throw new CliParseError("Expected action to be serve, search, spawn, session, or guides.");
980
1069
  const internal = parseSdkInternalArgv(this.argv);
981
1070
  if (internal.action === "session-host-internal") {
982
1071
  await runSessionHost();
@@ -985,6 +1074,9 @@ export default class Sdk extends Command {
985
1074
  const agentDir = internal.agentDir;
986
1075
  const broker = new Broker({
987
1076
  agentDir,
1077
+ masterOrphanGraceMs: (await Settings.loadForScope({ cwd: process.cwd(), agentDir })).get(
1078
+ "sdk.masterOrphanGraceMs",
1079
+ ),
988
1080
  resolveDirectoryMigration: async cwd => {
989
1081
  const settings = await Settings.loadForScope({ cwd, agentDir });
990
1082
  try {
@@ -360,8 +360,19 @@ export const TIER_MAP_SKIP_LIST = {
360
360
  "nvidia/thinkingmachines/inkling": { rationale: "post-rebase catalog addition from dev; not yet curated" },
361
361
  "nvidia/upstage/solar-10.7b-instruct": { rationale: "post-rebase catalog addition from dev; not yet curated" },
362
362
  "openai-codex/gpt-daybreak-blue-latest": { rationale: "post-rebase catalog addition from dev; not yet curated" },
363
+ "opencode-go/deepseek-v4-flash-vision-exp": {
364
+ rationale: "post-rebase catalog addition from dev; not yet curated",
365
+ },
366
+ "opencode-go/glm-5.3-flash": { rationale: "post-rebase catalog addition from dev; not yet curated" },
363
367
  "opencode-go/gpt-5.6-luna": { rationale: "post-rebase catalog addition from dev; not yet curated" },
368
+ "opencode-go/grok-4.6": { rationale: "post-rebase catalog addition from dev; not yet curated" },
364
369
  "opencode-go/hy3": { rationale: "post-rebase catalog addition from dev; not yet curated" },
370
+ "opencode-go/hy4-preview": { rationale: "post-rebase catalog addition from dev; not yet curated" },
371
+ "opencode-go/longcat-2.0": { rationale: "post-rebase catalog addition from dev; not yet curated" },
372
+ "opencode-go/muse-spark-1.2-contributor": {
373
+ rationale: "post-rebase catalog addition from dev; not yet curated",
374
+ },
375
+ "opencode-go/qwen3.8-flash": { rationale: "post-rebase catalog addition from dev; not yet curated" },
365
376
  "opencode-go/qwen3.8-max": { rationale: "post-rebase catalog addition from dev; not yet curated" },
366
377
  "opencode-zen/grok-4.6": { rationale: "post-rebase catalog addition from dev; not yet curated" },
367
378
  "opencode-zen/hy3-free": { rationale: "post-rebase catalog addition from dev; not yet curated" },
@@ -923,6 +934,7 @@ export const TIER_MAP_SKIP_LIST = {
923
934
  "anthropic/claude-3-5-sonnet-20241022": { baseline: true, rationale: "pre-feature baseline; not yet curated" },
924
935
  "anthropic/claude-3-haiku-20240307": { baseline: true, rationale: "pre-feature baseline; not yet curated" },
925
936
  "anthropic/claude-fable-5": { baseline: true, rationale: "pre-feature baseline; not yet curated" },
937
+ "anthropic/claude-fable-5-1": { rationale: "post-feature catalog addition; not yet curated" },
926
938
  "anthropic/claude-haiku-4-5-20251001": { baseline: true, rationale: "pre-feature baseline; not yet curated" },
927
939
  "anthropic/claude-opus-4-0": { baseline: true, rationale: "pre-feature baseline; not yet curated" },
928
940
  "anthropic/claude-opus-4-1": { baseline: true, rationale: "pre-feature baseline; not yet curated" },