@linkshell/wire 0.1.1 → 0.1.2

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.
package/src/rpc.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  terminalInfoSchema,
8
8
  sessionDriverSchema,
9
9
  sessionSummarySchema,
10
+ subagentInfoSchema,
10
11
  } from "./model.js";
11
12
  import { contentBlockSchema, sessionEventSchema } from "./updates.js";
12
13
 
@@ -60,6 +61,9 @@ export const directoryEntrySchema = z.object({
60
61
  path: z.string(),
61
62
  /** Looks like a code project (has .git, package.json, …). */
62
63
  project: z.boolean().optional(),
64
+ /** A file (only listed when asked for); everything else is a directory. */
65
+ file: z.boolean().optional(),
66
+ size: z.number().int().optional(),
63
67
  });
64
68
  export type DirectoryEntry = z.infer<typeof directoryEntrySchema>;
65
69
 
@@ -99,8 +103,47 @@ export const methods = {
99
103
  sessionId: z.string().min(1),
100
104
  /** Last seq the client already has; 0 for a fresh subscription. */
101
105
  fromSeq: z.number().int().nonnegative(),
106
+ /**
107
+ * The client loads pictures when it shows them (`sessions.image`): they
108
+ * arrive as image blocks with a `linkshell-event:` uri and no data.
109
+ * Without it, large pictures are replaced by a text note.
110
+ */
111
+ lazyImages: z.boolean().optional(),
102
112
  }),
103
- result: z.object({ session: sessionSummarySchema }),
113
+ /**
114
+ * A fresh subscription starts at the session's latest turns, not at its
115
+ * beginning: `startSeq` is the seq before the first event sent (0 when the
116
+ * whole log was). `sessions.history` pages back from there.
117
+ */
118
+ result: z.object({ session: sessionSummarySchema, startSeq: z.number().int().nonnegative().optional() }),
119
+ },
120
+ /** The page of events before `beforeSeq` (whole turns), oldest first; `startSeq` 0 means that was the beginning. */
121
+ "sessions.history": {
122
+ params: z.object({
123
+ sessionId: z.string().min(1),
124
+ beforeSeq: z.number().int().positive(),
125
+ lazyImages: z.boolean().optional(),
126
+ }),
127
+ result: z.object({ events: z.array(sessionEventSchema), startSeq: z.number().int().nonnegative() }),
128
+ },
129
+ /** The sub-agents this session started, newest first, wherever in its history they are. */
130
+ "sessions.subagents": {
131
+ params: z.object({ sessionId: z.string().min(1) }),
132
+ result: z.object({ subagents: z.array(subagentInfoSchema) }),
133
+ },
134
+ /**
135
+ * One sub-agent's conversation: the call that started it and the events
136
+ * under it (the latest 2000), oldest first. Later ones arrive on the
137
+ * session's own subscription, with this call as `parentToolCallId`.
138
+ */
139
+ "sessions.subagent": {
140
+ params: z.object({ sessionId: z.string().min(1), toolCallId: z.string().min(1), lazyImages: z.boolean().optional() }),
141
+ result: z.object({ events: z.array(sessionEventSchema) }),
142
+ },
143
+ /** The picture behind a `linkshell-event:` uri. */
144
+ "sessions.image": {
145
+ params: z.object({ sessionId: z.string().min(1), uri: z.string().min(1) }),
146
+ result: z.object({ mimeType: z.string(), data: z.string() }),
104
147
  },
105
148
  "sessions.unsubscribe": {
106
149
  params: z.object({ sessionId: z.string().min(1) }),
@@ -112,8 +155,15 @@ export const methods = {
112
155
  /** Client-generated idempotency key; a retried send is not delivered twice. */
113
156
  clientMessageId: z.string().min(1),
114
157
  content: z.array(contentBlockSchema).min(1),
158
+ /**
159
+ * What to do while a turn is running. "queue": hold the message (it
160
+ * shows in SessionSummary.queue, where it can be reordered, taken back
161
+ * or sent at once) and send it when the turn ends. Unset: the agent's
162
+ * own way — taken into the running turn where it can be, queued where not.
163
+ */
164
+ whenBusy: z.enum(["queue"]).optional(),
115
165
  }),
116
- /** queued: the agent can't take input mid-turn; it is sent when the turn ends. */
166
+ /** queued: held until the running turn ends (see `whenBusy`, `sessions.sendQueued`). */
117
167
  result: z.object({ delivery: z.enum(["started", "steered", "queued", "duplicate"]) }),
118
168
  },
119
169
  "sessions.cancel": {
@@ -136,6 +186,20 @@ export const methods = {
136
186
  }),
137
187
  result: empty,
138
188
  },
