@codeworksh/cli 0.5.0-canary.69.1.g5be1ef81 → 0.5.0-canary.71.1.g8233e4eb

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 (84) hide show
  1. package/README.md +27 -2
  2. package/{acp-BCeZqkaQ.mjs → acp-BTtByOlX.mjs} +92 -42
  3. package/acp-BTtByOlX.mjs.map +1 -0
  4. package/{add-Dzsv-fKd.mjs → add-Dm3RaxB1.mjs} +5 -5
  5. package/add-Dm3RaxB1.mjs.map +1 -0
  6. package/{catalog-C1G3pzHZ.mjs → catalog-Do2eDgti.mjs} +2 -2
  7. package/{catalog-C1G3pzHZ.mjs.map → catalog-Do2eDgti.mjs.map} +1 -1
  8. package/{check-DhojkK_E.mjs → check-BdfxwBaL.mjs} +4 -4
  9. package/{check-DhojkK_E.mjs.map → check-BdfxwBaL.mjs.map} +1 -1
  10. package/{entries-KpYvZdpi.mjs → entries-C6AeEOS1.mjs} +3 -3
  11. package/{entries-KpYvZdpi.mjs.map → entries-C6AeEOS1.mjs.map} +1 -1
  12. package/{error-BMI81XvR.mjs → error-CSeYCa9R.mjs} +195 -89
  13. package/error-CSeYCa9R.mjs.map +1 -0
  14. package/fork-CAu8PdA3.mjs +32 -0
  15. package/fork-CAu8PdA3.mjs.map +1 -0
  16. package/{identity-BfTAc3aC.mjs → identity-CpsvMaTx.mjs} +2 -2
  17. package/{identity-BfTAc3aC.mjs.map → identity-CpsvMaTx.mjs.map} +1 -1
  18. package/index.mjs +32 -25
  19. package/index.mjs.map +1 -1
  20. package/{install-BLRDBzp1.mjs → install-D5kkATcv.mjs} +4 -4
  21. package/{install-BLRDBzp1.mjs.map → install-D5kkATcv.mjs.map} +1 -1
  22. package/{link--UnM2qnT.mjs → link-CsfmcRa8.mjs} +3 -3
  23. package/{link--UnM2qnT.mjs.map → link-CsfmcRa8.mjs.map} +1 -1
  24. package/list-C60d6ifJ.mjs +36 -0
  25. package/list-C60d6ifJ.mjs.map +1 -0
  26. package/{list-9fVqelid.mjs → list-CHIzxyZx.mjs} +4 -4
  27. package/{list-9fVqelid.mjs.map → list-CHIzxyZx.mjs.map} +1 -1
  28. package/{list-BE4xSEe3.mjs → list-CakHFl9C.mjs} +4 -4
  29. package/{list-BE4xSEe3.mjs.map → list-CakHFl9C.mjs.map} +1 -1
  30. package/{login-CRwxi5WD.mjs → login-BaUYCO2G.mjs} +4 -4
  31. package/{login-CRwxi5WD.mjs.map → login-BaUYCO2G.mjs.map} +1 -1
  32. package/{logout-C89X0B9M.mjs → logout-BAks2xS7.mjs} +4 -4
  33. package/{logout-C89X0B9M.mjs.map → logout-BAks2xS7.mjs.map} +1 -1
  34. package/package-CmagAGZz.mjs +6 -0
  35. package/package-CmagAGZz.mjs.map +1 -0
  36. package/package.json +4 -4
  37. package/{providers-BrXJvY2P.mjs → providers-DVh6hhLU.mjs} +4 -4
  38. package/{providers-BrXJvY2P.mjs.map → providers-DVh6hhLU.mjs.map} +1 -1
  39. package/{refresh-D0wBP2Go.mjs → refresh-CNQBX5NK.mjs} +4 -4
  40. package/{refresh-D0wBP2Go.mjs.map → refresh-CNQBX5NK.mjs.map} +1 -1
  41. package/{refresh--j0Pj4Cw.mjs → refresh-tj3xbNRY.mjs} +4 -4
  42. package/{refresh--j0Pj4Cw.mjs.map → refresh-tj3xbNRY.mjs.map} +1 -1
  43. package/relink-BlRMYOra.mjs +23 -0
  44. package/relink-BlRMYOra.mjs.map +1 -0
  45. package/{reload-DgPawyzr.mjs → reload-h_2WARQN.mjs} +3 -3
  46. package/{reload-DgPawyzr.mjs.map → reload-h_2WARQN.mjs.map} +1 -1
  47. package/{remove-DfFme5w2.mjs → remove-CVbXLNTb.mjs} +4 -4
  48. package/{remove-DfFme5w2.mjs.map → remove-CVbXLNTb.mjs.map} +1 -1
  49. package/{restart-JPAbjFUh.mjs → restart-B0XSXYtj.mjs} +4 -4
  50. package/{restart-JPAbjFUh.mjs.map → restart-B0XSXYtj.mjs.map} +1 -1
  51. package/{run-CVRkm_Iz.mjs → run-CRV3jJ2d.mjs} +26 -17
  52. package/run-CRV3jJ2d.mjs.map +1 -0
  53. package/{runtime-B43doHjV.mjs → runtime-Bg4mc9BB.mjs} +59 -3
  54. package/runtime-Bg4mc9BB.mjs.map +1 -0
  55. package/{serve-DT8-nVCx.mjs → serve-XrW3PuIZ.mjs} +101 -73
  56. package/serve-XrW3PuIZ.mjs.map +1 -0
  57. package/{service-Cv-MLS-9.mjs → service-D_vwPPPh.mjs} +3 -3
  58. package/{service-Cv-MLS-9.mjs.map → service-D_vwPPPh.mjs.map} +1 -1
  59. package/{settings-BfmoL27m.mjs → settings-DcwstDUm.mjs} +2 -2
  60. package/{settings-BfmoL27m.mjs.map → settings-DcwstDUm.mjs.map} +1 -1
  61. package/{shared-CnuCjvcG.mjs → shared-D_3E8bu_.mjs} +3 -3
  62. package/{shared-CnuCjvcG.mjs.map → shared-D_3E8bu_.mjs.map} +1 -1
  63. package/{start-Qb1Ea9oT.mjs → start-CJ4XeLwV.mjs} +4 -4
  64. package/{start-Qb1Ea9oT.mjs.map → start-CJ4XeLwV.mjs.map} +1 -1
  65. package/{status-VGKqsdkQ.mjs → status-6NyZu0f_.mjs} +4 -4
  66. package/{status-VGKqsdkQ.mjs.map → status-6NyZu0f_.mjs.map} +1 -1
  67. package/{status-DVzFDoek.mjs → status-DBzQVkL2.mjs} +4 -4
  68. package/{status-DVzFDoek.mjs.map → status-DBzQVkL2.mjs.map} +1 -1
  69. package/{stop-mO7eg0nG.mjs → stop-BY1c7cUX.mjs} +4 -4
  70. package/{stop-mO7eg0nG.mjs.map → stop-BY1c7cUX.mjs.map} +1 -1
  71. package/thread-CEYjLavT.mjs +15 -0
  72. package/thread-CEYjLavT.mjs.map +1 -0
  73. package/{tui-DVVaevMF.mjs → tui-CBUtCHzR.mjs} +5 -5
  74. package/{tui-DVVaevMF.mjs.map → tui-CBUtCHzR.mjs.map} +1 -1
  75. package/{update-D0XPVfJu.mjs → update-CORsF38E.mjs} +4 -4
  76. package/{update-D0XPVfJu.mjs.map → update-CORsF38E.mjs.map} +1 -1
  77. package/acp-BCeZqkaQ.mjs.map +0 -1
  78. package/add-Dzsv-fKd.mjs.map +0 -1
  79. package/error-BMI81XvR.mjs.map +0 -1
  80. package/package-D9XCaBr1.mjs +0 -6
  81. package/package-D9XCaBr1.mjs.map +0 -1
  82. package/run-CVRkm_Iz.mjs.map +0 -1
  83. package/runtime-B43doHjV.mjs.map +0 -1
  84. package/serve-DT8-nVCx.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { Deferred, Duration, Effect, Layer, Predicate, Schema, Stream } from "effect";
