@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.
- package/CHANGELOG.md +36 -0
- package/index.js +70 -61
- 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 =
|
|
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], {
|
|
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
|
-
|
|
155
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
349
|
+
const reply = m.reportBack ? `
|
|
340
350
|
|
|
341
|
-
|
|
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
|
|
361
|
+
var reportBack = Type.Boolean({ description: "Whether it should reply to you." });
|
|
352
362
|
var TOOLS = {
|
|
353
|
-
|
|
354
|
-
label: "
|
|
363
|
+
session_list: {
|
|
364
|
+
label: "Sessions",
|
|
355
365
|
description: "List every open session but this one, in any project or harness.",
|
|
356
|
-
guidelines: ["Call
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
384
|
-
description: "Its first message
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
428
|
+
session_list_schedules: {
|
|
419
429
|
label: "Schedules",
|
|
420
|
-
description: "List the schedules this session set or receives
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
470
|
-
label: "
|
|
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
|
|
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.
|
|
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
|
-
|
|
507
|
-
description: "
|
|
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, ["
|
|
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("
|
|
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("
|
|
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
|
|
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
|
-
|
|
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.
|
|
954
|
-
...
|
|
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.
|
|
958
|
-
tool("session_schedule", (args) => `${args.session ? titleOf2(args.session) : "this session"} · ${args.
|
|
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.
|
|
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.
|
|
977
|
+
...args.report_back ? ["--report-back"] : [],
|
|
970
978
|
...args.snooze ? ["--snooze"] : []
|
|
971
979
|
], args.message));
|
|
972
|
-
tool("
|
|
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.
|
|
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)}` : "",
|