@gajae-code/coding-agent 0.15.6 → 0.16.1

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 (270) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/args.d.ts +2 -0
  5. package/dist/types/cli/root-flags.d.ts +14 -0
  6. package/dist/types/cli/update-cli.d.ts +14 -0
  7. package/dist/types/cli.d.ts +7 -0
  8. package/dist/types/commands/launch.d.ts +10 -0
  9. package/dist/types/commands/sdk.d.ts +2 -0
  10. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  11. package/dist/types/config/billing-path.d.ts +25 -0
  12. package/dist/types/config/config-schema-version.d.ts +13 -1
  13. package/dist/types/config/file-lock.d.ts +5 -0
  14. package/dist/types/config/model-registry.d.ts +22 -0
  15. package/dist/types/config/settings-schema.d.ts +27 -8
  16. package/dist/types/config/settings.d.ts +2 -0
  17. package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
  18. package/dist/types/crash/record-loader.d.ts +15 -0
  19. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  20. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  21. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  22. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  23. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  24. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
  25. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  26. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  27. package/dist/types/main.d.ts +1 -0
  28. package/dist/types/master-mode/context.d.ts +21 -0
  29. package/dist/types/master-mode/first-request.d.ts +34 -0
  30. package/dist/types/master-mode/snapshot.d.ts +32 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  32. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  33. package/dist/types/modes/components/model-selector.d.ts +4 -0
  34. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  35. package/dist/types/modes/components/session-selector.d.ts +1 -1
  36. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  37. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  38. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  39. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  40. package/dist/types/modes/interactive-mode.d.ts +16 -2
  41. package/dist/types/modes/print-mode.d.ts +17 -0
  42. package/dist/types/modes/theme/theme.d.ts +2 -1
  43. package/dist/types/modes/types.d.ts +2 -0
  44. package/dist/types/modes/ui-language.d.ts +7 -1
  45. package/dist/types/sdk/broker/broker.d.ts +79 -2
  46. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  47. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  48. package/dist/types/sdk/broker/index.d.ts +3 -0
  49. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  50. package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
  51. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  52. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  53. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  54. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  55. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  56. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  57. package/dist/types/sdk/broker/state-version.d.ts +21 -1
  58. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  59. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  60. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  61. package/dist/types/sdk/bus/index.d.ts +9 -1
  62. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  63. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  64. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  65. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  67. package/dist/types/sdk/cli/index.d.ts +1 -0
  68. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  69. package/dist/types/sdk/cli/rows.d.ts +12 -5
  70. package/dist/types/sdk/cli/session-cli.d.ts +20 -3
  71. package/dist/types/sdk/client/client.d.ts +3 -2
  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/events.d.ts +1 -0
  77. package/dist/types/sdk/host/host.d.ts +10 -0
  78. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  79. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  80. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  81. package/dist/types/sdk/host/session-runtime.d.ts +70 -17
  82. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  83. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  84. package/dist/types/sdk/lifecycle-session.d.ts +3 -1
  85. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  86. package/dist/types/sdk/receipt-state.d.ts +2 -0
  87. package/dist/types/sdk/router/session-router.d.ts +6 -0
  88. package/dist/types/sdk/session.d.ts +16 -0
  89. package/dist/types/sdk/startup-capability.d.ts +2 -1
  90. package/dist/types/sdk/turn-result.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +52 -7
  92. package/dist/types/session/credential-store-errors.d.ts +1 -0
  93. package/dist/types/session/fold-coordinator.d.ts +29 -2
  94. package/dist/types/session/session-manager.d.ts +79 -2
  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/tools/ask.d.ts +1 -0
  98. package/dist/types/tools/bash.d.ts +16 -0
  99. package/dist/types/tools/browser/launch.d.ts +10 -0
  100. package/dist/types/tools/browser/registry.d.ts +8 -0
  101. package/dist/types/tools/gh.d.ts +8 -0
  102. package/dist/types/tools/index.d.ts +10 -10
  103. package/dist/types/tools/job.d.ts +3 -1
  104. package/dist/types/utils/external-editor.d.ts +7 -0
  105. package/examples/extensions/README.md +1 -1
  106. package/examples/extensions/pirate.ts +7 -4
  107. package/package.json +7 -7
  108. package/scripts/generate-sdk-operation-inventory.ts +13 -1
  109. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  110. package/src/async/job-manager.ts +107 -9
  111. package/src/browser-backend.ts +29 -0
  112. package/src/cli/accounts-cli.ts +9 -5
  113. package/src/cli/args.ts +17 -0
  114. package/src/cli/fast-help.ts +1 -0
  115. package/src/cli/plugin-cli.ts +29 -0
  116. package/src/cli/root-flags.ts +5 -0
  117. package/src/cli/update-cli.ts +140 -23
  118. package/src/commands/launch.ts +86 -18
  119. package/src/commands/sdk.ts +135 -28
  120. package/src/config/autorouting-tier-map.ts +12 -0
  121. package/src/config/billing-path.ts +42 -0
  122. package/src/config/config-schema-version.ts +18 -1
  123. package/src/config/file-lock.ts +158 -83
  124. package/src/config/model-preset-registry.ts +178 -23
  125. package/src/config/model-profiles.ts +5 -5
  126. package/src/config/model-registry.ts +291 -42
  127. package/src/config/model-resolver.ts +5 -0
  128. package/src/config/settings-schema.ts +27 -8
  129. package/src/config/settings.ts +51 -15
  130. package/src/coordinator-mcp/projection-scan.ts +27 -6
  131. package/src/coordinator-mcp/question-state.ts +9 -11
  132. package/src/coordinator-mcp/server.ts +115 -21
  133. package/src/crash/index-store.ts +2 -0
  134. package/src/crash/record-loader.ts +10 -0
  135. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  136. package/src/extensibility/extensions/runner.ts +27 -0
  137. package/src/extensibility/extensions/types.ts +8 -3
  138. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  139. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  140. package/src/extensibility/skills.ts +22 -12
  141. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  142. package/src/gjc-runtime/launch-worktree.ts +285 -4
  143. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  144. package/src/gjc-runtime/session-state-lock.ts +213 -14
  145. package/src/gjc-runtime/session-state-sidecar.ts +938 -39
  146. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  147. package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
  148. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  149. package/src/hooks/skill-state.ts +6 -0
  150. package/src/internal-urls/docs-index.generated.ts +15 -15
  151. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  152. package/src/main.ts +61 -3
  153. package/src/master-mode/context.ts +50 -0
  154. package/src/master-mode/first-request.ts +65 -0
  155. package/src/master-mode/snapshot.ts +205 -0
  156. package/src/modes/acp/acp-agent.ts +610 -161
  157. package/src/modes/components/footer.ts +4 -2
  158. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  159. package/src/modes/components/model-selector.ts +76 -8
  160. package/src/modes/components/oauth-selector.ts +6 -1
  161. package/src/modes/components/session-selector.ts +23 -15
  162. package/src/modes/components/sessions-dashboard.ts +8 -5
  163. package/src/modes/components/settings-selector.ts +1 -0
  164. package/src/modes/components/status-line/model-name.ts +53 -0
  165. package/src/modes/components/status-line/priority-row.ts +107 -0
  166. package/src/modes/components/status-line/segments.ts +23 -9
  167. package/src/modes/components/tool-status-header.ts +151 -15
  168. package/src/modes/controllers/command-controller.ts +1 -1
  169. package/src/modes/controllers/event-controller.ts +30 -4
  170. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  171. package/src/modes/controllers/input-controller.ts +30 -2
  172. package/src/modes/controllers/selector-controller.ts +200 -98
  173. package/src/modes/interactive-mode.ts +36 -12
  174. package/src/modes/print-mode.ts +64 -0
  175. package/src/modes/prompt-action-autocomplete.ts +30 -4
  176. package/src/modes/runtime-init.ts +12 -9
  177. package/src/modes/theme/theme.ts +5 -0
  178. package/src/modes/types.ts +2 -0
  179. package/src/modes/ui-language.ts +49 -2
  180. package/src/modes/utils/ui-helpers.ts +22 -3
  181. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  182. package/src/prompts/system/master-mode.md +28 -0
  183. package/src/prompts/tools/ask.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -0
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/sdk/acp/adapter.ts +5 -0
  187. package/src/sdk/broker/broker.ts +1646 -27
  188. package/src/sdk/broker/endpoint-authority.ts +86 -0
  189. package/src/sdk/broker/ensure.ts +81 -3
  190. package/src/sdk/broker/index.ts +3 -0
  191. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  192. package/src/sdk/broker/lifecycle.ts +440 -75
  193. package/src/sdk/broker/master-capability.ts +232 -0
  194. package/src/sdk/broker/session-index.ts +121 -17
  195. package/src/sdk/broker/session-scope.ts +272 -0
  196. package/src/sdk/broker/spawn-authority.ts +1031 -0
  197. package/src/sdk/broker/spawn-substrate.ts +591 -0
  198. package/src/sdk/broker/startup-budget.ts +144 -2
  199. package/src/sdk/broker/state-version.ts +43 -14
  200. package/src/sdk/broker/transport.ts +1 -0
  201. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  202. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  203. package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
  204. package/src/sdk/bus/discord-daemon.ts +50 -10
  205. package/src/sdk/bus/index.ts +84 -7
  206. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  207. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  208. package/src/sdk/bus/notification-service.ts +22 -5
  209. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  210. package/src/sdk/bus/reconciliation-store.ts +55 -31
  211. package/src/sdk/bus/slack-conversation.ts +1 -1
  212. package/src/sdk/bus/slack-daemon.ts +72 -38
  213. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  214. package/src/sdk/cli/index.ts +1 -0
  215. package/src/sdk/cli/master-cli.ts +259 -0
  216. package/src/sdk/cli/rows.ts +41 -15
  217. package/src/sdk/cli/session-cli.ts +297 -26
  218. package/src/sdk/client/client.ts +14 -8
  219. package/src/sdk/host/control/dispatch.ts +101 -40
  220. package/src/sdk/host/control/index.ts +2 -0
  221. package/src/sdk/host/control/operations.ts +2 -0
  222. package/src/sdk/host/control/runtime-gate.ts +203 -11
  223. package/src/sdk/host/events.ts +1 -0
  224. package/src/sdk/host/host.ts +29 -2
  225. package/src/sdk/host/query/cursor.ts +52 -7
  226. package/src/sdk/host/query/handlers.ts +33 -12
  227. package/src/sdk/host/query/revision-store.ts +12 -0
  228. package/src/sdk/host/session-runtime.test.ts +673 -14
  229. package/src/sdk/host/session-runtime.ts +674 -103
  230. package/src/sdk/host/surface-policy.ts +3 -3
  231. package/src/sdk/lifecycle/broker-client.ts +28 -0
  232. package/src/sdk/lifecycle/service.ts +279 -20
  233. package/src/sdk/lifecycle-session.ts +17 -4
  234. package/src/sdk/prompt-deadline-manager.ts +2 -1
  235. package/src/sdk/protocol/operation-inventory.generated.json +105 -3
  236. package/src/sdk/protocol/operation-registry.ts +14 -4
  237. package/src/sdk/receipt-state.ts +10 -0
  238. package/src/sdk/router/session-router.ts +219 -118
  239. package/src/sdk/session.ts +416 -195
  240. package/src/sdk/startup-capability.ts +1 -0
  241. package/src/sdk/turn-result.ts +22 -0
  242. package/src/session/agent-session.ts +1628 -318
  243. package/src/session/blob-store.ts +15 -5
  244. package/src/session/credential-store-errors.ts +2 -0
  245. package/src/session/fold-coordinator.ts +40 -3
  246. package/src/session/internal/managed-session-storage.ts +153 -78
  247. package/src/session/session-manager.ts +850 -97
  248. package/src/session/yield-queue.ts +24 -10
  249. package/src/setup/frictionless-onboarding.ts +2 -0
  250. package/src/slash-commands/builtin-registry.ts +41 -0
  251. package/src/task/executor.ts +9 -4
  252. package/src/telemetry/events.ts +1138 -12
  253. package/src/tools/ask-contract.test.ts +64 -0
  254. package/src/tools/ask-contract.ts +36 -1
  255. package/src/tools/ask.ts +10 -8
  256. package/src/tools/bash-interactive.ts +11 -2
  257. package/src/tools/bash.ts +412 -75
  258. package/src/tools/browser/launch.ts +37 -0
  259. package/src/tools/browser/registry.ts +99 -9
  260. package/src/tools/cron.ts +6 -4
  261. package/src/tools/descriptors.test.ts +15 -0
  262. package/src/tools/descriptors.ts +5 -1
  263. package/src/tools/gh.ts +104 -48
  264. package/src/tools/github-cache.ts +2 -2
  265. package/src/tools/index.ts +10 -6
  266. package/src/tools/job.ts +4 -1
  267. package/src/tools/monitor.ts +12 -9
  268. package/src/tools/resolve.ts +30 -10
  269. package/src/tools/tool-catalog.generated.ts +2 -2
  270. package/src/utils/external-editor.ts +72 -1
