@paradow/ledge 0.40.0 → 0.42.0

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.
@@ -243,6 +243,10 @@ var TaskBlockSchema = z5.object({
243
243
  // ../protocol/src/decisions.ts
244
244
  import { z as z6 } from "zod";
245
245
  var DecisionSchema = z6.enum(["accept", "deny", "always"]);
246
+ var DecidedBySchema = z6.discriminatedUnion("kind", [
247
+ z6.object({ kind: z6.literal("pc") }),
248
+ z6.object({ kind: z6.literal("device"), id: z6.string().min(1).max(100), name: z6.string().min(1).max(60) })
249
+ ]);
246
250
  var NOTE_MAX = 4e3;
247
251
 
248
252
  // ../protocol/src/tasks.ts
@@ -1402,8 +1406,8 @@ var ThreadItemSchema = z22.discriminatedUnion("kind", [
1402
1406
  imagesDropped: z22.literal(true).optional()
1403
1407
  }),
1404
1408
  /** `unknown`: answered in the terminal, the daemon only saw the prompt go away; `always`: allowed and not asked again;
1405
- * `byClose`: denied by Ledge closing the session. */
1406
- z22.object({ kind: z22.literal("permission"), ...base, tool: z22.string(), text: z22.string(), decision: z22.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z22.literal(true).optional() }),
1409
+ * `byClose`: denied by Ledge closing the session; `by`: who answered it from Ledge (the PC or a device, its name then). */
1410
+ z22.object({ kind: z22.literal("permission"), ...base, tool: z22.string(), text: z22.string(), decision: z22.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z22.literal(true).optional(), by: DecidedBySchema.optional() }),
1407
1411
  z22.object({ kind: z22.literal("reply"), ...base, text: z22.string() }),
1408
1412
  z22.object({ kind: z22.literal("helper"), ...base, task: z22.string(), agentType: z22.string().optional(), state: z22.enum(["working", "exited"]) }),
1409
1413
  /** A message this session sent to another agent; `id` is the tool_use_id, `to` the address it used, `peer` the Ledge session id, `received` the paired item's id in the peer's thread, `via` set when Ledge's mailbox carries it. */
@@ -1486,6 +1490,9 @@ var MobileLinkStateSchema = z23.discriminatedUnion("state", [
1486
1490
  /** The mobile service did not start (a broken mobile.json, a missing key store): nothing is linked until the daemon restarts. */
1487
1491
  z23.object({ state: z23.literal("unavailable"), error: z23.string().max(200) })
1488
1492
  ]);
1493
+ var DeviceKindSchema = z23.enum(["phone", "tablet"]);
1494
+ var DEVICE_NAME_MAX = 40;
1495
+ var DeviceNameSchema = z23.string().trim().min(1).max(DEVICE_NAME_MAX).regex(/^[^\u0000-\u001f\u007f]+$/);
1489
1496
  var MobilePairingSchema = z23.object({
1490
1497
  id: z23.string(),
1491
1498
  label: z23.string(),
@@ -1501,7 +1508,16 @@ var MobileStateSchema = z23.object({
1501
1508
  /** The last sign-in that did not end with a session; cleared by the next one. */
1502
1509
  signinError: z23.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
1503
1510
  link: MobileLinkStateSchema,
1504
- devices: z23.array(z23.object({ id: z23.string(), label: z23.string(), approvedAt: z23.string(), online: z23.boolean(), lastSeenAt: z23.string().optional() })),
1511
+ devices: z23.array(z23.object({
1512
+ id: z23.string(),
1513
+ label: z23.string(),
1514
+ approvedAt: z23.string(),
1515
+ online: z23.boolean(),
1516
+ lastSeenAt: z23.string().optional(),
1517
+ /** Absent from an older daemon; `name` absent: the user gave none, the front derives one from the label. */
1518
+ kind: DeviceKindSchema.optional(),
1519
+ name: z23.string().optional()
1520
+ })),
1505
1521
  pairings: z23.array(MobilePairingSchema),
1506
1522
  /** « Taper dans les terminaux depuis le téléphone »: absent from an older daemon, which has no phone terminal. */
1507
1523
  terminalInput: z23.boolean().optional()
@@ -1556,6 +1572,8 @@ var SessionEventSchema = z24.object({
1556
1572
  decision: DecisionSchema.optional(),
1557
1573
  /** Decided: the request was refused by Ledge closing the session (its interrupt keys), not by the user's answer. */
1558
1574
  byClose: z24.literal(true).optional(),
1575
+ /** Decided: who answered, when the daemon typed it (absent: answered in the terminal, or an older daemon). */
1576
+ by: DecidedBySchema.optional(),
1559
1577
  /** The agent's own session id when the hook names it, so the daemon can resume that conversation. */
1560
1578
  agentSessionId: z24.string().optional()
1561
1579
  });
@@ -1832,7 +1850,9 @@ var ClientMessageSchema = z24.discriminatedUnion("type", [
1832
1850
  note: z24.string().trim().min(1).max(NOTE_MAX).optional(),
1833
1851
  /** The request the user saw when deciding: refused (the request changed) unless it is still the pending one. Left out by older fronts and the island.
1834
1852
  * `key` (pendingKey) covers its whole content; older fronts send kind and text only. */
1835
- expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().max(64).optional() }).optional()
1853
+ expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().max(64).optional() }).optional(),
1854
+ /** Who decides: set by the phone gateway for a device (over anything the device sent); left out by a local front, which means the PC. */
1855
+ by: DecidedBySchema.optional()
1836
1856
  }),
1837
1857
  z24.object({ type: z24.literal("session.focus"), id: z24.string() }),
1838
1858
  z24.object({ type: z24.literal("session.kill"), id: z24.string() }),
@@ -1886,10 +1906,13 @@ var ClientMessageSchema = z24.discriminatedUnion("type", [
1886
1906
  z24.object({ type: z24.literal("mobile.signout") }),
1887
1907
  /** While the link is offline: try again now. */
1888
1908
  z24.object({ type: z24.literal("mobile.retry") }),
1889
- z24.object({ type: z24.literal("mobile.pairing.approve"), id: z24.string().min(1).max(100) }),
1909
+ /** `replaces`: an approved device this one takes the place of (the same app reinstalled), revoked once this one is stored. */
1910
+ z24.object({ type: z24.literal("mobile.pairing.approve"), id: z24.string().min(1).max(100), replaces: z24.string().min(1).max(100).optional() }),
1890
1911
  z24.object({ type: z24.literal("mobile.pairing.deny"), id: z24.string().min(1).max(100) }),
1891
1912
  /** `id`: the device's fingerprint. */
1892
1913
  z24.object({ type: z24.literal("mobile.device.revoke"), id: z24.string().min(1).max(100) }),
1914
+ /** Names a device (`id`: its fingerprint); the device sees its new name in its next snapshot. */
1915
+ z24.object({ type: z24.literal("mobile.device.rename"), id: z24.string().min(1).max(100), name: DeviceNameSchema }),
1893
1916
  /** « Taper dans les terminaux depuis le téléphone »; turned off, every phone loses the hand. */
1894
1917
  z24.object({ type: z24.literal("mobile.terminal"), input: z24.boolean() }),
1895
1918
  /** « Reprendre la main »: the phone typing into the session's terminal goes back to reading it. */
@@ -2022,7 +2045,9 @@ var PushSubscriptionSchema = z27.object({
2022
2045
  });
2023
2046
  var PushPayloadSchema = z27.object({
2024
2047
  v: z27.literal(1),
2025
- kind: z27.enum(["request", "question", "finished", "chat", "decision"]),
2048
+ /** `resolved`: a request or question answered (anywhere), replacing its notification under the same tag; only sent to a
2049
+ * device whose subscription says its service worker knows it (`push.subscribe` `resolved`). */
2050
+ kind: z27.enum(["request", "question", "finished", "chat", "decision", "resolved"]),
2026
2051
  title: z27.string().max(80),
2027
2052
  body: z27.string().max(120),
2028
2053
  url: z27.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
@@ -2289,8 +2314,11 @@ var PhoneClientMessageSchema = z28.discriminatedUnion("type", [
2289
2314
  z28.object({ type: z28.literal("openrig.row"), qitem: QitemIdSchema }),
2290
2315
  /** The phone drops its passkey from the daemon. */
2291
2316
  z28.object({ type: z28.literal("device.forget"), op: OperationIdSchema }),
2292
- /** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). */
2293
- z28.object({ type: z28.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87) }),
2317
+ /** The device names itself (on the PC and in its next snapshot); applied to the sender only. */
2318
+ z28.object({ type: z28.literal("device.rename"), op: OperationIdSchema, name: DeviceNameSchema }),
2319
+ /** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). `resolved`: its service
2320
+ * worker shows a `resolved` push, which replaces a request's notification once it is answered elsewhere. */
2321
+ z28.object({ type: z28.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87), resolved: z28.literal(true).optional() }),
2294
2322
  z28.object({ type: z28.literal("push.unsubscribe"), op: OperationIdSchema }),
2295
2323
  /** The phone's own chat with Ledge. */
2296
2324
  z28.object({ type: z28.literal("chat.ask"), qid: OperationIdSchema, text: z28.string().trim().max(MESSAGE_MAX), attachments: z28.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional() }),
@@ -2382,7 +2410,16 @@ var PhoneServerMessageSchema = z28.discriminatedUnion("type", [
2382
2410
  openrig: z28.literal(true).optional(),
2383
2411
  /** The daemon takes `session.broadcast` (« Écrire au groupe »). */
2384
2412
  broadcast: z28.literal(true).optional()
2385
- }), device: z28.object({ confirm: z28.boolean(), push: z28.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z28.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
2413
+ }), device: z28.object({
2414
+ confirm: z28.boolean(),
2415
+ push: z28.enum(["on", "off", "stale", "unavailable"]).optional(),
2416
+ chat: z28.literal(true).optional(),
2417
+ contextNear: ContextPrefsSchema.shape.near.optional(),
2418
+ /** This device's id (its fingerprint, as decisions name it in `by`), its kind, and the name the user gave it (absent: none given). */
2419
+ id: z28.string().optional(),
2420
+ kind: DeviceKindSchema.optional(),
2421
+ name: z28.string().optional()
2422
+ }).optional() }),
2386
2423
  /** « À toi » on this PC: the answer to `openrig.asks`, then each change, whole. */
2387
2424
  z28.object({ type: z28.literal("openrig.asks"), channel: HumanChannelSchema, asks: z28.array(HumanAskSchema), partial: z28.literal(true).optional(), truncated: z28.literal(true).optional() }),
2388
2425
  /** An answer's end, as on the desktop (`openrig.answered`), under the phone's `op`. `rejected`: the device check refused it (`reason`). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paradow/ledge",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows.",
5
5
  "type": "module",
6
6
  "bin": { "ledge": "dist/cli/index.js" },