@thurstonsand/pi-agent-switchboard 0.10.0 → 0.12.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 (3) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/index.js +70 -61
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.12.0] - 2026-11-05
6
+
7
+ Schema v10: update swb and the pi line together.
8
+
9
+ ### Changed
10
+
11
+ - swb's tools and their arguments take names that say what the agent is choosing, all in snake_case: `session_reachable` is `session_list`, `session_ask` is `session_recall`, and `session_schedules` splits into `session_list_schedules` and `session_unschedule`. `session_launch`'s `goal` is `briefing`; `requestResponse` is `report_back`; a schedule's `label` is `schedule_label`; `swb_update_session`'s `force` is `cancel_turns` and `deleteWorktree` is `delete_worktree`; and the reader's `recall_read` takes `tool_output: trimmed | whole` in place of `body: preview | full`.
12
+ - The CLI follows: `swb reachable` is `swb list`, `swb schedules` is `swb list-schedules`, `--request-response` is `--report-back`, and `--label` is `--schedule-label`.
13
+ - swb's e2e runs pi-sessions 0.23.0, which takes the same names for its own tools.
14
+
15
+ ### Fixed
16
+
17
+ - A Claude Code session that snoozes itself until its next message, or archives itself, mid-turn stays that way. Its tool call could land before swb recorded the turn's start, and that record then counted as the next prompt, ending the snooze or undoing the archive.
18
+ - A working session no longer moves to Inactive when its turn outlasts `inactive_after`. Resolving a long-held attention prompt could hide the session and switch the Stage away before its reply landed, leaving that reply Unseen.
19
+ - Codex errors no longer go unmarked when its transcript writes lag behind its live status. swb records the error when Codex reports it, before settling the turn, instead of checking a history entry that may not yet contain it.
20
+ - Claude Code on a machine with managed settings takes a title set through swb with its next prompt, as everywhere else, and a conversation swb resumed in its Project root says so, to you and to the model. Its built-in `cc-plugin-sec-default` mod skipped the line's last two classic hooks, which carried both, and posted a notice for each; the line now hands them over through command hooks, which that mod leaves alone.
21
+ - In Claude Code, `session_recall` returns as soon as its reader starts, and the reader's answer arrives as a message once it's done, as a subagent's does. A Claude Code session in auto mode no longer gets the answer twice: Claude Code made the reader report through SubagentHandback, which posted the answer beside the tool's result.
22
+
23
+ ## [0.11.0] - 2026-11-03
24
+
25
+ Schema v9: update swb and the pi line together.
26
+
27
+ ### Added
28
+
29
+ - `u` in the roster, and `M-a u` from the agent, marks a session Unseen: its `●` shows at once, and holds for as long as the session stays on the Stage, through any turns it takes, and after you move on. Only coming back to it from another session, for the second a Visit takes, clears it; `u` again takes the mark off. Snoozed and archived sessions can't be marked.
30
+ - `[` and `]` in the roster step back and forward through the sessions you visited, as a browser's back and forward do, and `‹ ›` at the end of the legend does the same with a click. From the agent, `M-a [` and `M-a ]` focus the session there, waking it. A Visit counts as it does for Unseen, a second with the agent focused, so moving the cursor over a session doesn't. A new Deck reaches back through every earlier Visit. Archived sessions are passed over; snoozed ones aren't.
31
+
32
+ ### Changed
33
+
34
+ - An idle session nobody is viewing now stops after an hour by default, not 30 minutes, matching Claude Code's hour-long prompt cache. `reap_after` still sets it.
35
+ - The Deck lists its sessions sooner. It used to ask git, one directory at a time, about every directory any session ever ran in, archived ones included, before showing any; it now asks only about open sessions below their Project's root, all at once, and asks the harnesses what they offer after the list is up. A Deck opened when swb's tmux servers were down also no longer leaves its Stage blank for an extra 0.2 s.
36
+
37
+ ### Fixed
38
+
39
+ - `?` shows every key again. Its popup had a fixed size the list outgrew, so its first keys scrolled off the top, and on a Deck under 37 rows it didn't open at all. It now fits the list, short of the Deck's own size, and when the Deck is too short it scrolls by `j`/`k`, `↑`/`↓`, `space` and PgUp/PgDn, or the wheel, saying how much lies above and below.
40
+
5
41
  ## [0.10.0] - 2026-11-02