@@ -34,7 +34,7 @@ const CONTROL_BINDINGS: Readonly<Record<string, string | undefined>> = {
34
34
  "thinking.cycle": "cycleThinkingLevel",
35
35
  "queue.steering_mode.set": "setQueueMode",
36
36
  "queue.follow_up_mode.set": "setQueueMode",
37
- "queue.interrupt_mode.set": "setQueueMode",
37
+ "queue.tool_interrupt_policy.set": "setQueueMode",
38
38
  "todo.replace": "sdkControl",
39
39
  "permission_mode.set": "sdkControl",
40
40
  "skill.invoke": "invokeSkill",
@@ -62,8 +62,8 @@ const CONTROL_BINDINGS: Readonly<Record<string, string | undefined>> = {
62
62
  // Resource queries (`artifact.read`, `runtime.jobs.list`) remain dispatchable when their
63
63
  // backing session resource is absent so their handlers can return `resource_gone`.
64
64
  // `session.checkpoint` (Q30) is likewise unconditionally installed: it is an SDK-native
65
- // replay authority that degrades to the live transcript head when the host publishes no
66
- // durable checkpoint, so it must never be hidden behind a binding gate.
65
+ // replay authority whose handler returns explicit `unavailable` when the host cannot
66
+ // provide one atomic transcript/ring/idle capture, so it must never be hidden behind a binding gate.
67
67
  const QUERY_BINDINGS: Readonly<Record<string, string | undefined>> = {
68
68
  "skill.list/state": "getSkillState",
69
69
  "config.list/get": "getConfigItems",
@@ -51,3 +51,31 @@ export class AgentDirSessionLifecycleClient implements SessionLifecycleClient {
51
51
  export function createBrokerSessionLifecycleService(agentDir: string): SessionLifecycleService {
52
52
  return new SessionLifecycleService(new AgentDirSessionLifecycleClient(agentDir));
53
53
  }
54
+
55
+ /**
56
+ * Dedicated dispatch for the local master `session.spawn` surface. It shares
57
+ * the lifecycle client's credential boundary but bypasses the generic mutation
58
+ * outcome types: spawn responses are validated by the caller's allowlist.
59
+ */
60
+ export async function dispatchSpawnGlobal(
61
+ agentDir: string,
62
+ input: Record<string, unknown>,
63
+ idempotencyKey: string,
64
+ timeoutMs: number,
65
+ ): Promise<unknown> {
66
+ await ensureBroker({ agentDir });
67
+ const discovery = await readSdkBrokerDiscovery(agentDir);
68
+ if (!discovery) throw new Error("SDK broker discovery is unavailable.");
69
+ const client = await SdkClient.connect(discovery.url, discovery.token, { timeoutMs });
70
+ try {
71
+ return await client.global("session.spawn", input, { idempotencyKey, timeoutMs });
72
+ } finally {
73
+ try {
74
+ await client.close();
75
+ } catch (error) {
76
+ logger.warn("SDK spawn client cleanup failed", {
77
+ error: error instanceof Error ? error.message : String(error),
78
+ });
79
+ }
80
+ }
81
+ }
@@ -1,5 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import path from "node:path";
3
+ import {
4
+ resolvedScopeV1,
5
+ resolveScopeRequest,
6
+ type ScopeRequestV1,
7
+ type SdkSearchResultV1,
8
+ type SdkSearchRowV1,
9
+ scopeRequestV1,
10
+ searchRowV1,
11
+ } from "../broker/session-scope";
3
12
  import {
4
13
  SessionListTraversalError,
5
14
  type SessionListTraversalPage,
@@ -141,6 +150,9 @@ export interface SessionDeleteTarget {
141
150
  export interface SessionListTarget {
142
151
  readonly cwd?: string;
143
152
  readonly resolveSessionId?: string;
153
+ readonly scope?: ScopeRequestV1;
154
+ readonly limit?: number;
155
+ readonly cursor?: string;
144
156
  }
145
157
 
146
158
  interface SessionLifecycleMutationRequestBase<
@@ -197,7 +209,8 @@ export interface SessionLifecycleListEntry {
197
209
  readonly live?: boolean;
198
210
  readonly endpointGeneration?: number;
199
211
  readonly terminalUncertain?: boolean;
200
- readonly repo?: string;
212
+ readonly cwd?: string;
213
+ readonly locator?: { readonly cwd: string; readonly worktreeRoot: string | null; readonly stateRoot: string };
201
214
  }
202
215
 
203
216
  export interface SessionLifecycleListResult {
@@ -207,6 +220,8 @@ export interface SessionLifecycleListResult {
207
220
  readonly savedSession?: SessionLifecycleSavedSession;
208
221
  }
209
222
 
223
+ export type SessionScopedListResult = SdkSearchResultV1;
224
+
210
225
  export interface SessionCreateResult {
211
226
  readonly ok: true;
212
227
  readonly operation: "session.create";
@@ -240,7 +255,7 @@ export interface SessionReconcileUncertainResult {
240
255
  export interface SessionListSuccessResult {
241
256
  readonly ok: true;
242
257
  readonly operation: "session.list";
243
- readonly result: SessionLifecycleListResult;
258
+ readonly result: SessionLifecycleListResult | SessionScopedListResult;
244
259
  }
245
260
 
246
261
  export interface SessionLifecycleError {
@@ -289,6 +304,7 @@ export type SessionListFailure = {
289
304
  readonly operation: "session.list";
290
305
  readonly certainty: SessionLifecycleCertainty;
291
306
  readonly error: SessionLifecycleError;
307
+ readonly result?: SessionScopedListResult;
292
308
  };
293
309
 
294
310
  export type SessionCreateOutcome = SessionCreateResult | SessionCreateFailure;
@@ -582,12 +598,24 @@ function listResult(value: unknown): SessionLifecycleListResult | undefined {
582
598
  live?: boolean;
583
599
  endpointGeneration?: number;
584
600
  terminalUncertain?: boolean;
585
- repo?: string;
601
+ cwd?: string;
602
+ locator?: { cwd: string; worktreeRoot: string | null; stateRoot: string };
586
603
  } = { sessionId: entry.sessionId };
587
604
  if (typeof entry.live === "boolean") item.live = entry.live;
588
605
  if (typeof entry.endpointGeneration === "number") item.endpointGeneration = entry.endpointGeneration;
589
606
  if (typeof entry.terminalUncertain === "boolean") item.terminalUncertain = entry.terminalUncertain;
590
- if (isRecord(entry.locator) && typeof entry.locator.repo === "string") item.repo = entry.locator.repo;
607
+ if (isRecord(entry.locator) && typeof entry.locator.cwd === "string") item.cwd = entry.locator.cwd;
608
+ if (
609
+ isRecord(entry.locator) &&
610
+ typeof entry.locator.cwd === "string" &&
611
+ (entry.locator.worktreeRoot === null || typeof entry.locator.worktreeRoot === "string") &&
612
+ typeof entry.locator.stateRoot === "string"
613
+ )
614
+ item.locator = {
615
+ cwd: entry.locator.cwd,
616
+ worktreeRoot: entry.locator.worktreeRoot,
617
+ stateRoot: entry.locator.stateRoot,
618
+ };
591
619
  sessions.push(item);
592
620
  }
593
621
  const warnings: string[] = [];
@@ -600,10 +628,40 @@ function listResult(value: unknown): SessionLifecycleListResult | undefined {
600
628
  if (savedSessionPresent && !savedSession) return undefined;
601
629
  return { indexSeq, sessions, warnings, ...(savedSession ? { savedSession } : {}) };
602
630
  }
631
+
632
+ function scopedPage(value: unknown): { scope: SdkSearchResultV1["scope"]; observedAt: string } | undefined {
633
+ if (!isRecord(value)) return undefined;
634
+ const scope = resolvedScopeV1(value.scope);
635
+ return scope && typeof value.observedAt === "string" ? { scope, observedAt: value.observedAt } : undefined;
636
+ }
637
+
638
+ function scopedSearchRows(sessions: readonly SessionLifecycleListEntry[]): SdkSearchRowV1[] | undefined {
639
+ const rows: SdkSearchRowV1[] = [];
640
+ for (const session of sessions) {
641
+ const locator = session.locator;
642
+ if (
643
+ locator === undefined ||
644
+ typeof locator.cwd !== "string" ||
645
+ (locator.worktreeRoot !== null && typeof locator.worktreeRoot !== "string") ||
646
+ typeof locator.stateRoot !== "string"
647
+ )
648
+ return undefined;
649
+ rows.push(
650
+ searchRowV1({
651
+ sessionId: session.sessionId,
652
+ locator: { cwd: locator.cwd, worktreeRoot: locator.worktreeRoot, stateRoot: locator.stateRoot },
653
+ live: session.live === true,
654
+ }),
655
+ );
656
+ }
657
+ return rows;
658
+ }
603
659
  type LifecycleListPage = {
604
660
  readonly result: SessionLifecycleListResult;
605
661
  readonly sessions: readonly SessionLifecycleListEntry[];
606
662
  readonly continuationCursor?: unknown;
663
+ readonly scope?: SdkSearchResultV1["scope"];
664
+ readonly observedAt?: string;
607
665
  };
608
666
 
609
667
  function brokerError(value: unknown): { code: string; message: string } | undefined {
@@ -647,6 +705,24 @@ function brokerErrorFromThrown(value: unknown): { code: string; message: string;
647
705
  return { code, message, ...(requestSent === undefined ? {} : { requestSent }) };
648
706
  }
649
707
 
708
+ function unavailableScopedList(scope: SdkSearchResultV1["scope"]): SessionListFailure {
709
+ return {
710
+ ok: false,
711
+ operation: "session.list",
712
+ certainty: "retryable",
713
+ error: { code: "unavailable", message: "broker search is unavailable" },
714
+ result: {
715
+ version: 1,
716
+ scope,
717
+ status: "unavailable",
718
+ observedAt: new Date().toISOString(),
719
+ rows: [],
720
+ warnings: [],
721
+ error: { code: "unavailable", message: "broker search is unavailable" },
722
+ },
723
+ };
724
+ }
725
+
650
726
  const TRANSPORT_ERROR_CODES = new Set([
651
727
  "timeout",
652
728
  "connection_closed",
@@ -678,7 +754,110 @@ export class SessionLifecycleService {
678
754
  this.#client = client;
679
755
  }
680
756
 
681
- async #execute(
757
+ async scopedList(scope: ScopeRequestV1, limit?: number, cursor?: string): Promise<SessionListOutcome> {
758
+ return await this.#list(
759
+ {
760
+ actor: { id: "gjc-sdk-search-cli", namespace: "sdk:search-cli" },
761
+ capability: "session.list",
762
+ target: {
763
+ scope,
764
+ ...(limit === undefined ? {} : { limit }),
765
+ ...(cursor === undefined ? {} : { cursor }),
766
+ },
767
+ },
768
+ true,
769
+ );
770
+ }
771
+
772
+ async #singleScopedList(
773
+ request: Omit<SessionListRequest, "operation">,
774
+ target: Readonly<Record<string, unknown>>,
775
+ locallyResolvedScope: SdkSearchResultV1["scope"],
776
+ ): Promise<SessionListOutcome> {
777
+ let response: unknown;
778
+ try {
779
+ response = await this.#client.global(
780
+ "session.list",
781
+ { ...target },
782
+ {
783
+ ...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
784
+ },
785
+ );
786
+ } catch (thrown) {
787
+ const error = brokerError(thrown) ?? brokerErrorFromThrown(thrown);
788
+ if (!error || error.code === "unavailable") return unavailableScopedList(locallyResolvedScope);
789
+ return failure("session.list", certaintyForThrownError(error), error.code, error.message);
790
+ }
791
+ const error = brokerError(response);
792
+ if (error) {
793
+ if (error.code === "unavailable") return unavailableScopedList(locallyResolvedScope);
794
+ return failure("session.list", certaintyForBrokerCode(error.code), error.code, error.message);
795
+ }
796
+ if (!isRecord(response) || response.ok !== true)
797
+ return failure(
798
+ "session.list",
799
+ "uncertain",
800
+ "malformed_response",
801
+ "lifecycle broker returned a malformed scoped list result",
802
+ );
803
+ const page = sessionListPageFromResponse(response);
804
+ const result = page === undefined ? undefined : listResult(page);
805
+ const pageScope = page === undefined ? undefined : scopedPage(page);
806
+ if (page === undefined || !result || !pageScope)
807
+ return failure(
808
+ "session.list",
809
+ "uncertain",
810
+ "malformed_response",
811
+ "lifecycle broker returned a malformed scoped list result",
812
+ );
813
+ if (JSON.stringify(pageScope.scope) !== JSON.stringify(locallyResolvedScope))
814
+ return failure(
815
+ "session.list",
816
+ "uncertain",
817
+ "scope_observation_drift",
818
+ "lifecycle broker scope does not match the locally resolved request scope",
819
+ );
820
+ const continuationCursor = page.continuationCursor;
821
+ if (
822
+ continuationCursor !== undefined &&
823
+ (typeof continuationCursor !== "string" || continuationCursor.length === 0)
824
+ )
825
+ return failure(
826
+ "session.list",
827
+ "uncertain",
828
+ "malformed_response",
829
+ "lifecycle broker returned a malformed scoped list result",
830
+ );
831
+ const rows = scopedSearchRows(result.sessions);
832
+ if (!rows)
833
+ return failure(
834
+ "session.list",
835
+ "uncertain",
836
+ "malformed_response",
837
+ "lifecycle broker returned a malformed scoped list result",
838
+ );
839
+ return {
840
+ ok: true,
841
+ operation: "session.list",
842
+ result: {
843
+ version: 1,
844
+ scope: pageScope.scope,
845
+ status:
846
+ pageScope.scope.resolution === "not-in-git-worktree"
847
+ ? "not-in-git-worktree"
848
+ : rows.length === 0
849
+ ? "empty"
850
+ : "populated",
851
+ observedAt: pageScope.observedAt,
852
+ indexSeq: result.indexSeq,
853
+ rows,
854
+ ...(continuationCursor === undefined ? {} : { cursor: continuationCursor }),
855
+ warnings: result.warnings,
856
+ },
857
+ };
858
+ }
859
+
860
+ async execute(
682
861
  request: SessionLifecycleMutationRequest,
683
862
  idempotencyKeyOverride?: string,
684
863
  ): Promise<
@@ -759,19 +938,6 @@ export class SessionLifecycleService {
759
938
  | SessionReconcileUncertainOutcome;
760
939
  }
761
940
 
762
- async execute(
763
- request: SessionLifecycleMutationRequest,
764
- ): Promise<
765
- | SessionCreateOutcome
766
- | SessionForkOutcome
767
- | SessionResumeOutcome
768
- | SessionCloseOutcome
769
- | SessionDeleteOutcome
770
- | SessionReconcileUncertainOutcome
771
- > {
772
- return this.#execute(request);
773
- }
774
-
775
941
  async executeWithIdempotencyKey(
776
942
  request: SessionLifecycleMutationRequest,
777
943
  idempotencyKey: string,
@@ -783,7 +949,7 @@ export class SessionLifecycleService {
783
949
  | SessionDeleteOutcome
784
950
  | SessionReconcileUncertainOutcome
785
951
  > {
786
- return this.#execute(request, idempotencyKey);
952
+ return this.execute(request, idempotencyKey);
787
953
  }
788
954
 
789
955
  async create(request: Omit<SessionCreateRequest, "operation">): Promise<SessionCreateOutcome> {
@@ -816,6 +982,9 @@ export class SessionLifecycleService {
816
982
  }
817
983
 
818
984
  async list(request: Omit<SessionListRequest, "operation">): Promise<SessionListOutcome> {
985
+ return await this.#list(request, false);
986
+ }
987
+ async #list(request: Omit<SessionListRequest, "operation">, singleScopedPage: boolean): Promise<SessionListOutcome> {
819
988
  if (!validActor((request as { actor?: unknown }).actor))
820
989
  return failure("session.list", "terminal", "unauthorized", "authenticated actor is required");
821
990
  if ((request as { capability?: unknown }).capability !== "session.list")
@@ -823,6 +992,24 @@ export class SessionLifecycleService {
823
992
  const target = request.target ?? {};
824
993
  if (!validTarget(target))
825
994
  return failure("session.list", "terminal", "invalid_request", "target must be an object");
995
+ const scopeRequest = target.scope === undefined ? undefined : scopeRequestV1(target.scope);
996
+ if (target.scope !== undefined && !scopeRequest)
997
+ return failure("session.list", "terminal", "invalid_request", "scope must be a valid ScopeRequestV1");
998
+ let locallyResolvedScope: SdkSearchResultV1["scope"] | undefined;
999
+ if (scopeRequest !== undefined) {
1000
+ try {
1001
+ locallyResolvedScope = await resolveScopeRequest(scopeRequest);
1002
+ } catch (cause) {
1003
+ return failure(
1004
+ "session.list",
1005
+ "terminal",
1006
+ "invalid_request",
1007
+ cause instanceof Error ? cause.message : "scope could not be resolved",
1008
+ );
1009
+ }
1010
+ }
1011
+ if (singleScopedPage && locallyResolvedScope !== undefined)
1012
+ return await this.#singleScopedList(request, target, locallyResolvedScope);
826
1013
  let pages: readonly SessionListTraversalPage<unknown, LifecycleListPage>[];
827
1014
  try {
828
1015
  pages = await traverseSessionList<Record<string, unknown>, unknown, LifecycleListPage>(
@@ -838,14 +1025,22 @@ export class SessionLifecycleService {
838
1025
  const page = sessionListPageFromResponse(response);
839
1026
  if (!page) return undefined;
840
1027
  const result = listResult(page);
1028
+ const scoped = scopedPage(page);
841
1029
  return result
842
- ? { result, sessions: result.sessions, continuationCursor: page.continuationCursor }
1030
+ ? {
1031
+ result,
1032
+ sessions: result.sessions,
1033
+ continuationCursor: page.continuationCursor,
1034
+ ...(scoped === undefined ? {} : scoped),
1035
+ }
843
1036
  : undefined;
844
1037
  },
845
1038
  );
846
1039
  } catch (thrown) {
847
1040
  if (thrown instanceof BrokerSessionListResponseError) {
848
1041
  const error = thrown.brokerError;
1042
+ if (error.code === "unavailable" && locallyResolvedScope)
1043
+ return unavailableScopedList(locallyResolvedScope);
849
1044
  return failure("session.list", certaintyForBrokerCode(error.code), error.code, error.message);
850
1045
  }
851
1046
  if (thrown instanceof SessionListTraversalError)
@@ -860,6 +1055,8 @@ export class SessionLifecycleService {
860
1055
  : "session.list exceeded the page budget",
861
1056
  );
862
1057
  const error = brokerError(thrown) ?? brokerErrorFromThrown(thrown);
1058
+ if (locallyResolvedScope && (!error || error.code === "unavailable"))
1059
+ return unavailableScopedList(locallyResolvedScope);
863
1060
  return error
864
1061
  ? failure("session.list", certaintyForThrownError(error), error.code, error.message)
865
1062
  : failure("session.list", "retryable", "unavailable", "lifecycle broker request was unavailable");
@@ -873,6 +1070,68 @@ export class SessionLifecycleService {
873
1070
  "lifecycle broker returned a malformed list result",
874
1071
  );
875
1072
  const { result } = firstPage.page;
1073
+ if (request.target?.scope !== undefined) {
1074
+ const scope = firstPage.page.scope;
1075
+ const observedAt = firstPage.page.observedAt;
1076
+ if (!scope || !observedAt)
1077
+ return failure(
1078
+ "session.list",
1079
+ "uncertain",
1080
+ "malformed_response",
1081
+ "lifecycle broker returned a malformed scoped list result",
1082
+ );
1083
+ const frozenScope = JSON.stringify(scope);
1084
+ const frozenIndexSeq = result.indexSeq;
1085
+ for (const page of pages) {
1086
+ const pageScope = page.page.scope;
1087
+ const pageObservedAt = page.page.observedAt;
1088
+ if (
1089
+ pageScope === undefined ||
1090
+ pageObservedAt !== observedAt ||
1091
+ page.page.result.indexSeq !== frozenIndexSeq ||
1092
+ JSON.stringify(pageScope) !== frozenScope
1093
+ )
1094
+ return failure(
1095
+ "session.list",
1096
+ "uncertain",
1097
+ "scope_observation_drift",
1098
+ "lifecycle broker changed the frozen scope observation across list pages",
1099
+ );
1100
+ }
1101
+ if (locallyResolvedScope && JSON.stringify(locallyResolvedScope) !== frozenScope)
1102
+ return failure(
1103
+ "session.list",
1104
+ "uncertain",
1105
+ "scope_observation_drift",
1106
+ "lifecycle broker scope does not match the locally resolved request scope",
1107
+ );
1108
+ const rows = scopedSearchRows(pages.flatMap(page => page.page.result.sessions));
1109
+ if (!rows)
1110
+ return failure(
1111
+ "session.list",
1112
+ "uncertain",
1113
+ "malformed_response",
1114
+ "lifecycle broker returned a malformed scoped list result",
1115
+ );
1116
+ return {
1117
+ ok: true,
1118
+ operation: "session.list",
1119
+ result: {
1120
+ version: 1,
1121
+ scope,
1122
+ status:
1123
+ scope.resolution === "not-in-git-worktree"
1124
+ ? "not-in-git-worktree"
1125
+ : rows.length === 0
1126
+ ? "empty"
1127
+ : "populated",
1128
+ observedAt,
1129
+ indexSeq: result.indexSeq,
1130
+ rows,
1131
+ warnings: result.warnings,
1132
+ },
1133
+ };
1134
+ }
876
1135
  return {
877
1136
  ok: true,
878
1137
  operation: "session.list",
@@ -1,5 +1,5 @@
1
1
  import { parseModelString } from "../config/model-resolver";
2
- import type { AgentSession } from "../session/agent-session";
2
+ import { type AgentSession, isSessionDisposalIncompleteError } from "../session/agent-session";
3
3
  import { type CreateAgentSessionOptions, createAgentSession } from "./session";
4
4
  import {
5
5
  lifecycleMcpStartupTimeoutOption,
@@ -55,6 +55,8 @@ export type CreateLifecycleAgentSessionOptions = Omit<CreateAgentSessionOptions,
55
55
  * a prepared signal instead of readiness until it is explicitly activated.
56
56
  */
57
57
  readiness?: "immediate" | "deferred";
58
+ /** Broker-issued lifecycle request identity published with host registration. */
59
+ lifecycleRequestId?: string;
58
60
  };
59
61
 
60
62
  /** Internal lifecycle-only session construction with an owner-bound SDK startup result. */
@@ -62,9 +64,15 @@ export async function createLifecycleAgentSession(
62
64
  options: CreateLifecycleAgentSessionOptions = {},
63
65
  ): Promise<CreateLifecycleAgentSessionResult> {
64
66
  const rollback = new SdkStartupRollbackTracker();
65
- const capability = new SdkStartupCapability(rollback, options.readiness ?? "immediate");
67
+ const capability = new SdkStartupCapability(rollback, options.readiness ?? "immediate", options.lifecycleRequestId);
66
68
  try {
67
- const { modelId, mcpStartupTimeoutMs, readiness: _readiness, ...sessionOptions } = options;
69
+ const {
70
+ modelId,
71
+ mcpStartupTimeoutMs,
72
+ readiness: _readiness,
73
+ lifecycleRequestId: _lifecycleRequestId,
74
+ ...sessionOptions
75
+ } = options;
68
76
  const internalOptions = {
69
77
  ...sessionOptions,
70
78
  // Explicit model pin (#4707): resolve through the staged selector
@@ -98,7 +106,12 @@ export async function createLifecycleAgentSession(
98
106
  const expected = parseModelString(modelId);
99
107
  const expectedSelector = expected ? `${expected.provider}/${expected.id}` : modelId;
100
108
  if (!activeSelector || activeSelector.toLowerCase() !== expectedSelector.toLowerCase()) {
101
- await result.session.dispose().catch(() => {});
109
+ try {
110
+ await result.session.dispose();
111
+ } catch (error) {
112
+ if (!isSessionDisposalIncompleteError(error)) throw error;
113
+ await result.session.awaitDisposeCompletion();
114
+ }
102
115
  throw new Error(
103
116
  `Model "${modelId}" not found. Use --list-models to see available models.${
104
117
  activeSelector ? ` Session resolved ${activeSelector} instead.` : ""
@@ -7,6 +7,7 @@ import {
7
7
  promptDeadlineAt,
8
8
  recordAttributableProgress,
9
9
  } from "./prompt-deadline-lease";
10
+ import type { SdkPromptTerminalOutcome } from "./prompt-status";
10
11
  import type { TurnResultContent } from "./turn-result";
11
12
 
12
13
  const MAX_EXPIRY_RETRIES = 5;
@@ -26,7 +27,7 @@ export type PromptDeadlineOutcome = {
26
27
  export interface PromptTerminalTransitionEvidence {
27
28
  content?: TurnResultContent;
28
29
  hasActivity?: boolean;
29
- outcome?: { kind: "stopped"; reason: "cancelled"; provenance: "client_cancel" };
30
+ outcome?: SdkPromptTerminalOutcome;
30
31
  }
31
32
 
32
33
  function leaseKey(correlation: InvocationCorrelation): string {