@linkshell/wire 0.1.0 → 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/dist/model.d.ts +95 -0
- package/dist/model.js +27 -0
- package/dist/model.js.map +1 -1
- package/dist/relay-client.d.ts +2 -0
- package/dist/relay-client.js +12 -0
- package/dist/relay-client.js.map +1 -1
- package/dist/rpc.d.ts +5947 -152
- package/dist/rpc.js +126 -6
- package/dist/rpc.js.map +1 -1
- package/dist/updates.d.ts +45 -45
- package/package.json +1 -1
- package/src/model.ts +31 -0
- package/src/relay-client.ts +12 -0
- package/src/rpc.ts +126 -5
package/src/model.ts
CHANGED
|
@@ -100,6 +100,28 @@ export const pendingPermissionSchema = z.object({
|
|
|
100
100
|
});
|
|
101
101
|
export type PendingPermissionSummary = z.infer<typeof pendingPermissionSchema>;
|
|
102
102
|
|
|
103
|
+
export const queuedMessageSchema = z.object({
|
|
104
|
+
clientMessageId: z.string(),
|
|
105
|
+
/** The message's text (images are counted, not sent). */
|
|
106
|
+
text: z.string(),
|
|
107
|
+
images: z.number().int().nonnegative(),
|
|
108
|
+
});
|
|
109
|
+
export type QueuedMessage = z.infer<typeof queuedMessageSchema>;
|
|
110
|
+
|
|
111
|
+
/** A sub-agent a session started: the tool call it runs under, and how it is doing. */
|
|
112
|
+
export const subagentInfoSchema = z.object({
|
|
113
|
+
toolCallId: z.string(),
|
|
114
|
+
/** What it was asked to do. */
|
|
115
|
+
task: z.string(),
|
|
116
|
+
/** Its role or type, e.g. "Explore". */
|
|
117
|
+
agentType: z.string().optional(),
|
|
118
|
+
running: z.boolean(),
|
|
119
|
+
failed: z.boolean().optional(),
|
|
120
|
+
startedAt: z.number(),
|
|
121
|
+
endedAt: z.number().optional(),
|
|
122
|
+
});
|
|
123
|
+
export type SubagentInfo = z.infer<typeof subagentInfoSchema>;
|
|
124
|
+
|
|
103
125
|
export const sessionSummarySchema = z.object({
|
|
104
126
|
/** `${agent}:${nativeId}` — stable across daemon restarts and rediscovery. */
|
|
105
127
|
id: z.string().min(1),
|
|
@@ -118,6 +140,13 @@ export const sessionSummarySchema = z.object({
|
|
|
118
140
|
archived: z.boolean(),
|
|
119
141
|
activity: sessionActivitySchema.optional(),
|
|
120
142
|
permission: pendingPermissionSchema.optional(),
|
|
143
|
+
/**
|
|
144
|
+
* Live only: messages waiting for the current turn to end, when the host
|
|
145
|
+
* holds them (agents that can't take input mid-turn). Oldest first.
|
|
146
|
+
*/
|
|
147
|
+
queue: z.array(queuedMessageSchema).optional(),
|
|
148
|
+
/** Live only, for sessions that started sub-agents: how many, and how many are working now (`sessions.subagents` lists them). */
|
|
149
|
+
subagents: z.object({ total: z.number().int(), running: z.number().int() }).optional(),
|
|
121
150
|
});
|
|
122
151
|
export type SessionSummary = z.infer<typeof sessionSummarySchema>;
|
|
123
152
|
|
|
@@ -144,6 +173,8 @@ export const machineInfoSchema = z.object({
|
|
|
144
173
|
hostname: z.string(),
|
|
145
174
|
platform: z.string(),
|
|
146
175
|
hostVersion: z.string(),
|
|
176
|
+
/** The host user's home directory, so apps can show paths as `~/…`. */
|
|
177
|
+
home: z.string().optional(),
|
|
147
178
|
agents: z.array(agentInfoSchema),
|
|
148
179
|
});
|
|
149
180
|
export type MachineInfo = z.infer<typeof machineInfoSchema>;
|
package/src/relay-client.ts
CHANGED
|
@@ -122,6 +122,18 @@ export class RelayClient {
|
|
|
122
122
|
this.open();
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/** Connects afresh so the gateway sees the current account (after a login or logout). */
|
|
126
|
+
reauthenticate(): void {
|
|
127
|
+
if (this.statusValue === "stopped") return;
|
|
128
|
+
clearTimeout(this.timer);
|
|
129
|
+
this.attempt = 0;
|
|
130
|
+
const socket = this.socket;
|
|
131
|
+
this.socket = undefined;
|
|
132
|
+
socket?.close(1000, "signing in again");
|
|
133
|
+
this.failPending("connection closed");
|
|
134
|
+
this.open();
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
/** Waits (up to `timeoutMs`) for the gateway connection. */
|
|
126
138
|
waitOnline(timeoutMs: number): Promise<boolean> {
|
|
127
139
|
if (this.statusValue === "online") return Promise.resolve(true);
|
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
|
-
|
|
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:
|
|
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,43 @@ 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
|
+
},
|
|
203
|
+
/** Drops a message still waiting in the host's queue (see SessionSummary.queue). */
|
|
204
|
+
"sessions.unqueue": {
|
|
205
|
+
params: z.object({ sessionId: z.string().min(1), clientMessageId: z.string().min(1) }),
|
|
206
|
+
result: z.object({ removed: z.boolean() }),
|
|
207
|
+
},
|
|
208
|
+
/** Archives (or restores) a session: hidden from lists, kept on disk; natively where the agent supports it. */
|
|
209
|
+
"sessions.archive": {
|
|
210
|
+
params: z.object({ sessionId: z.string().min(1), archived: z.boolean() }),
|
|
211
|
+
result: z.object({ session: sessionSummarySchema }),
|
|
212
|
+
},
|
|
213
|
+
/** Names a session; an empty title goes back to the agent's own. */
|
|
214
|
+
"sessions.rename": {
|
|
215
|
+
params: z.object({ sessionId: z.string().min(1), title: z.string().max(200) }),
|
|
216
|
+
result: z.object({ session: sessionSummarySchema }),
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* Deletes a session: the agent's own record where it has one (Codex thread,
|
|
220
|
+
* Claude transcript), and LinkShell's copy either way. Not while it runs.
|
|
221
|
+
*/
|
|
222
|
+
"sessions.delete": {
|
|
223
|
+
params: z.object({ sessionId: z.string().min(1) }),
|
|
224
|
+
result: empty,
|
|
225
|
+
},
|
|
139
226
|
"sessions.takeover": {
|
|
140
227
|
params: z.object({ sessionId: z.string().min(1) }),
|
|
141
228
|
result: z.object({ driver: sessionDriverSchema.optional() }),
|
|
@@ -183,6 +270,12 @@ export const methods = {
|
|
|
183
270
|
},
|
|
184
271
|
// ── Gateway: reach this computer from anywhere ──
|
|
185
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 },
|
|
186
279
|
/**
|
|
187
280
|
* Opens a pairing window (local only). `link` is what the QR encodes; the
|
|
188
281
|
* code can be typed instead. `pairing.done` follows when a device pairs.
|
|
@@ -195,17 +288,34 @@ export const methods = {
|
|
|
195
288
|
// ── Directories: pick where a new session runs ──
|
|
196
289
|
/** Subdirectories of `path` (default: the home directory). */
|
|
197
290
|
"fs.list": {
|
|
198
|
-
params: z.object({
|
|
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
|
+
}),
|
|
199
297
|
result: z.object({
|
|
200
298
|
path: z.string(),
|
|
201
299
|
parent: z.string().optional(),
|
|
202
300
|
home: z.string(),
|
|
203
301
|
entries: z.array(directoryEntrySchema),
|
|
302
|
+
/** The directory holds more than a listing carries (1000 entries). */
|
|
303
|
+
truncated: z.boolean().optional(),
|
|
204
304
|
}),
|
|
205
305
|
},
|
|
206
|
-
/**
|
|
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
|
+
*/
|
|
207
313
|
"fs.read": {
|
|
208
|
-
params: z.object({
|
|
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
|
+
}),
|
|
209
319
|
result: z.object({
|
|
210
320
|
path: z.string(),
|
|
211
321
|
size: z.number().int(),
|
|
@@ -214,7 +324,10 @@ export const methods = {
|
|
|
214
324
|
text: z.string().optional(),
|
|
215
325
|
data: z.string().optional(),
|
|
216
326
|
mimeType: z.string().optional(),
|
|
327
|
+
/** Text: more of the file follows this part. */
|
|
217
328
|
truncated: z.boolean(),
|
|
329
|
+
/** Where the next part starts (pass it as `offset`). */
|
|
330
|
+
nextOffset: z.number().int().optional(),
|
|
218
331
|
}),
|
|
219
332
|
},
|
|
220
333
|
/**
|
|
@@ -343,7 +456,15 @@ export function isMethodName(name: string): name is MethodName {
|
|
|
343
456
|
|
|
344
457
|
export const notifications = {
|
|
345
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() }),
|
|
346
465
|
"session.summary": z.object({ session: sessionSummarySchema }),
|
|
466
|
+
/** A session was deleted (from any device, or natively by the agent). */
|
|
467
|
+
"session.removed": z.object({ sessionId: z.string() }),
|
|
347
468
|
/** To a desktop shim: a device is taking over; exit the native UI, then call desktop.yielded. */
|
|
348
469
|
"desktop.yield": z.object({ sessionId: z.string() }),
|
|
349
470
|
/** To a desktop shim: progress from the remote driver, to show in the terminal. */
|