@opencode-ai/client 0.0.0-next-17239 → 0.0.0-next-17247

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.
@@ -1236,6 +1236,7 @@ export type Endpoint5_31Output = ({
1236
1236
  export type SessionLogOperation<E = never> = (input: Endpoint5_31Input) => Stream.Stream<Endpoint5_31Output, E>;
1237
1237
  export type Endpoint5_32Input = {
1238
1238
  readonly sessionID: Session.ID;
1239
+ readonly continue?: boolean | undefined;
1239
1240
  };
1240
1241
  export type Endpoint5_32Output = void;
1241
1242
  export type SessionInterruptOperation<E = never> = (input: Endpoint5_32Input) => Effect.Effect<Endpoint5_32Output, E>;
@@ -130,7 +130,10 @@ const Endpoint5_31 = (raw) => (input) => preserveStream()(Stream.unwrap(raw["ses
130
130
  params: { sessionID: input["sessionID"] },
131
131
  query: { after: input["after"], follow: input["follow"] },
132
132
  }).pipe(Effect.mapError(mapClientError), Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))))));
133
- const Endpoint5_32 = (raw) => (input) => preserveEffect()(raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)));
133
+ const Endpoint5_32 = (raw) => (input) => preserveEffect()(raw["session.interrupt"]({
134
+ params: { sessionID: input["sessionID"] },
135
+ query: { continue: input["continue"] },
136
+ }).pipe(Effect.mapError(mapClientError)));
134
137
  const Endpoint5_33 = (raw) => (input) => preserveEffect()(raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)));
135
138
  const Endpoint5_34 = (raw) => (input) => preserveEffect()(raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(Effect.mapError(mapClientError), Effect.map((value) => value.data)));
