@paradow/ledge 0.26.0 → 0.28.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.
Files changed (32) hide show
  1. package/dist/cli/{action-SCMMRWYI.js → action-NQRSZEC5.js} +4 -4
  2. package/dist/cli/{attach-EFIN5VU6.js → attach-KXOSFUHO.js} +8 -8
  3. package/dist/cli/{bridge-QT5X6TWJ.js → bridge-XCTXVWKH.js} +1 -1
  4. package/dist/cli/{chunk-6F6JTPMC.js → chunk-3CF4FQWG.js} +2 -2
  5. package/dist/cli/{chunk-JHUIHBC7.js → chunk-3NQNWGDX.js} +2 -2
  6. package/dist/cli/{chunk-5PTC732E.js → chunk-CIQAFCX3.js} +3 -3
  7. package/dist/cli/{chunk-SYXW4YB6.js → chunk-CR3LGTZQ.js} +3 -3
  8. package/dist/cli/{chunk-II6VRGLU.js → chunk-JHFKBKSO.js} +1 -1
  9. package/dist/cli/{chunk-L3GYGJRE.js → chunk-JLUHY5AB.js} +3 -3
  10. package/dist/cli/{chunk-PTY4LXTG.js → chunk-LG3XFIPV.js} +1 -1
  11. package/dist/cli/{chunk-2V2OJR2N.js → chunk-OYDTU6AP.js} +373 -58
  12. package/dist/cli/{chunk-HCTXTN2X.js → chunk-QSRB3F5Y.js} +2 -2
  13. package/dist/cli/{chunk-WYVMCF2K.js → chunk-RYZ6FHYT.js} +5 -5
  14. package/dist/cli/chunk-UME2F7VM.js +6 -0
  15. package/dist/cli/{chunk-QRD74KPO.js → chunk-VKRFHDRK.js} +151 -13
  16. package/dist/cli/{chunk-NYGLT5G2.js → chunk-VMGACWNP.js} +1 -1
  17. package/dist/cli/{chunk-TYNRKZFE.js → chunk-ZI47U5CQ.js} +2 -2
  18. package/dist/cli/{daemon-U6NA6PVD.js → daemon-ZUHJKLSL.js} +34 -15
  19. package/dist/cli/{decide-XOUTZT3S.js → decide-OSSTI3FE.js} +3 -3
  20. package/dist/cli/{handover-6CQKUZJV.js → handover-2G7OEWIG.js} +4 -4
  21. package/dist/cli/{hook-ZIJACTCF.js → hook-XY4E5H7O.js} +9 -9
  22. package/dist/cli/index.js +16 -16
  23. package/dist/cli/{launch-QWPA2P66.js → launch-7WOARLZM.js} +8 -8
  24. package/dist/cli/{send-T5IOOVGX.js → send-RUHGKLMA.js} +4 -4
  25. package/dist/cli/{status-EZPFE5CL.js → status-4Z2VJFID.js} +2 -2
  26. package/dist/cli/{task-B2QVGHDF.js → task-2GESYTOL.js} +4 -4
  27. package/dist/cli/{team-V6VPO3CB.js → team-MTSKNMAE.js} +6 -6
  28. package/dist/cli/{tui-MM64KSQ2.js → tui-4IRYQXYU.js} +8 -8
  29. package/dist/cli/{version-SK4VC4AE.js → version-L2Y7MRH6.js} +1 -1
  30. package/dist/cli/{who-XXZUCJPD.js → who-PSKHASMK.js} +3 -3
  31. package/package.json +1 -1
  32. package/dist/cli/chunk-3PLC745L.js +0 -6
@@ -6,11 +6,11 @@ import {
6
6
  quoteScalar,
7
7
  readSmall,
8
8
  scalarValue
9
- } from "./chunk-II6VRGLU.js";
9
+ } from "./chunk-JHFKBKSO.js";
10
10
  import {
11
11
  SeatsSchema,
12
12
  TeamNameSchema
13
- } from "./chunk-QRD74KPO.js";
13
+ } from "./chunk-VKRFHDRK.js";
14
14
  import {
15
15
  log
16
16
  } from "./chunk-6IYPRUQG.js";
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  EFFORT_ARGS,
3
3
  MODEL_ARGS
4
- } from "./chunk-2V2OJR2N.js";
4
+ } from "./chunk-OYDTU6AP.js";
5
5
  import {
6
6
  findRole
7
- } from "./chunk-II6VRGLU.js";
7
+ } from "./chunk-JHFKBKSO.js";
8
8
  import {
9
9
  exitFile,
10
10
  spawnSession
11
- } from "./chunk-PTY4LXTG.js";
11
+ } from "./chunk-LG3XFIPV.js";
12
12
  import {
13
13
  DaemonRefusedError,
14
14
  connect
15
- } from "./chunk-NYGLT5G2.js";
15
+ } from "./chunk-VMGACWNP.js";
16
16
  import {
17
17
  PROTOCOL_PORT,
18
18
  autonomyArgs
19
- } from "./chunk-QRD74KPO.js";
19
+ } from "./chunk-VKRFHDRK.js";
20
20
  import {
21
21
  realTmux
22
22
  } from "./chunk-NLL2EQJ2.js";