2
2
  import { Model } from "@codeworksh/aikit";
3
- import { Control, Event, EventList, EventSchema, Location, ModelCatalog, Plugin, Project, PromptSchema, Runner, Sandbox, SandboxError, Session, SessionStore, Settings, optional } from "@codeworksh/harness/effect";
3
+ import { Control, Event, EventList, EventSchema, Location, ModelCatalog, Plugin, Project, PromptSchema, Runner, Sandbox, SandboxError, Session, SessionStore, Settings, ThreadSchema, ThreadStore, optional } from "@codeworksh/harness/effect";
4
4
  import { SandboxProvider } from "@codeworksh/harness/sandbox";
5
5
  import { NodeSocket } from "@effect/platform-node";
6
6
  import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/rpc";
@@ -22,6 +22,8 @@ const EventEnvelope = Schema.Struct({
22
22
  metadata: optional(Schema.Record(Schema.String, Schema.String)),
23
23
  data: Schema.Unknown
24
24
  });
25
+ /** Whether an event belongs to a thread: every thread-scoped event names its `threadId`. */
26
+ const ofThread = (threadId) => (event) => Predicate.hasProperty(event.data, "threadId") && event.data.threadId === threadId;
25
27
  var UnknownEventTypeError = class extends Schema.TaggedError()("UnknownEventTypeError", { type: Schema.String }) {};