136
139
  const adaptGroup5 = (raw) => ({
@@ -3,6 +3,7 @@ import { Effect, FileSystem, Option, Schedule, Schema } from "effect";
3
3
  import { spawn } from "node:child_process";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
+ import { defaultEnsureTiming, ensureTiming } from "../service-timing.js";
6
7
  export * from "../service.js";
7
8
  // Read-only lookup: registration file plus health check and version gate.
8
9
  // Never spawns; escalation to ensure() is the caller's policy.
@@ -33,11 +34,12 @@ const discoverLocal = Effect.fnUntraced(function* (options) {
33
34
  // becomes discoverable. A contender is never killed merely for slow startup.
34
35
  /** Ensure a healthy, compatible local service is running. */
35
36
  export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
37
+ const timing = ensureTiming(options);
36
38
  const contenders = new Set();
37
39
  let timeouts;
38
40
  let announced = false;
39
41
  let lastSpawn = 0;
40
- let spawnDelay = 5_000;
42
+ let spawnDelay = timing.spawnDelay;
41
43
  const announce = (reason, previousVersion) => Effect.sync(() => {
42
44
  if (announced)
43
45
  return;
@@ -62,7 +64,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
62
64
  });
63
65
  });
64
66
  const found = yield* Effect.gen(function* () {
65
- const registration = yield* registered(options.file, true);
67
+ const registration = yield* registered(options.file, true, timing.requestTimeout);
66
68
  const info = registration.info;
67
69
  const service = registration.service;
68
70
  if (registration.timedOut && info !== undefined) {
@@ -72,7 +74,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
72
74
  };
73
75
  if (timeouts.count >= 3) {
74
76
  yield* announce("missing");
75
- yield* evict(info, options);
77
+ yield* evict(info, options, timing);
76
78
  timeouts = undefined;
77
79
  lastSpawn = Date.now() - spawnDelay;
78
80
  }
@@ -80,7 +82,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
80
82
  else
81
83
  timeouts = undefined;
82
84
  if (service !== undefined) {
83
- spawnDelay = 5_000;
85
+ spawnDelay = timing.spawnDelay;
84
86
  const compatible = !service.legacy && (options.version === undefined || service.version === options.version);
85
87
  if (compatible && service.state === "ready")
86
88
  return Option.some(service);
@@ -89,7 +91,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
89
91
  if (compatible)
90
92
  return Option.none();
91
93
  yield* announce("version-mismatch", service.version);
92
- yield* kill(service, options).pipe(Effect.ignore);
94
+ yield* kill(service, options, timing).pipe(Effect.ignore);
93
95
  lastSpawn = 0;
94
96
  return Option.none();
95
97
  }
@@ -98,7 +100,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
98
100
  const finished = [...contenders].filter(contenderFinished);
99
101
  const failure = finished.map(contenderFailure).find((error) => error !== undefined);
100
102
  if (finished.some((item) => item.child.exitCode === 0)) {
101
- spawnDelay = Math.min(spawnDelay * 2, 30_000);
103
+ spawnDelay = Math.min(spawnDelay * 2, timing.maxSpawnDelay);
102
104
  }
103
105
  finished.forEach((item) => contenders.delete(item));
104
106
  if (failure !== undefined && contenders.size === 0)
@@ -112,7 +114,7 @@ export const ensure = Effect.fn("service.ensure")(function* (options = {}) {
112
114
  return Option.none();
113
115
  }).pipe(Effect.repeat({
114
116
  until: Option.isSome,
115
- schedule: Schedule.max([Schedule.spaced("1 second"), Schedule.recurs(120)]),
117
+ schedule: Schedule.max([Schedule.spaced(timing.pollInterval), Schedule.recurs(timing.attempts)]),
116
118
  }));
117
119
  if (Option.isNone(found))
118
120
  return yield* Effect.fail(new Error("Timed out waiting for the background service to start"));
@@ -135,7 +137,7 @@ function contenderFinished(contender) {
135
137
  export const stop = Effect.fn("service.stop")(function* (options = {}) {
136
138
  const existing = yield* find(options);
137
139
  if (existing !== undefined)
138
- yield* kill(existing, options);
140
+ yield* kill(existing, options, defaultEnsureTiming);
139
141
  });
140
142
  function fallback() {
141
143
  const state = process.env["XDG_STATE_HOME"] ?? join(homedir(), ".local", "state");
@@ -170,14 +172,14 @@ const read = Effect.fnUntraced(function* (file) {
170
172
  const probe = Effect.fnUntraced(function* (info, allowLegacy = false) {
171
173
  return (yield* probeResult(info, allowLegacy)).service;
172
174
  });
173
- const probeResult = Effect.fnUntraced(function* (info, allowLegacy = false) {
175
+ const probeResult = Effect.fnUntraced(function* (info, allowLegacy = false, timeout = defaultEnsureTiming.requestTimeout) {
174
176
  const endpoint = {
175
177
  url: info.url,
176
178
  auth: info.password === undefined
177
179
  ? undefined
178
180
  : { type: "basic", username: "opencode", password: info.password },
179
181
  };
180
- const signal = AbortSignal.timeout(2_000);
182
+ const signal = AbortSignal.timeout(timeout);
181
183
  const result = yield* Effect.promise(() => fetch(new URL("/api/health", info.url), {
182
184
  headers: headers(endpoint),
183
185
  signal,
@@ -214,11 +216,11 @@ const probeResult = Effect.fnUntraced(function* (info, allowLegacy = false) {
214
216
  timedOut: false,
215
217
  };
216
218
  });
217
- const registered = Effect.fnUntraced(function* (file, allowLegacy = false) {
219
+ const registered = Effect.fnUntraced(function* (file, allowLegacy = false, timeout) {
218
220
  const info = yield* read(file);
219
221
  if (info === undefined)
220
222
  return { info: undefined, service: undefined, timedOut: false };
221
- return { info, ...(yield* probeResult(info, allowLegacy)) };
223
+ return { info, ...(yield* probeResult(info, allowLegacy, timeout)) };
222
224
  });
223
225
  // Health-checked lookup without the version gate: lifecycle operations must be
224
226
  // able to see (and replace or stop) a server from a different version.
@@ -227,7 +229,7 @@ const find = Effect.fnUntraced(function* (options) {
227
229
  });
228
230
  // 50ms cadence bounded at ~5s, shared by stop escalation and each ensure
229
231
  // discovery window.
230
- const poll = Schedule.max([Schedule.spaced("50 millis"), Schedule.recurs(100)]);
232
+ const poll = (timing) => Schedule.max([Schedule.spaced(timing.stopPollInterval), Schedule.recurs(timing.stopPollAttempts)]);
231
233
  const signal = (pid, name) => Effect.try({ try: () => process.kill(pid, name), catch: (cause) => cause }).pipe(Effect.ignore);
232
234
  const stopped = Effect.fnUntraced(function* (pid) {
233
235
  const running = yield* Effect.try({ try: () => process.kill(pid, 0), catch: () => false }).pipe(Effect.orElseSucceed(() => false));
@@ -238,22 +240,22 @@ const stopped = Effect.fnUntraced(function* (pid) {
238
240
  function same(left, right) {
239
241
  return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
240
242
  }
241
- const evict = Effect.fnUntraced(function* (info, options) {
243
+ const evict = Effect.fnUntraced(function* (info, options, timing) {
242
244
  const current = yield* read(options.file);
243
245
  if (current === undefined || !same(current, info))
244
246
  return;
245
247
  yield* signal(info.pid, "SIGTERM");
246
- const done = yield* stopped(info.pid).pipe(Effect.retry(poll), Effect.option);
248
+ const done = yield* stopped(info.pid).pipe(Effect.retry(poll(timing)), Effect.option);
247
249
  if (Option.isSome(done))
248
250
  return;
249
251
  const latest = yield* read(options.file);
250
252
  if (latest === undefined || !same(latest, info))
251
253
  return;
252
254
  yield* signal(info.pid, "SIGKILL");
253
- yield* stopped(info.pid).pipe(Effect.retry(poll));
255
+ yield* stopped(info.pid).pipe(Effect.retry(poll(timing)));
254
256
  });
255
- const kill = Effect.fnUntraced(function* (service, options) {
256
- const requested = yield* requestStop(service);
257
+ const kill = Effect.fnUntraced(function* (service, options, timing) {
258
+ const requested = yield* requestStop(service, timing.requestTimeout);
257
259
  if (requested === "rejected")
258
260
  return;
259
261
  if (requested === "unsupported") {
@@ -264,24 +266,24 @@ const kill = Effect.fnUntraced(function* (service, options) {
264
266
  return;
265
267
  yield* signal(service.info.pid, "SIGTERM");
266
268
  }
267
- const done = yield* stopped(service.info.pid).pipe(Effect.retry(poll), Effect.option);
269
+ const done = yield* stopped(service.info.pid).pipe(Effect.retry(poll(timing)), Effect.option);
268
270
  if (Option.isSome(done))
269
271
  return;
270
272
  const latest = yield* find(options);
271
273
  if (latest === undefined || !same(latest.info, service.info))
272
274
  return;
273
275
  yield* signal(service.info.pid, "SIGKILL");
274
- yield* stopped(service.info.pid).pipe(Effect.retry(poll));
276
+ yield* stopped(service.info.pid).pipe(Effect.retry(poll(timing)));
275
277
  });
276
278
  const decodeStopResponse = Schema.decodeUnknownOption(ServiceStatus.StopResponse);
277
- const requestStop = Effect.fnUntraced(function* (service) {
279
+ const requestStop = Effect.fnUntraced(function* (service, timeout = defaultEnsureTiming.requestTimeout) {
278
280
  if (service.info.id === undefined || service.legacy)
279
281
  return "unsupported";
280
282
  const response = yield* Effect.tryPromise(() => fetch(new URL("/api/service/stop", service.info.url), {
281
283
  method: "POST",
282
284
  headers: { ...headers(service.endpoint), "content-type": "application/json" },
283
285
  body: JSON.stringify({ instanceID: service.info.id }),
284
- signal: AbortSignal.timeout(2_000),
286
+ signal: AbortSignal.timeout(timeout),
285
287
  })).pipe(Effect.option, Effect.map(Option.getOrUndefined));
286
288
  if (response === undefined || response.status === 404 || response.status === 405)
287
289
  return "unsupported";
@@ -477,6 +477,7 @@ export function make(options) {
477
477
  interrupt: (input, requestOptions) => request({
478
478
  method: "POST",
479
479
  path: `/api/session/${encodeURIComponent(input.sessionID)}/interrupt`,
480
+ query: { continue: input["continue"] },
480
481
  successStatus: 204,
481
482
  declaredStatuses: [404, 400, 401],
482
483
  empty: true,
@@ -5433,6 +5433,9 @@ export type SessionInterruptInput = {
5433
5433
  readonly sessionID: {
5434
5434
  readonly sessionID: string;
5435
5435
  }["sessionID"];
5436
+ readonly continue?: {
5437
+ readonly continue?: boolean | undefined;
5438
+ }["continue"];
5436
5439
  };
5437
5440
  export type SessionInterruptOutput = void;
5438
5441
  export type SessionBackgroundInput = {
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { spawn } from "node:child_process";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { defaultEnsureTiming, ensureTiming } from "../service-timing.js";
5
6
  export * from "../service.js";
6
7
  /** Discover a healthy, compatible local service without starting one. */
7
8
  export async function discover(options = {}) {
@@ -17,12 +18,13 @@ async function discoverLocal(options) {
17
18
  }
18
19
  /** Ensure a healthy, compatible local service is running. */
19
20
  export async function ensure(options = {}) {
20
- const deadline = Date.now() + 120_000;
21
+ const timing = ensureTiming(options);
22
+ const deadline = Date.now() + timing.promiseTimeout;
21
23
  const contenders = new Set();
22
24
  let timeouts;
23
25
  let announced = false;
24
26
  let lastSpawn = 0;
25
- let spawnDelay = 5_000;
27
+ let spawnDelay = timing.spawnDelay;
26
28
  const announce = (reason, previousVersion) => {
27
29
  if (announced)
28
30
  return;
@@ -49,7 +51,7 @@ export async function ensure(options = {}) {
49
51
  while (true) {
50
52
  if (Date.now() >= deadline)
51
53
  throw new Error("Timed out waiting for the background service to start");
52
- const registration = await registered(options.file, true);
54
+ const registration = await registered(options.file, true, timing.requestTimeout);
53
55
  if (registration.timedOut && registration.info !== undefined) {
54
56
  timeouts = {
55
57
  info: registration.info,
@@ -57,7 +59,7 @@ export async function ensure(options = {}) {
57
59
  };
58
60
  if (timeouts.count >= 3) {
59
61
  announce("missing");
60
- await evict(registration.info, options);
62
+ await evict(registration.info, options, timing);
61
63
  timeouts = undefined;
62
64
  lastSpawn = Date.now() - spawnDelay;
63
65
  }
@@ -65,7 +67,7 @@ export async function ensure(options = {}) {
65
67
  else
66
68
  timeouts = undefined;
67
69
  if (registration.service !== undefined) {
68
- spawnDelay = 5_000;
70
+ spawnDelay = timing.spawnDelay;
69
71
  const service = registration.service;
70
72
  const compatible = !service.legacy && (options.version === undefined || service.version === options.version);
71
73
  if (compatible && service.state === "ready")
@@ -74,7 +76,7 @@ export async function ensure(options = {}) {
74
76
  throw new Error("Background service failed to start");
75
77
  if (!compatible) {
76
78
  announce("version-mismatch", service.version);
77
- await kill(service, options).catch(() => undefined);
79
+ await kill(service, options, timing).catch(() => undefined);
78
80
  lastSpawn = 0;
79
81
  }
80
82
  }
@@ -84,7 +86,7 @@ export async function ensure(options = {}) {
84
86
  const finished = [...contenders].filter(contenderFinished);
85
87
  const failure = finished.map(contenderFailure).find((error) => error !== undefined);
86
88
  if (finished.some((item) => item.child.exitCode === 0)) {
87
- spawnDelay = Math.min(spawnDelay * 2, 30_000);
89
+ spawnDelay = Math.min(spawnDelay * 2, timing.maxSpawnDelay);
88
90
  }
89
91
  finished.forEach((item) => contenders.delete(item));
90
92
  if (failure !== undefined && contenders.size === 0)
@@ -96,7 +98,7 @@ export async function ensure(options = {}) {
96
98
  lastSpawn = Date.now();
97
99
  }
98
100
  }
99
- await delay(1_000);
101
+ await delay(timing.pollInterval);
100
102
  }
101
103
  }
102
104
  function contenderFailure(contender) {
@@ -116,7 +118,7 @@ function contenderFinished(contender) {
116
118
  export async function stop(options = {}) {
117
119
  const existing = await find(options);
118
120
  if (existing !== undefined)
119
- await kill(existing, options);
121
+ await kill(existing, options, defaultEnsureTiming);
120
122
  }
121
123
  function fallback() {
122
124
  return join(process.env["XDG_STATE_HOME"] ?? join(homedir(), ".local", "state"), "opencode", "service.json");
@@ -143,14 +145,14 @@ async function read(file) {
143
145
  async function probe(info, allowLegacy = false) {
144
146
  return (await probeResult(info, allowLegacy)).service;
145
147
  }
146
- async function probeResult(info, allowLegacy = false) {
148
+ async function probeResult(info, allowLegacy = false, timeout = defaultEnsureTiming.requestTimeout) {
147
149
  const endpoint = {
148
150
  url: info.url,
149
151
  auth: info.password === undefined
150
152
  ? undefined
151
153
  : { type: "basic", username: "opencode", password: info.password },
152
154
  };
153
- const signal = AbortSignal.timeout(2_000);
155
+ const signal = AbortSignal.timeout(timeout);
154
156
  const result = await fetch(new URL("/api/health", info.url), {
155
157
  headers: headers(endpoint),
156
158
  signal,
@@ -187,11 +189,11 @@ async function probeResult(info, allowLegacy = false) {
187
189
  timedOut: false,
188
190
  };
189
191
  }
190
- async function registered(file, allowLegacy = false) {
192
+ async function registered(file, allowLegacy = false, timeout) {
191
193
  const info = await read(file);
192
194
  if (info === undefined)
193
195
  return { info: undefined, service: undefined, timedOut: false };
194
- return { info, ...(await probeResult(info, allowLegacy)) };
196
+ return { info, ...(await probeResult(info, allowLegacy, timeout)) };
195
197
  }
196
198
  async function find(options) {
197
199
  return (await registered(options.file, true)).service;
@@ -211,34 +213,34 @@ function stopped(pid) {
211
213
  return true;
212
214
  }
213
215
  }
214
- async function waitUntilStopped(pid) {
215
- for (let attempt = 0; attempt <= 100; attempt++) {
216
+ async function waitUntilStopped(pid, timing) {
217
+ for (let attempt = 0; attempt <= timing.stopPollAttempts; attempt++) {
216
218
  if (stopped(pid))
217
219
  return true;
218
- if (attempt < 100)
219
- await delay(50);
220
+ if (attempt < timing.stopPollAttempts)
221
+ await delay(timing.stopPollInterval);
220
222
  }
221
223
  return false;
222
224
  }
223
225
  function same(left, right) {
224
226
  return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
225
227
  }
226
- async function evict(info, options) {
228
+ async function evict(info, options, timing) {
227
229
  const current = await read(options.file);
228
230
  if (current === undefined || !same(current, info))
229
231
  return;
230
232
  signal(info.pid, "SIGTERM");
231
- if (await waitUntilStopped(info.pid))
233
+ if (await waitUntilStopped(info.pid, timing))
232
234
  return;
233
235
  const latest = await read(options.file);
234
236
  if (latest === undefined || !same(latest, info))
235
237
  return;
236
238
  signal(info.pid, "SIGKILL");
237
- if (!(await waitUntilStopped(info.pid)))
239
+ if (!(await waitUntilStopped(info.pid, timing)))
238
240
  throw new Error(`Server process ${info.pid} is still running`);
239
241
  }
240
- async function kill(service, options) {
241
- const requested = await requestStop(service);
242
+ async function kill(service, options, timing) {
243
+ const requested = await requestStop(service, timing.requestTimeout);
242
244
  if (requested === "rejected")
243
245
  return;
244
246
  if (requested === "unsupported") {
@@ -247,23 +249,23 @@ async function kill(service, options) {
247
249
  return;
248
250
  signal(service.info.pid, "SIGTERM");
249
251
  }
250
- if (await waitUntilStopped(service.info.pid))
252
+ if (await waitUntilStopped(service.info.pid, timing))
251
253
  return;
252
254
  const latest = await find(options);
253
255
  if (latest === undefined || !same(latest.info, service.info))
254
256
  return;
255
257
  signal(service.info.pid, "SIGKILL");
256
- if (!(await waitUntilStopped(service.info.pid)))
258
+ if (!(await waitUntilStopped(service.info.pid, timing)))
257
259
  throw new Error(`Server process ${service.info.pid} is still running`);
258
260
  }
259
- async function requestStop(service) {
261
+ async function requestStop(service, timeout = defaultEnsureTiming.requestTimeout) {
260
262
  if (service.info.id === undefined || service.legacy)
261
263
  return "unsupported";
262
264
  const response = await fetch(new URL("/api/service/stop", service.info.url), {
263
265
  method: "POST",
264
266
  headers: { ...headers(service.endpoint), "content-type": "application/json" },
265
267
  body: JSON.stringify({ instanceID: service.info.id }),
266
- signal: AbortSignal.timeout(2_000),
268
+ signal: AbortSignal.timeout(timeout),
267
269
  }).catch(() => undefined);
268
270
  if (response === undefined || response.status === 404 || response.status === 405)
269
271
  return "unsupported";
@@ -0,0 +1,13 @@
1
+ export type EnsureTiming = {
2
+ readonly pollInterval: number;
3
+ readonly attempts: number;
4
+ readonly requestTimeout: number;
5
+ readonly spawnDelay: number;
6
+ readonly maxSpawnDelay: number;
7
+ readonly promiseTimeout: number;
8
+ readonly stopPollInterval: number;
9
+ readonly stopPollAttempts: number;
10
+ };
11
+ export declare const defaultEnsureTiming: EnsureTiming;
12
+ export declare function ensureTiming(options: object): EnsureTiming;
13
+ export declare function withEnsureTiming<A extends object>(options: A, overrides: Partial<EnsureTiming>): A;
@@ -0,0 +1,19 @@
1
+ const timings = new WeakMap();
2
+ export const defaultEnsureTiming = {
3
+ pollInterval: 1_000,
4
+ attempts: 120,
5
+ requestTimeout: 2_000,
6
+ spawnDelay: 5_000,
7
+ maxSpawnDelay: 30_000,
8
+ promiseTimeout: 120_000,
9
+ stopPollInterval: 50,
10
+ stopPollAttempts: 100,
11
+ };
12
+ export function ensureTiming(options) {
13
+ return timings.get(options) ?? defaultEnsureTiming;
14
+ }
15
+ // Keep test timing out of the public lifecycle option types.
16
+ export function withEnsureTiming(options, overrides) {
17
+ timings.set(options, { ...defaultEnsureTiming, ...overrides });
18
+ return options;
19
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode-ai/client",
4
- "version": "0.0.0-next-17239",
4
+ "version": "0.0.0-next-17247",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -53,8 +53,8 @@
53
53
  "typecheck": "tsgo --noEmit"
54
54
  },
55
55
  "dependencies": {
56
- "@opencode-ai/schema": "0.0.0-next-17239",
57
- "@opencode-ai/protocol": "0.0.0-next-17239"
56
+ "@opencode-ai/schema": "0.0.0-next-17247",
57
+ "@opencode-ai/protocol": "0.0.0-next-17247"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "effect": "4.0.0-beta.101"
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@effect/platform-node": "4.0.0-beta.101",
69
- "@opencode-ai/httpapi-codegen": "0.0.0-next-17239",
69
+ "@opencode-ai/httpapi-codegen": "0.0.0-next-17247",
70
70
  "@tsconfig/bun": "1.0.9",
71
71
  "@types/bun": "1.3.13",
72
72
  "@typescript/native-preview": "7.0.0-dev.20251207.1",