@@ -0,0 +1,6 @@
1
+ // src/version.ts
2
+ var LEDGE_VERSION = true ? "0.28.0" : "0.0.0-dev";
3
+
4
+ export {
5
+ LEDGE_VERSION
6
+ };
@@ -203,6 +203,8 @@ var ModelsListingSchema = z3.object({
203
203
  models: z3.array(ModelChoiceSchema),
204
204
  error: z3.string().optional()
205
205
  });
206
+ var ModelRefusalSchema = z3.enum(["not_launched", "ended", "no_conversation", "not_running", "effort_in_model", "restart_failed", "refused"]);
207
+ var SessionModelAcceptedSchema = z3.object({ type: z3.literal("session.model.accepted"), id: z3.string(), when: z3.enum(["now", "turn", "none"]) });
206
208
 
207
209
  // ../protocol/src/projects.ts
208
210
  import { z as z6 } from "zod";
@@ -412,7 +414,8 @@ var DefinitionsListingSchema = z7.object({
412
414
  }))
413
415
  });
414
416
  var DefinitionResultSchema = z7.object({ type: z7.literal("definition.result"), rid: z7.string(), id: z7.string().optional(), hash: DefinitionHashSchema.optional(), code: DefinitionCodeSchema.optional(), reason: z7.string().optional() });
415
- var TeamChangedSchema = z7.object({ type: z7.literal("team.changed"), rid: z7.string(), team: TeamSchema.optional(), reason: z7.string().optional() });
417
+ var SeatModelOutcomeSchema = z7.object({ role: z7.string(), session: z7.string(), when: z7.enum(["now", "turn"]).optional(), code: ModelRefusalSchema.optional() });
418
+ var TeamChangedSchema = z7.object({ type: z7.literal("team.changed"), rid: z7.string(), team: TeamSchema.optional(), reason: z7.string().optional(), models: z7.array(SeatModelOutcomeSchema).max(16).optional() });
416
419
  var TeamsMessageSchema = z7.object({ type: z7.literal("teams"), teams: z7.array(TeamSchema) });
417
420
  var TeamUpdatedSchema = z7.object({ type: z7.literal("team.updated"), team: TeamSchema });
418
421
  var TeamLaunchedSchema = z7.object({ type: z7.literal("team.launched"), rid: z7.string(), team: TeamSchema });