26
28
  /** The transport's own readiness frame; it has no meaning inside the harness. */
27
29
  const Connected = EventSchema.define({
@@ -135,9 +137,9 @@ const SandboxInfo = Schema.Struct({
135
137
  createdAt: Schema.DateFromString,
136
138
  updatedAt: Schema.DateFromString
137
139
  });
140
+ /** The container: where its threads run by default, and which host project it reads. */
138
141
  const SessionInfo = Schema.Struct({
139
142
  id: Session.SessionSchema.ID,
140
- title: Schema.String,
141
143
  directory: Session.AbsolutePath,
142
144
  /** The host directory this session is anchored to. Absent when it has none. */
143
145
  hostDir: optional(Session.AbsolutePath),
@@ -147,22 +149,70 @@ const SessionInfo = Schema.Struct({
147
149
  id: Schema.String,
148
150
  name: Schema.String
149
151
  })),
152
+ /** Last activity in any of its threads, epoch milliseconds. */
153
+ updatedAt: Schema.Finite,
154
+ /** Where unaddressed prompts go. */
155
+ mainThreadId: ThreadSchema.ID
156
+ });
157
+ const ThreadOrigin = Schema.Union([
158
+ Schema.Struct({ kind: Schema.Literal("main") }),
159
+ Schema.Struct({
160
+ kind: Schema.Literal("fork"),
161
+ threadId: Schema.String,
162
+ entryId: Schema.String
163
+ }),
164
+ Schema.Struct({
165
+ kind: Schema.Literal("subagent"),
166
+ threadId: Schema.String,
167
+ callId: Schema.String,
168
+ entryId: optional(Schema.String)
169
+ })
170
+ ]);
171
+ /** Billed usage of a thread's own entries; what it inherits through a fork point counts for its source. */
172
+ const ThreadUsage = Schema.Struct({
173
+ /** Total cost in US dollars, from the catalog's prices. */
174
+ cost: Schema.Finite,
175
+ input: Schema.Int,
176
+ output: Schema.Int,
177
+ cacheRead: Schema.Int,
178
+ cacheWrite: Schema.Int
179
+ });
180
+ /** One linear transcript of a session, with the session embedded. */
181
+ const ThreadInfo = Schema.Struct({
182
+ id: ThreadSchema.ID,
183
+ origin: ThreadOrigin,
184
+ /** Absent until its first own prompt; clients show a fork's source title until then. */
185
+ title: optional(Schema.String),
150
186
  /** Last activity, epoch milliseconds. */
151
187
  updatedAt: Schema.Finite,
152
- /** The model chosen for this session; absent when settings decide. */
188
+ /** The model chosen for this thread; absent when settings decide. */
153
189
  model: optional(Schema.Struct({
154
190
  provider: Schema.String,
155
191
  id: Schema.String
156
192
  })),
157
- /** The thinking level chosen for this session; absent when settings decide. */
193
+ /** The thinking level chosen for this thread; absent when settings decide. */
158
194
  thinkingLevel: optional(Schema.Enum(Model.ThinkingLevelEnum)),
195
+ /** Where it runs instead of the session's space and directory. */
196
+ placement: optional(Schema.Struct({
197
+ spaceId: Schema.String,
198
+ directory: Session.AbsolutePath
199
+ })),
159
200
  /** Its unfinished run was cut off by restarts too often to resume on its own; the next prompt runs it. */
160
- parked: optional(Schema.Boolean)
201
+ parked: optional(Schema.Boolean),
202
+ usage: ThreadUsage,
203
+ session: SessionInfo
161
204
  });
162
- /** One message on a session's active branch, oldest first. */
205
+ /** A thread of a session: `threadId`, or the session's `main` thread when absent. */
206
+ const ThreadRef = {
207
+ sessionId: Session.SessionSchema.ID,
208
+ threadId: optional(ThreadSchema.ID)
209
+ };
210
+ /** One message in a thread's line (inherited ones first), oldest first. */
163
211
  const HistoryEntry = Schema.Struct({
164
- /** The entry id: what live events and `session.message` refer to. */
212
+ /** The entry id: what live events and `thread.message` refer to. */
165
213
  id: Schema.String,
214
+ /** The thread that wrote it: this one, or a source it was forked from. */
215
+ threadId: Schema.String,
166
216
  /** Its place in the session's durable log; live events continue from it. */
167
217
  seq: Schema.Int,
168
218
  role: Schema.Literals(["user", "assistant"]),
@@ -194,12 +244,13 @@ const LogSynced = Schema.Struct({
194
244
  aggregateId: Schema.String,
195
245
  seq: optional(Schema.Int)
196
246
  });
247
+ /** A thread's model and thinking choices: a given field replaces the stored one, `null` clears it. */
197
248
  const RuntimeConfig = Schema.Struct({
198
- model: optional(Schema.Struct({
249
+ model: optional(Schema.NullOr(Schema.Struct({
199
250
  provider: Schema.String,
200
251
  id: Schema.String
201
- })),
202
- thinkingLevel: optional(Schema.Enum(Model.ThinkingLevelEnum))
252
+ }))),
253
+ thinkingLevel: optional(Schema.NullOr(Schema.Enum(Model.ThinkingLevelEnum)))
203
254
  });
204
255
  const SandboxRef = Sandbox.Selection;
205
256
  /** A model to choose, by the catalog's id and display name, with the thinking levels it supports. */
@@ -251,9 +302,11 @@ const OAuthInfo = Schema.Struct({
251
302
  });
252
303
  const SessionErrors = Schema.Union([
253
304
  SessionStore.SessionNotFoundError,
305
+ ThreadStore.ThreadNotFoundError,
254
306
  SessionStore.SessionLinkedSpaceNotFoundError,
255
307
  SessionStore.RelinkError
256
308
  ]);
309
+ const ThreadErrors = Schema.Union([SessionStore.SessionNotFoundError, ThreadStore.ThreadNotFoundError]);
257
310
  const SandboxErrors = Schema.Union([
258
311
  SandboxError.SandboxNotFoundError,
259
312
  SandboxError.SandboxDriverNotRegisteredError,
@@ -302,8 +355,10 @@ const Api = RpcGroup.make(
302
355
  success: Schema.Void,
303
356
  error: OAuthError$1
304
357
  }),
358
+ /** A session and its `main` thread; the main thread's info, with the session embedded. */
305
359
  Rpc.make("session.create", {
306
360
  payload: {
361
+ /** The main thread's title; absent = its first prompt names it. */
307
362
  title: optional(Schema.String),
308
363
  directory: optional(Schema.String),
309
364
  /**
@@ -325,7 +380,7 @@ const Api = RpcGroup.make(
325
380
  */
326
381
  project: optional(Schema.Struct({ name: ProjectName }))
327
382
  },
328
- success: SessionInfo,
383
+ success: ThreadInfo,
329
384
  error: Schema.Union([
330
385
  SessionErrors,
331
386
  SandboxErrors,
@@ -365,39 +420,67 @@ const Api = RpcGroup.make(
365
420
  }),
366
421
  error: Schema.Union([Settings.SettingsError, Runner.ModelCatalogError])
367
422
  }),
368
- Rpc.make("session.list", {
369
- payload: {},
370
- success: Schema.Array(SessionInfo)
371
- }),
372
423
  /**
373
- * The conversation on a session's active branch: user and assistant messages, oldest first.
374
- * Other branches and the harness's own entries (compactions, summaries) are left out.
424
+ * Point an existing session at a host directory, or clear it with `null`.
425
+ *
426
+ * Not `session.relink`: that moves where the *work* happens, this changes which host directory
427
+ * the session is *anchored to*. Conflating them would recreate exactly the `cwd`/`hostDir`
428
+ * confusion the split exists to remove, which is why they sound alike and stay apart.
375
429
  */
376
- Rpc.make("session.history", {
377
- payload: { sessionId: Session.SessionSchema.ID },
378
- success: Schema.Struct({ entries: Schema.Array(HistoryEntry) }),
430
+ Rpc.make("session.link", {
431
+ payload: {
432
+ sessionId: Session.SessionSchema.ID,
433
+ /** Absent clears the anchor, leaving the session with no host directory. */
434
+ hostDir: optional(Session.AbsolutePath)
435
+ },
436
+ success: SessionInfo,
379
437
  error: SessionStore.SessionNotFoundError
380
438
  }),
381
439
  /**
382
- * Prompts admitted but not yet promoted, in admission order. Admission and promotion are
383
- * durable events, so a client following `session.log` rereads this whenever the log moves.
440
+ * Moves where the work happens. Without `threadId`, the session's default space and directory,
441
+ * which every thread without its own placement follows; with it, that thread's placement only.
442
+ * Answers with the addressed thread (`main` when absent): its `placement` and embedded session
443
+ * show where it now runs.
384
444
  */
385
- Rpc.make("session.queue", {
445
+ Rpc.make("session.relink", {
446
+ payload: {
447
+ sessionId: Session.SessionSchema.ID,
448
+ threadId: optional(ThreadSchema.ID),
449
+ sandbox: optional(SandboxRef),
450
+ directory: optional(Schema.String)
451
+ },
452
+ success: ThreadInfo,
453
+ error: Schema.Union([
454
+ SessionErrors,
455
+ SandboxErrors,
456
+ LocationErrors
457
+ ])
458
+ }),
459
+ /** Stops every thread, then removes the session with its log. `deleted` is false when it did not exist. */
460
+ Rpc.make("session.delete", {
386
461
  payload: { sessionId: Session.SessionSchema.ID },
387
- success: Schema.Struct({ entries: Schema.Array(QueuedPrompt) }),
388
- error: SessionStore.SessionNotFoundError
462
+ success: Schema.Struct({ deleted: Schema.Boolean })
463
+ }),
464
+ /** Interrupts every running thread of the session. */
465
+ Rpc.make("session.interrupt", {
466
+ payload: { sessionId: Session.SessionSchema.ID },
467
+ success: Schema.Struct({ interrupted: Schema.Boolean })
389
468
  }),
390
469
  /**
391
470
  * A session's durable events after `after` (exclusive; omitted reads from the start), then a
392
471
  * `log.synced` marker at the head read when the call began. With `follow`, each event then
393
- * streams as it commits; without it the stream ends at the marker.
472
+ * streams as it commits; without it the stream ends at the marker. Every thread of the session
473
+ * writes this one log; events name their `threadId`.
394
474
  * Ephemeral events (text and tool progress deltas) are never stored, so they are not here;
395
475
  * `event.subscribe` carries them live. To resume, pass the higher of the last event's
396
476
  * `durable.seq` and the last marker's `seq`: the marker can sit past the last event sent.
477
+ * With `threadId`, only that thread's events; the marker still names the session's head. A fork's
478
+ * own log starts at its `session.thread.forked`: what it inherits is in `thread.history`.
397
479
  */
398
480
  Rpc.make("session.log", {
399
481
  payload: {
400
482
  sessionId: Session.SessionSchema.ID,
483
+ threadId: optional(ThreadSchema.ID),
401
484
  after: optional(Schema.Int),
402
485
  follow: optional(Schema.Boolean)
403
486
  },
@@ -405,105 +488,118 @@ const Api = RpcGroup.make(
405
488
  error: Schema.Union([SessionStore.SessionNotFoundError, EncodingError]),
406
489
  stream: true
407
490
  }),
408
- Rpc.make("session.configure", {
409
- payload: {
410
- sessionId: Session.SessionSchema.ID,
411
- runtime: RuntimeConfig
412
- },
413
- success: SessionInfo,
414
- error: SessionStore.SessionNotFoundError
491
+ /** Threads newest first, each with its session embedded: of one session, or all. */
492
+ Rpc.make("thread.list", {
493
+ payload: { sessionId: optional(Session.SessionSchema.ID) },
494
+ success: Schema.Array(ThreadInfo)
415
495
  }),
416
- Rpc.make("session.info", {
417
- payload: { sessionId: Session.SessionSchema.ID },
418
- success: SessionInfo,
419
- error: SessionStore.SessionNotFoundError
496
+ Rpc.make("thread.info", {
497
+ payload: ThreadRef,
498
+ success: ThreadInfo,
499
+ error: ThreadErrors
420
500
  }),
421
501
  /**
422
- * Point an existing session at a host directory, or clear it with `null`.
423
- *
424
- * Not `session.relink`: that moves where the *work* happens, this changes which host directory
425
- * the session is *anchored to*. Conflating them would recreate exactly the `cwd`/`hostDir`
426
- * confusion the split exists to remove, which is why they sound alike and stay apart.
502
+ * The conversation in a thread's line: user and assistant messages, inherited ones first, oldest
503
+ * first. The harness's own entries (compactions, resets, notices) are left out.
427
504
  */
428
- Rpc.make("session.link", {
505
+ Rpc.make("thread.history", {
506
+ payload: ThreadRef,
507
+ success: Schema.Struct({ entries: Schema.Array(HistoryEntry) }),
508
+ error: ThreadErrors
509
+ }),
510
+ /**
511
+ * Prompts admitted to the thread but not yet promoted, in admission order. Admission and
512
+ * promotion are durable events, so a client following `session.log` rereads this whenever the
513
+ * log moves.
514
+ */
515
+ Rpc.make("thread.queue", {
516
+ payload: ThreadRef,
517
+ success: Schema.Struct({ entries: Schema.Array(QueuedPrompt) }),
518
+ error: ThreadErrors
519
+ }),
520
+ Rpc.make("thread.configure", {
429
521
  payload: {
430
- sessionId: Session.SessionSchema.ID,
431
- /** Absent clears the anchor, leaving the session with no host directory. */
432
- hostDir: optional(Session.AbsolutePath)
522
+ ...ThreadRef,
523
+ runtime: RuntimeConfig
433
524
  },
434
- success: SessionInfo,
435
- error: SessionStore.SessionNotFoundError
525
+ success: ThreadInfo,
526
+ error: ThreadErrors
436
527
  }),
437
- Rpc.make("session.relink", {
528
+ /**
529
+ * A new thread whose history continues from this one's line through `entryId`, which must be
530
+ * committed. `before` stops at the entry's predecessor and returns its text to edit again.
531
+ */
532
+ Rpc.make("thread.fork", {
438
533
  payload: {
439
- sessionId: Session.SessionSchema.ID,
440
- sandbox: optional(SandboxRef),
441
- directory: optional(Schema.String)
534
+ ...ThreadRef,
535
+ entryId: Schema.String,
536
+ mode: optional(Schema.Literals(["at", "before"]))
442
537
  },
443
- success: SessionInfo,
444
- error: Schema.Union([
445
- SessionErrors,
446
- SandboxErrors,
447
- LocationErrors
448
- ])
538
+ success: Schema.Struct({
539
+ thread: ThreadInfo,
540
+ text: optional(Schema.String)
541
+ }),
542
+ error: Schema.Union([ThreadErrors, ThreadStore.ForkError])
449
543
  }),
450
- Rpc.make("session.prompt", {
544
+ Rpc.make("thread.prompt", {
451
545
  payload: {
452
- sessionId: Session.SessionSchema.ID,
546
+ ...ThreadRef,
453
547
  text: Schema.String,
454
548
  delivery: optional(PromptSchema.Delivery),
455
549
  id: optional(Session.SessionMessageSchema.ID)
456
550
  },
457
- success: Schema.Void,
458
- error: Schema.Union([SessionStore.SessionNotFoundError, Control.PromptConflictError])
551
+ success: Schema.Struct({ threadId: ThreadSchema.ID }),
552
+ error: Schema.Union([ThreadErrors, Control.PromptConflictError])
459
553
  }),
460
554
  /**
461
555
  * Withdraws a queued prompt before it joins the conversation. `cancelled` is false when it had
462
556
  * already been promoted (it then stays in the conversation) or no pending prompt has that id.
463
557
  */
464
- Rpc.make("session.cancel", {
558
+ Rpc.make("thread.cancel", {
465
559
  payload: {
466
- sessionId: Session.SessionSchema.ID,
560
+ ...ThreadRef,
467
561
  messageId: Session.SessionMessageSchema.ID
468
562
  },
469
563
  success: Schema.Struct({ cancelled: Schema.Boolean }),
470
- error: SessionStore.SessionNotFoundError
564
+ error: ThreadErrors
471
565
  }),
472
566
  /**
473
567
  * Moves a queued prompt to the other lane or rewrites it, keeping its place in the queue.
474
- * `delivery: "steer"` also wakes the session, so a prompt left waiting after a stop runs now.
568
+ * `delivery: "steer"` also wakes the thread, so a prompt left waiting after a stop runs now.
475
569
  * `updated` is false when it had already been promoted or cancelled, or no pending prompt has that id.
476
570
  */
477
- Rpc.make("session.update", {
571
+ Rpc.make("thread.update", {
478
572
  payload: {
479
- sessionId: Session.SessionSchema.ID,
573
+ ...ThreadRef,
480
574
  messageId: Session.SessionMessageSchema.ID,
481
575
  delivery: optional(PromptSchema.Delivery),
482
576
  text: optional(Schema.String)
483
577
  },
484
578
  success: Schema.Struct({ updated: Schema.Boolean }),
485
- error: SessionStore.SessionNotFoundError
579
+ error: ThreadErrors
486
580
  }),
487
- Rpc.make("session.interrupt", {
488
- payload: { sessionId: Session.SessionSchema.ID },
489
- success: Schema.Struct({ interrupted: Schema.Boolean })
581
+ Rpc.make("thread.interrupt", {
582
+ payload: ThreadRef,
583
+ success: Schema.Struct({ interrupted: Schema.Boolean }),
584
+ error: ThreadErrors
490
585
  }),
491
586
  /**
492
- * Whether a prompt materialized into the conversation. An admitted prompt
493
- * that is still queued has no entry yet, so this is the projection a client
494
- * reconciles against when the volatile stream left it unsure
587
+ * Whether a prompt materialized into the thread's conversation. An admitted prompt that is still
588
+ * queued has no entry yet, so this is the projection a client reconciles against when the
589
+ * volatile stream left it unsure.
495
590
  */
496
- Rpc.make("session.message", {
591
+ Rpc.make("thread.message", {
497
592
  payload: {
498
- sessionId: Session.SessionSchema.ID,
593
+ ...ThreadRef,
499
594
  messageId: Session.SessionMessageSchema.ID
500
595
  },
501
- success: Schema.Struct({ found: Schema.Boolean })
596
+ success: Schema.Struct({ found: Schema.Boolean }),
597
+ error: ThreadErrors
502
598
  }),
503
- Rpc.make("session.wait", {
504
- payload: { sessionId: Session.SessionSchema.ID },
599
+ Rpc.make("thread.wait", {
600
+ payload: ThreadRef,
505
601
  success: Schema.Void,
506
- error: SessionStore.SessionNotFoundError
602
+ error: ThreadErrors
507
603
  }),
508
604
  /**
509
605
  * Re-import every configured plugin, for the whole server.
@@ -548,8 +644,12 @@ const Api = RpcGroup.make(
548
644
  success: Schema.Void,
549
645
  error: SandboxErrors
550
646
  }),
647
+ /**
648
+ * Every event the server publishes, live, after a `server.connected` frame. With `threadId`,
649
+ * only the events naming that thread; the frame still comes first.
650
+ */
551
651
  Rpc.make("event.subscribe", {
552
- payload: {},
652
+ payload: { threadId: optional(ThreadSchema.ID) },
553
653
  success: EventEnvelope,
554
654
  error: Schema.Union([Event.SubscriptionOverflowError, EncodingError]),
555
655
  stream: true
@@ -568,7 +668,7 @@ var ExecutionError = class extends Schema.TaggedError()("Client.ExecutionError",
568
668
  /**
569
669
  * Runs one prompt against a server and renders its output.
570
670
  *
571
- * Sessions are shared, so the stream carries other clients' work too and every
671
+ * Threads are shared, so the stream carries other clients' work too and every
572
672
  * terminal event has to be attributed before it is believed. A terminal that
573
673
  * arrives before our own prompt is promoted belongs to somebody else's drain:
574
674
  * it is remembered rather than raised, and forgotten the moment our prompt is
@@ -594,9 +694,9 @@ const run = Effect.fn("Client.run")(function* (rpc, input, render) {
594
694
  ...event.data.error.status === void 0 ? {} : { status: event.data.error.status }
595
695
  });
596
696
  };
597
- const consume = rpc["event.subscribe"]({}).pipe(Stream.mapEffect(Effect.fnUntraced(function* (envelope) {
697
+ const consume = rpc["event.subscribe"]({ threadId: input.threadId }).pipe(Stream.mapEffect(Effect.fnUntraced(function* (envelope) {
598
698
  if (envelope.type === Connected.type) {
599
- yield* rpc["session.prompt"]({
699
+ yield* rpc["thread.prompt"]({
600
700
  ...input,
601
701
  id,
602
702
  delivery: "followUp"
@@ -604,7 +704,7 @@ const run = Effect.fn("Client.run")(function* (rpc, input, render) {
604
704
  return;
605
705
  }
606
706
  const event = yield* decode$1(envelope).pipe(Effect.catchTag("UnknownEventTypeError", () => Effect.void));
607
- if (event === void 0 || !Predicate.hasProperty(event.data, "sessionId") || event.data.sessionId !== input.sessionId) return;
707
+ if (event === void 0) return;
608
708
  if (Schema.is(EventList.PromptAdmitted)(event) && event.data.messageId === id) admitted = true;
609
709
  if (Schema.is(EventList.Prompted)(event) && event.data.messageId === id) {
610
710
  promoted = true;
@@ -626,8 +726,12 @@ const run = Effect.fn("Client.run")(function* (rpc, input, render) {
626
726
  yield* render(event);
627
727
  })), Stream.takeUntil(() => settled), Stream.runDrain);
628
728
  let abandoned = false;
629
- const reconcile = Deferred.await(stalled).pipe(Effect.andThen(rpc["session.wait"]({ sessionId: input.sessionId })), Effect.andThen(rpc["session.message"]({
729
+ const reconcile = Deferred.await(stalled).pipe(Effect.andThen(rpc["thread.wait"]({
730
+ sessionId: input.sessionId,
731
+ threadId: input.threadId
732
+ })), Effect.andThen(rpc["thread.message"]({
630
733
  sessionId: input.sessionId,
734
+ threadId: input.threadId,
631
735
  messageId: id
632
736
  })), Effect.andThen(({ found }) => found ? Effect.never : Effect.sync(() => {
633
737
  abandoned = true;
@@ -683,6 +787,8 @@ const usage = (summary, columns) => {
683
787
  return `${divider("usage", columns)}${row("model", model)}${row("tokens", `${number.format(summary.input)} input · ${number.format(summary.output)} output${reasoning} · ${number.format(summary.totalTokens)} total`)}${row("cache", `${number.format(summary.cacheRead)} read · ${number.format(summary.cacheWrite)} write`)}${row("cost", `${formatCost(summary.cost)} · ${turns}`)}`;
684
788
  };
685
789
  const writeOut = (value) => Effect.sync(() => void process.stdout.write(value));
790
+ /** A value in its wire form, as indented JSON. */
791
+ const writeJson = (schema, value) => Schema.encodeEffect(schema)(value).pipe(Effect.orDie, Effect.flatMap((encoded) => writeOut(`${JSON.stringify(encoded, null, " ")}\n`)));
686
792
  const writeError = (value) => Effect.sync(() => void process.stderr.write(value));
687
793
  const terminalColumns = () => process.stderr.isTTY ? process.stderr.columns ?? 72 : 72;
688
794
  //#endregion
@@ -844,6 +950,6 @@ const reportFailure = (error) => writeError(renderError(error)).pipe(Effect.andT
844
950
  process.exitCode = 1;
845
951
  })));
846
952
  //#endregion
847
- export { path as _, addUsage as a, Core as b, terminalColumns as c, writeOut as d, layer as f, OAuthError$1 as g, Api as h, reportFailure as i, usage as l, run as m, OAuthError as n, emptyUsage as o, make as p, renderError as r, header as s, InvalidInputError as t, writeError as u, decode as v, encode as x, Connected as y };
953
+ export { encode as C, Core as S, OAuthError$1 as _, addUsage as a, decode as b, terminalColumns as c, writeJson as d, writeOut as f, Api as g, run as h, reportFailure as i, usage as l, make as m, OAuthError as n, emptyUsage as o, layer as p, renderError as r, header as s, InvalidInputError as t, writeError as u, ThreadInfo as v, ofThread as w, Connected as x, path as y };
848
954
 
849
- //# sourceMappingURL=error-BMI81XvR.mjs.map
955
+ //# sourceMappingURL=error-CSeYCa9R.mjs.map