@vibesdotdev/code-sessions 0.0.0-stage → 1.0.0-rc.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 (217) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +165 -2
  3. package/dist/agent/coordinator.agent.descriptor.d.ts +69 -0
  4. package/dist/agent/coordinator.agent.descriptor.d.ts.map +1 -0
  5. package/dist/agent/coordinator.agent.descriptor.js +60 -0
  6. package/dist/agent/coordinator.agent.descriptor.js.map +1 -0
  7. package/dist/agent/coordinator.prompt.descriptor.d.ts +135 -0
  8. package/dist/agent/coordinator.prompt.descriptor.d.ts.map +1 -0
  9. package/dist/agent/coordinator.prompt.descriptor.js +29 -0
  10. package/dist/agent/coordinator.prompt.descriptor.js.map +1 -0
  11. package/dist/agent/coordinator.system-prompt.d.ts +3 -0
  12. package/dist/agent/coordinator.system-prompt.d.ts.map +1 -0
  13. package/dist/agent/coordinator.system-prompt.js +18 -0
  14. package/dist/agent/coordinator.system-prompt.js.map +1 -0
  15. package/dist/agent/developer.agent.descriptor.d.ts +74 -0
  16. package/dist/agent/developer.agent.descriptor.d.ts.map +1 -0
  17. package/dist/agent/developer.agent.descriptor.js +138 -0
  18. package/dist/agent/developer.agent.descriptor.js.map +1 -0
  19. package/dist/agent/developer.prompt.descriptor.d.ts +137 -0
  20. package/dist/agent/developer.prompt.descriptor.d.ts.map +1 -0
  21. package/dist/agent/developer.prompt.descriptor.js +29 -0
  22. package/dist/agent/developer.prompt.descriptor.js.map +1 -0
  23. package/dist/agent/reviewer.agent.descriptor.d.ts +69 -0
  24. package/dist/agent/reviewer.agent.descriptor.d.ts.map +1 -0
  25. package/dist/agent/reviewer.agent.descriptor.js +60 -0
  26. package/dist/agent/reviewer.agent.descriptor.js.map +1 -0
  27. package/dist/agent/reviewer.prompt.descriptor.d.ts +135 -0
  28. package/dist/agent/reviewer.prompt.descriptor.d.ts.map +1 -0
  29. package/dist/agent/reviewer.prompt.descriptor.js +29 -0
  30. package/dist/agent/reviewer.prompt.descriptor.js.map +1 -0
  31. package/dist/agent/reviewer.system-prompt.d.ts +3 -0
  32. package/dist/agent/reviewer.system-prompt.d.ts.map +1 -0
  33. package/dist/agent/reviewer.system-prompt.js +17 -0
  34. package/dist/agent/reviewer.system-prompt.js.map +1 -0
  35. package/dist/agent/system-prompt.d.ts +12 -0
  36. package/dist/agent/system-prompt.d.ts.map +1 -0
  37. package/dist/agent/system-prompt.js +28 -0
  38. package/dist/agent/system-prompt.js.map +1 -0
  39. package/dist/api/code-sessions.api-client.descriptor.d.ts +25 -0
  40. package/dist/api/code-sessions.api-client.descriptor.d.ts.map +1 -0
  41. package/dist/api/code-sessions.api-client.descriptor.js +26 -0
  42. package/dist/api/code-sessions.api-client.descriptor.js.map +1 -0
  43. package/dist/api/code-sessions.api-client.impl.d.ts +156 -0
  44. package/dist/api/code-sessions.api-client.impl.d.ts.map +1 -0
  45. package/dist/api/code-sessions.api-client.impl.js +852 -0
  46. package/dist/api/code-sessions.api-client.impl.js.map +1 -0
  47. package/dist/api/code-sessions.api-client.schemas.d.ts +2 -0
  48. package/dist/api/code-sessions.api-client.schemas.d.ts.map +1 -0
  49. package/dist/api/code-sessions.api-client.schemas.js +2 -0
  50. package/dist/api/code-sessions.api-client.schemas.js.map +1 -0
  51. package/dist/api/code-sessions.api-client.types.d.ts +252 -0
  52. package/dist/api/code-sessions.api-client.types.d.ts.map +1 -0
  53. package/dist/api/code-sessions.api-client.types.js +77 -0
  54. package/dist/api/code-sessions.api-client.types.js.map +1 -0
  55. package/dist/api/index.d.ts +13 -0
  56. package/dist/api/index.d.ts.map +1 -0
  57. package/dist/api/index.js +20 -0
  58. package/dist/api/index.js.map +1 -0
  59. package/dist/capability.d.ts +9 -0
  60. package/dist/capability.d.ts.map +1 -0
  61. package/dist/capability.js +9 -0
  62. package/dist/capability.js.map +1 -0
  63. package/dist/catalog/index.d.ts +2 -0
  64. package/dist/catalog/index.d.ts.map +1 -0
  65. package/dist/catalog/index.js +2 -0
  66. package/dist/catalog/index.js.map +1 -0
  67. package/dist/catalog/session-catalog.d.ts +131 -0
  68. package/dist/catalog/session-catalog.d.ts.map +1 -0
  69. package/dist/catalog/session-catalog.impl.consumer.worker.d.ts +113 -0
  70. package/dist/catalog/session-catalog.impl.consumer.worker.d.ts.map +1 -0
  71. package/dist/catalog/session-catalog.impl.consumer.worker.js +32 -0
  72. package/dist/catalog/session-catalog.impl.consumer.worker.js.map +1 -0
  73. package/dist/catalog/session-catalog.js +126 -0
  74. package/dist/catalog/session-catalog.js.map +1 -0
  75. package/dist/clock/clock.client.d.ts +18 -0
  76. package/dist/clock/clock.client.d.ts.map +1 -0
  77. package/dist/clock/clock.client.js +43 -0
  78. package/dist/clock/clock.client.js.map +1 -0
  79. package/dist/code-sessions.consumer.plugin.d.ts +4 -0
  80. package/dist/code-sessions.consumer.plugin.d.ts.map +1 -0
  81. package/dist/code-sessions.consumer.plugin.js +16 -0
  82. package/dist/code-sessions.consumer.plugin.js.map +1 -0
  83. package/dist/code-sessions.plugin.d.ts +15 -0
  84. package/dist/code-sessions.plugin.d.ts.map +1 -0
  85. package/dist/code-sessions.plugin.js +65 -0
  86. package/dist/code-sessions.plugin.js.map +1 -0
  87. package/dist/config/index.d.ts +9 -0
  88. package/dist/config/index.d.ts.map +1 -0
  89. package/dist/config/index.js +9 -0
  90. package/dist/config/index.js.map +1 -0
  91. package/dist/config/preparation.d.ts +2 -0
  92. package/dist/config/preparation.d.ts.map +1 -0
  93. package/dist/config/preparation.js +2 -0
  94. package/dist/config/preparation.js.map +1 -0
  95. package/dist/docs/code-sessions.docs.descriptor.d.ts +4 -0
  96. package/dist/docs/code-sessions.docs.descriptor.d.ts.map +1 -0
  97. package/dist/docs/code-sessions.docs.descriptor.js +241 -0
  98. package/dist/docs/code-sessions.docs.descriptor.js.map +1 -0
  99. package/dist/index.d.ts +26 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +28 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/pull-request-plan.d.ts +68 -0
  104. package/dist/pull-request-plan.d.ts.map +1 -0
  105. package/dist/pull-request-plan.js +94 -0
  106. package/dist/pull-request-plan.js.map +1 -0
  107. package/dist/schemas/preparation.d.ts +51 -0
  108. package/dist/schemas/preparation.d.ts.map +1 -0
  109. package/dist/schemas/preparation.js +20 -0
  110. package/dist/schemas/preparation.js.map +1 -0
  111. package/dist/schemas/requests.d.ts +411 -0
  112. package/dist/schemas/requests.d.ts.map +1 -0
  113. package/dist/schemas/requests.js +87 -0
  114. package/dist/schemas/requests.js.map +1 -0
  115. package/dist/schemas/responses.d.ts +275 -0
  116. package/dist/schemas/responses.d.ts.map +1 -0
  117. package/dist/schemas/responses.js +90 -0
  118. package/dist/schemas/responses.js.map +1 -0
  119. package/dist/svelte/actions.d.ts +70 -0
  120. package/dist/svelte/actions.d.ts.map +1 -0
  121. package/dist/svelte/actions.js +280 -0
  122. package/dist/svelte/clock.svelte.d.ts +6 -0
  123. package/dist/svelte/clock.svelte.d.ts.map +1 -0
  124. package/dist/svelte/clock.svelte.js +33 -0
  125. package/dist/svelte/components/CreateSessionDialog.svelte +470 -0
  126. package/dist/svelte/components/CreateSessionDialog.svelte.d.ts +57 -0
  127. package/dist/svelte/components/CreateSessionDialog.svelte.d.ts.map +1 -0
  128. package/dist/svelte/components/SessionCard.svelte +216 -0
  129. package/dist/svelte/components/SessionCard.svelte.d.ts +27 -0
  130. package/dist/svelte/components/SessionCard.svelte.d.ts.map +1 -0
  131. package/dist/svelte/components/SessionList.svelte +86 -0
  132. package/dist/svelte/components/SessionList.svelte.d.ts +23 -0
  133. package/dist/svelte/components/SessionList.svelte.d.ts.map +1 -0
  134. package/dist/svelte/components/SessionSeed.svelte +60 -0
  135. package/dist/svelte/components/SessionSeed.svelte.d.ts +9 -0
  136. package/dist/svelte/components/SessionSeed.svelte.d.ts.map +1 -0
  137. package/dist/svelte/components/TranscriptView.svelte +360 -0
  138. package/dist/svelte/components/TranscriptView.svelte.d.ts +26 -0
  139. package/dist/svelte/components/TranscriptView.svelte.d.ts.map +1 -0
  140. package/dist/svelte/create-session-form.d.ts +84 -0
  141. package/dist/svelte/create-session-form.d.ts.map +1 -0
  142. package/dist/svelte/create-session-form.js +107 -0
  143. package/dist/svelte/index.d.ts +20 -0
  144. package/dist/svelte/index.d.ts.map +1 -0
  145. package/dist/svelte/index.js +18 -0
  146. package/dist/svelte/live-session.d.ts +156 -0
  147. package/dist/svelte/live-session.d.ts.map +1 -0
  148. package/dist/svelte/live-session.js +152 -0
  149. package/dist/svelte/loaders.d.ts +74 -0
  150. package/dist/svelte/loaders.d.ts.map +1 -0
  151. package/dist/svelte/loaders.js +124 -0
  152. package/dist/svelte/outcome.d.ts +46 -0
  153. package/dist/svelte/outcome.d.ts.map +1 -0
  154. package/dist/svelte/outcome.js +49 -0
  155. package/dist/svelte/relative-time.d.ts +32 -0
  156. package/dist/svelte/relative-time.d.ts.map +1 -0
  157. package/dist/svelte/relative-time.js +51 -0
  158. package/dist/svelte/session-list-item.d.ts +23 -0
  159. package/dist/svelte/session-list-item.d.ts.map +1 -0
  160. package/dist/svelte/session-list-item.js +1 -0
  161. package/dist/svelte/styles.css +8 -0
  162. package/dist/svelte/transcript-input.d.ts +45 -0
  163. package/dist/svelte/transcript-input.d.ts.map +1 -0
  164. package/dist/svelte/transcript-input.js +188 -0
  165. package/dist/tui/code-sessions-tui.plugin.d.ts +10 -0
  166. package/dist/tui/code-sessions-tui.plugin.d.ts.map +1 -0
  167. package/dist/tui/code-sessions-tui.plugin.js +29 -0
  168. package/dist/tui/code-sessions-tui.plugin.js.map +1 -0
  169. package/dist/tui/code-sessions.tui-app.descriptor.d.ts +7 -0
  170. package/dist/tui/code-sessions.tui-app.descriptor.d.ts.map +1 -0
  171. package/dist/tui/code-sessions.tui-app.descriptor.js +30 -0
  172. package/dist/tui/code-sessions.tui-app.descriptor.js.map +1 -0
  173. package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts +14 -0
  174. package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts.map +1 -0
  175. package/dist/tui/code-sessions.tui-app.impl.consumer.js +453 -0
  176. package/dist/tui/code-sessions.tui-app.impl.consumer.js.map +1 -0
  177. package/dist/tui/code-sessions.tui-client.d.ts +22 -0
  178. package/dist/tui/code-sessions.tui-client.d.ts.map +1 -0
  179. package/dist/tui/code-sessions.tui-client.js +95 -0
  180. package/dist/tui/code-sessions.tui-client.js.map +1 -0
  181. package/dist/tui/code-sessions.tui-open.descriptor.d.ts +7 -0
  182. package/dist/tui/code-sessions.tui-open.descriptor.d.ts.map +1 -0
  183. package/dist/tui/code-sessions.tui-open.descriptor.js +16 -0
  184. package/dist/tui/code-sessions.tui-open.descriptor.js.map +1 -0
  185. package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts +7 -0
  186. package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts.map +1 -0
  187. package/dist/tui/code-sessions.tui-open.impl.consumer.js +11 -0
  188. package/dist/tui/code-sessions.tui-open.impl.consumer.js.map +1 -0
  189. package/dist/tui/code-sessions.tui-state.descriptor.d.ts +16 -0
  190. package/dist/tui/code-sessions.tui-state.descriptor.d.ts.map +1 -0
  191. package/dist/tui/code-sessions.tui-state.descriptor.js +50 -0
  192. package/dist/tui/code-sessions.tui-state.descriptor.js.map +1 -0
  193. package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts +30 -0
  194. package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts.map +1 -0
  195. package/dist/tui/code-sessions.tui-state.impl.consumer.js +533 -0
  196. package/dist/tui/code-sessions.tui-state.impl.consumer.js.map +1 -0
  197. package/dist/tui/code-sessions.tui-state.key.d.ts +322 -0
  198. package/dist/tui/code-sessions.tui-state.key.d.ts.map +1 -0
  199. package/dist/tui/code-sessions.tui-state.key.js +104 -0
  200. package/dist/tui/code-sessions.tui-state.key.js.map +1 -0
  201. package/dist/tui/index.d.ts +9 -0
  202. package/dist/tui/index.d.ts.map +1 -0
  203. package/dist/tui/index.js +8 -0
  204. package/dist/tui/index.js.map +1 -0
  205. package/dist/workspace/index.d.ts +2 -0
  206. package/dist/workspace/index.d.ts.map +1 -0
  207. package/dist/workspace/index.js +2 -0
  208. package/dist/workspace/index.js.map +1 -0
  209. package/dist/workspace/workspace-binding.d.ts +74 -0
  210. package/dist/workspace/workspace-binding.d.ts.map +1 -0
  211. package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts +17 -0
  212. package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts.map +1 -0
  213. package/dist/workspace/workspace-binding.impl.consumer.worker.js +134 -0
  214. package/dist/workspace/workspace-binding.impl.consumer.worker.js.map +1 -0
  215. package/dist/workspace/workspace-binding.js +72 -0
  216. package/dist/workspace/workspace-binding.js.map +1 -0
  217. package/package.json +238 -4
