@linkshell/wire 0.2.22 → 0.2.24

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 (56) hide show
  1. package/dist/acp.d.ts +68 -430
  2. package/dist/acp.js +5 -5
  3. package/dist/acp.js.map +1 -1
  4. package/dist/async-questions.d.ts +2 -10
  5. package/dist/computer-preview.d.ts +1 -15
  6. package/dist/computer-preview.js +1 -1
  7. package/dist/computer-preview.js.map +1 -1
  8. package/dist/content.d.ts +211 -376
  9. package/dist/content.js +4 -4
  10. package/dist/content.js.map +1 -1
  11. package/dist/editor.d.ts +48 -592
  12. package/dist/envelopes.js +13 -1
  13. package/dist/envelopes.js.map +1 -1
  14. package/dist/goal.d.ts +15 -48
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/live-activity.d.ts +83 -212
  19. package/dist/live-activity.js +1 -1
  20. package/dist/live-activity.js.map +1 -1
  21. package/dist/model-name.d.ts +2 -0
  22. package/dist/model-name.js +9 -0
  23. package/dist/model-name.js.map +1 -0
  24. package/dist/model.d.ts +467 -2317
  25. package/dist/model.js +2 -0
  26. package/dist/model.js.map +1 -1
  27. package/dist/relay-client.d.ts +2 -1
  28. package/dist/relay-client.js +10 -5
  29. package/dist/relay-client.js.map +1 -1
  30. package/dist/relay.d.ts +51 -267
  31. package/dist/relay.js +4 -1
  32. package/dist/relay.js.map +1 -1
  33. package/dist/rpc-mcp.d.ts +21 -136
  34. package/dist/rpc-mcp.js +1 -1
  35. package/dist/rpc-mcp.js.map +1 -1
  36. package/dist/rpc.d.ts +7272 -31804
  37. package/dist/rpc.js +18 -6
  38. package/dist/rpc.js.map +1 -1
  39. package/dist/side.d.ts +13 -61
  40. package/dist/updates.d.ts +1812 -6932
  41. package/dist/usage.d.ts +17 -195
  42. package/dist/watch.d.ts +223 -1141
  43. package/dist/workflow.d.ts +46 -139
  44. package/package.json +2 -2
  45. package/src/acp.ts +5 -5
  46. package/src/computer-preview.ts +1 -1
  47. package/src/content.ts +5 -5
  48. package/src/envelopes.ts +11 -1
  49. package/src/index.ts +1 -0
  50. package/src/live-activity.ts +1 -1
  51. package/src/model-name.ts +7 -0
  52. package/src/model.ts +2 -0
  53. package/src/relay-client.ts +10 -5
  54. package/src/relay.ts +6 -1
  55. package/src/rpc-mcp.ts +1 -1
  56. package/src/rpc.ts +18 -6
@@ -1,14 +1,35 @@
1
1
  import { z } from "zod";
2
- export declare const workflowStateSchema: z.ZodEnum<["running", "paused", "completed", "failed", "stopped"]>;
2
+ export declare const workflowStateSchema: z.ZodEnum<{
3
+ paused: "paused";
4
+ running: "running";
5
+ completed: "completed";
6
+ failed: "failed";
7
+ stopped: "stopped";
8
+ }>;
3
9
  export type WorkflowState = z.infer<typeof workflowStateSchema>;
4
- export declare const workflowAgentStateSchema: z.ZodEnum<["pending", "running", "paused", "completed", "failed", "stopped", "unknown"]>;
10
+ export declare const workflowAgentStateSchema: z.ZodEnum<{
11
+ unknown: "unknown";
12
+ paused: "paused";
13
+ running: "running";
14
+ completed: "completed";
15
+ failed: "failed";
16
+ stopped: "stopped";
17
+ pending: "pending";
18
+ }>;
5
19
  export type WorkflowAgentState = z.infer<typeof workflowAgentStateSchema>;