6
42
 
7
43
  ### Added
package/index.js CHANGED
@@ -5,10 +5,11 @@ import { dirname as dirname4, join as join3 } from "node:path";
5
5
  import { Text } from "@earendil-works/pi-tui";
6
6
 
7
7
  // packages/shared/src/index.ts
8
- import { execFileSync } from "node:child_process";
8
+ import { execFile, execFileSync } from "node:child_process";
9
9
  import { existsSync as existsSync2, readFileSync, realpathSync } from "node:fs";
10
10
  import { homedir } from "node:os";
11
11
  import { dirname as dirname2, join } from "node:path";
12
+ import { promisify } from "node:util";
12
13
 
13
14
  // packages/shared/src/db.ts
14
15
  import { closeSync, existsSync, linkSync, mkdirSync, openSync, unlinkSync } from "node:fs";
@@ -89,7 +90,7 @@ class Db {
89
90
  }
90
91
 
91
92
  // packages/shared/src/index.ts
92
- var SCHEMA_VERSION = 8;
93
+ var SCHEMA_VERSION = 10;
93
94
  var VIEWS = ["agent", "editor", "split"];
94
95
  var HARNESSES = ["pi", "claude", "codex"];
95
96
  var SNOOZE_PRESETS = ["one_hour", "one_day", "monday", "next_message"];
@@ -140,25 +141,34 @@ function pidAlive(pid) {
140
141
  return error.code === "EPERM";
141
142
  }
142
143
  }
144
+ var GIT_TIMEOUT_MS = 5000;
143
145
  function git(cwd, ...args) {
144
146
  try {
145
- return execFileSync("git", ["-C", cwd, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }).trim();
147
+ return execFileSync("git", ["-C", cwd, ...args], {
148
+ encoding: "utf8",
149
+ stdio: ["ignore", "pipe", "ignore"],
150
+ timeout: GIT_TIMEOUT_MS
151
+ }).trim();
146
152
  } catch {
147
153
  return null;
148
154
  }
149
155
  }
156
+ var execFileAsync = promisify(execFile);
150
157
  function projectRoot(cwd) {
151
158
  const common = git(cwd, "rev-parse", "--path-format=absolute", "--git-common-dir");
152
159
  return common ? dirname2(common) : cwd;
153
160
  }