189
+ /**
190
+ * Sends a queued message (the first, or `clientMessageId`) now instead of
191
+ * when the running turn ends: taken into that turn where the agent allows
192
+ * it, otherwise the turn is stopped first, wherever it runs.
193
+ */
194
+ "sessions.sendQueued": {
195
+ params: z.object({ sessionId: z.string().min(1), clientMessageId: z.string().min(1).optional() }),
196
+ result: empty,
197
+ },
198
+ /** Puts the queued messages in this order (ids not named keep theirs, after the named ones). */
199
+ "sessions.reorderQueue": {
200
+ params: z.object({ sessionId: z.string().min(1), clientMessageIds: z.array(z.string().min(1)) }),
201
+ result: empty,
202
+ },
139
203
  /** Drops a message still waiting in the host's queue (see SessionSummary.queue). */
140
204
  "sessions.unqueue": {
141
205
  params: z.object({ sessionId: z.string().min(1), clientMessageId: z.string().min(1) }),
@@ -206,6 +270,12 @@ export const methods = {
206
270
  },
207
271
  // ── Gateway: reach this computer from anywhere ──
208
272
  "gateway.status": { params: empty, result: gatewayStatusSchema },
273
+ /**
274
+ * Applies the computer's current gateway choice and account (local only):
275
+ * after `linkshell login` / `logout` or `linkshell host --gateway`, a running
276
+ * host connects, moves or disconnects without a restart.
277
+ */
278
+ "gateway.refresh": { params: empty, result: gatewayStatusSchema },
209
279
  /**
210
280
  * Opens a pairing window (local only). `link` is what the QR encodes; the
211
281
  * code can be typed instead. `pairing.done` follows when a device pairs.
@@ -218,17 +288,34 @@ export const methods = {
218
288
  // ── Directories: pick where a new session runs ──
219
289
  /** Subdirectories of `path` (default: the home directory). */
220
290
  "fs.list": {
221
- params: z.object({ path: z.string().optional(), hidden: z.boolean().default(false) }),
291
+ params: z.object({
292
+ path: z.string().optional(),
293
+ hidden: z.boolean().default(false),
294
+ /** Also list the directory's files (after its subdirectories), to browse a project. */
295
+ files: z.boolean().default(false),
296
+ }),
222
297
  result: z.object({
223
298
  path: z.string(),
224
299
  parent: z.string().optional(),
225
300
  home: z.string(),
226
301
  entries: z.array(directoryEntrySchema),
302
+ /** The directory holds more than a listing carries (1000 entries). */
303
+ truncated: z.boolean().optional(),
227
304
  }),
228
305
  },
229
- /** A file's contents, for viewing: text as-is, images as base64, anything else refused. */
306
+ /**
307
+ * A file's contents, for viewing. Text comes a part at a time: up to
308
+ * `maxBytes` from `offset`, with `nextOffset` when more follows. A picture
309
+ * comes whole, as base64. A binary file is only described, and a file too
310
+ * big to look at on a phone (text over 20 MB, a picture over 10 MB) is
311
+ * refused with `too_large`.
312
+ */
230
313
  "fs.read": {
231
- params: z.object({ path: z.string().min(1), maxBytes: z.number().int().min(1).max(8_000_000).default(2_000_000) }),
314
+ params: z.object({
315
+ path: z.string().min(1),
316
+ offset: z.number().int().nonnegative().default(0),
317
+ maxBytes: z.number().int().min(1).max(2_000_000).default(512_000),
318
+ }),
232
319
  result: z.object({
233
320
  path: z.string(),
234
321
  size: z.number().int(),
@@ -237,7 +324,10 @@ export const methods = {
237
324
  text: z.string().optional(),
238
325
  data: z.string().optional(),
239
326
  mimeType: z.string().optional(),
327
+ /** Text: more of the file follows this part. */
240
328
  truncated: z.boolean(),
329
+ /** Where the next part starts (pass it as `offset`). */
330
+ nextOffset: z.number().int().optional(),
241
331
  }),
242
332
  },
243
333
  /**
@@ -366,6 +456,12 @@ export function isMethodName(name: string): name is MethodName {
366
456
 
367
457
  export const notifications = {
368
458
  "session.event": sessionEventSchema,
459
+ /**
460
+ * Sent before a subscription's backlog when it doesn't continue from the
461
+ * client's `fromSeq`: the events that follow start after `startSeq`, so a
462
+ * client holding older events of this session drops them first.
463
+ */
464
+ "session.window": z.object({ sessionId: z.string(), startSeq: z.number().int().nonnegative() }),
369
465
  "session.summary": z.object({ session: sessionSummarySchema }),
370
466
  /** A session was deleted (from any device, or natively by the agent). */
371
467
  "session.removed": z.object({ sessionId: z.string() }),