6
20
  export declare const workflowAgentSchema: z.ZodObject<{
7
21
  id: z.ZodString;
8
- /** Present only after the host has imported this agent's transcript. */
9
22
  toolCallId: z.ZodOptional<z.ZodString>;
10
23
  title: z.ZodString;
11
- state: z.ZodEnum<["pending", "running", "paused", "completed", "failed", "stopped", "unknown"]>;
24
+ state: z.ZodEnum<{
25
+ unknown: "unknown";
26
+ paused: "paused";
27
+ running: "running";
28
+ completed: "completed";
29
+ failed: "failed";
30
+ stopped: "stopped";
31
+ pending: "pending";
32
+ }>;
12
33
  phaseId: z.ZodOptional<z.ZodString>;
13
34
  model: z.ZodOptional<z.ZodString>;
14
35
  tokens: z.ZodOptional<z.ZodNumber>;
@@ -17,54 +38,25 @@ export declare const workflowAgentSchema: z.ZodObject<{
17
38
  startedAt: z.ZodOptional<z.ZodNumber>;
18
39
  endedAt: z.ZodOptional<z.ZodNumber>;
19
40
  result: z.ZodOptional<z.ZodString>;
20
- }, "strip", z.ZodTypeAny, {
21
- id: string;
22
- title: string;
23
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
24
- toolCallId?: string | undefined;
25
- phaseId?: string | undefined;
26
- model?: string | undefined;
27
- tokens?: number | undefined;
28
- toolCalls?: number | undefined;
29
- durationMs?: number | undefined;
30
- startedAt?: number | undefined;
31
- endedAt?: number | undefined;
32
- result?: string | undefined;
33
- }, {
34
- id: string;
35
- title: string;
36
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
37
- toolCallId?: string | undefined;
38
- phaseId?: string | undefined;
39
- model?: string | undefined;
40
- tokens?: number | undefined;
41
- toolCalls?: number | undefined;
42
- durationMs?: number | undefined;
43
- startedAt?: number | undefined;
44
- endedAt?: number | undefined;
45
- result?: string | undefined;
46
- }>;
41
+ }, z.core.$strip>;
47
42
  export type WorkflowAgent = z.infer<typeof workflowAgentSchema>;
48
43
  export declare const workflowPhaseSchema: z.ZodObject<{
49
44
  id: z.ZodString;
50
45
  title: z.ZodString;
51
46
  order: z.ZodNumber;
52
- }, "strip", z.ZodTypeAny, {
53
- id: string;
54
- title: string;
55
- order: number;
56
- }, {
57
- id: string;
58
- title: string;
59
- order: number;
60
- }>;
47
+ }, z.core.$strip>;
61
48
  export type WorkflowPhase = z.infer<typeof workflowPhaseSchema>;
62
49
  /** A complete run snapshot, independent of the loaded conversation window. */