154
- function linkedWorktree(cwd) {
155
- const result = git(cwd, "rev-parse", "--path-format=absolute", "--show-toplevel", "--git-dir", "--git-common-dir");
161
+ var WORKTREE_QUERY = ["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-dir", "--git-common-dir"];
162
+ function worktreeFrom(result) {
156
163
  if (!result)
157
164
  return null;
158
165
  const [path, dir, common] = result.split(`
159
166
  `);
160
167
  return dir !== common ? { path, common } : null;
161
168
  }
169
+ function linkedWorktree(cwd) {
170
+ return worktreeFrom(git(cwd, ...WORKTREE_QUERY));
171
+ }
162
172
  function branch(cwd) {
163
173
  const name = git(cwd, "rev-parse", "--abbrev-ref", "HEAD");
164
174
  return name === "HEAD" ? git(cwd, "rev-parse", "--short", "HEAD") : name;
@@ -299,14 +309,14 @@ function harnessName(harness) {
299
309
  return HARNESS_NAMES[harness];
300
310
  }
301
311
  function pendingFor(db, targetId) {
302
- return db.all(`SELECT id, kind, source_id, target_id, body, request_response, created_at, schedule_id, scheduled_for FROM messages
312
+ return db.all(`SELECT id, kind, source_id, target_id, body, report_back, created_at, schedule_id, scheduled_for FROM messages
303
313
  WHERE target_id = ? AND delivered_at IS NULL AND cancelled_at IS NULL ORDER BY created_at, rowid`, targetId).map((row) => ({
304
314
  id: row.id,
305
315
  kind: row.kind,
306
316
  sourceId: row.source_id,
307
317
  targetId: row.target_id,
308
318
  body: row.body,
309
- requestResponse: row.request_response === 1,
319
+ reportBack: row.report_back === 1,
310
320
  createdAt: row.created_at,
311
321
  scheduleId: row.schedule_id,
312
322
  scheduledFor: row.scheduled_for
@@ -336,9 +346,9 @@ function frame(db, m, now = Date.now()) {
336
346
  if (m.scheduleId)
337
347
  when += ` (schedule: ${m.scheduleId})`;
338
348
  }
339
- const reply = m.requestResponse ? `
349
+ const reply = m.reportBack ? `
340
350
 
341
- Response requested: reply with session_send_message to session ${m.sourceId}.` : "";
351
+ Report back: reply with session_send_message to session ${m.sourceId}.` : "";
342
352
  return `Message from session "${from.title}" (session: ${m.sourceId}${who})${when}:
343
353
 
344
354
  ${m.body}${reply}`;
@@ -348,12 +358,12 @@ ${m.body}${reply}`;
348
358
  import { Type } from "typebox";
349
359
  import { Value } from "typebox/value";
350
360
  var session = Type.String({ description: "The session's id." });
351
- var requestResponse = Type.Boolean({ description: "Whether it should reply to you." });
361
+ var reportBack = Type.Boolean({ description: "Whether it should reply to you." });
352
362
  var TOOLS = {
353
- session_reachable: {
354
- label: "Reachable sessions",
363
+ session_list: {
364
+ label: "Sessions",
355
365
  description: "List every open session but this one, in any project or harness.",
356
- guidelines: ["Call session_reachable only for an id you don't already have."],
366
+ guidelines: ["Call session_list only for an id you don't already have."],
357
367
  parameters: Type.Object({}),
358
368
  readOnly: true
359
369
  },
@@ -364,13 +374,13 @@ var TOOLS = {
364
374
  parameters: Type.Object({
365
375
  session,
366
376
  message: Type.String({ description: "Self-contained: the recipient doesn't see this conversation." }),
367
- requestResponse
377
+ report_back: reportBack
368
378
  }),
369
379
  readOnly: false
370
380
  },
371
381
  session_launch: {
372
382
  label: "Launch",
373
- description: "Start a new session on one of the presets below. With at or cron and a label, as session_schedule takes them, launch later instead, or anew each time the cron fires.",
383
+ description: "Start a new session on one of the presets below. With at or cron and a schedule_label, as session_schedule takes them, launch later instead, or anew each time the cron fires.",
374
384
  guidelines: [
375
385
  "Launch a new session when the work is independent of this one, belongs to another directory, or the user asks. For part of this work you need back this turn, use your harness's own subagents, if it has them.",
376
386
  "Omit session_launch's preset fields to run what this session runs; otherwise pass the fields of the preset whose note fits the work."
@@ -380,18 +390,18 @@ var TOOLS = {
380
390
  model: Type.Optional(Type.String()),
381
391
  effort: Type.Optional(Type.String()),
382
392
  agent: Type.Optional(Type.String()),
383
- goal: Type.String({
384
- description: "Its first message: a self-contained briefing of the objective, context, constraints, and what done means."
393
+ briefing: Type.String({
394
+ description: "Its first message, self-contained: the objective, context, constraints, and what done means."
385
395
  }),
386
396
  cwd: Type.Optional(Type.String({ description: "Defaults to this session's." })),
387
397
  title: Type.String({ description: "Short, for the roster." }),
388
398
  group: Type.Optional(Type.Union([Type.String(), Type.Null()], {
389
399
  description: "Omit for this session's Group within its Project, or pass a Group's name, in any Project; null for none."
390
400
  })),
391
- requestResponse: Type.Boolean({ description: "Whether it reports back when done." }),
401
+ report_back: Type.Boolean({ description: "Whether it reports back when done." }),
392
402
  at: Type.Optional(Type.String()),
393
403
  cron: Type.Optional(Type.String()),
394
- label: Type.Optional(Type.String())
404
+ schedule_label: Type.Optional(Type.String())
395
405
  }),
396
406
  readOnly: false
397
407
  },
@@ -399,7 +409,7 @@ var TOOLS = {
399
409
  label: "Schedule",
400
410
  description: "Schedule a message, once or on a cron, to this session or another. session_launch launches on a schedule.",
401
411
  guidelines: [
402
- "Schedule only when the user asks to remind, follow up, check back, or repeat something. To wait on another session, set requestResponse on your message instead.",
412
+ "Schedule only when the user asks to remind, follow up, check back, or repeat something. To wait on another session, set report_back on your message instead.",
403
413
  "Write a scheduled message as instructions for when it fires: what to check, what to do with each outcome, and, if recurring, when to unschedule."
404
414
  ],
405
415
  parameters: Type.Object({
@@ -408,20 +418,25 @@ var TOOLS = {
408
418
  description: 'Once: an ISO 8601 time, local unless it has an offset ("2026-10-23T15:00"), or a duration from now ("PT2H").'
409
419
  })),
410
420
  cron: Type.Optional(Type.String({ description: 'Recurring: 5-field cron in local time ("57 8 * * 1-5" is weekdays at 8:57am).' })),
411
- label: Type.String({ description: 'When it runs, in plain words ("Weekdays, 8:57 AM").' }),
421
+ schedule_label: Type.String({ description: 'When it runs, in plain words ("Weekdays, 8:57 AM").' }),
412
422
  session: Type.Optional(Type.String({ description: "The recipient's id; omit for this session." })),
413
- requestResponse: Type.Optional(requestResponse),
423
+ report_back: Type.Optional(reportBack),
414
424
  snooze: Type.Optional(Type.Boolean({ description: "Snooze the recipient until it fires; only when the user asks." }))
415
425
  }),
416
426
  readOnly: false
417
427
  },
418
- session_schedules: {
428
+ session_list_schedules: {
419
429
  label: "Schedules",
420
- description: "List the schedules this session set or receives, or cancel one.",
430
+ description: "List the schedules this session set or receives.",
431
+ guidelines: [],
432
+ parameters: Type.Object({}),
433
+ readOnly: true
434
+ },
435
+ session_unschedule: {
436
+ label: "Unschedule",
437
+ description: "Cancel a pending schedule set on this session.",
421
438
  guidelines: ["Unschedule only when the user asks, or when the schedule's stated completion condition is met."],
422
- parameters: Type.Object({
423
- unschedule: Type.Optional(Type.String({ description: "A pending schedule's id, to cancel it." }))
424
- }),
439
+ parameters: Type.Object({ schedule: Type.String({ description: "The schedule's id." }) }),
425
440
  readOnly: false
426
441
  },
427
442
  swb_update_session: {
@@ -429,18 +444,18 @@ var TOOLS = {
429
444
  description: "Archive, snooze, group, retitle, or reload a session, or cancel its turn: this one unless you name another.",
430
445
  guidelines: [
431
446
  "Archive a session only when the user asks or you started it and drove it to completion. Snooze or unsnooze a session, or cancel another's turn, only when the user asks.",
432
- "Group sessions when the user asks or to keep sessions you started for one piece of work together, reusing a Group session_reachable lists.",
447
+ "Group sessions when the user asks or to keep sessions you started for one piece of work together, reusing a Group session_list lists.",
433
448
  "Retitle a session when the user asks or its title no longer fits its work.",
434
449
  "Reload a session when the user asks or after changing what it reads only at startup (AGENTS.md, CLAUDE.md, settings, extensions, plugins, skills, MCP servers)."
435
450
  ],
436
451
  parameters: Type.Object({
437
452
  session: Type.Optional(session),
438
453
  archived: Type.Optional(Type.Boolean({ description: "false unarchives." })),
439
- deleteWorktree: Type.Optional(Type.Boolean({
454
+ delete_worktree: Type.Optional(Type.Boolean({
440
455
  description: "With archived true, for this session only: delete its linked worktree, keeping the branch and conversations. With family true, archive its descendants too, but delete only this worktree. Only when the user asks. Refuses modified or untracked files, locked worktrees, scheduled launches into it, or open/live sessions using it outside the selected family. Ignored files are deleted. Conversations whose directory is gone resume in the Project root, or fail clearly if that is gone too. Call last, then end your turn."
441
456
  })),
442
457
  family: Type.Optional(Type.Boolean({ description: "With archived: every open session it started, and theirs, too." })),
443
- force: Type.Optional(Type.Boolean({ description: "With archived: also those mid-turn, blocked, cut off, or errored, cancelling their turns." })),
458
+ cancel_turns: Type.Optional(Type.Boolean({ description: "With archived: also archive those mid-turn, blocked, cut off, or errored, cancelling their turns." })),
444
459
  cancel: Type.Optional(Type.Boolean({ description: "Stop another session's running turn." })),
445
460
  group: Type.Optional(Type.Union([Type.String(), Type.Null()], { description: "The Group to move it into, in its Project; null takes it out." })),
446
461
  title: Type.Optional(Type.String()),
@@ -458,7 +473,7 @@ var TOOLS = {
458
473
  description: "Search other sessions' conversations, open or archived, subagents included. Lists matches best first, with passages.",
459
474
  guidelines: [
460
475
  'In session_search, plain words must all appear, as words or their starts; "phrases" match exactly; OR, -word, and parentheses narrow. Filters: harness:pi|claude|codex, project:NAME, file:PATH (read or changed), after: and before: (a date, an ISO 8601 time, or a duration ago like P7D), archived:yes|no. Filters alone list recent sessions.',
461
- "Question a session_search hit with session_ask."
476
+ "Question a session_search hit with session_recall."
462
477
  ],
463
478
  parameters: Type.Object({
464
479
  query: Type.String({ description: 'Such as "redirect loop" file:src/auth.ts after:P14D.' }),
@@ -466,10 +481,10 @@ var TOOLS = {
466
481
  }),
467
482
  readOnly: true
468
483
  },
469
- session_ask: {
470
- label: "Ask session",
484
+ session_recall: {
485
+ label: "Recall session",
471
486
  description: "Ask about another session's conversation, open or archived. A reader with its own context answers, keeping the transcript out of yours.",
472
- guidelines: ["Ask session_ask focused questions, not for a recap. To ask a session still at work, message it instead."],
487
+ guidelines: ["Ask session_recall focused questions, not for a recap. To ask a session still at work, message it instead."],
473
488
  parameters: Type.Object({ session, question: Type.String({ description: "Self-contained." }) }),
474
489
  readOnly: true
475
490
  },
@@ -496,15 +511,15 @@ var READER_TOOLS = {
496
511
  },
497
512
  recall_read: {
498
513
  label: "Read session",
499
- description: "Read the session's entries in order, from one ref along its branch: prompts, replies, tool calls and their results, summaries, and how a turn ended. Long results are cut in preview; full reads them whole.",
514
+ description: "Read the session's entries in order, from one ref along its branch: prompts, replies, tool calls and their results, summaries, and how a turn ended. trimmed cuts long ones short; whole reads them whole.",
500
515
  guidelines: [],
501
516
  parameters: Type.Object({
502
517
  ref: Type.Optional(Type.String({ description: "The entry to start at, or its first few characters; the session's first by default." })),
503
518
  to: Type.Optional(Type.String({ description: "The entry to stop at, later on the same branch." })),
504
519
  before: Type.Optional(Type.Integer({ minimum: 0, maximum: 100, description: "Entries to include before ref." })),
505
520
  after: Type.Optional(Type.Integer({ minimum: 0, maximum: 500, description: "Entries to read after ref; as many as fit by default." })),
506
- body: Type.Optional(Type.Enum(["preview", "full"], {
507
- description: "preview (the default) cuts tool calls and results short; full reads them whole."
521
+ tool_output: Type.Optional(Type.Enum(["trimmed", "whole"], {
522
+ description: "trimmed (the default) cuts tool calls, their results, and harness context short; whole reads them whole."
508
523
  }))
509
524
  }),
510
525
  readOnly: true
@@ -522,7 +537,7 @@ Rules:
522
537
  - End with your answer as your final message: self-contained, with the files that matter by absolute path.`;
523
538
  var ALL = { ...TOOLS, ...READER_TOOLS };
524
539
  function toolParameters(name, worktree) {
525
- return name === "swb_update_session" && !worktree ? Type.Omit(TOOLS.swb_update_session.parameters, ["deleteWorktree"]) : TOOLS[name].parameters;
540
+ return name === "swb_update_session" && !worktree ? Type.Omit(TOOLS.swb_update_session.parameters, ["delete_worktree"]) : TOOLS[name].parameters;
526
541
  }
527
542
 
528
543
  // packages/shared/src/trace.ts
@@ -846,12 +861,12 @@ ${details.body}`, 0, 0);
846
861
  };
847
862
  const self = (ctx) => ctx.sessionManager.getSessionId();
848
863
  const titleOf2 = (id) => typeof id === "string" ? titleOf(connect(), id) ?? id : "";
849
- tool("session_reachable", () => "", (_args, ctx) => swb(proc.launch.bin, ["reachable", "--from", self(ctx)]));
864
+ tool("session_list", () => "", (_args, ctx) => swb(proc.launch.bin, ["list", "--from", self(ctx)]));
850
865
  tool("session_search", (args) => String(args.query ?? ""), (args, ctx, _toolCallId, signal) => swb(proc.launch.bin, ["search", "--from", self(ctx), ...args.limit ? ["--limit", String(args.limit)] : []], args.query, signal));
851
- tool("session_ask", (args) => titleOf2(args.session), async (args, ctx, _toolCallId, signal) => {
866
+ tool("session_recall", (args) => titleOf2(args.session), async (args, ctx, _toolCallId, signal) => {
852
867
  signal?.throwIfAborted();
853
868
  if (!ctx.model)
854
- throw new Error("No active model is available for session_ask");
869
+ throw new Error("No active model is available for session_recall");
855
870
  const first = await swb(proc.launch.bin, ["recall", "begin", args.session], JSON.stringify({ question: args.question }), signal);
856
871
  const { createAgentSession, DefaultResourceLoader, defineTool, getAgentDir, ModelRuntime, SessionManager } = await import("@earendil-works/pi-coding-agent");
857
872
  const modelRuntime = Reflect.get(ctx.modelRegistry, "runtime");
@@ -930,17 +945,8 @@ ${details.body}`, 0, 0);
930
945
  }
931
946
  }
932
947
  });
933
- tool("session_send_message", (args) => titleOf2(args.session), (args, ctx, toolCallId) => swb(proc.launch.bin, [
934
- "send",
935
- "--from",
936
- self(ctx),
937
- "--to",
938
- args.session,
939
- "--request-id",
940
- toolCallId,
941
- ...args.requestResponse ? ["--request-response"] : []
942
- ], args.message));
943
- tool("session_launch", (args) => `${[args.harness, args.agent, args.model].filter(Boolean).join(" ") || "default"} · ${args.title}${args.label ? ` · ${args.label}` : ""}`, (args, ctx, toolCallId) => swb(proc.launch.bin, [
948
+ tool("session_send_message", (args) => titleOf2(args.session), (args, ctx, toolCallId) => swb(proc.launch.bin, ["send", "--from", self(ctx), "--to", args.session, "--request-id", toolCallId, ...args.report_back ? ["--report-back"] : []], args.message));
949
+ tool("session_launch", (args) => `${[args.harness, args.agent, args.model].filter(Boolean).join(" ") || "default"} · ${args.title}${args.schedule_label ? ` · ${args.schedule_label}` : ""}`, (args, ctx, toolCallId) => swb(proc.launch.bin, [
944
950
  "launch",
945
951
  ...["harness", "model", "effort", "agent"].flatMap((k) => args[k] ? [`--${k}`, args[k]] : []),
946
952
  "--from",
@@ -950,31 +956,34 @@ ${details.body}`, 0, 0);
950
956
  "--title",
951
957
  args.title,
952
958
  ...groupFlags(args.group),
953
- ...args.requestResponse ? ["--request-response"] : [],
954
- ...["at", "cron", "label"].flatMap((k) => args[k] !== undefined ? [`--${k}`, args[k]] : []),
959
+ ...args.report_back ? ["--report-back"] : [],
960
+ ...args.at !== undefined ? ["--at", args.at] : [],
961
+ ...args.cron !== undefined ? ["--cron", args.cron] : [],
962
+ ...args.schedule_label !== undefined ? ["--schedule-label", args.schedule_label] : [],
955
963
  "--request-id",
956
964
  toolCallId
957
- ], args.goal));
958
- tool("session_schedule", (args) => `${args.session ? titleOf2(args.session) : "this session"} · ${args.label}${args.snooze ? " · snooze" : ""}`, (args, ctx, toolCallId) => swb(proc.launch.bin, [
965
+ ], args.briefing));
966
+ tool("session_schedule", (args) => `${args.session ? titleOf2(args.session) : "this session"} · ${args.schedule_label}${args.snooze ? " · snooze" : ""}`, (args, ctx, toolCallId) => swb(proc.launch.bin, [
959
967
  "schedule",
960
968
  "--from",
961
969
  self(ctx),
962
- "--label",
963
- args.label,
970
+ "--schedule-label",
971
+ args.schedule_label,
964
972
  "--request-id",
965
973
  toolCallId,
966
974
  ...args.at !== undefined ? ["--at", args.at] : [],
967
975
  ...args.cron !== undefined ? ["--cron", args.cron] : [],
968
976
  ...args.session !== undefined ? ["--to", args.session] : [],
969
- ...args.requestResponse ? ["--request-response"] : [],
977
+ ...args.report_back ? ["--report-back"] : [],
970
978
  ...args.snooze ? ["--snooze"] : []
971
979
  ], args.message));
972
- tool("session_schedules", (args) => args.unschedule ? `unschedule ${args.unschedule}` : "", (args, ctx) => swb(proc.launch.bin, args.unschedule ? ["unschedule", args.unschedule, "--from", self(ctx)] : ["schedules", "--from", self(ctx)]));
980
+ tool("session_list_schedules", () => "", (_args, ctx) => swb(proc.launch.bin, ["list-schedules", "--from", self(ctx)]));
981
+ tool("session_unschedule", (args) => String(args.schedule ?? ""), (args, ctx) => swb(proc.launch.bin, ["unschedule", args.schedule, "--from", self(ctx)]));
973
982
  tool("swb_show_session", (args) => `${titleOf2(args.session)}${args.view ? ` · ${args.view}` : ""}`, (args, ctx) => swb(proc.launch.bin, ["show", args.session, "--from", self(ctx), ...args.view ? ["--view", args.view] : []]));
974
983
  const snoozeText = (snooze) => typeof snooze === "string" ? snooze : snooze.until;
975
984
  tool("swb_update_session", (args) => {
976
985
  const parts = [
977
- args.archived === true ? `archive${args.family ? " family" : ""}${args.force ? " · force" : ""}` : "",
986
+ args.archived === true ? `archive${args.family ? " family" : ""}${args.cancel_turns ? " · cancel turns" : ""}` : "",
978
987
  args.archived === false ? "unarchive" : "",
979
988
  args.cancel ? "cancel turn" : "",
980
989
  args.snooze !== undefined ? `snooze ${snoozeText(args.snooze)}` : "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thurstonsand/pi-agent-switchboard",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "description": "Records managed pi sessions for Agent Switchboard (swb)",
6
6
  "license": "MIT",