@@ -0,0 +1,852 @@
1
+ /**
2
+ * Code Sessions API Client — universal HTTP implementation.
3
+ *
4
+ * Cross-origin HTTP client against `${platformOrigin}/api/code-sessions/*`.
5
+ * Loaded in cli/browser/sveltekit/cloud scopes by the code-sessions plugin
6
+ * loader.
7
+ *
8
+ * Origin: explicit override, else the `platform` infra/service catalog origin.
9
+ *
10
+ * Authentication: `Authorization: Bearer <user-jwt>` (cli/ssr/cross-app), or
11
+ * same-origin `credentials: 'include'` in the browser. SSR never forwards a
12
+ * request cookie as a credential.
13
+ *
14
+ * Failures are typed (`CapabilityError` code and reason): a platform error
15
+ * status is a `CodeSessionsApiError` whose body is a bounded, redacted excerpt;
16
+ * an unreachable platform, an unreadable or malformed answer and every caller
17
+ * refusal carry their own reason. Every success body is parsed at this
18
+ * boundary before a caller sees it. Diagnostics go through the handed
19
+ * runtime's logger only.
20
+ *
21
+ * NEVER logs the bearer token.
22
+ */
23
+ import { BaseRuntimeClient } from '@vibesdotdev/kernel/client';
24
+ import { CapabilityError, CaughtFailureSchema, isCapabilityError } from '@vibesdotdev/kernel';
25
+ import { serviceOrigin } from '@vibesdotdev/infra-core/service';
26
+ import { resolveCallerBearer } from '@vibesdotdev/auth/caller-bearer';
27
+ import { filterCodeExecutionHosts } from '@vibesdotdev/platform/hosts/schemas';
28
+ import { AgentCodeSessionDescriptorSchema, resolveCodeSession } from '@vibesdotdev/agent-execution/code-session';
29
+ import { getLogger } from '@vibesdotdev/logging';
30
+ import { requestBoundary, CodeSessionCreateSchema, RunCommandRequestSchema, PushBranchRequestSchema, CreatePullRequestRequestSchema, SessionIdSchema, CodeSessionLinkSchema, ServiceAssertionSchema, HttpOptionsSchema, ListOptionsSchema, StartOptionsRequestSchema } from "../schemas/requests.js";
31
+ import { resolveSessionCapability } from "../capability.js";
32
+ import { attempt } from "../svelte/outcome.js";
33
+ import { codeSessionPreparationSchema } from "../config/preparation.js";
34
+ import { CodeSessionListEnvelopeSchema, CodeSessionRecordEnvelopeSchema, CreatePullRequestResponseSchema, PreparationEnvelopeSchema, PushBranchResponseSchema, RunCommandResponseSchema } from "./code-sessions.api-client.schemas.js";
35
+ import { CodeSessionsApiError, CodeSessionsNotSignedInError } from "./code-sessions.api-client.types.js";
36
+ const LOGGER_ID = 'code-sessions:api-client';
37
+ const SERVICE_PREPARE_ROUTE = '/api/internal/code-sessions/prepare';
38
+ function withLink(links, link) {
39
+ return [
40
+ ...(links ?? []).filter((candidate) => candidate.kind !== link.kind || candidate.id !== link.id || candidate.source !== link.source),
41
+ link
42
+ ];
43
+ }
44
+ /** True for the one resolution failure that means "no signed-in caller". */
45
+ function isNotSignedIn(failure) {
46
+ return isCapabilityError(failure) && failure.code === 'AUTH_REQUIRED' && failure.reason === 'not-signed-in';
47
+ }
48
+ async function resolveOrigin(runtime) {
49
+ return (await serviceOrigin(runtime, 'platform')).replace(/\/$/, '');
50
+ }
51
+ /**
52
+ * Whether a browser runtime may ride its page's same-origin Platform session
53
+ * cookies. Only a browser surface does, and never the packaged Desktop
54
+ * renderer (`cef`), which renders from `file://` with neither a same-origin
55
+ * Platform session nor permission to probe the network speculatively. Cloud
56
+ * functionality is admitted there later through an explicit bearer-backed
57
+ * runtime client; local sessions remain independently available offline.
58
+ */
59
+ export function canUseBrowserSessionCookies(scope) {
60
+ return scope.surface === 'browser' && scope.qualifiers.cef !== true;
61
+ }
62
+ /** One plain record of the caller's headers, names as given (a `Headers` instance reads lower-cased). */
63
+ function headersInitToRecord(headers) {
64
+ if (headers instanceof Headers)
65
+ return Object.fromEntries(headers);
66
+ if (Array.isArray(headers))
67
+ return Object.fromEntries(headers.map(([name, value]) => [name, value]));
68
+ return Object.fromEntries(Object.entries(headers ?? {}));
69
+ }
70
+ function hasHeader(record, name) {
71
+ const lower = name.toLowerCase();
72
+ return Object.keys(record).some((key) => key.toLowerCase() === lower);
73
+ }
74
+ function omitCookieHeaders(headers) {
75
+ return Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== 'cookie'));
76
+ }
77
+ /** Last layer wins; names are matched case-insensitively so `Authorization` replaces `authorization`. */
78
+ function mergeHeaders(...layers) {
79
+ const byName = new Map();
80
+ for (const layer of layers) {
81
+ for (const [key, value] of Object.entries(layer ?? {}))
82
+ byName.set(key.toLowerCase(), [key, value]);
83
+ }
84
+ return Object.fromEntries(byName.values());
85
+ }
86
+ function annotate(record) {
87
+ return {
88
+ ...record,
89
+ capability: resolveSessionCapability(record.hostTarget),
90
+ execution: 'platform'
91
+ };
92
+ }
93
+ function workspaceName(path) {
94
+ return (path
95
+ .replace(/[\\/]+$/, '')
96
+ .split(/[\\/]/)
97
+ .pop() || path);
98
+ }
99
+ function localRecordFromHandle(handle) {
100
+ const timestamp = handle.createdAt ?? new Date().toISOString();
101
+ return {
102
+ sessionId: handle.sessionId,
103
+ repositoryId: handle.repositoryId ?? null,
104
+ repositoryName: handle.workspaceOwnership === 'borrowed' ? workspaceName(handle.workspacePath) : null,
105
+ ephemeral: handle.ephemeral,
106
+ hostTarget: handle.hostTarget,
107
+ runtimeConfig: {},
108
+ status: 'active',
109
+ previewUrl: handle.previewUrl ?? null,
110
+ workspacePath: handle.workspacePath,
111
+ checkoutRef: null,
112
+ ownerType: 'local',
113
+ ownerId: null,
114
+ createdAt: timestamp,
115
+ lastActivityAt: timestamp,
116
+ capability: 'interactive',
117
+ execution: 'local'
118
+ };
119
+ }
120
+ function targetFromDescriptor(descriptor) {
121
+ const hostTarget = descriptor.hostTarget;
122
+ if (!hostTarget)
123
+ return null;
124
+ const execution = hostTarget.kind === 'host' && hostTarget.hostId === 'local' ? 'local' : 'platform';
125
+ return {
126
+ id: `${execution}:${descriptor.id}`,
127
+ name: execution === 'local'
128
+ ? 'This machine'
129
+ : hostTarget.kind === 'cloud'
130
+ ? 'Vibes cloud sandbox'
131
+ : descriptor.name,
132
+ description: execution === 'local'
133
+ ? 'Runs in the current TUI process on this machine.'
134
+ : (descriptor.description ?? `Runs with ${descriptor.name}.`),
135
+ hostTarget,
136
+ execution
137
+ };
138
+ }
139
+ /** A refused caller input: `INVALID_INPUT` with its reason. */
140
+ function invalidInput(message, reason) {
141
+ return new CapabilityError(message, 'INVALID_INPUT', { reason });
142
+ }
143
+ /**
144
+ * Runtime port `VibesRuntime` (Runtime SPEC `./client`): the caller bearer
145
+ * reads `declaredScope`, which `RuntimeRef` does not carry, directly and
146
+ * through auth's `resolveCallerBearer` port.
147
+ */
148
+ export class CodeSessionsApiClientConsumer extends BaseRuntimeClient {
149
+ originOverride = null;
150
+ tokenOverride = undefined;
151
+ httpOverrides = {};
152
+ serviceAssertion;
153
+ localSessions = new Map();
154
+ localSessionInFlight = null;
155
+ startOptionsInFlight = null;
156
+ constructor(descriptor = { id: 'code-sessions', kind: 'runtime/client' }, context, runtime) {
157
+ super(descriptor, context, runtime);
158
+ }
159
+ configureHttp(value) {
160
+ return this.configureHttpValidated(requestBoundary(HttpOptionsSchema).parse(value));
161
+ }
162
+ configureHttpValidated(overrides) {
163
+ if (overrides.origin !== undefined) {
164
+ this.originOverride = overrides.origin.replace(/\/$/, '');
165
+ }
166
+ if ('token' in overrides) {
167
+ this.tokenOverride = overrides.token ?? null;
168
+ }
169
+ this.httpOverrides = {
170
+ fetch: overrides.fetch,
171
+ headers: overrides.headers,
172
+ credentials: overrides.credentials
173
+ };
174
+ }
175
+ /** A request-scoped copy of this client on the same runtime. */
176
+ fork(overrides) {
177
+ const forked = new CodeSessionsApiClientConsumer(this.descriptor, undefined, this.runtime);
178
+ // Read the configured origin only when the caller does not supply it: an
179
+ // explicit value never depends on the host's environment. An explicit
180
+ // token (the caller's or this client's) carries over; otherwise the fork
181
+ // resolves the caller's bearer itself, per request.
182
+ const next = { ...overrides };
183
+ if (next.origin === undefined && this.originOverride !== null)
184
+ next.origin = this.originOverride;
185
+ if (!('token' in overrides) && this.tokenOverride !== undefined)
186
+ next.token = this.tokenOverride;
187
+ if (this.serviceAssertion) {
188
+ next.fetch ??= this.httpOverrides.fetch;
189
+ next.token = null;
190
+ next.headers = {};
191
+ next.credentials = 'omit';
192
+ }
193
+ forked.configureHttp(next);
194
+ forked.serviceAssertion = this.serviceAssertion;
195
+ return forked;
196
+ }
197
+ forRequest(value) {
198
+ return this.fork(requestBoundary(HttpOptionsSchema).parse(value));
199
+ }
200
+ forServiceRequest(input) {
201
+ return this.forServiceRequestValidated(requestBoundary(ServiceAssertionSchema, 'service-identity-invalid').parse(input));
202
+ }
203
+ forServiceRequestValidated(assertion) {
204
+ const { userId, organizationId } = assertion;
205
+ const forked = this.fork({
206
+ token: null,
207
+ headers: {},
208
+ credentials: 'omit',
209
+ fetch: this.httpOverrides.fetch
210
+ });
211
+ const serviceAssertion = { userId };
212
+ if (organizationId)
213
+ serviceAssertion.organizationId = organizationId;
214
+ forked.serviceAssertion = serviceAssertion;
215
+ return forked;
216
+ }
217
+ async prepare(input) {
218
+ return this.prepareValidated(requestBoundary(codeSessionPreparationSchema, 'preparation-input-invalid').parse(input));
219
+ }
220
+ async prepareValidated(data) {
221
+ if (!data.repositoryId && !data.createRepositoryIfMissing) {
222
+ throw invalidInput('Choose a repository or allow this Chat to create one.', 'repository-required');
223
+ }
224
+ const route = this.serviceAssertion ? SERVICE_PREPARE_ROUTE : '/api/code-sessions/prepare';
225
+ const payload = await this.requestJson('POST', route, PreparationEnvelopeSchema, data);
226
+ return payload.result;
227
+ }
228
+ /**
229
+ * True when a bearer is configured or the caller's bearer resolves. A
230
+ * signed-out caller is `false`; any other resolution failure propagates.
231
+ */
232
+ async isAuthenticated() {
233
+ return (await this.signedInBearer()) !== null;
234
+ }
235
+ noSessionHint() {
236
+ return new CodeSessionsNotSignedInError().message;
237
+ }
238
+ async canAttemptPlatformRequest() {
239
+ if (await this.signedInBearer())
240
+ return true;
241
+ // An explicitly configured origin marks an HTTP/request-scoped client.
242
+ // Its transport admission never depends on an ambient page global.
243
+ if (this.originOverride !== null)
244
+ return true;
245
+ return canUseBrowserSessionCookies(this.runtime.declaredScope);
246
+ }
247
+ async localSession() {
248
+ this.localSessionInFlight ??= this.resolveLocalSession();
249
+ return this.localSessionInFlight;
250
+ }
251
+ async resolveLocalSession() {
252
+ // Confirmed absence: a runtime that registers no code-session kind has no local plane.
253
+ if (!this.runtime.hasKind('agent/code-session'))
254
+ return null;
255
+ const resolved = await attempt(() => resolveCodeSession(this.runtime, { requiredHostTargetKind: 'host' }));
256
+ if (resolved.ok)
257
+ return resolved.value;
258
+ // Declared-optional: no resolvable local execution target means no local session.
259
+ const logger = getLogger(LOGGER_ID, this.runtime);
260
+ logger.warn('local code session unavailable', { error: resolved.failure });
261
+ return null;
262
+ }
263
+ async platformClient() {
264
+ if (!(await this.canAttemptPlatformRequest()))
265
+ return null;
266
+ const resolved = await attempt(() => this.runtime.query('runtime/client').withId('platform').resolve());
267
+ if (!resolved.ok) {
268
+ // Declared-optional: the platform client is degraded (`degraded: ['platform']`), not fatal.
269
+ const logger = getLogger(LOGGER_ID, this.runtime);
270
+ logger.warn('platform client unavailable', { error: resolved.failure });
271
+ return null;
272
+ }
273
+ const base = resolved.value;
274
+ const token = await this.signedInBearer();
275
+ const overrideHeaders = omitCookieHeaders(headersInitToRecord(this.httpOverrides.headers));
276
+ const bearer = {};
277
+ if (token && !hasHeader(overrideHeaders, 'authorization'))
278
+ bearer.Authorization = `Bearer ${token}`;
279
+ return base.forRequest({
280
+ origin: await this.resolveConfiguredOrigin(),
281
+ fetch: this.httpOverrides.fetch,
282
+ headers: mergeHeaders(bearer, overrideHeaders)
283
+ });
284
+ }
285
+ async rememberLocalHandle(handle, session) {
286
+ const impl = session ?? (await this.localSession());
287
+ if (!impl)
288
+ return null;
289
+ const projected = localRecordFromHandle(handle);
290
+ const existingLinks = this.localSessions.get(handle.sessionId)?.record.links;
291
+ const entry = {
292
+ record: existingLinks ? { ...projected, links: existingLinks } : projected,
293
+ session: impl
294
+ };
295
+ this.localSessions.set(handle.sessionId, entry);
296
+ return entry;
297
+ }
298
+ async discoverLocalSessions() {
299
+ const session = await this.localSession();
300
+ if (!session?.discover)
301
+ return;
302
+ for (const handle of await session.discover()) {
303
+ await this.rememberLocalHandle(handle, session);
304
+ }
305
+ }
306
+ async findLocalSession(sessionId) {
307
+ const known = this.localSessions.get(sessionId);
308
+ if (known)
309
+ return known;
310
+ const session = await this.localSession();
311
+ if (!session)
312
+ return null;
313
+ const attached = await attempt(() => session.attach(sessionId));
314
+ if (!attached.ok) {
315
+ // A local implementation that cannot attach an id has answered
316
+ // "not mine" — a MISS, never a failure of the lookup. Implementations
317
+ // word that refusal differently (the relay impl says `'<id>' is not a
318
+ // relay session id`), so a text match would send every cloud session
319
+ // viewed on a relay-resolving surface into a throw. The platform is the
320
+ // authoritative store for a session this client did not mint locally,
321
+ // so the miss falls through to it; the decline is reported here once.
322
+ const logger = getLogger(LOGGER_ID, this.runtime);
323
+ logger.warn('local attach declined; consulting the platform', { sessionId, error: attached.failure });
324
+ return null;
325
+ }
326
+ return this.rememberLocalHandle(attached.value, session);
327
+ }
328
+ /**
329
+ * The host's workspace, offered as the "current workspace" source. A host
330
+ * that binds no workspace (C1) offers none; any other failure propagates.
331
+ */
332
+ async runtimeWorkspacePath() {
333
+ try {
334
+ return await this.runtime.context('runtime/workspace-path');
335
+ }
336
+ catch (error) {
337
+ const failure = CaughtFailureSchema.parse(error);
338
+ // Confirmed absence of exactly this context, and only its unbound form.
339
+ if (CapabilityError.isAbsenceOf(failure, 'context', 'runtime/workspace-path') &&
340
+ isCapabilityError(failure) &&
341
+ failure.reason === 'workspace-unbound') {
342
+ return null;
343
+ }
344
+ throw failure;
345
+ }
346
+ }
347
+ async resolveStartTarget(descriptor) {
348
+ const resolved = await attempt(() => this.runtime.query('agent/code-session').withId(descriptor.id).resolve());
349
+ if (resolved.ok)
350
+ return true;
351
+ // Declared-optional: an unresolvable target is omitted from the options.
352
+ const logger = getLogger(LOGGER_ID, this.runtime);
353
+ logger.warn('code-session target did not resolve', { id: descriptor.id, error: resolved.failure });
354
+ return false;
355
+ }
356
+ async runtimeStartTargets() {
357
+ if (!this.runtime.hasKind('agent/code-session'))
358
+ return [];
359
+ const byExecutionTarget = new Map();
360
+ for (const registered of this.runtime.query('agent/code-session').descriptors()) {
361
+ const parsed = AgentCodeSessionDescriptorSchema.safeParse(registered);
362
+ if (!parsed.success) {
363
+ const logger = getLogger(LOGGER_ID, this.runtime);
364
+ logger.warn('code-session target descriptor is malformed; omitted', { error: parsed.error });
365
+ continue;
366
+ }
367
+ const descriptor = parsed.data;
368
+ if (descriptor.enabled === false)
369
+ continue;
370
+ const target = targetFromDescriptor(descriptor);
371
+ if (!target)
372
+ continue;
373
+ if (!(await this.resolveStartTarget(descriptor)))
374
+ continue;
375
+ const key = `${target.execution}:${target.hostTarget.kind}:${target.hostTarget.kind === 'host' ? target.hostTarget.hostId : 'cloud'}`;
376
+ if (!byExecutionTarget.has(key))
377
+ byExecutionTarget.set(key, target);
378
+ }
379
+ return [...byExecutionTarget.values()];
380
+ }
381
+ async getStartOptions(value) {
382
+ this.assertStandardAccess();
383
+ return this.getStartOptionsValidated(requestBoundary(StartOptionsRequestSchema).parse(value));
384
+ }
385
+ async getStartOptionsValidated(options) {
386
+ if (!options.force && this.startOptionsInFlight)
387
+ return this.startOptionsInFlight;
388
+ const load = async () => {
389
+ const degraded = [];
390
+ const targets = await this.runtimeStartTargets();
391
+ if (targets.length === 0)
392
+ degraded.push('runtime');
393
+ const platform = await this.platformClient();
394
+ let repositories = [];
395
+ if (await this.canAttemptPlatformRequest()) {
396
+ if (!platform) {
397
+ degraded.push('platform');
398
+ }
399
+ else {
400
+ const [hostsResult, repositoriesResult] = await Promise.allSettled([
401
+ platform.hosts.list({ status: 'active', limit: 200, offset: 0 }),
402
+ platform.repositories.list({ status: 'active', limit: 100, offset: 0 })
403
+ ]);
404
+ const logger = getLogger(LOGGER_ID, this.runtime);
405
+ if (hostsResult.status === 'fulfilled') {
406
+ for (const host of filterCodeExecutionHosts(hostsResult.value.results ?? [])) {
407
+ const id = `platform:host:${host.hostId}`;
408
+ if (targets.some((target) => target.id === id))
409
+ continue;
410
+ targets.push({
411
+ id,
412
+ name: host.name?.trim() || host.hostId,
413
+ description: [
414
+ host.relayReachable ? 'Connected fleet host' : 'Active fleet host',
415
+ host.region
416
+ ]
417
+ .filter(Boolean)
418
+ .join(' · '),
419
+ hostTarget: { kind: 'host', hostId: host.hostId },
420
+ execution: 'platform'
421
+ });
422
+ }
423
+ }
424
+ else {
425
+ logger.warn('platform host list failed', { error: hostsResult.reason });
426
+ degraded.push('hosts');
427
+ }
428
+ if (repositoriesResult.status === 'fulfilled') {
429
+ repositories = repositoriesResult.value.results ?? [];
430
+ }
431
+ else {
432
+ logger.warn('platform repository list failed', { error: repositoriesResult.reason });
433
+ degraded.push('repositories');
434
+ }
435
+ }
436
+ }
437
+ const localTargetIds = targets
438
+ .filter((target) => target.execution === 'local')
439
+ .map((target) => target.id);
440
+ const platformTargetIds = targets
441
+ .filter((target) => target.execution === 'platform')
442
+ .map((target) => target.id);
443
+ const workspacePath = await this.runtimeWorkspacePath();
444
+ const sources = [];
445
+ if (workspacePath && localTargetIds.length > 0) {
446
+ sources.push({
447
+ id: 'workspace:current',
448
+ name: workspaceName(workspacePath),
449
+ description: `Current directory · ${workspacePath}`,
450
+ compatibleTargetIds: localTargetIds,
451
+ workspacePath
452
+ });
453
+ }
454
+ if (targets.length > 0) {
455
+ sources.push({
456
+ id: 'workspace:ephemeral',
457
+ name: 'New empty workspace',
458
+ description: 'Creates a session-owned temporary Git workspace.',
459
+ compatibleTargetIds: targets.map((target) => target.id),
460
+ ephemeral: true
461
+ });
462
+ }
463
+ for (const repository of repositories) {
464
+ if (repository.status && repository.status !== 'active')
465
+ continue;
466
+ sources.push({
467
+ id: `repository:${repository.id}`,
468
+ name: repository.name?.trim() || repository.id,
469
+ description: 'Account repository',
470
+ compatibleTargetIds: platformTargetIds,
471
+ repositoryId: repository.id
472
+ });
473
+ }
474
+ const defaultTargetId = localTargetIds[0] ?? targets[0]?.id ?? null;
475
+ const defaultSourceId = sources.find((source) => defaultTargetId ? source.compatibleTargetIds.includes(defaultTargetId) : false)?.id ?? null;
476
+ return { targets, sources, defaultTargetId, defaultSourceId, degraded };
477
+ };
478
+ const pending = load().finally(() => {
479
+ if (this.startOptionsInFlight === pending)
480
+ this.startOptionsInFlight = null;
481
+ });
482
+ this.startOptionsInFlight = pending;
483
+ return pending;
484
+ }
485
+ async resolveConfiguredOrigin() {
486
+ if (this.originOverride)
487
+ return this.originOverride;
488
+ return resolveOrigin(this.runtime);
489
+ }
490
+ /**
491
+ * An explicit token wins; a browser host presents none (same-origin
492
+ * credentials); otherwise the caller's bearer from auth's `resolveCallerBearer`.
493
+ */
494
+ async resolveConfiguredToken() {
495
+ if (this.tokenOverride !== undefined)
496
+ return this.tokenOverride;
497
+ if (this.runtime.declaredScope.surface === 'browser')
498
+ return null;
499
+ return resolveCallerBearer(this.runtime);
500
+ }
501
+ /**
502
+ * The caller's bearer: a token, `null` when none is configured (a browser or
503
+ * anonymous caller), or the typed signed-out refusal carrying the resolution
504
+ * failure. Any other resolution failure propagates.
505
+ */
506
+ async callerBearer() {
507
+ try {
508
+ return await this.resolveConfiguredToken();
509
+ }
510
+ catch (error) {
511
+ const failure = CaughtFailureSchema.parse(error);
512
+ if (isNotSignedIn(failure))
513
+ return new CodeSessionsNotSignedInError({ cause: failure });
514
+ throw failure;
515
+ }
516
+ }
517
+ /** The configured bearer, `null` when the caller presents none or is signed out. */
518
+ async signedInBearer() {
519
+ const bearer = await this.callerBearer();
520
+ return bearer instanceof CodeSessionsNotSignedInError ? null : bearer;
521
+ }
522
+ requestCredentials() {
523
+ // A browser rides its session cookies automatically; CLI and SSR use a bearer.
524
+ if (this.httpOverrides.credentials)
525
+ return this.httpOverrides.credentials;
526
+ return this.runtime.declaredScope.surface === 'browser' ? 'include' : undefined;
527
+ }
528
+ /** Refuse before resolving either execution plane or dispatching a request. */
529
+ assertStandardAccess() {
530
+ if (this.serviceAssertion) {
531
+ throw invalidInput('Service code-session clients support owner-verified preparation only.', 'service-client-preparation-only');
532
+ }
533
+ }
534
+ /**
535
+ * One request to the platform: the response when it is ok, `null` when a
536
+ * failed status is the confirmed absence `absence` declares, otherwise a typed
537
+ * refusal. The bearer and service credential never reach a failure's text.
538
+ */
539
+ async send(method, path, body, absence = {}) {
540
+ if (path !== SERVICE_PREPARE_ROUTE)
541
+ this.assertStandardAccess();
542
+ const url = `${await this.resolveConfiguredOrigin()}${path}`;
543
+ const fetchFn = this.httpOverrides.fetch ?? fetch;
544
+ const credentialValues = [];
545
+ const headers = mergeHeaders({ Accept: 'application/json' }, body !== undefined ? { 'Content-Type': 'application/json' } : undefined, omitCookieHeaders(headersInitToRecord(this.httpOverrides.headers)));
546
+ // A signed-out caller sends no bearer; a configured origin may still
547
+ // carry the request (`canAttemptPlatformRequest`). An explicit
548
+ // Authorization on the request fork is left intact.
549
+ const token = await this.signedInBearer();
550
+ if (token)
551
+ credentialValues.push(token);
552
+ if (token && !hasHeader(headers, 'authorization'))
553
+ headers.Authorization = `Bearer ${token}`;
554
+ if (this.serviceAssertion) {
555
+ // The service credential comes only from the host's secrets/store (RC05);
556
+ // a typed failure (not set, no store, store failed) propagates as-is.
557
+ const service = await this.runtime.resolveCredential({ source: 'secret', key: 'VIBES_INTERNAL_SERVICE_TOKEN' });
558
+ if (!service.ok)
559
+ throw service.error;
560
+ credentialValues.push(service.value);
561
+ headers.Authorization = `Bearer ${service.value}`;
562
+ headers['x-vibes-user-id'] = this.serviceAssertion.userId;
563
+ if (this.serviceAssertion.organizationId) {
564
+ headers['x-vibes-organization-id'] = this.serviceAssertion.organizationId;
565
+ }
566
+ }
567
+ const init = { method, headers };
568
+ const credentials = this.requestCredentials();
569
+ if (credentials)
570
+ init.credentials = credentials;
571
+ if (body !== undefined)
572
+ init.body = JSON.stringify(body);
573
+ let res;
574
+ try {
575
+ res = await fetchFn(url, init);
576
+ }
577
+ catch (error) {
578
+ const failure = CaughtFailureSchema.parse(error);
579
+ if (failure instanceof Error && failure.name === 'AbortError') {
580
+ throw new CapabilityError('The code-sessions request was cancelled.', 'CANCELLED', {
581
+ reason: 'request-aborted',
582
+ details: { method },
583
+ cause: failure
584
+ });
585
+ }
586
+ // The URL (origin, path, any query) stays out of the message; the transport error rides as cause.
587
+ throw new CapabilityError('The platform could not be reached.', 'UNAVAILABLE', {
588
+ reason: 'platform-unreachable',
589
+ details: { method },
590
+ cause: failure
591
+ });
592
+ }
593
+ if (res.ok)
594
+ return res;
595
+ if (res.status === 401) {
596
+ if (absence.signedOutAsAbsent)
597
+ return null;
598
+ throw new CodeSessionsNotSignedInError();
599
+ }
600
+ if (res.status === 404 && absence.absentOn404)
601
+ return null;
602
+ let text;
603
+ try {
604
+ text = await res.text();
605
+ }
606
+ catch (error) {
607
+ throw new CapabilityError('The platform error response could not be read.', 'UNAVAILABLE', {
608
+ reason: 'platform-body-unreadable',
609
+ details: { status: res.status, method },
610
+ cause: CaughtFailureSchema.parse(error)
611
+ });
612
+ }
613
+ throw new CodeSessionsApiError(`code-sessions ${method} ${path} → ${res.status}`, res.status, text, false, {
614
+ method,
615
+ path,
616
+ redactions: credentialValues
617
+ });
618
+ }
619
+ /** The response body parsed by `schema`, or the typed `platform-response-invalid` refusal. */
620
+ async readJson(res, schema, method) {
621
+ let payload;
622
+ try {
623
+ payload = await res.json();
624
+ }
625
+ catch (error) {
626
+ throw new CapabilityError('The platform returned a response that is not JSON.', 'INTERNAL', {
627
+ reason: 'platform-response-invalid',
628
+ details: { status: res.status, method },
629
+ cause: CaughtFailureSchema.parse(error)
630
+ });
631
+ }
632
+ const parsed = schema.safeParse(payload);
633
+ if (!parsed.success) {
634
+ throw new CapabilityError('The platform returned an unexpected response.', 'INTERNAL', {
635
+ reason: 'platform-response-invalid',
636
+ details: { status: res.status, method },
637
+ });
638
+ }
639
+ return parsed.data;
640
+ }
641
+ async requestJson(method, path, schema, body) {
642
+ const res = await this.send(method, path, body);
643
+ if (!res) {
644
+ throw new CapabilityError('The platform answered without a response.', 'INTERNAL', {
645
+ reason: 'platform-response-invalid',
646
+ details: { method }
647
+ });
648
+ }
649
+ return this.readJson(res, schema, method);
650
+ }
651
+ async requestJsonUnlessAbsent(method, path, schema, absence) {
652
+ const res = await this.send(method, path, undefined, absence);
653
+ return res ? this.readJson(res, schema, method) : null;
654
+ }
655
+ async list(value) {
656
+ this.assertStandardAccess();
657
+ return this.listValidated(requestBoundary(ListOptionsSchema).parse(value));
658
+ }
659
+ async listValidated(options) {
660
+ await this.discoverLocalSessions();
661
+ const local = [...this.localSessions.values()]
662
+ .map((entry) => entry.record)
663
+ .filter((record) => !options?.status || record.status === options.status);
664
+ if (!(await this.canAttemptPlatformRequest()))
665
+ return local;
666
+ const params = new URLSearchParams();
667
+ if (options?.status)
668
+ params.set('status', options.status);
669
+ const qs = params.toString();
670
+ const path = `/api/code-sessions${qs ? `?${qs}` : ''}`;
671
+ const platform = await attempt(() => this.requestJsonUnlessAbsent('GET', path, CodeSessionListEnvelopeSchema, { signedOutAsAbsent: true }));
672
+ if (!platform.ok) {
673
+ // The local execution plane is independently useful. A cloud transport
674
+ // failure must not hide sessions already running on this machine; callers
675
+ // with no local records still receive the platform failure.
676
+ if (local.length === 0)
677
+ throw platform.failure;
678
+ const logger = getLogger(LOGGER_ID, this.runtime);
679
+ logger.warn('platform code-session list failed; showing local sessions only', { error: platform.failure });
680
+ return local;
681
+ }
682
+ if (!platform.value)
683
+ return local;
684
+ return [...local, ...platform.value.results.map(annotate)];
685
+ }
686
+ async get(value) {
687
+ this.assertStandardAccess();
688
+ const sessionId = requestBoundary(SessionIdSchema).parse(value);
689
+ const local = await this.findLocalSession(sessionId);
690
+ if (local)
691
+ return local.record;
692
+ return this.getOwned(sessionId);
693
+ }
694
+ /**
695
+ * PLATFORM-AUTHORITATIVE read: never consults the local plane.
696
+ *
697
+ * `get()` is local-plane-first, and the local relay implementation attaches
698
+ * any well-formed relay id via the service credential WITHOUT an ownership
699
+ * check — fine for a surface reading its own machine, but never evidence of
700
+ * ownership. Authorization decisions (e.g. binding an MCP caller to a code
701
+ * session) MUST use this method: the platform's `/api/code-sessions/[id]`
702
+ * route is owner-scoped and answers 404 for a foreign session. Fails closed
703
+ * with null only for absence or signed-out callers; transport failures propagate.
704
+ */
705
+ async getOwned(value) {
706
+ this.assertStandardAccess();
707
+ return this.getOwnedValidated(requestBoundary(SessionIdSchema).parse(value));
708
+ }
709
+ async getOwnedValidated(sessionId) {
710
+ if (!(await this.canAttemptPlatformRequest()))
711
+ return null;
712
+ const payload = await this.requestJsonUnlessAbsent('GET', `/api/code-sessions/${encodeURIComponent(sessionId)}`, CodeSessionRecordEnvelopeSchema, { absentOn404: true, signedOutAsAbsent: true });
713
+ return payload ? annotate(payload.result) : null;
714
+ }
715
+ async create(value) {
716
+ this.assertStandardAccess();
717
+ return this.createValidated(requestBoundary(CodeSessionCreateSchema).parse(value));
718
+ }
719
+ async createValidated(input) {
720
+ if (input.execution === 'local') {
721
+ const hostTarget = input.hostTarget ?? { kind: 'host', hostId: 'local' };
722
+ if (hostTarget.kind !== 'host' || hostTarget.hostId !== 'local') {
723
+ throw invalidInput('Local code sessions require hostTarget {kind:"host",hostId:"local"}.', 'local-host-target-required');
724
+ }
725
+ const session = await this.localSession();
726
+ if (!session) {
727
+ throw new CapabilityError('No local code-session implementation is registered.', 'NOT_FOUND', {
728
+ reason: 'local-implementation-unregistered'
729
+ });
730
+ }
731
+ const initialize = { hostTarget };
732
+ if (input.sessionId)
733
+ initialize.sessionId = input.sessionId;
734
+ if (input.workspacePath)
735
+ initialize.workspacePath = input.workspacePath;
736
+ if (input.ephemeral)
737
+ initialize.ephemeral = true;
738
+ if (input.checkoutRef)
739
+ initialize.checkoutRef = input.checkoutRef;
740
+ if (input.runtimeConfig)
741
+ initialize.runtimeConfig = input.runtimeConfig;
742
+ if (input.initialEnv)
743
+ initialize.initialEnv = input.initialEnv;
744
+ if (input.exposedPorts)
745
+ initialize.exposedPorts = input.exposedPorts;
746
+ const handle = await session.initialize(initialize);
747
+ const record = {
748
+ ...localRecordFromHandle(handle),
749
+ runtimeConfig: input.runtimeConfig ?? {},
750
+ checkoutRef: input.checkoutRef ?? null
751
+ };
752
+ if (input.links)
753
+ record.links = input.links;
754
+ this.localSessions.set(record.sessionId, { record, session });
755
+ return record;
756
+ }
757
+ const { execution: _execution, ...platformInput } = input;
758
+ const payload = await this.requestJson('POST', '/api/code-sessions', CodeSessionRecordEnvelopeSchema, platformInput);
759
+ return annotate(payload.result);
760
+ }
761
+ async link(value, relationship) {
762
+ this.assertStandardAccess();
763
+ return this.linkValidated(requestBoundary(SessionIdSchema).parse(value), requestBoundary(CodeSessionLinkSchema).parse(relationship));
764
+ }
765
+ async linkValidated(sessionId, link) {
766
+ const local = this.localSessions.get(sessionId);
767
+ if (local) {
768
+ local.record = { ...local.record, links: withLink(local.record.links, link) };
769
+ return local.record;
770
+ }
771
+ const payload = await this.requestJson('PATCH', `/api/code-sessions/${encodeURIComponent(sessionId)}`, CodeSessionRecordEnvelopeSchema, { link });
772
+ return annotate(payload.result);
773
+ }
774
+ async run(value) {
775
+ this.assertStandardAccess();
776
+ const input = requestBoundary(RunCommandRequestSchema).parse(value);
777
+ const { sessionId, ...body } = input;
778
+ const local = await this.findLocalSession(sessionId);
779
+ if (local) {
780
+ const startedAt = new Date();
781
+ const result = await local.session.runCommand(sessionId, body);
782
+ const endedAt = new Date();
783
+ local.record = { ...local.record, lastActivityAt: endedAt.toISOString() };
784
+ return {
785
+ sessionId,
786
+ command: input.command,
787
+ ...result,
788
+ startedAt: startedAt.toISOString(),
789
+ endedAt: endedAt.toISOString()
790
+ };
791
+ }
792
+ return this.requestJson('POST', `/api/code-sessions/${encodeURIComponent(sessionId)}/run`, RunCommandResponseSchema, body);
793
+ }
794
+ async push(value) {
795
+ this.assertStandardAccess();
796
+ return this.pushValidated(requestBoundary(PushBranchRequestSchema).parse(value));
797
+ }
798
+ async pushValidated(input) {
799
+ const { sessionId, ...body } = input;
800
+ // No local-session shortcut on purpose — see the interface note. Publishing
801
+ // must go through the platform so the authority chain and the bound,
802
+ // revocable credential are not bypassable from a client.
803
+ return this.requestJson('POST', `/api/code-sessions/${encodeURIComponent(sessionId)}/push`, PushBranchResponseSchema, body);
804
+ }
805
+ async createPullRequest(value) {
806
+ this.assertStandardAccess();
807
+ return this.createPullRequestValidated(requestBoundary(CreatePullRequestRequestSchema).parse(value));
808
+ }
809
+ async createPullRequestValidated(input) {
810
+ const { sessionId, ...body } = input;
811
+ return this.requestJson('POST', `/api/code-sessions/${encodeURIComponent(sessionId)}/pull-request`, CreatePullRequestResponseSchema, body);
812
+ }
813
+ async delete(value) {
814
+ this.assertStandardAccess();
815
+ return this.deleteValidated(requestBoundary(SessionIdSchema).parse(value));
816
+ }
817
+ async deleteValidated(sessionId) {
818
+ const local = await this.findLocalSession(sessionId);
819
+ if (local) {
820
+ await local.session.teardown(sessionId);
821
+ this.localSessions.delete(sessionId);
822
+ return;
823
+ }
824
+ await this.send('DELETE', `/api/code-sessions/${encodeURIComponent(sessionId)}`, undefined);
825
+ }
826
+ }
827
+ export default CodeSessionsApiClientConsumer;
828
+ /**
829
+ * Create a CodeSessionsApiClientConsumer configured for SSR use. It configures
830
+ * the client through the same public `configureHttp` path every request fork
831
+ * uses; it never touches private implementation fields.
832
+ *
833
+ * Usage:
834
+ * const client = createSsrCodeSessionsClient(event.locals.runtime, {
835
+ * origin: platformOrigin,
836
+ * token: event.locals.sessionToken
837
+ * });
838
+ * const sessions = await client.list();
839
+ */
840
+ export function createSsrCodeSessionsClient(runtime, opts) {
841
+ const client = new CodeSessionsApiClientConsumer({ id: 'code-sessions', kind: 'runtime/client' }, { runtime }, runtime);
842
+ const overrides = {
843
+ token: opts.token,
844
+ fetch: opts.fetch,
845
+ credentials: 'omit'
846
+ };
847
+ if (opts.origin)
848
+ overrides.origin = opts.origin;
849
+ client.configureHttp(overrides);
850
+ return client;
851
+ }
852
+ //# sourceMappingURL=code-sessions.api-client.impl.js.map