63
50
  export declare const workflowSchema: z.ZodObject<{
64
51
  runId: z.ZodOptional<z.ZodString>;
65
52
  name: z.ZodOptional<z.ZodString>;
66
- state: z.ZodOptional<z.ZodEnum<["running", "paused", "completed", "failed", "stopped"]>>;
67
- /** Observed counts: a dynamic script may start more agents later. */
53
+ state: z.ZodOptional<z.ZodEnum<{
54
+ paused: "paused";
55
+ running: "running";
56
+ completed: "completed";
57
+ failed: "failed";
58
+ stopped: "stopped";
59
+ }>>;
68
60
  started: z.ZodOptional<z.ZodNumber>;
69
61
  completed: z.ZodOptional<z.ZodNumber>;
70
62
  tokens: z.ZodOptional<z.ZodNumber>;
@@ -75,21 +67,20 @@ export declare const workflowSchema: z.ZodObject<{
75
67
  id: z.ZodString;
76
68
  title: z.ZodString;
77
69
  order: z.ZodNumber;
78
- }, "strip", z.ZodTypeAny, {
79
- id: string;
80
- title: string;
81
- order: number;
82
- }, {
83
- id: string;
84
- title: string;
85
- order: number;
86
- }>, "many">>;
70
+ }, z.core.$strip>>>;
87
71
  agents: z.ZodOptional<z.ZodArray<z.ZodObject<{
88
72
  id: z.ZodString;
89
- /** Present only after the host has imported this agent's transcript. */
90
73
  toolCallId: z.ZodOptional<z.ZodString>;
91
74
  title: z.ZodString;
92
- state: z.ZodEnum<["pending", "running", "paused", "completed", "failed", "stopped", "unknown"]>;
75
+ state: z.ZodEnum<{
76
+ unknown: "unknown";
77
+ paused: "paused";
78
+ running: "running";
79
+ completed: "completed";
80
+ failed: "failed";
81
+ stopped: "stopped";
82
+ pending: "pending";
83
+ }>;
93
84
  phaseId: z.ZodOptional<z.ZodString>;
94
85
  model: z.ZodOptional<z.ZodString>;
95
86
  tokens: z.ZodOptional<z.ZodNumber>;
@@ -98,90 +89,6 @@ export declare const workflowSchema: z.ZodObject<{
98
89
  startedAt: z.ZodOptional<z.ZodNumber>;
99
90
  endedAt: z.ZodOptional<z.ZodNumber>;
100
91
  result: z.ZodOptional<z.ZodString>;
101
- }, "strip", z.ZodTypeAny, {
102
- id: string;
103
- title: string;
104
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
105
- toolCallId?: string | undefined;
106
- phaseId?: string | undefined;
107
- model?: string | undefined;
108
- tokens?: number | undefined;
109
- toolCalls?: number | undefined;
110
- durationMs?: number | undefined;
111
- startedAt?: number | undefined;
112
- endedAt?: number | undefined;
113
- result?: string | undefined;
114
- }, {
115
- id: string;
116
- title: string;
117
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
118
- toolCallId?: string | undefined;
119
- phaseId?: string | undefined;
120
- model?: string | undefined;
121
- tokens?: number | undefined;
122
- toolCalls?: number | undefined;
123
- durationMs?: number | undefined;
124
- startedAt?: number | undefined;
125
- endedAt?: number | undefined;
126
- result?: string | undefined;
127
- }>, "many">>;
128
- }, "strip", z.ZodTypeAny, {
129
- name?: string | undefined;
130
- completed?: number | undefined;
131
- state?: "paused" | "running" | "completed" | "failed" | "stopped" | undefined;
132
- tokens?: number | undefined;
133
- durationMs?: number | undefined;
134
- startedAt?: number | undefined;
135
- endedAt?: number | undefined;
136
- runId?: string | undefined;
137
- started?: number | undefined;
138
- phases?: {
139
- id: string;
140
- title: string;
141
- order: number;
142
- }[] | undefined;
143
- agents?: {
144
- id: string;
145
- title: string;
146
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
147
- toolCallId?: string | undefined;
148
- phaseId?: string | undefined;
149
- model?: string | undefined;
150
- tokens?: number | undefined;
151
- toolCalls?: number | undefined;
152
- durationMs?: number | undefined;
153
- startedAt?: number | undefined;
154
- endedAt?: number | undefined;
155
- result?: string | undefined;
156
- }[] | undefined;
157
- }, {
158
- name?: string | undefined;
159
- completed?: number | undefined;
160
- state?: "paused" | "running" | "completed" | "failed" | "stopped" | undefined;
161
- tokens?: number | undefined;
162
- durationMs?: number | undefined;
163
- startedAt?: number | undefined;
164
- endedAt?: number | undefined;
165
- runId?: string | undefined;
166
- started?: number | undefined;
167
- phases?: {
168
- id: string;
169
- title: string;
170
- order: number;
171
- }[] | undefined;
172
- agents?: {
173
- id: string;
174
- title: string;
175
- state: "unknown" | "paused" | "running" | "completed" | "failed" | "stopped" | "pending";
176
- toolCallId?: string | undefined;
177
- phaseId?: string | undefined;
178
- model?: string | undefined;
179
- tokens?: number | undefined;
180
- toolCalls?: number | undefined;
181
- durationMs?: number | undefined;
182
- startedAt?: number | undefined;
183
- endedAt?: number | undefined;
184
- result?: string | undefined;
185
- }[] | undefined;
186
- }>;
92
+ }, z.core.$strip>>>;
93
+ }, z.core.$strip>;
187
94
  export type Workflow = z.infer<typeof workflowSchema>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkshell/wire",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Wire protocol for LinkShell v2: session model, JSON-RPC methods, end-to-end encryption and the relay client.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "@noble/ciphers": "^2.4.0",