@@ -514,12 +517,12 @@ var TaskBlockSchema = z9.object({
514
517
  var BLOCK = /```ledge-(session|relay|project|close|team|task|action)[ \t]*\n?([\s\S]*?)(?:```|$)/g;
515
518
  function extractProposal(answer) {
516
519
  const found = [];
517
- const text = answer.replace(BLOCK, (_m, kind, body) => {
520
+ const text2 = answer.replace(BLOCK, (_m, kind, body) => {
518
521
  found.push({ kind, raw: body.trim() });
519
522
  return "";
520
523
  });
521
524
  const last = found.at(-1);
522
- return last ? { text: text.replace(/\n{3,}/g, "\n\n").trim(), kind: last.kind, raw: last.raw, blocks: found } : { text: answer, kind: null, raw: null, blocks: [] };
525
+ return last ? { text: text2.replace(/\n{3,}/g, "\n\n").trim(), kind: last.kind, raw: last.raw, blocks: found } : { text: answer, kind: null, raw: null, blocks: [] };
523
526
  }
524
527
  function parseRelayBlock(raw) {
525
528
  try {
@@ -1343,6 +1346,7 @@ var DaemonInfoSchema = z21.object({
1343
1346
  });
1344
1347
  var UpdateFailureSchema = z21.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
1345
1348
  var ServerMessageSchema = z21.discriminatedUnion("type", [
1349
+ SessionModelAcceptedSchema,
1346
1350
  z21.object({ type: z21.literal("chat.routed"), qid: z21.string(), result: RouteResultSchema }),
1347
1351
  DocumentContentSchema,
1348
1352
  DocumentErrorSchema,
@@ -1717,8 +1721,8 @@ function pickName(taken, preferred) {
1717
1721
 
1718
1722
  // ../protocol/src/topic.ts
1719
1723
  var TOPIC_MAX = 80;
1720
- function cleanTopic(text) {
1721
- const line = text.split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0) ?? "";
1724
+ function cleanTopic(text2) {
1725
+ const line = text2.split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0) ?? "";
1722
1726
  const flat = line.replace(/\s+/g, " ");
1723
1727
  return flat.length > TOPIC_MAX ? `${flat.slice(0, TOPIC_MAX - 1).trimEnd()}\u2026` : flat;
1724
1728
  }
@@ -1903,13 +1907,28 @@ var THREAD_ITEMS_MAX = 200;
1903
1907
  var THREAD_BUDGET = 524288;
1904
1908
  var THREAD_TEXT_MAX = 8192;
1905
1909
  var TOOL_TEXT_MAX = 2048;
1910
+ var CHECKLIST_TASKS_MAX = 50;
1911
+ var CHECKLIST_TEXT_MAX = 200;
1912
+ var ChecklistStatusSchema = z26.enum(["pending", "in_progress", "completed"]);
1913
+ var PhoneChecklistSchema = z26.object({
1914
+ tasks: z26.array(z26.object({ text: z26.string().max(CHECKLIST_TEXT_MAX), status: ChecklistStatusSchema })).max(CHECKLIST_TASKS_MAX),
1915
+ done: z26.number().int().nonnegative(),
1916
+ total: z26.number().int().nonnegative(),
1917
+ cut: z26.literal(true).optional()
1918
+ });
1919
+ var PhoneChecklistSummarySchema = z26.object({
1920
+ done: z26.number().int().nonnegative(),
1921
+ total: z26.number().int().nonnegative(),
1922
+ current: z26.string().max(CHECKLIST_TEXT_MAX).optional(),
1923
+ cut: z26.literal(true).optional()
1924
+ });
1906
1925
  var PENDING_BODY_MAX = 65536;
1907
1926
  var SNAPSHOT_BODY_MAX = 2048;
1908
1927
  var RiskSchema = z26.enum(["low", "high"]);
1909
1928
  var RiskReasonSchema = z26.enum(["question", "read_only_command", "edit_inside_folder", "pushes", "destroys", "deploys", "publishes", "pipes_to_shell", "network", "writes_outside_folder", "sensitive_path", "unsafe_flag", "shell_syntax", "script", "plan", "unknown"]);
1910
1929
  var OperationIdSchema = z26.string().uuid();
1911
1930
  var OperationStatusSchema = z26.enum(["received", "applied", "rejected", "failed"]);
1912
- var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "changed", "exists", "unknown_project", "seat_failed"]);
1931
+ var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "changed", "exists", "unknown_project", "seat_failed", "scheduled", ...ModelRefusalSchema.options]);
1913
1932
  var CapabilitiesSchema = z26.object({
1914
1933
  /** A question can be answered in free text. */
1915
1934
  freeTextAnswer: z26.boolean(),
@@ -1921,7 +1940,8 @@ var CapabilitiesSchema = z26.object({
1921
1940
  var UNTYPABLE = /[\u0000-\u001f\u007f-\u009f\u200b\u200e\u200f\u2028-\u202e\u2066-\u2069\ufeff]/;
1922
1941
  var CapabilitiesByToolSchema = z26.object({ claude: CapabilitiesSchema, codex: CapabilitiesSchema, cursor: CapabilitiesSchema, omp: CapabilitiesSchema });
1923
1942
  var PhonePendingSchema = PendingSchema.extend({ key: z26.string().max(64), risk: RiskSchema, riskReason: RiskReasonSchema, cut: z26.literal(true).optional() });
1924
- var PhoneSessionSchema = SessionSchema.pick({
1943
+ var SwitchableSessionSchema = SessionSchema.extend({ canSwitch: z26.literal(true).optional() });
1944
+ var PhoneSessionSchema = SwitchableSessionSchema.pick({
1925
1945
  id: true,
1926
1946
  name: true,
1927
1947
  tool: true,
@@ -1936,6 +1956,11 @@ var PhoneSessionSchema = SessionSchema.pick({
1936
1956
  lastReply: true,
1937
1957
  startedAt: true,
1938
1958
  lastActivityAt: true,
1959
+ /** Its model, effort and the change waiting for its next free moment: the phone shows them and offers to change them. */
1960
+ model: true,
1961
+ effort: true,
1962
+ modelNext: true,
1963
+ canSwitch: true,
1939
1964
  /** Its role and its live team (ledge home spec §2.4): the agents screen groups a project's sessions by table. */
1940
1965
  role: true,
1941
1966
  roleLabel: true,
@@ -1943,7 +1968,13 @@ var PhoneSessionSchema = SessionSchema.pick({
1943
1968
  /** Its context and its relief (context handover spec §3.2): the phone shows the gauge; the relief is started from the PC. */
1944
1969
  context: true,
1945
1970
  relieves: true
1946
- }).extend({ pending: PhonePendingSchema.optional(), unread: z26.boolean(), handover: z26.object({ state: HandoverStateSchema, toName: z26.string().max(80).optional() }).strict().optional() });
1971
+ }).extend({
1972
+ pending: PhonePendingSchema.optional(),
1973
+ unread: z26.boolean(),
1974
+ handover: z26.object({ state: HandoverStateSchema, toName: z26.string().max(80).optional() }).strict().optional(),
1975
+ /** Absent: the agent has no checklist. */
1976
+ checklist: PhoneChecklistSummarySchema.optional()
1977
+ });
1947
1978
  var B64U2 = z26.string().regex(/^[A-Za-z0-9_-]*$/);
1948
1979
  var CredentialSchema = z26.object({
1949
1980
  id: B64U2.max(1366),
@@ -1960,7 +1991,7 @@ var AssertionSchema = z26.object({
1960
1991
  signature: B64U2.max(1024)
1961
1992
  });
1962
1993
  var TeamsDigestSchema = z26.string().regex(/^[0-9a-f]{64}$/);
1963
- var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "define", "team"]);
1994
+ var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "define", "team", "pc"]);
1964
1995
  var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
1965
1996
  var PhoneChatProposalSchema = z26.object({
1966
1997
  project: z26.string().max(80),
@@ -2077,6 +2108,8 @@ var sessionId = z26.string().min(1).max(100);
2077
2108
  var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z26.string().min(1).max(64) });
2078
2109
  var CardRowsSchema = z26.array(z26.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
2079
2110
  var teamsOp = { op: OperationIdSchema, assertion: AssertionSchema.optional() };
2111
+ var PcActionSchema = z26.enum(["update", "restart"]);
2112
+ var pcSubject = (action, version) => action === "update" ? `update@${version ?? ""}` : "restart";
2080
2113
  var teamsScope = { scope: DefinitionScopeSchema, project: z26.string().min(1).max(80).optional() };
2081
2114
  var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
2082
2115
  z26.object({ type: z26.literal("snapshot.request") }),
@@ -2094,7 +2127,10 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
2094
2127
  expect: PhoneExpectSchema.optional(),
2095
2128
  qid: OperationIdSchema.optional(),
2096
2129
  /** sha256 of the `define` or `team` operation the challenge is for (`teamsDigest`). */
2097
- digest: TeamsDigestSchema.optional()
2130
+ digest: TeamsDigestSchema.optional(),
2131
+ /** A `pc` challenge: what it is for, and for an update the version it covers. */
2132
+ action: PcActionSchema.optional(),
2133
+ version: z26.string().regex(SEMVER).optional()
2098
2134
  }),
2099
2135
  z26.object({ type: z26.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
2100
2136
  z26.object({
@@ -2129,6 +2165,10 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
2129
2165
  expect: PhoneExpectSchema
2130
2166
  }),
2131
2167
  z26.object({ type: z26.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
2168
+ /** Changes a live session's model and effort, `null` for the agent's own default; answered by `operation.result` and, when refused, `session.model.failed`. */
2169
+ z26.object({ type: z26.literal("session.model"), op: OperationIdSchema, id: sessionId, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
2170
+ /** The models an agent offers on the PC. */
2171
+ z26.object({ type: z26.literal("models.list"), tool: ToolSchema }),
2132
2172
  /** The phone drops its passkey from the daemon. */
2133
2173
  z26.object({ type: z26.literal("device.forget"), op: OperationIdSchema }),
2134
2174
  /** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). */
@@ -2181,13 +2221,38 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
2181
2221
  z26.object({ type: z26.literal("team.launch"), ...teamsOp, project: z26.string().min(1).max(80), name: TeamNameSchema, seats: PhoneSeatsSchema, template: z26.string().min(1).max(64).optional() }).strict(),
2182
2222
  z26.object({ type: z26.literal("team.compose"), ...teamsOp, team: TeamIdSchema, seats: PhoneSeatsSchema }).strict(),
2183
2223
  z26.object({ type: z26.literal("team.seat"), ...teamsOp, team: TeamIdSchema, role: RoleIdSchema, action: z26.enum(["start", "restart"]) }).strict(),
2184
- z26.object({ type: z26.literal("team.dissolve"), ...teamsOp, team: TeamIdSchema }).strict()
2224
+ z26.object({ type: z26.literal("team.dissolve"), ...teamsOp, team: TeamIdSchema }).strict(),
2225
+ /** Installs `version` on the PC, then restarts its daemon; the phone asks for its own build's version. Passkey, purpose `pc`. */
2226
+ z26.object({ type: z26.literal("daemon.update"), ...teamsOp, version: z26.string().regex(SEMVER) }).strict(),
2227
+ /** Restarts the PC's daemon without installing anything. Passkey, purpose `pc`. */
2228
+ z26.object({ type: z26.literal("daemon.restart"), ...teamsOp }).strict()
2185
2229
  ]).superRefine((m, ctx) => {
2186
2230
  const issue = "scope" in m ? scopeIssue(m) : null;
2187
2231
  if (issue) ctx.addIssue({ code: z26.ZodIssueCode.custom, message: issue, path: ["project"] });
2188
2232
  });
2233
+ var PhoneModelChoiceSchema = z26.object({
2234
+ id: ModelChoiceSchema.shape.id.max(120),
2235
+ label: z26.string().max(80),
2236
+ description: z26.string().max(200).optional(),
2237
+ efforts: z26.array(EffortSchema).max(8).optional(),
2238
+ defaultEffort: EffortSchema.optional(),
2239
+ isDefault: z26.boolean().optional()
2240
+ }).strict();
2241
+ var PhoneSeatModelSchema = z26.object({ role: z26.string().max(40), session: sessionId, status: z26.enum(["applied", "scheduled", "refused"]), reason: ModelRefusalSchema.optional() });
2189
2242
  var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
2190
- z26.object({ type: z26.literal("snapshot"), protocol: z26.literal(PHONE_PROTOCOL), sessions: z26.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z26.object({ version: z26.string(), name: z26.string().max(40), boot: z26.string().uuid().optional() }), device: z26.object({ confirm: z26.boolean(), push: z26.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z26.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
2243
+ z26.object({ type: z26.literal("snapshot"), protocol: z26.literal(PHONE_PROTOCOL), sessions: z26.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z26.object({
2244
+ version: z26.string(),
2245
+ name: z26.string().max(40),
2246
+ boot: z26.string().uuid().optional(),
2247
+ teams: z26.literal(true).optional(),
2248
+ /** The daemon takes `session.model`. */
2249
+ models: z26.literal(true).optional(),
2250
+ /** The daemon sends `session.checklist` and a session's `checklist` summary. */
2251
+ checklist: z26.literal(true).optional(),
2252
+ /** `update`: the daemon can install a version and restart (npm, systemd); `restart`: it can restart (systemd). */
2253
+ update: z26.literal(true).optional(),
2254
+ restart: z26.literal(true).optional()
2255
+ }), device: z26.object({ confirm: z26.boolean(), push: z26.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z26.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
2191
2256
  z26.object({
2192
2257
  type: z26.literal("confirm.challenge"),
2193
2258
  purpose: ConfirmPurposeSchema,
@@ -2200,13 +2265,19 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
2200
2265
  }),
2201
2266
  z26.object({ type: z26.literal("session.updated"), session: PhoneSessionSchema }),
2202
2267
  z26.object({ type: z26.literal("session.removed"), id: z26.string() }),
2268
+ /** A session's checklist, sent to the phone that watches it; `null`: it has none (any more). */
2269
+ z26.object({ type: z26.literal("session.checklist"), id: z26.string(), checklist: PhoneChecklistSchema.nullable() }),
2203
2270
  /** `pending.cut` is set when even this body was shortened. */
2204
2271
  z26.object({ type: z26.literal("session.pending"), id: z26.string(), pending: PhonePendingSchema }),
2205
2272
  z26.object({ type: z26.literal("session.preview"), id: z26.string(), lines: z26.array(z26.string()) }),
2206
2273
  /** `cut`: the ids of the items shortened; `more`: older items were left out. */
2207
2274
  z26.object({ type: z26.literal("session.thread"), id: z26.string(), items: z26.array(ThreadItemSchema), cut: z26.array(z26.string()), more: z26.boolean() }),
2208
2275
  z26.object({ type: z26.literal("session.thread.item"), id: z26.string(), item: ThreadItemSchema, cut: z26.literal(true).optional() }),
2209
- z26.object({ type: z26.literal("operation.result"), op: OperationIdSchema, status: OperationStatusSchema, reason: OperationReasonSchema.optional() }),
2276
+ z26.object({ type: z26.literal("operation.result"), op: OperationIdSchema, status: OperationStatusSchema, reason: OperationReasonSchema.optional(), models: z26.array(PhoneSeatModelSchema).max(16).optional() }),
2277
+ /** The models an agent offers; `error`: it could not be asked, `models` is then what Ledge knows without it. */
2278
+ z26.object({ type: z26.literal("models.listing"), tool: ToolSchema, models: z26.array(PhoneModelChoiceSchema).max(40), error: z26.literal(true).optional() }),
2279
+ /** A model change that was accepted and then failed (the agent could not restart). */
2280
+ z26.object({ type: z26.literal("session.model.failed"), id: z26.string(), reason: ModelRefusalSchema }),
2210
2281
  z26.object({ type: z26.literal("chat.history"), turns: z26.array(PhoneChatTurnSchema).max(50) }),
2211
2282
  z26.object({ type: z26.literal("chat.step"), qid: OperationIdSchema, step: PhoneChatStepSchema }),
2212
2283
  /** The whole text so far, not an increment. */
@@ -2214,6 +2285,10 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
2214
2285
  z26.object({ type: z26.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
2215
2286
  z26.object({ type: z26.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
2216
2287
  z26.object({ type: z26.literal("teams.catalog"), catalog: PhoneCatalogSchema }),
2288
+ /** The daemon updating or restarting itself, whoever asked: npm runs, then the service restarts (the link drops right after). */
2289
+ z26.object({ type: z26.literal("daemon.progress"), version: z26.string().max(40), step: z26.enum(["installing", "restarting"]) }),
2290
+ /** Nothing was changed. Never npm's output nor a command: both hold the PC's paths. */
2291
+ z26.object({ type: z26.literal("daemon.failed"), version: z26.string().max(40), reason: UpdateFailureSchema }),
2217
2292
  z26.object({ type: z26.literal("teams.role"), role: PhoneRoleSchema }),
2218
2293
  /** The live teams, after each snapshot and on every change. */
2219
2294
  z26.object({ type: z26.literal("teams"), teams: z26.array(PhoneTeamSchema).max(50) }),
@@ -2235,6 +2310,60 @@ async function teamsDigest(m) {
2235
2310
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2236
2311
  }
2237
2312
 
2313
+ // ../protocol/src/todos.ts
2314
+ var TODO_TOOLS = /* @__PURE__ */ new Set(["TodoWrite", "update_plan", "todo"]);
2315
+ var STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
2316
+ var statusOf = (s) => typeof s === "string" && STATUSES.has(s) ? s : "pending";
2317
+ var text = (s) => typeof s === "string" && s.trim() !== "" ? s : null;
2318
+ var record = (v) => v && typeof v === "object" ? v : {};
2319
+ function omp(input) {
2320
+ if (!Array.isArray(input.phases)) return void 0;
2321
+ const phases = input.phases.map(record);
2322
+ const out = [];
2323
+ for (const ph of phases) {
2324
+ for (const raw of Array.isArray(ph.tasks) ? ph.tasks : []) {
2325
+ const e = record(raw);
2326
+ const said = text(e.content);
2327
+ if (said === null) continue;
2328
+ const blocked = e.status === "blocked";
2329
+ const name = phases.length > 1 ? text(ph.name) : null;
2330
+ out.push({ text: `${name ? `${name} \xB7 ` : ""}${said}${blocked && text(e.blocker) ? ` (blocked: ${e.blocker})` : ""}`, status: blocked ? "pending" : statusOf(e.status) });
2331
+ }
2332
+ }
2333
+ return out;
2334
+ }
2335
+ function listOf(t) {
2336
+ if (!TODO_TOOLS.has(t.tool) || t.input === void 0) return void 0;
2337
+ let input;
2338
+ try {
2339
+ input = record(JSON.parse(t.input));
2340
+ } catch {
2341
+ return void 0;
2342
+ }
2343
+ if (t.tool === "todo") return omp(input);
2344
+ const claude = t.tool === "TodoWrite";
2345
+ const list = claude ? input.todos : input.plan;
2346
+ if (!Array.isArray(list)) return void 0;
2347
+ const out = [];
2348
+ for (const raw of list) {
2349
+ const e = record(raw);
2350
+ const status = statusOf(e.status);
2351
+ const said = claude ? status === "in_progress" ? text(e.activeForm) ?? text(e.content) : text(e.content) : text(e.step);
2352
+ if (said !== null) out.push({ text: said, status });
2353
+ }
2354
+ return out;
2355
+ }
2356
+ function latestTodos(items, helperId) {
2357
+ for (let i = items.length - 1; i >= 0; i--) {
2358
+ const it = items[i];
2359
+ if (it.kind !== "tool" || it.helperId !== helperId || !TODO_TOOLS.has(it.tool)) continue;
2360
+ const list = listOf(it);
2361
+ if (list) return list.length > 0 ? list : null;
2362
+ }
2363
+ return null;
2364
+ }
2365
+ var doneCount = (todos) => todos.filter((x) => x.status === "completed").length;
2366
+
2238
2367
  // src/auth.ts
2239
2368
  import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
2240
2369
  import { chmod, link, mkdir, readFile, unlink, writeFile } from "fs/promises";
@@ -2379,6 +2508,7 @@ export {
2379
2508
  isArchived,
2380
2509
  ModelIdSchema,
2381
2510
  EffortSchema,
2511
+ ModelRefusalSchema,
2382
2512
  extractProposal,
2383
2513
  parseRelayBlock,
2384
2514
  parseCloseBlock,
@@ -2429,6 +2559,7 @@ export {
2429
2559
  PROTOCOL_PORT,
2430
2560
  RefusalCodeSchema,
2431
2561
  RecentSchema,
2562
+ SEMVER,
2432
2563
  MESSAGE_MAX,
2433
2564
  SUMMARY_MAX,
2434
2565
  parseClientMessage,
@@ -2458,14 +2589,21 @@ export {
2458
2589
  THREAD_BUDGET,
2459
2590
  THREAD_TEXT_MAX,
2460
2591
  TOOL_TEXT_MAX,
2592
+ CHECKLIST_TASKS_MAX,
2593
+ CHECKLIST_TEXT_MAX,
2461
2594
  PENDING_BODY_MAX,
2462
2595
  SNAPSHOT_BODY_MAX,
2463
2596
  OperationIdSchema,
2464
2597
  UNTYPABLE,
2465
2598
  CHAT_HISTORY_BUDGET,
2599
+ pcSubject,
2466
2600
  PhoneClientMessageSchema,
2601
+ PhoneModelChoiceSchema,
2467
2602
  teamsPurpose,
2468
2603
  teamsDigest,
2604
+ TODO_TOOLS,
2605
+ latestTodos,
2606
+ doneCount,
2469
2607
  tokenEquals,
2470
2608
  hostAllowed,
2471
2609
  requestAuthorized,
@@ -4,7 +4,7 @@ import {
4
4
  SUMMARY_MAX,
5
5
  parseServerMessage,
6
6
  readDaemonToken
7
- } from "./chunk-QRD74KPO.js";
7
+ } from "./chunk-VKRFHDRK.js";
8
8
 
9
9
  // src/client.ts
10
10
  import WebSocket from "ws";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ledgeTitleRest
3
- } from "./chunk-PTY4LXTG.js";
3
+ } from "./chunk-LG3XFIPV.js";
4
4
  import {
5
5
  cleanTopic,
6
6
  isArchived,
7
7
  sessionTitle
8
- } from "./chunk-QRD74KPO.js";
8
+ } from "./chunk-VKRFHDRK.js";
9
9
 
10
10
  // src/daemon/seat.ts
11
11
  function teammates(team, sessions) {
@@ -9,9 +9,9 @@ import {
9
9
  startServer,
10
10
  tasksJournal,
11
11
  teamsJournal
12
- } from "./chunk-2V2OJR2N.js";
12
+ } from "./chunk-OYDTU6AP.js";
13
13
  import "./chunk-OLLQBK6N.js";
14
- import "./chunk-HCTXTN2X.js";
14
+ import "./chunk-QSRB3F5Y.js";
15
15
  import {
16
16
  QUESTION_TOOLS,
17
17
  crossSession,
@@ -26,11 +26,11 @@ import {
26
26
  readPeers,
27
27
  reduce,
28
28
  stripNote
29
- } from "./chunk-TYNRKZFE.js";
30
- import "./chunk-II6VRGLU.js";
29
+ } from "./chunk-ZI47U5CQ.js";
30
+ import "./chunk-JHFKBKSO.js";
31
31
  import {
32
32
  agentMessages
33
- } from "./chunk-PTY4LXTG.js";
33
+ } from "./chunk-LG3XFIPV.js";
34
34
  import {
35
35
  DEFAULT_RELAY_URL,
36
36
  DevicePollResponseSchema,
@@ -44,10 +44,10 @@ import {
44
44
  SealedFrameSchema,
45
45
  SessionSchema,
46
46
  backendOf
47
- } from "./chunk-QRD74KPO.js";
47
+ } from "./chunk-VKRFHDRK.js";
48
48
  import {
49
49
  LEDGE_VERSION
50
- } from "./chunk-3PLC745L.js";
50
+ } from "./chunk-UME2F7VM.js";
51
51
  import {
52
52
  SERVICE,
53
53
  inDaemonService,
@@ -799,6 +799,16 @@ var npmEnv = (execPath, env) => ({ ...env, PATH: `${dirname3(execPath)}:${env.PA
799
799
  function createDaemonUpdater(deps) {
800
800
  let busy = false;
801
801
  let restarting = false;
802
+ const restartService = async (version, send) => {
803
+ send({ type: "update.progress", version, step: "restarting" });
804
+ await deps.save();
805
+ const code = await deps.restart();
806
+ if (code !== 0) {
807
+ log(`update: systemctl restart failed (${code})`);
808
+ return send({ type: "update.failed", version, reason: "no-systemd", command: RESTART_COMMAND });
809
+ }
810
+ restarting = true;
811
+ };
802
812
  return {
803
813
  running: () => busy,
804
814
  /** `reply` reaches only the asking client: a busy refusal must not fail the flow other overlays are following. */
@@ -820,20 +830,29 @@ function createDaemonUpdater(deps) {
820
830
  ${r.output}`);
821
831
  return send({ type: "update.failed", version, reason: "npm-failed", detail: lastLines(r.output), command: shown });
822
832
  }
823
- send({ type: "update.progress", version, step: "restarting" });
824
- await deps.save();
825
- const code = await deps.restart();
826
- if (code !== 0) {
827
- log(`update: systemctl restart failed (${code})`);
828
- return send({ type: "update.failed", version, reason: "no-systemd", command: RESTART_COMMAND });
829
- }
830
- restarting = true;
833
+ await restartService(version, send);
831
834
  } catch (e) {
832
835
  log(`update: ${e.stack ?? String(e)}`);
833
836
  send({ type: "update.failed", version, reason: "npm-failed", detail: e instanceof Error ? e.message : String(e) });
834
837
  } finally {
835
838
  if (!restarting) busy = false;
836
839
  }
840
+ },
841
+ /** The restart alone, for the phone (spec 2026-10-28 mobile daemon update): the update's guards but npm, then the same restart. */
842
+ async restart(send, reply = send) {
843
+ const version = deps.info().version;
844
+ if (busy) return reply({ type: "update.failed", version, reason: "busy" });
845
+ if (!deps.info().systemd) return send({ type: "update.failed", version, reason: "no-systemd", command: RESTART_COMMAND });
846
+ busy = true;
847
+ try {
848
+ if (await deps.atRisk()) return send({ type: "update.failed", version, reason: "sessions-at-risk" });
849
+ await restartService(version, send);
850
+ } catch (e) {
851
+ log(`restart: ${e.stack ?? String(e)}`);
852
+ send({ type: "update.failed", version, reason: "no-systemd", command: RESTART_COMMAND });
853
+ } finally {
854
+ if (!restarting) busy = false;
855
+ }
837
856
  }
838
857
  };
839
858
  }
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  CODEX_HOOK_VAR
3
- } from "./chunk-PTY4LXTG.js";
3
+ } from "./chunk-LG3XFIPV.js";
4
4
  import {
5
5
  connect
6
- } from "./chunk-NYGLT5G2.js";
6
+ } from "./chunk-VMGACWNP.js";
7
7
  import {
8
8
  LEDGE_GIVER,
9
9
  TASK_TEXT_MAX
10
- } from "./chunk-QRD74KPO.js";
10
+ } from "./chunk-VKRFHDRK.js";
11
11
  import "./chunk-HOOHS3MX.js";
12
12
  import "./chunk-U67V476Y.js";
13
13
 
@@ -3,10 +3,10 @@ import {
3
3
  handoverArgs,
4
4
  handoverOnClient,
5
5
  runHandover
6
- } from "./chunk-L3GYGJRE.js";
7
- import "./chunk-PTY4LXTG.js";
8
- import "./chunk-NYGLT5G2.js";
9
- import "./chunk-QRD74KPO.js";
6
+ } from "./chunk-JLUHY5AB.js";
7
+ import "./chunk-LG3XFIPV.js";
8
+ import "./chunk-VMGACWNP.js";
9
+ import "./chunk-VKRFHDRK.js";
10
10
  import "./chunk-HOOHS3MX.js";
11
11
  import "./chunk-U67V476Y.js";
12
12
  export {
@@ -8,36 +8,36 @@ import {
8
8
  seatContext,
9
9
  takeImages,
10
10
  teammates
11
- } from "./chunk-TYNRKZFE.js";
11
+ } from "./chunk-ZI47U5CQ.js";
12
12
  import {
13
13
  findRole
14
- } from "./chunk-II6VRGLU.js";
14
+ } from "./chunk-JHFKBKSO.js";
15
15
  import {
16
16
  parseLedgeCommand,
17
17
  readOnClient,
18
18
  sendOnClient
19
- } from "./chunk-6F6JTPMC.js";
19
+ } from "./chunk-3CF4FQWG.js";
20
20
  import {
21
21
  taskOnClient
22
- } from "./chunk-5PTC732E.js";
22
+ } from "./chunk-CIQAFCX3.js";
23
23
  import {
24
24
  handoverOnClient
25
- } from "./chunk-L3GYGJRE.js";
25
+ } from "./chunk-JLUHY5AB.js";
26
26
  import {
27
27
  CODEX_ENV_VAR,
28
28
  CODEX_HOOK_VAR,
29
29
  codexPlan
30
- } from "./chunk-PTY4LXTG.js";
30
+ } from "./chunk-LG3XFIPV.js";
31
31
  import {
32
32
  formatWho
33
- } from "./chunk-JHUIHBC7.js";
33
+ } from "./chunk-3NQNWGDX.js";
34
34
  import {
35
35
  connect,
36
36
  sendMessageAndAwaitAck
37
- } from "./chunk-NYGLT5G2.js";
37
+ } from "./chunk-VMGACWNP.js";
38
38
  import {
39
39
  taskClosed
40
- } from "./chunk-QRD74KPO.js";
40
+ } from "./chunk-VKRFHDRK.js";
41
41
  import {
42
42
  log
43
43
  } from "./chunk-6IYPRUQG.js";