30
30
  "@noble/curves": "^1.9.7",
31
31
  "@noble/hashes": "^1.8.0",
32
- "zod": "^3.24.3"
32
+ "zod": "^4.6.7"
33
33
  },
34
34
  "devDependencies": {
35
35
  "vitest": "^3.2.1"
package/src/acp.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod";
2
2
 
3
- const env = z.record(z.string());
3
+ const env = z.record(z.string(), z.string());
4
4
  export const mcpServerSchema = z.discriminatedUnion("type", [
5
5
  z.object({ type: z.literal("stdio"), name: z.string().min(1), command: z.string().min(1), args: z.array(z.string()).default([]), env: env.default({}) }),
6
- z.object({ type: z.literal("http"), name: z.string().min(1), url: z.string().url(), headers: env.default({}) }),
7
- z.object({ type: z.literal("sse"), name: z.string().min(1), url: z.string().url(), headers: env.default({}) }),
6
+ z.object({ type: z.literal("http"), name: z.string().min(1), url: z.url(), headers: env.default({}) }),
7
+ z.object({ type: z.literal("sse"), name: z.string().min(1), url: z.url(), headers: env.default({}) }),
8
8
  z.object({ type: z.literal("acp"), name: z.string().min(1), command: z.string().min(1), args: z.array(z.string()).default([]), env: env.default({}) }),
9
9
  ]);
10
10
  export type AcpMcpServer = z.infer<typeof mcpServerSchema>;
@@ -23,10 +23,10 @@ export const acpRemoteAgentSchema = z.object({
23
23
  label: z.string().min(1).max(80),
24
24
  transport: z.enum(["stdio", "http", "websocket"]),
25
25
  command: z.string().optional(), args: z.array(z.string()).default([]),
26
- url: z.string().url().optional(),
26
+ url: z.url().optional(),
27
27
  /** Header name to environment-variable name. The UI does not store secrets. */
28
28
  headerEnv: env.default({}),
29
- settings: acpAgentSettingsSchema.default({}),
29
+ settings: acpAgentSettingsSchema.prefault({}),
30
30
  });
31
31
  export type AcpRemoteAgent = z.infer<typeof acpRemoteAgentSchema>;
32
32
 
@@ -4,7 +4,7 @@ export const PREVIEW_MAX_BYTES = 48 * 1024;
4
4
  export const previewFrameSchema = z.object({
5
5
  id: z.string().regex(/^[a-f0-9]{64}$/),
6
6
  target: z.string().max(1024),
7
- capturedAt: z.number().finite().nonnegative(),
7
+ capturedAt: z.number().nonnegative(),
8
8
  width: z.number().int().positive().max(2048),
9
9
  height: z.number().int().positive().max(2048),
10
10
  mimeType: z.literal("image/webp"),
package/src/content.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod";
2
2
 
3
- const optionalString = z.string().nullish().transform((value) => value ?? undefined);
4
- const optionalMeta = z.record(z.unknown()).nullish().transform((value) => value ?? undefined);
3
+ const optionalString = z.string().nullish().transform((value) => value ?? undefined).optional();
4
+ const optionalMeta = z.record(z.string(), z.unknown()).nullish().transform((value) => value ?? undefined).optional();
5
5
  const contentMetadata = {
6
- annotations: z.object({ audience: z.array(z.enum(["user", "assistant"])).nullish().transform((value) => value ?? undefined), priority: z.number().nullish().transform((value) => value ?? undefined), lastModified: optionalString }).passthrough().nullish().transform((value) => value ?? undefined),
6
+ annotations: z.looseObject({ audience: z.array(z.enum(["user", "assistant"])).nullish().transform((value) => value ?? undefined).optional(), priority: z.number().nullish().transform((value) => value ?? undefined).optional(), lastModified: optionalString }).nullish().transform((value) => value ?? undefined).optional(),
7
7
  _meta: optionalMeta,
8
8
  };
9
9
  const contentVariants = z.discriminatedUnion("type", [
@@ -28,7 +28,7 @@ const contentVariants = z.discriminatedUnion("type", [
28
28
  title: optionalString,
29
29
  description: optionalString,
30
30
  mimeType: optionalString,
31
- size: z.number().nullish().transform((value) => value ?? undefined),
31
+ size: z.number().nullish().transform((value) => value ?? undefined).optional(),
32
32
  ...contentMetadata,
33
33
  /** What the link names: a file (default), a skill the user invoked, or another agent. */
34
34
  kind: z.enum(["file", "skill", "agent"]).optional(),
@@ -36,7 +36,7 @@ const contentVariants = z.discriminatedUnion("type", [
36
36
  ]);
37
37
  export type ContentBlock = z.infer<typeof contentVariants>;
38
38
  // Hide the nested validator implementation from every exported RPC declaration.
39
- export const contentBlockSchema: z.ZodType<ContentBlock, z.ZodTypeDef, unknown> = contentVariants;
39
+ export const contentBlockSchema: z.ZodType<ContentBlock, unknown> = contentVariants;
40
40
 
41
41
  export const toolCallStatusSchema = z.enum(["pending", "in_progress", "completed", "failed"]);
42
42
  export type ToolCallStatus = z.infer<typeof toolCallStatusSchema>;
package/src/envelopes.ts CHANGED
@@ -95,6 +95,7 @@ export function stripHiddenContext(text: string, { keepPeers = false }: { keepPe
95
95
  .trim();
96
96
  }
97
97
 
98
+ // eslint-disable-next-line no-control-regex -- terminal colour codes start with ESC.
98
99
  const ANSI = /\u001b\[[\d;?]*[ -/]*[@-~]/g;
99
100
 
100
101
  function tagText(source: string, tag: string): string | undefined {
@@ -206,6 +207,15 @@ function unescapeHtml(value: string): string {
206
207
  });
207
208
  }
208
209
 
210
+ /** Output as a terminal shows it: no colour codes, a backspace takes back the character before it, other controls are not drawn. */
211
+ function printed(value: string): string {
212
+ let text = value.replace(ANSI, "");
213
+ // eslint-disable-next-line no-control-regex -- a backspace is the character being handled.
214
+ for (let previous = ""; previous !== text; ) { previous = text; text = text.replace(/[^\n\x08]\x08/g, ""); }
215
+ // eslint-disable-next-line no-control-regex -- what a terminal doesn't draw.
216
+ return text.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "");
217
+ }
218
+
209
219
  /**
210
220
  * Codex Desktop writes a mention of another of its threads as a Markdown link to
211
221
  * codex://threads/<id>, with the space before it as "&#x20;": read as the thread's title.
@@ -249,7 +259,7 @@ export function readUserText(raw: string, { images = 0 }: { images?: number } =
249
259
  }
250
260
 
251
261
  if (/^<bash-(?:input|stdout|stderr)>/.test(text)) {
252
- const clean = (value?: string) => (value === undefined ? undefined : unescapeHtml(value).replace(ANSI, "").replace(/\n+$/, "")) || undefined;
262
+ const clean = (value?: string) => (value === undefined ? undefined : printed(unescapeHtml(value)).replace(/\n+$/, "")) || undefined;
253
263
  const command = clean(tagText(text, "bash-input"))?.trim();
254
264
  const stdout = clean(tagText(text, "bash-stdout"));
255
265
  const stderr = clean(tagText(text, "bash-stderr"));
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./model.js";
2
+ export * from "./model-name.js";
2
3
  export * from "./goal.js";
3
4
  export * from "./updates.js";
4
5
  export * from "./workflow.js";
@@ -42,7 +42,7 @@ export interface LiveActivityPayload extends Partial<LiveActivityContentState> {
42
42
  export const liveActivityIdSchema = z.string().min(1).max(128).regex(/^[a-zA-Z0-9-]+$/);
43
43
  export const liveActivitySubscriptionSchema = z.object({
44
44
  activityId: liveActivityIdSchema,
45
- serviceUrl: z.string().url().max(256),
45
+ serviceUrl: z.url().max(256),
46
46
  grant: z.string().regex(/^[a-f0-9]{64}$/),
47
47
  expiresAt: z.number().int().positive(),
48
48
  computerKey: z.string().min(1).max(512),
@@ -0,0 +1,7 @@
1
+ /** Format a raw GPT model ID for display without changing the value sent to the agent. */
2
+ export function displayModelName(name: string): string {
3
+ const match = /^gpt-(\d+(?:\.\d+)*)(?:-([a-z][a-z0-9-]*))?$/i.exec(name.trim());
4
+ if (!match) return name;
5
+ const suffix = match[2]?.split("-").map((word) => word[0]!.toUpperCase() + word.slice(1).toLowerCase()).join(" ");
6
+ return `GPT-${match[1]}${suffix ? ` ${suffix}` : ""}`;
7
+ }
package/src/model.ts CHANGED
@@ -333,6 +333,8 @@ export type ProjectSummary = z.infer<typeof projectSummarySchema>;
333
333
  export const machineInfoSchema = z.object({
334
334
  machineId: z.string().min(1),
335
335
  hostname: z.string(),
336
+ /** Host-owned display name; its presence also advertises `machine.rename`. */
337
+ naming: z.object({ defaultName: z.string(), customName: z.string().optional(), configured: z.boolean() }).optional(),
336
338
  platform: z.string(),
337
339
  hostVersion: z.string(),
338
340
  /** The host's RPC revision (`HOST_PROTOCOL`); absent from hosts older than it. */
@@ -30,7 +30,8 @@ export interface RelayClientOptions {
30
30
  url: string;
31
31
  identity: Identity;
32
32
  role: PeerRole;
33
- name: string;
33
+ name: string | (() => string);
34
+ namingConfigured?: () => boolean;
34
35
  platform?: string;
35
36
  /** Account token, fetched fresh for each connection. */
36
37
  token?: () => Promise<string | undefined> | string | undefined;
@@ -240,7 +241,8 @@ export class RelayClient {
240
241
  this.heardAt = Date.now();
241
242
  let fatal: { code: string; message: string } | undefined;
242
243
  socket.onmessage = (event) => {
243
- if (this.socket === socket) this.heardAt = Date.now();
244
+ if (this.socket !== socket) return;
245
+ this.heardAt = Date.now();
244
246
  let frame;
245
247
  try {
246
248
  const parsed = serverFrameSchema.safeParse(JSON.parse(String(event.data)));
@@ -251,7 +253,7 @@ export class RelayClient {
251
253
  }
252
254
  switch (frame.t) {
253
255
  case "challenge":
254
- void this.authenticate(frame.nonce);
256
+ void this.authenticate(frame.nonce, socket);
255
257
  return;
256
258
  case "ready":
257
259
  this.attempt = 0;
@@ -362,19 +364,22 @@ export class RelayClient {
362
364
  this.scheduleReconnect({ code: "disconnected", message });
363
365
  }
364
366
 
365
- private async authenticate(nonce: string): Promise<void> {
367
+ private async authenticate(nonce: string, socket: RelaySocket): Promise<void> {
366
368
  let token: string | undefined;
367
369
  try {
368
370
  token = (await this.options.token?.()) ?? undefined;
369
371
  } catch {
370
372
  token = undefined;
371
373
  }
374
+ // A token refresh may outlive reauthentication; its nonce belongs to the socket that asked.
375
+ if (this.socket !== socket) return;
372
376
  this.sendFrame({
373
377
  t: "auth",
374
378
  role: this.options.role,
375
379
  identity: publicIdentity(this.options.identity),
376
380
  signature: sign(this.options.identity, gatewayChallengeMessage(nonce)),
377
- name: this.options.name.slice(0, 120),
381
+ name: (typeof this.options.name === "function" ? this.options.name() : this.options.name).slice(0, 120),
382
+ ...(this.options.namingConfigured?.() ? { namingConfigured: true } : {}),
378
383
  platform: this.options.platform,
379
384
  token,
380
385
  ...(this.options.locked?.() ? { locked: true } : {}),
package/src/relay.ts CHANGED
@@ -24,6 +24,8 @@ export type PeerRole = z.infer<typeof peerRoleSchema>;
24
24
  /** A machine as a device sees it in the gateway's list. */
25
25
  export const machineEntrySchema = publicIdentitySchema.extend({
26
26
  name: z.string(),
27
+ /** An explicit host-owned name/reset supersedes older device-local aliases. */
28
+ namingConfigured: z.boolean().optional(),
27
29
  platform: z.string().optional(),
28
30
  online: z.boolean(),
29
31
  /** How this device may reach it: it paired, or both belong to the same account. */
@@ -50,6 +52,7 @@ export const clientFrameSchema = z.discriminatedUnion("t", [
50
52
  identity: publicIdentitySchema,
51
53
  signature: z.string(),
52
54
  name: z.string().max(120),
55
+ namingConfigured: z.boolean().optional(),
53
56
  platform: z.string().max(40).optional(),
54
57
  /** Account access token, when signed in. */
55
58
  token: z.string().optional(),
@@ -74,7 +77,7 @@ export const serverFrameSchema = z.discriminatedUnion("t", [
74
77
  result: z.unknown().optional(),
75
78
  error: z.object({ code: z.string(), message: z.string() }).optional(),
76
79
  }),
77
- z.object({ t: z.literal("event"), name: z.string(), data: z.unknown() }),
80
+ z.object({ t: z.literal("event"), name: z.string(), data: z.unknown().optional() }),
78
81
  /** An encrypted frame from a peer, with the keys the gateway verified it holds. */
79
82
  z.object({ t: z.literal("from"), from: publicIdentitySchema, via: z.enum(["paired", "account"]), d: z.string() }),
80
83
  /** A frame couldn't be delivered: the peer is offline or not reachable. */
@@ -96,6 +99,8 @@ export interface RelayMethods {
96
99
  result: { machine: MachineEntry };
97
100
  };
98
101
  "machines.list": { params: Record<string, never>; result: { machines: MachineEntry[] } };
102
+ /** Machine: refresh its display metadata without replacing its authenticated connection. */
103
+ "machines.update": { params: { name: string; namingConfigured?: boolean }; result: Record<string, never> };
99
104
  "machines.forget": { params: { machineId: string }; result: Record<string, never> };
100
105
  "devices.list": { params: Record<string, never>; result: { devices: DeviceEntry[] } };
101
106
  "devices.revoke": { params: { deviceId: string }; result: Record<string, never> };
package/src/rpc-mcp.ts CHANGED
@@ -68,7 +68,7 @@ export const mcpMethods = {
68
68
  sessionId: z.string().min(1),
69
69
  server: z.string().min(1),
70
70
  tool: z.string().min(1),
71
- arguments: z.record(z.unknown()).optional(),
71
+ arguments: z.record(z.string(), z.unknown()).optional(),
72
72
  connector: z.string().optional(),
73
73
  }),
74
74
  result: z.object({
package/src/rpc.ts CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  questionAnswerSchema,
21
21
  pendingPermissionSchema,
22
22
  } from "./model.js";
23
- import { contentBlockSchema, sessionEventSchema, sessionNoticeSchema } from "./updates.js";
23
+ import { contentBlockSchema, sessionConfigOptionSchema, sessionEventSchema, sessionNoticeSchema } from "./updates.js";
24
24
  import { sideChatSchema } from "./side.js";
25
25
  import { mcpMethods } from "./rpc-mcp.js";
26
26
 
@@ -78,7 +78,7 @@ export const RPC_APP_ERROR = -32000;
78
78
  */
79
79
  export const HOST_PROTOCOL = 2;
80
80
 
81
- const empty = z.object({}).passthrough();
81
+ const empty = z.looseObject({});
82
82
 
83
83
  export const directoryEntrySchema = z.object({
84
84
  name: z.string(),
@@ -111,7 +111,7 @@ const acpMethods = {
111
111
  result: empty,
112
112
  },
113
113
  "agents.providers": {
114
- params: z.object({ agent: z.string().min(1), operation: z.enum(["list", "set", "disable"]), config: z.object({ providerId: z.string(), apiType: z.string().optional(), baseUrl: z.string().url().optional(), headers: z.record(z.string()).optional() }).optional() }),
114
+ params: z.object({ agent: z.string().min(1), operation: z.enum(["list", "set", "disable"]), config: z.object({ providerId: z.string(), apiType: z.string().optional(), baseUrl: z.url().optional(), headers: z.record(z.string(), z.string()).optional() }).optional() }),
115
115
  result: z.object({ providers: z.array(acpProviderSchema) }),
116
116
  },
117
117
  "agents.custom": {
@@ -135,6 +135,11 @@ const coreMethods = {
135
135
  params: empty,
136
136
  result: machineInfoSchema,
137
137
  },
138
+ "machine.rename": {
139
+ /** Empty restores the configured default; names never change peer identities. */
140
+ params: z.object({ name: z.string().trim().max(40) }),
141
+ result: machineInfoSchema,
142
+ },
138
143
  "projects.list": {
139
144
  params: z.object({ limit: z.number().int().positive().max(200).optional() }),
140
145
  result: z.object({ projects: z.array(projectSummarySchema) }),
@@ -167,11 +172,15 @@ const coreMethods = {
167
172
  agent: z.string().min(1),
168
173
  /** Where the session will work: an agent's project settings can choose another model. */
169
174
  cwd: z.string().optional(),
175
+ /** Preview settings supported by a selected model without changing the agent's default. */
176
+ model: z.string().optional(),
170
177
  }),
171
178
  result: z.object({
172
179
  /** As a session's model option lists them (`group`: the heading some agents put models under). */
173
180
  models: z.array(z.object({ value: z.string(), name: z.string(), description: z.string().optional(), group: z.string().optional() })),
174
181
  current: z.string().optional(),
182
+ /** Settings available before the first prompt; omitted by older hosts or agents without a template. */
183
+ config: z.array(sessionConfigOptionSchema).optional(),
175
184
  }),
176
185
  },
177
186
  "sessions.create": {
@@ -182,6 +191,8 @@ const coreMethods = {
182
191
  clientMessageId: z.string().optional(),
183
192
  /** One of `agents.models`' values; absent: the agent's own default. */
184
193
  model: z.string().optional(),
194
+ /** Explicit choices from agents.models.config, applied before the first prompt. */
195
+ config: z.record(z.string(), z.string()).optional(),
185
196
  /**
186
197
  * Start in a new git worktree of `cwd`'s repository (its own branch, from
187
198
  * the last commit) instead of in `cwd` itself. Refused with `not_a_repo`
@@ -434,12 +445,12 @@ const coreMethods = {
434
445
  args: z.array(z.string()).default([]),
435
446
  /** The terminal's working directory and environment. */
436
447
  cwd: z.string().optional(),
437
- env: z.record(z.string()).optional(),
448
+ env: z.record(z.string(), z.string()).optional(),
438
449
  }),
439
450
  result: z.object({
440
451
  command: z.string(),
441
452
  args: z.array(z.string()),
442
- env: z.record(z.string()).optional(),
453
+ env: z.record(z.string(), z.string()).optional(),
443
454
  /** Set for handoff agents: the session this terminal now drives. */
444
455
  sessionId: z.string().optional(),
445
456
  }),
@@ -455,7 +466,7 @@ const coreMethods = {
455
466
  result: z.object({
456
467
  command: z.string(),
457
468
  args: z.array(z.string()),
458
- env: z.record(z.string()).optional(),
469
+ env: z.record(z.string(), z.string()).optional(),
459
470
  }),
460
471
  },
461
472
  // ── Gateway: reach this computer from anywhere ──
@@ -741,6 +752,7 @@ export function isMethodName(name: string): name is MethodName {
741
752
  // ── Notifications (host → client) ────────────────────────────────────
742
753
 
743
754
  export const notifications = {
755
+ "machine.changed": machineInfoSchema,
744
756
  "agent.changed": z.object({ agent: agentInfoSchema }),
745
757
  "agent.interaction": z.object({ agent: z.string(), request: z.union([pendingPermissionSchema, z.object({ requestId: z.string(), resolved: z.literal(true) })]) }),
746
758
  "session.notice": z.object({ sessionId: z.string(), notice: sessionNoticeSchema }),